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