/*---------------------------------------------------------------------------- * 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 #include #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(time.tm_year), static_cast(time.tm_mon), static_cast(time.tm_mday), static_cast(time.tm_hour), static_cast(time.tm_min), static_cast(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); } }