Den NMEA 0183 Datensatz GGA zerlegen in die einzelnen Daten

Der GGA GPS-Datensatz soll in die Bestandteile zerlegt werden. Einzelne Funktionen geben die Werte zurück.

Der GGA Datensatz sieht so aus:
$GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
Er enthält mindestens die Uhrzeit, die Breite und Länge, siehe nmea.de .
Mehr Inhalt ist möglich, aber häufig nicht enthalten.

Zuerst werden die Daten eingelesen. Ich verwende hier eine GPS Antenne und einen selbstgebauten NMEA0183 > UART Wandler mit dem Arduino Uno zusammen.

Der NMEA0183 Eingangsdaten am RX Pin werden abgefragt

void loop() {
  if (Serial.available()) { // sind Daten im Eingangspuffer
    inByte = Serial.read(); // dann lesen
    if ((start==0) && ((inByte == '$')||(inByte == '!'))) {
      start=1; nmea=""; // bei $ oder ! starten
    }
    if(start==1) {nmea.concat((char)inByte);} // das Zeichen anhängen
    if((inByte==13) && (start==1)) { // CR > Datensatzende > NMEA ausgeben
      start=0;
//      Serial.println(nmea); // alles auf Konsole ausgeben
      if (nmea.substring(3,6) == "GGA") {
....

Dann werden die verschiedenen Funktionen aufgerufen. Zuerst die GPS Uhrzeit mit Stunden, Minuten und Sekunden.

int getGGA_hour(String s) {
// Aus dem GGA Datensatz die Stunden in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//        --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+1, k+3).toInt();
}

int getGGA_min(String s) {
// Aus dem GGA Datensatz die Minuten in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//          --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+3, k+5).toInt();;
}

int getGGA_sec(String s) {
// Aus dem GGA Datensatz die Sekunden in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//            --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+5, k+7).toInt();;
}

Dann die Breitenangaben mit Grad, Minuten, Minuten mit Dezimalstellen und der Nord/Süd Ausgabe.

int getGGA_LatDeg(String s) {
// Aus dem GGA Datensatz den Breiten Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                   --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+1, k+3).toInt();;
}

int getGGA_LatMinInt(String s) {
// Aus dem GGA Datensatz die Breiten Minuten auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                     --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+3, k+5).toInt();
}

float getGGA_LatMinFloat(String s) {
// Aus dem GGA Datensatz die Breiten Minuten mit Dezimalstellen auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                     -------
// von Matthias Busse 28.03.2015 Version 1.0  
  int k1 = s.indexOf( ',',1);
  k1 = s.indexOf( ',',k1+1);
  int k2 = s.indexOf( ',',k1+1);
  return s.substring(k1+3, k2).toFloat();
}

String getGGA_LatNS(String s) {
// Aus dem GGA Datensatz die Breiten N oder S auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                             -
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+1, k+2);
}

Nun die Längenenangaben mit den entsprechenden Funktionen.
Grad / Minuten / EW

int getGGA_LonDeg(String s) {
// Aus dem GGA Datensatz den Längen Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                               ---
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+1, k+4).toInt();;
}

int getGGA_LonMinInt(String s) {
// Aus dem GGA Datensatz die Längen Minuten auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                  --
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+4, k+6).toInt();
}

float getGGA_LonMinFloat(String s) {
// Aus dem GGA Datensatz die Längen Minuten mit Dezimalstellen Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                  -------
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,k2,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  k2 = s.indexOf( ',',k+1);
  return s.substring(k+4, k2).toFloat();
}

String getGGA_LonEW(String s) {
// Aus dem GGA Datensatz die Längen E oder W auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                          -
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<5;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+1, k+2);
}

Das komplette Programm mit der Konsolenausgabe sieht dann so aus.

// Hauptprogramm zum Testen der GGA NMEA Zerlegung
//
// Matthias Busse 29.3.2015 Version 1.0

int inByte, start=0;
String nmea = "NMEA Datensaetze zerlegen in die Bestandteile  Version 1.0  Matthias Busse"; // reservieren

void setup() {
  Serial.begin(4800); // 4800 Baud Dateneingang und Konsole
  Serial.println(nmea);
}

void loop() {
  if (Serial.available()) { // sind Daten im Eingangspuffer
    inByte = Serial.read(); // dann lesen
    if ((start==0) && ((inByte == '$')||(inByte == '!'))) {
      start=1; nmea=""; // bei $ oder ! starten
    }
    if(start==1) {nmea.concat((char)inByte);} // das Zeichen anhängen
    if((inByte==13) && (start==1)) { // CR > Datensatzende > NMEA ausgeben
      start=0;
//      Serial.println(nmea); // alles auf Konsole ausgeben
      if (nmea.substring(3,6) == "GGA") {
        Serial.println(nmea); // auf Konsole ausgeben
        Serial.print(" GGA ");
        Serial.print("UTC: H ");
        Serial.print(getGGA_hour(nmea));
        Serial.print(" M ");
        Serial.print(getGGA_min(nmea));
        Serial.print(" S ");
        Serial.println(getGGA_sec(nmea));

        Serial.print(" Deg ");
        Serial.print(getGGA_LatDeg(nmea));
        Serial.print(" Min ");
        Serial.print(getGGA_LatMinInt(nmea));
        Serial.print(" Min.xxxx ");
        Serial.print(getGGA_LatMinFloat(nmea),4);
        Serial.print(" NS ");
        Serial.println(getGGA_LatNS(nmea));

        Serial.print(" Deg ");
        Serial.print(getGGA_LonDeg(nmea));
        Serial.print(" Min ");
        Serial.print(getGGA_LonMinInt(nmea));
        Serial.print(" Min.xxxx ");
        Serial.print(getGGA_LonMinFloat(nmea),4);
        Serial.print(" EW ");
        Serial.println(getGGA_LonEW(nmea));

        Serial.println();
      }
    }
  }
}

int getGGA_hour(String s) {
// Aus dem GGA Datensatz die Stunden in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//        --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+1, k+3).toInt();
}

int getGGA_min(String s) {
// Aus dem GGA Datensatz die Minuten in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//          --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+3, k+5).toInt();;
}

int getGGA_sec(String s) {
// Aus dem GGA Datensatz die Sekunden in UTC auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//            --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  return s.substring(k+5, k+7).toInt();;
}

int getGGA_LatDeg(String s) {
// Aus dem GGA Datensatz den Breiten Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                   --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+1, k+3).toInt();;
}

int getGGA_LatMinInt(String s) {
// Aus dem GGA Datensatz die Breiten Minuten auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                     --
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+3, k+5).toInt();
}

float getGGA_LatMinFloat(String s) {
// Aus dem GGA Datensatz die Breiten Minuten mit Dezimalstellen auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                     -------
// von Matthias Busse 28.03.2015 Version 1.0  
  int k1 = s.indexOf( ',',1);
  k1 = s.indexOf( ',',k1+1);
  int k2 = s.indexOf( ',',k1+1);
  return s.substring(k1+3, k2).toFloat();
}

String getGGA_LatNS(String s) {
// Aus dem GGA Datensatz die Breiten N oder S auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                             -
// von Matthias Busse 28.03.2015 Version 1.0  
  int k = s.indexOf( ',',1);
  k = s.indexOf( ',',k+1);
  k = s.indexOf( ',',k+1);
  return s.substring(k+1, k+2);
}

int getGGA_LonDeg(String s) {
// Aus dem GGA Datensatz den Längen Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                               ---
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+1, k+4).toInt();;
}

int getGGA_LonMinInt(String s) {
// Aus dem GGA Datensatz die Längen Minuten auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                  --
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+4, k+6).toInt();
}

float getGGA_LonMinFloat(String s) {
// Aus dem GGA Datensatz die Längen Minuten mit Dezimalstellen Grad auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                  -------
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,k2,l;
  for(l=0;l<4;l++) { 
    k = s.indexOf( ',',k+1);
  }
  k2 = s.indexOf( ',',k+1);
  return s.substring(k+4, k2).toFloat();
}

String getGGA_LonEW(String s) {
// Aus dem GGA Datensatz die Längen E oder W auslesen  
// $GPGGA,172643.004,5415.6232,N,01004.9739,E,0,00,,,M,,M,,*43
//                                          -
// von Matthias Busse 28.03.2015 Version 1.0  
int k=1,l;
  for(l=0;l<5;l++) { 
    k = s.indexOf( ',',k+1);
  }
  return s.substring(k+1, k+2);
}

Hardware: Arduino Uno Rev 3, GPS Antenne mit NMEA0183 Ausgang, Max232 Wandler
Software: Arduino 1.5.8

von Matthias Busse

5 Gedanken zu „Den NMEA 0183 Datensatz GGA zerlegen in die einzelnen Daten

  1. Pingback: Die NMEA GSV Datensätze vom GPS zerlegen | Shelvin – Elektronik ausprobiert und erläutert

  2. Sigi

    Hallo Matthias,

    deine Beiträge gefallen mir sehr!
    Ich hab nach einer Lösung gesucht um die Daten aus meinen alten Navman TF6600 ins NMEA2000 Netz einzuspeisen (ohne externen Konverter) . Mit Hilfe deiner Programmbeispiele habe ich nun alle relevanten Datensätze zerlegt, NMEA2000 Datensätze zusammengebaut und mit einen MCP2515/MCP2551 ausgegeben.
    So passt der alte Plotter in meinen neuen Bus, ohne NMEA0183 über das HDS7 als Gateway zu verwenden

    Antworten
  3. Sigi

    Hallo Matthias,

    nein, den Code habe ich nicht veröffentlicht, da das ganze NMEA2000 ja nicht frei ist (:

    Ich beschäftige mich erst seit einem Jahr mit der Programmierung, habe einen CAN Logger gebaut und meine Geräte analysiert. So habe ich das ganze Netzwerkmanagement herausgefunden und ausprogrammiert.
    Momentan, seit letzter Saison im Betrieb, habe ich 2x Motorinterface die die Daten meiner beiden Motoren erfassen (ähnlich dem EMU, aber mit mehr Möglichkeiten) inkl. Messwertkalibrierung über USB mit einer Windowssoftware.
    Frischwassertankanzeige mit 1,8″ Display mit Durchflussgeber und NMEA Ausgabe, Ruderlagesensor (Magnetdrehfeldsensor) mit Motorraumtemperatur, Konsolensteuerung für Konsolenbeleuchtung inkl aller NMEA2000 Geräte (Displayhintergrundbeleuchtung) per Tasten oder von den MFS´s aus. Barometer, Aussentemperatur, Innentemperatur, Kühlschranktemperatur….
    Letzte Woche habe ich noch einen Trimmklappenindikator gebaut, wird aber erst im Mai eingebaut.

    Hatte auch einige MFD´s verbaut, zuerst ein Garmin GPSMAP 421, dann ein GPSMAP 527.
    Leider kommen die Garmin Geräte mit der Menge an Datensätzen nicht zurecht und ich hatte bei den Motoren immer einen Gerätemix zwischen Motorinterface und den dazugehörigen Garmin GFS10 Durchflusssensoren, da beide jeweils die gleiche Instanz verwenden müssen (BB -> 0, SB -> 1) und PGN127489 ausgeben.
    Mit den Navico Geräten, in meinem Fall den HDS7 Gen2 Touch, lässt sich jede Datenquelle gezielt zuweisen. Raymarine funktioniert auch sehr gut, habe ein Leihgerät auf meinem Testbus hängen.

    Sigi

    Antworten
  4. Gerhard Kalinka

    Schön, schön, gefällt mir.
    Da du weisst, dass der NMEA-Satz mit einem CR endet, könnest du auch Serial.ReadString() verwenden.
    Ich baue mir gerade einen Autopiloten, der den APB-Datensatz, wie er von der OpenCPN-Software geliefert wird, auswertet.
    Viel Erfolg noch!
    Gerd

    Antworten

Schreibe einen Kommentar zu Gerhard Kalinka Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.