GNSS 开发指导_V1.3

English

1 修订记录

版本

日期

作者

修订内容

Rev1.0

2024-11-25

WTY

创建文档

Rev1.1

2025-02-19

WTY

1. 修改 4.4 章节对 fix 参数的描述
2. 添加 NT26KCNE20GNB 型号改动的相关说明
3. 添加 4.2 章节中的 gnss_ic_model 参数说明
4. 新增 4.8 章节 liot_gnss_ic_model_e 类型的说明
5. 修改代码示例
6. 添加注意事项中对 NT26KCNE20GNB 型号的特殊说明

Rev1.2

2025-08-11

ZLC

修改定位系统说明

Rev1.3

2026-07-23

ZXQ

根据建议修改,增加 NT26K2E0_0G 和 NT26K2E1_0G 的模组说明

2 简介

2.1 GNSS 功能概述

本文档介绍 LTE-EC71X GNSS 接口 API 情况,API 接口位于 components/kernel/lierda_api/liot_gnss/liot_gnss.h 文件声明。

GNSS 数据来源于模组内部的 GNSS 定位芯片。GNSS 芯片接收卫星导航系统信号后输出 NMEA 数据,应用层可通过 liot_gnss_get_nmea() 获取指定类型的原始 NMEA 语句,也可通过 liot_gnss_get_location() 获取 SDK 解析后的定位信息,包括 UTC 时间、经纬度、海拔、航向、速度、定位状态、HDOP 水平精度因子和参与解算的卫星数量等。

本文档中的精度指标以接口输出字段为准,主要包括 hdop、fix 和 satcount。其中 hdop 表示水平精度因子,fix 表示定位模式,satcount 表示参与解算的卫星数量。实际定位精度会受卫星信号质量、天线设计、安装位置、遮挡环境和是否启用 AGNSS 等因素影响,本文档不对固定米级精度作保证。

当前仅 Lierda NT26KCNE20GNA、NT26KCNE20GNB、NT26K2E0_0G、NT26K2E1_0G 模组支持 GNSS 定位功能。NT26KCNE20GNA/NT26K2E0_0G 内部定位芯片为 CC1161W,支持 GPS、北斗、伽利略和 GLONASS 多系统定位,可通过 liot_gnss_config_t 中的 gnsscfg_type 参数配置定位系统组合。NT26KCNE20GNB/NT26K2E1_0G 内部定位芯片为 CC1177W,仅支持单北斗定位,如果将 gnsscfg_type 配置为其他卫星系统,配置不会实际生效。

示例程序位于 examples/demo/src/demo_gnss.c,通过 APPDEMO_GNSS_EN 创建 liot_gnss_demo_thread。示例采用事件队列处理 GNSS 回调,并使用一次性定时器控制 GNSS 关闭和重启流程;当前默认定义 GNSS_TEST_E1_0G,配置 GNSS_IC_CC1177W 和单北斗定位系统。

2.2 平台 GNSS 核心概念

2.2.1 GNSS / BDS

GNSS 指全球卫星导航系统总称,包括 GPS、北斗、GLONASS、Galileo 等系统。BDS(北斗)是中国的卫星导航系统。

2.2.2 定位模式(Fix)

fix 用于表示当前 GNSS 定位模式,是判断定位结果是否可靠的关键字段。

值

含义

1

未定位

2

2D 定位

3

3D 定位

2D 定位通常至少需要 3 颗卫星参与解算,只能提供经纬度信息,定位精度相对较差。3D 定位通常至少需要 4 颗卫星参与解算,可提供经纬度和海拔信息,是获取可靠位置数据的目标状态。

2.2.3 NMEA 语句

NMEA 是 GNSS 接收机输出的标准数据格式。不同 NMEA 语句包含不同类型的信息,例如位置、时间、速度、航向、卫星列表和定位质量等。

语句

说明

GGA

定位质量、UTC 时间、经纬度、海拔、卫星数量等

RMC

推荐最小定位信息,包括时间、经纬度、速度、航向等

GSV

可见卫星信息

GSA

DOP 精度因子和参与定位的卫星信息

VTG

地面航向和地面速度

GLL

经纬度和 UTC 时间

应用层可通过 liot_gnss_get_nmea() 获取指定类型的 NMEA 语句。客户如需更详细的原始定位信息,可根据业务需要解析对应 NMEA 语句。

2.2.4 AGNSS(辅助定位)

AGNSS 是辅助 GNSS 定位功能,可利用蜂窝网络提供的辅助信息加速 GNSS 芯片首次定位时间(TTFF)。

AGNSS 依赖数据拨号(Data Call)和网络连接。在使用 AGNSS 前,应确保蜂窝网络已注册、数据拨号已建立,并正确配置 AGNSS 服务器和鉴权信息。示例代码中 AGNSS_DEMO_ENABLE 为 1 时会调用 liot_agnss_config(NULL) 并等待网络相关流程完成;正式产品中应根据平台分配的信息配置有效的 liot_agnss_config_t 参数。

2.3 模组支持差异

不同模组内部集成的 GNSS 芯片不同,支持的卫星定位系统也不同。调用 liot_gnss_config() 时,必须根据实际模组型号正确配置 liot_gnss_config_t 中的 gnss_ic_model 和 gnsscfg_type 组合。

模组型号

GNSS 芯片

支持定位系统

推荐配置

NT26KCNE20GNA / NT26K2E0_0G

CC1161W

支持 GPS、北斗、GLONASS、Galileo 多模融合定位

gnss_ic_model 配置为 GNSS_IC_CC1161W,gnsscfg_type 按需配置定位系统组合

NT26KCNE20GNB / NT26K2E1_0G

CC1177W

仅支持单北斗系统定位

gnss_ic_model 配置为 GNSS_IC_CC1177W,gnsscfg_type 配置为 LIOT_GNSS_CFG_TYPE_BEIDOU

NT26KCNE20GNB/NT26K2E1_0G 仅支持单北斗定位,即使将 gnsscfg_type 配置为 GPS、GLONASS 或 Galileo,也不会实际生效。

3 API 函数概览

函数

说明

liot_gnss_config()

配置gnss模块参数

liot_agnss_config()

配置agnss功能参数

liot_gnss_open()

开启gnss模块

liot_gnss_close()

关闭gnss模块

liot_gnss_get_location()

获取定位信息

liot_gnss_get_nmea()

获取指令NMEA语句

liot_gnss_close_backup_power()

关闭GNSS芯片备用电源

4 类型说明

4.1 liot_gnss_errcode_e

GNSS API 执行结果错误码。

  1. 声明

typedef enum
{
    LIOT_GNSS_SUCCESS = LIOT_SUCCESS,
    LIOT_GNSS_EXECUTE_ERR = 1,
    LIOT_GNSS_PARAM_ERR,
} liot_gnss_errcode_e;
  1. 参数

  • LIOT_GNSS_SUCCESS:函数执行成功。

  • LIOT_GNSS_EXECUTE_ERR:函数执行错误。

  • LIOT_GNSS_PARAM_ERR:传入参数错误。

4.2 liot_gnss_config_t

GNSS 模块配置参数。

  1. 声明

typedef struct
{
    liot_gnss_ic_model_e gnss_ic_model;  // 可配置为 GNSS_IC_CC1161W 或 GNSS_IC_CC1177W
    uint8_t   gnssnmea_type;             // 配置 nmea 语句输出类型
    uint8_t   gnsscfg_type;              // 配置各系统组合进行定位
    uint8_t   apflash;                   // apflash 开关
    uint8_t   agnss_mode;                // agnss 开关
} liot_gnss_config_t;
  1. 参数

  • gnss_ic_model:GNSS定位芯片型号,可配置为 GNSS_IC_CC1161W 或 GNSS_IC_CC1177W,参数含义参考 4.8 liot_gnss_ic_model_e。

  • gnssnmea_type:GNSS通过回调函数输出的NMEA类型,不同二进制数据位对应不同NMEA语句,可配置参数与 liot_gnss_nmea_type_e 类型对应。

  • gnsscfg_type:GNSS使用的定位系统,不同二进制数据位对应不同的定位系统,可配置参数与 liot_gnss_cfg_type_e 类型对应。

  • apflash:AP FLASH 保存开关。配置为 1 时,GNSS 模块会将关键定位辅助信息保存到模组内部 AP FLASH,例如上次位置、星历数据等,用于下次启动时加快定位速度。该功能会占用模组内部 FLASH 空间。配置为 0 时关闭该功能。

  • agnss_mode:AGNSS 功能开关。配置为 1 时开启辅助定位,使用前必须确保蜂窝网络连接已就绪,并已调用 liot_agnss_config() 完成 AGNSS 参数配置。配置为 0 时关闭 AGNSS 功能。

4.3 liot_agnss_config_t

AGNSS 功能配置参数。

  1. 声明

typedef struct
{
    uint8_t   agnss_url[LIERDA_GNSS_APGS_URL_MAX];
    uint8_t   cid[64];
    uint8_t   mid[64];
    uint8_t   pw[64];
} liot_agnss_config_t;
  1. 参数

  • agnss_url:AGNSS 服务器地址,可配置为平台或客户提供的 AGNSS 服务器域名/地址,最大长度为 LIERDA_GNSS_APGS_URL_MAX。自定义服务器必须兼容模组使用的 AGNSS 服务协议,并能配合 cid、mid、pw 完成鉴权。

  • cid:AGNSS 平台用户名。

  • mid:AGNSS 平台客户标识。

  • pw:AGNSS 平台密码。

当前示例代码中调用 liot_agnss_config(NULL),未演示自定义 AGNSS 服务器配置;正式产品如需使用自定义服务器,应传入有效的 liot_agnss_config_t 参数。

4.4 liot_gnss_loc_info_t

定位信息参数结构体。

  1. 声明

typedef struct {
    uint8_t fs;                  // 定位状态:0-无定位,1-单点定位 2-差分定位
    char utc[20];                // UTC时间 YYMMDDhhmmss.sss
    char latitude[14];           // 纬度
    char longitude[14];          // 经度
    char hdop[8];                // HDOP 水平精度因子
    char altitude[10];           // 海拔 米
    uint8_t fix;                 // 定位状态:1-未定位, 2-2D 定位,3-3D 定位
    char cog[10];                // 以真北方向为基准的地面航向
    char speedkm[10];            // 速度 千米/小时
    char speedkn[10];            // 速度 节
    uint8_t satcount;            // 卫星数目
} liot_gnss_loc_info_t;
  1. 参数

  • fs:定位状态,0-无定位,1-单点定位,2-差分定位。

  • utc:UTC时间,格式为YYMMDDhhmmss.sss。

  • latitude:纬度,格式为ddmm.mmmmN/S(dd:度,范围:00~89;mm.mmmm:分,范围:00.0000~59.9999;N/S:北纬/南纬)

  • longitude:经度,格式为dddmm.mmmmN/S(ddd:度,范围:00~179;mm.mmmm:分,范围:00.0000~59.9999;E/W:东经/西经)

  • hdop:水平精度因子(HDOP),用于反映水平定位精度,数值越小表示水平定位精度越高。

  • altitude:天线的海拔高度。单位:米

  • fix:GNSS 定位模式,取值含义为:1-未定位,2-2D 定位,3-3D 定位。通常只有 fix 等于 3 时,获取的位置数据才被认为是可靠的。

  • cog:以真北方向为基准的地面航向。

  • speedkm:地面速率。单位:千米/时。

  • speedkn:地面速率。单位:节。

  • satcount:参与应用解算位置的卫星数量。

4.5 liot_gnss_event_type_e

GNSS 事件回调中的事件类型。

  1. 声明

typedef enum
{
    LIOT_GNSS_EVENT_GNSS_ERROR = 0,
    LIOT_GNSS_EVENT_GNSS_READY,
    LIOT_GNSS_EVENT_GNSS_CLOSED,
    LIOT_GNSS_EVENT_GNSS_NMEA,
    LIOT_GNSS_EVENT_FARMWARE_OK,
    LIOT_GNSS_EVENT_FARMWARE_ERROR,
    LIOT_GNSS_EVENT_AGNSS_OK,
    LIOT_GNSS_EVENT_AGNSS_ERROR,
} liot_gnss_event_type_e;
  1. 参数

  • LIOT_GNSS_EVENT_GNSS_ERROR:GNSS出现错误。

  • LIOT_GNSS_EVENT_GNSS_READY:GNSS已准备就绪。

  • LIOT_GNSS_EVENT_GNSS_CLOSED:GNSS已关闭。

  • LIOT_GNSS_EVENT_GNSS_NMEA:GNSS输出NMEA语句。

  • LIOT_GNSS_EVENT_FARMWARE_OK:GNSS固件加载成功。

  • LIOT_GNSS_EVENT_FARMWARE_ERROR:GNSS固件加载失败。

  • LIOT_GNSS_EVENT_AGNSS_OK:AGNSS功能配置成功。

  • LIOT_GNSS_EVENT_AGNSS_ERROR:AGNSS配置失败。

4.6 liot_gnss_nmea_type_e

GNSS NMEA语句配置枚举。

  1. 声明

typedef enum
{
    LIOT_GNSS_NMEA_TYPE_GGA = 0x0001,
    LIOT_GNSS_NMEA_TYPE_RMC = 0x0002,
    LIOT_GNSS_NMEA_TYPE_GSV = 0x0004,
    LIOT_GNSS_NMEA_TYPE_GSA = 0x0008,
    LIOT_GNSS_NMEA_TYPE_VTG = 0x0010,
    LIOT_GNSS_NMEA_TYPE_GLL = 0x0020,
} liot_gnss_nmea_type_e;
  1. 参数

  • LIOT_GNSS_NMEA_TYPE_GGA:GGA 语句。

  • LIOT_GNSS_NMEA_TYPE_RMC:RMC 语句。

  • LIOT_GNSS_NMEA_TYPE_GSV:GSV 语句。

  • LIOT_GNSS_NMEA_TYPE_GSA:GSA 语句。

  • LIOT_GNSS_NMEA_TYPE_VTG:VTG 语句。

  • LIOT_GNSS_NMEA_TYPE_GLL:GLL 语句。

4.7 liot_gnss_cfg_type_e

GNSS 定位系统配置枚举。

  1. 声明

typedef enum
{
    LIOT_GNSS_CFG_TYPE_GPS = 0x01,
    LIOT_GNSS_CFG_TYPE_BEIDOU = 0x02,
    LIOT_GNSS_CFG_TYPE_GLONASS = 0x04,
    LIOT_GNSS_CFG_TYPE_GALILEO = 0x08,
} liot_gnss_cfg_type_e;
  1. 参数

  • LIOT_GNSS_CFG_TYPE_GPS:GPS卫星系统。

  • LIOT_GNSS_CFG_TYPE_BEIDOU:北斗卫星系统。

  • LIOT_GNSS_CFG_TYPE_GLONASS:Glonass卫星系统。

  • LIOT_GNSS_CFG_TYPE_GALILEO:伽利略卫星系统。

4.8 liot_gnss_ic_model_e

GNSS 定位芯片型号。

  1. 声明

typedef enum
{
    GNSS_IC_CC1161W = 0,
    GNSS_IC_CC1177W,
} liot_gnss_ic_model_e;
  1. 参数

  • GNSS_IC_CC1161W:CC1161W定位芯片,支持GPS、北斗、伽利略和Glonass定位系统多模融合定位。

  • GNSS_IC_CC1177W:CC1177W定位芯片,仅支持单北斗系统定位。

4.9 LiotGnssEventCb

GNSS 事件回调函数类型。

  1. 声明

typedef void (*LiotGnssEventCb) (liot_gnss_event_type_e event, uint8_t *data, uint16_t datalen);
  1. 参数

  • event:事件类型。

  • data:事件相关数据指针。

  • datalen:数据长度,单位字节。

5 API 函数详解

5.1 liot_gnss_config

该函数用于配置GNSS参数

  1. 声明

liot_gnss_errcode_e liot_gnss_config(liot_gnss_config_t *config);
  1. 参数

  • config:[In] GNSS 配置信息,格式参考 4.2 liot_gnss_config_t。

  1. 返回值

  • LIOT_GNSS_SUCCESS:配置成功。

  • LIOT_GNSS_PARAM_ERR:参数错误。

5.2 liot_agnss_config

该函数用于配置AGNSS参数

  1. 声明

liot_gnss_errcode_e liot_agnss_config(liot_agnss_config_t *config);
  1. 参数

  • config:[In] AGNSS 配置信息,格式参考 4.3 liot_agnss_config_t。

  1. 返回值

  • LIOT_GNSS_SUCCESS:配置成功。

5.3 liot_gnss_open

该函数用于开启GNSS

  1. 声明

liot_gnss_errcode_e liot_gnss_open(LiotGnssEventCb event_cb);
  1. 参数

  • event_cb:[In] GNSS 事件回调函数,格式参考 4.9 LiotGnssEventCb。

  1. 返回值

  • LIOT_GNSS_SUCCESS:GNSS模块开启成功。

  • LIOT_GNSS_EXECUTE_ERR:GNSS模块开启失败。

5.4 liot_gnss_close

该函数用于关闭GNSS

  1. 声明

liot_gnss_errcode_e liot_gnss_close(void);
  1. 返回值

  • LIOT_GNSS_SUCCESS:GNSS模块关闭成功。

  • LIOT_GNSS_EXECUTE_ERR:GNSS模块关闭失败。

5.5 liot_gnss_get_location

该函数用于获取定位信息

  1. 声明

liot_gnss_errcode_e liot_gnss_get_location(liot_gnss_loc_info_t *loc_info);
  1. 参数

  • loc_info:[Out] GNSS 定位信息,格式参考 4.4 liot_gnss_loc_info_t。

  1. 返回值

  • LIOT_GNSS_SUCCESS:定位信息获取成功。

5.6 liot_gnss_get_nmea

该函数用于获取指定NMEA语句

  1. 声明

liot_gnss_errcode_e liot_gnss_get_nmea(uint16_t nmea_type, char *respBuf[], uint16_t respMaxNumber, uint16_t *respNumber);
  1. 参数

  • nmea_type:[In] NMEA 语句类型,不同二进制数据位对应不同NMEA语句,可配置参数与 4.6 liot_gnss_nmea_type_e 类型对应。

  • respBuf:[Out] 字符串指针数组,用于返回 NMEA 语句。函数执行成功后,会为 respBuf 数组中的前 respNumber 个指针动态分配地址空间,并将每条 NMEA 语句存储到对应空间中。数据应用完成后,用户必须逐个释放 respBuf 数组中已分配的每一个字符串指针,避免内存泄漏;在当前 OpenCPU 示例中使用 liot_rtos_free() 释放。

  • respMaxNumber:[In]可读取的NMEA语句最大数量。

  • respNumber:[Out]实际读取的NMEA语句数量。

  1. 返回值

  • LIOT_GNSS_SUCCESS:NMEA语句获取成功。

  • LIOT_GNSS_PARAM_ERR:参数错误,未指定有效NMEA类型。

  • LIOT_GNSS_EXECUTE_ERR:NMEA语句获取失败。

5.7 liot_gnss_close_backup_power

该函数用于强制关闭GNSS模块备用电源,备用电源处于开启状态时才能成功关闭。

注意:关闭备用电源会清空 GNSS 芯片 RAM 中缓存的定位辅助信息;下次启动时 GNSS 将进行冷启动(Cold Start),首次定位时间可能变长。通常仅在需要彻底重置 GNSS 状态时调用。

  1. 声明

liot_gnss_errcode_e liot_gnss_close_backup_power(void);
  1. 返回值

  • LIOT_GNSS_SUCCESS:备用电源关闭成功。

  • LIOT_GNSS_EXECUTE_ERR:备用电源关闭失败,或备用电源未开启。

6 代码示例

以下示例与 examples/demo/src/demo_gnss.c 的主要逻辑保持一致:

  1. liot_gnss_demo_thread() 启动后创建消息队列、GNSS 工作定时器和重启定时器。

  2. start_gnss_module() 配置并打开 GNSS,默认使用 GNSS_TEST_E1_0G,即 GNSS_IC_CC1177W + 单北斗定位。

  3. GNSS 回调 liot_gnss_demo_event() 将事件投递到队列中处理;NMEA 数据会先拷贝,避免回调返回后原始指针失效。

  4. GNSS 运行 GNSS_WORK_TIMEOUT 后关闭,等待 GNSS_RESTART_TIMEOUT 后再次启动。

#include <stdio.h>
#include <string.h>

#include "lierda_app_main.h"
#include "liot_os.h"
#include "liot_gnss.h"
#include "liot_datacall.h"

#define AGNSS_DEMO_ENABLE 1

#define GNSS_TEST_E1_0G
//#define GNSS_TEST_E0_0G

// Timer related definitions
#define GNSS_WORK_TIMEOUT     (60*1000)  // 1 minute
#define GNSS_RESTART_TIMEOUT  (10*1000)  // 10 seconds

char gnss_nmea_data[1024];
uint8_t gnss_ready_state = 0;

// Global queue handles and timer handles
liot_queue_t gnss_queuehandle = NULL;
liot_timer_t gnss_work_timer = NULL;
liot_timer_t gnss_restart_timer = NULL;

#define GNSS_QUEUE_MAX 10

// Demo internal message type
typedef enum {
    GNSS_DEMO_MSG_GNSS_EVENT = 0,
    GNSS_DEMO_MSG_WORK_TIMEOUT,
    GNSS_DEMO_MSG_RESTART_TIMEOUT
} gnss_demo_msg_type_e;

// GNSS event structure
typedef struct {
    gnss_demo_msg_type_e msg_type;
    liot_gnss_event_type_e event_type;
    uint8_t *data;
    uint16_t datalen;
    uint8_t data_owned;
} gnss_event_msg_t;

// GNSS state enumeration
typedef enum {
    GNSS_STATE_IDLE = 0,
    GNSS_STATE_RUNNING,
    GNSS_STATE_CLOSING,
    GNSS_STATE_RESTARTING
} gnss_state_e;

gnss_state_e g_gnss_state = GNSS_STATE_IDLE;

static void gnss_demo_free_msg_data(gnss_event_msg_t *msg)
{
    if(msg && msg->data_owned && msg->data)
    {
        liot_rtos_free(msg->data);
    }

    if(msg)
    {
        msg->data = NULL;
        msg->datalen = 0;
        msg->data_owned = 0;
    }
}

static int gnss_demo_queue_send(gnss_event_msg_t *msg)
{
    int ret = LIOT_SUCCESS;

    if(msg == NULL || gnss_queuehandle == NULL)
    {
        gnss_demo_free_msg_data(msg);
        liot_trace("GNSS demo queue is not ready");
        return -1;
    }

    ret = liot_rtos_queue_release(gnss_queuehandle, sizeof(gnss_event_msg_t), (uint8 *)msg, 0);
    if(ret != LIOT_SUCCESS)
    {
        gnss_demo_free_msg_data(msg);
        liot_trace("GNSS demo queue send failed, ret=%d", ret);
    }

    return ret;
}

void liot_show_mem(void)
{
    //Print out SRAM information
    liot_trace("========== rtos Get TotalHeapSize:%dKB,FreeHeapSize:%dKB,MinFreeHeapSize:%dKB,MaxFreeBlockSize:%dKB",
            (liot_xPortGetTotalHeapSize()) >> 10, (liot_xPortGetFreeHeapSize()) >> 10,
            (liot_xPortGetMinimumEverFreeHeapSize()) >> 10, (liot_xPortGetMaximumFreeBlockSize()) >> 10);
}

/**
 * @brief GNSS event callback function
 * @details Sends GNSS events to message queue for processing
 */
void liot_gnss_demo_event(liot_gnss_event_type_e event, uint8_t *data, uint16_t datalen)
{
    liot_trace("liot_gnss_demo_event %d", event);
    gnss_event_msg_t event_msg = {0};

    event_msg.msg_type = GNSS_DEMO_MSG_GNSS_EVENT;
    event_msg.event_type = event;
    event_msg.datalen = datalen;
    // Note: For NMEA data, need to copy data content as original data pointer may become invalid after callback
    if(data && datalen > 0 && (event == LIOT_GNSS_EVENT_GNSS_NMEA)) {
        event_msg.data = liot_rtos_malloc(datalen + 1);
        if(event_msg.data) {
            memcpy(event_msg.data, data, datalen);
            event_msg.data[datalen] = '\0';
            event_msg.data_owned = 1;
        } else {
            liot_trace("GNSS NMEA data malloc failed, len=%d", datalen);
            return;
        }
    } else {
        event_msg.data = data;
    }

    gnss_demo_queue_send(&event_msg);
}

/**
 * @brief 1 minute work timer callback function
 * @details Closes GNSS module after timer timeout
 */
void gnss_work_timer_callback(void *param)
{
    liot_trace("GNSS work timer timeout, closing GNSS...");

    // Send internal close request to queue
    gnss_event_msg_t close_msg = {0};
    close_msg.msg_type = GNSS_DEMO_MSG_WORK_TIMEOUT;
    close_msg.data = NULL;
    close_msg.datalen = 0;
    gnss_demo_queue_send(&close_msg);
}

/**
 * @brief 10 second restart timer callback function
 * @details Reopens GNSS module after timer timeout
 */
void gnss_restart_timer_callback(void *param)
{
    liot_trace("GNSS restart timer timeout, restarting GNSS...");

    // Send internal restart request to queue
    gnss_event_msg_t restart_msg = {0};
    restart_msg.msg_type = GNSS_DEMO_MSG_RESTART_TIMEOUT;
    restart_msg.data = NULL;
    restart_msg.datalen = 0;
    gnss_demo_queue_send(&restart_msg);
}

/**
 * @brief Get GNSS demo data
 * @details Retrieves and prints GNSS positioning information and NMEA data
 */
void liot_gnss_get_demo_data(void)
{
    liot_gnss_loc_info_t loc_info = {0};
    char *nmea_data[10] = {NULL};
    uint16_t nmea_data_len = 0;

    liot_trace("get demo data...");

    // Get NMEA data
    if(liot_gnss_get_nmea(LIOT_GNSS_NMEA_TYPE_GSA, nmea_data, 10, &nmea_data_len) == LIOT_GNSS_SUCCESS)
    {
        for(int i=0; i<nmea_data_len; i++)
        {
            liot_trace("%s", nmea_data[i]);
            liot_rtos_free(nmea_data[i]);
            nmea_data[i] = NULL;
        }
    }
    else
    {
        liot_trace("Failed to get GSA NMEA data");
    }

    nmea_data_len = 0;
    if(liot_gnss_get_nmea(LIOT_GNSS_NMEA_TYPE_VTG, nmea_data, 10, &nmea_data_len) == LIOT_GNSS_SUCCESS)
    {
        for(int i=0; i<nmea_data_len; i++)
        {
            liot_trace("%s", nmea_data[i]);
            liot_rtos_free(nmea_data[i]);
            nmea_data[i] = NULL;
        }
    }
    else
    {
        liot_trace("Failed to get VTG NMEA data");
    }

    // Get positioning information
    memset(&loc_info, 0, sizeof(loc_info));
    if(liot_gnss_get_location(&loc_info) == LIOT_GNSS_SUCCESS)
    {
        liot_trace("utc: %s, fix_type: %d, num_sats: %d", loc_info.utc, loc_info.fix, loc_info.satcount);
        liot_trace("Latitude: %s, Longitude: %s", loc_info.latitude, loc_info.longitude);
    }
    else
    {
        liot_trace("Failed to get GNSS location");
    }
}

/**
 * @brief Start GNSS module
 * @details Configures and opens GNSS module, starts work timer
 */
void start_gnss_module(void)
{
    liot_gnss_config_t gnss_config = {0};

    liot_trace("Starting GNSS module...");

    // GNSS configuration
#if defined (GNSS_TEST_E0_0G)
    gnss_config.gnss_ic_model = GNSS_IC_CC1161W;
    gnss_config.gnsscfg_type = LIOT_GNSS_CFG_TYPE_GPS | LIOT_GNSS_CFG_TYPE_BEIDOU;
#elif defined (GNSS_TEST_E1_0G)
    gnss_config.gnss_ic_model = GNSS_IC_CC1177W;
    gnss_config.gnsscfg_type = LIOT_GNSS_CFG_TYPE_BEIDOU;
#endif
    gnss_config.gnssnmea_type = LIOT_GNSS_NMEA_TYPE_GGA | LIOT_GNSS_NMEA_TYPE_GSA;
    gnss_config.apflash = 0;
    gnss_config.agnss_mode = 0;

#if AGNSS_DEMO_ENABLE
    liot_agnss_config(NULL);
    liot_rtos_task_sleep_ms(10000);
#endif

    // Configure and open GNSS
    if(liot_gnss_config(&gnss_config) != LIOT_GNSS_SUCCESS) {
        g_gnss_state = GNSS_STATE_IDLE;
        liot_trace("Failed to configure GNSS module");
        return;
    }

    if(liot_gnss_open(liot_gnss_demo_event) == LIOT_GNSS_SUCCESS) {
        g_gnss_state = GNSS_STATE_RUNNING;
        liot_trace("GNSS module opened successfully");

        // Start 1 minute work timer
        liot_rtos_timer_start(gnss_work_timer, GNSS_WORK_TIMEOUT);
        liot_trace("GNSS work timer started for %d ms", GNSS_WORK_TIMEOUT);
    } else {
        g_gnss_state = GNSS_STATE_IDLE;
        liot_trace("Failed to open GNSS module");
    }
}

/**
 * @brief Stop GNSS module
 * @details Closes GNSS module, starts restart timer
 */
void stop_gnss_module(void)
{
    liot_trace("Stopping GNSS module...");

    if(liot_gnss_close() == LIOT_GNSS_SUCCESS) {
        g_gnss_state = GNSS_STATE_IDLE;
        gnss_ready_state = 0;
        liot_trace("GNSS module closed successfully");

        // Start 10 second restart timer
        liot_rtos_timer_start(gnss_restart_timer, GNSS_RESTART_TIMEOUT);
        liot_trace("GNSS restart timer started for %d ms", GNSS_RESTART_TIMEOUT);

        liot_show_mem();
    } else {
        g_gnss_state = GNSS_STATE_RUNNING;
        liot_trace("Failed to close GNSS module");
    }
}

/**
 * @brief GNSS demo main thread
 * @details Uses event-driven approach to handle GNSS operations and timer control
 */
void liot_gnss_demo_thread(void *argv)
{

    gnss_event_msg_t event_msg = {0};
    int ret = 0;

    liot_rtos_task_sleep_ms(2000);
    liot_trace("max--GNSS demo thread started");

    // Create queue
    if(liot_rtos_queue_create(&gnss_queuehandle, sizeof(gnss_event_msg_t), GNSS_QUEUE_MAX) != LIOT_SUCCESS)
    {
        liot_trace("Failed to create GNSS demo queue");
        return;
    }

    // Create timers
    if(liot_rtos_timer_create(&gnss_work_timer, LIOT_TimerOnce, gnss_work_timer_callback, NULL) != LIOT_SUCCESS)
    {
        liot_trace("Failed to create GNSS work timer");
        return;
    }

    if(liot_rtos_timer_create(&gnss_restart_timer, LIOT_TimerOnce, gnss_restart_timer_callback, NULL) != LIOT_SUCCESS)
    {
        liot_trace("Failed to create GNSS restart timer");
        return;
    }

    // Start GNSS module
    start_gnss_module();

    liot_trace("GNSS demo started, waiting for events...");

    // Event processing loop
    while(1)
    {
        ret = liot_rtos_queue_wait(gnss_queuehandle, (uint8 *)&event_msg, sizeof(gnss_event_msg_t), LIOT_WAIT_FOREVER);
        if (ret != LIOT_SUCCESS)
        {
            continue;
        }

        // Process demo internal messages and GNSS events
        switch(event_msg.msg_type)
        {
            case GNSS_DEMO_MSG_WORK_TIMEOUT:
            {
                if(g_gnss_state == GNSS_STATE_RUNNING)
                {
                    g_gnss_state = GNSS_STATE_CLOSING;
                    stop_gnss_module();
                }
                else
                {
                    liot_trace("Ignore GNSS work timeout in state %d", g_gnss_state);
                }
            }
            break;

            case GNSS_DEMO_MSG_RESTART_TIMEOUT:
            {
                if(g_gnss_state == GNSS_STATE_IDLE)
                {
                    g_gnss_state = GNSS_STATE_RESTARTING;
                    start_gnss_module();
                }
                else
                {
                    liot_trace("Ignore GNSS restart timeout in state %d", g_gnss_state);
                }
            }
            break;

            case GNSS_DEMO_MSG_GNSS_EVENT:
            {
                switch(event_msg.event_type)
                {
                    case LIOT_GNSS_EVENT_GNSS_READY:
                    {
                        liot_trace("GNSS is ready");
                        gnss_ready_state = 1;

                        liot_show_mem();
                    }
                    break;

                    case LIOT_GNSS_EVENT_GNSS_NMEA:
                    {
                        if(event_msg.data && event_msg.datalen > 0)
                        {
                            uint16_t copy_len = event_msg.datalen;

                            if(copy_len >= sizeof(gnss_nmea_data))
                            {
                                copy_len = sizeof(gnss_nmea_data) - 1;
                                liot_trace("GNSS NMEA data truncated, len=%d", event_msg.datalen);
                            }

                            memset(gnss_nmea_data, 0, sizeof(gnss_nmea_data));
                            memcpy(gnss_nmea_data, (char *)event_msg.data, copy_len);
                            gnss_nmea_data[copy_len] = '\0';
                            liot_trace("GNSS NMEA: %s", gnss_nmea_data);
                        }

                        gnss_demo_free_msg_data(&event_msg);
                    }
                    break;

                    case LIOT_GNSS_EVENT_GNSS_CLOSED:
                    {
                        liot_trace("GNSS is closed");
                    }
                    break;

                    case LIOT_GNSS_EVENT_GNSS_ERROR:
                    {
                        liot_trace("GNSS error occurred");
                    }
                    break;

                    default:
                    {
                        liot_trace("Unknown GNSS event: %d", event_msg.event_type);
                    }
                    break;
                }
            }
            break;

            default:
            {
                liot_trace("Unknown GNSS demo message: %d", event_msg.msg_type);
            }
            break;
        }
    }
}

6.1 GNSS 启动流程分析

  1. 配置优先:启动 GNSS 前必须先完成参数配置。示例中 start_gnss_module() 先根据模组型号配置 gnss_ic_model 和 gnsscfg_type,再调用 liot_gnss_config() 写入 GNSS 参数;如需使用 AGNSS,还应先调用 liot_agnss_config() 配置辅助定位参数。

  2. 事件驱动:liot_gnss_open() 用于打开 GNSS 模块并注册事件回调,接口调用成功后会立即返回。GNSS 就绪、NMEA 输出、关闭、错误等状态通过 liot_gnss_demo_event() 回调上报,应用层不应假设 liot_gnss_open() 返回后 GNSS 已经完成定位。

  3. 异步就绪等待:当前示例使用消息队列等待 GNSS 事件。liot_gnss_demo_event() 将回调事件投递到队列,主循环通过 liot_rtos_queue_wait() 等待并处理事件;当收到 LIOT_GNSS_EVENT_GNSS_READY 后,将 gnss_ready_state 置为 1,表示 GNSS 已就绪。这是等待异步就绪的方式。

运行结果:

_images/gnss-guide/image_1.png
_images/gnss-guide/image_2.png

7 注意事项

  1. 该版本硬件中 Cat.1 芯片和 GNSS 芯片通过 UART2 连接,GNSS 电源通过 Cat.1 芯片 AGPIO3、AGPIO5 控制,请勿将 UART2、AGPIO3、AGPIO5 复用为其他功能,可能会导致模组工作异常。

  2. NT26KCNE20GNA/NT26K2E0_0G 模组内部定位芯片为 CC1161W、NT26KCNE20GNB/NT26K2E1_0G 模组内部定位芯片为 CC1177W,使用时请按照对应型号正确配置 gnss_ic_model 参数。

  3. NT26KCNE20GNB/NT26K2E1_0G 模组内部的定位芯片 CC1177W 仅支持单北斗卫星系统定位,如果在 gnsscfg_type 配置项中将其配置为其他参数,不会实际生效。

  4. 开启辅助定位将改善定位精度。

  5. 当前示例默认定义 GNSS_TEST_E1_0G。如需测试 GNSS_TEST_E0_0G,需切换对应宏,并按源码中的配置使用 GNSS_IC_CC1161W 和 GPS + 北斗融合定位。

  6. 当前示例中 GNSS_WORK_TIMEOUT 为 60 秒,GNSS_RESTART_TIMEOUT 为 10 秒;GNSS 打开后运行 60 秒并关闭,再等待 10 秒后重启。

  7. liot_gnss_get_nmea() 返回的 NMEA 字符串由接口分配,示例中使用 liot_rtos_free() 释放。

  8. 回调中的 data 指针不应在回调返回后长期保存;示例在 LIOT_GNSS_EVENT_GNSS_NMEA 事件中先复制 NMEA 数据,再通过队列处理。

  9. 当前示例中 AGNSS_DEMO_ENABLE 为 1 时会调用 liot_agnss_config(NULL) 并等待 10 秒,但 gnss_config.agnss_mode 设置为 0;如需正式使用 AGNSS,请按实际平台配置有效 liot_agnss_config_t 参数并同步开启 agnss_mode。