GPS数据处理——字符串编程练习(C语言)

题目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,150706,,,A*50

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


    字段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。
提示:^运算符的作用是异或。将$和*之间所有的字符做^运算(第一个字符和第二个字符异或,结果再和第三个字符异或,依此类推)之后的值对65536取余后的结果,应该和*后面的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了错误。注意这个十六进制值中是会出现A-F的大写字母的。
现在,你的程序要读入一系列GPS输出,其中包含$GPRMC,也包含其他语句。在数据的最后,有一行单独的
    END
表示数据的结束。

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


你的程序一定会读到一条有效的$GPRMC语句。

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

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

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

输出样例:

10:48:13


源程序(仅供参考):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>

#define ARRAY_SIZE 10
#define ARRAY_LEN 150

#define CLEAR(x)    memset(&(x), 0, sizeof(x))

/*
********************************************
函数:将一个字符串的第m位到第n位拷贝到另一个字符串中(m<n)
参数:
    @source:原始字符串
    @des:目标字符串
    @start:从第m位开始
    @end:到第n位结束
返回值:第m位的地址
********************************************
*/
char* copyStrFromTo(char* source, char* des, int start, int end)
{   
    int i=0; 
    
    if(!(source+start) || !(source+end) || (start >= end)) return NULL;
    
    for(i = 0; i <= (end - start); i++)
    {
        *(des+i) = *(source + start + i);   
    }   
    
    return (source+start);
}   


int main(int argc, char *argv[])
{
    int i=0, k=0; 
    int right_verify = 0; 
    char verify[2];                                //record verify hex data, use sprintf to change
    char *s_start, *s_end, s_verify[2], *xor_verify, *pstr;
    char UTC_time[10], *BJ_time = NULL;
    char s_hh[2]={'\0', '\0'}, s_mm[2]={'\0', '\0'}, s_ss[2]={'\0', '\0'};            //hhmmss.sss
    int d_hh, d_mm, d_ss;
    char *res = NULL;
    char *inputs[ARRAY_SIZE];
    
    while(1)                                    //read the line context is "END" or not
    {
        inputs[i] = (char *) malloc(ARRAY_LEN * sizeof(char));
        res = fgets(inputs[i], 500, stdin);     //read every line   
        if(strstr(inputs[i], "END") == 0)        //find string "END" from the input
        {
            i++;
            continue;
        }
        else
            break; 
    }  
    
    putchar('\n');
    for(int j=0; j<i; j++)                        //verify every input 
    {
        fputs(inputs[j], stdout);
        
        s_start = strstr(inputs[j], "$");
        s_end = strstr(inputs[j], "*");
        s_verify[0] = *(s_end+1) ;
        s_verify[1] = *(s_end+2) ;    
            
        xor_verify = s_start+1;
        for(pstr = s_start+2; pstr < s_end; pstr++)
        {
            *xor_verify ^= *pstr;                    //xor the key information
        }
        printf("xor_verify = %x\n", *xor_verify);
        
        sprintf(verify, "%x", (int)*xor_verify);     //sprintf hex to char
        
        if((atoi(verify) == atoi(s_verify)) && (strstr(inputs[j], ",A,")))    
        {
            printf("verify right !\n");
            
            while(',' != *(res = inputs[j]++));            //query the first character ',' 
            for(k=0, pstr = res+1; pstr <= res+10; pstr++)    //choose the next 10 char
            {
                UTC_time[k++] = *pstr;
            }
                
            
            right_verify++;                        //record the right input  
        }
        else
        {
        printf("verify error !\n"); 
        }  
        putchar('\n');
    }
    
    printf("UTC_time = %s\n", UTC_time); 
     
    res = copyStrFromTo(UTC_time, s_hh, 0, 1);       //check the result of res
    res = copyStrFromTo(UTC_time, s_mm, 2, 3);  
    res = copyStrFromTo(UTC_time, s_ss, 4, 5);  
    
    d_hh = atoi(s_hh) + 8;    
    if(d_hh >= 24) d_hh = d_hh - 24;
    if(d_hh < 10)
        sprintf(s_hh, "0%d", d_hh);
    else
        sprintf(s_hh, "%d", d_hh);
    
    d_mm = atoi(s_mm);
    if(d_mm < 10)
        sprintf(s_mm, "0%d", d_mm);
    else
        sprintf(s_mm, "%d", d_mm);
    
    d_ss = atoi(s_ss);
    if(d_ss < 10)
        sprintf(s_ss, "0%d", d_ss);
    else
        sprintf(s_ss, "%d", d_ss); 
    
    // printf("%d:%d:%d\n\n", d_hh, d_mm, d_ss);
    
    printf("BeiJing_Time = %s:%s:%s\n\n", s_hh, s_mm, s_ss);
    
    return 0;
}

运行结果:



分析:

(1)题目要求是多条GPS语句输入,每条语句以回车换行结束,最后一行是END三个大写字母,因此,我们可以使用fgets函数来接受标准输入。(#include <stdio.h>)
char * fgets(char* s,int size,FILE * stream);
说明:文件指针stream,对于标准输入输出分别取stdin和stdout
返回值:若成功则返回s指针,返回NULL则表示有错误发生。


(2)对于单行字符串的存储,我们首先会想到字符数组(char a[]),或者是字符型指针变量(char*),对于多行字符串数据,当然可以选用二维数组,那么就需要预先假设这个数组空间很大,在这里我们不用这个办法,选用指针数组(char *a[])存储,[]的优先级比*高,因此,这表示一个数组,数组中的每个元素类型都为char*型,需要存储字符串时,就使用malloc函数分配一块空间,用a[i]指向这一块存储空间。可见,在内存中,每个字符串存储空间之间并不是连续的。(#include <malloc.h>)

(3)判断是否输入结束,我们还需要判断每个字符串的内容,最后一行是END三个大写字母,可以使用strstr函数查找或者是strcmp函数比较。(#include <string.h>)
int strcmp(const char *s1,const char *s2);
返回值:若参数s1和s2字符串相同则返回0;
        s1若大于s2则返回大于0的值;
        s1若小于s2则返回小于0 的值。

char *strstr(const char *haystack,const char *needle);
返回值:返回指定字符串第一次出现的地址,否则返回0。

(4)验证校验和:我们需要查找字符'$'和'*',这里就有多种方法,1.已知字符'$'是第0个,'*'倒数第3个,直接就可以查找;2.利用for循环遍历每个字符,标记位置即可;3.用strchr或strstr查找函数,同样标记'$'和'*'出现的位置。找到了字符串开始和结束位置,依次对每个字符进行异或就可以得到校验和,我们自己计算出来的校验和可以是十进制或者16进制,而GPS语句最后两位数表示的检验和为16进制,因此这里可以使用格式化函数sprintf将字符转换成16进制格式化字符,再使用atoi函数将字符串转换成整数来比较,即可验证校验和。(#include <stdlib.h>)

char *strchr(const char *s, int c);

返回值:如果找到指定的字符则返回该字符所在地址,否则返回0。

int sprintf( char *str,const char * format,.........);
返回值:成功则返回参数str 字符串长度,失败则返回-1。

int atoi(const char *nptr);
返回值:返回转换后的整型数。
使用这两个函数之前自己最好简单测试下,看看适用情况

(5)提取每个字段的信息:GPS语句行中以逗号","隔开各个字段,每个字段的大小(长度)不一,我们要获得每个字段的信息,只有标记前后两个','字符。题目中要求查找字段1的时间信息,代码中使用的方法就是查找第一个字符',',而且字段0与字段1的长度都是一定的,因此设计了一个子函数来提取时间信息,将GPS语句中的字段1信息拷贝出来。
(6)UTC时间与北京时间的转换:本地时间 = UTC + 时区差




  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值