13.11.2014 Aufrufe

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

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!