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