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.
- package/CapacitorMotionCal.podspec +23 -0
- package/README.md +126 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/denizak/motioncalibration/MotionCalibrationPlugin.java +248 -0
- package/android/src/main/jniLibs/arm64-v8a/libmotioncalibration.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libmotioncalibration.so +0 -0
- package/android/src/main/jniLibs/x86/libmotioncalibration.so +0 -0
- package/android/src/main/jniLibs/x86_64/libmotioncalibration.so +0 -0
- package/common/imuread.h +156 -0
- package/common/magcal.c +602 -0
- package/common/mahony.c +330 -0
- package/common/matrix.c +446 -0
- package/common/motioncalibration.c +7 -0
- package/common/motioncalibration.h +12 -0
- package/common/quality.c +257 -0
- package/common/rawdata.c +349 -0
- package/common/serialdata.c +501 -0
- package/common/visualize.c +131 -0
- package/dist/docs.json +292 -0
- package/dist/esm/definitions.d.ts +122 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +54 -0
- package/dist/esm/web.js +58 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +72 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +75 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/MotionCalibrationPlugin/MotionCalibration-Bridging-Header.h +26 -0
- package/ios/Sources/MotionCalibrationPlugin/MotionCalibrationPlugin.swift +200 -0
- package/package.json +83 -0
package/common/magcal.c
ADDED
|
@@ -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
|
+
|