Wo bin ich?

Für die meisten mobilen Geräte ist das Ermitteln der geographischen Position eine grundlegende Funktion. GNSS-Dienste wie GPS, Galileo, GLONASS, Beidou erlauben es, den aktuellen Ort mittels Satelliten zu bestimmen. Hierbei steht GNSS für Global Navigation Satellite System. Entsprechende Empfänger befinden sich inzwischen standardmäßig in Fahrzeugen, Smartphones, Kameras, Tablets, Drohnen oder Industriesteuerungen. Wie aber lässt sich eigene Hardware um diese Funktionalität erweitern?

In Pocket speichern vorlesen Druckansicht 3 Kommentare lesen
Lesezeit: 17 Min.
Von
  • Dr. Michael Stal
Inhaltsverzeichnis

Für die meisten mobilen Geräte ist das Ermitteln der geographischen Position eine grundlegende Funktion. GNSS-Dienste wie GPS, Galileo, GLONASS, Beidou erlauben es, den aktuellen Ort mittels Satelliten zu bestimmen. Hierbei steht GNSS für Global Navigation Satellite System. Entsprechende Empfänger befinden sich inzwischen standardmäßig in Fahrzeugen, Smartphones, Kameras, Tablets, Drohnen oder Industriesteuerungen. Wie aber lässt sich eigene Hardware um diese Funktionalität erweitern?

GPS (Global Positioning System, ursprünglich NAVSTAR GPS, gibt es inzwischen schon seit den Siebziger Jahren. Insgesamt 24 Satelliten mit mittlerem Orbit von 20.200 km über der Erdoberfläche umkreisen unseren Planeten zweimal täglich. Je mindestens vier Satelliten bewegen sich dabei auf einer von sechs Bahnebenen, die 55 Grad gegen die Äquatorlinie geneigt sind. Dadurch sollen GPS-Empfänger zu jedem Zeitpunkt wenigstens vier Satelliten empfangen können. Nur mit Hilfe von vier Satelliten ist eine exakte Messung mit einer Fehlerabweichung von unter 10 Metern möglich.

Alle Satelliten senden diverse Information auf verschiedenen Frequenzen. Sie enthalten Atom- beziehungsweise Cäsium-Uhren, um eine genaue Zeit zusätzlich zur eigenen Position verschicken zu können. Diese Zeit berücksichtigt keine Schaltsekunden und weicht daher inzwischen rund 18 Sekunden von der koordinierten Weltzeit UTC ab, weshalb GPS-Empfänger die eigene Zeit entsprechend berechnen müssen. Aus den erhaltenen Daten kann der GPS-Empfänger unter anderem seine eigene Position, Bewegungsrichtung, Höhe und Geschwindigkeit ermitteln. Wissenswert am Rande ist die Tatsache, dass die GPS-Komponente wegen der hohen Geschwindigkeit von Satelliten relativ zum GPS-Empfänger relativistische Effekte berücksichtigen muss, weil sonst die Abweichung erheblich wäre.

Die Abbildung zeigt Bewegung der GPS-Satelliten um die Erde. Schwarze Punkte haben Kontakt zum blauen Bezugspunkt auf der Erdoberfläche.

(Bild: wikipedia.org)

Bei der Suche nach dem Stichwort „GPS module“ im Internet ergeben sich zahlreiche Treffer. Für Elektronikprojekte mit Arduino oder Raspberry Pi bieten die Neo 6M (oder 7M) - Bausteine des Schweizer Herstellers u-blox ein gutes Preis-/Leistungsverhältnis. Fertige Boards mit integriertem Neo6M-GPS-Baustein und brauchbarter passiver Keramik-Antenne schlagen mit Preisen ab 10-15€ zu Buche, wobei sich in den unteren Preisregionen überwiegend Nachbauten aus chinesischer Produktion tummeln. Zwar führen die billigen Klone nur serielle RX/TX-Pins nach außen, während die teureren Modelle zum Beispiel auch über einen I2C-Anschluss verfügen. Das tut dem Spaß aber keinen Abbruch.

Kommerzielle GPS-Module unterstützen den Standard NMEA 183 der National Marine Electronics Association, und senden typischerweise mit 8 Bit Datenbreite (inklusive einem Stop-Bit) ASCII-basierte Nachrichten über eine 4800 Baud-Verbindung.

Die Nachrichten sind wie folgt strukturiert, wobei jede Zeile eine separate Nachricht enthält:

$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43 $GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45

So beinhaltet das $GPGGA in der ersten Nachrichtenzeile Fix-Information, ob genügend Satelliten zum Positionieren zur Verfügung stehen.

$GPRMC stehen für eine Positionsbestimmung mit dem empfohlenen Minimum an Information (required minimum).

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

wobei hier

  • RMC bedeutet Recommended Minimum Sentence C - die minimale Menge an Information, die ein GPS-Modul liefern muss
  • 123519 Verbindung zu ausreichender Zahl an Satelliten erfolgte um 12:35:19 UTC
  • A Status (A= Active, V= Void)
  • 4807.038,N geographische Breite 48° 07.038' North
  • 01131.000,E die geographische Länge 11° 31.000' East
  • 022.4 Geschwindigkeit über Boden (in Knoten)
  • 084.4 Positionierungswinkel in Grad
  • 230394 das Datum - 23. März 1994
  • 003.1,W Magnetische Streuung
  • *6A die Checksumme (beginnt immer mit *)

Zum ersten Aufwärmen lassen sich die GPS-Module von u-blox mit einer Windows-Software namens u-center auslesen. Das ist insofern interessant, als sich dadurch die Funktion des GPS-Moduls veranschaulichen und prüfen lässt:

Das kostenlose Programm u-center im Betrieb (Microsoft Windows only). Auf dem Fenster sind die empfangbaren Satelliten grün markiert

Um das Modul an einen USB-Port des PCs anzuschließen, bietet sich ein FTDI-USB-to-TTL-Adapter an:

Zum direkten Anschluss des GPS-Empfängers an einen Computer, nutzt die Schaltung einen FTDI-Adapter (USB to TTL)

In der Schaltung existieren folgende Verbindungen:

FTDI TX mit NEO RX

FTDI RX mit NEO TX

FTDI Vcc mit NEO Vcc

FTDI GND mit NEO GND

Das USB-to-TTL-Adapterboard (FTDI) muss dabei auf 3,3V Betrieb gejumpert sein. Natürlich können Sie alternativ auch preisgünstige USB-TTL-Adapter mit Chipsets der Typen CP2102 oder CH340 nutzen, sofern Sie den entsprechenden Treiber installieren.

Nach Anschluss der Schaltung über USB (FTDI-Adapter) an den Windows-PC, und der Einstellung des verwendeten GPS-Produkts sowie des richtigen USB-Ports in u-center dauert es eine Weile bis der GPS-Empfänger Verbindungen zu genügend vielen Satelliten aufbauen konnte. Mindestens vier müssen es sein, wie bereits an anderer Stelle beschrieben. Danach ist die Position fixiert (fixed), eine grüne LED am NEO blinkt, und im Anwendungsfenster erscheinen die erfassten Satelliten und die festgestellte Position, nebst umfangreicher weiterer Information. Aus Platzgründen kann ich an dieser Stelle nicht auf die komplette Funktionalität der Software eingehen.

Für die Beispielsschaltung kommt ein Arduino Nano mit 5V-Logik zum Einsatz, der über die digitalen Pins D3 und D4 als Software-basierten seriellen Port mit dem GPS-Modul verbunden ist. Allerdings darf die Verbindung der beiden Komponenten nicht direkt erfolgen, da ein NEO 6M oder 7M Modul mit 3,3V-Logik arbeitet. Deshalb ist ein Logic-Level-Konverter nötig, der zwischen den beiden Logik-Welten adaptiert. Einige Maker im Internet halten den direkten Anschluss von GPS-Modul an den Arduino zwar für unproblematisch. Das mag kurzfristig gelten, aber bei langem Dauerbetrieb ist ein Abrauchen des GPS-Moduls definitiv nicht auszuschließen.

Folgende Zutaten (Bill of Material) sind notwendig, um die Schaltung aufzubauen:

  • Breadboard
  • Arduino Nano MCU
  • Logic Level Converter (zwischen 3,3V- und 5V-Logik) mit mindestens zwei Kanälen
  • GPS Modul wie z.B. NEO 6M oder 7M GPS
  • 10 Jumper Wires (male to male)
  • 2 Jumper Wires (male to female)

In der Schaltung sind das GPS-Modul (links oben), der Logic Level Konverter (roter Baustein), der Arduino Nano (Mitte) und das FTDI-Board (rechts) zu sehen

Die Schaltung implementiert folgende Verbindungen:

  • Der FTDI-Adapter dient nur zur Versorgung des GPS-Empfängers mit Strom. Dementsprechend laufen von den Ausgängen VCC und GND des Adapters jeweils ein Kabel zu den entsprechenden Stromschienen am Breadboard.
  • Der RX-Eingang des GPS-Moduls wird über den Logic Level Konverter mit dem TX-Ausgang des Arduino Nano verbunden. Im Beispiel fungiert der digitale Pin D4 als TX-Ausgang des Arduino-Boards.
  • Der TX-Ausgang des GPS-Moduls wird über den Logic Level Converter mit dem RX-Eingang des Arduino Nano verbunden. Im Beispiel fungiert der digitale Pin D3 als RX-Eingang des Arduino Boards.

Noch ein Wort zum verwendeten Arduino-Board. Wer ein Board mit seriellen Hardwareports einsetzt wie zum Beispiel einen Arduino Mega, sollte einen seriellen Hardware-Port (Serial1, Serial2, Serial3, ...) für die Verbindung verwenden. Das ist die schnellste und beste Lösung.

Bei Boards wie Arduino Uno, Leonardo, Nano muss stattdessen eine Software-basierte Lösung zum Einsatz kommen, etwa die Standard-Lösung SoftwareSerial. Leider führt SoftwareSerial betriebsbedingt zu hohen Latenzzeiten und kann deshalb nur geringere Baudraten unterstützen. Das können Alternativen wie AltSoftSerial deutlich besser. Für die Geschwindigkeiten der GPS-Module von 4800 Baud oder 9600 Baud erweist sich aber SoftwareSerial als mehr mehr als ausreichend.

Das GPS-Modul zeigt eine grüne, blinkende LED sobald ihm genügend viele Satelliten für die Positionsbestimmung zur Verfügung stehen:

Die Schaltung im Betrieb. Die grün blinkende LED zeigt an, dass der NEO-6m mit mindestens vier Satelliten in Kontakt steht

Es macht keinen Spaß, systemnah mit einem GPS-Modul beziehungsweise mit den Rohdaten im NMEA 183 Standardformat zu arbeiten. Daher existieren diverse Bibliotheken für Arduino, um diese Aufgabe zu erleichtern, darunter die Bibliotheken NEOGPS, GPS-NEO-6m-master und TinyGPSPlus. Eine Suche mit dem Begriff GPS im Menü der Arduino IDE unter Sketch > Include Library > Manage Libraries liefert die entsprechenden Bibliotheken.

Für den Artikel nutze ich TinyGPSPlus. Nach Installation von TinyGPSPlus lässt sich das Beispiel FullExample.ino über den Menüpfad File > Examples > TinyGPSPlus<Versionsnummer> > FullExample öffnen.

Der Anfang des Sketches muss ein Programm den entsprechenden Header inkludieren:

#include <SoftwareSerial.h>
#include <TinyGPS++.h>

Der Einfachheit halber benutzt der Sketch in unserem Fall SoftwareSerial. Ohnehin steht beim Arduino Nano kein eigener serieller Hardwareport für diesen Zweck zur Verfügung.

Die Pins des Nano-Boards für den Software-Port lauten im obigen Beispiel D3 (als RX) und D4 (als TX):

static const int RXPin = 3, TXPin = 4;
static const uint32_t GPSBaud = 9600;

// Das Objekt zum Zugriff auf die Bibliothek:

TinyGPSPlus gps;

// Software-basierter Serieller Port
SoftwareSerial ss(RXPin, TXPin);

Die Baudrate beträgt in dem von mir verwendeten NEO-6m-Modul 9600 Baud. Üblicherweise kommen auch 4800 Baud recht häufig vor. Dem Datenblatt Ihres GPS-Moduls sollten Sie daher den für Ihr Modul jeweils richtigen Wert entnehmen.

Im setup() des Sketches erfolgt die Verbindung mit dem integrierten seriellen Monitor (der für die Verbindung von PC/Mac mit dem Arduino-Board) über Serial.begin(), und die mit der emulierten seriellen Verbindung vom Arduino-Board zum GPS-Modul über ss.begin();

void setup()
{
Serial.begin(115200); // Serieller Monitor für print-Ausgaben
ss.begin(GPSBaud); // Serieller Software-Port zum GPS-Modul
// ...
}

Im eigentlichen Hauptprogramm loop() ist der Zugriff auf die vom GPS-Modul gelieferte Information möglich:

void loop()
{
while (ss.available() > 0) // Daten liegen an der Leitung an
if (gps.encode(ss.read())) // Lese Daten und enkodiere sie
{
... // Nutzung der gelesenen Daten
}

if (millis() > 10000 && gps.charsProcessed() < 10) // Timeout nach 10 secs
{
Serial.println(F("Kein GPS-Modul gefunden"));
while(true); // Fini, das Ende der Welt
}
}

Information lässt sich über die Variable gps auslesen:

gps.satellites.value() // falls gps.satellites.isValid()
gps.hdop.value() // falls gps.hdop.isValid()
gps.location.lat() // falls gps.location.isValid()
gps.location.lng() // falls gps.location.isValid()
gps.location.age() // falls gps.location.isValid()
gps.date // falls gps.date.isValid()
gps.time // falls gps.time.IsValid()
gps.altitude.meters() // falls gps.altitude.isValid()
gps.course.deg() // falls gps.course.isValid()
gps.speed.kmph() // falls gps.speed.isValid()

Das funktioniert immer nach dem selben Schema: Zuerst ist zu überprüfen, ob überhaupt ein gültiger Datenwert vorliegt wie zum Beispiel bei gps.location.isValid(). Ist dies der Fall, enthalten Variablen wie gps.location.lat() (= geographische Breite) gültige Werte.

Das Bild zeigt die Arduino IDE und den seriellen Monitor. Auf den Arduino läuft ein Beispielsprogramm, das die TinyGPSPlus-Bibliothek mitliefert

Die unten abgedruckte Quelldatei beinhaltet ein sehr einfaches Beispiel, bei dem das Arduino-Board den seriellen Monitor zur Ausgabe nutzt und einen Software-emulierten Port zur Kommunikation mit dem GPS-Modul. In der Regel dauert es nach Inbetriebnahme des GPS-Moduls etwas bis es genügend Satelliten zur Positionsbestimmung lokalisieren konnte. Der Einfachheit halber geht die Demo-Anwendung davon aus, dass das Modul betriebsbereit ist und gültige Daten liefert (grüne LED blinkt).

In der Hauptschleife loop bestimmt das Programm in jeder Sekunde die Position, und gibt die entsprechende Information zeilenweise am seriellen Monitor aus.

Die Daten werden im CSV-Format (Comma Separated Values) ausgegeben. Dieses Format hat Vorteile, wenn Benutzerinnen später die Daten weiterverarbeiten wollen. Dazu unten mehr.

/*******************************************************
*
* Demo-Anwendung zur Nutzung eines GPS-Moduls des
* Typs u-blox NEO 6m oder 7m
* (c) Michael Stal, 2018
* frei verwendbar gemaess Creative Commons
*
*******************************************************/


#include <TinyGPS++.h>

#include <SoftwareSerial.h>

// Verwendete digitale Pins für SoftwareSerial
static const int RX = 3, TX = 4;
// !!! Baudrate des GPS-Moduls: Anpassung an eigene HW notwendig
static const uint32_t GPSBaud = 9600;

// Zugriff auf TinyGPS++ ueber gps
TinyGPSPlus gps;

// Emulierter Port zum GPS Geraet
SoftwareSerial gpsPort(RX, TX);


/*******************************************************
*
* setup baut alle Verbindungen auf
* zum seriellen Monitor und zum GPS-Modul
*
*******************************************************/

void setup()
{
// Verbindung zum seriellen Monitor
Serial.begin(115200);
// Verbindung mit GPS-Modul
gpsPort.begin(GPSBaud);

// Header Zeile ausgeben (Format ist .csv)
Serial.println();
Serial.println("utc_t, lat, lon, elevation");
Serial.println("#name, latitude, longitude, elevation");
}

/*******************************************************
*
* Im Hauptprogramm wird jede Sekunde die aktuelle
* Position vom GPS-Geraet gelesen inklusive
* Datum und Zeit, geo. Breite, geo. Laenge, Hoehe
*
*
*******************************************************/

void loop()
{
while (gpsPort.available() > 0) // Daten vorhanden?
if (gps.encode(gpsPort.read())) {
showPositionData(); // ja, dann Ausgabe am ser. Monitor
delay(1000); // 1 Sekunde warten
}

if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println("Fehler: GPS Modul nicht gefunden");
while(true);
}
}

/*******************************************************
*
* showPositionData liest die aktuelle Position
* und gibt die Daten als Zeile (im .csv-Format) aus
*
*******************************************************/

void showPositionData()
{
// Erst Datum ...
if (gps.date.isValid())
{
Serial.print(gps.date.month());
Serial.print("/");
Serial.print(gps.date.day());
Serial.print("/");
Serial.print(gps.date.year());
}

Serial.print(" ");

// ... und Zeit
if (gps.time.isValid())
{
if (gps.time.hour() < 10) Serial.print("0");
Serial.print(gps.time.hour());
Serial.print(":");
if (gps.time.minute() < 10) Serial.print("0");
Serial.print(gps.time.minute());
Serial.print(":");
if (gps.time.second() < 10) Serial.print("0");
Serial.print(gps.time.second());
}


// Jetzt 2D-Position als geo. Breite, Laenge
if (gps.location.isValid())
{
Serial.print(" , ");
Serial.print(gps.location.lat(), 6);
Serial.print(" , ");
Serial.print(gps.location.lng(), 6);
}


// Und dann noch Hoehe
if (gps.altitude.isValid())
{
Serial.print(" , ");
Serial.print(gps.altitude.meters(), 6);
}

Serial.println();
}

Die Ausgabe des Sketches am seriellen Monitor schaut bei einem sehr einfachen Beispiel wie folgt aus:

utc_t, lat, lon, elevation
#name, latitude, longitude, elevation
2/25/2018 14:33:06 , 48.126541 , 11.571980 , 523.500000
2/25/2018 14:33:07 , 48.126533 , 11.571964 , 523.500000
...

Statt auf dem Monitor könnte die obige Anwendung alle Positionsdaten auch als CSV-Datei (zum Beispiel auf einer SD-Card) speichern. In einem früheren Posting hatte ich das dazu notwendige Vorgehen bereits beschrieben.

Dadurch können Maker die erzeugte Datei weiter verarbeiten, um die Tracking-Daten zum Beispiel in Google Earth oder Maps zu visualisieren.

Ein Programm von Format wie Google Earth erwartet allerdings .KML-Dateien (Keyhole Markup Language) oder .KMZ-Dateien. Woher also nehmen und nicht stehlen? Zum Glück gibt es genau zu diesem Zweck einen Online-Dienst namens GPS Visualizer.

Die obige Daten-Ausgabe des GPS-Empfängers habe ich in einer Datei arduinotrack.csv gespeichert und den Dienst GPS Visualizer dafür genutzt, um eine Google Map zu erzeugen.

Der freie Online-Dienst GPS Visualizer erlaubt es, GPS-Daten von/nach verschiedenen Formaten zu konvertieren

Die erzeugte Map ist aber zugegebenermaßen eher unspektakulär, weil sich der Testaufbau immer an derselben Stelle meines Büros befunden hat:

GPS Visualizer kann die Daten des GPS-Tracking z.B. über Google Earth oder Google Maps visualizieren

In dieser Folge ging es um Ortung der aktuellen Position über GPS. Ein Microcontroller (MCU), in unserem Fall ein Arduino Nano, kommuniziert via serieller Leitung mit der verwendeten GPS-Hardware. Die Einsatzmöglichkeiten sind vielfältig, etwa die Steuerung einer Drohne, das Tracking eines Kraftfahrzeugs oder die Positionsbestimmung eines Containers, um nur einige Beispiele zu nennen.

Wer GPS in seinem Arduino-Projekt integrieren möchte, ist mit den preisgünstigen Komponenten wie dem NEO 6M oder 7M aus der Produktfamilie von u-blox bestens bedient.

Der weiter oben beschriebene Versuchsaufbau gibt lediglich die GPS-Daten aus. Es gibt wie immer unzählige Erweiterungsmöglichkeiten:

  • Mittels eines SD-Card-Shields könnte das Programm auch eine GPS-Datei erzeugen, beispielsweise im KML- oder GPX-Format, die sich über Webdienste wie Google Maps visualisieren lässt.
  • Für mobile Geräte sollte die Stromversorgung über Akkus oder Batterien erfolgen.
  • Statt eines Arduino-Boards bietet sich natürlich auch an, ein kompatibles Board zu nutzen wie ESP32, Teensy, oder STM32F103C8T6 . Diese Boards laufen unter 3,3V, sodass der im Beitrag benutzte Logic Level Konverter entfallen kann.

Jetzt bleibt mir nur noch, Ihnen viel Spaß beim Experimentieren zu wünschen. ()