用GPS模块校准系统时间

http://blog.youkuaiyun.com/norains/article/details/4526557

//========================================================================
  //TITLE:
  //    用GPS模块校准系统时间
  //AUTHOR:
  //    norains
  //DATE:
  //    Friday 04-September-2009
  //Environment:
  //    WINDOWS CE 5.0
  //========================================================================

    众所周知,大家在使用XP桌面系统的时候,我们可以通过设置时间的属性,让其通过互联网进行校准,这样我们就不会为不准点而烦恼了。但这在大部分车载设备上就行不通,因为它们往往不会带有网络模块,更不用提在大马路上还要随处可见无线网络。
   
    条条大路通罗马。用不了互联网,那我们就用GPS模块的数据咯。做过GPS导航的人应该都不陌生,GPS模块会每隔1秒就会不停地往串口发送数据。其中数据包含的信息可就多了,经度,维度,速度,当然还少不了我们所需要的时间。
   
    本文的主题是时间,所以GPS模块的其它数据就随它去吧,我们只要知道带有$GPRMC标志开头的数据包含时间信息即可。
   
    我们先来看看$GPRMC数据各位代表的意义.
   
    $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
  <1> UTC时间,hhmmss(时分秒)格式
  <2> 数据状态,A=有效,V=无效
  <3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
  <4> 纬度半球N(北半球)或S(南半球)
  <5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)
  <6> 经度半球E(东经)或W(西经)
  <7> 地面速率(000.0~999.9节,前面的0也将被传输)
  <8> 地面航向(000.0~359.9度,以正北为参考基准,前面的0也将被传输)
  <9> UTC日期,ddmmyy(日月年)格式
  <10> 磁偏角(000.0~180.0度,前面的0也将被传输)
  <11> 磁偏角方向,E(东)或W(西)
  <12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

    在这12组数据之中,我们需要用到的是2和9。第9项大家都好理解,其中保存的是时间数据嘛,不用它还用谁呢?可第2项呢?为什么会有无效或有效这两种状态呢?其实,对于GPS模块来说,并不是检测到数据有效才会往外发送数据;而是不管三七二十一,有效还是无效,源源不断地往外发送数据。判断数据是否有效的唯一方式,就是查看第二项是否为A。
   
    于是,我们的软件流程就很简单明了:不停地从GPS模块中读取数据,然后判断标志头是否为$GPRMC,接着再查看第二项是否为A,如果为A有效,直接提取第9项UTC时间,通过SetSystemTime进行设置。
   
    以下给出一个从GPS数据中提取UTC时间并转化为SYSTEMTIME结构的函数代码:

  1. BOOL CGpsTimeSync::GetUTCFromGPS(const std::string &strBuf,SYSTEMTIME &sysTime)  
  2. {  
  3.     BOOL bResult = FALSE;  
  4.     std::string::size_type stPosBegin = 0;  
  5.   
  6.     memset(&sysTime,0,sizeof(sysTime));  
  7.       
  8.     stPosBegin = strBuf.find("$GPRMC");  
  9.     if(stPosBegin == std::string::npos || strBuf.find("*") == std::string::npos )   
  10.     {  
  11.         return FALSE;  
  12.     }  
  13.   
  14.     std::vector<char> vtBuf(10,0);          
  15.     int iCount = 0;  
  16.   
  17.     //The UTC time for hhmmss   
  18.     stPosBegin = strBuf.find(",",stPosBegin + 1);  
  19.     if(stPosBegin != std::string::npos)  
  20.     {  
  21.         iCount ++;  
  22.   
  23.         //The hour   
  24.         memset(&vtBuf[0],0,vtBuf.size());  
  25.         strncpy(&vtBuf[0],&(strBuf.c_str()[stPosBegin + 1]),2);  
  26.         sysTime.wHour = atoi(&vtBuf[0]);  
  27.   
  28.         //The minute   
  29.         memset(&vtBuf[0],0,vtBuf.size());  
  30.         strncpy(&vtBuf[0],&(strBuf.c_str()[stPosBegin + 3]),2);  
  31.         sysTime.wMinute = atoi(&vtBuf[0]);  
  32.   
  33.         //The second   
  34.         memset(&vtBuf[0],0,vtBuf.size());  
  35.         strncpy(&vtBuf[0],&(strBuf.c_str()[stPosBegin + 5]),2);  
  36.         sysTime.wSecond = atoi(&vtBuf[0]);  
  37.     }  
  38.   
  39.   
  40.     //The date   
  41.     while(TRUE)  
  42.     {  
  43.         stPosBegin = strBuf.find(",",stPosBegin + 1);  
  44.         if(stPosBegin != std::string::npos)  
  45.         {  
  46.             iCount ++;  
  47.               
  48.             if(iCount == 2 && (strBuf.c_str())[stPosBegin + 1] == 'A')  
  49.             {  
  50.                 bResult = TRUE;  
  51.             }  
  52.             else if(iCount >= 9)  
  53.             {  
  54.                 //The day   
  55.                 memset(&vtBuf[0],0,vtBuf.size());  
  56.                 strncpy(&vtBuf[0],&(strBuf.c_str()[stPosBegin + 1]),2);  
  57.                 sysTime.wDay = atoi(&vtBuf[0]);  
  58.   
  59.                 //The month   
  60.                 memset(&vtBuf[0],0,vtBuf.size());  
  61.                 strncpy(&vtBuf[0],&(strBuf.c_str()[stPosBegin + 3]),2);  
  62.                 sysTime.wMonth = atoi(&vtBuf[0]);  
  63.   
  64.                 //The year   
  65.                 memset(&vtBuf[0],0,vtBuf.size());  
  66.                 strncpy(&vtBuf[0],&((strBuf.c_str())[stPosBegin + 5]),2);  
  67.                 sysTime.wYear = atoi(&vtBuf[0]);  
  68.   
  69.                 //It's 2008 now when I write the source code. It cann't be less than 2008 forever when the GPS data is validate.  
  70.                 if(sysTime.wYear < 2000)  
  71.                 {  
  72.                     sysTime.wYear += 2000;  
  73.                 }  
  74.   
  75.                 break;  
  76.             }  
  77.         }  
  78.         else  
  79.         {  
  80.             break;  
  81.         }  
  82.     }  
  83.     return bResult;;  
  84. }  

 

    代码没有什么出彩的地方,唯一需要注意一点的是,因为年是以两位数表示,并且现在已经过了2000年,所以在代码中简单性地手工加了2000。我无法保证这代码永远正确,但至少在2099年之前还是能正常运作的。:-)

 

    调用很简单:

  1.  //获取GPS数据   
  2.  strData = GetGPSData();  
  3.    
  4.  //提取UTC时间   
  5.  SYSTEMTIME sysTimeUTC = {0};  
  6. if(GetUTCFromGPS(strData,sysTimeUTC) != FALSE)  
  7. {  
  8.   //设置系统时间   
  9.     SetSystemTime(&sysTimeUTC);  
  10. }  

 

   还有一点需要注意的是,在WINCE中,串口是独占设备。也就是说,如果你在程序中打开了串口进行数据的监控,那么在你进入导航软件之前,必须要在程序中将串口关闭。还有另一种方法,就是用虚拟串口的方式,让多个进程能同时获取数据(请见该文:http://blog.youkuaiyun.com/norains/archive/2009/03/28/4032257.aspx#1140231)。
  
   这校准时间的方式还有一个非常变态的用法。大家都知道,CPU都会有一个32.768KHZ的RTC晶振,用来给CPU准确计时。如果该晶振不工作,那么系统时钟将会停止。这时候,你就可以每隔1秒钟通过读取GPS数据对系统时间进行设置,让时间看起来真的是在不停变动一样。不过,这样方式比较耗费资源,实用性也不大,偶尔无聊时可为之。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值