Hallo zusammen,
ich empfange über die USART1 sekündlich einen NMEA-GPS-String und möchte
nur bestimmte Daten heraus filtern. Soweit so gut. Ich habe viele
Beiträge gelesen und versucht die Hilfen anzuwenden.
So lange ich die _delay_ms(1000); im Code habe wird nur Fehler gesendet.
Nehme ich die _delay_ms(1000); raus erscheint ein paar mal Fehler aber
oft auch das gewüschte $GPRMC und die Zeit. Wie kann ich den Code so
verändern, das nur noch meine gewünschten Werte ausgegeben werden.
Die USART0 ist mt der RS232 meines Computer verbunden.
Vielen Dank für eure Hilfe!
Nur so nebenbei...
> char *stringp = nmea_packet; // Inhalt von nmea_packet wird verändert!
Damit kopierst du nur den Pointer, nicht die eigentlichen Daten!
Du brauchst http://en.wikipedia.org/wiki/Strcpy und Konsorten.
Bene Ja schrieb:> So lange ich die _delay_ms(1000); im Code habe wird nur Fehler gesendet.
Das verwundert nicht sehr. Während der GPS sendet, hängt dein µC in der
Schleife fest und verliert währenddessen praktisch alles außer dem
letzten Zeichen. Da die GPS-Empfänger üblicherweise einmal pro Sekunde
ihre Daten senden, kommt dann natürlich nie was an, weil den Programm
für diese Sekunde immer komplett "taub" ist.
> Wie kann ich den Code so verändern, das nur noch meine gewünschten Werte> ausgegeben werden.
Möglicherweise, indem du die unbekannte Funktion getstr1() korrigierst.
Ich weiß nun nicht wie Du die Daten einsammelst, ich hoffe du
synchronisierst auf $ oder 0x0d damit Du weißt, wann ein neuer Datensatz
begonnen hat. In der Sekunde kommt nicht nur Dein GMRS Datensatz sondern
auch bestimmt 4 weitere (kann man einstellen).
- Daten sammeln und erstmal auf komplette Datensätze konzentrieren
- dann Datensatz filtern in Deinem Fall sind nur GPRMC Datensätze
interessant, steht was anderes drin kannst gleich wieder raus, dann ist
das ein uninteressanter Datensatz
- wurde ein GPRMC Datensatz gefunden, Aufbau überprüfen, dazu an das
Ende der Aufgenommenen Zeile Ende-2 sollte ein * sein, Ende-1 + Ende-0
ist die Checksumme, diese über die Daten von $ - * berechnen (immer XOR
Byte für Byte) kommst Du auf das gleicher Ergebnis dann passt der
Datensatz und Du kannst Dich auf Deine Auswertung stürzen
- die Token Geschiechte habe ich mir nun nicht weiter angesehen, ein
debugging würde ich vlt. mit einer festen Konstante versuchen, von einem
Datensatz wo ich weiß das der geht
Viel Erfolg, AVRli...
if(CharsInBuffer1>0)// es könnten ja auch mehrere Zeichen im Buffer warten
40
{
41
NextChar=getch1();// Hole 1 Zeichen aus dem Buffer
42
// und im String ablegen, wenn
43
// es sich nicht um das Zeilende handelt UND
44
// noch Platz im String ist
45
if(NextChar!='\n'&&StringLen<MaxLen-1){
46
*Buffer++=NextChar;
47
StringLen++;
48
}
49
}
50
}while(NextChar!='\n');
51
52
*Buffer='\0';
53
}
Die Token-Geschichte funktioniert mit einem festen String schonmal.
Aber wie kann ich jetzt den Datensatz am besten filtern?
Ich dachte, dass ich das eigentlich schon in meiner main-Schleife mache.
Mein erster Gedanke wäre jetzt die getstr1 zu verändern, sodass nach $
gesucht wird und ab dann nach dem * und der Checksumme. Die Schritte bis
zum * würde ich dann mit der Checksumme vergleichen. Wenn es
übereinstimmt, dann mache ich dann das, was in meiner main steht. Wenn
es nicht stimmt verwerfe ich den String. Soweit sollte der Grundgedanke
doch richtig sein?
Ich weiß nicht genau wie Du es machst, aber ich habe es bis jetzt immer
so gemacht das ich im ISR die Daten in einen ISR Buffer geschrieben
habe, bei Dir Buffer 1 den hab ich im Main in einen zweiten Buffer
Zeichen für Zeihen kopiert bis ich ein CR hatte. Dann hatte ich den
Datensatz und dann ging es damit weiter. Also mit dem Buffer dann.
Ob Deine Bufferzuordnung so funktioniert, weiß ich nicht. Da muß ich
erstmal schauen was da genau passiert.
Gruß AVRli...
Bene Jan schrieb:> es nicht stimmt verwerfe ich den String. Soweit sollte der Grundgedanke> doch richtig sein?
An deiner Stelle würde ich damit anfangen nicht einfach nur "Fehler"
auszugeben. Denn damit bist du so schlau wie vorher.
Lass dir doch den Anfang des Datensatzes ausgeben, dann siehst du schon
mehr warum ein empfangener Datensatz abgelehnt wurde.
Um Problemstellen bzw. Fehler zu suchen, ist es eine altbewährte
Strategie, dass man das Program so umändert, dass es einem bei der
Fehlersuche hilft. Dazu gehört auch, dass man das Programm relevante
Information ausgeben lässt.
Natürlich muss man darauf achten, dass man durch die zusätzlichen
Ausgaben das Timing des Programms nicht allzuseh verändert. Daher
Kontrollausgaben kurz und bündig halten. Wenn du dir im Fehlerfall die
ersten 8 oder 10 Buchstaben des fehlerhaften Strings ausgeben lässt,
dann tut es das fürs erste auch um eine Vorstellung davon zu bekommen,
was da abgeht.
Wenn ich die Funktion GPSAbfrage in meiner main-Schleife dann aufrufe
und kein delay eingebaut habe, funktioniert sie. Baue ich aber ein delay
ein oder rufe noch andere Funktionen auf, die etwas Zeit benötigen (zum
Abfragen von Temperaturen), funktioniert sie nur noch ganz selten.
Ich dachte eigentlich, dass durch den Ringbuffer und die ISR-Funktion es
so funktonieren sollte, wie ich mir das vorstelle.
Was mache ich denn noch falsch? Bzw. wie kann ich mein Programm so
umändern, dass mir getstr1() nur den passenden String liefert? Würde es
etwas bringen die ISR zu verändern?
eine Sicherung einbauen.
So wie die Funktion jetzt ist, ist es definitiv nicht zulässig sie
aufzurufen, wenn CharsInBuffer gleich 0 ist.
In deiner vorhergehenden Programmversion hast du das noch berücksichtigt
in dem du vor Aufruf der Funktion CharsInBuffer abgefragt hast.
In deiner jetzigen nicht mehr:
In
1
voidgetstr1(char*Buffer,uint8_tMaxLen)
2
{
3
.....
4
5
do
6
{
7
*Buffer++=NextChar;
8
StringLen++;
9
NextChar=getch1();
10
}while(NextChar!='\n');
wird getchl auf Biegen und Brechen aufgerufen. Dir ist auch völlig egal,
ob du deinen Buffer zumüllst oder gar überläufst.
Dies deshalb weil du deine String Empfangsroutine jetzt immer
unlogischer nach dem 'Na da probier ich mal was' Prinzip umänderst.
Wenn du in der String Empfangsroutine sowieso solange wartest, bis eine
Zeile komplett beisammen ist, dann schreib dir eine Funktion (oder
ändere die bestehende so um), die auch auf ein Zeichen wartet. Eine
eigene Funktion für diese Funktionalität: Warte bis ein Zeichen im
Empfangsbuffer vorliegt und hole es. Deine getch1 lässt sich relativ
leicht dahingehend umbauen.
Dann braucht sich ausserhalb der getchl niemand mehr Gedanken darüber
machen, ob es jetzt überhaupt zulässig ist getchl aufzurufen. Lerne
daraus, dass du deine eigenen Funktionen falsch benutzt hast, damit dir
das in Zukunft nicht wieder passiert. Funktionen immer so bauen, dass es
keine oder zumindest nur wenige Voraussetzungen für deren Aufruf gibt.
Wenn es aber Voraussetzungen gibt, dann müssen die strikt eingehalten
werden! Code so bauen, dass man derartige Kontrakt-Verletzungen erkennen
kann.
Der andere Punkt steht immer noch aus:
Wenn in einem Programm Dinge passieren, die du nicht verstehst, dann
musst du als allererstes die Ratespielchen abstellen. Und dazu gehört,
dass dir unter Umständen das Programm hilft, indem du dir für den
Programmablauf wichtige Variablenwerte ausgeben lässt. Und ja: ein
String, der von einer Schnittstelle empfangen wird, ist definitv ein
wichtiger Variablenwert. Denn wenn der nicht stimmt und nicht so
aussieht wie er deiner Meinung nach aussehen sollte, geht die ganze
weitere Ausswertung schief.
Mach dir Ausgaben in dein Programm rein! Das geht schneller und ist sehr
oft weit zielführender als deinen Code hier ins Forum zu stellen und
nach Hilfe zu rufen. Die 3 Anweisungen für die Ausgabe hast du schnell
eingebaut und genauso schnell wieder entfernt, wenn alles funktioniert.
Aber während der Fehlersuche sind sie von unschätzbarem Wert (wenn man
sich die richtigen Dinge ausgeben lässt! Bei Strings kann das manchmal
etwas tricky sein)
Nach tagelangem hin und her, dachte ich eigentlich, dass ich es jetzt
fast geschafft hätte, aber als ich den Code dann in meinen eigentlichen
Code einbauen wollte, funktionierte er nicht mehr.
Ich habe eine kleine Funktion eingebaut, die überprüft, ob Zeichen in
meinem Ringbuffer vorhanden sind.
Ich habe auch versucht zu verstehen, was falsch läuft und habe viele
Ausgaben auf die RS232-Schnittstelle eingebaut. Aber trotzdem habe ich
Probleme den Fehler zu verstehen und abzuändern.
Mein Code läuft, wenn der Controller sonst nichts anderes macht. Baue
ich ein delay z.B. 1sec ein, macht der Controller nicht mehr das was ich
will. D.h. er geht in die Funktion StringHolen in die 1. do-Schleife,
versucht /n zu finden und geht wieder aus der kompletten Funktion raus.
1x pro Minute geht er dann in die zweite do-Schleife und bleibt dort,
bis er /n das nächste mal findet. Es kommt aber nie vor, das $GPRMC
gefunden wird.
Wo kann ich ansetzen?
main.c:
Bene Jan schrieb:> Die anderen Funktionen sind gleich geblieben.
Das heisst, du bügelst hier
1
do
2
{
3
putch0('3');
4
buffer[StringLen]=NextChar;
5
StringLen++;
6
NextChar=getch1();
7
}while(NextChar!='\n');
immer noch den buffer nieder?
> Ich habe eine kleine Funktion eingebaut, die überprüft, ob Zeichen> in meinem Ringbuffer vorhanden sind.
Die hättest du dir sparen können.
Was ist so schwer daran getch1 so umzubauen, dass es auf ein Zeichen
wartet?
Damit ist sicher gestellt, dass getchl mit Sicherheit immer ein Zeichen
holt, WENN eines im Ringbuffer vorhanden ist. Den Fall, dass getch1
zurückkehrt ohne ein Zeichen (bzw. ein altes Zeichen) aus dem Rungbuffer
geholt zu haben kann es nicht mehr geben.
Hi all
will mich auch mal eintakten. beschaeftige mich z.Z auch mit dem Thema,
will einen Autopiloten fuer Flugmodelle bauen, das nach GPS koordinaten
fliegt. Ziel ist es ein Modell das ausser Reichweite (Sicht oder Funk)
ist, sicher wieder zum Startpunkt zurueck zu bringen. Hatten
schon einige Modellverluste dadurch.
Bin noch ganz am Anfang, aber hier schon mal meine GPS Versuche.
Bevor ich alles in eine Mega32 programmiere habe ich das Grundprinzip
der Datenauswertung erstmal auf Computer emuliert.
Und setze will das nun auf den Mega32 umsetzen.
Sehe darin kein grosses Problem, da die wichtigsten Grundfunktionen
getestet sind.
Bei meinen Versuchen verwende ich immer die int routinen (USART SPI
TIMER,..), finde es vom handling einfacher, hat sicher auch Nachteile.
hier mein code:
//-------------------------------------------------------------------
char stream [256];
int Status;
int ValidDataFlag;
char Key [86];
char ValidData [86];
char Breite [12];
char Laenge [12];
char Geschwindigkeit [32];
char Qualitaet [4];
char Satellit [4];
char Hoehe [8];
//--------------------------------------------------------------
// emulierte int routine, wird spaeter durch
// ATMega 32 int ersetzt
// jedes ankommende Zeichen wird unter Key gespeichert
// wenn ende \n erkannt dann nach ValidData kopieren
// um sicher zu stellen das waehrend des auswerten
// die Daten nicht durch neue daten ueberschrieben
// werden ( i.A unwahrscheinlich) aber besser ist besser
// da prozessor neben bei noch andere Aufaben ueber nehmen soll
// ValidDataFlag zeigt main an ob gueltige daten vorhanden
//-------------------------------------------------------------
// emulierte recv int routine
// in Mega32 entfaellt dann (char *s1)
void SIGNAL_RECV (char *s1)
{
char RecData;
char Tmp [2];
int count;
// naechste Zeile wenn Mega32 verwendet
// RecData = UDR;
// naechate Zeile entfallet in Mega32
RecData = s1[0];
sprintf (Tmp,"%c",RecData);
switch (Status)
{
case 0:
{
if (RecData == '$')
{
Key [0] = '\0';
Status = 1;
ValidDataFlag = 0;
}
break;
}
case 1:
{
if (RecData == '$') // error, string untgueltig wird verworfen
{
Key [0] = '\0';
Status = 0;
}
else
{
if (RecData == '\n') // string ende erkannt
{
//--------------------------------------------------------------------
// auf Key steht jetzt z.B.
// GPRMC,191410,A,4735.5634,N,00739.3538,E,0.0,0.0,181102,0.4,E,A*19
//--------------------------------------------------------------------
ValidDataFlag = 1; // Flag zur Weiterverarbeitung setzen
Status = 0;
ValidData [0] = '\0'; // kopieren in Auswertebereich
strcat (ValidData,Key);
}
else
{
strcat (Key,Tmp);
if (strlen(Key) > 86) //Laenge auf 82 Zeichen fest, NMEA
{
Status = 0; // abbruch ende nicht erkannt, string zu lang
}
}
}
}
}
}
//----------------------------------------------------------------------
--
// pars routine erkennet trenn/ende Zeichen
// hier , space \0 \n
// Leerzechen am Anfang werden ignoriert
// beispiel wenn **s1 GPRMC,191410,A,ist
// dann steht danach *s1 auf GPRMC,191410,A,
// und der return wert auf ,191410,A,
//----------------------------------------------------------------------
--
char *pars (char**s1)
{
static char *s2;
s2 = *s1;
while (*s2 == ' ' && *s2 != '\0') // leerzeichen ignorieren
s2++;
*s1 = s2; // string anfang
if (*s2 != '\0' && *s2 != '\n')
{
//ende string feststellen
while (*s2 != '\0' && *s2 != ' ' && *s2 != ',' && *s2 != '\n')
s2++;
}
return (s2);
}
//----------------------------------------------------------------------
---
// routine gibt decodierten string zurueck
// beispiel
// input **s1, GPRMC,191410,A,
// output *s1, 191410,A,
// return string, GPRMC
//----------------------------------------------------------------------
---
char *ParsValue (char **s1)
{
char *p1,*p2;
static char tmp [32];
p1 = *s1;
p2 = pars (&p1);
tmp [0] = '\0';
strncat (tmp,p1,p2 - p1);
p1 = tmp;
*s1 = p2;
return p1;
}
//------------------------------------
// decodiere Daten
// datenstehen unter ValidData
//------------------------------------
void DecodeGpsData ()
{
char *p1,*p2;
char code [32];
char tmp [32];
p1 = ValidData;
p2 = pars (&p1);
code [0] = '\0';
strncat (code,p1,p2 - p1);
if (strcmp(code,"GPRMC") == 0)
{
p1 = ++p2;
p2 = ParsValue (&p1); // ignore time
p2 = ParsValue (&(++p1));
if (strcmp (p2,"A") == 0) // check ob GPS data valid
{
p2 = ParsValue (&(++p1));
Breite [0] = '\0';
strcat (Breite,p2); // speichert die Breite
p2 = ParsValue (&(++p1)); // ignore nord or sued
p2 = ParsValue (&(++p1));
Laenge [0] = '\0';
strcat (Laenge,p2); // speichert die Laenge
p2 = ParsValue (&(++p1)); // ignore ost or west
p2 = ParsValue (&(++p1));
Geschwindigkeit [0] = '\0';
strcat (Geschwindigkeit,p2); // speichertgeschwindigkeits info
}
}
if (strcmp(code,"GPGGA") == 0)
{
p1 = ++p2;
p2 = ParsValue (&p1); // ignore time
p2 = ParsValue (&(++p1));
Breite [0] = '\0';
strcat (Breite,p2);
p2 = ParsValue (&(++p1)); // ignore nord or sued
p2 = ParsValue (&(++p1));
Laenge [0] = '\0';
strcat (Laenge,p2);
p2 = ParsValue (&(++p1)); // ignore ost or west
p2 = ParsValue (&(++p1));
Qualitaet [0] = '\0';
strcat (Qualitaet,p2);
if (strcmp(Qualitaet,"1") == 0)
{
p2 = ParsValue (&(++p1));
Satellit [0] = '\0';
strcat (Satellit,p2);
p2 = ParsValue (&(++p1)); // ignore HDOP
p2 = ParsValue (&(++p1));
Hoehe [0] = '\0';
strcat (Hoehe,p2); // speichert die hoehe
}
}
}
//------------------------------------
// ueberprueft die checksumme der daten
//------------------------------------
int CheckSum ()
{
char *p1,*p2;
p1 = Key;
unsigned int Checksum = 0;
unsigned int OriginalCheckSum = 0;
if (strlen(p1) == 0)
{
return 0;
}
while (*p1 != '*') // berechnen der checksumme
{
Checksum ^= *p1;
p1++;
}
p1++;
p2 = p1 + 1;
// lesen der orginalchecksumme
OriginalCheckSum = (*p1 - '0') << 4 | (*p2 -'0');
if (OriginalCheckSum == Checksum)
{
return 1; // kein datenfehler
}
else
{
return 0; // datenfehler
}
}
int main(int argc, char *argv[])
{
char *p1,*p2;
char ret;
stream [0] = '\0';
strcat
(stream,"$GPRMC,191410,A,4735.5634,N,00739.3538,E,0.0,0.0,181102,0.4,E,A
*19\n$GPGGA,191410,4735.5634,N,00739.3538,E,1,04,4.4,351.5,M,48.0,M,,*45
\n");
p1 = stream;
Status = 0;
Breite [0] = '\0';
Laenge [0] = '\0';
Geschwindigkeit [0] = '\0';
Hoehe [0] = '\0';
while (*p1 != '\0')
{
SIGNAL_RECV (p1);
// Flag zeigt an ob string komplett vorhanden von $ bis \n,
// $ nicht im string enthalten
if (ValidDataFlag == 1)
{
// pruefen ob checksumme o.k wenn nicht verwerfen
ret = CheckSum ();
if (ret == 1)
{
// decodieren der Strings nach den Daten, hier GPRMC und GPGGA daten
DecodeGpsData ();
}
}
p1++;
}
}
Habe zur Zeit die Transmit int routine fuer mega32 implemtiert, und
funktioniert.
Als GPS empfaenger verwende ich den NL 552 ETTL mit 38400 Baud.
Alle Info zu GPS habe ich mir von der Seite
http://www.kowoma.de/gps/
geholt.
Habe mich hier eingetaktet um meine Erfahrungen zu vermittlen.
Vielleicht kann das als Anregung mit dienen. Es gibt sicher noch andere
Wege, die sicher auch optimaler sind.
Moechte das nur als Anregung sehen
Gruss W.G.