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


von C. H. (hedie)


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-nmea-latitude-longitude-to.html

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
1
ufLatitude = ucN[2] * 10 + ucN[3] * 1 + ucN[4] * 0.1 + ucN[5] * 0.01 + ucN[6] * 0.001 + ucN[7] * 0.0001;
2
      ucTemp = ucN[0] * 10 + ucN[1] * 1;
3
      ufLatitude = ufLatitude / 60;
4
      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:
1
string[0] = (ufLatitude / 100);
2
string[1] = (ufLatitude / 10);
3
string[2] = (ufLatitude / 1);
4
string[3] = '.';
5
string[4] = (ufLatitude / 0.1);
6
string[5] = (ufLatitude / 0.01);
7
string[6] = (ufLatitude / 0.001);
8
string[7] = (ufLatitude / 0.0001);
9
string[8] = (ufLatitude / 0.00001);
10
      
11
      
12
ucCount = 0;
13
while(ucCount != 9)
14
{
15
  while (!(USR & (1<<UDRE)));
16
  UDR = string[ucCount]  + 0x30;
17
  ucCount++;        
18
}

es kommt jedoch leider nur "müll" raus

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

Hoffe ihr könnt mir helfen...

Danke schonmal

von Dean R. (smile_t)


Lesenswert?

Hallo

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

versuch es mal mit google ;)

von holger (Gast)


Lesenswert?

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

And so on;)

von Karl H. (kbuchegg)


Lesenswert?

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

von Tim S. (maxxie)


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.

von holger (Gast)


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.

von C. H. (hedie)


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
1
//Infos unsigned char buffer[8] 
2
3
ufLatitude = strtod(ucN,NULL);
4
ufLongtitude = strtod(ucE,NULL);
5
      
6
ufLatitude = ((ufLatitude / 60) + strtod(ucNx,NULL)); 
7
ufLongtitude = ((ufLongtitude / 60) + strtod(ucEx,NULL));
8
      
9
ucTemp = sprintf(buffer, "%d", ufLatitude);
10
      
11
      
12
ucCount = 0;
13
while(ucCount != 9)
14
{
15
  while (!(USR & (1<<UDRE)));
16
  UDR = buffer[ucCount];
17
  ucCount++;        
18
}
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

von Karl H. (kbuchegg)


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

von C. H. (hedie)


Lesenswert?

Ok danke für die antwort... hier mein gesamter code
1
unsigned char ucTemp = 0;
2
unsigned char ucCount = 0;
3
unsigned char ucN[6];
4
unsigned char ucE[6];
5
unsigned char ucEx[2];
6
unsigned char ucNx[1];
7
unsigned char buffer[8];
8
unsigned char GPS_SAT = 0;
9
unsigned char GPS_STAT = 0;
10
double ufLatitude = 0;
11
double ufLongtitude = 0;
12
13
void waitsigns(unsigned char ucSigns)
14
{
15
  ucCount = 0;
16
  while(ucCount != ucSigns)    //3 Zeichen aussetzen 
17
  {
18
    while (!(USR & (1<<RXC)));
19
    ucTemp = UDR;
20
    ucCount++;
21
  }
22
}
23
24
void RX_GPS(void)
25
{
26
  while (!(USR & (1<<RXC)));   // warten bis Zeichen verfuegbar
27
  if(UDR == '$')
28
  {  
29
    waitsigns(5);
30
    
31
    if(ucTemp == 'A')     //Prüfen ob GPGGA (letztes zeichen)
32
    {  
33
      
34
      waitsigns(12);
35
      
36
      ucCount = 0;
37
      while(ucCount != 2)    //2 Zeichen speichern N Breitengrad Anfang
38
      { 
39
        while (!(USR & (1<<RXC)));
40
        ucNx[ucCount] = UDR;
41
        ucCount++;
42
      }
43
      
44
      
45
      ucCount = 0;
46
      while(ucCount != 7)    //7 Zeichen speichern N Breitengrad
47
      {
48
        while (!(USR & (1<<RXC)));
49
        ucN[ucCount] = UDR;
50
        ucCount++;
51
      }
52
      
53
      
54
      waitsigns(3);
55
      
56
      ucCount = 0;
57
      while(ucCount != 3)    //3 Zeichen speichern E Längengrad Anfang
58
      { 
59
        while (!(USR & (1<<RXC)));
60
        ucEx[ucCount] = UDR;
61
        ucCount++;
62
      }
63
      
64
      ucCount = 0;
65
      while(ucCount != 7)  //7 Zeichen Speichern E Längengrad
66
      {
67
        while (!(USR & (1<<RXC)));
68
        ucE[ucCount] = UDR;
69
        ucCount++;
70
      }
71
      
72
      
73
      waitsigns(3); //Anzahl zeichen warten (auslassen)
74
      
75
      while (!(USR & (1<<RXC)));
76
      GPS_STAT = (UDR - 0x30);
77
      
78
      waitsigns(1);
79
      
80
      while (!(USR & (1<<RXC)));  
81
      ucTemp = UDR;
82
      GPS_SAT = (ucTemp - 0x30);
83
      GPS_SAT = GPS_SAT << 4;
84
      
85
      while (!(USR & (1<<RXC)));
86
      ucTemp = UDR;
87
      GPS_SAT = (GPS_SAT | (ucTemp - 0x30));
88
      
89
      
90
      ufLatitude = strtod(ucN,NULL);
91
      ufLongtitude = strtod(ucE,NULL);
92
      
93
      ufLatitude = ((ufLatitude / 60) + strtod(ucNx,NULL)); 
94
      ufLongtitude = ((ufLongtitude / 60) + strtod(ucEx,NULL));
95
      
96
      ucTemp = sprintf(buffer, "%d", ufLatitude);
97
      
98
      //Ausgabe der Variablen zum testen
99
      ucCount = 0;
100
      while(ucCount != 9)
101
      {
102
        while (!(USR & (1<<UDRE)));
103
        UDR = buffer[ucCount];
104
        ucCount++;        
105
      }
106
      
107
      
108
      while (!(USR & (1<<UDRE)));
109
      UDR = '-';
110
      
111
      ucTemp = sprintf(buffer, "%d", strtod(ucEx,NULL));
112
      
113
      //Ausgabe der Variablen zum testen
114
      ucCount = 0; 
115
      while(ucCount != ucTemp)
116
      {
117
        while (!(USR & (1<<UDRE)));
118
        UDR = buffer[ucCount];
119
        ucCount++;        
120
      }
121
      
122
      
123
      while (!(USR & (1<<UDRE)));
124
      UDR = '-';
125
      
126
      
127
    }
128
  }
129
}

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

von Karl H. (kbuchegg)


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.

von C. H. (hedie)


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?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Mein NMEA Datensatz:
> $GPGGA,110447.099,4729.7067,N,00844.1100,E,1,04,20.6,526.2...
1
#include <math.h>   
2
#include <string.h>   
3
4
double grad2dezimal(char *grad, char *ref)
5
{
6
  double ergebnis = 999.999; // ungültiger Wert
7
  double d;
8
  double g;
9
  char *endp;
10
11
  if (grad != NULL && ref != NULL) 
12
  {
13
    // http://www.opengroup.org/onlinepubs/000095399/functions/strtod.html
14
    g = strtod(grad, &endp);
15
    if (grad != endp && *endp == `\0')
16
    {
17
      // http://www.delorie.com/gnu/docs/glibc/libc_415.html
18
      d = trunc(g/100); 
19
      ergebnis = d + (g - d*100)/60;
20
      if ( strcmp(ref, "W") == NULL || strcmp(ref, "S") == NULL )
21
        ergebnis = -ergebnis;
22
    }
23
  }
24
  return ergebnis;
25
}
26
27
void machwas(const char *s)
28
{
29
#define PUFFERLAENGE 80
30
  char nmea[PUFFERLAENGE];
31
  char *grad;
32
  char *ref;
33
  double laenge;
34
  double breite;
35
36
  // http://home.fhtw-berlin.de/~junghans/cref/FUNCTIONS/strncpy.html
37
  strncpy(nmea, s, PUFFERLAENGE-1); 
38
  nmea[PUFFERLAENGE-1] = '\0';
39
40
  // http://de.wikipedia.org/wiki/NMEA_0183
41
  // http://home.fhtw-berlin.de/~junghans/cref/FUNCTIONS/strtok.html
42
  // http://www.cppreference.com/wiki/c/string/strtok
43
  strtok( nmea, "," ); // $GPGGA
44
  strtok( NULL, "," ); // 110447.099
45
46
  grad = strtok( NULL, "," ); // 4729.7067
47
  ref  = strtok( NULL, "," ); // N
48
  // Umwandlung Grad => Dezimal
49
  breite = grad2dezimal(grad, ref);
50
51
  grad = strtok( NULL, "," ); // 00844.1100
52
  ref  = strtok( NULL, "," ); // E
53
  // Umwandlung Grad => Dezimal
54
  laenge = grad2dezimal(grad, ref);
55
56
  // Debug (nmea gross genug anlegen!)
57
  sprintf(nmea, "Breite = %g # Laenge = %g ", breite, laenge);
58
59
  // Hier laenge und breite auf ungültige Werte (999.999) abfragen
60
  // ...
61
}
62
63
int main(void)
64
{
65
  machwas("$GPGGA,110447.099,4729.7067,N,00844.1100,E,1,04,20.6,526.2...");
66
}

von C. H. (hedie)


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:
1
ucCount = 0;
2
while(ucCount != 81)
3
{
4
  while (!(USR & (1<<UDRE)));
5
  UDR = nmea[ucCount];
6
  ucCount++;        
7
}

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

von Karl H. (kbuchegg)


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
1
void uart_putc( char c )
2
{
3
  while (!(USR & (1<<UDRE)))
4
    ;
5
  UDR = c;
6
}

und dann noch eine
1
void uart_puts( const char* string )
2
{
3
  while( *string )
4
    uart_putc( *string++ );
5
}

(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
1
  ....
2
  // Umwandlung Grad => Dezimal
3
  laenge = grad2dezimal(grad, ref);
4
5
  // Debug (nmea gross genug anlegen!)
6
  sprintf(nmea, "Breite = %g # Laenge = %g ", breite, laenge);
7
8
  uart_puts( nmea );
9
10
  ....

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

von Stefan B. (stefan) Benutzerseite


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#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio

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.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

S. Hinweis von Karl heinz Buchegger mit der universelleren Lösung

Oder in deinem Code
1
  ucCount = 0;
2
  while(ucCount < PUFFERLAENGE && nmea[ucCount] != '\0')
3
  {
4
    while (!(USR & (1<<UDRE)));
5
    UDR = nmea[ucCount];
6
    ucCount++;        
7
  }

von Karl H. (kbuchegg)


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.

von C. H. (hedie)


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 :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.