GPS数据解析

1.摘要

  GPS模块使用串口通信,那么它的的数据处理本质上还是串口通信处理,只是GPS模块的输出的有其特定的格式,需要字符串处理逻辑来解析其含义。如何高效的处理从GPS模块接收到的数据帧,是GPS驱动设计的重点,本文使用状态机的思想来处理GPS输出的串口数据流,相对于定时从串口环形bufer取数据包然后依次解析有更高的实时性并且单片机负荷更低。

2. GPS数据协议简介

常用的GPS模块大多采用NMEA-0183 协议,目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。NMEA-0183 是美国国家海洋电子协会(National Marine Electronics Association)所指定的标准规格,这一标准制订所有航海电子仪器间的通讯标准,其中包含传输资料的格式以及传输资料的通讯协议。

GPS数据格式如下:

帧格式形如:$aaccc,ddd,ddd,?,ddd*hh(CR)(LF)

1、“$”:帧命令起始位

2、aaccc:地址域,前两位为识别符(aa),后三位为语句名(ccc)

3、ddd?ddd:数据

4、“*”:校验和前缀(也可以作为语句数据结束的标志)

5、hh:校验和,$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到

校验和后,再转换16进制格式的ASCII字符)

6、(CR)(LF):帧结束,回车和换行符

可以从串口抓数据帧:

$GPGSV,2,2,08,21,15,076,,23,52,270,,26,50,050,,27,52,179,*7D$GPRMC,132043.00,V,,,,,,,120116,,,N*7F$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30$GPRMC,133308.00,A,3949.63002,N,11616.48641,E,1.101,,120116,,,A*70

3. GPS状态机接收

一般的应用中我们最关心的数据是GPRMC,即推荐定位信息。我们常见GPS数据接收方法主要是串口中断法,串口中断一直开着,然后定时从中断中取一包数据,解析这包数据,找到定位信息,常见的主要是找到GPRMC帧。

这有几个问题。

1. 一般而言,中断数据很快,而数据处理过程会发生丢接收中断。

2. 为了避免丢数据,可以使用双buffer来处理,读数据时候就把这个buffer锁定,然后再来了中断数据就往另外一个buffer放。

3. 如果取数据时刻刚好是一个有效定位信息,那么切换到第二个buffer后,就导致一条帧分成2份,两个buffer中该数据帧都不完整。

4.代码结构不清晰,应用层收到的数据可能不是完整的一条帧。

基于以上几点,改进方法使用状态机来接收,可以每次完整的给应用层发送一帧数据。

并且不需要关闭串口中断,接收过程一直进行。

接收到一个完整帧后就往上层送一次,上层负责解释数据的含义。

下面是在stm32平台上的接收GPS数据的处理过程。

  1 //gps receive gps_state machine.
  2 #define Start 0// $
  3 #define G 1
  4 #define P 2
  5 #define R 3
  6 #define M 4
  7 #define C 5
  8 #define Data 6
  9 #define Check0 7 // *
 10 #define Check1  8// *
 11 void UART4_IRQHandler(void)
 12 {
 13     static uint8_t len = 0;
 14     static uint8_t crc = 0;
 15     static GPS_MSG_T GpsMsg;
 16     uint8_t data = 0;
 17     uint8_t tmp_flg = 0;
 18     //$GPRMC,144601.00,A,3916.72973,N,11706.60267,E,0.719,,180117,,,A*76
 19     if (USART_GetITStatus(GPS_UART, USART_IT_RXNE) != RESET)
 20     {
 21         data = (uint8_t)USART_ReceiveData(GPS_UART);
 22
 23         switch(gps_state) // find GPRMC
 24         {
 25             case Start:
 26                 if(data == ‘$‘) {
 27                     gps_state = G;
 28                     len = 0;
 29                     GpsMsg.maxLen = MaxGPSMsgLen;
 30                     memset(GpsMsg.buffer, 0, GpsMsg.maxLen);
 31                     GpsMsg.buffer[len++] = data;
 32                     crc = 0;
 33                 }
 34                 else gps_state = Start;
 35                 break;
 36             case G:
 37                 if(data == ‘G‘){
 38                     gps_state = P;
 39                     GpsMsg.buffer[len++] = data;
 40                     crc ^= data;
 41                 }
 42                 else gps_state = Start;
 43                 break;
 44             case P:
 45                 if(data == ‘P‘){
 46                     gps_state = R;
 47                     GpsMsg.buffer[len++] = data;
 48                     crc ^= data;
 49                 }
 50                 else gps_state = Start;
 51                 break;
 52             case R:
 53                 if(data == ‘R‘){
 54                     gps_state = M;
 55                     GpsMsg.buffer[len++] = data;
 56                     crc ^= data;
 57                 }
 58                 else gps_state = Start;
 59                 break;
 60             case M:
 61                 if(data == ‘M‘){
 62                     gps_state = C;
 63                     GpsMsg.buffer[len++] = data;
 64                     crc ^= data;
 65                 }
 66                 else gps_state = Start;
 67                 break;
 68             case C:
 69                 if(data == ‘C‘){
 70                     gps_state = Data;
 71                     GpsMsg.buffer[len++] = data;
 72                     crc ^= data;
 73                 }
 74                 else gps_state = Start;
 75                 break;
 76             case Data:
 77                 if(data == ‘*‘){
 78                     gps_state = Check0;
 79                     GpsMsg.buffer[len++] = data;
 80                 }
 81                 else{
 82                     gps_state = Data;
 83                     GpsMsg.buffer[len++] = data;
 84                     crc ^= data;
 85                     if(len>GpsMsg.maxLen) gps_state = Start;
 86                 }
 87                 break;
 88
 89             case Check0:
 90                 gps_state = Check1;
 91                 GpsMsg.buffer[len++] = data;
 92                 break;
 93             case Check1: //*hh
 94                 gps_state = Start;
 95                 GpsMsg.buffer[len++] = data;
 96                 if(crc == ((GpsMsg.buffer[len-2]-‘0‘)*16 + (GpsMsg.buffer[len-1]-‘0‘)))
 97                 {
 98                     GpsMsg.buffer[len++] = ‘\r‘;
 99                     GpsMsg.buffer[len] = ‘\n‘;
100                     GpsMsg.length = len;
101                     //send to gps task
102                     xQueueSendFromISR(GpsQueue, (void *) &GpsMsg, 0 );
103                 }
104       }
105
106       USART_ClearITPendingBit(GPS_UART, USART_IT_RXNE);
107     }113 }

  这段代码完成4个功能。1)串口接收数据;2)状态机切换;3)数据校验;4)把通过校验的数据发给应用层。

4. GPS数据解析

  应用层已经收到数据了,剩下的工作就是字符串解析了。如果只关注GPRMC信息的话,上面已经做了校验,出错的概率极小,那么应用层就可以直接从收到的数据帧里提取经纬度了。

  如果希望数据全部都处理,那么在串口接收部分就不能只保留GPRMC信息,应该全部都保留然后发给应用层,应用层解析数据帧。这里给出一个开源的例子,其中使用了多个c标准库字符处理函数,优点是通用性强功能完备,当然在嵌入式中可能比较占内存,如果资源紧张可以自己写该部分处理逻辑。



 10
 11 /*! \file tok.h */
 12
 13 //#include "nmea/tok.h"
 14 #include "tok.h"
 15 #include <stdarg.h>
 16 #include <stdlib.h>
 17 #include <stdio.h>
 18 #include <ctype.h>
 19 #include <string.h>
 20 #include <limits.h>
 21 //#include "config.h"
 22
 23 #define NMEA_TOKS_COMPARE   (1)
 24 #define NMEA_TOKS_PERCENT   (2)
 25 #define NMEA_TOKS_WIDTH     (3)
 26 #define NMEA_TOKS_TYPE      (4)
 27
 28 /**
 29  * \brief Calculate control sum of binary buffer
 30  */
 31 int nmea_calc_crc(const char *buff, int buff_sz)
 32 {
 33     int chsum = 0,
 34         it;
 35
 36     for(it = 0; it < buff_sz; ++it)
 37         chsum ^= (int)buff[it];
 38
 39     return chsum;
 40 }
 41
 42 /**
 43  * \brief Convert string to number
 44  */
 45 int nmea_atoi(const char *str, int str_sz, int radix)
 46 {
 47     char *tmp_ptr;
 48     char buff[NMEA_CONVSTR_BUF];
 49     int res = 0;
 50
 51     if(str_sz < NMEA_CONVSTR_BUF)
 52     {
 53         memcpy(&buff[0], str, str_sz);
 54         buff[str_sz] = ‘\0‘;
 55         res = strtol(&buff[0], &tmp_ptr, radix);
 56     }
 57
 58     return res;
 59 }
 60
 61 /**
 62  * \brief Convert string to fraction number
 63  */
 64 double nmea_atof(const char *str, int str_sz)
 65 {
 66     char *tmp_ptr;
 67     char buff[NMEA_CONVSTR_BUF];
 68     double res = 0;
 69
 70     if(str_sz < NMEA_CONVSTR_BUF)
 71     {
 72         memcpy(&buff[0], str, str_sz);
 73         buff[str_sz] = ‘\0‘;
 74         res = strtod(&buff[0], &tmp_ptr);
 75     }
 76
 77     return res;
 78 }
 79
 80 /**
 81  * \brief Analyse string (specificate for NMEA sentences)
 82  */
 83 int nmea_scanf(const char *buff, int buff_sz, const char *format, ...)
 84 {
 85     const char *beg_tok;
 86     const char *end_buf = buff + buff_sz;
 87
 88     va_list arg_ptr;
 89     int tok_type = NMEA_TOKS_COMPARE;
 90     int width = 0;
 91     const char *beg_fmt = 0;
 92     int snum = 0, unum = 0;
 93
 94     int tok_count = 0;
 95     void *parg_target;
 96
 97     va_start(arg_ptr, format);
 98
 99     for(; *format && buff < end_buf; ++format)
100     {
101         switch(tok_type)
102         {
103         case NMEA_TOKS_COMPARE:
104             if(‘%‘ == *format)
105                 tok_type = NMEA_TOKS_PERCENT;
106             else if(*buff++ != *format)
107                 goto fail;
108             break;
109         case NMEA_TOKS_PERCENT:
110             width = 0;
111             beg_fmt = format;
112             tok_type = NMEA_TOKS_WIDTH;
113         case NMEA_TOKS_WIDTH:
114             if(isdigit(*format))
115                 break;
116             {
117                 tok_type = NMEA_TOKS_TYPE;
118                 if(format > beg_fmt)
119                     width = nmea_atoi(beg_fmt, (int)(format - beg_fmt), 10);
120             }
121         case NMEA_TOKS_TYPE:
122             beg_tok = buff;
123
124             if(!width && (‘c‘ == *format || ‘C‘ == *format) && *buff != format[1])
125                 width = 1;
126
127             if(width)
128             {
129                 if(buff + width <= end_buf)
130                     buff += width;
131                 else
132                     goto fail;
133             }
134             else
135             {
136                 if(!format[1] || (0 == (buff = (char *)memchr(buff, format[1], end_buf - buff))))
137                     buff = end_buf;
138             }
139
140             if(buff > end_buf)
141                 goto fail;
142
143             tok_type = NMEA_TOKS_COMPARE;
144             tok_count++;
145
146             parg_target = 0; width = (int)(buff - beg_tok);
147
148             switch(*format)
149             {
150             case ‘c‘:
151             case ‘C‘:
152                 parg_target = (void *)va_arg(arg_ptr, char *);
153                 if(width && 0 != (parg_target))
154                     *((char *)parg_target) = *beg_tok;
155                 break;
156             case ‘s‘:
157             case ‘S‘:
158                 parg_target = (void *)va_arg(arg_ptr, char *);
159                 if(width && 0 != (parg_target))
160                 {
161                     memcpy(parg_target, beg_tok, width);
162                     ((char *)parg_target)[width] = ‘\0‘;
163                 }
164                 break;
165             case ‘f‘:
166             case ‘g‘:
167             case ‘G‘:
168             case ‘e‘:
169             case ‘E‘:
170                 parg_target = (void *)va_arg(arg_ptr, double *);
171                 if(width && 0 != (parg_target))
172                     *((double *)parg_target) = nmea_atof(beg_tok, width);
173                 break;
174             };
175
176             if(parg_target)
177                 break;
178             if(0 == (parg_target = (void *)va_arg(arg_ptr, int *)))
179                 break;
180             if(!width)
181                 break;
182
183             switch(*format)
184             {
185             case ‘d‘:
186             case ‘i‘:
187                 snum = nmea_atoi(beg_tok, width, 10);
188                 memcpy(parg_target, &snum, sizeof(int));
189                 break;
190             case ‘u‘:
191                 unum = nmea_atoi(beg_tok, width, 10);
192                 memcpy(parg_target, &unum, sizeof(unsigned int));
193                 break;
194             case ‘x‘:
195             case ‘X‘:
196                 unum = nmea_atoi(beg_tok, width, 16);
197                 memcpy(parg_target, &unum, sizeof(unsigned int));
198                 break;
199             case ‘o‘:
200                 unum = nmea_atoi(beg_tok, width, 8);
201                 memcpy(parg_target, &unum, sizeof(unsigned int));
202                 break;
203             default:
204                 goto fail;
205             };
206
207             break;
208         };
209     }
210
211 fail:
212
213     va_end(arg_ptr);
214
215     return tok_count;
216 }


10
11 #ifndef __TOK_H__
12 #define __TOK_H__
13
14 //#include "config.h"
15
16 #ifdef  __cplusplus
17 extern "C" {
18 #endif
19
20 #define NMEA_CONVSTR_BUF    (256)
21
22 int     nmea_calc_crc(const char *buff, int buff_sz);
23 int     nmea_atoi(const char *str, int str_sz, int radix);
24 double  nmea_atof(const char *str, int str_sz);
25 int     nmea_printf(char *buff, int buff_sz, const char *format, ...);
26 int     nmea_scanf(const char *buff, int buff_sz, const char *format, ...);
27
28 #ifdef  __cplusplus
29 }
30 #endif
31
32 #endif /* __NMEA_TOK_H__ */
				
时间: 2024-11-10 01:34:54

GPS数据解析的相关文章

GPS(NMEA)数据解析

一.GPS定位信息 设置好gps模式,启动gps,正常的话在gps通路有NMEA数据上报,如下: $GPGSV,3,1,11,01,62,130,42,07,61,201,43,11,72,075,28,17,20,251,38*7A $GPGSV,3,2,11,30,63,272,44,03,00,149,,08,34,046,,13,05,309,*76 $GPGSV,3,3,11,22,08,127,,27,03,057,,28,34,312,*4C $GPGGA,042523.0,341

GPS北斗双模技术应用开发研究--数据解析

上一篇文章大家介绍了几个重要的概念,那下面我们就来解析一下这样的双模输出log,在解析之前我们来看看各个重要标准字段的意义.笔者在实际开发中只用到了其中的四个,分别是GGA,GSA,GSV,RMC,GLL,当然还有其他一些字段,感觉有些是重复的,根据需要解析就好了.下面就分别介绍一下: $XXGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF) 各部分所对应的含义为: (1)定位UTC时间:05时09分01秒

iOS网络数据解析

iOS开发过程中,网络数据的传输过程一般是:客户端发送请求给服务器,服务器接收到客户端发送的网络请求后返回相应的数据.此时客户端需要把服务器返回的数据转化为前段和移动端开发中使用的数据格式(如OC/java).后台服务器一般使用php.java..net进行开发,而前段和移动端使用的一般是OC/JAVA/HTML/CSS/JS,做好前后端的数据交互极为重要,如今数据交互常用的就是JSON和XML.下面就iOS开发过程中的JSON解析和XML解析进行简单的说明. 一.JSON解析 JSON是一种轻

json数据解析,并实现将网络json数据获取用listview显示

需要使用jar包 fastjson或gson这两个jar包. //Gson的使用方式 Gson gson=new Gson(); String str=ReadAssetsFile.readtext(this,"json_ss");//this当前类,"json_ss"需要解析的文件名 UserMessage userMessage=gson.fromJson(str,UserMessage.class);//需要解析的json文件最外层类名 //fastjson的

Gprinter光栅位图点阵数据解析工具

最近参与的项目有一个需求,解析佳博热敏打印机的光栅位图点阵数据并保存为图片文件.数据是通过Bus Hound抓取的,如下图所示. 其中1b 40为初始化打印机的指令,对应的ASCII码为ESC @,1b 4a 18为打印并走纸的指令,对应的ASCII码为ESC J,1d 76 30为打印光栅位图的指令,对应的ASCII码为GS v 0,其后紧跟光栅位图模式(0x00).水平方向位图字节数(0x0036)和垂直方向位图点数(0x0018),后面则为本帧的位图数据(0x36*0x18=1296字节)

iOS GET、POST数据解析

在实际开发中,JSON数据解析更简单易行,一般均使用json数据解析,因此,程序猿们请务必和后台搞好关系,让他给你json数据.    XML解析: ios SDK提供了NSXMLParser和libxml2两个类库,另外还有很多第三方类库可选:TBXML.TouchXML.KissXML.TinyXML和GDataXML. 解析XML通常有两种方式:DOM和SAX DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),遍历树结构可以检索任意XML节点,读取它的属性和值.

第十五讲.数据解析(XML与JSON两种数据解析)

一.XML数据解析 1.SAX:Simple API for XML.基于事件驱动的解析方式,逐行解析数据.(采用协议回调机制) NSXMLParser的解析方法: 1 #import "ViewController.h" 2 #import "Model.h" 3 4 @interface ViewController ()<NSXMLParserDelegate> 5 6 @property(nonatomic,strong)UITableView

iOS开发——网络编程OC篇&amp;数据解析总结

数据解析总结 1 //***************************************************XML 2 3 /** 4 NSXML 5 */ 6 /** 7 // 1. 开始解析XML文档 8 - (void)parserDidStartDocument: 9 10 // 2. 开始解析某个元素,会遍历整个XML,识别元素节点名称 11 - (void)parser:didStartElement:namespaceURI:qualifiedName:attrib

使用Socket通信实现Silverlight客户端实时数据的获取(模拟GPS数据,地图实时位置)

原文:使用Socket通信实现Silverlight客户端实时数据的获取(模拟GPS数据,地图实时位置) 在上一篇中说到了Silverlight下的Socket通信,在最后的时候说到本篇将会结合地图.下面就来看看本文实现的功能: Silverlight 与服务器利用Socket通讯,实时从服务器获取数据(本文中的数据是地理坐标),由于没有GPS,所以本文在服务器写了一个构造新坐标的函数(本文是一个三角函数),然后利用Timer组件,实时调用,得到新的坐标,并将新的坐标发送给客户端,客户端接收到发