NMEA-0183协议是美国国家航海电子协会制定的一套通信协议(NMEA),以建立统一的BTCM(海事无线电技术委员会)标准(全球定位系统)导航设备。GPS接收器将位置、速度和其它信息传送到PC,PDA等设备,GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息传送给PC机,PDA等设备通过串口按NMEA-0183协议规范进行通信。
NMEA-0183协议是GPS接收机应该遵循的标准协议,也是GPS接收机上应用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件和导航软件都遵循或至少兼容该协议。
NMEA-0183协议中定义了许多语句,但唯一常用或最广泛兼容的语句是$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。
$GPRMC语句之一具有以下格式。
美国通用价格管理委员会,024813.640,澳大利亚元,3158.4608,新西兰元,11848.3737,澳大利亚元,10.05,324.27,150706,澳大利亚元 * 50
这里的整个语句是一个文本行,该行用逗号"",由各个字段分隔,每个字段的大小(长度)各不相同,这里的例子只是一种可能,并不假设字段的大小与上面的例句相同。
字段0:$GPRMC,语句ID,表示语句是推荐的最小特定GPS/TRANSIT数据(RMC)推荐的最小定位信息
字段1:UTC时间,hhmmss. sss格式
字段2:状态,A =已定位,V =未定位
字段3:纬度ddmm. mmmmm,度分格式(前导数字不足以构成0)
字段4:纬度N(北)或S(南)
字段5:经度dddmm. mmmm,度数拆分格式(0表示前导数字不足)
字段6:经度E(东)或W(西)
字段7:速度、结、结
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导数字不足以构成0)
字段11:磁偏角方向,E =东W =西
字段16:检查值
这里,""是校验和标识符,后面两位是校验和,表示"$"和""之间所有字符(不包括这两个字符)十六进制值的异方差值,上句校验和十六进制为50,十进制为80。
提示:^运算符的作用类似于iso-or。对$和 * 之间的所有字符执行^运算的结果(第一个字符和第二个字符是iso-OR,第三个字符的结果是iso-OR,依此类推)应该等于值65536后的 * 后面的两个十六进制数字的值,否则语句在传输中出错。注意这个十六进制值是一个包含大写字母A-F的值。
现在,程序将读入一系列包含$GPRMC和其他语句的GPS输出。在数据的末尾,有一个单独的行
结束
以指示数据的结束。
你的程序要从中找到$GPRMC语句,计算校验和,找到校验和正确且字段2表示已经定位的语句,从中计算时间,并转换为北京时间,数据中一次会有多条$GPRMC语句,最后一条语句得到的北京时间将作为结果输出。
程序必须读取有效的$GPRMC语句。
输入格式:
多个GPS语句,每个语句都以回车符结束。最后一行是三个大写字母的END。
输出格式。
6-数字时间,表示为。
时:分:秒
其中hh是两位数的小时,如果小于两位数,前面加0; mm是两位数的分钟,小于两位数时加0; ss是两位数的秒,对于小于两位数,在其前面加0。
样本输入。
美国通用价格管理委员会,024813.640,澳大利亚元,3158.4608,新西兰元,11848.3737,澳大利亚元,10.05,324.27,150706,澳大利亚元 * 50
结束
我根据这个问题写了一个代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char a[100]; // 喂入的数据数组
char *time; // 储存时间
int h, m, s, flag = 0;
char *check(char *a);
int main()
{
while (1)
{
memset(a, 0, sizeof(a));
scanf("%s", a);
if (a[0] == 'E')
break;
time = check(a);
if (time == NULL)
{
continue;
}
else
{
sscanf(time, "%02d%02d%02d.", &h, &m, &s);
h = h + 8; // 换算北京时间
h = h > 24 ? h - 24 : h;
flag = 1;
} // 检查返回值
}
if (flag)
printf("%02d:%02d:%02d\n", h, m, s);
else
printf("No vaild data\n");
return 0;
}
char *check(char *a)
{
char *c = a;
char *p[20] = {NULL};
char j[100]; // 前检测数组
int answer; // 校验值
int cal = 0; // 计算值
sscanf(c, "$%[^*]*%x", j, &answer); // 拆封
//printf("%s %x",j,answer);
for (int s = 0; j[s] != 0; s++)
{
cal ^= j[s];
}
if (cal % 65536 == answer)
{
for (int i = 0; p[i] != NULL; i++)
p[i] = i == 0 ? strtok(c, ",") : strtok(NULL, ",");
if (strcmp(p[0], "$GPRMC") == 0 && *(p[2]+0) == 'A')
{
return p[1];
}
}
return NULL;
}
'
我想得到以下效果
/*
input sample:
$GPRMC,014600.00,A,2237.496474,N,11356.089515,E,0.0,225.5,310518,2.3,W,A*23
$GPRMC,010101.130,A,3606.6834,N,12021.7778,E,0.0,238.3,010807,,,A*6C
END
output sample:
09:01:01
*/
但当我运行代码时,
Segmentation fault appears in the 'if (strcmp(p[0], "$GPRMC") == 0 && *(p[2]+0) == 'A')' line of code
我该怎么办?(我是一个学习c语言的新手,请给我更多的指导。)
1条答案
按热度按时间rqmkfv5c1#
这可能无法解决您的问题。我没有足够的勇气在我的计算机上运行
scanf()
说明符...但是,有一个明显的bug需要解决:
for()循环怎么可能运行呢?20个指针中的任何一个都还没有改变呢?
下面的更干净,可能更接近您的预期。
正如所说,这可能不能解决你的问题。只是一个圣诞礼物...:-)
下面的代码以类似的方式工作,但稍微有点神秘。
这个版本的优点之一是指针数组中最后一个赋值的元素将被设置为NULL。如果指针数组是持久的(堆?),并且函数需要返回数组(实际上是地址)和元素数,则可以使用此方法。NULL与标记C字符串结束的'\0'一样有用。