C语言 GPS NMEA 字符串的解析代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19868156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parsing code for GPS NMEA string
提问by shailendra
i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code.
What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out.
我正在尝试使用 Arduino uno 和以下代码解析传入的 GPGGA NMEA GPS 字符串。我想要做的是我只使用 GPGGA NMEA 字符串来获取纬度、经度和高度的值。在我下面的代码中,我进行了某些检查以检查传入的字符串是否为 GPGGA,然后存储数组中的其他字符串可以使用 strtok 函数进一步解析,并且可以轻松找到所有 3 个 GPS 坐标。
But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working.
但我无法弄清楚如何只存储 GPGGA 字符串而不是进一步的字符串。我正在使用 for 循环,但它不起作用。
I am not trying to use any library.I had came across certain existing codes like this.
我不想使用任何库。我遇到过某些这样的现有代码。
Here is the GPGGA string information link
i am trying to have following functionlity i) Check if incoming string is GPGGA ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this) iii) Then parse the stored array(this is done, i tried this with a different array)
我正在尝试具有以下功能 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);
}
Edit 1: Considering Joachim Pileborg, editing the for loop in the code.
编辑 1:考虑 Joachim Pileborg,编辑代码中的 for 循环。
I am adding a pic to show the undefined output of the code.
我正在添加一张图片来显示代码的未定义输出。
Input for the code:
代码输入:
$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
回答by Some programmer dude
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me:
在快速检查了有关 NMEA 0183 协议的链接文章后,我突然想到了这一点:
<CR><LF>ends the message.
<CR><LF>结束消息。
This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop.
这意味着,您应该寻找该序列,而不是从串行端口随意读取。如果找到,您应该终止该字符串,并跳出循环。
Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
此外,您可能希望对开始的数据字符串进行零初始化,以便轻松查看其中是否确实有任何要打印的数据(使用 eg strlen)。
回答by ryyker
Offering this as a suggestion in support of what you are doing...
将此作为支持您正在做的事情的建议......
Would it not be useful to replace all of the nested if()s in your loop with something like:
将if()循环中的所有嵌套s替换为以下内容是否有用:
EDITadded global string to copy myString into once captured
编辑添加了全局字符串以将 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
}
Now, the return valuefrom mySerial.available()tells you exactlyhow many bytes to read, you can read the entire buffer, and test for validity all in one.
现在,from的返回值mySerial.available()告诉您要读取的确切字节数,您可以读取整个缓冲区,并一次性测试有效性。
回答by nickolastd21
I have a project that will need to pull the same information out of the same sentence. I got this out of a log file
我有一个项目需要从同一个句子中提取相同的信息。我从日志文件中得到了这个
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')
The result is this: gps.dat
结果是这样的: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
回答by AMPS
回答by AMPS
回答by skypuppy
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done.
每次读取字符串时都使用“malloc”会产生大量的计算开销。(并且没有看到相应的 free() 函数调用。没有它,在程序终止或系统内存不足之前,您永远不会获得该内存。)只需选择您将需要的最长字符串的大小,将 10 添加到它,并声明您的字符串数组大小。设置一次就完成了。
There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead.
有几个 C 函数可以从字符串中获取子字符串,使用 coma 的 strtok() 可能是最少的开销。
You are on an embedded microcontroller. Keep it small, keep overhead down. :)
您使用的是嵌入式微控制器。保持小规模,降低开销。:)
回答by Hyman-xtal
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
您可以使用 C 库libnmea 中的一些函数。有一些函数可以用逗号将一个句子分割成多个值,然后解析它们。

