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