ISSDK  1.8
IoT Sensing Software Development Kit
magnetic.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 /*! \file magnetic.c
10  \brief Lower level magnetic calibration interface
11 
12  Many developers can utilize the NXP Sensor Fusion Library without ever
13  making any adjustment to the lower level magnetic calibration functions
14  defined in this file.
15 */
16 
17 #include "sensor_fusion.h"
18 #include "math.h"
19 #include "stdlib.h"
20 #include "time.h"
21 
22 #if F_USING_MAG
23 // function resets the magnetometer buffer and magnetic calibration
24 void fInitializeMagCalibration(struct MagCalibration *pthisMagCal,
25  struct MagBuffer *pthisMagBuffer)
26 {
27  float *pFlash; // pointer into flash memory
28  int8 i,
29  j; // loop counters
30 
31  // set magnetic buffer index to invalid value -1 to denote no measurement present
32  pthisMagBuffer->iMagBufferCount = 0;
33  for (i = 0; i < MAGBUFFSIZEX; i++)
34  for (j = 0; j < MAGBUFFSIZEY; j++) pthisMagBuffer->index[i][j] = -1;
35 
36  // initialize the array of (MAGBUFFSIZEX - 1) elements of 100 * tangents used for buffer indexing
37  // entries cover the range 100 * tan(-PI/2 + PI/MAGBUFFSIZEX), 100 * tan(-PI/2 + 2*PI/MAGBUFFSIZEX) to
38  // 100 * tan(-PI/2 + (MAGBUFFSIZEX - 1) * PI/MAGBUFFSIZEX).
39  // for MAGBUFFSIZEX=12, the entries range in value from -373 to +373
40  for (i = 0; i < (MAGBUFFSIZEX - 1); i++)
41  pthisMagBuffer->tanarray[i] = (int16) (100.0F * tanf(PI * (-0.5F + (float) (i + 1) / MAGBUFFSIZEX)));
42 
43  // check to see if the stored magnetic calibration has been erased
44  // the standard value for erased flash is 0xFF in each byte but for portability check against 0x12345678
45 #ifndef SIMULATION
46 
47  pFlash = (float *) (CALIBRATION_NVM_ADDR + MAG_NVM_OFFSET);
48  if (*((uint32 *) pFlash++) == 0x12345678)
49  {
50  // a magnetic calibration is present in flash
51  // copy magnetic calibration elements (15x float + 1x int32 total 64 bytes) from flash to RAM
52  for (i = CHX; i <= CHZ; i++) pthisMagCal->fV[i] = *(pFlash++);
53  for (i = CHX; i <= CHZ; i++)
54  for (j = CHX; j <= CHZ; j++)
55  pthisMagCal->finvW[i][j] = *(pFlash++);
56  pthisMagCal->fB = *(pFlash++);
57  pthisMagCal->fBSq = *(pFlash++);
58  pthisMagCal->fFitErrorpc = *(pFlash++);
59  pthisMagCal->iValidMagCal = *((int32 *) pFlash);
60  }
61  else
62  {
63 #endif
64  // flash has been erased and no magnetic calibration is present
65  // initialize the magnetic calibration in RAM to null default
66  pthisMagCal->fV[CHX] = pthisMagCal->fV[CHY] = pthisMagCal->fV[CHZ] = 0.0F;
67  f3x3matrixAeqI(pthisMagCal->finvW);
68  pthisMagCal->fB = DEFAULTB;
69  pthisMagCal->fBSq = DEFAULTB * DEFAULTB;
70  pthisMagCal->fFitErrorpc = 100.0F;
71  pthisMagCal->iValidMagCal = 0;
72 #ifndef SIMULATION
73  }
74 #endif
75 
76  // initialize remaining elements of the magnetic calibration structure
77  pthisMagCal->iCalInProgress = 0;
78  pthisMagCal->iInitiateMagCal = 0;
79  pthisMagCal->iNewCalibrationAvailable = 0;
80  pthisMagCal->iMagBufferReadOnly = false;
81  pthisMagCal->i4ElementSolverTried = false;
82  pthisMagCal->i7ElementSolverTried = false;
83  pthisMagCal->i10ElementSolverTried = false;
84 
85  return;
86 }
87 
88 // function updates the magnetic measurement buffer with most recent magnetic data (typically 200Hz)
89 
90 // the uncalibrated measurements iBs are stored in the buffer but the calibrated measurements iBc are used for indexing.
91 void iUpdateMagBuffer(struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag,
92  int32 loopcounter)
93 {
94  // local variables
95  int32 idelta; // absolute vector distance
96  int32 i; // counter
97  int16 itanj,
98  itank; // indexing accelerometer ratios
99  int8 j,
100  k,
101  l,
102  m; // counters
103  int8 itooclose; // flag denoting measurement is too close to existing ones
104 
105  // calculate the magnetometer buffer bins from the tangent ratios
106  if (pthisMag->iBc[CHZ] == 0) return;
107  itanj = (100 * (int32) pthisMag->iBc[CHX]) / ((int32) pthisMag->iBc[CHZ]);
108  itank = (100 * (int32) pthisMag->iBc[CHY]) / ((int32) pthisMag->iBc[CHZ]);
109 
110  // map tangent ratios to bins j and k using equal angle bins: C guarantees left to right execution of the test
111  // and add an offset of MAGBUFFSIZEX bins to k to mimic atan2 on this ratio
112  // j will vary from 0 to MAGBUFFSIZEX - 1 and k from 0 to 2 * MAGBUFFSIZEX - 1
113  j = k = 0;
114  while ((j < (MAGBUFFSIZEX - 1) && (itanj >= pthisMagBuffer->tanarray[j])))
115  j++;
116  while ((k < (MAGBUFFSIZEX - 1) && (itank >= pthisMagBuffer->tanarray[k])))
117  k++;
118  if (pthisMag->iBc[CHX] < 0) k += MAGBUFFSIZEX;
119 
120  // case 1: buffer is full and this bin has a measurement: over-write without increasing number of measurements
121  // this is the most common option at run time
122  if ((pthisMagBuffer->iMagBufferCount == MAXMEASUREMENTS) &&
123  (pthisMagBuffer->index[j][k] != -1))
124  {
125  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
126  for (i = CHX; i <= CHZ; i++)
127  {
128  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
129  }
130 
131  pthisMagBuffer->index[j][k] = loopcounter;
132  return;
133  } // end case 1
134 
135  // case 2: the buffer is full and this bin does not have a measurement: store and retire the oldest
136  // this is the second most common option at run time
137  if ((pthisMagBuffer->iMagBufferCount == MAXMEASUREMENTS) &&
138  (pthisMagBuffer->index[j][k] == -1))
139  {
140  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
141  for (i = CHX; i <= CHZ; i++)
142  {
143  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
144  }
145 
146  pthisMagBuffer->index[j][k] = loopcounter;
147 
148  // set l and m to the oldest active entry and disable it
149  i = loopcounter;
150  l = m = 0; // to avoid compiler complaint
151  for (j = 0; j < MAGBUFFSIZEX; j++)
152  {
153  for (k = 0; k < MAGBUFFSIZEY; k++)
154  {
155  // check if the time stamp is older than the oldest found so far (normally fails this test)
156  if (pthisMagBuffer->index[j][k] < i)
157  {
158  // check if this bin is active (normally passes this test)
159  if (pthisMagBuffer->index[j][k] != -1)
160  {
161  // set l and m to the indices of the oldest entry found so far
162  l = j;
163  m = k;
164 
165  // set i to the time stamp of the oldest entry found so far
166  i = pthisMagBuffer->index[l][m];
167  } // end of test for active
168  } // end of test for older
169  } // end of loop over k
170  } // end of loop over j
171 
172  // deactivate the oldest measurement (no need to zero the measurement data)
173  pthisMagBuffer->index[l][m] = -1;
174  return;
175  } // end case 2
176 
177  // case 3: buffer is not full and this bin is empty: store and increment number of measurements
178  if ((pthisMagBuffer->iMagBufferCount < MAXMEASUREMENTS) &&
179  (pthisMagBuffer->index[j][k] == -1))
180  {
181  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
182  for (i = CHX; i <= CHZ; i++)
183  {
184  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
185  }
186 
187  pthisMagBuffer->index[j][k] = loopcounter;
188  (pthisMagBuffer->iMagBufferCount)++;
189  return;
190  } // end case 3
191 
192  // case 4: buffer is not full and this bin has a measurement: over-write if close or try to slot in
193  // elsewhere if not close to the other measurements so as to create a mesh at power up
194  if ((pthisMagBuffer->iMagBufferCount < MAXMEASUREMENTS) &&
195  (pthisMagBuffer->index[j][k] != -1))
196  {
197  // calculate the vector difference between current measurement and the buffer entry
198  idelta = 0;
199  for (i = CHX; i <= CHZ; i++)
200  {
201  idelta += abs((int32) pthisMag->iBs[i] -
202  (int32) pthisMagBuffer->iBs[i][j][k]);
203  }
204 
205  // check to see if the current reading is close to this existing magnetic buffer entry
206  if (idelta < MESHDELTACOUNTS)
207  {
208  // simply over-write the measurement and return
209  for (i = CHX; i <= CHZ; i++)
210  {
211  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
212  }
213 
214  pthisMagBuffer->index[j][k] = loopcounter;
215  }
216  else
217  {
218  // reset the flag denoting that the current measurement is close to any measurement in the buffer
219  itooclose = 0;
220 
221  // to avoid compiler warning
222  l = m = 0;
223 
224  // loop over the buffer j from 0 potentially up to MAGBUFFSIZEX - 1
225  j = 0;
226  while (!itooclose && (j < MAGBUFFSIZEX))
227  {
228  // loop over the buffer k from 0 potentially up to MAGBUFFSIZEY - 1
229  k = 0;
230  while (!itooclose && (k < MAGBUFFSIZEY))
231  {
232  // check whether this buffer entry already has a measurement or not
233  if (pthisMagBuffer->index[j][k] != -1)
234  {
235  // calculate the vector difference between current measurement and the buffer entry
236  idelta = 0;
237  for (i = CHX; i <= CHZ; i++)
238  {
239  idelta += abs((int32) pthisMag->iBs[i] -
240  (int32) pthisMagBuffer->iBs[i][j][k]);
241  }
242 
243  // check to see if the current reading is close to this existing magnetic buffer entry
244  if (idelta < MESHDELTACOUNTS)
245  {
246  // set the flag to abort the search
247  itooclose = 1;
248  }
249  }
250  else
251  {
252  // store the location of this empty bin for future use
253  l = j;
254  m = k;
255  } // end of test for valid measurement in this bin
256 
257  k++;
258  } // end of k loop
259 
260  j++;
261  } // end of j loop
262 
263  // if none too close, store the measurement in the last empty bin found and return
264  // l and m are guaranteed to be set if no entries too close are detected
265  if (!itooclose)
266  {
267  for (i = CHX; i <= CHZ; i++)
268  {
269  pthisMagBuffer->iBs[i][l][m] = pthisMag->iBs[i];
270  }
271 
272  pthisMagBuffer->index[l][m] = loopcounter;
273  (pthisMagBuffer->iMagBufferCount)++;
274  }
275  } // end of test for closeness to current buffer entry
276 
277  return;
278  } // end case 4
279 
280  // this line should be unreachable
281  return;
282 }
283 
284 // function maps the uncalibrated magnetometer data fBs (uT) onto calibrated averaged data fBc (uT), iBc (counts)
285 void fInvertMagCal(struct MagSensor *pthisMag, struct MagCalibration *pthisMagCal)
286 {
287  // local variables
288  float ftmp[3]; // temporary array
289  int8 i; // loop counter
290 
291  // remove the computed hard iron offsets (uT): ftmp[]=fBs[]-V[]
292  for (i = CHX; i <= CHZ; i++)
293  {
294  ftmp[i] = pthisMag->fBs[i] - pthisMagCal->fV[i];
295  }
296 
297  // remove the computed soft iron offsets (uT and counts): fBc=inv(W)*(fBs[]-V[])
298  for (i = CHX; i <= CHZ; i++)
299  {
300  pthisMag->fBc[i] = pthisMagCal->finvW[i][CHX] *
301  ftmp[CHX] +
302  pthisMagCal->finvW[i][CHY] *
303  ftmp[CHY] +
304  pthisMagCal->finvW[i][CHZ] *
305  ftmp[CHZ];
306  pthisMag->iBc[i] = (int16) (pthisMag->fBc[i] * pthisMag->fCountsPeruT);
307  }
308 
309  return;
310 }
311 
312 // function runs the magnetic calibration
313 void fRunMagCalibration(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer,
314  struct MagSensor *pthisMag, int32 loopcounter)
315 {
316  int8 i,
317  j; // loop counters
318 
319  // determine whether to initiate a new magnetic calibration
320  if (!pthisMagCal->iCalInProgress)
321  {
322  // clear the flag
323  pthisMagCal->iInitiateMagCal = 0;
324 
325  // try one calibration attempt with the best model available given the number of measurements
326  if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS10CAL) &&
327  (!pthisMagCal->i10ElementSolverTried))
328  {
329  pthisMagCal->i10ElementSolverTried = true;
330  pthisMagCal->iInitiateMagCal = 10;
331  }
332  else if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS7CAL) &&
333  (!pthisMagCal->i7ElementSolverTried))
334  {
335  pthisMagCal->i7ElementSolverTried = true;
336  pthisMagCal->iInitiateMagCal = 7;
337  }
338  else if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS4CAL) &&
339  (!pthisMagCal->i4ElementSolverTried))
340  {
341  pthisMagCal->i4ElementSolverTried = true;
342  pthisMagCal->iInitiateMagCal = 4;
343  }
344 
345  // otherwise start a calibration at regular interval defined by CAL_INTERVAL_SECS
346  else if (!pthisMagCal->iInitiateMagCal &&
347  !(loopcounter % (CAL_INTERVAL_SECS * FUSION_HZ)))
348  {
349  if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS10CAL)
350  {
351  pthisMagCal->i10ElementSolverTried = true;
352  pthisMagCal->iInitiateMagCal = 10;
353  }
354  else if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS7CAL)
355  {
356  pthisMagCal->i7ElementSolverTried = true;
357  pthisMagCal->iInitiateMagCal = 7;
358  }
359  else if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS4CAL)
360  {
361  pthisMagCal->i4ElementSolverTried = true;
362  pthisMagCal->iInitiateMagCal = 4;
363  }
364  }
365 
366  // store the selected calibration model (if any) to be run
367  pthisMagCal->iCalInProgress = pthisMagCal->iInitiateMagCal;
368  }
369 
370  // on entry each of the calibration functions resets iInitiateMagCal and on completion sets
371  // iCalInProgress=0 and iNewCalibrationAvailable=4,7,10 according to the solver used
372  switch (pthisMagCal->iCalInProgress)
373  {
374  case 0:
375  break;
376 
377  case 4:
378  fUpdateMagCalibration4Slice(pthisMagCal, pthisMagBuffer, pthisMag);
379  break;
380 
381  case 7:
382  fUpdateMagCalibration7Slice(pthisMagCal, pthisMagBuffer, pthisMag);
383  break;
384 
385  case 10:
386  fUpdateMagCalibration10Slice(pthisMagCal, pthisMagBuffer, pthisMag);
387  break;
388 
389  default:
390  break;
391  }
392 
393  // evaluate the new calibration to determine whether to accept it
394  if (pthisMagCal->iNewCalibrationAvailable)
395  {
396  // the geomagnetic field strength must be in range (earth is 22uT to 67uT) with reasonable fit error
397  if ((pthisMagCal->ftrB >= MINBFITUT) && (pthisMagCal->ftrB <= MAXBFITUT) &&
398  (pthisMagCal->ftrFitErrorpc <= 15.0F))
399  {
400  // the fit error must be improved or be from a more sophisticated solver but still 5 bars (under 3.5% fit error)
401  if ((pthisMagCal->ftrFitErrorpc <= pthisMagCal->fFitErrorpc) || ((
402  pthisMagCal->iNewCalibrationAvailable > pthisMagCal->
403  iValidMagCal) && (pthisMagCal->ftrFitErrorpc <= 3.5F)))
404  {
405  // accept the new calibration
406  pthisMagCal->iValidMagCal = pthisMagCal->iNewCalibrationAvailable;
407  pthisMagCal->fFitErrorpc = pthisMagCal->ftrFitErrorpc;
408  pthisMagCal->fB = pthisMagCal->ftrB;
409  pthisMagCal->fBSq = pthisMagCal->fB * pthisMagCal->fB;
410  for (i = CHX; i <= CHZ; i++)
411  {
412  pthisMagCal->fV[i] = pthisMagCal->ftrV[i];
413  for (j = CHX; j <= CHZ; j++)
414  pthisMagCal->finvW[i][j] = pthisMagCal->ftrinvW[i][j];
415  }
416  }
417  }
418  else if(pthisMagCal->i10ElementSolverTried)
419  {
420  // the magnetic buffer is presumed corrupted so clear out all measurements and restart calibration attempts
421  pthisMagBuffer->iMagBufferCount = 0;
422  for (i = 0; i < MAGBUFFSIZEX; i++)
423  for (j = 0; j < MAGBUFFSIZEY; j++)
424  pthisMagBuffer->index[i][j] = -1;
425  pthisMagCal->i4ElementSolverTried = false;
426  pthisMagCal->i7ElementSolverTried = false;
427  pthisMagCal->i10ElementSolverTried = false;
428  } // end of test for new calibration within field strength and fit error limits
429 
430  // reset the new calibration flag
431  pthisMagCal->iNewCalibrationAvailable = 0;
432  } // end of test for new calibration available
433 
434  // age the existing fit error very slowly to avoid one good calibration locking out future updates.
435  // this prevents a calibration remaining for ever if a unit is never powered down
436  if (pthisMagCal->iValidMagCal)
437  pthisMagCal->fFitErrorpc += 1.0F / ((float) FUSION_HZ * FITERRORAGINGSECS);
438 
439  return;
440 }
441 
442 // 4 element calibration using 4x4 matrix inverse
444  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
445 {
446  // local variables
447  int8 i,
448  j,
449  k; // loop counters
450 
451  // working arrays for 4x4 matrix inversion
452  float *pfRows[4];
453  int8 iColInd[4];
454  int8 iRowInd[4];
455  int8 iPivot[4];
456 
457  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
458  if (pthisMagCal->iInitiateMagCal)
459  {
460  pthisMagCal->itimeslice = 0;
461  pthisMagCal->iInitiateMagCal = false;
462  pthisMagCal->iMagBufferReadOnly = true;
463  }
464 
465  // time slice 0: 18.8K ticks = 0.39ms for 300 measurements on KL25Z
466  // initialize matrices and compute average of measurements in magnetic buffer
467  if (pthisMagCal->itimeslice == 0)
468  {
469  int16 iM; // number of measurements in the buffer
470 
471  // zero on and above diagonal matrix X^T.X (in fmatA), vector X^T.Y (in fvecA), scalar Y^T.Y (in fYTY)
472  pthisMagCal->fYTY = 0.0F;
473  for (i = 0; i < 4; i++)
474  {
475  pthisMagCal->fvecA[i] = 0.0F;
476  for (j = i; j < 4; j++) pthisMagCal->fmatA[i][j] = 0.0F;
477  }
478 
479  // zero total number of measurements and measurement sums
480  iM = 0;
481  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
482 
483  // compute the sum of measurements in the magnetic buffer
484  for (i = 0; i < MAGBUFFSIZEX; i++)
485  {
486  for (j = 0; j < MAGBUFFSIZEY; j++)
487  {
488  if (pthisMagBuffer->index[i][j] != -1)
489  {
490  iM++;
491  for (k = 0; k < 3; k++)
492  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
493  }
494  }
495  }
496 
497  // compute the magnetic buffer measurement averages with rounding
498  for (i = 0; i < 3; i++)
499  {
500  if (pthisMagCal->iSumBs[i] >= 0)
501  pthisMagCal->iMeanBs[i] =
502  (
503  pthisMagCal->iSumBs[i] +
504  ((int32) iM >> 1)
505  ) /
506  (int32) iM;
507  else
508  pthisMagCal->iMeanBs[i] =
509  (
510  pthisMagCal->iSumBs[i] -
511  ((int32) iM >> 1)
512  ) /
513  (int32) iM;
514  }
515 
516  // for defensive programming, re-store the number of active measurements in the buffer
517  pthisMagBuffer->iMagBufferCount = iM;
518 
519  // increment the time slice
520  (pthisMagCal->itimeslice)++;
521  } // end of time slice 0
522 
523  // time slices 1 to MAGBUFFSIZEX: each up to 81K ticks = 1.7ms
524  // accumulate the matrices fmatA=X^T.X and fvecA=X^T.Y and Y^T.Y from the magnetic buffer
525  else if ((pthisMagCal->itimeslice >= 1) &&
526  (pthisMagCal->itimeslice <= MAGBUFFSIZEX))
527  {
528  float fBsZeroMeanSq; // squared magnetic measurement (counts^2)
529  int32 iBsZeroMean[3]; // zero mean magnetic buffer measurement (counts)
530 
531  // accumulate the measurement matrix elements XTX (in fmatA), XTY (in fvecA) and YTY on the zero mean measurements
532  i = pthisMagCal->itimeslice - 1;
533  for (j = 0; j < MAGBUFFSIZEY; j++)
534  {
535  if (pthisMagBuffer->index[i][j] != -1)
536  {
537  // compute zero mean measurements
538  for (k = 0; k < 3; k++)
539  iBsZeroMean[k] = (int32) pthisMagBuffer->iBs[k][i][j] - (int32) pthisMagCal->iMeanBs[k];
540 
541  // accumulate the non-zero elements of zero mean XTX (in fmatA)
542  pthisMagCal->fmatA[0][0] += (float) (iBsZeroMean[0] * iBsZeroMean[0]);
543  pthisMagCal->fmatA[0][1] += (float) (iBsZeroMean[0] * iBsZeroMean[1]);
544  pthisMagCal->fmatA[0][2] += (float) (iBsZeroMean[0] * iBsZeroMean[2]);
545  pthisMagCal->fmatA[1][1] += (float) (iBsZeroMean[1] * iBsZeroMean[1]);
546  pthisMagCal->fmatA[1][2] += (float) (iBsZeroMean[1] * iBsZeroMean[2]);
547  pthisMagCal->fmatA[2][2] += (float) (iBsZeroMean[2] * iBsZeroMean[2]);
548 
549  // accumulate XTY (in fvecA)
550  fBsZeroMeanSq = (float)
551  (
552  iBsZeroMean[CHX] *
553  iBsZeroMean[CHX] +
554  iBsZeroMean[CHY] *
555  iBsZeroMean[CHY] +
556  iBsZeroMean[CHZ] *
557  iBsZeroMean[CHZ]
558  );
559  for (k = 0; k < 3; k++)
560  pthisMagCal->fvecA[k] += (float) iBsZeroMean[k] * fBsZeroMeanSq;
561  pthisMagCal->fvecA[3] += fBsZeroMeanSq;
562 
563  // accumulate fYTY
564  pthisMagCal->fYTY += fBsZeroMeanSq * fBsZeroMeanSq;
565  }
566  }
567 
568  // increment the time slice
569  (pthisMagCal->itimeslice)++;
570  } // end of time slices 1 to MAGBUFFSIZEX
571 
572  // time slice MAGBUFFSIZEX+1: 18.3K ticks = 0.38ms on KL25Z (constant) (stored in systick[2])
573  // re-enable magnetic buffer for writing and invert fmatB = fmatA = X^T.X in situ
574  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX + 1))
575  {
576  int8 ierror; // matrix inversion error flag
577 
578  // set fmatA[3][3] = X^T.X[3][3] to number of measurements found
579  pthisMagCal->fmatA[3][3] = (float) pthisMagBuffer->iMagBufferCount;
580 
581  // enable the magnetic buffer for writing now that the matrices have been computed
582  pthisMagCal->iMagBufferReadOnly = false;
583 
584  // set fmatA and fmatB to above diagonal elements of fmatA
585  for (i = 0; i < 4; i++)
586  {
587  for (j = 0; j <= i; j++)
588  pthisMagCal->fmatB[i][j] = pthisMagCal->fmatB[j][i] = pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
589  }
590 
591  // set fmatB = inv(fmatB) = inv(X^T.X)
592  for (i = 0; i < 4; i++) pfRows[i] = pthisMagCal->fmatB[i];
593  fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4, &ierror);
594 
595  // increment the time slice
596  (pthisMagCal->itimeslice)++;
597  } // // end of time slice MAGBUFFSIZEX+1
598 
599  // time slice MAGBUFFSIZEX+2: 17.2K ticks = 0.36ms on KL25Z (constant) (stored in systick[3])
600  // compute the solution vector and the calibration coefficients
601  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX + 2))
602  {
603  float fE; // error function = r^T.r
604  float ftmp; // scratch
605 
606  // the trial inverse soft iron matrix invW always equals the identity matrix for 4 element calibration
607  f3x3matrixAeqI(pthisMagCal->ftrinvW);
608 
609  // calculate solution vector fvecB = beta (4x1) = inv(X^T.X).X^T.Y = fmatB * fvecA (counts)
610  for (i = 0; i < 4; i++)
611  {
612  pthisMagCal->fvecB[i] = pthisMagCal->fmatB[i][0] * pthisMagCal->fvecA[0];
613  for (j = 1; j < 4; j++)
614  pthisMagCal->fvecB[i] += pthisMagCal->fmatB[i][j] * pthisMagCal->fvecA[j];
615  }
616 
617  // compute the hard iron vector (uT) correction for zero mean data
618  ftmp = 0.5F * pthisMag->fuTPerCount;
619  for (i = CHX; i <= CHZ; i++)
620  pthisMagCal->ftrV[i] = ftmp * pthisMagCal->fvecB[i];
621 
622  // compute the geomagnetic field strength B (uT)
623  pthisMagCal->ftrB = pthisMagCal->fvecB[3] *
624  pthisMag->fuTPerCount *
625  pthisMag->fuTPerCount;
626  for (i = CHX; i <= CHZ; i++)
627  pthisMagCal->ftrB += pthisMagCal->ftrV[i] * pthisMagCal->ftrV[i];
628  pthisMagCal->ftrB = sqrtf(fabs(pthisMagCal->ftrB));
629 
630  // add in the previously subtracted magnetic buffer mean to get true hard iron offset (uT)
631  ftmp = pthisMag->fuTPerCount / (float) pthisMagBuffer->iMagBufferCount;
632  for (i = CHX; i <= CHZ; i++)
633  pthisMagCal->ftrV[i] += (float) pthisMagCal->iSumBs[i] * ftmp;
634 
635  // calculate E = r^T.r = Y^T.Y - 2 * beta^T.(X^T.Y) + beta^T.(X^T.X).beta
636  // set E = beta^T.(X^T.Y) = fvecB^T.fvecA
637  fE = pthisMagCal->fvecB[0] * pthisMagCal->fvecA[0];
638  for (i = 1; i < 4; i++)
639  fE += pthisMagCal->fvecB[i] * pthisMagCal->fvecA[i];
640 
641  // set E = YTY - 2 * beta^T.(X^T.Y) = YTY - 2 * E;
642  fE = pthisMagCal->fYTY - 2.0F * fE;
643 
644  // set fvecA = (X^T.X).beta = fmatA.fvecB
645  for (i = 0; i < 4; i++)
646  {
647  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][0] * pthisMagCal->fvecB[0];
648  for (j = 1; j < 4; j++)
649  pthisMagCal->fvecA[i] += pthisMagCal->fmatA[i][j] * pthisMagCal->fvecB[j];
650  }
651 
652  // add beta^T.(X^T.X).beta = fvecB^T * fvecA to give fit error E (un-normalized counts^4)
653  for (i = 0; i < 4; i++)
654  fE += pthisMagCal->fvecB[i] * pthisMagCal->fvecA[i];
655 
656  // normalize fit error to the number of measurements and take square root to get error in uT^2
657  fE = sqrtf(fabs(fE) / (float) pthisMagBuffer->iMagBufferCount) *
658  pthisMag->fuTPerCount *
659  pthisMag->fuTPerCount;
660 
661  // obtain dimensionless error by dividing square square of the geomagnetic field and convert to percent
662  pthisMagCal->ftrFitErrorpc = 50.0F * fE / (pthisMagCal->ftrB * pthisMagCal->ftrB);
663 
664  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
665  // that a new 4 element calibration is available
666  pthisMagCal->iCalInProgress = 0;
667  pthisMagCal->iNewCalibrationAvailable = 4;
668  } // end of time slice MAGBUFFSIZEX+2
669 
670  return;
671 }
672 
673 // 7 element calibration using direct eigen-decomposition
675  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
676 {
677  // local variables
678  float fresidue; // eigen-decomposition residual sum
679  float ftmp; // scratch variable
680  int8 i,
681  j,
682  k,
683  l; // loop counters
684 #define MATRIX_7_SIZE 7
685  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
686  if (pthisMagCal->iInitiateMagCal)
687  {
688  pthisMagCal->itimeslice = 0;
689  pthisMagCal->iInitiateMagCal = false;
690  pthisMagCal->iMagBufferReadOnly = true;
691  }
692 
693  // time slice 0: 18.1K KL25Z ticks for 300 measurements = 0.38ms on KL25Z (variable) stored in systick[0]
694  // zero measurement matrix and calculate the mean values in the magnetic buffer
695  if (pthisMagCal->itimeslice == 0)
696  {
697  int16 iM; // number of measurements in the magnetic buffer
698 
699  // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix fmatA
700  for (i = 0; i < MATRIX_7_SIZE; i++)
701  for (j = i; j < MATRIX_7_SIZE; j++)
702  pthisMagCal->fmatA[i][j] = 0.0F;
703 
704  // compute the sum of measurements in the magnetic buffer
705  iM = 0;
706  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
707  for (i = 0; i < MAGBUFFSIZEX; i++)
708  {
709  for (j = 0; j < MAGBUFFSIZEY; j++)
710  {
711  if (pthisMagBuffer->index[i][j] != -1)
712  {
713  iM++;
714  for (k = 0; k < 3; k++)
715  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
716  }
717  }
718  }
719 
720  // compute the magnetic buffer measurement averages with nearest integer rounding
721  for (i = 0; i < 3; i++)
722  {
723  if (pthisMagCal->iSumBs[i] >= 0)
724  pthisMagCal->iMeanBs[i] =
725  (
726  pthisMagCal->iSumBs[i] +
727  ((int32) iM >> 1)
728  ) /
729  (int32) iM;
730  else
731  pthisMagCal->iMeanBs[i] =
732  (
733  pthisMagCal->iSumBs[i] -
734  ((int32) iM >> 1)
735  ) /
736  (int32) iM;
737  }
738 
739  // as defensive programming also ensure the number of measurements found is re-stored
740  pthisMagBuffer->iMagBufferCount = iM;
741 
742  // increment the time slice for the next iteration
743  (pthisMagCal->itimeslice)++;
744  } // end of time slice 0
745 
746  // time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY: accumulate matrices: 8.6K KL25Z ticks = 0.18ms (with max stored in systick[1])
747  else if ((pthisMagCal->itimeslice >= 1) &&
748  (pthisMagCal->itimeslice <= MAGBUFFSIZEX * MAGBUFFSIZEY))
749  {
750  // accumulate the symmetric matrix fmatA on the zero mean measurements
751  i = (pthisMagCal->itimeslice - 1) / MAGBUFFSIZEY; // matrix row i ranges 0 to MAGBUFFSIZEX-1
752  j = (pthisMagCal->itimeslice - 1) % MAGBUFFSIZEY; // matrix column j ranges 0 to MAGBUFFSIZEY-1
753  if (pthisMagBuffer->index[i][j] != -1)
754  {
755  // set fvecA to be vector of zero mean measurements and their squares
756  for (k = 0; k < 3; k++)
757  {
758  pthisMagCal->fvecA[k + 3] = (float)
759  (
760  (int32) pthisMagBuffer->iBs[k][i][j] -
761  (int32) pthisMagCal->iMeanBs[k]
762  );
763  pthisMagCal->fvecA[k] = pthisMagCal->fvecA[k + 3] * pthisMagCal->fvecA[k + 3];
764  }
765 
766  // update non-zero elements fmatA[0-2][6] of fmatA ignoring fmatA[6][6] which is set later.
767  // elements fmatA[3-5][6] are zero as a result of subtracting the mean value
768  for (k = 0; k < 3; k++)
769  pthisMagCal->fmatA[k][6] += pthisMagCal->fvecA[k];
770 
771  // update the remaining on and above diagonal elements fmatA[0-5][0-5]
772  for (k = 0; k < (MATRIX_7_SIZE - 1); k++)
773  {
774  for (l = k; l < (MATRIX_7_SIZE - 1); l++)
775  pthisMagCal->fmatA[k][l] += pthisMagCal->fvecA[k] * pthisMagCal->fvecA[l];
776  }
777  }
778 
779  // increment the time slice for the next iteration
780  (pthisMagCal->itimeslice)++;
781  } // end of time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY
782 
783  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1: 0.8K ticks on KL25Z = 0.02ms on KL25Z (constant) (stored in systick[2])
784  // re-enable magnetic buffer for writing and prepare fmatA, fmatB, fvecA for eigendecomposition
785  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 1))
786  {
787  // set fmatA[6][6] to the number of magnetic measurements found
788  pthisMagCal->fmatA[MATRIX_7_SIZE - 1][MATRIX_7_SIZE - 1] = (float) pthisMagBuffer->iMagBufferCount;
789 
790  // clear the magnetic buffer read only flag now that the matrices have been computed
791  pthisMagCal->iMagBufferReadOnly = false;
792 
793  // set below diagonal elements of 7x7 matrix fmatA to above diagonal elements
794  for (i = 1; i < MATRIX_7_SIZE; i++)
795  for (j = 0; j < i; j++)
796  pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
797 
798  // set matrix of eigenvectors fmatB to identity matrix and eigenvalues vector fvecA to diagonal elements of fmatA
799  for (i = 0; i < MATRIX_7_SIZE; i++)
800  {
801  for (j = 0; j < MATRIX_7_SIZE; j++)
802  pthisMagCal->fmatB[i][j] = 0.0F;
803  pthisMagCal->fmatB[i][i] = 1.0F;
804  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
805  }
806 
807  // increment the time slice for the next iteration
808  (pthisMagCal->itimeslice)++;
809  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1
810 
811  // repeating 21 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 22 inclusive
812  // to perform the eigendecomposition of the measurement matrix fmatA.
813  // 19.6k ticks = 0.41ms on KL25Z (with max stored in systick[3]).
814  // for a 7x7 matrix there are 21 above diagonal elements: 6+5+4+3+2+1+0=21
815  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 2)) &&
816  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 22)))
817  {
818  // set k to the matrix element in range 0 to 20 to be zeroed and used it to set row i and column j
819  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 2);
820  if (k < 6)
821  {
822  i = 0;
823  j = k + 1;
824  }
825  else if (k < 11)
826  {
827  i = 1;
828  j = k - 4;
829  }
830  else if (k < 15)
831  {
832  i = 2;
833  j = k - 8;
834  }
835  else if (k < 18)
836  {
837  i = 3;
838  j = k - 11;
839  }
840  else if (k < 20)
841  {
842  i = 4;
843  j = k - 13;
844  }
845  else
846  {
847  i = 5;
848  j = 6;
849  }
850 
851  // only continue if matrix element i, j has not already been zeroed
852  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
853  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
854  pthisMagCal->fvecA, i, j, MATRIX_7_SIZE);
855 
856  // increment the time slice for the next iteration
857  (pthisMagCal->itimeslice)++;
858  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 22 inclusive
859 
860  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 23: 2.6k ticks on KL25Z = 0.05ms on KL25Z (constant) (stored in systick[4])
861  // compute the sum of the absolute values of above diagonal elements in fmatA as eigen-decomposition exit criterion
862  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 23))
863  {
864  // sum residue of all above-diagonal elements
865  fresidue = 0.0F;
866  for (i = 0; i < MATRIX_7_SIZE; i++)
867  for (j = i + 1; j < MATRIX_7_SIZE; j++)
868  fresidue += fabsf(pthisMagCal->fmatA[i][j]);
869 
870  // determine whether to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
871  if (fresidue > 0.0F)
872  // continue the eigen-decomposition
873  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 2;
874  else
875  // continue to compute the calibration coefficients since the eigen-decomposition is complete
876  (pthisMagCal->itimeslice)++;
877  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 23
878 
879  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 24: 27.8k ticks = 0.58ms on KL25Z (constant) (stored in systick[5])
880  // compute the calibration coefficients from the solution eigenvector
881  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 24))
882  {
883  float fdetA; // determinant of ellipsoid matrix A
884  int8 imin; // column of solution eigenvector with minimum eigenvalue
885 
886  // set imin to the index of the smallest eigenvalue in fvecA
887  imin = 0;
888  for (i = 1; i < MATRIX_7_SIZE; i++)
889  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[imin]) imin = i;
890 
891  // the ellipsoid fA must have positive determinant but the eigensolver can equally easily return a negated
892  // normalized eigenvector without changing the eigenvalue. compute the determinant of ellipsoid matrix A
893  // from first three elements of eigenvector and negate the eigenvector if negative.
894  fdetA = pthisMagCal->fmatB[CHX][imin] *
895  pthisMagCal->fmatB[CHY][imin] *
896  pthisMagCal->fmatB[CHZ][imin];
897  if (fdetA < 0.0F)
898  {
899  fdetA = fabs(fdetA);
900  for (i = 0; i < MATRIX_7_SIZE; i++)
901  pthisMagCal->fmatB[i][imin] = -pthisMagCal->fmatB[i][imin];
902  }
903 
904  // set diagonal elements of ellipsoid matrix A to the solution vector imin with smallest eigenvalue and
905  // zero off-diagonal elements since these are always zero in the 7 element model
906  // compute the hard iron offset fV for zero mean data (counts)
907  for (i = CHX; i <= CHZ; i++)
908  pthisMagCal->ftrV[i] = -0.5F *
909  pthisMagCal->fmatB[i + 3][imin] /
910  pthisMagCal->fmatB[i][imin];
911 
912  // set ftmp to the square of the geomagnetic field strength fB (counts) corresponding to current value of fdetA
913  ftmp = -pthisMagCal->fmatB[6][imin];
914  for (i = CHX; i <= CHZ; i++)
915  ftmp += pthisMagCal->fmatB[i][imin] *
916  pthisMagCal->ftrV[i] *
917  pthisMagCal->ftrV[i];
918 
919  // calculate the trial normalized fit error as a percentage normalized by the geomagnetic field strength squared
920  pthisMagCal->ftrFitErrorpc = 50.0F * sqrtf(fabsf(pthisMagCal->fvecA[imin]) /
921  (float) pthisMagBuffer->iMagBufferCount) / fabsf(ftmp);
922 
923  // compute the geomagnetic field strength (uT) for current value of fdetA
924  pthisMagCal->ftrB = sqrtf(fabsf(ftmp)) * pthisMag->fuTPerCount;
925 
926  // compute the normalized ellipsoid matrix A with unit determinant and derive invW also with unit determinant.
927  ftmp = powf(fabs(fdetA), -(ONETHIRD));
928  for (i = CHX; i <= CHZ; i++)
929  {
930  pthisMagCal->fA[i][i] = pthisMagCal->fmatB[i][imin] * ftmp;
931  pthisMagCal->ftrinvW[i][i] = sqrtf(fabsf(pthisMagCal->fA[i][i]));
932  }
933 
934  pthisMagCal->fA[CHX][CHY] = pthisMagCal->fA[CHX][CHZ] = pthisMagCal->fA[CHY][CHZ] = 0.0F;
935  pthisMagCal->ftrinvW[CHX][CHY] = pthisMagCal->ftrinvW[CHX][CHZ] = pthisMagCal->ftrinvW[CHY][CHZ] = 0.0F;
936 
937  // each element of fA has been scaled by fdetA^(-1/3) so the square of geomagnetic field strength B^2
938  // must be adjusted by the same amount and B by the square root to keep the ellipsoid equation valid:
939  // (Bk-V)^T.A.(Bk-V) = (Bk-V)^T.(invW)^T.invW.(Bk-V) = B^2
940  pthisMagCal->ftrB *= sqrt(fabs(ftmp));
941 
942  // compute the final hard iron offset (uT) by adding in previously subtracted zero mean offset (counts)
943  for (i = CHX; i <= CHZ; i++)
944  pthisMagCal->ftrV[i] =
945  (
946  pthisMagCal->ftrV[i] +
947  (float) pthisMagCal->iMeanBs[i]
948  ) *
949  pthisMag->fuTPerCount;
950 
951  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
952  // that a new 7 element calibration is available
953  pthisMagCal->iCalInProgress = 0;
954  pthisMagCal->iNewCalibrationAvailable = 7;
955  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 24
956 
957  return;
958 }
959 
960 // 10 element calibration using direct eigen-decomposition
962  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
963 {
964  // local variables
965  float fresidue; // eigen-decomposition residual sum
966  float ftmp; // scratch variable
967  int8 i,
968  j,
969  k,
970  l; // loop counters
971 #define MATRIX_10_SIZE 10
972  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
973  if (pthisMagCal->iInitiateMagCal)
974  {
975  pthisMagCal->itimeslice = 0;
976  pthisMagCal->iInitiateMagCal = false;
977  pthisMagCal->iMagBufferReadOnly = true;
978  }
979 
980  // time slice 0: 18.7k KL25Z ticks for 300 measurements = 0.39ms on KL25Z (variable) stored in systick[0]
981  // zero measurement matrix fmatA and calculate the mean values in the magnetic buffer
982  if (pthisMagCal->itimeslice == 0)
983  {
984  int16 iM; // number of measurements in the magnetic buffer
985 
986  // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix fmatA
987  for (i = 0; i < MATRIX_10_SIZE; i++)
988  for (j = i; j < MATRIX_10_SIZE; j++)
989  pthisMagCal->fmatA[i][j] = 0.0F;
990 
991  // compute the sum of measurements in the magnetic buffer
992  iM = 0;
993  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
994  for (i = 0; i < MAGBUFFSIZEX; i++)
995  {
996  for (j = 0; j < MAGBUFFSIZEY; j++)
997  {
998  if (pthisMagBuffer->index[i][j] != -1)
999  {
1000  iM++;
1001  for (k = 0; k < 3; k++)
1002  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
1003  }
1004  }
1005  }
1006 
1007  // compute the magnetic buffer measurement averages with nearest integer rounding
1008  for (i = 0; i < 3; i++)
1009  {
1010  if (pthisMagCal->iSumBs[i] >= 0)
1011  pthisMagCal->iMeanBs[i] =
1012  (
1013  pthisMagCal->iSumBs[i] +
1014  ((int32) iM >> 1)
1015  ) /
1016  (int32) iM;
1017  else
1018  pthisMagCal->iMeanBs[i] =
1019  (
1020  pthisMagCal->iSumBs[i] -
1021  ((int32) iM >> 1)
1022  ) /
1023  (int32) iM;
1024  }
1025 
1026  // as defensive programming also ensure the number of measurements found is re-stored
1027  pthisMagBuffer->iMagBufferCount = iM;
1028 
1029  // increment the time slice for the next iteration
1030  (pthisMagCal->itimeslice)++;
1031  } // end of time slice 0
1032 
1033  // time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY: accumulate matrices: 17.9k KL25Z ticks = 0.37ms for 300 measurements
1034  // (with max measured stored in systick[1])
1035  else if ((pthisMagCal->itimeslice >= 1) &&
1036  (pthisMagCal->itimeslice <= MAGBUFFSIZEX * MAGBUFFSIZEY))
1037  {
1038  // accumulate the symmetric matrix fmatA on the zero mean measurements
1039  i = (pthisMagCal->itimeslice - 1) / MAGBUFFSIZEY; // matrix row i ranges 0 to MAGBUFFSIZEX-1
1040  j = (pthisMagCal->itimeslice - 1) % MAGBUFFSIZEY; // matrix column j ranges 0 to MAGBUFFSIZEY-1
1041  if (pthisMagBuffer->index[i][j] != -1)
1042  {
1043  // set fvecA[6-8] to the zero mean measurements
1044  for (k = 0; k < 3; k++)
1045  pthisMagCal->fvecA[k + 6] = (float)
1046  (
1047  (int32) pthisMagBuffer->iBs[k][i][j] -
1048  (int32) pthisMagCal->iMeanBs[k]
1049  );
1050 
1051  // compute fvecA[0-5] from fvecA[6-8]
1052  pthisMagCal->fvecA[0] = pthisMagCal->fvecA[6] * pthisMagCal->fvecA[6];
1053  pthisMagCal->fvecA[1] = 2.0F *
1054  pthisMagCal->fvecA[6] *
1055  pthisMagCal->fvecA[7];
1056  pthisMagCal->fvecA[2] = 2.0F *
1057  pthisMagCal->fvecA[6] *
1058  pthisMagCal->fvecA[8];
1059  pthisMagCal->fvecA[3] = pthisMagCal->fvecA[7] * pthisMagCal->fvecA[7];
1060  pthisMagCal->fvecA[4] = 2.0F *
1061  pthisMagCal->fvecA[7] *
1062  pthisMagCal->fvecA[8];
1063  pthisMagCal->fvecA[5] = pthisMagCal->fvecA[8] * pthisMagCal->fvecA[8];
1064 
1065  // update non-zero elements fmatA[0-5][9] of fmatA ignoring fmatA[9][9] which is set later.
1066  // elements fmatA[6-8][9] are zero as a result of subtracting the mean value
1067  for (k = 0; k < 6; k++)
1068  pthisMagCal->fmatA[k][9] += pthisMagCal->fvecA[k];
1069 
1070  // update the remaining on and above diagonal elements fmatA[0-8][0-8]
1071  for (k = 0; k < (MATRIX_10_SIZE - 1); k++)
1072  {
1073  for (l = k; l < (MATRIX_10_SIZE - 1); l++)
1074  pthisMagCal->fmatA[k][l] += pthisMagCal->fvecA[k] * pthisMagCal->fvecA[l];
1075  }
1076  }
1077 
1078  // increment the time slice for the next iteration
1079  (pthisMagCal->itimeslice)++;
1080  } // end of time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY
1081 
1082  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1: 1.2k ticks on KL25Z = 0.025ms on KL25Z (constant) (stored in systick[2])
1083  // re-enable magnetic buffer for writing and prepare fmatA, fmatB, fvecA for eigendecomposition
1084  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 1))
1085  {
1086  // set fmatA[9][9] to the number of magnetic measurements found
1087  pthisMagCal->fmatA[MATRIX_10_SIZE - 1][MATRIX_10_SIZE - 1] = (float) pthisMagBuffer->iMagBufferCount;
1088 
1089  // clear the magnetic buffer read only flag now that the matrices have been computed
1090  pthisMagCal->iMagBufferReadOnly = false;
1091 
1092  // set below diagonal elements of 10x10 matrix fmatA to above diagonal elements
1093  for (i = 1; i < MATRIX_10_SIZE; i++)
1094  for (j = 0; j < i; j++)
1095  pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
1096 
1097  // set matrix of eigenvectors fmatB to identity matrix and eigenvalues vector fvecA to diagonal elements of fmatA
1098  for (i = 0; i < MATRIX_10_SIZE; i++)
1099  {
1100  for (j = 0; j < MATRIX_10_SIZE; j++)
1101  pthisMagCal->fmatB[i][j] = 0.0F;
1102  pthisMagCal->fmatB[i][i] = 1.0F;
1103  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
1104  }
1105 
1106  // increment the time slice for the next iteration
1107  (pthisMagCal->itimeslice)++;
1108  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1
1109 
1110  // repeating 45 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 46 inclusive
1111  // to perform the eigendecomposition of the measurement matrix fmatA.
1112  // 28.2k ticks = 0.56ms on KL25Z (with max stored in systick[3]).
1113  // for a 10x10 matrix there are 45 above diagonal elements: 9+8+7+6+5+4+3+2+1+0=45
1114  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 2)) &&
1115  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 46)))
1116  {
1117  // set k to the matrix element of interest in range 0 to 44 to be zeroed and set row i and column j
1118  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 2);
1119  if (k < 9)
1120  {
1121  i = 0;
1122  j = k + 1;
1123  }
1124  else if (k < 17)
1125  {
1126  i = 1;
1127  j = k - 7;
1128  }
1129  else if (k < 24)
1130  {
1131  i = 2;
1132  j = k - 14;
1133  }
1134  else if (k < 30)
1135  {
1136  i = 3;
1137  j = k - 20;
1138  }
1139  else if (k < 35)
1140  {
1141  i = 4;
1142  j = k - 25;
1143  }
1144  else if (k < 39)
1145  {
1146  i = 5;
1147  j = k - 29;
1148  }
1149  else if (k < 42)
1150  {
1151  i = 6;
1152  j = k - 32;
1153  }
1154  else if (k < 44)
1155  {
1156  i = 7;
1157  j = k - 34;
1158  }
1159  else
1160  {
1161  i = 8;
1162  j = 9;
1163  }
1164 
1165  // only continue if matrix element i, j has not already been zeroed
1166  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
1167  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
1168  pthisMagCal->fvecA, i, j, MATRIX_10_SIZE);
1169 
1170  // increment the time slice for the next iteration
1171  (pthisMagCal->itimeslice)++;
1172  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 46 inclusive
1173 
1174  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 47: 5.6k ticks on KL25Z = 0.12ms on KL25Z (constant) (stored in systick[4])
1175  // compute the sum of the absolute values of above diagonal elements in fmatA as eigen-decomposition exit criterion
1176  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 47))
1177  {
1178  // sum residue of all above-diagonal elements
1179  fresidue = 0.0F;
1180  for (i = 0; i < MATRIX_10_SIZE; i++)
1181  for (j = i + 1; j < MATRIX_10_SIZE; j++)
1182  fresidue += fabsf(pthisMagCal->fmatA[i][j]);
1183 
1184  // determine whether to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
1185  if (fresidue > 0.0F)
1186  // continue the eigen-decomposition
1187  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 2;
1188  else
1189  // continue to compute the calibration coefficients since the eigen-decomposition is complete
1190  (pthisMagCal->itimeslice)++;
1191  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 47
1192 
1193  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 48: 38.5k ticks = 0.80ms on KL25Z (constant) (stored in systick[5])
1194  // compute the calibration coefficients (excluding invW) from the solution eigenvector
1195  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 48))
1196  {
1197  float fdetA; // determinant of ellipsoid matrix A
1198  int8 imin; // column of solution eigenvector with minimum eigenvalue
1199 
1200  // set imin to the index of the smallest eigenvalue in fvecA
1201  imin = 0;
1202  for (i = 1; i < MATRIX_10_SIZE; i++)
1203  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[imin]) imin = i;
1204 
1205  // set the ellipsoid matrix A from elements 0 to 5 of the solution eigenvector.
1206  pthisMagCal->fA[CHX][CHX] = pthisMagCal->fmatB[0][imin];
1207  pthisMagCal->fA[CHX][CHY] = pthisMagCal->fA[CHY][CHX] = pthisMagCal->fmatB[1][imin];
1208  pthisMagCal->fA[CHX][CHZ] = pthisMagCal->fA[CHZ][CHX] = pthisMagCal->fmatB[2][imin];
1209  pthisMagCal->fA[CHY][CHY] = pthisMagCal->fmatB[3][imin];
1210  pthisMagCal->fA[CHY][CHZ] = pthisMagCal->fA[CHZ][CHY] = pthisMagCal->fmatB[4][imin];
1211  pthisMagCal->fA[CHZ][CHZ] = pthisMagCal->fmatB[5][imin];
1212 
1213  // negate A and the entire solution vector A has negative determinant
1214  fdetA = f3x3matrixDetA(pthisMagCal->fA);
1215  if (fdetA < 0.0F)
1216  {
1217  f3x3matrixAeqMinusA(pthisMagCal->fA);
1218  fdetA = fabs(fdetA);
1219  for (i = 0; i < MATRIX_10_SIZE; i++)
1220  pthisMagCal->fmatB[i][imin] = -pthisMagCal->fmatB[i][imin];
1221  }
1222 
1223  // set finvA to the inverse of the ellipsoid matrix fA
1224  f3x3matrixAeqInvSymB(pthisMagCal->finvA, pthisMagCal->fA);
1225 
1226  // compute the hard iron offset fV for zero mean data (counts)
1227  for (i = CHX; i <= CHZ; i++)
1228  {
1229  pthisMagCal->ftrV[i] = pthisMagCal->finvA[i][0] *
1230  pthisMagCal->fmatB[6][imin] +
1231  pthisMagCal->finvA[i][1] *
1232  pthisMagCal->fmatB[7][imin] +
1233  pthisMagCal->finvA[i][2] *
1234  pthisMagCal->fmatB[8][imin];
1235  pthisMagCal->ftrV[i] *= -0.5F;
1236  }
1237 
1238  // compute the geomagnetic field strength B (counts) for current (un-normalized) ellipsoid matrix determinant.
1239  ftmp = pthisMagCal->fA[CHX][CHY] *
1240  pthisMagCal->ftrV[CHX] *
1241  pthisMagCal->ftrV[CHY] +
1242  pthisMagCal->fA[CHX][CHZ] *
1243  pthisMagCal->ftrV[CHX] *
1244  pthisMagCal->ftrV[CHZ] +
1245  pthisMagCal->fA[CHY][CHZ] *
1246  pthisMagCal->ftrV[CHY] *
1247  pthisMagCal->ftrV[CHZ];
1248  ftmp *= 2.0F;
1249  ftmp -= pthisMagCal->fmatB[9][imin];
1250  ftmp += pthisMagCal->fA[CHX][CHX] *
1251  pthisMagCal->ftrV[CHX] *
1252  pthisMagCal->ftrV[CHX];
1253  ftmp += pthisMagCal->fA[CHY][CHY] *
1254  pthisMagCal->ftrV[CHY] *
1255  pthisMagCal->ftrV[CHY];
1256  ftmp += pthisMagCal->fA[CHZ][CHZ] *
1257  pthisMagCal->ftrV[CHZ] *
1258  pthisMagCal->ftrV[CHZ];
1259  pthisMagCal->ftrB = sqrtf(fabsf(ftmp));
1260 
1261  // calculate the normalized fit error as a percentage
1262  pthisMagCal->ftrFitErrorpc = 50.0F * sqrtf(fabsf(
1263  pthisMagCal->fvecA[imin] /
1264  (float) pthisMagBuffer->iMagBufferCount
1265  )) / (pthisMagCal->ftrB * pthisMagCal->ftrB);
1266 
1267  // convert the trial geomagnetic field strength B from counts to uT for un-normalized soft iron matrix A
1268  pthisMagCal->ftrB *= pthisMag->fuTPerCount;
1269 
1270  // compute the final hard iron offset (uT) by adding in previously subtracted zero mean offset (counts)
1271  for (i = CHX; i <= CHZ; i++)
1272  pthisMagCal->ftrV[i] =
1273  (
1274  pthisMagCal->ftrV[i] +
1275  (float) pthisMagCal->iMeanBs[i]
1276  ) *
1277  pthisMag->fuTPerCount;
1278 
1279  // normalize the ellipsoid matrix A to unit determinant
1280  ftmp = powf(fabs(fdetA), -(ONETHIRD));
1281  f3x3matrixAeqAxScalar(pthisMagCal->fA, ftmp);
1282 
1283  // each element of fA has been scaled by fdetA^(-1/3) so the square of geomagnetic field strength B^2
1284  // must be adjusted by the same amount and B by the square root to keep the ellipsoid equation valid:
1285  // (Bk-V)^T.A.(Bk-V) = (Bk-V)^T.(invW)^T.invW.(Bk-V) = B^2
1286  pthisMagCal->ftrB *= sqrt(fabs(ftmp));
1287 
1288  // prepare for eigendecomposition of fA to compute finvW from the square root of fA by setting
1289  // fmatA (upper left) to the 3x3 matrix fA, set fmatB (eigenvectors to identity matrix and
1290  // fvecA (eigenvalues) to diagonal elements of fmatA = fA
1291  for (i = 0; i < 3; i++)
1292  {
1293  for (j = 0; j < 3; j++)
1294  {
1295  pthisMagCal->fmatA[i][j] = pthisMagCal->fA[i][j];
1296  pthisMagCal->fmatB[i][j] = 0.0F;
1297  }
1298 
1299  pthisMagCal->fmatB[i][i] = 1.0F;
1300  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
1301  }
1302 
1303  // increment the time slice for the next iteration
1304  (pthisMagCal->itimeslice)++;
1305  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 48
1306 
1307  // repeating 3 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 49 to MAGBUFFSIZEX * MAGBUFFSIZEY + 51 inclusive
1308  // 9.7kticks = 0.2ms on KL25Z (with max stored in systick[6]).
1309  // perform the eigendecomposition of the 3x3 ellipsoid matrix fA which has 3 above diagonal elements: 2+1+0=3
1310  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 49)) &&
1311  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 51)))
1312  {
1313  // set k to the matrix element of interest in range 0 to 2 to be zeroed and set row i and column j
1314  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 49);
1315  if (k == 0)
1316  {
1317  i = 0;
1318  j = 1;
1319  }
1320  else if (k == 1)
1321  {
1322  i = 0;
1323  j = 2;
1324  }
1325  else
1326  {
1327  i = 1;
1328  j = 2;
1329  }
1330 
1331  // only continue with eigen-decomposition if matrix element i, j has not already been zeroed
1332  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
1333  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
1334  pthisMagCal->fvecA, i, j, 3);
1335 
1336  // increment the time slice for the next iteration
1337  (pthisMagCal->itimeslice)++;
1338  } // end of time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 49 to MAGBUFFSIZEX * MAGBUFFSIZEY + 51 inclusive
1339 
1340  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 52: 0.3k ticks = 0.006ms on KL25Z (constant) (stored in systick[7])
1341  // determine whether the eigen-decomposition of fA is complete
1342  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 52))
1343  {
1344  // sum residue of all above-diagonal elements and use to determine whether
1345  // to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
1346  fresidue = fabsf(pthisMagCal->fmatA[0][1]) + fabsf(pthisMagCal->fmatA[0][2]) + fabsf(pthisMagCal->fmatA[1][2]);
1347  if (fresidue > 0.0F)
1348  // continue the eigen-decomposition
1349  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 49;
1350  else
1351  // continue to compute the calibration coefficients since the eigen-decomposition is complete
1352  (pthisMagCal->itimeslice)++;
1353  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 52
1354 
1355  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 53: 9.3k ticks = 0.19ms on KL25Z (constant) (stored in systick[8])
1356  // compute the inverse gain matrix invW from the eigendecomposition of the ellipsoid matrix A
1357  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 53))
1358  {
1359  // set pthisMagCal->fmatB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) = fmatB . diag(sqrt(sqrt(fvecA))
1360  for (j = 0; j < 3; j++) // loop over columns j
1361  {
1362  ftmp = sqrtf(sqrtf(fabsf(pthisMagCal->fvecA[j])));
1363  for (i = 0; i < 3; i++) // loop over rows i
1364  pthisMagCal->fmatB[i][j] *= ftmp;
1365  }
1366 
1367  // set ftrinvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T
1368  // = fmatB * fmatB^T = sqrt(fA) (guaranteed symmetric)
1369  // loop over on and above diagonal elements
1370  for (i = 0; i < 3; i++)
1371  {
1372  for (j = i; j < 3; j++)
1373  {
1374  pthisMagCal->ftrinvW[i][j] = pthisMagCal->ftrinvW[j][i] =
1375  pthisMagCal->fmatB[i][0] *
1376  pthisMagCal->fmatB[j][0] +
1377  pthisMagCal->fmatB[i][1] *
1378  pthisMagCal->fmatB[j][1] +
1379  pthisMagCal->fmatB[i][2] *
1380  pthisMagCal->fmatB[j][2];
1381  }
1382  }
1383 
1384  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
1385  // that a new 10 element calibration is available
1386  pthisMagCal->iCalInProgress = 0;
1387  pthisMagCal->iNewCalibrationAvailable = 10;
1388  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 53
1389 
1390  return;
1391 }
1392 
1393 // 4 element calibration using 4x4 matrix inverse
1394 void fComputeMagCalibration4(struct MagCalibration *pthisMagCal,
1395  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
1396 {
1397  // local variables
1398  float fBs2; // fBs[CHX]^2+fBs[CHY]^2+fBs[CHZ]^2
1399  float fSumBs4; // sum of fBs2
1400  float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
1401  float fE; // error function = r^T.r
1402  int16 iOffset[3]; // offset to remove large DC hard iron bias in matrix
1403  int16 iCount; // number of measurements counted
1404  int8 ierror; // matrix inversion error flag
1405  int8 i,
1406  j,
1407  k,
1408  l; // loop counters
1409 
1410  // working arrays for 4x4 matrix inversion
1411  float *pfRows[4];
1412  int8 iColInd[4];
1413  int8 iRowInd[4];
1414  int8 iPivot[4];
1415 
1416  // compute fscaling to reduce multiplications later
1417  fscaling = pthisMag->fuTPerCount / DEFAULTB;
1418 
1419  // the trial inverse soft iron matrix invW always equals the identity matrix for 4 element calibration
1420  f3x3matrixAeqI(pthisMagCal->ftrinvW);
1421 
1422  // zero fSumBs4=Y^T.Y, fvecB=X^T.Y (4x1) and on and above diagonal elements of fmatA=X^T*X (4x4)
1423  fSumBs4 = 0.0F;
1424  for (i = 0; i < 4; i++)
1425  {
1426  pthisMagCal->fvecB[i] = 0.0F;
1427  for (j = i; j < 4; j++)
1428  {
1429  pthisMagCal->fmatA[i][j] = 0.0F;
1430  }
1431  }
1432 
1433  // the offsets are guaranteed to be set from the first element but to avoid compiler error
1434  iOffset[CHX] = iOffset[CHY] = iOffset[CHZ] = 0;
1435 
1436  // use entries from magnetic buffer to compute matrices
1437  iCount = 0;
1438  for (j = 0; j < MAGBUFFSIZEX; j++)
1439  {
1440  for (k = 0; k < MAGBUFFSIZEY; k++)
1441  {
1442  if (pthisMagBuffer->index[j][k] != -1)
1443  {
1444  // use first valid magnetic buffer entry as estimate (in counts) for offset
1445  if (iCount == 0)
1446  {
1447  for (l = CHX; l <= CHZ; l++)
1448  {
1449  iOffset[l] = pthisMagBuffer->iBs[l][j][k];
1450  }
1451  }
1452 
1453  // store scaled and offset fBs[XYZ] in fvecA[0-2] and fBs[XYZ]^2 in fvecA[3-5]
1454  for (l = CHX; l <= CHZ; l++)
1455  {
1456  pthisMagCal->fvecA[l] = (float)
1457  (
1458  (int32) pthisMagBuffer->iBs[l][j][k] -
1459  (int32) iOffset[l]
1460  ) * fscaling;
1461  pthisMagCal->fvecA[l + 3] = pthisMagCal->fvecA[l] * pthisMagCal->fvecA[l];
1462  }
1463 
1464  // calculate fBs2 = fBs[CHX]^2 + fBs[CHY]^2 + fBs[CHZ]^2 (scaled uT^2)
1465  fBs2 = pthisMagCal->fvecA[3] +
1466  pthisMagCal->fvecA[4] +
1467  pthisMagCal->fvecA[5];
1468 
1469  // accumulate fBs^4 over all measurements into fSumBs4=Y^T.Y
1470  fSumBs4 += fBs2 * fBs2;
1471 
1472  // now we have fBs2, accumulate fvecB[0-2] = X^T.Y =sum(fBs2.fBs[XYZ])
1473  for (l = CHX; l <= CHZ; l++)
1474  {
1475  pthisMagCal->fvecB[l] += pthisMagCal->fvecA[l] * fBs2;
1476  }
1477 
1478  //accumulate fvecB[3] = X^T.Y =sum(fBs2)
1479  pthisMagCal->fvecB[3] += fBs2;
1480 
1481  // accumulate on and above-diagonal terms of fmatA = X^T.X ignoring fmatA[3][3]
1482  pthisMagCal->fmatA[0][0] += pthisMagCal->fvecA[CHX + 3];
1483  pthisMagCal->fmatA[0][1] += pthisMagCal->fvecA[CHX] * pthisMagCal->fvecA[CHY];
1484  pthisMagCal->fmatA[0][2] += pthisMagCal->fvecA[CHX] * pthisMagCal->fvecA[CHZ];
1485  pthisMagCal->fmatA[0][3] += pthisMagCal->fvecA[CHX];
1486  pthisMagCal->fmatA[1][1] += pthisMagCal->fvecA[CHY + 3];
1487  pthisMagCal->fmatA[1][2] += pthisMagCal->fvecA[CHY] * pthisMagCal->fvecA[CHZ];
1488  pthisMagCal->fmatA[1][3] += pthisMagCal->fvecA[CHY];
1489  pthisMagCal->fmatA[2][2] += pthisMagCal->fvecA[CHZ + 3];
1490  pthisMagCal->fmatA[2][3] += pthisMagCal->fvecA[CHZ];
1491 
1492  // increment the counter for next iteration
1493  iCount++;
1494  }
1495  }
1496  }
1497 
1498  // set the last element of the measurement matrix to the number of buffer elements used
1499  pthisMagCal->fmatA[3][3] = (float) iCount;
1500 
1501  // store the number of measurements accumulated (defensive programming, should never be needed)
1502  pthisMagBuffer->iMagBufferCount = iCount;
1503 
1504  // use above diagonal elements of symmetric fmatA to set both fmatB and fmatA to X^T.X
1505  for (i = 0; i < 4; i++)
1506  {
1507  for (j = i; j < 4; j++)
1508  {
1509  pthisMagCal->fmatB[i][j] = pthisMagCal->fmatB[j][i] = pthisMagCal->fmatA[j][i] = pthisMagCal->fmatA[i][j];
1510  }
1511  }
1512 
1513  // calculate in situ inverse of fmatB = inv(X^T.X) (4x4) while fmatA still holds X^T.X
1514  for (i = 0; i < 4; i++)
1515  {
1516  pfRows[i] = pthisMagCal->fmatB[i];
1517  }
1518 
1519  fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4, &ierror);
1520 
1521  // calculate fvecA = solution beta (4x1) = inv(X^T.X).X^T.Y = fmatB * fvecB
1522  for (i = 0; i < 4; i++)
1523  {
1524  pthisMagCal->fvecA[i] = 0.0F;
1525  for (k = 0; k < 4; k++)
1526  {
1527  pthisMagCal->fvecA[i] += pthisMagCal->fmatB[i][k] * pthisMagCal->fvecB[k];
1528  }
1529  }
1530 
1531  // calculate E = r^T.r = Y^T.Y - 2 * beta^T.(X^T.Y) + beta^T.(X^T.X).beta
1532  // = fSumBs4 - 2 * fvecA^T.fvecB + fvecA^T.fmatA.fvecA
1533  // first set E = Y^T.Y - 2 * beta^T.(X^T.Y) = fSumBs4 - 2 * fvecA^T.fvecB
1534  fE = 0.0F;
1535  for (i = 0; i < 4; i++)
1536  {
1537  fE += pthisMagCal->fvecA[i] * pthisMagCal->fvecB[i];
1538  }
1539 
1540  fE = fSumBs4 - 2.0F * fE;
1541 
1542  // set fvecB = (X^T.X).beta = fmatA.fvecA
1543  for (i = 0; i < 4; i++)
1544  {
1545  pthisMagCal->fvecB[i] = 0.0F;
1546  for (k = 0; k < 4; k++)
1547  {
1548  pthisMagCal->fvecB[i] += pthisMagCal->fmatA[i][k] * pthisMagCal->fvecA[k];
1549  }
1550  }
1551 
1552  // complete calculation of E by adding beta^T.(X^T.X).beta = fvecA^T * fvecB
1553  for (i = 0; i < 4; i++)
1554  {
1555  fE += pthisMagCal->fvecB[i] * pthisMagCal->fvecA[i];
1556  }
1557 
1558  // compute the hard iron vector (in uT but offset and scaled by FMATRIXSCALING)
1559  for (l = CHX; l <= CHZ; l++)
1560  {
1561  pthisMagCal->ftrV[l] = 0.5F * pthisMagCal->fvecA[l];
1562  }
1563 
1564  // compute the scaled geomagnetic field strength B (in uT but scaled by FMATRIXSCALING)
1565  pthisMagCal->ftrB = sqrtf(pthisMagCal->fvecA[3] + pthisMagCal->ftrV[CHX] *
1566  pthisMagCal->ftrV[CHX] + pthisMagCal->ftrV[CHY] *
1567  pthisMagCal->ftrV[CHY] + pthisMagCal->ftrV[CHZ] *
1568  pthisMagCal->ftrV[CHZ]);
1569 
1570  // calculate the trial fit error (percent) normalized to number of measurements and scaled geomagnetic field strength
1571  pthisMagCal->ftrFitErrorpc = sqrtf(fE / (float) pthisMagBuffer->iMagBufferCount) * 100.0F / (2.0F * pthisMagCal->ftrB * pthisMagCal->ftrB);
1572 
1573  // correct the hard iron estimate for FMATRIXSCALING and the offsets applied (result in uT)
1574  for (l = CHX; l <= CHZ; l++)
1575  {
1576  pthisMagCal->ftrV[l] = pthisMagCal->ftrV[l] *
1577  DEFAULTB +
1578  (float) iOffset[l] *
1579  pthisMag->fuTPerCount;
1580  }
1581 
1582  // correct the geomagnetic field strength B to correct scaling (result in uT)
1583  pthisMagCal->ftrB *= DEFAULTB;
1584 
1585  return;
1586 }
1587 
1588 // 7 element calibration using direct eigen-decomposition
1589 void fComputeMagCalibration7(struct MagCalibration *pthisMagCal,
1590  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
1591 {
1592  // local variables
1593  float det; // matrix determinant
1594  float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
1595  float ftmp; // scratch variable
1596  int16 iOffset[3]; // offset to remove large DC hard iron bias
1597  int16 iCount; // number of measurements counted
1598  int8 i,
1599  j,
1600  k,
1601  l,
1602  m,
1603  n; // loop counters
1604 
1605  // compute fscaling to reduce multiplications later
1606  fscaling = pthisMag->fuTPerCount / DEFAULTB;
1607 
1608  // the offsets are guaranteed to be set from the first element but to avoid compiler error
1609  iOffset[CHX] = iOffset[CHY] = iOffset[CHZ] = 0;
1610 
1611  // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix fmatA
1612  for (m = 0; m < 7; m++)
1613  {
1614  for (n = m; n < 7; n++)
1615  {
1616  pthisMagCal->fmatA[m][n] = 0.0F;
1617  }
1618  }
1619 
1620  // add megnetic buffer entries into product matrix fmatA
1621  iCount = 0;
1622  for (j = 0; j < MAGBUFFSIZEX; j++)
1623  {
1624  for (k = 0; k < MAGBUFFSIZEY; k++)
1625  {
1626  if (pthisMagBuffer->index[j][k] != -1)
1627  {
1628  // use first valid magnetic buffer entry as offset estimate (bit counts)
1629  if (iCount == 0)
1630  {
1631  for (l = CHX; l <= CHZ; l++)
1632  {
1633  iOffset[l] = pthisMagBuffer->iBs[l][j][k];
1634  }
1635  }
1636 
1637  // apply the offset and scaling and store in fvecA
1638  for (l = CHX; l <= CHZ; l++)
1639  {
1640  pthisMagCal->fvecA[l + 3] = (float)
1641  (
1642  (int32) pthisMagBuffer->iBs[l][j][k] -
1643  (int32) iOffset[l]
1644  ) * fscaling;
1645  pthisMagCal->fvecA[l] = pthisMagCal->fvecA[l + 3] * pthisMagCal->fvecA[l + 3];
1646  }
1647 
1648  // accumulate the on-and above-diagonal terms of pthisMagCal->fmatA=Sigma{fvecA^T * fvecA}
1649  // with the exception of fmatA[6][6] which will sum to the number of measurements
1650  // and remembering that fvecA[6] equals 1.0F
1651  // update the right hand column [6] of fmatA except for fmatA[6][6]
1652  for (m = 0; m < 6; m++)
1653  {
1654  pthisMagCal->fmatA[m][6] += pthisMagCal->fvecA[m];
1655  }
1656 
1657  // update the on and above diagonal terms except for right hand column 6
1658  for (m = 0; m < 6; m++)
1659  {
1660  for (n = m; n < 6; n++)
1661  {
1662  pthisMagCal->fmatA[m][n] += pthisMagCal->fvecA[m] * pthisMagCal->fvecA[n];
1663  }
1664  }
1665 
1666  // increment the measurement counter for the next iteration
1667  iCount++;
1668  }
1669  }
1670  }
1671 
1672  // finally set the last element fmatA[6][6] to the number of measurements
1673  pthisMagCal->fmatA[6][6] = (float) iCount;
1674 
1675  // store the number of measurements accumulated (defensive programming, should never be needed)
1676  pthisMagBuffer->iMagBufferCount = iCount;
1677 
1678  // copy the above diagonal elements of fmatA to below the diagonal
1679  for (m = 1; m < 7; m++)
1680  {
1681  for (n = 0; n < m; n++)
1682  {
1683  pthisMagCal->fmatA[m][n] = pthisMagCal->fmatA[n][m];
1684  }
1685  }
1686 
1687  // set tmpA7x1 to the unsorted eigenvalues and fmatB to the unsorted eigenvectors of fmatA
1688  fEigenCompute10(pthisMagCal->fmatA, pthisMagCal->fvecA, pthisMagCal->fmatB,
1689  7);
1690 
1691  // find the smallest eigenvalue
1692  j = 0;
1693  for (i = 1; i < 7; i++)
1694  {
1695  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[j])
1696  {
1697  j = i;
1698  }
1699  }
1700 
1701  // set ellipsoid matrix A to the solution vector with smallest eigenvalue, compute its determinant
1702  // and the hard iron offset (scaled and offset)
1703  f3x3matrixAeqScalar(pthisMagCal->fA, 0.0F);
1704  det = 1.0F;
1705  for (l = CHX; l <= CHZ; l++)
1706  {
1707  pthisMagCal->fA[l][l] = pthisMagCal->fmatB[l][j];
1708  det *= pthisMagCal->fA[l][l];
1709  pthisMagCal->ftrV[l] = -0.5F *
1710  pthisMagCal->fmatB[l + 3][j] /
1711  pthisMagCal->fA[l][l];
1712  }
1713 
1714  // negate A if it has negative determinant
1715  if (det < 0.0F)
1716  {
1717  f3x3matrixAeqMinusA(pthisMagCal->fA);
1718  pthisMagCal->fmatB[6][j] = -pthisMagCal->fmatB[6][j];
1719  det = -det;
1720  }
1721 
1722  // set ftmp to the square of the trial geomagnetic field strength B (counts times FMATRIXSCALING)
1723  ftmp = -pthisMagCal->fmatB[6][j];
1724  for (l = CHX; l <= CHZ; l++)
1725  {
1726  ftmp += pthisMagCal->fA[l][l] *
1727  pthisMagCal->ftrV[l] *
1728  pthisMagCal->ftrV[l];
1729  }
1730 
1731  // normalize the ellipsoid matrix A to unit determinant
1732  f3x3matrixAeqAxScalar(pthisMagCal->fA, powf(det, -(ONETHIRD)));
1733 
1734  // convert the geomagnetic field strength B into uT for normalized soft iron matrix A and normalize
1735  pthisMagCal->ftrB = sqrtf(fabsf(ftmp)) * DEFAULTB * powf(det, -(ONESIXTH));
1736 
1737  // compute trial invW from the square root of A also with normalized determinant and hard iron offset in uT
1738  f3x3matrixAeqI(pthisMagCal->ftrinvW);
1739  for (l = CHX; l <= CHZ; l++)
1740  {
1741  pthisMagCal->ftrinvW[l][l] = sqrtf(fabsf(pthisMagCal->fA[l][l]));
1742  pthisMagCal->ftrV[l] = pthisMagCal->ftrV[l] *
1743  DEFAULTB +
1744  (float) iOffset[l] *
1745  pthisMag->fuTPerCount;
1746  }
1747 
1748  return;
1749 }
1750 
1751 // 10 element calibration using direct eigen-decomposition
1752 void fComputeMagCalibration10(struct MagCalibration *pthisMagCal,
1753  struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
1754 {
1755  // local variables
1756  float det; // matrix determinant
1757  float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
1758  float ftmp; // scratch variable
1759  int16 iOffset[3]; // offset to remove large DC hard iron bias in matrix
1760  int16 iCount; // number of measurements counted
1761  int8 i,
1762  j,
1763  k,
1764  l,
1765  m,
1766  n; // loop counters
1767 
1768  // compute fscaling to reduce multiplications later
1769  fscaling = pthisMag->fuTPerCount / DEFAULTB;
1770 
1771  // the offsets are guaranteed to be set from the first element but to avoid compiler error
1772  iOffset[CHX] = iOffset[CHY] = iOffset[CHZ] = 0;
1773 
1774  // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix fmatA
1775  for (m = 0; m < 10; m++)
1776  {
1777  for (n = m; n < 10; n++)
1778  {
1779  pthisMagCal->fmatA[m][n] = 0.0F;
1780  }
1781  }
1782 
1783  // add magnetic buffer entries into the 10x10 product matrix fmatA
1784  iCount = 0;
1785  for (j = 0; j < MAGBUFFSIZEX; j++)
1786  {
1787  for (k = 0; k < MAGBUFFSIZEY; k++)
1788  {
1789  if (pthisMagBuffer->index[j][k] != -1)
1790  {
1791  // use first valid magnetic buffer entry as estimate for offset to help solution (bit counts)
1792  if (iCount == 0)
1793  {
1794  for (l = CHX; l <= CHZ; l++)
1795  {
1796  iOffset[l] = pthisMagBuffer->iBs[l][j][k];
1797  }
1798  }
1799 
1800  // apply the fixed offset and scaling and enter into fvecA[6-8]
1801  for (l = CHX; l <= CHZ; l++)
1802  {
1803  pthisMagCal->fvecA[l + 6] = (float)
1804  (
1805  (int32) pthisMagBuffer->iBs[l][j][k] -
1806  (int32) iOffset[l]
1807  ) * fscaling;
1808  }
1809 
1810  // compute measurement vector elements fvecA[0-5] from fvecA[6-8]
1811  pthisMagCal->fvecA[0] = pthisMagCal->fvecA[6] * pthisMagCal->fvecA[6];
1812  pthisMagCal->fvecA[1] = 2.0F *
1813  pthisMagCal->fvecA[6] *
1814  pthisMagCal->fvecA[7];
1815  pthisMagCal->fvecA[2] = 2.0F *
1816  pthisMagCal->fvecA[6] *
1817  pthisMagCal->fvecA[8];
1818  pthisMagCal->fvecA[3] = pthisMagCal->fvecA[7] * pthisMagCal->fvecA[7];
1819  pthisMagCal->fvecA[4] = 2.0F *
1820  pthisMagCal->fvecA[7] *
1821  pthisMagCal->fvecA[8];
1822  pthisMagCal->fvecA[5] = pthisMagCal->fvecA[8] * pthisMagCal->fvecA[8];
1823 
1824  // accumulate the on-and above-diagonal terms of fmatA=Sigma{fvecA^T * fvecA}
1825  // with the exception of fmatA[9][9] which equals the number of measurements
1826  // update the right hand column [9] of fmatA[0-8][9] ignoring fmatA[9][9]
1827  for (m = 0; m < 9; m++)
1828  {
1829  pthisMagCal->fmatA[m][9] += pthisMagCal->fvecA[m];
1830  }
1831 
1832  // update the on and above diagonal terms of fmatA ignoring right hand column 9
1833  for (m = 0; m < 9; m++)
1834  {
1835  for (n = m; n < 9; n++)
1836  {
1837  pthisMagCal->fmatA[m][n] += pthisMagCal->fvecA[m] * pthisMagCal->fvecA[n];
1838  }
1839  }
1840 
1841  // increment the measurement counter for the next iteration
1842  iCount++;
1843  }
1844  }
1845  }
1846 
1847  // set the last element fmatA[9][9] to the number of measurements
1848  pthisMagCal->fmatA[9][9] = (float) iCount;
1849 
1850  // store the number of measurements accumulated (defensive programming, should never be needed)
1851  pthisMagBuffer->iMagBufferCount = iCount;
1852 
1853  // copy the above diagonal elements of symmetric product matrix fmatA to below the diagonal
1854  for (m = 1; m < 10; m++)
1855  {
1856  for (n = 0; n < m; n++)
1857  {
1858  pthisMagCal->fmatA[m][n] = pthisMagCal->fmatA[n][m];
1859  }
1860  }
1861 
1862  // set pthisMagCal->fvecA to the unsorted eigenvalues and fmatB to the unsorted normalized eigenvectors of fmatA
1863  fEigenCompute10(pthisMagCal->fmatA, pthisMagCal->fvecA, pthisMagCal->fmatB,
1864  10);
1865 
1866  // set ellipsoid matrix A from elements of the solution vector column j with smallest eigenvalue
1867  j = 0;
1868  for (i = 1; i < 10; i++)
1869  {
1870  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[j])
1871  {
1872  j = i;
1873  }
1874  }
1875 
1876  pthisMagCal->fA[0][0] = pthisMagCal->fmatB[0][j];
1877  pthisMagCal->fA[0][1] = pthisMagCal->fA[1][0] = pthisMagCal->fmatB[1][j];
1878  pthisMagCal->fA[0][2] = pthisMagCal->fA[2][0] = pthisMagCal->fmatB[2][j];
1879  pthisMagCal->fA[1][1] = pthisMagCal->fmatB[3][j];
1880  pthisMagCal->fA[1][2] = pthisMagCal->fA[2][1] = pthisMagCal->fmatB[4][j];
1881  pthisMagCal->fA[2][2] = pthisMagCal->fmatB[5][j];
1882 
1883  // negate entire solution if A has negative determinant
1884  det = f3x3matrixDetA(pthisMagCal->fA);
1885  if (det < 0.0F)
1886  {
1887  f3x3matrixAeqMinusA(pthisMagCal->fA);
1888  pthisMagCal->fmatB[6][j] = -pthisMagCal->fmatB[6][j];
1889  pthisMagCal->fmatB[7][j] = -pthisMagCal->fmatB[7][j];
1890  pthisMagCal->fmatB[8][j] = -pthisMagCal->fmatB[8][j];
1891  pthisMagCal->fmatB[9][j] = -pthisMagCal->fmatB[9][j];
1892  det = -det;
1893  }
1894 
1895  // compute the inverse of the ellipsoid matrix
1896  f3x3matrixAeqInvSymB(pthisMagCal->finvA, pthisMagCal->fA);
1897 
1898  // compute the trial hard iron vector in offset bit counts times FMATRIXSCALING
1899  for (l = CHX; l <= CHZ; l++)
1900  {
1901  pthisMagCal->ftrV[l] = 0.0F;
1902  for (m = CHX; m <= CHZ; m++)
1903  {
1904  pthisMagCal->ftrV[l] += pthisMagCal->finvA[l][m] * pthisMagCal->fmatB[m + 6][j];
1905  }
1906 
1907  pthisMagCal->ftrV[l] *= -0.5F;
1908  }
1909 
1910  // compute the trial geomagnetic field strength B in bit counts times FMATRIXSCALING
1911  pthisMagCal->ftrB = sqrtf(fabsf(pthisMagCal->fA[0][0] *
1912  pthisMagCal->ftrV[CHX] * pthisMagCal->ftrV[CHX] +
1913  2.0F * pthisMagCal->fA[0][1] *
1914  pthisMagCal->ftrV[CHX] * pthisMagCal->ftrV[CHY] +
1915  2.0F * pthisMagCal->fA[0][2] *
1916  pthisMagCal->ftrV[CHX] * pthisMagCal->ftrV[CHZ] +
1917  pthisMagCal->fA[1][1] * pthisMagCal->ftrV[CHY] *
1918  pthisMagCal->ftrV[CHY] + 2.0F *
1919  pthisMagCal->fA[1][2] * pthisMagCal->ftrV[CHY] *
1920  pthisMagCal->ftrV[CHZ] + pthisMagCal->fA[2][2] *
1921  pthisMagCal->ftrV[CHZ] * pthisMagCal->ftrV[CHZ] -
1922  pthisMagCal->fmatB[9][j]));
1923 
1924  // calculate the trial normalized fit error as a percentage
1925  pthisMagCal->ftrFitErrorpc = 50.0F * sqrtf(fabsf(pthisMagCal->fvecA[j]) /
1926  (float) pthisMagBuffer->iMagBufferCount) / (pthisMagCal->ftrB * pthisMagCal->ftrB);
1927 
1928  // correct for the measurement matrix offset and scaling and get the computed hard iron offset in uT
1929  for (l = CHX; l <= CHZ; l++)
1930  {
1931  pthisMagCal->ftrV[l] = pthisMagCal->ftrV[l] *
1932  DEFAULTB +
1933  (float) iOffset[l] *
1934  pthisMag->fuTPerCount;
1935  }
1936 
1937  // convert the trial geomagnetic field strength B into uT for un-normalized soft iron matrix A
1938  pthisMagCal->ftrB *= DEFAULTB;
1939 
1940  // normalize the ellipsoid matrix A to unit determinant and correct B by root of this multiplicative factor
1941  f3x3matrixAeqAxScalar(pthisMagCal->fA, powf(det, -(ONETHIRD)));
1942  pthisMagCal->ftrB *= powf(det, -(ONESIXTH));
1943 
1944  // compute trial invW from the square root of fA (both with normalized determinant)
1945  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted eigenvectors of fmatA
1946  // where fmatA holds the 3x3 matrix fA in its top left elements
1947  for (i = 0; i < 3; i++)
1948  {
1949  for (j = 0; j < 3; j++)
1950  {
1951  pthisMagCal->fmatA[i][j] = pthisMagCal->fA[i][j];
1952  }
1953  }
1954 
1955  fEigenCompute10(pthisMagCal->fmatA, pthisMagCal->fvecA, pthisMagCal->fmatB,
1956  3);
1957 
1958  // set pthisMagCal->fmatB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) = fmatB . diag(sqrt(sqrt(fvecA))
1959  for (j = 0; j < 3; j++) // loop over columns j
1960  {
1961  ftmp = sqrtf(sqrtf(fabsf(pthisMagCal->fvecA[j])));
1962  for (i = 0; i < 3; i++) // loop over rows i
1963  {
1964  pthisMagCal->fmatB[i][j] *= ftmp;
1965  }
1966  }
1967 
1968  // set ftrinvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T
1969  // = fmatB * fmatB^T = sqrt(fA) (guaranteed symmetric)
1970  // loop over rows
1971  for (i = 0; i < 3; i++)
1972  {
1973  // loop over on and above diagonal columns
1974  for (j = i; j < 3; j++)
1975  {
1976  pthisMagCal->ftrinvW[i][j] = 0.0F;
1977 
1978  // accumulate the matrix product
1979  for (k = 0; k < 3; k++)
1980  {
1981  pthisMagCal->ftrinvW[i][j] += pthisMagCal->fmatB[i][k] * pthisMagCal->fmatB[j][k];
1982  }
1983 
1984  // copy to below diagonal element
1985  pthisMagCal->ftrinvW[j][i] = pthisMagCal->ftrinvW[i][j];
1986  }
1987  }
1988 
1989  return;
1990 }
1991 #endif
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
Definition: magnetic.h:74
float fmatA[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:72
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 fFitErrorpc
current fit error %
Definition: magnetic.h:62
void fComputeEigSlice(float fmatA[10][10], float fmatB[10][10], float fvecA[10], int8 i, int8 j, int8 iMatrixSize)
Definition: matrix.c:554
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:51
#define CAL_INTERVAL_SECS
300s or 5min interval for regular calibration checks
Definition: magnetic.h:33
void f3x3matrixAeqAxScalar(float A[][3], float Scalar)
function multiplies all elements of 3x3 matrix A by the specified scalar
Definition: matrix.c:110
void iUpdateMagBuffer(struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag, int32 loopcounter)
Definition: magnetic.c:91
#define CALIBRATION_NVM_ADDR
start of final 4K (sector size) of 1M flash
Definition: frdm_k64f.h:147
void f3x3matrixAeqMinusA(float A[][3])
function negates all elements of 3x3 matrix A
Definition: matrix.c:129
float finvW[3][3]
current inverse soft iron matrix
Definition: magnetic.h:59
void fmatrixAeqInvA(float *A[], int8 iColInd[], int8 iRowInd[], int8 iPivot[], int8 isize, int8 *pierror)
Definition: matrix.c:648
#define MATRIX_10_SIZE
int32_t iValidMagCal
solver used: 0 (no calibration) or 4, 7, 10 element
Definition: magnetic.h:63
#define MESHDELTACOUNTS
magnetic buffer mesh spacing in counts (here 5uT)
Definition: magnetic.h:37
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:27
int32_t int32
Definition: sensor_fusion.h:41
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:49
#define CHZ
Used to access Z-channel entries in various data data structures.
Definition: sensor_fusion.h:62
The MagSensor structure stores raw and processed measurements for a 3-axis magnetic sensor...
#define ONETHIRD
one third
Definition: sensor_fusion.h:86
void fComputeMagCalibration4(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:1394
#define ONESIXTH
one sixth
Definition: sensor_fusion.h:87
int8_t i10ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:86
#define MINMEASUREMENTS4CAL
minimum number of measurements for 4 element calibration
Definition: magnetic.h:29
float fBc[3]
averaged calibrated measurement (uT)
void fEigenCompute10(float A[][10], float eigval[], float eigvec[][10], int8 n)
Definition: matrix.c:216
int8_t int8
Definition: sensor_fusion.h:39
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:61
float ftrV[3]
trial value of hard iron offset z, y, z (uT)
Definition: magnetic.h:66
float fCountsPeruT
counts per uT
int16_t tanarray[MAGBUFFSIZEX - 1]
array of tangents of (100 * angle)
Definition: magnetic.h:50
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:28
float fYTY
Y^T.Y for 4 element calibration = (iB^2)^2.
Definition: magnetic.h:76
float ftrinvW[3][3]
trial inverse soft iron matrix size
Definition: magnetic.h:67
#define MATRIX_7_SIZE
float ftrFitErrorpc
trial value of fit error %
Definition: magnetic.h:69
int16_t int16
Definition: sensor_fusion.h:40
float fV[3]
current hard iron offset x, y, z, (uT)
Definition: magnetic.h:58
int16_t iBs[3]
averaged uncalibrated measurement (counts)
int16_t iBs[3][MAGBUFFSIZEX][MAGBUFFSIZEY]
uncalibrated magnetometer readings
Definition: magnetic.h:48
void fRunMagCalibration(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag, int32 loopcounter)
Definition: magnetic.c:313
#define DEFAULTB
default geomagnetic field (uT)
Definition: magnetic.h:38
#define MAXBFITUT
maximum acceptable geomagnetic field B (uT) for valid calibration
Definition: magnetic.h:35
int32_t iMeanBs[3]
average magnetic measurement (counts)
Definition: magnetic.h:78
float ftrB
trial value of geomagnetic field magnitude in uT
Definition: magnetic.h:68
#define MAG_NVM_OFFSET
Definition: frdm_k64f.h:153
The sensor_fusion.h file implements the top level programming interface.
Magnetic Calibration Structure.
Definition: magnetic.h:55
void fComputeMagCalibration7(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:1589
float fA[3][3]
ellipsoid matrix A
Definition: magnetic.h:70
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:82
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:27
int32_t iSumBs[3]
sum of measurements in buffer (counts)
Definition: magnetic.h:77
#define PI
pi
Definition: sensor_fusion.h:81
#define MAXMEASUREMENTS
maximum number of measurements used for calibration
Definition: magnetic.h:32
void fUpdateMagCalibration7Slice(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:674
void fInitializeMagCalibration(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer)
Definition: magnetic.c:24
#define FUSION_HZ
(int) actual rate of fusion algorithm execution and sensor FIFO reads
float fuTPerCount
uT per count
#define MINMEASUREMENTS10CAL
minimum number of measurements for 10 element calibration
Definition: magnetic.h:31
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:81
int8_t i4ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:84
void fComputeMagCalibration10(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:1752
#define MINMEASUREMENTS7CAL
minimum number of measurements for 7 element calibration
Definition: magnetic.h:30
#define MINBFITUT
minimum acceptable geomagnetic field B (uT) for valid calibration
Definition: magnetic.h:34
int32_t itimeslice
counter for tine slicing magnetic calibration calculations
Definition: magnetic.h:79
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:60
float fBs[3]
averaged un-calibrated measurement (uT)
int8_t i7ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:85
void fUpdateMagCalibration4Slice(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:443
float f3x3matrixDetA(float A[][3])
function calculates the determinant of a 3x3 matrix
Definition: matrix.c:191
int8_t iMagBufferReadOnly
flag to denote that the magnetic measurement buffer is temporarily read only
Definition: magnetic.h:83
float fB
current geomagnetic field magnitude (uT)
Definition: magnetic.h:60
uint32_t uint32
Definition: sensor_fusion.h:44
float fmatB[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:73
int16_t iBc[3]
averaged calibrated measurement (counts)
void fUpdateMagCalibration10Slice(struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
Definition: magnetic.c:961
float fvecB[4]
scratch 4x1 vector used by calibration algorithms
Definition: magnetic.h:75
float finvA[3][3]
inverse of ellipsoid matrix A
Definition: magnetic.h:71
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:80
void fInvertMagCal(struct MagSensor *pthisMag, struct MagCalibration *pthisMagCal)
Definition: magnetic.c:285
float fBSq
square of fB (uT^2)
Definition: magnetic.h:61
void f3x3matrixAeqInvSymB(float A[][3], float B[][3])
Definition: matrix.c:150
#define FITERRORAGINGSECS
24 hours: time (s) for fit error to increase (age) by e=2.718
Definition: magnetic.h:36