[chirp_devel] [PATCH] Add RFinder support as an import source
Dan Smith
Tue May 17 15:28:59 PDT 2011
# HG changeset patch
# User Dan Smith <dsmith at danplanet.com>
# Date 1305671333 25200
# Node ID bf2e12513c27a3b685009ad6561850c8a83f7fca
# Parent 72131a755f0b41dc94556813517da998eeeb1759
Add RFinder support as an import source
RFinder is a subscription-based repeater database. This adds an
item under the Radio menu for "Import from RFinder".
diff -r 72131a755f0b -r bf2e12513c27 build/version
--- a/build/version Mon May 16 16:41:09 2011 -0700
+++ b/build/version Tue May 17 15:28:53 2011 -0700
@@ -1,1 +1,1 @@
-0.1.11
+0.1.11rfinder3
diff -r 72131a755f0b -r bf2e12513c27 chirp/__init__.py
--- a/chirp/__init__.py Mon May 16 16:41:09 2011 -0700
+++ b/chirp/__init__.py Tue May 17 15:28:53 2011 -0700
@@ -15,4 +15,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-CHIRP_VERSION="0.1.11"
+CHIRP_VERSION="0.1.11rfinder2"
diff -r 72131a755f0b -r bf2e12513c27 chirp/directory.py
--- a/chirp/directory.py Mon May 16 16:41:09 2011 -0700
+++ b/chirp/directory.py Tue May 17 15:28:53 2011 -0700
@@ -22,7 +22,7 @@
from chirp import kenwood_live, tmv71, thd72
from chirp import alinco
from chirp import wouxun
-from chirp import xml, chirp_common, csv, util
+from chirp import xml, chirp_common, csv, util, rfinder
DRV_TO_RADIO = {
@@ -116,6 +116,12 @@
raise Exception("Unsupported model")
def get_radio_by_image(image_file):
+ if image_file.startswith("rfinder://"):
+ method, _, email, passwd, lat, lon = image_file.split("/")
+ rf = rfinder.RFinderRadio(None)
+ rf.set_params(float(lat), float(lon), email, passwd)
+ return rf
+
if image_file.lower().endswith(".chirp"):
return xml.XMLRadio(image_file)
diff -r 72131a755f0b -r bf2e12513c27 chirp/rfinder.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/rfinder.py Tue May 17 15:28:53 2011 -0700
@@ -0,0 +1,161 @@
+# Copyright 2011 Dan Smith <dsmith at danplanet.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 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 urllib
+import hashlib
+
+from chirp import chirp_common, CHIRP_VERSION
+
+SCHEMA = [
+ "ID",
+ "TRUSTEE",
+ "OUTFREQUENCY",
+ "CITY",
+ "STATE",
+ "COUNTRY",
+ "LATITUDE",
+ "LONGITUDE",
+ "CLUB",
+ "DESCRIPTION",
+ "NOTES",
+ "RANGE",
+ "OFFSETSIGN",
+ "OFFSETFREQ",
+ "PL",
+ "DCS",
+ "REPEATERTYPE",
+ "BAND",
+ "IRLP",
+ "ECHOLINK",
+ "DOC_ID",
+ ]
+
+class RFinderParser:
+ def __init__(self):
+ self.__memories = []
+ self.__cheat = {}
+
+ def fetch_data(self, lat, lon, email, passwd):
+ args = {
+ "lat" : "%7.5f" % lat,
+ "lon" : "%8.5f" % lon,
+ "email" : urllib.quote_plus(email),
+ "pass" : hashlib.md5(passwd).hexdigest(),
+ "vers" : "CH%s" % CHIRP_VERSION,
+ }
+
+ url = "http://sync.rfinder.net/radio/repeaters.nsf/getlocal?openagent&%s"\
+ % "&".join(["%s=%s" % (k,v) for k,v in args.items()])
+
+ f = urllib.urlopen(url)
+ data = f.read()
+ f.close()
+
+ return data
+
+ def parse_line(self, line):
+ mem = chirp_common.Memory()
+
+ _vals = line.split("|")
+
+ vals = {}
+ for i in range(0, len(SCHEMA)):
+ vals[SCHEMA[i]] = _vals[i]
+ self.__cheat = vals
+
+ mem.name = vals["TRUSTEE"]
+ mem.freq = float(vals["OUTFREQUENCY"])
+ if vals["OFFSETSIGN"] != "X":
+ mem.duplex = vals["OFFSETSIGN"]
+ if vals["OFFSETFREQ"]:
+ mem.offset = abs(float(vals["OFFSETFREQ"]))
+
+ if vals["PL"] and vals["PL"] != "0":
+ mem.rtone = float(vals["PL"])
+ mem.tmode = "Tone"
+ elif vals["DCS"] and vals["DCS"] != "0":
+ mem.dtcs = int(vals["DCS"])
+ mem.tmode = "DTCS"
+
+ return mem
+
+ def parse_data(self, data):
+ number = 1
+ for line in data.split("\n"):
+ if line.startswith("<"):
+ continue
+ elif not line.strip():
+ continue
+ try:
+ mem = self.parse_line(line)
+ mem.number = number
+ number += 1
+ self.__memories.append(mem)
+ except Exception, e:
+ print "Error in record %s:" % self.__cheat["DOC_ID"]
+ print e
+ print self.__cheat
+ print "\n\n"
+
+ def get_memories(self):
+ return self.__memories
+
+class RFinderRadio(chirp_common.Radio):
+ VENDOR = "ITWeRKS"
+ MODEL = "RFinder"
+
+ def __init__(self, *args, **kwargs):
+ chirp_common.Radio.__init__(self, *args, **kwargs)
+
+ self._lat = 0
+ self._lon = 0
+ self._call = ""
+ self._email = ""
+
+ self._rfp = None
+
+ def set_params(self, lat, lon, call, email):
+ self._lat = lat
+ self._lon = lon
+ self._call = call
+ self._email = email
+
+ def do_fetch(self):
+ self._rfp = RFinderParser()
+ self._rfp.parse_data(self._rfp.fetch_data(self._lat, self._lon, self._call, self._email))
+
+ def get_features(self):
+ if not self._rfp:
+ self.do_fetch()
+
+ rf = chirp_common.RadioFeatures()
+ rf.memory_bounds = (1, len(self._rfp.get_memories()))
+ return rf
+
+ def get_memory(self, number):
+ if not self._rfp:
+ self.do_fetch()
+
+ return self._rfp.get_memories()[number-1]
+
+if __name__ == "__main__":
+ import sys
+
+ rfp = RFinderParser()
+ data = rfp.fetch_data(45.525, -122.9164, "KK7DS", "dsmith at danplanet.com")
+ rfp.parse_data(data)
+
+ for m in rfp.get_memories():
+ print m
diff -r 72131a755f0b -r bf2e12513c27 chirpui/mainapp.py
--- a/chirpui/mainapp.py Mon May 16 16:41:09 2011 -0700
+++ b/chirpui/mainapp.py Tue May 17 15:28:53 2011 -0700
@@ -75,7 +75,7 @@
for i in ["cancelq"]:
set_action_sensitive(i, eset is not None and not mmap_sens)
- for i in ["export", "import", "close", "columns"]:
+ for i in ["export", "import", "close", "columns", "rfinder"]:
set_action_sensitive(i, eset is not None)
def ev_status(self, editorset, msg):
@@ -394,6 +394,67 @@
count = eset.do_import(filen)
reporting.report_model_usage(eset.rthread.radio, "import", count > 0)
+ def do_rfinder_prompt(self):
+ fields = {"1Email" : (gtk.Entry(),
+ lambda x: "@" in x),
+ "2Password" : (gtk.Entry(),
+ lambda x: x),
+ "3Latitude" : (gtk.Entry(),
+ lambda x: float(x) < 90 and float(x) > -90),
+ "4Longitude": (gtk.Entry(),
+ lambda x: float(x) < 180 and float(x) > -180),
+ }
+
+ d = inputdialog.FieldDialog(title="RFinder Login", 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:], "rfinder") 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(), "rfinder")
+ 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_rfinder(self):
+ self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ if not self.do_rfinder_prompt():
+ self.window.set_cursor(None)
+ return
+
+ lat = CONF.get_float("Latitude", "rfinder")
+ lon = CONF.get_float("Longitude", "rfinder")
+ passwd = CONF.get("Password", "rfinder")
+ email = CONF.get("Email", "rfinder")
+
+ # 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("rfinder://%s/%s/%f/%f" % (email, passwd, lat, lon))
+ reporting.report_model_usage(eset.rthread.radio, "import", count > 0)
+
+ self.window.set_cursor(None)
+
def do_export(self):
types = [("CSV Files (*.csv)", "csv"),
("CHIRP Files (*.chirp)", "chirp"),
@@ -537,6 +598,8 @@
self.do_close()
elif action == "import":
self.do_import()
+ elif action == "rfinder":
+ self.do_rfinder()
elif action == "export":
self.do_export()
elif action == "about":
@@ -591,7 +654,8 @@
<menu action="radio" name="radio">
<menuitem action="download"/>
<menuitem action="upload"/>
- <menu action="recent" name="recent"/>
+ <separator/>
+ <menuitem action="rfinder"/>
<separator/>
<menuitem action="cancelq"/>
</menu>
@@ -622,6 +686,7 @@
('upload', None, "Upload To Radio", "<Alt>u", None, self.mh),
('import', None, 'Import', "<Alt>i", None, self.mh),
('export', None, 'Export', "<Alt>e", 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),
('cancelq', gtk.STOCK_STOP, None, "Escape", None, self.mh),
More information about the chirp_devel
mailing list