Go Down

Topic: GPS Uhrzeit auswerten (Read 1 time) previous topic - next topic

socke

Hallo,

ich programmiere schon eine Weile mit dem Arduino. Jetzt bin ich aber an einer Stelle angekommen, bei der ich nicht mehr weiter weiß. Ich habe ein GPS-Modul (NL-552ETTL) das ich über SoftwareSerial am Arduino Uno angeschlossen habe. Folgende Strings bekomme ich mit 9600 Baud gesendet:

Code: [Select]
$GPRMC,235945.180,V,0000.0000,N,00000.0000,E,0.00,0.00,120136,,,N*74
$GPVTG,0.00,T,,M,0.00,N,0.0,K,N*02
$GPGGA,235945.180,0000.0000,N,00000.0000,E,0,00,99.9,-17.0,M,17.0,M,,0000*7C
$GPGSA,A,1,,,,,,,,,,,,,99.9,99.9,99.9*09
$GPGSV,1,1,00*79
$GPGLL,0000.0000,N,00000.0000,E,235945.180,V,N*44


Für mich ist $GPGGA am wichtigsten, da die ersten Zahlen die Uhrzeit sind, hier: 23:59:45 UTC.
Wie mache ich das jetzt am besten, dass nur die $GPGGA-Zeile ausgewertet wird und die anderen ignoriert werden? Die Uhrzeit soll dann über die USB-Schnittstelle im Terminal angezeigt werden.

Gruß
Felix

jurs


Wie mache ich das jetzt am besten, dass nur die $GPGGA-Zeile ausgewertet wird und die anderen ignoriert werden? Die Uhrzeit soll dann über die USB-Schnittstelle im Terminal angezeigt werden.


1. Zeilenpuffer als C-String deklarieren, z.B. "char linebuffer[80];" dann
2. die an der seriellen Schnittstelle eintreffenden Zeichen zeilenweise einlesen, dann
3  if (strstr(linebuffer,"$GPGGA")==linebuffer)

Eins, zwei oder drei, wo liegt das Problem?

socke

Danke für die schnelle Antwort.

Quote
Eins, zwei oder drei, wo liegt das Problem?


Irgendwie bei allem  :smiley-roll-sweat:

Das ist mein Code den ich bis jetzt habe:
Code: [Select]
#include <SoftwareSerial.h>
#include <string.h>

SoftwareSerial gps(8, 9); // RX, TX

char linebuffer[80];

void setup()
{
  Serial.begin(19200);
  gps.begin(9600);
}

void loop()
{
  if (strstr(linebuffer,"$GPGGA")==linebuffer)
    Serial.write(gps.read()); 
}

jurs


Irgendwie bei allem  :smiley-roll-sweat:

Das ist mein Code den ich bis jetzt habe:


Oh, oh, ich sehe schon.

Ich habe zwar kein solches GPS-Modul, aber ich habe mal versucht, Dir einen Sketch zu machen.

Code: [Select]

#include <SoftwareSerial.h>
#include <string.h>

SoftwareSerial gps(8, 9); // RX, TX

char linebuffer[80]; // Zeilenpuffer als C-String

void setup()
{
  Serial.begin(19200);
  gps.begin(9600);
}

boolean timeFromGps()
{
  int charnum;
  char c;
  if (gps.available()) // Es ist mindestens 1 Zeichen im seriellen Eingangspuffer
  {
    delay(100);  // Zeit geben, um die Zeile komplett zu empfangen
    memset(linebuffer,0,sizeof(linebuffer)); // Zeilenpuffer löschen
    charnum=0;
    while (gps.available())
    {
      c=gps.read(); // Zeichen von Schnittstelle einlesen
      if (c>=32 && charnum<sizeof(linebuffer)-1) // Nur Zeichen mit ASCII-Code ab Leerzeichen
      {
        linebuffer[charnum] = c;
        charnum++;
      }
      else break; // Sonst liegt ein Steuerzeichen vor, z.B. "Zeilenende"
    }
    if (strstr(linebuffer,"$GPGGA")==linebuffer)  // wonach wir suchen
    {
      // Ab Zeichen 7 insgesamt 10 Zeichen an den Anfang des Zeilenpuffers kopieren
      strncpy(linebuffer,&linebuffer[7],10);
      linebuffer[10]=0; // Stringende nach 10 Zeichen setzen
      return(true); // Rückkehr mit Erfolgsmeldung
    } 
  } 
  return(false); // Leider nichts empfangen
}   


void loop()
{
  if (timeFromGps())
    Serial.println(linebuffer);
}


Die timeFromGps() Funktion soll die ganze Arbeit erledigen, Du brauchst diese Funktion in der Loop nur regelmäßig aufrufen, und wenn der Rückgabewert "true" ist, dann steht die herauskopierte Zeit im linebuffer.

Beim Herauskopieren der Zeit aus der Zeile habe ich mich jetzt mal darauf verlassen, dass die Zeit immer mit 10 Stellen (einschließlich drei Nachkommastellen) ab dem 7. Zeichen in der Zeile angeliefert wird. Falls das nicht immer der Fall ist, muß man an der Stelle natürlich nachbessern.

Schau's Dir mal an, ob Du mit den angebrachten Kommentaren klarer siehst, was da überhaupt abläuft.

P.S.: Du brauchst dringend einen C-Programmierkurs oder ein Programmierbuch zum Selbststudium.

socke

Vielen Dank für den Sketch, ich bin begeistert.

Ich habe den Code gerade mal ausprobiert, bekomme allerdings keine Zeichen gesendet. Später werde ich mir das ganze noch einmal genauer anschauen.

Quote
P.S.: Du brauchst dringend einen C-Programmierkurs oder ein Programmierbuch zum Selbststudium.

Das muss ich wohl. Ich programmiere zwar schon seit Jahren mit AVR (Atmel) Studio, aber so wie ich das sehe ist das großer Aufholbedarf  :)

Gruß
Felix

jurs


Ich habe den Code gerade mal ausprobiert, bekomme allerdings keine Zeichen gesendet.


Von meinem Sketch wird jetzt alles rausgefiltert, was keine Zeitangabe ist.

Wie oft wird die Zeit im seriellen Protokoll gesendet?
So lange müßtest Du dann natürlich warten, bis Du was im seriellen Monitor siehst.

Wenn Du doch wieder alles angezeigt bekommen möchtest, z.B. zum Debuggen, kannst Du die loop-Funktion ändern auf:

Code: [Select]

void loop()
{
  if (timeFromGps())
    Serial.println(linebuffer);
  else
    Serial.println(linebuffer);
}


Dann wird im Fall einer Zeitangabe die verarbeitete und herauskopierte Zeit in der Zeile angezeigt und ansonsten die unveränderte Zeile wie sie empfangen wurde.

Wie schnell kommen denn die Zeilen hintereinander weg? Ich bin mal ausgegangen von ca. 1 Sekunde pro Zeile. Sobald mehr als 10 Zeilen pro Sekunde gesendet werden, könnte es sein, dass mein Code nicht mehr läuft, wegen "delay(100)", d.h. es wird eine Zehntelsekunde auf den Empfang einer vollständigen Zeile gewartet, und das funktioniert nur, wenn in einer Zehntelsekunde nicht mehr als eine Zeile gesendet wird. Wenn Dein Modul ständig mit Volldampf und ohne Pause Zeile für Zeile für Zeile sendet, müßte die Einleseroutine darauf angepaßt werden und etwas anders aussehen.

socke

Quote
Wie schnell kommen denn die Zeilen hintereinander weg? Ich bin mal ausgegangen von ca. 1 Sekunde pro Zeile. Sobald mehr als 10 Zeilen pro Sekunde gesendet werden, könnte es sein, dass mein Code nicht mehr läuft, wegen "delay(100)", d.h. es wird eine Zehntelsekunde auf den Empfang einer vollständigen Zeile gewartet, und das funktioniert nur, wenn in einer Zehntelsekunde nicht mehr als eine Zeile gesendet wird. Wenn Dein Modul ständig mit Volldampf und ohne Pause Zeile für Zeile für Zeile sendet, müßte die Einleseroutine darauf angepaßt werden und etwas anders aussehen.


Ich kann bei diesem Modul die Zeit einstellen. Zur Zeit sind es 1000ms. Also jede Sekunde werden diese 6 Zeilen gesendet.

Jetzt habe ich loop-Schleife geändert und bekomme im Terminal folgendes:
Code: [Select]
000146.000
000146.000
000146.000
$GPRMC,000147.000,V,0000.0000,N,00000.0000,E,0.00,0.00,060180,,2


Gruß

jurs


Ich kann bei diesem Modul die Zeit einstellen. Zur Zeit sind es 1000ms. Also jede Sekunde werden diese 6 Zeilen gesendet.

Jetzt habe ich loop-Schleife geändert und bekomme im Terminal folgendes:


Hm, 6 Zeilen pro Sekunde. Und die werden offenbar ohne Pause nacheinander weg gesendet?

Das paßt mit meinem Einlesecode und dem darin enthaltenen "delay(100)" nicht zusammen. Der von mir gepostete Code funktioniert nur, wenn nach jeder gesendeten Zeile mindestens 100 Millisekunden lang Pause auf der seriellen Schnittstelle herrscht. So läuft offenbar der serielle Eingangspuffer über und Du bekommst nur noch verstümmelte Zeilen.

Du brauchst eine andere Einlese-Logik ohne delay, wenn Du Dein Modul nicht so umkonfigurieren kannst, dass es nach jeder Zeile für mindestens 100 ms nichts sendet, sondern z.B. nur eine Zeile pro eine Sekunde.

Muss ich nochmal überlegen. Auf jeden Fall muß es ohne delay auskommen, denn Dein Modul sendet zu viele Zeichen zu schnell hintereinander weg. Und der serielle Eingangspuffer kann überhaupt nur 63 Zeichen zwischenpuffern.

socke

#8
Mar 10, 2013, 12:54 pm Last Edit: Mar 10, 2013, 01:13 pm by socke Reason: 1
So, habe jetzt über die Software des GPS-Moduls die Baudrate auf 4800 herruntergesetzt und das Delay auf 2000ms erhöht.

Quote
Hm, 6 Zeilen pro Sekunde. Und die werden offenbar ohne Pause nacheinander weg gesendet?

Das ist die hier die Frage. Im Terminal bekomme ich ja immer neue Zeilen, also muss ein Zeilensprungbefehl gesendet werden.

Ich werde mich mal mit der Software auseinandersetzen, vielleicht ist es ja möglich nur die $GPGGA Zeile zu senden.

Gruß

Nachtrag:
Der Linebuffer muss doch 78 sein, oder?
Quote
NMEA GPGGA,  Size  78,  'Global Positioning System Fix Data'

jurs


Der Linebuffer muss doch 78 sein, oder?


Der linebuffer muss mindestens so lang sein wie die längste Zeile, dier er enthalten soll, PLUS EINS für das Zeilendende-Zeichen (Nullzeichen) bei C-Strings. D.h. um eine Zeile mit 78 Zeichen aufzunehmen muß mindestens ein "char linebuffer[79] deklariert sein.

So, anbei mal ein verbesserter Auslesesketch zum Auslesen der Zeitangabe "ohne delay", so daß num vom Prinzip her keine Annahmen über die Geschwindigkeit der Aussendung und Pausen zwischen den Zeilen gemacht werden müssen.

Wenn Du die Kommentarstriche der in der loop auskommentierten Zeile entfernst, bekommst Du die anderen Zeilen.

Code: [Select]

#include <SoftwareSerial.h>

SoftwareSerial gps(8, 9); // RX, TX

enum gpsResults {GPSNONE,GPSTIME,GPSOTHER};
char linebuffer[80]; // Zeilenpuffer als C-String
char timebuffer[11]; // Zeitpuffer als C-String

void setup()
{
  Serial.begin(19200);
  gps.begin(9600);
}

int timeFromGps()
{
  static int charnum;
  static boolean eol; // End of Line marker
  char c;
  if (eol) // end of line marker wurde beim letzten Aufruf gesetzt
  { // linebuffer und eol marker löschen
    memset(linebuffer,0,sizeof(linebuffer)); // Zeilenpuffer löschen
    charnum=0; // Zeichenzähler auf 0
    eol=false; // end of line marker auf false setzen
  }
  if (!gps.available()) return GPSNONE;
  while (gps.available())
  {
    c=gps.read(); // Zeichen von Schnittstelle einlesen
    if (c>=32) // Nur Zeichen mit ASCII-Code ab Leerzeichen
    {
      linebuffer[charnum] = c;
      if (charnum<sizeof(linebuffer)-1) charnum++;
    }
    else break; // Sonst liegt ein Steuerzeichen vor, z.B. "Zeilenende"
  }
  if (c<32) eol=true;
  if (eol && strstr(linebuffer,"$GPGGA")==linebuffer)  // Zeilenende und gefunden wonach wir suchen
  {
    // Ab Zeichen 7 insgesamt 10 Zeichen an den Anfang des Zeilenpuffers kopieren
    strncpy(timebuffer,&linebuffer[7],10);
    timebuffer[10]=0; // Stringende nach 10 Zeichen setzen
    memset(linebuffer,0,sizeof(linebuffer)); // Zeilenpuffer löschen
    charnum=0; // Zeichenzähler auf 0
    return GPSTIME; // Zeitstempel in Zeile gefunden
  } 
  else if (eol && strlen(linebuffer)>0) return GPSOTHER;
  else return GPSNONE; // Zeile ist noch nicht vollständig
}   


void loop()
{
  switch (timeFromGps())
  {
    case GPSTIME : Serial.println(timebuffer);break;
//    case GPSOTHER: Serial.println(linebuffer);break;
  } 
}


socke

Es funktioniert!
Vielen Dank. Ich bin echt überrascht wie gut man hier im Forum Hilfe bekommt, da gibt es auch anderes.

Ich werde den Code für meine Anwendung noch ein bisschen anpassen und dann stimmt es. Auch in C werde ich mich jetzt intensiever einarbeiten.

Gruß
Felix

Go Up