GPS項目實戰系列1:GPS數據解析1

topsemic 發佈 2020-02-28T08:17:24+00:00

在編譯時,一上來就報了一個錯誤,提示沒有libstdc++.so.6文件libstdc++.so.6:cannot open shared object file: No such file or directory咋整,不會就問度娘或者谷哥唄,強烈建議技術相關的問題用谷哥,經過

前面寫了一系列關於Linux的文章,從這一篇開始換個題目,叫做GPS項目實戰系列,後面的很多篇內容都會圍繞著這個項目展開。這個項目要完成的任務,可以參考之前的這篇文章:

今天這篇文章的主題是GPS數據解析,關於GPS的基礎知識,我在很早之前的一篇文章里有過介紹過,網上也有大量的內容可以參考,不在這裡解釋了。

關於硬體,之前一直使用的是NUC972板子,今天換一個I.MX 8M的板子,原因是之前那個板子上沒有GPS模塊,我這正好有個帶GPS模塊的I.MX板子。其實,對應用程式來說,硬體用什麼都無所謂。

因為我的編程水平真的很菜,所以我一般寫代碼的套路是先從網上搜索,就是典型的拿來主義,先找一個能用的工程跑通,再根據需要去修改。經過簡單搜索,我找到了下面的這篇文章,

blog.csdn.net/zouleideboke/article/details/73521122

由於作者沒有給出完整工程的下載地址,所以我只能手動去複製代碼,經過簡單一通Ctrl+C和Ctrl+V操作,在GPSProj文件夾里整了下面五個文件

之後,咱們還得簡單修改下,一是Makefile中交叉工具鏈的設置,

把Make的里第一句改成你實際使用的,

另外一處是main函數裡,修改下你板上GPS模塊對應的串口

char *dev_name="/dev/ttyUSB0";

我這裡改成了我用的/dev/ttymxc1。

然後先去編譯一把,看看有沒有報錯,因為很多時候網上的東西都無法正常編譯。

之前我一直是在我的虛擬機上編譯的,由於我電腦性能較差,經常把我電腦幹卡機。今天想起自己有一個阿里雲帳號,裝的是Ubuntu系統,所以就把它利用了一下,在它上面放了交叉工具鏈。訪問它只需要通過SecureCRT工具登錄即可,非常方便。

在編譯時,一上來就報了一個錯誤,提示沒有libstdc++.so.6文件

libstdc++.so.6: cannot open shared object file: No such file or directory

咋整,不會就問度娘或者谷哥唄,強烈建議技術相關的問題用谷哥,經過我多次對比後發現,在度娘那查了好久解決不了的問題,用谷哥很快就能搞定。至於怎麼用谷哥,你自己開動腦筋去解決吧。上述問題解決方法如下:

     apt-get install lib32stdc++6

然後再次編譯,又報錯了。。。

經過再一次網上搜索,我嘗試把Makefile中命令行前面的空白(如下圖紅框部分所示)刪掉,用Tab鍵代替,就可以了

這一次編譯成功,生成gps_test文件。這個作者還不錯,最起碼給的原始碼本身沒有語法錯誤。

在執行程序前,建議先用microcom指令看一下板子的串口是否確實收到了GPS串口的數據。如果GPS接收正常的話,會顯示類似如下的信息,

OK,我們把gps_test放到板子裡跑一把,先看看情況如何

如果你發現上述中文對應的是一堆亂碼(這個問題折騰了我大半天,我起初以為是編譯的問題),後來才發現是SecureCRT里配置的問題,改成下面配置就行了,

從上述執行結果來看,程序是跑起來了,也正確讀取到了串口數據,但是沒有解析成功。這是啥原因呢?這時就得看一下代碼了,首先比較容易發現的是,程序里判斷的是$GPRMC,但是我這裡讀取到的是$GNRMC,所以在下面這段代碼就直接返回了:

      if(NULL==(ptr=strstr(buff,"$GPRMC")))
      {
         return -1;
      }

怎麼改呢,你是不是會想直接把$GPRMC改成$GNRMC,可以是可以,但是不夠好,萬一你遇到的是$GPRMC開頭的呢?可以改成如下判斷條件,含義是既找不到$GPRMC同時也找不到$GNRMC的情況下才返回-1

      if( NULL==(ptr=strstr(buff,"$GPRMC")) && NULL==(ptr=strstr(buff,"$GNRMC")) )
      {
         return -1;
      }

改完了之後下面這句話也得改:

 sscanf(ptr,"$GNRMC,%d.00,%c,%f,N,%f,E,%f,,%d,6.91,W,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->date),&(gps_data->mode));

我一開始做了如下修改,我的邏輯是如果查詢到有$GPRMC就執行if條件下的語句,否則執行else下面的語句,

if(NULL != (ptr=strstr(buff,"$GPRMC")))
         sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));
else
         sscanf(ptr,"$GNRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));

放進去一跑,結果提示Segmentation fault,在做應用開發的過程中,會經常遇到這個問題。這個問題調試了我好大一會才找到原因,你們能很快發現問題在哪裡嗎?

問題出在,在if判斷時會給ptr指針賦一個null指針,然後在else語句里用到了ptr,自然會出問題,於是我又做了一次改動

 if(NULL != (ptr=strstr(buff,"$GPRMC")))
         sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));
else if(NULL != (ptr=strstr(buff,"$GNRMC")))
         sscanf(ptr,"$GNRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));

興致勃勃放進去運行了一把,結果如下:

沒有解析出正確的欄位,我們很容易定位是sscanf那條語句出了問題,由於我之前沒用過這個函數,為此我上網查了好久這個函數的用法,後來把思路轉向對比作者的GPRMC語句和我收到的GNRMC語句,除了開頭不同,其他地方的差異,

$GPRMC,131913.000,A,3029.64972,N,11423.62352,E,0.00,0.00,200617,,,A*67

$GNRMC,085959.00,A,4000.73433,N,11628.03429,E,0.461,,280220,6.91,W,D*29

發現有以下幾處的不同,

1) 欄位1:UTC時間這裡,他的小數點後面是3個0,但我的是2個0

2) 欄位8:方位角這裡,他的值是0.00,我的是空白

3) 欄位11和12,磁偏角及磁偏角方向,他的是空白,我的有值

正是這3處不同,導致的上述解析不正確。原作者完全是按照他的GPS數據格式來寫的代碼,只要有任何一處不同,就會出現解析錯誤。為了先匹配我的這個格式,將第二個sscanf做了如下修改:

sscanf(ptr,"$GNRMC,%d.00,%c,%f,N,%f,E,%f,,%d,6.91,W,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->date),&(gps_data->mode));

之後運行,各個欄位就解析出來了

我們來看一看解析的對不對

$GNRMC,085959.00,A,4000.73433,N,11628.03429,E,0.461,,280220,6.91,W,D*29

欄位9:UTC日期,DDMMYY格式,280220對應2020年02月28日,沒錯

欄位1:UTC時間,hhmmss.sss格式,轉換後也是對的,代碼里加了一個8(utc時間和北京時間差8小時),表示的是北京時間。

緯度:dddmm.mmmm度分格式,4000.73433對應,40度,0分,0.73433分,轉換秒,0.73433*60=44.0598秒,沒錯

經度:dddmm.mmmm度分格式,11628.03429對應,116度,28分,0.03429分,轉換秒,0.03429*60=2.0574秒,沒錯

速度:單位是節,1節=1.852 km/h=0.5144 m/s,速度錯了,轉換為m/s,需要乘以一個係數。

但是這個程序真的能這麼寫嗎?

答案是否!

因為你只考慮了這一種情況,上述方向角欄位空是因為GPS模塊處於靜止狀態,如果運動起來這個欄位就會有值,上述代碼就會出問題。

另外如果在GPS信號不好的時候,整個欄位的內容也會發生變化,會有很多空欄位的情況,比如剛啟動的時候,格式就會類似如下,

$GNRMC,064016.00,V,,,,,,,280220,6.91,W,N*2B

所以使用sscanf去處理這種協議類型的數據(數據欄位之間以逗號分隔,但是欄位可能會為空),顯然是不合適的。針對這種類型格式,該如何用去解析呢?希望讀者們也想一想,查一查,最好是動手去實現一下,沒有板子沒關係,直接在電腦上就可以運行。時間不早了,今天就到這裡了,敬請期待下一篇文章。

關鍵字: