- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我需要为我正在开发的板(Cubietruck with armbian debian jessie 8.0)创建一个 c gps nmea 解析器。根据我在互联网上找到的几个例子,我得出以下结论:
#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 可以实现。我增加了 sleep 时间,但没有任何变化。有谁知道我可以做些什么来实现正确的解析?
最佳答案
您的解析思路似乎不错,但实现起来有一些问题。
我多年前写了一个 gps nmea 解析器,据我所知,以 "\r\n"
结尾的行,似乎也是这种情况,因为对于这一行
printf("|%s| \n", nmea_line);
你得到
| GPVTG,,T,,M,,N,,K,N*2C
如果你把它改成
printf(">%s| \n", nmea_line);
你很可能会看到
< GNGNS,,,,,,NNN,,,,,,*1D
第二个问题是您正在以一种可重入的方式使用 strtok
。在在循环的开始,您执行 nmea_line = strtok(read_buffer, "\n");
。然后你去解析该行并进入一个新的循环。然后你做东西线char *token = strtok(nmea_line, ",");
通过这样做 strtok
忘记了有关第一次通话的信息。
在所有操作结束时再次nmea_line = strtok(NULL, "\n");
,但是这个NULL
适用于哪个strtok
?根据输出你永远不会知道,但它肯定不会与 nmea_line = strtok(read_buffer, "\n");
匹配。
幸运的是 strtok
有一个可重入版本:strtok_r
man strtok
#include <string.h>
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);DESCRIPTION
The
strtok()
function breaks a string into a sequence of zero or more nonempty tokens. On the first call tostrtok()
, the string to be parsed should be specified instr
. In each subsequent call that should parse the same string,str
must beNULL
.[...]
The
strtok_r()
function is a reentrant versionstrtok()
. The saveptr argument is a pointer to achar*
variable that is used internally bystrtok_r()
in order to maintain context between successive calls that parse the same string.
例子:
char line[] = "a:b:c,d:e:f,x:y:z";
char *s1, *s2, *token1, *token2, *in1, *in2;
in1 = line;
while(token1 = strtok_r(in1, ",", &s1))
{
in1 = NULL; // for subsequent calls
in2 = token1;
printf("First block: %s\n", token1);
while(token2 = strtok_r(in2, ":", &s2))
{
in2 = NULL; // for subsequent calls
printf(" val: %s\n", token2);
}
}
输出:
First block: a:b:c
val: a
val: b
val: c
First block: d:e:f
val: d
val: e
val: f
First block: x:y:z
val: x
val: y
val: z
我看到的另一个问题是:
while(...)
{
read(fd, &read_buffer,1000);
nmea_line = strtok(read_buffer, "\n");
}
read
函数与fgets
不同,它不读取字符串,而是读取字节。那意味着 read
不关心它在读什么。如果顺序恰好是与 ASCII 表的值匹配的值序列,它不会添加'\0'
- 读取缓冲区中的终止字节。这是一个问题,因为你正在使用需要有效字符串的函数。如果读取输入不包含换行符,strtok
将继续读取,直到找到 '\0'
并且如果该字节不存在,它将读取超出限制。这是未定义的行为。
这样做的第二个问题是 read
再次不关心你准备好的字节,你不是在读行,你准备好了 1000 字节可能包含也可能不包含字符串的内存块。很有可能该 block 不包含字符串,因为 /dev/ttyACM2
将生成一个无尽的流,永远不会向用户发送 '\0'
。
我会使用 fgets
获取一行并解析它,然后再获取另一行,然后很快。因为您只有文件描述符,所以您应该使用:
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);The
fdopen()
function associates a stream with the existing file descriptor,fd
. Themode
of the stream (one of the values"r"
,"r+"
,"w"
,"w+"
,"a"
,"a+"
) must be compatible with the mode of the file descriptor. The file position indicator of the new stream is set to that belonging tofd
, and the error and end-of-file indicators are cleared. Modes"w"
or"w+"
do not cause truncation of the file. The file descriptor is not dup'ed, and will be closed when the stream created byfdopen()
is closed. The result of applyingfdopen()
to a shared memory object is undefined.
所以我会这样做:
FILE *f = fopen(fd, "r");
// the gps nmea lines are never that long
char line[64];
char *t1_save;
while(fgets(line, sizeof line, f))
{
// get rid of \r\n
line[strcspn(line, "\r\n")] = 0;
parser = strstr(line, "$GPRMC");
if(parser)
{
// do the parsing
}
...
}
在这个版本中你甚至不需要 strtok_r
因为你不需要嵌套 strtok
调用。
编辑
我之前错过了一件事:
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[])
{
...
while(end_of_loop == 0)
{
}
}
根据你的编译器的优化,你最终会陷入无穷无尽的循环,即使在按下 Ctrl+C 之后。编译器可能将 while
循环优化为 while(1)
,因为在 main
中 end_of_loop
变量永远不会改变,因此没有必要总是检查该值。
当试图用捕获信号停止循环时,最好至少声明变量为 volatile
,这样编译器就不会优化该变量离开。大多数情况下(参见 1、2)最好的方法是:
volatile sig_atomic_t 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);
}
}
关于来自linux串口的C Gps nmea解析器不解析读取缓冲区的最后一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48608805/
这个问题在这里已经有了答案: Possible to make an event handler wait until async / Promise-based code is done? (2
我经常有多个运行的进程(R,Python,eshell/shell),对于每个进程,我经常都有一个相关的脚本,可以从中发送摘要。为此,我通常将每个框架垂直地分成两个窗口,以便脚本文件(例如.py)位于
如何修改 emacs 在关闭缓冲区后选择要显示的缓冲区的方式? 当我有多个列显示相同的缓冲区,然后在其中一个缓冲区中打开另一个文件,然后关闭新打开的缓冲区时,它不会切换回前一个缓冲区,而是切换到另一个
如何将 ex 命令复制到剪贴板或粘贴到缓冲区? 在 Windows 上使用 gvim。 最佳答案 windows剪贴板可以通过the buffer + 访问.因此,可以使用 + 将剪贴板粘贴为前命令。
在 javascript 中如何以比以下更简单的方式获取 b 缓冲区? var num=6553599 var a = new Buffer(4); a.writeInt32LE(num)
每次我在 Google 上搜索有关 OpenGL 编程的文章时,我都会找到一些文章,但似乎所有文章都提到了着色器和缓冲区。那些是什么?你能解释其中的一些吗: 深度缓冲区 模板缓冲区 像素着色器 帧缓冲
我有java考试,当我学习时,我看到了这个练习,我尝试解决它,但我发现一些困难,所以请帮助我考虑实用程序中方法的以下注释、 header 和部分代码名为 Atbash 的加密类。 /**
每次我在 Google 上搜索有关 OpenGL 编程的文章时,我都会找到一些文章,但似乎所有文章都提到了着色器和缓冲区。那些是什么?你能解释其中的一些吗: 深度缓冲区 模板缓冲区 像素着色器 帧缓冲
对于每个属性使用跨步顶点缓冲区与紧密打包缓冲区有何优缺点?我的意思是例如: 步幅:xyzrgb xyzrgb xyzrgb 紧:xyzxyzxyz rgbrgbrgb 乍一看,使用步幅时您似乎可以轻松
我正在尝试将文本文件中每行的数字读取到 ArrayList 中。当我执行以下函数时,它总是跳过最后一个元素。有人可以帮我吗?因为我在这里没有遇到问题,因为它读取直到缓冲区为空,所以他应该在到达 Fil
#include #include int main () { time_t time_raw_format; struct tm * ptr_time; char *buff
基本上我有一个包含不同类型数据的自定义结构。例如: typedef struct example_structure{ uint8_t* example_1[4]; int example_2[4];
我之前的列表实现是一个简单的 LinearLayout,位于一个装满我的项目的 ScrollView 中。 我切换到 ListView 的 Android 实现以简单地使用 CursorAdapter
我想创建一个可变长度的输入事件窗口/缓冲区,当它接收到额外的事件时会变长。 这是为了实现“键入时搜索”功能。我想捕获点击,但为了不给服务器造成压力,我想明智地进行服务调用。 我想到的逻辑是缓冲击键,从
我想将 yuv420P 像素写入缓冲区而不是二进制文件。假设我在指针中存储了 luma 、 Cb 和 Cr。 luma = output_pixel.luma; cb = output_pixel.c
我想在 Go 中构建一个支持多个并发读取器和一个写入器的缓冲区。所有写入缓冲区的内容都应由所有读者读取。允许新读者随时加入,这意味着已经写入的数据必须能够为迟到的读者回放。 缓冲区应满足以下接口(in
本文转载自微信公众号「小明菜市场」,作者小明菜市场。转载本文请联系小明菜市场公众号。 前言 Java NIO 需要理解的主要有缓冲区,通道,选择器,这三个主要的部分。 基础
一 点睛 NIO,可以称为 New IO 或 Non Blocking IO,是在 JDK 1.4 后提供的新 API。传统的I/O 是阻塞式的 I/O、面向流的操作;而 NIO 是非阻塞 I/O 、
我正在寻找一种切换到包含搜索文本的缓冲区的方法。 例如。如果我打开了 100 个缓冲区,我想切换到一个包含 'fooBar = 1' 的缓冲区 最佳答案 我写了一个 Vim 插件来做到这一点:buff
我正在尝试将提取的视频帧(我使用 ffmpeg)推送到 FFMPEG 缓冲区中。我已经查看了 ffmpeg 的缓冲区源文件,例如 buffersrc.c 和 fifo.c,以确定我是否可以这样做,但我
我是一名优秀的程序员,十分优秀!