代码之家  ›  专栏  ›  技术社区  ›  weyhei

使用arduino解码GPS NMEA代码

  •  2
  • weyhei  · 技术社区  · 10 年前

    我知道这已经被问了很多次,但我真的找不到我真正在寻找的东西。

    我使用的是Arduino Uno和通过串行显示GPS数据的GPS Shield。

    以下是我上传到Arduino以连接GPS Shield的代码:

    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/ ),但我不知道如何使用它。

    有人能帮我吗?非常感谢。

    5 回复  |  直到 10 年前
        1
  •  2
  •   Stephen Rauch Eugen    6 年前

    NMEA数据采用GPS样式(ddmm.sss)格式,谷歌希望采用十进制样式(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;
    }
    
        2
  •  2
  •   Frightera    3 年前

    我写了一段很好的代码,它最多可以用两位小数。

    代码:

    String gpsData;
    String LATval = "######";
    String LNGval = "######";
    char inChar;
    String gpsData;
    String latt;
    String la;
    String lonn;
    String lo;
    float lattt;
    float lonnn;
    int latDeg;
    int lonDeg;
    float latMin;
    float lonMin;
    float latttt;
    float lonnnn;
    String sGPRMC;
    void setup() {
       Serial.begin(9600);
    }
    
    void loop() {
    
      while (Serial.available()) {
        inChar = Serial.read();
        gpsData += inChar;
        if (inChar == '$') {
          gpsData = Serial.readStringUntil('\n');
          break;
        }
      }
    
      Serial.println(gpsData);
      sGPRMC = gpsData.substring(0, 5);
      if (sGPRMC == "GPRMC") {
        Serial.flush();
        latt = gpsData.substring(18, 28);
        la = gpsData.substring(29, 30);
        lonn = gpsData.substring(31, 42);
        lo = gpsData.substring(43, 44);
        Serial.print("latt:");
        Serial.println(latt);
        Serial.print("la:");
        Serial.println(la);
        Serial.print("lonn:");
        Serial.println(lonn);
        Serial.print("lo:");
        Serial.println(lo);
        lattt = latt.toFloat();
        lonnn = lonn.toFloat();
        Serial.print("lattt:");
        Serial.println(lattt);
        Serial.print("lonnn:");
        Serial.println(lonnn);
    
        if (la == "N" and lo == "E") {
    
          latDeg = float(int(lattt / 100));
          latMin = float(lattt - (latDeg * 100));
          latMin = latMin / 60;
            
          lonDeg = float(int(lonnn / 100));
          lonMin = float(lonnn - (lonDeg * 100));
          lonMin = lonMin / 60;
              
          latttt = latDeg + latMin;
          lonnnn = lonDeg + lonMin;
          LATval = String(latttt);
          LNGval = String(lonnnn);
          Serial.print("latDeg:");
          Serial.println(latDeg);
          Serial.print("latMin:");
          Serial.println(latMin);
          Serial.print("lonDeg:");
          Serial.println(lonDeg);
          Serial.print("lonMin:");
          Serial.println(lonMin);
          Serial.print("LATval:");
          Serial.println(LATval);
          Serial.print("LNGval:");
          Serial.println(LNGval);
        }
      }
    }
    
        3
  •  1
  •   weyhei    10 年前

    我在互联网上搜索过,最好的答案是使用Arduino的“TinyGPS++”库。几乎所有与GPS相关的代码都已包含在库中。

        4
  •  0
  •   whatnick    10 年前

    您可以使用 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; }
    
        5
  •  0
  •   D.Meghanath Chary    8 年前

    试试这个可以帮你

    #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);
    }