Sensor Fusion Library 0.6.1
Orientation sensing for Espressif (ESP32, ESP8266) processors
Loading...
Searching...
No Matches
orientation.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9// This file contains functions designed to operate on, or compute, orientations.
10// These may be in rotation matrix form, quaternion form, or Euler angles.
11// It also includes functions designed to operate with specify reference frames
12// (Android, Windows 8, NED).
13//
14
22#include <math.h>
23
24#include "sensor_fusion.h"
25#include "orientation.h"
26#include "matrix.h"
27#include "approximations.h"
28
29// compile time constants that are private to this file
30#define SMALLQ0 1E-4F // limit of quaternion scalar component requiring special algorithm
31#define CORRUPTQUAT 0.001F // threshold for deciding rotation quaternion is corrupt
32#define SMALLMODULUS 0.01F // limit where rounding errors may appear
33
34#if F_USING_ACCEL // Need tilt conversion routines
35// Aerospace NED accelerometer 3DOF tilt function computing rotation matrix fR
36#if (THISCOORDSYSTEM == NED) || (THISCOORDSYSTEM == ANDROID)
37void f3DOFTiltNED(float fR[][3], float fGc[])
38{
39 // the NED self-consistency twist occurs at 90 deg pitch
40
41 // local variables
42 int16_t i; // counter
43 float fmodGxyz; // modulus of the x, y, z accelerometer readings
44 float fmodGyz; // modulus of the y, z accelerometer readings
45 float frecipmodGxyz; // reciprocal of modulus
46 float ftmp; // scratch variable
47
48 // compute the accelerometer squared magnitudes
49 fmodGyz = fGc[CHY] * fGc[CHY] + fGc[CHZ] * fGc[CHZ];
50 fmodGxyz = fmodGyz + fGc[CHX] * fGc[CHX];
51
52 // check for freefall special case where no solution is possible
53 if (fmodGxyz == 0.0F)
54 {
56 return;
57 }
58
59 // check for vertical up or down gimbal lock case
60 if (fmodGyz == 0.0F)
61 {
62 f3x3matrixAeqScalar(fR, 0.0F);
63 fR[CHY][CHY] = 1.0F;
64 if (fGc[CHX] >= 0.0F)
65 {
66 fR[CHX][CHZ] = 1.0F;
67 fR[CHZ][CHX] = -1.0F;
68 }
69 else
70 {
71 fR[CHX][CHZ] = -1.0F;
72 fR[CHZ][CHX] = 1.0F;
73 }
74 return;
75 }
76
77 // compute moduli for the general case
78 fmodGyz = sqrtf(fmodGyz);
79 fmodGxyz = sqrtf(fmodGxyz);
80 frecipmodGxyz = 1.0F / fmodGxyz;
81 ftmp = fmodGxyz / fmodGyz;
82
83 // normalize the accelerometer reading into the z column
84 for (i = CHX; i <= CHZ; i++)
85 {
86 fR[i][CHZ] = fGc[i] * frecipmodGxyz;
87 }
88
89 // construct x column of orientation matrix
90 fR[CHX][CHX] = fmodGyz * frecipmodGxyz;
91 fR[CHY][CHX] = -fR[CHX][CHZ] * fR[CHY][CHZ] * ftmp;
92 fR[CHZ][CHX] = -fR[CHX][CHZ] * fR[CHZ][CHZ] * ftmp;
93
94 // construct y column of orientation matrix
95 fR[CHX][CHY] = 0.0F;
96 fR[CHY][CHY] = fR[CHZ][CHZ] * ftmp;
97 fR[CHZ][CHY] = -fR[CHY][CHZ] * ftmp;
98
99 return;
100}
101#endif // #if THISCOORDSYSTEM == NED
102
103// Android accelerometer 3DOF tilt function computing rotation matrix fR
104#if THISCOORDSYSTEM == ANDROID
105void f3DOFTiltAndroid(float fR[][3], float fGc[])
106{
107 // the Android tilt matrix is mathematically identical to the NED tilt matrix
108 // the Android self-consistency twist occurs at 90 deg roll
109 f3DOFTiltNED(fR, fGc);
110 return;
111}
112#endif // #if THISCOORDSYSTEM == ANDROID
113
114// Windows 8 accelerometer 3DOF tilt function computing rotation matrix fR
115#if (THISCOORDSYSTEM == WIN8) || (THISCOORDSYSTEM == ANDROID)
116void f3DOFTiltWin8(float fR[][3], float fGc[])
117{
118 // the Win8 self-consistency twist occurs at 90 deg roll
119
120 // local variables
121 float fmodGxyz; // modulus of the x, y, z accelerometer readings
122 float fmodGxz; // modulus of the x, z accelerometer readings
123 float frecipmodGxyz; // reciprocal of modulus
124 float ftmp; // scratch variable
125 int8 i; // counter
126
127 // compute the accelerometer squared magnitudes
128 fmodGxz = fGc[CHX] * fGc[CHX] + fGc[CHZ] * fGc[CHZ];
129 fmodGxyz = fmodGxz + fGc[CHY] * fGc[CHY];
130
131 // check for freefall special case where no solution is possible
132 if (fmodGxyz == 0.0F)
133 {
134 f3x3matrixAeqI(fR);
135 return;
136 }
137
138 // check for vertical up or down gimbal lock case
139 if (fmodGxz == 0.0F)
140 {
141 f3x3matrixAeqScalar(fR, 0.0F);
142 fR[CHX][CHX] = 1.0F;
143 if (fGc[CHY] >= 0.0F)
144 {
145 fR[CHY][CHZ] = -1.0F;
146 fR[CHZ][CHY] = 1.0F;
147 }
148 else
149 {
150 fR[CHY][CHZ] = 1.0F;
151 fR[CHZ][CHY] = -1.0F;
152 }
153 return;
154 }
155
156 // compute moduli for the general case
157 fmodGxz = sqrtf(fmodGxz);
158 fmodGxyz = sqrtf(fmodGxyz);
159 frecipmodGxyz = 1.0F / fmodGxyz;
160 ftmp = fmodGxyz / fmodGxz;
161 if (fGc[CHZ] < 0.0F)
162 {
163 ftmp = -ftmp;
164 }
165
166 // normalize the negated accelerometer reading into the z column
167 for (i = CHX; i <= CHZ; i++)
168 {
169 fR[i][CHZ] = -fGc[i] * frecipmodGxyz;
170 }
171
172 // construct x column of orientation matrix
173 fR[CHX][CHX] = -fR[CHZ][CHZ] * ftmp;
174 fR[CHY][CHX] = 0.0F;
175 fR[CHZ][CHX] = fR[CHX][CHZ] * ftmp;;
176
177 // // construct y column of orientation matrix
178 fR[CHX][CHY] = fR[CHX][CHZ] * fR[CHY][CHZ] * ftmp;
179 fR[CHY][CHY] = -fmodGxz * frecipmodGxyz;
180 if (fGc[CHZ] < 0.0F)
181 {
182 fR[CHY][CHY] = -fR[CHY][CHY];
183 }
184 fR[CHZ][CHY] = fR[CHY][CHZ] * fR[CHZ][CHZ] * ftmp;
185
186 return;
187}
188#endif // #if (THISCOORDSYSTEM == WIN8)
189#endif // #if F_USING_ACCEL // Need tilt conversion routines
190
191#if F_USING_MAG // Need eCompass conversion routines
192// Aerospace NED magnetometer 3DOF flat eCompass function computing rotation matrix fR
193#if THISCOORDSYSTEM == NED
194void f3DOFMagnetometerMatrixNED(float fR[][3], float fBc[])
195{
196 // local variables
197 float fmodBxy; // modulus of the x, y magnetometer readings
198
199 // compute the magnitude of the horizontal (x and y) magnetometer reading
200 fmodBxy = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY]);
201
202 // check for zero field special case where no solution is possible
203 if (fmodBxy == 0.0F)
204 {
205 f3x3matrixAeqI(fR);
206 return;
207 }
208
209 // define the fixed entries in the z row and column
210 fR[CHZ][CHX] = fR[CHZ][CHY] = fR[CHX][CHZ] = fR[CHY][CHZ] = 0.0F;
211 fR[CHZ][CHZ] = 1.0F;
212
213 // define the remaining entries
214 fR[CHX][CHX] = fR[CHY][CHY] = fBc[CHX] / fmodBxy;
215 fR[CHY][CHX] = fBc[CHY] / fmodBxy;
216 fR[CHX][CHY] = -fR[CHY][CHX];
217
218 return;
219}
220#endif // #if THISCOORDSYSTEM == NED
221
222// Android magnetometer 3DOF flat eCompass function computing rotation matrix fR
223#if (THISCOORDSYSTEM == ANDROID) || (THISCOORDSYSTEM == WIN8)
224void f3DOFMagnetometerMatrixAndroid(float fR[][3], float fBc[])
225{
226 // local variables
227 float fmodBxy; // modulus of the x, y magnetometer readings
228
229 // compute the magnitude of the horizontal (x and y) magnetometer reading
230 fmodBxy = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY]);
231
232 // check for zero field special case where no solution is possible
233 if (fmodBxy == 0.0F)
234 {
235 f3x3matrixAeqI(fR);
236 return;
237 }
238
239 // define the fixed entries in the z row and column
240 fR[CHZ][CHX] = fR[CHZ][CHY] = fR[CHX][CHZ] = fR[CHY][CHZ] = 0.0F;
241 fR[CHZ][CHZ] = 1.0F;
242
243 // define the remaining entries
244 fR[CHX][CHX] = fR[CHY][CHY] = fBc[CHY] / fmodBxy;
245 fR[CHX][CHY] = fBc[CHX] / fmodBxy;
246 fR[CHY][CHX] = -fR[CHX][CHY];
247
248 return;
249}
250#endif // #if THISCOORDSYSTEM == ANDROID
251
252// Windows 8 magnetometer 3DOF flat eCompass function computing rotation matrix fR
253#if (THISCOORDSYSTEM == WIN8)
254void f3DOFMagnetometerMatrixWin8(float fR[][3], float fBc[])
255{
256 // call the Android function since it is identical to the Windows 8 matrix
258
259 return;
260}
261#endif // #if (THISCOORDSYSTEM == WIN8)
262
263// NED: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
264#if THISCOORDSYSTEM == NED
265void feCompassNED(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
266{
267 // local variables
268 float fmod[3]; // column moduli
269 float fGcdotBc; // dot product of vectors G.Bc
270 float ftmp; // scratch variable
271 int8_t i, j; // loop counters
272
273 // set the inclination angle to zero in case it is not computed later
274 *pfDelta = *pfsinDelta = 0.0F;
275 *pfcosDelta = 1.0F;
276
277 // place the un-normalized gravity and geomagnetic vectors into the rotation matrix z and x axes
278 for (i = CHX; i <= CHZ; i++)
279 {
280 fR[i][CHZ] = fGc[i];
281 fR[i][CHX] = fBc[i];
282 }
283
284 // set y vector to vector product of z and x vectors
285 fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
286 fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
287 fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
288
289 // set x vector to vector product of y and z vectors
290 fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
291 fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
292 fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
293
294 // calculate the rotation matrix column moduli
295 fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
296 fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
297 fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
298
299 // normalize the rotation matrix columns
300 if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
301 {
302 // loop over columns j
303 for (j = CHX; j <= CHZ; j++)
304 {
305 ftmp = 1.0F / fmod[j];
306 // loop over rows i
307 for (i = CHX; i <= CHZ; i++)
308 {
309 // normalize by the column modulus
310 fR[i][j] *= ftmp;
311 }
312 }
313 }
314 else
315 {
316 // no solution is possible so set rotation to identity matrix
317 f3x3matrixAeqI(fR);
318 return;
319 }
320
321 // compute the geomagnetic inclination angle (deg)
322 *pfmodGc = fmod[CHZ];
323 *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
324 fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
325 if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
326 {
327 *pfsinDelta = fGcdotBc / (*pfmodGc * *pfmodBc);
328 *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
329 *pfDelta = fasin_deg(*pfsinDelta);
330 }
331
332 return;
333}
334#endif // #if THISCOORDSYSTEM == NED
335
336// Android: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
337#if THISCOORDSYSTEM == ANDROID
338void feCompassAndroid(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[],
339 float *pfmodBc, float *pfmodGc)
340{
341 // local variables
342 float fmod[3]; // column moduli
343 float fGcdotBc; // dot product of vectors G.Bc
344 float ftmp; // scratch variable
345 int8 i, j; // loop counters
346
347 // set the inclination angle to zero in case it is not computed later
348 *pfDelta = *pfsinDelta = 0.0F;
349 *pfcosDelta = 1.0F;
350
351 // place the un-normalized gravity and geomagnetic vectors into the rotation matrix z and y axes
352 for (i = CHX; i <= CHZ; i++)
353 {
354 fR[i][CHZ] = fGc[i];
355 fR[i][CHY] = fBc[i];
356 }
357
358 // set x vector to vector product of y and z vectors
359 fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
360 fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
361 fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
362
363 // set y vector to vector product of z and x vectors
364 fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
365 fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
366 fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
367
368 // calculate the rotation matrix column moduli
369 fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
370 fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
371 fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
372
373 // normalize the rotation matrix columns
374 if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
375 {
376 // loop over columns j
377 for (j = CHX; j <= CHZ; j++)
378 {
379 ftmp = 1.0F / fmod[j];
380 // loop over rows i
381 for (i = CHX; i <= CHZ; i++)
382 {
383 // normalize by the column modulus
384 fR[i][j] *= ftmp;
385 }
386 }
387 }
388 else
389 {
390 // no solution is possible so set rotation to identity matrix
391 f3x3matrixAeqI(fR);
392 return;
393 }
394
395 // compute the geomagnetic inclination angle (deg)
396 *pfmodGc = fmod[CHZ];
397 *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
398 fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
399 if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
400 {
401 *pfsinDelta = -fGcdotBc / (*pfmodGc * *pfmodBc);
402 *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
403 *pfDelta = fasin_deg(*pfsinDelta);
404 }
405
406 return;
407}
408#endif // #if THISCOORDSYSTEM == ANDROID
409
410// Win8: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
411#if (THISCOORDSYSTEM == WIN8)
412void feCompassWin8(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[],
413 float *pfmodBc, float *pfmodGc)
414{
415 // local variables
416 float fmod[3]; // column moduli
417 float fGcdotBc; // dot product of vectors G.Bc
418 float ftmp; // scratch variable
419 int8 i, j; // loop counters
420
421 // set the inclination angle to zero in case it is not computed later
422 *pfDelta = *pfsinDelta = 0.0F;
423 *pfcosDelta = 1.0F;
424
425 // place the negated un-normalized gravity and un-normalized geomagnetic vectors into the rotation matrix z and y axes
426 for (i = CHX; i <= CHZ; i++)
427 {
428 fR[i][CHZ] = -fGc[i];
429 fR[i][CHY] = fBc[i];
430 }
431
432 // set x vector to vector product of y and z vectors
433 fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
434 fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
435 fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
436
437 // set y vector to vector product of z and x vectors
438 fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
439 fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
440 fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
441
442 // calculate the rotation matrix column moduli
443 fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
444 fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
445 fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
446
447 // normalize the rotation matrix columns
448 if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
449 {
450 // loop over columns j
451 for (j = CHX; j <= CHZ; j++)
452 {
453 ftmp = 1.0F / fmod[j];
454 // loop over rows i
455 for (i = CHX; i <= CHZ; i++)
456 {
457 // normalize by the column modulus
458 fR[i][j] *= ftmp;
459 }
460 }
461 }
462 else
463 {
464 // no solution is possible so set rotation to identity matrix
465 f3x3matrixAeqI(fR);
466 return;
467 }
468
469 // compute the geomagnetic inclination angle (deg)
470 *pfmodGc = fmod[CHZ];
471 *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
472 fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
473 if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
474 {
475 *pfsinDelta = fGcdotBc / (*pfmodGc * *pfmodBc);
476 *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
477 *pfDelta = fasin_deg(*pfsinDelta);
478 }
479
480 return;
481}
482#endif // #if (THISCOORDSYSTEM == WIN8)
483#endif // #if F_USING_MAG // Need eCompass conversion routines
484
485// extract the NED angles in degrees from the NED rotation matrix
486#if THISCOORDSYSTEM == NED
487void fNEDAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
488 float *pfRhoDeg, float *pfChiDeg)
489{
490 // calculate the pitch angle -90.0 <= Theta <= 90.0 deg
491 *pfTheDeg = fasin_deg(-R[CHX][CHZ]);
492
493 // calculate the roll angle range -180.0 <= Phi < 180.0 deg
494 *pfPhiDeg = fatan2_deg(R[CHY][CHZ], R[CHZ][CHZ]);
495
496 // map +180 roll onto the functionally equivalent -180 deg roll
497 if (*pfPhiDeg == 180.0F)
498 {
499 *pfPhiDeg = -180.0F;
500 }
501
502 // calculate the yaw (compass) angle 0.0 <= Psi < 360.0 deg
503 if (*pfTheDeg == 90.0F)
504 {
505 // vertical upwards gimbal lock case
506 *pfPsiDeg = fatan2_deg(R[CHZ][CHY], R[CHY][CHY]) + *pfPhiDeg;
507 }
508 else if (*pfTheDeg == -90.0F)
509 {
510 // vertical downwards gimbal lock case
511 *pfPsiDeg = fatan2_deg(-R[CHZ][CHY], R[CHY][CHY]) - *pfPhiDeg;
512 }
513 else
514 {
515 // general case
516 *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]);
517 }
518
519 // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
520 if (*pfPsiDeg < 0.0F)
521 {
522 *pfPsiDeg += 360.0F;
523 }
524
525 // check for rounding errors mapping small negative angle to 360 deg
526 if (*pfPsiDeg >= 360.0F)
527 {
528 *pfPsiDeg = 0.0F;
529 }
530
531 // for NED, the compass heading Rho equals the yaw angle Psi
532 *pfRhoDeg = *pfPsiDeg;
533
534 // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
535 *pfChiDeg = facos_deg(R[CHZ][CHZ]);
536
537 return;
538}
539#endif // #if THISCOORDSYSTEM == NED
540
541// extract the Android angles in degrees from the Android rotation matrix
542#if THISCOORDSYSTEM == ANDROID
543void fAndroidAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
544 float *pfRhoDeg, float *pfChiDeg)
545{
546 // calculate the roll angle -90.0 <= Phi <= 90.0 deg
547 *pfPhiDeg = fasin_deg(R[CHX][CHZ]);
548
549 // calculate the pitch angle -180.0 <= The < 180.0 deg
550 *pfTheDeg = fatan2_deg(-R[CHY][CHZ], R[CHZ][CHZ]);
551
552 // map +180 pitch onto the functionally equivalent -180 deg pitch
553 if (*pfTheDeg == 180.0F)
554 {
555 *pfTheDeg = -180.0F;
556 }
557
558 // calculate the yaw (compass) angle 0.0 <= Psi < 360.0 deg
559 if (*pfPhiDeg == 90.0F)
560 {
561 // vertical downwards gimbal lock case
562 *pfPsiDeg = fatan2_deg(R[CHY][CHX], R[CHY][CHY]) - *pfTheDeg;
563 }
564 else if (*pfPhiDeg == -90.0F)
565 {
566 // vertical upwards gimbal lock case
567 *pfPsiDeg = fatan2_deg(R[CHY][CHX], R[CHY][CHY]) + *pfTheDeg;
568 }
569 else
570 {
571 // general case
572 *pfPsiDeg = fatan2_deg(-R[CHX][CHY], R[CHX][CHX]);
573 }
574
575 // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
576 if (*pfPsiDeg < 0.0F)
577 {
578 *pfPsiDeg += 360.0F;
579 }
580
581 // check for rounding errors mapping small negative angle to 360 deg
582 if (*pfPsiDeg >= 360.0F)
583 {
584 *pfPsiDeg = 0.0F;
585 }
586
587 // the compass heading angle Rho equals the yaw angle Psi
588 // this definition is compliant with Motorola Xoom tablet behavior
589 *pfRhoDeg = *pfPsiDeg;
590
591 // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
592 *pfChiDeg = facos_deg(R[CHZ][CHZ]);
593
594 return;
595}
596#endif // #if THISCOORDSYSTEM == ANDROID
597
598// extract the Windows 8 angles in degrees from the Windows 8 rotation matrix
599#if (THISCOORDSYSTEM == WIN8)
600void fWin8AnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
601 float *pfRhoDeg, float *pfChiDeg)
602{
603 // calculate the roll angle -90.0 <= Phi <= 90.0 deg
604 if (R[CHZ][CHZ] == 0.0F)
605 {
606 if (R[CHX][CHZ] >= 0.0F)
607 {
608 // tan(phi) is -infinity
609 *pfPhiDeg = -90.0F;
610 }
611 else
612 {
613 // tan(phi) is +infinity
614 *pfPhiDeg = 90.0F;
615 }
616 }
617 else
618 {
619 // general case
620 *pfPhiDeg = fatan_deg(-R[CHX][CHZ] / R[CHZ][CHZ]);
621 }
622
623 // first calculate the pitch angle The in the range -90.0 <= The <= 90.0 deg
624 *pfTheDeg = fasin_deg(R[CHY][CHZ]);
625
626 // use R[CHZ][CHZ]=cos(Phi)*cos(The) to correct the quadrant of The remembering
627 // cos(Phi) is non-negative so that cos(The) has the same sign as R[CHZ][CHZ].
628 if (R[CHZ][CHZ] < 0.0F)
629 {
630 // wrap The around +90 deg and -90 deg giving result 90 to 270 deg
631 *pfTheDeg = 180.0F - *pfTheDeg;
632 }
633
634 // map the pitch angle The to the range -180.0 <= The < 180.0 deg
635 if (*pfTheDeg >= 180.0F)
636 {
637 *pfTheDeg -= 360.0F;
638 }
639
640 // calculate the yaw angle Psi
641 if (*pfTheDeg == 90.0F)
642 {
643 // vertical upwards gimbal lock case: -270 <= Psi < 90 deg
644 *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]) - *pfPhiDeg;
645 }
646 else if (*pfTheDeg == -90.0F)
647 {
648 // vertical downwards gimbal lock case: -270 <= Psi < 90 deg
649 *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]) + *pfPhiDeg;
650 }
651 else
652 {
653 // general case: -180 <= Psi < 180 deg
654 *pfPsiDeg = fatan2_deg(-R[CHY][CHX], R[CHY][CHY]);
655
656 // correct the quadrant for Psi using the value of The (deg) to give -180 <= Psi < 380 deg
657 if (fabsf(*pfTheDeg) >= 90.0F)
658 {
659 *pfPsiDeg += 180.0F;
660 }
661 }
662
663 // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
664 if (*pfPsiDeg < 0.0F)
665 {
666 *pfPsiDeg += 360.0F;
667 }
668
669 // check for any rounding error mapping small negative angle to 360 deg
670 if (*pfPsiDeg >= 360.0F)
671 {
672 *pfPsiDeg = 0.0F;
673 }
674
675 // compute the compass angle Rho = 360 - Psi
676 *pfRhoDeg = 360.0F - *pfPsiDeg;
677
678 // check for rounding errors mapping small negative angle to 360 deg and zero degree case
679 if (*pfRhoDeg >= 360.0F)
680 {
681 *pfRhoDeg = 0.0F;
682 }
683
684 // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
685 *pfChiDeg = facos_deg(R[CHZ][CHZ]);
686
687 return;
688}
689#endif // #if (THISCOORDSYSTEM == WIN8)
690
691// computes normalized rotation quaternion from a rotation vector (deg)
692void fQuaternionFromRotationVectorDeg(Quaternion *pq, const float rvecdeg[], float fscaling)
693{
694 float fetadeg; // rotation angle (deg)
695 float fetarad; // rotation angle (rad)
696 float fetarad2; // eta (rad)^2
697 float fetarad4; // eta (rad)^4
698 float sinhalfeta; // sin(eta/2)
699 float fvecsq; // q1^2+q2^2+q3^2
700 float ftmp; // scratch variable
701
702 // compute the scaled rotation angle eta (deg) which can be both positve or negative
703 fetadeg = fscaling * sqrtf(rvecdeg[CHX] * rvecdeg[CHX] + rvecdeg[CHY] * rvecdeg[CHY] + rvecdeg[CHZ] * rvecdeg[CHZ]);
704 fetarad = fetadeg * FPIOVER180;
705 fetarad2 = fetarad * fetarad;
706
707 // calculate the sine and cosine using small angle approximations or exact
708 // angles under sqrt(0.02)=0.141 rad is 8.1 deg and 1620 deg/s (=936deg/s in 3 axes) at 200Hz and 405 deg/s at 50Hz
709 if (fetarad2 <= 0.02F)
710 {
711 // use MacLaurin series up to and including third order
712 sinhalfeta = fetarad * (0.5F - ONEOVER48 * fetarad2);
713 }
714 else if (fetarad2 <= 0.06F)
715 {
716 // use MacLaurin series up to and including fifth order
717 // angles under sqrt(0.06)=0.245 rad is 14.0 deg and 2807 deg/s (=1623deg/s in 3 axes) at 200Hz and 703 deg/s at 50Hz
718 fetarad4 = fetarad2 * fetarad2;
719 sinhalfeta = fetarad * (0.5F - ONEOVER48 * fetarad2 + ONEOVER3840 * fetarad4);
720 }
721 else
722 {
723 // use exact calculation
724 sinhalfeta = (float)sinf(0.5F * fetarad);
725 }
726
727 // compute the vector quaternion components q1, q2, q3
728 if (fetadeg != 0.0F)
729 {
730 // general case with non-zero rotation angle
731 ftmp = fscaling * sinhalfeta / fetadeg;
732 pq->q1 = rvecdeg[CHX] * ftmp; // q1 = nx * sin(eta/2)
733 pq->q2 = rvecdeg[CHY] * ftmp; // q2 = ny * sin(eta/2)
734 pq->q3 = rvecdeg[CHZ] * ftmp; // q3 = nz * sin(eta/2)
735 }
736 else
737 {
738 // zero rotation angle giving zero vector component
739 pq->q1 = pq->q2 = pq->q3 = 0.0F;
740 }
741
742 // compute the scalar quaternion component q0 by explicit normalization
743 // taking care to avoid rounding errors giving negative operand to sqrt
744 fvecsq = pq->q1 * pq->q1 + pq->q2 * pq->q2 + pq->q3 * pq->q3;
745 if (fvecsq <= 1.0F)
746 {
747 // normal case
748 pq->q0 = sqrtf(1.0F - fvecsq);
749 }
750 else
751 {
752 // rounding errors are present
753 pq->q0 = 0.0F;
754 }
755
756 return;
757}
758
759// compute the orientation quaternion from a 3x3 rotation matrix
761{
762 float fq0sq; // q0^2
763 float recip4q0; // 1/4q0
764
765 // get q0^2 and q0
766 fq0sq = 0.25F * (1.0F + R[CHX][CHX] + R[CHY][CHY] + R[CHZ][CHZ]);
767 pq->q0 = sqrtf(fabsf(fq0sq));
768
769 // normal case when q0 is not small meaning rotation angle not near 180 deg
770 if (pq->q0 > SMALLQ0)
771 {
772 // calculate q1 to q3
773 recip4q0 = 0.25F / pq->q0;
774 pq->q1 = recip4q0 * (R[CHY][CHZ] - R[CHZ][CHY]);
775 pq->q2 = recip4q0 * (R[CHZ][CHX] - R[CHX][CHZ]);
776 pq->q3 = recip4q0 * (R[CHX][CHY] - R[CHY][CHX]);
777 } // end of general case
778 else
779 {
780 // special case of near 180 deg corresponds to nearly symmetric matrix
781 // which is not numerically well conditioned for division by small q0
782 // instead get absolute values of q1 to q3 from leading diagonal
783 pq->q1 = sqrtf(fabsf(0.5F + 0.5F * R[CHX][CHX] - fq0sq));
784 pq->q2 = sqrtf(fabsf(0.5F + 0.5F * R[CHY][CHY] - fq0sq));
785 pq->q3 = sqrtf(fabsf(0.5F + 0.5F * R[CHZ][CHZ] - fq0sq));
786
787 // correct the signs of q1 to q3 by examining the signs of differenced off-diagonal terms
788 if ((R[CHY][CHZ] - R[CHZ][CHY]) < 0.0F) pq->q1 = -pq->q1;
789 if ((R[CHZ][CHX] - R[CHX][CHZ]) < 0.0F) pq->q2 = -pq->q2;
790 if ((R[CHX][CHY] - R[CHY][CHX]) < 0.0F) pq->q3 = -pq->q3;
791 } // end of special case
792
793 // ensure that the resulting quaternion is normalized even if the input rotation matrix was not
794 fqAeqNormqA(pq);
795
796
797 return;
798}
799
800// compute the rotation matrix from an orientation quaternion
801void fRotationMatrixFromQuaternion(float R[][3], const Quaternion *pq)
802{
803 float f2q;
804 float f2q0q0, f2q0q1, f2q0q2, f2q0q3;
805 float f2q1q1, f2q1q2, f2q1q3;
806 float f2q2q2, f2q2q3;
807 float f2q3q3;
808
809 // set f2q to 2*q0 and calculate products
810 f2q = 2.0F * pq->q0;
811 f2q0q0 = f2q * pq->q0;
812 f2q0q1 = f2q * pq->q1;
813 f2q0q2 = f2q * pq->q2;
814 f2q0q3 = f2q * pq->q3;
815 // set f2q to 2*q1 and calculate products
816 f2q = 2.0F * pq->q1;
817 f2q1q1 = f2q * pq->q1;
818 f2q1q2 = f2q * pq->q2;
819 f2q1q3 = f2q * pq->q3;
820 // set f2q to 2*q2 and calculate products
821 f2q = 2.0F * pq->q2;
822 f2q2q2 = f2q * pq->q2;
823 f2q2q3 = f2q * pq->q3;
824 f2q3q3 = 2.0F * pq->q3 * pq->q3;
825
826 // calculate the rotation matrix assuming the quaternion is normalized
827 R[CHX][CHX] = f2q0q0 + f2q1q1 - 1.0F;
828 R[CHX][CHY] = f2q1q2 + f2q0q3;
829 R[CHX][CHZ] = f2q1q3 - f2q0q2;
830 R[CHY][CHX] = f2q1q2 - f2q0q3;
831 R[CHY][CHY] = f2q0q0 + f2q2q2 - 1.0F;
832 R[CHY][CHZ] = f2q2q3 + f2q0q1;
833 R[CHZ][CHX] = f2q1q3 + f2q0q2;
834 R[CHZ][CHY] = f2q2q3 - f2q0q1;
835 R[CHZ][CHZ] = f2q0q0 + f2q3q3 - 1.0F;
836
837 return;
838}
839
840// computes rotation vector (deg) from rotation quaternion
842{
843 float fetarad; // rotation angle (rad)
844 float fetadeg; // rotation angle (deg)
845 float sinhalfeta; // sin(eta/2)
846 float ftmp; // scratch variable
847
848 // calculate the rotation angle in the range 0 <= eta < 360 deg
849 if ((pq->q0 >= 1.0F) || (pq->q0 <= -1.0F))
850 {
851 // rotation angle is 0 deg or 2*180 deg = 360 deg = 0 deg
852 fetarad = 0.0F;
853 fetadeg = 0.0F;
854 }
855 else
856 {
857 // general case returning 0 < eta < 360 deg
858 fetarad = 2.0F * acosf(pq->q0);
859 fetadeg = fetarad * F180OVERPI;
860 }
861
862 // map the rotation angle onto the range -180 deg <= eta < 180 deg
863 if (fetadeg >= 180.0F)
864 {
865 fetadeg -= 360.0F;
866 fetarad = fetadeg * FPIOVER180;
867 }
868
869 // calculate sin(eta/2) which will be in the range -1 to +1
870 sinhalfeta = (float)sinf(0.5F * fetarad);
871
872 // calculate the rotation vector (deg)
873 if (sinhalfeta == 0.0F)
874 {
875 // the rotation angle eta is zero and the axis is irrelevant
876 rvecdeg[CHX] = rvecdeg[CHY] = rvecdeg[CHZ] = 0.0F;
877 }
878 else
879 {
880 // general case with non-zero rotation angle
881 ftmp = fetadeg / sinhalfeta;
882 rvecdeg[CHX] = pq->q1 * ftmp;
883 rvecdeg[CHY] = pq->q2 * ftmp;
884 rvecdeg[CHZ] = pq->q3 * ftmp;
885 }
886
887 return;
888}
889
890// function low pass filters an orientation quaternion and computes virtual gyro rotation rate
891void fLPFOrientationQuaternion(Quaternion *pq, Quaternion *pLPq, float flpf, float fdeltat, float fOmega[])
892{
893 // local variables
894 Quaternion fdeltaq; // delta rotation quaternion
895 float rvecdeg[3]; // rotation vector (deg)
896 float ftmp; // scratch variable
897
898 // set fdeltaq to the delta rotation quaternion conjg(fLPq[n-1) . fqn
899 fdeltaq = qconjgAxB(pLPq, pq);
900 if (fdeltaq.q0 < 0.0F)
901 {
902 fdeltaq.q0 = -fdeltaq.q0;
903 fdeltaq.q1 = -fdeltaq.q1;
904 fdeltaq.q2 = -fdeltaq.q2;
905 fdeltaq.q3 = -fdeltaq.q3;
906 }
907
908 // set ftmp to a scaled value which equals flpf in the limit of small rotations (q0=1)
909 // but which rises to 1 (all pass) as the delta rotation angle increases (q0 tends to zero)
910 ftmp = flpf + (1.0F - flpf) * sqrtf(fabs(1.0F - fdeltaq.q0 * fdeltaq.q0));
911
912 // scale the vector component of the delta rotation quaternion by the corrected lpf value
913 // and re-compute the scalar component q0 to ensure normalization
914 fdeltaq.q1 *= ftmp;
915 fdeltaq.q2 *= ftmp;
916 fdeltaq.q3 *= ftmp;
917 fdeltaq.q0 = sqrtf(fabsf(1.0F - fdeltaq.q1 * fdeltaq.q1 - fdeltaq.q2 * fdeltaq.q2 - fdeltaq.q3 * fdeltaq.q3));
918
919 // calculate the delta rotation vector from fdeltaq and the virtual gyro angular velocity (deg/s)
920 fRotationVectorDegFromQuaternion(&fdeltaq, rvecdeg);
921 ftmp = 1.0F / fdeltat;
922 fOmega[CHX] = rvecdeg[CHX] * ftmp;
923 fOmega[CHY] = rvecdeg[CHY] * ftmp;
924 fOmega[CHZ] = rvecdeg[CHZ] * ftmp;
925
926 // set LPq[n] = LPq[n-1] . deltaq[n]
927 qAeqAxB(pLPq, &fdeltaq);
928
929 // renormalize the low pass filtered quaternion to prevent error accumulation.
930 // the renormalization function also ensures that q0 is non-negative
931 fqAeqNormqA(pLPq);
932
933 return;
934}
935
936// function compute the quaternion product qA * qB
937void qAeqBxC(Quaternion *pqA, const Quaternion *pqB, const Quaternion *pqC)
938{
939 pqA->q0 = pqB->q0 * pqC->q0 - pqB->q1 * pqC->q1 - pqB->q2 * pqC->q2 - pqB->q3 * pqC->q3;
940 pqA->q1 = pqB->q0 * pqC->q1 + pqB->q1 * pqC->q0 + pqB->q2 * pqC->q3 - pqB->q3 * pqC->q2;
941 pqA->q2 = pqB->q0 * pqC->q2 - pqB->q1 * pqC->q3 + pqB->q2 * pqC->q0 + pqB->q3 * pqC->q1;
942 pqA->q3 = pqB->q0 * pqC->q3 + pqB->q1 * pqC->q2 - pqB->q2 * pqC->q1 + pqB->q3 * pqC->q0;
943
944 return;
945}
946
947// function compute the quaternion product qA = qA * qB
948void qAeqAxB(Quaternion *pqA, const Quaternion *pqB)
949{
950 Quaternion qProd;
951
952 // perform the quaternion product
953 qProd.q0 = pqA->q0 * pqB->q0 - pqA->q1 * pqB->q1 - pqA->q2 * pqB->q2 - pqA->q3 * pqB->q3;
954 qProd.q1 = pqA->q0 * pqB->q1 + pqA->q1 * pqB->q0 + pqA->q2 * pqB->q3 - pqA->q3 * pqB->q2;
955 qProd.q2 = pqA->q0 * pqB->q2 - pqA->q1 * pqB->q3 + pqA->q2 * pqB->q0 + pqA->q3 * pqB->q1;
956 qProd.q3 = pqA->q0 * pqB->q3 + pqA->q1 * pqB->q2 - pqA->q2 * pqB->q1 + pqA->q3 * pqB->q0;
957
958 // copy the result back into qA
959 *pqA = qProd;
960
961 return;
962}
963
964// function compute the quaternion product conjg(qA) * qB
966{
967 Quaternion qProd;
968
969 qProd.q0 = pqA->q0 * pqB->q0 + pqA->q1 * pqB->q1 + pqA->q2 * pqB->q2 + pqA->q3 * pqB->q3;
970 qProd.q1 = pqA->q0 * pqB->q1 - pqA->q1 * pqB->q0 - pqA->q2 * pqB->q3 + pqA->q3 * pqB->q2;
971 qProd.q2 = pqA->q0 * pqB->q2 + pqA->q1 * pqB->q3 - pqA->q2 * pqB->q0 - pqA->q3 * pqB->q1;
972 qProd.q3 = pqA->q0 * pqB->q3 - pqA->q1 * pqB->q2 + pqA->q2 * pqB->q1 - pqA->q3 * pqB->q0;
973
974 return qProd;
975}
976
977// function normalizes a rotation quaternion and ensures q0 is non-negative
979{
980 float fNorm; // quaternion Norm
981
982 // calculate the quaternion Norm
983 fNorm = sqrtf(pqA->q0 * pqA->q0 + pqA->q1 * pqA->q1 + pqA->q2 * pqA->q2 + pqA->q3 * pqA->q3);
984 if (fNorm > CORRUPTQUAT)
985 {
986 // general case
987 fNorm = 1.0F / fNorm;
988 pqA->q0 *= fNorm;
989 pqA->q1 *= fNorm;
990 pqA->q2 *= fNorm;
991 pqA->q3 *= fNorm;
992 }
993 else
994 {
995 // return with identity quaternion since the quaternion is corrupted
996 pqA->q0 = 1.0F;
997 pqA->q1 = pqA->q2 = pqA->q3 = 0.0F;
998 }
999
1000 // correct a negative scalar component if the function was called with negative q0
1001 if (pqA->q0 < 0.0F)
1002 {
1003 pqA->q0 = -pqA->q0;
1004 pqA->q1 = -pqA->q1;
1005 pqA->q2 = -pqA->q2;
1006 pqA->q3 = -pqA->q3;
1007 }
1008
1009 return;
1010}
1011
1012// set a quaternion to the unit quaternion
1014{
1015 pqA->q0 = 1.0F;
1016 pqA->q1 = pqA->q2 = pqA->q3 = 0.0F;
1017
1018 return;
1019}
1020
1021// function computes the rotation quaternion that rotates unit vector u onto unit vector v as v=q*.u.q
1022// using q = 1/sqrt(2) * {sqrt(1 + u.v) - u x v / sqrt(1 + u.v)}
1023void fveqconjgquq(Quaternion *pfq, float fu[], float fv[])
1024{
1025 float fuxv[3]; // vector product u x v
1026 float fsqrt1plusudotv; // sqrt(1 + u.v)
1027 float ftmp; // scratch
1028
1029 // compute sqrt(1 + u.v) and scalar quaternion component q0 (valid for all angles including 180 deg)
1030 fsqrt1plusudotv = sqrtf(fabsf(1.0F + fu[CHX] * fv[CHX] + fu[CHY] * fv[CHY] + fu[CHZ] * fv[CHZ]));
1031 pfq->q0 = ONEOVERSQRT2 * fsqrt1plusudotv;
1032
1033 // calculate the vector product uxv
1034 fuxv[CHX] = fu[CHY] * fv[CHZ] - fu[CHZ] * fv[CHY];
1035 fuxv[CHY] = fu[CHZ] * fv[CHX] - fu[CHX] * fv[CHZ];
1036 fuxv[CHZ] = fu[CHX] * fv[CHY] - fu[CHY] * fv[CHX];
1037
1038 // compute the vector component of the quaternion
1039 if (fsqrt1plusudotv != 0.0F)
1040 {
1041 // general case where u and v are not anti-parallel where u.v=-1
1042 ftmp = ONEOVERSQRT2 / fsqrt1plusudotv;
1043 pfq->q1 = -fuxv[CHX] * ftmp;
1044 pfq->q2 = -fuxv[CHY] * ftmp;
1045 pfq->q3 = -fuxv[CHZ] * ftmp;
1046 }
1047 else
1048 {
1049 // degenerate case where u and v are anti-aligned and the 180 deg rotation quaternion is not uniquely defined.
1050 // first calculate the un-normalized vector component (the scalar component q0 is already set to zero)
1051 pfq->q1 = fu[CHY] - fu[CHZ];
1052 pfq->q2 = fu[CHZ] - fu[CHX];
1053 pfq->q3 = fu[CHX] - fu[CHY];
1054 // and normalize the quaternion for this case checking for fu[CHX]=fu[CHY]=fuCHZ] where q1=q2=q3=0.
1055 ftmp = sqrtf(fabsf(pfq->q1 * pfq->q1 + pfq->q2 * pfq->q2 + pfq->q3 * pfq->q3));
1056 if (ftmp != 0.0F)
1057 {
1058 // normal case where all three components of fu (and fv=-fu) are not equal
1059 ftmp = 1.0F / ftmp;
1060 pfq->q1 *= ftmp;
1061 pfq->q2 *= ftmp;
1062 pfq->q3 *= ftmp;
1063 }
1064 else
1065 {
1066 // final case where the three entries are equal but since the vectors are known to be normalized
1067 // the vector u must be 1/root(3)*{1, 1, 1} or -1/root(3)*{1, 1, 1} so simply set the
1068 // rotation vector to 1/root(2)*{1, -1, 0} to cover both cases
1069 pfq->q1 = ONEOVERSQRT2;
1070 pfq->q2 = -ONEOVERSQRT2;
1071 pfq->q3 = 0.0F;
1072 }
1073 }
1074
1075 return;
1076}
Math approximations file.
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition matrix.c:26
void f3x3matrixAeqScalar(float A[][3], float Scalar)
function sets every entry in the 3x3 matrix A to a constant scalar
Definition matrix.c:90
Matrix manipulation functions.
void fQuaternionFromRotationMatrix(float R[][3], Quaternion *pq)
compute the orientation quaternion from a 3x3 rotation matrix
void qAeqBxC(Quaternion *pqA, const Quaternion *pqB, const Quaternion *pqC)
function compute the quaternion product qB * qC
void fWin8AnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the Windows 8 angles in degrees from the Windows 8 rotation matrix
void qAeqAxB(Quaternion *pqA, const Quaternion *pqB)
function compute the quaternion product qA = qA * qB
void f3DOFMagnetometerMatrixNED(float fR[][3], float fBc[])
Aerospace NED magnetometer 3DOF flat eCompass function, computing rotation matrix fR.
void fqAeqNormqA(Quaternion *pqA)
function normalizes a rotation quaternion and ensures q0 is non-negative
void feCompassAndroid(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
Android: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle f...
void fveqconjgquq(Quaternion *pfq, float fu[], float fv[])
void fqAeq1(Quaternion *pqA)
set a quaternion to the unit quaternion
void f3DOFMagnetometerMatrixAndroid(float fR[][3], float fBc[])
Android magnetometer 3DOF flat eCompass function, computing rotation matrix fR.
void fAndroidAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the Android angles in degrees from the Android rotation matrix
void f3DOFMagnetometerMatrixWin8(float fR[][3], float fBc[])
Windows 8 magnetometer 3DOF flat eCompass function, computing rotation matrix fR.
void fQuaternionFromRotationVectorDeg(Quaternion *pq, const float rvecdeg[], float fscaling)
computes normalized rotation quaternion from a rotation vector (deg)
void fRotationVectorDegFromQuaternion(Quaternion *pq, float rvecdeg[])
computes rotation vector (deg) from rotation quaternion
void fNEDAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the NED angles in degrees from the NED rotation matrix
void feCompassWin8(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
Win8: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle fDel...
void fRotationMatrixFromQuaternion(float R[][3], const Quaternion *pq)
compute the rotation matrix from an orientation quaternion
void fLPFOrientationQuaternion(Quaternion *pq, Quaternion *pLPq, float flpf, float fdeltat, float fOmega[])
function low pass filters an orientation quaternion and computes virtual gyro rotation rate
Quaternion qconjgAxB(const Quaternion *pqA, const Quaternion *pqB)
function compute the quaternion product conjg(qA) * qB
void feCompassNED(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
NED: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle fDelt...
Functions to convert between various orientation representations.
void f3DOFTiltWin8(float fR[][3], float fGc[])
Windows 8 accelerometer 3DOF tilt function computing, rotation matrix fR.
void f3DOFTiltAndroid(float fR[][3], float fGc[])
Android accelerometer 3DOF tilt function computing, rotation matrix fR.
void f3DOFTiltNED(float fR[][3], float fGc[])
Aerospace NED accelerometer 3DOF tilt function, computing rotation matrix fR.
The sensor_fusion.h file implements the top level programming interface.
#define ONEOVER3840
1 / 3840
#define ONEOVERSQRT2
1/sqrt(2)
#define CHX
Used to access X-channel entries in various data data structures.
#define CHY
Used to access Y-channel entries in various data data structures.
#define CHZ
Used to access Z-channel entries in various data data structures.
#define F180OVERPI
radians to degrees conversion = 180 / pi
#define FPIOVER180
degrees to radians conversion = pi / 180
#define ONEOVER48
1 / 48
quaternion structure definition
Definition orientation.h:25
float q3
z vector component
Definition orientation.h:29
float q0
scalar component
Definition orientation.h:26
float q1
x vector component
Definition orientation.h:27
float q2
y vector component
Definition orientation.h:28