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。如有疑问请在下方留言