/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name          : Kalman.c
* Current Version    : V2.0
* Author             : logzhan
* Date of Issued     : 2022.10.15
* Comments           : PDR扩展卡尔曼滤波器融合
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <malloc.h>
#include "PDRBase.h"
#include "Kalman.h"
#include "Location.h"
#include "Matrix.h"
#include "Utils.h"
#include "LinearFit.h"
#include "CoordTrans.h"

// 扩展卡尔曼向量缓存
static double EkfVecBuf[5][N] = { {0.0} };
// 扩展卡尔曼矩阵缓存
static double EkfMatBuf[7][N][N] = { { {0.0} } };

extern double GpsPos[HIST_GPS_NUM][3];

extern int    debugCount;
extern double angle_mean[3];

static int         hold_type;
static double      b_timestamp;
static double      b_last_time;
static double      attitude_yaw;
static Detector_t *pDetector;
static int         hold_type;
static double      BUL_duration;
static double      FUR_duration;

/**----------------------------------------------------------------------
* Function    : KalmanFilter_Init
* Description : 初始化卡尔曼滤波器相关
* Date        : 2022/09/16 logzhan
*---------------------------------------------------------------------**/
void EKF_Init(void)
{
	b_timestamp  = -1;
	b_last_time  = -1;
	BUL_duration = -1;
	FUR_duration = -1;
	attitude_yaw = -1;
	hold_type = 0;
}

/**----------------------------------------------------------------------
* Function    : EKFStatePredict
* Description : pdr卡尔曼滤波器的状态预测方程
* Date        : 2022/10/15 logzhan
*---------------------------------------------------------------------**/
void EKFStatePredict(EKFPara_t *ekf, double stepLen, PDR_t* pdr, int step) {

	double (*Phi)[N]   = (double(*)[N])&(EkfMatBuf[0][0][0]);
	double (*pvec1)[N] = (double(*)[N])&(EkfMatBuf[1][0][0]);
	double (*pvec2)[N] = (double(*)[N])&(EkfMatBuf[2][0][0]);

	memset(EkfMatBuf, 0, sizeof(double) * 7 * N * N);

	double deltaHeading = pdr->heading - pdr->lastHeading;
	double angle        = pdr->heading;

	//printf("deltaHeading = %f\n", R2D(deltaHeading));
	//if (fabs(deltaHeading) < D2R(10) && g_pdr->lastHeading != 0.0f) {
	//	angle = deltaHeading + kf->pXk[3];
	//}else {
	//	angle = g_pdr->heading;
	//}

	if (pdr->MotionType != PDR_MOTION_TYPE_HANDHELD) {
		deltaHeading = 0;
	}

	angle = deltaHeading + ekf->pXk[3];


	if (fabs(deltaHeading) > 5.5) {
		if (deltaHeading < 0) {
			deltaHeading = deltaHeading + D2R(360);
		}else {
			deltaHeading = deltaHeading - D2R(360);
		}
		deltaHeading = 0;
		angle = deltaHeading + ekf->pXk[3];
	}

	if(step == 0){
		ekf->pXk[3] = angle;
	}

    // xk+1 = xk + step * cos(angle)   PDR位置更新方程
	// yk+1 = yk + step * sin(angle)   PDR位置更新方程
	// sk+1 = sk                       步长和上一次相同
	// ak+1 = ak * (angle / ak) 或 ak+1 = ak

	Phi[0][0] = 1;            Phi[1][0] = 0;          Phi[2][0] = 0;  Phi[3][0] = 0;
	Phi[0][1] = 0;			  Phi[1][1] = 1;		  Phi[2][1] = 0;  Phi[3][1] = 0;
	Phi[0][2] = cos(angle);	  Phi[1][2] = sin(angle); Phi[2][2] = 1;  Phi[3][2] = 0;
	Phi[0][3] = 0;            Phi[1][3] = 0;          Phi[2][3] = 0;  Phi[3][3] = 1;


	for (uchar i = 0; i < step; i++) {
		// logzhan 21/02/06:
		// phi[3][3]是为让kf的预测每次都是最新更新值, 如果phi[3][3]每次都是1，可能在惯性
		// 导航更新过程中，偏航角的预测是固定的，实时性没有那么好，下面if判断可以在一定程度
		// 让预测的值更新，但是效果暂时没有感觉出明显的好
		if (fabs(ekf->pXk[3]) > 0.000001 &&
			fabs(angle / ekf->pXk[3]) < 3)
		{
			Phi[3][3] = angle / ekf->pXk[3];
		}

		// 卡尔曼状态更新方程
		// pXk = phi * pXk
		VecMatMultiply(ekf->pXk, Phi, ekf->pXk);

		// 卡尔曼更新协方差矩阵 : Pk = FPFt + Q
		MatrixMultiply(Phi, ekf->pPk, pvec1);               // F*P
		MatrixTrans(Phi, pvec2);                            // Ft
		MatrixMultiply(pvec1, pvec2, pvec1);                // F*P*Ft
		MatrixAdd(pvec1, ekf->Q, ekf->pPk);                 // F*P*Ft + Q
	}
}

/**----------------------------------------------------------------------
* Function    : EKFStateUpdate
* Description : 扩展卡尔曼滤波器的状态更新方程
* Date        : 2020/7/22 logzhan
*---------------------------------------------------------------------**/
void EKFStateUpdate(EKFPara_t *kf, GNSS_t *pgnss,  Classifer_t *sys, PDR_t *g_pdr, 
	int scene_type) {

	double lambda = 0;

	// NxN矩阵定义
	double(*H)[N]     = (double(*)[N])&(EkfMatBuf[0][0][0]);
	double(*I)[N]     = (double(*)[N])&(EkfMatBuf[1][0][0]);
	double(*pvec1)[N] = (double(*)[N])&(EkfMatBuf[2][0][0]);
	double(*Ht)[N]    = (double(*)[N])&(EkfMatBuf[3][0][0]);
	double(*pvec3)[N] = (double(*)[N])&(EkfMatBuf[4][0][0]);
	double(*pvec4)[N] = (double(*)[N])&(EkfMatBuf[5][0][0]);
	double(*pv3)[N]   = (double(*)[N])&(EkfMatBuf[6][0][0]);

	memset(EkfMatBuf, 0, sizeof(double) * 7 * N * N);
	memset(EkfVecBuf, 0, sizeof(double) * 5 * N);

	// Nx1向量定义
	double(*pvec5)    = &(EkfVecBuf[1][0]);
	double(*pvec6)    = &(EkfVecBuf[2][0]);
	double(*pv1)      = &(EkfVecBuf[3][0]);
	double(*pv2)      = &(EkfVecBuf[4][0]);
	
	// 创建单位矩阵
	for (char i = 0; i < 4; i++) {
		H[i][i] = 1;
		I[i][i] = 1;
	}

	double* z = &(EkfVecBuf[0][0]);
	// 获取观测向量
	z[0] = pgnss->xNed;                                          // 北向位置
	z[1] = pgnss->yNed;                                          // 东向位置   
	z[2] = 0.5;                                                  // 步长默认0.5

	// 这个if条件实际是使用GPS的位置变化量和步数变化量估计步长, 这部分代码还没
	// 调好，实际类似于步长不变
	//if (pgnss->error > 0 && pgnss->error < 3.8 && pgnss->HDOP <= 0.4 && 
	//	g_pdr->deltaStepsPerGps != 0 && g_pdr->deltaStepsPerGps < 5) {
	//	// 利用GPS速度和步频估计步长
	//	//if (g_pdr->motionFreq > 2.5 && (sys->type == 1)) {
	//	//	
	//	//	//printf("z[2] = %f\n", z[2]);
	//	//}
	//	z[2] = pgnss->vel * 0.514 / (g_pdr->deltaStepsPerGps);
	//}else {
	//	z[2] = kf->pXk[2];
	//}

	z[3] = pgnss->yaw * PI / 180;                                // 观测的航向角为GPS的航向角

	// 计算 K = K = PHt / (R + H*P*H^T)
	MatrixTrans(H, Ht);                                             // 计算Ht
	MatrixMultiply(H, kf->pPk, pvec1);                             // 计算HP
	MatrixMultiply(pvec1, Ht, pvec3);                               // pvec3 = H*P*H^T
	MatrixAdd(pvec3, kf->R, pvec4);                                 // pvec4 = R + H*P*H^T
	MatrixInverse(pvec4, pvec1);                                    // 计算 1 / (R + H*P*H^T)

	for (char i = 0; i < N; i++) {
		for (uchar j = 0; j < N; j++) {
			pv3[i][j] = pvec3[i][j];
		}
	}
	MatrixMultiply(kf->pPk, Ht, pvec3);                                  // 计算PHt
	MatrixMultiply(pvec3, pvec1, kf->Kk);                                 // 计算增益K = PHt / (R + H*P*H^T)
								                                  
								                                  
	VecMatMultiply(kf->pXk, H, pvec5);                                   // pvec5 = Hx'
	VectorSub(z, pvec5, pvec6);                                       // pvec6 = Z - Hx'
	modAngle(&pvec6[3], -PI, PI);
	for (char i = 0; i < N; i++) {
		pv1[i] = pvec6[i];
	}
	
	VecMatMultiply(pvec6, kf->Kk, pvec5); // pvec5 = K*( z - Hx')
	// Get the optimally updated state estimation
	VectorAdd(kf->pXk, pvec5, kf->Xk);

	// ---- prepare the matrix for updated estimate covariance ------
	// pvec1 = K*H
	MatrixMultiply(kf->Kk, H, pvec1);
	// pvec2 = (I - K*H)
	MatrixSub(I, pvec1, Ht);
	// pvec3 = (I - K*H)*P
	MatrixMultiply(Ht, kf->pPk, pvec3);
	// pvec4 = (I- K*H)^T
	MatrixTrans(Ht, pvec4);
	// pvec1 = (I - K*H)*P*(I- K*H)^T
	MatrixMultiply(pvec3, pvec4, pvec1);

	// pvec2 = K*R
	MatrixMultiply(kf->Kk, kf->R, Ht);
	// pvec3 = K^T
	MatrixTrans(kf->Kk, pvec3);
	// pvec4 = K*R*K^T
	MatrixMultiply(Ht, pvec3, pvec4);
	// Get the updated estimate covariance: P' = (I - K*H)*P*(I- K*H)^T + K*R*K^T
	MatrixAdd(pvec1, pvec4, kf->Pk);

	// pv2 = (z - Hx')*( H*P*H^T )
	VecMatMultiply(pv1, pv3, pv2);

	// inner product the pre-fit residual and the transitioned residual: lamda = (z - Hx')*( H*P*H^T )  .*  (z-Hx')
	// Calculate the difference between original differenc and the transitioned,
	// if the value is close to zero, it shows that there is a big gap in the 
	// oprimization. Thus, we believe our PDR algorithm
	for (char i = 0; i < N; i++) {
		lambda += pv2[i] * pv1[i];
	}

	kf->Lambda = lambda;
	kf->pLat = pgnss->lat;
	kf->pLon = pgnss->lon;

	if (lambda >= 200)return;

	for (char i = 0; i < N; i++) {
		kf->Xk[i] = kf->pXk[i];
		for (int j = 0; j < N; j++) {
			kf->Pk[i][j] = kf->pPk[i][j];
		}
	}
	
}

void calStepLastTime(StepPredict *stepPredict, double timestamp, unsigned long steps_last, 
	unsigned long steps)
{
	float tmpTime = 0;
	if (stepPredict->lastTime > 0 && stepPredict->deltaStep == 0 && (steps - steps_last) > 0)
	{
		tmpTime = (float)((timestamp - stepPredict->lastTime) / (steps - steps_last));
		if (tmpTime > 300 && tmpTime < 800)
		{
			if (stepPredict->meanTime > 0){
				stepPredict->meanTime = stepPredict->meanTime * 0.5f + tmpTime * 0.5f;
			}else{
				stepPredict->meanTime = tmpTime;
			}
		}
	}

	stepPredict->fnum = 0;
	stepPredict->fsum = 0;

	stepPredict->lastTime = timestamp;
	stepPredict->deltaStep = 0;

}

int predictStep(StepPredict *stepPredict, double timestamp, unsigned long steps_last, float gnssVel)
{
	int step = 0;
	float mean_time = 400;
	double dis = 0;

	if (stepPredict->meanTime > 0)
		mean_time = stepPredict->meanTime;

	if ((steps_last > 0) && (stepPredict->fnum > 0) && (timestamp - stepPredict->lastTime > mean_time * 3) && (stepPredict->lastTime > 0))
	{
		stepPredict->fsum = stepPredict->fsum / stepPredict->fnum;		
		dis = sqrt(pow(GpsPos[HIST_GPS_NUM - 1][0]- GpsPos[HIST_GPS_NUM - 2][0], 2) + pow(GpsPos[HIST_GPS_NUM - 1][1] - GpsPos[HIST_GPS_NUM - 2][1], 2)
			 + pow(GpsPos[HIST_GPS_NUM - 1][2] - GpsPos[HIST_GPS_NUM - 2][2], 2));

		if (stepPredict->fsum != 0.0 && (gnssVel > 1.0 || (dis > 1.0 && dis < 3.0)))
		{
			step = (int)((timestamp - stepPredict->lastTime) / mean_time);
			stepPredict->deltaStep += step;
		}

		stepPredict->lastTime = timestamp;
		stepPredict->fnum = 0;
		stepPredict->fsum = 0;
	}

	return step;
}

/**----------------------------------------------------------------------
* Function    : StateRecognition
* Description : 根据加速度及判断当前状态是否平稳 1: 不平稳  0: 平稳
* Date        : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
void StateRecognition(float *acc, Classifer_t *sys) {

	int i, j, k;
	float accNorm = 0;
	float sum = 0, max_min = 0, max = -1, min = 999;
	float mean = 0, std = 0;

	// 先计算当前加速度的模
	accNorm = (float)sqrt(pow(acc[0], 2) + pow(acc[1], 2) + pow(acc[2], 2));

	// 缓存更新
	for (i = 0; i < PATTERN_RECOGNITION_LEN - 1; i++) {
		sys->accBuffer[i] = sys->accBuffer[i + 1];
	}
	sys->accBuffer[PATTERN_RECOGNITION_LEN - 1] = accNorm;

	// 计算最大最小值和sum
	for (j = 0; j < PATTERN_RECOGNITION_LEN - 1; j++) {
		sum += sys->accBuffer[j];
		if (sys->accBuffer[j] > max) {
			max = sys->accBuffer[j];
		}
		if (sys->accBuffer[j] < min) {
			min = sys->accBuffer[j];
		}
	}

	// 计算均值
	mean = sum / PATTERN_RECOGNITION_LEN;
	// 计算方差
	for (k = 0; k < PATTERN_RECOGNITION_LEN - 1; k++) {
		std += (sys->accBuffer[k] - mean) * (sys->accBuffer[k] - mean) / PATTERN_RECOGNITION_LEN;
	}
	std = (float)sqrt(std);

	// 计算最大最小差值
	max_min = max - min;

	if (std > 5 && max_min > 25) {
		sys->type = 1;
	}
	else {
		sys->type = 0;
	}
}

/**----------------------------------------------------------------------
* Function    : calPredAngle
* Description : 根据pdr->cal_direction与gps_yaw角度综合确定角度angle
* Date        : 2020/7/22 logzhan
*---------------------------------------------------------------------**/
double calPredAngle(PDR_t *g_pdr, Classifer_t *sys, double gpsYaw, double trackYaw, IMU_t*ss_data) {
	int attitude_flag = PDR_FALSE;
	pDetector = GetDetectorObj();

	/*姿态角YAW辅助计算start*/
	if (DETECTOR_TYPE_HANDHELD == pDetector->type)
	{
		b_timestamp = ss_data->gyr.time;
		if (!(angle_mean[0] == 0 && angle_mean[1] == 0 && angle_mean[2] == 0))
		{
			if (b_last_time > 0 && b_timestamp - b_last_time >= 5000 && gpsYaw != 0.0 && gpsYaw != -1 && trackYaw != -1)
			{
				/* 将姿态yaw角调整到0到2*PI */

				/*左臂包，头朝上，屏朝外 or 右臂包，头朝上，屏朝内*/
				if (ss_data->acc.s[1] > 9 && (fabs(angle_mean[2] + 6.15 - gpsYaw) < PI / 18 && fabs(angle_mean[2] + 6.15 - trackYaw) < PI / 18))
				{
					FUR_duration = -1;
					if (BUL_duration < 0)
					{
						BUL_duration = ss_data->gyr.time;
					}
					if (BUL_duration >= 0 && (b_timestamp - BUL_duration) >= 1500)
					{
						attitude_yaw = angle_mean[2] + 6.15 + PI;
						attitude_flag = PDR_TRUE;
						hold_type = BACKWARD_UP_LEFT;
					}
				}

				/*右臂包，头朝上，屏朝外 or 左臂包，头朝上，屏朝内*/
				if (ss_data->acc.s[1] > 9 && (fabs(angle_mean[2] + 7.10 - gpsYaw) < PI / 18 && fabs(angle_mean[2] + 7.10 - trackYaw) < PI / 18))
				{
					BUL_duration = -1;
					if (FUR_duration < 0)
					{
						FUR_duration = ss_data->gyr.time;
					}
					if (FUR_duration >= 0 && (b_timestamp - FUR_duration) >= 1500)
					{
						attitude_yaw = angle_mean[2] + 7.10;
						attitude_flag = PDR_TRUE;
						hold_type = FORWARD_UP_RIGHT;
					}
				}
			}
			if (-1 == b_last_time)
			{
				b_last_time = b_timestamp;
			}
		}
	}
	else{
		b_last_time = -1;
		FUR_duration = -1;
		BUL_duration = -1;
	}
	/*姿态角YAW辅助计算end*/

	double angle = -1;
	double ahrsYaw = atan2(g_pdr->cal_direction[1], g_pdr->cal_direction[0]);

	//printf("PCA Yaw = %f\n", ahrsYaw * (180 / 3.1415926));

	if (ahrsYaw < 0) {
		ahrsYaw += TWO_PI;
	}
	
	// 评估PCA计算方向角的准确性
	if ((g_pdr->bias_accuracy > 0 && g_pdr->bias_accuracy < 0.9) && (g_pdr->pca_accuracy > 0 && g_pdr->pca_accuracy < 0.25)) {
		angle = ahrsYaw;
		//printf("PCA准确性满足条件, angle = ahrsYaw %f\n", ahrsYaw * (180 / 3.1415926));
	}
	if (gpsYaw != 0.0 && gpsYaw != -1 ) //&& scene_type
	{
		// Status -- Explosion
		if (fabs(ss_data->acc.s[0]) > 70 || fabs(ss_data->acc.s[1]) > 70 || fabs(ss_data->acc.s[2]) > 70 || fabs(ss_data->gyr.s[0]) > 30 || fabs(ss_data->gyr.s[1]) > 30 || fabs(ss_data->gyr.s[2]) > 30) {
			angle = gpsYaw;
			//printf("Explosion, angle = gpsYaw = %f\n", gps_yaw * (180 / 3.1415926));
		}
		// Status -- Running 

		if (g_pdr->motionFreq > 2.7 || (g_pdr->MotionType != 3 && g_pdr->MotionType != 2) || (sys->type == 1)) { //&& g_pdr->motion_type != 2
			angle = gpsYaw;
			//printf("Running, angle = gpsYaw = %f\n", gps_yaw * (180 / 3.1415926));
		}
	}

	if (trackYaw != -1 && ((g_pdr->bias_accuracy < 0 || g_pdr->bias_accuracy > 0.05) || (g_pdr->pca_accuracy < 0 || g_pdr->pca_accuracy > 0.05))) {
		angle = trackYaw;//gps_yaw: obtained from the datasheet;     G_yaw : calculated from the route
		if (attitude_flag == PDR_TRUE)
		{
			angle = attitude_yaw;
		}
	}

	if (angle < 0)
	{
		if (g_pdr->bias_accuracy > 0 && g_pdr->pca_accuracy > 0 && (g_pdr->bias_accuracy < 1.5 && g_pdr->pca_accuracy < 0.5))//g_pdr->bias_accuracy < 6
		{
			angle = ahrsYaw;
		}
		else if (attitude_flag == PDR_TRUE)
		{
			angle = attitude_yaw;
		}
		else if (gpsYaw != 0.0 && gpsYaw != -1)
		{
			angle = gpsYaw;
		}
		else //if (lastYaw > 0)
		{
			angle = -1;  //lastYaw;
		}
	}

	return angle;
}



double CalGnssTrackHeading(double gpsPos[][3], GNSS_t pgnss)
{
	int i;
	double lla[3] = { 0.0 };
	double ned1[3] = { 0.0 };
	double temp[2][HIST_GPS_NUM] = { { 0.0 } };
	double angle[HIST_GPS_NUM - 1] = { 0.0 };

	double ned2[3] = { 0.0 };
	double maxn = 0;
	double maxe = 0;
	double minn = 0;
	double mine = 0;
	double meanx = 0;
	double meany = 0;

	float error = 0;
	float sst = 0;
	float r2 = 0;

	double range = 0;
	double yaw = 0;
	double tempYaw = 0;
	double diffAngle = 0;

	double para[3] = { 0 };
	int flg = 0;

	lla[0] = pgnss.lat;
	lla[1] = pgnss.lon;

	for (i = 0; i < HIST_GPS_NUM - 1; i++)
	{
		gpsPos[i][0] = gpsPos[i + 1][0];
		gpsPos[i][1] = gpsPos[i + 1][1];
		gpsPos[i][2] = gpsPos[i + 1][2];
	}

	if ((lla[0] > 0) && (lla[1] > 0))
	{
		//经纬度WGS84转地心地固坐标系ECEF
		WGS842ECEF(lla, gpsPos[HIST_GPS_NUM - 1]);
	}
	else
	{
		gpsPos[HIST_GPS_NUM - 1][0] = 0;
		gpsPos[HIST_GPS_NUM - 1][1] = 0;
		gpsPos[HIST_GPS_NUM - 1][2] = 0;
		return -1;
	}
	if (pgnss.error > 40)
	{
		return -1;
	}

	for (i = 0; i < HIST_GPS_NUM - 1; i++)
	{
		if ((fabs(gpsPos[i][0]) < 1e-6) && (fabs(gpsPos[i][1]) < 1e-6) && (fabs(gpsPos[i][2]) < 1e-6))
		{
			return -1;
		}
	}
	//地心地固坐标系ECEF转东北天坐标系NED
	ECEF2NED(gpsPos[HIST_GPS_NUM - 1], lla, ned1);

	for (i = HIST_GPS_NUM - 2; i >= 0; i--) {

		ECEF2NED(gpsPos[i], lla, ned2);

		temp[0][i] = ned1[0] - ned2[0];
		temp[1][i] = ned1[1] - ned2[1];

		meanx += temp[0][i] / HIST_GPS_NUM;
		meany += temp[1][i] / HIST_GPS_NUM;

		if (maxn > temp[0][i])
		{
			maxn = temp[0][i];
		}
		if (minn < temp[0][i])
		{
			minn = temp[0][i];
		}
		if (maxe > temp[1][i])
		{
			maxe = temp[1][i];
		}
		if (mine < temp[1][i])
		{
			mine = temp[1][i];
		}

		angle[i] = atan2(temp[1][i], temp[0][i]);
		if (angle[i] < 0)
			angle[i] += TWO_PI;
	}
	flg = leastDistanceLinearFit(temp[0], temp[1], HIST_GPS_NUM, para);
	if (flg < 0)
		return -1;

	for (i = HIST_GPS_NUM - 1; i >= 0; i--) {
		error += (float)(pow(para[0] * temp[0][i] + para[1] * temp[1][i] + para[2], 2) / (pow(para[0], 2) + pow(para[1], 2)));
		sst += (float)(pow(meanx - temp[0][i], 2) + pow(meany - temp[1][i], 2));
	}

	if (sst > 0)
	{
		r2 = 1 - error / sst;
	}
	range = sqrt(pow(maxn - minn, 2) + pow(maxe - mine, 2));
	//printf("%f,%f,%f\n", r2, error, range);
	if (r2 < 0.95 || range < 1.5 || error > 3.0)
		return -1;

	yaw = atan(-para[0] / para[1]);
	modAngle(&yaw, 0, TWO_PI);

	tempYaw = meanAngle(angle, HIST_GPS_NUM - 1);
	modAngle(&tempYaw, 0, TWO_PI);

	diffAngle = tempYaw - yaw;
	modAngle(&diffAngle, -PI, PI);

	if (fabs(diffAngle) > PI / 2)
		yaw += PI;
	modAngle(&yaw, 0, TWO_PI);

	return yaw;
}

/**----------------------------------------------------------------------
* Function    : InsLocationPredict
* Description : PDR惯导位置更新
* Date        : 2021/02/06 logzhan : 感觉计步器输出不够平均，有时候
*               会造成PDR前后位置不够均匀
*---------------------------------------------------------------------**/
void InsLocationPredict(PDR_t *g_pdr, StepPredict *stepPredict, IMU_t *ss_data, 
	GNSS_t *pgnss, EKFPara_t *kf) {
	int step;
	long deltaStep;
	double stepLength = 0.7;

	stepPredict->fnum++;
	stepPredict->fsum += g_pdr->motionFreq;

	ulong  steps     = g_pdr->steps;
	ulong  lastSteps = g_pdr->lastSteps;
	
	if (steps - lastSteps > 0 && g_pdr->motionFreq != 0.0) {
		deltaStep = steps - lastSteps - stepPredict->deltaStep;

		// 应该是防止计步器异常情况，一次计步特别多
		if (deltaStep > 5)deltaStep = 5;
			
		if (stepPredict->meanTime > 0)
		{
			step = (int)((ss_data->gyr.time - stepPredict->lastTime) / stepPredict->meanTime + 0.5);
			
			if (step > deltaStep && step <= 3)deltaStep = step;
		}
		if (lastSteps == 0)deltaStep = 0;

		if (g_pdr->heading > 0) {
			EKFStatePredict(kf, stepLength, g_pdr, deltaStep);
			g_pdr->fusionPdrFlg = PDR_TRUE;
		}
		calStepLastTime(stepPredict, ss_data->gyr.time, lastSteps, steps);
	}else{
		// 有时候，计步器可能考虑到稳定性暂时不更新步数，因此需要利用GNSS速度预测步数，达到
		// 惯导更新位置的目的
		int preStep = predictStep(stepPredict, ss_data->gyr.time, lastSteps, pgnss->vel); 
		if (g_pdr->heading > 0){
			EKFStatePredict(kf, stepLength, g_pdr, preStep);
			if(preStep > 0)g_pdr->fusionPdrFlg = PDR_TRUE;
		}
	}
}


/**----------------------------------------------------------------------
* Function    : EKFCalQRMatrix
* Description : 根据GPS信号特征调整卡尔曼滤波噪声矩阵，q矩阵是过程噪声矩阵，
*               需要跟惯导预测位置精度相关。r矩阵为GNSS观测噪声，跟GPS输出的
*               信息精度有关。
* Date        : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
void EKFCalQRMatrix(Nmea_t *nmea, PDR_t *g_pdr, Classifer_t *sys,
	EKFPara_t *kf) {

	if (g_pdr->MotionType != PDR_MOTION_TYPE_HANDHELD && 
		g_pdr->MotionType != PDR_MOTION_TYPE_STATIC) {
		kf->Q[0][0] = 25;                   // PDR预测位置噪声
		kf->Q[1][1] = 25;                   // PDR预测位置噪声
		kf->Q[2][2] = 1;                    // PDR步长噪声
		kf->Q[3][3] = 100;                  // PDR方向噪声

		kf->R[0][0] = 50;                    // GPS观测位置噪声
		kf->R[1][1] = 50;					 // GPS观测位置噪声
		kf->R[2][2] = 0.1;					 // GPS步长噪声
		kf->R[3][3] = 50;					 // GPS方向噪声
		return;
	}

	kf->Q[0][0] = 0.1;                       // PDR预测位置噪声
	kf->Q[1][1] = 0.1;                       // PDR预测位置噪声
	kf->Q[2][2] = 1;                         // PDR步长噪声
	kf->Q[3][3] = 1;                         // PDR方向噪声
										     
	kf->R[0][0] = 100;                       // GPS预测位置噪声
	kf->R[1][1] = 100;					     // GPS预测位置噪声
	kf->R[2][2] = 0.1;					     // GPS步长噪声
	kf->R[3][3] = 1000;				         // GPS方向噪声

	if (nmea->accuracy.err > 0 && 
		nmea->accuracy.err < 3.80 && 
		nmea->gga.hdop <= 0.4) 
	{
		kf->R[0][0] = 50;                    // GPS观测位置噪声
		kf->R[1][1] = 50;					 // GPS观测位置噪声
		kf->R[2][2] = 0.1;					 // GPS步长噪声
		kf->R[3][3] = 50;					 // GPS方向噪声
	}

	if (fabs(R2D((g_pdr->GpsHeading - g_pdr->lastGpsHeading))) > 50 ||
		g_pdr->gpsSpeed < 0.7 || g_pdr->GpsHeading == -1) {
		kf->R[3][3] = 10000;
	}
}
/**----------------------------------------------------------------------
* Function    : GnssInsLocFusion
* Description : PDR的GNSS和INS融合定位
* Date        : 2022/09/21 logzhan
*---------------------------------------------------------------------**/
void GnssInsLocFusion(GNSS_t *pgnss, PDR_t *g_pdr, Classifer_t *sys, double yaw_bias,
	EKFPara_t *kf, LctFs_t *res) {
	double plla[3] = { 0 };
	double ned[3] = { 0 };
	double yaw_thr = 6;

	int scene_type = g_pdr->sceneType;

	plla[0] = pgnss->lat;
	plla[1] = pgnss->lon;
	plla[2] = 0;

	WGS842NED(plla, g_pdr->PllaInit, ned);

	pgnss->xNed = ned[0];
	pgnss->yNed = ned[1];

	//调整融合策略
	EKFStateUpdate(kf, pgnss, sys, g_pdr, scene_type);

	for (uchar i = 0; i < N; i++) {
		for (uchar l = 0; l < N; l++) {
			kf->pPk[i][l] = kf->Pk[i][l];
		}
		kf->pXk[i] = kf->Xk[i];
	}

	ned[0] = kf -> pXk[0] - g_pdr->x0;
	ned[1] = kf -> pXk[1] - g_pdr->y0;
	ned[2] = 0;

	NED2WGS84(g_pdr->PllaInit, ned, plla);

	res->latitude        = plla[0];
	res->latitude_ns     = pgnss->lat_ns;
	res->longitudinal    = plla[1];
	res->longitudinal_ew = pgnss->lon_ew;
	res->time            = pgnss->timestamps;

	g_pdr->NoGnssCount = 0;
}
