/******************** (C) COPYRIGHT 2020 VIVO************************************
* File Name          : pdr_ahrs.c
* Department         : Sensor Algorithm Team
* Current Version    : V1.2
* Author             : yuanlin_rjyb@vivo.com & zhanli@vivo.com
* Date of Issued     : 2020.7.3
* Comments           : PDR AHRS 姿态解算相关
********************************************************************************/
/* Header File Including -----------------------------------------------------*/
#include <math.h>
#include <string.h>
#include "pdr_base.h"
#include "pdr_ahrs.h"
#include "pdr_detector.h"
#include "Filter.h"
#include "buffer.h"
#include "pdr_kalman.h"
#include "pdr_util.h"

/* Macro Definition ----------------------------------------------------------*/
#define AHRS_BUF_LEN                        256
#define FLT_THRES                           0.000001f                    // 浮点数最小阈值
//计算模值
#define GET_MOL(v)                          sqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2])
//获取数组长度
#define ARR_LEN(arr)                        (sizeof(arr) / sizeof(*arr))
#define SET_BIT(msk)                        (g_ahrs.status |= AHRS_STATUS_##msk)
#define CLR_BIT(msk)                        (g_ahrs.status &= ~AHRS_STATUS_##msk)
#define TST_BIT(msk)                        (g_ahrs.status & AHRS_STATUS_##msk)
#define INC_PTR(buf, ptr)                   (((ptr) + 1) % ((buf).length + 1))

#define GRV_BUF_NME(i)                      "grav_buf_##i"
#define GYR_BUF_NME(i)                      "gyro_buf_##i"
#define MAG_BUF_NME(i)                      "magn_buf_##i"

/* Global Variable Definition ------------------------------------------------*/
/* Macro Definition ----------------------------------------------------------*/

/* Global Variable Definition ------------------------------------------------*/
static IMU     g_imu;
AHRS           g_ahrs;
static uint64_t    g_ticker;
static BUFFER_LONG g_erro_buf;
static BUFFER_LONG g_grav_buf[3];
static BUFFER_LONG g_gyro_buf[3];
static BUFFER_LONG g_magn_buf[3];

static float dt = 1.0f / AHRS_SAMPLE_FREQ;                           // sample interval in second
static float Kp = 0;                                                 // 2 * proportional gain (Kp)
static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;             // 初始四元数

static const float g_x_axis[] = {1, 0, 0};
static const float g_y_axis[] = {0, 1, 0};
static const float g_z_axis[] = {0, 0, 1};

/**----------------------------------------------------------------------
* Function    : mahonyUpdateAHRS
* Description : mahony姿态更新算法
* Date        : 2020/8/3 zhanli@vivo.com
*               2020/02/18 zhanli@vivo.com :
*               增加了AHRS稳定性标志位，如果AHRS稳定后不再计算error, 降低运算
                量。能提升PDR仿真10%的运行速度
*---------------------------------------------------------------------**/
static void mahonyUpdateAHRS(float gx, float gy, float gz, float ax, float ay, float az, 
                             float mx, float my, float mz) {
    // 归一化磁力计和加速度计
    pdr_v3Norm(&ax, &ay, &az);

    float wx = 0.0f, wy = 0.0f, wz = 0.0f;

    
    //if (!((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f))) {

    //    pdr_v3Norm(&mx, &my, &mz);

    //    // 把磁场从载体系转换到地理坐标系 h = R^-1 * (mx,my,mz)
    //    float hx = 2.0f * (mx * (0.5f - q2 * q2 - q3 * q3) + my * (q1 * q2 - q0 * q3) + mz * (q1 * q3 + q0 * q2));
    //    float hy = 2.0f * (mx * (q1 * q2 + q0 * q3) + my * (0.5f - q1 * q1 - q3 * q3) + mz * (q2 * q3 - q0 * q1));
    //    float hz = 2.0f * (mx * (q1 * q3 - q0 * q2) + my * (q2 * q3 + q0 * q1) + mz * (0.5f - q1 * q1 - q2 * q2));

    //    // 理论上bx = 0, by 指向北向，因为手机IMU数据使用的是东北天(x,y,z)坐标系
    //    float by = (float)sqrt(hx * hx + hy * hy);
    //    float bz = hz;

    //    wx = by * (q0 * q3 + q1 * q2) + bz * (q1 * q3 - q0 * q2);
    //    wy = by * (0.5f - q1 * q1 - q3 * q3) + bz * (q0 * q1 + q2 * q3);
    //    wz = by * (q2 * q3 - q0 * q1) + bz * (0.5f - q1 * q1 - q2 * q2);
    //}

    // 把重力转换到地理坐标系   v = R * (0,0,1)
    float vx = q1*q3 - q0*q2;
    float vy = q0*q1 + q2*q3;
    float vz = q0*q0 - 0.5f + q3*q3;

    // 计算矢量叉乘误差
    float ex = ay*vz - az*vy + my*wz - mz*wy;
    float ey = az*vx - ax*vz + mz*wx - mx*wz;
    float ez = ax*vy - ay*vx + mx*wy - my*wx;

    // Apply proportional feedback
    gx += Kp * ex;
    gy += Kp * ey;
    gz += Kp * ez;

    // 这两个函数占了大量的时间
    if (g_ahrs.stable == PDR_FALSE) {
        buffer_push((BUFFER*)&g_erro_buf, (float)(2 * sqrt(ex * ex + ey * ey + ez * ez)));
        Buffer_mean((BUFFER*)&g_erro_buf, &g_ahrs.error);
    }

    // 龙格库塔法更新四元数
    float qa = q0; float qb = q1; float qc = q2;
    q0 += (-qb * gx - qc * gy - q3 * gz) * 0.5f * (dt);
    q1 += ( qa * gx + qc * gz - q3 * gy) * 0.5f * (dt);
    q2 += ( qa * gy - qb * gz + q3 * gx) * 0.5f * (dt);
    q3 += ( qa * gz + qb * gy - qc * gx) * 0.5f * (dt);

    // 四元数归一化
    quatNorm(&q0, &q1, &q2, &q3);

    // 四元数转欧拉角
    g_ahrs.pitch = (float)asin(-2.0f * (q3*q1 - q0*q2)) * (180.0f / 3.141592f);
    g_ahrs.yaw   = (float)atan2(q2*q1 + q0*q3, 0.5f - q2*q2 - q3*q3) * (180.0f / 3.141592f);
    g_ahrs.roll  = (float)atan2(q2*q3 + q0*q1, 0.5f - q2*q2 - q1*q1) * (180.0f / 3.141592f);
}

/**---------------------------------------------------------------------
* Function    : AHRS_sensorFrame2EarthFrame
* Description : 转换加速度计值到地球坐标系
* Date        : 2020/7/20 yuanlin_rjyb@vivo.com
*---------------------------------------------------------------------**/
void AHRS_sensorFrame2EarthFrame(float e[], float s[]) {
    float quaVec[4];
    float quaTmp[4];
    float quaCnj[4];

    quaVec[0] = 0;
    quaVec[1] = s[0];
    quaVec[2] = s[1];
    quaVec[3] = s[2];
    quaternProd(quaTmp, g_ahrs.qnb, quaVec);
    quaternConj(quaCnj, g_ahrs.qnb);
    quaternProd(quaVec, quaTmp, quaCnj);
    e[0] = quaVec[1];
    e[1] = quaVec[2];
    e[2] = quaVec[3];
}

void pdr_initAhrs(void) {
    int i;
    g_ahrs.stable = PDR_FALSE;
    Buffer_initialize((BUFFER *)&g_erro_buf, "erro_buf", BUFFER_TYPE_QUEUE, AHRS_BUF_LEN);
    for (i = 0; i < 3; ++i) {
        Buffer_initialize((BUFFER *)&g_grav_buf[i], GRV_BUF_NME(i), BUFFER_TYPE_QUEUE, AHRS_BUF_LEN);
        Buffer_initialize((BUFFER *)&g_gyro_buf[i], GYR_BUF_NME(i), BUFFER_TYPE_QUEUE, AHRS_BUF_LEN);
        Buffer_initialize((BUFFER *)&g_magn_buf[i], MAG_BUF_NME(i), BUFFER_TYPE_QUEUE, AHRS_BUF_LEN);
    }
    pdr_ahrsReset();
}

void pdr_ahrsReset(void) {

    Buffer_clear((BUFFER *)&g_erro_buf);
    for (uchar i = 0; i < 3; ++i) {
        Buffer_clear((BUFFER *)&g_grav_buf[i]);
        Buffer_clear((BUFFER *)&g_gyro_buf[i]);
        Buffer_clear((BUFFER *)&g_magn_buf[i]);
    }
    g_ticker = 0;
    q0 = 1.0f;
    q1 = 0.0f;
    q2 = 0.0f;
    q3 = 0.0f;
    memset(&g_ahrs, 0, sizeof(g_ahrs));
    g_ahrs.status = (uint8_t)AHRS_STATUS_RESET_PHASE_0;
    g_ahrs.error = -1;
    g_ahrs.qnb[0] = 1.0f;
    g_ahrs.qnb[1] = 0.0f;
    g_ahrs.qnb[2] = 0.0f;
    g_ahrs.qnb[3] = 0.0f;
    g_ahrs.gravity[0] = 0.0f;
    g_ahrs.gravity[1] = 0.0f;
    g_ahrs.gravity[2] = 9.8f;
    g_ahrs.x_axis[0] = 1.0f;
    g_ahrs.x_axis[1] = 0.0f;
    g_ahrs.x_axis[2] = 0.0f;
    g_ahrs.y_axis[0] = 0.0f;
    g_ahrs.y_axis[1] = 1.0f;
    g_ahrs.y_axis[2] = 0.0f;
    g_ahrs.z_axis[0] = 0.0f;
    g_ahrs.z_axis[1] = 0.0f;
    g_ahrs.z_axis[2] = 1.0f;
}

/**---------------------------------------------------------------------
* Function    : estimate_sensor_vector
* Description : 利用当前四元数q与陀螺仪，计算在当前姿态下grav
* input       : float eVec[], float gyro[]
* output      : float sVec[]
* Date        : 2020/7/20 yuanlin_rjyb@vivo.com
*---------------------------------------------------------------------**/
static void estimate_sensor_vector(float sVec[], float eVec[], float gyro[]) {
    float q[4];
    float gx, gy, gz;
    float qa, qb, qc;
    float recipNorm;
    float quaVec[4];
    float quaTmp[4];
    float quaCnj[4];

    /* estimate */
    gx = gyro[0] * (0.5f * (dt));
    gy = gyro[1] * (0.5f * (dt));
    gz = gyro[2] * (0.5f * (dt));
    qa = q0;
    qb = q1;
    qc = q2;
    q[0] = q0 + (-qb * gx - qc * gy - q3 * gz);
    q[1] = q1 + (qa * gx + qc * gz - q3 * gy);
    q[2] = q2 + (qa * gy - qb * gz + q3 * gx);
    q[3] = q3 + (qa * gz + qb * gy - qc * gx); 
    recipNorm = pdr_invSqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
    q[0] *= recipNorm;
    q[1] *= recipNorm;
    q[2] *= recipNorm;
    q[3] *= recipNorm;

    /* project */
    quaternConj(q, q);
    quaVec[0] = 0;
    quaVec[1] = eVec[0];
    quaVec[2] = eVec[1];
    quaVec[3] = eVec[2];
    quaternProd(quaTmp, q, quaVec);
    quaternConj(quaCnj, q);
    quaternProd(quaVec, quaTmp, quaCnj);
    sVec[0] = quaVec[1];
    sVec[1] = quaVec[2];
    sVec[2] = quaVec[3];
}
/**----------------------------------------------------------------------
* Function    : pdr_imuUpdateAhrs
* Description : 1、AHRS融合解算
*               2、Error分析
 *              3、PCA方向计算
* Date        : 2020/6/30 yuanlin_rjyb@vivo.com
*---------------------------------------------------------------------**/
int pdr_imuUpdateAhrs(imu* imu) {
    float gyro[3], acce[3], magn[3], grav[3], mean[2];
	int   index;
	int   count;
    int i;

    for (uchar i = 0; i < 3; ++i) {
        gyro[i] = imu->gyr.s[i];
        acce[i] = imu->acc.s[i];
        magn[i] = imu->mag.s[i];
    }

	//g_ahrs.status(初始状态0x80) 按位与 AHRS_STATUS_RESET_PHASE_0(0x80)
    if (g_ahrs.status & AHRS_STATUS_RESET_PHASE_0) {

        // ARHS BUF 中存储了256个sensor的缓存
        if (g_ticker <= AHRS_BUF_LEN) {
            for (i = 0; i < 3; ++i) {
                buffer_push((BUFFER *)&g_grav_buf[i], acce[i]);
                buffer_push((BUFFER *)&g_gyro_buf[i], gyro[i]);
                buffer_push((BUFFER *)&g_magn_buf[i], magn[i]);
            }
        }
        // 只有缓存满的时候才计算
        if (g_ticker >= AHRS_BUF_LEN) {
            //int index;
            index = (g_magn_buf[0]._top + AHRS_BUF_LEN + 1 - AHRS_BUF_LEN / 2) % (g_magn_buf[0].length + 1);
            for (i = 0; i < 3; ++i) {
                Buffer_mean((BUFFER *)&g_grav_buf[i], &grav[i]);
                magn[i] = g_magn_buf[i].data[index];
                g_grav_buf[i]._top = g_grav_buf[i]._bottom;
            }

            Kp = (float)(2 * 2);
			/* 这里循环运行1000次,收敛，趋于正确姿态 */
            for (i = 0; i < 1000; ++i) {
                mahonyUpdateAHRS(0, 0, 0, grav[0], grav[1], grav[2], magn[0], magn[1], magn[2]);
            }

            AHRS_sensorFrame2EarthFrame(g_ahrs.gravity, grav);

            Kp = (float)(2 * 2);
            for (index = INC_PTR(g_gyro_buf[0], index); index != g_gyro_buf[0]._top; index = INC_PTR(g_gyro_buf[0], index)) {
                for (i = 0; i < 3; ++i) {
                    buffer_push((BUFFER *)&g_grav_buf[i], g_ahrs.gravity[i]);
                    Buffer_mean((BUFFER *)&g_grav_buf[i], &grav[i]);
                    gyro[i] = g_gyro_buf[i].data[index];
                    acce[i] = g_grav_buf[i].data[index];
                    magn[i] = g_magn_buf[i].data[index];
                }
                estimate_sensor_vector(grav, grav, gyro);

                // AHRS 更新
                mahonyUpdateAHRS(gyro[0], gyro[1], gyro[2], grav[0], grav[1], grav[2], magn[0],
                                 magn[1], magn[2]);

                AHRS_sensorFrame2EarthFrame(g_ahrs.gravity, acce);
            }

            g_ahrs.qnb[0] = q0;
            g_ahrs.qnb[1] = q1;
            g_ahrs.qnb[2] = q2;
            g_ahrs.qnb[3] = q3;

			CLR_BIT(RESET_PHASE_0);            /*#define CLR_BIT(msk)  (g_ahrs.status &= ~AHRS_STATUS_##msk)；清零g_ahrs.status*/
            SET_BIT(RESET_PHASE_1);            /*#define SET_BIT(msk)  (g_ahrs.status |= AHRS_STATUS_##msk)；赋值g_ahrs.status，与phase1相同*/
        }
        ++g_ticker;
        //return;
     //g_ahrs.status(0x80) 按位与 AHRS_STATUS_RESET_PHASE_1(0x40)
	}else if (g_ahrs.status & AHRS_STATUS_RESET_PHASE_1) {
        //PDR_LOGD("阶段1");
        // 应该是判断是否进入PCA的条件
		if (g_ahrs.error < 0.3) {

			Buffer_count((BUFFER *)&g_erro_buf, &count);
			memset((void *)mean, 0, sizeof(mean));
			for (index = g_erro_buf._bottom, i = 0; index != g_erro_buf._top; index = INC_PTR(g_erro_buf, index), ++i) {
			    int c = count >> 1;
				if (i < c ) {
					mean[0] += g_erro_buf.data[index];
				}
				else {
					mean[1] += g_erro_buf.data[index];
				}
			}
			int a = count >> 1;
			int b = count - (count >> 1);
			mean[0] /= a;
			mean[1] /= b;

			if (fabs(mean[0] - mean[1]) < 0.025) {
				Kp = (float)(2 * AHRS_KP);

				// 这个标志位决定是否进入PCA方向估计
				CLR_BIT(RESET_PHASE_1);
				SET_BIT(STABLE);
                g_ahrs.stable = PDR_TRUE;
			}
		}

		for (i = 0; i < 3; ++i) {
            buffer_push((BUFFER *)&g_grav_buf[i], g_ahrs.gravity[i]);
			Buffer_mean((BUFFER *)&g_grav_buf[i], &grav[i]);
		}
		estimate_sensor_vector(grav, grav, gyro);

		mahonyUpdateAHRS(gyro[0], gyro[1], gyro[2], 
                         grav[0], grav[1], grav[2], 
                         magn[0], magn[1], magn[2]);

		g_ahrs.qnb[0] = q0;
		g_ahrs.qnb[1] = q1;
		g_ahrs.qnb[2] = q2;
		g_ahrs.qnb[3] = q3;

		AHRS_sensorFrame2EarthFrame(g_ahrs.gravity, acce);
		AHRS_sensorFrame2EarthFrame(g_ahrs.x_axis, (float*)g_x_axis);
		AHRS_sensorFrame2EarthFrame(g_ahrs.y_axis, (float*)g_y_axis);
		AHRS_sensorFrame2EarthFrame(g_ahrs.z_axis, (float*)g_z_axis);

		pdr_computePCA(&g_ahrs);

		++g_ticker;
	}
	else {

        // Buffer_mean的计算时间太长了，10ms更新来算，计算256个数的平均
        // 计算量实在太大
		//for (i = 0; i < 3; ++i) {
		//	Buffer_push((BUFFER *)&g_grav_buf[i], g_ahrs.gravity[i]);
		//	Buffer_mean((BUFFER *)&g_grav_buf[i], &grav[i]);
		//}
		//estimate_sensor_vector(grav, grav, gyro);

        // zhanli@vivo.com 21/02/06 : 感觉是acc输入精度会高一些
		mahonyUpdateAHRS(gyro[0], gyro[1], gyro[2], 
                         acce[0], acce[1], acce[2],
                         magn[0], magn[1], magn[2]);

		g_ahrs.qnb[0] = q0;
		g_ahrs.qnb[1] = q1;
		g_ahrs.qnb[2] = q2;
		g_ahrs.qnb[3] = q3;

        // 把载体系的加速度转换为地理坐标系,
		AHRS_sensorFrame2EarthFrame(g_ahrs.gravity, acce);
        // 这部分代码都是可以简化以增加运算速度的，暂时不清楚作用
		AHRS_sensorFrame2EarthFrame(g_ahrs.x_axis, (float*)g_x_axis);
		AHRS_sensorFrame2EarthFrame(g_ahrs.y_axis, (float*)g_y_axis);
		AHRS_sensorFrame2EarthFrame(g_ahrs.z_axis, (float*)g_z_axis);

		++g_ticker;
        return PDR_TRUE;
    }
    return PDR_FALSE;
}

/**---------------------------------------------------------------------
* Function    : fv3Norm
* Description : 三维浮点数向量归一化
* Date        : 2021/01/26 zhanli@vivo.com
*---------------------------------------------------------------------**/
void fv3Norm(float* vx, float* vy, float* vz)
{
    float norm = sqrtf(((*vx) * (*vx) + (*vy) * (*vy) +
        (*vz) * (*vz)));
    // 防止出现模为0的情况
    if (norm > FLT_THRES) {
        norm = 1 / norm;
        (*vx) *= norm;   (*vy) *= norm;   (*vz) *= norm;
    }
}
