home     Inhaltsverzeichnis
erste Version am 12.07.2016
letzte Änderung am 15.07.2017

Raspberry Pi als GPS-Tracker
Seite 4


eine Datenbank für den kismet_server

Mittlerweile habe ich zwei weitere Python-Scripte erstellt. Das erste Script füttert die Daten aus den .netxml- und .gpsxml-Dateien in eine Datenbank und berechnet danach alle WLAN-Positionen neu, für die neue/weitere Eingangsdaten zugefügt wurden.
Das zweite Script liest lediglich die Datenbank aus und erzeugt daraus eine gpx-Datei mit einem wpt-Element pro WLAN.
Beide Scripte funktionieren zwar, sind aber ziemlich hingepfuscht - und sozusagen als Machbarkeits-Studie zu betrachten. Sie werden noch ordentlicher.

Weiterhin habe ich vorhin meine dritte Rundfahrt durch Wees gemacht und sollte nun alle WLANs beisammen haben.
Es sind tatsächlich 775 Stück geworden und die sqlite3-Datenbank hat bereits 1.9 MB.

Hier die Karte von Wees in bekannter Darstellungsform. Wieder anonymisiert ... Wees-Bahnhof fehlt noch.


Seit gestern Abend sind auch Wees-Bahnhof und Oxbüll-Nord erfasst. Jetzt habe ich 1.074 AccessPoints in der DB.
Laut Wikipedia hatte Wees im Dez. 2014 ganze 2.258 Einwohner.
Selbst wenn es heute vielleicht 2.500 sind, bedeutet das immer noch: mehr AccessPoints als Haushalte.

Die Scripte sind nun hinreichend ordentlich, um sie hier zu releasen.
Erstmal habe ich die Funktionen für Datenbank-Zugriffe und für die AP-Standort-Berechnung in eigene Scripte ausgelagert.
Das Parser-Script nutzt nun lxml, statt meiner selbstgebauten Funktionen.
Und das wpt-Schreibe-Script ist durch die DB-Funktions-Auslagerung sehr übersichtlich geworden.
Ein weiteres Script dient der Anzeige der <gps-point>-Sätze, die zu einem AP-Standort geführt haben.
Und hier alle zusammen.

Was mir noch nicht so ganz richtig gefällt, sind die beiden Funktionen zur Berechnung eines AP-Standortes.
Derzeit gehen da alle Messpunkte gleichwertig ein. Richtiger wäre es natürlich, wenn Messpunkte mit einem geringeren dbm-Wert stärker berücksichtigt würden. Weiterhin stört sich der eigentlich bessere Algorithmus "Polygon-Schwerpunkt" nicht daran, wenn sich Messpunkte an einem Ort häufen.

Perfekt kann es wahrscheinlich nie werden, weil die gemessenen Werte (gps-Koordinaten und dbm-Werte) an sich schon nicht immer ganz valide erscheinen und vor allen Dingen, weil die Funkwellen-Ausbreitungs-Störer (wie etwa Häuser) mangels Daten nicht berücksichtigt werden können.
Aber wenn sich in genau einer Ecke der konvexen Hülle von Messpunkten geringe dbm-Werte häufen, dann müsste der AP-Standort irgendwie noch in deren Richtung gesaugt werden. Oder die konvexe Hülle dürfte nur aus solchen Messpunkten gebildet werden, die sich alle in der selben dbm-Range befinden.
Derzeit fehlen mir noch die Daten, um das analysieren oder gar verbessern zu können. Dazu bräuchte ich ein paar Rundfahrten um AccessPoints mit bekanntem Standort. Aber das lässt sich sicher finden......

Anbei noch die v1.3 von GPS_tracker.py und status.py3.


Weitere Erkenntnisse

Mittlerweile habe ich 5.214 AccessPoints in der Datenbank.
Es zeigt sich, dass der Polygon-Schwerpunkt-Algorithmus doch nicht so der optimale Ansatz zur Berechnung der AccessPoint-Position ist.
Würde ich jeden AP immer von allen Seiten sehen, könnte er vielleicht brauchbar sein. Praktisch sehe ich APs aber bestenfalls von drei Seiten, meist nur von einer.
Daher bin ich wieder zur Mittelwert-Bildung mit Berücksichtigung des dbm-Wertes zurückgekehrt.

Trotzdem hatte ich weiterhin noch vereinzelte Irrläufer. Also APs an Orten, an denen weit und breit kein Haus in der Nähe ist.
Meist lag es daran, dass der AP von weit auseinander liegenden Positionen (max. 100m) aus gesehen wurde.
Einmal waren aber locker 10km zwischen den Positionen, an denen der AP (zu unterschiedlichen Zeiten) gesehen wurde. Das könnte also auf ein Duplikat bei der BSSID bzw. MAC-Adresse hinweisen. Nach Vergleich der Datensätze in den .netxml-Dateien erschien mir aber ein Umzug (oder Verkauf) des APs wahrscheinlicher.

Und umgezogene oder verkaufte APs sind ein Problem, an das ich bisher überhaupt nicht gedacht hatte.
Weiterhin fahren offenbar einzelne PKW mit einem WLAN-AP ("Audi_MMI", "VW_WLAN") durch die Gegend... Da nützt nicht einmal die Einbeziehung des Sichtungs-Datums etwas. Denn schließlich kann ich so einen PKW-WLAN-AP theoretisch mehrmals in einer Stunde an deutlich unterschiedlichen Orten sehen.
Ziemlich übel...
Welche Daten habe ich denn, die ich diesbezüglich berücksichtigen könnte?
Gut, bestimmte Entfernungen sind ein deutlicher Indikator. Also etwa >= 10km.
Zumindest unter der Annahme, dass ich nie in der Wüste um einen freistehenden Turm mit einem WLAN-AP in 100m Höhe herumfahren werde.
Aber was ist z.B. mit einem Kilometer. Unter optimalen Bedingungen könnte das ohne weiteres eine valide Sichtung eines stationären APs sein.

Tatsächlich habe ich einen AP in der Datenbank, der viermal gesehen wurde und bei dem zwei der Punkte 1085.8m bzw. 1089.4m von der berechneten AP-Position entfernt sind. Ein Blick auf das Satellitenbild zeigt, dass da nur (flacher) Acker zwischen den Punkten ist und die Daten somit durchaus valide erscheinen.

Daher habe ich jetzt einfach 2.5km als Grenze festgelegt. Wird die überschritten, fliegt der AP raus.
Also...eigentlich fliegt er real nicht raus, sondern landet nur nicht in der Tabelle mit den berechneten AP-Koordinaten.
Weil darauf aber nur per Inner-Join mit der Netzwerk-Tabelle (als View "AccessPoints") zugegriffen wird, werden entsprechende APs nicht mehr berücksichtigt.


Anzeige aktuell gesehener AccessPoints

Weil ich auf dem Smartphone gerne noch die aktuell gesehen AccessPoints sehen wollte, musste ich mich mit der Kommunikation mit dem kismet_server befassen. Dessen diesbezügliche Dokumentation ist zwar äußerst bescheiden, aber glücklicherweise bin ich nicht der Erste, der auf diese Idee gekommen ist.
Daher konnte ich ein bischen in bereits erstellter Software spicken.
Dabei kam folgende Klasse heraus:
# ######################################################################
# Eine Klasse, um mit dem kismet_server zu kommunizieren.
class KismetSrv():
  def __init__(self, address=("127.0.0.1", 2501)):
    self.data=socket.create_connection(address).makefile("w", 1)
    self.cmdnr=0
    self.setup()

  # ######################################################################
  # Dem kismet_server sagen, welche Infos er senden soll.
  def setup(self):
    self.sendCmd("ENABLE BSSID bssid,signal_dbm")
    self.__getData()
    self.sendCmd("ENABLE SSID mac,ssid")

  # ######################################################################
  # Das Kommando "cmd" mit vorangestellter fortlaufender Nummer an den
  # kismet_server senden.
  def sendCmd(self, cmd):
    snd="!%d %s\n"%(self.cmdnr, cmd)
    self.data.write(snd)
    self.cmdnr+=1

  # ######################################################################
  # Verfügbare Zeilen vom kismet_server lesen und als Liste zurückliefern.
  def __getData(self):
    lns=self.data.readline().rstrip("\n").split("\x01")
    return(lns)

  # ######################################################################
  # Liefert die letzten beim kismet_server verfügbaren Zeilen, deren
  # erste Zeile mit "prefix" beginnt. Andere Zeilen werden überlesen.
  def __getDataWithPrefix(self, prefix):
    lns=["-"]
    while not lns[0].startswith(prefix):
      lns=self.__getData()
    return(lns)

  # ######################################################################
  # Liefert die letzt-verfügbare BSSID + AccessPoint-Namen mit Dämpfung
  # vom kismet_server oder "", wenn ein Fehler aufgetreten ist.
  def getAccessPointInfo(self):
    ln1=self.__getDataWithPrefix("*BSSID:") # ['*BSSID: B8:27:EB:65:BA:64 -17 ']
    ln2=self.__getDataWithPrefix("*SSID:")  # ['*SSID: B8:27:EB:65:BA:64 ', 'RaspAP', ' ']
    try:
      if ln1[0].startswith("*BSSID:") and ln2[0].startswith("*SSID:"):
        bssid=ln1[0].split()  # ['*BSSID:', 'B8:27:EB:65:BA:64', '-17']
        ssid=ln2[0].split()   # ['*SSID:', 'B8:27:EB:65:BA:64']
        if ln2[1]!="" and bssid[1]==ssid[1]:
          return(bssid[1], ln2[1], bssid[2])  # ('B8:27:EB:65:BA:64', 'RaspAP', '-17')
    except:
      pass
    return("")
Die Funktion getAccessPointInfo() liefert jeweils einen AccessPoint-Namen und dessen dbm-Wert. Diese Infos müssen nun kontinuierlich gelesen und gesammelt werden. Immer wenn die WebSeite anfragt, werden die gesammelten AP-Namen samt Dämpungs-Wert-Liste an die WebSeite bzw. status.py3 übergeben und danach die Sammlung gelöscht.
Die Infos werden auf der WebSeite zwischen den Status-Infos und den Buttons angezeigt. Man muss also etwas runter scrollen, um die aktuell sichtbaren AccessPoints angezeigt zu bekommen.
Sieht dann so aus, wenn der RasPi in meinem Büro liegt:
Screenshot
Der AccessPoint des RasPi erscheint hier nicht, weil ich den kismet_server zwischenzeitlich über dessen Konfig-File (/usr/local/etc/kismet.conf) angewiesen hatte, dessen MAC-Adresse zu ignorieren.
# RaspAP nicht aufzeichnen
filter_tracker=BSSID(!B8:27:EB:65:BA:64)

Anbei die 1.4'er Version von GPS_tracker.py und status.py3.


Alternative Komponenten

Bei meinen letzten Warbiking-Touren hat mich mein Jüngster begleitet. Allein das dabei sein hat ihm schon Spaß gemacht.
Weil ich aber annehme, dass es ihm noch mehr Spaß machen würde, wenn er richtig mitmachen könnte, habe ich einen zweiten RasPi3 samt Zubehör bestellt.
Jedoch gab es den WiFi-Adapter von Adattatore nicht mehr direkt über Amazon zu bestellen.
Daher habe ich stattdessen dieses WiFi Dongle und vorsichtshalber noch ein Racksoy WiFi Dongle bestellt. Beide tun, was sie sollen.
Auch habe ich eine etwa 10€ billigere 167 Kanal USB GPS Empfänger Maus geordert.

Der neue GPS-Empfänger scheint mir schneller Satelliten zu finden, als der alte. Allerdings liefert er als Datum erstmal den 28.06.2006 - obwohl er seine Position noch nicht bestimmt hat. Weiterhin liefert er deutlich falsche Koordinaten (bis zu 100m daneben), wenn die Anzahl der genutzten Satelliten noch unter 7 liegt. Dementsprechend musste GPS_tracker.py etwas angepasst werden.
Beide WiFi-Adapter werden am neuen RasPi3 als wlan1 erkannt, auch wenn sie bereits beim Hochfahren gesteckt sind.

Der zweite RasPi hat von mir die 192.168.3.1 bekommen und der kismet_server ignoriert jetzt zwei APs:
# RaspAP nicht aufzeichnen
filter_tracker=BSSID(!B8:27:EB:65:BA:64,!B8:27:EB:18:9B:07)


Erweiterungen bzgl. Wardriving

Was noch fehlt, ist das Löschen von APs, die nicht mehr erneut gesehen werden. Statt sie zu löschen, könnte man sie vielleicht lieber nur als gelöscht markieren.
Die Frage ist allerdings, wie das halbwegs performant umgesetzt werden könnte....
Exakt identische GPS-Koordinaten wird man schließlich eher selten bekommen, wenn ein Ort erneut besucht wird.
Es bedarf somit einer Entfernungs-Bestimmung.

Oder Rundung auf eine definierte Anzahl von Nachkommastellen.
Wenn bei einer jüngeren Tour an [mehreren] Koordinaten ein AP nicht mehr gesehen wird, der bei einer älteren Tour noch gesehen wurde, müsste er als gelöscht markiert werden.
Dazu müsste jede neu in die Datenbank aufzunehmende Koordinate gegen alle bereits in der Datenbank enthaltenen Koordinaten geprüft werden.
Die Koordinaten in der gpspoints-Tabelle haben sechs Nachkommastellen.
Eine Änderung um Eins bei der vierten Nachkommastelle ergibt folgende Entfernungen für
lat
11.1194926639 [m]
lon
6.40882864108 [m]
An der fünften Nachkommastelle entsprechend
lat
1.11194926589 [m]
lon
0.640882863996 [m]

Eine Rundung an der vierten Nachkommastelle wäre wohl etwas zu üppig, an der fünften eher etwas knapp.
So um die vier Meter erscheint mir passender.
Wenn für eine neu aufzunehmende Koordinate (lat_n, lon_n) eine alte Koordinate (lat_a, lon_a) existiert, für die gilt:

lat_n-0.00002 <=
lat_a
<=
lat_n+0.00002
AND
lon_n-0.00003 <=
lon_a
<=
lon_n+0.00003
und zu (lat_n, lon_n) keine BSSID existiert, die gleich der BSSID von (lat_a, lon_a) ist und
dies auf mehrere Koordinaten der [alten] BSSID zutrifft und
es keine neue Koordinate für die BSSID gibt, dann ist der [alte] AP ein Kandidat für eine Löschmarkierung.

Also drei Bedingungen:
1.) neuer Messpunkt muss passen
2.) mehrere Messpunkte müssen passen (z.B. 10%)
3.) die BSSID darf nicht neu gesehen worden sein
Damit 3.) funktioniert, muss eine Tour erstmal komplett in die Datenbank geladen werden.
In einem zweiten Schritt könnten dann anhand von gpspoint.last_seen die neuen Sichtungs-Koordinaten ermittelt und gegen die alten Sichtungs-Koordinaten geprüft werden.
Au weia.....dafür sollte mindestens noch je ein Index auf gpspoint.lat und gpspoint.lon angelegt werden.
Die Spalte für die Löschmarkierung könnte in der Tabelle ap_loc landen.
Und vielleicht wäre das Vorhalten einer Tour-ID (in gpspoint) pro .gpsxml-Datei noch ganz sinnvoll, um nicht immer mit Datum+Zeit operieren zu müssen, wenn zwischen alten und neuen AP-Sichtungen unterschieden werden soll.

Irgendwie habe ich das dringende Gefühl, dass das so nicht hinreichend performant funktionieren wird.
Die zu betrachtende Datenmenge muss noch eingeschränkt werden.
Man könnte z.B. nur solche Sätze betrachten, deren Wert von gpspoint.last_seen mindestens einen Monat alt ist.

Denn aus 2.) ergäbe sich noch eine vierte Bedingung:
4.) network.last_seen NOT IS NULL
...weil es schließlich mindestens zwei Sichtungen geben muss, um einen AP als "nicht mehr gesehen" einstufen zu können.
Mal ein bischen analysieren.....
Die letzte Tour war am 2016-09-18. Nach dessen Import befanden sich 27.686 Sätze im View AccessPoints.
Davon 20.063 Sätze mit  last_seen IS NULL  und 7.623 Sätze mit  last_seen IS NOT NULL.
Der Query für die seit mindestens einem Monat nicht nicht mehr gesehenen APs liefert
zu  last_seen IS NULL OR last_seen<"2016-08-18"  20.289 Sätze,
zu  last_seen<"2016-08-18"  hingegen nur 226 Sätze und
zu  last_seen>"2016-08-18"  7.397 Sätze.
Also ein sehr deutlicher Unterschied bei den zu analysierenden Sätzen.
Andererseits werden bei dieser Methode nie solche APs als verschwunden erkannt, die zufällig nur bei der ersten Vorbei-Fahrt aktiv bzw. sichtbar waren.

...Obwohl....Das war jetzt gerade Quatsch... Die Annahme, dass es "mindestens zwei Sichtungen geben muss" ist falsch formuliert.
Es muss mindestens zwei Vorbei-Fahrten geben. Außerdem darf nicht der View AccessPoints verwendet werden, weil die dort abgelegten Koordinaten idealerweise in Gebäuden liegen - durch die ich eher nie fahren werde. Stattdessen ist die Tabelle gpspoint heranzuziehen.


Nachtrag 2017

Erstmal vorab: mittlerweile habe ich ein sehr viel performanteres Visualisierungs-GUI für die gefundenen AccessPoints gebaut.

Neulich hat sich der WLAN-Stick (vom ersten RasPi3) ins Nirvana verabschiedet und ich musste einen anderen USB-WLAN-Stick drauf stecken. Bei ersten Tests hat das auch direkt und komplett schmerzfrei funktioniert.
Dann stand gestern die erste Tour mit meiner Frau an, bei der ihr nagelneues Pedelec zum Einsatz kommen sollte.
Es war eine 80 Kilometer lange Tour geplant, bei der ich natürlich auch WLANs sammeln wollte.
Aber nach Start des Kismet-Servers wurden auf dem Smartphone keine aktuell gesehenen AccessPoints angezeigt.
Wir wollten jedoch los ... bzw. war keine Zeit, das noch schnell zu fixen. Sehr ärgerlich...
Die heutige Analyse hat ergeben, dass Raspbian die Reihenfolge von wlan0, wlan1, ..., wlanX wohl mehr oder weniger zufällig den erkannten WLAN-Devices zuordnet.
Um dieses Verhalten abzustellen, ist in der Datei:
/lib/udev/rules.d/75-persistent-net-generator.rules
der Abschnitt:
# device name whitelist
KERNEL!="ath*|msh*|ra*|sta*|ctc*|lcs*|hsi*", \
                                        GOTO="persistent_net_generator_end"
zu suchen und zu ändern auf:   (hier eine der diesbezüglich gesichteten Quellen)
# device name whitelist
KERNEL!="wlan*|ath*|msh*|ra*|sta*|ctc*|lcs*|hsi*", \
                                        GOTO="persistent_net_generator_end"
Danach ist der RasPi3 ohne den WLAN-Stick zu rebooten.
Dadurch wird das onBoard-WLAN nun zukünftig immer wlan0 heißen.
Dann den USB-WLAN-Stick stecken, etwas warten und nochmal rebooten.
Ab nun sollte dieser WLAN-Stick zukünftig immer wlan1 werden.

Die Änderung bewirkt, dass nun bei jedem Reboot folgende Datei erzeugt wird:
dede@rp3-1:~ $ cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.

# Unknown net device (/devices/platform/soc/3f300000.mmc/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0) (brcmfmac_sdio)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="b8:27:eb:65:ba:64", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan0"

# USB device 0x:0x (rt2800usb)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:19:86:81:21:26", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan1"

...und damit quasi Interface-Namen für MAC-Adressen reserviert werden.

Jetzt kann folgende Zuordnung fest und endgültig eingestellt werden:
Konfig-Datei
Interface-Name
Funktion
/etc/network/interfaces
wlan0 (onboard)
Kommunikation mit dem Smartphone
/etc/dnsmasq.conf
wlan0 (onboard) Kommunikation mit dem Smartphone
/etc/hostapd/hostapd.conf
wlan0 (onboard) Kommunikation mit dem Smartphone
/usr/local/etc/kismet.conf
wlan1 (USB-Stick)
Nach WLANs schnuppern

BTW: ich habe mir gerade einen Alfa AWUS036H samt einer 12dBi- und einer etwas kürzeren 8dBi-Antenne bestellt.
Das soll der Ersatz für den jetzigen WLAN-Stick werden. Mal schauen, wie sich das auf die Anzahl der gesehenen WLANs auswirkt.
Die Dokumentation der entsprechenden Erkenntnisse folgt beizeiten.


RasPi-GUI v1.6a

Eben ist mir aufgefallen, dass hier die neuste Version noch auf Stand v1.4 ist - ich aber bereits eine v1.6a nutze.
Bei der neuen Version können bzw. müssen die Dienste "Track-Writer" und "Kismet-Server" per Button gestartet werden.
Der Sinn dahinter ist die Möglichkeit, den Zustand, in dem das GPS-Modul noch zu wenig Satelliten sieht, um seine Position halbwegs korrekt bestimmen zu können, in den geschriebenen Dateien ausblenden zu können.
Der Einschalt-Workflow ist jetzt:
- RasPi3 einschalten
- Smartphone einschalten und auf Erreichbarkeit RaspAP1 warten
- per Webbrowser mit dem RasPi3 verbinden
- auf korrekte Uhrzeit und Satelliten-Anzahl >=7 warten
- Track-Writer starten
- Kismet-Server starten
- Tour beginnen
Vorher gab es immer irre Sprünge bei den ersten 10 bis 20 Sätzen mit entsprechend irren Geschwindigkeiten. Analog dazu gab es natürlich auch irre AccessPoint-Sichtungspunkte. Angefallen sind sie, weil Datum und Uhrzeit zwar schon empfangen wurden, aber z.B. erst drei Satelliten zur Positionsbestimmung genutzt werden konnten.

Per crontab wird -wie gehabt- nur GPS_tracker.py gestartet:
@reboot  /usr/bin/python /home/dede/bin/GPS_tracker.py
Alles weitere wird über die Button auf der Webseite zugeschaltet, die ihrerseits Aufträge an GPS_tracker.py erstellen.
Lediglich Shutdown und Reboot des RasPi3 werden direkt von der Webseite initiiert - für den Fall, dass GPS_tracker.py gestoppt oder gestorben sein sollte.
Dementsprechend sieht das GUI mittlerweile so aus (nachdem das USB-Tethering aus dem nächsten Kapitel auch schon aktiv ist):
Screenshot vom GUI v1.6        Screenshot vom GUIv1.6 - zu den
        Buttons runtergescrollt


USB-Tethering

Vorhin habe ich spaßeshalber mal in der Doku zu der erweiterten Version meines Fahrrad-Bordcomputers geschmökert, bin darüber auf die Idee gekommen, Google mal nach ip over bluetooth zu befragen - um dann schließlich beim Suchbegriff android raspberry pi 3 usb zu landen. Dabei bin ich auf diese Seite gestoßen, in der beschrieben wird, wie ein Raspberry Pi über ein USB-Kabel IP mit einem Smartphone spricht und dieses damit als Display nutzt.
Weil das so einfach aussah, habe ich es kurzerhand ausprobiert und war sofort begeistert, wie problemlos das funktioniert.
Nix muss nachinstalliert werden, kein Kernel will neu übersetzt werden, kein modprobe, ... klappt direkt.
Es braucht nur einen Eintrag in /etc/network/interfaces:
auto usb0
allow-hotplug usb0
iface usb0 inet static
   address 192.168.42.180
   netmask 255.255.255.0
   network 192.168.42.0
   broadcast 192.168.4.255
Nachdem dann ein Smartphone per USB-Kabel an den RasPi angeschlossen und auf diesem "USB-Tethering" aktiviert wird, kann die Seite
http://192.168.42.180/cgi-bin/status.py3
mit einem Webbrowser geöffnet werden.

Das ist schon fast verdächtig einfach. Zu einfach....
- warum klappt es nicht mit 192.168.4.1 (statt 192.168.42.xxx)?
- und wo bekommt das Smartphone eigentlich seine IP-Adresse her? (der dnsmasq antwortet nur auf dem wlan0-Interface)

Ahh...hier steht, dass die IP-Adresse im Android hardcoded ist und fix auf 192.168.42.129 liegt.
Das deckt sich auch mit dem, was der Webserver mittels
cgi.escape(os.environ["REMOTE_ADDR"])
meldet.
Gut. Eine fest verdrahtete IP-Adresse erklärt das Verhalten.
Damit braucht es jetzt den hostapd und den dnsmasq eigentlich nicht mehr. Ich lasse beide einfach mal drin und deaktiviere sie lediglich per rcconf.
Nun muss noch das onboard-WLAN-Interface des RasPi und das WLAN am Smartphone deaktiviert werden und schon sollte mein Wardriving-System selbst Funkstille halten. Das müsste den Sichtungen zugute kommen. Denn wahrscheinlich hat der RaspAP bisher die Sichtungen anderer APs auf seinem Kanal insofern geschwächt, als das er als stärkster bzw. räumlich am nächsten gelegener Sender die Signale der anderen überlagert hat.

Wenn ich mir eine beliebige (alte) gpsxml-Datei genauer ansehe, dann finde ich darin reichlich Zeilen mit der MAC-Adresse des RaspAP und der meines darauf zugreifenden Smartphones.
Ganz konkret etwa:
Zeilen gesamt 118.341
RaspAP-MAC-Adresse 8.884
Smartphone-MAC-Adresse
1.350
GP:SD:TR:AC:KL:OG-Sätze
9.069
Also knapp 10% der Zeilen waren bisher vollkommen überflüssig.
Weiterhin spart das abgeschaltete WLAN Strom auf beiden Seiten und zusätzlich wird das Smartphone zukünftig dauerhaft vom 20Ah-Akku des RasPi versorgt.
Neue Hardware ist nicht nötig, weil ich sowieso schon immer ein USB-Lade-Kabel für das Smartphone dabei (und am zweiten Port des Akkus angeschlossen) hatte - nur für den Fall, dass dem Smartphone ansonsten der Saft ausgegangen wäre.
Dieses Kabel wandert jetzt einfach an einen USB-Port vom RasPi und überträgt neben Strom nun auch noch Daten.

BTW: Weil das USB-Tethering am Smartphone jedes mal wieder tief in den Einstellungen aktiviert werden muss, habe ich noch diese kleine App installiert. Na gut....damit sind es zweimal tippen, vorher war es viermal tippen...aber immerhin.


Alfa AWUS036H

Heute ist der Alfa AWUS036H endlich geliefert worden.
In /usr/local/etc/kismet.conf habe ich eine Zeile geändert:
ncsource=wlan1:name=AlfaAWUS036H
...wobei das "name=..." nicht zwingend notwendig ist.
Der Name wird im Header der netxml-Datei als <card-name>AlfaAWUS036H</card-name> eingetragen.
Ohne "name=..." stände da nur ein profanes wlan1 drin. Daher: reine Kosmetik.

Aber dann....beim ersten Test mit vollem Equipment ging erst mal gar nix mehr. Die vier dabei angeschlossenen USB-Komponenten überfordern den RasPi3 offenbar.
Der 32GB-USB-Stick wird wohl noch das geringste Problem sein. GPS-Modul und WLAN-Dongle werden schon etwas mehr zulangen. Den größten Strombedarf wird das Smartphone haben, wenn es parallel seinen eigenen Akku lädt.

Was kann man nun dagegen tun. Die erste Idee war ein USB-Hub mit Stromversorgung. Allerdings dürfe es wohl schwierig werden, ein Modell zu finden, dass statt eines Stecker-Netzteils einen zusätzlichen USB-Strom-Eingang besitzt. Mit 12V-Zigarettenanzünder-Anschluß könnte es was geben - hilft mir aber nicht.
Vielleicht ginge es auch mit einem USB-Y-Adapter. Ich habe sogar irgendwo einen rumliegen - jedoch ist der dafür gedacht, eine USB-HDD aus zwei normalen USB-Ports zu versorgen. Was ich bräuchte, wäre einer, der einen Strom- und einen Daten-Eingang besitzt. Sowas hier. Aber was wäre das dann für ein unschöner Kabelsalat.... Schließlich musste ich den Akku gerade in eine eigene Fahrrad-Tasche auslagern, weil die bisherige Fahrrad-Tasche zu eng geworden ist (mit dem Alfa).

Also habe ich Google mal nach raspberry pi 3 usb too much power befragt und bin nach ein oder zwei Zwischenstationen auf dieser Seite gelandet. Das entscheidende Keyword ist max_usb_current=1 (in der /boot/config.txt).
Um das auszuprobieren, habe ich den RasPi ohne angeschlossenes Smartphone und WLAN-Dongle gestartet und per Ethernet-Kabel mit meinem Rechner verbunden. Nach ssh-Logon dann ein sudo nano /boot/config.txt und den Parameter hinten anhängen:
#dtparam=audio=on
max_usb_current=1
Nebenbei habe ich dem RasPi noch das Audio abgedreht. Das brauche ich nicht und es kostet höchstens Strom.

Und..... klappt!
.... Aber nicht zuverlässig.
Es sieht so aus, dass der Alfa der größte Stromfresser ist. Ziehe ich den vor dem PowerOn ab, fährt der RasPi3 auch mit angeschlossenem Smartphone hoch. Wenn der RasPi3 hoch ist, kann ich den Alfa stecken und habe ein funktionsfähiges System.
Andersrum klappt es nur sehr sporadisch. Also wenn ich mit angeschlossenem Alfa und gezogenem Smartphone den PowerOn mache.

So ein elender Scheiß.
Aber immerhin erspart mir diese Erkenntnis, mich mit BlueTooth-Tethering beschäftigen zu müssen. Das war zeitweilig mal eine Idee, um den Strom für das Smartphone nicht aus einem USB-Port des RasPi3 entnehmen zu müssen und trotzdem WLAN deaktiviert lassen zu können.
Das nachträgliche Stecken kann jedoch noch nicht die Lösung sein. Auch wenn das ehemalige Problem mit dem Reset durch nachträgliches anstecken von USB-Devices beim RasPi3 nicht mehr besteht, fände ich es sehr unschön, jedesmal dran denken zu müssen, dass der Alfa beim Hochfahren gezogen sein muss.

Weil ich ja eh gerade dabei war, habe ich BlueTooth auch noch abgeschaltet.
Das geht mit einer weiteren Zeile in /boot/config.txt. Jetzt somit:
#dtparam=audio=on
max_usb_current=1
dtoverlay=pi3-disable-bt
Konsequenterweise sollte der BlueTooth-Systemdienst nun noch (per rcconf) deaktiviert werden.
Und welch freudige Überraschung: damit kommt der RasPi3 auch hoch, wenn alle vier USB-Komponenten angeschlossen sind.
Somit sollte jetzt alles bereit für die erste Wardriving-Tour mit dem Alfa sein.

Nun bin ich 45 Minuten lang im Schneckentempo durchs Dorf gezuckelt (9.6km).
Erstes Ergebnis der anschließenden Analyse: AccessPoints, die an ähnlicher Stelle erneut gesehen wurden, hatten auf der älteren Tour an dem Ort z.B. einen dbm-Wert von -71, auf der heutigen Tour von -59. Oder -69 zu -62, oder -77 zu -65 oder sogar -87 zu -61.
Eine klar erkennbare Verbesserung. Primär wirds am Alfa und der 8dBi-Antenne liegen. Vielleicht auch ein bischen daran, dass RasPi3 und Smartphone nun Funkstille halten.
Die nächste Erkenntnis ist, dass ich endlich die Routine zur Markierung von nicht mehr gesehenen AccessPoints bauen muss.

Weiter zu Seite 5.