mcu_hi3321_watch/tjd/service_cpp/gnss/service_pgnss.cpp
2025-05-26 20:15:20 +08:00

494 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*----------------------------------------------------------------------------
* Copyright (c) TJD Technologies Co., Ltd. 2025. All rights reserved.
*
* Description: service_pgnss.c
*
* Author: luziquan@ss-tjd.com
*
* Create: 2025-05-09
*--------------------------------------------------------------------------*/
#include "service_pgnss.h"
#include "gnss_datatypes.h"
#include "gnss_nmea_parse.h"
#include "osal_atomic.h"
#include "osal_task.h"
#include "pgnss_encode.h"
#include "pm_definition.h"
#include "pm_veto.h"
#include "rtc_api.h"
#include "securec.h"
#include "sys_config.h"
#include "tiot_service_interface.h"
#include "tiot_service_interface_ext.h"
#include <stdio.h>
#include <string>
#define ENABLE_PRINT_INFO 1
#define ENABLE_DEBUG 0
#if ENABLE_PRINT_INFO
#define static_print_info(...) sys_gps_log_i(__VA_ARGS__) //一般信息打印宏控制
#define static_print_warn(...) sys_gps_log_w(__VA_ARGS__) //警告信息打印一般常开
#define static_print_error(...) sys_gps_log_e(__VA_ARGS__) //错误信息打印一般常开
#if ENABLE_DEBUG
#define static_print_debug(...) sys_gps_log_d(__VA_ARGS__)
#else
#define static_print_debug(...)
#endif
#else
#define static_print_info(...)
#define static_print_warn(...)
#define static_print_error(...)
#endif
namespace TJD {
int GnssNmeaDecodeFrame(const char *nmea, unsigned int nmeaLen, GnssInfo *gnssInfo,
GnssNmeaFrameProcess *nmeaFrameProcess);
GnssNmeaFrameType GnssNmeaGetFrameType(const char *nmeaBuf, unsigned int length);
} // namespace TJD
#define MSG_TYPE_ASCII 1
static GnssServiceContext g_gnssServiceCtx = {
.gnssEnabled = false,
.gnssStarted = false,
.gnssCallback = {},
.gnssInfo = {},
// .location = {},
.satStatus = {},
.listenGnssDataThreadId = nullptr,
};
static osThreadId_t g_gnssThreadId = NULL;
static FILE *g_gnssLogFp = NULL;
static uint8_t *g_gnssServiceBuff = NULL;
static osMutexId_t g_gnssMutex = NULL;
static int32_t QryVersion(tiot_handle handle)
{
const uint8_t qryVersion[] = {0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
// 查询版本号
int32_t ret = tiot_service_write(handle, qryVersion, sizeof(qryVersion));
if (ret < 0) {
static_print_error("error: QryVersion error: %d", ret);
return 0;
}
return 1;
}
// 注入PGNSS使用默认配置启动
static int32_t InjectPgnssLessFilesCfgAutoStartFixModeAndNmeaDefault(tiot_handle handle)
{
// clang-format off
const uint8_t cfgColdStartCmd[] = {0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
const uint8_t cfgAutoStartCmd[] = {0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00};
const uint8_t cfgLogLevelInfo[] = {0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x10, 0x01, 0x00, 0x03};
const uint8_t cfgFixModeAllCmd[] = {0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x00, 0x00};
const uint8_t cfgAllNmeaCmd[] = {0x03, 0x00, 0x04, 0x01, 0x00, 0x00, 0x06, 0x00, 0x03, 0x00,0x02, 0x00, 0xff, 0x00};
// // const uint8_t cfgNmeaDefaultCmd[] = {0x03,0x00,0x16,0x00,0x00,0x00,0x06,0x00,0x03,0x00,0x02,0x00,0x11,0x00};
// // const uint8_t cfgDisablePmCmd[] = {0x03,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x02,0x00,0x00,0x00};
// // const uint8_t cfgEnablePmCmd[] = {0x03,0x00,0x0A,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x02,0x00,0x01,0x02};
// clang-format on
int32_t ret = 0;
// 配置启动模式:自动模式。
ret = tiot_service_write(handle, cfgAutoStartCmd, sizeof(cfgAutoStartCmd));
if (ret < 0) {
static_print_error("cfgAutoStartCmd error: %d", ret);
return 0;
}
// 配置启动模式:冷启动模式。
// ret = tiot_service_write(handle, cfgColdStartCmd, sizeof(cfgColdStartCmd));
// if (ret < 0) {
// static_print_error("cfgColdStartCmd error: %d", ret);
// return 0;
// }
// 配置log级别info级别。
ret = tiot_service_write(handle, cfgLogLevelInfo, sizeof(cfgLogLevelInfo));
if (ret < 0) {
static_print_error("cfgLogLevelInfo error: %d", ret);
return 0;
}
// 配置使能的GNSS星座QZSS/GAL/BDS/GLO/GPS。
ret = tiot_service_write(handle, cfgFixModeAllCmd, sizeof(cfgFixModeAllCmd));
if (ret < 0) {
static_print_error("cfgFixModeAllCmd error: %d", ret);
return 0;
}
// 配置上报NMEA类型PNT/ZDA/VTG/RMC/GSV/GSA/GLL/GGA。
ret = tiot_service_write(handle, cfgAllNmeaCmd, sizeof(cfgAllNmeaCmd));
if (ret < 0) {
static_print_error("cfgAllNmeaCmd error: %d", ret);
return 0;
}
// ret = tiot_service_write(handle, cfgDisablePmCmd, sizeof(cfgDisablePmCmd));
// if (ret < 0) {
// static_print_error("error: cfgDisablePmCmd error: %d", ret);
// return 0;
// }
// 注入UTC时间, 注入Pgnss辅助数据前必须先注入参考时间
struct rtc_time time;
tjd_driver_rtc_get_ops()->get_rtc_utc_time(&time);
// 需要用当前的UTC时间替换
GnssUtcTime utcTime = {static_cast<uint16_t>(time.tm_year),
static_cast<uint8_t>(time.tm_mon),
static_cast<uint8_t>(time.tm_mday),
static_cast<uint8_t>(time.tm_hour),
static_cast<uint8_t>(time.tm_min),
static_cast<uint8_t>(time.tm_sec),
0,
1};
uint32_t ts = 0;
ret = PgnssInjectUtcTime(handle, &utcTime, &ts);
if (ret == 0) {
static_print_error("error: PgnssInjectUtcTime failed!");
return 0;
}
static_print_debug("PgnssInjectUtcTime success!");
// PGNSS注入
PgnssInjectLessFiles(handle, ts);
return 1;
}
static int32_t StartGnss(tiot_handle handle)
{
const uint8_t startCmd[] = {0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
// 启动GNSS
int32_t ret = tiot_service_write(handle, startCmd, sizeof(startCmd));
if (ret < 0) {
static_print_error("error: start gnss error: %d", ret);
return 0;
}
static_print_debug("start gnss");
return 1;
}
static int32_t StopGnss(tiot_handle handle)
{
const uint8_t stopCmd[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// 停止GNSS
int32_t ret = tiot_service_write(handle, stopCmd, sizeof(stopCmd));
if (ret < 0) {
static_print_error("error: stop gnss error: %d", ret);
return 0;
}
static_print_debug("stop gnss");
return 1;
}
static void ResetGnssInfo(GnssServiceContext *gnssServiceCtx)
{
int32_t ret = memset_s(&gnssServiceCtx->gnssInfo, sizeof(GnssInfo), 0, sizeof(GnssInfo));
if (ret != EOK) {
static_print_error("memset_s GnssInfo failed:%d", ret);
}
}
static void ResetLocationSatelliteStatus(GnssServiceContext *gnssServiceCtx)
{
int32_t ret = memset_s(&gnssServiceCtx->location, sizeof(GnssLocation), 0, sizeof(GnssLocation));
if (ret != EOK) {
static_print_warn("memset_s GnssLocation failed:%d", ret);
}
ret = memset_s(&gnssServiceCtx->satStatus, sizeof(GnssSatelliteStatus), 0, sizeof(GnssSatelliteStatus));
if (ret != EOK) {
static_print_warn("memset_s GnssSatelliteStatus failed:%d", ret);
}
}
static int32_t LocationGnssUtilsFillGnssLocation(const GnssInfo *gnssInfo, GnssLocation *location)
{
if (gnssInfo->signal == 0) {
static_print_warn("Location invalid");
return GNSS_FAILURE;
}
location->fieldValidity = GNSS_NMEA_FIELD_TYPE_SMASK;
if (gnssInfo->field & GNSS_NMEA_FIELD_TYPE_LAT) {
location->latitude = gnssInfo->lat;
location->fieldValidity |= GNSS_LOCATION_LAT_VALID;
}
if (gnssInfo->field & GNSS_NMEA_FIELD_TYPE_LON) {
location->longitude = gnssInfo->lon;
location->fieldValidity |= GNSS_LOCATION_LONG_VALID;
}
if (gnssInfo->field & GNSS_NMEA_FIELD_TYPE_ANTALT) {
location->altitude = gnssInfo->altitude;
location->fieldValidity |= GNSS_LOCATION_ALTITUDE_VALID;
}
if (gnssInfo->field & GNSS_NMEA_FIELD_TYPE_SPEED) {
location->speed = gnssInfo->speed;
location->fieldValidity |= GNSS_LOCATION_SPEED_VALID;
}
if (gnssInfo->field & GNSS_NMEA_FIELD_TYPE_TRACK) {
location->bearing = gnssInfo->trackAng;
location->fieldValidity |= GNSS_LOCATION_BEARING_VALID;
}
return GNSS_SUCCESS;
}
GnssQualityLevel GetGnssSignalQualityLevel(const GnssInfo &gnssInfo)
{
const GnssSatelliteList *satList = &gnssInfo.satelist;
GnssQualityLevel quality = GnssQualityLevel::GNSS_QUAL_POOR;
int visableSatNum = 0;
int CnrMax = 0;
GnssSatelliteStatusInfo satInfos = satList->gps;
for (uint32_t i = 0; i < satInfos.satViewedCnt; ++i) {
if (satInfos.satelliteStatus[i].cnr > CnrMax) {
CnrMax = satInfos.satelliteStatus[i].cnr;
}
}
visableSatNum += satInfos.satViewedCnt;
satInfos = satList->bds;
for (uint32_t i = 0; i < satInfos.satViewedCnt; ++i) {
if (satInfos.satelliteStatus[i].cnr > CnrMax) {
CnrMax = satInfos.satelliteStatus[i].cnr;
}
}
visableSatNum += satInfos.satViewedCnt;
satInfos = satList->glonass;
for (uint32_t i = 0; i < satInfos.satViewedCnt; ++i) {
if (satInfos.satelliteStatus[i].cnr > CnrMax) {
CnrMax = satInfos.satelliteStatus[i].cnr;
}
}
visableSatNum += satInfos.satViewedCnt;
satInfos = satList->galileo;
for (uint32_t i = 0; i < satInfos.satViewedCnt; ++i) {
if (satInfos.satelliteStatus[i].cnr > CnrMax) {
CnrMax = satInfos.satelliteStatus[i].cnr;
}
}
visableSatNum += satInfos.satViewedCnt;
satInfos = satList->qzss;
for (uint32_t i = 0; i < satInfos.satViewedCnt; ++i) {
if (satInfos.satelliteStatus[i].cnr > CnrMax) {
CnrMax = satInfos.satelliteStatus[i].cnr;
}
}
visableSatNum += satInfos.satViewedCnt;
if (gnssInfo.hdop <= 1.5 && visableSatNum >= 8 && CnrMax >= 40) {
quality = GnssQualityLevel::GNSS_QUAL_HIGH; // 开阔环境高精度定位
} else if (gnssInfo.pdop <= 6 && visableSatNum >= 4 && CnrMax >= 35) {
quality = GnssQualityLevel::GNSS_QUAL_MEDIUM; // 城市街道常规导航
} else {
quality = GnssQualityLevel::GNSS_QUAL_POOR; // 隧道/室内定位失效
}
static_print_debug("quality:%d, visableSatNum:%d, CnrMax:%d", quality, visableSatNum, CnrMax);
return quality;
}
static void GnssContextDispatchEvent(const GnssInfo &gnssInfo, GnssNmeaFrameType nmeaType,
const GnssNmeaFrameProcess &nmeaFrameProcess)
{
int32_t ret = 0;
switch (nmeaType) {
case GnssNmeaFrameType::GNSS_NMEA_FRAME_GGA: {
const GnssNmeaGGA *gga = &nmeaFrameProcess.nmeaFrame.nmeaGGA;
break;
}
case GnssNmeaFrameType::GNSS_NMEA_FRAME_GSV: {
if (g_gnssServiceCtx.gnssCallback.onGsvUpdate != NULL) {
g_gnssServiceCtx.gnssCallback.onGsvUpdate(&gnssInfo.satelist);
}
if (g_gnssServiceCtx.gnssCallback.onSignalQualityUpdate != NULL) {
g_gnssServiceCtx.gnssCallback.onSignalQualityUpdate(GetGnssSignalQualityLevel(gnssInfo));
}
break;
}
case GnssNmeaFrameType::GNSS_NMEA_FRAME_RMC: {
break;
}
case GnssNmeaFrameType::GNSS_NMEA_FRAME_TH002: {
ResetLocationSatelliteStatus(&g_gnssServiceCtx);
ret = LocationGnssUtilsFillGnssLocation(&g_gnssServiceCtx.gnssInfo, &g_gnssServiceCtx.location);
if (ret == GNSS_SUCCESS) {
g_gnssServiceCtx.gnssCallback.onLocationUpdate(&g_gnssServiceCtx.location);
}
break;
}
default:
break;
}
}
static void GnssListenDataTask(void *argument)
{
int32_t ret = 0;
UNUSED(argument);
g_gnssServiceCtx.gnssStarted = true;
uapi_pm_add_sleep_veto(PM_ID_SYS);
tiot_handle handle = tiot_service_open("gn71", NULL);
if (handle == 0) {
static_print_error("open tiot service fail");
goto GNSS_STOP;
}
static_print_debug("open tiot service success");
if (InjectPgnssLessFilesCfgAutoStartFixModeAndNmeaDefault(handle) == 0) {
static_print_error("InjectPgnssLessFilesCfgAutoStartFixModeAndNmeaDefault failed");
goto GNSS_STOP;
}
if (QryVersion(handle) == 0) {
static_print_error("QryVersion failed");
goto GNSS_STOP;
}
if (StartGnss(handle) == 0) {
static_print_error("start gnss failed");
goto GNSS_STOP;
}
if (g_gnssServiceBuff == NULL) {
g_gnssServiceBuff = (uint8_t *)malloc(REPORT_MAX_BYTES * sizeof(uint8_t));
if (g_gnssServiceBuff == NULL) {
static_print_error("malloc failed in gps_service_task");
goto GNSS_STOP;
}
}
// listening gnss data
while (true) {
osMutexAcquire(g_gnssMutex, osWaitForever);
bool shouldExit = !g_gnssServiceCtx.gnssStarted || g_gnssServiceCtx.gnssEnabled;
osMutexRelease(g_gnssMutex);
if (shouldExit) {
break;
}
GnssNmeaFrameProcess nmeaFrameProcess = {};
ret = tiot_service_read(handle, g_gnssServiceBuff, REPORT_MAX_BYTES, LISTEN_TIME_THR);
if (ret <= 0) {
static_print_error("error: tiot_service_read error: %d", ret);
continue;
}
uint16_t *type = (uint16_t *)g_gnssServiceBuff;
if (type[0] != MSG_TYPE_ASCII) {
continue;
}
g_gnssServiceBuff[ret] = '\0';
const char *nmeaLine = ((const char *)&g_gnssServiceBuff[MSG_CONTENT_IDX]);
int32_t nmeaLen = ret - MSG_CONTENT_IDX;
GnssNmeaFrameType nmeaType = TJD::GnssNmeaGetFrameType(nmeaLine, nmeaLen);
static_print_debug("nmeaType:%d nmeaStr:%s nmeaLen:%d", nmeaType, nmeaLine, nmeaLen);
if (nmeaType >= GNSS_NMEA_FRAME_MAX) {
static_print_warn("invalid nmeaStr:%s", nmeaLine);
continue;
}
if (nmeaType == GNSS_NMEA_FRAME_GGA) {
ResetGnssInfo(&g_gnssServiceCtx);
}
ret = TJD::GnssNmeaDecodeFrame(nmeaLine, nmeaLen, &g_gnssServiceCtx.gnssInfo, &nmeaFrameProcess);
if (ret != GNSS_SUCCESS) {
static_print_warn("invalid nmeaStr:%s", nmeaLine);
}
if (ret == GNSS_SUCCESS) {
GnssContextDispatchEvent(g_gnssServiceCtx.gnssInfo, nmeaType, nmeaFrameProcess);
}
printf("%s", nmeaLine);
fprintf(g_gnssLogFp, "%s\n", nmeaLine);
}
GNSS_STOP:
StopGnss(handle);
tiot_service_close(handle);
fclose(g_gnssLogFp);
g_gnssLogFp = NULL;
if (g_gnssServiceBuff) {
free(g_gnssServiceBuff);
g_gnssServiceBuff = NULL;
}
uapi_pm_remove_sleep_veto(PM_ID_SYS);
static_print_debug("finish gnss");
osMutexAcquire(g_gnssMutex, osWaitForever);
g_gnssServiceCtx.gnssEnabled = false;
osMutexRelease(g_gnssMutex);
}
void GnssServiceRegisterCallback(GnssCallbackIfaces *callback)
{
memcpy_s(&g_gnssServiceCtx.gnssCallback, sizeof(GnssCallbackIfaces), callback, sizeof(GnssCallbackIfaces));
}
uint32_t GnssServiceOpen(void)
{
osMutexAcquire(g_gnssMutex, osWaitForever);
if (g_gnssServiceCtx.gnssStarted || g_gnssServiceCtx.gnssEnabled) {
osMutexRelease(g_gnssMutex);
return GNSS_THREAD_RUNNING;
}
// 创建线程前标记状态
g_gnssServiceCtx.gnssStarted = true;
osMutexRelease(g_gnssMutex);
// 打开日志文件
g_gnssLogFp = fopen("/user/gnss.log", "w");
if (g_gnssLogFp == NULL) {
static_print_error("open log file error");
return GNSS_FILE_OPEN_FAILURE;
}
osThreadAttr_t task_attr = {"TjdGnssListenDataTask", 0, NULL, 0, NULL, 0x2000, osPriorityHigh, 0, 0};
task_attr.stack_mem = memalign(16, task_attr.stack_size); // add
g_gnssThreadId = osThreadNew(GnssListenDataTask, NULL, &task_attr);
if (g_gnssThreadId == NULL) {
static_print_error("create gps service task failed");
return GNSS_THREAD_CREATE_FAILURE;
}
return GNSS_SUCCESS;
}
void GnssServiceClose(void)
{
osMutexAcquire(g_gnssMutex, osWaitForever);
if (!g_gnssServiceCtx.gnssStarted) {
osMutexRelease(g_gnssMutex);
return;
}
g_gnssServiceCtx.gnssStarted = false;
g_gnssServiceCtx.gnssEnabled = true; // 设置强制退出标志
osMutexRelease(g_gnssMutex);
// 等待线程实际退出
if (osThreadGetState(g_gnssThreadId) != osThreadTerminated) {
osThreadJoin(g_gnssThreadId);
}
}