解析GPS NMEA字符串的代码

时间:2013-11-08 20:42:04

标签: c parsing gps arduino nmea

enter image description here我正在尝试使用Arduino uno及以下代码解析传入的GPGGA NMEA GPS字符串。 我想要做的是,我只使用GPGGA NMEA字符串来获取纬度,经度和海拔高度的值。在我的下面的代码中,我已经进行了某些检查以检查传入的字符串是否为GPGGA,然后存储数组中的其他字符串,可以进一步解析surt strtok函数,所有3个GPS坐标都可以轻松找到。

但我无法弄清楚如何只存储GPGGA字符串而不是另外的字符串。我正在使用for循环,但它无效。

我没有尝试使用任何库。我遇到了某些existing codes like this.

Here is the GPGGA string information link

我想尝试以下功能 i)检查传入的字符串是否为GPGGA ii)如果是,则在数组中将以下字符串存储到EOL或upto *(后跟数组的校验和),数组长度是可变的(我无法找到解决方案) iii)然后解析存储的数组(这已完成,我尝试使用不同的数组)

 #include <SoftwareSerial.h>

    SoftwareSerial mySerial(10,11);  // 10 RX / 11 TX

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

    void loop()
    {
    uint8_t x;
    char gpsdata[65];

    if((mySerial.available()))
    {
    char c = mySerial.read();
    if(c == '$')
      {char c1 = mySerial.read();
       if(c1 == 'G')
         {char c2 = mySerial.read();
          if(c2 == 'P')
            {char c3 = mySerial.read();
             if(c3 == 'G')
               {char c4 = mySerial.read();
                if(c4 == 'G')
                   {char c5 = mySerial.read();
                    if(c5 == 'A')
                       {for(x=0;x<65;x++)
                        { 
                        gpsdata[x]=mySerial.read();


    while (gpsdata[x] == '\r' || gpsdata[x] == '\n')
                    {
                    break;
                    }

                        }

                       }
                       else{
                          Serial.println("Not a GPGGA string");
                        }
                   }
               }

            }     

         }

      }

    }

    Serial.println(gpsdata);
    }

编辑1: 考虑到Joachim Pileborg,在代码中编辑for循环。

我正在添加一张图片来显示未定义的代码输出。

代码输入:

$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43
$GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45

7 个答案:

答案 0 :(得分:3)

在快速检查了NMEA 0183协议上的链接文章之后,我跳了出来:

  

<CR><LF>结束了此消息。

这意味着,您应该寻找该序列,而不是只是从串口中不加选择地读取。如果找到,你应该终止字符串,并打破循环。

此外,您可能希望将数据字符串初始化为零,以便轻松查看其中是否有任何数据要打印(使用例如strlen)。

答案 1 :(得分:0)

提供此作为支持您正在做的事情的建议......

在循环中替换所有嵌套的if()并不会有用:

编辑 添加全局字符串以将myString复制到捕获的内容

char globalString[100];//declare a global sufficiently large to hold you results


void loop() 
{
    int chars = mySerial.available();
    int i;
    char *myString;
    if (chars>0)
    {
        myString = calloc(chars+1, sizeof(char));
        for(i=0;i<chars;i++)
        {
            myString[i] = mySerial.read();
            //test for EOF
            if((myString[i] == '\n') ||(myString[i] == '\r'))
            {
                //pick this...
                myString[i]=0;//strip carriage - return line feed(or skip)
                //OR pick this... (one or the other. i.e.,I do not know the requirements for your string)
                if(i<chars)
                {
                    myString[i+1] = mySerial.read() //get remaining '\r' or '\n'
                    myString[i+2]=0;//add null term if necessary
                }

                break;
            }
        }
        if(strstr(myString, "GPGGA") == NULL)
          {
               Serial.println("Not a GPGGA string");   
               //EDIT
               strcpy(globalString, "");//if failed, do not want globalString populated
          }
          else
          {    //EDIT
               strcpy(globalString, myString);
          }

    }
    //free(myString) //somewhere when you are done with it
}

现在,来自mySerial.available() 返回值 告诉您 完全 要读取的字节数,你可以阅读整个缓冲区,并测试所有的有效性。

答案 2 :(得分:0)

我有一个项目需要从同一个句子中提取相同的信息。 我从日志文件中得到了这个

import serial
import time
ser = serial.Serial(1)

ser.read(1)
read_val = ("nothing")

gpsfile="gpscord.dat"
l=0

megabuffer=''
def buffThis(s):
        global megabuffer
        megabuffer +=s
def buffLines():
        global megabuffer
        megalist=megabuffer.splitlines()
        megabuffer=megalist.pop()
        return megalist

def readcom():
        ser.write("ati")
        time.sleep(3)
        read_val = ser.read(size=500)
        lines=read_val.split('\n')
        for l in lines:
                if l.startswith("$GPGGA"):
                        if l[:len(l)-3].endswith("*"):
                                outfile=open('gps.dat','w')
                                outfile.write(l.rstrip())
                                outfile.close()

readcom()

while 1==1:
    readcom()

answer=raw_input('not looping , CTRL+C to abort')

结果如下: gps.dat

$GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B

答案 3 :(得分:0)

只需添加这些代码即可。使用引脚8和9作为tx和rx

Main.c gps.h gps.c

答案 4 :(得分:0)

只需添加这些代码即可。使用引脚8和9作为tx和rx

  1. gps.h
  2. main.c
  3. gps.c

答案 5 :(得分:0)

每次读取字符串时都使用“malloc”会产生大量的计算开销。 (并没有看到相应的free()函数调用。没有它,你永远不会得到那个内存,直到程序终止或系统内存不足。)只需选择你需要的最长字符串的大小,添加10到它,并声明你的字符串数组大小。设置一次并完成。

有几个C函数可以从字符串中获取子字符串,使用昏迷的strtok()可能是开销最小的。

您使用的是嵌入式微控制器。保持小,保持头顶下降。 :)

答案 6 :(得分:0)

您可以使用C库libnmea中的一些功能。 Theres用于通过逗号将句子拆分为值,然后解析它们。