[chirp_devel] [PATCH] Add RFinder support as an import source
# HG changeset patch # User Dan Smith dsmith@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@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%22%5C + % "&".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@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),
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".
This database is not publicly available right now, but will soon become a subscription-based service. Right now, they don't check the credentials of queries, which means you can put anything for the email/password fields and query the database. After their announcement at Dayton, they will secure it and start charging a (small) fee for subscription.
However, I've gotten their permission to send this out to you guys while it's still open to the world for the purposes of testing the code. Let me know what you think.
Be advised that their "server" is apparently a 386/16 and takes a long time to return queries :)
On 5/17/2011 6:32 PM, Dan Smith wrote:
RFinder is a subscription-based repeater database. This adds an item under the Radio menu for "Import from RFinder".
This database is not publicly available right now, but will soon become a subscription-based service.
Have anyone here looked at repeaterbook.com at all?
It's a fairly open web based system that allows anyone to submit updates which get routed to volunteer "data admins" by geography. It's got some very useful functionality like proximity search -- try your zip code:
http://www.repeaterbook.com/repeaters/prox.php
It also has some areas that need work. The guy who wrote the site is a self-taught php developer. He's been pretty open to the suggestions/changes that I've proposed.
There are a couple of export formats currently supported. I think we have a reasonable chance of getting chirp csv export format added. (Chirp native xml is in the range of possibilities too of course)
There are lots of repeater databases out there, but most seem to have put little to no thought into the data maintenance process. All the effort seems to go into the GUI and the database/site quickly looses relevance as the data slowly rots.
Thoughts? Comments?
--Rob
Have anyone here looked at repeaterbook.com at all?
Heh, free, a sane web interface, and they have an iOS app too. I wonder how comprehensive the database is. It also makes me wonder what the RFinder value proposition will be :)
It also has some areas that need work. The guy who wrote the site is a self-taught php developer. He's been pretty open to the suggestions/changes that I've proposed.
It returns queries semi-instantly, so at least he has a grasp on the backend. There is some information in there on "paper repeaters" in my area that are coordinated, but never got put up. Looks like some of the data was imported from the local coordination body (gotta get it somehow, I suppose).
There are a couple of export formats currently supported. I think we have a reasonable chance of getting chirp csv export format added. (Chirp native xml is in the range of possibilities too of course)
That would be great. Maybe you can bring it up if you already have a relationship with him? It'd be even nicer to have a web services interface to the query, but that would cut down on his ad revenue.
There are lots of repeater databases out there, but most seem to have put little to no thought into the data maintenance process.
Yeah, I've just never been one to preload my radio like that before traveling, so I'm usually not on the lookout for stuff like this. I guess some people are, though.
All the effort seems to go into the GUI and the database/site quickly looses relevance as the data slowly rots.
Thoughts? Comments?
Yeah, the RFinder guys seem to have no consistency checks in the data they put in (or imported, or whatever). Tone values with transposed digits and what-not. Does the repeater book site handle that better? Requiring a human editor is a nice idea, but weeding out invalid tones and things of that sort is much better done in code, I'd say.
Unless nobody finds it interesting/useful, I think the integrated RFinder client functionality in chirp is still worthwhile. Would you agree, or not?
participants (2)
-
Dan Smith
-
Robert Terzi