/* $License: Copyright 2011 InvenSense, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. $ */ #include "mlmath.h" #include "mlMathFunc.h" #include "mlinclude.h" /** * Performs one iteration of the filter, generating a new y(0) * 1 / N / N \\ * y(0) = ---- * |SUM b(k) * x(k) - | SUM a(k) * y(k)|| for N = length * a(0) \k=0 \ k=1 // * * The filters A and B should be (sizeof(long) * state->length). * The state variables x and y should be (sizeof(long) * (state->length - 1)) * * The state variables x and y should be initialized prior to the first call * * @param state Contains the length of the filter, filter coefficients and * filter state variables x and y. * @param x New input into the filter. */ void inv_filter_long(struct filter_long *state, long x) { const long *b = state->b; const long *a = state->a; long length = state->length; long long tmp; int ii; /* filter */ tmp = (long long)x *(b[0]); for (ii = 0; ii < length - 1; ii++) { tmp += ((long long)state->x[ii] * (long long)(b[ii + 1])); } for (ii = 0; ii < length - 1; ii++) { tmp -= ((long long)state->y[ii] * (long long)(a[ii + 1])); } tmp /= (long long)a[0]; /* Delay */ for (ii = length - 2; ii > 0; ii--) { state->y[ii] = state->y[ii - 1]; state->x[ii] = state->x[ii - 1]; } /* New values */ state->y[0] = (long)tmp; state->x[0] = x; } /** Performs a multiply and shift by 29. These are good functions to write in assembly on * with devices with small memory where you want to get rid of the long long which some * assemblers don't handle well * @param[in] a * @param[in] b * @return ((long long)a*b)>>29 */ long inv_q29_mult(long a, long b) { long long temp; long result; temp = (long long)a *b; result = (long)(temp >> 29); return result; } /** Performs a multiply and shift by 30. These are good functions to write in assembly on * with devices with small memory where you want to get rid of the long long which some * assemblers don't handle well * @param[in] a * @param[in] b * @return ((long long)a*b)>>30 */ long inv_q30_mult(long a, long b) { long long temp; long result; temp = (long long)a *b; result = (long)(temp >> 30); return result; } void inv_q_mult(const long *q1, const long *q2, long *qProd) { INVENSENSE_FUNC_START; qProd[0] = (long)(((long long)q1[0] * q2[0] - (long long)q1[1] * q2[1] - (long long)q1[2] * q2[2] - (long long)q1[3] * q2[3]) >> 30); qProd[1] = (int)(((long long)q1[0] * q2[1] + (long long)q1[1] * q2[0] + (long long)q1[2] * q2[3] - (long long)q1[3] * q2[2]) >> 30); qProd[2] = (long)(((long long)q1[0] * q2[2] - (long long)q1[1] * q2[3] + (long long)q1[2] * q2[0] + (long long)q1[3] * q2[1]) >> 30); qProd[3] = (long)(((long long)q1[0] * q2[3] + (long long)q1[1] * q2[2] - (long long)q1[2] * q2[1] + (long long)q1[3] * q2[0]) >> 30); } void inv_q_add(long *q1, long *q2, long *qSum) { INVENSENSE_FUNC_START; qSum[0] = q1[0] + q2[0]; qSum[1] = q1[1] + q2[1]; qSum[2] = q1[2] + q2[2]; qSum[3] = q1[3] + q2[3]; } void inv_q_normalize(long *q) { INVENSENSE_FUNC_START; double normSF = 0; int i; for (i = 0; i < 4; i++) { normSF += ((double)q[i]) / 1073741824L * ((double)q[i]) / 1073741824L; } if (normSF > 0) { normSF = 1 / sqrt(normSF); for (i = 0; i < 4; i++) { q[i] = (int)((double)q[i] * normSF); } } else { q[0] = 1073741824L; q[1] = 0; q[2] = 0; q[3] = 0; } } void inv_q_invert(const long *q, long *qInverted) { INVENSENSE_FUNC_START; qInverted[0] = q[0]; qInverted[1] = -q[1]; qInverted[2] = -q[2]; qInverted[3] = -q[3]; } void inv_q_multf(const float *q1, const float *q2, float *qProd) { INVENSENSE_FUNC_START; qProd[0] = (q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3]); qProd[1] = (q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2]); qProd[2] = (q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1]); qProd[3] = (q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0]); } void inv_q_addf(float *q1, float *q2, float *qSum) { INVENSENSE_FUNC_START; qSum[0] = q1[0] + q2[0]; qSum[1] = q1[1] + q2[1]; qSum[2] = q1[2] + q2[2]; qSum[3] = q1[3] + q2[3]; } void inv_q_normalizef(float *q) { INVENSENSE_FUNC_START; float normSF = 0; float xHalf = 0; normSF = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); if (normSF < 2) { xHalf = 0.5f * normSF; normSF = normSF * (1.5f - xHalf * normSF * normSF); normSF = normSF * (1.5f - xHalf * normSF * normSF); normSF = normSF * (1.5f - xHalf * normSF * normSF); normSF = normSF * (1.5f - xHalf * normSF * normSF); q[0] *= normSF; q[1] *= normSF; q[2] *= normSF; q[3] *= normSF; } else { q[0] = 1.0; q[1] = 0.0; q[2] = 0.0; q[3] = 0.0; } normSF = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); } /** Performs a length 4 vector normalization with a square root. * @param[in,out] vector to normalize. Returns [1,0,0,0] is magnitude is zero. */ void inv_q_norm4(float *q) { float mag; mag = sqrtf(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); if (mag) { q[0] /= mag; q[1] /= mag; q[2] /= mag; q[3] /= mag; } else { q[0] = 1.f; q[1] = 0.f; q[2] = 0.f; q[3] = 0.f; } } void inv_q_invertf(const float *q, float *qInverted) { INVENSENSE_FUNC_START; qInverted[0] = q[0]; qInverted[1] = -q[1]; qInverted[2] = -q[2]; qInverted[3] = -q[3]; } /** * Converts a quaternion to a rotation matrix. * @param[in] quat 4-element quaternion in fixed point. One is 2^30. * @param[out] rot Rotation matrix in fixed point. One is 2^30. The * First 3 elements of the rotation matrix, represent * the first row of the matrix. Rotation matrix multiplied * by a 3 element column vector transform a vector from Body * to World. */ void inv_quaternion_to_rotation(const long *quat, long *rot) { rot[0] = inv_q29_mult(quat[1], quat[1]) + inv_q29_mult(quat[0], quat[0]) - 1073741824L; rot[1] = inv_q29_mult(quat[1], quat[2]) - inv_q29_mult(quat[3], quat[0]); rot[2] = inv_q29_mult(quat[1], quat[3]) + inv_q29_mult(quat[2], quat[0]); rot[3] = inv_q29_mult(quat[1], quat[2]) + inv_q29_mult(quat[3], quat[0]); rot[4] = inv_q29_mult(quat[2], quat[2]) + inv_q29_mult(quat[0], quat[0]) - 1073741824L; rot[5] = inv_q29_mult(quat[2], quat[3]) - inv_q29_mult(quat[1], quat[0]); rot[6] = inv_q29_mult(quat[1], quat[3]) - inv_q29_mult(quat[2], quat[0]); rot[7] = inv_q29_mult(quat[2], quat[3]) + inv_q29_mult(quat[1], quat[0]); rot[8] = inv_q29_mult(quat[3], quat[3]) + inv_q29_mult(quat[0], quat[0]) - 1073741824L; } /** Converts a 32-bit long to a big endian byte stream */ unsigned char *inv_int32_to_big8(long x, unsigned char *big8) { big8[0] = (unsigned char)((x >> 24) & 0xff); big8[1] = (unsigned char)((x >> 16) & 0xff); big8[2] = (unsigned char)((x >> 8) & 0xff); big8[3] = (unsigned char)(x & 0xff); return big8; } /** Converts a big endian byte stream into a 32-bit long */ long inv_big8_to_int32(const unsigned char *big8) { long x; x = ((long)big8[0] << 24) | ((long)big8[1] << 16) | ((long)big8[2] << 8) | ((long)big8[3]); return x; } /** Converts a 16-bit short to a big endian byte stream */ unsigned char *inv_int16_to_big8(short x, unsigned char *big8) { big8[0] = (unsigned char)((x >> 8) & 0xff); big8[1] = (unsigned char)(x & 0xff); return big8; } void inv_matrix_det_inc(float *a, float *b, int *n, int x, int y) { int k, l, i, j; for (i = 0, k = 0; i < *n; i++, k++) { for (j = 0, l = 0; j < *n; j++, l++) { if (i == x) i++; if (j == y) j++; *(b + 10 * k + l) = *(a + 10 * i + j); } } *n = *n - 1; } void inv_matrix_det_incd(double *a, double *b, int *n, int x, int y) { int k, l, i, j; for (i = 0, k = 0; i < *n; i++, k++) { for (j = 0, l = 0; j < *n; j++, l++) { if (i == x) i++; if (j == y) j++; *(b + 10 * k + l) = *(a + 10 * i + j); } } *n = *n - 1; } float inv_matrix_det(float *p, int *n) { float d[10][10], sum = 0; int i, j, m; m = *n; if (*n == 2) return (*p ** (p + 11) - *(p + 1) ** (p + 10)); for (i = 0, j = 0; j < m; j++) { *n = m; inv_matrix_det_inc(p, &d[0][0], n, i, j); sum = sum + *(p + 10 * i + j) * SIGNM(i + j) * inv_matrix_det(&d[0][0], n); } return (sum); } double inv_matrix_detd(double *p, int *n) { double d[10][10], sum = 0; int i, j, m; m = *n; if (*n == 2) return (*p ** (p + 11) - *(p + 1) ** (p + 10)); for (i = 0, j = 0; j < m; j++) { *n = m; inv_matrix_det_incd(p, &d[0][0], n, i, j); sum = sum + *(p + 10 * i + j) * SIGNM(i + j) * inv_matrix_detd(&d[0][0], n); } return (sum); } /** Wraps angle from (-M_PI,M_PI] * @param[in] ang Angle in radians to wrap * @return Wrapped angle from (-M_PI,M_PI] */ float inv_wrap_angle(float ang) { if (ang > M_PI) return ang - 2 * (float)M_PI; else if (ang <= -(float)M_PI) return ang + 2 * (float)M_PI; else return ang; } /** Finds the minimum angle difference ang1-ang2 such that difference * is between [-M_PI,M_PI] * @param[in] ang1 * @param[in] ang2 * @return angle difference ang1-ang2 */ float inv_angle_diff(float ang1, float ang2) { float d; ang1 = inv_wrap_angle(ang1); ang2 = inv_wrap_angle(ang2); d = ang1 - ang2; if (d > M_PI) d -= 2 * (float)M_PI; else if (d < -(float)M_PI) d += 2 * (float)M_PI; return d; }