张 超 ,范娟利 ,章传银 ,方书山
(1.中国测绘科学研究院,北京 100830; 2.山东科技大学,山东 青岛266510;3.中测新图(北京)遥感技术有限责任公司,北京 100039)
随着计算机通信技术的发展,越来越多的新技术被用到测绘行业获取高质量的测量数据,但正确的获取观测数据是测量的根本,也是避免误差的重要措施之一。全球导航定位系统的数据格式遵循NEMA-0183协议,目前关于解析该类的文献很多,但是这些文献有的是简单介绍一下单行数据的解析,有的是基于后处理的数据解析,有的是没有结合读取串口或者网口数据流来分析,而基于ASCII的无地址格式数据的解析更是少之又少。
在实际应用中,GPS接收机、罗经方位系统、数字罗盘等仪器输出的数据都是NEMA-0183格式的数据[1]。另外,一些硬件厂商为了产品需求,方便数据收发而自定义各自的数据通信格式(用户层面的通讯协议)的数据。
基于上述情况,本文以NEMA-0183格式数据与U型激光传感器所示的基于ASCII码的无地址格式数据为基础,详细介绍基于串口 NEMA-0183 格式和ASCII无地址实时数据流的解析。
NEMA 0183是美国国家海洋电子协会(NMEA )为海用电子设备制定的标准格式。目前已成了GPS导航设备统一的国际海运事业无线电技术委员会(RTCM)标准协议。NEMA-0183 格式数据串的所有数据都采用 ASCII文本字符表示,数据传输以“$”开头,后面是语句头。语句头由五个字母组成,分两部分,前两个字母表示“系统ID”,即表示该语句是属于哪种系统,后面三个字母表示“语句ID”,表示该语句是关于哪方面的数据。语句头后是数据体,包含不同的数据体字段,语句末尾为校验码(可选,由十六进制组成),以回车换行符
NEMA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS 接收机上使用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件、导航软件都遵守或者至少兼容这个协议。本文中使用的是 NovAtel 的GPS-OEMV4板接收机。其输出的数据格式$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表示经度,182147.00表示18点21分47.00秒,A表示数据可用,如果是V表示数据不可用,*标示此句马上结束,1C为校验和。
导航用的三维数字罗盘也输出 NEMA-0183格式数据,数字罗盘主要输出姿态(航向、横滚、俯仰)数据,本文以型号为XW_EC1730的电子罗盘为例,其串口输出的 NEMA-0183 数据格式举例:$C220.6,P4.5,R0.3,X-8.20,Y-10.74,Z-20.33,T15.0*49.其中$:数据包帧头,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无地址格式数据是厂商根据串口通信用户层协议编制原则:1) 数据包必须有包头;2) 非定长数据包必须有包尾;3) 一般应对数据进行检校;4) 要求便于观察的数据应该在结尾加入换行等符号;5) 要求更新快的数据,应尽量简短[3]自定义通信协议的典型。其数据格式是$<1>,<2>,<3>…$<1>,<2>,<3>…$<1>,<2>,<3>…,……,该数据格式与NEMA-0183数据格式最大的区别:1) 没有类似于符号“*”的数据结束标示符;2) “$”符号后的字段数不固定;3)每个字段长度也不固定。例如:A.如果现在只需要测量距离信息和激光反射强度,通过串口写数据函数,设置好后其数据流为$BM10.841,22348$BM10.842,22355$BM10.840,22991……,说明所测距离为10.841 m、10.842 m和10.840 m,分别对应的激光反射强度为22348B、22355B、22991B.如果仅需要测量距离信息,同样通过串口写数据函数,设置好后其数据流为$BM20.882$BM21,235$BM20.013……,其中所测距离分别为20.882 m、21,235 m、20.013 m.
将GPS串口、数字罗盘串口、U型传感器串口分别与电脑连接,利用串口调试工具查找各自对应串口号。将GPS串口波特率设置为115 200,罗盘串口波特率设置为192 00,U型激光传感器的波特率设置为115 200.GPS 串口输出的数据类型为 NEMA-0183 格式的 GPGLL 数据,其包含测站位置数据(测站经纬度、 对应时间、状态及检校位)。数字罗盘的数据流包含姿态数据(俯仰角、方位角及翻滚角)。U型传感器数据流包含测距数据或激光反射强度信息。连接本文设计的程序,设置好各自串口号及波特率,连接成功后通过串口采集两种格式的ASCII数据,将所需数据解析提取并存储。
串行口的本质功能是作为CPU和串行设备间的编码转换器,当数据从CPU经过串口发送出去时,字节数据转换为串行位。在接收数据时,串口的位被转换为字节数据。在Windows环境下,串口是系统资源的一部分,应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请(打开串口),通信完成后必须释放资源(关闭串口)[4-7]。那么按照上节两种格式的ASCII数据流的规律及区别,对串口数据流的读取,采用如图1所示的思路设计串口类。
图1 串口数据流解析流程图
本文串口类的设计以多线程串口类CSerialPort为基础,需要用到的主要函数有:打开关闭串口函数、串口参数设置和初始化函数、串口读写函数。
本文中,串口类名为:CSerialCom.定义如下:
class CSerialCom
{
……
public:
HANDLE m_hComm; //串口句柄
BOOL CreateComEvent();//建立串口通讯事件
BOOL OpenPort(int port); //打开串口,即通知其他程序禁用此串口
BOOL ConfigurePort(int baud, int timeout); //串口参数的设置和初始化
ReadFile(m_hComm, unsigned char*data, DWORD len, DWORD &read, NULL);//读串口数据
WriteFile(m_hComm, unsigned void*data, DWORD len, DWORD &read, NULL); //写串口数据
BOOL ClosePort(int port);//关闭使用的串口,即释放串口以供其他程序使用
……
};
根据所需数据的类型,设计该类时只用了简单的同步I/O数据读写方式。其中所用函数为Windows API函数,具体使用说明可查阅相关资料。
由于GPS-OEM板卡输出的数据格式与罗盘XW_EC1730输出的数据格式都是NEMA-0183格式数据,是特殊数据格式。U型激光传感器输出的数据格式为基于ASCII的无地址格式数据,是以串口通讯标准的最基本形式。所以以U型激光传感器输出的基于ASCII无地址格式数据流为例。NEMA-0183数据流的解析只需在此基础上稍作修改即可。
设计U型激光传感器的基于ASCCII无地址格式数据解析类,命名为CLaserThread。利用多线程函数的功能,设计虚成员函数为Run函数。Run函数的功能就是从串口中读取数据、解析并存储数据。
CLaserThread的定义如下:
class CLaserThread:public CWinThread,public CSerialCom
{
public:
virtual int Run();//接收并解析串口数据
bool StoreData();//数据存储函数
}
其中CWinThread为系统自带类,具体功能可查阅相关资料。
由第1节U型激光传感器的基于ASCII无地址格式数据流的规律,通过串口数据解析的方法为:先读取缓存中的所有数据,再对数据进行解析。由于该数据只有数据头标示符“$”,没有结束符标志,就需要特殊的方法来解析缓存区的数据,指针首先找到“$”字符,然后下移直到再次遇到“$”字符,去掉最后一个“$”字符后就可以提取出第一组数据,按照事先测试的每个逗号前的数据内容,以“,”为标志提取相关数据,而“$”前的最后一个数据可通过用该组数据的总长度减去截止最后一个逗号的字符串长度来获取,关键在提取第二组数据时,指针起始位置需向上移动一位,这样再按照第一组数据提取的方式进行解析就可以得到第二组数据。如此反复,就可以将读入缓存区的数据解析完。其实现过程如图2所示。
图2 基于ASCII无格式数据流解析过程
按照上述方法设计Run函数,如下:
int ClaserThread::Run()
{
unsigned char szBuffer[2048];//设置数据缓冲区大小
DWORD nReadLen = 0;//缓冲区中被读取的字节数
int nLeftLen = 0; //未解析剩余的字节数
DWORD nOffset = 0; //每次解析完的字节数
DWORD nIndexEnd = 0; //结束符索引值
//读取罗盘串口数据
ReadData(szBuffer + nLeftLen, sizeof(szBuffer) -nLeftLen -1, nReadLen); nLeftLen += nReadLen;
nOffset = 0;
while(nLeftLen>0){
while(nLeftLen>0){
if(szBuffer[nOffset] == '$')break;//找到数据包的帧头
nOffset += 1;nLeftLen -= 1;}
while(int(nIndexEnd -nOffset) <= nLeftLen && nIndexEnd < (sizeof szBuffer))
{
switch(szBuffer[nIndexEnd])
{case '$':isFind=1;//是否找到数据包帧头标志$
break;
default:isFind=0;break;}
if (isFind){break; } else{nIndexEnd+=1; continue; }}
//如果数据包不完整情况就舍弃
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;//判断是否到数据包结尾
StoreData();//将提取的测量数据存储起来
}}
if (szBuffer[nIndexEnd]==_T('$')) //查找下一个字段的开始标示符$
{nLeftLen-=nIndexEnd+1;
nOffset=nIndexEnd;}}}
::PostThreadMessage(m_nThreadID, WM_QUIT, 0, 0);
return CWinThread::Run();
}
由于篇幅限制,Run函数中部分内容省去,仅留关键部分。
对于NEMA-0183格式数据流的解析,以数字电子罗盘数据为例。其解析方法为:先读取缓存中的所有数据,然后再对数据进行解析。针对NEMA-0183格式数据每句都是以“$”开头,以“*”符号标示结尾,他们之间的数据就是我们所需要的姿态数据。将这之间按“P”“C”“R”的标志将三个姿态数据提取出来。解析完该句后,指针继续下移,分别找到“$”字符和“*”字符。又解析出下组数据,如此反复直到解析完该缓存区中的所有数据。等下一时刻缓存区存储数据后再次进行解析,重复以上步骤就可实时获取所需数据。其实现过程如图3所示。
图3 NEMA-0183格式数据流解析过程
NEMA-0183数据流解析的实现过程可在基于ASCII无地址格式数据流解析过程基础上将代码稍作调整即可。
由于实时数据流速率和大小的不确定性,作者基于vs2008sp1平台上编写了能够同时接收多个串口数据的多线程程序,利用上述数据解析方法,通过自制控制板卡(串口转USB接口控制),将连接好天线的NovAtel-OEM4板卡、数字罗盘和U型激光传感器与PC连接,分别实现了NovAtel-OEM4板卡的GPS数据解析(需将存储文件通过相应转换软件分离出观测文件、导航文件及气象文件中的相关数据),数字罗盘数据解析和U型激光传感器的基于ASCII码的无地址数据解析。为方便查看和使用所获数据,电子罗盘数据和基于ASCII码的无地址数据按照所需的设计格式进行存储。如表1所示。
表1 解析结果示例
由于篇幅限制,对GPS数据的解析[8-9]不作列出。
通过对基于ASCII无地址格式与NEMA-0183格式实时数据流解析方法介绍与程序编写,实现了相关数据的提取,验证了上述解析方法的适用性。如果按照串口通信中用户层协议一般编制原则,其他类似数据格式的ASCII数据,就可以以NEMA-0183格式数据和基于ASCII码无地址格式数据为基础,按照需求目的,在原有代码基础上,稍作改动,就可获取所需测量信息。
[1] 袁德宝,崔希民,郎 博,等.基于VC++的GPS-0EM板卡串行通信关键算法的设计与实现[J].测绘科学,2008,33(6):170-172.
[2] 龚建伟.Visual C++/Turbo C串口通信编程实践[M].北京:电子工业出版社,2007.
[3] 汪 颖,孙华军.基于VC++串口通信的设计与实现[J].现代电子技术,2011,34(14):19-20.
[4] 唐新国.计算机串口数据采集与数据存储的设计与应用[J].西华师范大学学报·自然科学版,2004,25(2):213-218.
[5] 高振松,过静珺,李冰皓,等.Windows CE下实现掌上机和GPS-OEM板的通信[J].测绘通报,2001(5):35-36.
[6] 汪 兵.Windows CE 嵌入式高级编程及其实例详解[M].北京:水利水电出版社,2008.
[7] 申晓宁,赵毅强,张 进,等.多线程串口类在实时数据采集系统中的应用[J].Computer Era,2010(1):28-30.
[8] 王晓东.基于VC++的GPS数据采集系统的设计与实现[J].湖北汽车工业学院学报,2006,20(2):46-49.
[9] 李洪涛,许国昌,薛鸿印,等.GPS应用程序设计[M].北京:科学出版社,1999.