mcu_hi3321_watch/tjd/service/gnss/pgnss_encode.c
2025-05-26 20:15:20 +08:00

614 lines
20 KiB
C

/*
* Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2024-2024. All rights reserved.
* Description: pgnss encode
* Author:
* Create:
*/
#include "pgnss_encode.h"
#include "fs_user_common.h"
#include "gnss_datatypes.h"
#include "osal_task.h"
#include "rtc_api.h"
#include "securec.h"
#include "sys_config.h"
#include "tiot_service_interface.h"
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#define ENABLE_PRINT_INFO 1
#define ENABLE_DEBUG 1
#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
#define GPS_PATH TJD_FS_DIR_GPS "/"
#define GNSS_ENCODE_BUFF_MAX_LEN 9300
#define FILE_PATH_LEN 1024
#define GLO_EPH_VALID_TIME 900
#define OTHER_EPH_VALID_TIME 7200
#define ONE_CENTURY_YEARS 100
#define FOUR_CENTURY_YEARS 400
#define LEAP_YEAR_LENGTH 4
#define UNIX_TIME_START_YEAR 1970 // start year of unix time
#define TIME_UNIT 1000
#define SECONDS_PER_MIN (60)
#define SECONDS_PER_HOUR (60 * SECONDS_PER_MIN)
#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
#define SECONDS_PER_NORMAL_YEAR (365 * SECONDS_PER_DAY)
#define NON_GLO_GNSS_TYPES 4
#define GLO_EPH_NUM_ONE_FILE 8
#define OTHER_ASSIST_TYPES 7
#define INJECT_SLEEP_MS_TIME 500
#define PGNSS_FILE_MAX_SIZE 1024000
/** days per month on normal and leap year */
int32_t g_normMonthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int32_t g_leapMonthDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 监听消息
static uint8_t g_ackBuff[REPORT_MAX_BYTES] = {0};
/* 获取begin和end年份之间的闰年数 */
int32_t GetLeapYearNum(uint16_t begin, uint16_t end)
{
if (end <= begin || end < 1) {
return 0;
}
int32_t numPer400Year = (end - 1) / FOUR_CENTURY_YEARS - begin / FOUR_CENTURY_YEARS;
int32_t numPer100Year = (end - 1) / ONE_CENTURY_YEARS - begin / ONE_CENTURY_YEARS;
int32_t numPer4Year = (end - 1) / LEAP_YEAR_LENGTH - begin / LEAP_YEAR_LENGTH;
return (numPer400Year + numPer4Year - numPer100Year);
}
/* 闰年判断 */
int32_t CheckIfLeapYear(uint16_t year)
{
if ((year % ONE_CENTURY_YEARS) == 0) {
if ((year % FOUR_CENTURY_YEARS) == 0) {
return 1;
}
} else {
if ((year % LEAP_YEAR_LENGTH) == 0) {
return 1;
}
}
return 0;
}
/* 将日历时(年月日时分秒) 转换为 unix时间(1970年1月1日起的时间秒数) */
int32_t Utc2UnixTime(GnssUtcTime utcTime, uint32_t *unixTime)
{
if (unixTime == NULL) {
static_print_debug("error: [Utc2UnixTime] null pointer!");
return 0;
}
if (utcTime.month > 12 || utcTime.valid == 0) {
static_print_debug("error: [Utc2UnixTime] utcTime invalid!");
return 0;
}
// 判断是否为闰年
int32_t *monthDays = (CheckIfLeapYear(utcTime.year) == 1) ? g_leapMonthDays : g_normMonthDays;
// 计算从1970.01.01开始的unix 秒数
uint32_t timestamp = 0;
timestamp += (uint32_t)(utcTime.year - UNIX_TIME_START_YEAR) * SECONDS_PER_NORMAL_YEAR;
timestamp += (uint32_t)GetLeapYearNum(UNIX_TIME_START_YEAR, utcTime.year) * SECONDS_PER_DAY;
for (int32_t i = 1; i < utcTime.month; i++) {
timestamp += (uint32_t)monthDays[i - 1] * SECONDS_PER_DAY;
}
timestamp += (uint32_t)(utcTime.day - 1) * SECONDS_PER_DAY;
timestamp += (uint32_t)utcTime.hour * SECONDS_PER_HOUR;
timestamp += (uint32_t)utcTime.minute * SECONDS_PER_MIN;
uint32_t timestampSec = (uint32_t)(utcTime.second + utcTime.ms / TIME_UNIT);
*unixTime = timestamp + timestampSec;
return 1;
}
/* 计算命令内容校准和 */
static uint16_t CalcChecksum(uint8_t *buff, uint16_t len)
{
uint16_t checksum = 0;
uint16_t i = 0;
while (i++ < len) {
checksum += *buff;
buff++;
}
return checksum;
}
int32_t PgnssInjectUtcTime(tiot_handle handle, GnssUtcTime *utcTime, uint32_t *ts)
{
uint8_t *buff = (uint8_t *)malloc(GNSS_ENCODE_BUFF_MAX_LEN);
if (buff == NULL) {
static_print_error("malloc failed in PgnssINjectUtcTime");
return 0;
}
memset_s(buff, GNSS_ENCODE_BUFF_MAX_LEN, 0, GNSS_ENCODE_BUFF_MAX_LEN);
GnssMsg *hdr = (GnssMsg *)buff;
hdr->cmd = 0x0404;
hdr->sequence = 0;
errno_t copyRet = memcpy_s(hdr->data, (GNSS_ENCODE_BUFF_MAX_LEN - sizeof(GnssMsg)), utcTime, sizeof(GnssUtcTime));
if (copyRet != EOK) {
static_print_debug("memcpy UtcTime failed! Errno is %d", copyRet);
free(buff);
return 0;
}
hdr->dataLength = sizeof(GnssUtcTime);
hdr->checkSum = CalcChecksum(hdr->data, hdr->dataLength);
int32_t ret = tiot_service_write(handle, buff, (sizeof(GnssMsg) + hdr->dataLength));
if (ret < 0) {
static_print_error("PgnssInjectUtcTime error: %d", ret);
free(buff);
return 0;
}
// if (Utc2UnixTime((*utcTime), ts) == 0) {
// static_print_error("error: Utc2UnixTime error");
// free(buff);
// return 0;
// }
uint64_t time_stamp = 0;
tjd_driver_rtc_get_ops()->get_timestamp(&time_stamp);
*ts = time_stamp;
free(buff);
return 1;
}
static uint16_t FillGnssMessageData(uint8_t *inBuff, uint32_t inLen, uint8_t *outBuff, uint32_t outLen)
{
errno_t ret = memcpy_s(outBuff, outLen, inBuff, inLen);
if (ret != EOK) {
static_print_debug("memcpy XgnssData failed! Errno is %d", ret);
return 0;
}
return inLen;
}
static int32_t EncodeGnssMsg(uint8_t *inBuff, uint32_t inLen, uint8_t *outBuff, uint32_t outLen)
{
uint16_t len = FillGnssMessageData(inBuff, inLen, outBuff, outLen);
if (len > 0) {
return 1;
}
return 0;
}
static void ListenAck(tiot_handle handle)
{
int32_t i = 0;
for (;;) {
// 监听
int32_t ret = tiot_service_read(handle, g_ackBuff, REPORT_MAX_BYTES, LISTEN_TIME_THR);
if (ret > 0) {
uint16_t *type = (uint16_t *)g_ackBuff;
// 二进制消息类型
if (type[0] == 0) {
g_ackBuff[ret] = '\0';
}
i++;
}
if (i >= 1) {
break;
}
}
}
static int32_t EncodeAndInjectCmd(tiot_handle handle, GnssCmdType cmdType, uint8_t *inBuff, uint32_t inLen)
{
// 获取GNSS业务消息头
int32_t len = 0;
uint8_t *buff = (uint8_t *)malloc(GNSS_ENCODE_BUFF_MAX_LEN);
if (buff == NULL) {
static_print_debug("error: malloc failed in EncodeAndInjectCmd");
return 0;
}
memset_s(buff, GNSS_ENCODE_BUFF_MAX_LEN, 0, GNSS_ENCODE_BUFF_MAX_LEN);
GnssMsg *gnssHdr = (GnssMsg *)buff;
len += sizeof(GnssMsg);
gnssHdr->sequence = 0;
int32_t retLen = EncodeGnssMsg(inBuff, inLen, buff + len, (GNSS_ENCODE_BUFF_MAX_LEN - len));
if (retLen != 1) {
static_print_debug("error: Encode Cmd failed: %d", cmdType);
free(buff);
return retLen;
}
gnssHdr->cmd = cmdType;
gnssHdr->dataLength = inLen;
gnssHdr->checkSum = CalcChecksum(gnssHdr->data, gnssHdr->dataLength);
int32_t ret = tiot_service_write(handle, buff, (sizeof(GnssMsg) + gnssHdr->dataLength));
if (ret < 0) {
static_print_debug("error: Inject Cmd failed: %d", cmdType);
free(buff);
return 0;
}
ListenAck(handle);
static_print_debug("Inject Cmd SUCCESS: %x", cmdType);
free(buff);
return 1;
}
void PgnssSendFileContent(tiot_handle handle, const char *fileName, GnssCmdType cmdType)
{
FILE *fp = fopen(fileName, "rb");
if (fp == NULL) {
static_print_error("error: open file failed: %s", fileName);
return;
}
fseek(fp, 0, SEEK_END);
int32_t len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (len < 0 || len > PGNSS_FILE_MAX_SIZE) {
fclose(fp);
static_print_error("error: get file len failed: %s", fileName);
return;
}
uint8_t *buff = (uint8_t *)malloc(len);
if (buff == NULL) {
fclose(fp);
static_print_error("error: malloc file len failed: %s", fileName);
return;
}
int32_t dataSize = fread(buff, len, 1, fp);
if (dataSize < 0) {
fclose(fp);
static_print_error("error: read file failed: %s\n", fileName);
free(buff);
return;
}
fclose(fp);
EncodeAndInjectCmd(handle, cmdType, buff, len);
free(buff);
}
static int32_t FormatFileName(char *fileName, char *dName)
{
int32_t ret = snprintf_s(fileName, FILE_PATH_LEN, (FILE_PATH_LEN - 1), "%s%s", GPS_PATH "", dName);
if (ret <= 0) {
return 0;
}
return 1;
}
int32_t GetEphFileName(int32_t ts, const char *prefix, char *fileName)
{
int32_t interval = (strcmp(prefix, "GLO") == 0) ? GLO_EPH_VALID_TIME : OTHER_EPH_VALID_TIME;
DIR *dir = opendir(GPS_PATH "");
if (dir == NULL) {
static_print_error("Failed to open /user/tjd_gps directory.");
return 0;
}
int32_t time, ret;
struct dirent *entry;
for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
// 检查文件名
if (strstr(entry->d_name, ".eph") != NULL) {
char *p = strrchr(entry->d_name, '_');
if (p != NULL) {
char *q = strstr(entry->d_name, prefix);
if (q != NULL) {
time = atoi(p + 1);
if (abs(time - ts) <= interval) {
ret = FormatFileName(fileName, entry->d_name);
closedir(dir);
return ret;
}
}
}
}
}
closedir(dir);
return 0;
}
void PgnssSendEphFile(tiot_handle handle, const char *prefix, GnssCmdType cmdType, uint32_t ts)
{
char fileName[FILE_PATH_LEN] = {0};
int32_t ret = GetEphFileName(ts, prefix, fileName);
if (ret == 0) {
static_print_error("error: GetEphFileName failed\n");
return;
}
static_print_debug("GetEphFileName success");
PgnssSendFileContent(handle, fileName, cmdType);
}
void PgnssInjectAllEph(tiot_handle handle, uint32_t ts)
{
PgnssSendEphFile(handle, "GPS", CMD_PGNSS_INJECT_EPH_GPS, ts);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GPS eph success");
PgnssSendEphFile(handle, "GLO", CMD_PGNSS_INJECT_EPH_GANSS, ts);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GLO eph success");
PgnssSendEphFile(handle, "BDS", CMD_PGNSS_INJECT_EPH_GANSS, ts);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send BDS eph success");
PgnssSendEphFile(handle, "GAL", CMD_PGNSS_INJECT_EPH_GANSS, ts);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GAL eph success");
PgnssSendEphFile(handle, "QZS", CMD_PGNSS_INJECT_EPH_GANSS, ts);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send QZS eph success");
}
void PgnssInjectOtherAssist(tiot_handle handle)
{
PgnssSendFileContent(handle, GPS_PATH "GPS_UTC.dat", CMD_PGNSS_INJECT_UTC_GPS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GPS_UTC success");
PgnssSendFileContent(handle, GPS_PATH "GPS_RTI.dat", CMD_PGNSS_INJECT_RTI_GPS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GPS_RTI success");
PgnssSendFileContent(handle, GPS_PATH "GPS_ION.dat", CMD_PGNSS_INJECT_ION_GPS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GPS_ION success");
PgnssSendFileContent(handle, GPS_PATH "GLO_RTI.dat", CMD_PGNSS_INJECT_RTI_GANSS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GLO_RTI success");
PgnssSendFileContent(handle, GPS_PATH "GLO_AUX.dat", CMD_PGNSS_INJECT_AUX_GANSS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GLO_AUX success");
PgnssSendFileContent(handle, GPS_PATH "BDS_RTI.dat", CMD_PGNSS_INJECT_RTI_GANSS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send BDS_RTI success");
PgnssSendFileContent(handle, GPS_PATH "GAL_RTI.dat", CMD_PGNSS_INJECT_RTI_GANSS);
osal_msleep(INJECT_SLEEP_MS_TIME);
static_print_debug("send GAL_RTI success");
}
void PgnssInject(tiot_handle handle, uint32_t ts)
{
// 注入Pgnss辅助数据
PgnssInjectAllEph(handle, ts);
PgnssInjectOtherAssist(handle);
}
// 以下为使用PGNSS解析数据合并输出接口生成的星历文件进行注入的代码
static int32_t GetNonGloEphFileName(int32_t ts, char *fileName)
{
const char prefix[] = "NonGlo";
DIR *dir = opendir(GPS_PATH "");
if (dir == NULL) {
static_print_error("Failed to open /user/tjd_gps directory.");
return 0;
}
int32_t time, ret;
int32_t minDist = OTHER_EPH_VALID_TIME;
int32_t closestTime = 0;
struct dirent *entry;
for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
// 检查文件名
if (strstr(entry->d_name, ".eph") != NULL) {
char *p = strrchr(entry->d_name, '_');
if (p != NULL) {
char *q = strstr(entry->d_name, prefix);
if (q != NULL) {
time = atoi(p + 1);
// 查找星历文件时间戳与当前时间相差2小时以内且距离最近的
if (abs(time - ts) < minDist) {
minDist = abs(time - ts);
closestTime = time;
}
}
}
}
}
if (closestTime != 0) {
ret =
snprintf_s(fileName, FILE_PATH_LEN, (FILE_PATH_LEN - 1), "%s%d%s", GPS_PATH "NonGlo_", closestTime, ".eph");
closedir(dir);
static_print_debug("closest eph fileName:%s", fileName);
return ((ret <= 0) ? 0 : 1);
}
closedir(dir);
return 0;
}
static int32_t GetGloEphFileName(int32_t ts, char *fileName)
{
const char prefix[] = "GLO";
DIR *dir = opendir(GPS_PATH "");
if (dir == NULL) {
static_print_debug("Failed to open /user/tjd_gps directory.");
return 0;
}
int32_t ephTime, ret;
struct dirent *entry;
for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
// 检查文件名
if (strstr(entry->d_name, ".eph") != NULL) {
char *p = strrchr(entry->d_name, '_');
if (p != NULL) {
char *q = strstr(entry->d_name, prefix);
if (q != NULL) {
ephTime = atoi(p + 1);
static_print_debug("ephTime:%ld, ts:%ld", ephTime, ts);
// 当前时间大于GLO星历文件时间戳,则需要选取相差2小时以内的星历
// 当前时间小于GLO星历文件时间戳,则需要选取相差小于15分钟的星历
if (((ts - ephTime) >= 0 && abs(ephTime - ts) < OTHER_EPH_VALID_TIME) ||
((ts - ephTime) <= 0 && abs(ephTime - ts) < GLO_EPH_VALID_TIME)) {
ret = FormatFileName(fileName, entry->d_name);
static_print_debug("glo eph fileName:%s", fileName);
closedir(dir);
return ret;
}
}
}
}
}
closedir(dir);
return 0;
}
static void PgnssSendNonGloEph(tiot_handle handle, const char *fileName)
{
FILE *fp = fopen(fileName, "rb");
if (fp == NULL) {
static_print_error("error: open file failed: %s\n", fileName);
return;
}
fseek(fp, 0, SEEK_END);
int32_t len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (len < 0 || len > PGNSS_FILE_MAX_SIZE) {
fclose(fp);
static_print_error("error: get file len failed: %s\n", fileName);
return;
}
uint8_t *buff = (uint8_t *)malloc(len);
if (buff == NULL) {
fclose(fp);
static_print_error("error: malloc file len failed: %s\n", fileName);
return;
}
int32_t dataSize = fread(buff, len, 1, fp);
if (dataSize < 0) {
fclose(fp);
static_print_error("error: read file failed: %s\n", fileName);
free(buff);
return;
}
fclose(fp);
int32_t fileLen = 0;
for (int32_t i = 0; i < NON_GLO_GNSS_TYPES && fileLen <= len; i++) {
EphHeadInfo *ephHdr = (EphHeadInfo *)(buff + fileLen);
EncodeAndInjectCmd(handle, ephHdr->cmd, (buff + fileLen + sizeof(EphHeadInfo)), ephHdr->len);
fileLen += (ephHdr->len + sizeof(EphHeadInfo));
osal_msleep(INJECT_SLEEP_MS_TIME);
}
free(buff);
}
static void PgnssSendGloEph(tiot_handle handle, const char *fileName, int32_t ts)
{
FILE *fp = fopen(fileName, "rb");
if (fp == NULL) {
static_print_error("error: open file failed: %s\n", fileName);
return;
}
fseek(fp, 0, SEEK_END);
int32_t len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (len < 0 || len > PGNSS_FILE_MAX_SIZE) {
fclose(fp);
static_print_error("error: get file len failed: %s\n", fileName);
return;
}
uint8_t *buff = (uint8_t *)malloc(len);
if (buff == NULL) {
fclose(fp);
static_print_error("error: malloc file len failed: %s\n", fileName);
return;
}
int32_t dataSize = fread(buff, len, 1, fp);
if (dataSize < 0) {
fclose(fp);
static_print_error("error: read file failed: %s\n", fileName);
free(buff);
return;
}
fclose(fp);
int32_t fileLen = 0;
for (int32_t i = 0; i < GLO_EPH_NUM_ONE_FILE && fileLen <= len; i++) {
EphHeadInfo *ephHdr = (EphHeadInfo *)(buff + fileLen);
static_print_debug("glo eph, time:%u, ts:%d", ephHdr->time, ts);
if (abs((int32_t)ephHdr->time - ts) < GLO_EPH_VALID_TIME) {
EncodeAndInjectCmd(handle, ephHdr->cmd, (buff + fileLen + sizeof(EphHeadInfo)), ephHdr->len);
osal_msleep(INJECT_SLEEP_MS_TIME);
break;
}
fileLen += (ephHdr->len + sizeof(EphHeadInfo));
}
free(buff);
}
static void PgnssInjectAllEphLessFiles(tiot_handle handle, uint32_t ts)
{
char fileName[FILE_PATH_LEN] = {0};
if (GetNonGloEphFileName(ts, fileName) == 0) {
static_print_error("GetNonGloEphFileName failed");
return;
}
PgnssSendNonGloEph(handle, fileName);
if (GetGloEphFileName(ts, fileName) == 0) {
static_print_error("GetGloEphFileName failed");
return;
}
PgnssSendGloEph(handle, fileName, ts);
}
static void PgnssSendOtherAssistInfo(tiot_handle handle, const char *fileName)
{
FILE *fp = fopen(fileName, "rb");
if (fp == NULL) {
static_print_error("open file failed: %s", fileName);
return;
}
fseek(fp, 0, SEEK_END);
int32_t len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (len < 0 || len > PGNSS_FILE_MAX_SIZE) {
fclose(fp);
static_print_error("get file len failed: %s", fileName);
return;
}
uint8_t *buff = (uint8_t *)malloc(len);
if (buff == NULL) {
fclose(fp);
static_print_error("malloc file len failed: %s", fileName);
return;
}
int32_t dataSize = fread(buff, len, 1, fp);
if (dataSize < 0) {
fclose(fp);
static_print_error("read file failed: %s", fileName);
free(buff);
return;
}
fclose(fp);
int32_t fileLen = 0;
for (int32_t i = 0; i < OTHER_ASSIST_TYPES && fileLen <= len; i++) {
OtherAssistHeadInfo *otherHdr = (OtherAssistHeadInfo *)(buff + fileLen);
EncodeAndInjectCmd(handle, otherHdr->cmd, (buff + fileLen + sizeof(OtherAssistHeadInfo)), otherHdr->len);
fileLen += (otherHdr->len + sizeof(OtherAssistHeadInfo));
osal_msleep(INJECT_SLEEP_MS_TIME);
}
free(buff);
}
static void PgnssInjectOtherAssistLessFiles(tiot_handle handle)
{
PgnssSendOtherAssistInfo(handle, GPS_PATH "AssistInfo.dat");
}
// 注入PGNSS解析数据合并输出接口生成的星历文件
void PgnssInjectLessFiles(tiot_handle handle, uint32_t ts)
{
// 注入Pgnss辅助数据
PgnssInjectAllEphLessFiles(handle, ts);
PgnssInjectOtherAssistLessFiles(handle);
}