28. BWINF, Runde 1, Aufgabe 3: Wegfehler - Matthias Springer .DE
28. BWINF, Runde 1, Aufgabe 3: Wegfehler - Matthias Springer .DE
28. BWINF, Runde 1, Aufgabe 3: Wegfehler - Matthias Springer .DE
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>28.</strong> <strong>BWINF</strong>, <strong>Runde</strong> 1, <strong>Aufgabe</strong> 3: <strong>Wegfehler</strong><br />
Stefan Hansch Markus Wirsing <strong>Matthias</strong> <strong>Springer</strong><br />
12. November 2009<br />
1 Lösungsidee<br />
1.1 Einlesen der Bilddatei<br />
Momentan sollen noch keine Änderungen oder Anpassungen an den GPS-Daten<br />
vorgenommen werden, sie sollen lediglich unverändert eingezeichnet werden. Wie<br />
bei <strong>Aufgabe</strong> 5 handelt es sich um das PPM-Format. Deshalb wurden einige Zeilen<br />
Quelltext aus <strong>Aufgabe</strong> 5 übernommen und es gelten die gleichen Anforderungen<br />
an die Eingabedatei. So muss die PPM-Datei etwa im P5-Format vorliegen<br />
und es können nur 24-bit-PPMs verarbeitet werden. Die gesamte Bilddatei wird<br />
in eine W x H x C-Matrix eingelesen, wobei W die Breite und H die Höhe der<br />
Bilddatei in Pixel ist. Die dritte Dimension der Matrix dient wie gewohnt zur<br />
Identifikation der drei Farbkanäle rot, grün und blau (RGB). Es soll nun nicht<br />
weiter auf das Einlesen der Bilddatei eingegangen werden, da es wie in <strong>Aufgabe</strong><br />
5 durchgeführt wird und im Quelltext gut kommentiert wurde.<br />
1.2 Einlesen der GPS-Daten<br />
Mit Pixel-Daten lässt es sich meiner Meinung nach leichter arbeiten, als mit<br />
GPS-Daten. Deshalb werden Längen- und Breitengrade in entsprechende Pixel-<br />
Koordinaten auf der Bilddatei umgerechnet. Die Koordinaten der Ecken der<br />
Bilddatei wurden der <strong>BWINF</strong>-Seite (Material zu den <strong>Aufgabe</strong>n) entnommen<br />
und können vom Benutzer nicht verändert werden. Da es sich bei der Landkarte<br />
um einen relativ kleinen Bereich der Erdoberfläche handelt, kann die Krümmung<br />
der Erde in diesem Bereich vernachlässigt werden. Das Problem wird vereinfacht,<br />
indem einfach angenommen wird, dass es sich um einen rechteckigen Bereich<br />
handelt.<br />
Um nun zum Beispiel den x-Pixelwert eines Punktes zu berechnen, wird<br />
zuerst das Verhältnis Pixel pro Längengrad bestimmt. Dieses ergibt sich aus<br />
dem Quotienten der Breite (Proportionen in x-Richtung) des Bildes in Pixel und<br />
der Breite des Bildes in geographischer Länge. Letzterer Wert ergibt sich aus der<br />
Differenz der geographischen Längen der gegebenen Eckpunkte der Landkarte.<br />
Definition 1 Somit kann die x-Koordinate eines geographischen Punktes mittels<br />
der Funktion<br />
proportions[0]<br />
x pixel(Longitude[x]) =<br />
Longitude[rechts]−Longitude[links]<br />
· (Longitude[x] −<br />
Logitude[links])<br />
1
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
in einen Pixel-Wert auf der zu Grunde liegenden Landkarte konvertiert werden,<br />
wobei x pixel der x-Koordinate eines Punktes auf der Landkarte, Logitude[x]<br />
dem Längengrad (geographische Länge) des Punktes aus dem GPS-Daten<br />
und proportions[0] der Breite des Bildes in Pixel entsprechen.<br />
Die Bezeichnungen der Variablen entsprechen dabei so weit wie möglich den<br />
im Quelltext verwendeten Variablennamen und den bereits in <strong>Aufgabe</strong> 5 verwendeten<br />
Bezeichnungen. Analog wird bei der Berechnung der y-Koordinaten<br />
verfahren, jedoch ist hier zu beachten, dass die Breitengrade entgegengesetzt zu<br />
den y-Pixelwerten wachsen. Das heißt, dass die Pixel-Werte von oben nach unten<br />
zunehmen, die Breitengrade jedoch von unten nach oben (auf der Bilddatei). Die<br />
komplette Log-Datei wird im Arbeitsspeicher in drei Arrays gespeichert, wobei<br />
Zeit, Längengrad und Breitengrad (jeweils als Pixel) gespeichert werden. Zeitangaben<br />
werden in vorzeichenlose Ganzzahlen umgewandelt, die die seit 00:00:00<br />
Uhr vegangene Zeit in Sekunden angeben. Zu Problemen kommt es jedoch, wenn<br />
eine Log-Datei über mehrere Tage geht, d. h. wenn ein Sprung von 23:59:59 Uhr<br />
auf 00:00:00 Uhr stattfindet. Ein solcher Spezialfall wird nicht abgedeckt, da er<br />
in Dominics GPS-Logs nicht auftritt.<br />
1.3 Einzeichnen eines Weges auf der Karte<br />
Schreibe ein Programm, das einen Weg auf der Karte einzeichnet.<br />
Es sollen zunächst keine Anpassungen an den GPS-Daten vorgenommen werden.<br />
Es sollen alle Daten (ggf. mit Fehlern) auf die Karte gezeichnet werden.<br />
Dazu iteriert das Programm über alle Einträge in den Log-Daten und zeichnet<br />
entsprechende Linien auf der Landkarte ein, indem die betroffenen Pixel<br />
grün eingefärbt werden. Für diesen Vorgang sind lediglich die Koordinaten der<br />
GPS-Daten von Interesse, nicht jedoch die Zeitangaben. Zum Schluss wird das<br />
neue Bild wie in <strong>Aufgabe</strong> 5 auf der Festplatte gespeichert. Das Bild vom <strong>Aufgabe</strong>nblatt<br />
erhält man, indem man alle drei Log-Dateien auf eine Landkarte<br />
einzeichnet.<br />
Eine Strecke wird dazu als ein Vektor ⃗v mit Aufpunkt A aufgefasst. Die<br />
Länge einer Strecke ergibt sich durch den<br />
√<br />
Betrag des Vektors |⃗v| und wird mittels<br />
Pythagoras über die Beziehung |⃗v| = vx 2 + vy 2 berechnet. Nun wird aus den<br />
Pixelangaben (Start- und Zielpunkt der Strecke) der Einheitsvektor gebildet.<br />
Dieser Vektor hat die Länge 1 und dient lediglich dazu, die Richtung anzuzeigen.<br />
Er wird gebildet, indem der Vektor durch seine Länge (ein Skalar) dividiert<br />
wird. Nun iteriert das Programm ganzzahlig über das Intervall i ∈ [0; |⃗v|] und<br />
berechnet den Punkt, der vom Aufpunkt A genau i (Pixel-)Einheiten entfernt ist<br />
und die Richtung von ⃗v besitzt. Dazu wird der Einheitsvektor mit i multipliziert.<br />
Dieser Punkt wird eingefärbt. Zum Schluss ist also mit Sicherheit die ganze<br />
Strecke eingefärbt.<br />
1.4 Korrektur der Fehler<br />
In den GPS-Logs können Daten fehlen oder falsch sein. Erweitere<br />
Dein Programm so, dass es versucht, die Fehler zu korrigieren, um<br />
so den Weg möglichst realistisch einzeichnen zu können.<br />
2
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Bei der Betrachtung des eingezeichneten Weges fallen bereits einige offensichtliche<br />
Fehler auf. So liegen manche Punkte, die Dominic besucht, beispielsweise<br />
außerhalb der Landkarte und werden mit einer atemberaubenden Geschwindigkeit<br />
erreicht. Diese und weitere Fehler sollen nun behoben werden. Dabei habe<br />
ich angenommen, dass die in den Log-Dateien auftretenden Fehler repräsentativ<br />
für alle Testfälle sind, also nur solche Fehler behoben werden müssen, die in<br />
den drei bekannten Testfällen auftreten. So könnte es theoretisch vorkommen,<br />
dass manche Zeitangaben falsch sind. Das ist jedoch in den bekannten Testfällen<br />
nicht der Fall. Es kann lediglich vorkommen, dass manche Einträge komplett fehlen<br />
oder nicht im 5-Sekunden-Rhythmus aufgenommen wurden. Das Programm<br />
muss die folgenden Fehler in den Log-Dateien verarbeiten können.<br />
1. Die GPS-Koordinaten sind zu einem bestimmten Zeitpunkt oder in einem<br />
ganzen Zeitintervall falsch.<br />
2. Zu bestimmten Zeitpunkten oder in einem ganzen Zeitintervall fehlen Log-<br />
Einträge.<br />
3. Aufeinanderfolgende Log-Einträge wurden nicht im 5-Sekunden-Rhythmus<br />
aufgezeichnet.<br />
Die nun folgenden Annahmen über die Log-Daten müssen erfüllt sein, damit der<br />
Algorithmus die GPS-Daten möglichst korrekt rekonstruieren kann. Die meisten<br />
dieser Anforderungen könnten mit Änderungen am Algoritmus mit Sicherheit<br />
fallen gelassen werden, jedoch würde dies einerseits einen großen Aufwand<br />
erfordern und andererseits treten diese Spezialfälle in den Testfällen von der<br />
<strong>BWINF</strong>-Seite ohnehin nicht auf.<br />
1. Es treten nur die in der vorherigen Liste genannten Fehler auf.<br />
2. Der erste Eintrag in der Log-Datei ist immer korrekt.<br />
3. Die Fehler in der Log-Datei halten sich in Grenzen.<br />
4. Dominic bewegt sich mit annähernd konstanter Geschwindigkeit. Leichte<br />
Abweichungen führen in der Regel nicht zu Problemen.<br />
1.5 Fehler in den GPS-Koordinaten<br />
Ein Fehler in den GPS-Koordinaten soll mit Hilfe von späteren GPS-Daten<br />
behoben werden. Da keine weiteren Informationen zur Verfügung stehen, ist<br />
natürlich nicht genau bekannt, welchen Weg Dominic zurücklegt. Deshalb kann<br />
nur mit Hilfe von später aufgezeichneten Daten ein Fehler in den GPS-Koordinaten<br />
behoben werden, indem man annimmt, dass sich Dominic zum Beispiel geradlinig<br />
auf einen Koordinatenpunkt zubewegt, von dem bekannt ist, dass er korrekt<br />
ist. Allerdings ist nicht bekannt, welche Koordinaten richtig und welche falsch<br />
sind. Aus diesem Grund berechnet das Programm eine Wahrscheinlichkeitsfunktion,<br />
die für jeden Punkt in den GPS-Koordinaten ausgibt, wie wahrscheinlich es<br />
ist, dass sich Dominic auf irgendeinen späteren Punkt in den GPS-Koordinaten<br />
zubewegt.<br />
Hinter der Wahrscheinlichkeitsfunktion steckt die Idee, dass sich Dominic<br />
mehr oder weniger mit konstanter Geschwindigkeit bewegt. In den GPS-Daten<br />
können bestimmte Fehler dadurch erkannt werden, dass innerhalb kürzester Zeit<br />
3
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
sehr große Strecken zurückgelegt werden. Eine Wahrscheinlichkeitsfunktion soll<br />
nun für jede beliebige Geschwindigkeit (velocity) eine Aussage darüber geben,<br />
wie wahrscheinlich es ist, dass sich Dominic mit dieser Geschwindigkeit fortbewegt.<br />
Dazu iteriert das Programm über alle GPS-Koordinaten und berechnet<br />
die Geschwindigkeit, mit der sich Dominic zum nächsten Punkt fortbewegt<br />
(Geschwindigkeit = Strecke / Zeit, Array db p segment). Eine Geschwindigkeit<br />
velocity soll genau dann als sehr wahrscheinlich angenommen werden, wenn die<br />
Funktion db p getProbability an dieser Stelle minimal ist.<br />
Definition 2 Die Funktion ist dabei mit db p getProbability(velocity) = ∑ n−1<br />
i=1 v i−<br />
velocity definiert.<br />
Mit Hilfe der Funktion db p getProbability kann also zu jeder beliebigen<br />
Geschwindigkeit velocity ausgesagt werden, wie wahrscheinlich es ist, dass sich<br />
Dominic mit dieser Geschwindigkeit fortbewegt. Dazu werden einfach die Differenzen<br />
zwischen allen Geschwindigkeiten aus der Log-Datei und der Geschwindigkeit<br />
velocity aufsummiert. Die Geschwindigkeiten aus der Log-Datei können<br />
einfach mit der Beziehung Geschwindigkeit = Strecke/Zeit berechnet werden.<br />
Dabei ist Strecke der geometrische Abstand zwischen zwei aufeinanderfolgenden<br />
GPS-Einträgen und Zeit der zeitliche Abstand zwischen den zwei<br />
GPS-Einträgen. Der Term n − 1 in Definition 2 steht für die Anzahl aller<br />
Geschwindigkeits-Daten, die der Log-Datei entnommen werden können, wenn<br />
n Einträge in der Log-Datei vorliegen.<br />
Nach einigen Experimenten, habe ich die Funktion db p getProbability so<br />
abgeändert, dass insgesamt nur Geschwindigkeitsüberschreitungen betrachtet<br />
werden, und keine Unterschreitungen. Dadurch erzielt das Programm bessere<br />
Ergebnisse. Es kann schließlich vorkommen, dass Dominic auch mal langsamer<br />
geht. Nur besonders hohe Geschwindigkeiten sind besonders unwahrscheinlich.<br />
Wenn Fehler in den GPS-Koordinaten auftreten, können diese nicht gänzlich<br />
korrigiert werden. Schließlich kann keine sichere Aussage darüber getroffen werden,<br />
wie sich Dominic wirklich bewegt hat. Es besteht lediglich die Möglichkeit,<br />
bestimmte GPS-Koordinaten als falsch zu deklarieren, wenn es sehr unwahrscheinlich<br />
ist, dass sich Dominic in einem bestimmten Zeitfenster dort hin begeben<br />
hat. Wenn der fehlerhafte Bereich erst einmal identifiziert wurde, kann er<br />
mit sinnvollen Daten ersetzt werden. Ob sich Dominic jedoch wirklich so bewegt<br />
hat, kann man nicht sagen.<br />
Abbildung 1 zeigt einen Weg, den Dominics GPS-Gerät aufgezeichnet hat.<br />
Dabei stellen die schwarzen Linien den korrekt aufgezeichneten Weg dar. Die<br />
roten Linien befinden sich ebenfalls in den GPS-Logs, sind jedoch falsch. Wenn<br />
das Programm erkannt hat, dass der fehlerhafte Bereich zwischen den Punkten<br />
E und H liegt, versucht es, die Logs zu korrigieren. Dazu wählt es die grüne Linie<br />
als Ersatz für die roten Linien. Ob sich Dominic jedoch wirklich so fortbewegt<br />
hat, oder aber so, wie die blauen Linien verlaufen, bleibt ungewiss.<br />
Um nun Fehler in den GPS-Koordinaten zu korrigieren, iteriert das Programm<br />
über alle Punkte aus der Log-Datei (äußere Schleife). Jetzt wird für<br />
jeden solchen Punkt der nächste (darauffolgende) Punkt bestimmt. Dazu iteriert<br />
das Programm über alle Punkte, die zeitlich nach dem aktuellen Punkt<br />
liegen (innere Schleife). Mit Hilfe der Funktion db p getProbability kann nun<br />
bestimmt werden, wie wahrscheinlich es ist, dass der aktuelle Punkt in der innersten<br />
Schleife der Nachfolger des aktuellen Punktes in der äußeren Schleife<br />
4
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 1: Korrektur von GPS-Fehlern<br />
5
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 2: Falsche Korrektur von GPS-Fehlern<br />
ist. Als Strecke wird die geometrische und als Zeit die zeitliche Differenz zwischen<br />
den beiden Punkte in die Funktion eingesetzt (Geschwindigkeit = Strecke<br />
/ Zeit).<br />
Bei der Verarbeitung der Daten scheiden somit diejenigen Punkte aus, die<br />
laut GPS-Daten mit einer unwahrscheinlichen Geschwindigkeit erreicht werden.<br />
In Abbildung 1 sei der zeitliche Abstand in den GPS-Logs zwischen den einzelnen<br />
Punkte A bis J jeweils eine Zeiteinheit (also gleich). Dann ist es sehr<br />
unwahrscheinlich, dass sich Dominic von Punkt E nach Punkt F fortbewegt,<br />
weil er seine Geschwindigkeit mehr als versechsfacht. Wenn die äußere Schleife<br />
bei Punkt E angelangt ist und nach einem geeigneten Nachfolger sucht, wird<br />
Punkt F sehr schlecht bewertet. Ebenso wird Punkt G schlecht bewertet. Dominic<br />
stehen nun zwar zwei Zeiteinheiten zur Verfügung, um jedoch von Punkt E<br />
nach Punkt G zu gelangen, würde Dominic seine Geschwindigkeit immer noch<br />
mehr als verfünffachen. Schließlich wird Punkt H betrachtet. Dieser Punkt wird<br />
als viel wahrscheinlicher eingeschätzt, weil Dominic seine Geschwindigkeit von<br />
E nach H beibehälten würde.<br />
Jedoch ergibt sich das Problem, dass möglicherweise viele Punkte aus den<br />
GPS-Logs übersprungen werden, weil die Wahrscheinlichkeitsfunktion (bisher)<br />
nur Strecke und Zeit beachtet. Dazu betrachte man Abbildung 2. Es sind keine<br />
Fehler in den GPS-Logs aufgetreten und trotzdem schlägt das Programm<br />
die grüne Strecke von Punkt E nach Punkt J vor. Ab Punkt E beschleunigt<br />
Dominic etwas. Man stelle sich vor, die GPS-Logs seinen etwas größer, als der<br />
dargestellte Bereich und Dominic würde sich nach Punkt J weiter mit konstanter<br />
6
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Geschwindigkeit, wie von Punkt A bis Punkt E, bewegen. Das bedeutet, dass es<br />
wahrscheinlicher ist, dass sich Dominic mit der Geschwindigkeit wie von Punkt<br />
A bis Punkt E bewegt, als mit der Geschwindigkeit wie von Punkt E bis Punkt<br />
J. Wenn die äußere Schleife bei Punkt E angelangt ist, werden alle potentiellen<br />
Nachfolgepunkte betrachtet. Schließlich wird Punkt J gewählt, weil Dominic bei<br />
geradliniger Bewegung von Punkt E nach Punkt J seine Geschwindigkeit beibehalten<br />
würde. Eine minimale Änderung der Fortbewegungsgeschwindigkeit hat<br />
also dazu geführt, dass ein richtiger Weg verworfen wird.<br />
Dies kann man verhindern, indem man Punkte in der inneren Schleife nach<br />
der Anzahl der übersprungenen Punkte (Anzahl der übersprungenen Punkte<br />
zwischen aktuellen Punkten in der inneren und äußeren Schleife) gewichtet.<br />
Je mehr Punkte übersprungen werden müssten, desto unwahrscheinlicher wird<br />
dieser Nachfolger eingestuft. Wenn Fehler in den GPS-Logs zu stark überhöhten<br />
Geschwindigkeiten führen, werden die Logs trotzdem korrigiert, weil der Geschwindigkeits-<br />
Aspekt in der Wahrscheinlichkeitsfunktion stärker wiegt, als die Gewichtung<br />
auf Grund von übersprungenen Punkten. Zu Problemen kann es aber kommen,<br />
wenn zu große Teile der Log-Datei fehlerhaft sind. Eine große Anzahl an GPS-<br />
Koordinaten wird in der Regel nicht übersprungen.<br />
Man betrachte hierzu wieder Abbildung 2. Um den Weg von Punkt E nach<br />
Punkt J wie vorgeschlagen zu korrigieren, müssten vier Punkte (F bis I) übersprungen<br />
werden. Dieser Faktor Vier fließt auf irgendeine negative Weise in die<br />
Gewichtung ein, sodass der Weg in Abbildung 2 nicht korrigiert wird. Es muss<br />
nun festgestellt werden, wie dieser Faktor genau in die Gewichtung einfließt.<br />
Wiegt er zu stark, werden evtl. Fehler wie in Abbildung 1 nicht mehr korrigiert,<br />
wiegt er zu schwach, treten Fehler wie in Abbildung 2 auf. Der Faktor könnte<br />
beispielweise linear oder exponentiell in die Bewertung einfließen. Im letzteren<br />
Fall wiegen große Zahlen auf Grund des schnellen Wachstums der exponentiellen<br />
Funktion sehr stark, kleine Zahlen jedoch nicht so stark. Es wird also angenommen,<br />
dass es schon mal vorkommen kann, dass einzelne Punkte falsch sind. Auf<br />
der anderen Seite wird auch angenommen, dass es sehr unwahrscheinlich ist,<br />
dass weite Punkt-Intervalle falsch sind.<br />
1.6 Fehler in den Zeitangaben<br />
Wie bereits erwähnt, können Log-Einträge nur entweder ganz fehlen oder in<br />
verschiedenen Zeitintervallen aufgenommen werden. Beide Fälle können gleich<br />
behandelt werden, weil das Fehlen von Log-Einträgen dem Aufzeichnen von<br />
Log-Daten in unterschiedlichen Zeitintervallen gleicht. Bei Betrachtung des im<br />
vorherigen Kapitel vorgestellten Algorithmus wird klar, dass fast keine weiteren<br />
Änderungen notwendig sind. Der Algorithmus funktioniert auch dann, wenn die<br />
Zeitintervalle zwischen zwei Log-Einträgen unterschiedlich sind.<br />
Zu Problemen kann es nur dann kommen, wenn große Teile der GPS-Daten<br />
fehlen. In diesem Fall kann der fehlende Bereich nicht sinnvoll rekonstruiert<br />
werden. Stattdessen wird der Weg durch eine gerade Strecke angegleicht. Dieser<br />
Fall wurde bereits im vorherigen Kapitel erläutert. In der Ausgabedatei werden<br />
solche Bereiche mit einer anderen Farbe eingezeichnet.<br />
7
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
2 Programm-Dokumentation<br />
Das Programm wurde mit C++ geschrieben und als Linux-Binary kompiliert.<br />
Wenn der Quelltext unter Windows kompiliert werden soll, sind offensichtlich einige<br />
Änderungen notwendig (besonders im Zusammenhang mit Zeilenbrüchen).<br />
Zumindest kam es bei meinen Versuchen zu Problemen.<br />
Das Programm geht bei der Verarbeitung einer Log-Datei nach den folgenden<br />
Schritten vor.<br />
1. Einlesen der Bilddatei und der Log-Datei in den Arbeitsspeicher.<br />
2. Anwendung der Wahrscheinlichkeitsfunktion auf jeden Log-Eintrag.<br />
3. Bearbeitung der Bilddatei und Speichern der Bilddatei.<br />
2.1 Einlesen der Eingabedateien<br />
Das Einlesen der Eingabedateien funktioniert analog zu <strong>Aufgabe</strong> 5. Es wurden<br />
für die Bilddatei die gleichen Arrays und Bezeichnungen verwendet. Einige<br />
Zeilen Quelltext wurden sogar komplett übernommen. Die Log-Datei wird<br />
in drei Arrays übertragen, wobei db time[i] den Zeitpunkt des i-ten Eintrags,<br />
db pos x[i] die x-Koordinate und db pos y[i] die y-Koordinate bezeichnen. Der<br />
Zeitwert gibt die seit 00:00:00 Uhr vergangenen Sekunden an und wird nach<br />
der Formel db time[i] = stunden · 3600 + minuten · 60 + sekunden berechnet.<br />
Weil keine weiteren Anpassungen vorgenommen wurden, kann es zu den bereits<br />
genannten Problemen kommen.<br />
GPS-Koordinaten werden in Pixel-Koordinaten konvertiert. Jeder Pixel-Wert<br />
wird auf einen ganzen Integer-Wert abgerundet.<br />
Die Log-Datei wird zeilenweise eingelesen. Weil anfangs noch nicht bekannt<br />
ist, wie viele Zeilen gelesen werden müssen, werden eine Endlosschleife benutzt<br />
und so lange neue Zeilen eingelesen, bis die aktuelle Zeile weniger als 5 Zeichen<br />
lang ist. In einem solchen Fall springt das Programm in den ersten Teil der<br />
entsprechenden If-Struktur und fährt dort mit der Verarbeitung der eingelesenen<br />
Daten fort. Es gibt keine explizite Abbruchbedingung für die Endlosschleife.<br />
Diese terminiert dann, wenn das Programm nach der Verarbeitung der Daten<br />
terminiert.<br />
Es gibt einige Anforderungen an die Eingabedateien, die erfüllt sein müssen,<br />
damit die sie korrekt einlesen werden können.<br />
1. Die Log-Datei weist das gleiche Format wie die Testfälle von der <strong>BWINF</strong>-<br />
Seite auf. Insbesondere überprüft das Programm, ob die erste Zeile mit der<br />
Zeichenfolge TIME,LATITU<strong>DE</strong>,LONGITU<strong>DE</strong> übereinstimmt, um grob<br />
zu überprüfen, ob der Benutzer wirklich eine Log-Datei ausgewählt hat.<br />
2. Die Log-Datei hat den Dateinamen log.csv und die Bilddatei hat den Namen<br />
karte.ppm. Die Ausgabe erfolgt in die Datei karte out.ppm.<br />
3. Es handelt sich um eine 24-bit-PPM vom Typ P6, siehe auch <strong>Aufgabe</strong> 5.<br />
4. Es findet kein Zeitsprung von 23:59:59 Uhr auf 00:00:00 Uhr statt.<br />
5. Es liegen weniger als 2000 Log-Einträge vor.<br />
6. Die Log-Datei ist korrekt formatiert (wie in den <strong>BWINF</strong>-Testfällen).<br />
8
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
2.2 Die Wahrscheinlichkeitsfunktion<br />
Der Wert der Wahrscheinlichkeitsfunktion ergibt sich, indem die Differenzen einer<br />
Geschwindigkeit zu allen Geschwindigkeiten in der Log-Datei aufsummiert<br />
werden (siehe Lösungsidee). Dazu wird die Wahrscheinlichkeitsfunktion bei jedem<br />
Aufruf neu berechnet. Es wäre möglich, diesen Vorgang etwas effizienter<br />
zu gestalten, indem die Durchschnittsgeschwindigkeit verwendet wird, jedoch<br />
ergibt sich dadurch effektiv nur ein geringer Laufzeitgewinn, da es sich bei den<br />
<strong>BWINF</strong>-Testfällen um kleine Eingabedaten handelt.<br />
Nach dem Einlesen der Eingabedateien gilt es, die Wahrscheinlichkeitstabelle<br />
aufzubauen. Da in der Log-Datei nur Zeitangaben und Positionswerte eingetragen<br />
sind, müssen Geschwindigkeiten aus diesen Daten berechnet werden. Nach<br />
diesem Vorgang ist für jedes Paar von aufeinanderfolgenden Log-Einträgen ein<br />
entsprechender Geschwindigkeitseintrag vorhanden (Array db p segment).<br />
√<br />
(db pos x[i+1]−db pos x[i])2 +(db pos y[i+1]−db pos y[i]) 2<br />
Definition 3 Jeder Eintrag im Geschwindigkeitsarray ergibt sich durch die Formel<br />
db p segment[i] =<br />
db time[i+1]−db time[i]<br />
. Dabei<br />
bezeichnen der im Zähler stehende Term den geometrischen Abstand zweier<br />
Punkte und der im Nenner stehende Term den zeitlichen Abstand zweier Punkte.<br />
Nun iteriert das Programm über alle Punkte in der Log-Datei und sucht für<br />
jeden solchen Punkt den wahrscheinlichsten Nachfolger. Dazu wird die Wahrscheinlichkeitsfunktion<br />
auf jeden potentiellen Nachfolger eines solchen Punktes<br />
angewendet. Als Eingabe erwartet die Wahrscheinlichkeitsfunktion eine Geschwindigkeit<br />
die sich analog zu Definition 3 ergibt, nur dass es sich nun nicht<br />
notwendigerweise um aufeinanderfolgende Punkte handeln muss. Je kleiner die<br />
Ausgabe der Wahrscheinlichkeitsfunktion ist, desto wahrscheinlicher ist es, dass<br />
der aktuelle Punkt der beste Nachfolger ist.<br />
In der Funktion db p getProbability wird die Summe der Geschwindigkeitsdifferenzen<br />
wird durch die Durchschnittsgeschwindigkeit und durch n−1 geteilt<br />
(n ist die Anzahl der Punkte). Dadurch erhält man sozusagen eine prozentuale<br />
Abweichung von der Durchschnittsgeschwindigkeit. Ursprünglich hatte ich eine<br />
andere Idee, die ich jedoch aus Zeitgründen nicht realisieren konnte (deshalb so<br />
umständlich). Im Kapitel Lösungsidee wurde bereits angedeutet, wie eine Wahrscheinlichkeitsfunktion<br />
aussehen kann. Ich habe schließlich keine Exponentialfunktion<br />
gewählt. Stattdessen wird die Anzahl der übersprungenen Punkte mit<br />
dem Faktor 0, 45 gewichtet und einfach aufaddiert. Der Nachteil dieser Methode<br />
liegt darin, dass sie speziell auf die in den Testfällen auftretenden Geschwindigkeiten<br />
optimiert ist. Diese Geschwindigkeiten wirken sich auf die Ausgabe<br />
der Wahrscheinlichkeitsfunktion (Funktion db p getProbability, ohne Beachtung<br />
der übersprungenen Punkte) aus. Wenn sich Dominic nun beispielsweise immer<br />
doppelt so schnell bewegt, oder allgemein die auftretenden Geschwindigkeiten<br />
zwischen zwei aufeinanderfolgenden Punkten proportional zueinander um einen<br />
relativen Wert vergrößert werden, nimmt der Einfluss der übersprungenen Punkte<br />
ab. Man müsste in diesem Fall den Faktor 0, 45 ebenfalls vergrößern, nämlich<br />
um den Faktor 2.<br />
Man könnte das Problem auch lösen, indem man den Wert nicht aufaddiert,<br />
sondern multipliziert. Dann müsste man aber einen anderen Faktor als 0, 45<br />
verwenden. In meinen eigenen Versuchen habe ich mit dem Aufaddieren die<br />
besten Ergebnisse erzielt.<br />
9
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Wenn das Programm mehrere Punkte übersprungen hat, so wird der dazwischen<br />
liegende Bereich durch eine Gerade angegleicht. Dazu müssen die Koordinaten<br />
der dazwischen liegenden Punkte entsprechend geändert werden. Wenn<br />
gerade der Punkt mit dem Index i betrachtet wird und der Punkt j > i als Nachfolger<br />
ermittelt wurde, so wird zunächst die Geschwindigkeit ermittelt, mit der<br />
sich Dominic von i nach j bewegt. Dazu wird Definition 3 angewendet. Dann<br />
wird ermittelt, welche Strecke Dominic in der Zeit db time[i + 1] − db time[i]<br />
zurückgelegt haben muss. Die Koordinaten von i+1 werden dann entsprechend<br />
gesetzt, die anderen Punkte bleiben unverändert. Im nächsten Schritt wird ein<br />
Nachfolger für i + 1 gesucht. Die Wahrscheinlichkeitsfunktion bewertet jetzt<br />
j wieder als wahrscheinlichsten Nachfolger, da j schon vorher der wahrscheinlichste<br />
Nachfolger war. Die Wahrscheinlichkeit steigt u. U. sogar etwas, weil sich<br />
Dominic schon gezielt auf den Punkt j zubewegt hat. Der Vorgang setzt sich so<br />
lange fort, bis der der Punkt mit dem Index j erreicht wurde.<br />
2.3 Kürzen fehlerhafter Pfade<br />
Es gibt gewisse Fehler, die der vorgestellte Algorithmus möglicherweise nicht<br />
korrekt löst. Unter Kapitel Programm-Ablaufprotokoll zeigt die Abbildung log2.csv<br />
mit Änderungen und ohne Kürzen einen solchen Fall. Es fehlen einige GPS-<br />
Einträge, weshalb rote Strecken eingezeichnet wurden. Dass Dominic sich zur<br />
Straße Neupforte bewegt, halte ich für einen offensichtlichen Fehler. Trotzdem<br />
konnte das Programm den Fehler zunächst nicht beheben. Das liegt einerseits<br />
daran, dass kein passender Punkt als Nachfolger gefunden werden konnte, als<br />
die erste rote Strecke gezeichnet wurde. Offensichtlich ist Dominic ein paar Mal<br />
im Kreis gelaufen oder hat sich nicht direkt auf einen passenden Punkt mit<br />
der richtigen Geschwindigkeit zubewegt. Viel wichtiger ist aber, dass etwa 10<br />
Punkte hätten übersprungen werden müssen. Diesen Fall hat die Wahrscheinlichkeitsfunktion<br />
als äußerst unwahrscheinlich eingestuft und deshalb abgelehnt.<br />
Eine Änderung der Gewichtung in der Wahrscheinlichkeitsfunktion brachte bei<br />
meinen Experimenten keine wirkliche Verbesserung, da dann auch noch viele<br />
richtige Punkte korrigiert wurden.<br />
Solche Fehler können behoben werden, indem das Programm die Anzahl der<br />
zwischen zwei roten Strecken vorkommenden Punkte zählt. Handelt es sich um<br />
weniger als 15 Punkte, so wird der Zwischenraum übersprungen. Rote Strecken<br />
kommen genau dann zu Stande, wenn mehr als 30 Sekunden zwischen zwei<br />
GPS-Einträgen fehlen.<br />
2.4 Bearbeitung der Bilddatei<br />
Bevor der neue Weg schließlich auf die Karte gezeichnet wird, untersucht das<br />
Programm die Koordinaten auf mögliche Unstimmigkeiten und Fehler. Dadurch<br />
ist es möglich, die Qualität der Rekonstruktion abzuschätzen. Bevor eine Strecke<br />
gezeichnet wird, wird untersucht, wie schnell sich Dominic dort im Vergleich<br />
zur Durchschnittsgeschwindigkeit in der Log-Datei bewegt. Wenn die Geschwindigkeit<br />
stark überschritten wird, wird der Bereich gelb eingezeichnet, wenn die<br />
Geschwindigkeit hingegen stark unterschritten wurde, wird der Bereich hellblau<br />
eingezeichnet. Wenn zwischen zwei Punkten mehr als 30 Sekunden in der Log-<br />
Datei fehlen, so wird der Bereich rot eingezeichnet. In einem solchen Fall kann<br />
10
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
der Weg nicht richtig rekonstruiert wurde, weil keine Informationen darüber vorliegen,<br />
wie sich Dominic bewegt haben könnte. Rote Segmente, sowie kleinere<br />
Stücke, die von roten Segmenten eingeschlossen werden, sind also mit Vorsicht<br />
zu genießen.<br />
Das Speichern der Bilddatei erfolgt wie in <strong>Aufgabe</strong> 5 und Änderungen an<br />
der Log-Datei werden nicht gespeichert. Stattdessen werden Änderungen in der<br />
Kommandozeile ausgegeben.<br />
2.5 Fakten zum Programm<br />
Das Programm hat, so wie es aufgebaut ist, einige Vorteile, aber auch Macken.<br />
So arbeitet es beispielsweise relativ schnell, wurde aber nur auf den <strong>BWINF</strong>-<br />
Ausgangsdaten ausgiebig getestet. Es ist jedoch nicht so einfach möglich, den<br />
Algorithmus zu erweitern, sodass er mehr Fälle abdecken kann. Dennoch behebt<br />
er die Fehler auf den <strong>BWINF</strong>-Testfällen meiner Meinung nach relativ gut und<br />
Dominic erhält einen guten Überblick darüber, wo er überall war. Es werden<br />
nun einige Fakten und Ideen zum Programm und Algorithmus aufgelistet.<br />
1. Es handelt sich um einen Greedy-Algorithmus, der immer die aktuell beste<br />
Möglichkeit sucht und auswählt. Deshalb wählt das Programm immer den<br />
nächsten Nachfolger so, wie er aktuell als am wahrscheinlichsten erachtet<br />
wird. Der weitere Verlauf des Pfades wird nicht beachtet, es wird immer<br />
nur der beste Nachfolger für den aktuellen Punkt betrachtet. Das führt<br />
sicher nicht immer zum besten Ergebnis.<br />
2. Wie in der <strong>BWINF</strong>-Newsgroup bereits diskutiert, spielt die Landkarte<br />
keine Rolle, sie wird lediglich unter den Weg gelegt.<br />
3. Es wäre möglich, nicht nur die Geschwindigkeit, sondern auch die Beschleunigung<br />
Dominics zu betrachten, das wurde aber nicht implementiert.<br />
4. Änderungen am Pfad werden immer durch Geraden durchgeführt.<br />
3 Programm-Ablaufprotokoll<br />
Es folgen nun die Ausgaben des Programms in die Ausgabedatei karte out.ppm,<br />
wobei immer der Weg zuerst ohne und dann mit Änderungen angezeigt wird.<br />
Grüne Linien stellen Strecken dar, auf denen sich Dominic mit seiner Durchschnittsgeschwindigkeit<br />
aus der aktuellen Log-Datei bewegt. Wenn Linien gelb<br />
dargestellt werden, so bewegt sich Dominic schneller und wenn Linien hellblau<br />
dargestellt werden, so bewegt sich Dominic langsamer. Der Übergang ist dabei<br />
fließend. Rote Strecken sind mit Vorsicht zu genießen, da dort mindestens 30<br />
Sekunden Log-Daten fehlen. Es kann somit nicht festgestellt werden, wie sich<br />
Dominic dort fortbewegt hat.<br />
Eine Ausgabe des Programms ist dann gut, wenn sie möglichst wenige gelbe<br />
Strecken und möglichst wenige Abweichungen von den Original-Daten aufweist.<br />
Betrachtet man die Karte von log1.csv, so fallen u. a. Änderungen im<br />
nördlichen Teil der Karte auf. Dort bewegt sich Dominic nun anders. Besonders<br />
auffällig ist, dass in den Original-Daten eine große gelbe Strecke, gefolgt<br />
von sehr vielen, kleinen, hellblauen Stecken, auftritt. Der Algorithmus hat die<br />
11
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 3: log1.csv ohne Änderungen<br />
12
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 4: log1.csv mit Änderungen<br />
13
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 5: log2.csv ohne Änderungen<br />
Abbildung 6: log2.csv mit Änderungen, ohne Kürzen<br />
14
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 7: log2.csv mit Änderungen<br />
15
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Abbildung 8: log3.csv ohne Änderungen<br />
Abbildung 9: log3.csv mit Änderungen<br />
16
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
Log-Daten entsprechend verändert und nun sind an dieser Stelle keine gelben<br />
Strecken mehr zu sehen. Der ursprüngliche Pfadverlauf wurde weitestgehend<br />
beibehalten. Viel interessanter sind die Ausgaben für log2.csv. Dort fallen zwei<br />
große Fehler, nämlich die Ausschweifer nach Norden und nach Westen auf. Diese<br />
Wege hat Dominic laut den Logs mit überhöhter Geschwindigkeit begangen.<br />
Zudem fehlen dort auch noch Log-Einträge. Der Weg konnte größtenteils korrigiert<br />
werden, jedoch sind noch einige kurze, rote Strecken zu sehen, an denen<br />
der genaue Weg nicht bekannt ist, weil Log-Daten fehlen. Kleinere Änderungen<br />
wurden im Bereich Domschatzkammer durchgeführt, wo sich Dominic mit<br />
überhöhter Geschwindigkeit fortbewegt hat. Die Karte von log3.csv zeigt nicht<br />
viel Neues. Einige Ausläufer von Dominics Weg an der Jakobstraße sind nun<br />
nicht mehr so deutlich, weil Dominic diese bei genauer Betrachtung wieder mit<br />
hoher Geschwindigkeit durchquert hat.<br />
Abschließend lässt sich sagen, dass das Programm hauptsächlich die sofort<br />
offensichtlichen Fehler gut behebt. Kleine Geschwindigkeitsüberschreitungen haben<br />
keine allzu großen Auswirkungen.<br />
4 Programm-Quelltext<br />
1 #include <br />
2 #include <br />
3 #include <br />
4 #include <br />
5 #include <br />
6 #include <br />
7 #include <br />
8<br />
10<br />
9 using namespace std;<br />
11 // Vordeklarationen fuer eigene Funktionen<br />
12 void readLine(FILE * file_handle);<br />
13 void paintDot(int x, int y);<br />
14 void paintLine(int x1, int x2, int y1, int y2, int r, int g, int b);<br />
15 void setPixel(int x, int y, int r, int g, int b);<br />
16 int convertCoordinateToPixelEast(double value);<br />
17 int convertCoordinateToPixelNorth(double value);<br />
18 void insertDatabaseEntry(int index);<br />
19 void db_calculateAverageDistance();<br />
20 double db_calculateDistance(int index1, int index2);<br />
21 double db_p_getProbability(double velocity);<br />
22<br />
23 // Aktuell eingelesene Zeile. Keine Zeile darf mehr als 9999 Zeichen haben!<br />
24 char currentLine[10000];<br />
25<br />
26 // Matrix: Enthaelt das gesamte Bild (Karte)<br />
27 unsigned char*** matrix;<br />
28<br />
29 // Proportionen (Groesse in Pixel) der Bilddatei<br />
30 int proportions[2];<br />
31<br />
32 // Log-Datenbank, maximal 2000 Datensaetze<br />
33 // TO-DO: Verkettete Liste anstatt Array, um Entries effizient einfuegen zu<br />
koennen!<br />
34 int db_time[2000];<br />
35 int db_pos_x[2000]; // Pixelangaben<br />
36 int db_pos_y[2000]; // Pixelangaben<br />
37 int db_count_entries = 0;<br />
38 double db_average_distance_per_time = 0.0; // Durchschnittliche<br />
Geschwindigkeit<br />
39 double db_average_velocity_difference = 0.0; // Durchschnittliche<br />
Abweichung der Geschwindigkeiten voneinander (absolut)<br />
40<br />
41 // Wahrscheinlichkeitsfunktion fuer Geschwindigkeit<br />
17
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
42 double db_p_segment[2000];<br />
43<br />
44 // Konstanten fuer Koordinatenecken<br />
45 const double coord_left = 6.0711;<br />
46 const double coord_right = 6.0918;<br />
47 const double coord_top = 50.7820;<br />
48 const double coord_bottom = 50.7716;<br />
49<br />
50 /// *************************************************<br />
51 /// * Hier startet das Programm. *<br />
52 /// *************************************************<br />
53 int main(int argc, char** argv)<br />
54 {<br />
55 // freopen( "file.txt", "w", stdout );<br />
56<br />
57 FILE * pFileMap = fopen("karte.ppm", "r");<br />
58 FILE * pFileLog = fopen("log.csv", "r");<br />
59<br />
60 // Maximale Laenge einer Zeile im Header: 5000 Zeichen (5 KB)<br />
61 char fbuffer_header[5000];<br />
62 memset(fbuffer_header , 5000, 0);<br />
63 fread(fbuffer_header , 2, 1, pFileMap);<br />
64<br />
65 // Dateiformat ueberpruefen<br />
66 if (strcmp(fbuffer_header , "P6") != 0)<br />
67 {<br />
68 printf("Die Eingabedatei karte.ppm weist ein unerwartetes Format auf<br />
und kann nicht verarbeitet werden (nur PPM-P6).\n");<br />
69 return 0;<br />
70 }<br />
71<br />
72 // Zeilenumbruch einlesen<br />
73 fread(fbuffer_header , 1, 1, pFileMap);<br />
74<br />
75 // Proportionen einlesen<br />
76 readLine(pFileMap);<br />
77 sscanf(currentLine, "%d %d", &proportions[0], &proportions[1]);<br />
78 printf("Proportionen der Datei karte.ppm: %d x %d Pixel.\n", proportions<br />
[0], proportions[1]);<br />
79<br />
80 // Farbtiefe auslesen<br />
81 readLine(pFileMap);<br />
82<br />
83 if (strcmp(currentLine, "255") != 0)<br />
84 {<br />
85 printf("Die Eingabedatei karte.ppm weist ein unerwartetes Format auf<br />
und kann nicht verarbeitet werden (nur 24-bit-Farbtiefe).\n");<br />
86 return 0;<br />
87 }<br />
88<br />
89 printf("Reserviere Speicherplatz. Dieser Vorgang kann einige Sekunden<br />
dauern...\n");<br />
90<br />
91 // Matrix: Enthaelt die ganze Bilddatei<br />
92 // Speicher dynamisch allokieren<br />
93 matrix = new unsigned char**[proportions[0]];<br />
94<br />
95 for (int i = 0; i < proportions[0]; i++)<br />
96 {<br />
97 matrix[i] = new unsigned char*[proportions[1]];<br />
98<br />
99 for (int j = 0; j < proportions[1]; j++)<br />
100 {<br />
101 matrix[i][j] = new unsigned char[3];<br />
102 }<br />
103 }<br />
104<br />
105 // Matrix fuellen<br />
106 printf("Lese alle Pixel in den Arbeitsspeicher. Dieser Vorgang kann<br />
einige Sekunden dauern...\n");<br />
107<br />
108 for (int i = 0; i < proportions[1]; i++)<br />
109 {<br />
110 // Iteriere ueber alle "Zeilen"<br />
18
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
111<br />
112 for (int j = 0; j < proportions[0]; j++)<br />
113 {<br />
114 // Iteriere ueber alle Zeichen in der aktuellen "Zeile"<br />
115 fscanf(pFileMap,"%c%c%c", &matrix[j][i][0], &matrix[j][i][1], &<br />
matrix[j][i][2]);<br />
116 }<br />
117 }<br />
118<br />
119 printf("Lese LOG-Datei in den Arbeitsspeicher...\n");<br />
120 fread(fbuffer_header , 23, 1, pFileLog);<br />
121<br />
122 if (strcmp(fbuffer_header , "TIME,LATITU<strong>DE</strong>,LONGITU<strong>DE</strong>") != 0)<br />
123 {<br />
124 printf("Die Log-Datei weist ein unbekanntes Format auf und kann nicht<br />
verarbeitet werden (ungueltiger Header).\n");<br />
125 return 0;<br />
126 }<br />
127<br />
128 // Zeilenumbruch einlesen<br />
129 fread(fbuffer_header , 1, 1, pFileLog);<br />
130<br />
131 while (true)<br />
132 {<br />
133 readLine(pFileLog);<br />
134<br />
135 if (strlen(currentLine) < 5)<br />
136 {<br />
137 // Log-Datei wurde komplett eingelesen, weiterer Programmablauf<br />
in dieser If-Struktur<br />
138<br />
139 printf("Sollen die Log-Daten ohne weitere Anpassungen auf die<br />
Karte gezeichnet werden? (j/n)\n");<br />
140 char tmp_input;<br />
141 scanf("%c", &tmp_input);<br />
142<br />
143 // Berechne durchschnittliche Geschwindigkeit<br />
144 db_calculateAverageDistance();<br />
145 printf("Die durchschnittliche Geschwindigkeit betraegt %f Pixel/<br />
Sekunde.\n", db_average_distance_per_time);<br />
146<br />
147 if (tmp_input == ’n’)<br />
148 {<br />
149 // Verarbeite Log-Daten<br />
150 printf("Verarbeite Daten, Berechnung laeuft. Dieser Vorgang<br />
kann einige Sekunden dauern.\n");<br />
151<br />
152 // Baue Wahrscheinlichkeitsfunktion fuer Geschwindigkeit auf<br />
153 for (int i = 0; i < db_count_entries - 1; i++)<br />
154 {<br />
155 // Iteriere ueber alle Eintraege in der Log-Datenbank<br />
156 // Geschwindigkeit = Strecke / Zeit<br />
157 db_p_segment[i] = db_calculateDistance(i, i + 1) / (<br />
db_time[i + 1] - db_time[i]);<br />
158 }<br />
159<br />
160 int db_last_missing = -1; // Wann haben zum lezten Mal<br />
zu viele Eintraege gefehlt?<br />
161<br />
162 // Wende Wahrscheinlichkeitsfunktion auf jeden Eintrag in der<br />
Log-Datenbank an<br />
163 for (int i = 0; i < db_count_entries - 1; i++)<br />
164 {<br />
165 // Iteriere ueber alle Eintraege in der Log-Datenbank.<br />
Der erste und letzte Eintrag sind per Definition<br />
richtig!<br />
166<br />
167 if (db_time[i + 1] - db_time[i] > 30)<br />
168 {<br />
169 printf("[W] Es fehlen zu viele Eintraege in %d bis %d<br />
!\n", i, i + 1);<br />
170<br />
171 if (db_last_missing == -1)<br />
172 {<br />
19
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
173 // nichts machen<br />
174 }<br />
175 else<br />
176 {<br />
177 if (i - db_last_missing < 15)<br />
178 {<br />
179 printf("[W] Es fehlen in einem kleinem<br />
Intervall zu viele Eintraege!\n");<br />
180<br />
181 // Innerhalb eines kleines Intervalls fehlen<br />
zu viele Daten!<br />
182 for (int j = db_last_missing; j < i + 1; j++)<br />
183 {<br />
184 // Alle Koordinaten zuruecksetzen<br />
185 db_pos_x[j] = db_pos_x[i + 1];<br />
186 db_pos_y[j] = db_pos_y[i + 1];<br />
187 }<br />
188 }<br />
189 }<br />
190<br />
191 db_last_missing = i + 1;<br />
192 }<br />
193<br />
194 int currentBestIndex = -1; // Eintrag mit hoechster<br />
Wahrscheinlichkeit<br />
195 double currentBestProbability = 1000000.0; // Wert der<br />
hoechsten Wahrscheinlichkeit<br />
196<br />
197 for (int j = i + 1; j < db_count_entries - 1; j++)<br />
198 {<br />
199 // Berechne Wahrscheinlichkeit fuer den Weg von i<br />
nach j<br />
200 double newProb = db_p_getProbability(<br />
db_calculateDistance(i, j) / (db_time[j] -<br />
db_time[i])) + ((j - i) * 0.45);<br />
201 if (newProb < currentBestProbability)<br />
202 {<br />
203 //printf(" [Min] Neues Minimum bei %d mit %f<br />
fuer [%d].\n", j, newProb, i + 1);<br />
204<br />
205 // Maximum der Wahrscheinlichkeitsfunktion<br />
speichern<br />
206 currentBestProbability = newProb;<br />
207 currentBestIndex = j;<br />
208 }<br />
209 }<br />
210<br />
211 if (currentBestIndex != -1)<br />
212 {<br />
213 // Es wurde eine Loesung gefunden<br />
214<br />
215 if (currentBestIndex > i + 1)<br />
216 {<br />
217 double currentBestSpeedX = (db_pos_x[<br />
currentBestIndex] - db_pos_x[i]) / (db_time[<br />
currentBestIndex] - db_time[i]);<br />
218 double currentBestSpeedY = (db_pos_y[<br />
currentBestIndex] - db_pos_y[i]) / (db_time[<br />
currentBestIndex] - db_time[i]);<br />
219<br />
220 // Aendere Koordinaten des Punktes<br />
221 db_pos_x[i + 1] = db_pos_x[i] + currentBestSpeedX<br />
* (db_time[i + 1] - db_time[i]);<br />
222 db_pos_y[i + 1] = db_pos_y[i] + currentBestSpeedY<br />
* (db_time[i + 1] - db_time[i]);<br />
223<br />
224 printf("[W] Aendere Koordinaten des Punktes %d<br />
auf (%d | %d).\n", i + 1, db_pos_x[i + 1],<br />
db_pos_y[i + 1]);<br />
225 printf(" Geschwindigkeit: %f Pixel / Sekunde.\<br />
n", sqrt(currentBestSpeedX *<br />
currentBestSpeedX + currentBestSpeedY *<br />
currentBestSpeedY));<br />
226<br />
20
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
227 }<br />
228 else if (currentBestIndex == i + 1)<br />
229 {<br />
230 //printf("[I] Koordinaten von %d nicht geaendert<br />
.\n", i + 1);<br />
231 }<br />
232<br />
233 //printf(" p = %f\n", currentBestProbability);<br />
234 }<br />
235 else<br />
236 {<br />
237 printf("[W] Kein Eintrag in der<br />
Wahrscheinlichkeitstabelle fuer %d gefunden.\n",<br />
db_time[i + 1]);<br />
238<br />
239 // Keine Loesung gefunden<br />
240 // TO-DO: Kann dieser Fall ueberhaupt eintreten?<br />
241 }<br />
242 }<br />
243 }<br />
244<br />
245 // Zeichne Weg<br />
246 printf("Zeichne den Weg auf die Karte. Dieser Vorgang kann einige<br />
Sekunden dauern.\n");<br />
247<br />
248 // Gehe alle Eintraege in der Log-Datenbank durch<br />
249 for (int i = 0; i < db_count_entries - 1; i++)<br />
250 {<br />
251 if (db_time[i + 1] - db_time[i] > 30)<br />
252 {<br />
253 paintLine(db_pos_x[i], db_pos_x[i + 1], db_pos_y[i],<br />
db_pos_y[i + 1], 255, 0, 0);<br />
254 }<br />
255 else<br />
256 {<br />
257 int color_blue = 0;<br />
258 double currentVelocity = ((double)db_calculateDistance(i,<br />
i + 1)) / ((double)(db_time[i + 1] - db_time[i]));<br />
259<br />
260 color_blue = (int)((currentVelocity -<br />
db_average_distance_per_time) /<br />
db_average_distance_per_time * 255.0);<br />
261<br />
262 if (color_blue > 254) color_blue = 254;<br />
263 if (color_blue < -254) color_blue = -254;<br />
264<br />
265 if (color_blue < 0)<br />
266 {<br />
267 paintLine(db_pos_x[i], db_pos_x[i + 1], db_pos_y[i],<br />
db_pos_y[i + 1], 0, 255, color_blue * (-1));<br />
268 }<br />
269 else<br />
270 {<br />
271 paintLine(db_pos_x[i], db_pos_x[i + 1], db_pos_y[i],<br />
db_pos_y[i + 1], color_blue, 255, 0);<br />
272 }<br />
273 }<br />
274 }<br />
275<br />
276 // Speichere Datei im PPM-Format ab<br />
277 FILE * pFileOut = fopen("karte_out.ppm", "w");<br />
278 fprintf(pFileOut, "P6\n#CREATOR: 28_1_bwinf_3\n%d %d\n255\n",<br />
proportions[0], proportions[1]);<br />
279<br />
280 for (int i = 0; i < proportions[1]; i++)<br />
281 {<br />
282 for (int j = 0; j < proportions[0]; j++)<br />
283 {<br />
284 fprintf(pFileOut, "%c%c%c", matrix[j][i][0], matrix[j][i<br />
][1], matrix[j][i][2]);<br />
285 }<br />
286 }<br />
287<br />
288 fclose(pFileOut);<br />
21
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
289 fclose(pFileMap);<br />
290 fclose(pFileLog);<br />
291<br />
292 printf("Ausgabedatei karte_out.ppm wurde erstellt. Fertig.\n");<br />
293 return 0;<br />
294 }<br />
295 else<br />
296 {<br />
297 // Eingelesene Zeile in das Array umkopieren<br />
298 // Zeile auswerten<br />
299<br />
300 int beginCurrentRecord = 0; // An welcher Stelle beginnt der<br />
aktuelle "Datensatz" in der aktuellen Zeile?<br />
301 int recordCounter = 0; // Der Index des aktuellen<br />
Datensatzes (der wieviele Datensatz in dieser Zeile?)<br />
302<br />
303 // String anpassen (Terminatoren einfuegen, um einzelne Werte<br />
auszulesen)<br />
304 currentLine[2] = 0;<br />
305 currentLine[5] = 0;<br />
306 currentLine[8] = 0;<br />
307 currentLine[18] = 0;<br />
308 currentLine[27] = 0;<br />
309<br />
310 // Drei Datensaetze einlesen<br />
311 int currentTime = atoi(currentLine) * 3600 + atoi(currentLine +<br />
3) * 60 + atoi(currentLine + 6);<br />
312 double currentLat = atof(currentLine + 9);<br />
313 double currentLong = atof(currentLine + 19);<br />
314 db_pos_y[db_count_entries] = convertCoordinateToPixelNorth(<br />
currentLat);<br />
315 db_pos_x[db_count_entries] = convertCoordinateToPixelEast(<br />
currentLong);<br />
316 db_time[db_count_entries] = currentTime;<br />
317<br />
318 printf("Datensatz eingelesen: time=%d, latitude=%f, longitude=%f<br />
.\n", currentTime, currentLat, currentLong);<br />
319 db_count_entries++;<br />
320 }<br />
321 }<br />
322 }<br />
323<br />
324 /// *************************************************<br />
325 /// * Liest die naechste Zeile ohne Kommentare ein. *<br />
326 /// *************************************************<br />
327 void readLine(FILE * file_handle)<br />
328 {<br />
329 memset(currentLine, 10000, 0);<br />
330<br />
331 if (feof(file_handle))<br />
332 {<br />
333 currentLine[0] = 0;<br />
334 return;<br />
335 }<br />
336<br />
337 // Erstes Zeichen einlesen<br />
338 fread(currentLine, 1, 1, file_handle);<br />
339<br />
340 if (feof(file_handle))<br />
341 {<br />
342 currentLine[0] = 0;<br />
343 return;<br />
344 }<br />
345<br />
346 if (strcmp(currentLine, "#") == 0)<br />
347 {<br />
348 // Bei dieser Zeile handelt es sich um einen Kommentar<br />
349 char tmp = 0;<br />
350<br />
351 while (currentLine[0] != 10)<br />
352 {<br />
353 // Zeichen fuer Zeichen bis zum Zeilensprung einlesen<br />
354 fread(currentLine, 1, 1, file_handle);<br />
355 }<br />
22
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
356<br />
357 // Naechste Zeile lesen, da es die aktuelle Zeile ein Kommentar ist<br />
358 readLine(file_handle);<br />
359 }<br />
360 else<br />
361 {<br />
362 int counter = 0;<br />
363 // Erstes Zeichen der Zeile einlesen<br />
364 // fread(currentLine + 1, 1, 1, file_handle);<br />
365<br />
366 while (currentLine[counter] != 10)<br />
367 {<br />
368 // Zeichen fuer Zeichen bis zum Zeilensprung einlesen<br />
369 counter++;<br />
370 fread(currentLine + counter, 1, 1, file_handle);<br />
371 }<br />
372<br />
373 currentLine[counter] = 0;<br />
374 }<br />
375 }<br />
376<br />
377 /// *************************************************<br />
378 /// * Zeichnet einen dicken Punkt auf die Map. *<br />
379 /// *************************************************<br />
380 void paintDot(int x, int y)<br />
381 {<br />
382 // Ueberpruefe, ob der Punkt ueberhalb auf die Karte gezeichnet werden<br />
kann<br />
383<br />
384 if ((x > -1) && (x < proportions[0]) && (y > -1) && (y < proportions[1]))<br />
385 {<br />
386 setPixel(x, y, 0, 0, 0);<br />
387<br />
388 // Zeichne einen dicken Punkt<br />
389 setPixel(x - 1, y, 0, 0, 0);<br />
390 setPixel(x + 1, y, 0, 0, 0);<br />
391 setPixel(x, y - 1, 0, 0, 0);<br />
392 setPixel(x, y + 1, 0, 0, 0);<br />
393 }<br />
394 else<br />
395 {<br />
396 printf("[W] Punkt kann nicht gezeichnet werden, da ausserhalb der<br />
Landkarte! Pixel(%d | %d).\n", x, y);<br />
397 }<br />
398 }<br />
399<br />
400 /// *************************************************<br />
401 /// * Zeichnet eine Linie auf die Map. *<br />
402 /// *************************************************<br />
403 void paintLine(int x1, int x2, int y1, int y2, int r, int g, int b)<br />
404 {<br />
405 // Startpunkte der Linien zeichen<br />
406 paintDot(x1, y1);<br />
407 paintDot(x2, y2);<br />
408<br />
409 // Ueberprufe, ob die Stecke ueberhaupt gezeichnet werden kann<br />
410 if ((x1 > -1) && (x2 > -1) && (y1 > -1) && (y2 > -1) && (x1 < proportions<br />
[0]) && (x2 < proportions[0]) && (y1 < proportions[1]) && (y2 <<br />
proportions[1]))<br />
411 {<br />
412 // Alles in Ordnung!<br />
413 }<br />
414 else<br />
415 {<br />
416 printf("[W] Linie kann nicht gezeichnet werden, da ausserhalb der<br />
Landkarte! Zeichne nur den sichtbaren Bereich ein!\n");<br />
417 }<br />
418<br />
419 // Zeichen Linie<br />
420 double vector_direction[2]; // Der Richtungsvektor der Gerade<br />
421 vector_direction[0] = x2 - x1;<br />
422 vector_direction[1] = y2 - y1;<br />
423 // Richtungsvektor zum Einheitsvektor (Laenge 1) machen<br />
424 double vector_length = sqrt(vector_direction[0] * vector_direction[0] +<br />
23
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
vector_direction[1] * vector_direction[1]);<br />
425 vector_direction[0] /= vector_length;<br />
426 vector_direction[1] /= vector_length;<br />
427<br />
428 // Zeichne die Strecke<br />
429 for (int i = 0; i < (int)ceil(vector_length) + 1; i++)<br />
430 {<br />
431 setPixel(x1 + (int)(vector_direction[0] * (double)i), y1 + (int)(<br />
vector_direction[1]* (double)i), r, g, b);<br />
432 }<br />
433 }<br />
434<br />
435 /// *************************************************<br />
436 /// * Setzt einen Pixel auf eine Farbe. *<br />
437 /// *************************************************<br />
438 void setPixel(int x, int y, int r, int g, int b)<br />
439 {<br />
440 // Ueberpruefe, ob der Pixel gueltig ist, d.h. in der Matrix liegt<br />
441<br />
442 if ((x > -1) && (x < proportions[0]) && (y > -1) && (y < proportions[1]))<br />
443 {<br />
444 matrix[x][y][0] = r;<br />
445 matrix[x][y][1] = g;<br />
446 matrix[x][y][2] = b;<br />
447 }<br />
448 }<br />
449<br />
450 /// *************************************************<br />
451 /// * Gibt zu einer x-Koordinate den Pixelwert aus. *<br />
452 /// *************************************************<br />
453 int convertCoordinateToPixelEast(double value)<br />
454 {<br />
455 double coord_difference = value - coord_left;<br />
456 double pixel_per_coordinate_quotient = (double)proportions[0] / (<br />
coord_right - coord_left);<br />
457<br />
458 return (int)(coord_difference * pixel_per_coordinate_quotient);<br />
459 }<br />
460<br />
461 /// *************************************************<br />
462 /// * Gibt zu einer y-Koordinate den Pixelwert aus. *<br />
463 /// *************************************************<br />
464 int convertCoordinateToPixelNorth(double value)<br />
465 {<br />
466 double coord_difference = value - coord_bottom;<br />
467 double pixel_per_coordinate_quotient = (double)proportions[1] / (<br />
coord_top - coord_bottom);<br />
468<br />
469 return proportions[1] - (int)(coord_difference *<br />
pixel_per_coordinate_quotient);<br />
470 }<br />
471<br />
472 /// *************************************************<br />
473 /// * Macht einen Eintrag in der Datenbank frei. *<br />
474 /// *************************************************<br />
475 // edit: nicht benoetigt!<br />
476 void insertDatabaseEntry(int index)<br />
477 {<br />
478 for (int i = db_count_entries; i > index; i--)<br />
479 {<br />
480 db_time[i] = db_time[i - 1];<br />
481 db_pos_x[i] = db_pos_x[i - 1];<br />
482 db_pos_y[i] = db_pos_y[i - 1];<br />
483 }<br />
484<br />
485 db_count_entries++;<br />
486 }<br />
487<br />
488 /// *************************************************<br />
489 /// * Berechnet die durschnittliche Geschwindigkeit.*<br />
490 /// *************************************************<br />
491 void db_calculateAverageDistance()<br />
492 {<br />
493 db_average_distance_per_time = 0.0;<br />
24
<strong>Aufgabe</strong> 3<br />
Stefan Hansch, Markus Wirsing, <strong>Matthias</strong> <strong>Springer</strong><br />
494<br />
495 double timeElapsed = 0.0; // Vergangene Zeit<br />
496<br />
497 for (int i = 1; i < db_count_entries; i++)<br />
498 {<br />
499 timeElapsed += db_time[i] - db_time[i - 1];<br />
500 db_average_distance_per_time += db_calculateDistance(i - 1, i);<br />
501 }<br />
502<br />
503 db_average_distance_per_time /= timeElapsed;<br />
504<br />
505 // Berechne durchschnittlichen Unterschied zwischen Geschwindigkeiten (<br />
siehe Doku)<br />
506 db_average_velocity_difference = 0.0;<br />
507 double sortedVelocities[2000];<br />
508<br />
509 for (int i = 0; i < db_count_entries - 1; i++)<br />
510 {<br />
511 sortedVelocities[i] = db_calculateDistance(i, i + 1) / (db_time[i +<br />
1] - db_time[i]);<br />
512 }<br />
513<br />
514 // Geschwindigkeiten sortieren, edit: wird fuer den neuen Algorithmus<br />
nicht mehr benoetigt!<br />
515 vector v_sortedVelocites(sortedVelocities , sortedVelocities +<br />
db_count_entries - 1);<br />
516 sort(v_sortedVelocites.begin(), v_sortedVelocites.end());<br />
517<br />
518 for (int i = 0; i < db_count_entries - 2; i++)<br />
519 {<br />
520 // Iteriere ueber alle Intervalle in der sortieren<br />
Geschwindigkeitsauflistung<br />
521<br />
522 db_average_velocity_difference += v_sortedVelocites[i + 1] -<br />
v_sortedVelocites[i];<br />
523 }<br />
524<br />
525 db_average_velocity_difference /= db_count_entries - 2;<br />
526 }<br />
527<br />
528 /// *************************************************<br />
529 /// * Berechnet den Abstand zwischen 2 Punkten. *<br />
530 /// *************************************************<br />
531 double db_calculateDistance(int index1, int index2)<br />
532 {<br />
533 return sqrt((db_pos_y[index2] - db_pos_y[index1]) * (db_pos_y[index2] -<br />
db_pos_y[index1]) + (db_pos_x[index2] - db_pos_x[index1]) * (<br />
db_pos_x[index2] - db_pos_x[index1]));<br />
534 }<br />
535<br />
536 double db_p_getProbability(double velocity)<br />
537 {<br />
538 // Iteriere ueber alle Geschwindigkeitsintervalle<br />
539 double return_value = 0.0;<br />
540<br />
541 for (int i = 0; i < db_count_entries - 1; i++)<br />
542 {<br />
543 return_value += velocity - db_p_segment[i];<br />
544 }<br />
545<br />
546 if (return_value < 0.0)<br />
547 {<br />
548 return 0.0;<br />
549 }<br />
550<br />
551 // Normen auf einen Punkt, nur wichtig, damit der Faktor 0.45 im<br />
Algorithmus passt; hatte urspruenglich andere Plaene, eigentlich ist<br />
das also nicht unbedingt notwendig<br />
552 return return_value / (db_average_distance_per_time * (db_count_entries -<br />
1));<br />
553 }<br />
25