[chirp_devel] [PATCH 0 of 2] First round of pylint-informed cleanups

One problem with coding is python is the lack of a compile stage to point out obvious things like typos in variable names. I'd like to be able to use pylint for help with this, but the list of complaints is too large to be readable. I think it would be nice if we (I) could do the initial work to get the code in line with python coding standards so that the pylint output is reasonable. This is the first stab at that.
The first patch adds a pylintrc configuration file and the second is just one in a series of many to come which whittles down the list of complaints by a few thousand lines. It's mostly aimed at the chirp module core, drivers, etc. It doesn't touch the UI and it doesn't touch the bitwise stuff.
I'd recommend you run pylint like this:
pylint --rcfile=pylintrc --ignore=tmv71.py,tmv71_ll.py,thd72.py,\ pyPEG.py,bitwise.py,bitwise_grammar.py,ft50.py
There are still loads of things to fix, but this patch makes a good first stab at it. I'm sending this to the list, not because I'm really expecting any review, but more for a heads-up and/or round of comments about the approach. I want to get this committed to the tree soon because it's likely to clas with any/all patches from other folks... :)
Here's the (horrific) diffstat:
chirp/alinco.py | 89 ++++++++++--------- chirp/bitwise.py | 9 + chirp/chirp_common.py | 214 ++++++++++++++++++++++++++++------------------ chirp/detect.py | 48 ++++------ chirp/directory.py | 51 ++++------- chirp/errors.py | 6 + chirp/ft2800.py | 34 +++---- chirp/ft60.py | 28 +++--- chirp/ft7800.py | 76 +++++++++------- chirp/ft817.py | 222 ++++++++++++++++++++++++++++-------------------- chirp/ft857.py | 85 +++++++++++------- chirp/generic_csv.py | 76 ++++++++-------- chirp/generic_tpe.py | 19 ++-- chirp/generic_xml.py | 29 ++---- chirp/ic2100.py | 151 ++++++++++++++++---------------- chirp/ic2200.py | 45 ++++----- chirp/ic2720.py | 7 - chirp/ic2820.py | 64 +++++++------ chirp/ic9x.py | 101 ++++++++------------- chirp/ic9x_icf.py | 4 chirp/ic9x_icf_ll.py | 29 ++++-- chirp/ic9x_ll.py | 152 +++++++++++++++----------------- chirp/icf.py | 147 +++++++++++++++++++------------ chirp/icomciv.py | 47 +++++++--- chirp/icq7.py | 8 - chirp/ict70.py | 9 + chirp/icw32.py | 32 +++--- chirp/icx8x.py | 21 ++-- chirp/id31.py | 61 ++++++------- chirp/id800.py | 89 +++++++++---------- chirp/id880.py | 81 +++++++++-------- chirp/idrp.py | 28 +++--- chirp/import_logic.py | 15 ++- chirp/kenwood_hmk.py | 3 chirp/kenwood_live.py | 167 ++++++++++++++++++++---------------- chirp/memmap.py | 5 + chirp/platform.py | 90 +++++++++---------- chirp/radioreference.py | 23 +++- chirp/rfinder.py | 108 +++++++++++++---------- chirp/settings.py | 47 ++++++++-- chirp/template.py | 13 +- chirp/util.py | 12 +- chirp/uv5r.py | 194 ++++++++++++++++++++--------------------- chirp/vx3.py | 35 ++++--- chirp/vx5.py | 7 - chirp/vx6.py | 58 ++++++------ chirp/vx7.py | 32 +++--- chirp/vx8.py | 19 ++-- chirp/vxa700.py | 85 +++++++++--------- chirp/wouxun.py | 151 ++++++++++++++++++-------------- chirp/xml_ll.py | 20 ++-- chirp/yaesu_clone.py | 73 ++++++--------- chirpui/clone.py | 2 chirpui/mainapp.py | 2 54 files changed, 1755 insertions(+), 1468 deletions(-)

# HG changeset patch # User Dan Smith dsmith@danplanet.com # Date 1335562540 25200 # Node ID db82df47f205409ac1f71304f7372ca7f7d88e52 # Parent 958417a4504186fdaf05d42979c577cda19be42a Add a pylintrc file #93
diff -r 958417a45041 -r db82df47f205 pylintrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pylintrc Fri Apr 27 14:35:40 2012 -0700 @@ -0,0 +1,249 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +#disable= + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=text + +# Include message's id in output +include-ids=no + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=no + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy|foo + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,v,f,e,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__|_.* + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=20 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=25 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=0 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=40 + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception

# HG changeset patch # User Dan Smith dsmith@danplanet.com # Date 1335562541 25200 # Node ID f805c420592f8a8b1b0dd89107920c6bbed7fdf7 # Parent db82df47f205409ac1f71304f7372ca7f7d88e52 pylint fixes round 1 #93
diff -r db82df47f205 -r f805c420592f chirp/alinco.py --- a/chirp/alinco.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/alinco.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, bitwise, memmap, errors, directory +from chirp import chirp_common, bitwise, memmap, errors, directory, util
import time
-DRx35_mem_format = """ +DRX35_MEM_FORMAT = """ #seekto 0x0120; u8 used_flags[25];
@@ -59,7 +59,9 @@ STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0]
class AlincoStyleRadio(chirp_common.CloneModeRadio): + """Base class for all known Alinco radios""" _memsize = 0 + _model = "NONE"
def _send(self, data): self.pipe.write(data) @@ -96,20 +98,19 @@ time.sleep(0.1)
if self.status_fn: - s = chirp_common.Status() - s.cur = addr + 16 - s.max = self._memsize - s.msg = "Downloading from radio" - self.status_fn(s) + status = chirp_common.Status() + status.cur = addr + 16 + status.max = self._memsize + status.msg = "Downloading from radio" + self.status_fn(status)
self._send("AL~E\r\n") - r = self.pipe.read(20) - #print r + self.pipe.read(20)
return memmap.MemoryMap(data)
def _identify(self): - for i in range(0, 3): + for _i in range(0, 3): self._send("%s\r\n" % self._model) resp = self.pipe.read(6) if resp.strip() == "OK": @@ -137,18 +138,18 @@ time.sleep(0.1)
if self.status_fn: - s = chirp_common.Status() - s.cur = addr + 16 - s.max = self._memsize - s.msg = "Uploading to radio" - self.status_fn(s) + status = chirp_common.Status() + status.cur = addr + 16 + status.max = self._memsize + status.msg = "Uploading to radio" + self.status_fn(status)
self._send("AL~E\r\n") - r = self.pipe.read(20) + self.pipe.read(20) #print r
def process_mmap(self): - self._memobj = bitwise.parse(DRx35_mem_format, self._mmap) + self._memobj = bitwise.parse(DRX35_MEM_FORMAT, self._mmap)
def sync_in(self): try: @@ -183,32 +184,33 @@ [chr(x + ord("A")) for x in range(0, 26)] + [" "] + \ list("\x00" * 128)
+def _get_name(_mem): + name = "" + for i in _mem.name: + if not i: + break + name += CHARSET[i] + return name + +def _set_name(mem, _mem): + name = [0x00] * 7 + j = 0 + for i in range(0, 7): + try: + name[j] = CHARSET.index(mem.name[i]) + j += 1 + except IndexError: + pass + except ValueError: + pass + return name + class DRx35Radio(AlincoStyleRadio): + """Base class for the DR-x35 radios""" _range = [(118000000, 155000000)] _power_levels = [] _valid_tones = list(chirp_common.TONES)
- def _get_name(self, mem, _mem): - name = "" - for i in _mem.name: - if not i: - break - name += CHARSET[i] - return name - - def _set_name(self, mem, _mem): - name = [0x00] * 7 - j = 0 - for i in range(0, 7): - try: - name[j] = CHARSET.index(mem.name[i]) - j += 1 - except IndexError: - pass - except ValueError: - pass - return name - def get_features(self): rf = chirp_common.RadioFeatures() rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"] @@ -254,7 +256,7 @@ if _skp & bit: mem.skip = "S"
- mem.name = self._get_name(mem, _mem).rstrip() + mem.name = _get_name(_mem).rstrip()
return mem
@@ -298,10 +300,11 @@ else: _skp &= ~bit
- _mem.name = self._set_name(mem, _mem) + _mem.name = _set_name(mem, _mem)
@directory.register class DR03Radio(DRx35Radio): + """Alinco DR03""" VENDOR = "Alinco" MODEL = "DR03T"
@@ -316,6 +319,7 @@
@directory.register class DR06Radio(DRx35Radio): + """Alinco DR06""" VENDOR = "Alinco" MODEL = "DR06T"
@@ -330,6 +334,7 @@
@directory.register class DR135Radio(DRx35Radio): + """Alinco DR135""" VENDOR = "Alinco" MODEL = "DR135T"
@@ -344,6 +349,7 @@
@directory.register class DR235Radio(DRx35Radio): + """Alinco DR235""" VENDOR = "Alinco" MODEL = "DR235T"
@@ -358,6 +364,7 @@
@directory.register class DR435Radio(DRx35Radio): + """Alinco DR435""" VENDOR = "Alinco" MODEL = "DR435T"
@@ -385,6 +392,7 @@
@directory.register class DJ596Radio(DRx35Radio): + """Alinco DJ596""" VENDOR = "Alinco" MODEL = "DJ596"
@@ -402,6 +410,7 @@
@directory.register class JT220MRadio(DRx35Radio): + """Jetstream JT220""" VENDOR = "Jetstream" MODEL = "JT220M"
diff -r db82df47f205 -r f805c420592f chirp/bitwise.py --- a/chirp/bitwise.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/bitwise.py Fri Apr 27 14:35:41 2012 -0700 @@ -136,6 +136,10 @@ def set_raw(self, data): self._data[self._offset] = data[:self._size]
+ def __getattr__(self, name): + raise AttributeError("Unknown attribute %s in %s" % (name, + self.__class__)) + def __repr__(self): return "(%s:%i bytes @ %04x)" % (self.__class__.__name__, self._size, @@ -542,7 +546,10 @@ self._keys.append(key)
def __getattr__(self, name): - return self._generators[name] + try: + return self._generators[name] + except KeyError: + raise AttributeError("No attribute %s in struct" % name)
def __setattr__(self, name, value): if not self.__dict__.has_key("_structDataElement__init"): diff -r db82df47f205 -r f805c420592f chirp/chirp_common.py --- a/chirp/chirp_common.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/chirp_common.py Fri Apr 27 14:35:41 2012 -0700 @@ -17,9 +17,7 @@
#print "Using separation character of '%s'" % SEPCHAR
-import threading import math -from decimal import Decimal
from chirp import errors, memmap
@@ -60,7 +58,8 @@ "->DTCS", ]
-MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR", "P25"] +MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", + "DIG", "PKT", "NCW", "NCWR", "CWR", "P25"]
STD_6M_OFFSETS = [ (51620000, 51980000, -500000), @@ -108,6 +107,7 @@
# NB: This only works for some bands, throws an Exception otherwise def freq_to_band(freq): + """Returns the band (in cm) for a given frequency""" for band, (lo, hi) in BAND_TO_MHZ.items(): if int(freq) > lo and int(freq) < hi: return band @@ -138,12 +138,15 @@ CHARSET_ASCII = "".join([chr(x) for x in range(ord(" "), ord("~")+1)])
def watts_to_dBm(watts): + """Converts @watts in watts to dBm""" return int(10 * math.log10(int(watts * 1000)))
def dBm_to_watts(dBm): + """Converts @dBm from dBm to watts""" return int(math.pow(10, (dBm - 30) / 10))
class PowerLevel: + """Represents a power level supported by a radio""" def __init__(self, label, watts=0, dBm=0): if watts: dBm = watts_to_dBm(watts) @@ -180,6 +183,7 @@ return "%s (%i dBm)" % (self._label, self._power)
def parse_freq(freqstr): + """Parse a frequency string and return the value in integral Hz""" if "." in freqstr: MHz, kHz = freqstr.split(".") else: @@ -192,9 +196,12 @@ return int(("%s%-6s" % (MHz, kHz)).replace(" ", "0"))
def format_freq(freq): + """Format a frequency given in Hz as a string""" + return "%i.%06i" % (freq / 1000000, freq % 1000000)
class Memory: + """Base class for a single radio memory""" freq = 0 number = 0 extd_number = "" @@ -262,13 +269,15 @@ return "Memory[%i]" % self.number
def dupe(self): - m = self.__class__() + """Return a deep copy of @self""" + mem = self.__class__() for k, v in self.__dict__.items(): - m.__dict__[k] = v + mem.__dict__[k] = v
- return m + return mem
def clone(self, source): + """Absorb all of the properties of @source""" for k, v in source.__dict__.items(): self.__dict__[k] = v
@@ -294,9 +303,11 @@ self.__dict__[name] = val
def format_freq(self): + """Return a properly-formatted string of this memory's frequency""" return format_freq(self.freq)
def parse_freq(self, freqstr): + """Set the frequency from a string""" self.freq = parse_freq(freqstr) return self.freq
@@ -321,7 +332,7 @@ else: dup = self.duplex
- return "Memory %i: %s%s%s %s (%s) r%.1f%s c%.1f%s d%03i%s%s [TS=%.2f]"% \ + return "Memory %i: %s%s%s %s (%s) r%.1f%s c%.1f%s d%03i%s%s [%.2f]"% \ (self.number, format_freq(self.freq), dup, @@ -338,6 +349,7 @@ self.tuning_step)
def to_csv(self): + """Return a CSV representation of this memory""" return [ "%i" % self.number, "%s" % self.name, @@ -355,18 +367,16 @@ "%s" % self.comment, "", "", "", ""]
- class Callable: - def __init__(self, target): - self.__call__ = target - - def _from_csv(_line): + @classmethod + def _from_csv(cls, _line): line = _line.strip() if line.startswith("Location"): raise errors.InvalidMemoryLocation("Non-CSV line")
vals = line.split(SEPCHAR) if len(vals) < 11: - raise errors.InvalidDataError("CSV format error (14 columns expected)") + raise errors.InvalidDataError("CSV format error " + + "(14 columns expected)")
if vals[10] == "DV": mem = DVMemory() @@ -376,9 +386,8 @@ mem.really_from_csv(vals) return mem
- from_csv = Callable(_from_csv) - def really_from_csv(self, vals): + """Careful parsing of split-out @vals""" try: self.number = int(vals[0]) except: @@ -450,6 +459,7 @@ return True
class DVMemory(Memory): + """A Memory with D-STAR attributes""" dv_urcall = "CQCQCQ" dv_rpt1call = "" dv_rpt2call = "" @@ -493,10 +503,11 @@ self.dv_rpt2call = vals[17].rstrip()[:8] try: self.dv_code = int(vals[18].strip()) - except: + except Exception: self.dv_code = 0
class Bank: + """Base class for a radio's Bank""" def __init__(self, model, index, name): self._model = model self._index = index @@ -520,6 +531,7 @@ return self.get_index() == other.get_index()
class NamedBank(Bank): + """A bank that can have a name""" def set_name(self, name): """Changes the user-adjustable bank name""" self._name = name @@ -556,6 +568,7 @@ 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") @@ -579,18 +592,16 @@ pass
def console_status(status): + """Write a status object to the console""" import sys
sys.stderr.write("\r%s" % status)
-class Callable: - def __init__(self, target): - self.__call__ = target - BOOLEAN = [True, False]
class RadioFeatures: + """Radio Feature Flags""" _valid_map = { # General "has_bank_index" : BOOLEAN, @@ -602,7 +613,6 @@ "has_bank" : BOOLEAN, "has_bank_names" : BOOLEAN, "has_tuning_step" : BOOLEAN, - "has_name" : BOOLEAN, "has_ctone" : BOOLEAN, "has_cross" : BOOLEAN, "has_infinite_number" : BOOLEAN, @@ -663,11 +673,17 @@ name)) self.__dict__[name] = val
+ def __getattr__(self, name): + raise AttributeError("pylint is confused by RadioFeatures") + def init(self, attribute, default, doc=None): + """Initialize a feature flag @attribute with default value @default, + and documentation string @doc""" self.__setattr__(attribute, default) self.__docs[attribute] = doc
def get_doc(self, attribute): + """Return the description of @attribute""" return self.__docs[attribute]
def __init__(self): @@ -751,70 +767,79 @@ "callsign at the beginning of the master URCALL list")
def is_a_feature(self, name): + """Returns True if @name is a valid feature flag name""" return name in self._valid_map.keys()
def __getitem__(self, name): return self.__dict__[name]
class ValidationMessage(str): + """Base class for Validation Errors and Warnings""" pass
class ValidationWarning(ValidationMessage): + """A non-fatal warning during memory validation""" pass
class ValidationError(ValidationMessage): + """A fatal error during memory validation""" pass
class Radio: + """Base class for all Radio drivers""" BAUD_RATE = 9600 HARDWARE_FLOW = False VENDOR = "Unknown" MODEL = "Unknown" VARIANT = ""
- status_fn = lambda x, y: console_status(y) + def status_fn(self, status): + """Deliver @status to the UI""" + console_status(status)
def __init__(self, pipe): self.errors = [] self.pipe = pipe
def get_features(self): + """Return a RadioFeatures object for this radio""" return RadioFeatures()
def get_settings(self): + """Return a RadioSettingsGroup object for this radio""" return None
def set_settings(self, settings): + """Sets the settings contained in the @settings object that has + been queried with get_settings() and modified""" raise Exception("Not implemented")
- def _get_name_raw(*args): - cls = args[-1] + @classmethod + def get_name(cls): + """Return a printable name for this radio""" return "%s %s" % (cls.VENDOR, cls.MODEL)
- def get_name(self): - return self._get_name_raw(self.__class__) - - _get_name = Callable(_get_name_raw) - def set_pipe(self, pipe): + """Set the serial object to be used for communications""" self.pipe = pipe
def get_memory(self, number): + """Return a Memory object for the memory at location @number""" pass
def erase_memory(self, number): - m = Memory() - m.number = number - m.empty = True - self.set_memory(m) + """Erase memory at location @number""" + mem = Memory() + mem.number = number + mem.empty = True + self.set_memory(mem)
def get_memories(self, lo=None, hi=None): + """Get all the memories between @lo and @hi""" pass
def set_memory(self, memory): - pass - - def set_memories(self, memories): + """Set the memory object @memory""" pass
def get_bank_model(self): @@ -822,22 +847,30 @@ return None
def get_raw_memory(self, number): + """Return a raw string describing the memory at @number""" pass
def get_special_locations(self): + """Return a list of special memory location names""" return []
def filter_name(self, name): + """Filter @name to just the length and characters supported""" rf = self.get_features() if rf.valid_characters == rf.valid_characters.upper(): # Radio only supports uppercase, so help out here name = name.upper() - return "".join([x for x in name[:rf.valid_name_length] if x in rf.valid_characters]) + return "".join([x for x in name[:rf.valid_name_length] + if x in rf.valid_characters])
def get_sub_devices(self): + """Return a list of sub-device Radio objects, if + RadioFeatures.has_sub_devices is True""" return []
def validate_memory(self, mem): + """Return a list of warnings and errors that will be encoundered + if trying to set @mem on the current radio""" msgs = [] rf = self.get_features()
@@ -857,8 +890,10 @@ msgs.append(msg) else: if mem.tmode == "Cross": - if rf.valid_cross_modes and mem.cross_mode not in rf.valid_cross_modes: - msg = ValidationError("Cross tone mode %s not supported" % mem.cross_mode) + if rf.valid_cross_modes and \ + mem.cross_mode not in rf.valid_cross_modes: + msg = ValidationError("Cross tone mode %s not supported" % \ + mem.cross_mode) msgs.append(msg)
if rf.has_dtcs_polarity and mem.dtcs_polarity not in rf.valid_dtcs_pols: @@ -933,16 +968,24 @@ """A file-backed radio stores its data in a file""" FILE_EXTENSION = "img"
+ def __init__(self, *args, **kwargs): + Radio.__init__(self, *args, **kwargs) + self._memobj = None + def save(self, filename): + """Save the radio's memory map to @filename""" self.save_mmap(filename)
def load(self, filename): + """Load the radio's memory map object from @filename""" self.load_mmap(filename)
def process_mmap(self): + """Process a newly-loaded or downloaded memory map""" pass
def load_mmap(self, filename): + """Load the radio's memory map from @filename""" mapfile = file(filename, "rb") self._mmap = memmap.MemoryMap(mapfile.read()) mapfile.close() @@ -957,26 +1000,13 @@ mapfile = file(filename, "wb") mapfile.write(self._mmap.get_packed()) mapfile.close() - except IOError,e: + except IOError: raise Exception("File Access Error")
def get_mmap(self): + """Return the radio's memory map object""" return self._mmap
- def get_memsize(self): - return self._memsize - - @classmethod - def match_model(cls, filedata, filename): - """Given contents of a stored file (@filedata), return True if - this radio driver handles the represented model""" - - # Unless the radio driver does something smarter, claim - # support if the data is the same size as our memory. - # Ideally, each radio would perform an intelligent analysis to - # make this determination to avoid model conflicts with - # memories of the same size. - return len(filedata) == cls._memsize
class CloneModeRadio(FileBackedRadio): @@ -997,7 +1027,23 @@ self._mmap = pipe self.process_mmap() else: - Radio.__init__(self, pipe) + FileBackedRadio.__init__(self, pipe) + + def get_memsize(self): + """Return the radio's memory size""" + return self._memsize + + @classmethod + def match_model(cls, filedata, filename): + """Given contents of a stored file (@filedata), return True if + this radio driver handles the represented model""" + + # Unless the radio driver does something smarter, claim + # support if the data is the same size as our memory. + # Ideally, each radio would perform an intelligent analysis to + # make this determination to avoid model conflicts with + # memories of the same size. + return len(filedata) == cls._memsize
def sync_in(self): "Initiate a radio-to-PC clone operation" @@ -1008,36 +1054,47 @@ pass
class LiveRadio(Radio): + """Base class for all Live-Mode radios""" pass
class NetworkSourceRadio(Radio): + """Base class for all radios based on a network source""" def do_fetch(self): + """Fetch the source data from the network""" pass
class IcomDstarSupport: + """Base interface for radios supporting Icom's D-STAR technology""" MYCALL_LIMIT = (1, 1) URCALL_LIMIT = (1, 1) RPTCALL_LIMIT = (1, 1)
def get_urcall_list(self): + """Return a list of URCALL callsigns""" return []
def get_repeater_call_list(self): + """Return a list of RPTCALL callsigns""" return []
def get_mycall_list(self): + """Return a list of MYCALL callsigns""" return []
def set_urcall_list(self, calls): + """Set the URCALL callsign list""" pass
def set_repeater_call_list(self, calls): + """Set the RPTCALL callsign list""" pass
def set_mycall_list(self, calls): + """Set the MYCALL callsign list""" pass
class Status: + """Clone status object for conveying clone progress to the UI""" name = "Job" msg = "Unknown" max = 100 @@ -1055,21 +1112,27 @@ return "|%-10s| %2.1f%% %s" % (ticks, pct, self.msg)
def is_fractional_step(freq): + """Returns True if @freq requires a 12.5kHz or 6.25kHz step""" return not is_5_0(freq) and (is_12_5(freq) or is_6_25(freq))
def is_5_0(freq): + """Returns True if @freq is reachable by a 5kHz step""" return (freq % 5000) == 0
def is_12_5(freq): + """Returns True if @freq is reachable by a 12.5kHz step""" return (freq % 12500) == 0
def is_6_25(freq): + """Returns True if @freq is reachable by a 6.25kHz step""" return (freq % 6250) == 0
def is_2_5(freq): + """Returns True if @freq is reachable by a 2.5kHz step""" return (freq % 2500) == 0
def required_step(freq): + """Returns the simplest tuning step that is required to reach @freq""" if is_5_0(freq): return 5.0 elif is_12_5(freq): @@ -1080,10 +1143,13 @@ return 2.5 else: raise errors.InvalidDataError("Unable to calculate the required " + - "tuning step for %i.%5i" % (freq / 1000000, - freq % 1000000)) + "tuning step for %i.%5i" % \ + (freq / 1000000, + freq % 1000000))
def fix_rounded_step(freq): + """Some radios imply the last bit of 12.5kHz and 6.25kHz step + frequencies. Take the base @freq and return the corrected one""" try: required_step(freq) return freq @@ -1112,57 +1178,43 @@ format_freq(freq))
def _name(name, len, just_upper): + """Justify @name to @len, optionally converting to all uppercase""" if just_upper: name = name.upper() return name.ljust(len)[:len]
def name6(name, just_upper=True): + """6-char name""" return _name(name, 6, just_upper)
def name8(name, just_upper=False): + """8-char name""" return _name(name, 8, just_upper)
def name16(name, just_upper=False): + """16-char name""" return _name(name, 16, just_upper)
-class KillableThread(threading.Thread): - def __tid(self): - if not self.isAlive(): - raise threading.ThreadError("Not running") - - for tid, thread in threading._active.items(): - if thread == self: - return tid - - raise threading.ThreadError("I don't know my own TID") - - def kill(self, exception): - import ctypes - import inspect - - if not inspect.isclass(exception): - raise Exception("Parameter is not an Exception") - - ctype = ctypes.py_object(exception) - ret = ctypes.pythonapi.PyThreadState_SetAsyncExc(self.__tid(), ctype) - if ret != 1: - ctypes.pythonapi.PyThreadState_SetAsyncExc(self.__tid(), 0) - raise Exception("Failed to signal thread!") - def to_GHz(val): + """Convert @val in GHz to Hz""" return val * 1000000000
def to_MHz(val): + """Convert @val in MHz to Hz""" return val * 1000000
def to_kHz(val): + """Convert @val in kHz to Hz""" return val * 1000
def from_GHz(val): + """Convert @val in Hz to GHz""" return val / 100000000
def from_MHz(val): + """Convert @val in Hz to MHz""" return val / 100000
def from_kHz(val): + """Convert @val in Hz to kHz""" return val / 100 diff -r db82df47f205 -r f805c420592f chirp/detect.py --- a/chirp/detect.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/detect.py Fri Apr 27 14:35:41 2012 -0700 @@ -15,16 +15,16 @@
import serial
-from chirp import chirp_common, errors, idrp, util, icf, directory, ic9x_ll -from chirp import kenwood_live, tmv71, tmv71_ll, icomciv, thd72 +from chirp import errors, icf, directory, ic9x_ll +from chirp import kenwood_live, icomciv
def _icom_model_data_to_rclass(md): - for rtype, rclass in directory.DRV_TO_RADIO.items(): + for _rtype, rclass in directory.DRV_TO_RADIO.items(): if rclass.VENDOR != "Icom": continue - if not rclass._model: + if not rclass.get_model(): continue - if rclass._model[:4] == md[:4]: + if rclass.get_model()[:4] == md[:4]: return rclass
raise errors.RadioError("Unknown radio type %02x%02x%02x%02x" %\ @@ -33,22 +33,21 @@ ord(md[2]), ord(md[3])))
-def _detect_icom_radio(s): +def _detect_icom_radio(ser): # ICOM VHF/UHF Clone-type radios @ 9600 baud
try: - s.setBaudrate(9600) - md = icf.get_model_data(s) + ser.setBaudrate(9600) + md = icf.get_model_data(ser) return _icom_model_data_to_rclass(md) except errors.RadioError, e: print e - pass
# ICOM IC-91/92 Live-mode radios @ 4800/38400 baud
- s.setBaudrate(4800) + ser.setBaudrate(4800) try: - ic9x_ll.send_magic(s) + ic9x_ll.send_magic(ser) return _icom_model_data_to_rclass("ic9x") except errors.RadioError: pass @@ -57,25 +56,26 @@
for rate in [9600, 4800, 19200]: try: - s.setBaudrate(rate) - return icomciv.probe_model(s) + ser.setBaudrate(rate) + return icomciv.probe_model(ser) except errors.RadioError: pass
- s.close() + ser.close()
raise errors.RadioError("Unable to get radio model")
def detect_icom_radio(port): - s = serial.Serial(port=port, timeout=0.5) + """Detect which Icom model is connected to @port""" + ser = serial.Serial(port=port, timeout=0.5)
try: - result = _detect_icom_radio(s) + result = _detect_icom_radio(ser) except Exception: - s.close() + ser.close() raise
- s.close() + ser.close()
print "Auto-detected %s %s on %s" % (result.VENDOR, result.MODEL, @@ -84,9 +84,10 @@ return result
def detect_kenwoodlive_radio(port): - s = serial.Serial(port=port, baudrate=9600, timeout=0.5) - r_id = kenwood_live.get_id(s) - s.close() + """Detect which Kenwood model is connected to @port""" + ser = serial.Serial(port=port, baudrate=9600, timeout=0.5) + r_id = kenwood_live.get_id(ser) + ser.close()
models = {} for rclass in directory.DRV_TO_RADIO.values(): @@ -102,8 +103,3 @@ "Icom" : detect_icom_radio, "Kenwood" : detect_kenwoodlive_radio, } - -if __name__ == "__main__": - import sys - - print "Found %s" % detect_radio(sys.argv[1]) diff -r db82df47f205 -r f805c420592f chirp/directory.py --- a/chirp/directory.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/directory.py Fri Apr 27 14:35:41 2012 -0700 @@ -21,6 +21,7 @@ from chirp import chirp_common, util, rfinder, radioreference, errors
def radio_class_id(cls): + """Return a unique identification string for @cls""" ident = "%s_%s" % (cls.VENDOR, cls.MODEL) if cls.VARIANT: ident += "_%s" % cls.VARIANT @@ -31,6 +32,7 @@ return ident
def register(cls): + """Register radio @cls with the directory""" global DRV_TO_RADIO ident = radio_class_id(cls) if ident in DRV_TO_RADIO.keys(): @@ -45,20 +47,24 @@ RADIO_TO_DRV = {}
def get_radio(driver): + """Get radio driver class by identification string""" if DRV_TO_RADIO.has_key(driver): return DRV_TO_RADIO[driver] else: raise Exception("Unknown radio type `%s'" % driver)
-def get_driver(radio): - if RADIO_TO_DRV.has_key(radio): - return RADIO_TO_DRV[radio] - elif RADIO_TO_DRV.has_key(radio.__bases__[0]): - return RADIO_TO_DRV[radio.__bases__[0]] +def get_driver(rclass): + """Get the identification string for a given class""" + if RADIO_TO_DRV.has_key(rclass): + return RADIO_TO_DRV[rclass] + elif RADIO_TO_DRV.has_key(rclass.__bases__[0]): + return RADIO_TO_DRV[rclass.__bases__[0]] else: - raise Exception("Unknown radio type `%s'" % radio) + raise Exception("Unknown radio type `%s'" % rclass)
def icf_to_image(icf_file, img_file): + # FIXME: Why is this here? + """Convert an ICF file to a .img file""" mdata, mmap = icf.read_file(icf_file) img_data = None
@@ -80,16 +86,17 @@ raise Exception("Unsupported model")
def get_radio_by_image(image_file): + """Attempt to get the radio class that owns @image_file""" if image_file.startswith("radioreference://"): - method, _, zipcode, username, password = image_file.split("/", 4) + _, _, zipcode, username, password = image_file.split("/", 4) rr = radioreference.RadioReferenceRadio(None) rr.set_params(zipcode, username, password) return rr
if image_file.startswith("rfinder://"): - method, _, email, passwd, lat, lon, miles = image_file.split("/") + _, _, email, passwd, lat, lon, miles = image_file.split("/") rf = rfinder.RFinderRadio(None) - rf.set_params(float(lat), float(lon), int(miles), email, passwd) + rf.set_params((float(lat), float(lon)), int(miles), email, passwd) return rf
if os.path.exists(image_file) and icf.is_icf_file(image_file): @@ -105,27 +112,9 @@ else: filedata = ""
- for radio in DRV_TO_RADIO.values(): - if not issubclass(radio, chirp_common.FileBackedRadio): + for rclass in DRV_TO_RADIO.values(): + if not issubclass(rclass, chirp_common.FileBackedRadio): continue - if radio.match_model(filedata, image_file): - return radio(image_file) + if rclass.match_model(filedata, image_file): + return rclass(image_file) raise errors.ImageDetectFailed("Unknown file format") - -def get_radio_name(driver): - cls = DRV_TO_RADIO[driver] - return cls._get_name(cls) - -if __name__ == "__main__": - vendors = { - "Icom" : {}, - "Yaesu" : {}, - "Kenwood" : {}, - } - - for radio in DRV_TO_RADIO.values(): - vendors[radio.VENDOR][radio.MODEL] - print "%s %s:" % (radio.VENDOR, radio.MODEL) - if radio.VARIANT: - print " Variant: %s" % radio.VARIANT - print " Baudrate: %i" % radio.BAUD_RATE diff -r db82df47f205 -r f805c420592f chirp/errors.py --- a/chirp/errors.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/errors.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,19 +14,25 @@ # along with this program. If not, see http://www.gnu.org/licenses/.
class InvalidDataError(Exception): + """The radio driver encountered some invalid data""" pass
class InvalidValueError(Exception): + """An invalid value for a given parameter was used""" pass
class InvalidMemoryLocation(Exception): + """The requested memory location does not exist""" pass
class RadioError(Exception): + """An error occurred while talking to the radio""" pass
class UnsupportedToneError(Exception): + """The radio does not support the specified tone value""" pass
class ImageDetectFailed(Exception): + """The driver for the supplied image could not be determined""" pass diff -r db82df47f205 -r f805c420592f chirp/ft2800.py --- a/chirp/ft2800.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ft2800.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,7 +14,6 @@ # along with this program. If not, see http://www.gnu.org/licenses/.
import time -import struct import os
from chirp import util, memmap, chirp_common, bitwise, directory, errors @@ -23,7 +22,7 @@ DEBUG = os.getenv("CHIRP_DEBUG") and True or False
CHUNK_SIZE = 16 -def send(s, data): +def _send(s, data): for i in range(0, len(data), CHUNK_SIZE): chunk = data[i:i+CHUNK_SIZE] s.write(chunk) @@ -35,9 +34,9 @@ TRAILER = "\x0c\x02\x41\x33\x35\x00\x00\xb7" ACK = "\x0C\x06\x00"
-def download(radio): +def _download(radio): data = "" - for i in range(0, 10): + for _i in range(0, 10): data = radio.pipe.read(8) if data == IDBLOCK: break @@ -48,7 +47,7 @@ if len(data) != 8: raise Exception("Failed to read header")
- send(radio.pipe, ACK) + _send(radio.pipe, ACK)
data = ""
@@ -72,7 +71,7 @@
data += chunk[5:-1]
- send(radio.pipe, ACK) + _send(radio.pipe, ACK) if radio.status_fn: status = chirp_common.Status() status.max = radio._block_sizes[1] @@ -85,14 +84,14 @@
return memmap.MemoryMap(data)
-def upload(radio): - for i in range(0, 10): +def _upload(radio): + for _i in range(0, 10): data = radio.pipe.read(256) if not data: break print "What is this garbage?\n%s" % util.hexprint(data)
- send(radio.pipe, IDBLOCK) + _send(radio.pipe, IDBLOCK) time.sleep(1) ack = radio.pipe.read(300) if DEBUG: @@ -101,9 +100,9 @@ raise Exception("Radio did not ack ID")
block = 0 - while block < (radio._memsize / 32): + while block < (radio.get_memsize() / 32): data = "\x0C\x03\x00\x00" + chr(block) - data += radio._mmap[block*32:(block+1)*32] + data += radio.get_mmap()[block*32:(block+1)*32] cs = 0 for byte in data: cs += ord(byte) @@ -112,7 +111,7 @@ if DEBUG: print "Writing block %i:\n%s" % (block, util.hexprint(data))
- send(radio.pipe, data) + _send(radio.pipe, data) time.sleep(0.1) ack = radio.pipe.read(3) if ack != ACK: @@ -126,9 +125,9 @@ radio.status_fn(status) block += 1
- send(radio.pipe, TRAILER) + _send(radio.pipe, TRAILER)
-mem_format = """ +MEM_FORMAT = """ struct { bbcd freq[4]; u8 unknown1[4]; @@ -164,6 +163,7 @@
@directory.register class FT2800Radio(YaesuCloneModeRadio): + """Yaesu FT-2800""" VENDOR = "Yaesu" MODEL = "FT-2800M"
@@ -196,7 +196,7 @@ self.pipe.setParity("E") start = time.time() try: - self._mmap = download(self) + self._mmap = _download(self) except errors.RadioError: raise except Exception, e: @@ -209,7 +209,7 @@ self.pipe.setParity("E") start = time.time() try: - upload(self) + _upload(self) except errors.RadioError: raise except Exception, e: @@ -217,7 +217,7 @@ print "Uploaded in %.2f sec" % (time.time() - start)
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number]) diff -r db82df47f205 -r f805c420592f chirp/ft60.py --- a/chirp/ft60.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ft60.py Fri Apr 27 14:35:41 2012 -0700 @@ -19,13 +19,13 @@
ACK = "\x06"
-def send(pipe, data): +def _send(pipe, data): pipe.write(data) echo = pipe.read(len(data)) if echo != data: raise errors.RadioError("Error reading echo (Bad cable?)")
-def download(radio): +def _download(radio): data = "" for i in range(0, 10): chunk = radio.pipe.read(8) @@ -40,12 +40,12 @@ if not data: raise Exception("Radio is not responding")
- send(radio.pipe, ACK) + _send(radio.pipe, ACK)
for i in range(0, 448): chunk = radio.pipe.read(64) data += chunk - send(radio.pipe, ACK) + _send(radio.pipe, ACK) if len(chunk) == 1 and i == 447: break elif len(chunk) != 64: @@ -53,14 +53,14 @@ if radio.status_fn: status = chirp_common.Status() status.cur = i * 64 - status.max = radio._memsize + status.max = radio.get_memsize() status.msg = "Cloning from radio" radio.status_fn(status)
return memmap.MemoryMap(data)
-def upload(radio): - send(radio.pipe, radio._mmap[0:8]) +def _upload(radio): + _send(radio.pipe, radio.get_mmap()[0:8])
ack = radio.pipe.read(1) if ack != ACK: @@ -68,7 +68,7 @@
for i in range(0, 448): offset = 8 + (i * 64) - send(radio.pipe, radio._mmap[offset:offset+64]) + _send(radio.pipe, radio.get_mmap()[offset:offset+64]) ack = radio.pipe.read(1) if ack != ACK: raise Exception("Radio did not ack block %i" % i) @@ -76,11 +76,11 @@ if radio.status_fn: status = chirp_common.Status() status.cur = offset+64 - status.max = radio._memsize + status.max = radio.get_memsize() status.msg = "Cloning to radio" radio.status_fn(status)
-mem_format = """ +MEM_FORMAT = """ #seekto 0x0238; struct { u8 used:1, @@ -136,6 +136,7 @@
@directory.register class FT60Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu FT-60""" BAUD_RATE = 9600 VENDOR = "Yaesu" MODEL = "FT-60" @@ -158,6 +159,7 @@ rf.has_ctone = False rf.has_bank = False rf.has_dtcs_polarity = False + return rf
def _checksums(self): @@ -165,7 +167,7 @@
def sync_in(self): try: - self._mmap = download(self) + self._mmap = _download(self) except errors.RadioError: raise except Exception, e: @@ -175,14 +177,14 @@ def sync_out(self): self.update_checksums() try: - upload(self) + _upload(self) except errors.RadioError: raise except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e)
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number]) + \ diff -r db82df47f205 -r f805c420592f chirp/ft7800.py --- a/chirp/ft7800.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ft7800.py Fri Apr 27 14:35:41 2012 -0700 @@ -15,11 +15,11 @@
import time from chirp import chirp_common, yaesu_clone, memmap, directory -from chirp import bitwise, util, errors +from chirp import bitwise, errors
ACK = chr(0x06)
-mem_format = """ +MEM_FORMAT = """ #seekto 0x04C8; struct { u8 used:1, @@ -89,16 +89,15 @@ chirp_common.PowerLevel("Mid2", watts=10), chirp_common.PowerLevel("Low", watts=5)]
-def send(s, data): - +def _send(ser, data): for i in data: - s.write(i) + ser.write(i) time.sleep(0.002) - echo = s.read(len(data)) + echo = ser.read(len(data)) if echo != data: raise errors.RadioError("Error reading echo (Bad cable?)")
-def download(radio): +def _download(radio): data = ""
chunk = "" @@ -111,49 +110,49 @@ raise Exception("Failed to read header (%i)" % len(chunk)) data += chunk
- send(radio.pipe, ACK) + _send(radio.pipe, ACK)
for i in range(0, radio._block_lengths[1], 64): chunk = radio.pipe.read(64) data += chunk if len(chunk) != 64: break - raise Exception("No block at %i" % i) time.sleep(0.01) - send(radio.pipe, ACK) + _send(radio.pipe, ACK) if radio.status_fn: status = chirp_common.Status() - status.max = radio._memsize + status.max = radio.get_memsize() status.cur = i+len(chunk) status.msg = "Cloning from radio" radio.status_fn(status)
data += radio.pipe.read(1) - send(radio.pipe, ACK) + _send(radio.pipe, ACK)
return memmap.MemoryMap(data)
-def upload(radio): +def _upload(radio): cur = 0 for block in radio._block_lengths: - for i in range(0, block, 64): + for _i in range(0, block, 64): length = min(64, block) #print "i=%i length=%i range: %i-%i" % (i, length, # cur, cur+length) - send(radio.pipe, radio._mmap[cur:cur+length]) + _send(radio.pipe, radio.get_mmap()[cur:cur+length]) if radio.pipe.read(1) != ACK: raise errors.RadioError("Radio did not ack block at %i" % cur) cur += length time.sleep(0.05)
if radio.status_fn: - s = chirp_common.Status() - s.cur = cur - s.max = radio._memsize - s.msg = "Cloning to radio" - radio.status_fn(s) + status = chirp_common.Status() + status.cur = cur + status.max = radio.get_memsize() + status.msg = "Cloning to radio" + radio.status_fn(status)
def get_freq(rawfreq): + """Decode a frequency that may include a fractional step flag""" # Ugh. The 0x80 and 0x40 indicate values to add to get the # real frequency. Gross. if rawfreq > 8000000000: @@ -165,6 +164,7 @@ return rawfreq
def set_freq(freq, obj, field): + """Encode a frequency with any necessary fractional step flags""" obj[field] = freq / 10000 if (freq % 1000) == 500: obj[field][0].set_bits(0x40) @@ -175,6 +175,7 @@ return freq
class FTx800Radio(yaesu_clone.YaesuCloneModeRadio): + """Base class for FT-7800,7900,8800,8900 radios""" BAUD_RATE = 9600 VENDOR = "Yaesu"
@@ -200,30 +201,30 @@ return [ yaesu_clone.YaesuChecksum(0x0000, 0x7B47) ]
def sync_in(self): - t = time.time() + start = time.time() try: - self._mmap = download(self) + self._mmap = _download(self) except errors.RadioError: raise except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e) - print "Download finished in %i seconds" % (time.time() - t) + print "Download finished in %i seconds" % (time.time() - start) self.check_checksums() self.process_mmap()
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def sync_out(self): self.update_checksums() - t = time.time() + start = time.time() try: - upload(self) + _upload(self) except errors.RadioError: raise except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e) - print "Upload finished in %i seconds" % (time.time() - t) + print "Upload finished in %i seconds" % (time.time() - start)
def get_raw_memory(self, number): return repr(self._memobj.memory[number-1]) @@ -330,6 +331,7 @@ self._set_mem_skip(mem, _mem)
class FT7800BankModel(chirp_common.BankModel): + """Yaesu FT-7800/7900 bank model""" def get_num_banks(self): return 20
@@ -353,8 +355,8 @@ _bitmap = self._radio._memobj.bank_channels[bank.index] ishft = 31 - (index % 32) if not (_bitmap.bitmap[index / 32] & (1 << ishft)): - raise Exception(_("Memory {num} is " - "not in bank {bank}").format(num=memory.number, + raise Exception("Memory {num} is " + + "not in bank {bank}".format(num=memory.number, bank=bank)) _bitmap.bitmap[index / 32] &= ~(1 << ishft)
@@ -377,6 +379,7 @@
@directory.register class FT7800Radio(FTx800Radio): + """Yaesu FT-7800""" MODEL = "FT-7800"
_model = "AH016" @@ -396,9 +399,10 @@ FTx800Radio.set_memory(self, memory)
class FT7900Radio(FT7800Radio): + """Yaesu FT-7900""" MODEL = "FT-7900"
-mem_format_8800 = """ +MEM_FORMAT_8800 = """ #seekto %s; struct { u8 used:1, @@ -434,6 +438,7 @@
@directory.register class FT8800Radio(FTx800Radio): + """Base class for Yaesu FT-8800""" MODEL = "FT-8800"
_model = "AH018" @@ -460,7 +465,7 @@ if not self._memstart: return
- self._memobj = bitwise.parse(mem_format_8800 % self._memstart, + self._memobj = bitwise.parse(MEM_FORMAT_8800 % self._memstart, self._mmap)
def _get_mem_offset(self, mem, _mem): @@ -502,14 +507,16 @@ _mem.nameused = bool(mem.name.rstrip())
class FT8800RadioLeft(FT8800Radio): + """Yaesu FT-8800 Left VFO subdevice""" VARIANT = "Left" _memstart = "0x0948"
class FT8800RadioRight(FT8800Radio): + """Yaesu FT-8800 Right VFO subdevice""" VARIANT = "Right" _memstart = "0x2948"
-mem_format_8900 = """ +MEM_FORMAT_8900 = """ #seekto 0x0708; struct { u8 used:1, @@ -546,6 +553,7 @@
@directory.register class FT8900Radio(FT8800Radio): + """Yaesu FT-8900""" MODEL = "FT-8900"
_model = "AH008" @@ -553,7 +561,7 @@ _block_lengths = [8, 14784, 1]
def process_mmap(self): - self._memobj = bitwise.parse(mem_format_8900, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT_8900, self._mmap)
def get_features(self): rf = FT8800Radio.get_features(self) @@ -595,7 +603,7 @@ # the memory should show up on the sub (right) band _mem = self._memobj.memory[mem.number - 1] if mem.freq < 108000000 or mem.freq > 480000000: - _mem.sub_used = 0; + _mem.sub_used = 0 else: _mem.sub_used = 1
diff -r db82df47f205 -r f805c420592f chirp/ft817.py --- a/chirp/ft817.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ft817.py Fri Apr 27 14:35:41 2012 -0700 @@ -20,26 +20,29 @@
CMD_ACK = 0x06
-def ft817_read(pipe, block, blocknum): - for i in range(0,60): +def _ft8x7_read(pipe, block, blocknum): + for _i in range(0, 60): data = pipe.read(block+2) - if data: + if data: break - time.sleep(0.5) + time.sleep(0.5) if len(data) == block+2 and data[0] == chr(blocknum): - checksum = yaesu_clone.YaesuChecksum(1, block) - if checksum.get_existing(data) != \ - checksum.get_calculated(data): - raise Exception("Checksum Failed [%02X<>%02X] block %02X" % (checksum.get_existing(data), checksum.get_calculated(data), blocknum)) - data = data[1:block+1] # Chew away the block number and the checksum + checksum = yaesu_clone.YaesuChecksum(1, block) + if checksum.get_existing(data) != \ + checksum.get_calculated(data): + raise Exception("Checksum Failed [%02X<>%02X] block %02X" % \ + (checksum.get_existing(data), + checksum.get_calculated(data), blocknum)) + data = data[1:block+1] # Chew away the block number and the checksum else: - raise Exception("Unable to read block %02X expected %i got %i" % (blocknum, block+2, len(data))) + raise Exception("Unable to read block %02X expected %i got %i" % \ + (blocknum, block+2, len(data)))
if os.getenv("CHIRP_DEBUG"): print "Read %i" % len(data) return data
-def clone_in(radio): +def _ft8x7_clone_in(radio): pipe = radio.pipe
# Be very patient with the radio @@ -54,21 +57,21 @@ status.max = len(radio._block_lengths) + 39 for block in radio._block_lengths: if blocks == 8: - repeat = 40 # repeated read of 40 block same size (memory area btw) + repeat = 40 # repeated read of 40 block same size (memory area) else: repeat = 1 - for i in range(0, repeat): - data += ft817_read(pipe, block, blocks) - pipe.write(chr(CMD_ACK)) - blocks += 1 - status.cur = blocks - radio.status_fn(status) + for _i in range(0, repeat): + data += _ft8x7_read(pipe, block, blocks) + pipe.write(chr(CMD_ACK)) + blocks += 1 + status.cur = blocks + radio.status_fn(status)
print "Clone completed in %i seconds" % (time.time() - start)
return memmap.MemoryMap(data)
-def clone_out(radio): +def _ft8x7_clone_out(radio): delay = 0.5 pipe = radio.pipe
@@ -81,21 +84,24 @@ status.max = len(radio._block_lengths) + 39 for block in radio._block_lengths: if blocks == 8: - repeat = 40 # repeated read of 40 block same size (memory area btw) + repeat = 40 # repeated read of 40 block same size (memory area) else: repeat = 1 - for i in range(0, repeat): + for _i in range(0, repeat): time.sleep(0.01) checksum = yaesu_clone.YaesuChecksum(pos, pos+block-1) if os.getenv("CHIRP_DEBUG"): - print "Block %i - will send from %i to %i byte " % (blocks, pos, pos+block) + print "Block %i - will send from %i to %i byte " % (blocks, + pos, + pos+block) print util.hexprint(chr(blocks)) - print util.hexprint(radio._mmap[pos:pos+block]) - print util.hexprint(chr(checksum.get_calculated(radio._mmap))) + print util.hexprint(radio.get_mmap()[pos:pos+block]) + print util.hexprint(chr(checksum.get_calculated(\ + radio.get_mmap()))) pipe.write(chr(blocks)) - pipe.write(radio._mmap[pos:pos+block]) - pipe.write(chr(checksum.get_calculated(radio._mmap))) - buf = pipe.read(1) + pipe.write(radio.get_mmap()[pos:pos+block]) + pipe.write(chr(checksum.get_calculated(radio.get_mmap()))) + buf = pipe.read(1) if not buf or buf[0] != chr(CMD_ACK): time.sleep(delay) buf = pipe.read(1) @@ -105,12 +111,12 @@ raise Exception("Radio did not ack block %i" % blocks) pos += block blocks += 1 - status.cur = blocks - radio.status_fn(status) + status.cur = blocks + radio.status_fn(status)
print "Clone completed in %i seconds" % (time.time() - start)
-mem_format = """ +MEM_FORMAT = """ struct mem_struct { u8 tag_on_off:1, tag_default:1, @@ -167,21 +173,29 @@
@directory.register class FT817Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu FT-817""" BAUD_RATE = 9600 MODEL = "FT-817" _model = ""
DUPLEX = ["", "-", "+", "split"] - MODES = ["LSB", "USB", "CW", "CWR", "AM", "FM", "DIG", "PKT", "NCW", "NCWR", "NFM"] # narrow modes has to be at end + # narrow modes has to be at end + MODES = ["LSB", "USB", "CW", "CWR", "AM", "FM", "DIG", "PKT", "NCW", + "NCWR", "NFM"] TMODES = ["", "Tone", "TSQL", "DTCS"] STEPSFM = [5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0] STEPSAM = [2.5, 5.0, 9.0, 10.0, 12.5, 25.0] STEPSSSB = [1.0, 2.5, 5.0] - VALID_BANDS = [(100000,33000000), (33000000,56000000), (76000000,108000000), (108000000,137000000), (137000000,154000000), (420000000,470000000)] # warning ranges has to be in this exact order + + # warning ranges has to be in this exact order + VALID_BANDS = [(100000, 33000000), (33000000, 56000000), + (76000000, 108000000), (108000000, 137000000), + (137000000, 154000000), (420000000, 470000000)]
CHARSET = [chr(x) for x in range(0, 256)]
- POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.00), # not used in memory + # Hi not used in memory + POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.00), chirp_common.PowerLevel("L3", watts=2.50), chirp_common.PowerLevel("L2", watts=1.00), chirp_common.PowerLevel("L1", watts=0.5)] @@ -190,7 +204,8 @@ # block 9 (130 Bytes long) is to be repeted 40 times _block_lengths = [ 2, 40, 208, 182, 208, 182, 198, 53, 130, 118, 118]
- SPECIAL_MEMORIES = { # WARNING Index are hard wired in memory management code !!! + # WARNING Index are hard wired in memory management code !!! + SPECIAL_MEMORIES = { "VFOa-1.8M" : -35, "VFOa-3.5M" : -34, "VFOa-7M" : -33, @@ -232,11 +247,12 @@ FIRST_VFOA_INDEX = -21 LAST_VFOA_INDEX = -35
- SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), SPECIAL_MEMORIES.keys())) + SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), + SPECIAL_MEMORIES.keys()))
def sync_in(self): try: - self._mmap = clone_in(self) + self._mmap = _ft8x7_clone_in(self) except errors.RadioError: raise except Exception, e: @@ -245,14 +261,14 @@
def sync_out(self): try: - clone_out(self) + _ft8x7_clone_out(self) except errors.RadioError: raise except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e)
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -276,27 +292,28 @@ def get_raw_memory(self, number): return repr(self._memobj.memory[number-1])
- def get_duplex(self, mem, _mem): + def _get_duplex(self, mem, _mem): if _mem.is_duplex == 1: mem.duplex = self.DUPLEX[_mem.duplex] else: mem.duplex = ""
- def get_tmode(self, mem, _mem): + def _get_tmode(self, mem, _mem): mem.tmode = self.TMODES[_mem.tmode]
- def set_duplex(self, mem, _mem): + def _set_duplex(self, mem, _mem): _mem.duplex = self.DUPLEX.index(mem.duplex) _mem.is_duplex = mem.duplex != ""
- def set_tmode(self, mem, _mem): + def _set_tmode(self, mem, _mem): _mem.tmode = self.TMODES.index(mem.tmode)
def get_memory(self, number): if isinstance(number, str): return self._get_special(number) elif number < 0: - # I can't stop delete operation from loosing extd_number but I know how to get it back + # I can't stop delete operation from loosing extd_number but + # I know how to get it back return self._get_special(self.SPECIAL_MEMORIES_REV[number]) else: return self._get_normal(number) @@ -315,27 +332,32 @@ mem.number = self.SPECIAL_MEMORIES[number] mem.extd_number = number
- if mem.number in range(self.FIRST_VFOA_INDEX, self.LAST_VFOA_INDEX -1, -1): + if mem.number in range(self.FIRST_VFOA_INDEX, + self.LAST_VFOA_INDEX - 1, + -1): _mem = self._memobj.vfoa[-self.LAST_VFOA_INDEX + mem.number] - immutable = ["number", "skip", "rtone", "ctone", "extd_number", "name", - "dtcs_polarity", "power", "comment"] - elif mem.number in range(self.FIRST_VFOB_INDEX, self.LAST_VFOB_INDEX -1, -1): + immutable = ["number", "skip", "rtone", "ctone", "extd_number", + "name", "dtcs_polarity", "power", "comment"] + elif mem.number in range(self.FIRST_VFOB_INDEX, + self.LAST_VFOB_INDEX - 1, + -1): _mem = self._memobj.vfob[-self.LAST_VFOB_INDEX + mem.number] - immutable = ["number", "skip", "rtone", "ctone", "extd_number", "name", - "dtcs_polarity", "power", "comment"] + immutable = ["number", "skip", "rtone", "ctone", "extd_number", + "name", "dtcs_polarity", "power", "comment"] elif mem.number in range(-2, -6, -1): _mem = self._memobj.home[5 + mem.number] immutable = ["number", "skip", "rtone", "ctone", "extd_number", "dtcs_polarity", "power", "comment"] elif mem.number == -1: _mem = self._memobj.qmb - immutable = ["number", "skip", "rtone", "ctone", "extd_number", "name", - "dtcs_polarity", "power", "comment"] - else: - raise Exception("Sorry, special memory index %i unknown you hit a bug!!" % mem.number) + immutable = ["number", "skip", "rtone", "ctone", "extd_number", + "name", "dtcs_polarity", "power", "comment"] + else: + raise Exception("Sorry, special memory index %i " % mem.number + + "unknown you hit a bug!!")
mem = self._get_memory(mem, _mem) - mem.immutable = immutable + mem.immutable = immutable
return mem
@@ -352,16 +374,21 @@ "is not supported on this channel")
# TODO add frequency range check for vfo and home memories - if mem.number in range(self.FIRST_VFOA_INDEX, self.LAST_VFOA_INDEX -1, -1): + if mem.number in range(self.FIRST_VFOA_INDEX, + self.LAST_VFOA_INDEX -1, + -1): _mem = self._memobj.vfoa[-self.LAST_VFOA_INDEX + mem.number] - elif mem.number in range(self.FIRST_VFOB_INDEX, self.LAST_VFOB_INDEX -1, -1): + elif mem.number in range(self.FIRST_VFOB_INDEX, + self.LAST_VFOB_INDEX -1, + -1): _mem = self._memobj.vfob[-self.LAST_VFOB_INDEX + mem.number] elif mem.number in range(-2, -6, -1): _mem = self._memobj.home[5 + mem.number] elif mem.number == -1: _mem = self._memobj.qmb - else: - raise Exception("Sorry, special memory index %i unknown you hit a bug!!" % mem.number) + else: + raise Exception("Sorry, special memory index %i " % mem.number + + "unknown you hit a bug!!")
self._set_memory(mem, _mem)
@@ -382,8 +409,10 @@
def _set_normal(self, mem): _mem = self._memobj.memory[mem.number-1] - wasused = (self._memobj.visible[(mem.number-1)/8] >> (mem.number-1)%8) & 0x01 - wasvalid = (self._memobj.filled[(mem.number-1)/8] >> (mem.number-1)%8) & 0x01 + wasused = (self._memobj.visible[(mem.number - 1) / 8] >> \ + (mem.number - 1) % 8) & 0x01 + wasvalid = (self._memobj.filled[(mem.number - 1) / 8] >> \ + (mem.number - 1) % 8) & 0x01
if mem.empty: if mem.number == 1: @@ -391,18 +420,20 @@ # if you ulpoad an empty image you can brick your radio raise Exception("Sorry, can't delete first memory") if wasvalid and not wasused: - self._memobj.filled[(mem.number-1)/8] &= ~ (1 << (mem.number-1)%8) - self._memobj.visible[(mem.number-1)/8] &= ~ (1 << (mem.number-1)%8) + self._memobj.filled[(mem.number-1) / 8] &= \ + ~(1 << (mem.number - 1) % 8) + self._memobj.visible[(mem.number-1) / 8] &= \ + ~(1 << (mem.number - 1) % 8) return
- self._memobj.visible[(mem.number-1)/8] |= 1 << (mem.number-1)%8 - self._memobj.filled[(mem.number-1)/8] |= 1 << (mem.number-1)%8 + self._memobj.visible[(mem.number - 1) / 8] |= 1 << (mem.number - 1) % 8 + self._memobj.filled[(mem.number - 1) / 8] |= 1 << (mem.number - 1) % 8 self._set_memory(mem, _mem)
def _get_memory(self, mem, _mem): mem.freq = int(_mem.freq) * 10 mem.offset = int(_mem.offset) * 10 - self.get_duplex(mem, _mem) + self._get_duplex(mem, _mem) mem.mode = self.MODES[_mem.mode] if mem.mode == "FM": if _mem.is_fm_narrow == 1: @@ -420,11 +451,11 @@ except IndexError: pass mem.skip = _mem.skip and "S" or "" - self.get_tmode(mem, _mem) + self._get_tmode(mem, _mem) mem.rtone = mem.ctone = chirp_common.TONES[_mem.tone] mem.dtcs = chirp_common.DTCS_CODES[_mem.dcs]
- if _mem.tag_on_off == 1: + if _mem.tag_on_off == 1: for i in _mem.name: if i == "\xFF": break @@ -442,31 +473,34 @@ else: _mem.tag_on_off = 0 _mem.tag_default = 0 # never use default label "CH-nnn" - self.set_duplex(mem, _mem) + self._set_duplex(mem, _mem) if mem.mode[0] == "N": # is it narrow? _mem.mode = self.MODES.index(mem.mode[1:]) - _mem.is_fm_narrow = _mem.is_cwdig_narrow = 1 # here I suppose it's safe to set both + # here I suppose it's safe to set both + _mem.is_fm_narrow = _mem.is_cwdig_narrow = 1 else: _mem.mode = self.MODES.index(mem.mode) - _mem.is_fm_narrow = _mem.is_cwdig_narrow = 0 # here I suppose it's safe to set both - i = 0 # This search can probably be written better but - for lo, hi in self.VALID_BANDS: # I just don't know python enought + # here I suppose it's safe to set both + _mem.is_fm_narrow = _mem.is_cwdig_narrow = 0 + i = 0 + for lo, hi in self.VALID_BANDS: if mem.freq > lo and mem.freq < hi: break - i+=1 + i += 1 _mem.freq_range = i - if mem.duplex == "split": # all this should be safe also when not in split but ... + # all this should be safe also when not in split but ... + if mem.duplex == "split": _mem.tx_mode = _mem.mode - i = 0 # This search can probably be written better but - for lo, hi in self.VALID_BANDS: # I just don't know python enought + i = 0 + for lo, hi in self.VALID_BANDS: if mem.offset >= lo and mem.offset < hi: break - i+=1 + i += 1 _mem.tx_freq_range = i _mem.skip = mem.skip == "S" _mem.ipo = 0 # not supported in chirp _mem.att = 0 # not supported in chirp - self.set_tmode(mem, _mem) + self._set_tmode(mem, _mem) try: _mem.ssb_step = self.STEPSSSB.index(mem.tuning_step) except ValueError: @@ -490,10 +524,11 @@ def validate_memory(self, mem): msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem)
- lo, hi = self.VALID_BANDS[2] # this is fm broadcasting - if mem.freq >= lo and mem.freq <= hi: + lo, hi = self.VALID_BANDS[2] # this is fm broadcasting + if mem.freq >= lo and mem.freq <= hi: if mem.mode != "FM": - msgs.append(chirp_common.ValidationError("Only FM is supported in this band")) + msgs.append(chirp_common.ValidationError(\ + "Only FM is supported in this band")) # TODO check that step is valid in current mode return msgs
@@ -503,6 +538,7 @@
@directory.register class FT817NDRadio(FT817Radio): + """Yaesu FT-817ND""" MODEL = "FT-817ND"
_model = "" @@ -512,8 +548,9 @@
@directory.register class FT817ND_US_Radio(FT817Radio): - # seems that radios configured for 5MHz operations send one paket more than others - # so we have to distinguish sub models + """Yaesu FT-817ND (US version)""" + # seems that radios configured for 5MHz operations send one paket + # more than others so we have to distinguish sub models MODEL = "FT-817ND (US)"
_model = "" @@ -533,14 +570,16 @@ SPECIAL_MEMORIES = dict(FT817Radio.SPECIAL_MEMORIES) SPECIAL_MEMORIES.update(SPECIAL_60M)
- SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), SPECIAL_MEMORIES.keys())) + SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), + SPECIAL_MEMORIES.keys()))
def _get_special_60M(self, number): mem = chirp_common.Memory() mem.number = self.SPECIAL_60M[number] mem.extd_number = number
- _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + mem.number] + _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + + mem.number]
mem = self._get_memory(mem, _mem)
@@ -564,16 +603,19 @@ "is not supported on M-60x channels")
if mem.mode not in ["USB", "LSB", "CW", "CWR", "NCW", "NCWR", "DIG"]: - raise errors.RadioError(_("Mode {mode} is not valid " - "in 60m channels").format(mode=mem.mode)) - _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + mem.number] + raise errors.RadioError("Mode {mode} is not valid " + "in 60m channels".format(mode=mem.mode)) + _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + + mem.number] self._set_memory(mem, _mem)
def get_memory(self, number): if number in self.SPECIAL_60M.keys(): return self._get_special_60M(number) - elif number < 0 and self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_60M.keys(): - # I can't stop delete operation from loosing extd_number but I know how to get it back + elif number < 0 and \ + self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_60M.keys(): + # I can't stop delete operation from loosing extd_number but + # I know how to get it back return self._get_special_60M(self.SPECIAL_MEMORIES_REV[number]) else: return FT817Radio.get_memory(self, number) diff -r db82df47f205 -r f805c420592f chirp/ft857.py --- a/chirp/ft857.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ft857.py Fri Apr 27 14:35:41 2012 -0700 @@ -18,7 +18,7 @@ from chirp import bitwise import os
-mem_format = """ +MEM_FORMAT = """ struct mem_struct{ u8 tag_on_off:1, tag_default:1, @@ -84,6 +84,7 @@
@directory.register class FT857Radio(ft817.FT817Radio): + """Yaesu FT-857/897""" MODEL = "FT-857/897" _model = ""
@@ -121,10 +122,15 @@ _memsize = 7341 # block 9 (140 Bytes long) is to be repeted 40 times # should be 42 times but this way I can use original 817 functions - _block_lengths = [ 2, 82, 252, 196, 252, 196, 212, 55, 140, 140, 140, 38, 176] - VALID_BANDS = [(100000,33000000), (33000000,56000000), (76000000,108000000), (108000000,137000000), (137000000,164000000), (420000000,470000000)] # warning ranges has to be in this exact order + _block_lengths = [ 2, 82, 252, 196, 252, 196, 212, 55, 140, 140, 140, + 38, 176] + # warning ranges has to be in this exact order + VALID_BANDS = [(100000, 33000000), (33000000, 56000000), + (76000000, 108000000), (108000000, 137000000), + (137000000, 164000000), (420000000, 470000000)]
- SPECIAL_MEMORIES = { # WARNING Index are hard wired in memory management code !!! + # WARNING Index are hard wired in memory management code !!! + SPECIAL_MEMORIES = { "VFOa-1.8M" : -37, "VFOa-3.5M" : -36, "VFOa-5M" : -35, @@ -168,7 +174,8 @@ FIRST_VFOA_INDEX = -22 LAST_VFOA_INDEX = -37
- SPECIAL_PMS = { # WARNING Index are hard wired in memory management code !!! + # WARNING Index are hard wired in memory management code !!! + SPECIAL_PMS = { "PMS-1L" : -47, "PMS-1U" : -46, "PMS-2L" : -45, @@ -184,7 +191,8 @@ SPECIAL_MEMORIES = dict(SPECIAL_MEMORIES) SPECIAL_MEMORIES.update(SPECIAL_PMS)
- SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), SPECIAL_MEMORIES.keys())) + SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), + SPECIAL_MEMORIES.keys()))
def get_features(self): rf = ft817.FT817Radio.get_features(self) @@ -193,22 +201,25 @@ rf.valid_cross_modes = self.CROSS_MODES_REV.keys() return rf
- def get_duplex(self, mem, _mem): + def _get_duplex(self, mem, _mem): # radio set is_duplex only for + and - but not for split - # at the same time it does not complain if we set it same way 817 does (so no set_duplex here) + # at the same time it does not complain if we set it same way 817 does + # (so no set_duplex here) mem.duplex = self.DUPLEX[_mem.duplex]
- def get_tmode(self, mem, _mem): - # I do not use is_split_tone here because the radio sometimes set it also for standard tone mode + def _get_tmode(self, mem, _mem): + # I do not use is_split_tone here because the radio sometimes set it + # also for standard tone mode try: mem.tmode = self.TMODES[int(_mem.tmode)] except KeyError: mem.tmode = "Cross" mem.cross_mode = self.CROSS_MODES[int(_mem.tmode)]
- def set_tmode(self, mem, _mem): - _mem.unknown_flag = 0 # have to put this bit to 0 otherwise we get strange display in tone frequency (menu 83) - # see bug #88 + def _set_tmode(self, mem, _mem): + # have to put this bit to 0 otherwise we get strange display in tone + # frequency (menu 83). See bug #88 + _mem.unknown_flag = 0 if mem.tmode != "Cross": _mem.is_split_tone = 0 _mem.tmode = self.TMODES_REV[mem.tmode] @@ -217,18 +228,24 @@ _mem.is_split_tone = 1
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def _get_special_pms(self, number): mem = chirp_common.Memory() mem.number = self.SPECIAL_PMS[number] mem.extd_number = number
- bitindex = 47 + mem.number + bitindex = 47 + mem.number used = (self._memobj.pmsvisible >> bitindex) & 0x01 valid = (self._memobj.pmsfilled >> bitindex) & 0x01 if os.getenv("CHIRP_DEBUG"): - print "mem.number %i bitindex %i pmsvisible %i pmsfilled %i used %i filled %i" % (mem.number, bitindex, self._memobj.pmsvisible, self._memobj.pmsfilled, used, valid) + print "mem.number %i bitindex %i pmsvisible %i" % \ + (mem.number, + bitindex, + self._memobj.pmsvisible), + print "pmsfilled %i used %i filled %i" % (self._memobj.pmsfilled, + used, + valid) if not used: mem.empty = True if not valid: @@ -249,7 +266,7 @@ def _set_special_pms(self, mem): cur_mem = self._get_special_pms(self.SPECIAL_MEMORIES_REV[mem.number])
- bitindex = 47 + mem.number + bitindex = 47 + mem.number wasused = (self._memobj.pmsvisible >> bitindex) & 0x01 wasvalid = (self._memobj.pmsfilled >> bitindex) & 0x01
@@ -273,8 +290,10 @@ def get_memory(self, number): if number in self.SPECIAL_PMS.keys(): return self._get_special_pms(number) - elif number < 0 and self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_PMS.keys(): - # I can't stop delete operation from loosing extd_number but I know how to get it back + elif number < 0 and \ + self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_PMS.keys(): + # I can't stop delete operation from loosing extd_number but + # I know how to get it back return self._get_special_pms(self.SPECIAL_MEMORIES_REV[number]) else: return ft817.FT817Radio.get_memory(self, number) @@ -287,16 +306,17 @@
@directory.register class FT857_US_Radio(FT857Radio): - # seems that radios configured for 5MHz operations send one paket more than others - # so we have to distinguish sub models + """Yaesu FT857/897 (US version)""" + # seems that radios configured for 5MHz operations send one paket more + # than others so we have to distinguish sub models MODEL = "FT-857/897 (US)"
_model = "" _memsize = 7481 # block 9 (140 Bytes long) is to be repeted 40 times # should be 42 times but this way I can use original 817 functions - _block_lengths = [ 2, 82, 252, 196, 252, 196, 212, 55, 140, 140, 140, 38, 176, 140] - + _block_lengths = [ 2, 82, 252, 196, 252, 196, 212, 55, 140, 140, 140, 38, + 176, 140]
SPECIAL_60M = { "M-601" : -52, @@ -310,7 +330,8 @@ SPECIAL_MEMORIES = dict(FT857Radio.SPECIAL_MEMORIES) SPECIAL_MEMORIES.update(SPECIAL_60M)
- SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), SPECIAL_MEMORIES.keys())) + SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), + SPECIAL_MEMORIES.keys()))
# this is identical to the one in FT817ND_US_Radio but we inherit from 857 def _get_special_60M(self, number): @@ -318,7 +339,8 @@ mem.number = self.SPECIAL_60M[number] mem.extd_number = number
- _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + mem.number] + _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + + mem.number]
mem = self._get_memory(mem, _mem)
@@ -343,16 +365,19 @@ "is not supported on M-60x channels")
if mem.mode not in ["USB", "LSB", "CW", "CWR", "NCW", "NCWR", "DIG"]: - raise errors.RadioError(_("Mode {mode} is not valid " - "in 60m channels").format(mode=mem.mode)) - _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + mem.number] + raise errors.RadioError("Mode {mode} is not valid " + "in 60m channels".format(mode=mem.mode)) + _mem = self._memobj.sixtymeterchannels[-self.LAST_SPECIAL60M_INDEX + + mem.number] self._set_memory(mem, _mem)
def get_memory(self, number): if number in self.SPECIAL_60M.keys(): return self._get_special_60M(number) - elif number < 0 and self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_60M.keys(): - # I can't stop delete operation from loosing extd_number but I know how to get it back + elif number < 0 and \ + self.SPECIAL_MEMORIES_REV[number] in self.SPECIAL_60M.keys(): + # I can't stop delete operation from loosing extd_number but + # I know how to get it back return self._get_special_60M(self.SPECIAL_MEMORIES_REV[number]) else: return FT857Radio.get_memory(self, number) diff -r db82df47f205 -r f805c420592f chirp/generic_csv.py --- a/chirp/generic_csv.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/generic_csv.py Fri Apr 27 14:35:41 2012 -0700 @@ -19,10 +19,29 @@ from chirp import chirp_common, errors, directory
class OmittedHeaderError(Exception): + """Internal exception to signal that a column has been omitted""" pass
+def get_datum_by_header(headers, data, header): + """Return the column corresponding to @headers[@header] from @data""" + if header not in headers: + raise OmittedHeaderError("Header %s not provided" % header) + + try: + return data[headers.index(header)] + except IndexError: + raise OmittedHeaderError("Header %s not provided on this line" % \ + header) + +def write_memory(writer, mem): + """Write @mem using @writer if not empty""" + if mem.empty: + return + writer.writerow(mem.to_csv()) + @directory.register class CSVRadio(chirp_common.FileBackedRadio, chirp_common.IcomDstarSupport): + """A driver for Generic CSV files""" VENDOR = "Generic" MODEL = "CSV" FILE_EXTENSION = "csv" @@ -51,13 +70,14 @@ self.errors = [] self.memories = [] for i in range(0, 1000): - m = chirp_common.Memory() - m.number = i - m.empty = True - self.memories.append(m) + mem = chirp_common.Memory() + mem.number = i + mem.empty = True + self.memories.append(mem)
def __init__(self, pipe): chirp_common.FileBackedRadio.__init__(self, None) + self.memories = []
self._filename = pipe if self._filename and os.path.exists(self._filename): @@ -86,34 +106,17 @@
return rf
- def _parse_quoted_line(self, line): - line = line.replace("\n", "") - line = line.replace("\r", "") - line = line.replace('"', "") - - return line.split(",") - - def _get_datum_by_header(self, headers, data, header): - if header not in headers: - raise OmittedHeaderError("Header %s not provided" % header) - - try: - return data[headers.index(header)] - except IndexError: - raise OmittedHeaderError("Header %s not provided on this line" %\ - header) - def _parse_csv_data_line(self, headers, line): mem = chirp_common.Memory() try: - if self._get_datum_by_header(headers, line, "Mode") == "DV": + if get_datum_by_header(headers, line, "Mode") == "DV": mem = chirp_common.DVMemory() except OmittedHeaderError: pass
for header, (typ, attr) in self.ATTR_MAP.items(): try: - val = self._get_datum_by_header(headers, line, header) + val = get_datum_by_header(headers, line, header) if not val and typ == int: val = None else: @@ -174,12 +177,6 @@ print self.errors raise errors.InvalidDataError("No channels found")
- def save_memory(self, writer, mem): - if mem.empty: - return - - writer.writerow(mem.to_csv()) - def save(self, filename=None): if filename is None and self._filename is None: raise errors.RadioError("Need a location to save to") @@ -192,7 +189,7 @@ writer.writerow(chirp_common.Memory.CSV_FORMAT)
for mem in self.memories: - self.save_memory(writer, mem) + write_memory(writer, mem)
f.close()
@@ -220,20 +217,20 @@ delta += 1
for i in range(len(self.memories), len(self.memories) + delta + 1): - m = chirp_common.Memory() - m.empty = True - m.number = i - self.memories.append(m) + mem = chirp_common.Memory() + mem.empty = True + mem.number = i + self.memories.append(mem)
def set_memory(self, newmem): self._grow(newmem.number) self.memories[newmem.number] = newmem
def erase_memory(self, number): - m = chirp_common.Memory() - m.number = number - m.empty = True - self.memories[number] = m + mem = chirp_common.Memory() + mem.number = number + mem.empty = True + self.memories[number] = mem
def get_raw_memory(self, number): return ",".join(chirp_common.Memory.CSV_FORMAT) + \ @@ -241,5 +238,6 @@ ",".join(self.memories[number].to_csv())
@classmethod - def match_model(cls, filedata, filename): + def match_model(cls, _filedata, filename): + """Match files ending in .CSV""" return filename.lower().endswith("." + cls.FILE_EXTENSION) diff -r db82df47f205 -r f805c420592f chirp/generic_tpe.py --- a/chirp/generic_tpe.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/generic_tpe.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,12 +13,10 @@ # 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 csv - +import UserDict from chirp import chirp_common, directory, generic_csv
-class TpeMap: +class TpeMap(UserDict.UserDict): """Pretend we're a dict""" def items(self): return [ @@ -27,13 +25,20 @@ ("Call Sign" , (str, "name")), ("Output Frequency", (chirp_common.parse_freq, "freq")), ("Input Frequency" , (str, "duplex")), - ("CTCSS Tones" , (lambda v: "Tone" if float(v) in chirp_common.TONES else "", "tmode")), - ("CTCSS Tones" , (lambda v: float(v) if float(v) in chirp_common.TONES else 88.5, "rtone")), - ("CTCSS Tones" , (lambda v: float(v) if float(v) in chirp_common.TONES else 88.5, "ctone")), + ("CTCSS Tones" , (lambda v: "Tone" + if float(v) in chirp_common.TONES + else "", "tmode")), + ("CTCSS Tones" , (lambda v: float(v) + if float(v) in chirp_common.TONES + else 88.5, "rtone")), + ("CTCSS Tones" , (lambda v: float(v) + if float(v) in chirp_common.TONES + else 88.5, "ctone")), ]
@directory.register class TpeRadio(generic_csv.CSVRadio): + """Generic ARRL Travel Plus""" VENDOR = "ARRL" MODEL = "Travel Plus" FILE_EXTENSION = "tpe" diff -r db82df47f205 -r f805c420592f chirp/generic_xml.py --- a/chirp/generic_xml.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/generic_xml.py Fri Apr 27 14:35:41 2012 -0700 @@ -19,6 +19,7 @@ from chirp import chirp_common, errors, xml_ll, platform, directory
def validate_doc(doc): + """Validate the document""" basepath = platform.get_platform().executable_path() path = os.path.abspath(os.path.join(basepath, "chirp.xsd")) if not os.path.exists(path): @@ -37,16 +38,16 @@ errs = [] warnings = []
- def err(msg, arg=None): + def _err(msg, *_args): errs.append("ERROR: %s" % msg)
- def wrn(msg, arg=None): + def _wrn(msg, *_args): print "WARNING: %s" % msg warnings.append("WARNING: %s" % msg)
- validCtx = schema.schemaNewValidCtxt() - validCtx.setValidityErrorHandler(err, wrn) - err = validCtx.schemaValidateDoc(doc) + validctx = schema.schemaNewValidCtxt() + validctx.setValidityErrorHandler(_err, _wrn) + err = validctx.schemaValidateDoc(doc) print os.linesep.join(warnings) if err: print "---DOC---\n%s\n------" % doc.serialize(format=1) @@ -54,6 +55,7 @@ raise errors.RadioError("Schema error")
def default_banks(): + """Return an empty set of banks""" banks = []
for i in range(0, 26): @@ -63,6 +65,7 @@
@directory.register class XMLRadio(chirp_common.FileBackedRadio, chirp_common.IcomDstarSupport): + """Generic XML driver""" VENDOR = "Generic" MODEL = "XML" FILE_EXTENSION = "chirp" @@ -135,18 +138,6 @@ xml_ll.del_memory(self.doc, number)
@classmethod - def match_model(cls, filedata, filename): + def match_model(cls, _filedata, filename): + """Match this driver if the extension matches""" return filename.lower().endswith("." + cls.FILE_EXTENSION) - -if __name__ == "__main__": - r = XMLRadio("testmem.chirp") - - print r.get_memory(3) - - m = chirp_common.Memory() - m.name = "TestMem2" - m.freq = 123.456 - m.number = 10 - - #r.set_memory(m) - #r.erase_memory(10) diff -r db82df47f205 -r f805c420592f chirp/ic2100.py --- a/chirp/ic2100.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic2100.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,7 +16,7 @@ from chirp import chirp_common, icf, util, directory from chirp import bitwise, memmap
-mem_format = """ +MEM_FORMAT = """ struct { bbcd freq[2]; u8 freq_10khz:4, @@ -82,8 +82,73 @@ DUPLEX = ["", "", "+", "-"] STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0]
+def _get_special(): + special = { "C": 506 } + for i in range(0, 3): + ida = "%iA" % (i + 1) + idb = "%iB" % (i + 1) + num = 500 + (i * 2) + special[ida] = num + special[idb] = num + 1 + + return special + +def _get_freq(mem): + freq = (int(mem.freq) * 100000) + \ + (mem.freq_10khz * 10000) + \ + (mem.freq_1khz * 1000) + + if mem.is_12_5: + if chirp_common.is_12_5(freq): + pass + elif mem.freq_1khz == 2: + freq += 500 + elif mem.freq_1khz == 5: + freq += 2500 + elif mem.freq_1khz == 7: + freq += 500 + else: + raise Exception("Unable to resolve 12.5kHz: %i" % freq) + + return freq + +def _set_freq(mem, freq): + mem.freq = freq / 100000 + mem.freq_10khz = (freq / 10000) % 10 + khz = (freq / 1000) % 10 + mem.freq_1khz = khz + mem.is_12_5 = chirp_common.is_12_5(freq) + +def _get_offset(mem): + raw = memmap.MemoryMap(mem.get_raw()) + if ord(raw[5]) & 0x0A: + raw[5] = ord(raw[5]) & 0xF0 + mem.set_raw(raw.get_packed()) + offset = int(mem.offset) * 1000 + 5000 + raw[5] = ord(raw[5]) | 0x0A + mem.set_raw(raw.get_packed()) + return offset + else: + return int(mem.offset) * 1000 + +def _set_offset(mem, offset): + if (offset % 10) == 5000: + extra = 0x0A + offset -= 5000 + else: + extra = 0x00 + + mem.offset = offset / 1000 + raw = memmap.MemoryMap(mem.get_raw()) + raw[5] = ord(raw[5]) | extra + mem.set_raw(raw.get_packed()) + +def _wipe_memory(mem, char): + mem.set_raw(char * (mem.size() / 8)) + @directory.register class IC2100Radio(icf.IcomCloneModeRadio): + """Icom IC-2100""" VENDOR = "Icom" MODEL = "IC-2100H"
@@ -110,81 +175,20 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) - - def _get_special(self): - special = { "C": 506 } - for i in range(0, 3): - idA = "%iA" % (i+1) - idB = "%iB" % (i+1) - num = 500 + (i * 2) - special[idA] = num - special[idB] = num + 1 - - return special + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_special_locations(self): - return sorted(self._get_special().keys()) - - def __get_freq(self, mem): - freq = (int(mem.freq) * 100000) + \ - (mem.freq_10khz * 10000) + \ - (mem.freq_1khz * 1000) - - if mem.is_12_5: - if chirp_common.is_12_5(freq): - pass - elif mem.freq_1khz == 2: - freq += 500 - elif mem.freq_1khz == 5: - freq += 2500 - elif mem.freq_1khz == 7: - freq += 500 - else: - raise Exception("Unable to resolve 12.5kHz: %i" % freq) - - return freq - - def __set_freq(self, mem, freq): - mem.freq = freq / 100000 - mem.freq_10khz = (freq / 10000) % 10 - khz = (freq / 1000) % 10 - mem.freq_1khz = khz - mem.is_12_5 = chirp_common.is_12_5(freq) - - def __get_offset(self, mem): - raw = memmap.MemoryMap(mem.get_raw()) - if ord(raw[5]) & 0x0A: - raw[5] = ord(raw[5]) & 0xF0 - mem.set_raw(raw.get_packed()) - offset = int(mem.offset) * 1000 + 5000 - raw[5] = ord(raw[5]) | 0x0A - mem.set_raw(raw.get_packed()) - return offset - else: - return int(mem.offset) * 1000 - - def __set_offset(self, mem, offset): - if (offset % 10) == 5000: - extra = 0x0A - offset -= 5000 - else: - extra = 0x00 - - mem.offset = offset / 1000 - raw = memmap.MemoryMap(mem.get_raw()) - raw[5] = ord(raw[5]) | extra - mem.set_raw(raw.get_packed()) + return sorted(_get_special().keys())
def get_memory(self, number): mem = chirp_common.Memory()
if isinstance(number, str): if number == "C": - number = self._get_special()[number] + number = _get_special()[number] _mem = self._memobj.call[0] else: - number = self._get_special()[number] + number = _get_special()[number] _mem = self._memobj.special[number - 500] empty = False else: @@ -202,15 +206,15 @@ if number <= 100: mem.skip = isskip and "S" or "" else: - mem.extd_number = util.get_dict_rev(self._get_special(), number) + mem.extd_number = util.get_dict_rev(_get_special(), number) mem.immutable = ["number", "skip", "extd_number"]
if empty: mem.empty = True return mem
- mem.freq = self.__get_freq(_mem) - mem.offset = self.__get_offset(_mem) + mem.freq = _get_freq(_mem) + mem.offset = _get_offset(_mem) mem.rtone = chirp_common.TONES[_mem.rtone] mem.ctone = chirp_common.TONES[_mem.ctone] mem.tmode = TMODES[_mem.tmode] @@ -218,14 +222,11 @@
return mem
- def _wipe_memory(self, mem, char): - mem.set_raw(char * (mem.size() / 8)) - def set_memory(self, mem): if mem.number == "C": _mem = self._memobj.call[0] elif isinstance(mem.number, str): - _mem = self._memobj.special[self._get_special[number] - 500] + _mem = self._memobj.special[_get_special[mem.number] - 500] else: number = mem.number - 1 _mem = self._memobj.memory[number] @@ -242,8 +243,8 @@ _skp &= ~mask _mem.name = mem.name.ljust(6)
- self.__set_freq(_mem, mem.freq) - self.__set_offset(_mem, mem.offset) + _set_freq(_mem, mem.freq) + _set_offset(_mem, mem.offset) _mem.rtone = chirp_common.TONES.index(mem.rtone) _mem.ctone = chirp_common.TONES.index(mem.ctone) _mem.tmode = TMODES.index(mem.tmode) diff -r db82df47f205 -r f805c420592f chirp/ic2200.py --- a/chirp/ic2200.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic2200.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,7 +16,7 @@ from chirp import chirp_common, icf, util, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ struct { ul16 freq; ul16 offset; @@ -83,8 +83,23 @@ chirp_common.PowerLevel("MidLow", watts=10), chirp_common.PowerLevel("Low", watts=5)]
+def _get_special(): + special = { "C" : 206 } + for i in range(0, 3): + ida = "%iA" % (i+1) + idb = "%iB" % (i+1) + num = 200 + i * 2 + special[ida] = num + special[idb] = num + 1 + + return special + +def _wipe_memory(mem, char): + mem.set_raw(char * (mem.size() / 8)) + @directory.register class IC2200Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom IC-2200""" VENDOR = "Icom" MODEL = "IC-2200H"
@@ -145,25 +160,14 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) - - def _get_special(self): - special = { "C" : 206 } - for i in range(0, 3): - idA = "%iA" % (i+1) - idB = "%iB" % (i+1) - num = 200 + i * 2 - special[idA] = num - special[idB] = num + 1 - - return special + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_special_locations(self): - return sorted(self._get_special().keys()) + return sorted(_get_special().keys())
def get_memory(self, number): if isinstance(number, str): - number = self._get_special()[number] + number = _get_special()[number]
_mem = self._memobj.memory[number] _flag = self._memobj.flags[number] @@ -183,7 +187,7 @@ if number < 200: mem.skip = _flag.skip and "S" or "" else: - mem.extd_number = util.get_dict_rev(self._get_special(), number) + mem.extd_number = util.get_dict_rev(_get_special(), number) mem.immutable = ["number", "skip", "bank", "bank_index", "extd_number"]
@@ -214,12 +218,9 @@
return [m for m in self._memories if m.number >= lo and m.number <= hi]
- def _wipe_memory(self, mem, char): - mem.set_raw(char * (mem.size() / 8)) - def set_memory(self, mem): if isinstance(mem.number, str): - number = self._get_special()[mem.number] + number = _get_special()[mem.number] else: number = mem.number
@@ -230,11 +231,11 @@
_flag.empty = mem.empty if mem.empty: - self._wipe_memory(_mem, "\xFF") + _wipe_memory(_mem, "\xFF") return
if was_empty: - self._wipe_memory(_mem, "\x00") + _wipe_memory(_mem, "\x00")
_mem.unknown8 = 0 _mem.is_625 = chirp_common.is_fractional_step(mem.freq) diff -r db82df47f205 -r f805c420592f chirp/ic2720.py --- a/chirp/ic2720.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic2720.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, util, directory +from chirp import chirp_common, icf, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ struct { u32 freq; u32 offset; @@ -69,6 +69,7 @@
@directory.register class IC2720Radio(icf.IcomCloneModeRadio): + """Icom IC-2720""" VENDOR = "Icom" MODEL = "IC-2720H"
@@ -113,7 +114,7 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number]) diff -r db82df47f205 -r f805c420592f chirp/ic2820.py --- a/chirp/ic2820.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic2820.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, errors, util, directory -from chirp import bitwise; +from chirp import chirp_common, icf, util, directory +from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ struct { u32 freq; u32 offset; @@ -84,7 +84,8 @@
MEM_LOC_SIZE = 48
-class IC2820Bank(icf.IcomBank): +class IC2820Bank(icf.IcomNamedBank): + """An IC2820 bank""" def get_name(self): _banks = self._model._radio._memobj.bank_names return str(_banks[self.index].name).rstrip() @@ -93,8 +94,30 @@ _banks = self._model._radio._memobj.bank_names _banks[self.index].name = str(name).ljust(8)[:8]
+def _get_special(): + special = {"C0" : 500 + 20, + "C1" : 500 + 21} + + for i in range(0, 10): + ida = "%iA" % i + idb = "%iB" % i + special[ida] = 500 + i * 2 + special[idb] = 500 + i * 2 + 1 + + return special + +def _resolve_memory_number(number): + if isinstance(number, str): + return _get_special()[number] + else: + return number + +def _wipe_memory(mem, char): + mem.set_raw(char * (mem.size() / 8)) + @directory.register class IC2820Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom IC-2820""" VENDOR = "Icom" MODEL = "IC-2820H"
@@ -156,32 +179,14 @@ rf.valid_name_length = 8 return rf
- def _get_special(self): - special = {"C0" : 500 + 20, - "C1" : 500 + 21} - - for i in range(0, 10): - idA = "%iA" % i - idB = "%iB" % i - special[idA] = 500 + i * 2 - special[idB] = 500 + i * 2 + 1 - - return special - def get_special_locations(self): - return sorted(self._get_special().keys()) + return sorted(_get_special().keys())
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) - - def _resolve_memory_number(self, number): - if isinstance(number, str): - return self._get_special()[number] - else: - return number + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_memory(self, number): - number = self._resolve_memory_number(number) + number = _resolve_memory_number(number)
bitpos = (1 << (number % 8)) bytepos = number / 8 @@ -208,7 +213,7 @@ elif _pskip & bitpos: mem.skip = "P" else: - mem.extd_number = util.get_dict_rev(self._get_special(), number) + mem.extd_number = util.get_dict_rev(_get_special(), number) mem.immutable = ["number", "skip", "bank", "bank_index", "extd_number"]
@@ -233,9 +238,6 @@
return mem
- def _wipe_memory(self, mem, char): - mem.set_raw(char * (mem.size() / 8)) - def set_memory(self, mem): bitpos = (1 << (mem.number % 8)) bytepos = mem.number / 8 @@ -259,13 +261,13 @@
if mem.empty: _used |= bitpos - self._wipe_memory(_mem, "\xFF") + _wipe_memory(_mem, "\xFF") self._set_bank(mem.number, None) return
_used &= ~bitpos if was_empty: - self._wipe_memory(_mem, "\x00") + _wipe_memory(_mem, "\x00")
_mem.freq = mem.freq _mem.offset = mem.offset diff -r db82df47f205 -r f805c420592f chirp/ic9x.py --- a/chirp/ic9x.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic9x.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,13 +16,11 @@ import time import threading
-from chirp import chirp_common, errors, memmap, ic9x_ll, util, icf, directory +from chirp import chirp_common, errors, ic9x_ll, icf, util, directory from chirp import bitwise
-IC9xA_SPECIAL = {} -IC9xA_SPECIAL_REV = {} -IC9xB_SPECIAL = {} -IC9xB_SPECIAL_REV = {} +IC9XA_SPECIAL = {} +IC9XB_SPECIAL = {}
for i in range(0, 25): idA = "%iA" % i @@ -30,30 +28,18 @@ Anum = 800 + i * 2 Bnum = 400 + i * 2
- IC9xA_SPECIAL[idA] = Anum - IC9xA_SPECIAL[idB] = Bnum - IC9xA_SPECIAL_REV[Anum] = idA - IC9xA_SPECIAL_REV[Bnum] = idB + IC9XA_SPECIAL[idA] = Anum + IC9XA_SPECIAL[idB] = Bnum
- IC9xB_SPECIAL[idA] = Bnum - IC9xB_SPECIAL[idB] = Bnum + 1 - IC9xB_SPECIAL_REV[Bnum] = idA - IC9xB_SPECIAL_REV[Bnum+1] = idB + IC9XB_SPECIAL[idA] = Bnum + IC9XB_SPECIAL[idB] = Bnum + 1
-IC9xA_SPECIAL["C0"] = IC9xB_SPECIAL["C0"] = -1 -IC9xA_SPECIAL["C1"] = IC9xB_SPECIAL["C1"] = -2 +IC9XA_SPECIAL["C0"] = IC9XB_SPECIAL["C0"] = -1 +IC9XA_SPECIAL["C1"] = IC9XB_SPECIAL["C1"] = -2
-IC9xA_SPECIAL_REV[-1] = IC9xB_SPECIAL_REV[-1] = "C0" -IC9xA_SPECIAL_REV[-2] = IC9xB_SPECIAL_REV[-2] = "C1" - -IC9x_SPECIAL = { - 1 : IC9xA_SPECIAL, - 2 : IC9xB_SPECIAL, -} - -IC9x_SPECIAL_REV = { - 1 : IC9xA_SPECIAL_REV, - 2 : IC9xB_SPECIAL_REV, +IC9X_SPECIAL = { + 1 : IC9XA_SPECIAL, + 2 : IC9XB_SPECIAL, }
CHARSET = chirp_common.CHARSET_ALPHANUMERIC + \ @@ -61,7 +47,8 @@
LOCK = threading.Lock()
-class IC9xBank(icf.IcomBank): +class IC9xBank(icf.IcomNamedBank): + """Icom 9x Bank""" def get_name(self): banks = self._model._radio._ic9x_get_banks() return banks[self.index] @@ -73,6 +60,7 @@
@directory.register class IC9xRadio(icf.IcomLiveRadio): + """Base class for Icom IC-9x radios""" MODEL = "IC-91/92AD"
_model = "ic9x" # Fake model info for detect.py @@ -102,7 +90,7 @@ self.set_memory(mem)
def __init__(self, *args, **kwargs): - chirp_common.LiveRadio.__init__(self, *args, **kwargs) + icf.IcomLiveRadio.__init__(self, *args, **kwargs)
if self.pipe: self.pipe.setTimeout(0.1) @@ -119,35 +107,22 @@ ic9x_ll.send_magic(self.pipe) self.__last = time.time()
- def get_available_bank_index(self, bank): - indexes = [] - for mem in self.__memcache.values(): - if mem.bank == bank and mem.bank_index >= 0: - indexes.append(mem.bank_index) - - print "Index list for %i: %s" % (bank, indexes) - - for i in range(0, 99): - if i not in indexes: - return i - - raise errors.RadioError("Out of slots in this bank") - def get_special_locations(self): - return sorted(IC9x_SPECIAL[self.vfo].keys()) + return sorted(IC9X_SPECIAL[self.vfo].keys())
def get_memory(self, number): if isinstance(number, str): try: - number = IC9x_SPECIAL[self.vfo][number] + number = IC9X_SPECIAL[self.vfo][number] except KeyError: - raise InvalidMemoryLocation("Unknown channel %s" % number) + raise errors.InvalidMemoryLocation("Unknown channel %s" % \ + number)
if number < -2 or number > 999: raise errors.InvalidValueError("Number must be between 0 and 999")
if self.__memcache.has_key(number): - return self.__memcache[number] + return self.__memcache[number]
self._lock.acquire() try: @@ -165,7 +140,8 @@ self._lock.release()
if number > self._upper or number < 0: - mem.extd_number = IC9x_SPECIAL_REV[self.vfo][number] + mem.extd_number = util.get_dict_rev(IC9X_SPECIAL, + [self.vfo][number]) mem.immutable = ["number", "skip", "bank", "bank_index", "extd_number"]
@@ -184,7 +160,7 @@
self._lock.release()
- return repr(bitwise.parse(ic9x_ll.memory_frame_format, mframe)) + return repr(bitwise.parse(ic9x_ll.MEMORY_FRAME_FORMAT, mframe))
def get_memories(self, lo=0, hi=None): if hi is None: @@ -192,7 +168,7 @@
memories = []
- for i in range(lo, hi+1): + for i in range(lo, hi + 1): try: print "Getting %i" % i mem = self.get_memory(i) @@ -244,7 +220,8 @@
def _ic9x_get_banks(self): if len(self.__bankcache.keys()) == 26: - return [self.__bankcache[k] for k in sorted(self.__bankcache.keys())] + return [self.__bankcache[k] for k in + sorted(self.__bankcache.keys())]
self._lock.acquire() try: @@ -302,6 +279,7 @@ return rf
class IC9xRadioA(IC9xRadio): + """IC9x Band A subdevice""" VARIANT = "Band A" vfo = 1 _upper = 849 @@ -323,6 +301,7 @@ return rf
class IC9xRadioB(IC9xRadio, chirp_common.IcomDstarSupport): + """IC9x Band B subdevice""" VARIANT = "Band B" vfo = 2 _upper = 399 @@ -362,7 +341,7 @@ calls = []
self._maybe_send_magic() - for i in range(ulimit-1): + for i in range(ulimit - 1): call = ic9x_ll.get_call(self.pipe, cstype, i+1) calls.append(call)
@@ -426,15 +405,13 @@ self.RPTCALL_LIMIT[1], calls)
+def _test(): + import serial + ser = IC9xRadioB(serial.Serial(port="/dev/ttyUSB1", + baudrate=38400, timeout=0.1)) + print ser.get_urcall_list() + print "-- FOO --" + ser.set_urcall_list(["K7TAY", "FOOBAR", "BAZ"]) + if __name__ == "__main__": - def test(): - import serial - import util - r = IC9xRadioB(serial.Serial(port="/dev/ttyUSB1", - baudrate=38400, timeout=0.1)) - print r.get_urcall_list() - #r.set_urcall_list(["K7TAY", "FOOBAR"]) - print "-- FOO --" - r.set_urcall_list(["K7TAY", "FOOBAR", "BAZ"]) - - test() + _test() diff -r db82df47f205 -r f805c420592f chirp/ic9x_icf.py --- a/chirp/ic9x_icf.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic9x_icf.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, ic9x_icf_ll, util, directory +from chirp import chirp_common, icf, ic9x_icf_ll, util, directory, errors
@directory.register class IC9xICFRadio(chirp_common.CloneModeRadio): @@ -45,7 +45,7 @@ return ic9x_icf_ll.get_memory(self._mmap, number)
def load_mmap(self, filename): - mdata, self._mmap = icf.read_file(filename) + _mdata, self._mmap = icf.read_file(filename)
def get_sub_devices(self): return [IC9xICFRadioA(self._mmap), diff -r db82df47f205 -r f805c420592f chirp/ic9x_icf_ll.py --- a/chirp/ic9x_icf_ll.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic9x_icf_ll.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,7 +14,7 @@ # along with this program. If not, see http://www.gnu.org/licenses/.
import struct -from chirp import chirp_common, util +from chirp import chirp_common from chirp.memmap import MemoryMap
MEM_LOC_SIZE_A = 20 @@ -31,12 +31,14 @@ POS_NAME = 12
def get_mem_offset(number): + """Get the offset into the memory map for memory @number""" if number < 850: return MEM_LOC_SIZE_A * number else: return (MEM_LOC_SIZE_A * 850) + (MEM_LOC_SIZE_B * (number - 850))
def get_raw_memory(mmap, number): + """Return a raw representation of memory @number""" offset = get_mem_offset(number) if number >= 850: size = MEM_LOC_SIZE_B @@ -45,6 +47,7 @@ return MemoryMap(mmap[offset:offset+size])
def get_freq(mmap): + """Return the memory frequency""" if ord(mmap[10]) & 0x10: mult = 6250 else: @@ -53,22 +56,27 @@ return val * mult
def get_offset(mmap): + """Return the memory offset""" val, = struct.unpack(">H", mmap[POS_OFFSET:POS_OFFSET+2]) return val * 5000
def get_rtone(mmap): + """Return the memory rtone""" val = (ord(mmap[POS_TONE]) & 0xFC) >> 2 return chirp_common.TONES[val]
def get_ctone(mmap): + """Return the memory ctone""" val = (ord(mmap[POS_TONE]) & 0x03) | ((ord(mmap[POS_TONE+1]) & 0xF0) >> 4) return chirp_common.TONES[val]
def get_dtcs(mmap): + """Return the memory dtcs value""" val = ord(mmap[POS_DTCS]) >> 1 return chirp_common.DTCS_CODES[val]
def get_mode(mmap): + """Return the memory mode""" val = ord(mmap[POS_MODE]) & 0x07
modemap = ["FM", "NFM", "WFM", "AM", "DV", "FM"] @@ -76,20 +84,22 @@ return modemap[val]
def get_ts(mmap): + """Return the memory tuning step""" val = (ord(mmap[POS_TS]) & 0xF0) >> 4 if val == 14: return 5.0 # Coerce "Auto" to 5.0
- ICF_TS = list(chirp_common.TUNING_STEPS) - ICF_TS.insert(2, 8.33) - ICF_TS.insert(3, 9.00) - ICF_TS.append(100.0) - ICF_TS.append(125.0) - ICF_TS.append(200.0) + icf_ts = list(chirp_common.TUNING_STEPS) + icf_ts.insert(2, 8.33) + icf_ts.insert(3, 9.00) + icf_ts.append(100.0) + icf_ts.append(125.0) + icf_ts.append(200.0)
- return ICF_TS[val] + return icf_ts[val]
def get_dtcs_polarity(mmap): + """Return the memory dtcs polarity""" val = (ord(mmap[POS_DTCSPOL]) & 0x03)
pols = ["NN", "NR", "RN", "RR"] @@ -97,6 +107,7 @@ return pols[val]
def get_duplex(mmap): + """Return the memory duplex""" val = (ord(mmap[POS_DUPLEX]) & 0x0C) >> 2
dup = ["", "-", "+", ""] @@ -104,9 +115,11 @@ return dup[val]
def get_name(mmap): + """Return the memory name""" return mmap[POS_NAME:POS_NAME+8]
def get_memory(_mmap, number): + """Get memory @number from global memory map @_mmap""" mmap = get_raw_memory(_mmap, number) mem = chirp_common.Memory() mem.number = number diff -r db82df47f205 -r f805c420592f chirp/ic9x_ll.py --- a/chirp/ic9x_ll.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ic9x_ll.py Fri Apr 27 14:35:41 2012 -0700 @@ -32,11 +32,13 @@
# Dirty hack until I clean up this IC9x mess class IC9xMemory(chirp_common.Memory): + """A dirty hack to stash bank information in a memory""" _bank = None _bank_index = 0 def __init__(self): chirp_common.Memory.__init__(self) class IC9xDVMemory(chirp_common.DVMemory): + """See above dirty hack""" _bank = None _bank_index = 0 def __init__(self): @@ -89,32 +91,34 @@
return _ic9x_parse_frames(data)
-class IcomFrame: - pass - -class IC92Frame(IcomFrame): +class IC92Frame: + """IC9x frame base class""" def get_vfo(self): + """Return the vfo number""" return ord(self._map[0])
def set_vfo(self, vfo): + """Set the vfo number""" self._map[0] = chr(vfo)
def from_raw(self, data): + """Construct the frame from raw data""" self._map = MemoryMap(data)
- #self._map.printable() - def from_frame(self, frame): - self._map = frame._map + """Construct the frame by copying another frame""" + self._map = MemoryMap(frame.get_raw())
def __init__(self, subcmd=0, flen=0, cmd=0x1A): self._map = MemoryMap("\x00" * (4 + flen)) self._map[0] = "\x01\x80" + chr(cmd) + chr(subcmd)
def get_payload(self): + """Return the entire payload (sans header)""" return self._map[4:]
def get_raw(self): + """Return the raw version of the frame""" return self._map.get_packed()
def __str__(self): @@ -126,6 +130,7 @@ return string
def send(self, pipe, verbose=False): + """Send the frame to the radio via @pipe""" if verbose: print "Sending:\n%s" % util.hexprint(self.get_raw())
@@ -146,10 +151,11 @@ return self._map[start+4:end+4]
class IC92GetBankFrame(IC92Frame): + """A frame for requesting bank information""" def __init__(self): IC92Frame.__init__(self, 0x09)
- def send(self, pipe): + def send(self, pipe, verbose=False): rframes = ic9x_send(pipe, self.get_raw())
if len(rframes) == 0: @@ -158,27 +164,30 @@ return rframes
class IC92BankFrame(IC92Frame): + """A frame for bank information""" def __init__(self): # 1 byte for identifier # 8 bytes for name IC92Frame.__init__(self, 0x0B, 9)
- def __str__(self): - return "Bank %s: %s" % (self._data[2], self._data[3:]) - def get_name(self): + """Return the bank name""" return self[1:]
def get_identifier(self): + """Return the letter for the bank (A-Z)""" return self[0]
def set_name(self, name): + """Set the bank name""" self[1] = name[:8].ljust(8)
def set_identifier(self, ident): + """Set the letter for the bank (A-Z)""" self[0] = ident[0]
class IC92MemClearFrame(IC92Frame): + """A frame for clearing (erasing) a memory""" def __init__(self, loc): # 2 bytes for location # 1 byte for 0xFF @@ -187,24 +196,27 @@ self[0] = struct.pack(">BHB", 1, int("%i" % loc, 16), 0xFF)
class IC92MemGetFrame(IC92Frame): - def __init__(self, loc, call=False): + """A frame for requesting a memory""" + def __init__(self, loc, iscall=False): # 2 bytes for location IC92Frame.__init__(self, 0x00, 3)
- if call: - c = 2 + if iscall: + call = 2 else: - c = 1 + call = 1
- self[0] = struct.pack(">BH", c, int("%i" % loc, 16)) + self[0] = struct.pack(">BH", call, int("%i" % loc, 16))
class IC92GetCallsignFrame(IC92Frame): - def __init__(self, type, number): - IC92Frame.__init__(self, type, 1, 0x1D) + """A frame for getting callsign information""" + def __init__(self, calltype, number): + IC92Frame.__init__(self, calltype, 1, 0x1D)
self[0] = chr(number)
class IC92CallsignFrame(IC92Frame): + """A frame to communicate callsign information""" command = 0 # Invalid width = 8
@@ -216,19 +228,23 @@ self[0] = chr(number) + callsign[:self.width].ljust(self.width)
def get_callsign(self): + """Return the actual callsign""" return self[1:self.width+1].rstrip()
class IC92YourCallsignFrame(IC92CallsignFrame): + """URCALL frame""" command = 6 # Your
class IC92RepeaterCallsignFrame(IC92CallsignFrame): + """RPTCALL frame""" command = 7 # Repeater
class IC92MyCallsignFrame(IC92CallsignFrame): + """MYCALL frame""" command = 8 # My width = 12 # 4 bytes for /STID
-memory_frame_format = """ +MEMORY_FRAME_FORMAT = """ struct { u8 vfo; bbcd number[2]; @@ -261,6 +277,7 @@ """
class IC92MemoryFrame(IC92Frame): + """A frame for communicating memory information""" def __init__(self): IC92Frame.__init__(self, 0, DV_MEM_LEN)
@@ -284,21 +301,24 @@ self._map.truncate(MEM_LEN + 4)
def set_iscall(self, iscall): + """This frame refers to a call channel if @iscall is True""" if iscall: self[0] = 2 else: self[0] = 1
def get_iscall(self): + """Return True if this frame refers to a call channel""" return ord(self[0]) == 2
def set_memory(self, mem): + """Take Memory object @mem and configure the frame accordingly""" if mem.number < 0: self.set_iscall(True) mem.number = abs(mem.number) - 1 print "Memory is %i (call %s)" % (mem.number, self.get_iscall())
- _mem = bitwise.parse(memory_frame_format, self).mem + _mem = bitwise.parse(MEMORY_FRAME_FORMAT, self).mem
_mem.number = mem.number
@@ -329,7 +349,8 @@ _mem.digital_code = mem.dv_code
def get_memory(self): - _mem = bitwise.parse(memory_frame_format, self).mem + """Return a Memory object based on the contents of the frame""" + _mem = bitwise.parse(MEMORY_FRAME_FORMAT, self).mem
if MODES[_mem.mode] == "DV": mem = IC9xDVMemory() @@ -372,74 +393,55 @@
return mem
-def print_frames(frames): - count = 0 - for i in frames: - print "Frame %i:" % count - print i - count += 1 - def _send_magic_4800(pipe): cmd = "\x01\x80\x19" magic = ("\xFE" * 25) + cmd - for i in [0,1]: - r = ic9x_send(pipe, magic) - if r: - return r[0].get_raw()[0] == "\x80" - return r and r[0].get_raw()[:3] == rsp + for _i in [0, 1]: + resp = ic9x_send(pipe, magic) + if resp: + return resp[0].get_raw()[0] == "\x80" + return True
def _send_magic_38400(pipe): cmd = "\x01\x80\x19" - rsp = "\x80\x01\x19" + #rsp = "\x80\x01\x19" magic = ("\xFE" * 400) + cmd - for i in [0,1]: - r = ic9x_send(pipe, magic) - if r: - return r[0].get_raw()[0] == "\x80" + for _i in [0, 1]: + resp = ic9x_send(pipe, magic) + if resp: + return resp[0].get_raw()[0] == "\x80" return False
def send_magic(pipe): + """Send the magic incantation to wake up an ic9x radio""" if pipe.getBaudrate() == 38400: - r = _send_magic_38400(pipe) - if r: + resp = _send_magic_38400(pipe) + if resp: return print "Switching from 38400 to 4800" pipe.setBaudrate(4800) - r = _send_magic_4800(pipe) + resp = _send_magic_4800(pipe) pipe.setBaudrate(38400) - if r: + if resp: return raise errors.RadioError("Radio not responding") elif pipe.getBaudrate() == 4800: - r = _send_magic_4800(pipe) - if r: + resp = _send_magic_4800(pipe) + if resp: return print "Switching from 4800 to 38400" pipe.setBaudrate(38400) - r = _send_magic_38400(pipe) - if r: + resp = _send_magic_38400(pipe) + if resp: return pipe.setBaudrate(4800) raise errors.RadioError("Radio not responding") else: - raise errors.InvalidDataError("Radio in unknown state (%i)" % r.getBaudrate()) - -def print_banks(pipe): - frames = send(pipe, "\x01\x80\x1a\x09") # Banks - - print "A Banks:" - for i in range(180, 180+26): - bf = IC92BankFrame() - bf.from_frame(frames[i]) - print str(bf) - - print "B Banks:" - for i in range(237, 237+26): - bf = IC92BankFrame() - bf.from_frame(frames[i]) - print str(bf) + raise errors.InvalidDataError("Radio in unknown state (%i)" % \ + pipe.getBaudrate())
def get_memory_frame(pipe, vfo, number): + """Get the memory frame for @vfo and @number via @pipe""" if number < 0: number = abs(number + 1) call = True @@ -452,6 +454,7 @@ return frame.send(pipe)
def get_memory(pipe, vfo, number): + """Get a memory object for @vfo and @number via @pipe""" rframe = get_memory_frame(pipe, vfo, number)
if len(rframe.get_payload()) < 1: @@ -466,6 +469,7 @@ return mf.get_memory()
def set_memory(pipe, vfo, memory): + """Set memory @memory on @vfo via @pipe""" frame = IC92MemoryFrame() frame.set_memory(memory) frame.set_vfo(vfo) @@ -480,6 +484,7 @@ util.hexprint(rframe.get_payload()))
def erase_memory(pipe, vfo, number): + """Erase memory @number on @vfo via @pipe""" frame = IC92MemClearFrame(number) frame.set_vfo(vfo)
@@ -488,6 +493,7 @@ raise errors.InvalidDataError("Radio reported error")
def get_banks(pipe, vfo): + """Get banks for @vfo via @pipe""" frame = IC92GetBankFrame() frame.set_vfo(vfo)
@@ -509,6 +515,7 @@ return banks
def set_banks(pipe, vfo, banks): + """Set banks for @vfo via @pipe""" for i in range(0, 26): bframe = IC92BankFrame() bframe.set_vfo(vfo) @@ -520,6 +527,7 @@ raise errors.InvalidDataError("Radio reported error")
def get_call(pipe, cstype, number): + """Get @cstype callsign @number via @pipe""" cframe = IC92GetCallsignFrame(cstype.command, number) cframe.set_vfo(2) rframe = cframe.send(pipe) @@ -530,28 +538,10 @@ return cframe.get_callsign()
def set_call(pipe, cstype, number, call): + """Set @cstype @call at position @number via @pipe""" cframe = cstype(number, call) cframe.set_vfo(2) rframe = cframe.send(pipe)
if rframe.get_payload() != "\xfb": raise errors.RadioError("Radio reported error") - -def print_memory(pipe, vfo, number): - if vfo not in [1, 2]: - raise errors.InvalidValueError("VFO must be 1 or 2") - - if number < 0 or number > 399: - raise errors.InvalidValueError("Number must be between 0 and 399") - - mf = get_memory(pipe, vfo, number) - - print "Memory %i from VFO %i: %s" % (number, vfo, str(mf)) - -if __name__ == "__main__": - print util.hexprint(util.bcd_encode(1072)) - print util.hexprint(util.bcd_encode(146900000, False)) - print util.hexprint(util.bcd_encode(25, width=4)) - print util.hexprint(util.bcd_encode(5000000, False, 6)) - print util.hexprint(util.bcd_encode(600000, False, 6)) - diff -r db82df47f205 -r f805c420592f chirp/icf.py --- a/chirp/icf.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/icf.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,21 +13,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
- import struct import re
from chirp import chirp_common, errors, util, memmap -from chirp import ic9x_ll # for magic; may need to move later
CMD_CLONE_OUT = 0xE2 CMD_CLONE_IN = 0xE3 CMD_CLONE_DAT = 0xE4 -CMD_CLONE_END = 0xE5 +CMD_CLONE_END = 0xE5
-save_pipe = None +SAVE_PIPE = None
class IcfFrame: + """A single ICF communication frame""" src = 0 dst = 0 cmd = 0 @@ -53,6 +52,7 @@ pass
def parse_frame_generic(data): + """Parse an ICF frame of unknown type from the beginning of @data""" frame = IcfFrame()
frame.src = ord(data[2]) @@ -69,6 +69,7 @@ return frame, data[end+1:]
class RadioStream: + """A class to make reading a stream of IcfFrames easier""" def __init__(self, pipe): self.pipe = pipe self.data = "" @@ -105,6 +106,7 @@ return frames
def get_frames(self, nolimit=False): + """Read any pending frames from the stream""" while True: _data = self.pipe.read(64) if not _data: @@ -114,7 +116,7 @@
if not nolimit and len(self.data) > 128 and "\xFD" in self.data: break # Give us a chance to do some status - if len(data) > 1024: + if len(_data) > 1024: break # Avoid an endless loop of chewing garbage
if not self.data: @@ -122,8 +124,9 @@
return self._process_frames()
-def get_model_data(pipe, model="\x00\x00\x00\x00"): - send_clone_frame(pipe, 0xe0, model, raw=True) +def get_model_data(pipe, mdata="\x00\x00\x00\x00"): + """Query the radio connected to @pipe for its model data""" + send_clone_frame(pipe, 0xe0, mdata, raw=True)
stream = RadioStream(pipe) frames = stream.get_frames() @@ -134,7 +137,10 @@ return frames[0].payload
def get_clone_resp(pipe, length=None): + """Read the response to a clone frame""" def exit_criteria(buf, length): + """Stop reading a clone response if we have enough data or encounter + the end of a frame""" if length is None: return buf.endswith("\xfd") else: @@ -147,6 +153,8 @@ return resp
def send_clone_frame(pipe, cmd, data, raw=False, checksum=False): + """Send a clone frame with @cmd and @data to the radio attached + to @pipe""" cs = 0
if raw: @@ -166,9 +174,9 @@
frame = "\xfe\xfe\xee\xef%s%s%s\xfd" % (chr(cmd), hed, cs)
- if save_pipe: + if SAVE_PIPE: print "Saving data..." - save_pipe.write(frame) + SAVE_PIPE.write(frame)
#print "Sending:\n%s" % util.hexprint(frame) #print "Sending:\n%s" % util.hexprint(hed[6:]) @@ -182,6 +190,7 @@ return frame
def process_bcd(bcddata): + """Convert BCD-encoded data to raw""" data = "" i = 0 while i < range(len(bcddata)) and i+1 < len(bcddata): @@ -195,36 +204,45 @@
return data
-def process_data_frame(frame, mmap): +def process_data_frame(frame, _mmap): + """Process a data frame, adding the payload to @_mmap""" _data = process_bcd(frame.payload) - if len(mmap) >= 0x10000: + if len(_mmap) >= 0x10000: saddr, = struct.unpack(">I", _data[0:4]) - bytes, = struct.unpack("B", _data[4]) - data = _data[5:5+bytes] + length, = struct.unpack("B", _data[4]) + data = _data[5:5+length] else: saddr, = struct.unpack(">H", _data[0:2]) - bytes, = struct.unpack("B", _data[2]) - data = _data[3:3+bytes] + length, = struct.unpack("B", _data[2]) + data = _data[3:3+length]
try: - mmap[saddr] = data + _mmap[saddr] = data except IndexError: - print "Error trying to set %i bytes at %05x (max %05x)" %\ - (bytes, saddr, len(mmap)) - return saddr, saddr + bytes + print "Error trying to set %i bytes at %05x (max %05x)" % \ + (bytes, saddr, len(_mmap)) + return saddr, saddr + length
def start_hispeed_clone(radio, cmd): - buf = ("\xFE" * 20) + "\xEE\xEF\xE8" + radio._model + "\x00\x00\x02\x01\xFD" + """Send the magic incantation to the radio to go fast""" + buf = ("\xFE" * 20) + \ + "\xEE\xEF\xE8" + \ + radio.get_model() + \ + "\x00\x00\x02\x01\xFD" print "Starting HiSpeed:\n%s" % util.hexprint(buf) radio.pipe.write(buf) radio.pipe.flush() - r = radio.pipe.read(128) - print "Response:\n%s" % util.hexprint(r) + resp = radio.pipe.read(128) + print "Response:\n%s" % util.hexprint(resp)
print "Switching to 38400 baud" radio.pipe.setBaudrate(38400)
- buf = ("\xFE" * 14) + "\xEE\xEF" + chr(cmd) + radio._model[:3] + "\x00\xFD" + buf = ("\xFE" * 14) + \ + "\xEE\xEF" + \ + chr(cmd) + \ + radio.get_model()[:3] + \ + "\x00\xFD" print "Starting HiSpeed Clone:\n%s" % util.hexprint(buf) radio.pipe.write(buf) radio.pipe.flush() @@ -247,7 +265,7 @@ stream = RadioStream(radio.pipe)
addr = 0 - mmap = memmap.MemoryMap(chr(0x00) * radio._memsize) + _mmap = memmap.MemoryMap(chr(0x00) * radio.get_memsize()) last_size = 0 while True: frames = stream.get_frames() @@ -256,7 +274,7 @@
for frame in frames: if frame.cmd == CMD_CLONE_DAT: - src, dst = process_data_frame(frame, mmap) + src, dst = process_data_frame(frame, _mmap) if last_size != (dst - src): print "ICF Size change from %i to %i at %04x" % (last_size, dst - src, @@ -276,16 +294,18 @@ status.cur = addr radio.status_fn(status)
- return mmap + return _mmap
def clone_from_radio(radio): + """Do a full clone out of the radio's memory""" try: return _clone_from_radio(radio) except Exception, e: raise errors.RadioError("Failed to communicate with the radio: %s" % e)
def send_mem_chunk(radio, start, stop, bs=32): - mmap = radio.get_mmap() + """Send a single chunk of the radio's memory from @start-@stop""" + _mmap = radio.get_mmap()
status = chirp_common.Status() status.msg = "Cloning to radio" @@ -297,11 +317,11 @@ else: size = stop - i
- if radio._memsize >= 0x10000: + if radio.get_memsize() >= 0x10000: chunk = struct.pack(">IB", i, size) else: chunk = struct.pack(">HB", i, size) - chunk += mmap[i:i+size] + chunk += _mmap[i:i+size]
send_clone_frame(radio.pipe, CMD_CLONE_DAT, @@ -315,10 +335,10 @@ return True
def _clone_to_radio(radio): - global save_pipe + global SAVE_PIPE
# Uncomment to save out a capture of what we actually write to the radio - # save_pipe = file("pipe_capture.log", "w", 0) + # SAVE_PIPE = file("pipe_capture.log", "w", 0)
md = get_model_data(radio.pipe)
@@ -347,9 +367,9 @@ send_clone_frame(radio.pipe, CMD_CLONE_END, radio.get_endframe(), raw=True) frames += stream.get_frames(True)
- if save_pipe: - save_pipe.close() - save_pipe = None + if SAVE_PIPE: + SAVE_PIPE.close() + SAVE_PIPE = None
try: result = frames[-1] @@ -359,21 +379,24 @@ return result.payload[0] == '\x00'
def clone_to_radio(radio): + """Initiate a full memory clone out to @radio""" try: return _clone_to_radio(radio) except Exception, e: raise errors.RadioError("Failed to communicate with the radio: %s" % e)
def convert_model(mod_str): + """Convert an ICF-style model string into what we get from the radio""" data = "" for i in range(0, len(mod_str), 2): - hex = mod_str[i:i+2] - val = int(hex, 16) - data += chr(val) + hexval = mod_str[i:i+2] + intval = int(hexval, 16) + data += chr(intval)
return data
def convert_data_line(line): + """Convert an ICF data line to raw memory format""" if line.startswith("#"): return ""
@@ -381,12 +404,10 @@
if len(line) == 38: # Small memory (< 0x10000) - pos = int(line[0:4], 16) size = int(line[4:6], 16) data = line[6:] else: # Large memory (>= 0x10000) - pos = int(line[0:8], 16) size = int(line[8:10], 16) data = line[10:]
@@ -404,6 +425,7 @@ return _mmap
def read_file(filename): + """Read an ICF file and return the model string and memory data""" f = file(filename)
mod_str = f.readline() @@ -419,6 +441,7 @@ return model, memmap.MemoryMap(_mmap)
def is_9x_icf(filename): + """Returns True if @filename is an IC9x ICF file""" f = file(filename) mdata = f.read(8) f.close() @@ -426,6 +449,7 @@ return mdata in ["30660000", "28880000"]
def is_icf_file(filename): + """Returns True if @filename is an ICF file""" f = file(filename) data = f.readline() data += f.readline() @@ -436,10 +460,17 @@ return bool(re.match("^[0-9]{8}#", data))
class IcomBank(chirp_common.Bank): + """A bank that works for all Icom radios""" # Integral index of the bank (not to be confused with per-memory # bank indexes index = 0
+class IcomNamedBank(IcomBank): + """A bank with an adjustable name""" + def set_name(self, name): + """Set the name of the bank""" + pass + class IcomBankModel(chirp_common.BankModel): """Icom radios all have pretty much the same simple bank model. This central implementation can, with a few icom-specific radio interfaces @@ -483,6 +514,7 @@ return [self.get_banks()[index]]
class IcomIndexedBankModel(IcomBankModel, chirp_common.BankIndexInterface): + """Generic bank model for Icom radios with indexed banks""" def get_index_bounds(self): return self._radio._bank_index_bounds
@@ -512,6 +544,7 @@
class IcomCloneModeRadio(chirp_common.CloneModeRadio): + """Base class for Icom clone-mode radios""" VENDOR = "Icom" BAUDRATE = 9600
@@ -523,17 +556,25 @@ _bank_class = IcomBank _can_hispeed = False
- def is_hispeed(self): - return self._can_hispeed + @classmethod + def is_hispeed(cls): + """Returns True if the radio supports hispeed cloning""" + return cls._can_hispeed
- def get_model(self): - return self._model + @classmethod + def get_model(cls): + """Returns the Icom model data for this radio""" + return cls._model
- def get_endframe(self): - return self._endframe + @classmethod + def get_endframe(cls): + """Returns the magic clone end frame for this radio""" + return cls._endframe
- def get_ranges(self): - return self._ranges + @classmethod + def get_ranges(cls): + """Returns the ranges this radio likes to have in a clone""" + return cls._ranges
def sync_in(self): self._mmap = clone_from_radio(self) @@ -563,6 +604,7 @@ raise Exception("Not implemented")
class IcomLiveRadio(chirp_common.LiveRadio): + """Base class for an Icom Live-mode radio""" VENDOR = "Icom" BAUD_RATE = 38400
@@ -579,14 +621,3 @@ return IcomBankModel(self) else: return None - -if __name__ == "__main__": - import sys - - model, mmap = read_file(sys.argv[1]) - - print util.hexprint(model) - - f = file("out.img", "w") - f.write(mmap.get_packed()) - f.close() diff -r db82df47f205 -r f805c420592f chirp/icomciv.py --- a/chirp/icomciv.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/icomciv.py Fri Apr 27 14:35:41 2012 -0700 @@ -1,18 +1,18 @@
import struct -from chirp import chirp_common, icf, util, errors, bitwise, ic9x_ll, directory +from chirp import chirp_common, icf, util, errors, bitwise, directory from chirp.memmap import MemoryMap
DEBUG = True
-mem_format = """ +MEM_FORMAT = """ bbcd number[2]; u8 unknown1; lbcd freq[5]; u8 unknown2:5, mode:3; """ -mem_vfo_format = """ +MEM_VFO_FORMAT = """ u8 vfo; bbcd number[2]; u8 unknown1; @@ -35,6 +35,7 @@ """
class Frame: + """Base class for an ICF frame""" _cmd = 0x00 _sub = 0x00
@@ -42,16 +43,20 @@ self._data = ""
def set_command(self, cmd, sub): + """Set the command number (and optional subcommand)""" self._cmd = cmd self._sub = sub
def get_data(self): + """Return the data payload""" return self._data
def set_data(self, data): + """Set the data payload""" self._data = data
def send(self, src, dst, serial, willecho=True): + """Send the frame over @serial, using @src and @dst addresses""" raw = struct.pack("BBBBBB", 0xFE, 0xFE, src, dst, self._cmd, self._sub) raw += str(self._data) + chr(0xFD)
@@ -68,13 +73,14 @@ print util.hexprint(echo)
def read(self, serial): + """Read the frame from @serial""" data = "" while not data.endswith(chr(0xFD)): - c = serial.read(1) - if not c: + char = serial.read(1) + if not char: print "Read %i bytes total" % len(data) raise errors.RadioError("Timeout") - data += c + data += char
if data == chr(0xFD): raise errors.RadioError("Radio reported error") @@ -90,39 +96,49 @@ return src, dst
class MemFrame(Frame): + """A memory frame""" _cmd = 0x1A _sub = 0x00 _loc = 0
def set_location(self, loc): + """Set the memory location number""" self._loc = loc self._data = struct.pack(">H", int("%04i" % loc, 16))
def make_empty(self): + """Mark as empty so the radio will erase the memory""" self._data = struct.pack(">HB", int("%04i" % self._loc, 16), 0xFF)
def is_empty(self): + """Return True if memory is marked as empty""" return len(self._data) < 5
def get_obj(self): + """Return a bitwise parsed object""" self._data = MemoryMap(str(self._data)) # Make sure we're assignable - return bitwise.parse(mem_format, self._data) + return bitwise.parse(MEM_FORMAT, self._data)
def initialize(self): + """Initialize to sane values""" self._data = MemoryMap("".join(["\x00"] * (self.get_obj().size() / 8)))
class MultiVFOMemFrame(MemFrame): + """A memory frame for radios with multiple VFOs""" def set_location(self, loc, vfo=1): self._loc = loc self._data = struct.pack(">BH", vfo, int("%04i" % loc, 16))
def get_obj(self): self._data = MemoryMap(str(self._data)) # Make sure we're assignable - return bitwise.parse(mem_vfo_format, self._data) + return bitwise.parse(MEM_VFO_FORMAT, self._data)
class IcomCIVRadio(icf.IcomLiveRadio): + """Base class for ICOM CIV-based radios""" BAUD_RATE = 19200 MODEL = "CIV Radio" + _model = "\x00" + _template = 0
def _send_frame(self, frame): return frame.send(ord(self._model), 0xE0, self.pipe, @@ -140,9 +156,9 @@ def _detect_echo(self): echo_test = "\xfe\xfe\xe0\xe0\xfa\xfd" self.pipe.write(echo_test) - r = self.pipe.read(6) - print "Echo:\n%s" % util.hexprint(r) - return r == echo_test + resp = self.pipe.read(6) + print "Echo:\n%s" % util.hexprint(resp) + return resp == echo_test
def __init__(self, *args, **kwargs): icf.IcomLiveRadio.__init__(self, *args, **kwargs) @@ -256,6 +272,7 @@
@directory.register class Icom7200Radio(IcomCIVRadio): + """Icom IC-7200""" MODEL = "7200" _model = "\x76" _template = 201 @@ -277,6 +294,7 @@
@directory.register class Icom7000Radio(IcomCIVRadio): + """Icom IC-7000""" MODEL = "7000" _model = "\x70" _template = 102 @@ -305,14 +323,15 @@ (0x70, 0xE0) : Icom7000Radio, }
-def probe_model(s): +def probe_model(ser): + """Probe the radio attatched to @ser for its model""" f = Frame() f.set_command(0x19, 0x00)
for model, controller in CIV_MODELS.keys(): - f.send(model, controller, s) + f.send(model, controller, ser) try: - f.read(s) + f.read(ser) except errors.RadioError: continue
diff -r db82df47f205 -r f805c420592f chirp/icq7.py --- a/chirp/icq7.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/icq7.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,12 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, errors, util, directory +from chirp import chirp_common, icf, directory from chirp import bitwise -from chirp.memmap import MemoryMap from chirp.chirp_common import to_GHz, from_GHz
-mem_format = """ +MEM_FORMAT = """ struct { bbcd freq[3]; u8 fractional:1, @@ -49,6 +48,7 @@
@directory.register class ICQ7Radio(icf.IcomCloneModeRadio): + """Icom IC-Q7A""" VENDOR = "Icom" MODEL = "IC-Q7A"
@@ -76,7 +76,7 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number]) diff -r db82df47f205 -r f805c420592f chirp/ict70.py --- a/chirp/ict70.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/ict70.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,11 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, errors, util, directory +from chirp import chirp_common, icf, directory from chirp import bitwise -from chirp.memmap import MemoryMap
-mem_format = """ +MEM_FORMAT = """ struct { u24 freq; ul16 offset; @@ -71,6 +70,7 @@ ]
class ICT70Bank(icf.IcomBank): + """ICT70 bank""" def get_name(self): _bank = self._model._radio._memobj.bank_names[self.index] return str(_bank.name).rstrip() @@ -81,6 +81,7 @@
@directory.register class ICT70Radio(icf.IcomCloneModeRadio): + """Icom IC-T70""" VENDOR = "Icom" MODEL = "IC-T70"
@@ -133,7 +134,7 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number]) diff -r db82df47f205 -r f805c420592f chirp/icw32.py --- a/chirp/icw32.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/icw32.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, icf, errors, util, directory +from chirp import chirp_common, icf, util, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ #seekto 0x%x; struct { bbcd freq[3]; @@ -61,8 +61,16 @@ DUPLEX = ["", "", "-", "+"] TONE = ["", "", "Tone", "TSQL"]
+def _get_special(): + special = {} + for i in range(0, 5): + special["M%iA" % (i+1)] = 100 + i*2 + special["M%iB" % (i+1)] = 100 + i*2 + 1 + return special + @directory.register class ICW32ARadio(icf.IcomCloneModeRadio): + """Icom IC-W32A""" VENDOR = "Icom" MODEL = "IC-W32A"
@@ -97,26 +105,18 @@ return rf
def process_mmap(self): - format = mem_format % self._mem_positions - self._memobj = bitwise.parse(format, self._mmap) + fmt = MEM_FORMAT % self._mem_positions + self._memobj = bitwise.parse(fmt, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number])
- def _get_special(self): - special = {} - for i in range(0, 5): - special["M%iA" % (i+1)] = 100 + i*2 - special["M%iB" % (i+1)] = 100 + i*2 + 1 - - return special - def get_special_locations(self): - return sorted(self._get_special().keys()) + return sorted(_get_special().keys())
def get_memory(self, number): if isinstance(number, str): - number = self._get_special()[number] + number = _get_special()[number]
_mem = self._memobj.memory[number] _flg = self._memobj.flag[number] @@ -129,7 +129,7 @@ mem.skip = _flg.skip and "S" or "" else: # Special memories - mem.extd_number = util.get_dict_rev(self._get_special(), number) + mem.extd_number = util.get_dict_rev(_get_special(), number)
if _flg.empty: mem.empty = True @@ -190,11 +190,13 @@ return filedata[-16:] == "IcomCloneFormat3"
class ICW32ARadioVHF(ICW32ARadio): + """ICW32 VHF subdevice""" VARIANT = "VHF" _limits = (118000000, 174000000) _mem_positions = (0x0000, 0x0DC0)
class ICW32ARadioUHF(ICW32ARadio): + """ICW32 UHF subdevice""" VARIANT = "UHF" _limits = (400000000, 470000000) _mem_positions = (0x06E0, 0x0E2E) diff -r db82df47f205 -r f805c420592f chirp/icx8x.py --- a/chirp/icx8x.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/icx8x.py Fri Apr 27 14:35:41 2012 -0700 @@ -15,7 +15,7 @@
from chirp import chirp_common, icf, icx8x_ll, errors, directory
-def isUHF(pipe): +def _isuhf(pipe): try: md = icf.get_model_data(pipe) val = ord(md[20]) @@ -29,6 +29,7 @@
@directory.register class ICx8xRadio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom IC-V/U82""" VENDOR = "Icom" MODEL = "IC-V82/U82"
@@ -69,7 +70,7 @@ rf.valid_duplexes = ["", "-", "+"] rf.valid_tuning_steps = [x for x in chirp_common.TUNING_STEPS if x != 6.25] - if self.isUHF: + if self._isuhf: rf.valid_bands = [(420000000, 470000000)] else: rf.valid_bands = [(118000000, 176000000)] @@ -78,12 +79,12 @@ return rf
def _get_type(self): - flag = (isUHF(self.pipe) != 0) + flag = (_isuhf(self.pipe) != 0)
- if self.isUHF is not None and (self.isUHF != flag): + if self._isuhf is not None and (self._isuhf != flag): raise errors.RadioError("VHF/UHF model mismatch")
- self.isUHF = flag + self._isuhf = flag
return flag
@@ -95,15 +96,15 @@ # file, look for the flag. If we're syncing from serial, set # that flag. if isinstance(pipe, str): - self.isUHF = (ord(self._mmap[0x1930]) != 0) + self._isuhf = (ord(self._mmap[0x1930]) != 0) #print "Found %s image" % (self.isUHF and "UHF" or "VHF") else: - self.isUHF = None + self._isuhf = None
def sync_in(self): self._get_type() icf.IcomCloneModeRadio.sync_in(self) - self._mmap[0x1930] = self.isUHF and 1 or 0 + self._mmap[0x1930] = self._isuhf and 1 or 0
def sync_out(self): self._get_type() @@ -116,7 +117,7 @@ if not self._mmap: self.sync_in()
- if self.isUHF: + if self._isuhf: base = 400 else: base = 0 @@ -134,7 +135,7 @@ if not self._mmap: self.sync_in()
- if self.isUHF: + if self._isuhf: base = 400 else: base = 0 diff -r db82df47f205 -r f805c420592f chirp/id31.py --- a/chirp/id31.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/id31.py Fri Apr 27 14:35:41 2012 -0700 @@ -15,7 +15,7 @@
from chirp import directory, icf, bitwise, chirp_common
-mem_format = """ +MEM_FORMAT = """ struct { u24 freq; u16 offset; @@ -111,7 +111,6 @@
def _encode_call(call): _call = [0x00] * 7 - mask = 0 for i in range(0, 7): val = ord(call[i]) << (i + 1) if i > 0: @@ -121,7 +120,32 @@
return _call
+def _get_freq(_mem): + freq = int(_mem.freq) + offs = int(_mem.offset) + + if freq & 0x00200000: + mult = 6250 + else: + mult = 5000 + + freq &= 0x0003FFFF + + return (freq * mult), (offs * mult) + +def _set_freq(_mem, freq, offset): + if chirp_common.is_fractional_step(freq): + mult = 6250 + flag = 0x00200000 + else: + mult = 5000 + flag = 0x00000000 + + _mem.freq = (freq / mult) | flag + _mem.offset = (offset / mult) + class ID31Bank(icf.IcomBank): + """A ID-31 Bank""" def get_name(self): _banks = self._model._radio._memobj.bank_names return str(_banks[self.index].name).rstrip() @@ -132,6 +156,7 @@
@directory.register class ID31Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom ID-31""" MODEL = "ID-31A"
_memsize = 0x15500 @@ -179,35 +204,11 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number])
- def _get_freq(self, _mem): - freq = int(_mem.freq) - offs = int(_mem.offset) - - if freq & 0x00200000: - mult = 6250 - else: - mult = 5000 - - freq &= 0x0003FFFF - - return (freq * mult), (offs * mult) - - def _set_freq(self, _mem, freq, offset): - if chirp_common.is_fractional_step(freq): - mult = 6250 - flag = 0x00200000 - else: - mult = 5000 - flag = 0x00000000 - - _mem.freq = (freq / mult) | flag - _mem.offset = (offset / mult) - def get_memory(self, number): _mem = self._memobj.memory[number] _usd = self._memobj.used_flags[number / 8] @@ -226,7 +227,7 @@ mem.empty = True return mem
- mem.freq, mem.offset = self._get_freq(_mem) + mem.freq, mem.offset = _get_freq(_mem) mem.name = str(_mem.name).rstrip() mem.rtone = chirp_common.TONES[_mem.rtone] mem.ctone = chirp_common.TONES[_mem.ctone] @@ -261,7 +262,7 @@
bit = (1 << (memory.number % 8))
- self._set_freq(_mem, memory.freq, memory.offset) + _set_freq(_mem, memory.freq, memory.offset) _mem.name = memory.name.ljust(12)[:12] _mem.rtone = chirp_common.TONES.index(memory.rtone) _mem.ctone = chirp_common.TONES.index(memory.ctone) @@ -308,10 +309,8 @@
def get_repeater_call_list(self): calls = [] - i = 0 for rptcall in self._memobj.rptcall: call = _decode_call(rptcall.call) - i += 1 if call.rstrip() and not call == "CALLSIGN": calls.append(call) for repeater in self._memobj.repeaters: diff -r db82df47f205 -r f805c420592f chirp/id800.py --- a/chirp/id800.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/id800.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,7 +16,7 @@ from chirp import chirp_common, icf, errors, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ #seekto 0x0020; struct { u24 freq; @@ -104,8 +104,50 @@ ALPHA_CHARSET = " ABCDEFGHIJKLMNOPQRSTUVWXYZ" NUMERIC_CHARSET = "0123456789+-=*/()|"
+def get_name(_mem): + """Decode the name from @_mem""" + def _get_char(val): + if val == 0: + return " " + elif val & 0x20: + return ALPHA_CHARSET[val & 0x1F] + else: + return NUMERIC_CHARSET[val & 0x0F] + + name_bytes = [_mem.name1, _mem.name2, _mem.name3, + _mem.name4, _mem.name5, _mem.name6] + name = "" + for val in name_bytes: + name += _get_char(val) + + return name.rstrip() + +def set_name(_mem, name): + """Encode @name in @_mem""" + def _get_index(char): + if char == " ": + return 0 + elif char.isalpha(): + return ALPHA_CHARSET.index(char) | 0x20 + else: + return NUMERIC_CHARSET.index(char) | 0x10 + + name = name.ljust(6)[:6] + + _mem.usealpha = bool(name.strip()) + + # The element override calling convention makes this harder to automate. + # It's just six, so do it manually + _mem.name1 = _get_index(name[0]) + _mem.name2 = _get_index(name[1]) + _mem.name3 = _get_index(name[2]) + _mem.name4 = _get_index(name[3]) + _mem.name5 = _get_index(name[4]) + _mem.name6 = _get_index(name[5]) + @directory.register class ID800v2Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom ID800""" VENDOR = "Icom" MODEL = "ID-800H" VARIANT = "v2" @@ -186,50 +228,11 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_special_locations(self): return sorted(ID800_SPECIAL.keys())
- def _get_name(self, _mem): - def get_char(val): - if val == 0: - return " " - elif val & 0x20: - return ALPHA_CHARSET[val & 0x1F] - else: - return NUMERIC_CHARSET[val & 0x0F] - - name_bytes = [_mem.name1, _mem.name2, _mem.name3, - _mem.name4, _mem.name5, _mem.name6] - name = "" - for val in name_bytes: - name += get_char(val) - - return name.rstrip() - - def _set_name(self, _mem, name): - def get_index(char): - if char == " ": - return 0 - elif char.isalpha(): - return ALPHA_CHARSET.index(char) | 0x20 - else: - return NUMERIC_CHARSET.index(char) | 0x10 - - name = name.ljust(6)[:6] - - _mem.usealpha = bool(name.strip()) - - # The element override calling convention makes this harder to automate. - # It's just six, so do it manually - _mem.name1 = get_index(name[0]) - _mem.name2 = get_index(name[1]) - _mem.name3 = get_index(name[2]) - _mem.name4 = get_index(name[3]) - _mem.name5 = get_index(name[4]) - _mem.name6 = get_index(name[5]) - def get_memory(self, number): if isinstance(number, str): try: @@ -268,7 +271,7 @@ mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs] mem.dtcs_polarity = DTCS_POL[_mem.dtcs_polarity] mem.tuning_step = STEPS[_mem.tuning_step] - mem.name = self._get_name(_mem) + mem.name = get_name(_mem)
mem.skip = _flg.pskip and "P" or _flg.skip and "S" or ""
@@ -295,7 +298,7 @@ _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs) _mem.dtcs_polarity = DTCS_POL.index(mem.dtcs_polarity) _mem.tuning_step = STEPS.index(mem.tuning_step) - self._set_name(_mem, mem.name) + set_name(_mem, mem.name)
_flg.pskip = mem.skip == "P" _flg.skip = mem.skip == "S" diff -r db82df47f205 -r f805c420592f chirp/id880.py --- a/chirp/id880.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/id880.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,7 +16,7 @@ from chirp import chirp_common, icf, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ struct { u24 freq; u16 offset; @@ -86,12 +86,13 @@ 100.0, 125.0, 200.0]
def decode_call(sevenbytes): + """Decode a callsign from a packed region @sevenbytes""" if len(sevenbytes) != 7: raise Exception("%i (!=7) bytes to decode_call" % len(sevenbytes))
i = 0 rem = 0 - str = "" + call = "" for byte in [ord(x) for x in sevenbytes]: i += 1
@@ -100,7 +101,7 @@ code = (byte >> i) | rem # Code gets the upper bits of remainder # plus all but the i lower bits of this # byte - str += chr(code) + call += chr(code)
rem = (byte & mask) << 7 - i # Remainder for next time are the masked # bits, moved to the high places for the @@ -108,14 +109,13 @@
# After seven trips gathering overflow bits, we chould have seven # left, which is the final character - str += chr(rem) + call += chr(rem)
- return str.rstrip() + return call.rstrip()
def encode_call(call): + """Encode @call into a 7-byte region""" call = call.ljust(8) - val = 0 - buf = []
for i in range(0, 8): @@ -132,7 +132,33 @@
return "".join([chr(x) for x in buf[:7]])
-class ID880Bank(icf.IcomBank): +def _get_freq(_mem): + val = int(_mem.freq) + + if val & 0x00200000: + mult = 6250 + else: + mult = 5000 + + val &= 0x0003FFFF + + return (val * mult) + +def _set_freq(_mem, freq): + if chirp_common.is_fractional_step(freq): + mult = 6250 + flag = 0x00200000 + else: + mult = 5000 + flag = 0x00000000 + + _mem.freq = (freq / mult) | flag + +def _wipe_memory(mem, char): + mem.set_raw(char * (mem.size() / 8)) + +class ID880Bank(icf.IcomNamedBank): + """ID880 Bank""" def get_name(self): _bank = self._model._radio._memobj.bank_names[self.index] return str(_bank.name).rstrip() @@ -143,6 +169,7 @@
@directory.register class ID880Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport): + """Icom ID880""" VENDOR = "Icom" MODEL = "ID-880H"
@@ -185,7 +212,7 @@ _bank.index = index
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -208,28 +235,6 @@ def get_raw_memory(self, number): return repr(self._memobj.memory[number])
- def _get_freq(self, _mem): - val = int(_mem.freq) - - if val & 0x00200000: - mult = 6250 - else: - mult = 5000 - - val &= 0x0003FFFF - - return (val * mult) - - def _set_freq(self, _mem, freq): - if chirp_common.is_fractional_step(freq): - mult = 6250 - flag = 0x00200000 - else: - mult = 5000 - flag = 0x00000000 - - _mem.freq = (freq / mult) | flag - def get_memory(self, number): bytepos = number / 8 bitpos = 1 << (number % 8) @@ -263,7 +268,7 @@ mem.empty = True return mem
- mem.freq = self._get_freq(_mem) + mem.freq = _get_freq(_mem) mem.offset = (_mem.offset * 5) * 1000 mem.rtone = chirp_common.TONES[_mem.rtone] mem.ctone = chirp_common.TONES[_mem.ctone] @@ -280,9 +285,6 @@
return mem
- def _wipe_memory(self, mem, char): - mem.set_raw(char * (mem.size() / 8)) - def set_memory(self, mem): bitpos = (1 << (mem.number % 8)) bytepos = mem.number / 8 @@ -295,16 +297,16 @@
if mem.empty: _used |= bitpos - self._wipe_memory(_mem, "\xFF") + _wipe_memory(_mem, "\xFF") self._set_bank(mem.number, None) return
_used &= ~bitpos
if was_empty: - self._wipe_memory(_mem, "\x00") + _wipe_memory(_mem, "\x00")
- self._set_freq(_mem, mem.freq) + _set_freq(_mem, mem.freq) _mem.offset = int((mem.offset / 1000) / 5) _mem.rtone = chirp_common.TONES.index(mem.rtone) _mem.ctone = chirp_common.TONES.index(mem.ctone) @@ -359,7 +361,7 @@ _calls = self._memobj.rptcall calls = ["*NOTUSE*"]
- for i in range(*self.RPTCALL_LIMIT): + for _i in range(*self.RPTCALL_LIMIT): # FIXME: Not sure where the repeater list actually is calls.append("UNSUPRTD") continue @@ -370,6 +372,7 @@ # the ID-880. So, don't register right now #@directory.register class ID80Radio(ID880Radio): + """Icom ID80""" MODEL = "ID-80H"
_model = "\x31\x55\x00\x01" diff -r db82df47f205 -r f805c420592f chirp/idrp.py --- a/chirp/idrp.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/idrp.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,16 +13,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-import struct import serial
from chirp import chirp_common, errors -from chirp.memmap import MemoryMap from chirp import util
DEBUG_IDRP = False
def parse_frames(buf): + """Parse frames from the radio""" frames = []
while "\xfe\xfe" in buf: @@ -38,8 +37,9 @@
return frames
-def send(pipe, buffer): - pipe.write("\xfe\xfe%s\xfd" % buffer) +def send(pipe, buf): + """Send data in @buf to @pipe""" + pipe.write("\xfe\xfe%s\xfd" % buf) pipe.flush()
data = "" @@ -55,15 +55,18 @@ return parse_frames(data)
def send_magic(pipe): + """Send the magic wakeup call to @pipe""" send(pipe, ("\xfe" * 15) + "\x01\x7f\x19")
def drain(pipe): + """Chew up any data waiting on @pipe""" while True: buf = pipe.read(4096) if not buf: break
def set_freq(pipe, freq): + """Set the frequency of the radio on @pipe to @freq""" freqbcd = util.bcd_encode(freq, bigendian=False, width=9) buf = "\x01\x7f\x05" + freqbcd
@@ -78,6 +81,7 @@ raise errors.InvalidDataError("Repeater reported error")
def get_freq(pipe): + """Get the frequency of the radio attached to @pipe""" buf = "\x01\x7f\x1a\x09"
drain(pipe) @@ -103,10 +107,8 @@ "ctone", "dtcs", "tmode", "dtcs_polarity", "skip", "duplex", "offset", "mode", "tuning_step", "bank_index"]
-class IcomRepeater(chirp_common.LiveRadio): - pass - -class IDRPx000V(IcomRepeater): +class IDRPx000V(chirp_common.LiveRadio): + """Icom IDRP-*""" BAUD_RATE = 19200 VENDOR = "Icom" MODEL = "ID-2000V/4000V/2D/2V" @@ -154,7 +156,11 @@
set_freq(self.pipe, mem.freq)
+def do_test(): + """Get the frequency of /dev/icom""" + ser = serial.Serial(port="/dev/icom", baudrate=19200, timeout=0.5) + #set_freq(pipe, 439.920) + get_freq(ser) + if __name__ == "__main__": - pipe = serial.Serial(port="/dev/icom", baudrate=19200, timeout=0.5) - #set_freq(pipe, 439.920) - get_freq(pipe) + do_test() diff -r db82df47f205 -r f805c420592f chirp/import_logic.py --- a/chirp/import_logic.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/import_logic.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,12 +16,15 @@ from chirp import chirp_common, errors
class ImportError(Exception): + """An import error""" pass
class DestNotCompatible(ImportError): + """Memory is not compatible with the destination radio""" pass
def ensure_has_calls(radio, memory): + """Make sure @radio has the necessary D-STAR callsigns for @memory""" ulist_changed = rlist_changed = False
ulist = radio.get_urcall_list() @@ -59,10 +62,10 @@ radio.set_repeater_cal_list(rlist)
# Filter the name according to the destination's rules -def _import_name(dst_radio, srcrf, mem): +def _import_name(dst_radio, _srcrf, mem): mem.name = dst_radio.filter_name(mem.name)
-def _import_power(dst_radio, srcrf, mem): +def _import_power(dst_radio, _srcrf, mem): levels = dst_radio.get_features().valid_power_levels if not levels: mem.power = None @@ -101,6 +104,8 @@ mem.ctone = mem.rtone
def import_mem(dst_radio, src_features, src_mem, overrides={}): + """Perform import logic to create a destination memory from + src_mem that will be compatible with @dst_radio""" dst_rf = dst_radio.get_features()
if isinstance(src_mem, chirp_common.DVMemory): @@ -123,10 +128,10 @@ dst_mem.__dict__[k] = v
msgs = dst_radio.validate_memory(dst_mem) - errors = [x for x in msgs if isinstance(x, chirp_common.ValidationError)] - if errors: + errs = [x for x in msgs if isinstance(x, chirp_common.ValidationError)] + if errs: raise DestNotCompatible("Unable to create import memory: %s" %\ - ", ".join(errors)) + ", ".join(errs))
return dst_mem
diff -r db82df47f205 -r f805c420592f chirp/kenwood_hmk.py --- a/chirp/kenwood_hmk.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/kenwood_hmk.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,16 +14,17 @@ # 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 csv
from chirp import chirp_common, errors, directory, generic_csv
class OmittedHeaderError(Exception): + """An internal exception to indicate that a header was omitted""" pass
@directory.register class HMKRadio(generic_csv.CSVRadio): + """Kenwood HMK format""" VENDOR = "Kenwood" MODEL = "HMK" FILE_EXTENSION = "hmk" diff -r db82df47f205 -r f805c420592f chirp/kenwood_live.py --- a/chirp/kenwood_live.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/kenwood_live.py Fri Apr 27 14:35:41 2012 -0700 @@ -23,8 +23,10 @@ import sys sys.path.insert(0, "..")
-from chirp import chirp_common, errors, directory -from chirp.settings import * +from chirp import chirp_common, errors, directory, util +from chirp.settings import RadioSetting, RadioSettingGroup, \ + RadioSettingValueInteger, RadioSettingValueBoolean, \ + RadioSettingValueString, RadioSettingValueList
DEBUG = True
@@ -35,31 +37,24 @@
THF6_MODES = ["FM", "WFM", "AM", "LSB", "USB", "CW"]
-def rev(hash, value): - reverse = {} - for k, v in hash.items(): - reverse[v] = k - - return reverse[value] - LOCK = threading.Lock()
-def command(s, command, *args): +def command(ser, cmd, *args): + """Send @cmd to radio via @ser""" global LOCK
start = time.time()
LOCK.acquire() - cmd = command if args: cmd += " " + " ".join(args) if DEBUG: print "PC->RADIO: %s" % cmd - s.write(cmd + "\r") + ser.write(cmd + "\r")
result = "" while not result.endswith("\r"): - result += s.read(8) + result += ser.read(8) if (time.time() - start) > 0.5: print "Timeout waiting for data" break @@ -72,7 +67,8 @@ return result.strip()
LAST_BAUD = 9600 -def get_id(s): +def get_id(ser): + """Get the ID of the radio attached to @ser""" global LAST_BAUD bauds = [9600, 19200, 38400, 57600] bauds.remove(LAST_BAUD) @@ -80,17 +76,18 @@
for i in bauds: print "Trying ID at baud %i" % i - s.setBaudrate(i) - s.write("\r") - s.read(25) - r = command(s, "ID") - if " " in r: + ser.setBaudrate(i) + ser.write("\r") + ser.read(25) + resp = command(ser, "ID") + if " " in resp: LAST_BAUD = i - return r.split(" ")[1] + return resp.split(" ")[1]
raise errors.RadioError("No response from radio")
def get_tmode(tone, ctcss, dcs): + """Get the tone mode based on the values of the tone, ctcss, dcs""" if dcs and int(dcs) == 1: return "DTCS" elif int(ctcss): @@ -101,9 +98,11 @@ return ""
def iserr(result): + """Returns True if the @result from a radio is an error""" return result in ["N", "?"]
class KenwoodLiveRadio(chirp_common.LiveRadio): + """Base class for all live-mode kenwood radios""" BAUD_RATE = 9600 VENDOR = "Kenwood" MODEL = "" @@ -178,9 +177,9 @@ if " " in result: value = result.split(" ", 1)[1] if value.count(",") == 2: - zero, loc, mem.name = value.split(",") + _zero, _loc, mem.name = value.split(",") else: - loc, mem.name = value.split(",") + _loc, mem.name = value.split(",")
if mem.duplex == "" and self._kenwood_split: result = command(self.pipe, *self._cmd_get_split(number)) @@ -212,7 +211,6 @@ spec = ",".join(spec) r1 = command(self.pipe, *self._cmd_set_memory(memory.number, spec)) if not iserr(r1): - import time time.sleep(0.5) r2 = command(self.pipe, *self._cmd_set_memory_name(memory.number, memory.name)) @@ -238,8 +236,8 @@ if not self.__memcache.has_key(number): return
- r = command(self.pipe, *self._cmd_set_memory(number, "")) - if iserr(r): + resp = command(self.pipe, *self._cmd_set_memory(number, "")) + if iserr(resp): raise errors.RadioError("Radio refused delete of %i" % number) del self.__memcache[number]
@@ -271,6 +269,7 @@
@directory.register class THD7Radio(KenwoodLiveRadio): + """Kenwood TH-D7""" MODEL = "TH-D7"
_kenwood_split = True @@ -294,7 +293,7 @@
def _make_mem_spec(self, mem): if mem.duplex in " -+": - duplex = rev(DUPLEX, mem.duplex) + duplex = util.get_dict_rev(DUPLEX, mem.duplex) offset = mem.offset else: duplex = 0 @@ -312,7 +311,7 @@ "", # DCS Code "%02i" % (self._kenwood_valid_tones.index(mem.ctone) + 1), "%09i" % offset, - "%i" % rev(MODES, mem.mode), + "%i" % util.get_dict_rev(MODES, mem.mode), "%i" % ((mem.skip == "S") and 1 or 0))
return spec @@ -341,15 +340,15 @@ return mem
def _kenwood_get(self, cmd): - r = command(self.pipe, cmd) - if " " in r: - return r.split(" ", 1) + resp = command(self.pipe, cmd) + if " " in resp: + return resp.split(" ", 1) else: raise errors.RadioError("Radio refused to return %s" % cmd)
def _kenwood_set(self, cmd, value): - r = command(self.pipe, cmd, value) - if " " in r: + resp = command(self.pipe, cmd, value) + if " " in resp: return raise errors.RadioError("Radio refused to set %s" % cmd)
@@ -377,7 +376,7 @@ aux, tnc, save, display, dtmf) sky = RadioSettingGroup("sky", "SkyCommand") aprs = RadioSettingGroup("aprs", "APRS") - all = RadioSettingGroup("top", "All Settings", radio, aprs, sky) + top = RadioSettingGroup("top", "All Settings", radio, aprs, sky)
bools = [("AMR", aprs, "APRS Message Auto-Reply"), ("AIP", aux, "Advanced Intercept Point"), @@ -394,9 +393,9 @@
for setting, group, name in bools: value = self._kenwood_get_bool(setting) - s = RadioSetting(setting, name, - RadioSettingValueBoolean(value)) - group.append(s) + rs = RadioSetting(setting, name, + RadioSettingValueBoolean(value)) + group.append(rs)
lists = [("BAL", all, "Balance"), ("BEP", aux, "Beep"), @@ -420,18 +419,18 @@ for setting, group, name in lists: value = self._kenwood_get_int(setting) options = TH_D7_SETTINGS[setting] - s = RadioSetting(setting, name, - RadioSettingValueList(options, - options[value])) - group.append(s) + rs = RadioSetting(setting, name, + RadioSettingValueList(options, + options[value])) + group.append(rs)
ints = [("CNT", display, "Contrast", 1, 16), ] - for setting, group, name, min, max in ints: + for setting, group, name, minv, maxv in ints: value = self._kenwood_get_int(setting) - s = RadioSetting(setting, name, - RadioSettingValueInteger(min, max, value)) - group.append(s) + rs = RadioSetting(setting, name, + RadioSettingValueInteger(minv, maxv, value)) + group.append(rs)
strings = [("MES", display, "Power-on Message", 8), ("MYC", aprs, "APRS Callsign", 8), @@ -442,11 +441,11 @@ ] for setting, group, name, length in strings: _cmd, value = self._kenwood_get(setting) - s = RadioSetting(setting, name, - RadioSettingValueString(0, length, value)) - group.append(s) + rs = RadioSetting(setting, name, + RadioSettingValueString(0, length, value)) + group.append(rs)
- return all + return top
def set_settings(self, settings): for element in settings: @@ -471,10 +470,12 @@
@directory.register class THD7GRadio(THD7Radio): + """Kenwood TH-D7G""" MODEL = "TH-D7G"
@directory.register class TMD700Radio(KenwoodLiveRadio): + """Kenwood TH-D700""" MODEL = "TM-D700"
_kenwood_split = True @@ -497,7 +498,7 @@
def _make_mem_spec(self, mem): if mem.duplex in " -+": - duplex = rev(DUPLEX, mem.duplex) + duplex = util.get_dict_rev(DUPLEX, mem.duplex) else: duplex = 0 spec = ( \ @@ -512,7 +513,7 @@ "%03i0" % (chirp_common.DTCS_CODES.index(mem.dtcs) + 1), "%02i" % (self._kenwood_valid_tones.index(mem.ctone) + 1), "%09i" % mem.offset, - "%i" % rev(MODES, mem.mode), + "%i" % util.get_dict_rev(MODES, mem.mode), "%i" % ((mem.skip == "S") and 1 or 0))
return spec @@ -555,6 +556,7 @@
@directory.register class TMV7Radio(KenwoodLiveRadio): + """Kenwood TM-V7""" MODEL = "TM-V7"
mem_upper_limit = 200 # Will be updated @@ -592,7 +594,7 @@ spec = ( \ "%011i" % mem.freq, "%X" % STEPS.index(mem.tuning_step), - "%i" % rev(DUPLEX, mem.duplex), + "%i" % util.get_dict_rev(DUPLEX, mem.duplex), "0", "%i" % (mem.tmode == "Tone"), "%i" % (mem.tmode == "TSQL"), @@ -637,14 +639,14 @@ mem.empty = False try: self.set_memory(mem) - except: + except Exception: # Failed, so we're past the limit return False
# Erase what we did try: self.erase_memory(loc) - except: + except Exception: pass # V7A Can't delete just yet
return True @@ -653,20 +655,24 @@ return 50
class TMV7RadioSub(TMV7Radio): + """Base class for the TM-V7 sub devices""" def __init__(self, pipe): - KenwoodLiveRadio.__init__(self, pipe) + TMV7Radio.__init__(self, pipe) self._detect_split()
class TMV7RadioVHF(TMV7RadioSub): + """TM-V7 VHF subdevice""" VARIANT = "VHF" _vfo = 0
class TMV7RadioUHF(TMV7RadioSub): + """TM-V7 UHF subdevice""" VARIANT = "UHF" _vfo = 1
@directory.register class TMG707Radio(TMV7Radio): + """Kenwood TM-G707""" MODEL = "TM-G707"
def get_features(self): @@ -677,13 +683,15 @@ (430000000, 450000000)] return rf
-THF6A_STEPS = [5.0, 6.25, 8.33, 9.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0, 100.0] +THF6A_STEPS = [5.0, 6.25, 8.33, 9.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0, + 100.0]
THF6A_DUPLEX = dict(DUPLEX) THF6A_DUPLEX[3] = "split"
@directory.register class THF6ARadio(KenwoodLiveRadio): + """Kenwood TH-F6""" MODEL = "TH-F6"
_upper = 399 @@ -751,7 +759,7 @@
def _make_mem_spec(self, mem): if mem.duplex in " +-": - duplex = rev(THF6A_DUPLEX, mem.duplex) + duplex = util.get_dict_rev(THF6A_DUPLEX, mem.duplex) offset = mem.offset elif mem.duplex == "split": duplex = 0 @@ -777,6 +785,7 @@
@directory.register class THF7ERadio(THF6ARadio): + """Kenwood TH-F7""" MODEL = "TH-F7"
D710_DUPLEX = ["", "+", "-", "split"] @@ -786,6 +795,7 @@
@directory.register class TMD710Radio(KenwoodLiveRadio): + """Kenwood TM-D710""" MODEL = "TM-D710"
_upper = 999 @@ -846,11 +856,11 @@ return mem
def _make_mem_spec(self, mem): - print "Index %i for step %.2f" % (chirp_common.TUNING_STEPS.index(mem.tuning_step), mem.tuning_step) spec = ( \ "%010i" % mem.freq, "%X" % D710_STEPS.index(mem.tuning_step), - "%i" % (0 if mem.duplex == "split" else D710_DUPLEX.index(mem.duplex)), + "%i" % (0 if mem.duplex == "split" else \ + D710_DUPLEX.index(mem.duplex)), "0", # Reverse "%i" % (mem.tmode == "Tone" and 1 or 0), "%i" % (mem.tmode == "TSQL" and 1 or 0), @@ -860,7 +870,7 @@ "%03i" % (chirp_common.DTCS_CODES.index(mem.dtcs)), "%08i" % (0 if mem.duplex == "split" else mem.offset), # Offset "%i" % D710_MODES.index(mem.mode), - "%010i" % (mem.offset if mem.duplex == "split" else 0), # TX Frequency + "%010i" % (mem.offset if mem.duplex == "split" else 0), # TX Freq "0", # Unknown "%i" % D710_SKIP.index(mem.skip), # Memory Lockout ) @@ -869,6 +879,7 @@
@directory.register class THD72Radio(TMD710Radio): + """Kenwood TH-D72""" MODEL = "TH-D72" HARDWARE_FLOW = True
@@ -901,11 +912,11 @@ return mem
def _make_mem_spec(self, mem): - print "Index %i for step %.2f" % (chirp_common.TUNING_STEPS.index(mem.tuning_step), mem.tuning_step) spec = ( \ "%010i" % mem.freq, "%X" % D710_STEPS.index(mem.tuning_step), - "%i" % (0 if mem.duplex == "split" else D710_DUPLEX.index(mem.duplex)), + "%i" % (0 if mem.duplex == "split" else \ + D710_DUPLEX.index(mem.duplex)), "0", # Reverse "%i" % (mem.tmode == "Tone" and 1 or 0), "%i" % (mem.tmode == "TSQL" and 1 or 0), @@ -917,7 +928,7 @@ "0", "%08i" % (0 if mem.duplex == "split" else mem.offset), # Offset "%i" % D710_MODES.index(mem.mode), - "%010i" % (mem.offset if mem.duplex == "split" else 0), # TX Frequency + "%010i" % (mem.offset if mem.duplex == "split" else 0), # TX Freq "0", # Unknown "%i" % D710_SKIP.index(mem.skip), # Memory Lockout ) @@ -926,7 +937,8 @@
@directory.register class TMV71Radio(TMD710Radio): - MODEL = "TM-V71" + """Kenwood TM-V71""" + MODEL = "TM-V71"
THK2_DUPLEX = ["", "+", "-"] THK2_MODES = ["FM", "NFM"] @@ -944,6 +956,7 @@
@directory.register class THK2Radio(KenwoodLiveRadio): + """Kenwood TH-K2""" MODEL = "TH-K2"
_kenwood_valid_tones = list(THK2_TONES) @@ -1028,6 +1041,7 @@
@directory.register class TM271Radio(THK2Radio): + """Kenwood TM-271""" MODEL = "TM-271"
def get_features(self): @@ -1059,26 +1073,35 @@ def _cmd_set_memory_name(self, number, name): return "MN", "%03i,%s" % (number, name)
-if __name__ == "__main__": - m = chirp_common.Memory() - m.number = 1 - m.freq = 144000000 - m.duplex = "split" - m.offset = 146000000 +def do_test(): + """Dev test""" + mem = chirp_common.Memory() + mem.number = 1 + mem.freq = 144000000 + mem.duplex = "split" + mem.offset = 146000000
- TestClass = THF6ARadio + tc = THF6ARadio class FakeSerial: + """Faked serial line""" buf = "" def write(self, buf): + """Write""" self.buf = buf def read(self, count): + """Read""" if self.buf[:2] == "ID": return "ID %s\r" % TestClass.MODEL return self.buf def setTimeout(self, foo): + """Set Timeout""" pass def setBaudrate(self, foo): + """Set Baudrate""" pass
- r = TestClass(FakeSerial()) - r.set_memory(m) + radio = tc(FakeSerial()) + radio.set_memory(mem) + +if __name__ == "__main__": + do_test() diff -r db82df47f205 -r f805c420592f chirp/memmap.py --- a/chirp/memmap.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/memmap.py Fri Apr 27 14:35:41 2012 -0700 @@ -24,6 +24,7 @@ self._data = list(data)
def printable(self, start=None, end=None, printit=True): + """Return a printable representation of the memory map""" if not start: start = 0
@@ -38,12 +39,14 @@ return string
def get(self, start, length=1): + """Return a chunk of memory of @length bytes from @start""" if start == -1: return "".join(self._data[start:]) else: return "".join(self._data[start:start+length])
def set(self, pos, value): + """Set a chunk of memory at @pos to @value""" if isinstance(value, int): self._data[pos] = chr(value) elif isinstance(value, str): @@ -55,6 +58,7 @@ type(value).__name__)
def get_packed(self): + """Return the entire memory map as raw data""" return "".join(self._data)
def __len__(self): @@ -80,4 +84,5 @@ return self.printable(printit=False)
def truncate(self, size): + """Truncate the memory map to @size""" self._data = self._data[:size] diff -r db82df47f205 -r f805c420592f chirp/platform.py --- a/chirp/platform.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/platform.py Fri Apr 27 14:35:41 2012 -0700 @@ -1,19 +1,3 @@ -# Copyright 2008 Dan Smith dsmith@danplanet.com -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. - -# # Copyright 2008 Dan Smith dsmith@danplanet.com # # This program is free software: you can redistribute it and/or modify @@ -34,26 +18,30 @@ import glob from subprocess import Popen
-def find_me(): +def _find_me(): return sys.modules["chirp.platform"].__file__
class Platform: - # pylint: disable-msg=R0201 + """Base class for platform-specific functions"""
def __init__(self, basepath): self._base = basepath self._last_dir = self.default_dir()
def get_last_dir(self): + """Return the last directory used""" return self._last_dir
def set_last_dir(self, last_dir): + """Set the last directory used""" self._last_dir = last_dir
def config_dir(self): + """Return the preferred configuration file directory""" return self._base
def log_dir(self): + """Return the preferred log file directory""" logdir = os.path.join(self.config_dir(), "logs") if not os.path.isdir(logdir): os.mkdir(logdir) @@ -61,29 +49,37 @@ return logdir
def filter_filename(self, filename): + """Filter @filename for platform-forbidden characters""" return filename
def log_file(self, filename): + """Return the full path to a log file with @filename""" filename = self.filter_filename(filename + ".txt").replace(" ", "_") return os.path.join(self.log_dir(), filename)
def config_file(self, filename): + """Return the full path to a config file with @filename""" return os.path.join(self.config_dir(), self.filter_filename(filename))
def open_text_file(self, path): + """Spawn the necessary program to open a text file at @path""" raise NotImplementedError("The base class can't do that")
def open_html_file(self, path): + """Spawn the necessary program to open an HTML file at @path""" raise NotImplementedError("The base class can't do that")
def list_serial_ports(self): + """Return a list of valid serial ports""" return []
def default_dir(self): + """Return the default directory for this platform""" return "."
def gui_open_file(self, start_dir=None, types=[]): + """Prompt the user to pick a file to open""" import gtk
if not start_dir: @@ -114,6 +110,7 @@ return None
def gui_save_file(self, start_dir=None, default_name=None, types=[]): + """Prompt the user to pick a filename to save""" import gtk
if not start_dir: @@ -154,6 +151,7 @@ return None
def gui_select_dir(self, start_dir=None): + """Prompt the user to pick a directory""" import gtk
if not start_dir: @@ -178,9 +176,11 @@ return None
def os_version_string(self): + """Return a string that describes the OS/platform version""" return "Unknown Operating System"
def executable_path(self): + """Return a full path to the program executable""" def we_are_frozen(): return hasattr(sys, "frozen")
@@ -190,7 +190,8 @@ sys.getfilesystemencoding())) else: # UNIX: Find the parent directory of this module - return os.path.dirname(os.path.abspath(os.path.join(find_me(), ".."))) + return os.path.dirname(os.path.abspath(os.path.join(_find_me(), + "..")))
def _unix_editor(): macos_textedit = "/Applications/TextEdit.app/Contents/MacOS/TextEdit" @@ -201,6 +202,7 @@ return "gedit"
class UnixPlatform(Platform): + """A platform module suitable for UNIX systems""" def __init__(self, basepath): if not basepath: basepath = os.path.abspath(os.path.join(self.default_dir(), @@ -214,7 +216,7 @@ # This is a hack that needs to be properly fixed by importing the # latest changes to this module from d-rats. In the interest of # time, however, I'll throw it here - if sys.platform == "darwin": + if sys.platform == "darwin": if not os.environ.has_key("DISPLAY"): print "Forcing DISPLAY for MacOS" os.environ["DISPLAY"] = ":0" @@ -252,7 +254,6 @@ glob.glob("/dev/tty.KeySerial*"))
def os_version_string(self): - # pylint: disable-msg=W0703 try: issue = file("/etc/issue.net", "r") ver = issue.read().strip().replace("\r", "").replace("\n", "")[:64] @@ -264,6 +265,7 @@ return ver
class Win32Platform(Platform): + """A platform module suitable for Windows systems""" def __init__(self, basepath=None): if not basepath: appdata = os.getenv("APPDATA") @@ -319,7 +321,6 @@ return ports
def gui_open_file(self, start_dir=None, types=[]): - # pylint: disable-msg=W0703,W0613 import win32gui
typestrs = "" @@ -337,11 +338,10 @@ return str(fname)
def gui_save_file(self, start_dir=None, default_name=None, types=[]): - # pylint: disable-msg=W0703,W0613 import win32gui import win32api
- (pform, _, build, _, _) = win32api.GetVersionEx() + (pform, _, _, _, _) = win32api.GetVersionEx()
typestrs = "" custom = "%s\0*.%s\0" % (types[0][0], types[0][1]) @@ -358,10 +358,10 @@
def_ext = "*.%s" % types[0][1] try: - fname, filter, _ = win32gui.GetSaveFileNameW(File=default_name, - CustomFilter=custom, - DefExt=def_ext, - Filter=typestrs) + fname, _, _ = win32gui.GetSaveFileNameW(File=default_name, + CustomFilter=custom, + DefExt=def_ext, + Filter=typestrs) except Exception, e: print "Failed to get filename: %s" % e return None @@ -369,7 +369,6 @@ return str(fname)
def gui_select_dir(self, start_dir=None): - # pylint: disable-msg=W0703,W0613 from win32com.shell import shell
try: @@ -401,8 +400,7 @@
PLATFORM = None def get_platform(basepath=None): - #pylint: disable-msg=W0602 - + """Return the platform singleton""" global PLATFORM
if not PLATFORM: @@ -410,19 +408,19 @@
return PLATFORM
+def _do_test(): + __pform = get_platform() + + print "Config dir: %s" % __pform.config_dir() + print "Default dir: %s" % __pform.default_dir() + print "Log file (foo): %s" % __pform.log_file("foo") + print "Serial ports: %s" % __pform.list_serial_ports() + print "OS Version: %s" % __pform.os_version_string() + #__pform.open_text_file("d-rats.py") + + #print "Open file: %s" % __pform.gui_open_file() + #print "Save file: %s" % __pform.gui_save_file(default_name="Foo.txt") + print "Open folder: %s" % __pform.gui_select_dir("/tmp") + if __name__ == "__main__": - def do_test(): - __pform = get_platform() - - print "Config dir: %s" % __pform.config_dir() - print "Default dir: %s" % __pform.default_dir() - print "Log file (foo): %s" % __pform.log_file("foo") - print "Serial ports: %s" % __pform.list_serial_ports() - print "OS Version: %s" % __pform.os_version_string() - #__pform.open_text_file("d-rats.py") - - #print "Open file: %s" % __pform.gui_open_file() - #print "Save file: %s" % __pform.gui_save_file(default_name="Foo.txt") - print "Open folder: %s" % __pform.gui_select_dir("/tmp") - - do_test() + _do_test() diff -r db82df47f205 -r f805c420592f chirp/radioreference.py --- a/chirp/radioreference.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/radioreference.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, CHIRP_VERSION, errors +from chirp import chirp_common, errors try: from suds.client import Client HAVE_SUDS = True @@ -31,6 +31,7 @@ }
class RadioReferenceRadio(chirp_common.NetworkSourceRadio): + """RadioReference.com data source""" VENDOR = "Radio Reference LLC" MODEL = "RadioReference.com"
@@ -38,7 +39,7 @@ APPKEY = "46785108"
def __init__(self, *args, **kwargs): - chirp_common.Radio.__init__(self, *args, **kwargs) + chirp_common.NetworkSourceRadio.__init__(self, *args, **kwargs)
if not HAVE_SUDS: raise errors.RadioError( @@ -49,9 +50,11 @@ self._client = Client(self.URL) self._freqs = None self._modes = None + self._zip = None
- def set_params(self, zip, username, password): - self._zip = zip + def set_params(self, zipcode, username, password): + """Set the parameters to be used for a query""" + self._zip = zipcode self._auth["username"] = username self._auth["password"] = password
@@ -72,7 +75,8 @@ print "Fetching category:", cat.cName for subcat in cat.subcats: print "\t", subcat.scName - result = self._client.service.getSubcatFreqs(subcat.scid, self._auth) + result = self._client.service.getSubcatFreqs(subcat.scid, + self._auth) self._freqs += result status.cur += 1 self.status_fn(status) @@ -85,7 +89,8 @@ print "Fetching category:", cat.cName for subcat in cat.subcats: print "\t", subcat.scName - result = self._client.service.getSubcatFreqs(subcat.scid, self._auth) + result = self._client.service.getSubcatFreqs(subcat.scid, + self._auth) self._freqs += result status.cur += 1 self.status_fn(status) @@ -126,7 +131,7 @@ else: try: tone, tmode = freq.tone.split(" ") - except: + except Exception: tone, tmode = None, None if tmode == "PL": mem.tmode = "TSQL" @@ -164,7 +169,9 @@ """ import sys rrr = RadioReferenceRadio(None) - rrr.set_params(zip=sys.argv[1], username=sys.argv[2], password=sys.argv[3]) + rrr.set_params(zipcode=sys.argv[1], + username=sys.argv[2], + password=sys.argv[3]) rrr.do_fetch() print rrr.get_raw_memory(0) print rrr.get_memory(0) diff -r db82df47f205 -r f805c420592f chirp/rfinder.py --- a/chirp/rfinder.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/rfinder.py Fri Apr 27 14:35:41 2012 -0700 @@ -17,7 +17,7 @@ import hashlib import re
-from math import pi,cos,acos,sin,atan2 +from math import pi, cos, acos, sin, atan2
from chirp import chirp_common, CHIRP_VERSION
@@ -48,46 +48,55 @@ ]
def deg2rad(deg): + """Convert degrees to radians""" return deg * (pi / 180)
def rad2deg(rad): + """Convert radians to degrees""" return rad / (pi / 180)
-def dm2deg(deg, min): - return deg + (min / 60.0) +def dm2deg(degrees, minutes): + """Convert degrees and minutes to decimal degrees""" + return degrees + (minutes / 60.0)
def deg2dm(decdeg): - deg = int(decdeg) - min = (decdeg - deg) * 60.0 + """Convert decimal degrees to degrees and minutes""" + degrees = int(decdeg) + minutes = (decdeg - degrees) * 60.0
- return deg, min + return degrees, minutes
-def nmea2deg(nmea, dir="N"): +def nmea2deg(nmea, direction="N"): + """Convert NMEA-encoded value to float""" deg = int(nmea) / 100 try: - min = nmea % (deg * 100) - except ZeroDivisionError, e: - min = int(nmea) + minutes = nmea % (deg * 100) + except ZeroDivisionError: + minutes = int(nmea)
- if dir == "S" or dir == "W": - m = -1 + if direction == "S" or direction == "W": + sign = -1 else: - m = 1 + sign = 1
- return dm2deg(deg, min) * m + return dm2deg(deg, minutes) * sign
def deg2nmea(deg): - deg, min = deg2dm(deg) + """Convert degrees to a NMEA-encoded value""" + degrees, minutes = deg2dm(deg)
- return (deg * 100) + min + return (degrees * 100) + minutes
def meters2feet(meters): + """Convert meters to feet""" return meters * 3.2808399
def feet2meters(feet): + """Convert feet to meters""" return feet * 0.3048
def distance(lat_a, lon_a, lat_b, lon_b): + """Calculate the distance between two points""" lat_a = deg2rad(lat_a) lon_a = deg2rad(lon_a)
@@ -108,30 +117,27 @@ elif tmp < -1: tmp = -1
- distance = acos(tmp) + dist = acos(tmp)
- return distance * earth_radius + return dist * earth_radius
def bearing(lat_a, lon_a, lat_b, lon_b): + """Calculate the bearing between two points""" lat_me = deg2rad(lat_a) - lon_me = deg2rad(lon_a) - lat_u = deg2rad(lat_b) - lon_u = deg2rad(lon_b) - - lat_d = deg2rad(lat_b - lat_a) lon_d = deg2rad(lon_b - lon_a)
- y = sin(lon_d) * cos(lat_u) - x = cos(lat_me) * sin(lat_u) - \ + posy = sin(lon_d) * cos(lat_u) + posx = cos(lat_me) * sin(lat_u) - \ sin(lat_me) * cos(lat_u) * cos(lon_d)
- bearing = rad2deg(atan2(y, x)) + bear = rad2deg(atan2(posy, posx))
- return (bearing + 360) % 360 + return (bear + 360) % 360
def fuzzy_to(lat_a, lon_a, lat_b, lon_b): - dir = bearing(lat_a, lon_a, lat_b, lon_b) + """Calculate a fuzzy distance to a point""" + bear = bearing(lat_a, lon_a, lat_b, lon_b)
dirs = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", @@ -143,27 +149,29 @@
direction = "?" for i in dirs: - if dir > angle and dir < (angle + delta): + if bear > angle and bear < (angle + delta): direction = i angle += delta
return direction
class RFinderParser: + """Parser for RFinder's data format""" def __init__(self, lat, lon): self.__memories = [] self.__cheat = {} self.__lat = lat self.__lon = lon
- def fetch_data(self, user, pw, lat, lon, radius): + def fetch_data(self, user, pw, coords, radius): + """Fetches the data for a set of parameters""" print user print pw args = { "email" : urllib.quote_plus(user), "pass" : hashlib.md5(pw).hexdigest(), - "lat" : "%7.5f" % lat, - "lon" : "%8.5f" % lon, + "lat" : "%7.5f" % coords[0], + "lon" : "%8.5f" % coords[1], "radius": "%i" % radius, "vers" : "CH%s" % CHIRP_VERSION, } @@ -183,7 +191,7 @@
return data
- def parse_line(self, line): + def _parse_line(self, line): mem = chirp_common.Memory()
_vals = line.split("|") @@ -217,15 +225,16 @@ try: lat = float(vals["LATITUDE"]) lon = float(vals["LONGITUDE"]) - d = distance(self.__lat, self.__lon, lat, lon) - b = fuzzy_to(self.__lat, self.__lon, lat, lon) - mem.comment = "(%imi %s) %s" % (d, b, mem.comment) + dist = distance(self.__lat, self.__lon, lat, lon) + bear = fuzzy_to(self.__lat, self.__lon, lat, lon) + mem.comment = "(%imi %s) %s" % (dist, bear, mem.comment) except Exception, e: print "Failed to calculate distance: %s" % e
return mem
def parse_data(self, data): + """Parse the fetched data""" number = 1 for line in data.split("\n"): if line.startswith("<"): @@ -233,7 +242,7 @@ elif not line.strip(): continue try: - mem = self.parse_line(line) + mem = self._parse_line(line) mem.number = number number += 1 self.__memories.append(mem) @@ -247,23 +256,27 @@ print "\n\n"
def get_memories(self): + """Return the Memory objects associated with the fetched data""" return self.__memories
class RFinderRadio(chirp_common.NetworkSourceRadio): + """A network source radio that supports the RFinder repeater directory""" VENDOR = "ITWeRKS" MODEL = "RFinder"
def __init__(self, *args, **kwargs): - chirp_common.Radio.__init__(self, *args, **kwargs) + chirp_common.NetworkSourceRadio.__init__(self, *args, **kwargs)
self._lat = 0 self._lon = 0 self._user = "" self._pass = "" + self._miles = 25
self._rfp = None
- def set_params(self, lat, lon, miles, email, password): + def set_params(self, (lat, lon), miles, email, password): + """Sets the parameters to use for the query""" self._lat = lat self._lon = lon self._miles = miles @@ -275,8 +288,7 @@
self._rfp.parse_data(self._rfp.fetch_data(self._user, self._pass, - self._lat, - self._lon, + (self._lat, self._lon), self._miles))
def get_features(self): @@ -297,12 +309,14 @@
return self._rfp.get_memories()[number-1]
-if __name__ == "__main__": - import sys - +def _test(): rfp = RFinderParser() - data = rfp.fetch_data(45.525, -122.9164, "KK7DS", "dsmith@danplanet.com") + data = rfp.fetch_data("KK7DS", "dsmith@danplanet.com", + (45.5, -122.91), 25) rfp.parse_data(data)
- for m in rfp.get_memories(): - print m + for mem in rfp.get_memories(): + print mem + +if __name__ == "__main__": + _test() diff -r db82df47f205 -r f805c420592f chirp/settings.py --- a/chirp/settings.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/settings.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,25 +16,31 @@ from chirp import chirp_common
class InvalidValueError(Exception): + """An invalid value was specified for a given setting""" pass
class InternalError(Exception): + """A driver provided an invalid settings object structure""" pass
class RadioSettingValue: + """Base class for a single radio setting""" def __init__(self): self._current = None self._has_changed = False
def changed(self): + """Returns True if the setting has been changed since init""" return self._has_changed
def set_value(self, value): + """Sets the current value, triggers changed""" if self._current != None and value != self._current: self._has_changed = True self._current = value
def get_value(self): + """Gets the current value""" return self._current
def __trunc__(self): @@ -44,10 +50,11 @@ return str(self.get_value())
class RadioSettingValueInteger(RadioSettingValue): - def __init__(self, min, max, current, step=1): + """An integer setting""" + def __init__(self, minval, maxval, current, step=1): RadioSettingValue.__init__(self) - self._min = min - self._max = max + self._min = minval + self._max = maxval self._step = step self.set_value(current)
@@ -63,15 +70,19 @@ RadioSettingValue.set_value(self, value)
def get_min(self): + """Returns the minimum allowed value""" return self._min
def get_max(self): + """Returns the maximum allowed value""" return self._max
def get_step(self): + """Returns the step increment""" return self._step
class RadioSettingValueBoolean(RadioSettingValue): + """A boolean setting""" def __init__(self, current): RadioSettingValue.__init__(self) self.set_value(current) @@ -83,6 +94,7 @@ return str(bool(self.get_value()))
class RadioSettingValueList(RadioSettingValue): + """A list-of-strings setting""" def __init__(self, options, current): RadioSettingValue.__init__(self) self._options = options @@ -94,12 +106,14 @@ RadioSettingValue.set_value(self, value)
def get_options(self): + """Returns the list of valid option values""" return self._options
def __trunc__(self): return self._options.index(self._current)
class RadioSettingValueString(RadioSettingValue): + """A string setting""" def __init__(self, minlength, maxlength, current, autopad=True): RadioSettingValue.__init__(self) @@ -110,6 +124,7 @@ self.set_value(current)
def set_charset(self, charset): + """Sets the set of allowed characters""" self._charset = charset
def set_value(self, value): @@ -128,6 +143,7 @@ return self._current.rstrip()
class RadioSettingGroup(object): + """A group of settings""" def _validate(self, element): # RadioSettingGroup can only contain RadioSettingGroup objects if not isinstance(element, RadioSettingGroup): @@ -146,37 +162,43 @@ self.append(element)
def get_name(self): + """Returns the group name""" return self._name
def get_shortname(self): + """Returns the short group identifier""" return self._shortname
def set_doc(self, doc): + """Sets the docstring for the group""" self.__doc__ = doc
def __str__(self): - s = "{Settings Group %s:\n" % self._name + string = "{Settings Group %s:\n" % self._name for element in self._elements.values(): - s += str(element) + "\n" - s += "}" - return s + string += str(element) + "\n" + string += "}" + return string
# Kinda list interface
def append(self, element): + """Adds an element to the group""" self[element.get_name()] = element
def __iter__(self): class RSGIterator: + """Iterator for a RadioSettingsGroup""" def __init__(self, rsg): self.__rsg = rsg self.__i = 0 def __iter__(self): return self def next(self): - if self.__i >= len(self.__rsg._element_order): + """Next Iterator Interface""" + if self.__i >= len(self.__rsg.keys()): raise StopIteration() - e = self.__rsg._elements[self.__rsg._element_order[self.__i]] + e = self.__rsg[self.__rsg.keys()[self.__i]] self.__i += 1 return e return RSGIterator(self) @@ -196,21 +218,26 @@ self._element_order.append(name)
def items(self): + """Returns a key=>value set of elements, like a dict""" return [(name, self._elements[name]) for name in self._element_order]
def keys(self): + """Returns a list of string element names""" return self._element_order
def values(self): - return [self.elements[name] for name in self._element_order] + """Returns the list of elements""" + return [self._elements[name] for name in self._element_order]
class RadioSetting(RadioSettingGroup): + """A single setting, which could be an array of items like a group""" def _validate(self, value): # RadioSetting can only contain RadioSettingValue objects if not isinstance(value, RadioSettingValue): raise InternalError("Incorrect type")
def changed(self): + """Returns True if any of the elements in the group have been changed""" for element in self._elements.values(): if element.changed(): return True diff -r db82df47f205 -r f805c420592f chirp/template.py --- a/chirp/template.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/template.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, yaesu_clone, util, directory, memmap +from chirp import chirp_common, directory, memmap from chirp import bitwise
# Here is where we define the memory map for the radio. Since @@ -24,7 +24,7 @@ # With some very basic settings, a 32-bit unsigned integer for the # frequency (in Hertz) and an eight-character alpha tag # -mem_format = """ +MEM_FORMAT = """ #seekto 0x0000; struct { u32 freq; @@ -33,6 +33,7 @@ """
def do_download(radio): + """This is your download function""" # NOTE: Remove this in your real implementation! return memmap.MemoryMap("\x00" * 1000)
@@ -43,12 +44,13 @@ # from the serial port. Do that one byte at a time and # store them in the memory map data = "" - for i in range(0, 1000): + for _i in range(0, 1000): data = serial.read(1)
return memmap.MemoryMap(data)
def do_upload(radio): + """This is your upload function""" # NOTE: Remove this in your real implementation! raise Exception("This template driver does not really work!")
@@ -59,11 +61,12 @@ # to the serial port. Do that one byte at a time, reading # from our memory map for i in range(0, 1000): - serial.write(radio._mmap[i]) + serial.write(radio.get_mmap()[i])
# Uncomment this to actually register this radio in CHIRP # @directory.register class TemplateRadio(chirp_common.CloneModeRadio): + """Acme Template""" VENDOR = "Acme" # Replace this with your vendor MODEL = "Template" # Replace this with your model BAUD_RATE = 9600 # Replace this with your baud rate @@ -82,7 +85,7 @@ # Do a download of the radio from the serial port def sync_in(self): self._mmap = do_download(self) - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
# Do an upload of the radio to the serial port def sync_out(self): diff -r db82df47f205 -r f805c420592f chirp/util.py --- a/chirp/util.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/util.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,6 +16,7 @@ import struct
def hexprint(data): + """Return a hexdump-like encoding of @data""" line_sz = 8
lines = len(data) / line_sz @@ -52,10 +53,8 @@
return out
-def write_in_place(mem, start, data): - return mem[:start] + data + mem[start+len(data):] - def bcd_encode(val, bigendian=True, width=None): + """This is really old and shouldn't be used anymore""" digits = [] while val != 0: digits.append(val % 10) @@ -78,8 +77,9 @@
return result
-def get_dict_rev(dict, key): +def get_dict_rev(thedict, value): + """Return the first matching key for a given @value in @dict""" _dict = {} - for k,v in dict.items(): + for k, v in thedict.items(): _dict[v] = k - return _dict[key] + return _dict[value] diff -r db82df47f205 -r f805c420592f chirp/uv5r.py --- a/chirp/uv5r.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/uv5r.py Fri Apr 27 14:35:41 2012 -0700 @@ -15,11 +15,13 @@
import struct
-from chirp import chirp_common, errors, util, directory, memmap, settings +from chirp import chirp_common, errors, util, directory, memmap from chirp import bitwise -from chirp.settings import * +from chirp.settings import RadioSetting, RadioSettingGroup, \ + RadioSettingValueInteger, RadioSettingValueList, \ + RadioSettingValueList, RadioSettingValueBoolean
-mem_format = """ +MEM_FORMAT = """ #seekto 0x0008; struct { lbcd rxfreq[4]; @@ -83,31 +85,31 @@ """
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0] -step_list = [str(x) for x in STEPS] -timeout_list = ["%s sec" % x for x in range(15, 615, 15)] -resume_list = ["TO", "CO", "SE"] -mode_list = ["Channel", "Name", "Frequency"] -color_list = ["Off", "Blue", "Orange", "Purple"] +STEP_LIST = [str(x) for x in STEPS] +TIMEOUT_LIST = ["%s sec" % x for x in range(15, 615, 15)] +RESUME_LIST = ["TO", "CO", "SE"] +MODE_LIST = ["Channel", "Name", "Frequency"] +COLOR_LIST = ["Off", "Blue", "Orange", "Purple"]
SETTING_LISTS = { - "step" : step_list, - "timeout" : timeout_list, - "screv" : resume_list, - "mdfa" : mode_list, - "mdfb" : mode_list, - "wtled" : color_list, - "rxled" : color_list, - "txled" : color_list, + "step" : STEP_LIST, + "timeout" : TIMEOUT_LIST, + "screv" : RESUME_LIST, + "mdfa" : MODE_LIST, + "mdfb" : MODE_LIST, + "wtled" : COLOR_LIST, + "rxled" : COLOR_LIST, + "txled" : COLOR_LIST, }
-def do_status(radio, block): - s = chirp_common.Status() - s.msg = "Cloning" - s.cur = block - s.max = radio._memsize - radio.status_fn(s) +def _do_status(radio, block): + status = chirp_common.Status() + status.msg = "Cloning" + status.cur = block + status.max = radio.get_memsize() + radio.status_fn(status)
-def do_ident(radio): +def _do_ident(radio): serial = radio.pipe serial.setTimeout(1)
@@ -130,12 +132,12 @@
return ident
-def do_download(radio): +def _do_download(radio): serial = radio.pipe
- data = do_ident(radio) + data = _do_ident(radio)
- for i in range(0, radio._memsize - 0x08, 0x40): + for i in range(0, radio.get_memsize() - 0x08, 0x40): msg = struct.pack(">BHB", ord("S"), i, 0x40) serial.write(msg)
@@ -162,23 +164,23 @@ if ack != "\x06": raise errors.RadioError("Radio refused to send block 0x%04x" % i)
- do_status(radio, i) + _do_status(radio, i)
return memmap.MemoryMap(data)
-def do_upload(radio): +def _do_upload(radio): serial = radio.pipe
- do_ident(radio) + _do_ident(radio)
- for i in range(0x08, radio._memsize, 0x10): + for i in range(0x08, radio.get_memsize(), 0x10): msg = struct.pack(">BHB", ord("X"), i - 0x08, 0x10) - serial.write(msg + radio._mmap[i:i+0x10]) + serial.write(msg + radio.get_mmap()[i:i+0x10])
ack = serial.read(1) if ack != "\x06": raise errors.RadioError("Radio refused to accept block 0x%04x" % i) - do_status(radio, i) + _do_status(radio, i)
UV5R_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=4.00), chirp_common.PowerLevel("Low", watts=1.00)] @@ -186,6 +188,7 @@ # Uncomment this to actually register this radio in CHIRP @directory.register class BaofengUV5R(chirp_common.CloneModeRadio): + """Baofeng UV-5R""" VENDOR = "Baofeng" MODEL = "UV-5R" BAUD_RATE = 9600 @@ -212,12 +215,12 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap) print self.get_settings()
def sync_in(self): try: - self._mmap = do_download(self) + self._mmap = _do_download(self) except errors.RadioError: raise except Exception, e: @@ -226,7 +229,7 @@
def sync_out(self): try: - do_upload(self) + _do_upload(self) except errors.RadioError: raise except Exception, e: @@ -387,81 +390,81 @@ advanced = RadioSettingGroup("advanced", "Advanced Settings") group = RadioSettingGroup("top", "All Settings", basic, advanced)
- s = RadioSetting("squelch", "Carrier Squelch Level", - RadioSettingValueInteger(0, 9, _settings.squelch)) - basic.append(s) + rs = RadioSetting("squelch", "Carrier Squelch Level", + RadioSettingValueInteger(0, 9, _settings.squelch)) + basic.append(rs)
- s = RadioSetting("step", "Tuning Step", - RadioSettingValueList(step_list, - step_list[_settings.step])) - advanced.append(s) + rs = RadioSetting("step", "Tuning Step", + RadioSettingValueList(STEP_LIST, + STEP_LIST[_settings.step])) + advanced.append(rs)
- s = RadioSetting("save", "Battery Saver", - RadioSettingValueInteger(0, 4, _settings.save)) - basic.append(s) + rs = RadioSetting("save", "Battery Saver", + RadioSettingValueInteger(0, 4, _settings.save)) + basic.append(rs)
- s = RadioSetting("vox", "VOX Sensitivity", - RadioSettingValueInteger(0, 10, _settings.vox)) - advanced.append(s) + rs = RadioSetting("vox", "VOX Sensitivity", + RadioSettingValueInteger(0, 10, _settings.vox)) + advanced.append(rs)
- s = RadioSetting("abr", "Backlight Timeout", - RadioSettingValueInteger(0, 5, _settings.abr)) - basic.append(s) + rs = RadioSetting("abr", "Backlight Timeout", + RadioSettingValueInteger(0, 5, _settings.abr)) + basic.append(rs)
- s = RadioSetting("tdr", "Dual Watch", - RadioSettingValueBoolean(_settings.tdr)) - advanced.append(s) + rs = RadioSetting("tdr", "Dual Watch", + RadioSettingValueBoolean(_settings.tdr)) + advanced.append(rs)
- s = RadioSetting("beep", "Beep", - RadioSettingValueBoolean(_settings.beep)) - basic.append(s) + rs = RadioSetting("beep", "Beep", + RadioSettingValueBoolean(_settings.beep)) + basic.append(rs)
- s = RadioSetting("timeout", "Timeout Timer", - RadioSettingValueList(timeout_list, - timeout_list[_settings.tdr])) - basic.append(s) + rs = RadioSetting("timeout", "Timeout Timer", + RadioSettingValueList(TIMEOUT_LIST, + TIMEOUT_LIST[_settings.tdr])) + basic.append(rs)
- s = RadioSetting("voice", "Voice", - RadioSettingValueBoolean(_settings.voice)) - advanced.append(s) + rs = RadioSetting("voice", "Voice", + RadioSettingValueBoolean(_settings.voice)) + advanced.append(rs)
- s = RadioSetting("screv", "Scan Resume", - RadioSettingValueList(resume_list, - resume_list[_settings.screv])) - advanced.append(s) + rs = RadioSetting("screv", "Scan Resume", + RadioSettingValueList(RESUME_LIST, + RESUME_LIST[_settings.screv])) + advanced.append(rs)
- s = RadioSetting("mdfa", "Display Mode (A)", - RadioSettingValueList(mode_list, - mode_list[_settings.mdfa])) - basic.append(s) + rs = RadioSetting("mdfa", "Display Mode (A)", + RadioSettingValueList(MODE_LIST, + MODE_LIST[_settings.mdfa])) + basic.append(rs)
- s = RadioSetting("mdfb", "Display Mode (B)", - RadioSettingValueList(mode_list, - mode_list[_settings.mdfb])) - basic.append(s) + rs = RadioSetting("mdfb", "Display Mode (B)", + RadioSettingValueList(MODE_LIST, + MODE_LIST[_settings.mdfb])) + basic.append(rs)
- s = RadioSetting("bcl", "Busy Channel Lockout", - RadioSettingValueBoolean(_settings.bcl)) - advanced.append(s) + rs = RadioSetting("bcl", "Busy Channel Lockout", + RadioSettingValueBoolean(_settings.bcl)) + advanced.append(rs)
- s = RadioSetting("autolk", "Automatic Key Lock", - RadioSettingValueBoolean(_settings.autolk)) - advanced.append(s) + rs = RadioSetting("autolk", "Automatic Key Lock", + RadioSettingValueBoolean(_settings.autolk)) + advanced.append(rs)
- s = RadioSetting("wtled", "Standby LED Color", - RadioSettingValueList(color_list, - color_list[_settings.wtled])) - basic.append(s) + rs = RadioSetting("wtled", "Standby LED Color", + RadioSettingValueList(COLOR_LIST, + COLOR_LIST[_settings.wtled])) + basic.append(rs)
- s = RadioSetting("rxled", "RX LED Color", - RadioSettingValueList(color_list, - color_list[_settings.rxled])) - basic.append(s) + rs = RadioSetting("rxled", "RX LED Color", + RadioSettingValueList(COLOR_LIST, + COLOR_LIST[_settings.rxled])) + basic.append(rs)
- s = RadioSetting("txled", "TX LED Color", - RadioSettingValueList(color_list, - color_list[_settings.txled])) - basic.append(s) + rs = RadioSetting("txled", "TX LED Color", + RadioSettingValueList(COLOR_LIST, + COLOR_LIST[_settings.txled])) + basic.append(rs)
return group
@@ -471,9 +474,6 @@ if not isinstance(element, RadioSetting): self.set_settings(element) continue - - if element.get_name() in SETTING_LISTS.keys(): - value = SETTING_LISTS[element.get_name()].index(str(element.value)) try: setattr(_settings, element.get_name(), element.value) except Exception, e: diff -r db82df47f205 -r f805c420592f chirp/vx3.py --- a/chirp/vx3.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vx3.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, yaesu_clone, util, directory +from chirp import chirp_common, yaesu_clone, directory from chirp import bitwise
#interesting offsets which may be checksums needed later @@ -23,7 +23,7 @@ #0x0409 checksum2? #0x04C9 checksum2a?
-mem_format = """ +MEM_FORMAT = """ #seekto 0x7F4A; u8 checksum;
@@ -93,6 +93,7 @@ chirp_common.PowerLevel("Low", watts=0.10)]
class VX3Bank(chirp_common.NamedBank): + """A VX3 Bank""" def get_name(self): _bank = self._model._radio._memobj.bank_names[self.index] name = "" @@ -108,6 +109,7 @@ _bank.name = [CHARSET.index(x) for x in name.ljust(6)[:6]]
class VX3BankModel(chirp_common.BankModel): + """A VX-3 bank model""" def get_num_banks(self): return 24
@@ -121,8 +123,18 @@ banks.append(bank) return banks
+def _wipe_memory(mem): + mem.set_raw("\x00" * (mem.size() / 8)) + #the following settings are set to match the defaults + #on the radio, some of these fields are unknown + mem.name = [0xFF for _i in range(0, 6)] + mem.unknown5 = 0x0D #not sure what this is + mem.unknown7 = 0x01 #this likely is part of autostep + mem.automode = 0x01 #autoselect mode + @directory.register class VX3Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu VX-3""" BAUD_RATE = 19200 VENDOR = "Yaesu" MODEL = "VX-3" @@ -139,7 +151,7 @@ return [ yaesu_clone.YaesuChecksum(0x0000, 0x7F49) ]
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -169,7 +181,7 @@
nibble = ((number-1) % 2) and "even" or "odd" used = _flag["%s_masked" % nibble] - valid =_flag["%s_valid" % nibble] + valid = _flag["%s_valid" % nibble] pskip = _flag["%s_pskip" % nibble] skip = _flag["%s_skip" % nibble]
@@ -203,16 +215,6 @@ mem.name = mem.name.rstrip() return mem
- def _wipe_memory(self, mem): - mem.set_raw("\x00" * (mem.size() / 8)) - #the following settings are set to match the defaults - #on the radio, some of these fields are unknown - mem.name = [0xFF for i in range(0, 6)] - mem.unknown5 = 0x0D #not sure what this is - mem.unknown7 = 0x01 #this likely is part of autostep - mem.automode = 0x01 #autoselect mode - - def set_memory(self, mem): _mem = self._memobj.memory[mem.number-1] _flag = self._memobj.flags[(mem.number-1)/2] @@ -223,7 +225,7 @@ valid = _flag["%s_valid" % nibble]
if not mem.empty and not valid: - self._wipe_memory(_mem) + _wipe_memory(_mem)
if mem.empty and valid and not used: _flag["%s_valid" % nibble] = False @@ -251,7 +253,8 @@
for i in range(0, 6): _mem.name[i] = CHARSET.index(mem.name.ljust(6)[i]) - if mem.name.strip(): _mem.name[0] |= 0x80 + if mem.name.strip(): + _mem.name[0] |= 0x80
def validate_memory(self, mem): msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem) diff -r db82df47f205 -r f805c420592f chirp/vx5.py --- a/chirp/vx5.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vx5.py Fri Apr 27 14:35:41 2012 -0700 @@ -14,10 +14,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, yaesu_clone, util, directory +from chirp import chirp_common, yaesu_clone, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ #seekto 0x012A; struct { u8 zeros:4, @@ -67,6 +67,7 @@
@directory.register class VX5Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu VX-5""" BAUD_RATE = 9600 VENDOR = "Yaesu" MODEL = "VX-5" @@ -97,7 +98,7 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_raw_memory(self, number): return repr(self._memobj.memory[number-1]) diff -r db82df47f205 -r f805c420592f chirp/vx6.py --- a/chirp/vx6.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vx6.py Fri Apr 27 14:35:41 2012 -0700 @@ -27,14 +27,15 @@ # cpu_shifted: CPU freq has been shifted (to move a birdie out of channel) # power: 0-3: ["L1", "L2", "L3", "Hi"] # pager: Set if this is a paging memory -# tmodes: 0-7: ["", "Tone", "TSQL", "DTCS", "Rv Tn", "D Code", "T DCS", "D Tone"] +# tmodes: 0-7: ["", "Tone", "TSQL", "DTCS", "Rv Tn", "D Code", +# "T DCS", "D Tone"] # Rv Tn: Reverse CTCSS - mutes receiver on tone # The final 3 are for split: # D Code: DCS Encode only # T DCS: Encodes tone, decodes DCS code # D Tone: Encodes DCS code, decodes tone # } -mem_format = """ +MEM_FORMAT = """ #seekto 0x018A; u16 bank_sizes[24];
@@ -110,6 +111,7 @@
@directory.register class VX6Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu VX-6""" BAUD_RATE = 19200 VENDOR = "Yaesu" MODEL = "VX-6" @@ -123,7 +125,7 @@ return [ yaesu_clone.YaesuChecksum(0x0000, 0x7F49) ]
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -225,35 +227,35 @@ _flag["%s_pskip" % nibble] = mem.skip == "P" _flag["%s_skip" % nibble] = mem.skip == "S"
- _mem.name == ("\xFF" * 6) + _mem.name = [0xFF] * 6 for i in range(0, 6): _mem.name[i] = CHARSET.index(mem.name.ljust(6)[i])
if mem.name.strip(): _mem.name[0] |= 0x80
- def get_banks(self): - _banks = self._memobj.bank_names +# def get_banks(self): +# _banks = self._memobj.bank_names +# +# banks = [] +# for bank in _banks: +# name = "" +# for i in bank.name: +# name += CHARSET[i & 0x7F] +# banks.append(name.rstrip()) +# +# return banks +# +# # Return channels for a bank. Bank given as number +# def get_bank_channels(self, bank): +# nchannels = 0 +# size = self._memobj.bank_sizes[bank] +# if size <= 198: +# nchannels = 1 + size/2 +# _channels = self._memobj.bank_channels[bank] +# channels = [] +# for i in range(0, nchannels): +# channels.append(int(_channels.channel[i])) +# +# return channels
- banks = [] - for bank in _banks: - name = "" - for i in bank.name: - name += CHARSET[i & 0x7F] - banks.append(name.rstrip()) - - return banks - - # Return channels for a bank. Bank given as number - def get_bank_channels(self, bank): - nchannels = 0 - size = self._memobj.bank_sizes[bank] - if size <= 198: - nchannels = 1 + size/2 - _channels = self._memobj.bank_channels[bank] - channels = [] - for i in range(0, nchannels): - channels.append(int(_channels.channel[i])) - - return channels - diff -r db82df47f205 -r f805c420592f chirp/vx7.py --- a/chirp/vx7.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vx7.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-from chirp import chirp_common, yaesu_clone, util, directory +from chirp import chirp_common, yaesu_clone, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ #seekto 0x0611; u8 checksum1;
@@ -96,6 +96,7 @@ chirp_common.PowerLevel("L1", watts=0.05)]
class VX7BankModel(chirp_common.BankModel): + """A VX-7 Bank model""" def get_num_banks(self): return 9
@@ -130,8 +131,8 @@ remaining_members += 1
if not found: - raise Exception(_("Memory {num} not in " - "bank {bank}").format(num=memory.number, + raise Exception("Memory {num} not in " + + "bank {bank}".format(num=memory.number, bank=bank)) if not remaining_members: _bank_used.in_use = 0xFFFF @@ -154,12 +155,19 @@ def get_memory_banks(self, memory): banks = [] for bank in self.get_banks(): - if memory.number in [x.number for x in self.get_bank_memories(bank)]: - banks.append(bank) + if memory.number in [x.number for x in + self.get_bank_memories(bank)]: + banks.append(bank) return banks
+def _wipe_memory(mem): + mem.set_raw("\x00" * (mem.size() / 8)) + mem.unknown1 = 0x05 + mem.ones = 0x03 + @directory.register class VX7Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu VX-7""" BAUD_RATE = 19200 VENDOR = "Yaesu" MODEL = "VX-7" @@ -176,7 +184,7 @@ ]
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -243,11 +251,6 @@
return mem
- def _wipe_memory(self, mem): - mem.set_raw("\x00" * (mem.size() / 8)) - mem.unknown1 = 0x05 - mem.ones = 0x03 - def set_memory(self, mem): _mem = self._memobj.memory[mem.number-1] _flag = self._memobj.flags[(mem.number-1)/2] @@ -258,7 +261,7 @@ used = _flag["%s_masked" % nibble]
if not mem.empty and not valid: - self._wipe_memory(_mem) + _wipe_memory(_mem) self._wipe_memory_banks(mem)
if mem.empty and valid and not used: @@ -294,7 +297,8 @@ if mem.freq >= 222000000 and mem.freq <= 225000000: if mem.power not in POWER_LEVELS_220: msgs.append(chirp_common.ValidationError(\ - "Power level %s not supported on 220MHz band" % mem.power)) + "Power level %s not supported on 220MHz band" % \ + mem.power))
return msgs
diff -r db82df47f205 -r f805c420592f chirp/vx8.py --- a/chirp/vx8.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vx8.py Fri Apr 27 14:35:41 2012 -0700 @@ -16,7 +16,7 @@ from chirp import chirp_common, yaesu_clone, directory from chirp import bitwise
-mem_format = """ +MEM_FORMAT = """ #seekto 0x54a; struct { u16 in_use; @@ -88,6 +88,8 @@ chirp_common.PowerLevel("L1", watts=0.05)]
class VX8Bank(chirp_common.NamedBank): + """A VX-8 bank""" + def get_name(self): _bank = self._model._radio._memobj.bank_info[self.index] _bank_used = self._model._radio._memobj.bank_used[self.index] @@ -104,6 +106,7 @@ _bank.name = [CHARSET.index(x) for x in name.ljust(16)[:16]]
class VX8BankModel(chirp_common.BankModel): + """A VX-8 bank model""" def get_num_banks(self): return 24
@@ -172,8 +175,13 @@
return banks
+def _wipe_memory(mem): + mem.set_raw("\x00" * (mem.size() / 8)) + mem.unknown1 = 0x05 + @directory.register class VX8Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu VX-8""" BAUD_RATE = 38400 VENDOR = "Yaesu" MODEL = "VX-8" @@ -185,7 +193,7 @@ _block_size = 32
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -242,10 +250,6 @@
return mem
- def _wipe_memory(self, mem): - mem.set_raw("\x00" * (mem.size() / 8)) - mem.unknown1 = 0x05 - def _debank(self, mem): bm = self.get_bank_model() for bank in bm.get_memory_banks(mem): @@ -256,7 +260,7 @@ flag = self._memobj.flag[mem.number-1]
if not mem.empty and not flag.valid: - self._wipe_memory(_mem) + _wipe_memory(_mem)
if mem.empty and flag.valid and not flag.used: flag.valid = False @@ -300,5 +304,6 @@
@directory.register class VX8DRadio(VX8Radio): + """Yaesu VX-8DR""" _model = "AH29D" VARIANT = "DR" diff -r db82df47f205 -r f805c420592f chirp/vxa700.py --- a/chirp/vxa700.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/vxa700.py Fri Apr 27 14:35:41 2012 -0700 @@ -19,21 +19,21 @@ import time import struct
-def debug(string): +def _debug(string): pass print string
-def send(radio, data): - debug("Sending %s" % repr(data)) +def _send(radio, data): + _debug("Sending %s" % repr(data)) radio.pipe.write(data) radio.pipe.flush() echo = radio.pipe.read(len(data)) if len(echo) != len(data): raise errors.RadioError("Invalid echo")
-def spoonfeed(radio, data): - count = 0 - debug("Writing %i:\n%s" % (len(data), util.hexprint(data))) +def _spoonfeed(radio, data): + #count = 0 + _debug("Writing %i:\n%s" % (len(data), util.hexprint(data))) for byte in data: radio.pipe.write(byte) radio.pipe.flush() @@ -45,12 +45,12 @@ if echo != byte: print "%02x != %02x" % (ord(echo), ord(byte)) raise errors.RadioError("No echo?") - count += 1 + #count += 1
-def download(radio): +def _download(radio): count = 0 data = "" - while len(data) < radio._memsize: + while len(data) < radio.get_memsize(): count += 1 chunk = radio.pipe.read(133) if len(chunk) == 0 and len(data) == 0 and count < 30: @@ -58,10 +58,9 @@ if len(chunk) != 132: raise errors.RadioError("Got short block (length %i)" % len(chunk))
- flag, length, block = struct.unpack("BBB", chunk[:3]) checksum = ord(chunk[-1]) - - flag, length, block, _data, checksum = struct.unpack("BBB128sB", chunk) + _flag, _length, _block, _data, checksum = \ + struct.unpack("BBB128sB", chunk)
cs = 0 for byte in chunk[:-1]: @@ -70,32 +69,31 @@ raise errors.RadioError("Invalid checksum at 0x%02x" % len(data))
data += _data - send(radio, "\x06") + _send(radio, "\x06")
if radio.status_fn: status = chirp_common.Status() status.msg = "Cloning from radio" status.cur = len(data) - status.max = radio._memsize + status.max = radio.get_memsize() radio.status_fn(status)
return memmap.MemoryMap(data)
-def upload(radio): - for i in range(0, radio._memsize, 128): - chunk = radio._mmap[i:i+128] +def _upload(radio): + for i in range(0, radio.get_memsize(), 128): + chunk = radio.get_mmap()[i:i+128] cs = 0x20 + 130 + (i / 128) for byte in chunk: cs += ord(byte) - spoonfeed(radio, - struct.pack("BBB128sB", - 0x20, - 130, - i / 128, - chunk, - cs % 256)) + _spoonfeed(radio, + struct.pack("BBB128sB", + 0x20, + 130, + i / 128, + chunk, + cs % 256)) radio.pipe.write("") - start = time.time() # This is really unreliable for some reason, so just # blindly proceed # ack = radio.pipe.read(1) @@ -109,10 +107,10 @@ status = chirp_common.Status() status.msg = "Cloning to radio" status.cur = i - status.max = radio._memsize + status.max = radio.get_memsize() radio.status_fn(status)
-mem_format = """ +MEM_FORMAT = """ struct memory_struct { u8 unknown1; u8 unknown2:2, @@ -158,19 +156,24 @@ chirp_common.PowerLevel("Low3", watts=2.500), chirp_common.PowerLevel("High", watts=5.000)]
+def _wipe_memory(_mem): + _mem.set_raw("\x00" * (_mem.size() / 8)) + @directory.register class VXA700Radio(chirp_common.CloneModeRadio): + """Vertex Standard VXA-700""" VENDOR = "Vertex Standard" MODEL = "VXA-700" _memsize = 4096
def sync_in(self): try: - self._mmap = download(self) + self._mmap = _download(self) except errors.RadioError: raise except Exception, e: - raise errors.RadioError("Failed to communicate with the radio: %s" % e) + raise errors.RadioError("Failed to communicate " + + "with the radio: %s" % e) self.process_mmap()
def sync_out(self): @@ -180,14 +183,15 @@ # 0x02 <- air band only try: self.pipe.setTimeout(2) - upload(self) + _upload(self) except errors.RadioError: raise except Exception, e: - raise errors.RadioError("Failed to communicate with the radio: %s" % e) + raise errors.RadioError("Failed to communicate " + + "with the radio: %s" % e)
def process_mmap(self): - self._memobj = bitwise.parse(mem_format, self._mmap) + self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -203,7 +207,7 @@ rf.valid_tuning_steps = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0] rf.valid_modes = ["AM", "FM"] rf.valid_power_levels = POWER - rf.memory_bounds = (1,100) + rf.memory_bounds = (1, 100) return rf
def _get_mem(self, number): @@ -215,8 +219,8 @@
def get_memory(self, number): _mem = self._get_mem(number) - byte = (number - 1) / 8; - bit = 1 << ((number - 1) % 8); + byte = (number - 1) / 8 + bit = 1 << ((number - 1) % 8)
mem = chirp_common.Memory() mem.number = number @@ -251,13 +255,10 @@
return mem
- def _wipe_memory(self, _mem): - _mem.set_raw("\x00" * (_mem.size() / 8)) - def set_memory(self, mem): _mem = self._get_mem(mem.number) - byte = (mem.number - 1) / 8; - bit = 1 << ((mem.number - 1) % 8); + byte = (mem.number - 1) / 8 + bit = 1 << ((mem.number - 1) % 8)
if mem.empty and self._memobj.invisible_bits[byte] & bit: self._memobj.invalid_bits[byte] |= bit @@ -267,7 +268,7 @@ return
if self._memobj.invalid_bits[byte] & bit: - self._wipe_memory(_mem) + _wipe_memory(_mem)
self._memobj.invisible_bits[byte] &= ~bit self._memobj.invalid_bits[byte] &= ~bit @@ -293,7 +294,7 @@ _mem.skip = mem.skip == "S" try: _mem.power = POWER.index(mem.power) - except: + except ValueError: _mem.power = 3 # High
for i in range(0, 8): diff -r db82df47f205 -r f805c420592f chirp/wouxun.py --- a/chirp/wouxun.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/wouxun.py Fri Apr 27 14:35:41 2012 -0700 @@ -23,7 +23,7 @@ else: DEBUG = False
-wouxun_mem_format = """ +WOUXUN_MEM_FORMAT = """ #seekto 0x0010; struct { lbcd rx_freq[4]; @@ -50,22 +50,24 @@ """
def wouxun_identify(radio): - for i in range(0, 5): + """Do the original wouxun identification dance""" + for _i in range(0, 5): radio.pipe.write("HiWOUXUN\x02") - r = radio.pipe.read(9) - if len(r) != 9: + resp = radio.pipe.read(9) + if len(resp) != 9: print "Retrying identification..." time.sleep(1) continue - if r[2:8] != radio._model: + if resp[2:8] != radio._model: raise Exception("I can't talk to this model") return - if len(r) == 0: + if len(resp) == 0: raise Exception("Radio not responding") else: raise Exception("Unable to identify radio")
def wouxun_start_transfer(radio): + """Tell the radio to go into transfer mode""" radio.pipe.write("\x02\x06") time.sleep(0.05) ack = radio.pipe.read(1) @@ -73,6 +75,7 @@ raise Exception("Radio refused transfer mode")
def do_download(radio, start, end, blocksize): + """Initiate a download of @radio between @start and @end""" image = "" for i in range(start, end, blocksize): cmd = struct.pack(">cHb", "R", i, blocksize) @@ -80,30 +83,32 @@ print util.hexprint(cmd) radio.pipe.write(cmd) length = len(cmd) + blocksize - r = radio.pipe.read(length) - if len(r) != (len(cmd) + blocksize): - print util.hexprint(r) - raise Exception("Failed to read full block (%i!=%i)" % (len(r), - len(cmd)+blocksize)) + resp = radio.pipe.read(length) + if len(resp) != (len(cmd) + blocksize): + print util.hexprint(resp) + raise Exception("Failed to read full block (%i!=%i)" % \ + (len(resp), + len(cmd) + blocksize))
radio.pipe.write("\x06") radio.pipe.read(1) - image += r[4:] + image += resp[4:]
if radio.status_fn: - s = chirp_common.Status() - s.cur = i - s.max = end - s.msg = "Cloning from radio" - radio.status_fn(s) + status = chirp_common.Status() + status.cur = i + status.max = end + status.msg = "Cloning from radio" + radio.status_fn(status)
return memmap.MemoryMap(image)
def do_upload(radio, start, end, blocksize): + """Initiate an upload of @radio between @start and @end""" ptr = start for i in range(start, end, blocksize): cmd = struct.pack(">cHb", "W", i, blocksize) - chunk = radio._mmap[ptr:ptr+blocksize] + chunk = radio.get_mmap()[ptr:ptr+blocksize] ptr += blocksize radio.pipe.write(cmd + chunk) if DEBUG: @@ -115,13 +120,14 @@ #radio.pipe.write(ack)
if radio.status_fn: - s = chirp_common.Status() - s.cur = i - s.max = end - s.msg = "Cloning to radio" - radio.status_fn(s) + status = chirp_common.Status() + status.cur = i + status.max = end + status.msg = "Cloning to radio" + radio.status_fn(status)
def wouxun_download(radio): + """Talk to an original wouxun and do a download""" try: wouxun_identify(radio) wouxun_start_transfer(radio) @@ -132,6 +138,7 @@ raise errors.RadioError("Failed to communicate with radio: %s" % e)
def wouxun_upload(radio): + """Talk to an original wouxun and do an upload""" try: wouxun_identify(radio) wouxun_start_transfer(radio) @@ -147,8 +154,12 @@ POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.00), chirp_common.PowerLevel("Low", watts=1.00)]
+def wipe_memory(_mem, byte): + _mem.set_raw(byte * (_mem.size() / 8)) + @directory.register class KGUVD1PRadio(chirp_common.CloneModeRadio): + """Wouxun KG-UVD1P,UV2,UV3""" VENDOR = "Wouxun" MODEL = "KG-UVD1P" _model = "KG669V" @@ -172,7 +183,7 @@ # format, padding 16 bytes of 0xFF in front. self._mmap = memmap.MemoryMap(("\xFF" * 16) + \ self._mmap.get_packed()[8:8184]) - self._memobj = bitwise.parse(wouxun_mem_format, self._mmap) + self._memobj = bitwise.parse(WOUXUN_MEM_FORMAT, self._mmap)
def get_features(self): rf = chirp_common.RadioFeatures() @@ -194,14 +205,14 @@ def get_raw_memory(self, number): return repr(self._memobj.memory[number - 1])
- def get_tone(self, _mem, mem): - def get_dcs(val): + def _get_tone(self, _mem, mem): + def _get_dcs(val): code = int("%03o" % (val & 0x07FF)) pol = (val & 0x8000) and "R" or "N" return code, pol
if _mem.tx_tone != 0xFFFF and _mem.tx_tone > 0x2800: - tcode, tpol = get_dcs(_mem.tx_tone) + tcode, tpol = _get_dcs(_mem.tx_tone) mem.dtcs = tcode txmode = "DTCS" elif _mem.tx_tone != 0xFFFF: @@ -211,7 +222,7 @@ txmode = ""
if _mem.rx_tone != 0xFFFF and _mem.rx_tone > 0x2800: - rcode, rpol = get_dcs(_mem.rx_tone) + rcode, rpol = _get_dcs(_mem.rx_tone) mem.dtcs = rcode rxmode = "DTCS" elif _mem.rx_tone != 0xFFFF: @@ -268,7 +279,7 @@ if not _mem.iswide: mem.mode = "NFM"
- self.get_tone(_mem, mem) + self._get_tone(_mem, mem)
mem.power = POWER_LEVELS[not _mem.power_high]
@@ -279,11 +290,8 @@
return mem
- def wipe_memory(self, _mem, byte): - _mem.set_raw(byte * (_mem.size() / 8)) - - def set_tone(self, mem, _mem): - def set_dcs(code, pol): + def _set_tone(self, mem, _mem): + def _set_dcs(code, pol): val = int("%i" % code, 8) + 0x2800 if pol == "R": val += 0xA000 @@ -299,7 +307,7 @@
if tx_mode == "DTCS": - _mem.tx_tone = set_dcs(mem.dtcs, mem.dtcs_polarity[0]) + _mem.tx_tone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0]) elif tx_mode: _mem.tx_tone = tx_mode == "Tone" and \ int(mem.rtone * 10) or int(mem.ctone * 10) @@ -307,7 +315,7 @@ _mem.tx_tone = 0xFFFF
if rx_mode == "DTCS": - _mem.rx_tone = set_dcs(mem.dtcs, mem.dtcs_polarity[1]) + _mem.rx_tone = _set_dcs(mem.dtcs, mem.dtcs_polarity[1]) elif rx_mode: _mem.rx_tone = int(mem.ctone * 10) else: @@ -322,11 +330,11 @@ _nam = self._memobj.names[mem.number - 1]
if mem.empty: - self.wipe_memory(_mem, "\xFF") + wipe_memory(_mem, "\xFF") return
if _mem.get_raw() == ("\xFF" * 16): - self.wipe_memory(_mem, "\x00") + wipe_memory(_mem, "\x00")
_mem.rx_freq = int(mem.freq / 10) if mem.duplex == "split": @@ -341,7 +349,7 @@ _mem.skip = mem.skip != "S" _mem.iswide = mem.mode != "NFM"
- self.set_tone(mem, _mem) + self._set_tone(mem, _mem)
if mem.power: _mem.power_high = not POWER_LEVELS.index(mem.power) @@ -386,7 +394,8 @@ raise Exception("Radio did not ACK ident")
def puxing_prep(radio): - for i in range(0, 10): + """Do the Puxing PX-777 identification dance""" + for _i in range(0, 10): try: return _puxing_prep(radio) except Exception, e: @@ -395,6 +404,7 @@ raise e
def puxing_download(radio): + """Talk to a Puxing PX-777 and do a download""" try: puxing_prep(radio) return do_download(radio, 0x0000, 0x0C60, 0x0008) @@ -404,6 +414,7 @@ raise errors.RadioError("Failed to communicate with radio: %s" % e)
def puxing_upload(radio): + """Talk to a Puxing PX-777 and do an upload""" try: puxing_prep(radio) return do_upload(radio, 0x0000, 0x0C40, 0x0008) @@ -412,7 +423,7 @@ except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e)
-puxing_mem_format = """ +PUXING_MEM_FORMAT = """ #seekto 0x0000; struct { lbcd rx_freq[4]; @@ -477,6 +488,7 @@
@directory.register class Puxing777Radio(KGUVD1PRadio): + """Puxing PX-777""" VENDOR = "Puxing" MODEL = "PX-777"
@@ -513,7 +525,7 @@ return rf
def process_mmap(self): - self._memobj = bitwise.parse(puxing_mem_format, self._mmap) + self._memobj = bitwise.parse(PUXING_MEM_FORMAT, self._mmap)
@classmethod def match_model(cls, filedata, filename): @@ -526,16 +538,16 @@ _mem = self._memobj.memory[number - 1] _nam = self._memobj.names[number - 1]
- def is_empty(): - for i in range(0,4): + def _is_empty(): + for i in range(0, 4): if _mem.rx_freq[i].get_raw() != "\xFF": return False return True
- def is_no_tone(field): + def _is_no_tone(field): return field[0].get_raw() == "\xFF"
- def get_dtcs(value): + def _get_dtcs(value): # Upper nibble 0x80 -> DCS, 0xC0 -> Inv. DCS if value > 12000: return "R", value - 12000 @@ -544,19 +556,19 @@ else: raise Exception("Unable to convert DCS value")
- def do_dtcs(mem, txfield, rxfield): + def _do_dtcs(mem, txfield, rxfield): if int(txfield) < 8000 or int(rxfield) < 8000: raise Exception("Split tone not supported")
if txfield[0].get_raw() == "\xFF": tp, tx = "N", None else: - tp, tx = get_dtcs(int(txfield)) + tp, tx = _get_dtcs(int(txfield))
if rxfield[0].get_raw() == "\xFF": rp, rx = "N", None else: - rp, rx = get_dtcs(int(rxfield)) + rp, rx = _get_dtcs(int(rxfield))
if not rx: rx = tx @@ -572,7 +584,7 @@ mem = chirp_common.Memory() mem.number = number
- if is_empty(): + if _is_empty(): mem.empty = True return mem
@@ -588,15 +600,15 @@ if not _mem.iswide: mem.mode = "NFM"
- if is_no_tone(_mem.tx_tone): + if _is_no_tone(_mem.tx_tone): pass # No tone elif int(_mem.tx_tone) > 8000 or \ - (not is_no_tone(_mem.rx_tone) and int(_mem.rx_tone) > 8000): + (not _is_no_tone(_mem.rx_tone) and int(_mem.rx_tone) > 8000): mem.tmode = "DTCS" - do_dtcs(mem, _mem.tx_tone, _mem.rx_tone) + _do_dtcs(mem, _mem.tx_tone, _mem.rx_tone) else: mem.rtone = int(_mem.tx_tone) / 10.0 - mem.tmode = is_no_tone(_mem.rx_tone) and "Tone" or "TSQL" + mem.tmode = _is_no_tone(_mem.rx_tone) and "Tone" or "TSQL"
mem.power = POWER_LEVELS[not _mem.power_high]
@@ -612,7 +624,7 @@ _nam = self._memobj.names[mem.number - 1]
if mem.empty: - self.wipe_memory(_mem, "\xFF") + wipe_memory(_mem, "\xFF") return
_mem.rx_freq = mem.freq / 10 @@ -666,6 +678,7 @@ raise Exception("Character `%s' not supported")
def puxing_2r_prep(radio): + """Do the Puxing 2R identification dance""" radio.pipe.setTimeout(0.2) radio.pipe.write("PROGRAM\x02") ack = radio.pipe.read(1) @@ -677,6 +690,7 @@ print "Radio ident: %s (%i)" % (repr(ident), len(ident))
def puxing_2r_download(radio): + """Talk to a Puxing 2R and do a download""" try: puxing_2r_prep(radio) return do_download(radio, 0x0000, 0x0FE0, 0x0010) @@ -686,6 +700,7 @@ raise errors.RadioError("Failed to communicate with radio: %s" % e)
def puxing_2r_upload(radio): + """Talk to a Puxing 2R and do an upload""" try: puxing_2r_prep(radio) return do_upload(radio, 0x0000, 0x0FE0, 0x0010) @@ -694,7 +709,7 @@ except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e)
-puxing_2r_mem_format = """ +PUXING_2R_MEM_FORMAT = """ #seekto 0x0010; struct { lbcd freq[4]; @@ -719,6 +734,7 @@
@directory.register class Puxing2RRadio(KGUVD1PRadio): + """Puxing PX-2R""" VENDOR = "Puxing" MODEL = "PX-2R" _memsize = 0x0FE0 @@ -753,7 +769,7 @@ puxing_2r_upload(self)
def process_mmap(self): - self._memobj = bitwise.parse(puxing_2r_mem_format, self._mmap) + self._memobj = bitwise.parse(PUXING_2R_MEM_FORMAT, self._mmap)
def get_memory(self, number): _mem = self._memobj.memory[number-1] @@ -780,16 +796,17 @@ mem.rtone = chirp_common.TONES[_mem.tx_tone - 1] mem.tmode = _mem.rx_tone and "TSQL" or "Tone"
- c = 0 + count = 0 for i in _mem.name: if i == 0xFF: break try: mem.name += PX2R_CHARSET[i] - except: - print "Unknown name char %i: 0x%02x (mem %i)" % (c, i, number) + except Exception: + print "Unknown name char %i: 0x%02x (mem %i)" % (count, + i, number) mem.name += " " - c += 1 + count += 1 mem.name = mem.name.rstrip()
return mem @@ -845,7 +862,8 @@ raise errors.RadioError("Radio did not ACK ident")
def uv3r_prep(radio): - for i in range(0, 10): + """Do the UV3R identification dance""" + for _i in range(0, 10): try: return _uv3r_prep(radio) except errors.RadioError, e: @@ -854,6 +872,7 @@ raise e
def uv3r_download(radio): + """Talk to a UV3R and do a download""" try: uv3r_prep(radio) return do_download(radio, 0x0000, 0x0E40, 0x0010) @@ -863,6 +882,7 @@ raise errors.RadioError("Failed to communicate with radio: %s" % e)
def uv3r_upload(radio): + """Talk to a UV3R and do an upload""" try: uv3r_prep(radio) return do_upload(radio, 0x0000, 0x0E40, 0x0010) @@ -871,7 +891,7 @@ except Exception, e: raise errors.RadioError("Failed to communicate with radio: %s" % e)
-uv3r_mem_format = """ +UV3R_MEM_FORMAT = """ #seekto 0x0010; struct { lbcd rx_freq[4]; @@ -920,6 +940,7 @@
@directory.register class UV3RRadio(KGUVD1PRadio): + """Baofeng UV-3R""" VENDOR = "Baofeng" MODEL = "UV-3R"
@@ -950,7 +971,7 @@ uv3r_upload(self)
def process_mmap(self): - self._memobj = bitwise.parse(uv3r_mem_format, self._mmap) + self._memobj = bitwise.parse(UV3R_MEM_FORMAT, self._mmap)
def get_memory(self, number): _mem = self._memobj.rx_memory[number - 1] @@ -962,7 +983,7 @@ return mem
mem.freq = int(_mem.rx_freq) * 10 - mem.offset = int(_mem.offset) * 10; + mem.offset = int(_mem.offset) * 10 mem.duplex = UV3R_DUPLEX[_mem.duplex] if mem.offset > 60000000: if mem.duplex == "+": diff -r db82df47f205 -r f805c420592f chirp/xml_ll.py --- a/chirp/xml_ll.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/xml_ll.py Fri Apr 27 14:35:41 2012 -0700 @@ -13,12 +13,12 @@ # 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 errors import re
-from chirp import chirp_common +from chirp import chirp_common, errors
def get_memory(doc, number): + """Extract a Memory object from @doc""" ctx = doc.xpathNewContext()
base = "//radio/memories/memory[@location=%i]" % number @@ -98,6 +98,7 @@ return mem
def set_memory(doc, mem): + """Set @mem in @doc""" ctx = doc.xpathNewContext()
base = "//radio/memories/memory[@location=%i]" % mem.number @@ -105,7 +106,7 @@ fields = ctx.xpathEval(base) if len(fields) > 1: raise errors.RadioError("%i memories claiming to be %i" % (len(fields), - number)) + mem.number)) elif len(fields) == 1: fields[0].unlinkNode()
@@ -197,6 +198,7 @@ dc.addContent(str(mem.dv_code))
def del_memory(doc, number): + """Remove memory @number from @doc""" path = "//radio/memories/memory[@location=%i]" % number ctx = doc.xpathNewContext() fields = ctx.xpathEval(path) @@ -206,11 +208,12 @@
def _get_bank(node): bank = chirp_common.Bank(node.prop("label")) - id = int(node.prop("id")) + ident = int(node.prop("id"))
- return id, bank + return ident, bank
def get_banks(doc): + """Return a list of banks from @doc""" path = "//radio/banks/bank" ctx = doc.xpathNewContext() fields = ctx.xpathEval(path) @@ -219,14 +222,15 @@ for field in fields: banks.append(_get_bank(field))
- def cmp(x, y): - return x[0] - y[0] + def _cmp(itema, itemb): + return itema[0] - itemb[0]
- banks.sort(cmp=cmp) + banks.sort(cmp=_cmp)
return [x[1] for x in banks]
def set_banks(doc, banklist): + """Set the list of banks in @doc""" path = "//radio/banks/bank" ctx = doc.xpathNewContext() fields = ctx.xpathEval(path) diff -r db82df47f205 -r f805c420592f chirp/yaesu_clone.py --- a/chirp/yaesu_clone.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirp/yaesu_clone.py Fri Apr 27 14:35:41 2012 -0700 @@ -18,10 +18,10 @@ from chirp import chirp_common, util, memmap, errors import time, os
-def safe_read(pipe, count, times=60): +def _safe_read(pipe, count): buf = "" first = True - for i in range(0, 60): + for _i in range(0, 60): buf += pipe.read(count - len(buf)) #print "safe_read: %i/%i\n" % (len(buf), count) if buf: @@ -34,17 +34,15 @@ print util.hexprint(buf) return buf
-def chunk_read(pipe, count, status_fn): +def _chunk_read(pipe, count, status_fn): block = 32 data = "" - first = True - for i in range(0, count, block): + for _i in range(0, count, block): data += pipe.read(block) if data: if data[0] == chr(CMD_ACK): data = data[1:] # Chew an echo'd ack if using a 2-pin cable #print "Chewed an ack" - first = False status = chirp_common.Status() status.msg = "Cloning from radio" status.max = count @@ -54,7 +52,7 @@ print "Read %i/%i" % (len(data), count) return data
-def _clone_in(radio): +def __clone_in(radio): pipe = radio.pipe
start = time.time() @@ -64,28 +62,28 @@ for block in radio._block_lengths: blocks += 1 if blocks == len(radio._block_lengths): - chunk = chunk_read(pipe, block, radio.status_fn) + chunk = _chunk_read(pipe, block, radio.status_fn) else: - chunk = safe_read(pipe, block) + chunk = _safe_read(pipe, block) pipe.write(chr(CMD_ACK)) if not chunk: raise errors.RadioError("No response from radio") data += chunk
- if len(data) != radio._memsize: + if len(data) != radio.get_memsize(): raise errors.RadioError("Received incomplete image from radio")
print "Clone completed in %i seconds" % (time.time() - start)
return memmap.MemoryMap(data)
-def clone_in(radio): +def _clone_in(radio): try: - return _clone_in(radio) + return __clone_in(radio) except Exception, e: raise errors.RadioError("Failed to communicate with the radio: %s" % e)
-def chunk_write(pipe, data, status_fn, block): +def _chunk_write(pipe, data, status_fn, block): delay = 0.03 count = 0 for i in range(0, len(data), block): @@ -101,15 +99,15 @@ status.cur = count status_fn(status)
-def _clone_out(radio): +def __clone_out(radio): pipe = radio.pipe - l = radio._block_lengths + block_lengths = radio._block_lengths total_written = 0
- def status(): + def _status(): status = chirp_common.Status() status.msg = "Cloning to radio" - status.max = l[0] + l[1] + l[2] + status.max = block_lengths[0] + block_lengths[1] + block_lengths[2] status.cur = total_written radio.status_fn(status)
@@ -121,28 +119,29 @@ blocks += 1 if blocks != len(radio._block_lengths): #print "Sending %i-%i" % (pos, pos+block) - pipe.write(radio._mmap[pos:pos+block]) + pipe.write(radio.get_mmap()[pos:pos+block]) buf = pipe.read(1) if buf and buf[0] != chr(CMD_ACK): buf = pipe.read(block) if not buf or buf[-1] != chr(CMD_ACK): raise Exception("Radio did not ack block %i" % blocks) else: - chunk_write(pipe, radio._mmap[pos:], - radio.status_fn, radio._block_size) + _chunk_write(pipe, radio.get_mmap()[pos:], + radio.status_fn, radio._block_size) pos += block
pipe.read(pos) # Chew the echo if using a 2-pin cable
print "Clone completed in %i seconds" % (time.time() - start)
-def clone_out(radio): +def _clone_out(radio): try: - return _clone_out(radio) + return __clone_out(radio) except Exception, e: raise errors.RadioError("Failed to communicate with the radio: %s" % e)
class YaesuChecksum: + """A Yaesu Checksum Object""" def __init__(self, start, stop, address=None): self._start = start self._stop = stop @@ -156,12 +155,14 @@ return ord(mmap[self._address])
def get_calculated(self, mmap): + """Return the calculated value of the checksum""" cs = 0 for i in range(self._start, self._stop+1): cs += ord(mmap[i]) return cs % 256
def update(self, mmap): + """Update the checksum with the data in @mmap""" mmap[self._address] = self.get_calculated(mmap)
def __str__(self): @@ -170,6 +171,7 @@ self._address)
class YaesuCloneModeRadio(chirp_common.CloneModeRadio): + """Base class for all Yaesu clone-mode radios""" _block_lengths = [8, 65536] _block_size = 8
@@ -181,10 +183,12 @@ return []
def update_checksums(self): + """Update the radio's checksums from the current memory map""" for checksum in self._checksums(): checksum.update(self._mmap)
def check_checksums(self): + """Validate the checksums stored in the memory map""" for checksum in self._checksums(): if checksum.get_existing(self._mmap) != \ checksum.get_calculated(self._mmap): @@ -192,13 +196,13 @@ print "Checksum %s: OK" % checksum
def sync_in(self): - self._mmap = clone_in(self) + self._mmap = _clone_in(self) self.check_checksums() self.process_mmap()
def sync_out(self): self.update_checksums() - clone_out(self) + _clone_out(self)
@classmethod def match_model(cls, filedata, filename): @@ -209,24 +213,3 @@ bm = self.get_bank_model() for bank in bm.get_memory_banks(mem): bm.remove_memory_from_bank(mem, bank) - - -if __name__ == "__main__": - import sys, serial - s = serial.Serial(port=sys.argv[1], baudrate=19200, timeout=5) - - d = s.read(20) - print util.hexprint(d) - - s.write(chr(0x06)) - - d = "" - while True: - c = s.read(32) - if not c: - break - d += c - - print len(d) - - diff -r db82df47f205 -r f805c420592f chirpui/clone.py --- a/chirpui/clone.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirpui/clone.py Fri Apr 27 14:35:41 2012 -0700 @@ -191,7 +191,7 @@ class CloneCancelledException(Exception): pass
-class CloneThread(chirp_common.KillableThread): +class CloneThread(threading.Thread): def __status(self, status): gobject.idle_add(self.__progw.status, status)
diff -r db82df47f205 -r f805c420592f chirpui/mainapp.py --- a/chirpui/mainapp.py Fri Apr 27 14:35:40 2012 -0700 +++ b/chirpui/mainapp.py Fri Apr 27 14:35:41 2012 -0700 @@ -887,7 +887,7 @@ else: from chirp import rfinder radio = rfinder.RFinderRadio(None) - radio.set_params(lat, lon, miles, email, passwd) + radio.set_params((lat, lon), miles, email, passwd) self.do_open_live(radio, read_only=True)
self.window.set_cursor(None)

pylint --rcfile=pylintrc --ignore=tmv71.py,tmv71_ll.py,thd72.py,\
Oops, I meant to also say:
1. All the tests still pass after this, but obviously it's extremely invasive and the potential for breakage is high 2. The patch reduced the number of output lines from 2300 to 1901 (many of the lines in the output aren't complaints and/or won't be addressed) 3. I found and fixed bugs numbering in the double-digits while doing this, mostly due to typos in error paths and other such silly things. That makes me think it's worth the time and effort.
participants (1)
-
Dan Smith