[chirp_devel] [PATCH] Add import support for Kenwood *.hmk files

Tom Hayward
Tue Apr 3 15:22:07 PDT 2012


# HG changeset patch
# User Tom Hayward <tom at tomh.us>
# Date 1333491695 21600
# Node ID 1bb3df3d624fa4ee7dfc959b1dc6bd42a5e019f0
# Parent  91be43cc7ac4063d921b0d8eb0bdd73fdb9aa9fa
Add import support for Kenwood *.hmk files.

diff -r 91be43cc7ac4 -r 1bb3df3d624f chirp/directory.py
--- a/chirp/directory.py	Tue Apr 03 11:19:10 2012 -0700
+++ b/chirp/directory.py	Tue Apr 03 16:21:35 2012 -0600
@@ -91,6 +91,9 @@
     if image_file.lower().endswith(".csv"):
         return get_radio("Generic_CSV")(image_file)
 
+    if image_file.lower().endswith(".hmk"):
+        return get_radio("Kenwood_HMK")(image_file)
+
     if icf.is_9x_icf(image_file):
         return get_radio("Icom_IC91_92AD_ICF")(image_file)
 
diff -r 91be43cc7ac4 -r 1bb3df3d624f chirp/kenwood_hmk.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/kenwood_hmk.py	Tue Apr 03 16:21:35 2012 -0600
@@ -0,0 +1,227 @@
+# Copyright 2008 Dan Smith <dsmith at danplanet.com>
+# Copyright 2012 Tom Haywward <tom at tomh.us>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import csv
+
+from chirp import chirp_common, errors, directory
+
+class OmittedHeaderError(Exception):
+    pass
+
+ at directory.register
+class HMKRadio(chirp_common.CloneModeRadio):
+    VENDOR = "Kenwood"
+    MODEL = "HMK"
+    FILE_EXTENSION = "hmk"
+
+    ATTR_MAP = {
+        "!!Ch"         : (int,   "number"),
+        "M.Name"       : (str,   "name"),
+        "Rx Freq."     : (chirp_common.parse_freq, "freq"),
+        "Shift/Split"  : (str,   "duplex"),
+        "Offset"       : (chirp_common.parse_freq, "offset"),
+        "T/CT/DCS"     : (str,   "tmode"),
+        "TO Freq."     : (float, "rtone"),
+        "CT Freq."     : (float, "ctone"),
+        "DCS Code"     : (int,   "dtcs"),
+        "Mode"         : (str,   "mode"),
+        "Tx Freq."     : (chirp_common.parse_freq, "txfreq"),
+        "Rx Step"      : (float, "tuning_step"),
+        "L.Out"        : (str,   "skip"),
+        }
+
+    TMODE_MAP = {
+        "Off":  "",
+        "T":    "Tone",
+        "CT":   "TSQL",
+        "DCS":  "DTCS",
+        "":     "Cross",
+        }
+
+    SKIP_MAP = {
+        "Off":  "",
+        "On":   "S",
+        }
+
+    DUPLEX_MAP = {
+        " ":    "",
+        "S":    "split",
+        "+":    "+",
+        "-":    "-",
+    }
+
+    def _blank(self):
+        self.errors = []
+        self.memories = []
+        for i in range(0, 1000):
+            m = chirp_common.Memory()
+            m.number = i
+            m.empty = True
+            self.memories.append(m)
+
+    def __init__(self, pipe):
+        chirp_common.CloneModeRadio.__init__(self, None)
+
+        self._filename = pipe
+        if self._filename and os.path.exists(self._filename):
+            self.load()
+        else:
+            self._blank()
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_bank = False
+        rf.has_dtcs_polarity = False
+        rf.memory_bounds = (0, len(self.memories))
+        rf.has_infinite_number = True
+
+        rf.valid_modes = list(chirp_common.MODES)
+        rf.valid_tmodes = list(chirp_common.TONE_MODES)
+        rf.valid_duplexes = ["", "-", "+", "split"]
+        rf.valid_tuning_steps = list(chirp_common.TUNING_STEPS)
+        rf.valid_bands = [(1, 10000000000)]
+        rf.valid_skips = ["", "S"]
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_name_length = 999
+
+        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()
+        odd_split = False
+            
+        for header, (typ, attr) in self.ATTR_MAP.items():
+            try:
+                val = self._get_datum_by_header(headers, line, header)
+                if not val and typ == int:
+                    val = None
+                elif attr == "duplex":
+                    val = typ(self.DUPLEX_MAP[val])
+                    if val == "split":
+                        odd_split = True
+                elif attr == "skip":
+                    val = typ(self.SKIP_MAP[val])
+                elif attr == "tmode":
+                    val = typ(self.TMODE_MAP[val])
+                elif attr == 'txfreq':
+                    tx_freq = typ(val)
+                else:
+                    val = typ(val)
+                if hasattr(mem, attr):
+                    setattr(mem, attr, val)
+            except OmittedHeaderError, e:
+                pass
+            except Exception, e:
+                raise Exception("[%s] %s" % (attr, e))
+
+        if odd_split:
+            mem.offset = tx_freq
+
+        return mem
+
+    def load(self, filename=None):
+        if filename is None and self._filename is None:
+            raise errors.RadioError("Need a location to load from")
+
+        if filename:
+            self._filename = filename
+
+        self._blank()
+
+        f = file(self._filename, "r")
+        for i in range(0, 10):
+            f.readline().strip()
+
+        #f.seek(0, 0)
+        reader = csv.reader(f, delimiter=chirp_common.SEPCHAR, quotechar='"')
+
+        good = 0
+        lineno = 0
+        for line in reader:
+            lineno += 1
+            if lineno == 1:
+                header = line
+                continue
+
+            if len(header) > len(line):
+                print "Line %i has %i columns, expected %i" % (lineno,
+                                                               len(line),
+                                                               len(header))
+                self.errors.append("Column number mismatch on line %i" % lineno)
+                continue
+
+            try:
+                mem = self._parse_csv_data_line(header, line)
+                if mem.number is None:
+                    raise Exception("Invalid Location field" % lineno)
+            except Exception, e:
+                print "Line %i: %s" % (lineno, e)
+                self.errors.append("Line %i: %s" % (lineno, e))
+                continue
+
+            self.__grow(mem.number)
+            self.memories[mem.number] = mem
+            good += 1
+
+        if not good:
+            print self.errors
+            raise errors.InvalidDataError("No channels found")
+
+    def load_mmap(self, filename):
+        return self.load(filename)
+
+    def get_memories(self, lo=0, hi=999):
+        return [x for x in self.memories if x.number >= lo and x.number <= hi]
+
+    def get_memory(self, number):
+        try:
+            return self.memories[number]
+        except:
+            raise errors.InvalidMemoryLocation("No such memory %s" % number)
+
+    def __grow(self, target):
+        delta = target - len(self.memories)
+        if delta < 0:
+            return
+
+        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)
+
+    def get_raw_memory(self, number):
+        return ",".join(self.memories[number].to_csv())
diff -r 91be43cc7ac4 -r 1bb3df3d624f chirpui/mainapp.py
--- a/chirpui/mainapp.py	Tue Apr 03 11:19:10 2012 -0700
+++ b/chirpui/mainapp.py	Tue Apr 03 16:21:35 2012 -0600
@@ -667,6 +667,7 @@
                  (_("CSV Files") + " (*.csv)", "*.csv"),
                  (_("EVE Files (VX5)") + " (*.eve)", "*.eve"),
                  (_("ICF Files") + " (*.icf)", "*.icf"),
+                 (_("Kenwood HMK Files") + " (*.hmk)", "*.hmk"),
                  (_("VX5 Commander Files") + " (*.vx5)", "*.vx5"),
                  (_("VX7 Commander Files") + " (*.vx7)", "*.vx7")]
         filen = platform.get_platform().gui_open_file(types=types)



More information about the chirp_devel mailing list