经常需求使用 GPS 串口 NMEA 解析的功能,写了一段代码来完成引功能。
分享一下,大家一起学习。
头文件:
#ifndef _GPS_MONITER_HH_
#define _GPS_MONITER_HH_
BOOL InitSerailPort(CString csSerialPort,LPVOID pParent = NULL);
DWORD WINAPI ReadNMEAThread(LPVOID lpParameter);
void SetSystemTimeFormUTC(CString csDate,CString csUTCTime);
void DeinitSerialPort(void);
#endif
源文件:
#include "stdafx.h"
#include "GPSMoniter.h"
#define SATTATOLNUMBER 32
// 用于在 GPS 监控界面显示 NMEA 信息
char gcBuff[4096];
CString gcsTime;
CString gcsDate;
int gdSignalNumber[SATTATOLNUMBER];
CString csLat;
CString csLatdir;
CString csLon;
CString csLondir;
CString csAltitude;
CString csSpeed;
CString csOrientation;
int nNumDisplayed;
int giGSVSatNumber;
HANDLE ghCommHandle;
HANDLE nmeathread_hand; // Global handle to the NMEA reading thread
CString gcsGPSState; //定位: A; 导航: V
CString gcsTimeOp;
CString gcsLatField;
CString gcsLonField;
int giResult;
int giSalNumber;
int giGSVCurrentPackage;
int giHourDiff;
BOOL InitSerailPort(CString csSerialPort,LPVOID pParent)
{
DCB commDCB;
COMMTIMEOUTS timeouts;
ghCommHandle = CreateFile(csSerialPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
if(INVALID_HANDLE_VALUE == ghCommHandle)
{
RETAILMSG(1, (TEXT("Opening GPS %s failed: %d!\r\n"),csSerialPort,(int)GetLastError()));
return FALSE;
}
commDCB.DCBlength = sizeof (DCB);
if(!GetCommState(ghCommHandle,&commDCB))
{
CloseHandle(ghCommHandle);
ghCommHandle = INVALID_HANDLE_VALUE;
RETAILMSG(1,(L"Failed in getting GPS %s DCB settings: %d!\r\n",csSerialPort,(int)GetLastError()));
return FALSE;
}
commDCB.DCBlength = sizeof(DCB);
commDCB.BaudRate = 9600; // Current baud
commDCB.ByteSize = 8; // Number of bits/bytes, 4-8
commDCB.Parity = NOPARITY; // 0-4=no,odd,even,mark,space
commDCB.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2
// Setting serial port to Centrality speicifcations
if(!SetCommState(ghCommHandle,&commDCB))
{
CloseHandle(ghCommHandle);
ghCommHandle = INVALID_HANDLE_VALUE;
RETAILMSG(1,(L"Error in trying to set GPS %s DCB settings: %d!\r\n",csSerialPort,(int)GetLastError()));
return FALSE;
}
// Get the default timeout settings for port
if(!GetCommTimeouts(ghCommHandle, &timeouts))
{
CloseHandle(ghCommHandle);
ghCommHandle = INVALID_HANDLE_VALUE;
RETAILMSG(1,(L"Failed in getting GPS %s timeout settings: %d!\r\n",csSerialPort,(int)GetLastError()));
return FALSE;
}
// RETAILMSG(1,(L"%s DCB set successfully!\r\n",csSerialPort));
// Change the timeouts structure settings to Centrality settings
timeouts.ReadIntervalTimeout = 500;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
// Set the time-out parameters for all read and write operations on the port.
if(!SetCommTimeouts(ghCommHandle,&timeouts))
{
CloseHandle(ghCommHandle);
ghCommHandle = INVALID_HANDLE_VALUE;
RETAILMSG(1,(L"Error in trying to set GPS %s timeout settings: %d!\r\n",csSerialPort,(int)GetLastError()));
return FALSE;
}
if(0 != SetCommMask(ghCommHandle,EV_RXCHAR))
{
RETAILMSG(1,(L"==Set %s mask OK!\r\n",csSerialPort));
}
else
{
RETAILMSG(1,(TEXT("==Set %s mask failure:%d!\r\n"),csSerialPort,GetLastError()));
}
nmeathread_hand = CreateThread(NULL,0,ReadNMEAThread,pParent,0,NULL);
//nmeathread_hand = CreateThread(NULL, 0, ReadNMEAThread, this, CREATE_SUSPENDED, NULL);
if(!nmeathread_hand)
{
RETAILMSG(1, (L"Could not create NMEA read thread.\r\n"));
return FALSE;
}
else
{
//SetThreadPriority(nmeathread_hand, THREAD_PRIORITY_BELOW_NORMAL);
//ResumeThread(nmeathread_hand);
}
SYSTEMTIME stUTC;
SYSTEMTIME stLocal;
GetLocalTime(&stLocal);
GetSystemTime(&stUTC);
giHourDiff = stLocal.wHour - stUTC.wHour;
return TRUE;
}
// nmeathread_hand = CreateThread(NULL, 0, ReadNMEAThread, this, 0, NULL);
DWORD WINAPI ReadNMEAThread(LPVOID lpParameter)
{
//DECLARE_USER_MESSAGE(UWM_NEW_NMEA);
int start, endline, onestart, oneend, linelen, degdig, iPos;
ULONG bytesRead;
DWORD EventMask = EV_RXCHAR;
CString field;
TCHAR *stopstring;
static int iCount = 0;
CWnd *mpNmea;
mpNmea = (CWnd*)lpParameter;
// Wait on the event
while(WaitCommEvent(ghCommHandle, &EventMask, NULL))
{
// RETAILMSG(1,(TEXT("---------------------------------------------ReadNMEAThread:%d,Tick: %d\r\n"),iCount++,GetTickCount()));
// Clear the buffer before you start reading
memset(gcBuff, 0, 4096);
// Read from serial port (4b)
if (ReadFile(ghCommHandle, gcBuff, 4096, &bytesRead, NULL))
{
if(bytesRead == 0)
continue;
CString dacstr(gcBuff); /*Leo:从串口读GPS卫星数据*/
start = 0;
endline = 0;
// Parse/Process the output (4c)
while(1)
{
int i = 0;
start = dacstr.Find(L"$G", start);
if(start < 0)
break;
endline = dacstr.Find(L"\r\n", start);
if(endline < 0)
break;
linelen = endline - start;
//DebugOutput(MSG_DEBUG, "GPSViewer msg: start = %d endline = %d length = %d", start, endline, linelen);
// Extract one line
CString oneline;
oneline = dacstr.Mid(start, linelen);
#if _DEBUG
// RETAILMSG(1,(TEXT("*******************************GPSViewer msg: Oneline = %s\r\n"),oneline));
#endif
onestart = 0;
oneend = 0;
i = 0;
//
//$GPRMC,075017.31,V,2232.6057,N,11356.3074,E,,,190708,,W,N*15
//$GPRMC,080525.82,A,2232.5196,N,11356.3719,E,,,190708,,W,A*08
if(oneline.Left(6) == L"$GPRMC")
{
while((iPos = oneline.Find(L",")) >= 0)
{
field = oneline.Left(iPos);
i ++;
oneline = oneline.Mid(iPos + 1);
if(3 == i)
{
gcsGPSState = field;
}
else if(10 == i) // <9>当前UTC日期ddmmyy 格式 - 例如: 030222
{
gcsDate = field;
}
}
}
//
//$GPGGA,080514.82,2232.5203,N,11356.3719,E,1,6,1.327,239.386,M,,M,,*4D
else if(oneline.Left(6) == L"$GPGGA")
{
while((iPos = oneline.Find(L",")) >= 0)
{
static int iOrientation = 0;
field = oneline.Left(iPos);
i ++;
oneline = oneline.Mid(iPos + 1);
if (i == 2)
{
//
if(iOrientation != giResult)
{
RETAILMSG(1,(L"[GPS]Status of GPS is changed: %d(Old: %d)\r\n",giResult,iOrientation));
iOrientation = giResult;
}
//
gcsTimeOp = field;
// 将格式从: --:--:--.-- 修改为: --:--:-- ,即不显示秒后的数据 ---Leo 2009-03-26
CString csTmp = gcsTimeOp.Right(5);
CString csHour = gcsTimeOp.Left(2);
int iHour = _wtoi(csHour);
if(iHour + giHourDiff < 24)
{
csHour.Format(L"%d",iHour + giHourDiff);
}
else
{
csHour.Format(L"%d",((iHour + giHourDiff) - 24));
}
// gcsTime = csHour + L":" + time.Mid(2,2) + L":" + csTmp.Left(2);
gcsTime = csHour + gcsTimeOp.Mid(2,2) + csTmp.Left(2);
}
else if (i == 3)
{ //Get Latitude from GGA - Value may not be valid. Check flag to be sure.
gcsLatField = field;
degdig = gcsLatField.GetLength() - 2;
csLat = gcsLatField.Left(2) + CString(" ") + gcsLatField.Right(degdig);
}
else if (i == 4)
{ //Get Latitude Direction (N,S) from GGA - Value may not be valid. Check flag to be sure.
csLatdir = field;
}
else if (i == 5)
{ //Get Longitude from GGA - Value may not be valid. Check flag to be sure.
gcsLonField = field;
degdig = gcsLonField.GetLength() - 3;
csLon = gcsLonField.Left(3) + CString(" ") + gcsLonField.Right(degdig);
}
else if (i == 6)
{ //Get Longitude Direction (E,W) from GGA - Value may not be valid. Check flag to be sure.
csLondir = field;
}
else if (i == 7) //<6>GPS状态批示0-未定位 1-无差分定位信息 2-带差分定位信息
{ //Get Flag from GGA indicating position fix. Position output from GGA is valid.
giResult = atoi((const char*)((LPCTSTR)field));
if(0 == giResult)
{
// RETAILMSG(1,(TEXT("===No orientation\r\n")));
}
else if(1 == giResult)
{
// RETAILMSG(1,(TEXT("===Orientation with no difference\r\n")));
}
else if(2 == giResult)
{
// RETAILMSG(1,(TEXT("===Orientation with difference\r\n")));
}
}
else if (i == 10)
{
csAltitude = field;
}
}
}
//$GPGSV,3,3,11,29,21,93,36,30,33,40,36,31,49,324,*46
else if (oneline.Left(6) == L"$GPGSV")
{
while((iPos = oneline.Find(L",")) >= 0)
{
field = oneline.Left(iPos);
i ++;
oneline = oneline.Mid(iPos + 1);
if(3 == i)
{
if(_ttoi(field) > 0)
{
giGSVCurrentPackage = _ttoi(field);
if (giGSVCurrentPackage == 1) // new GSV sentence
{
nNumDisplayed = 0;
for(int j = 0;j < SATTATOLNUMBER;j++)
{
gdSignalNumber[j] = -1;
}
}
}
}
else if(4 == i)
{
if(_tcstod(field, &stopstring) > 0)
{
giGSVSatNumber = (int)_tcstod(field, &stopstring);
}
}
else if(0 == (i - 5) % 4) //卫星的PRV号星号
{
if (_ttoi(field) > 0)
{
giSalNumber = _ttoi(field);
}
}
else if(3 == (i - 5) % 4) // SNR
{
if (_ttoi(field) > 0)
{
gdSignalNumber[giSalNumber-1] = _ttoi(field);
nNumDisplayed++;
}
}
}
if ((iPos = oneline.Find(L"*")) >= 0) // last sat
{
field = oneline.Left(iPos);
i ++;
if(3 == (i - 5) % 4) // SNR
{
if (_ttoi(field) > 0)
{
gdSignalNumber[giSalNumber-1] = _ttoi(field);
nNumDisplayed++;
}
}
}
}
//$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh
// <1> 对地航向(单位:度)
// <2> 磁偏角(单位:度)
// <3> 对地航速(单位:哩/小时)
// <4> 地面速率(0000.0~1851.8公里/小时,前面的0也将被传输)
//如: $GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
else if(oneline.Left(6) == L"$GPVTG")
{
while((iPos = oneline.Find(L",")) >= 0)
{
field = oneline.Left(iPos);
i ++;
oneline = oneline.Mid(iPos + 1);
if(2 == i)
{
csOrientation = field;
}
else if(8 == i)
{
csSpeed = field;
}
}
}
start = endline + 2;
} // end of buffer processing
}
//Sleep(1000);// end of ReadFile
} // end of WaitCommEvent
{
// RETAILMSG(1,(TEXT("Exit GPS thread:%d"),GetLastError()));
}
return 0;
}// end of ReadNMEAThread
//csDate: 01-01-03
//csUTCTime: 04:07:44.08
void SetSystemTimeFormUTC(CString csDate,CString csUTCTime)
{
SYSTEMTIME st;
int iHour = 0;
int iMinute = 0;
int iSecond = 0;
int iYear = 0;
int iMonth = 0;
int iDay = 0;
CString csSubString;
TCHAR *stopstring;
// RETAILMSG(1,(TEXT("====Set system time from UTC.Date: %s, Time: %s"),csDate,csUTCTime));
GetSystemTime(&st);
csSubString = csDate.Left(2);
iDay = (int)_tcstod(csSubString, &stopstring);
csSubString = csDate.Mid(3,2);
iMonth = (int)_tcstod(csSubString, &stopstring);
csSubString = csDate.Right(2);
iYear = 2000 + (int)_tcstod(csSubString, &stopstring);
st.wYear = iYear;
st.wMonth = iMonth;
st.wDay = iDay;
csSubString = csUTCTime.Left(2);
iHour = (int)_tcstod(csSubString, &stopstring);
csSubString = csUTCTime.Mid(3,2);
iMinute = (int)_tcstod(csSubString, &stopstring);
csSubString = csUTCTime.Mid(6,2);
iSecond = (int)_tcstod(csSubString, &stopstring);
st.wHour = iHour;
st.wMinute = iMinute;
st.wSecond = iSecond;
SetSystemTime(&st);
}
void DeinitSerialPort(void)
{
SetCommMask(ghCommHandle,0);
if(nmeathread_hand)
{
TerminateThread(nmeathread_hand,1);
CloseHandle(nmeathread_hand);
}
if(INVALID_HANDLE_VALUE != ghCommHandle)
{
EscapeCommFunction(ghCommHandle,CLRDTR);
EscapeCommFunction(ghCommHandle,CLRRTS);
//清除驱动程序内部的发送和接收队列
PurgeComm(ghCommHandle,PURGE_TXCLEAR|PURGE_RXCLEAR);
CloseHandle(ghCommHandle);
ghCommHandle = INVALID_HANDLE_VALUE;
}
}