GPS數據解析

来源:http://www.cnblogs.com/pingwen/archive/2017/02/02/6361573.html
-Advertisement-
Play Games

GPS模塊使用串口通信,那麼它的的數據處理本質上還是串口通信處理,只是GPS模塊的輸出的有其特定的格式,需要字元串處理邏輯來解析其含義。如何高效的處理從GPS模塊接收到的數據幀,是GPS驅動設計的重點,本文使用狀態機的思想來處理GPS輸出的串口數據流,相對於定時從串口環形bufer取數據包然後依次解... ...


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__ */

 




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • /*自定義oracle的分割函數*//*定義一個type,用戶接收返回的數據集合*/create or replace type splitType as table of varchar2(4000); /* 參數1: 被分割的字元串 參數2:分割字元串,預設是英文逗號*/create or re ...
  • 當創建一個表時,需要為表的各個列指定數據類型,Oracle的數據類型主要有5種,字元類型、數值類型、日期時間類型、LOB類型和偽列。 一、字元類型 1、CHAR類型 定長字元串,長度為1~2000位元組,如果定義時未指定大小,預設為1,使用時,若存儲的值大小小於指定的長度,則用空格填充剩餘長度, 若大 ...
  • 當你開始編寫 Apache Spark 代碼或者瀏覽公開的 API 的時候,你會遇到各種各樣術語,比如transformation,action,RDD 等等。 瞭解到這些是編寫 Spark 代碼的基礎。 同樣,當你任務開始失敗或者你需要透過web界面去瞭解自己的應用為何如此費時的時候,你需要去瞭解 ...
  • 非電腦專業,導師基本做單片機的項目,所以基本靠自學,經過兩年實踐,證明該學習路線基本可靠 所以分亨給對嵌入式Linux感興趣的學弟學妹 要學的東西真的很多,這裡僅提供入門之道,分為基礎知識和實踐兩個部分 第一部分,專業知識 C語言學習 《C和指針》 《C專家編程》 《C陷阱與缺陷》 上面三本書,認 ...
  • 1、安裝kernel-debuginfo-common 和 kernel-debuginfo(下載地址:http://debuginfo.centos.org/6/x86_64/),安裝之前,先通過uname -r 確認內核版本 [root@xxxmysqlbkuat01 ~]# uname -r ...
  • 1.讀readme獲取信息 1.1 由Building the Software可知,需修改頂層makefile,指定架構和編譯器 ifeq ($(HOSTARCH),$(ARCH)) CROSS_COMPILE ?= arm-linux- endif ARCH = arm CROSS_COMPIL ...
  • 本文地址 分享提綱: 1. shell中的函數 2. shell中的數組 1. shell中的函數 1.1)【定義shell函數(define function)】 說明: 1、可以帶function fun() 定義,也可以直接fun() 定義,不帶任何參數。 2、參數返回,可以顯示加:retur ...
  • 七. 構建臨時系統 1. 通用編譯指南 a. 確認是否正確設置了 LFS 環境變數 b. 假定你已經正確地設置了宿主系統需求和符號鏈接 c. 對於每個軟體包: (1). 確保解壓軟體包時你使用的是 lfs 用戶 (2). 除非特別說明,刪除解壓出來的目錄和所有編譯過程中生成的 build 目錄 2. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...