GPS数据格式解析源代码举例
随着内置GPS的手机越来越多,GPS相关的应用也越来越广泛,GPS已经不仅仅只是得到一个经纬度的信息,可以通过GPS开发出更多的应用,比如位置图片,比如好友位置显示,比如跟踪等等,TimeSyncPPC就是可以使用GPS的时钟来进行时间同步的。
所有这些功能都需要知道GPS的数据格式并能够解析出自己需要的数据出来。下面就以TimeSyncPPC中如何得到GPS的日期和时间为例来说明如何解析GPS数据。
TimeSyncPPC是用于Pocket PC上的时间同步工具,因此得到GPS的时间和日期,使用的GPS的指令是$GPRMC,其指令格式如下:
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>
1) 标准定位时间(UTC time)格式:时时分分秒秒.秒秒秒(hhmmss.sss)。
2) 定位状态,A = 数据可用,V = 数据不可用。
3) 纬度,格式:度度分分.分分分分(ddmm.mmmm)。
4) 纬度区分,北半球(N)或南半球(S)。
5) 经度,格式:度度分分.分分分分。
6) 经度区分,东(E)半球或西(W)半球。
7) 相对位移速度, 0.0 至 1851.8 knots
8) 相对位移方向,000.0 至 359.9度。实际值。
9) 日期,格式:日日月月年年(ddmmyy)。
10) 磁极变量,000.0 至180.0。
11) 度数。
12) Checksum.(检查位)
从数据格式中可以看出,我们需要得到1和9两个字段的数据即可。
当然,使用GPS首先要打开GPS的串口,代码如下:
HANDLE OpenCom(CString strCom, DWORD BaudRate, BYTE ByteSize, BYTE StopBits, BYTE Parity, int FlowControl)
{
HANDLE hCommPort;
CString strTemp;
if((hCommPort = CreateFile(strCom, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
{
return NULL;
}
DCB commDCB;
CString strWinText;
GetCommState(hCommPort, &commDCB);
commDCB.BaudRate = BaudRate;
commDCB.ByteSize = ByteSize;
commDCB.StopBits = StopBits;
commDCB.fParity = (NOPARITY == Parity) ? FALSE : TRUE;
commDCB.Parity = Parity;
commDCB.fDsrSensitivity = FALSE;
commDCB.fDtrControl = DTR_CONTROL_ENABLE;
if(FlowControl == 1) // Hardware
{
// Enable RTS/CTS Flow Control
commDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
commDCB.fOutxCtsFlow = 1;
commDCB.fOutX = 0;
commDCB.fInX = 0;
}
else if(FlowControl == 0) // Software
{
// Enable XON/XOFF Flow Control
commDCB.fRtsControl = RTS_CONTROL_ENABLE;
commDCB.fOutxCtsFlow = 0;
commDCB.fOutX = 1;
commDCB.fInX = 1;
}
else
{
commDCB.fRtsControl = RTS_CONTROL_ENABLE;
commDCB.fOutxCtsFlow = 0;
commDCB.fOutX = 0;
commDCB.fInX = 0;
}
SetCommState(hCommPort, &commDCB);
return hCommPort;
}
串口打开后需要得到一行GPS数据,GPS数据是以结束,代码如下:
// 返回值为-1:超时,其他值为数据长度
int GetGPSLineData(HANDLE hCommPort, char *ReadBuf, int Length)
{
// 读一行GPS数据
DWORD nBytes;
int i = 0;
DWORD TimeOut = 0;
while(i < Length)
{
if(g_isQuit)
return i;
TimeOut ++;
if(TimeOut >= 100)
return -1;
ReadFile(hCommPort, (LPVOID)&ReadBuf[i], 1, &nBytes, NULL);
if(nBytes == 0)
{
Sleep(1);
continue;
}
TimeOut = 0;
if(ReadBuf[i] == 0x0d)
{
ReadBuf[i] = 0;
break;
}
if(ReadBuf[i] == 0x0a)
{
continue;
}
i ++;
}
return i;
}
下面是解析一行GPS数据,将数据结果存储在一个数组中,代码如下:
// 解析一行GPS数据
int AnalyzeGPSData(char *GPSData, int Length, char Command[][100])
{
int i = 0;
int j = 0, k = 0;
while(i < Length)
{
if(GPSData[i] == ',')
{
Command[j][k] = 0;
j ++;
k = 0;
i ++;
continue;
}
Command[j][k] = GPSData[i];
k ++;
i ++;
}
return j;
}
解析之后的GPS数据包含命令和参数,下面就是判断是否是我们需要的命令,如果是得到第一个和第九个参数即可。不过要注意,GPS时间是世界标准时间,因此要转换成本地时间,代码如下:
DWORD WINAPI GetGPSTime(LPVOID lpParameter)
{
char ReadBuf[1000];
DWORD TimeOut = 0;
char Command[40][100];
HANDLE hCommPort = OpenCom(g_strComPort, g_BaudRate);
if(hCommPort == NULL)
{
MessageBox(NULL, _T("Can not open COM port!"), NULL, MB_OK | MB_TOPMOST);
goto GPSExit;
}
while(1)
{
if(g_isQuit)
break;
// 读一行GPS数据
int Length = GetGPSLineData(hCommPort, ReadBuf, 999);
if(Length == -1)
{
TimeOut ++;
if(TimeOut < 100)
continue;
}
if(TimeOut >= 100)
{
MessageBox(NULL, _T("Read GPS data timeout!"), NULL, MB_OK | MB_TOPMOST);
goto GPSExit;
}
TimeOut = 0;
AnalyzeGPSData(ReadBuf, Length, Command);
if(strcmp(Command[0], "$GPRMC") == 0)
{
SYSTEMTIME st, LocalSt;
st.wHour = (Command[1][0] - '0') * 10 + (Command[1][1] - '0');
st.wMinute = (Command[1][2] - '0') * 10 + (Command[1][3] - '0');
st.wSecond = (Command[1][4] - '0') * 10 + (Command[1][5] - '0');
st.wMilliseconds = (Command[1][7] - '0') * 100 + (Command[1][8] - '0') * 10 + (Command[1][9] - '0');
st.wDay = (Command[9][0] - '0') * 10 + (Command[9][1] - '0');
st.wMonth = (Command[9][2] - '0') * 10 + (Command[9][3] - '0');
st.wYear = (Command[9][4] - '0') * 10 + (Command[9][5] - '0') + 2000;
FILETIME FileTime, LocalFileTime;
SystemTimeToFileTime(&st, &FileTime);
FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
FileTimeToSystemTime(&LocalFileTime, &LocalSt);
g_GPSDateCtrl->SetTime(LocalSt);
g_GPSTimeCtrl->SetTime(LocalSt);
if(strcmp(Command[2], "A") == 0)
{
BOOL ret = SetSystemTime(&st);
MessageBox(NULL, _T("SyncTime Success!"), _T(""), MB_OK | MB_TOPMOST);
strGPSButton = _T("Get GPS Time");
g_ThisDlg->SetDlgItemText(IDC_GETGPSTIME, strGPSButton);
g_ThisDlg->GetDlgItem(IDC_SETGPSTIME)->EnableWindow(0);
break;
}
else
{
TimeOut ++;
}
}
}
GPSExit:
if(hCommPort != NULL)
CloseHandle(hCommPort);
g_GetGPSTimeThread = NULL;
strGPSButton = _T("Get GPS Time");
g_ThisDlg->SetDlgItemText(IDC_GETGPSTIME, strGPSButton);
g_ThisDlg->GetDlgItem(IDC_SETGPSTIME)->EnableWindow(0);
return TRUE;
}
如果需要解析其他指令,可以参考函数DWORD WINAPI GetGPSTime(LPVOID lpParameter)的实现方法,其实还是比较简单的。
这些代码已经在TimeSyncPPC中测试过,可以直接使用,开发环境为VS2005,Windows Mobile 6.1 for PPC。如有疑问请在下方留言