MagicMap Stumbler Interface
aus Nomads, der freien Wissensdatenbank
Das Stumbler Interface beschreibt die Schnittstelle zwischen den einzelnen Stumblern (WLAN, Bluetoth, GSM) und MagicMap (siehe auch MagicMap_Architektur_und_Schnittstellen). Diese Schnittstelle definiert die Übergabe von Daten zu den im Funkbereich gestumbelten Objekten. Dies umfasst neben der gemessenen Signalstärken auch Informationen zur verwendeten Hardware, etwa der name des Gerätes oder des verwendeten Betriebssystems. Diese Daten können dann dazu verwendet werden, die Signalstärken von verschiedenen Stumblern und verschiedenen Funktypen (WLAN, Bluetooth, etc.) auf Distanzschätzungen zu normalisieren, siehe dazu Normalisierung.
Übertragung der gestumbelten Daten
Um eigene Stumblingdaten an MagicMap zu übertragen, muss man zunächst ein eigenes Plugin erstellen, siehe PluginHowTo.
Zur Übertragung der gestumbelten Daten braucht man zuerst eigene ScanResults, die vom AbstractScanResult abgeleitet sind. In diese ScanResults kommen alle relevanten Daten vom Stumbler, die an den Client weitergeleitet werden sollen. Im typischen WLAN-Szenario bestehen diese ScanResults aus der MAC-Adresse der WLAN-Karte (identifier), dem Zeitstempel der Messung (timeStamp), der Signalstärke (signalLevel), sowie dem Noisewert (Noise), der SSID(ssid) des aktiven Netzes, der verwendeten (Funk-) Technologie und den zusätzlichen Hardwareinformationen (hardwareInfos).
public AdvancedWifiScanResult(String identifier, double signalLevel, Date timeStamp, double noise, String ssid, String technology,
HashMap<String, String> hardwareInfos) {
super(identifier, timeStamp);
this.signalLevel = signalLevel;
this.noise = noise;
this.ssid = ssid;
this.technology = technology;
this.hardwareInfos = hardwareInfos;
}
Die hardwareInfos wiederum bestehen aus Key-Value <String,Object> Paaren, die genaue Informationen zur verwendeten Hardware enthalten. Ziel ist es mit Hilfe dieser Informationen eine Normalisierung aller Signalstärkewerte durchzuführen, um daraus eine Entfernungsschätzung und die dazugehörige Streuung zu berechnen, die dann dem Berechnungsalgorithmus übergeben wird.
Im Detail bestehen diese Hardwareinformationen aus: dem verwendeten Betriebssystem (OSNAME), dem Funktyp (RADIOTYPE), dem Hardwarenamen (DEVICENAME), dem Hersteller (VENDOR), der Treiberversion (VERSION), den unterstützten Funktypen (SUPPORTEDTYPES) und dem gerade aktiven Funktyp (TYPE).
Die Stumblingklasse, die die Daten an die Berechnung weiterleiten soll, muss den AbstractScanner extenden. Dadurch stehen die Methoden
protected synchronized void notifyScan(AbstractScanResult result){
Controller.getInstance().handleScanResult(result, this);
}
die bei jedem nuen ScanResult aufgerufen werden muss und
protected synchronized void notifyRoundComplete(){
Controller.getInstance().handleRoundComplete(this);
}
zur Verfügung, die nach jedem durchlauf, also nachdem alle ScanResults übermittelt wurden, aufgerufen wird.
Damit der Client diese Informationen auswerten kann, muss man zusätzlich noch einen eigenen Handler schreiben, der vom AbstractScannerHandler abgeleitet ist und die beiden Methoden handleScanResultScan und roundComplete implementiert. Diese beiden Methoden sind die passenden Gegenstücke zu notifyScan und notifyRoundComplete.
notifyScan
Die notifyScan Methode dient zur Übertragung einzelner ScanResults. Sie leitet diese automatisch an den passenden Handler weiter, der diese über seine handleScanResult Methode and den Client übergibt.
Beispiel: Die handleScanResult Methode des WifiScanners, die über
getClient().getMeasurementModel().updateScanResult(apMac, result.ssid, signalLevel, result.getTimeStamp().getTime(), result.technology);
alle wichtigen Informationen an die Berechnung weiterreicht.
private Map<String,SeenAccessPoint> previous = Collections.synchronizedMap((new HashMap<String,SeenAccessPoint>()));
public void handleScanResult(final AbstractScanResult scanResult) {
final AdvancedWifiScanResult result = (AdvancedWifiScanResult) scanResult;
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
String apMac = result.getIdentifier();
double signalLevel = result.signalLevel;
if (previous.containsKey(apMac)) previous.remove(apMac);
getClient().getMeasurementModel().updateScanResult(apMac, result.ssid, signalLevel, result.getTimeStamp().getTime(),
result.technology);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
getClient().getMeasurementModel().updateScanResult(apMac, result.ssid, signalLevel, result.getTimeStamp().getTime(), result.technology) sagt dem Model, dass der aktuelle Client auf dem das Plugin läuft, in diesem Fall den AccessPoint zu dem die MAC-Adresse aus dem ScanResult gehört, mit der Signalstärke und allen weiteren Informationen sieht, die im ScanResult stehen. An dieser Stelle könnten die Signalstärken durch einen Kalibrierungsfaktor angepasst werden, so dass Hardwareabhängigkeiten ausgeglichen werden, oder unterschiedliche Signale z.B. WLAN und GSM miteinander vergleichbar gemacht werden.
notifyRoundComplete
Die notifyRoundComplete Methode dient dazu, Berechnungen durchzuführen, die erst möglich sind, wenn alle Signalquellen übermittelt wurden. Am Beispiel des WifiHandlers werden hier für alle Accesspoints negativ unendlich grosse Signalstärken übertragen, wenn sie in der vorherigen Messung, aber nicht in der aktuellen gesehen wurden. Dadurch werden nicht mehr gesehene AccessPoints langsam aus der Berechnung und damit auch aus der GUI ausgeblendet.
public void roundComplete() {
// Modeländerungen wirken sich auf den SWING-Thread aus. Daher synchronisieren wir mit ihm...
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
// Alle toten AccessPoints unter Previous aus dem Model entfernen
Set<String> apMacs = previous.keySet();
IMeasurementModel measurementModel = getClient()
.getMeasurementModel();
for(String mac : apMacs) {
SeenAccessPoint sap = previous.get(mac);
// für nicht gesehene AP's Double.NEGATIVE_INFINITY als
// signalstärke ans measurementmodel schicken
// damit der deadcounter für diese AP's hochgezählt wird
measurementModel.updateScanResult(sap.getMac(),sap.getSSID(),
Double.NEGATIVE_INFINITY, sap.getLastseen(),sap.getTechnology());
}
previous.clear();
// Den aktuell gesehenen Zustand halten um bei der nächsten
// Runde wieder die verlorenen zu löschen.
for (SeenAccessPoint ap : measurementModel.get()) {
previous.put(ap.getMac(),ap);
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
