一:NMEA 协议
是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等 设备。
$GPGGA
例:$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
字段:$GPGGA,语句ID,表明该语句为Global Positioning System Fix Data(GGA)GPS定位信息
字段:UTC 时间,hhmmss.sss,时分秒格式
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:GPS状态,=未定位,=非差分定位,=差分定位,=无效PPS,=正在估算
字段:正在使用的卫星数量( - 12)(前导位数不足则补)
字段:HDOP水平精度因子(.5 - 99.9)
字段:海拔高度(-9999.9 - 99999.9)
字段:地球椭球面相对大地水准面的高度
字段:差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空)
字段:差分站ID号 - 1023(前导位数不足则补,如果不是差分定位将为空)
字段:校验值
$GPGSA
例:$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A
字段:$GPGSA,语句ID,表明该语句为GPS DOP and Active Satellites(GSA)当前卫星信息
字段:定位模式,A=自动手动D/3D,M=手动D/3D
字段:定位类型,=未定位,=2D定位,=3D定位
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PDOP综合位置精度因子(.5 - 99.9)
字段:HDOP水平精度因子(.5 - 99.9)
字段:VDOP垂直精度因子(.5 - 99.9)
字段:校验值
$GPGSV
例:$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
字段:$GPGSV,语句ID,表明该语句为GPS Satellites in View(GSV)可见卫星信息
字段:本次GSV语句的总数目( - 3)
字段:本条GSV语句是本次GSV语句的第几条( - 3)
字段:当前可见卫星总数( - 12)(前导位数不足则补)
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:校验值
$GPRMC
例:$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
字段:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段:UTC时间,hhmmss.sss格式
字段:状态,A=定位,V=未定位
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:速度,节,Knots
字段:方位角,度
字段:UTC日期,DDMMYY格式
字段:磁偏角,( - 180)度(前导位数不足则补)
字段:磁偏角方向,E=东W=西
字段:校验值
$GPVTG
例:$GPVTG,89.68,T,,M,0.00,N,0.0,K*5F
字段:$GPVTG,语句ID,表明该语句为Track Made Good and Ground Speed(VTG)地面速度信息
字段:运动角度, - 359,(前导位数不足则补)
字段:T=真北参照系
字段:运动角度, - 359,(前导位数不足则补)
字段:M=磁北参照系
字段:水平运动速度(.00)(前导位数不足则补)
字段:N=节,Knots
字段:水平运动速度(.00)(前导位数不足则补)
字段:K=公里/时,km/h
字段:校验值
$GPGLL
例:$GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D
字段:$GPGLL,语句ID,表明该语句为Geographic Position(GLL)地理定位信息
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:UTC时间,hhmmss.sss格式
字段:状态,A=定位,V=未定位
字段:校验值
二:GPS指定虚拟的串口号,就可以通过串口编程进行通讯了。主要过程:从串口读数据,找出完整的协议数据,然后将数据中的字段含义解析出来即可。
if (pGps->HaveValidGPSData(pArray,strGps))
{
PGPSData pGpsData = NULL;
pGpsData = pGps->AnalyseGpsData(strGps);
if (pGpsData != NULL)
{
//将接收到的GPS数据填充到最新当前数据
pGps->m_gpsCurData = (*pGpsData);
//发送接收有效GPS位置信息WINDOWS消息通知
//由消息处理函数释放内存
::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_VALID_LONGLAT,WPARAM(pGpsData),0);
}
}
1:串口通讯类
//定义串口接收数据函数类型
typedef void (CALLBACK* ONSERIESREAD)(void * pOwner /*父对象指针*/
,BYTE* buf /*接收到的缓冲区*/
,DWORD dwBufLen /*接收到的缓冲区长度*/);
class CCESeries
{
public:
CCESeries(void);
~CCESeries(void);
public:
//打开串口
BOOL OpenPort(void* pOwner,/*指向父指针*/
UINT portNo = 1, /*串口号*/
UINT baud = 9600, /*波特率*/
UINT parity = NOPARITY, /*奇偶校验*/
UINT databits = 8, /*数据位*/
UINT stopbits = 0 /*停止位*/
);
//关闭串口
void ClosePort();
//同步写入数据
BOOL WriteSyncPort(const BYTE*buf , DWORD bufLen);
//设置串口读取、写入超时
BOOL SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts);
//得到串口是否打开
BOOL GetComOpened();
private:
//串口读线程函数
static DWORD WINAPI ReadThreadFunc(LPVOID lparam);
private:
//关闭读线程
void CloseReadThread();
private:
//已打开的串口句柄
HANDLE m_hComm;
//读线程句柄
HANDLE m_hReadThread;
//读线程ID标识
DWORD m_dwReadThreadID;
//读线程退出事件
HANDLE m_hReadCloseEvent;
BOOL m_bOpened; //串口是否打开
void * m_pOwner; //指定父对象指针
public:
ONSERIESREAD m_OnSeriesRead; //串口读取回调函数
};
//构造函数
CCESeries::CCESeries()
{
//初始化内部变量
m_hComm = INVALID_HANDLE_VALUE;
m_OnSeriesRead = NULL;
m_bOpened = 0;
}
//析构函数
CCESeries::~CCESeries()
{
if (m_bOpened)
{
//关闭串口
ClosePort();
}
}
//串口读线程函数
DWORD CCESeries::ReadThreadFunc(LPVOID lparam)
{
CCESeries *ceSeries = (CCESeries*)lparam;
DWORD evtMask;
BYTE * readBuf = NULL;//读取的字节
DWORD actualReadLen=0;//实际读取的字节数
DWORD willReadLen;
DWORD dwReadErrors;
COMSTAT cmState;
// 清空缓冲,并检查串口是否打开。
ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);
//清空串口
PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );
SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
while (TRUE)
{
if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))
{
SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
//表示串口收到字符
if (evtMask & EV_RXCHAR)
{
ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
willReadLen = cmState.cbInQue ;
if (willReadLen <= 0)
{
continue;
}
//分配内存
readBuf = new BYTE[willReadLen];
ZeroMemory(readBuf,willReadLen);
//读取串口数据
ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);
//如果读取的数据大于,
if (actualReadLen>0)
{
//触发读取回调函数
if (ceSeries->m_OnSeriesRead)
{
ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
}
}
//释放内存
delete[] readBuf;
readBuf = NULL;
}
}
//如果收到读线程退出信号,则退出线程
if (WaitForSingleObject(ceSeries->m_hReadCloseEvent,500) == WAIT_OBJECT_0)
{
break;
}
}
return 0;
}
//关闭读线程
void CCESeries::CloseReadThread()
{
SetEvent(m_hReadCloseEvent);
//设置所有事件无效无效
SetCommMask(m_hComm, 0);
//清空所有将要读的数据
PurgeComm( m_hComm, PURGE_RXCLEAR );
//等待秒,如果读线程没有退出,则强制退出
if (WaitForSingleObject(m_hReadThread,4000) == WAIT_TIMEOUT)
{
TerminateThread(m_hReadThread,0);
}
m_hReadThread = NULL;
}
/*
*函数介绍:打开串口
*入口参数:pPortOwner :使用此串口类的窗体句柄
portNo :串口号
baud :波特率
parity :奇偶校验
databits :数据位
stopbits :停止位
*出口参数:(无)
*返回值:TRUE:成功打开串口;FALSE:打开串口失败
*/
BOOL CCESeries::OpenPort(void * pOwner,
UINT portNo , /*串口号*/
UINT baud , /*波特率*/
UINT parity , /*奇偶校验*/
UINT databits , /*数据位*/
UINT stopbits /*停止位*/
)
{
DCB commParam;
TCHAR szPort[15];
ASSERT(pOwner!=NULL);
m_pOwner = pOwner;
// 已经打开的话,直接返回
if (m_hComm != INVALID_HANDLE_VALUE)
{
return TRUE;
}
//设置串口名
wsprintf(szPort, L"COM%d:", portNo);
//打开串口
m_hComm = CreateFile(
szPort,
GENERIC_READ | GENERIC_WRITE, //允许读和写
0, //独占方式(共享模式)
NULL,
OPEN_EXISTING, //打开而不是创建(创建方式)
0,
NULL
);
if (m_hComm == INVALID_HANDLE_VALUE)
{
// 无效句柄,返回。
TRACE(_T("CreateFile 返回无效句柄/n"));
return FALSE;
}
// 得到打开串口的当前属性参数,修改后再重新设置串口。
if (!GetCommState(m_hComm,&commParam))
{
//关闭串口
CloseHandle (m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
return FALSE;
}
//设置串口参数
commParam.BaudRate = baud; // 设置波特率
commParam.fBinary = TRUE; // 设置二进制模式,此处必须设置TRUE
commParam.fParity = TRUE; // 支持奇偶校验
commParam.ByteSize = databits; // 数据位,范围:4-8
commParam.Parity = parity; // 校验模式
commParam.StopBits = stopbits; // 停止位
commParam.fOutxCtsFlow = FALSE; // No CTS output flow control
commParam.fOutxDsrFlow = FALSE; // No DSR output flow control
commParam.fDtrControl = DTR_CONTROL_ENABLE;
// DTR flow control type
commParam.fDsrSensitivity = FALSE; // DSR sensitivity
commParam.fTXContinueOnXoff = TRUE; // XOFF continues Tx
commParam.fOutX = FALSE; // No XON/XOFF out flow control
commParam.fInX = FALSE; // No XON/XOFF in flow control
commParam.fErrorChar = FALSE; // Disable error replacement
commParam.fNull = FALSE; // Disable null stripping
commParam.fRtsControl = RTS_CONTROL_ENABLE;
// RTS flow control
commParam.fAbortOnError = FALSE; // 当串口发生错误,并不终止串口读写
//设置串口参数
if (!SetCommState(m_hComm, &commParam))
{
TRACE(_T("SetCommState error"));
//关闭串口
CloseHandle (m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
return FALSE;
}
//设置串口读写时间
COMMTIMEOUTS CommTimeOuts;
GetCommTimeouts (m_hComm, &CommTimeOuts);
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
if(!SetCommTimeouts( m_hComm, &CommTimeOuts ))
{
TRACE( _T("SetCommTimeouts 返回错误") );
//关闭串口
CloseHandle (m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
return FALSE;
}
//指定端口监测的事件集
SetCommMask (m_hComm, EV_RXCHAR);
//分配串口设备缓冲区
SetupComm(m_hComm,512,512);
//初始化缓冲区中的信息
PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
CString strEvent;
strEvent.Format(L"Com_ReadCloseEvent%d",portNo);
m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,strEvent);
//创建串口读数据监听线程
m_hReadThread = CreateThread(NULL,0,ReadThreadFunc,this,0,&m_dwReadThreadID);
TRACE(_T("串口打开成功"));
m_bOpened = TRUE;
return TRUE;
}
/*
*函数介绍:关闭串口
*入口参数:(无)
*出口参数:(无)
*返回值:(无)
*/
void CCESeries::ClosePort()
{
//表示串口还没有打开
if (m_hComm == INVALID_HANDLE_VALUE)
{
return ;
}
//关闭读线程
CloseReadThread();
//关闭串口
CloseHandle (m_hComm);
//关闭事件
CloseHandle(m_hReadCloseEvent);
m_hComm = INVALID_HANDLE_VALUE;
m_bOpened = FALSE;
}
/*
*函数介绍:往串口写入数据
*入口参数:buf :待写入数据缓冲区
bufLen : 待写入缓冲区长度
*出口参数:(无)
*返回值:TRUE:设置成功;FALSE:设置失败
*/
BOOL CCESeries::WriteSyncPort(const BYTE*buf , DWORD bufLen)
{
DWORD dwNumBytesWritten;
DWORD dwHaveNumWritten =0 ; //已经写入多少
int iInc = 0; //如果次写入不成功,返回FALSE
ASSERT(m_hComm != INVALID_HANDLE_VALUE);
do
{
if (WriteFile (m_hComm, //串口句柄
buf+dwHaveNumWritten, //被写数据缓冲区
bufLen - dwHaveNumWritten, //被写数据缓冲区大小
&dwNumBytesWritten, //函数执行成功后,返回实际向串口写的个数
NULL)) //此处必须设置NULL
{
dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
//写入完成
if (dwHaveNumWritten == bufLen)
{
break;
}
iInc++;
if (iInc >= 3)
{
return FALSE;
}
Sleep(10);
}
else
{
return FALSE;
}
}while (TRUE);
return TRUE;
}
/*
*函数介绍:设置串口读取、写入超时
*入口参数:CommTimeOuts : 指向COMMTIMEOUTS结构
*出口参数:(无)
*返回值:TRUE:设置成功;FALSE:设置失败
*/
BOOL CCESeries::SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts)
{
ASSERT(m_hComm != INVALID_HANDLE_VALUE);
return SetCommTimeouts(m_hComm,&CommTimeOuts);
}
//得到串口是否打开
BOOL CCESeries::GetComOpened()
{
return m_bOpened;
}
2:GPRS通讯类
#include "CESeries.h"
//定义GPS数据接收显示
#define WM_GPS_RECV_BUF WM_USER + 101
//定义GPS状态改变消息
#define WM_GPS_STATE_CHANGE_MESSAGE WM_USER + 102
//定义收到正确的GPS位置信息
#define WM_GPS_RECV_VALID_LONGLAT WM_USER + 103
//定义GPS设备状态常量
enum GPSDEV_STATE
{
GPS_VALID_DATA = 0, //获取有效数据
GPS_INVALID_DATA,//获取无效数据
GPS_DEV_NOTOPENED, //GPS串口未打开
GPS_DEV_OPENED, //GPS串口已打开
GPS_NODATA//GPS未收到数据
};
//GPS数据结构
typedef struct _GPSData
{
char date[11] ; //Gps数据日期
char time[9] ; //Gps数据时间
char latitude_type[2]; //纬度类型,北纬,南纬
char latitude[10] ; //纬度值
char longitude_type[2]; //经度类型,东经,西经
char longitude[11] ;//经度值
char speed[6];//速度
char starNum; //卫星数目
}GPSData,*PGPSData;
class CGPS
{
public:
CGPS(void);
~CGPS(void);
public:
//打开GPS设备
BOOL Open( CWnd *pWnd , /*拥有者窗口句柄*/
UINT portNo = 1, /*串口号*/
UINT baud = 9600, /*波特率*/
UINT parity = NOPARITY, /*奇偶校验*/
UINT databits = 8, /*数据位*/
UINT stopbits = 0 );
//关闭GPS设备
void Close();
//获取GPS设备状态
GPSDEV_STATE GetGpsState();
//得到当前GPS数据
GPSData GetCurGpsData();
private:
//在缓冲区中查找子字符串
int Pos(LPCSTR subString , CByteArray * pArray,int iPos);
//判断是否存在有效GPS数据
BOOL HaveValidGPSData(CByteArray * pArray,/*分析的缓冲区队列*/
CString &outStr);
//解析GPS数据
PGPSData AnalyseGpsData(CString &aRecvStr);
private:
//串口接收数据回调函数
static void CALLBACK GpsOnSeriesRead(void* pOwner,BYTE* buf,DWORD dwBufLen);
private:
//GPS数据检测线程函数
static DWORD WINAPI GpsCheckThreadFunc(LPVOID lparam);
private:
GPSDEV_STATE m_gpsDev_State; //GPS当前设备状态
GPSData m_gpsCurData; // GPS当前数据
GPSData m_gpsLastData; //GPS上一次数据
CCESeries m_ceSeries; //GPS串口通讯类
CByteArray m_aRecvBuf ; //接收缓冲区
CWnd *m_pWnd; //存储主窗体句柄
HANDLE m_hThreadQuitEvent; //线程退出事件
HANDLE m_hGpsThread; //GPS检测线程句柄
DWORD m_dwGpsThreadID; //GPS检测线程标识
};
//构造函数
CGPS::CGPS()
{
m_gpsDev_State = GPS_DEV_NOTOPENED; //GPS状态
m_hGpsThread = NULL; //GPS检测线程句柄
ZeroMemory(&m_gpsCurData,sizeof(m_gpsCurData)); //GPS当前数据
ZeroMemory(&m_gpsLastData,sizeof(m_gpsLastData)); //GPS上一次数据
}
//析构函数
CGPS::~CGPS(void)
{
}
/*
*函数介绍:打开GPS设备
*入口参数:pWnd :使用此GPS类的窗体句柄
portNo :串口号
baud :波特率
parity :奇偶校验
databits :数据位
stopbits :停止位
*出口参数:(无)
*返回值:TRUE:成功打开GPS设备;FALSE:打开GPS设备失败
*/
BOOL CGPS::Open(CWnd *pWnd , /*拥有者窗口句柄*/
UINT portNo, /*串口号*/
UINT baud, /*波特率*/
UINT parity, /*奇偶校验*/
UINT databits, /*数据位*/
UINT stopbits /*停止位*/
)
{
m_pWnd = pWnd; //储存窗口句柄
//创建GPS检测线程退出事件
m_hThreadQuitEvent = CreateEvent(NULL,false,false,L"EVENT_GPS_THREAD");
//指定串口读回调函数
m_ceSeries.m_OnSeriesRead = GpsOnSeriesRead;
//打开GPS设备串口
BOOL bResult = m_ceSeries.OpenPort(this,portNo,baud,parity,databits,stopbits);
if (bResult)
{
//设置当前GPS状态
m_gpsDev_State = GPS_DEV_OPENED;
//发送GPS状态变化消息
::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_OPENED),1);
//创建GPS状态检测线程
m_hGpsThread = CreateThread(NULL,0,GpsCheckThreadFunc,this,0,&m_dwGpsThreadID);
}
else
{
//设置当前GPS状态
m_gpsDev_State = GPS_DEV_NOTOPENED;
//发送GPS状态变化消息
::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_NOTOPENED),1);
}
return bResult;
}
/*
*函数介绍:关闭GPS设备
*入口参数:(无)
*出口参数:(无)
*返回值:TRUE:成功关闭GPS设备;FALSE:关闭GPS设备失败
*/
void CGPS::Close()
{
//先退出GPS检测线程
if (m_hGpsThread != NULL)
{
//发送线程退出信号
SetEvent(m_hThreadQuitEvent);
//等待线程退出
if (WaitForSingleObject(m_hGpsThread,1000) == WAIT_TIMEOUT)
{
TerminateThread(m_hGpsThread,0);
}
}
m_hGpsThread = NULL;
CloseHandle(m_hThreadQuitEvent);
//将接收数据回掉函数置空
m_ceSeries.m_OnSeriesRead = NULL;
//关闭GPS串口
m_ceSeries.ClosePort();
//设置GPS状态
m_gpsDev_State = GPS_DEV_NOTOPENED;
//发送GPS状态变化消息
::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_NOTOPENED),1);
}
/*
*函数介绍:获取GPS设备状态
*入口参数:(无)
*出口参数:(无)
*返回值:返回GPS设备状态
*/
GPSDEV_STATE CGPS::GetGpsState()
{
return m_gpsDev_State;
}
/*
*函数介绍:得到当前GPS数据
*入口参数:(无)
*出口参数:(无)
*返回值:返回GPS设备当前GPS数据
*/
GPSData CGPS::GetCurGpsData()
{
return m_gpsCurData;
}
/*--------------------------------------------------------------------
【函数介绍】: 在pArray缓冲区,查找subString字符串,如存在,返回当前位置,否则返回-1
【入口参数】: pArray:指定接收到的缓冲区队列
【出口参数】: pArray:指定接收到的缓冲区队列,解析后需要进行适当修改
【返回值】: -1表示没有找到指定的子串,>=0表示发现第个子串的位置
---------------------------------------------------------------------*/
int CGPS::Pos(LPCSTR subString , CByteArray * pArray,int iPos)
{
//得到子串长度
int subLen = strlen(subString);
//得到缓冲区的长度
int bufLen = pArray->GetUpperBound()+1;
bool aResult = TRUE;
//
for ( int i=iPos;i<bufLen-subLen+1;i++)
{
aResult = TRUE;
for (int j=0;j<subLen;j++)
{
if (pArray->GetAt(i+j) != *(subString + j))
{
aResult = FALSE;
break;
}
int k = 0;
}
if (aResult)
{
return i;
}
}
return -1;
}
/*
*函数介绍:判断是否存在有效GPS数据
*入口参数:aRecvStr :缓冲数据
*出口参数:aRecvStr : 缓冲数据,outStr:得到的一个完整的GPS数据
*返回值:TRUE : 成功初始化, FALSE : 初始化失败
*/
BOOL CGPS::HaveValidGPSData(CByteArray * pArray,CString &outStr)
{
int tmpPos1,tmpPos2;
tmpPos1 = Pos("$GPRMC",pArray,0);
tmpPos2 = Pos("$GPRMC",pArray,tmpPos1+6);
if (tmpPos2 >= 0) //代表已包含两个$GPRMC
{
if (tmpPos1 >= 0 )
{
BYTE *pBuf = pArray->GetData();
char *sBuf = new char[tmpPos2-tmpPos1+1];
ZeroMemory(sBuf,tmpPos2-tmpPos1+1);
CopyMemory(sBuf,pBuf+tmpPos1,tmpPos2-tmpPos1+1);
outStr = CString(sBuf);
//释放内存
delete[] sBuf;
sBuf = NULL;
pArray->RemoveAt(0,tmpPos2);
return TRUE;
}
}
return FALSE;
}
/*
*函数介绍:解析GPS数据
*入口参数:aRecvStr :指待解析的GPS缓冲数据
*出口参数:(无)
*返回值:指CGPSData结构体的指针,如果无效即为:NULL;
*/
PGPSData CGPS::AnalyseGpsData(CString &aRecvStr)
{
CString tmpTime;
CString tmpState;
CString tmpDate;
CString tmpLONG;
CString tmpLONGType;
CString tmpLAT;
CString tmpLATType;
CString tmpSpeed;
LPSTR pStrDate = NULL;
LPSTR pStrTime = NULL;
LPSTR pStrLong = NULL;
LPSTR pStrLongType = NULL;
LPSTR pStrLat = NULL;
LPSTR pStrLatType = NULL;
LPSTR pStrSpeed = NULL;
PGPSData pGpsData = NULL;
int tmpPos,tmpPos1;
int len;
tmpPos = aRecvStr.Find(',',0); //第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
//得到时间
tmpTime = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
tmpTime = tmpTime.Mid(0,2)+L":"+tmpTime.Mid(2,2)+L":"+tmpTime.Mid(4,2);
len = tmpTime.GetLength();
pStrTime = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpTime.GetBuffer(len),len
,pStrTime,len ,NULL,NULL);
//数据状态,是否有效
tmpPos = aRecvStr.Find(',',tmpPos+1); //第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpState = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
if (tmpState != 'A')//代表数据无效,返回
{
if (m_gpsDev_State != GPS_INVALID_DATA)
{
//设置GPS状态
m_gpsDev_State = GPS_INVALID_DATA;
//发送GPS状态变化消息
::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_INVALID_DATA),1);
}
LocalFree(pStrTime);
return NULL;
}
else //代表数据有效
{
if (m_gpsDev_State != GPS_VALID_DATA)
{
//设置GPS状态
m_gpsDev_State = GPS_VALID_DATA;
//发送GPS状态变化消息
::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_VALID_DATA),1);
}
}
//得到纬度值
tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpLAT = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
len = tmpLAT.GetLength();
pStrLat = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLAT.GetBuffer(len),len
,pStrLat,len ,NULL,NULL);
tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpLATType = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
len = tmpLATType.GetLength();
pStrLatType = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLATType.GetBuffer(len),len
,pStrLatType,len ,NULL,NULL);
//得到经度值
tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpLONG = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
len = tmpLONG.GetLength();
pStrLong = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLONG.GetBuffer(len),len
,pStrLong,len ,NULL,NULL);
tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpLONGType = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
len = tmpLONGType.GetLength();
pStrLongType = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLONGType.GetBuffer(len),len
,pStrLongType,len ,NULL,NULL);
//得到车速
tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
tmpSpeed = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
len = tmpSpeed.GetLength();
pStrSpeed = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpSpeed.GetBuffer(len),len
,pStrSpeed,len ,NULL,NULL);
tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值
//得到日期
tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值
tmpPos1 = aRecvStr.Find(',',tmpPos+1);
//格式化一下
tmpDate = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
tmpDate = L"20"+tmpDate.Mid(4,2)+L"-"+tmpDate.Mid(2,2)+L"-"+tmpDate.Mid(0,2);
len = tmpDate.GetLength();
pStrDate = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpDate.GetBuffer(len),len
,pStrDate,len ,NULL,NULL);
pGpsData = new GPSData();
ZeroMemory(pGpsData,sizeof(GPSData));
//得到GPS数据指针
CopyMemory(pGpsData->date,pStrDate,10);
CopyMemory(pGpsData->time,pStrTime,8);
CopyMemory(pGpsData->latitude_type,pStrLatType,1);
CopyMemory(pGpsData->latitude,pStrLat,9);
CopyMemory(pGpsData->longitude_type,pStrLongType,1);
CopyMemory(pGpsData->longitude,pStrLong,10);
//先置默认速度
FillMemory(pGpsData->speed,5,'0');
CopyMemory(pGpsData->speed,pStrSpeed,5);
//释放内存
LocalFree(pStrTime);
LocalFree(pStrDate);
LocalFree(pStrLatType);
LocalFree(pStrLat);
LocalFree(pStrLongType);
LocalFree(pStrLong);
LocalFree(pStrSpeed);
return pGpsData;
}
//GPS接收数据事件
void CALLBACK CGPS::GpsOnSeriesRead(void * powner,BYTE* buf,DWORD dwBufLen)
{
CGPS * pGps = (CGPS*)powner;
//得到本类指针
CByteArray * pArray = &(pGps->m_aRecvBuf);
//得到缓冲区大小
int iMaxSize = pArray->GetSize();
//得到缓冲区所使用的大小
int iUpperBound = pArray->GetUpperBound();
for (int i=0;i<dwBufLen;i++)
{
pArray->Add(*(buf+i));
}
//将收到的数据发给主程序显示出来
char* pRecvBuf = new char[dwBufLen+1];
ZeroMemory(pRecvBuf,dwBufLen+1);
CopyMemory(pRecvBuf,buf,dwBufLen);
//发送接收串口原始数据WINDOWS消息通知
//消息处理完毕后,应释放内存
::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_BUF,WPARAM(pRecvBuf),dwBufLen+1);
CString strGps;
//检查是否已经存在有效的GPS数据
if (pGps->HaveValidGPSData(pArray,strGps))
{
PGPSData pGpsData = NULL;
pGpsData = pGps->AnalyseGpsData(strGps);
if (pGpsData != NULL)
{
//将接收到的GPS数据填充到最新当前数据
pGps->m_gpsCurData = (*pGpsData);
//发送接收有效GPS位置信息WINDOWS消息通知
//由消息处理函数释放内存
::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_VALID_LONGLAT,WPARAM(pGpsData),0);
}
}
}
//检测GPS当前数据
DWORD WINAPI CGPS::GpsCheckThreadFunc(LPVOID lparam)
{
//得到当前GPS指针
CGPS *pGps = (CGPS*)lparam;
int iRecCount = 0;
//然后开始做循环检测,间隔为秒
while (TRUE)
{
//判断两次收到的时间是否相同
if (strcmp(pGps->m_gpsCurData.time,pGps->m_gpsLastData.time) == 0)
{
//计数加
iRecCount++;
}
else
{
//将当前的GPS数据赋给历史值
pGps->m_gpsLastData = pGps->m_gpsCurData;
iRecCount = 0 ;
}
//代表连续三次没有收到数据
if (iRecCount == 3)
{
if (pGps->m_gpsDev_State != GPS_NODATA)
{
//将GPS状态置为“无数据”
pGps->m_gpsDev_State = GPS_NODATA;
//发送GPS状态改变消息
::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_NODATA),1);
}
}
//延时秒
for (int i =0; i<10;i++)
{
//线程退出
if (WaitForSingleObject(pGps->m_hThreadQuitEvent,100) == WAIT_OBJECT_0)
{
goto finish;
}
}
}
finish:
TRACE(L"GPS 检测线程退出/n");
return 0;
}
3:操作类
CGPS *m_pGps; //GPS类对象
// 显示GPS串口接收的数据
afx_msg LONG OnRecvSerialData(WPARAM wParam,LPARAM lParam);
// 显示GPS状态变化
afx_msg LONG OnGpsStateChange(WPARAM wParam,LPARAM lParam);
// 显示GPS有效数据
afx_msg LONG OnRecvValidGps(WPARAM wParam,LPARAM lParam);
ON_MESSAGE(WM_GPS_RECV_BUF,OnRecvSerialData)
ON_MESSAGE(WM_GPS_STATE_CHANGE_MESSAGE,OnGpsStateChange)
ON_MESSAGE(WM_GPS_RECV_VALID_LONGLAT,OnRecvValidGps)
打开GPS
//先检查是否释放
if (m_pGps != NULL)
{
m_pGps->Close();
delete m_pGps;
m_pGps = NULL;
}
//创建GPS逻辑类
m_pGps = new CGPS();
//
if (m_pGps->Open(this,GPS_COM_NO,115200))
{
AfxMessageBox(L"GPS设备打开成功");
}
else
{
AfxMessageBox(L"GPS 设备打开失败");
return ;
}
关闭GPRS
//释放GPS对象
if (m_pGps != NULL)
{
m_pGps->Close();
delete m_pGps;
m_pGps = NULL;
}
三:GPS解析
//获得GPS参数
//注意:从GPS接收到的字符串已经在m_strRecv中,由于是定时接收,所以在这个字符串的头和尾都可能存在
// 不完整的NMEA输出字符串,在处理时要特别注意
//返回:TRUE(格式正确);FALSE(格式错误)
BOOL CGPSDlg::GetGPSParam()
{
int i,j;
CString str,strNEMA;
//先判断是否接收到数据
if (m_strRecv.IsEmpty())
return FALSE;
//若字符串不是以'$'开头的,必须删掉这部分不完整的
if (m_strRecv[0] != '$')
{
i = m_strRecv.Find('/n', 0);
if (i == -1)
return FALSE; //尾部未接收完整,必须等接收完后才能删除
m_strRecv.Delete(0, i+1); //尾部已接收完整(尾部为/r/n结束),删除不完整的部分
}
//截取完整的NMEA-0183输出语句(m_strRecv中可能有多条语句,每条间以/r/n分隔)
for (;;)
{
i = m_strRecv.Find('/n', 0);
if (i == -1)
break; //所有的完整输出语句都已经处理完毕,退出循环
//截取完整的NMEA-0183输出语句
strNEMA = m_strRecv.Left(i+1);
m_strRecv.Delete(0, i+1);
//下面对各种输出语句进行分别处理
if (strNEMA.Find("$GPRMC",0) == 0)
{
//该输出语句中的各项以','分隔
for (i=j=0; strNEMA[i]!='/r'; i++) //j为逗号的计数器
{
if (strNEMA[i] == ',')
{
j++;
str = "";
for (i++; strNEMA[i]!=','&&strNEMA[i]!='/r'; i++)
str += strNEMA[i]; //str为某项的值
i--;
//对各项数据分别处理
switch (j)
{
case 1: //时间(UTC)
m_strTime = str.Left(6);
m_strTime.Insert(2, ':');
m_strTime.Insert(5, ':');
break;
case 2: //状态(A-数据有效;V-数据无效,还未定位)
if (str == "A")
m_strStatus = "有效数据";
else if(str == "V")
m_strStatus = "正在定位...";
else
m_strStatus = "非法数据格式";
break;
case 3: //纬度(ddmm.mmmm)
str.Insert(2, "度");
str += "分";
m_strLatitude = str;
break;
case 4: //纬度指示(N-北纬;S-南纬)
if (str == "N")
m_strLatitude.Insert(0, "北纬");
else
m_strLatitude.Insert(0, "南纬");
break;
case 5: //经度(dddmm.mmmm)
str.Insert(3, "度");
str += "分";
m_strLongitude = str;
break;
case 6: //经度指示(E-东经;W-西经)
if (str == "E")
m_strLongitude.Insert(0, "东经");
else
m_strLongitude.Insert(0, "西经");
break;
case 7: //速度(单位:节)
m_strSpeed = str;
break;
case 8: //航向(单位:度)
m_strCourse = str;
break;
case 9: //日期(UTC)
m_strDate = "";
m_strDate += "20";
m_strDate += str[4];
m_strDate += str[5];
m_strDate += "-";
m_strDate += str[2];
m_strDate += str[3];
m_strDate += "-";
m_strDate += str[0];
m_strDate += str[1];
break;
default:
break;
}
}
}
}
else if (strNEMA.Find("$GPGGA",0) == 0)
{
}
else if (strNEMA.Find("$GPGSA",0) == 0)
{
}
else if (strNEMA.Find("$GPGSV",0) == 0)
{
}
else if (strNEMA.Find("$GPGLL",0) == 0)
{
}
else if (strNEMA.Find("$GPVTG",0) == 0)
{
}
else
return FALSE; //格式错误
}
return TRUE;
}