mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik GPS Daten Umrechnen und ausgeben (float to string)


Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich habe einen RGM-2000 GPS Empfänger...

Dieser arbeitet einwandfrei und liefert auch brav seinen NMEA 
Datenstrom...
Diesen habe ich in Arrays abgepackt und möchte diese nun weiter 
verarbeiten.

Mein NMEA Datensatz:
$GPGGA,110447.099,4729.7067,N,00844.1100,E,1,04,20.6,526.2,M,48.0,M,0.0, 
0000*4C<\r><\n>$GPVTG,,T,,M,0.00,N,0.0,K*7E<\r><\n>


Zur umrechnung verwende ich folgende formel 
http://notinthemanual.blogspot.com/2008/07/convert...

Das problem war erstmal die Zeichen im ASCII code aus dem Array in eine 
Variable zum rechnen zu bekommen habe dies dann so gelöst
ufLatitude = ucN[2] * 10 + ucN[3] * 1 + ucN[4] * 0.1 + ucN[5] * 0.01 + ucN[6] * 0.001 + ucN[7] * 0.0001;
      ucTemp = ucN[0] * 10 + ucN[1] * 1;
      ufLatitude = ufLatitude / 60;
      ufLatitude = ufLatitude + ucTemp;
ufLatitude ist eine Float... gibt es eine möglichkeit dies ohne Float zu 
lösen?

in ucN stehen die ASCII Zeichen von links nach rechts (links 
höchstwertige zahl etc..)

Bisher konnte ich nicht überprüfen ob es richtig gerechnet wurde


Nun möchte ich die Float wieder per RS232 zum PC Senden die habe ich so 
versucht:
string[0] = (ufLatitude / 100);
string[1] = (ufLatitude / 10);
string[2] = (ufLatitude / 1);
string[3] = '.';
string[4] = (ufLatitude / 0.1);
string[5] = (ufLatitude / 0.01);
string[6] = (ufLatitude / 0.001);
string[7] = (ufLatitude / 0.0001);
string[8] = (ufLatitude / 0.00001);
      
      
ucCount = 0;
while(ucCount != 9)
{
  while (!(USR & (1<<UDRE)));
  UDR = string[ucCount]  + 0x30;
  ucCount++;        
}

es kommt jedoch leider nur "müll" raus

^<15>?S?0009^????<23>04_^

Hoffe ihr könnt mir helfen...

Danke schonmal

Autor: Dean Richard (smile_t)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich denke das du dies völlig falsch angehst...

versuch es mal mit google ;)

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ucTemp = (ucN[0]-'0') * 10 + (ucN[1]-'0') * 1;

And so on;)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du versuchst momentan die Funktionen
dtostrf bzw. strtod
mehr schlecht als recht zu implementieren. Verwende einfach diese 
Standardfunktionen.

Autor: Tim Seidel (maxxie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
- string[position] = 0 ist nicht das gleiche wie string[position] = '0'
letzteres ist aber eher, dass was du vermutlich willst (ASCII Code) Das 
macht dir bereits Probleme beim einlesen. '0' = (char)48.

- du kannst nicht einfach eine Zahl durch die Wertigkeit einer Ziffer 
teilen um die Ziffer zu ermitteln. Beispiel: 1.23:  1.23 / 0.01 = 123, 
du willst aber vermutlich nur die 3 haben. (Division und Modulo, bzw 
ftoa)

Überleg dir nochmal ganz genau, was du machen möchtest, wie du es von 
Hand lösen würdest.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hätte ich fast vergessen;)

string[0] = (ufLatitude / 100)+'0';
string[1] = (ufLatitude / 10)+'0';
string[2] = (ufLatitude / 1)+'0';

Aber wozu das ganze hin und hergerechne?
Sende dem PC doch einfach deine GPS-Strings
und popel dort dann die entsprechenden Daten raus.

Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Vielen Dank für eure antworten...

Also weshalb ich es wieder zurück konvertiere liegt daran, das ich 
ansonsten keine möglichkeit habe zu überprüfen ob er es intern korrekt 
von String zu double konvertiert...

Ich habe meinen code nun so angepasst
//Infos unsigned char buffer[8] 

ufLatitude = strtod(ucN,NULL);
ufLongtitude = strtod(ucE,NULL);
      
ufLatitude = ((ufLatitude / 60) + strtod(ucNx,NULL)); 
ufLongtitude = ((ufLongtitude / 60) + strtod(ucEx,NULL));
      
ucTemp = sprintf(buffer, "%d", ufLatitude);
      
      
ucCount = 0;
while(ucCount != 9)
{
  while (!(USR & (1<<UDRE)));
  UDR = buffer[ucCount];
  ucCount++;        
}
ucNx und ucEx sind die vorderen stellen der längen und breitengrad 
angaben welche danach addiert werden müssen (siehe link im ersten post)

ufLatitude und longtitude sind nun double variablen
wenn ich %d durch %s ersetze kommt er nie mehr von dieser stelle los

Wenn ich es so mache wie hier, kommt wieder mal müll zurück

19518<\0><\0><\0>4

Ich hoffe ihr seht meinen Fehler

Für optimierungen bin ich immer offen derzeit benötigt mein code wegen 
Sprintf und double 5000bytes

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Claudio H. schrieb:

> Ich hoffe ihr seht meinen Fehler

Bring mal etwas mehr.
Aber diesmal bitte mit Datentypen

> ufLatitude und longtitude sind nun double variablen
> ucTemp = sprintf(buffer, "%d", ufLatitude);

Das kann dann aber nicht stimmen. %d bezeichnet einen int.

> Für optimierungen bin ich immer offen derzeit benötigt mein code wegen
> Sprintf und double 5000bytes

Der Löwenanteil wird wohl für sprintf draufgehen.



>   ufLatitude = ((ufLatitude / 60) ...

Das kann auch nicht stimmen.

  0348

sind 3 Grad und 48 Minuten und nicht 348/60 ( = 5.8) Grad

0348 / 100  ->  3     3 ganze Grad ...
0348 % 100  -> 48     ... und 48 Minuten

48 Minuten sind  48 / 60 = 0.8  Grad

0348  sind daher 3 Grad 48 Minuten oder 3.8 Grad

Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok danke für die antwort... hier mein gesamter code

unsigned char ucTemp = 0;
unsigned char ucCount = 0;
unsigned char ucN[6];
unsigned char ucE[6];
unsigned char ucEx[2];
unsigned char ucNx[1];
unsigned char buffer[8];
unsigned char GPS_SAT = 0;
unsigned char GPS_STAT = 0;
double ufLatitude = 0;
double ufLongtitude = 0;

void waitsigns(unsigned char ucSigns)
{
  ucCount = 0;
  while(ucCount != ucSigns)    //3 Zeichen aussetzen 
  {
    while (!(USR & (1<<RXC)));
    ucTemp = UDR;
    ucCount++;
  }
}

void RX_GPS(void)
{
  while (!(USR & (1<<RXC)));   // warten bis Zeichen verfuegbar
  if(UDR == '$')
  {  
    waitsigns(5);
    
    if(ucTemp == 'A')     //Prüfen ob GPGGA (letztes zeichen)
    {  
      
      waitsigns(12);
      
      ucCount = 0;
      while(ucCount != 2)    //2 Zeichen speichern N Breitengrad Anfang
      { 
        while (!(USR & (1<<RXC)));
        ucNx[ucCount] = UDR;
        ucCount++;
      }
      
      
      ucCount = 0;
      while(ucCount != 7)    //7 Zeichen speichern N Breitengrad
      {
        while (!(USR & (1<<RXC)));
        ucN[ucCount] = UDR;
        ucCount++;
      }
      
      
      waitsigns(3);
      
      ucCount = 0;
      while(ucCount != 3)    //3 Zeichen speichern E Längengrad Anfang
      { 
        while (!(USR & (1<<RXC)));
        ucEx[ucCount] = UDR;
        ucCount++;
      }
      
      ucCount = 0;
      while(ucCount != 7)  //7 Zeichen Speichern E Längengrad
      {
        while (!(USR & (1<<RXC)));
        ucE[ucCount] = UDR;
        ucCount++;
      }
      
      
      waitsigns(3); //Anzahl zeichen warten (auslassen)
      
      while (!(USR & (1<<RXC)));
      GPS_STAT = (UDR - 0x30);
      
      waitsigns(1);
      
      while (!(USR & (1<<RXC)));  
      ucTemp = UDR;
      GPS_SAT = (ucTemp - 0x30);
      GPS_SAT = GPS_SAT << 4;
      
      while (!(USR & (1<<RXC)));
      ucTemp = UDR;
      GPS_SAT = (GPS_SAT | (ucTemp - 0x30));
      
      
      ufLatitude = strtod(ucN,NULL);
      ufLongtitude = strtod(ucE,NULL);
      
      ufLatitude = ((ufLatitude / 60) + strtod(ucNx,NULL)); 
      ufLongtitude = ((ufLongtitude / 60) + strtod(ucEx,NULL));
      
      ucTemp = sprintf(buffer, "%d", ufLatitude);
      
      //Ausgabe der Variablen zum testen
      ucCount = 0;
      while(ucCount != 9)
      {
        while (!(USR & (1<<UDRE)));
        UDR = buffer[ucCount];
        ucCount++;        
      }
      
      
      while (!(USR & (1<<UDRE)));
      UDR = '-';
      
      ucTemp = sprintf(buffer, "%d", strtod(ucEx,NULL));
      
      //Ausgabe der Variablen zum testen
      ucCount = 0; 
      while(ucCount != ucTemp)
      {
        while (!(USR & (1<<UDRE)));
        UDR = buffer[ucCount];
        ucCount++;        
      }
      
      
      while (!(USR & (1<<UDRE)));
      UDR = '-';
      
      
    }
  }
}


Ich wollte beim teil der Längengrade (4729.7067) die ersten 2 zeichen 
abschneden da ich diese am schluss addieren muss laut link (hab es 
manuell gerechnet was im link steht klappt problemlos)

Diese ersten 2 zeichen beim Längen grad und 3 beim breitengrad stehen in 
ucNx und uxEx

danach werden die restlichen zeichen in die variablen ucN und ucE 
geschrieben

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mein Rat:
Trenne die Funktionalitäten voneinander.

Da gibt es zuallererst eine Funktion die eine komplette Zeile in einen 
String einliest. Vom ersten $ bis zum abschliessenden \n.

Das kannst du leicht testen. String ausgeben lassen.

Dann brauchst du eine Funktion, die dir aus diesem kompletten String die 
für dich interessanten Teile (Länge und Breite) herausholt. Die ',' 
führen dich durch den String und zeigen dir wo diese Teile im String 
liegen. Genau dazu haben sie die Macher des NMEA Protokolls eingeführt, 
damit man eben nicht Zeichen zählen muss. Wenn ich mir ansehe, was du im 
Code alles an Annahmen über Zeichenlängen triffst, dann läuft es mir 
kalt den Buckel runter.

Auch das kannst du leicht testen.
Du brauchst dir ja nur die beiden so erhaltenen Teil-Strings ausgeben 
lassen

Und erst dann gehst du her und veranstaltest deinen arithmetischen 
Klimbim.
(Aber vorher schnappst du dir noch einen Taschenrechner und probierst 
noch ein wenig. Die Umrechnung hast du noch nicht wirklich verstanden)

Fazit: Altes Prinzip in der Programmierung. Nicht alles auf einmal 
machen, sondern die Aufgabe in Teilaufgaben zerlegen, die jede für sich 
einzeln testbar ist.

Und achte auch darauf, dass Strings in C immer mit einem '\0' Zeichen 
abgeschlossen sind. Ansonsten wird dir nämlich strtod etwas husten.

Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die antwort...

Also das mit den einzelnen funktionalitäten habe ich ereits gemacht... 
habe alles nach einander überprüft und getestet....

Bis hier hin hat alles funktioniert in den arrays stehen die richtigen 
korrekten zeichen....

Auch wenn ich es nun mit komma suchen löse, weiss ich trozdem noch nicht 
wie ich den nun die umrechnung anstellen kann...
Ich habe ja zb 02.7846 als ASCII zeichen im Array... diese zeichen muss 
ich ja nun in eine Float oder double variabel schieben damit ich

02.7846 / 60 rechnen kann und die ersten beider oder ersten 3 zeichen 
dazu addieren kann.

mein problem ist ja nun das ich nicht weiss ob er es richtig 
konvertiert....

Was die konvertierung angeht bin ich noch anfänger deshalb noch die 
frage wegen '\0' also jedes zeichen einzeln ans ende des arrays?

was meinst du mit husten? falsches resultat?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Mein NMEA Datensatz:
> $GPGGA,110447.099,4729.7067,N,00844.1100,E,1,04,20.6,526.2...

#include <math.h>   
#include <string.h>   

double grad2dezimal(char *grad, char *ref)
{
  double ergebnis = 999.999; // ungültiger Wert
  double d;
  double g;
  char *endp;

  if (grad != NULL && ref != NULL) 
  {
    // http://www.opengroup.org/onlinepubs/000095399/functions/strtod.html
    g = strtod(grad, &endp);
    if (grad != endp && *endp == `\0')
    {
      // http://www.delorie.com/gnu/docs/glibc/libc_415.html
      d = trunc(g/100); 
      ergebnis = d + (g - d*100)/60;
      if ( strcmp(ref, "W") == NULL || strcmp(ref, "S") == NULL )
        ergebnis = -ergebnis;
    }
  }
  return ergebnis;
}

void machwas(const char *s)
{
#define PUFFERLAENGE 80
  char nmea[PUFFERLAENGE];
  char *grad;
  char *ref;
  double laenge;
  double breite;

  // http://home.fhtw-berlin.de/~junghans/cref/FUNCTIONS/strncpy.html
  strncpy(nmea, s, PUFFERLAENGE-1); 
  nmea[PUFFERLAENGE-1] = '\0';

  // http://de.wikipedia.org/wiki/NMEA_0183
  // http://home.fhtw-berlin.de/~junghans/cref/FUNCTIONS/strtok.html
  // http://www.cppreference.com/wiki/c/string/strtok
  strtok( nmea, "," ); // $GPGGA
  strtok( NULL, "," ); // 110447.099

  grad = strtok( NULL, "," ); // 4729.7067
  ref  = strtok( NULL, "," ); // N
  // Umwandlung Grad => Dezimal
  breite = grad2dezimal(grad, ref);

  grad = strtok( NULL, "," ); // 00844.1100
  ref  = strtok( NULL, "," ); // E
  // Umwandlung Grad => Dezimal
  laenge = grad2dezimal(grad, ref);

  // Debug (nmea gross genug anlegen!)
  sprintf(nmea, "Breite = %g # Laenge = %g ", breite, laenge);

  // Hier laenge und breite auf ungültige Werte (999.999) abfragen
  // ...
}

int main(void)
{
  machwas("$GPGGA,110447.099,4729.7067,N,00844.1100,E,1,04,20.6,526.2...");
}


Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wooow vielen dank für dein Code...

Habe ihn sogleich getestet.... aber leider kommt wie bei mir nichts 
sinnvolles raus...

Hier das was ausgegebn wird...

Laenge = ? 
<\0>67<\0>N<\0>00844.1100<\0>E<\0>1,04,20.6,526.2...<\0><\0><\0><\0><\0> 
<\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0>_Breite  = ? #

Senden tu ich nmea so:
ucCount = 0;
while(ucCount != 81)
{
  while (!(USR & (1<<UDRE)));
  UDR = nmea[ucCount];
  ucCount++;        
}

kann es sein das hier die Optimierungen von GCC eine rolle spielen?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Claudio H. schrieb:

> kann es sein das hier die Optimierungen von GCC eine rolle spielen?

Nein.
Aber es kann sein, dass du C lernen solltest.

Mach dir eine Funktion
void uart_putc( char c )
{
  while (!(USR & (1<<UDRE)))
    ;
  UDR = c;
}

und dann noch eine
void uart_puts( const char* string )
{
  while( *string )
    uart_putc( *string++ );
}

(Ich kanns einfach nicht mehr sehen, wenn da die banalsten 
Funktionalitäten immer wieder in die Funktionen hineingequetscht werden, 
nur weil man zu faul ist, sich einmalig einen Grundstock an 
Basisfunktionen zu schreiben. Wenn man mit der UART arbeitet braucht man 
eine Basisfunktion um ein einzelnes Zeichen auszugeben und man braucht 
praktisch immer eine Funktion um einen String auszugeben. uart_putc ... 
c wie Character / uart_puts ... s wie String)

Aufgerufen wird das dann
  ....
  // Umwandlung Grad => Dezimal
  laenge = grad2dezimal(grad, ref);

  // Debug (nmea gross genug anlegen!)
  sprintf(nmea, "Breite = %g # Laenge = %g ", breite, laenge);

  uart_puts( nmea );

  ....

und dann liest du noch das hier
http://www.mikrocontroller.net/articles/FAQ#Aktivi...
und machst die Modifikationen.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Claudio H. schrieb:

> Laenge = ?
> <\0>67<\0>N<\0>00844.1100<\0>E<\0>1,04,20.6,526.2...<\0><\0><\0><\0><\0> 
<\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0>_Breite
> = ? #

Damit sind zwei Fehler im Programm.

1) Laenge = ?

Die Mathebibliothek ist nicht mit den übrigen Objektdateien zusammen zum 
Programm gelinkt worden. Das ist ein Problem in deinem Projekt.

http://www.mikrocontroller.net/articles/FAQ#Aktivi...

2) Ein Bufferoverflow ist aufgetreten

Das ist kann ein Fehler in meinem Code sein. Das kann ich erst heute 
abend kontrollieren.

Oder du benutzt den Code falsch.

Stelle sicher, dass die Ausgabe von nmea beim ersten auftretenden '\0' 
beendet wird.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. Hinweis von Karl heinz Buchegger mit der universelleren Lösung

Oder in deinem Code

  ucCount = 0;
  while(ucCount < PUFFERLAENGE && nmea[ucCount] != '\0')
  {
    while (!(USR & (1<<UDRE)));
    UDR = nmea[ucCount];
    ucCount++;        
  }


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan B. schrieb:

> 2) Ein Bufferoverflow ist aufgetreten
>
> Das ist kann ein Fehler in meinem Code sein. Das kann ich erst heute
> abend kontrollieren.
>
> Oder du benutzt den Code falsch.

Ich denke mal, da hat sich die Ausgabe eines empfangenen NMEA Strings 
mitten in die Ausgabe der Ergebnisse eingemischt. Könnte vorkommen, wenn 
der Empfang über Interrupt passiert und die ISR nach dem Empfang einer 
Zeile die Zeile zu Kontrollzwecken ausgibt.

Der Ergebnisstring ist ja grundsätzlich vorhanden (wenn auch mit den ?), 
nur mitten drinn taucht plötzlich ein NMEA String auf.

Edit: Ach, jetzt seh ichs erst.

Laenge = ?
<\0>67<\0>N<\0>00844.1100<\0>E<\0>1,04,20.6,526.2...<\0><\0><\0><\0><\0> 
<\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0><\0>_Breite
= ? #


Da ist einfach nur die Zuordnung zu den Zeilen falsch. Der Code, der den 
NMEA String zu Kontrollzwecken ausgibt, gibt als erstes einen \n aus 
(zumindest sieht es so aus). Könnte auch sein, dass das Terminal von 
sich aus einen Zeilenumbruch macht, und den nach dem ? für Länge macht, 
weil dort ein Leerzeichen ist.

  sprintf(nmea, "\nBreite = %g # Laenge = %g\n", breite, laenge);

Ein \n nach den Ergebnissen schafft hier Klarheit und stellt die 
Zeilenumbrüche richtig.

Autor: Claudio H. (hedie)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank an alle die mir hier so tatkräftig geholfen habe...

Es hat alles bestens geklappt... es war das problem mit der Floating 
Point variable...

Danke :)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.