Linux C 用GPS时间更新系统时间的方法。
思路:
1、GPS模块会自动收到带时间信息的消息。
GPS模块会收到很多的协议消息带时间信息的。我们选择"$GPRMC"这条协议。其中的时间格式有的是hhmmss(时分秒)
有的是的精确到秒后两位hmmss.ss(时分秒),有的是的精确到秒后三位hmmss.ss(时分秒),具体估计需要看实际模块输出。
2、从消息中解析出GPS收到的时间
3、如系统时间和GPS时间差异大时,用GPS时间更新系统时间
GPS协议介绍:
GPS遵循一下NMEA 0183 协议
NMEA 0183 协议
NMEA 0183 是美国国家海洋电子协会(National Marine Electronics Association)为海用电子设备制定的标准格式。目前业已成了 GPS 导航设备统一的 RTCM(Radio Technical Commission for Maritime services)标准协议。NMEA0183 协议采用 ASCII 码来传递 GPS 定位信息,我们称之为帧。帧格式形如:
$aaccc,ddd,ddd,„,ddd*hh(CR)(LF)
1. “$”:帧命令起始位
2. “aaccc”:地址域,前两位为识别符(aa),后三位为语句名(ccc)
3. “ddd,ddd”:数据内容
4. “*”:校验和前缀(也可以作为语句数据结束的标志)
5. “hh”:校验和(check sum),$与*之间所有字符 ASCII 码的校验和(各字 节做异或运算,得到校验和后,再转换 16 进制格式的 ASCII 字符)
6. “(CR)(LF)”:帧结束,回车和换行符
主要命令:
序号 命令 说明 最大帧长(Byte)
1 $GPRMC 推荐定位信息 70
2 $GPGGA GPS 定位信息 72
3 $GPVTG 地面速度信息 34
4 $RHXZ 地磁信息 24
5 $GPGSA 当前卫星信息 65
6 $GPGSV 可见卫星数 210
7 $GPGLL 大地坐标信息
指令解析:
1.$GPRMC(推荐定位信息,Recommended Minimum Specific GPS/Transit Data) $GPRMC 语句的基本格式如下: $GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
(1)UTC 时间,hhmmss(时分秒)
(2)定位状态,A=有效定位,V=无效定位
(3)纬度 ddmm.mmmmm 度分)
(4)纬度半球 N(北半球)或 S(南半球)
(5)经度 dddmm.mmmmm 度分)
(6)经度半球 E(东经)或 W(西经)
(7)地面速率(000.0~999.9 节)
(8)地面航向(000.0~359.9 度,以真北方为参考基准)
(9)UTC 日期,ddmmyy(日月年)
(10)磁偏角(000.0~180.0 度,前导位数不足则补 0)
(11)磁偏角方向,E(东)或 W(西) (12)模式指示(A=自主定位,D=差分,R=RTK,E=估算,N=数据无效) 举例如下: $GPRMC,084103.00,A,2233.395441,N,11356.556656,E,0.035,,220618,,,A*7A
2.$GPGGA(GPS 定位信息,Global Positioning System Fix Data) $GPGGA 语句的基本格式如下(其中 M 指单位 M,下同): $GPGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)( LF)
(1)UTC 时间,格式为 hhmmss.ss;
(2)纬度,格式为 ddmm.mmmmmm 度分格式);
(3)纬度半球,N 或 S(北纬或南纬);
(4)经度,格式为 dddmm.mmmmmm 度分格式);
(5)经度半球,E 或 W(东经或西经);
(6)GPS 状态,0=未定位,1=非差分定位,2=差分定位;
(7)正在使用的用于定位的卫星数量(00~12)
(8)HDOP 水平精确度因子(0.5~99.9)
(9)海拔高度(-9999.9 到 9999.9 米)
(10)大地水准面高度(-9999.9 到 9999.9 米)
(11)差分时间(从最近一次接收到差分信号开始的秒数,非差分定位,此项为 空)
(12)差分参考基站标号(0000 到 1023,首位 0 也将传送,非差分定位,此项为 空) 举例如下: $GPGGA,070343.90,2236.360900,N,11352.021690,E,1,04,68.82,-72.83,M,- 1.00,M,,*68
3.$GPVTG(地面速度信息,Track Made Good and Ground Speed) $GPVTG 语句的基本格式如下: $GPVTG,(1),T,(2),M,(3),N,(4),K,(5)*hh(CR)(LF)
(1)以真北为参考基准的地面航向(000~359 度,前面的 0 也将被传输)
(2)以磁北为参考基准的地面航向(000~359 度,前面的 0 也将被传输)
(3)地面速率(000.0~999.9 节,前面的 0 也将被传输)
(4)地面速率(0000.0~1851.8 公里/小时,前面的 0 也将被传输)
(5)模式指示(A=自主定位,D=差分,E=估算,N=数据无效) 举例如下: $GPVTG,,T,,M,0.106,N,0.196,K,A*2A
4.$RHXZ(地磁传感器信息) $RHXZ 语句的基本格式如下: $RHXZ,(1),(2),(3) *hh(CR)(LF)
(1)地磁传感器 X 轴的 16 进制值(高位在前,如 0057 表示 0x0057,范围 0000~FFFF,前面的 0 也将被传输)
(2)地磁传感器 Y 轴的 16 进制值(高位在前,如 FE6E 表示 0xFE6E,范围 0000~FFFF,前面的 0 也将被传输)
(3)地磁传感器 Z 轴的 16 进制值(高位在前,如 0210 表示 0x0210,范围 0000~FFFF,前面的 0 也将被传输) 举例如下: $RHXZ,0057,FE6E,0210*45
5.$GPGSA(当前卫星信息) $GPGSA 语句的基本格式如下: $GPGSA,(1),(2),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(3),(4),(5 ),(6)*hh(CR)(LF)
(1)模式,M = 手动,A = 自动。
(2)定位类型,1=未定位,2=2D 定位,3=3D 定位。
(3)正在用于定位的卫星号(01~32)
(4)PDOP 综合位置精度因子(0.5-99.9)
(5)HDOP 水平精度因子 1(0.5-99.9)
(6)VDOP 垂直精度因子(0.5-99.9) 举例如下: $GPGSA,A,3,26,02,05,29,15,21,,,,,,,2.45,1.49,1.94*0E
6.$GPGSV(可见卫星数,GPS Satellites in View) $GPGSV 语句的基本格式如下: $GPGSV,(1),(2),(3),(4),(5),(6),(7),...,(4),(5),(6),(7)*hh(CR)(LF)
(1)GSV 语句总数。
(2)本句 GSV 的编号。
(3)可见卫星的总数(00~12,前面的 0 也将被传输)。
(4)卫星编号(01~32,前面的 0 也将被传输)。
(5)卫星仰角(00~90 度,前面的 0 也将被传输)。
(6)卫星方位角(000~359 度,前面的 0 也将被传输)
(7)信噪比(00~99dB,没有跟踪到卫星时为空)。
注:每条 GSV 语句最多包括四颗卫星的信息,其他卫星的信息将在下一条 $GPGSV 语句中输出。
举例如下: $GPGSV,3,1,12,02,39,117,25,04,02,127,,05,40,036,24,08,10,052,*7E $GPGSV,3,2,12,09,35,133,,10,01,073,,15,72,240,22,18,05,274,*7B $GPGSV,3,3,12,21,10,316,31,24,16,176,,26,65,035,42,29,46,277,18*7A
7.$GPGLL(定位地理信息,Geographic Position) $GPGLL 语句的基本格式如下: $GPGLL,(1),(2),(3),(4),(5),(6),(7)*hh(CR)(LF)
(1)纬度 ddmm.mmmmm(度分)
(2)纬度半球 N(北半球)或 S(南半球)
(3)经度 dddmm.mmmmm(度分)
(4)经度半球 E(东经)或 W(西经)
(5)UTC 时间:hhmmss(时分秒
(6)定位状态,A=有效定位,V=无效定位
(7)模式指示(A=自主定位,D=差分,E=估算,N=数据无效)
代码:
因项目后续调整,代码写完,一直没有实际调试。参考时,请知悉。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <sys/time.h> #include "uart_oper.h" #include "gps_oper.h" #include "play.h" #define GPS_DEV_NAME "/dev/ttyS0" /*需根据实际端口修改*/ #define GPS_LEN 1024 #define TIME_VAL (10*1000) static int gps_analyse(char * buff, GPRMC *gps_data); static int print_gps(GPRMC * gps_data); static void set_ensync_deal(SyncStateEnum sync_val); static int update_time_fromgps(GPRMC * gps_data); /** *@brief 解析收到的gps信息 *@param buff:收到的gps信息. gps_data:返回解析到的结构体 *@return 失败返回-1;成功返回0; */ static int gps_analyse(char * buff, GPRMC *gps_data) { char *ptr = NULL; if(gps_data == NULL) { return - 1; } if(strlen(buff) < 0) { return - 1; } ptr = strstr(buff, "$GPRMC"); /*找到GPRMC的位置*/ if(ptr == NULL) { return - 1; } sscanf(ptr, "$GPRMC,%d.%d,%c,%f,N,%f,E,%f,%f,%d,,,%c*", &(gps_data->time), &(gps_data->ptime), &(gps_data->pos_state), &(gps_data->latitude), &(gps_data->longitude), &(gps_data->speed), &(gps_data->direction), &(gps_data->date), &(gps_data->mode)); return 0; } /** *@brief 打印解析后的gps信息 *@param gps_data:解析到的结构体 *@return 失败返回-1;成功返回0; */ static int print_gps(GPRMC * gps_data) { if(gps_data == NULL) { return - 1; } printf("GPS state bit : %c [A:有效状态] V:无效状态]\n", gps_data->pos_state); printf("GPS mode bit :%c [A:自主定位 D:查分定位]\n", gps_data->mode); printf("Data: 20%02d-%02d-%02d \n", (gps_data->date % 100), /*GPRMC的日期格式为:ddmmyy*/ ((gps_data->date % 1000) / 100), (gps_data->date / 10000)); printf("Time: %02d:%02d%02d.%03d\n", ((gps_data->time / 10000 + 8) % 24), ((gps_data->time % 10000) / 100), (gps_data->time % 100), gps_data->ptime); printf("纬度:%d度%d分%d秒\n", (int)(gps_data->latitude / 100), (int)(gps_data->latitude - ((int)(gps_data->latitude / 100 * 100))), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0)); printf("经度 :%d度%d分%d秒 \n", ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100)))* 60.0)); printf("速度:%.3f m/s \n", gps_data->speed); return 0; } /* *@brief 用gps时间更新系统时间.相差10ms时进行更新时间 *@param none. *@return 不更新返回-1,更新返回0. */ static int update_time_fromgps(GPRMC * gps_data) { struct timeval cur_tv; struct timeval gps_tv; struct tm gps_tm; int ret = -1; gps_tm.tm_year = (gps_data->date % 100) + 2000 - 1900; /*年*/ gps_tm.tm_mon = (gps_data->date % 1000) / 100 - 1; /*月*/ gps_tm.tm_mday = (gps_data->date / 10000); /*日*/ gps_tm.tm_hour = gps_data->time / 10000; /*时*/ gps_tm.tm_min = (gps_data->time % 10000) / 100;/*分*/ gps_tm.tm_sec = gps_data->time % 100; /*秒*/ gps_tm.tm_isdst = - 1; gps_tv.tv_sec = mktime(&gps_tm); /*tm 转换为秒*/ gps_tv.tv_usec = gps_data->ptime *1000; gettimeofday(&cur_tv, NULL); if(gps_tv.tv_sec != cur_tv.tv_sec) { settimeofday(&gps_tv, NULL); tim->set_timzone(0); system("hwclock -w");/*软件时间更新硬件时间*/ tim->set_timzone(8); ret = 0; } else if(((gps_tv.tv_usec - cur_tv.tv_usec) > TIME_VAL) || ((cur_tv.tv_usec - gps_tv.tv_usec) > TIME_VAL)) { /*相差10ms*/ settimeofday(&gps_tv, NULL); tim->set_timzone(0); system("hwclock -w");/*软件时间更新硬件时间*/ tim->set_timzone(8); ret = 0; } return ret; } /* *@brief set sync 枚举值(设置系统同步标识) *@param sync_val 设置的枚举值 *@return none. */ static void set_ensync_deal(SyncStateEnum sync_val) { if(SetInfo.euSync != sync_val) { pthread_rwlock_wrlock(&rwlock_sys); SetInfo.euSync = sync_val; pthread_rwlock_wrlock(&rwlock_sys); } } /* *@brief Gps 处理线程函数 *@param none. *@return none. */ void * Gps_proc_thread(void *arg) { int fd; int ret; int n = 0; GPRMC gprmc; char buff[GPS_LEN]; fd_set Rdfet; struct timeval timeout; struct timeval vaild_tv; struct timeval invaild_tv; pthread_detach(pthread_self()); fd = open(GPS_DEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) { perror("Can't open gps uart port"); set_ensync_deal(SYNC_STATE_NO); return(void *)"gps dev error"; } ret = set_serial(fd, 9600, 8, 'N', 1); /*可能需要根据情况调整*/ { printf("set_serial error\n"); goto endline; } FD_ZERO(&Rdfet); printf("Gps proc thread start......\n"); for(;;) /*循环读取gps信息*/ { timeout.tv_sec = 3; timeout.tv_usec = 0; FD_SET(fd, &Rdfet); ret = select(fd + 1, &Rdfet, NULL, NULL, &timeout); /*监控fd*/ if(ret < 0) /*出错*/ { perror("select error"); msleep(50); } else if(ret > 0) /*有数据可读*/ { if((n = read(fd, buff, sizeof(buff))) < 0) { perror("gps read error"); } memset(&gprmc, 0 , sizeof(gprmc)); gps_analyse(buff, &gprmc); #if DEBUG_SWITCH printf("buff = %s\n", buff); print_gps(&gprmc); #endif if(gprmc.pos_state == 'A') /*有效*/ { gettimeofday(&vaild_tv, NULL); // set_ensync_deal(SYNC_STATE_OK); /*时间相差10ms时,更新系统时间*/ update_time_fromgps(&gprmc); } else /*无有效数据*/ { gettimeofday(&invaild_tv, NULL); if((invaild_tv.tv_sec - vaild_tv.tv_sec) > 3) /*距离有效GPS时间过去3秒,判断GPS无信号*/ { set_ensync_deal(SYNC_STATE_NO); } } } else if(ret == 0) /*timeout*/ { set_ensync_deal(SYNC_STATE_NO); } } /*end for(;;) */ endline: set_ensync_deal(SYNC_STATE_NO); close(fd); return NULL; }