capacitor-motioncal 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CapacitorMotionCal.podspec +23 -0
  2. package/README.md +126 -0
  3. package/android/build.gradle +67 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/denizak/motioncalibration/MotionCalibrationPlugin.java +248 -0
  6. package/android/src/main/jniLibs/arm64-v8a/libmotioncalibration.so +0 -0
  7. package/android/src/main/jniLibs/armeabi-v7a/libmotioncalibration.so +0 -0
  8. package/android/src/main/jniLibs/x86/libmotioncalibration.so +0 -0
  9. package/android/src/main/jniLibs/x86_64/libmotioncalibration.so +0 -0
  10. package/common/imuread.h +156 -0
  11. package/common/magcal.c +602 -0
  12. package/common/mahony.c +330 -0
  13. package/common/matrix.c +446 -0
  14. package/common/motioncalibration.c +7 -0
  15. package/common/motioncalibration.h +12 -0
  16. package/common/quality.c +257 -0
  17. package/common/rawdata.c +349 -0
  18. package/common/serialdata.c +501 -0
  19. package/common/visualize.c +131 -0
  20. package/dist/docs.json +292 -0
  21. package/dist/esm/definitions.d.ts +122 -0
  22. package/dist/esm/definitions.js +2 -0
  23. package/dist/esm/definitions.js.map +1 -0
  24. package/dist/esm/index.d.ts +4 -0
  25. package/dist/esm/index.js +7 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/web.d.ts +54 -0
  28. package/dist/esm/web.js +58 -0
  29. package/dist/esm/web.js.map +1 -0
  30. package/dist/plugin.cjs.js +72 -0
  31. package/dist/plugin.cjs.js.map +1 -0
  32. package/dist/plugin.js +75 -0
  33. package/dist/plugin.js.map +1 -0
  34. package/ios/Sources/MotionCalibrationPlugin/MotionCalibration-Bridging-Header.h +26 -0
  35. package/ios/Sources/MotionCalibrationPlugin/MotionCalibrationPlugin.swift +200 -0
  36. package/package.json +83 -0
@@ -0,0 +1,602 @@
1
+ // Copyright (c) 2014, Freescale Semiconductor, Inc.
2
+ // All rights reserved.
3
+ // vim: set ts=4:
4
+ //
5
+ // Redistribution and use in source and binary forms, with or without
6
+ // modification, are permitted provided that the following conditions are met:
7
+ // * Redistributions of source code must retain the above copyright
8
+ // notice, this list of conditions and the following disclaimer.
9
+ // * Redistributions in binary form must reproduce the above copyright
10
+ // notice, this list of conditions and the following disclaimer in the
11
+ // documentation and/or other materials provided with the distribution.
12
+ // * Neither the name of Freescale Semiconductor, Inc. nor the
13
+ // names of its contributors may be used to endorse or promote products
14
+ // derived from this software without specific prior written permission.
15
+ //
16
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ // DISCLAIMED. IN NO EVENT SHALL FREESCALE SEMICONDUCTOR, INC. BE LIABLE FOR ANY
20
+ // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
+ // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ //
27
+ // This file contains magnetic calibration functions. It is STRONGLY RECOMMENDED
28
+ // that the casual developer NOT TOUCH THIS FILE. The mathematics behind this file
29
+ // is extremely complex, and it will be very easy (almost inevitable) that you screw
30
+ // it up.
31
+ //
32
+ // Haha - This file has been edited! Please do not blame or pester NXP (formerly
33
+ // Freescale) about the "almost inevitable" issues!
34
+
35
+ #include "imuread.h"
36
+
37
+ #define FXOS8700_UTPERCOUNT 0.1f
38
+ #define DEFAULTB 50.0F // default geomagnetic field (uT)
39
+ #define X 0 // vector components
40
+ #define Y 1
41
+ #define Z 2
42
+ #define ONETHIRD 0.33333333F // one third
43
+ #define ONESIXTH 0.166666667F // one sixth
44
+ #define MINMEASUREMENTS4CAL 40 // minimum number of measurements for 4 element calibration
45
+ #define MINMEASUREMENTS7CAL 100 // minimum number of measurements for 7 element calibration
46
+ #define MINMEASUREMENTS10CAL 150 // minimum number of measurements for 10 element calibration
47
+ #define MINBFITUT 22.0F // minimum geomagnetic field B (uT) for valid calibration
48
+ #define MAXBFITUT 67.0F // maximum geomagnetic field B (uT) for valid calibration
49
+ #define FITERRORAGINGSECS 7200.0F // 2 hours: time for fit error to increase (age) by e=2.718
50
+
51
+ static void fUpdateCalibration4INV(MagCalibration_t *MagCal);
52
+ static void fUpdateCalibration7EIG(MagCalibration_t *MagCal);
53
+ static void fUpdateCalibration10EIG(MagCalibration_t *MagCal);
54
+
55
+ // run the magnetic calibration
56
+ int MagCal_Run(void)
57
+ {
58
+ int i, j; // loop counters
59
+ int isolver; // magnetic solver used
60
+ int count=0;
61
+ static int waitcount=0;
62
+
63
+ // only do the calibration occasionally
64
+ if (++waitcount < 20) return 0;
65
+ waitcount = 0;
66
+
67
+ // count number of data points
68
+ for (i=0; i < MAGBUFFSIZE; i++) {
69
+ if (magcal.valid[i]) count++;
70
+ }
71
+
72
+ if (count < MINMEASUREMENTS4CAL) return 0;
73
+
74
+ if (magcal.ValidMagCal) {
75
+ // age the existing fit error to avoid one good calibration locking out future updates
76
+ magcal.FitErrorAge *= 1.02f;
77
+ }
78
+
79
+ // is enough data collected
80
+ if (count < MINMEASUREMENTS7CAL) {
81
+ isolver = 4;
82
+ fUpdateCalibration4INV(&magcal); // 4 element matrix inversion calibration
83
+ if (magcal.trFitErrorpc < 12.0f) magcal.trFitErrorpc = 12.0f;
84
+ } else if (count < MINMEASUREMENTS10CAL) {
85
+ isolver = 7;
86
+ fUpdateCalibration7EIG(&magcal); // 7 element eigenpair calibration
87
+ if (magcal.trFitErrorpc < 7.5f) magcal.trFitErrorpc = 7.5f;
88
+ } else {
89
+ isolver = 10;
90
+ fUpdateCalibration10EIG(&magcal); // 10 element eigenpair calibration
91
+ }
92
+
93
+ // the trial geomagnetic field must be in range (earth is 22uT to 67uT)
94
+ if ((magcal.trB >= MINBFITUT) && (magcal.trB <= MAXBFITUT)) {
95
+ // always accept the calibration if
96
+ // 1: no previous calibration exists
97
+ // 2: the calibration fit is reduced or
98
+ // 3: an improved solver was used giving a good trial calibration (4% or under)
99
+ if ((magcal.ValidMagCal == 0) ||
100
+ (magcal.trFitErrorpc <= magcal.FitErrorAge) ||
101
+ ((isolver > magcal.ValidMagCal) && (magcal.trFitErrorpc <= 4.0F))) {
102
+ // accept the new calibration solution
103
+ //printf("new magnetic cal, B=%.2f uT\n", magcal.trB);
104
+ magcal.ValidMagCal = isolver;
105
+ magcal.FitError = magcal.trFitErrorpc;
106
+ if (magcal.trFitErrorpc > 2.0f) {
107
+ magcal.FitErrorAge = magcal.trFitErrorpc;
108
+ } else {
109
+ magcal.FitErrorAge = 2.0f;
110
+ }
111
+ magcal.B = magcal.trB;
112
+ magcal.FourBsq = 4.0F * magcal.trB * magcal.trB;
113
+ for (i = X; i <= Z; i++) {
114
+ magcal.V[i] = magcal.trV[i];
115
+ for (j = X; j <= Z; j++) {
116
+ magcal.invW[i][j] = magcal.trinvW[i][j];
117
+ }
118
+ }
119
+ return 1; // indicates new calibration applied
120
+ }
121
+ }
122
+ return 0;
123
+ }
124
+
125
+ // 4 element calibration using 4x4 matrix inverse
126
+ static void fUpdateCalibration4INV(MagCalibration_t *MagCal)
127
+ {
128
+ float fBp2; // fBp[X]^2+fBp[Y]^2+fBp[Z]^2
129
+ float fSumBp4; // sum of fBp2
130
+ float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
131
+ float fE; // error function = r^T.r
132
+ int16_t iOffset[3]; // offset to remove large DC hard iron bias in matrix
133
+ int16_t iCount; // number of measurements counted
134
+ int i, j, k; // loop counters
135
+
136
+ // working arrays for 4x4 matrix inversion
137
+ float *pfRows[4];
138
+ int8_t iColInd[4];
139
+ int8_t iRowInd[4];
140
+ int8_t iPivot[4];
141
+
142
+ // compute fscaling to reduce multiplications later
143
+ fscaling = FXOS8700_UTPERCOUNT / DEFAULTB;
144
+
145
+ // the trial inverse soft iron matrix invW always equals
146
+ // the identity matrix for 4 element calibration
147
+ f3x3matrixAeqI(MagCal->trinvW);
148
+
149
+ // zero fSumBp4=Y^T.Y, vecB=X^T.Y (4x1) and on and above
150
+ // diagonal elements of matA=X^T*X (4x4)
151
+ fSumBp4 = 0.0F;
152
+ for (i = 0; i < 4; i++) {
153
+ MagCal->vecB[i] = 0.0F;
154
+ for (j = i; j < 4; j++) {
155
+ MagCal->matA[i][j] = 0.0F;
156
+ }
157
+ }
158
+
159
+ // the offsets are guaranteed to be set from the first element but to avoid compiler error
160
+ iOffset[X] = iOffset[Y] = iOffset[Z] = 0;
161
+
162
+ // use from MINEQUATIONS up to MAXEQUATIONS entries from magnetic buffer to compute matrices
163
+ iCount = 0;
164
+ for (j = 0; j < MAGBUFFSIZE; j++) {
165
+ if (MagCal->valid[j]) {
166
+ // use first valid magnetic buffer entry as estimate (in counts) for offset
167
+ if (iCount == 0) {
168
+ for (k = X; k <= Z; k++) {
169
+ iOffset[k] = MagCal->BpFast[k][j];
170
+ }
171
+ }
172
+
173
+ // store scaled and offset fBp[XYZ] in vecA[0-2] and fBp[XYZ]^2 in vecA[3-5]
174
+ for (k = X; k <= Z; k++) {
175
+ MagCal->vecA[k] = (float)((int32_t)MagCal->BpFast[k][j]
176
+ - (int32_t)iOffset[k]) * fscaling;
177
+ MagCal->vecA[k + 3] = MagCal->vecA[k] * MagCal->vecA[k];
178
+ }
179
+
180
+ // calculate fBp2 = Bp[X]^2 + Bp[Y]^2 + Bp[Z]^2 (scaled uT^2)
181
+ fBp2 = MagCal->vecA[3] + MagCal->vecA[4] + MagCal->vecA[5];
182
+
183
+ // accumulate fBp^4 over all measurements into fSumBp4=Y^T.Y
184
+ fSumBp4 += fBp2 * fBp2;
185
+
186
+ // now we have fBp2, accumulate vecB[0-2] = X^T.Y =sum(Bp2.Bp[XYZ])
187
+ for (k = X; k <= Z; k++) {
188
+ MagCal->vecB[k] += MagCal->vecA[k] * fBp2;
189
+ }
190
+
191
+ //accumulate vecB[3] = X^T.Y =sum(fBp2)
192
+ MagCal->vecB[3] += fBp2;
193
+
194
+ // accumulate on and above-diagonal terms of matA = X^T.X ignoring matA[3][3]
195
+ MagCal->matA[0][0] += MagCal->vecA[X + 3];
196
+ MagCal->matA[0][1] += MagCal->vecA[X] * MagCal->vecA[Y];
197
+ MagCal->matA[0][2] += MagCal->vecA[X] * MagCal->vecA[Z];
198
+ MagCal->matA[0][3] += MagCal->vecA[X];
199
+ MagCal->matA[1][1] += MagCal->vecA[Y + 3];
200
+ MagCal->matA[1][2] += MagCal->vecA[Y] * MagCal->vecA[Z];
201
+ MagCal->matA[1][3] += MagCal->vecA[Y];
202
+ MagCal->matA[2][2] += MagCal->vecA[Z + 3];
203
+ MagCal->matA[2][3] += MagCal->vecA[Z];
204
+
205
+ // increment the counter for next iteration
206
+ iCount++;
207
+ }
208
+ }
209
+
210
+ // set the last element of the measurement matrix to the number of buffer elements used
211
+ MagCal->matA[3][3] = (float) iCount;
212
+
213
+ // store the number of measurements accumulated
214
+ MagCal->MagBufferCount = iCount;
215
+
216
+ // use above diagonal elements of symmetric matA to set both matB and matA to X^T.X
217
+ for (i = 0; i < 4; i++) {
218
+ for (j = i; j < 4; j++) {
219
+ MagCal->matB[i][j] = MagCal->matB[j][i]
220
+ = MagCal->matA[j][i] = MagCal->matA[i][j];
221
+ }
222
+ }
223
+
224
+ // calculate in situ inverse of matB = inv(X^T.X) (4x4) while matA still holds X^T.X
225
+ for (i = 0; i < 4; i++) {
226
+ pfRows[i] = MagCal->matB[i];
227
+ }
228
+ fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4);
229
+
230
+ // calculate vecA = solution beta (4x1) = inv(X^T.X).X^T.Y = matB * vecB
231
+ for (i = 0; i < 4; i++) {
232
+ MagCal->vecA[i] = 0.0F;
233
+ for (k = 0; k < 4; k++) {
234
+ MagCal->vecA[i] += MagCal->matB[i][k] * MagCal->vecB[k];
235
+ }
236
+ }
237
+
238
+ // calculate P = r^T.r = Y^T.Y - 2 * beta^T.(X^T.Y) + beta^T.(X^T.X).beta
239
+ // = fSumBp4 - 2 * vecA^T.vecB + vecA^T.matA.vecA
240
+ // first set P = Y^T.Y - 2 * beta^T.(X^T.Y) = SumBp4 - 2 * vecA^T.vecB
241
+ fE = 0.0F;
242
+ for (i = 0; i < 4; i++) {
243
+ fE += MagCal->vecA[i] * MagCal->vecB[i];
244
+ }
245
+ fE = fSumBp4 - 2.0F * fE;
246
+
247
+ // set vecB = (X^T.X).beta = matA.vecA
248
+ for (i = 0; i < 4; i++) {
249
+ MagCal->vecB[i] = 0.0F;
250
+ for (k = 0; k < 4; k++) {
251
+ MagCal->vecB[i] += MagCal->matA[i][k] * MagCal->vecA[k];
252
+ }
253
+ }
254
+
255
+ // complete calculation of P by adding beta^T.(X^T.X).beta = vecA^T * vecB
256
+ for (i = 0; i < 4; i++) {
257
+ fE += MagCal->vecB[i] * MagCal->vecA[i];
258
+ }
259
+
260
+ // compute the hard iron vector (in uT but offset and scaled by FMATRIXSCALING)
261
+ for (k = X; k <= Z; k++) {
262
+ MagCal->trV[k] = 0.5F * MagCal->vecA[k];
263
+ }
264
+
265
+ // compute the scaled geomagnetic field strength B (in uT but scaled by FMATRIXSCALING)
266
+ MagCal->trB = sqrtf(MagCal->vecA[3] + MagCal->trV[X] * MagCal->trV[X] +
267
+ MagCal->trV[Y] * MagCal->trV[Y] + MagCal->trV[Z] * MagCal->trV[Z]);
268
+
269
+ // calculate the trial fit error (percent) normalized to number of measurements
270
+ // and scaled geomagnetic field strength
271
+ MagCal->trFitErrorpc = sqrtf(fE / (float) MagCal->MagBufferCount) * 100.0F /
272
+ (2.0F * MagCal->trB * MagCal->trB);
273
+
274
+ // correct the hard iron estimate for FMATRIXSCALING and the offsets applied (result in uT)
275
+ for (k = X; k <= Z; k++) {
276
+ MagCal->trV[k] = MagCal->trV[k] * DEFAULTB
277
+ + (float)iOffset[k] * FXOS8700_UTPERCOUNT;
278
+ }
279
+
280
+ // correct the geomagnetic field strength B to correct scaling (result in uT)
281
+ MagCal->trB *= DEFAULTB;
282
+ }
283
+
284
+ // 7 element calibration using direct eigen-decomposition
285
+ static void fUpdateCalibration7EIG(MagCalibration_t *MagCal)
286
+ {
287
+ float det; // matrix determinant
288
+ float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
289
+ float ftmp; // scratch variable
290
+ int16_t iOffset[3]; // offset to remove large DC hard iron bias
291
+ int16_t iCount; // number of measurements counted
292
+ int i, j, k, m, n; // loop counters
293
+
294
+ // compute fscaling to reduce multiplications later
295
+ fscaling = FXOS8700_UTPERCOUNT / DEFAULTB;
296
+
297
+ // the offsets are guaranteed to be set from the first element but to avoid compiler error
298
+ iOffset[X] = iOffset[Y] = iOffset[Z] = 0;
299
+
300
+ // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix matA
301
+ for (m = 0; m < 7; m++) {
302
+ for (n = m; n < 7; n++) {
303
+ MagCal->matA[m][n] = 0.0F;
304
+ }
305
+ }
306
+
307
+ // place from MINEQUATIONS to MAXEQUATIONS entries into product matrix matA
308
+ iCount = 0;
309
+ for (j = 0; j < MAGBUFFSIZE; j++) {
310
+ if (MagCal->valid[j]) {
311
+ // use first valid magnetic buffer entry as offset estimate (bit counts)
312
+ if (iCount == 0) {
313
+ for (k = X; k <= Z; k++) {
314
+ iOffset[k] = MagCal->BpFast[k][j];
315
+ }
316
+ }
317
+
318
+ // apply the offset and scaling and store in vecA
319
+ for (k = X; k <= Z; k++) {
320
+ MagCal->vecA[k + 3] = (float)((int32_t)MagCal->BpFast[k][j]
321
+ - (int32_t)iOffset[k]) * fscaling;
322
+ MagCal->vecA[k] = MagCal->vecA[k + 3] * MagCal->vecA[k + 3];
323
+ }
324
+
325
+ // accumulate the on-and above-diagonal terms of
326
+ // MagCal->matA=Sigma{vecA^T * vecA}
327
+ // with the exception of matA[6][6] which will sum to the number
328
+ // of measurements and remembering that vecA[6] equals 1.0F
329
+ // update the right hand column [6] of matA except for matA[6][6]
330
+ for (m = 0; m < 6; m++) {
331
+ MagCal->matA[m][6] += MagCal->vecA[m];
332
+ }
333
+ // update the on and above diagonal terms except for right hand column 6
334
+ for (m = 0; m < 6; m++) {
335
+ for (n = m; n < 6; n++) {
336
+ MagCal->matA[m][n] += MagCal->vecA[m] * MagCal->vecA[n];
337
+ }
338
+ }
339
+
340
+ // increment the measurement counter for the next iteration
341
+ iCount++;
342
+ }
343
+ }
344
+
345
+ // finally set the last element matA[6][6] to the number of measurements
346
+ MagCal->matA[6][6] = (float) iCount;
347
+
348
+ // store the number of measurements accumulated
349
+ MagCal->MagBufferCount = iCount;
350
+
351
+ // copy the above diagonal elements of matA to below the diagonal
352
+ for (m = 1; m < 7; m++) {
353
+ for (n = 0; n < m; n++) {
354
+ MagCal->matA[m][n] = MagCal->matA[n][m];
355
+ }
356
+ }
357
+
358
+ // set tmpA7x1 to the unsorted eigenvalues and matB to the unsorted eigenvectors of matA
359
+ eigencompute(MagCal->matA, MagCal->vecA, MagCal->matB, 7);
360
+
361
+ // find the smallest eigenvalue
362
+ j = 0;
363
+ for (i = 1; i < 7; i++) {
364
+ if (MagCal->vecA[i] < MagCal->vecA[j]) {
365
+ j = i;
366
+ }
367
+ }
368
+
369
+ // set ellipsoid matrix A to the solution vector with smallest eigenvalue,
370
+ // compute its determinant and the hard iron offset (scaled and offset)
371
+ f3x3matrixAeqScalar(MagCal->A, 0.0F);
372
+ det = 1.0F;
373
+ for (k = X; k <= Z; k++) {
374
+ MagCal->A[k][k] = MagCal->matB[k][j];
375
+ det *= MagCal->A[k][k];
376
+ MagCal->trV[k] = -0.5F * MagCal->matB[k + 3][j] / MagCal->A[k][k];
377
+ }
378
+
379
+ // negate A if it has negative determinant
380
+ if (det < 0.0F) {
381
+ f3x3matrixAeqMinusA(MagCal->A);
382
+ MagCal->matB[6][j] = -MagCal->matB[6][j];
383
+ det = -det;
384
+ }
385
+
386
+ // set ftmp to the square of the trial geomagnetic field strength B
387
+ // (counts times FMATRIXSCALING)
388
+ ftmp = -MagCal->matB[6][j];
389
+ for (k = X; k <= Z; k++) {
390
+ ftmp += MagCal->A[k][k] * MagCal->trV[k] * MagCal->trV[k];
391
+ }
392
+
393
+ // calculate the trial normalized fit error as a percentage
394
+ MagCal->trFitErrorpc = 50.0F *
395
+ sqrtf(fabs(MagCal->vecA[j]) / (float) MagCal->MagBufferCount) / fabs(ftmp);
396
+
397
+ // normalize the ellipsoid matrix A to unit determinant
398
+ f3x3matrixAeqAxScalar(MagCal->A, powf(det, -(ONETHIRD)));
399
+
400
+ // convert the geomagnetic field strength B into uT for normalized
401
+ // soft iron matrix A and normalize
402
+ MagCal->trB = sqrtf(fabs(ftmp)) * DEFAULTB * powf(det, -(ONESIXTH));
403
+
404
+ // compute trial invW from the square root of A also with normalized
405
+ // determinant and hard iron offset in uT
406
+ f3x3matrixAeqI(MagCal->trinvW);
407
+ for (k = X; k <= Z; k++) {
408
+ MagCal->trinvW[k][k] = sqrtf(fabs(MagCal->A[k][k]));
409
+ MagCal->trV[k] = MagCal->trV[k] * DEFAULTB + (float)iOffset[k] * FXOS8700_UTPERCOUNT;
410
+ }
411
+ }
412
+
413
+ // 10 element calibration using direct eigen-decomposition
414
+ static void fUpdateCalibration10EIG(MagCalibration_t *MagCal)
415
+ {
416
+ float det; // matrix determinant
417
+ float fscaling; // set to FUTPERCOUNT * FMATRIXSCALING
418
+ float ftmp; // scratch variable
419
+ int16_t iOffset[3]; // offset to remove large DC hard iron bias in matrix
420
+ int16_t iCount; // number of measurements counted
421
+ int i, j, k, m, n; // loop counters
422
+
423
+ // compute fscaling to reduce multiplications later
424
+ fscaling = FXOS8700_UTPERCOUNT / DEFAULTB;
425
+
426
+ // the offsets are guaranteed to be set from the first element but to avoid compiler error
427
+ iOffset[X] = iOffset[Y] = iOffset[Z] = 0;
428
+
429
+ // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix matA
430
+ for (m = 0; m < 10; m++) {
431
+ for (n = m; n < 10; n++) {
432
+ MagCal->matA[m][n] = 0.0F;
433
+ }
434
+ }
435
+
436
+ // sum between MINEQUATIONS to MAXEQUATIONS entries into the 10x10 product matrix matA
437
+ iCount = 0;
438
+ for (j = 0; j < MAGBUFFSIZE; j++) {
439
+ if (MagCal->valid[j]) {
440
+ // use first valid magnetic buffer entry as estimate for offset
441
+ // to help solution (bit counts)
442
+ if (iCount == 0) {
443
+ for (k = X; k <= Z; k++) {
444
+ iOffset[k] = MagCal->BpFast[k][j];
445
+ }
446
+ }
447
+
448
+ // apply the fixed offset and scaling and enter into vecA[6-8]
449
+ for (k = X; k <= Z; k++) {
450
+ MagCal->vecA[k + 6] = (float)((int32_t)MagCal->BpFast[k][j]
451
+ - (int32_t)iOffset[k]) * fscaling;
452
+ }
453
+
454
+ // compute measurement vector elements vecA[0-5] from vecA[6-8]
455
+ MagCal->vecA[0] = MagCal->vecA[6] * MagCal->vecA[6];
456
+ MagCal->vecA[1] = 2.0F * MagCal->vecA[6] * MagCal->vecA[7];
457
+ MagCal->vecA[2] = 2.0F * MagCal->vecA[6] * MagCal->vecA[8];
458
+ MagCal->vecA[3] = MagCal->vecA[7] * MagCal->vecA[7];
459
+ MagCal->vecA[4] = 2.0F * MagCal->vecA[7] * MagCal->vecA[8];
460
+ MagCal->vecA[5] = MagCal->vecA[8] * MagCal->vecA[8];
461
+
462
+ // accumulate the on-and above-diagonal terms of matA=Sigma{vecA^T * vecA}
463
+ // with the exception of matA[9][9] which equals the number of measurements
464
+ // update the right hand column [9] of matA[0-8][9] ignoring matA[9][9]
465
+ for (m = 0; m < 9; m++) {
466
+ MagCal->matA[m][9] += MagCal->vecA[m];
467
+ }
468
+ // update the on and above diagonal terms of matA ignoring right hand column 9
469
+ for (m = 0; m < 9; m++) {
470
+ for (n = m; n < 9; n++) {
471
+ MagCal->matA[m][n] += MagCal->vecA[m] * MagCal->vecA[n];
472
+ }
473
+ }
474
+
475
+ // increment the measurement counter for the next iteration
476
+ iCount++;
477
+ }
478
+ }
479
+
480
+ // set the last element matA[9][9] to the number of measurements
481
+ MagCal->matA[9][9] = (float) iCount;
482
+
483
+ // store the number of measurements accumulated
484
+ MagCal->MagBufferCount = iCount;
485
+
486
+ // copy the above diagonal elements of symmetric product matrix matA to below the diagonal
487
+ for (m = 1; m < 10; m++) {
488
+ for (n = 0; n < m; n++) {
489
+ MagCal->matA[m][n] = MagCal->matA[n][m];
490
+ }
491
+ }
492
+
493
+ // set MagCal->vecA to the unsorted eigenvalues and matB to the unsorted
494
+ // normalized eigenvectors of matA
495
+ eigencompute(MagCal->matA, MagCal->vecA, MagCal->matB, 10);
496
+
497
+ // set ellipsoid matrix A from elements of the solution vector column j with
498
+ // smallest eigenvalue
499
+ j = 0;
500
+ for (i = 1; i < 10; i++) {
501
+ if (MagCal->vecA[i] < MagCal->vecA[j]) {
502
+ j = i;
503
+ }
504
+ }
505
+ MagCal->A[0][0] = MagCal->matB[0][j];
506
+ MagCal->A[0][1] = MagCal->A[1][0] = MagCal->matB[1][j];
507
+ MagCal->A[0][2] = MagCal->A[2][0] = MagCal->matB[2][j];
508
+ MagCal->A[1][1] = MagCal->matB[3][j];
509
+ MagCal->A[1][2] = MagCal->A[2][1] = MagCal->matB[4][j];
510
+ MagCal->A[2][2] = MagCal->matB[5][j];
511
+
512
+ // negate entire solution if A has negative determinant
513
+ det = f3x3matrixDetA(MagCal->A);
514
+ if (det < 0.0F) {
515
+ f3x3matrixAeqMinusA(MagCal->A);
516
+ MagCal->matB[6][j] = -MagCal->matB[6][j];
517
+ MagCal->matB[7][j] = -MagCal->matB[7][j];
518
+ MagCal->matB[8][j] = -MagCal->matB[8][j];
519
+ MagCal->matB[9][j] = -MagCal->matB[9][j];
520
+ det = -det;
521
+ }
522
+
523
+ // compute the inverse of the ellipsoid matrix
524
+ f3x3matrixAeqInvSymB(MagCal->invA, MagCal->A);
525
+
526
+ // compute the trial hard iron vector in offset bit counts times FMATRIXSCALING
527
+ for (k = X; k <= Z; k++) {
528
+ MagCal->trV[k] = 0.0F;
529
+ for (m = X; m <= Z; m++) {
530
+ MagCal->trV[k] += MagCal->invA[k][m] * MagCal->matB[m + 6][j];
531
+ }
532
+ MagCal->trV[k] *= -0.5F;
533
+ }
534
+
535
+ // compute the trial geomagnetic field strength B in bit counts times FMATRIXSCALING
536
+ MagCal->trB = sqrtf(fabs(MagCal->A[0][0] * MagCal->trV[X] * MagCal->trV[X] +
537
+ 2.0F * MagCal->A[0][1] * MagCal->trV[X] * MagCal->trV[Y] +
538
+ 2.0F * MagCal->A[0][2] * MagCal->trV[X] * MagCal->trV[Z] +
539
+ MagCal->A[1][1] * MagCal->trV[Y] * MagCal->trV[Y] +
540
+ 2.0F * MagCal->A[1][2] * MagCal->trV[Y] * MagCal->trV[Z] +
541
+ MagCal->A[2][2] * MagCal->trV[Z] * MagCal->trV[Z] - MagCal->matB[9][j]));
542
+
543
+ // calculate the trial normalized fit error as a percentage
544
+ MagCal->trFitErrorpc = 50.0F * sqrtf(
545
+ fabs(MagCal->vecA[j]) / (float) MagCal->MagBufferCount) /
546
+ (MagCal->trB * MagCal->trB);
547
+
548
+ // correct for the measurement matrix offset and scaling and
549
+ // get the computed hard iron offset in uT
550
+ for (k = X; k <= Z; k++) {
551
+ MagCal->trV[k] = MagCal->trV[k] * DEFAULTB + (float)iOffset[k] * FXOS8700_UTPERCOUNT;
552
+ }
553
+
554
+ // convert the trial geomagnetic field strength B into uT for
555
+ // un-normalized soft iron matrix A
556
+ MagCal->trB *= DEFAULTB;
557
+
558
+ // normalize the ellipsoid matrix A to unit determinant and
559
+ // correct B by root of this multiplicative factor
560
+ f3x3matrixAeqAxScalar(MagCal->A, powf(det, -(ONETHIRD)));
561
+ MagCal->trB *= powf(det, -(ONESIXTH));
562
+
563
+ // compute trial invW from the square root of fA (both with normalized determinant)
564
+ // set vecA to the unsorted eigenvalues and matB to the unsorted eigenvectors of matA
565
+ // where matA holds the 3x3 matrix fA in its top left elements
566
+ for (i = 0; i < 3; i++) {
567
+ for (j = 0; j < 3; j++) {
568
+ MagCal->matA[i][j] = MagCal->A[i][j];
569
+ }
570
+ }
571
+ eigencompute(MagCal->matA, MagCal->vecA, MagCal->matB, 3);
572
+
573
+ // set MagCal->matB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) =
574
+ // matB . diag(sqrt(sqrt(vecA))
575
+ for (j = 0; j < 3; j++) { // loop over columns j
576
+ ftmp = sqrtf(sqrtf(fabs(MagCal->vecA[j])));
577
+ for (i = 0; i < 3; i++) { // loop over rows i
578
+ MagCal->matB[i][j] *= ftmp;
579
+ }
580
+ }
581
+
582
+ // set trinvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T =
583
+ // matB * matB^T = sqrt(fA) (guaranteed symmetric)
584
+ // loop over rows
585
+ for (i = 0; i < 3; i++) {
586
+ // loop over on and above diagonal columns
587
+ for (j = i; j < 3; j++) {
588
+ MagCal->trinvW[i][j] = 0.0F;
589
+ // accumulate the matrix product
590
+ for (k = 0; k < 3; k++) {
591
+ MagCal->trinvW[i][j] += MagCal->matB[i][k] * MagCal->matB[j][k];
592
+ }
593
+ // copy to below diagonal element
594
+ MagCal->trinvW[j][i] = MagCal->trinvW[i][j];
595
+ }
596
+ }
597
+ }
598
+
599
+
600
+
601
+
602
+