Wie in Teil 1 bereits kurz erklärt, soll das Arduino-Radio mit dem TEA5767-FM-Chip die UKW-Senderliste passend zum eigenen Standort automatisch erhalten. Im ersten Schritt wird daher zunächst versucht, den eigenen Standort zu bestimmen. Dafür braucht es keinen GPS-Sensor. Schon die IP-Adresse liefert hinreichend gut den Standort, zumindest auf einige Kilometer genau. Das genügt, um die Radio-Sendemasten in der Nähe zu finden. Hinweise darüber liefert die Webseite fmscan.org: Die Seite listet für einen beliebigen Ort die empfangbaren Radiosender auf.
Für die Standortbestimmung per IP-Adresse gibt es einige Web-Datenbanken. Viele davon können kostenlos genutzt werden. So ganz zuverlässig ist das Ganze nicht, besonders, wenn der Internetanschluss von einem internationalen Anbieter genutzt werden. Ob es funktioniert, muss man einfach ausprobieren. Ein Dienst, der bei mir gut funktioniert hat, ist ip-api.com. Der Dienst liefert ausführliche Informationen zum Standort der abgefragten IP-Adresse und gibt sie auf Wunsch im JSON Format zurück. Das Format hat den Vorteil, dass es die Daten lesbar und in strukturierter Form vorhält. Mit einer entsprechenden Parser-Bibiliothek ist es sehr einfach, die Daten aus einer JSON-Datei in Skripten zu verwenden.
Am Ende des Beitrags findet sich das python-Skript, mit dem der Standort bestimmt und die Senderliste von fmscan.org geladen wird. Der Code für die Standortbestimmung ist recht kurz. Er basiert auf den url*-Bibiliotheken (Zeile 3 – 6) und dem JSON-Parser (Zeile 7). In Zeile 25 wird die Webseite von ip-api.com geladen und die Daten im JSON angefordert. Schon in der nächsten Zeile wird das Ergebnis mit dem JSON-Parser ausgelesen. Der Webdienst holt die IP-Adresse automatisch aus der Anfrage, so dass sie nicht extra als Parameter übergeben werden muss. In der Antwort ist der Name der Stadt sowie die Geokoordinaten (Länge = longitude und Breite = latitude) enthalten.
Der Rest des Skriptes kümmert sich um die Abfrage der Senderliste von fmscan.org. Dieser Teil ist etwas tricky, da die Daten aus der Javascript-lastigen Webseite extrahiert werden müssen. Dafür muss auf Screen-Scraping zurückgegriffen werden. Die Bibliothek dryscape übernimmt dafür das komplette Rendern einer Webseite, die zudem auch per Javascript dynamisch generierte Inhalte enthalten darf. In Zeile 41 wird eine Session gestartet, in der die Eingaben in Formulare erzeugt und Mausklicks auf Links emuliert werden. So wird in Zeile 51 bis 54 die Stadt gesucht, die im ersten Teil des Skriptes anhand der IP-Adresse identifiziert wurde.
Die so generierte Webseite kann, wie in Zeile 57 auskommentiert, als PNG-Datei abgespeichert werden. Danach folgen XPath-Abfragen, um in der Seite zu navigieren und die Daten zu extrahieren. Zunächst wird aus dem Suchergebnis nach der Stadt diejenige ausgewählt, die am nächsten an den Geokoordinaten aus der IP-Abfrage liegt (Zeile 67 bis 79). In Zeile 81 wird für diese Stadt eine Abfrage der verfügbaren Sendestationen durchgeführt. Aus der zurückgegebenen Tabelle werden die Stationen ausgelesen, nach Sendestärke sortiert und in eine Datei geschrieben.
Das python-Skript wird auf dem RasPi ausgeführt und liefert die Senderliste, die im Anschluss an den Arduino übertragen wird. Wie das geht, wird im nächsten Teil 3 gezeigt.
#!/bin/env python # -*- codepage: utf-8 -*- import urllib import urllib2 from urllib2 import urlopen from contextlib import closing import json import dryscrape import re from decimal import Decimal from operator import itemgetter # # Geolokation per IP-Adresse # # stadt = 'Berlin' # longitude = '13.4584' # latitude = '52.4975' stadt = '' longitude = '' latitude = '' url ='http://ip-api.com/json' try: with closing(urlopen(url)) as response: location = json.load(response) print location stadt = location['city'] longitude = location['lon'] latitude = location['lat'] print stadt print longitude print latitude except: print("Location could not be determined automatically") # # Suche lokaler Radiosender mit fmscan.org # url = 'http://www.fmscan.org/' sess = dryscrape.Session(base_url = url) # we don't need images sess.set_attribute('auto_load_images', False) # visit homepage and search for a term sess.visit('/') freq = sess.at_xpath('//a[@href="form.php?m=s&ex=0"]') freq.click() q = sess.at_xpath('//*[@name="search"]') search_term = stadt q.set(search_term) q.form().submit() # sess.render('geo.png') # print "Screenshot written to 'geo.png'" locations = [] # extract all links for link in sess.xpath('//a[@href]'): url = link['href'] m = re.search('qthset=([\w\s\./,]+)&lset=([-]*\d+\.\d+)&bset=([-]*\d+\.\d+)', url) if m is not None: # print 'Stadt:', m.group(1),'longitude:', m.group(2), 'latitude:', m.group(3) diff = abs(Decimal(longitude) - Decimal(m.group(2))) + abs(Decimal(latitude) - Decimal(m.group(3))) locations.append((url, m.group(1), m.group(2), m.group(3), diff)) s = sorted(locations, key=itemgetter(4)) print 'In der Datenbank gefunden:' print 'url:', s[0][0] print 'Stadt:', s[0][1] print 'longitude:', s[0][2] print 'latitude:', s[0][3] print 'Entfernung:', "{:5.5f}".format(s[0][4]), "°" url = s[0][0] sess.visit(url) g = sess.at_xpath('//input[@value="Generate"]') g.form().submit() stations = [('Freq','RDS','Programm','Distanz','dBuV')] rows = sess.xpath('//div[2]/table[1]/tbody/tr') for row in rows: if (len(row.xpath('./td/a/u'))>0): freq = row.xpath('./td/a/u')[0].text() else: freq = '' if (len(row.xpath('./td[5]'))>0): rds = row.xpath('./td[5]')[0].text() else: rds = '' if (len(row.xpath('./td[6]'))>0): program = row.xpath('./td[6]')[0].text() else: program = '' if (len(row.xpath('./td[10]'))>0): dist = row.xpath('./td[10]')[0].text() else: dist = '' if (len(row.xpath('./td[12]'))>0): dBuV = Decimal(re.search('(\d+)',row.xpath('./td[12]')[0].text()).group(1)) # dBuV = row.xpath('./td[12]')[0].text() else: dBuV = 0.0 stations.append((freq, rds, program, dist, dBuV)) s = sorted(stations, key=itemgetter(4), reverse=True) station_file = 'stations.txt' open(station_file,'w').write('\n'.join('%s;%s;%s;%s;%s' % x for x in s)) print 'Lokale Radiosender in die Datei %s geschrieben' % station_file
0 Kommentare
3 Pingbacks