//gyro_data unit rad/s  dT unit s
static void EKF_StatePrediction(float gyro_data[3], float dT)
{
		static float U[3];
		static float q[4], euler[3];
		U[0] = gyro_data[0];
    U[1] = gyro_data[1];
    U[2] = gyro_data[2];
		EKF_LinearizeFG(EKF_X, U, EKF_F, EKF_G);
		EKF_RungeKutta(EKF_X, U, dT);
#if 1 // ·Àֹƫº½½ÇÔÚ+/-180¡ãÊÇ·¢ÉúÍ»±ä
		q[0] = EKF_X[0];
		q[1] = EKF_X[1];
		q[2] = EKF_X[2];
		q[3] = EKF_X[3];
		Quaternion2Euler(q, euler);	
		if(euler[2] > 180)
		{
				euler[2] = -180;
		}
		else if(euler[2] < -180)
		{
				euler[2] = 180;
		}
		Euler2Quaternion(euler, q);
		EKF_X[0] = q[0];
		EKF_X[1] = q[1];
		EKF_X[2] = q[2];
		EKF_X[3] = q[3];
#endif
}
void EKF_Attitude(void)
{
		float gyro[3], accel[3], mag_data[3];
		float euler[3];
		float dT = Deal_Period_ms / 1000.0f;
		static uint8_t isInit = false;
	
		if(!isInit)
		{
				EKF_AttitudeInit();
				isInit = true;
		}
		else
		{
				gyro[0] = Global_Gyro_Sensor.filtered.x;
				gyro[1] = Global_Gyro_Sensor.filtered.y;
				gyro[2] = Global_Gyro_Sensor.filtered.z;
			
				accel[0] = Global_Accel_Sensor.filtered.x;
				accel[1] = Global_Accel_Sensor.filtered.y;
				accel[2] = Global_Accel_Sensor.filtered.z;
			
				mag_data[0] = (float)Global_Mag_Val[0];
				mag_data[1] = (float)Global_Mag_Val[1];
				mag_data[2] = (float)Global_Mag_Val[2];
			
				EKF_StatePrediction(gyro, dT);
			
				EKF_INSCovariancePrediction(dT);

				EKF_Z_Cal(accel, mag_data, EKF_Z);
				EKF_LinearizeH(EKF_H);
#if 1		
				EKF_K_Cal(EKF_P, EKF_H, EKF_R, EKF_K);					
				EKF_Correction_X(EKF_Z, EKF_H, EKF_K, EKF_X);
				EKF_Correction_P(EKF_K, EKF_H, EKF_P);
#endif
				Nav.q[0] = EKF_X[0];
				Nav.q[1] = EKF_X[1];
				Nav.q[2] = EKF_X[2];
				Nav.q[3] = EKF_X[3];
			
				Quaternion2Euler(Nav.q, euler);	
				
				Global_Now_Euler[0] = euler[0];
				Global_Now_Euler[1] = euler[1];
				Global_Now_Euler[2] = euler[2];
		}
}
// function for SLERP quaternion
void Interpolator::LinearInterpolationQuaternion(Motion * pInputMotion, Motion * pOutputMotion, int N)
{
  int inputLength = pInputMotion->GetNumFrames(); // frames are indexed 0, ..., inputLength-1

  int startKeyframe = 0;
  while (startKeyframe + N + 1 < inputLength)
  {
    int endKeyframe = startKeyframe + N + 1;

    Posture * startPosture = pInputMotion->GetPosture(startKeyframe);
    Posture * endPosture = pInputMotion->GetPosture(endKeyframe);

    // copy start and end keyframe
    pOutputMotion->SetPosture(startKeyframe, *startPosture);
    pOutputMotion->SetPosture(endKeyframe, *endPosture);

    // interpolate in between
    for(int frame=1; frame<=N; frame++)
    {
      Posture interpolatedPosture;
      double t = 1.0 * frame / (N+1);

      // interpolate root position
      interpolatedPosture.root_pos = startPosture->root_pos * (1-t) + endPosture->root_pos * t;

      // interpolate bone rotations
      for (int bone = 0; bone < MAX_BONES_IN_ASF_FILE; bone++)
	  {
		  // convert euler angles to quaternions
		  Quaternion<double> boneStartRot = Vector2Quaternion(startPosture->bone_rotation[bone]);
		  Quaternion<double> boneEndRot = Vector2Quaternion(endPosture->bone_rotation[bone]);

		  // do slerp for converted quaternions at t
		  double interpolatedAngles[3];
		  Quaternion2Euler(Slerp(t, boneStartRot, boneEndRot), interpolatedAngles);

		  interpolatedPosture.bone_rotation[bone].setValue(interpolatedAngles);
	  }

      pOutputMotion->SetPosture(startKeyframe + frame, interpolatedPosture);
    }

    startKeyframe = endKeyframe;
  }

  for(int frame=startKeyframe+1; frame<inputLength; frame++)
    pOutputMotion->SetPosture(frame, *(pInputMotion->GetPosture(frame)));
}
static void EKF_Z_Cal(float accel[3], float mag_data[3], float z[Zn])
{
#if defined USE_MAG	
	
		#if defined USE_MAG_HMC5883L
			euler[2] = atan2(mag_data[1], mag_data[0]) * RAD_DEG;		
		#elif defined	USE_MAG_LSM303D
			Get_Ref_Euler(accel, mag_data, &Mag_LSM303D_Calibration, euler);
		#endif
	
		Global_Show_Val[5] = euler[0];
		Global_Show_Val[6] = euler[1];
		Global_Show_Val[7] = euler[2];
	
		//euler[2] -= Global_Heading_Ref;
#else		
		u[0] = accel[0];
    u[1] = accel[1];
    u[2] = accel[2];
		
		q[0] = EKF_X[0];
		q[1] = EKF_X[1];
		q[2] = EKF_X[2];
		q[3] = EKF_X[3];
		
		Quaternion2Euler(q, euler);
		
		Global_Show_Val[5] = euler[0] = atan2(u[1], u[2]) * RAD_DEG;
		Global_Show_Val[6] = euler[1] = atan2(-1 * u[0], u[2]) * RAD_DEG;
#endif	
		Euler2Quaternion(euler, z);
		qmag = sqrt(z[0]*z[0] + z[1]*z[1] + z[2]*z[2] + z[3]*z[3]); 
		z[0] /= qmag;
		z[1] /= qmag;
		z[2] /= qmag;
		z[3] /= qmag;		
}
// function for SLERP bezier quaternion interpolation
void Interpolator::BezierInterpolationQuaternion(Motion * pInputMotion, Motion * pOutputMotion, int N)
{
  // students should implement this
  int inputLength = pInputMotion->GetNumFrames(); // frames are indexed 0, ..., inputLength-1

  int startKeyframe = 0;
  // default and next 'a' values for bezier are stored in memory
  Quaternion<double> aDef[MAX_BONES_IN_ASF_FILE];
  Quaternion<double> aNext[MAX_BONES_IN_ASF_FILE];

  double bezRatio = 1.0/3.0;
  vector aNextRoot;
  // use a0 for the first frame by backtracking from next 2 known frames
  vector v1 = pInputMotion->GetPosture(0)->root_pos;
  vector v2 = pInputMotion->GetPosture(N+1)->root_pos;
  vector v3 = pInputMotion->GetPosture(N+1+N+1)->root_pos;
  vector aDefRoot = v1*(1-bezRatio) + (v3 * (-1) + v2 * 2) * bezRatio;

  for (int i = 0; i < MAX_BONES_IN_ASF_FILE; i++)
  {
	Quaternion<double> q1 = Vector2Quaternion(pInputMotion->GetPosture(0)->bone_rotation[i]);
	Quaternion<double> q2 = Vector2Quaternion(pInputMotion->GetPosture(N+1)->bone_rotation[i]);
	Quaternion<double> q3 = Vector2Quaternion(pInputMotion->GetPosture(N+1+N+1)->bone_rotation[i]);
	aDef[i] = Slerp(bezRatio, q1, Slerp(2.0, q3, q2));
  }

  while (startKeyframe + N + 1 < inputLength)
  {
    int endKeyframe = startKeyframe + N + 1;

    Posture * startPosture = pInputMotion->GetPosture(startKeyframe);
    Posture * endPosture = pInputMotion->GetPosture(endKeyframe);
	Posture * nextPosture;
	if (endKeyframe + N + 1 >= inputLength)	nextPosture = pInputMotion->GetPosture(inputLength-1);
	else nextPosture = pInputMotion->GetPosture(endKeyframe + (N+1));
	
    // copy start and end keyframe
    pOutputMotion->SetPosture(startKeyframe, *startPosture);
    pOutputMotion->SetPosture(endKeyframe, *endPosture);

    // interpolate in between
    for(int frame=1; frame<=N; frame++)
    {
      Posture interpolatedPosture;
      double t = 1.0 * frame / (N+1);

	  // bez interpolate root position
	  vector rootStartPos = startPosture->root_pos;
  	  vector rootEndPos = endPosture->root_pos;
	  vector rootNextPos = nextPosture->root_pos;
	  vector p1 = startKeyframe == 0? aDefRoot : aNextRoot; // use a0 if first frame, otherwise use a calculated in previous iteration
	  vector aN = rootEndPos*(1-bezRatio) + ((rootStartPos*-1+ rootEndPos*2)*0.5+ rootNextPos*0.5)*bezRatio;
	  vector p2 = aN*-1+rootEndPos*2; // bn

	  interpolatedPosture.root_pos = DeCasteljauEuler(t, rootStartPos, p1, p2, rootEndPos);

	  // interpolate bone rotations
      for (int bone = 0; bone < MAX_BONES_IN_ASF_FILE; bone++)
	  {
		  Quaternion<double> boneStartRot = Vector2Quaternion(startPosture->bone_rotation[bone]); // qn
		  Quaternion<double> boneEndRot = Vector2Quaternion(endPosture->bone_rotation[bone]); // qn+1
		  Quaternion<double> boneNextRot = Vector2Quaternion(nextPosture->bone_rotation[bone]); // qn+2

		  Quaternion<double> p1 = startKeyframe == 0? aDef[bone] : aNext[bone]; // use a0 if first frame, otherwise use a calculated in previous iteration

		  Quaternion<double> aBar = Slerp(0.5, Slerp(2.0, boneStartRot, boneEndRot), boneNextRot);
		  Quaternion<double> p2 = Slerp(-bezRatio, boneEndRot, aBar);

		  double interpolatedAngles[3];
		  Quaternion2Euler(DeCasteljauQuaternion(t, boneStartRot, p1, p2, boneEndRot), interpolatedAngles);

		  interpolatedPosture.bone_rotation[bone].setValue(interpolatedAngles);
	  }
	  pOutputMotion->SetPosture(startKeyframe + frame, interpolatedPosture);
    }

	// set the aN values for the next N frames (bezier)
	vector rootStartPos = startPosture->root_pos;
  	vector rootEndPos = endPosture->root_pos;
	vector rootNextPos = nextPosture->root_pos;
	aNextRoot = rootEndPos*(1-bezRatio) + ((rootStartPos*-1+ rootEndPos*2)*0.5+ rootNextPos*0.5)*bezRatio;

	for (int bone = 0; bone < MAX_BONES_IN_ASF_FILE; bone++)
    {
	   Quaternion<double>  boneStartRot = Vector2Quaternion(startPosture->bone_rotation[bone]); // qn
	   Quaternion<double> boneEndRot = Vector2Quaternion(endPosture->bone_rotation[bone]); // qn+1
	   Quaternion<double> boneNextRot = Vector2Quaternion(nextPosture->bone_rotation[bone]); // qn+2
	   aNext[bone] = Slerp(bezRatio, boneEndRot, Slerp(0.5, Slerp(2.0, boneStartRot, boneEndRot), boneNextRot)); // an+1
	}

    startKeyframe = endKeyframe;
  }

  for(int frame=startKeyframe+1; frame<inputLength; frame++)
    pOutputMotion->SetPosture(frame, *(pInputMotion->GetPosture(frame)));
}