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;
}

 

posted @ 2022-06-17 16:56  大龄小凡  阅读(601)  评论(0编辑  收藏  举报