【发布时间】:2018-02-04 13:56:48
【问题描述】:
我需要为我正在开发的电路板创建一个 c gps nmea 解析器(Cubietruck with armbian debian jessie 8.0)。根据我在互联网上找到的几个例子,我得出以下结论:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
int fd = -1;
int end_of_loop= 0;
void sig_handler(int sig)
{
if(sig == SIGINT)
{
printf("GPS parsing stopped by SIGINT\n");
end_of_loop = 1;
close(fd);
}
}
int main(int argc, char *argv[])
{
struct termios newt;
char *nmea_line;
char *parser;
double latitude;
float longitude;
float altitude;
signal(SIGINT, sig_handler);
fd = open("/dev/ttyACM2", O_RDWR | O_NONBLOCK);
if (fd >= 0)
{
tcgetattr(fd, &newt);
newt.c_iflag &= ~IGNBRK;
newt.c_iflag &= ~(IXON | IXOFF | IXANY);
newt.c_oflag = 0;
newt.c_cflag |= (CLOCAL | CREAD);
newt.c_cflag |= CS8;
newt.c_cflag &= ~(PARENB | PARODD);
newt.c_cflag &= ~CSTOPB;
newt.c_lflag = 0;
newt.c_cc[VMIN] = 0;
newt.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &newt);
usleep(100000);
while(end_of_loop == 0)
{
char read_buffer[1000];
read(fd, &read_buffer,1000);
//printf("|%s|", r_buf);
nmea_line = strtok(read_buffer, "\n");
while (nmea_line != NULL)
{
parser = strstr(nmea_line, "$GPRMC");
if (parser != NULL)
{
printf("|%s| \n", nmea_line);
char *token = strtok(nmea_line, ",");
int index = 0;
while (token != NULL)
{
if (index == 3)
{
latitude = atof(token);
printf("found latitude: %s %f\n", token, latitude);
}
if (index == 5)
{
longitude = atof(token);
printf("found longitude: %s %f\n", token, longitude);
}
token = strtok(NULL, ",");
index++;
}
}
parser = strstr(nmea_line, "$GPGGA");
if (parser != NULL)
{
printf("|%s| \n", nmea_line);
char *token = strtok(nmea_line, ",");
int index = 0;
while (token != NULL)
{
if (index == 13)
{
altitude = atof(token);
printf("found altitude: %s %f\n", token, altitude);
}
token = strtok(NULL, ",");
index++;
}
}
printf("|%s| \n", nmea_line);
nmea_line = strtok(NULL, "\n");
}
usleep(500000);
}
close(fd);
return 0;
}
else
{
printf("Port cannot be opened");
return -1;
}
}
目前我测试没有 GPS 定位的负面情况。 这种情况下串行端口的输出是每次读取的:
$GNGNS,,,,,,NNN,,,,,,*1D
$GPVTG,,T,,M,,N,,K,N*2C
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GNGSA,A,1,,,,,,,,,,,,,,,*00
$GPGGA,,,,,,0,,,,,,,,*66
$GPRMC,,V,,,,,,,,,,N*53
当我运行代码时,我得到了 GPGGA 的解析打印输出,而不是 GPRMC:
GNGNS,,,,,,NNN,,,,,,*1D
| GPVTG,,T,,M,,N,,K,N*2C
| GPGSA,A,1,,,,,,,,,,,,,,,*1E
| GNGSA,A,1,,,,,,,,,,,,,,,*00
| GPGGA,,,,,,0,,,,,,,,*66
|$GPGGA|
| GNGNS,,,,,,NNN,,,,,,*1D
| GNGNS,,,,,,NNN,,,,,,*1D
| GPVTG,,T,,M,,N,,K,N*2C
| GPGSA,A,1,,,,,,,,,,,,,,,*1E
| GNGSA,A,1,,,,,,,,,,,,,,,*00
| GPGGA,,,,,,0,,,,,,,,*66
|$GPGGA|
我认为这与 GPRMC 在最后一行的事实有关,当执行 nmea_line = strtok(NULL, "\n"); 时 nmea_lime 变为 NULL。我用strcat 在read_buffer 上添加了一个虚拟行,但没有成功。
我打印了索引,我发现对于 GPGGA,只有 index = 3 被实现。我增加了睡眠时间,但没有任何变化。
有谁知道我可以做些什么来实现正确的解析?
【问题讨论】:
-
不要使用 strtok()。它无法识别连续的分隔符。
-
@wildplasser 我可以用什么代替?
-
你可以使用 strchr(),或者手动比较字符。最好是构建一个小型 DFA。(您的代码已经非常接近 DFA)
-
另外:
read(fd, &read_buffer,1000);不考虑换行符(并且缓冲区不会被 NUL 终止!)。您还必须找到'\n'字符才能同步到行尾/下一行的开头。
标签: c linux serial-port nmea