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/quality.c
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#include "imuread.h"
|
|
2
|
+
|
|
3
|
+
// Discussion of what these 4 quality metrics really do
|
|
4
|
+
// https://forum.pjrc.com/threads/59277-Motion-Sensor-Calibration-Tool-Parameter-Understanding
|
|
5
|
+
|
|
6
|
+
//static int countdown=1000;
|
|
7
|
+
//static int pr=0;
|
|
8
|
+
|
|
9
|
+
// return 0 to 99 - which region on the sphere (100 of equal surface area)
|
|
10
|
+
static int sphere_region(float x, float y, float z)
|
|
11
|
+
{
|
|
12
|
+
float latitude, longitude;
|
|
13
|
+
int region;
|
|
14
|
+
|
|
15
|
+
//if (pr) printf(" region %.1f,%.1f,%.1f ", x, y, z);
|
|
16
|
+
|
|
17
|
+
// longitude = 0 to 2pi (meaning 0 to 360 degrees)
|
|
18
|
+
longitude = atan2f(y, x) + (float)M_PI;
|
|
19
|
+
// latitude = -pi/2 to +pi/2 (meaning -90 to +90 degrees)
|
|
20
|
+
latitude = (float)(M_PI / 2.0) - atan2f(sqrtf(x * x + y * y), z);
|
|
21
|
+
|
|
22
|
+
//if (pr) printf(" lat=%.1f", latitude * (float)(180.0 / M_PI));
|
|
23
|
+
//if (pr) printf(",lon=%.1f ", longitude * (float)(180.0 / M_PI));
|
|
24
|
+
|
|
25
|
+
// https://etna.mcs.kent.edu/vol.25.2006/pp309-327.dir/pp309-327.html
|
|
26
|
+
// sphere equations....
|
|
27
|
+
// area of unit sphere = 4*pi
|
|
28
|
+
// area of unit sphere cap = 2*pi*h h = cap height
|
|
29
|
+
// lattitude of unit sphere cap = arcsin(1 - h)
|
|
30
|
+
if (latitude > 1.37046f /* 78.52 deg */) {
|
|
31
|
+
// arctic cap, 1 region
|
|
32
|
+
region = 0;
|
|
33
|
+
} else if (latitude < -1.37046f /* -78.52 deg */) {
|
|
34
|
+
// antarctic cap, 1 region
|
|
35
|
+
region = 99;
|
|
36
|
+
} else if (latitude > 0.74776f /* 42.84 deg */ || latitude < -0.74776f ) {
|
|
37
|
+
// temperate zones, 15 regions each
|
|
38
|
+
region = floorf(longitude * (float)(15.0 / (M_PI * 2.0)));
|
|
39
|
+
if (region < 0) region = 0;
|
|
40
|
+
else if (region > 14) region = 14;
|
|
41
|
+
if (latitude > 0.0) {
|
|
42
|
+
region += 1; // 1 to 15
|
|
43
|
+
} else {
|
|
44
|
+
region += 84; // 84 to 98
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// tropic zones, 34 regions each
|
|
48
|
+
region = floorf(longitude * (float)(34.0 / (M_PI * 2.0)));
|
|
49
|
+
if (region < 0) region = 0;
|
|
50
|
+
else if (region > 33) region = 33;
|
|
51
|
+
if (latitude >= 0.0) {
|
|
52
|
+
region += 16; // 16 to 49
|
|
53
|
+
} else {
|
|
54
|
+
region += 50; // 50 to 83
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//if (pr) printf(" %d\n", region);
|
|
58
|
+
return region;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
static int count=0;
|
|
63
|
+
static int spheredist[100];
|
|
64
|
+
static Point_t spheredata[100];
|
|
65
|
+
static Point_t sphereideal[100];
|
|
66
|
+
static int sphereideal_initialized=0;
|
|
67
|
+
static float magnitude[MAGBUFFSIZE];
|
|
68
|
+
static float quality_gaps_buffer;
|
|
69
|
+
static float quality_variance_buffer;
|
|
70
|
+
static float quality_wobble_buffer;
|
|
71
|
+
static int quality_gaps_computed=0;
|
|
72
|
+
static int quality_variance_computed=0;
|
|
73
|
+
static int quality_wobble_computed=0;
|
|
74
|
+
|
|
75
|
+
void quality_reset(void)
|
|
76
|
+
{
|
|
77
|
+
float longitude;
|
|
78
|
+
int i;
|
|
79
|
+
/*
|
|
80
|
+
countdown--;
|
|
81
|
+
if (countdown == 0) {
|
|
82
|
+
countdown = 1000;
|
|
83
|
+
pr = 1;
|
|
84
|
+
} else {
|
|
85
|
+
pr = 0;
|
|
86
|
+
}
|
|
87
|
+
*/
|
|
88
|
+
count=0;
|
|
89
|
+
memset(spheredist, 0, sizeof(spheredist));
|
|
90
|
+
memset(spheredata, 0, sizeof(spheredata));
|
|
91
|
+
if (!sphereideal_initialized) {
|
|
92
|
+
sphereideal[0].x = 0.0f;
|
|
93
|
+
sphereideal[0].y = 0.0f;
|
|
94
|
+
sphereideal[0].z = 1.0f;
|
|
95
|
+
for (i=1; i <= 15; i++) {
|
|
96
|
+
longitude = ((float)(i - 1) + 0.5f) * (M_PI * 2.0 / 15.0);
|
|
97
|
+
sphereideal[i].x = cosf(longitude) * cosf(1.05911) * -1.0f;
|
|
98
|
+
sphereideal[i].y = sinf(longitude) * cosf(1.05911) * -1.0f;
|
|
99
|
+
sphereideal[i].z = sinf(1.05911);
|
|
100
|
+
}
|
|
101
|
+
for (i=16; i <= 49; i++) {
|
|
102
|
+
longitude = ((float)(i - 16) + 0.5f) * (M_PI * 2.0 / 34.0);
|
|
103
|
+
sphereideal[i].x = cosf(longitude) * cos(0.37388) * -1.0f;
|
|
104
|
+
sphereideal[i].y = sinf(longitude) * cos(0.37388) * -1.0f;
|
|
105
|
+
sphereideal[i].z = sinf(0.37388);
|
|
106
|
+
}
|
|
107
|
+
for (i=50; i <= 83; i++) {
|
|
108
|
+
longitude = ((float)(i - 50) + 0.5f) * (M_PI * 2.0 / 34.0);
|
|
109
|
+
sphereideal[i].x = cosf(longitude) * cos(0.37388) * -1.0f;
|
|
110
|
+
sphereideal[i].y = sinf(longitude) * cos(0.37388) * -1.0f;
|
|
111
|
+
sphereideal[i].z = sinf(-0.37388);
|
|
112
|
+
}
|
|
113
|
+
for (i=84; i <= 98; i++) {
|
|
114
|
+
longitude = ((float)(i - 1) + 0.5f) * (M_PI * 2.0 / 15.0);
|
|
115
|
+
sphereideal[i].x = cosf(longitude) * cosf(1.05911) * -1.0f;
|
|
116
|
+
sphereideal[i].y = sinf(longitude) * cosf(1.05911) * -1.0f;
|
|
117
|
+
sphereideal[i].z = sinf(-1.05911);
|
|
118
|
+
}
|
|
119
|
+
sphereideal[99].x = 0.0f;
|
|
120
|
+
sphereideal[99].y = 0.0f;
|
|
121
|
+
sphereideal[99].z = -1.0f;
|
|
122
|
+
sphereideal_initialized = 1;
|
|
123
|
+
}
|
|
124
|
+
quality_gaps_computed = 0;
|
|
125
|
+
quality_variance_computed = 0;
|
|
126
|
+
quality_wobble_computed = 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void quality_update(const Point_t *point)
|
|
130
|
+
{
|
|
131
|
+
float x, y, z;
|
|
132
|
+
int region;
|
|
133
|
+
|
|
134
|
+
x = point->x;
|
|
135
|
+
y = point->y;
|
|
136
|
+
z = point->z;
|
|
137
|
+
magnitude[count] = sqrtf(x * x + y * y + z * z);
|
|
138
|
+
region = sphere_region(x, y, z);
|
|
139
|
+
spheredist[region]++;
|
|
140
|
+
spheredata[region].x += x;
|
|
141
|
+
spheredata[region].y += y;
|
|
142
|
+
spheredata[region].z += z;
|
|
143
|
+
count++;
|
|
144
|
+
quality_gaps_computed = 0;
|
|
145
|
+
quality_variance_computed = 0;
|
|
146
|
+
quality_wobble_computed = 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// How many surface gaps
|
|
150
|
+
float quality_surface_gap_error(void)
|
|
151
|
+
{
|
|
152
|
+
float error=0.0f;
|
|
153
|
+
int i, num;
|
|
154
|
+
|
|
155
|
+
if (quality_gaps_computed) return quality_gaps_buffer;
|
|
156
|
+
for (i=0; i < 100; i++) {
|
|
157
|
+
num = spheredist[i];
|
|
158
|
+
if (num == 0) {
|
|
159
|
+
error += 1.0f;
|
|
160
|
+
} else if (num == 1) {
|
|
161
|
+
error += 0.2f;
|
|
162
|
+
} else if (num == 2) {
|
|
163
|
+
error += 0.01f;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
quality_gaps_buffer = error;
|
|
167
|
+
quality_gaps_computed = 1;
|
|
168
|
+
return quality_gaps_buffer;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Variance in magnitude
|
|
172
|
+
float quality_magnitude_variance_error(void)
|
|
173
|
+
{
|
|
174
|
+
float sum, mean, diff, variance;
|
|
175
|
+
int i;
|
|
176
|
+
|
|
177
|
+
if (quality_variance_computed) return quality_variance_buffer;
|
|
178
|
+
sum = 0.0f;
|
|
179
|
+
for (i=0; i < count; i++) {
|
|
180
|
+
sum += magnitude[i];
|
|
181
|
+
}
|
|
182
|
+
mean = sum / (float)count;
|
|
183
|
+
variance = 0.0f;
|
|
184
|
+
for (i=0; i < count; i++) {
|
|
185
|
+
diff = magnitude[i] - mean;
|
|
186
|
+
variance += diff * diff;
|
|
187
|
+
}
|
|
188
|
+
variance /= (float)count;
|
|
189
|
+
quality_variance_buffer = sqrtf(variance) / mean * 100.0f;
|
|
190
|
+
quality_variance_computed = 1;
|
|
191
|
+
return quality_variance_buffer;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Offset of piecewise average data from ideal sphere surface
|
|
195
|
+
float quality_wobble_error(void)
|
|
196
|
+
{
|
|
197
|
+
float sum, radius, x, y, z, xi, yi, zi;
|
|
198
|
+
float xoff=0.0f, yoff=0.0f, zoff=0.0f;
|
|
199
|
+
int i, n=0;
|
|
200
|
+
|
|
201
|
+
if (quality_wobble_computed) return quality_wobble_buffer;
|
|
202
|
+
sum = 0.0f;
|
|
203
|
+
for (i=0; i < count; i++) {
|
|
204
|
+
sum += magnitude[i];
|
|
205
|
+
}
|
|
206
|
+
radius = sum / (float)count;
|
|
207
|
+
//if (pr) printf(" radius = %.2f\n", radius);
|
|
208
|
+
for (i=0; i < 100; i++) {
|
|
209
|
+
if (spheredist[i] > 0) {
|
|
210
|
+
//if (pr) printf(" i=%3d", i);
|
|
211
|
+
x = spheredata[i].x / (float)spheredist[i];
|
|
212
|
+
y = spheredata[i].y / (float)spheredist[i];
|
|
213
|
+
z = spheredata[i].z / (float)spheredist[i];
|
|
214
|
+
//if (pr) printf(" at: %5.1f %5.1f %5.1f :", x, y, z);
|
|
215
|
+
xi = sphereideal[i].x * radius;
|
|
216
|
+
yi = sphereideal[i].y * radius;
|
|
217
|
+
zi = sphereideal[i].z * radius;
|
|
218
|
+
//if (pr) printf(" ideal: %5.1f %5.1f %5.1f :", xi, yi, zi);
|
|
219
|
+
xoff += x - xi;
|
|
220
|
+
yoff += y - yi;
|
|
221
|
+
zoff += z - zi;
|
|
222
|
+
//if (pr) printf("\n");
|
|
223
|
+
n++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (n == 0) return 100.0f;
|
|
227
|
+
//if (pr) printf(" off = %.2f, %.2f, %.2f\n", xoff, yoff, zoff);
|
|
228
|
+
xoff /= (float)n;
|
|
229
|
+
yoff /= (float)n;
|
|
230
|
+
zoff /= (float)n;
|
|
231
|
+
//if (pr) printf(" off = %.2f, %.2f, %.2f\n", xoff, yoff, zoff);
|
|
232
|
+
quality_wobble_buffer = sqrtf(xoff * xoff + yoff * yoff + zoff * zoff) / radius * 100.0f;
|
|
233
|
+
quality_wobble_computed = 1;
|
|
234
|
+
return quality_wobble_buffer;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Freescale's algorithm fit error
|
|
238
|
+
float quality_spherical_fit_error(void)
|
|
239
|
+
{
|
|
240
|
+
return magcal.FitError;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
short is_send_cal_available(void) {
|
|
244
|
+
float gaps, variance, wobble, fiterror;
|
|
245
|
+
|
|
246
|
+
gaps = quality_surface_gap_error();
|
|
247
|
+
variance = quality_magnitude_variance_error();
|
|
248
|
+
wobble = quality_wobble_error();
|
|
249
|
+
fiterror = quality_spherical_fit_error();
|
|
250
|
+
if (gaps < 15.0f && variance < 4.5f && wobble < 4.0f && fiterror < 5.0f) {
|
|
251
|
+
return 1;
|
|
252
|
+
} else if (gaps > 20.0f && variance > 5.0f && wobble > 5.0f && fiterror > 6.0f) {
|
|
253
|
+
return 0;
|
|
254
|
+
} else {
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
package/common/rawdata.c
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#include "imuread.h"
|
|
2
|
+
|
|
3
|
+
extern float draw_points[MAGBUFFSIZE * 3];
|
|
4
|
+
|
|
5
|
+
static int rawcount=OVERSAMPLE_RATIO;
|
|
6
|
+
static AccelSensor_t accel;
|
|
7
|
+
static MagSensor_t mag;
|
|
8
|
+
static GyroSensor_t gyro;
|
|
9
|
+
|
|
10
|
+
static float cal_data_sent[19];
|
|
11
|
+
static int cal_confirm_needed=0;
|
|
12
|
+
|
|
13
|
+
void raw_data_reset(void)
|
|
14
|
+
{
|
|
15
|
+
rawcount = OVERSAMPLE_RATIO;
|
|
16
|
+
fusion_init();
|
|
17
|
+
memset(&magcal, 0, sizeof(magcal));
|
|
18
|
+
magcal.V[2] = 80.0f; // initial guess
|
|
19
|
+
magcal.invW[0][0] = 1.0f;
|
|
20
|
+
magcal.invW[1][1] = 1.0f;
|
|
21
|
+
magcal.invW[2][2] = 1.0f;
|
|
22
|
+
magcal.FitError = 100.0f;
|
|
23
|
+
magcal.FitErrorAge = 100.0f;
|
|
24
|
+
magcal.B = 50.0f;
|
|
25
|
+
|
|
26
|
+
// Clear draw points
|
|
27
|
+
clear_draw_points();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static int choose_discard_magcal(void)
|
|
31
|
+
{
|
|
32
|
+
int32_t rawx, rawy, rawz;
|
|
33
|
+
int32_t dx, dy, dz;
|
|
34
|
+
float x, y, z;
|
|
35
|
+
uint64_t distsq, minsum=0xFFFFFFFFFFFFFFFFull;
|
|
36
|
+
static int runcount=0;
|
|
37
|
+
int i, j, minindex=0;
|
|
38
|
+
Point_t point;
|
|
39
|
+
float gaps, field, error, errormax;
|
|
40
|
+
|
|
41
|
+
// When enough data is collected (gaps error is low), assume we
|
|
42
|
+
// have a pretty good coverage and the field stregth is known.
|
|
43
|
+
gaps = quality_surface_gap_error();
|
|
44
|
+
if (gaps < 25.0f) {
|
|
45
|
+
// occasionally look for points farthest from average field strength
|
|
46
|
+
// always rate limit assumption-based data purging, but allow the
|
|
47
|
+
// rate to increase as the angular coverage improves.
|
|
48
|
+
if (gaps < 1.0f) gaps = 1.0f;
|
|
49
|
+
if (++runcount > (int)(gaps * 10.0f)) {
|
|
50
|
+
j = MAGBUFFSIZE;
|
|
51
|
+
errormax = 0.0f;
|
|
52
|
+
for (i=0; i < MAGBUFFSIZE; i++) {
|
|
53
|
+
rawx = magcal.BpFast[0][i];
|
|
54
|
+
rawy = magcal.BpFast[1][i];
|
|
55
|
+
rawz = magcal.BpFast[2][i];
|
|
56
|
+
apply_calibration(rawx, rawy, rawz, &point);
|
|
57
|
+
x = point.x;
|
|
58
|
+
y = point.y;
|
|
59
|
+
z = point.z;
|
|
60
|
+
field = sqrtf(x * x + y * y + z * z);
|
|
61
|
+
// if magcal.B is bad, things could go horribly wrong
|
|
62
|
+
error = fabsf(field - magcal.B);
|
|
63
|
+
if (error > errormax) {
|
|
64
|
+
errormax = error;
|
|
65
|
+
j = i;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
runcount = 0;
|
|
69
|
+
if (j < MAGBUFFSIZE) {
|
|
70
|
+
//printf("worst error at %d\n", j);
|
|
71
|
+
return j;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
runcount = 0;
|
|
76
|
+
}
|
|
77
|
+
// When solid info isn't availabe, find 2 points closest to each other,
|
|
78
|
+
// and randomly discard one. When we don't have good coverage, this
|
|
79
|
+
// approach tends to add points into previously unmeasured areas while
|
|
80
|
+
// discarding info from areas with highly redundant info.
|
|
81
|
+
for (i=0; i < MAGBUFFSIZE; i++) {
|
|
82
|
+
for (j=i+1; j < MAGBUFFSIZE; j++) {
|
|
83
|
+
dx = magcal.BpFast[0][i] - magcal.BpFast[0][j];
|
|
84
|
+
dy = magcal.BpFast[1][i] - magcal.BpFast[1][j];
|
|
85
|
+
dz = magcal.BpFast[2][i] - magcal.BpFast[2][j];
|
|
86
|
+
distsq = (int64_t)dx * (int64_t)dx;
|
|
87
|
+
distsq += (int64_t)dy * (int64_t)dy;
|
|
88
|
+
distsq += (int64_t)dz * (int64_t)dz;
|
|
89
|
+
if (distsq < minsum) {
|
|
90
|
+
minsum = distsq;
|
|
91
|
+
minindex = (random() & 1) ? i : j;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return minindex;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
static void add_magcal_data(const int16_t *data)
|
|
100
|
+
{
|
|
101
|
+
int i;
|
|
102
|
+
|
|
103
|
+
// first look for an unused caldata slot
|
|
104
|
+
for (i=0; i < MAGBUFFSIZE; i++) {
|
|
105
|
+
if (!magcal.valid[i]) break;
|
|
106
|
+
}
|
|
107
|
+
// If the buffer is full, we must choose which old data to discard.
|
|
108
|
+
// We must choose wisely! Throwing away the wrong data could prevent
|
|
109
|
+
// collecting enough data distributed across the entire 3D angular
|
|
110
|
+
// range, preventing a decent cal from ever happening at all. Making
|
|
111
|
+
// any assumption about good vs bad data is particularly risky,
|
|
112
|
+
// because being wrong could cause an unstable feedback loop where
|
|
113
|
+
// bad data leads to wrong decisions which leads to even worse data.
|
|
114
|
+
// But if done well, purging bad data has massive potential to
|
|
115
|
+
// improve results. The trick is telling the good from the bad while
|
|
116
|
+
// still in the process of learning what's good...
|
|
117
|
+
if (i >= MAGBUFFSIZE) {
|
|
118
|
+
i = choose_discard_magcal();
|
|
119
|
+
if (i < 0 || i >= MAGBUFFSIZE) {
|
|
120
|
+
i = random() % MAGBUFFSIZE;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// add it to the cal buffer
|
|
124
|
+
magcal.BpFast[0][i] = data[6];
|
|
125
|
+
magcal.BpFast[1][i] = data[7];
|
|
126
|
+
magcal.BpFast[2][i] = data[8];
|
|
127
|
+
magcal.valid[i] = 1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static int is_float_ok(float actual, float expected)
|
|
131
|
+
{
|
|
132
|
+
float err, maxerr;
|
|
133
|
+
|
|
134
|
+
err = fabsf(actual - expected);
|
|
135
|
+
maxerr = 0.0001f + fabsf(expected) * 0.00003f;
|
|
136
|
+
if (err <= maxerr) return 1;
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
void cal1_data(const float *data)
|
|
141
|
+
{
|
|
142
|
+
int i, ok;
|
|
143
|
+
|
|
144
|
+
if (cal_confirm_needed) {
|
|
145
|
+
ok = 1;
|
|
146
|
+
for (i=0; i<10; i++) {
|
|
147
|
+
if (!is_float_ok(data[i], cal_data_sent[i])) ok = 0;
|
|
148
|
+
}
|
|
149
|
+
if (ok) {
|
|
150
|
+
cal_confirm_needed &= ~1; // got cal1 confirm
|
|
151
|
+
if (cal_confirm_needed == 0) {
|
|
152
|
+
// TODO: remove this
|
|
153
|
+
// calibration_confirmed();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
void cal2_data(const float *data)
|
|
160
|
+
{
|
|
161
|
+
int i, ok;
|
|
162
|
+
|
|
163
|
+
if (cal_confirm_needed) {
|
|
164
|
+
ok = 1;
|
|
165
|
+
for (i=0; i<9; i++) {
|
|
166
|
+
if (!is_float_ok(data[i], cal_data_sent[i+10])) ok = 0;
|
|
167
|
+
}
|
|
168
|
+
if (ok) {
|
|
169
|
+
cal_confirm_needed &= ~2; // got cal2 confirm
|
|
170
|
+
if (cal_confirm_needed == 0) {
|
|
171
|
+
// TODO: remove this
|
|
172
|
+
// calibration_confirmed();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
void raw_data(const int16_t *data)
|
|
179
|
+
{
|
|
180
|
+
static int force_orientation_counter=0;
|
|
181
|
+
float x, y, z, ratio, magdiff;
|
|
182
|
+
Point_t point;
|
|
183
|
+
|
|
184
|
+
add_magcal_data(data);
|
|
185
|
+
x = magcal.V[0];
|
|
186
|
+
y = magcal.V[1];
|
|
187
|
+
z = magcal.V[2];
|
|
188
|
+
if (MagCal_Run()) {
|
|
189
|
+
x -= magcal.V[0];
|
|
190
|
+
y -= magcal.V[1];
|
|
191
|
+
z -= magcal.V[2];
|
|
192
|
+
magdiff = sqrtf(x * x + y * y + z * z);
|
|
193
|
+
//printf("magdiff = %.2f\n", magdiff);
|
|
194
|
+
if (magdiff > 0.8f) {
|
|
195
|
+
fusion_init();
|
|
196
|
+
rawcount = OVERSAMPLE_RATIO;
|
|
197
|
+
force_orientation_counter = 240;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (force_orientation_counter > 0) {
|
|
202
|
+
if (--force_orientation_counter == 0) {
|
|
203
|
+
//printf("delayed forcible orientation reset\n");
|
|
204
|
+
fusion_init();
|
|
205
|
+
rawcount = OVERSAMPLE_RATIO;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (rawcount >= OVERSAMPLE_RATIO) {
|
|
210
|
+
memset(&accel, 0, sizeof(accel));
|
|
211
|
+
memset(&mag, 0, sizeof(mag));
|
|
212
|
+
memset(&gyro, 0, sizeof(gyro));
|
|
213
|
+
rawcount = 0;
|
|
214
|
+
}
|
|
215
|
+
x = (float)data[0] * G_PER_COUNT;
|
|
216
|
+
y = (float)data[1] * G_PER_COUNT;
|
|
217
|
+
z = (float)data[2] * G_PER_COUNT;
|
|
218
|
+
accel.GpFast[0] = x;
|
|
219
|
+
accel.GpFast[1] = y;
|
|
220
|
+
accel.GpFast[2] = z;
|
|
221
|
+
accel.Gp[0] += x;
|
|
222
|
+
accel.Gp[1] += y;
|
|
223
|
+
accel.Gp[2] += z;
|
|
224
|
+
|
|
225
|
+
x = (float)data[3] * DEG_PER_SEC_PER_COUNT;
|
|
226
|
+
y = (float)data[4] * DEG_PER_SEC_PER_COUNT;
|
|
227
|
+
z = (float)data[5] * DEG_PER_SEC_PER_COUNT;
|
|
228
|
+
gyro.Yp[0] += x;
|
|
229
|
+
gyro.Yp[1] += y;
|
|
230
|
+
gyro.Yp[2] += z;
|
|
231
|
+
gyro.YpFast[rawcount][0] = x;
|
|
232
|
+
gyro.YpFast[rawcount][1] = y;
|
|
233
|
+
gyro.YpFast[rawcount][2] = z;
|
|
234
|
+
|
|
235
|
+
apply_calibration(data[6], data[7], data[8], &point);
|
|
236
|
+
mag.BcFast[0] = point.x;
|
|
237
|
+
mag.BcFast[1] = point.y;
|
|
238
|
+
mag.BcFast[2] = point.z;
|
|
239
|
+
mag.Bc[0] += point.x;
|
|
240
|
+
mag.Bc[1] += point.y;
|
|
241
|
+
mag.Bc[2] += point.z;
|
|
242
|
+
|
|
243
|
+
rawcount++;
|
|
244
|
+
if (rawcount >= OVERSAMPLE_RATIO) {
|
|
245
|
+
ratio = 1.0f / (float)OVERSAMPLE_RATIO;
|
|
246
|
+
accel.Gp[0] *= ratio;
|
|
247
|
+
accel.Gp[1] *= ratio;
|
|
248
|
+
accel.Gp[2] *= ratio;
|
|
249
|
+
gyro.Yp[0] *= ratio;
|
|
250
|
+
gyro.Yp[1] *= ratio;
|
|
251
|
+
gyro.Yp[2] *= ratio;
|
|
252
|
+
mag.Bc[0] *= ratio;
|
|
253
|
+
mag.Bc[1] *= ratio;
|
|
254
|
+
mag.Bc[2] *= ratio;
|
|
255
|
+
fusion_update(&accel, &mag, &gyro, &magcal);
|
|
256
|
+
fusion_read(¤t_orientation);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static uint16_t crc16(uint16_t crc, uint8_t data)
|
|
261
|
+
{
|
|
262
|
+
unsigned int i;
|
|
263
|
+
|
|
264
|
+
crc ^= data;
|
|
265
|
+
for (i = 0; i < 8; ++i) {
|
|
266
|
+
if (crc & 1) {
|
|
267
|
+
crc = (crc >> 1) ^ 0xA001;
|
|
268
|
+
} else {
|
|
269
|
+
crc = (crc >> 1);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return crc;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static uint8_t * copy_lsb_first(uint8_t *dst, float f)
|
|
276
|
+
{
|
|
277
|
+
union {
|
|
278
|
+
float f;
|
|
279
|
+
uint32_t n;
|
|
280
|
+
} data;
|
|
281
|
+
|
|
282
|
+
data.f = f;
|
|
283
|
+
*dst++ = data.n;
|
|
284
|
+
*dst++ = data.n >> 8;
|
|
285
|
+
*dst++ = data.n >> 16;
|
|
286
|
+
*dst++ = data.n >> 24;
|
|
287
|
+
return dst;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
static uint8_t data_result[68];
|
|
291
|
+
int send_calibration(void)
|
|
292
|
+
{
|
|
293
|
+
uint8_t *p, buf[68];
|
|
294
|
+
uint16_t crc;
|
|
295
|
+
int i;
|
|
296
|
+
|
|
297
|
+
p = buf;
|
|
298
|
+
*p++ = 117; // 2 byte signature
|
|
299
|
+
*p++ = 84;
|
|
300
|
+
for (i=0; i < 3; i++) {
|
|
301
|
+
p = copy_lsb_first(p, 0.0f); // accelerometer offsets
|
|
302
|
+
cal_data_sent[0+i] = 0.0f;
|
|
303
|
+
}
|
|
304
|
+
for (i=0; i < 3; i++) {
|
|
305
|
+
p = copy_lsb_first(p, 0.0f); // gyroscope offsets
|
|
306
|
+
cal_data_sent[3+i] = 0.0f;
|
|
307
|
+
}
|
|
308
|
+
for (i=0; i < 3; i++) {
|
|
309
|
+
p = copy_lsb_first(p, magcal.V[i]); // 12 bytes offset/hardiron
|
|
310
|
+
cal_data_sent[6+i] = magcal.V[i];
|
|
311
|
+
}
|
|
312
|
+
p = copy_lsb_first(p, magcal.B); // field strength
|
|
313
|
+
p = copy_lsb_first(p, magcal.invW[0][0]); //10
|
|
314
|
+
p = copy_lsb_first(p, magcal.invW[1][1]); //11
|
|
315
|
+
p = copy_lsb_first(p, magcal.invW[2][2]); //12
|
|
316
|
+
p = copy_lsb_first(p, magcal.invW[0][1]); //13
|
|
317
|
+
p = copy_lsb_first(p, magcal.invW[0][2]); //14
|
|
318
|
+
p = copy_lsb_first(p, magcal.invW[1][2]); //15
|
|
319
|
+
cal_data_sent[9] = magcal.B;
|
|
320
|
+
cal_data_sent[10] = magcal.invW[0][0];
|
|
321
|
+
cal_data_sent[11] = magcal.invW[0][1];
|
|
322
|
+
cal_data_sent[12] = magcal.invW[0][2];
|
|
323
|
+
cal_data_sent[13] = magcal.invW[1][0];
|
|
324
|
+
cal_data_sent[14] = magcal.invW[1][1];
|
|
325
|
+
cal_data_sent[15] = magcal.invW[1][2];
|
|
326
|
+
cal_data_sent[16] = magcal.invW[2][0];
|
|
327
|
+
cal_data_sent[17] = magcal.invW[2][1];
|
|
328
|
+
cal_data_sent[18] = magcal.invW[2][2];
|
|
329
|
+
// PhiEs: update on 17/02/2025 at
|
|
330
|
+
// Change cal_confirm_needed = 2 to adjust data resend from
|
|
331
|
+
// cal_confirm_needed = 3;
|
|
332
|
+
cal_confirm_needed = 3;
|
|
333
|
+
// PhiEs: end
|
|
334
|
+
crc = 0xFFFF;
|
|
335
|
+
for (i=0; i < 66; i++) {
|
|
336
|
+
crc = crc16(crc, buf[i]);
|
|
337
|
+
}
|
|
338
|
+
*p++ = crc; // 2 byte crc check
|
|
339
|
+
*p++ = crc >> 8;
|
|
340
|
+
|
|
341
|
+
memcpy(data_result, buf, 68);
|
|
342
|
+
|
|
343
|
+
return write_ipc_file_data(buf, 68);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const uint8_t* get_calibration_data(void)
|
|
347
|
+
{
|
|
348
|
+
return data_result;
|
|
349
|
+
}
|