MOOC习题:“GPS数据处理”题目个人解析(C语言)

MOOC翁凯C语言课的题目,题目如下:

GPS数据处理(6分)

题日内容:
NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置,速度等低息通过串口传送到PC机、PDA等设备。

NMEA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS接收机上使用最广泛的协议,大多数常见的GPS接收机,GPS数据处理软件,导航软件都遵守或者至少兼容这个协议。

NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。

其中$GPRMC语句的格式如下:
$GPRMC.024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706A50

这里整条语句是一个文本行,行中以逗号,隔开各个字段,每个字段的大小(长度)不一,这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。

字段0:$GPRMC, 语句ID, 表明该请句为Recommended Minimum Specific GPS/TRANSIT Data (RMC)推荐最小定位信息
字段1 :UTC时间,hhmmss.sss格式
字段2:状态,A=定位,V=未定位
字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段4:纬度N(北纬)或S(南纬)
字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段6:经度E(东经)或W(西经)
字段7:速度,节,Knots
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000-180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段16:校验值

这里,“*”为校验和识别符,其后面的两位数为校验和,代表了“$“和“*”之间所有字符(不包括这两个字符)的异或值的十六进制值。上面这条例句的校验和是十六进制的50,也就是十进制的80。

提示:“运算符的作用是异或。将S和*之间所有的字符做^运算(第一个字符和第二个字符异或,结果再和第三个字符异或,依此类推)之后的值对65536取余后的结果,应该和*后面的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了锚误,注意这个十六进制值中是会出现 A-F的大写字母的

现在,你的程序要读入一系列GPS输出,其中包含$GPRMC,也包含其他语句。在数据的最后,有一行单独的 :
END
表示数据的结束。

你的程序要从中找出$GPRMC语句,计算校验和,找出其中校验正确,并且字段2表示已定位的语句,从中计算出时间,换算成北京时间。一次数据中会包含多条$GPRMC语句,以最后一条语句得到的北京时间作为结果输出。你的程序一定会读到一条有效的$GPRMC语句。

输入格式:
多条GPS语句,每条均以回车换行结束。最后一行是END三个大写字母

输出格式:
6位数时间,表达为:
hh:mm:ss
其中,hh是两位数的小时,不足两位时前面补0;mm是两位数的分钟,不足两位时前面补0;ss是两位数的秒,不足两位时前面补0。

输入样例:
$GPRMC,024813.640A,3158.4608,N,11848.3737,E,10.05,324.27.150706,,,A*50
END

输出样例:
10:48:13

时间限制:500ms 内存限制:32000kb

唔,看完题目可以知道其实就是要求读入多行字符串,然后找出符合条件的字符串,从中抽取六位数的UTC时间,把它转化成北京时间输出。

我想的大致思路就是:外层循环读入字符串,一直到END,然后里面把读入的单行字符串进行条件判断(先是需要$GPRMC,然后校验值要正确),遇到符合条件的字符串提取UTC时间并且转化,直到最后读到END之后再输出(因为要求只输出最后符合条件的)。

代码如下,由于我是初学者,可能很多便捷的函数我都不知道,所以如果有不妥欢迎批评指正。大部分解释都在代码注释里啦。

csdn_export_md
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
void copy(char *a,char *b,int start,int end);/*函数作用:把数组b中的start至end号元素copy到数组a中。(因为我对字符串函数不太了解,所以用自己写的)*/
void transform(int *h,int *m,int *s,int );/*把的6位数int的UTC时间转化成hms的北京时间*/
int main(void)
{
    char ch[100];    /*放置读入的单行字符串*/
    int ret,h,m,s;    /*ret用来存储最后得到的六位UTC时间,hms就是结果的时分秒*/
    scanf("%s",ch);
    while (strcmp(ch,"END"))    /*ch不等于END时进入循环体*/
    {
        char type[10];    /*用来储存GPS语句格式*/
        copy(type,ch,0,5);    /*从读取到的字符串中提取GPS语句格式*/
        if (!strcmp(type,"$GPRMC")    )/*type是$GPRMC时进入*/
        {
            int i,test;    /*test用来存储读取到的字符串的异或运算值*/
            test=ch[1];    /*先让它等于$后的第一个字符*/
            for (i=2;ch[i]!='*';i++)    /*循环计算$和*之间的字符串的异或运算值*/
            {
                test=test^ch[i];
            }
            test%=65530;    /*得到读取字符串的理论校验值的十进制*/
            char xtest[10]={0};    /*用来存放test的十六进制形式*/
            sprintf(xtest,"%X",test);    /*把test的十六进制形式放入xtest*/
            char check[10]={0};    /*用于存储实际校验值*/
            copy(check,ch,i+1,i+2);    /*从字符串提取校验值*/
            if (!strcmp(xtest,check))    /*比较理论校验值和实际校验值*/
            {
                char utc[10];    /*用于存储UTC时间*/
                copy(utc,ch,7,12);    /*从字符串提取UTC时间*/
                ret=atoi(utc);    /*把字符串转化为int*/
            }
        }
        scanf("%s", ch);
    }
    transform(&h,&m,&s,ret);    /*把六位数UTC时间转化为北京时间hms*/
    printf("%02d:%02d:%02d", h, m, s);    /*输出所得北京时间*/
    return 0;
}
void copy(char *a,char *b,int start,int end)
{
    int i;
    for (i=start;i<=end;i++)    /*把字符串b中的start到end号元素复制到a*/
    {
        *(a++)=*(b+i);
    }
    *a='\0';    /*数组a加上结尾的\0才是字符串*/
}
void transform(int *h,int *m,int *s,int ret)
{
    ret/=1;
    *s=ret%100;
    ret/=100;
    *m=ret%100;
    *h=ret/100;
    *h+=8;    /*北京时间是UTC+8*/
    if (*h>=24) *h-=24;
}

唔,基本上就是酱紫啦,大部分代码解析标注在代码里啦,如果有不懂的可以问我,如果代码有不妥的地方欢迎批评指正。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注