#include "madgwick.h" #include // Initialize the filter void MadgwickFilter_Init(MadgwickFilter* filter, float sampleFreq, float beta) { filter->q[0] = 1.0f; // qw (scalar component) filter->q[1] = 0.0f; // qx filter->q[2] = 0.0f; // qy filter->q[3] = 0.0f; // qz filter->beta = beta; // Filter gain (e.g., 0.1) filter->sampleFreq = sampleFreq; // Sampling frequency in Hz } // Update the quaternion using accelerometer and gyroscope data void MadgwickFilter_Update(MadgwickFilter* filter, float gx, float gy, float gz, float ax, float ay, float az) { float q[4] = {filter->q[0], filter->q[1], filter->q[2], filter->q[3]}; float recipNorm; float s[4]; float qDot[4]; float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3; // Convert gyroscope data from degrees/s to radians/s gx *= 0.0174533f; // deg/s to rad/s gy *= 0.0174533f; gz *= 0.0174533f; // Rate of change of quaternion from gyroscope qDot[0] = 0.5f * (-q[1] * gx - q[2] * gy - q[3] * gz); qDot[1] = 0.5f * (q[0] * gx + q[2] * gz - q[3] * gy); qDot[2] = 0.5f * (q[0] * gy - q[1] * gz + q[3] * gx); qDot[3] = 0.5f * (q[0] * gz + q[1] * gy - q[2] * gx); // Compute accelerometer objective function and Jacobian if ((ax != 0.0f) || (ay != 0.0f) || (az != 0.0f)) { // Normalize accelerometer measurement recipNorm = 1.0f / sqrtf(ax * ax + ay * ay + az * az); ax *= recipNorm; ay *= recipNorm; az *= recipNorm; // Auxiliary variables to avoid repeated calculations _2q0 = 2.0f * q[0]; _2q1 = 2.0f * q[1]; _2q2 = 2.0f * q[2]; _2q3 = 2.0f * q[3]; _4q0 = 4.0f * q[0]; _4q1 = 4.0f * q[1]; _4q2 = 4.0f * q[2]; _8q1 = 8.0f * q[1]; _8q2 = 8.0f * q[2]; q0q0 = q[0] * q[0]; q1q1 = q[1] * q[1]; q2q2 = q[2] * q[2]; q3q3 = q[3] * q[3]; // Gradient descent algorithm corrective step s[0] = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; s[1] = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q[1] - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; s[2] = 4.0f * q0q0 * q[2] + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; s[3] = 4.0f * q1q1 * q[3] - _2q1 * ax + 4.0f * q2q2 * q[3] - _2q2 * ay; // Normalize step magnitude recipNorm = 1.0f / sqrtf(s[0] * s[0] + s[1] * s[1] + s[2] * s[2] + s[3] * s[3]); s[0] *= recipNorm; s[1] *= recipNorm; s[2] *= recipNorm; s[3] *= recipNorm; // Apply feedback step qDot[0] -= filter->beta * s[0]; qDot[1] -= filter->beta * s[1]; qDot[2] -= filter->beta * s[2]; qDot[3] -= filter->beta * s[3]; } // Integrate rate of change of quaternion float dt = 1.0f / filter->sampleFreq; q[0] += qDot[0] * dt; q[1] += qDot[1] * dt; q[2] += qDot[2] * dt; q[3] += qDot[3] * dt; // Normalize quaternion recipNorm = 1.0f / sqrtf(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; // Update filter state filter->q[0] = q[0]; filter->q[1] = q[1]; filter->q[2] = q[2]; filter->q[3] = q[3]; } // Convert quaternion to Euler angles (roll, pitch, yaw) in degrees void MadgwickFilter_GetEulerAngles(MadgwickFilter* filter, float* roll, float* pitch, float* yaw) { float q0 = filter->q[0]; float q1 = filter->q[1]; float q2 = filter->q[2]; float q3 = filter->q[3]; // Roll (x-axis rotation) *roll = atan2f(2.0f * (q0 * q1 + q2 * q3), 1.0f - 2.0f * (q1 * q1 + q2 * q2)) * 57.2958f; // Pitch (y-axis rotation) float sinp = 2.0f * (q0 * q2 - q3 * q1); if (fabsf(sinp) >= 1) *pitch = copysignf(M_PI / 2, sinp) * 57.2958f; // Use 90 degrees if out of range else *pitch = asinf(sinp) * 57.2958f; // Yaw (z-axis rotation) - Note: Without magnetometer, yaw will drift *yaw = atan2f(2.0f * (q0 * q3 + q1 * q2), 1.0f - 2.0f * (q2 * q2 + q3 * q3)) * 57.2958f; }