2014-08-31 233 views
1

我知道這已被問了很多次,但我真的找不到我真正搜索的東西。使用arduino解碼GPS NMEA代碼

我正在使用Arduino Uno和一個通過串行顯示GPS數據的GPS屏蔽。

這裏是我上傳到我的Arduino接口的GPS盾代碼:

void loop() // run over and over 
{ 
    while(!(mySerial.available())){} 
     Serial.write(mySerial.read()); 
} 

這只是代碼。儘管如此,隨着串行監視器的不斷循環,它還會每秒輸出一次GPS數據。

下面是它的輸出每秒:

$GPGGA,013856.000,000.9090,N,9090.90,E,1,09,1.1,316.97,M,0.00,M,,*66 
$GPGSA,A,3,07,08,11,1ÿ3,16,19,23,27,42,,,,2.8,1.1,2.5*3F 
$GPRMC,013856.000,A,000.9090,N,9090.90,E,0.0,038.1,310814,,,A*62 
$GPGSV,ÿ3,1,12,16,26,059,33,27,33,025,44,08,30,330,32,07,31,326,34*7A 
$GPGSV,3,2,12,19,58,354,31,01,33,186,18,23,32,221,24,11,5ÿ9,198,31*70 
$GPGSV,3,3,12,42,60,129,32,13,38,253,27,32,06,161,,31,01,140,*7E 

由於它每秒更新,座標的變化最小的,這意味着GPS屏蔽工作。

這裏的問題是,我想解析GPS數據,特別是在GPGGA線路上,而忽略其他線路。我想分析狀態,緯度,N/S指標,經度和E/W指標。

我已經搜索了NMEA庫(http://nmea.sourceforge.net/),但我不知道如何使用它。

是否有人可以幫助我在這裏?謝謝。

+0

我想你可以使用正則表達式從所有數據解析GPS。 – 2014-08-31 01:50:23

回答

0

您可以使用TinyGPS來解析NMEA字符串。如果你只對一句話感興趣。您只能爲該句子編寫如下的自定義分析器。

int handle_byte(int byteGPS) { 
buf[counter1] = byteGPS; 
//Serial.print((char)byteGPS); 
counter1++; 
if (counter1 == 300) { 
    return 0; 
} 

if (byteGPS == ',') { 
    counter2++; 
    offsets[counter2] = counter1; 
    if (counter2 == 13) { 
     return 0; 
    } } if (byteGPS == '*') { 
    offsets[12] = counter1; } 

    // Check if we got a <LF>, which indicates the end of line if (byteGPS == 10) { 

    // Check that we got 12 pieces, and that the first piece is 6 characters 
    if (counter2 != 12 || (get_size(0) != 6)) { 
     return 0; 
    } 

    // Check that we received $GPRMC 
    // CMD buffer contains $GPRMC 
    for (int j=0; j<6; j++) { 

     if (buf[j] != cmd[j]) { 
     return 0; 
     } 
    } 

    // Check that time is well formed 
    if (get_size(1) != 10) { 

     return 0; 
    } 

    // Check that date is well formed 
    if (get_size(9) != 6) { 
     return 0; 
    } 

    SeeedOled.setTextXY(7,0); 
    for (int j=0; j<6; j++) { 
     SeeedOled.putChar(*(buf+offsets[1]+j)); 
    } 
    SeeedOled.setTextXY(7,7); 

    for (int j=0; j<6; j++) { 
     SeeedOled.putChar(*(buf+offsets[9]+j)); 
    } 

    // TODO: compute and validate checksum 

    // TODO: handle timezone offset 

     return 0; } 
return 1; } 
1

我已經搜索了互聯網,最好的答案是使用Arduino的「TinyGPS ++」庫。幾乎所有與GPS相關的代碼都已包含在庫中。

0

試試這個,它可以幫助你

#include <SoftwareSerial.h> 
#include <TinyGPS.h> 

TinyGPS gps; 
SoftwareSerial ss(3,4); 


static void smartdelay(unsigned long ms); 
static void print_float(float val, float invalid, int len, int prec); 
static void print_int(unsigned long val, unsigned long invalid, int len); 
static void print_date(TinyGPS &gps); 
static void print_str(const char *str, int len); 

void setup() 
{ 
    Serial.begin(9600); 
    ss.begin(9600); 
} 

void loop() 
{ 
    float flat, flon; 

    unsigned short sentences = 0, failed = 0; 

    gps.f_get_position(&flat, &flon); 
    Serial.print("LATITUDE: "); 
    print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 10, 6); 
    Serial.println(" "); 
    Serial.print("LONGITUDE: "); 
    print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 11, 6); 
    Serial.println(" "); 

    Serial.print("altitude: "); 
    print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 7, 2); 
    Serial.println(" "); 
    Serial.print("COURSE:"); 
    print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2); 
    Serial.println(""); 

    Serial.print("DIRECTION: "); 
    int d; 
    print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6); 
    d=gps.f_course(); 
    Serial.println(); 
    Serial.println(); 
    smartdelay(1000);  

} 

static void smartdelay(unsigned long ms) 
{ 
    unsigned long start = millis(); 
    do 
    { 
    while (ss.available()) 
     gps.encode(ss.read()); 
    } while (millis() - start < ms); 
} 

static void print_float(float val, float invalid, int len, int prec) 
{ 
    if (val == invalid) 
    { 
    while (len-- > 1) 
     Serial.print('*'); 
    Serial.print(' '); 
    } 
    else 
    { 
    Serial.print(val, prec); 
    int vi = abs((int)val); 
    int flen = prec + (val < 0.0 ? 2 : 1); // . and - 
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; 
    for (int i=flen; i<len; ++i) 
     Serial.print(' '); 
    } 
    smartdelay(0); 
} 

static void print_int(unsigned long val, unsigned long invalid, int len) 
{ 
    char sz[32]; 
    if (val == invalid) 
    strcpy(sz, "*******"); 
    else 
    sprintf(sz, "%ld", val); 
    sz[len] = 0; 
    for (int i=strlen(sz); i<len; ++i) 
    sz[i] = ' '; 
    if (len > 0) 
    sz[len-1] = ' '; 
    Serial.print(sz); 
    smartdelay(0); 
} 
static void print_str(const char *str, int len) 
{ 
    int slen = strlen(str); 
    for (int i=0; i<len; ++i) 
    Serial.print(i<slen ? str[i] : ' '); 
    smartdelay(0); 
} 
1

NMEA數據是在GPS式(ddmm.ssss)格式,谷歌希望它在小數樣式(dd.mmssss),還有在丁文功能這一步的代碼的底部。

我寫這個是因爲我不喜歡大的複雜的庫來做簡單的小事情,特別是當我試圖弄清楚它是如何工作的時候。 這將分析GLL語句,但您可以更改它正在查找的語句,並在需要時重新排列分段。

String ReadString; 

void setup() { 
    Serial.begin(9600); //Arduino serial monitor thru USB cable 
    Serial1.begin(9600); // Serial1 port connected to GPS 
} 

void loop() { 
    ReadString=Serial1.readStringUntil(13); //NMEA data ends with 'return' character, which is ascii(13) 
    ReadString.trim();      // they say NMEA data starts with "$", but the Arduino doesn't think so. 
    // Serial.println(ReadString);   //All the raw sentences will be sent to monitor, if you want them, maybe to see the labels and data order. 

    //Start Parsing by finding data, put it in a string of character array, then removing it, leaving the rest of thes sentence for the next 'find' 
    if (ReadString.startsWith("$GPGLL")) { //I picked this sentence, you can pick any of the other labels and rearrange/add sections as needed. 
     Serial.println(ReadString);  // display raw GLL data in Serial Monitor 
    // mine looks like this: "$GPGLL,4053.16598,N,10458.93997,E,224431.00,A,D*7D" 

//This section gets repeated for each delimeted bit of data by looking for the commas 
    //Find Lattitude is first in GLL sentence, other senetences have data in different order 
     int Pos=ReadString.indexOf(','); //look for comma delimetrer 
     ReadString.remove(0, Pos+1); // Remove Pos+1 characters starting at index=0, this one strips off "$GPGLL" in my sentence 
     Pos=ReadString.indexOf(','); //looks for next comma delimetrer, which is now the first comma because I removed the first segment 
     char Lat[Pos];   //declare character array Lat with a size of the dbit of data 
      for (int i=0; i <= Pos-1; i++){ // load charcters into array 
      Lat[i]=ReadString.charAt(i);   
      } 
      Serial.print(Lat);   // display raw latitude data in Serial Monitor, I'll use Lat again in a few lines for converting 
//repeating with a different char array variable   
     //Get Lattitude North or South 
     ReadString.remove(0, Pos+1);    
     Pos=ReadString.indexOf(',');  
     char LatSide[Pos];   //declare different variable name 
      for (int i=0; i <= Pos-1; i++){ 
      LatSide[i]=ReadString.charAt(i); //fill the array   
      Serial.println(LatSide[i]);  //display N or S 
      } 

      //convert the variable array Lat to degrees Google can use 
      float LatAsFloat = atof (Lat);   //atof converts the char array to a float type 
      float LatInDeg; 
      if(LatSide[0]==char(78)) {  //char(69) is decimal for the letter "N" in ascii chart 
       LatInDeg= ConvertData(LatAsFloat); //call the conversion funcion (see below) 
      } 
      if(LatSide[0]==char(83)) {  //char(69) is decimal for the letter "S" in ascii chart 
       LatInDeg= -(ConvertData(LatAsFloat)); //call the conversion funcion (see below) 
      } 
      Serial.println(LatInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high 
//repeating with a different char array variable    
     //Get Longitude 
     ReadString.remove(0, Pos+1);    
     Pos=ReadString.indexOf(',');  
     char Longit[Pos];    //declare different variable name 
      for (int i=0; i <= Pos-1; i++){ 
      Longit[i]=ReadString.charAt(i);  //fill the array 
      } 
      Serial.print(Longit);  //display raw longitude data in Serial Monitor  
//repeating with a different char array variable 
      //Get Longitude East or West 
     ReadString.remove(0, Pos+1);    
     Pos=ReadString.indexOf(',');  
     char LongitSide[Pos];   //declare different variable name 
      for (int i=0; i <= Pos-1; i++){ 
      LongitSide[i]=ReadString.charAt(i);  //fill the array   
      Serial.println(LongitSide[i]);  //display raw longitude data in Serial Monitor 
      }  
      //convert to degrees Google can use 
      float LongitAsFloat = atof (Longit); //atof converts the char array to a float type 
      float LongInDeg; 
     if(LongitSide[0]==char(69)) {  //char(69) is decimal for the letter "E" in ascii chart 
       LongInDeg=ConvertData(LongitAsFloat); //call the conversion funcion (see below 
     }  
     if(LongitSide[0]==char(87)) {   //char(87) is decimal for the letter "W" in ascii chart 
       LongInDeg=-(ConvertData(LongitAsFloat)); //call the conversion funcion (see below 
     }    
      Serial.println(LongInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high 
//repeating with a different char array variable 
      //Get TimeStamp - GMT 
     ReadString.remove(0, Pos+1);     
     Pos=ReadString.indexOf(',');  
     char TimeStamp[Pos];   //declare different variable name 
      for (int i=0; i <= Pos-1; i++){ 
      TimeStamp[i]=ReadString.charAt(i);   //fill the array  
      } 
      Serial.print(TimeStamp); //display raw longitude data in Serial Monitor, GMT 
      Serial.println("");  
    } 
} 

//Conversion function 
float ConvertData(float RawDegrees) 
{ 
    float RawAsFloat = RawDegrees; 
    int firstdigits = ((int)RawAsFloat)/100; // Get the first digits by turning f into an integer, then doing an integer divide by 100; 
    float nexttwodigits = RawAsFloat - (float)(firstdigits*100); 
    float Converted = (float)(firstdigits + nexttwodigits/60.0); 
    return Converted; 
}