ISSDK  1.7
IoT Sensing Software Development Kit
precisionAccelerometer.c
Go to the documentation of this file.
1 /*
2  * The Clear BSD License
3  * Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc.
4  * Copyright 2016-2017 NXP
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without modification,
8  * are permitted (subject to the limitations in the disclaimer below) provided
9  * that the following conditions are met:
10  *
11  * o Redistributions of source code must retain the above copyright notice, this list
12  * of conditions and the following disclaimer.
13  *
14  * o Redistributions in binary form must reproduce the above copyright notice, this
15  * list of conditions and the following disclaimer in the documentation and/or
16  * other materials provided with the distribution.
17  *
18  * o Neither the name of the copyright holder nor the names of its
19  * contributors may be used to endorse or promote products derived from this
20  * software without specific prior written permission.
21  *
22  * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
27  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <stdio.h>
36 #include "sensor_fusion.h"
37 #include "fusion.h"
38 
39 /*! \file precisionAccelerometer.c
40  \brief Implements accelerometer calibration routines
41 */
42 
43 // function resets the accelerometer buffer and accelerometer calibration
44 void fInitializeAccelCalibration(struct AccelCalibration *pthisAccelCal,
45  struct AccelBuffer *pthisAccelBuffer,
46  volatile int8 *AccelCalPacketOn)
47 {
48  float *pFlash; // pointer into flash memory
49  int8_t i,
50  j; // loop counters
51 
52  // set flags to false to denote no precision accelerometer measurements
53  pthisAccelBuffer->iStoreFlags = 0;
54 
55  // perform one transmission of the precision calibration (using invalid value MAX_ACCEL_CAL_ORIENTATIONS for calibration only)
56  *AccelCalPacketOn = MAX_ACCEL_CAL_ORIENTATIONS;
57 
58  // check to see if the stored accelerometer calibration has been erased
59  // the standard value for erased flash is 0xFF in each byte but for portability check against 0x12345678
60 #ifndef SIMULATION
61  pFlash = (float *) (CALIBRATION_NVM_ADDR + ACCEL_NVM_OFFSET);
62  if (*((uint32 *) pFlash++) == 0x12345678)
63  {
64  // a precision accelerometer calibration is present in flash
65  // copy accelerometer calibration elements (21x float total 84 bytes) from flash to RAM
66  for (i = CHX; i <= CHZ; i++) pthisAccelCal->fV[i] = *(pFlash++);
67  for (i = CHX; i <= CHZ; i++)
68  for (j = CHX; j <= CHZ; j++)
69  pthisAccelCal->finvW[i][j] = *(pFlash++);
70  for (i = CHX; i <= CHZ; i++)
71  for (j = CHX; j <= CHZ; j++)
72  pthisAccelCal->fR0[i][j] = *(pFlash++);
73  }
74  else
75  {
76 #endif
77  // flash has been erased and no accelerometer calibration is present
78  // initialize the precision accelerometer calibration in RAM to null default
79  pthisAccelCal->fV[CHX] = pthisAccelCal->fV[CHY] = pthisAccelCal->fV[CHZ] = 0.0F;
80  f3x3matrixAeqI(pthisAccelCal->finvW);
81  f3x3matrixAeqI(pthisAccelCal->fR0);
82 #ifndef SIMULATION
83  }
84 #endif
85 
86  // set current averaging location and counter to -1 for invalid
87  pthisAccelBuffer->iStoreLocation = pthisAccelBuffer->iStoreCounter = -1;
88 
89  return;
90 }
91 
92 void fUpdateAccelBuffer(struct AccelCalibration *pthisAccelCal,
93  struct AccelBuffer *pthisAccelBuffer,
94  struct AccelSensor *pthisAccel, volatile int8 *AccelCalPacketOn)
95 {
96  int16_t i; // loop counter
97 
98  // iStoreCounter > 0: precision measurements are still on-going
99  // iStoreCounter = 0: the precision measurement has just finished
100  // iStoreCounter = -1: no precision measurement is in progress
101  // check if a new precision measurement has started and zero sums if one has
102  if (pthisAccelBuffer->iStoreCounter == (ACCEL_CAL_AVERAGING_SECS * FUSION_HZ))
103  {
104  for (i = CHX; i <= CHZ; i++) pthisAccelBuffer->fSumGs[i] = 0.0F;
105  }
106 
107  // accumulate sum if averaging not yet complete
108  if (pthisAccelBuffer->iStoreCounter > 0)
109  {
110  for (i = CHX; i <= CHZ; i++)
111  pthisAccelBuffer->fSumGs[i] += pthisAccel->fGs[i];
112  pthisAccelBuffer->iStoreCounter--;
113  }
114 
115  // check if the measurement accumulation is complete and, if so, store average and set packet transmit flag
116  if (pthisAccelBuffer->iStoreCounter == 0)
117  {
118  // store the measurement, set the relevant flag and decrement the counter down to -1 denoting completion
119  for (i = CHX; i <= CHZ; i++)
120  {
121  pthisAccelBuffer->fGsStored[pthisAccelBuffer->iStoreLocation][i] =
122  pthisAccelBuffer->fSumGs[i] /
123  (float) (ACCEL_CAL_AVERAGING_SECS * FUSION_HZ);
124  }
125 
126  pthisAccelBuffer->iStoreFlags |=
127  (
128  1 <<
129  pthisAccelBuffer->iStoreLocation
130  );
131  pthisAccelBuffer->iStoreCounter--;
132 
133  // compute the new precision accelerometer calibration including rotation using all measurements
134  fRunAccelCalibration(pthisAccelCal, pthisAccelBuffer, pthisAccel);
135 
136  // and make one packet transmission of this measurement with the new calibration
137  *AccelCalPacketOn = pthisAccelBuffer->iStoreLocation;
138  }
139 
140  return;
141 }
142 
143 // function maps the accelerometer data fGs (g) onto precision calibrated and de-rotated data fGc (g), iGc (counts)
144 void fInvertAccelCal(struct AccelSensor *pthisAccel,
145  struct AccelCalibration *pthisAccelCal)
146 {
147  // local variables
148  float ftmp[3]; // temporary array
149  int8_t i; // loop counter
150 
151  //subtract the offset vector fV (g): ftmp[]=fGs[]-V[]
152  for (i = CHX; i <= CHZ; i++)
153  ftmp[i] = pthisAccel->fGs[i] - pthisAccelCal->fV[i];
154 
155  // apply the inverse rotation correction matrix finvW: fGc=inv(W)*(fGs[]-V[])
156  for (i = CHX; i <= CHZ; i++)
157  {
158  pthisAccel->fGc[i] = pthisAccelCal->finvW[i][CHX] *
159  ftmp[CHX] +
160  pthisAccelCal->finvW[i][CHY] *
161  ftmp[CHY] +
162  pthisAccelCal->finvW[i][CHZ] *
163  ftmp[CHZ];
164  }
165 
166  // apply the inverse of the forward rotation matrix fR0: fGc=inv(R).inv(W)*(fGs[]-V[])
167  for (i = CHX; i <= CHZ; i++) ftmp[i] = pthisAccel->fGc[i];
168  for (i = CHX; i <= CHZ; i++)
169  {
170  pthisAccel->fGc[i] = pthisAccelCal->fR0[CHX][i] *
171  ftmp[CHX] +
172  pthisAccelCal->fR0[CHY][i] *
173  ftmp[CHY] +
174  pthisAccelCal->fR0[CHZ][i] *
175  ftmp[CHZ];
176  pthisAccel->iGc[i] = (int16_t) (pthisAccel->fGc[i] * pthisAccel->iCountsPerg);
177  }
178 
179  return;
180 }
181 
182 // function runs the precision accelerometer calibration
183 void fRunAccelCalibration(struct AccelCalibration *pthisAccelCal,
184  struct AccelBuffer *pthisAccelBuffer,
185  struct AccelSensor *pthisAccel)
186 {
187  float fGc0[3]; // calibrated but not de-rotated measurement 0
188  uint8_t iMeasurements; // number of stored measurements
189  int8_t i; // loop counters
190 
191  // calculate how many measurements are present in the accelerometer measurement buffer
192  iMeasurements = 0;
193  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
194  {
195  if (pthisAccelBuffer->iStoreFlags & (1 << i)) iMeasurements++;
196  }
197 
198  // perform the highest quality calibration possible given this number
199  if (iMeasurements >= 9)
200  {
201  // perform the 10 element calibration
202  fComputeAccelCalibration10(pthisAccelBuffer, pthisAccelCal, pthisAccel);
203  }
204  else if (iMeasurements >= 6)
205  {
206  // perform the 7 element calibration
207  fComputeAccelCalibration7(pthisAccelBuffer, pthisAccelCal, pthisAccel);
208  }
209  else if (iMeasurements >= 4)
210  {
211  // perform the 4 element calibration
212  fComputeAccelCalibration4(pthisAccelBuffer, pthisAccelCal, pthisAccel);
213  }
214 
215  // calculate the rotation correction matrix to rotate calibrated measurement 0 to flat
216  if (pthisAccelBuffer->iStoreFlags & 1)
217  {
218  // apply offset and gain calibration but not rotation to measurement 0 (flat) if present
219  // set ftmpA3x1 = invW . (fGs - fV)
220  for (i = CHX; i <= CHZ; i++)
221  fGc0[i] = pthisAccelBuffer->fGsStored[0][i] - pthisAccelCal->fV[i];
222  fVeq3x3AxV(fGc0, pthisAccelCal->finvW);
223 
224  // compute the new final rotation matrix if rotation 0 (flat) is present.
225  // multiplying by the transpose of this matrix therefore forces measurement zero to flat.
226 #if THISCOORDSYSTEM == NED
227  f3DOFTiltNED(pthisAccelCal->fR0, fGc0);
228 #endif
229 #if THISCOORDSYSTEM == ANDROID
230  f3DOFTiltAndroid(pthisAccelCal->fR0, fGc0);
231 #endif
232 #if THISCOORDSYSTEM == WIN8
233  f3DOFTiltWin8(pthisAccelCal->fR0, fGc0);
234 #endif
235  }
236 
237  return;
238 }
239 
240 // calculate the 4 element calibration from the available measurements
241 void fComputeAccelCalibration4(struct AccelBuffer *pthisAccelBuffer,
242  struct AccelCalibration *pthisAccelCal,
243  struct AccelSensor *pthisAccel)
244 {
245  int32 i,
246  j; // loop counters
247  float ftmp; // scratch
248  int8_t ierror; // flag from matrix inversion
249 
250  // working arrays for 4x4 matrix inversion
251  float *pfRows[4];
252  int8_t iColInd[4];
253  int8_t iRowInd[4];
254  int8_t iPivot[4];
255 
256  // zero the 4x4 matrix XTX (in upper left of fmatA) and 4x1 vector XTY (in upper fvecA)
257  for (i = 0; i < 4; i++)
258  {
259  pthisAccelCal->fvecA[i] = 0.0F;
260  for (j = 0; j < 4; j++)
261  {
262  pthisAccelCal->fmatA[i][j] = 0.0F;
263  }
264  }
265 
266  // accumulate fXTY (in fvecA) and fXTX4x4 (in fmatA)
267  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
268  {
269  // accumulate vector X^T.Y if this entry is valid
270  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
271  {
272  ftmp = pthisAccelBuffer->fGsStored[i][CHX] *
273  pthisAccelBuffer->fGsStored[i][CHX] +
274  pthisAccelBuffer->fGsStored[i][CHY] *
275  pthisAccelBuffer->fGsStored[i][CHY] +
276  pthisAccelBuffer->fGsStored[i][CHZ] *
277  pthisAccelBuffer->fGsStored[i][CHZ];
278  pthisAccelCal->fvecA[0] += pthisAccelBuffer->fGsStored[i][CHX] * ftmp;
279  pthisAccelCal->fvecA[1] += pthisAccelBuffer->fGsStored[i][CHY] * ftmp;
280  pthisAccelCal->fvecA[2] += pthisAccelBuffer->fGsStored[i][CHZ] * ftmp;
281  pthisAccelCal->fvecA[3] += ftmp;
282 
283  // accumulate above diagonal terms of matrix X^T.X
284  pthisAccelCal->fmatA[CHX][CHX] += pthisAccelBuffer->fGsStored[i][
285  CHX] *
286  pthisAccelBuffer->fGsStored[i][CHX];
287  pthisAccelCal->fmatA[CHX][CHY] += pthisAccelBuffer->fGsStored[i][
288  CHX] *
289  pthisAccelBuffer->fGsStored[i][CHY];
290  pthisAccelCal->fmatA[CHX][CHZ] += pthisAccelBuffer->fGsStored[i][
291  CHX] *
292  pthisAccelBuffer->fGsStored[i][CHZ];
293  pthisAccelCal->fmatA[CHX][3] += pthisAccelBuffer->fGsStored[i][CHX];
294  pthisAccelCal->fmatA[CHY][CHY] += pthisAccelBuffer->fGsStored[i][
295  CHY] *
296  pthisAccelBuffer->fGsStored[i][CHY];
297  pthisAccelCal->fmatA[CHY][CHZ] += pthisAccelBuffer->fGsStored[i][
298  CHY] *
299  pthisAccelBuffer->fGsStored[i][CHZ];;
300  pthisAccelCal->fmatA[CHY][3] += pthisAccelBuffer->fGsStored[i][CHY];
301  pthisAccelCal->fmatA[CHZ][CHZ] += pthisAccelBuffer->fGsStored[i][
302  CHZ] *
303  pthisAccelBuffer->fGsStored[i][CHZ];;
304  pthisAccelCal->fmatA[CHZ][3] += pthisAccelBuffer->fGsStored[i][CHZ];
305  pthisAccelCal->fmatA[3][3] += 1.0F;
306  }
307  }
308 
309  // copy above diagonal elements of fXTX4x4 = X^T.X to below diagonal elements
310  pthisAccelCal->fmatA[CHY][CHX] = pthisAccelCal->fmatA[CHX][CHY];
311  pthisAccelCal->fmatA[CHZ][CHX] = pthisAccelCal->fmatA[CHX][CHZ];
312  pthisAccelCal->fmatA[CHZ][CHY] = pthisAccelCal->fmatA[CHY][CHZ];
313  pthisAccelCal->fmatA[3][CHX] = pthisAccelCal->fmatA[CHX][3];
314  pthisAccelCal->fmatA[3][CHY] = pthisAccelCal->fmatA[CHY][3];
315  pthisAccelCal->fmatA[3][CHZ] = pthisAccelCal->fmatA[CHZ][3];
316 
317  // calculate in situ inverse of X^T.X
318  for (i = 0; i < 4; i++)
319  {
320  pfRows[i] = pthisAccelCal->fmatA[i];
321  }
322 
323  fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4, &ierror);
324 
325  // calculate the solution vector fvecB = inv(X^T.X).X^T.Y
326  for (i = 0; i < 4; i++)
327  {
328  pthisAccelCal->fvecB[i] = 0.0F;
329  for (j = 0; j < 4; j++)
330  {
331  pthisAccelCal->fvecB[i] += pthisAccelCal->fmatA[i][j] * pthisAccelCal->fvecA[j];
332  }
333  }
334 
335  // extract the offset vector
336  pthisAccelCal->fV[CHX] = 0.5F * pthisAccelCal->fvecB[CHX];
337  pthisAccelCal->fV[CHY] = 0.5F * pthisAccelCal->fvecB[CHY];
338  pthisAccelCal->fV[CHZ] = 0.5F * pthisAccelCal->fvecB[CHZ];
339 
340  // set ftmp to 1/W where W is the forward gain to fit the 1g sphere
341  ftmp = 1.0F / sqrtf(fabsf(pthisAccelCal->fvecB[3] + pthisAccelCal->fV[CHX] *
342  pthisAccelCal->fV[CHX] + pthisAccelCal->fV[CHY] *
343  pthisAccelCal->fV[CHY] + pthisAccelCal->fV[CHZ] *
344  pthisAccelCal->fV[CHZ]));
345 
346  // copy the inverse gain 1/W to the inverse gain matrix
347  pthisAccelCal->finvW[CHX][CHY] = pthisAccelCal->finvW[CHY][CHX] = 0.0F;
348  pthisAccelCal->finvW[CHX][CHZ] = pthisAccelCal->finvW[CHZ][CHX] = 0.0F;
349  pthisAccelCal->finvW[CHY][CHZ] = pthisAccelCal->finvW[CHZ][CHY] = 0.0F;
350  pthisAccelCal->finvW[CHX][CHX] = pthisAccelCal->finvW[CHY][CHY] = pthisAccelCal->finvW[CHZ][CHZ] = ftmp;
351 
352  return;
353 }
354 
355 // calculate the 7 element calibration from the available measurements
356 void fComputeAccelCalibration7(struct AccelBuffer *pthisAccelBuffer,
357  struct AccelCalibration *pthisAccelCal,
358  struct AccelSensor *pthisAccel)
359 {
360  int32 i,
361  j,
362  m,
363  n; // loop counters
364  float det; // matrix determinant
365  float fg0; // fitted local gravity magnitude
366 
367  // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix fmatA
368  for (i = 0; i < 7; i++)
369  for (j = i; j < 7; j++) pthisAccelCal->fmatA[i][j] = 0.0F;
370 
371  // last entry of vector fvecA is always 1.0 so move assignment outside the loop
372  pthisAccelCal->fvecA[6] = 1.0F;
373 
374  // loop over all orientations
375  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
376  {
377  // accumulate fvecA if this entry is valid
378  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
379  {
380  // compute the remaining members of the measurement vector fvecA
381  pthisAccelCal->fvecA[0] = pthisAccelBuffer->fGsStored[i][CHX] * pthisAccelBuffer->fGsStored[i][CHX];
382  pthisAccelCal->fvecA[1] = pthisAccelBuffer->fGsStored[i][CHY] * pthisAccelBuffer->fGsStored[i][CHY];
383  pthisAccelCal->fvecA[2] = pthisAccelBuffer->fGsStored[i][CHZ] * pthisAccelBuffer->fGsStored[i][CHZ];
384  pthisAccelCal->fvecA[3] = pthisAccelBuffer->fGsStored[i][CHX];
385  pthisAccelCal->fvecA[4] = pthisAccelBuffer->fGsStored[i][CHY];
386  pthisAccelCal->fvecA[5] = pthisAccelBuffer->fGsStored[i][CHZ];
387 
388  // accumulate the on-and above-diagonal terms of fmatA=Sigma{fvecA^T * fvecA}
389  for (m = 0; m < 7; m++)
390  for (n = m; n < 7; n++)
391  pthisAccelCal->fmatA[m][n] += pthisAccelCal->fvecA[m] * pthisAccelCal->fvecA[n];
392  } // end of test for stored data
393  } // end of loop over orientations
394 
395  // copy the above diagonal elements of symmetric product matrix fmatA to below the diagonal
396  for (m = 1; m < 7; m++)
397  for (n = 0; n < m; n++)
398  pthisAccelCal->fmatA[m][n] = pthisAccelCal->fmatA[n][m];
399 
400  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted normalized eigenvectors of fmatA
401  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
402  pthisAccelCal->fmatB, 7);
403 
404  // set ellipsoid matrix A from elements of the solution vector column j with smallest eigenvalue
405  j = 0;
406  for (i = 1; i < 7; i++)
407  if (pthisAccelCal->fvecA[i] < pthisAccelCal->fvecA[j]) j = i;
408 
409  // negate the entire solution vector if ellipsoid matrix has negative determinant
410  det = pthisAccelCal->fmatB[0][j] *
411  pthisAccelCal->fmatB[1][j] *
412  pthisAccelCal->fmatB[2][j];
413  if (det < 0.0F)
414  {
415  for (i = 0; i < 7; i++)
416  {
417  pthisAccelCal->fmatB[i][j] = -pthisAccelCal->fmatB[i][j];
418  }
419  }
420 
421  // compute invW and V and fitted gravity g0 from solution vector j
422  f3x3matrixAeqScalar(pthisAccelCal->finvW, 0.0F);
423  pthisAccelCal->finvW[CHX][CHX] = sqrtf(fabsf(pthisAccelCal->fmatB[0][j]));
424  pthisAccelCal->finvW[CHY][CHY] = sqrtf(fabsf(pthisAccelCal->fmatB[1][j]));
425  pthisAccelCal->finvW[CHZ][CHZ] = sqrtf(fabsf(pthisAccelCal->fmatB[2][j]));
426  pthisAccelCal->fV[CHX] = -0.5F *
427  pthisAccelCal->fmatB[3][j] /
428  pthisAccelCal->fmatB[0][j];
429  pthisAccelCal->fV[CHY] = -0.5F *
430  pthisAccelCal->fmatB[4][j] /
431  pthisAccelCal->fmatB[1][j];
432  pthisAccelCal->fV[CHZ] = -0.5F *
433  pthisAccelCal->fmatB[5][j] /
434  pthisAccelCal->fmatB[2][j];
435  fg0 = sqrtf(fabsf(pthisAccelCal->fmatB[0][j] * pthisAccelCal->fV[CHX] *
436  pthisAccelCal->fV[CHX] + pthisAccelCal->fmatB[1][j] *
437  pthisAccelCal->fV[CHY] * pthisAccelCal->fV[CHY] +
438  pthisAccelCal->fmatB[2][j] * pthisAccelCal->fV[CHZ] *
439  pthisAccelCal->fV[CHZ] - pthisAccelCal->fmatB[6][j]));
440 
441  // normalize invW to fit the 1g sphere
442  pthisAccelCal->finvW[CHX][CHX] /= fg0;
443  pthisAccelCal->finvW[CHY][CHY] /= fg0;
444  pthisAccelCal->finvW[CHZ][CHZ] /= fg0;
445 
446  return;
447 }
448 
449 // calculate the 10 element calibration from the available measurements
450 void fComputeAccelCalibration10(struct AccelBuffer *pthisAccelBuffer,
451  struct AccelCalibration *pthisAccelCal,
452  struct AccelSensor *pthisAccel)
453 {
454  int32 i,
455  j,
456  k,
457  l,
458  m,
459  n; // loop counters
460  float det; // matrix determinant
461  float ftmp; // scratch
462  float fg0; // fitted local gravity magnitude
463 
464  // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix fmatA
465  for (i = 0; i < 10; i++)
466  for (j = i; j < 10; j++) pthisAccelCal->fmatA[i][j] = 0.0F;
467 
468  // last entry of vector fvecA is always 1.0 so move assignment outside the loop
469  pthisAccelCal->fvecA[9] = 1.0F;
470 
471  // loop over all orientations
472  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
473  {
474  // accumulate fvecA if this entry is valid
475  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
476  {
477  // compute the remaining members of the measurement vector fvecA
478  pthisAccelCal->fvecA[6] = pthisAccelBuffer->fGsStored[i][CHX];
479  pthisAccelCal->fvecA[7] = pthisAccelBuffer->fGsStored[i][CHY];
480  pthisAccelCal->fvecA[8] = pthisAccelBuffer->fGsStored[i][CHZ];
481  pthisAccelCal->fvecA[0] = pthisAccelCal->fvecA[6] * pthisAccelCal->fvecA[6];
482  pthisAccelCal->fvecA[1] = 2.0F *
483  pthisAccelCal->fvecA[6] *
484  pthisAccelCal->fvecA[7];
485  pthisAccelCal->fvecA[2] = 2.0F *
486  pthisAccelCal->fvecA[6] *
487  pthisAccelCal->fvecA[8];
488  pthisAccelCal->fvecA[3] = pthisAccelCal->fvecA[7] * pthisAccelCal->fvecA[7];
489  pthisAccelCal->fvecA[4] = 2.0F *
490  pthisAccelCal->fvecA[7] *
491  pthisAccelCal->fvecA[8];
492  pthisAccelCal->fvecA[5] = pthisAccelCal->fvecA[8] * pthisAccelCal->fvecA[8];
493 
494  // accumulate the on-and above-diagonal terms of fmatA=Sigma{fvecA^T * fvecA}
495  for (m = 0; m < 10; m++)
496  {
497  for (n = m; n < 10; n++)
498  {
499  pthisAccelCal->fmatA[m][n] += pthisAccelCal->fvecA[m] * pthisAccelCal->fvecA[n];
500  }
501  }
502  } // end of test for stored data
503  } // end of loop over orientations
504 
505  // copy the above diagonal elements of symmetric product matrix fmatA to below the diagonal
506  for (m = 1; m < 10; m++)
507  for (n = 0; n < m; n++)
508  pthisAccelCal->fmatA[m][n] = pthisAccelCal->fmatA[n][m];
509 
510  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted normalized eigenvectors of fmatA
511  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
512  pthisAccelCal->fmatB, 10);
513 
514  // set ellipsoid matrix A from elements of the solution vector column j with smallest eigenvalue
515  j = 0;
516  for (i = 1; i < 10; i++)
517  {
518  if (pthisAccelCal->fvecA[i] < pthisAccelCal->fvecA[j])
519  {
520  j = i;
521  }
522  }
523 
524  pthisAccelCal->fA[0][0] = pthisAccelCal->fmatB[0][j];
525  pthisAccelCal->fA[0][1] = pthisAccelCal->fA[1][0] = pthisAccelCal->fmatB[1][j];
526  pthisAccelCal->fA[0][2] = pthisAccelCal->fA[2][0] = pthisAccelCal->fmatB[2][j];
527  pthisAccelCal->fA[1][1] = pthisAccelCal->fmatB[3][j];
528  pthisAccelCal->fA[1][2] = pthisAccelCal->fA[2][1] = pthisAccelCal->fmatB[4][j];
529  pthisAccelCal->fA[2][2] = pthisAccelCal->fmatB[5][j];
530 
531  // negate entire solution if A has negative determinant
532  det = f3x3matrixDetA(pthisAccelCal->fA);
533  if (det < 0.0F)
534  {
535  f3x3matrixAeqMinusA(pthisAccelCal->fA);
536  pthisAccelCal->fmatB[6][j] = -pthisAccelCal->fmatB[6][j];
537  pthisAccelCal->fmatB[7][j] = -pthisAccelCal->fmatB[7][j];
538  pthisAccelCal->fmatB[8][j] = -pthisAccelCal->fmatB[8][j];
539  pthisAccelCal->fmatB[9][j] = -pthisAccelCal->fmatB[9][j];
540  det = -det;
541  }
542 
543  // compute the inverse of the ellipsoid matrix
544  f3x3matrixAeqInvSymB(pthisAccelCal->finvA, pthisAccelCal->fA);
545 
546  // compute the offset vector V
547  for (l = CHX; l <= CHZ; l++)
548  {
549  pthisAccelCal->fV[l] = 0.0F;
550  for (m = CHX; m <= CHZ; m++)
551  {
552  pthisAccelCal->fV[l] += pthisAccelCal->finvA[l][m] * pthisAccelCal->fmatB[m + 6][j];
553  }
554 
555  pthisAccelCal->fV[l] *= -0.5F;
556  }
557 
558  // compute the local gravity fit to these calibration coefficients
559  fg0 = sqrtf(fabsf(pthisAccelCal->fA[0][0] * pthisAccelCal->fV[CHX] *
560  pthisAccelCal->fV[CHX] + 2.0F * pthisAccelCal->fA[0][1] *
561  pthisAccelCal->fV[CHX] * pthisAccelCal->fV[CHY] + 2.0F *
562  pthisAccelCal->fA[0][2] * pthisAccelCal->fV[CHX] *
563  pthisAccelCal->fV[CHZ] + pthisAccelCal->fA[1][1] *
564  pthisAccelCal->fV[CHY] * pthisAccelCal->fV[CHY] + 2.0F *
565  pthisAccelCal->fA[1][2] * pthisAccelCal->fV[CHY] *
566  pthisAccelCal->fV[CHZ] + pthisAccelCal->fA[2][2] *
567  pthisAccelCal->fV[CHZ] * pthisAccelCal->fV[CHZ] -
568  pthisAccelCal->fmatB[9][j]));
569 
570  // compute trial invW from the square root of fA
571  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted eigenvectors of fmatA
572  // where fmatA holds the 3x3 matrix fA in its top left elements
573  for (i = 0; i < 3; i++)
574  for (j = 0; j < 3; j++)
575  pthisAccelCal->fmatA[i][j] = pthisAccelCal->fA[i][j];
576  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
577  pthisAccelCal->fmatB, 3);
578 
579  // set fmatB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) = fmatB . diag(sqrt(sqrt(fvecA))
580  for (j = 0; j < 3; j++) // loop over columns j
581  {
582  ftmp = sqrtf(sqrtf(fabsf(pthisAccelCal->fvecA[j])));
583  for (i = 0; i < 3; i++) // loop over rows i
584  {
585  pthisAccelCal->fmatB[i][j] *= ftmp;
586  }
587  }
588 
589  // set finvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T
590  // = fmatB * fmatB^T = sqrt(fA) (guaranteed symmetric)
591  // loop over rows
592  for (i = 0; i < 3; i++)
593  {
594  // loop over on and above diagonal columns
595  for (j = i; j < 3; j++)
596  {
597  pthisAccelCal->finvW[i][j] = 0.0F;
598 
599  // accumulate the matrix product
600  for (k = 0; k < 3; k++)
601  {
602  pthisAccelCal->finvW[i][j] += pthisAccelCal->fmatB[i][k] * pthisAccelCal->fmatB[j][k];
603  }
604 
605  // copy to below diagonal element
606  pthisAccelCal->finvW[j][i] = pthisAccelCal->finvW[i][j];
607  }
608  }
609 
610  // scale finvW so that the calibrated measurements fit on the 1g sphere
611  for (i = CHX; i <= CHZ; i++)
612  {
613  for (j = CHX; j <= CHZ; j++)
614  {
615  pthisAccelCal->finvW[i][j] /= fg0;
616  }
617  }
618 
619  return;
620 }
float fGc[3]
averaged precision calibrated measurement (g)
float finvA[3][3]
inverse of the ellipsoid matrix A
int16_t iStoreLocation
-1 for none, 0 to 11 for the 12 storage locations
void fmatrixAeqInvA(float *A[], int8 iColInd[], int8 iRowInd[], int8 iPivot[], int8 isize, int8 *pierror)
Definition: matrix.c:674
float fvecB[4]
scratch 4x1 vector used by calibration algorithms
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:87
void f3DOFTiltNED(float fR[][3], float fGc[])
Aerospace NED accelerometer 3DOF tilt function, computing rotation matrix fR.
#define CALIBRATION_NVM_ADDR
start of final 4K (sector size) of 1M flash
Definition: frdm_k64f.h:173
int16_t iGc[3]
averaged precision calibrated measurement (counts)
void fInitializeAccelCalibration(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, volatile int8 *AccelCalPacketOn)
Initialize the accelerometer calibration functions.
Lower level sensor fusion interface.
void fUpdateAccelBuffer(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, struct AccelSensor *pthisAccel, volatile int8 *AccelCalPacketOn)
Update the buffer used to store samples used for accelerometer calibration.
void f3x3matrixAeqMinusA(float A[][3])
function negates all elements of 3x3 matrix A
Definition: matrix.c:155
void fInvertAccelCal(struct AccelSensor *pthisAccel, struct AccelCalibration *pthisAccelCal)
function maps the accelerometer data fGs (g) onto precision calibrated and de-rotated data fGc (g)...
int16_t iCountsPerg
counts per g
#define ACCEL_CAL_AVERAGING_SECS
calibration constants
void fComputeAccelCalibration7(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 7 element calibration from the available measurements
float f3x3matrixDetA(float A[][3])
function calculates the determinant of a 3x3 matrix
Definition: matrix.c:217
float fV[3]
offset vector (g)
The AccelSensor structure stores raw and processed measurements for a 3-axis accelerometer.
int32_t int32
Definition: sensor_fusion.h:67
float fSumGs[3]
averaging sum for current storage location
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
uint32_t uint32
Definition: sensor_fusion.h:70
float finvW[3][3]
inverse gain matrix
accelerometer measurement buffer
float fmatA[10][10]
scratch 10x10 matrix used by calibration algorithms
void fEigenCompute10(float A[][10], float eigval[], float eigvec[][10], int8 n)
Definition: matrix.c:242
void fComputeAccelCalibration4(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 4 element calibration from the available measurements
void fComputeAccelCalibration10(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 10 element calibration from the available measurements
float fGs[3]
averaged measurement (g)
int16_t iStoreFlags
denotes which measurements are present
int16_t iStoreCounter
number of remaining iterations at FUSION_HZ to average measurement
The sensor_fusion.h file implements the top level programming interface.
void fVeq3x3AxV(float V[3], float A[][3])
function multiplies the 3x1 vector V by a 3x3 matrix A
Definition: matrix.c:879
#define CHZ
Used to access Z-channel entries in various data data structures.
Definition: sensor_fusion.h:88
void f3x3matrixAeqInvSymB(float A[][3], float B[][3])
Definition: matrix.c:176
precision accelerometer calibration structure
void f3DOFTiltAndroid(float fR[][3], float fGc[])
Android accelerometer 3DOF tilt function computing, rotation matrix fR.
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:86
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:53
void fRunAccelCalibration(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, struct AccelSensor *pthisAccel)
function runs the precision accelerometer calibration
#define FUSION_HZ
(int) actual rate of fusion algorithm execution and sensor FIFO reads
#define MAX_ACCEL_CAL_ORIENTATIONS
number of stored precision accelerometer measurements
float fGsStored[MAX_ACCEL_CAL_ORIENTATIONS][3]
uncalibrated accelerometer measurements (g)
#define ACCEL_NVM_OFFSET
Definition: frdm_k64f.h:181
float fmatB[10][10]
scratch 10x10 matrix used by calibration algorithms
void f3x3matrixAeqScalar(float A[][3], float Scalar)
function sets every entry in the 3x3 matrix A to a constant scalar
Definition: matrix.c:117
void f3DOFTiltWin8(float fR[][3], float fGc[])
Windows 8 accelerometer 3DOF tilt function computing, rotation matrix fR.
int8_t int8
Definition: sensor_fusion.h:65
float fA[3][3]
ellipsoid matrix A
float fR0[3][3]
forward rotation matrix for measurement 0