C语言 GPS数据处理

slmsl1lt  于 2022-12-26  发布在  其他
关注(0)|答案(1)|浏览(187)

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语言的新手,请给我更多的指导。)

rqmkfv5c

rqmkfv5c1#

这可能无法解决您的问题。我没有足够的勇气在我的计算机上运行scanf()说明符...
但是,有一个明显的bug需要解决:

char *check(char *a)
{
    char *c = a;
    char *p[20] = {NULL};

    /* abridged code */

    if (cal % 65536 == answer)
    {
        for (int i = 0; p[i] != NULL; i++)
            p[i] = i == 0 ? strtok(c, ",") : strtok(NULL, ",");

for()循环怎么可能运行呢?20个指针中的任何一个都还没有改变呢?
下面的更干净,可能更接近您的预期。

int i = 0;
for( char *cp = c; (cp = strtok( cp, ",") ) != NULL; cp = NULL )
    p[i++] = cp;

正如所说,这可能不能解决你的问题。只是一个圣诞礼物...:-)

    • 更新日期:**

下面的代码以类似的方式工作,但稍微有点神秘。

int i;
for( p[i = 0] = c; (p[i] = strtok( p[i], "," ) ) != NULL; p[++i] = NULL ) {}

这个版本的优点之一是指针数组中最后一个赋值的元素将被设置为NULL。如果指针数组是持久的(堆?),并且函数需要返回数组(实际上是地址)和元素数,则可以使用此方法。NULL与标记C字符串结束的'\0'一样有用。

相关问题