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