張 超 ,范娟利 ,章傳銀 ,方書山
(1.中國測繪科學(xué)研究院,北京 100830; 2.山東科技大學(xué),山東 青島266510;3.中測新圖(北京)遙感技術(shù)有限責(zé)任公司,北京 100039)
隨著計算機通信技術(shù)的發(fā)展,越來越多的新技術(shù)被用到測繪行業(yè)獲取高質(zhì)量的測量數(shù)據(jù),但正確的獲取觀測數(shù)據(jù)是測量的根本,也是避免誤差的重要措施之一。全球?qū)Ш蕉ㄎ幌到y(tǒng)的數(shù)據(jù)格式遵循NEMA-0183協(xié)議,目前關(guān)于解析該類的文獻很多,但是這些文獻有的是簡單介紹一下單行數(shù)據(jù)的解析,有的是基于后處理的數(shù)據(jù)解析,有的是沒有結(jié)合讀取串口或者網(wǎng)口數(shù)據(jù)流來分析,而基于ASCII的無地址格式數(shù)據(jù)的解析更是少之又少。
在實際應(yīng)用中,GPS接收機、羅經(jīng)方位系統(tǒng)、數(shù)字羅盤等儀器輸出的數(shù)據(jù)都是NEMA-0183格式的數(shù)據(jù)[1]。另外,一些硬件廠商為了產(chǎn)品需求,方便數(shù)據(jù)收發(fā)而自定義各自的數(shù)據(jù)通信格式(用戶層面的通訊協(xié)議)的數(shù)據(jù)。
基于上述情況,本文以NEMA-0183格式數(shù)據(jù)與U型激光傳感器所示的基于ASCII碼的無地址格式數(shù)據(jù)為基礎(chǔ),詳細(xì)介紹基于串口 NEMA-0183 格式和ASCII無地址實時數(shù)據(jù)流的解析。
NEMA 0183是美國國家海洋電子協(xié)會(NMEA )為海用電子設(shè)備制定的標(biāo)準(zhǔn)格式。目前已成了GPS導(dǎo)航設(shè)備統(tǒng)一的國際海運事業(yè)無線電技術(shù)委員會(RTCM)標(biāo)準(zhǔn)協(xié)議。NEMA-0183 格式數(shù)據(jù)串的所有數(shù)據(jù)都采用 ASCII文本字符表示,數(shù)據(jù)傳輸以“$”開頭,后面是語句頭。語句頭由五個字母組成,分兩部分,前兩個字母表示“系統(tǒng)ID”,即表示該語句是屬于哪種系統(tǒng),后面三個字母表示“語句ID”,表示該語句是關(guān)于哪方面的數(shù)據(jù)。語句頭后是數(shù)據(jù)體,包含不同的數(shù)據(jù)體字段,語句末尾為校驗碼(可選,由十六進制組成),以回車換行符
NEMA-0183協(xié)議是GPS接收機應(yīng)當(dāng)遵守的標(biāo)準(zhǔn)協(xié)議,也是目前GPS 接收機上使用最廣泛的協(xié)議,大多數(shù)常見的GPS接收機、GPS數(shù)據(jù)處理軟件、導(dǎo)航軟件都遵守或者至少兼容這個協(xié)議。本文中使用的是 NovAtel 的GPS-OEMV4板接收機。其輸出的數(shù)據(jù)格式$GPGLL,5106.9802869,N,11402.3037325,W,182147.00,A*1C。其中$GPGLL 為語句頭,5106.9802869表示51°06.9802869分,N表示緯度,11402.3037325表示 114°02.3037325 s,W表示經(jīng)度,182147.00表示18點21分47.00秒,A表示數(shù)據(jù)可用,如果是V表示數(shù)據(jù)不可用,*標(biāo)示此句馬上結(jié)束,1C為校驗和。
導(dǎo)航用的三維數(shù)字羅盤也輸出 NEMA-0183格式數(shù)據(jù),數(shù)字羅盤主要輸出姿態(tài)(航向、橫滾、俯仰)數(shù)據(jù),本文以型號為XW_EC1730的電子羅盤為例,其串口輸出的 NEMA-0183 數(shù)據(jù)格式舉例:$C220.6,P4.5,R0.3,X-8.20,Y-10.74,Z-20.33,T15.0*49.其中$:數(shù)據(jù)包幀頭,C220.6:航向角,值為220.6°,P4.5:俯仰角,值為4.5°,R0.3:橫滾角,值為0.3°,X-8.20:X軸磁場強度,值為-8.20,Y-10.74:Y軸磁場強度,值為-10.74,Z-20.33,Z軸磁場強度,值為-20.33,T15.0:傳感器溫度,值為15.0,*49,校驗和。
基于ASCII無地址格式數(shù)據(jù)是廠商根據(jù)串口通信用戶層協(xié)議編制原則:1) 數(shù)據(jù)包必須有包頭;2) 非定長數(shù)據(jù)包必須有包尾;3) 一般應(yīng)對數(shù)據(jù)進行檢校;4) 要求便于觀察的數(shù)據(jù)應(yīng)該在結(jié)尾加入換行等符號;5) 要求更新快的數(shù)據(jù),應(yīng)盡量簡短[3]自定義通信協(xié)議的典型。其數(shù)據(jù)格式是$<1>,<2>,<3>…$<1>,<2>,<3>…$<1>,<2>,<3>…,……,該數(shù)據(jù)格式與NEMA-0183數(shù)據(jù)格式最大的區(qū)別:1) 沒有類似于符號“*”的數(shù)據(jù)結(jié)束標(biāo)示符;2) “$”符號后的字段數(shù)不固定;3)每個字段長度也不固定。例如:A.如果現(xiàn)在只需要測量距離信息和激光反射強度,通過串口寫數(shù)據(jù)函數(shù),設(shè)置好后其數(shù)據(jù)流為$BM10.841,22348$BM10.842,22355$BM10.840,22991……,說明所測距離為10.841 m、10.842 m和10.840 m,分別對應(yīng)的激光反射強度為22348B、22355B、22991B.如果僅需要測量距離信息,同樣通過串口寫數(shù)據(jù)函數(shù),設(shè)置好后其數(shù)據(jù)流為$BM20.882$BM21,235$BM20.013……,其中所測距離分別為20.882 m、21,235 m、20.013 m.
將GPS串口、數(shù)字羅盤串口、U型傳感器串口分別與電腦連接,利用串口調(diào)試工具查找各自對應(yīng)串口號。將GPS串口波特率設(shè)置為115 200,羅盤串口波特率設(shè)置為192 00,U型激光傳感器的波特率設(shè)置為115 200.GPS 串口輸出的數(shù)據(jù)類型為 NEMA-0183 格式的 GPGLL 數(shù)據(jù),其包含測站位置數(shù)據(jù)(測站經(jīng)緯度、 對應(yīng)時間、狀態(tài)及檢校位)。數(shù)字羅盤的數(shù)據(jù)流包含姿態(tài)數(shù)據(jù)(俯仰角、方位角及翻滾角)。U型傳感器數(shù)據(jù)流包含測距數(shù)據(jù)或激光反射強度信息。連接本文設(shè)計的程序,設(shè)置好各自串口號及波特率,連接成功后通過串口采集兩種格式的ASCII數(shù)據(jù),將所需數(shù)據(jù)解析提取并存儲。
串行口的本質(zhì)功能是作為CPU和串行設(shè)備間的編碼轉(zhuǎn)換器,當(dāng)數(shù)據(jù)從CPU經(jīng)過串口發(fā)送出去時,字節(jié)數(shù)據(jù)轉(zhuǎn)換為串行位。在接收數(shù)據(jù)時,串口的位被轉(zhuǎn)換為字節(jié)數(shù)據(jù)。在Windows環(huán)境下,串口是系統(tǒng)資源的一部分,應(yīng)用程序要使用串口進行通信,必須在使用之前向操作系統(tǒng)提出資源申請(打開串口),通信完成后必須釋放資源(關(guān)閉串口)[4-7]。那么按照上節(jié)兩種格式的ASCII數(shù)據(jù)流的規(guī)律及區(qū)別,對串口數(shù)據(jù)流的讀取,采用如圖1所示的思路設(shè)計串口類。
圖1 串口數(shù)據(jù)流解析流程圖
本文串口類的設(shè)計以多線程串口類CSerialPort為基礎(chǔ),需要用到的主要函數(shù)有:打開關(guān)閉串口函數(shù)、串口參數(shù)設(shè)置和初始化函數(shù)、串口讀寫函數(shù)。
本文中,串口類名為:CSerialCom.定義如下:
class CSerialCom
{
……
public:
HANDLE m_hComm; //串口句柄
BOOL CreateComEvent();//建立串口通訊事件
BOOL OpenPort(int port); //打開串口,即通知其他程序禁用此串口
BOOL ConfigurePort(int baud, int timeout); //串口參數(shù)的設(shè)置和初始化
ReadFile(m_hComm, unsigned char*data, DWORD len, DWORD &read, NULL);//讀串口數(shù)據(jù)
WriteFile(m_hComm, unsigned void*data, DWORD len, DWORD &read, NULL); //寫串口數(shù)據(jù)
BOOL ClosePort(int port);//關(guān)閉使用的串口,即釋放串口以供其他程序使用
……
};
根據(jù)所需數(shù)據(jù)的類型,設(shè)計該類時只用了簡單的同步I/O數(shù)據(jù)讀寫方式。其中所用函數(shù)為Windows API函數(shù),具體使用說明可查閱相關(guān)資料。
由于GPS-OEM板卡輸出的數(shù)據(jù)格式與羅盤XW_EC1730輸出的數(shù)據(jù)格式都是NEMA-0183格式數(shù)據(jù),是特殊數(shù)據(jù)格式。U型激光傳感器輸出的數(shù)據(jù)格式為基于ASCII的無地址格式數(shù)據(jù),是以串口通訊標(biāo)準(zhǔn)的最基本形式。所以以U型激光傳感器輸出的基于ASCII無地址格式數(shù)據(jù)流為例。NEMA-0183數(shù)據(jù)流的解析只需在此基礎(chǔ)上稍作修改即可。
設(shè)計U型激光傳感器的基于ASCCII無地址格式數(shù)據(jù)解析類,命名為CLaserThread。利用多線程函數(shù)的功能,設(shè)計虛成員函數(shù)為Run函數(shù)。Run函數(shù)的功能就是從串口中讀取數(shù)據(jù)、解析并存儲數(shù)據(jù)。
CLaserThread的定義如下:
class CLaserThread:public CWinThread,public CSerialCom
{
public:
virtual int Run();//接收并解析串口數(shù)據(jù)
bool StoreData();//數(shù)據(jù)存儲函數(shù)
}
其中CWinThread為系統(tǒng)自帶類,具體功能可查閱相關(guān)資料。
由第1節(jié)U型激光傳感器的基于ASCII無地址格式數(shù)據(jù)流的規(guī)律,通過串口數(shù)據(jù)解析的方法為:先讀取緩存中的所有數(shù)據(jù),再對數(shù)據(jù)進行解析。由于該數(shù)據(jù)只有數(shù)據(jù)頭標(biāo)示符“$”,沒有結(jié)束符標(biāo)志,就需要特殊的方法來解析緩存區(qū)的數(shù)據(jù),指針首先找到“$”字符,然后下移直到再次遇到“$”字符,去掉最后一個“$”字符后就可以提取出第一組數(shù)據(jù),按照事先測試的每個逗號前的數(shù)據(jù)內(nèi)容,以“,”為標(biāo)志提取相關(guān)數(shù)據(jù),而“$”前的最后一個數(shù)據(jù)可通過用該組數(shù)據(jù)的總長度減去截止最后一個逗號的字符串長度來獲取,關(guān)鍵在提取第二組數(shù)據(jù)時,指針起始位置需向上移動一位,這樣再按照第一組數(shù)據(jù)提取的方式進行解析就可以得到第二組數(shù)據(jù)。如此反復(fù),就可以將讀入緩存區(qū)的數(shù)據(jù)解析完。其實現(xiàn)過程如圖2所示。
圖2 基于ASCII無格式數(shù)據(jù)流解析過程
按照上述方法設(shè)計Run函數(shù),如下:
int ClaserThread::Run()
{
unsigned char szBuffer[2048];//設(shè)置數(shù)據(jù)緩沖區(qū)大小
DWORD nReadLen = 0;//緩沖區(qū)中被讀取的字節(jié)數(shù)
int nLeftLen = 0; //未解析剩余的字節(jié)數(shù)
DWORD nOffset = 0; //每次解析完的字節(jié)數(shù)
DWORD nIndexEnd = 0; //結(jié)束符索引值
//讀取羅盤串口數(shù)據(jù)
ReadData(szBuffer + nLeftLen, sizeof(szBuffer) -nLeftLen -1, nReadLen); nLeftLen += nReadLen;
nOffset = 0;
while(nLeftLen>0){
while(nLeftLen>0){
if(szBuffer[nOffset] == '$')break;//找到數(shù)據(jù)包的幀頭
nOffset += 1;nLeftLen -= 1;}
while(int(nIndexEnd -nOffset) <= nLeftLen && nIndexEnd < (sizeof szBuffer))
{
switch(szBuffer[nIndexEnd])
{case '$':isFind=1;//是否找到數(shù)據(jù)包幀頭標(biāo)志$
break;
default:isFind=0;break;}
if (isFind){break; } else{nIndexEnd+=1; continue; }}
//如果數(shù)據(jù)包不完整情況就舍棄
if(int(nIndexEnd -nOffset) > nLeftLen|| szBuffer[nIndexEnd] == 0)
{
memmove(szBuffer, szBuffer + nOffset, nLeftLen);break;}
nLeftLen -= (nIndexEnd -nOffset + 1);
…………
while(nOffset < nIndexEnd)
{
for(int i=2;lastData;i++){
if(*(szBuffer+i)==44){
L=atof((const char*)(szBuffer+2+nOffset));//距離值
S=atof((const char*)(szBuffer+i+nOffset));//激光強度大小
lastData=false;//判斷是否到數(shù)據(jù)包結(jié)尾
StoreData();//將提取的測量數(shù)據(jù)存儲起來
}}
if (szBuffer[nIndexEnd]==_T('$')) //查找下一個字段的開始標(biāo)示符$
{nLeftLen-=nIndexEnd+1;
nOffset=nIndexEnd;}}}
::PostThreadMessage(m_nThreadID, WM_QUIT, 0, 0);
return CWinThread::Run();
}
由于篇幅限制,Run函數(shù)中部分內(nèi)容省去,僅留關(guān)鍵部分。
對于NEMA-0183格式數(shù)據(jù)流的解析,以數(shù)字電子羅盤數(shù)據(jù)為例。其解析方法為:先讀取緩存中的所有數(shù)據(jù),然后再對數(shù)據(jù)進行解析。針對NEMA-0183格式數(shù)據(jù)每句都是以“$”開頭,以“*”符號標(biāo)示結(jié)尾,他們之間的數(shù)據(jù)就是我們所需要的姿態(tài)數(shù)據(jù)。將這之間按“P”“C”“R”的標(biāo)志將三個姿態(tài)數(shù)據(jù)提取出來。解析完該句后,指針繼續(xù)下移,分別找到“$”字符和“*”字符。又解析出下組數(shù)據(jù),如此反復(fù)直到解析完該緩存區(qū)中的所有數(shù)據(jù)。等下一時刻緩存區(qū)存儲數(shù)據(jù)后再次進行解析,重復(fù)以上步驟就可實時獲取所需數(shù)據(jù)。其實現(xiàn)過程如圖3所示。
圖3 NEMA-0183格式數(shù)據(jù)流解析過程
NEMA-0183數(shù)據(jù)流解析的實現(xiàn)過程可在基于ASCII無地址格式數(shù)據(jù)流解析過程基礎(chǔ)上將代碼稍作調(diào)整即可。
由于實時數(shù)據(jù)流速率和大小的不確定性,作者基于vs2008sp1平臺上編寫了能夠同時接收多個串口數(shù)據(jù)的多線程程序,利用上述數(shù)據(jù)解析方法,通過自制控制板卡(串口轉(zhuǎn)USB接口控制),將連接好天線的NovAtel-OEM4板卡、數(shù)字羅盤和U型激光傳感器與PC連接,分別實現(xiàn)了NovAtel-OEM4板卡的GPS數(shù)據(jù)解析(需將存儲文件通過相應(yīng)轉(zhuǎn)換軟件分離出觀測文件、導(dǎo)航文件及氣象文件中的相關(guān)數(shù)據(jù)),數(shù)字羅盤數(shù)據(jù)解析和U型激光傳感器的基于ASCII碼的無地址數(shù)據(jù)解析。為方便查看和使用所獲數(shù)據(jù),電子羅盤數(shù)據(jù)和基于ASCII碼的無地址數(shù)據(jù)按照所需的設(shè)計格式進行存儲。如表1所示。
表1 解析結(jié)果示例
由于篇幅限制,對GPS數(shù)據(jù)的解析[8-9]不作列出。
通過對基于ASCII無地址格式與NEMA-0183格式實時數(shù)據(jù)流解析方法介紹與程序編寫,實現(xiàn)了相關(guān)數(shù)據(jù)的提取,驗證了上述解析方法的適用性。如果按照串口通信中用戶層協(xié)議一般編制原則,其他類似數(shù)據(jù)格式的ASCII數(shù)據(jù),就可以以NEMA-0183格式數(shù)據(jù)和基于ASCII碼無地址格式數(shù)據(jù)為基礎(chǔ),按照需求目的,在原有代碼基礎(chǔ)上,稍作改動,就可獲取所需測量信息。
[1] 袁德寶,崔希民,郎 博,等.基于VC++的GPS-0EM板卡串行通信關(guān)鍵算法的設(shè)計與實現(xiàn)[J].測繪科學(xué),2008,33(6):170-172.
[2] 龔建偉.Visual C++/Turbo C串口通信編程實踐[M].北京:電子工業(yè)出版社,2007.
[3] 汪 穎,孫華軍.基于VC++串口通信的設(shè)計與實現(xiàn)[J].現(xiàn)代電子技術(shù),2011,34(14):19-20.
[4] 唐新國.計算機串口數(shù)據(jù)采集與數(shù)據(jù)存儲的設(shè)計與應(yīng)用[J].西華師范大學(xué)學(xué)報·自然科學(xué)版,2004,25(2):213-218.
[5] 高振松,過靜珺,李冰皓,等.Windows CE下實現(xiàn)掌上機和GPS-OEM板的通信[J].測繪通報,2001(5):35-36.
[6] 汪 兵.Windows CE 嵌入式高級編程及其實例詳解[M].北京:水利水電出版社,2008.
[7] 申曉寧,趙毅強,張 進,等.多線程串口類在實時數(shù)據(jù)采集系統(tǒng)中的應(yīng)用[J].Computer Era,2010(1):28-30.
[8] 王曉東.基于VC++的GPS數(shù)據(jù)采集系統(tǒng)的設(shè)計與實現(xiàn)[J].湖北汽車工業(yè)學(xué)院學(xué)報,2006,20(2):46-49.
[9] 李洪濤,許國昌,薛鴻印,等.GPS應(yīng)用程序設(shè)計[M].北京:科學(xué)出版社,1999.