一,GPS接受
1、打开串口
m_hCom=CreateFile("COM1",GENERIC_READ|GENERIC_WRITE,0,
NULL,OPEN_EXISTING, 0,NULL); /同步方式打开,不是异步,WINCE会堵塞线程
2、SetCommMask (m_hCom, EV_RXCHAR ) ; //添加或修改Windows所报告的事件列表一个字符来将产生一个触发事件(注意,不是一个字符串来,因为速度很快,所以在事件来的时候,读取到的数据可能是一串。为了防止丢失数据,设置合适的超时可以解决)
3、设置读写缓冲区SetupComm (m_hCom,READBUFLEN/*读缓冲*/,WRITEBUFLEN/*写缓冲*/);
4、// 清除缓冲信息
PurgeComm (m_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR) ;
5、对同步IO超时进行设置
GetCommTimeouts ( devCom , &CommTimeOuts );
CommTimeOuts.ReadIntervalTimeout = 100;
CommTimeOuts.ReadTotalTimeoutMultiplier = 20;
CommTimeOuts.ReadTotalTimeoutConstant = 20;
CommTimeOuts.WriteTotalTimeoutMultiplier = 20;
CommTimeOuts.WriteTotalTimeoutConstant = 200;
SetCommTimeouts( devCom , &CommTimeOuts );
6、对同步IO进行端口设置
GetCommState( devCom , &dcb );
dcb.DCBlength = sizeof( DCB );
dcb.BaudRate = CBR_4800;
dcb.fBinary = TRUE;
dcb.fParity = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = TRUE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fAbortOnError = FALSE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState( devCom , &dcb );
7、在成功打开并设置通讯口后,可采取轮询串口和事件触发两种方式对数据进行接收处理,可以采取效率比较高的事件触发方式进行接收处理,通过等待EV_RXCHAR事件的发生来启动ReadFile函数完成对GPS定位信息的接收:
while(true){
WaitCommEvent (m_hCom,&dwEvtMask,NULL);
if (dwEvtMask&EV_RXCHAR == EV_RXCHAR){
if(ComStat.cbInQue>0)
ReadFile(m_hCom,m_readbuf,ComStat.cbInQue,&nLength,&olRead);
}
在GPS接受过程中,最好使用轮询串口的方式读取数据,因为事件触发的方式可能导致收取数据不“整齐”,导致解析GPS的时候数据丢失现象。
while(1)
{
num=0;
rev$=false;
LF=false;
while(!rev$)
{
Sleep(10);
ReadFile(ceSeries->devCom,buf1,1,&Num1,NULL);
if(Num1==1 && buf1[0]=='$')rev$=true;
};
buf[num++]=buf1[0];
while(!LF && num<80 )
{
ReadFile(ceSeries->devCom,buf1,1,&Num1,NULL);
if(Num1==1)
{
buf[num++]=buf1[0];
if(buf1[0]==0x0A) LF=true;
}
};
if(LF)
{
for(i=0;i<num;i++) gBuf[i]=buf[i];
//发送给GPS解析类中处理
ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,(BYTE*)gBuf,100);
}
}
采用头尾收取的方法可以确保收到的数据“整齐”。
二,GPS解析
提取定位数据
GPS接收机只要处于工作状态就会源源不断地接收GPS导航定位信息。前面的代码只负责从串口接收数据并采用事件回调的方式把数据放入缓存发送到进程处理,在没有进一步处理之前缓存中是一长串字节流,这些信息在没有经过分类提取之前是无法加以利用的。因此,必须通过程序将各个字段的信息从缓存字节流中提取出来,将其转化成有实际意义的。同其他通讯协议类似,对GPS进行信息提取必须首先明确其帧结构,然后才能根据其结构完成对各定位信息的提取。对于本文所使用的天线板,其接受的数据主要由帧头、帧尾和帧内数据组成,根据数据帧的不同,帧头也不相同,主要有"$GPGGA"、"$GPGSA"、"$GPGSV"以及"$GPRMC"等。这些帧头标识了后续帧内数据的组成结构,各帧均以回车符和换行符(0X0D、0X0A)作为帧尾标识一帧的结束。对于通常的情况,我们所关心的定位数据如经纬度、速度、时间等均可以从"$GPRMC"帧中获取得到,该帧的结构及各字段释义如下,
数据丰富的最典型情况。
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>*hh
<1> 当前位置的格林尼治时间,格式为hhmmss.sss
<2> 状态, A 为有效位置, V为非有效接收警告,即当前天线视野上方的卫星个数少于3颗。
<3> 纬度, 格式为ddmm.mmmm格式不定长,例如:3111.4364
<4> 标明南北半球, N 为北半球、S为南半球
<5> 经度,格式为dddmm.mmmm 格式不定长,例如:12125.1027
<6> 标明东西半球,E为东半球、W为西半球
<7> 地面上的速度,范围为0.0到999.9
<8> 方位角,范围为000.0到 359.9 度
<9> 日期, 格式为ddmmyy
<10> 地磁变化,从000.0到 180.0 度(不考虑)
<11> 地磁变化方向,为E 或 W(不考虑)
数据缺失的最典型情况将是:
$GPRMC,<1>,<2>,,,,,,,,,*hh
<1> 当前位置的格林尼治时间,格式为hhmmss.sss
<2> 状态, A 为有效位置, V为非有效接收警告,即当前天线视野上方的卫星个数少于3颗。
数据将会出项缺失,时间回复到出厂时间,定位情况为V,(不定位警告)。
至于其他几种帧格式,除了特殊用途外,平时并不常用,虽然接收机也在源源不断地向主机发送各种数据帧,但在处理时一般先通过对帧头的判断而只对"$GPRMC"帧进行数据的提取处理。如果情况特殊,需要从其他帧获取数据,处理方法与之也是完全类似的。由于帧内各数据段由逗号分割,因此在处理缓存数据时一般是通过搜寻ASCII码"$"来判断是否是帧头,在对帧头的类别进行识别后再通过对所经历逗号个数的计数来判断出当前正在处理的是哪一种定位导航参数,并作出相应的处理。下面就是对缓存Data中的数据进行解帧处理的主要代码,
//GPS数据结构
typedef struct _GPSData
{
char date[15] ; //Gps数据日期
char time[15] ; //Gps数据时间
char latitude_type; //纬度类型,北纬,南纬
char latitude[15] ; //纬度值
char longitude_type; //经度类型,东经,西经
char longitude[15] ;//经度值
char speed[6];//速度
char starNum; //卫星数目
char IsValid;
}GPSData,*PGPSData;
void CGPS::AnalyGpsData(char *point, DWORD bufLen)
{
int i,err = 0,nDotNum = 0;
memset(&m_gpsCurData,0,sizeof(m_gpsCurData));//注意数据结构的初始化
char buf[16] = {0}; //储存','到','之间的数据
while(1)
{
if(*(point) == '$')
{
//$GPRMC
if((*(point) == '$')&&(*(point+1) == 'G')&&(*(point+2) == 'P')&&(*(point+3)== 'R')&&(*(point+4) == 'M')
&&(*(point+5) == 'C'))
{
while(1)
{
i = 0;
while(*point != ',')
{
buf[i] = *point;
i++;
point++;
}
point++;
buf[i] = '/0';
nDotNum++;
switch(nDotNum)
{
case 2:
memcpy(m_gpsCurData.time,&buf[0],strlen(buf));
break;
case 3: // Valid(A)/InValid(V)
if(buf[0] == 'V')
{
err = 1;//没有定位
}
m_gpsCurData.IsValid = buf[0];
break;
case 4: // 纬度:22.342551
if(err == 0)
{
memcpy(m_gpsCurData.latitude,&buf[0],strlen(buf));
}
break;
case 5: //南北纬
//if( (buf[0] != 'N') && (buf[0] != 'S') ) goto Wrong;
if(err == 0)
{
m_gpsCurData.latitude_type = buf[0];
}
break;
case 6: // 经度:114.080338
if(err == 0)
{
memcpy(m_gpsCurData.longitude,&buf[0],strlen(buf));
}
break;
case 7: //东西经
if(err == 0)
{
m_gpsCurData.longitude_type = buf[0];
}
break;
case 10: // 150805: 2005-8-15 ddmmyy
memcpy(m_gpsCurData.date,&buf[0],strlen(buf));
::PostMessage(this->m_pWnd->m_hWnd,WM_GPS_RECV_BUF,WPARAM(&m_gpsCurData),0);//发送回主界面进行显示或其他处理
goto Wrong;
default:
break;
}
}
}
}
goto Wrong;//other sentence
}
Wrong:
return;
//delete[]buf;
}
到此为止,已将时间和经纬度信息提取到GPS结构数组GPSData中的各个变量中去,后续的处理可根据该结构中存储的数据作出相应的处理。