GPS数据处理

(Global Positioning System,GPS)全球定位系统是一种以空中卫星为基础的高精度无线电导航的定位系统,它在全球任何地方以及近地空间都能够提供准确的地理位置、车行速度及精确的时间信息。GPS自问世以来,就以其高精度、全天候、全球覆盖、方便灵活吸引了众多用户。GPS不仅是汽车的守护神,同时也是物流行业管理的智多星。随着物流业的快速发展,GPS有着举足轻重的作用,成为继汽车市场后的第二大主要消费群体。

目前常用的GPS模块接收到卫星信号后使用串口协议以字符串的形式输出,前面学习过字符串的基本使用,本章节就学习解析GPS卫星数据,提取GPS信息里的有效数据。

GPS返回的数据如下(采用中科微GPS模块):

$GNGGA,114955.000,2842.4158,N,11549.5439,E,1,05,3.8,54.8,M,0.0,M,,*4F$GNGLL,2842.4158,N,11549.5439,E,114955.000,A,A*4D$GPGSA,A,3,10,31,18,,,,,,,,,,5.7,3.8,4.2*37$BDGSA,A,3,07,10,,,,,,,,,,,5.7,3.8,4.2*2A$GPGSV,3,1,10,10,49,184,42,12,16,039,,14,54,341,,18,22,165,23*7B$GPGSV,3,2,10,22,11,318,,25,51,055,,26,24,205,,29,13,110,*7C$GPGSV,3,3,10,31,50,287,36,32,66,018,*7F$BDGSV,1,1,04,03,,,07,05,,,29,07,79,246,33,10,52,232,19*62$GNRMC,114955.000,A,2842.4158,N,11549.5439,E,0.00,44.25,061117,,,A*4D$GNVTG,44.25,T,,M,0.00,N,0.00,K,A*14$GNZDA,114955.000,06,11,2017,00,00*47$GPTXT,01,01,01,ANTENNA OK*35

其中每个字段的含义如下:

序号 命令 说明 最大帧长1 $GNGGA GPS/北斗定位信息 722 $GNGSA 当前卫星信息 653 $GPGSV 可见 GPS 卫星信息 2104 $BDGSV 可见北斗卫星信息 2105 $GNRMC 推荐定位信息 706 $GNVTG 地面速度信息 347 $GNGLL 大地坐标信息 --8 $GNZDA 当前时间(UTC1)信息 --

1、$GNGGA(GPS 定位信息)

$GNGGA 语句的基本格式如下(其中 M 指单位 M, hh 指校验和, CR 和 LF 代表回车换行,下同):$GNGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF)(1) UTC 时间,格式为 hhmmss.ss;(2) 纬度,格式为 ddmm.mmmmm(度分格式);(3) 纬度半球, N 或 S(北纬或南纬);(4) 经度,格式为 dddmm.mmmmm(度分格式);(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 也将传送,非差分定位,此项为空)举例如下:$GNGGA,095528.000,2318.1133,N,11319.7210,E,1,06,3.7,55.1,M,-5.4,M,,0000*69

2、$GNGSA(当前卫星信息)

$GNGSA 语句的基本格式如下:$GNGSA,(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)ALIENTEK(5) HDOP 水平精度因子 1(0.5-99.9)(6) VDOP 垂直精度因子(0.5-99.9)举例如下:$GNGSA,A,3,14,22,24,12,,,,,,,,,4.2,3.7,2.1*2D$GNGSA,A,3,209,214,,,,,,,,,,,4.2,3.7,2.1*21注 1:精度因子值越小,则准确度越高。

3、$GPGSV(可见卫星数)

$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,11,18,73,129,19,10,71,335,40,22,63,323,41,25,49,127,06*78$GPGSV,3,2,11,14,41,325,46,12,36,072,34,31,32,238,22,21,23,194,08*76$GPGSV,3,3,11,24,21,039,40,20,08,139,07,15,08,086,03*45

4、$BDGSV(可见卫星数)

$BDGSV 语句的基本格式如下:$BDGSV, (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 语句最多包括四颗卫星的信息,其他卫星的信息将在下一条$BDGSV 语句中输出。举例如下:$BDGSV,1,1,02,209,64,354,40,214,05,318,40*69

5、$GNRMC(推荐定位信息)

$GNRMC 语句的基本格式如下:$GNRMC,(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(南半球)ALIENTEK(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=差分, E=估算, N=数据无效)举例如下:$GNRMC,095554.000,A,2318.1327,N,11319.7252,E,000.0,005.7,081215,,,A*73

6、$GNVTG(地面速度信息)

$GNVTG 语句的基本格式如下:$GNVTG,(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=数据无效)举例如下:$GNVTG,005.7,T,,M,000.0,N,000.0,K,A*11

7、$GNGLL(定位地理信息)

$GNGLL 语句的基本格式如下:$GNGLL,(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=数据无效)举例如下:$GNGLL,2318.1330,N,11319.7250,E,095556.000,A,A*4F

8、$GNZDA(当前时间信息)

$GNZDA 语句的基本格式如下:$GNZDA,(1),(2),(3),(4), (5), (6)*hh(CR)(LF)(1) UTC 时间:hhmmss(时分秒)(2)(3)(4)(5) 本地区域小时(NEO-6M 未用到,为 00)(6) 本地区域分钟(NEO-6M 未用到,为 00)举例如下:$GNZDA,095555.000,08,12,2015,00,00*4C

C语言代码解析GPS数据示例:

/*
 * @Author: maplerian
 * @Date: 2020-07-06 17:05:11
 * @LastEditors: maplerian
 * @LastEditTime: 2020-07-09 21:48:30
 * @Description: file content
 */ 

#include "gps_rmc.h"
// #define GPS_RMC_SHOW_DEBUG

/**
 * @name:   UTC2BTC
 * @msg:    utc日期和时间 转 北京时间
 * @param   {date: 待转换日期结构体指针} 
 * @return: void
 */
void UTC2BTC(gps_date_t *date)
{
    date->hour += 8;
    if (date->hour >= 24)
    {
        date->day++;
        date->hour -= 24;
        if (date->day >= 29)
        {
            if (date->month == 2)
            {
                date->month++;
                if (date->year / 4 != 0 && date->year / 100 == 0)
                    date->day -= 28;
                else
                    date->day -= 29;
            }
            else if (date->day >= 31 && (date->month == 4 || date->month == 6 || date->month == 9 || date->month == 11))
            {
                date->day -= 30;
                date->month += 1;
            }
            else if (date->day >= 32)
            {
                date->day -= 31;
                date->month += 1;
                if (date->month > 12)
                {
                    date->month -= 12;
                    date->year += 1;
                }
            }
        }
    }

    sprintf(
        date->string
        ,GPS_DATE_TO_STRING_FORMAT
        ,date->year
        ,date->month
        ,date->day
        ,date->hour
        ,date->minute
        ,date->second
    );
}
/**
 * @name:   gps_date_parse
 * @msg:    GPS RMC 时间解析
 * @param   {date:  解析结果存储结构体指针, utc_time: utc格式的时间, utc_date: utc格式的日期} 
 * @return: void
 */
void gps_date_parse(gps_date_t *date, uint32_t utc_time, uint32_t utc_date)
{
    if (!date) return;
    date->year = utc_date % 100 + 2000;
    date->month = utc_date % 10000 / 100;
    date->day = utc_date / 10000;
    date->second = utc_time % 100;
    date->minute = utc_time % 10000 / 100;
    date->hour = utc_time / 10000;
    UTC2BTC(date);
}

/**
 * @name:   gps_float_parse
 * @msg:    解析字符串中的float数据
 * @param   {info: 需要解析的结构体, 前提是这个结构体中的 string 必须要有值} 
 * @return: void
 */
void gps_float_parse(gps_float_t *info)
{
    if (!info) return;
    info->value = atof(info->string);
    info->value_h = atol(info->string);
    info->value_l = 0;
    char *p = strstr(info->string,".");
    if (p)
        info->value_l = atol(++p);
#ifdef GPS_RMC_SHOW_DEBUG
    printf("string: %s --> %ld.%ld\r\n", info->string, info->value_h, info->value_l);
#endif // GPS_RMC_SHOW_DEBUG
}

/**
 *  坐标处理相关函数
 * ------------------------------------------------------------------------
*/
#ifdef GPS_RMC_USE_DMS_FORMAT
/**
 * @name:   gps_source_to_dms_format
 * @msg:    经纬度格式 转换成 度分秒格式
 * @param   {coord: 度分秒存储结构体, source: 经纬度信息结构体} 
 * @return: void
 */
void gps_source_to_dms_format(gps_dms_format_t *coord, gps_float_t *source)
{
    if (!source || !coord) return;
    if (!source->value)
        gps_float_parse(source);
    double temp = source->value;
    coord->d = temp / 100;
    coord->m = temp - coord->d * 100;
    coord->s = ((temp - coord->d * 100) - coord->m) * 60;
    sprintf(coord->string,"%d`%d\'%d\"", coord->d, coord->m, coord->s);
#ifdef GPS_RMC_SHOW_DEBUG
    printf("source: %s --> dms: %s\r\n", source->string, coord->string);
#endif // GPS_RMC_SHOW_DEBUG
}
/**
 * @name:   gps_coord_dms_format_transform
 * @msg:    坐标度分秒格式转换
 * @param   {coord: 待存储转换结构的信息的结构体, source: 从这个结构体中解析转换} 
 * @return: void
 */
void gps_coord_dms_format_transform(gps_coord_dms_format_t *coord, gps_source_coord_t *source)
{
    if (!coord || !source) return ;
    gps_source_to_dms_format(&coord->latitude, &source->latitude);
    gps_source_to_dms_format(&coord->longitude, &source->longitude);
}
#endif //   GPS_RMC_USE_DMS_FORMAT

/**
 * @name:   gps_source_to_location_format
 * @msg:    原始坐标 转 10进制坐标
 * @param   {location: 待存储结构体, source: 从这个结构体中解析转换} 
 * @return: void
 */
void gps_source_to_location_format(gps_float_t *location, gps_float_t *source)
{
    if (!location || !source) return ;
    if (source->value == 0)
        gps_float_parse(source);
    //                      (dddmm / 100 = ddd)    +   (dddmm.mmmmm   -   (dddmm / 100 = ddd) * 100 = ddd00) = mm.mmmmm / 60
    //  ddd + mm.mmmmm/60
    location->value = (double)(source->value_h / 100) + (source->value - (source->value_h / 100) * 100) / 60;
    location->value_h = (uint32_t)location->value;
    location->value_l = (uint32_t)((location->value - (uint32_t)location->value) * 10000000);
    sprintf(location->string,"%ld.%ld", location->value_h, location->value_l);
#ifdef GPS_RMC_SHOW_DEBUG
    printf("source:%s -> location:%s\r\n", source->string, location->string);
#endif // GPS_RMC_SHOW_DEBUG
}

/**
 * @name:   gps_coord_location_format_transform
 * @msg:    坐标10进制转换
 * @param   {location: 待存储结构体, source: 从这个结构体中解析转换} 
 * @return: void
 */
void gps_coord_location_format_transform(gps_coord_t *location, gps_coord_t * source)
{
    if (!location || !source) return ;
    gps_source_to_location_format(&location->latitude, &source->latitude);
    gps_source_to_location_format(&location->longitude, &source->longitude);
}

/**
 * @name:   gps_coord_handle
 * @msg:    坐标处理
 * @param   {location: 待处理存储结构体} 
 * @return: void
 */
void gps_coord_handle(gps_coord_info_t *location)
{
    if (!location) return ;
#ifdef GPS_RMC_USE_DMS_FORMAT
    gps_coord_dms_format_transform(&location->dms, &location->source);
#endif //   GPS_RMC_USE_DMS_FORMAT
    gps_coord_location_format_transform(&location->location, &location->source);
}
/**
 * ------------------------------------------------------------------------
 *  坐标处理相关函数
*/

/**
 * @name:   gps_print_info
 * @msg:    解析结构体信息,并打印出来
 * @param   {info: 待解析结构体指针} 
 * @return: void
 */
void gps_print_info(gps_info_t info)
{
    if (info->AV == 0 || (info->date.year == 0 || info->date.year == 2000))
    {
        printf("Null\r\n");
        return ;
    }
    printf("Mode: %c\r\n", info->ADEN);
    printf("State: %s\r\n", info->AV == 'A'?"Effective":"Invalid");
    printf("Time: %s\r\n", info->date.string);
    if (info->AV == 'A')
    {
        printf("Location coord: %s %c, %s %c\r\n", info->coord.location.latitude.string, info->EW, info->coord.location.longitude.string, info->NS);
        printf("Speed: %s\r\n",info->speed.string);
        printf("Direction: %s\r\n",info->direction.string);
    }
}
/**
 * @name:   gps_rmc_parse
 * @msg:    gps rmc类型信息解析
 * @param   {info: 待存储解析结构结构体指针, buff: rmc类型信息字符串} 
 * @return: void
 */
char gps_rmc_parse(gps_info_t info, char *buff)
{
    if (!info) return 0;
    if (strlen(buff) < 30)
    {
#ifdef GPS_RMC_SHOW_ERROR_INFO
        printf("[gps.rmc.parse] Invalid Data.\r\n");
#endif // GPS_RMC_SHOW_ERROR_INFO
#ifdef GPS_RMC_SHOW_DEBUG
        printf("无效数据.\r\n");
#endif // GPS_RMC_SHOW_DEBUG
        return 0;
    }
    static char hms_s[15] = {0}, ymd_s[15] = {0};
    static uint32_t hms, ymd;
    //  解析出状态
    sscanf(
        buff,
        "%*[^,],%*[^,],%c,,,,,,,%*[^,],,,%*c",
        &info->AV
    );
    if (info->AV == 'A')
    {
        //  进一步解析所有数据
        sscanf(
            buff,
            "%*[^,],%[^,],%*c,%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],,,%[^,]",
            hms_s,
            info->coord.source.longitude.string,
            &info->NS,
            info->coord.source.latitude.string,
            &info->EW,
            info->speed.string,
            info->direction.string,
            ymd_s,
            &info->ADEN
        );
        //  有效,处理所有数据
        gps_coord_handle(&info->coord);
        sprintf(info->speed.string, "%d.%ld", (int)(atof(info->speed.string) * 1.85), (uint32_t)(atof(info->speed.string) * 1850) % 1000);
        gps_float_parse(&info->speed);
        gps_float_parse(&info->direction);
    }
    else
    //  解析正确的时间和模式
        sscanf(
            buff,
            "%*[^,],%[^,],%*c,,,,,,,%[^,],,,%c",
            hms_s,
            ymd_s,
            &info->ADEN
        );
    //  处理时间数据
    hms = atol(hms_s);
    ymd = atol(ymd_s);
    gps_date_parse(&info->date, hms, ymd);
#ifdef GPS_RMC_SHOW_DEBUG
    printf("%s\r\n",buff);
    printf(
        "时间:%s\r\n"
        "状态:%s\r\n"
        "经度:%s %c\r\n"
        "纬度:%s %c\r\n"
        "速度:%s\r\n"
        "方向:%s\r\n"
        "模式:%c\r\n"
        "\r\n"
        ,info->date.string
        ,info->AV == 'A'?"有效":"无效"
        ,info->coord.location.latitude.string,info->EW
        ,info->coord.location.longitude.string,info->NS
        ,info->speed.string
        ,info->direction.string
        ,info->ADEN
    );
#endif // GPS_RMC_SHOW_DEBUG
    return 1;
}

-----------------------------------

/*
 * @Author: maplerian
 * @Date: 2020-07-09 11:28:00
 * @LastEditors: maplerian
 * @LastEditTime: 2020-07-09 21:49:30
 * @Description: file content
 */ 
#include 
#include 
#include "gps_rmc.h"

#ifndef GPS_RMC_SAMPLE_UART_NAME
#define GPS_RMC_SAMPLE_UART_NAME "uart6"
#endif // !GPS_RMC_SAMPLE_UART_NAME

/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t uart;

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

void gps_rmc_sample_entry(void *p)
{
    char buff[128] = {0}, *buff_p = buff, ch = 0;
    struct gps_info info_data = {0};
    gps_info_t info = &info_data;
    while (1)
    {
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(uart, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        if (ch == '\n')
        {
            if (rt_strstr((const char *)buff, "RMC"))
            {
                if (gps_rmc_parse(info, buff))
                    gps_print_info(info);
            }
            rt_memset(buff, 0, 128);
            rt_memset(info, 0, sizeof(struct gps_info));
            buff_p = buff;
            continue;
        }
        *buff_p++ = ch;
    }
}

int gps_rmc_sample_entry_init(void)
{

    uart = rt_device_find(GPS_RMC_SAMPLE_UART_NAME);
    if (uart == RT_NULL)
    {
        rt_kprintf("Not find %s device.\r\n", GPS_RMC_SAMPLE_UART_NAME);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, GPS_RMC_SAMPLE_UART_NAME"_rx", 0, RT_IPC_FLAG_FIFO);

    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    config.baud_rate = BAUD_RATE_9600;
    rt_device_control(uart, RT_DEVICE_CTRL_CONFIG, &config);
    rt_device_open(uart, RT_DEVICE_FLAG_INT_RX);
    rt_device_set_rx_indicate(uart, uart_input);

    rt_thread_t t = rt_thread_create(
        "gps_rmc_p", gps_rmc_sample_entry, RT_NULL, 
        2048, 20, 10
    );
    if (t == RT_NULL)
    {
        rt_kprintf("Failde to create gps rmc info procees thread.\r\n");
        return RT_ERROR;
    }
    if (rt_thread_startup(t) != RT_EOK)
    {
        rt_kprintf("Failde to startup gps rmc info procees thread.\r\n");
        rt_thread_delete(t);
        return RT_ERROR;
    }
    return RT_EOK;
}
INIT_APP_EXPORT(gps_rmc_sample_entry_init);

原始数据转度分秒

//	对原始gps数据进行转换为度分秒
//    char buff[10] = {0};
//    degree_minute_to_degree("2813.55525", buff, 1);
//    degree_minute_to_degree("11256.92563", buff, 2);
//
void degree_minute_to_degree(char *data, char *out, int type)
{
    char degree_result_char[11] = {0};
    char degree_temp_char[4] = {0};
    char minute_temp_char[9] = {0};

    double degree_result_float = 0.0;
    double degree_temp_flaot = 0.0;
    double minute_temp_float = 0.0;

    int num = 0;

    if (1 == type) // 纬度 /
    {
        num = 2;
    }
    else if (2 == type)
    {
        num = 3;
    }
    else
    {
        ;
    }

    strncpy(degree_temp_char, data, num);
    strcpy(minute_temp_char, data + num);

    degree_temp_flaot = atof(degree_temp_char);
    printf("degree_temp_flaot %s", degree_temp_flaot);
    minute_temp_float = atof(minute_temp_char) / 60;
    printf("minute_temp_float %s", minute_temp_float);
    degree_result_float = degree_temp_flaot + minute_temp_float;
    printf("degree_result_float %s", degree_result_float);

    sprintf(degree_result_char, "%.6f", degree_result_float);
    strcpy(out, degree_result_char);
}

工具

经纬度换算器

https://www.sioe.cn/xinqing/jingweidu.php

经纬度gps坐标查询

https://adminun.com/gpsjwd

地图坐标系转换

https://tool.lu/coordinate/

你可能感兴趣的:(嵌入式,C语言,c++,c语言,数据结构)