[chirp_devel] [PATCH] Add import from RadioReference.com. Feature #114

Tom Hayward
Tue Apr 10 20:43:37 PDT 2012


# HG changeset patch
# User Tom Hayward <tom at tomh.us>
# Date 1334115754 21600
# Node ID 7f61e3dfb31f2be17279e79e01e85f05d98bef13
# Parent  806b80ebe58e7d62f62ee9d6e9ab0fc929236e49
Add import from RadioReference.com. Feature #114

diff -r 806b80ebe58e -r 7f61e3dfb31f chirp/chirp_common.py
--- a/chirp/chirp_common.py	Tue Apr 10 10:00:18 2012 -0600
+++ b/chirp/chirp_common.py	Tue Apr 10 21:42:34 2012 -0600
@@ -60,7 +60,7 @@
     "->DTCS",
 ]
 
-MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR"]
+MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR", "P25"]
 
 STD_6M_OFFSETS = [
     (51620000, 51980000, -500000),
diff -r 806b80ebe58e -r 7f61e3dfb31f chirp/directory.py
--- a/chirp/directory.py	Tue Apr 10 10:00:18 2012 -0600
+++ b/chirp/directory.py	Tue Apr 10 21:42:34 2012 -0600
@@ -1,4 +1,5 @@
 # Copyright 2010 Dan Smith <dsmith at danplanet.com>
+# Copyright 2012 Tom Hayward <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
@@ -17,7 +18,7 @@
 import tempfile
 
 from chirp import icf
-from chirp import chirp_common, util, rfinder, errors
+from chirp import chirp_common, util, rfinder, radioreference, errors
 
 def radio_class_id(cls):
     ident = "%s_%s" % (cls.VENDOR, cls.MODEL)
@@ -79,6 +80,12 @@
         raise Exception("Unsupported model")
 
 def get_radio_by_image(image_file):
+    if image_file.startswith("radioreference://"):
+        method, _, 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 = image_file.split("/")
         rf = rfinder.RFinderRadio(None)
diff -r 806b80ebe58e -r 7f61e3dfb31f chirp/radioreference.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/radioreference.py	Tue Apr 10 21:42:34 2012 -0600
@@ -0,0 +1,156 @@
+# Copyright 2012 Tom Hayward <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/>.
+
+from chirp import chirp_common, CHIRP_VERSION, errors
+try:
+    from suds.client import Client
+    HAVE_SUDS = True
+except ImportError:
+    HAVE_SUDS = False
+
+MODES = {
+    "FM"    : "FM",
+    "AM"    : "AM",
+    "FMN"   : "NFM",
+    "D-STAR": "DV",
+    "USB"   : "USB",
+    "LSB"   : "LSB",
+    "P25"   : "P25",
+}
+
+class RadioReferenceRadio(chirp_common.Radio):
+    VENDOR = "Radio Reference LLC"
+    MODEL = "RadioReference.com"
+
+    URL = "http://api.radioreference.com/soap2/?wsdl"
+    APPKEY = "46785108"
+
+    def __init__(self, *args, **kwargs):
+        chirp_common.Radio.__init__(self, *args, **kwargs)
+
+        if not HAVE_SUDS:
+            raise errors.RadioError(
+                "Suds library required for RadioReference.com import.\n" + \
+                "Try installing your distribution's python-suds package.")
+
+        self._auth = {"appKey": self.APPKEY, "username": "", "password": ""}
+        self._client = Client(self.URL)
+        self._freqs = None
+        self._modes = None
+
+    def set_params(self, zip, username, password):
+        self._zip = zip
+        self._auth["username"] = username
+        self._auth["password"] = password
+
+    def do_fetch(self):
+        """Fetches frequencies for all subcategories in a county."""
+        self._freqs = []
+
+        zipcode = self._client.service.getZipcodeInfo(self._zip, self._auth)
+        county = self._client.service.getCountyInfo(zipcode.ctid, self._auth)
+        for cat in county.cats:
+            print "Fetching category:", cat.cName
+            for subcat in cat.subcats:
+                print "\t", subcat.scName
+                result = self._client.service.getSubcatFreqs(subcat.scid, self._auth)
+                self._freqs += result
+        for agency in county.agencyList:
+            agency = self._client.service.getAgencyInfo(agency.aid, self._auth)
+            for cat in agency.cats:
+                print "Fetching category:", cat.cName
+                for subcat in cat.subcats:
+                    print "\t", subcat.scName
+                    result = self._client.service.getSubcatFreqs(subcat.scid, self._auth)
+                    self._freqs += result
+
+    def get_features(self):
+        if not self._freqs:
+            self.do_fetch()
+
+        rf = chirp_common.RadioFeatures()
+        rf.memory_bounds = (0, len(self._freqs)-1)
+        return rf
+
+    def get_raw_memory(self, number):
+        return repr(self._freqs[number])
+
+    def get_memory(self, number):
+        if not self._freqs:
+            self.do_fetch()
+
+        freq = self._freqs[number]
+
+        mem = chirp_common.Memory()
+        mem.number = number
+
+        mem.name = freq.alpha or freq.descr or ""
+        mem.freq = chirp_common.parse_freq(str(freq.out))
+        if freq["in"] == 0.0:
+            mem.duplex = ""
+        else:
+            mem.duplex = "split"
+            mem.offset = chirp_common.parse_freq(str(freq["in"]))
+        if freq.tone is not None:
+            if str(freq.tone) == "CSQ": # Carrier Squelch
+                mem.tmode = ""
+            else:
+                try:
+                    tone, tmode = freq.tone.split(" ")
+                except:
+                    tone, tmode = None, None
+                if tmode == "PL":
+                    mem.tmode = "TSQL"
+                    mem.rtone = mem.ctone = float(tone)
+                elif tmode == "DPL":
+                    mem.tmode = "DTCS"
+                    mem.dtcs = int(tone)
+                else:
+                    print "Error: unsupported tone"
+                    print freq
+        try:
+            mem.mode = self._get_mode(freq.mode)
+        except KeyError:
+            # skip memory if mode is unsupported
+            mem.empty = True
+            return mem
+        mem.comment = freq.descr.strip()
+
+        return mem
+
+    def _get_mode(self, modeid):
+        if not self._modes:
+            self._modes = {}
+            for mode in self._client.service.getMode("0", self._auth):
+                # sax.text.Text cannot be coerced directly to int
+                self._modes[int(str(mode.mode))] = str(mode.modeName)
+        return MODES[self._modes[int(str(modeid))]]
+
+
+def main():
+    """
+    Usage:
+    cd ~/src/chirp.hg
+    python ./chirp/radioreference.py [ZIPCODE] [USERNAME] [PASSWORD]
+    """
+    import sys
+    rrr = RadioReferenceRadio(None)
+    rrr.set_params(zip=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)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff -r 806b80ebe58e -r 7f61e3dfb31f chirpui/mainapp.py
--- a/chirpui/mainapp.py	Tue Apr 10 10:00:18 2012 -0600
+++ b/chirpui/mainapp.py	Tue Apr 10 21:42:34 2012 -0600
@@ -1,4 +1,5 @@
 # Copyright 2008 Dan Smith <dsmith at danplanet.com>
+# Copyright 2012 Tom Hayward <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
@@ -96,7 +97,7 @@
             set_action_sensitive(i, eset is not None and not mmap_sens)
         
         for i in ["export", "import", "close", "columns", "rbook", "rfinder",
-                  "stock", "move_up", "move_dn", "exchange",
+                  "stock", "move_up", "move_dn", "exchange", "radioreference",
                   "cut", "copy", "paste", "delete", "viewdeveloper"]:
             set_action_sensitive(i, eset is not None)
 
@@ -872,6 +873,60 @@
 
         self.window.set_cursor(None)
 
+    def do_radioreference_prompt(self):
+        fields = {"1Username"    : (gtk.Entry(), lambda x: x),
+                  "2Password"    : (gtk.Entry(), lambda x: x),
+                  "3Zipcode"     : (gtk.Entry(), lambda x: x),
+                  }
+
+        d = inputdialog.FieldDialog(title="RadioReference.com Query", parent=self)
+        for k in sorted(fields.keys()):
+            d.add_field(k[1:], fields[k][0])
+            fields[k][0].set_text(CONF.get(k[1:], "radioreference") or "")
+            fields[k][0].set_visibility(k != "2Password")
+
+        while d.run() == gtk.RESPONSE_OK:
+            valid = True
+            for k in sorted(fields.keys()):
+                widget, validator = fields[k]
+                try:
+                    if validator(widget.get_text()):
+                        CONF.set(k[1:], widget.get_text(), "radioreference")
+                        continue
+                except Exception:
+                    pass
+                common.show_error("Invalid value for %s" % k[1:])
+                valid = False
+                break
+
+            if valid:
+                d.destroy()
+                return True
+
+        d.destroy()
+        return False
+
+    def do_radioreference(self):
+        self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+        if not self.do_radioreference_prompt():
+            self.window.set_cursor(None)
+            return
+
+        username = CONF.get("Username", "radioreference")
+        passwd = CONF.get("Password", "radioreference")
+        zipcode = CONF.get("Zipcode", "radioreference")
+
+        # Do this in case the import process is going to take a while
+        # to make sure we process events leading up to this
+        gtk.gdk.window_process_all_updates()
+        while gtk.events_pending():
+            gtk.main_iteration(False)
+
+        eset = self.get_current_editorset()
+        count = eset.do_import("radioreference://%s/%s/%s" % (zipcode, username, passwd))
+
+        self.window.set_cursor(None)
+
     def do_export(self):
         types = [(_("CSV Files") + " (*.csv)", "csv"),
                  (_("CHIRP Files") + " (*.chirp)", "chirp"),
@@ -1079,6 +1134,8 @@
             self.do_import()
         elif action == "rfinder":
             self.do_rfinder()
+        elif action == "radioreference":
+            self.do_radioreference()
         elif action == "export":
             self.do_export()
         elif action == "rbook":
@@ -1151,6 +1208,7 @@
     <menu action="radio" name="radio">
       <menuitem action="download"/>
       <menuitem action="upload"/>
+      <menuitem action="radioreference"/>
       <menuitem action="rbook"/>
       <menuitem action="rfinder"/>
       <menu action="stock" name="stock"/>
@@ -1197,6 +1255,7 @@
             ('upload', None, _("Upload To Radio"), "<Alt>u", None, self.mh),
             ('import', None, _("Import"), "<Alt>i", None, self.mh),
             ('export', None, _("Export"), "<Alt>x", None, self.mh),
+            ('radioreference', None, _("Import from RadioReference.com"), None, None, self.mh),
             ('rfinder', None, _("Import from RFinder"), None, None, self.mh),
             ('export_chirp', None, _("CHIRP Native File"), None, None, self.mh),
             ('export_csv', None, _("CSV File"), None, None, self.mh),



More information about the chirp_devel mailing list