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,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
+ }
@@ -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(&current_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
+ }