1 /*
2 $License:
3 Copyright (C) 2011-2012 InvenSense Corporation, All Rights Reserved.
4 See included License.txt for License information.
5 $
6 */
7
8 /**
9 * @defgroup Data_Builder data_builder
10 * @brief Motion Library - Data Builder
11 * Constructs and Creates the data for MPL
12 *
13 * @{
14 * @file data_builder.c
15 * @brief Data Builder.
16 */
17
18 #undef MPL_LOG_NDEBUG
19 #define MPL_LOG_NDEBUG 1 /* Use 0 to turn on MPL_LOGV output */
20
21 #include <string.h>
22
23 #include "ml_math_func.h"
24 #include "data_builder.h"
25 #include "mlmath.h"
26 #include "storage_manager.h"
27 #include "message_layer.h"
28 #include "results_holder.h"
29
30 #include "log.h"
31 #undef MPL_LOG_TAG
32 #define MPL_LOG_TAG "MLLITE"
33
34 typedef inv_error_t (*inv_process_cb_func)(struct inv_sensor_cal_t *data);
35
36 struct process_t {
37 inv_process_cb_func func;
38 int priority;
39 int data_required;
40 };
41
42 struct inv_data_builder_t {
43 int num_cb;
44 struct process_t process[INV_MAX_DATA_CB];
45 struct inv_db_save_t save;
46 struct inv_db_save_mpl_t save_mpl;
47 struct inv_db_save_accel_mpl_t save_accel_mpl;
48 int compass_disturbance;
49 int mode;
50 #ifdef INV_PLAYBACK_DBG
51 int debug_mode;
52 int last_mode;
53 FILE *file;
54 #endif
55 };
56
57 void inv_apply_calibration(struct inv_single_sensor_t *sensor, const long *bias);
58 static void inv_set_contiguous(void);
59
60 static struct inv_data_builder_t inv_data_builder;
61 static struct inv_sensor_cal_t sensors;
62
63 #ifdef INV_PLAYBACK_DBG
64
65 /** Turn on data logging to allow playback of same scenario at a later time.
66 * @param[in] file File to write to, must be open.
67 */
inv_turn_on_data_logging(FILE * file)68 void inv_turn_on_data_logging(FILE *file)
69 {
70 MPL_LOGV("input data logging started\n");
71 inv_data_builder.file = file;
72 inv_data_builder.debug_mode = RD_RECORD;
73 }
74
75 /** Turn off data logging to allow playback of same scenario at a later time.
76 * File passed to inv_turn_on_data_logging() must be closed after calling this.
77 */
inv_turn_off_data_logging()78 void inv_turn_off_data_logging()
79 {
80 MPL_LOGV("input data logging stopped\n");
81 inv_data_builder.debug_mode = RD_NO_DEBUG;
82 inv_data_builder.file = NULL;
83 }
84 #endif
85
86 /** Gets last value of raw compass data.
87 * @param[out] raw Raw compass data in mounting frame in hardware units. Length 3.
88 */
inv_get_raw_compass(short * raw)89 void inv_get_raw_compass(short *raw)
90 {
91 memcpy(raw, sensors.compass.raw, sizeof(sensors.compass.raw));
92 }
93
94 /** This function receives the data that was stored in non-volatile memory between power off */
inv_db_load_func(const unsigned char * data)95 static inv_error_t inv_db_load_func(const unsigned char *data)
96 {
97 memcpy(&inv_data_builder.save, data, sizeof(inv_data_builder.save));
98 // copy in the saved accuracy in the actual sensors accuracy
99 sensors.gyro.accuracy = inv_data_builder.save.gyro_accuracy;
100 sensors.accel.accuracy = inv_data_builder.save.accel_accuracy;
101 sensors.compass.accuracy = inv_data_builder.save.compass_accuracy;
102 // TODO
103 if (sensors.accel.accuracy == 3) {
104 inv_set_accel_bias_found(1);
105 }
106 if (sensors.compass.accuracy == 3) {
107 inv_set_compass_bias_found(1);
108 }
109 return INV_SUCCESS;
110 }
111
112 /** This function returns the data to be stored in non-volatile memory between power off */
inv_db_save_func(unsigned char * data)113 static inv_error_t inv_db_save_func(unsigned char *data)
114 {
115 memcpy(data, &inv_data_builder.save, sizeof(inv_data_builder.save));
116 return INV_SUCCESS;
117 }
118
119 /** This function receives the data for mpl that was stored in non-volatile memory between power off */
inv_db_load_mpl_func(const unsigned char * data)120 static inv_error_t inv_db_load_mpl_func(const unsigned char *data)
121 {
122 memcpy(&inv_data_builder.save_mpl, data, sizeof(inv_data_builder.save_mpl));
123
124 return INV_SUCCESS;
125 }
126
127 /** This function returns the data for mpl to be stored in non-volatile memory between power off */
inv_db_save_mpl_func(unsigned char * data)128 static inv_error_t inv_db_save_mpl_func(unsigned char *data)
129 {
130 memcpy(data, &inv_data_builder.save_mpl, sizeof(inv_data_builder.save_mpl));
131 return INV_SUCCESS;
132 }
133
134 /** This function receives the data for mpl that was stored in non-volatile memory between power off */
inv_db_load_accel_mpl_func(const unsigned char * data)135 static inv_error_t inv_db_load_accel_mpl_func(const unsigned char *data)
136 {
137 memcpy(&inv_data_builder.save_accel_mpl, data, sizeof(inv_data_builder.save_accel_mpl));
138
139 return INV_SUCCESS;
140 }
141
142 /** This function returns the data for mpl to be stored in non-volatile memory between power off */
inv_db_save_accel_mpl_func(unsigned char * data)143 static inv_error_t inv_db_save_accel_mpl_func(unsigned char *data)
144 {
145 memcpy(data, &inv_data_builder.save_accel_mpl, sizeof(inv_data_builder.save_accel_mpl));
146 return INV_SUCCESS;
147 }
148
149 /** Initialize the data builder
150 */
inv_init_data_builder(void)151 inv_error_t inv_init_data_builder(void)
152 {
153 /* TODO: Hardcode temperature scale/offset here. */
154 memset(&inv_data_builder, 0, sizeof(inv_data_builder));
155 memset(&sensors, 0, sizeof(sensors));
156
157 // disable the soft iron transform process
158 inv_reset_compass_soft_iron_matrix();
159
160 return ((inv_register_load_store(inv_db_load_func, inv_db_save_func,
161 sizeof(inv_data_builder.save),
162 INV_DB_SAVE_KEY))
163 | (inv_register_load_store(inv_db_load_mpl_func, inv_db_save_mpl_func,
164 sizeof(inv_data_builder.save_mpl),
165 INV_DB_SAVE_MPL_KEY))
166 | (inv_register_load_store(inv_db_load_accel_mpl_func, inv_db_save_accel_mpl_func,
167 sizeof(inv_data_builder.save_accel_mpl),
168 INV_DB_SAVE_ACCEL_MPL_KEY)) );
169 }
170
171 /** Gyro sensitivity.
172 * @return A scale factor to convert device units to degrees per second scaled by 2^16
173 * such that degrees_per_second = device_units * sensitivity / 2^30. Typically
174 * it works out to be the maximum rate * 2^15.
175 */
inv_get_gyro_sensitivity(void)176 long inv_get_gyro_sensitivity(void)
177 {
178 return sensors.gyro.sensitivity;
179 }
180
181 /** Accel sensitivity.
182 * @return A scale factor to convert device units to g's scaled by 2^16
183 * such that g_s = device_units * sensitivity / 2^30. Typically
184 * it works out to be the maximum accel value in g's * 2^15.
185 */
inv_get_accel_sensitivity(void)186 long inv_get_accel_sensitivity(void)
187 {
188 return sensors.accel.sensitivity;
189 }
190
191 /** Compass sensitivity.
192 * @return A scale factor to convert device units to micro Tesla scaled by 2^16
193 * such that uT = device_units * sensitivity / 2^30. Typically
194 * it works out to be the maximum uT * 2^15.
195 */
inv_get_compass_sensitivity(void)196 long inv_get_compass_sensitivity(void)
197 {
198 return sensors.compass.sensitivity;
199 }
200
201 /** Sets orientation and sensitivity field for a sensor.
202 * @param[out] sensor Structure to apply settings to
203 * @param[in] orientation Orientation description of how part is mounted.
204 * @param[in] sensitivity A Scale factor to convert from hardware units to
205 * standard units (dps, uT, g).
206 */
set_sensor_orientation_and_scale(struct inv_single_sensor_t * sensor,int orientation,long sensitivity)207 void set_sensor_orientation_and_scale(struct inv_single_sensor_t *sensor,
208 int orientation, long sensitivity)
209 {
210 int error = 0;
211
212 if (!sensitivity) {
213 // Sensitivity can't be zero
214 sensitivity = 1L<<16;
215 MPL_LOGE("\n\nCritical error! Sensitivity is zero.\n\n");
216 }
217
218 sensor->sensitivity = sensitivity;
219 // Make sure we don't describe some impossible orientation
220 if ((orientation & 3) == 3) {
221 error = 1;
222 }
223 if ((orientation & 0x18) == 0x18) {
224 error = 1;
225 }
226 if ((orientation & 0xc0) == 0xc0) {
227 error = 1;
228 }
229 if (error) {
230 orientation = 0x88; // Identity
231 MPL_LOGE("\n\nCritical error! Impossible mounting orientation given. Using Identity instead\n\n");
232 }
233 sensor->orientation = orientation;
234 }
235
236 /** Sets the Orientation and Sensitivity of the gyro data.
237 * @param[in] orientation A scalar defining the transformation from chip mounting
238 * to the body frame. The function inv_orientation_matrix_to_scalar()
239 * can convert the transformation matrix to this scalar and describes the
240 * scalar in further detail.
241 * @param[in] sensitivity A scale factor to convert device units to degrees per second scaled by 2^16
242 * such that degrees_per_second = device_units * sensitivity / 2^30. Typically
243 * it works out to be the maximum rate * 2^15.
244 */
inv_set_gyro_orientation_and_scale(int orientation,long sensitivity)245 void inv_set_gyro_orientation_and_scale(int orientation, long sensitivity)
246 {
247 #ifdef INV_PLAYBACK_DBG
248 if (inv_data_builder.debug_mode == RD_RECORD) {
249 int type = PLAYBACK_DBG_TYPE_G_ORIENT;
250 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
251 fwrite(&orientation, sizeof(orientation), 1, inv_data_builder.file);
252 fwrite(&sensitivity, sizeof(sensitivity), 1, inv_data_builder.file);
253 }
254 #endif
255 set_sensor_orientation_and_scale(&sensors.gyro, orientation,
256 sensitivity);
257 }
258
259 /** Set Gyro Sample rate in micro seconds.
260 * @param[in] sample_rate_us Set Gyro Sample rate in us
261 */
inv_set_gyro_sample_rate(long sample_rate_us)262 void inv_set_gyro_sample_rate(long sample_rate_us)
263 {
264 #ifdef INV_PLAYBACK_DBG
265 if (inv_data_builder.debug_mode == RD_RECORD) {
266 int type = PLAYBACK_DBG_TYPE_G_SAMPLE_RATE;
267 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
268 fwrite(&sample_rate_us, sizeof(sample_rate_us), 1, inv_data_builder.file);
269 }
270 #endif
271 sensors.gyro.sample_rate_us = sample_rate_us;
272 sensors.gyro.sample_rate_ms = sample_rate_us / 1000;
273 if (sensors.gyro.bandwidth == 0) {
274 sensors.gyro.bandwidth = (int)(1000000L / sample_rate_us);
275 }
276 }
277
278 /** Set Accel Sample rate in micro seconds.
279 * @param[in] sample_rate_us Set Accel Sample rate in us
280 */
inv_set_accel_sample_rate(long sample_rate_us)281 void inv_set_accel_sample_rate(long sample_rate_us)
282 {
283 #ifdef INV_PLAYBACK_DBG
284 if (inv_data_builder.debug_mode == RD_RECORD) {
285 int type = PLAYBACK_DBG_TYPE_A_SAMPLE_RATE;
286 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
287 fwrite(&sample_rate_us, sizeof(sample_rate_us), 1, inv_data_builder.file);
288 }
289 #endif
290 sensors.accel.sample_rate_us = sample_rate_us;
291 sensors.accel.sample_rate_ms = sample_rate_us / 1000;
292 if (sensors.accel.bandwidth == 0) {
293 sensors.accel.bandwidth = (int)(1000000L / sample_rate_us);
294 }
295 }
296
297 /** Pick the smallest non-negative number. Priority to td1 on equal
298 * If both are negative, return the largest.
299 */
inv_pick_best_time_difference(long td1,long td2)300 static int inv_pick_best_time_difference(long td1, long td2)
301 {
302 if (td1 >= 0) {
303 if (td2 >= 0) {
304 if (td1 <= td2) {
305 // td1
306 return 0;
307 } else {
308 // td2
309 return 1;
310 }
311 } else {
312 // td1
313 return 0;
314 }
315 } else if (td2 >= 0) {
316 // td2
317 return 1;
318 } else {
319 // Both are negative
320 if (td1 >= td2) {
321 // td1
322 return 0;
323 } else {
324 // td2
325 return 1;
326 }
327 }
328 }
329
330 /** Returns timestame based upon a raw sensor, and returns if that sample has a new piece of data.
331 */
inv_raw_sensor_timestamp(int sensor_number,inv_time_t * ts)332 static int inv_raw_sensor_timestamp(int sensor_number, inv_time_t *ts)
333 {
334 int status = 0;
335 switch (sensor_number) {
336 case 0: // Quat
337 *ts = sensors.quat.timestamp;
338 if (inv_data_builder.mode & INV_QUAT_NEW)
339 if (sensors.quat.timestamp_prev != sensors.quat.timestamp)
340 status = 1;
341 return status;
342 case 1: // Gyro
343 *ts = sensors.gyro.timestamp;
344 if (inv_data_builder.mode & INV_GYRO_NEW)
345 if (sensors.gyro.timestamp_prev != sensors.gyro.timestamp)
346 status = 1;
347 return status;
348 case 2: // Accel
349 *ts = sensors.accel.timestamp;
350 if (inv_data_builder.mode & INV_ACCEL_NEW)
351 if (sensors.accel.timestamp_prev != sensors.accel.timestamp)
352 status = 1;
353 return status;
354 case 3: // Compass
355 *ts = sensors.compass.timestamp;
356 if (inv_data_builder.mode & INV_MAG_NEW)
357 if (sensors.compass.timestamp_prev != sensors.compass.timestamp)
358 status = 1;
359 return status;
360 default:
361 *ts = 0;
362 return 0;
363 }
364 return 0;
365 }
366
367 /** Gets best timestamp and if there is a new piece of data for a 9-axis sensor combination.
368 * It does this by finding a raw sensor that has the closest sample rate that is at least as
369 * often desired. It also returns if that raw sensor has a new piece of data.
370 * Priority is 9-axis quat, 6-axis quat, 3-axis quat, gyro, compass, accel on ties.
371 * @return Returns 1, if the raw sensor being attached has new data, 0 otherwise.
372 */
inv_get_9_axis_timestamp(long sample_rate_us,inv_time_t * ts)373 int inv_get_9_axis_timestamp(long sample_rate_us, inv_time_t *ts)
374 {
375 int status = 0;
376 long td[3];
377 int idx,idx2;
378
379 if ((sensors.quat.status & (INV_QUAT_9AXIS | INV_SENSOR_ON)) == (INV_QUAT_9AXIS | INV_SENSOR_ON)) {
380 // 9-axis from DMP
381 *ts = sensors.quat.timestamp;
382 if (inv_data_builder.mode & INV_QUAT_NEW)
383 if (sensors.quat.timestamp_prev != sensors.quat.timestamp)
384 status = 1;
385 return status;
386 }
387
388 if ((sensors.compass.status & INV_SENSOR_ON) == 0) {
389 return 0; // Compass must be on
390 }
391
392 // At this point, we know compass is on. Check if accel or 6-axis quat is on
393 td[0] = sample_rate_us - sensors.quat.sample_rate_us;
394 td[1] = sample_rate_us - sensors.compass.sample_rate_us;
395 if ((sensors.quat.status & (INV_QUAT_6AXIS | INV_SENSOR_ON)) == (INV_QUAT_6AXIS | INV_SENSOR_ON)) {
396 // Sensor tied to compass or 6-axis
397 idx = inv_pick_best_time_difference(td[0], td[1]);
398 idx *= 3; // Sensor number is 0 (Quat) or 3 (Compass)
399 return inv_raw_sensor_timestamp(idx, ts);
400 } else if ((sensors.accel.status & INV_SENSOR_ON) == 0) {
401 return 0; // Accel must be on or 6-axis quat must be on
402 }
403
404 // At this point, we know accel is on. Check if 3-axis quat is on
405 td[2] = sample_rate_us - sensors.accel.sample_rate_us;
406 if ((sensors.quat.status & (INV_QUAT_3AXIS | INV_SENSOR_ON)) == (INV_QUAT_3AXIS | INV_SENSOR_ON)) {
407 idx = inv_pick_best_time_difference(td[0], td[1]);
408 idx2 = inv_pick_best_time_difference(td[idx], td[2]);
409 if (idx2 == 1)
410 idx = 2;
411 if (idx > 0)
412 idx++; // Skip gyro sensor in next function call
413 // 0 = quat, 2 = compass, 3=accel
414 return inv_raw_sensor_timestamp(idx, ts);
415 }
416
417 // No Quaternion data from outside, Gyro must be on
418 if ((sensors.gyro.status & INV_SENSOR_ON) == 0) {
419 return 0; // Gyro must be on
420 }
421
422 td[0] = sample_rate_us - sensors.gyro.sample_rate_us;
423 idx = inv_pick_best_time_difference(td[0], td[1]);
424 idx2 = inv_pick_best_time_difference(td[idx], td[2]);
425 if (idx2 == 1)
426 idx = 2;
427 idx++;
428 // 1 = gyro, 2 = compass, 3=accel
429 return inv_raw_sensor_timestamp(idx, ts);
430 }
431
432 /** Gets best timestamp and if there is a new piece of data for a 9-axis sensor combination.
433 * It does this by finding a raw sensor that has the closest sample rate that is at least as
434 * often desired. It also returns if that raw sensor has a new piece of data.
435 * Priority compass, accel on a tie
436 * @return Returns 1, if the raw sensor being attached has new data, 0 otherwise.
437 */
inv_get_6_axis_compass_accel_timestamp(long sample_rate_us,inv_time_t * ts)438 int inv_get_6_axis_compass_accel_timestamp(long sample_rate_us, inv_time_t *ts)
439 {
440 long td[2];
441 int idx;
442
443 if ((sensors.compass.status & INV_SENSOR_ON) == 0) {
444 return 0; // Compass must be on
445 }
446 if ((sensors.accel.status & INV_SENSOR_ON) == 0) {
447 return 0; // Accel must be on
448 }
449
450 // At this point, we know compass & accel are both on.
451 td[0] = sample_rate_us - sensors.accel.sample_rate_us;
452 td[1] = sample_rate_us - sensors.compass.sample_rate_us;
453 idx = inv_pick_best_time_difference(td[0], td[1]);
454 idx += 2;
455 return inv_raw_sensor_timestamp(idx, ts);
456 }
457
458 /** Gets best timestamp and if there is a new piece of data for a 9-axis sensor combination.
459 * It does this by finding a raw sensor that has the closest sample rate that is at least as
460 * often desired. It also returns if that raw sensor has a new piece of data.
461 * Priority is Quaternion-6axis, Quaternion 3-axis, Gyro, Accel
462 * @return Returns 1, if the raw sensor being attached has new data, 0 otherwise.
463 */
inv_get_6_axis_gyro_accel_timestamp(long sample_rate_us,inv_time_t * ts)464 int inv_get_6_axis_gyro_accel_timestamp(long sample_rate_us, inv_time_t *ts)
465 {
466 long td[2];
467 int idx;
468
469 if ((sensors.quat.status & (INV_QUAT_6AXIS | INV_SENSOR_ON)) == (INV_QUAT_6AXIS | INV_SENSOR_ON)) {
470 // Sensor number is 0 (Quat)
471 return inv_raw_sensor_timestamp(0, ts);
472 } else if ((sensors.accel.status & INV_SENSOR_ON) == 0) {
473 return 0; // Accel must be on or 6-axis quat must be on
474 }
475
476 // At this point, we know accel is on. Check if 3-axis quat is on
477 td[0] = sample_rate_us - sensors.quat.sample_rate_us;
478 td[1] = sample_rate_us - sensors.accel.sample_rate_us;
479 if ((sensors.quat.status & (INV_QUAT_3AXIS | INV_SENSOR_ON)) == (INV_QUAT_3AXIS | INV_SENSOR_ON)) {
480 idx = inv_pick_best_time_difference(td[0], td[1]);
481 idx *= 2;
482 // 0 = quat, 3=accel
483 return inv_raw_sensor_timestamp(idx, ts);
484 }
485
486 // No Quaternion data from outside, Gyro must be on
487 if ((sensors.gyro.status & INV_SENSOR_ON) == 0) {
488 return 0; // Gyro must be on
489 }
490
491 td[0] = sample_rate_us - sensors.gyro.sample_rate_us;
492 idx = inv_pick_best_time_difference(td[0], td[1]);
493 idx *= 2; // 0=gyro 2=accel
494 idx++;
495 // 1 = gyro, 3=accel
496 return inv_raw_sensor_timestamp(idx, ts);
497 }
498
499 /** Set Compass Sample rate in micro seconds.
500 * @param[in] sample_rate_us Set Gyro Sample rate in micro seconds.
501 */
inv_set_compass_sample_rate(long sample_rate_us)502 void inv_set_compass_sample_rate(long sample_rate_us)
503 {
504 #ifdef INV_PLAYBACK_DBG
505 if (inv_data_builder.debug_mode == RD_RECORD) {
506 int type = PLAYBACK_DBG_TYPE_C_SAMPLE_RATE;
507 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
508 fwrite(&sample_rate_us, sizeof(sample_rate_us), 1, inv_data_builder.file);
509 }
510 #endif
511 sensors.compass.sample_rate_us = sample_rate_us;
512 sensors.compass.sample_rate_ms = sample_rate_us / 1000;
513 if (sensors.compass.bandwidth == 0) {
514 sensors.compass.bandwidth = (int)(1000000L / sample_rate_us);
515 }
516 }
517
inv_get_gyro_sample_rate_ms(long * sample_rate_ms)518 void inv_get_gyro_sample_rate_ms(long *sample_rate_ms)
519 {
520 *sample_rate_ms = sensors.gyro.sample_rate_ms;
521 }
522
inv_get_accel_sample_rate_ms(long * sample_rate_ms)523 void inv_get_accel_sample_rate_ms(long *sample_rate_ms)
524 {
525 *sample_rate_ms = sensors.accel.sample_rate_ms;
526 }
527
inv_get_compass_sample_rate_ms(long * sample_rate_ms)528 void inv_get_compass_sample_rate_ms(long *sample_rate_ms)
529 {
530 *sample_rate_ms = sensors.compass.sample_rate_ms;
531 }
532
533 /** Set Quat Sample rate in micro seconds.
534 * @param[in] sample_rate_us Set Quat Sample rate in us
535 */
inv_set_quat_sample_rate(long sample_rate_us)536 void inv_set_quat_sample_rate(long sample_rate_us)
537 {
538 #ifdef INV_PLAYBACK_DBG
539 if (inv_data_builder.debug_mode == RD_RECORD) {
540 int type = PLAYBACK_DBG_TYPE_Q_SAMPLE_RATE;
541 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
542 fwrite(&sample_rate_us, sizeof(sample_rate_us), 1, inv_data_builder.file);
543 }
544 #endif
545 sensors.quat.sample_rate_us = sample_rate_us;
546 sensors.quat.sample_rate_ms = sample_rate_us / 1000;
547 }
548
549 /** Set Gyro Bandwidth in Hz
550 * @param[in] bandwidth_hz Gyro bandwidth in Hz
551 */
inv_set_gyro_bandwidth(int bandwidth_hz)552 void inv_set_gyro_bandwidth(int bandwidth_hz)
553 {
554 sensors.gyro.bandwidth = bandwidth_hz;
555 }
556
557 /** Set Accel Bandwidth in Hz
558 * @param[in] bandwidth_hz Gyro bandwidth in Hz
559 */
inv_set_accel_bandwidth(int bandwidth_hz)560 void inv_set_accel_bandwidth(int bandwidth_hz)
561 {
562 sensors.accel.bandwidth = bandwidth_hz;
563 }
564
565 /** Set Compass Bandwidth in Hz
566 * @param[in] bandwidth_hz Gyro bandwidth in Hz
567 */
inv_set_compass_bandwidth(int bandwidth_hz)568 void inv_set_compass_bandwidth(int bandwidth_hz)
569 {
570 sensors.compass.bandwidth = bandwidth_hz;
571 }
572
573 /** Helper function stating whether the compass is on or off.
574 * @return TRUE if compass if on, 0 if compass if off
575 */
inv_get_compass_on()576 int inv_get_compass_on()
577 {
578 return (sensors.compass.status & INV_SENSOR_ON) == INV_SENSOR_ON;
579 }
580
581 /** Helper function stating whether the gyro is on or off.
582 * @return TRUE if gyro if on, 0 if gyro if off
583 */
inv_get_gyro_on()584 int inv_get_gyro_on()
585 {
586 return (sensors.gyro.status & INV_SENSOR_ON) == INV_SENSOR_ON;
587 }
588
589 /** Helper function stating whether the acceleromter is on or off.
590 * @return TRUE if accel if on, 0 if accel if off
591 */
inv_get_accel_on()592 int inv_get_accel_on()
593 {
594 return (sensors.accel.status & INV_SENSOR_ON) == INV_SENSOR_ON;
595 }
596
597 /** Get last timestamp across all 3 sensors that are on.
598 * This find out which timestamp has the largest value for sensors that are on.
599 * @return Returns INV_SUCCESS if successful or an error code if not.
600 */
inv_get_last_timestamp()601 inv_time_t inv_get_last_timestamp()
602 {
603 inv_time_t timestamp = 0;
604 if (sensors.accel.status & INV_SENSOR_ON) {
605 timestamp = sensors.accel.timestamp;
606 }
607 if (sensors.gyro.status & INV_SENSOR_ON) {
608 if (timestamp < sensors.gyro.timestamp) {
609 timestamp = sensors.gyro.timestamp;
610 }
611 MPL_LOGV("g ts: %lld", timestamp);
612 }
613 if (sensors.compass.status & INV_SENSOR_ON) {
614 if (timestamp < sensors.compass.timestamp) {
615 timestamp = sensors.compass.timestamp;
616 }
617 }
618 if (sensors.temp.status & INV_SENSOR_ON) {
619 if (timestamp < sensors.temp.timestamp) {
620 timestamp = sensors.temp.timestamp;
621 }
622 }
623 if (sensors.quat.status & INV_SENSOR_ON) {
624 if (timestamp < sensors.quat.timestamp) {
625 timestamp = sensors.quat.timestamp;
626 }
627 MPL_LOGV("q ts: %lld", timestamp);
628 }
629
630 return timestamp;
631 }
632
633 /** Sets the orientation and sensitivity of the gyro data.
634 * @param[in] orientation A scalar defining the transformation from chip mounting
635 * to the body frame. The function inv_orientation_matrix_to_scalar()
636 * can convert the transformation matrix to this scalar and describes the
637 * scalar in further detail.
638 * @param[in] sensitivity A scale factor to convert device units to g's
639 * such that g's = device_units * sensitivity / 2^30. Typically
640 * it works out to be the maximum g_value * 2^15.
641 */
inv_set_accel_orientation_and_scale(int orientation,long sensitivity)642 void inv_set_accel_orientation_and_scale(int orientation, long sensitivity)
643 {
644 #ifdef INV_PLAYBACK_DBG
645 if (inv_data_builder.debug_mode == RD_RECORD) {
646 int type = PLAYBACK_DBG_TYPE_A_ORIENT;
647 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
648 fwrite(&orientation, sizeof(orientation), 1, inv_data_builder.file);
649 fwrite(&sensitivity, sizeof(sensitivity), 1, inv_data_builder.file);
650 }
651 #endif
652 set_sensor_orientation_and_scale(&sensors.accel, orientation,
653 sensitivity);
654 }
655
656 /** Sets the Orientation and Sensitivity of the gyro data.
657 * @param[in] orientation A scalar defining the transformation from chip mounting
658 * to the body frame. The function inv_orientation_matrix_to_scalar()
659 * can convert the transformation matrix to this scalar and describes the
660 * scalar in further detail.
661 * @param[in] sensitivity A scale factor to convert device units to uT
662 * such that uT = device_units * sensitivity / 2^30. Typically
663 * it works out to be the maximum uT_value * 2^15.
664 */
inv_set_compass_orientation_and_scale(int orientation,long sensitivity)665 void inv_set_compass_orientation_and_scale(int orientation, long sensitivity)
666 {
667 #ifdef INV_PLAYBACK_DBG
668 if (inv_data_builder.debug_mode == RD_RECORD) {
669 int type = PLAYBACK_DBG_TYPE_C_ORIENT;
670 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
671 fwrite(&orientation, sizeof(orientation), 1, inv_data_builder.file);
672 fwrite(&sensitivity, sizeof(sensitivity), 1, inv_data_builder.file);
673 }
674 #endif
675 set_sensor_orientation_and_scale(&sensors.compass, orientation, sensitivity);
676 }
677
inv_matrix_vector_mult(const long * A,const long * x,long * y)678 void inv_matrix_vector_mult(const long *A, const long *x, long *y)
679 {
680 y[0] = inv_q30_mult(A[0], x[0]) + inv_q30_mult(A[1], x[1]) + inv_q30_mult(A[2], x[2]);
681 y[1] = inv_q30_mult(A[3], x[0]) + inv_q30_mult(A[4], x[1]) + inv_q30_mult(A[5], x[2]);
682 y[2] = inv_q30_mult(A[6], x[0]) + inv_q30_mult(A[7], x[1]) + inv_q30_mult(A[8], x[2]);
683 }
684
685 /** Takes raw data stored in the sensor, removes bias, and converts it to
686 * calibrated data in the body frame. Also store raw data for body frame.
687 * @param[in,out] sensor structure to modify
688 * @param[in] bias bias in the mounting frame, in hardware units scaled by
689 * 2^16. Length 3.
690 */
inv_apply_calibration(struct inv_single_sensor_t * sensor,const long * bias)691 void inv_apply_calibration(struct inv_single_sensor_t *sensor, const long *bias)
692 {
693 long raw32[3];
694
695 // Convert raw to calibrated
696 raw32[0] = (long)sensor->raw[0] << 15;
697 raw32[1] = (long)sensor->raw[1] << 15;
698 raw32[2] = (long)sensor->raw[2] << 15;
699
700 inv_convert_to_body_with_scale(sensor->orientation, sensor->sensitivity << 1, raw32, sensor->raw_scaled);
701
702 raw32[0] -= bias[0] >> 1;
703 raw32[1] -= bias[1] >> 1;
704 raw32[2] -= bias[2] >> 1;
705
706 inv_convert_to_body_with_scale(sensor->orientation, sensor->sensitivity << 1, raw32, sensor->calibrated);
707
708 sensor->status |= INV_CALIBRATED;
709 }
710
711 /** Returns the current bias for the compass
712 * @param[out] bias Compass bias in hardware units scaled by 2^16. In mounting frame.
713 * Length 3.
714 */
inv_get_compass_bias(long * bias)715 void inv_get_compass_bias(long *bias)
716 {
717 if (bias != NULL) {
718 memcpy(bias, inv_data_builder.save.compass_bias, sizeof(inv_data_builder.save.compass_bias));
719 }
720 }
721
722 /** Sets the compass bias
723 * @param[in] bias Length 3, in body frame, in hardware units scaled by 2^16 to allow fractional bit correction.
724 * @param[in] accuracy Accuracy of compass data, where 3=most accurate, and 0=least accurate.
725 */
inv_set_compass_bias(const long * bias,int accuracy)726 void inv_set_compass_bias(const long *bias, int accuracy)
727 {
728 if (memcmp(inv_data_builder.save.compass_bias, bias, sizeof(inv_data_builder.save.compass_bias))) {
729 memcpy(inv_data_builder.save.compass_bias, bias, sizeof(inv_data_builder.save.compass_bias));
730 inv_apply_calibration(&sensors.compass, inv_data_builder.save.compass_bias);
731 }
732 sensors.compass.accuracy = accuracy;
733 inv_data_builder.save.compass_accuracy = accuracy;
734 inv_set_message(INV_MSG_NEW_CB_EVENT, INV_MSG_NEW_CB_EVENT, 0);
735 }
736
737 /** Set the state of a compass disturbance
738 * @param[in] dist 1=disturbance, 0=no disturbance
739 */
inv_set_compass_disturbance(int dist)740 void inv_set_compass_disturbance(int dist)
741 {
742 inv_data_builder.compass_disturbance = dist;
743 }
744
inv_get_compass_disturbance(void)745 int inv_get_compass_disturbance(void) {
746 return inv_data_builder.compass_disturbance;
747 }
748
749 /**
750 * Sets the factory accel bias
751 * @param[in] bias
752 * Accel bias in hardware units (+/- 2 gee full scale assumed)
753 * scaled by 2^16. In chip mounting frame. Length of 3.
754 */
inv_set_accel_bias(const long * bias)755 void inv_set_accel_bias(const long *bias)
756 {
757 if (!bias)
758 return;
759
760 if (memcmp(inv_data_builder.save.factory_accel_bias, bias,
761 sizeof(inv_data_builder.save.factory_accel_bias))) {
762 memcpy(inv_data_builder.save.factory_accel_bias, bias,
763 sizeof(inv_data_builder.save.factory_accel_bias));
764 }
765 inv_set_message(INV_MSG_NEW_FAB_EVENT, INV_MSG_NEW_FAB_EVENT, 0);
766 }
767
768 /** Sets the accel accuracy.
769 * @param[in] accuracy Accuracy rating from 0 to 3, with 3 being most accurate.
770 */
inv_set_accel_accuracy(int accuracy)771 void inv_set_accel_accuracy(int accuracy)
772 {
773 sensors.accel.accuracy = accuracy;
774 inv_data_builder.save.accel_accuracy = accuracy;
775 }
776
777 /** Sets the accel bias with control over which axis.
778 * @param[in] bias Accel bias, length 3. In HW units scaled by 2^16 in body frame
779 * @param[in] accuracy Accuracy rating from 0 to 3, with 3 being most accurate.
780 * @param[in] mask Mask to select axis to apply bias set.
781 */
inv_set_accel_bias_mask(const long * bias,int accuracy,int mask)782 void inv_set_accel_bias_mask(const long *bias, int accuracy, int mask)
783 {
784 if (bias) {
785 if (mask & 1){
786 inv_data_builder.save_accel_mpl.accel_bias[0] = bias[0];
787 }
788 if (mask & 2){
789 inv_data_builder.save_accel_mpl.accel_bias[1] = bias[1];
790 }
791 if (mask & 4){
792 inv_data_builder.save_accel_mpl.accel_bias[2] = bias[2];
793 }
794
795 inv_apply_calibration(&sensors.accel, inv_data_builder.save_accel_mpl.accel_bias);
796 }
797 inv_set_accel_accuracy(accuracy);
798 inv_set_message(INV_MSG_NEW_AB_EVENT, INV_MSG_NEW_AB_EVENT, 0);
799 }
800
801 #ifdef WIN32
802 /** Sends out a message to activate writing 9-axis quaternion to the DMP.
803 */
inv_overwrite_dmp_9quat(void)804 void inv_overwrite_dmp_9quat(void)
805 {
806 inv_set_message(INV_MSG_NEW_DMP_QUAT_WRITE_EVENT, INV_MSG_NEW_DMP_QUAT_WRITE_EVENT, 0);
807 }
808 #endif
809
810 /**
811 * Sets the factory gyro bias
812 * @param[in] bias
813 * Gyro bias in hardware units (+/- 2000 dps full scale assumed)
814 * scaled by 2^16. In chip mounting frame. Length of 3.
815 */
inv_set_gyro_bias(const long * bias)816 void inv_set_gyro_bias(const long *bias)
817 {
818 if (!bias)
819 return;
820
821 if (memcmp(inv_data_builder.save.factory_gyro_bias, bias,
822 sizeof(inv_data_builder.save.factory_gyro_bias))) {
823 memcpy(inv_data_builder.save.factory_gyro_bias, bias,
824 sizeof(inv_data_builder.save.factory_gyro_bias));
825 }
826 inv_set_message(INV_MSG_NEW_FGB_EVENT, INV_MSG_NEW_FGB_EVENT, 0);
827 }
828
829 /**
830 * Sets the mpl gyro bias
831 * @param[in] bias
832 * Gyro bias in hardware units scaled by 2^16 (+/- 2000 dps full
833 * scale assumed). In chip mounting frame. Length 3.
834 * @param[in] accuracy
835 * Accuracy of bias. 0 = least accurate, 3 = most accurate.
836 */
inv_set_mpl_gyro_bias(const long * bias,int accuracy)837 void inv_set_mpl_gyro_bias(const long *bias, int accuracy)
838 {
839 if (bias != NULL) {
840 if (memcmp(inv_data_builder.save_mpl.gyro_bias, bias,
841 sizeof(inv_data_builder.save_mpl.gyro_bias))) {
842 memcpy(inv_data_builder.save_mpl.gyro_bias, bias,
843 sizeof(inv_data_builder.save_mpl.gyro_bias));
844 inv_apply_calibration(&sensors.gyro,
845 inv_data_builder.save_mpl.gyro_bias);
846 }
847 }
848 sensors.gyro.accuracy = accuracy;
849 inv_data_builder.save.gyro_accuracy = accuracy;
850
851 /* TODO: What should we do if there's no temperature data? */
852 if (sensors.temp.calibrated[0])
853 inv_data_builder.save.gyro_temp = sensors.temp.calibrated[0];
854 else
855 /* Set to 27 deg C for now until we've got a better solution. */
856 inv_data_builder.save.gyro_temp = 27L << 16;
857 inv_set_message(INV_MSG_NEW_GB_EVENT, INV_MSG_NEW_GB_EVENT, 0);
858
859 /* TODO: this flag works around the synchronization problem seen with using
860 the user-exposed message layer to signal the temperature compensation
861 module that gyro biases were set.
862 A better, cleaner method is certainly needed. */
863 inv_data_builder.save.gyro_bias_tc_set = true;
864 }
865
866 /**
867 * @internal
868 * @brief Return whether gyro biases were set to signal the temperature
869 * compensation algorithm that it can collect a data point to build
870 * the temperature slope while in no motion state.
871 * The flag clear automatically after is read.
872 * @return true if the flag was set, indicating gyro biases were set.
873 * false if the flag was not set.
874 */
inv_get_gyro_bias_tc_set(void)875 int inv_get_gyro_bias_tc_set(void)
876 {
877 int flag = (inv_data_builder.save.gyro_bias_tc_set == true);
878 inv_data_builder.save.gyro_bias_tc_set = false;
879 return flag;
880 }
881
882
883 /**
884 * Get the mpl gyro biases
885 * @param[in] bias
886 * Gyro calibrated bias.
887 * Length 3.
888 */
inv_get_mpl_gyro_bias(long * bias,long * temp)889 void inv_get_mpl_gyro_bias(long *bias, long *temp)
890 {
891 if (bias != NULL)
892 memcpy(bias, inv_data_builder.save_mpl.gyro_bias,
893 sizeof(inv_data_builder.save_mpl.gyro_bias));
894
895 if (temp != NULL)
896 temp[0] = inv_data_builder.save.gyro_temp;
897 }
898
899 /** Gyro Bias in the form used by the DMP.
900 * @param[out] bias Gyro Bias in the form used by the DMP. It is scaled appropriately
901 * and is in the body frame as needed. If this bias is applied in the DMP
902 * then any quaternion must have the flag INV_BIAS_APPLIED set if it is a
903 * 3-axis quaternion, or INV_QUAT_6AXIS if it is a 6-axis quaternion
904 */
inv_get_gyro_bias_dmp_units(long * bias)905 void inv_get_gyro_bias_dmp_units(long *bias)
906 {
907 if (bias == NULL)
908 return;
909 inv_convert_to_body_with_scale(sensors.gyro.orientation, 46850825L,
910 inv_data_builder.save_mpl.gyro_bias, bias);
911 }
912
913 /* TODO: Add this information to inv_sensor_cal_t */
914 /**
915 * Get the gyro biases and temperature record from MPL
916 * @param[in] bias
917 * Gyro bias in hardware units scaled by 2^16.
918 * In chip mounting frame.
919 * Length 3.
920 */
inv_get_gyro_bias(long * bias)921 void inv_get_gyro_bias(long *bias)
922 {
923 if (bias != NULL)
924 memcpy(bias, inv_data_builder.save.factory_gyro_bias,
925 sizeof(inv_data_builder.save.factory_gyro_bias));
926 }
927
928 /**
929 * Get factory accel bias mask
930 * @param[in] bias
931 * Accel bias mask
932 * 1 is set, 0 is not set, Length 3 = x,y,z.
933 */
inv_get_factory_accel_bias_mask()934 int inv_get_factory_accel_bias_mask()
935 {
936 long bias[3];
937 int bias_mask = 0;
938 inv_get_accel_bias(bias);
939 if (bias != NULL) {
940 int i;
941 for(i = 0; i < 3; i++) {
942 if(bias[i] != 0) {
943 bias_mask |= 1 << i;
944 } else {
945 bias_mask &= ~ (1 << i);
946 }
947 }
948 }
949 return bias_mask;
950 }
951
952 /**
953 * Get accel bias from MPL
954 * @param[in] bias
955 * Accel bias in hardware units scaled by 2^16.
956 * In chp mounting frame.
957 * Length 3.
958 */
inv_get_accel_bias(long * bias)959 void inv_get_accel_bias(long *bias)
960 {
961 if (bias != NULL)
962 memcpy(bias, inv_data_builder.save.factory_accel_bias,
963 sizeof(inv_data_builder.save.factory_accel_bias));
964 }
965
966 /** Get Accel Bias
967 * @param[out] bias Accel bias
968 * @param[out] temp Temperature where 1 C = 2^16
969 */
inv_get_mpl_accel_bias(long * bias,long * temp)970 void inv_get_mpl_accel_bias(long *bias, long *temp)
971 {
972 if (bias != NULL)
973 memcpy(bias, inv_data_builder.save_accel_mpl.accel_bias,
974 sizeof(inv_data_builder.save_accel_mpl.accel_bias));
975 if (temp != NULL)
976 temp[0] = inv_data_builder.save.accel_temp;
977 }
978
979 /** Accel Bias in the form used by the DMP.
980 * @param[out] bias Accel Bias in the form used by the DMP. It is scaled appropriately
981 * and is in the body frame as needed.
982 */
inv_get_accel_bias_dmp_units(long * bias)983 void inv_get_accel_bias_dmp_units(long *bias)
984 {
985 if (bias == NULL)
986 return;
987 inv_convert_to_body_with_scale(sensors.accel.orientation, 536870912L,
988 inv_data_builder.save_accel_mpl.accel_bias, bias);
989 }
990
991 /**
992 * Record new accel data for use when inv_execute_on_data() is called
993 * @param[in] accel
994 * accel data, length 3.
995 * Calibrated data is in m/s^2 scaled by 2^16 in body frame.
996 * Raw data is in device units in chip mounting frame.
997 * @param[in] status
998 * Lower 2 bits are the accuracy, with 0 being inaccurate, and 3
999 * being most accurate.
1000 * The upper bit INV_CALIBRATED, is set if the data was calibrated
1001 * outside MPL and it is not set if the data being passed is raw.
1002 * Raw data should be in device units, typically in a 16-bit range.
1003 * @param[in] timestamp
1004 * Monotonic time stamp, for Android it's in nanoseconds.
1005 * @return Returns INV_SUCCESS if successful or an error code if not.
1006 */
inv_build_accel(const long * accel,int status,inv_time_t timestamp)1007 inv_error_t inv_build_accel(const long *accel, int status, inv_time_t timestamp)
1008 {
1009 #ifdef INV_PLAYBACK_DBG
1010 if (inv_data_builder.debug_mode == RD_RECORD) {
1011 int type = PLAYBACK_DBG_TYPE_ACCEL;
1012 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1013 fwrite(accel, sizeof(accel[0]), 3, inv_data_builder.file);
1014 fwrite(×tamp, sizeof(timestamp), 1, inv_data_builder.file);
1015 }
1016 #endif
1017
1018 if ((status & INV_CALIBRATED) == 0) {
1019 sensors.accel.raw[0] = (short)accel[0];
1020 sensors.accel.raw[1] = (short)accel[1];
1021 sensors.accel.raw[2] = (short)accel[2];
1022 sensors.accel.status |= INV_RAW_DATA;
1023 inv_apply_calibration(&sensors.accel, inv_data_builder.save_accel_mpl.accel_bias);
1024 } else {
1025 sensors.accel.calibrated[0] = accel[0];
1026 sensors.accel.calibrated[1] = accel[1];
1027 sensors.accel.calibrated[2] = accel[2];
1028 sensors.accel.status |= INV_CALIBRATED;
1029 sensors.accel.accuracy = status & 3;
1030 inv_data_builder.save.accel_accuracy = status & 3;
1031 }
1032 sensors.accel.status |= INV_NEW_DATA | INV_SENSOR_ON;
1033 sensors.accel.timestamp_prev = sensors.accel.timestamp;
1034 sensors.accel.timestamp = timestamp;
1035
1036 return INV_SUCCESS;
1037 }
1038
1039 /** Record new gyro data and calls inv_execute_on_data() if previous
1040 * sample has not been processed.
1041 * @param[in] gyro Data is in device units. Length 3.
1042 * @param[in] timestamp Monotonic time stamp, for Android it's in nanoseconds.
1043 * @param[out] executed Set to 1 if data processing was done.
1044 * @return Returns INV_SUCCESS if successful or an error code if not.
1045 */
inv_build_gyro(const short * gyro,inv_time_t timestamp)1046 inv_error_t inv_build_gyro(const short *gyro, inv_time_t timestamp)
1047 {
1048 #ifdef INV_PLAYBACK_DBG
1049 if (inv_data_builder.debug_mode == RD_RECORD) {
1050 int type = PLAYBACK_DBG_TYPE_GYRO;
1051 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1052 fwrite(gyro, sizeof(gyro[0]), 3, inv_data_builder.file);
1053 fwrite(×tamp, sizeof(timestamp), 1, inv_data_builder.file);
1054 }
1055 #endif
1056
1057 memcpy(sensors.gyro.raw, gyro, 3 * sizeof(short));
1058 sensors.gyro.status |= INV_NEW_DATA | INV_RAW_DATA | INV_SENSOR_ON;
1059 sensors.gyro.timestamp_prev = sensors.gyro.timestamp;
1060 sensors.gyro.timestamp = timestamp;
1061 inv_apply_calibration(&sensors.gyro, inv_data_builder.save_mpl.gyro_bias);
1062
1063 return INV_SUCCESS;
1064 }
1065
1066 /** Record new compass data for use when inv_execute_on_data() is called
1067 * @param[in] compass Compass data, if it was calibrated outside MPL, the units are uT scaled by 2^16.
1068 * Length 3.
1069 * @param[in] status Lower 2 bits are the accuracy, with 0 being inaccurate, and 3 being most accurate.
1070 * The upper bit INV_CALIBRATED, is set if the data was calibrated outside MPL and it is
1071 * not set if the data being passed is raw. Raw data should be in device units, typically
1072 * in a 16-bit range.
1073 * @param[in] timestamp Monotonic time stamp, for Android it's in nanoseconds.
1074 * @param[out] executed Set to 1 if data processing was done.
1075 * @return Returns INV_SUCCESS if successful or an error code if not.
1076 */
inv_build_compass(const long * compass,int status,inv_time_t timestamp)1077 inv_error_t inv_build_compass(const long *compass, int status,
1078 inv_time_t timestamp)
1079 {
1080 #ifdef INV_PLAYBACK_DBG
1081 if (inv_data_builder.debug_mode == RD_RECORD) {
1082 int type = PLAYBACK_DBG_TYPE_COMPASS;
1083 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1084 fwrite(compass, sizeof(compass[0]), 3, inv_data_builder.file);
1085 fwrite(×tamp, sizeof(timestamp), 1, inv_data_builder.file);
1086 }
1087 #endif
1088
1089 if ((status & INV_CALIBRATED) == 0) {
1090 long data[3];
1091 inv_set_compass_soft_iron_input_data(compass);
1092 inv_get_compass_soft_iron_output_data(data);
1093 sensors.compass.raw[0] = (short)data[0];
1094 sensors.compass.raw[1] = (short)data[1];
1095 sensors.compass.raw[2] = (short)data[2];
1096 inv_apply_calibration(&sensors.compass, inv_data_builder.save.compass_bias);
1097 sensors.compass.status |= INV_RAW_DATA;
1098 } else {
1099 sensors.compass.calibrated[0] = compass[0];
1100 sensors.compass.calibrated[1] = compass[1];
1101 sensors.compass.calibrated[2] = compass[2];
1102 sensors.compass.status |= INV_CALIBRATED;
1103 sensors.compass.accuracy = status & 3;
1104 inv_data_builder.save.compass_accuracy = status & 3;
1105 }
1106 sensors.compass.timestamp_prev = sensors.compass.timestamp;
1107 sensors.compass.timestamp = timestamp;
1108 sensors.compass.status |= INV_NEW_DATA | INV_SENSOR_ON;
1109
1110 return INV_SUCCESS;
1111 }
1112
1113 /** Record new temperature data for use when inv_execute_on_data() is called.
1114 * @param[in] temp Temperature data in q16 format.
1115 * @param[in] timestamp Monotonic time stamp; for Android it's in
1116 * nanoseconds.
1117 * @return Returns INV_SUCCESS if successful or an error code if not.
1118 */
inv_build_temp(const long temp,inv_time_t timestamp)1119 inv_error_t inv_build_temp(const long temp, inv_time_t timestamp)
1120 {
1121 #ifdef INV_PLAYBACK_DBG
1122 if (inv_data_builder.debug_mode == RD_RECORD) {
1123 int type = PLAYBACK_DBG_TYPE_TEMPERATURE;
1124 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1125 fwrite(&temp, sizeof(temp), 1, inv_data_builder.file);
1126 fwrite(×tamp, sizeof(timestamp), 1, inv_data_builder.file);
1127 }
1128 #endif
1129 sensors.temp.calibrated[0] = temp;
1130 sensors.temp.status |= INV_NEW_DATA | INV_RAW_DATA | INV_SENSOR_ON;
1131 sensors.temp.timestamp_prev = sensors.temp.timestamp;
1132 sensors.temp.timestamp = timestamp;
1133 /* TODO: Apply scale, remove offset. */
1134
1135 return INV_SUCCESS;
1136 }
1137 /** quaternion data
1138 * @param[in] quat Quaternion data. 2^30 = 1.0 or 2^14=1 for 16-bit data.
1139 * Real part first. Length 4.
1140 * @param[in] status number of axis, 16-bit or 32-bit
1141 * set INV_QUAT_3ELEMENT if input quaternion has only 3 elements (no scalar).
1142 * inv_compute_scalar_part() assumes 32-bit data. If using 16-bit quaternion,
1143 * shift 16 bits first before calling this function.
1144 * @param[in] timestamp
1145 * @param[in] timestamp Monotonic time stamp; for Android it's in
1146 * nanoseconds.
1147 * @param[out] executed Set to 1 if data processing was done.
1148 * @return Returns INV_SUCCESS if successful or an error code if not.
1149 */
inv_build_quat(const long * quat,int status,inv_time_t timestamp)1150 inv_error_t inv_build_quat(const long *quat, int status, inv_time_t timestamp)
1151 {
1152 #ifdef INV_PLAYBACK_DBG
1153 if (inv_data_builder.debug_mode == RD_RECORD) {
1154 int type = PLAYBACK_DBG_TYPE_QUAT;
1155 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1156 fwrite(quat, sizeof(quat[0]), 4, inv_data_builder.file);
1157 fwrite(×tamp, sizeof(timestamp), 1, inv_data_builder.file);
1158 }
1159 #endif
1160
1161 /* Android version DMP does not have scalar part */
1162 if (status & INV_QUAT_3ELEMENT) {
1163 long resultQuat[4];
1164 MPL_LOGV("3q: %ld,%ld,%ld\n", quat[0], quat[1], quat[2]);
1165 inv_compute_scalar_part(quat, resultQuat);
1166 MPL_LOGV("4q: %ld,%ld,%ld,%ld\n", resultQuat[0], resultQuat[1], resultQuat[2], resultQuat[3]);
1167 memcpy(sensors.quat.raw, resultQuat, sizeof(sensors.quat.raw));
1168 } else {
1169 memcpy(sensors.quat.raw, quat, sizeof(sensors.quat.raw));
1170 }
1171 sensors.quat.timestamp_prev = sensors.quat.timestamp;
1172 sensors.quat.timestamp = timestamp;
1173 sensors.quat.status |= INV_NEW_DATA | INV_RAW_DATA | INV_SENSOR_ON;
1174 sensors.quat.status |= (INV_QUAT_3AXIS & status);
1175 sensors.quat.status |= (INV_QUAT_6AXIS & status);
1176 sensors.quat.status |= (INV_QUAT_9AXIS & status);
1177 sensors.quat.status |= (INV_BIAS_APPLIED & status);
1178 sensors.quat.status |= (INV_DMP_BIAS_APPLIED & status);
1179 sensors.quat.status |= (INV_QUAT_3ELEMENT & status);
1180 MPL_LOGV("quat.status: %d", sensors.quat.status);
1181 if (sensors.quat.status & (INV_QUAT_9AXIS | INV_QUAT_6AXIS)) {
1182 // Set quaternion
1183 inv_store_gaming_quaternion(quat, timestamp);
1184 }
1185 if (sensors.quat.status & INV_QUAT_9AXIS) {
1186 long identity[4] = {(1L<<30), 0, 0, 0};
1187 inv_set_compass_correction(identity, timestamp);
1188 }
1189 return INV_SUCCESS;
1190 }
1191
inv_build_pressure(const long pressure,int status,inv_time_t timestamp)1192 inv_error_t inv_build_pressure(const long pressure, int status, inv_time_t timestamp)
1193 {
1194 sensors.pressure.status |= INV_NEW_DATA;
1195 return INV_SUCCESS;
1196 }
1197
1198 /** This should be called when the accel has been turned off. This is so
1199 * that we will know if the data is contiguous.
1200 */
inv_accel_was_turned_off()1201 void inv_accel_was_turned_off()
1202 {
1203 #ifdef INV_PLAYBACK_DBG
1204 if (inv_data_builder.debug_mode == RD_RECORD) {
1205 int type = PLAYBACK_DBG_TYPE_COMPASS_OFF;
1206 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1207 }
1208 #endif
1209 sensors.accel.status = 0;
1210 }
1211
1212 /** This should be called when the compass has been turned off. This is so
1213 * that we will know if the data is contiguous.
1214 */
inv_compass_was_turned_off()1215 void inv_compass_was_turned_off()
1216 {
1217 #ifdef INV_PLAYBACK_DBG
1218 if (inv_data_builder.debug_mode == RD_RECORD) {
1219 int type = PLAYBACK_DBG_TYPE_COMPASS_OFF;
1220 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1221 }
1222 #endif
1223 sensors.compass.status = 0;
1224 }
1225
1226 /** This should be called when the quaternion data from the DMP has been turned off. This is so
1227 * that we will know if the data is contiguous.
1228 */
inv_quaternion_sensor_was_turned_off(void)1229 void inv_quaternion_sensor_was_turned_off(void)
1230 {
1231 #ifdef INV_PLAYBACK_DBG
1232 if (inv_data_builder.debug_mode == RD_RECORD) {
1233 int type = PLAYBACK_DBG_TYPE_QUAT_OFF;
1234 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1235 }
1236 #endif
1237 sensors.quat.status = 0;
1238 }
1239
1240 /** This should be called when the gyro has been turned off. This is so
1241 * that we will know if the data is contiguous.
1242 */
inv_gyro_was_turned_off()1243 void inv_gyro_was_turned_off()
1244 {
1245 #ifdef INV_PLAYBACK_DBG
1246 if (inv_data_builder.debug_mode == RD_RECORD) {
1247 int type = PLAYBACK_DBG_TYPE_GYRO_OFF;
1248 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1249 }
1250 #endif
1251 sensors.gyro.status = 0;
1252 }
1253
1254 /** This should be called when the temperature sensor has been turned off.
1255 * This is so that we will know if the data is contiguous.
1256 */
inv_temperature_was_turned_off()1257 void inv_temperature_was_turned_off()
1258 {
1259 sensors.temp.status = 0;
1260 }
1261
1262 /** Registers to receive a callback when there is new sensor data.
1263 * @internal
1264 * @param[in] func Function pointer to receive callback when there is new sensor data
1265 * @param[in] priority Lower priority numbers receive a callback before larger numbers. All priority
1266 * numbers must be unique.
1267 * @param[in] sensor_type Sets the type of data that triggers the callback. Must be non-zero. May be
1268 * a combination. INV_ACCEL_NEW = accel data, INV_GYRO_NEW =
1269 * gyro data, INV_MAG_NEW = compass data. So passing in
1270 * INV_ACCEL_NEW | INV_MAG_NEW, a
1271 * callback would be generated if there was new magnetomer data OR new accel data.
1272 */
inv_register_data_cb(inv_error_t (* func)(struct inv_sensor_cal_t * data),int priority,int sensor_type)1273 inv_error_t inv_register_data_cb(
1274 inv_error_t (*func)(struct inv_sensor_cal_t *data),
1275 int priority, int sensor_type)
1276 {
1277 inv_error_t result = INV_SUCCESS;
1278 int kk, nn;
1279
1280 // Make sure we haven't registered this function already
1281 // Or used the same priority
1282 for (kk = 0; kk < inv_data_builder.num_cb; ++kk) {
1283 if ((inv_data_builder.process[kk].func == func) ||
1284 (inv_data_builder.process[kk].priority == priority)) {
1285 return INV_ERROR_INVALID_PARAMETER; //fixme give a warning
1286 }
1287 }
1288
1289 // Make sure we have not filled up our number of allowable callbacks
1290 if (inv_data_builder.num_cb <= INV_MAX_DATA_CB - 1) {
1291 kk = 0;
1292 if (inv_data_builder.num_cb != 0) {
1293 // set kk to be where this new callback goes in the array
1294 while ((kk < inv_data_builder.num_cb) &&
1295 (inv_data_builder.process[kk].priority < priority)) {
1296 kk++;
1297 }
1298 if (kk != inv_data_builder.num_cb) {
1299 // We need to move the others
1300 for (nn = inv_data_builder.num_cb; nn > kk; --nn) {
1301 inv_data_builder.process[nn] =
1302 inv_data_builder.process[nn - 1];
1303 }
1304 }
1305 }
1306 // Add new callback
1307 inv_data_builder.process[kk].func = func;
1308 inv_data_builder.process[kk].priority = priority;
1309 inv_data_builder.process[kk].data_required = sensor_type;
1310 inv_data_builder.num_cb++;
1311 } else {
1312 MPL_LOGE("Unable to add feature callback as too many were already registered\n");
1313 result = INV_ERROR_MEMORY_EXAUSTED;
1314 }
1315
1316 return result;
1317 }
1318
1319 /** Unregisters the callback that happens when new sensor data is received.
1320 * @internal
1321 * @param[in] func Function pointer to receive callback when there is new sensor data
1322 * @param[in] priority Lower priority numbers receive a callback before larger numbers. All priority
1323 * numbers must be unique.
1324 * @param[in] sensor_type Sets the type of data that triggers the callback. Must be non-zero. May be
1325 * a combination. INV_ACCEL_NEW = accel data, INV_GYRO_NEW =
1326 * gyro data, INV_MAG_NEW = compass data. So passing in
1327 * INV_ACCEL_NEW | INV_MAG_NEW, a
1328 * callback would be generated if there was new magnetomer data OR new accel data.
1329 */
inv_unregister_data_cb(inv_error_t (* func)(struct inv_sensor_cal_t * data))1330 inv_error_t inv_unregister_data_cb(
1331 inv_error_t (*func)(struct inv_sensor_cal_t *data))
1332 {
1333 int kk, nn;
1334
1335 for (kk = 0; kk < inv_data_builder.num_cb; ++kk) {
1336 if (inv_data_builder.process[kk].func == func) {
1337 // Delete this callback
1338 for (nn = kk + 1; nn < inv_data_builder.num_cb; ++nn) {
1339 inv_data_builder.process[nn - 1] =
1340 inv_data_builder.process[nn];
1341 }
1342 inv_data_builder.num_cb--;
1343 return INV_SUCCESS;
1344 }
1345 }
1346
1347 return INV_SUCCESS; // We did not find the callback
1348 }
1349
1350 /** After at least one of inv_build_gyro(), inv_build_accel(), or
1351 * inv_build_compass() has been called, this function should be called.
1352 * It will process the data it has received and update all the internal states
1353 * and features that have been turned on.
1354 * @return Returns INV_SUCCESS if successful or an error code if not.
1355 */
inv_execute_on_data(void)1356 inv_error_t inv_execute_on_data(void)
1357 {
1358 inv_error_t result, first_error;
1359 int kk;
1360
1361 #ifdef INV_PLAYBACK_DBG
1362 if (inv_data_builder.debug_mode == RD_RECORD) {
1363 int type = PLAYBACK_DBG_TYPE_EXECUTE;
1364 fwrite(&type, sizeof(type), 1, inv_data_builder.file);
1365 }
1366 #endif
1367 // Determine what new data we have
1368 inv_data_builder.mode = 0;
1369 if (sensors.gyro.status & INV_NEW_DATA)
1370 inv_data_builder.mode |= INV_GYRO_NEW;
1371 if (sensors.accel.status & INV_NEW_DATA)
1372 inv_data_builder.mode |= INV_ACCEL_NEW;
1373 if (sensors.compass.status & INV_NEW_DATA)
1374 inv_data_builder.mode |= INV_MAG_NEW;
1375 if (sensors.temp.status & INV_NEW_DATA)
1376 inv_data_builder.mode |= INV_TEMP_NEW;
1377 if (sensors.quat.status & INV_NEW_DATA)
1378 inv_data_builder.mode |= INV_QUAT_NEW;
1379 if (sensors.pressure.status & INV_NEW_DATA)
1380 inv_data_builder.mode |= INV_PRESSURE_NEW;
1381
1382 first_error = INV_SUCCESS;
1383
1384 for (kk = 0; kk < inv_data_builder.num_cb; ++kk) {
1385 if (inv_data_builder.mode & inv_data_builder.process[kk].data_required) {
1386 result = inv_data_builder.process[kk].func(&sensors);
1387 if (result && !first_error) {
1388 first_error = result;
1389 }
1390 }
1391 }
1392
1393 inv_set_contiguous();
1394
1395 return first_error;
1396 }
1397
1398 /** Cleans up status bits after running all the callbacks. It sets the contiguous flag.
1399 *
1400 */
inv_set_contiguous(void)1401 static void inv_set_contiguous(void)
1402 {
1403 inv_time_t current_time = 0;
1404 if (sensors.gyro.status & INV_NEW_DATA) {
1405 sensors.gyro.status |= INV_CONTIGUOUS;
1406 current_time = sensors.gyro.timestamp;
1407 }
1408 if (sensors.accel.status & INV_NEW_DATA) {
1409 sensors.accel.status |= INV_CONTIGUOUS;
1410 current_time = MAX(current_time, sensors.accel.timestamp);
1411 }
1412 if (sensors.compass.status & INV_NEW_DATA) {
1413 sensors.compass.status |= INV_CONTIGUOUS;
1414 current_time = MAX(current_time, sensors.compass.timestamp);
1415 }
1416 if (sensors.temp.status & INV_NEW_DATA) {
1417 sensors.temp.status |= INV_CONTIGUOUS;
1418 current_time = MAX(current_time, sensors.temp.timestamp);
1419 }
1420 if (sensors.quat.status & INV_NEW_DATA) {
1421 sensors.quat.status |= INV_CONTIGUOUS;
1422 current_time = MAX(current_time, sensors.quat.timestamp);
1423 }
1424
1425 #if 0
1426 /* See if sensors are still on. These should be turned off by inv_*_was_turned_off()
1427 * type functions. This is just in case that breaks down. We make sure
1428 * all the data is within 2 seconds of the newest piece of data*/
1429 if (inv_delta_time_ms(current_time, sensors.gyro.timestamp) >= 2000)
1430 inv_gyro_was_turned_off();
1431 if (inv_delta_time_ms(current_time, sensors.accel.timestamp) >= 2000)
1432 inv_accel_was_turned_off();
1433 if (inv_delta_time_ms(current_time, sensors.compass.timestamp) >= 2000)
1434 inv_compass_was_turned_off();
1435 /* TODO: Temperature might not need to be read this quickly. */
1436 if (inv_delta_time_ms(current_time, sensors.temp.timestamp) >= 2000)
1437 inv_temperature_was_turned_off();
1438 #endif
1439
1440 /* clear bits */
1441 sensors.gyro.status &= ~INV_NEW_DATA;
1442 sensors.accel.status &= ~INV_NEW_DATA;
1443 sensors.compass.status &= ~INV_NEW_DATA;
1444 sensors.temp.status &= ~INV_NEW_DATA;
1445 sensors.quat.status &= ~INV_NEW_DATA;
1446 sensors.pressure.status &= ~INV_NEW_DATA;
1447 }
1448
1449 /** Gets a whole set of accel data including data, accuracy and timestamp.
1450 * @param[out] data Accel Data where 1g = 2^16
1451 * @param[out] accuracy Accuracy 0 being not accurate, and 3 being most accurate.
1452 * @param[out] timestamp The timestamp of the data sample.
1453 */
inv_get_accel_set(long * data,int8_t * accuracy,inv_time_t * timestamp)1454 void inv_get_accel_set(long *data, int8_t *accuracy, inv_time_t *timestamp)
1455 {
1456 if (data != NULL) {
1457 memcpy(data, sensors.accel.calibrated, sizeof(sensors.accel.calibrated));
1458 }
1459 if (timestamp != NULL) {
1460 *timestamp = sensors.accel.timestamp;
1461 }
1462 if (accuracy != NULL) {
1463 *accuracy = sensors.accel.accuracy;
1464 }
1465 }
1466
1467 /** Gets a whole set of gyro data including data, accuracy and timestamp.
1468 * @param[out] data Gyro Data where 1 dps = 2^16
1469 * @param[out] accuracy Accuracy 0 being not accurate, and 3 being most accurate.
1470 * @param[out] timestamp The timestamp of the data sample.
1471 */
inv_get_gyro_set(long * data,int8_t * accuracy,inv_time_t * timestamp)1472 void inv_get_gyro_set(long *data, int8_t *accuracy, inv_time_t *timestamp)
1473 {
1474 memcpy(data, sensors.gyro.calibrated, sizeof(sensors.gyro.calibrated));
1475 if (timestamp != NULL) {
1476 *timestamp = sensors.gyro.timestamp;
1477 }
1478 if (accuracy != NULL) {
1479 *accuracy = sensors.gyro.accuracy;
1480 }
1481 }
1482
1483 /** Gets a whole set of gyro raw data including data, accuracy and timestamp.
1484 * @param[out] data Gyro Data where 1 dps = 2^16
1485 * @param[out] accuracy Accuracy 0 being not accurate, and 3 being most accurate.
1486 * @param[out] timestamp The timestamp of the data sample.
1487 */
inv_get_gyro_set_raw(long * data,int8_t * accuracy,inv_time_t * timestamp)1488 void inv_get_gyro_set_raw(long *data, int8_t *accuracy, inv_time_t *timestamp)
1489 {
1490 memcpy(data, sensors.gyro.raw_scaled, sizeof(sensors.gyro.raw_scaled));
1491 if (timestamp != NULL) {
1492 *timestamp = sensors.gyro.timestamp;
1493 }
1494 if (accuracy != NULL) {
1495 *accuracy = 0;
1496 }
1497 }
1498
1499 /** Get's latest gyro data.
1500 * @param[out] gyro Gyro Data, Length 3. 1 dps = 2^16.
1501 */
inv_get_gyro(long * gyro)1502 void inv_get_gyro(long *gyro)
1503 {
1504 memcpy(gyro, sensors.gyro.calibrated, sizeof(sensors.gyro.calibrated));
1505 }
1506
1507 /** Gets a whole set of compass data including data, accuracy and timestamp.
1508 * @param[out] data Compass Data where 1 uT = 2^16
1509 * @param[out] accuracy Accuracy 0 being not accurate, and 3 being most accurate.
1510 * @param[out] timestamp The timestamp of the data sample.
1511 */
inv_get_compass_set(long * data,int8_t * accuracy,inv_time_t * timestamp)1512 void inv_get_compass_set(long *data, int8_t *accuracy, inv_time_t *timestamp)
1513 {
1514 memcpy(data, sensors.compass.calibrated, sizeof(sensors.compass.calibrated));
1515 if (timestamp != NULL) {
1516 *timestamp = sensors.compass.timestamp;
1517 }
1518 if (accuracy != NULL) {
1519 if (inv_data_builder.compass_disturbance)
1520 *accuracy = 0;
1521 else
1522 *accuracy = sensors.compass.accuracy;
1523 }
1524 }
1525
1526 /** Gets a whole set of compass raw data including data, accuracy and timestamp.
1527 * @param[out] data Compass Data where 1 uT = 2^16
1528 * @param[out] accuracy Accuracy 0 being not accurate, and 3 being most accurate.
1529 * @param[out] timestamp The timestamp of the data sample.
1530 */
inv_get_compass_set_raw(long * data,int8_t * accuracy,inv_time_t * timestamp)1531 void inv_get_compass_set_raw(long *data, int8_t *accuracy, inv_time_t *timestamp)
1532 {
1533 memcpy(data, sensors.compass.raw_scaled, sizeof(sensors.compass.raw_scaled));
1534 if (timestamp != NULL) {
1535 *timestamp = sensors.compass.timestamp;
1536 }
1537 //per Michele, since data is raw, accuracy should = 0
1538 *accuracy = 0;
1539 }
1540
1541 /** Gets a whole set of temperature data including data, accuracy and timestamp.
1542 * @param[out] data Temperature data where 1 degree C = 2^16
1543 * @param[out] accuracy 0 to 3, where 3 is most accurate.
1544 * @param[out] timestamp The timestamp of the data sample.
1545 */
inv_get_temp_set(long * data,int * accuracy,inv_time_t * timestamp)1546 void inv_get_temp_set(long *data, int *accuracy, inv_time_t *timestamp)
1547 {
1548 data[0] = sensors.temp.calibrated[0];
1549 if (timestamp)
1550 *timestamp = sensors.temp.timestamp;
1551 if (accuracy)
1552 *accuracy = sensors.temp.accuracy;
1553 }
1554
1555 /** Returns accuracy of gyro.
1556 * @return Accuracy of gyro with 0 being not accurate, and 3 being most accurate.
1557 */
inv_get_gyro_accuracy(void)1558 int inv_get_gyro_accuracy(void)
1559 {
1560 return sensors.gyro.accuracy;
1561 }
1562
1563 /** Returns accuracy of compass.
1564 * @return Accuracy of compass with 0 being not accurate, and 3 being most accurate.
1565 */
inv_get_mag_accuracy(void)1566 int inv_get_mag_accuracy(void)
1567 {
1568 if (inv_data_builder.compass_disturbance)
1569 return 0;
1570 return sensors.compass.accuracy;
1571 }
1572
1573 /** Returns accuracy of accel.
1574 * @return Accuracy of accel with 0 being not accurate, and 3 being most accurate.
1575 */
inv_get_accel_accuracy(void)1576 int inv_get_accel_accuracy(void)
1577 {
1578 return sensors.accel.accuracy;
1579 }
1580
inv_get_gyro_orient(int * orient)1581 inv_error_t inv_get_gyro_orient(int *orient)
1582 {
1583 *orient = sensors.gyro.orientation;
1584 return 0;
1585 }
1586
inv_get_accel_orient(int * orient)1587 inv_error_t inv_get_accel_orient(int *orient)
1588 {
1589 *orient = sensors.accel.orientation;
1590 return 0;
1591 }
1592
1593 /*======================================================================*/
1594 /* compass soft iron module */
1595 /*======================================================================*/
1596
1597 /** Gets the 3x3 compass transform matrix in 32 bit Q30 fixed point format.
1598 * @param[out] the pointer of the 3x3 matrix in Q30 format
1599 */
inv_get_compass_soft_iron_matrix_d(long * matrix)1600 void inv_get_compass_soft_iron_matrix_d(long *matrix) {
1601 int i;
1602 for (i=0; i<9; i++) {
1603 matrix[i] = sensors.soft_iron.matrix_d[i];
1604 }
1605 }
1606
1607 /** Sets the 3x3 compass transform matrix in 32 bit Q30 fixed point format.
1608 * @param[in] the pointer of the 3x3 matrix in Q30 format
1609 */
inv_set_compass_soft_iron_matrix_d(long * matrix)1610 void inv_set_compass_soft_iron_matrix_d(long *matrix) {
1611 int i;
1612 for (i=0; i<9; i++) {
1613 // set the floating point matrix
1614 sensors.soft_iron.matrix_d[i] = matrix[i];
1615 // convert to Q30 format
1616 sensors.soft_iron.matrix_f[i] = inv_q30_to_float(matrix[i]);
1617 }
1618 }
1619 /** Gets the 3x3 compass transform matrix in 32 bit floating point format.
1620 * @param[out] the pointer of the 3x3 matrix in floating point format
1621 */
inv_get_compass_soft_iron_matrix_f(float * matrix)1622 void inv_get_compass_soft_iron_matrix_f(float *matrix) {
1623 int i;
1624 for (i=0; i<9; i++) {
1625 matrix[i] = sensors.soft_iron.matrix_f[i];
1626 }
1627 }
1628 /** Sets the 3x3 compass transform matrix in 32 bit floating point format.
1629 * @param[in] the pointer of the 3x3 matrix in floating point format
1630 */
inv_set_compass_soft_iron_matrix_f(float * matrix)1631 void inv_set_compass_soft_iron_matrix_f(float *matrix) {
1632 int i;
1633 for (i=0; i<9; i++) {
1634 // set the floating point matrix
1635 sensors.soft_iron.matrix_f[i] = matrix[i];
1636 // convert to Q30 format
1637 sensors.soft_iron.matrix_d[i] = (long )(matrix[i]*ROT_MATRIX_SCALE_LONG);
1638 }
1639 }
1640
1641 /** This subroutine gets the fixed point Q30 compass data after the soft iron transformation.
1642 * @param[out] the pointer of the 3x1 vector compass data in MPL format
1643 */
inv_get_compass_soft_iron_output_data(long * data)1644 void inv_get_compass_soft_iron_output_data(long *data) {
1645 int i;
1646 for (i=0; i<3; i++) {
1647 data[i] = sensors.soft_iron.trans[i];
1648 }
1649 }
1650 /** This subroutine gets the fixed point Q30 compass data before the soft iron transformation.
1651 * @param[out] the pointer of the 3x1 vector compass data in MPL format
1652 */
inv_get_compass_soft_iron_input_data(long * data)1653 void inv_get_compass_soft_iron_input_data(long *data) {
1654 int i;
1655 for (i=0; i<3; i++) {
1656 data[i] = sensors.soft_iron.raw[i];
1657 }
1658 }
1659 /** This subroutine sets the compass raw data for the soft iron transformation.
1660 * @param[int] the pointer of the 3x1 vector compass raw data in MPL format
1661 */
inv_set_compass_soft_iron_input_data(const long * data)1662 void inv_set_compass_soft_iron_input_data(const long *data) {
1663 int i;
1664 for (i=0; i<3; i++) {
1665 sensors.soft_iron.raw[i] = data[i];
1666 }
1667 if (sensors.soft_iron.enable == 1) {
1668 mlMatrixVectorMult(sensors.soft_iron.matrix_d, data, sensors.soft_iron.trans);
1669 } else {
1670 for (i=0; i<3; i++) {
1671 sensors.soft_iron.trans[i] = data[i];
1672 }
1673 }
1674 }
1675
1676 /** This subroutine resets the the soft iron transformation to unity matrix and
1677 * disable the soft iron transformation process by default.
1678 */
inv_reset_compass_soft_iron_matrix(void)1679 void inv_reset_compass_soft_iron_matrix(void) {
1680 int i;
1681 for (i=0; i<9; i++) {
1682 sensors.soft_iron.matrix_f[i] = 0.0f;
1683 }
1684
1685 memset(&sensors.soft_iron.matrix_d,0,sizeof(sensors.soft_iron.matrix_d));
1686
1687 for (i=0; i<3; i++) {
1688 // set the floating point matrix
1689 sensors.soft_iron.matrix_f[i*4] = 1.0;
1690 // set the fixed point matrix
1691 sensors.soft_iron.matrix_d[i*4] = ROT_MATRIX_SCALE_LONG;
1692 }
1693
1694 inv_disable_compass_soft_iron_matrix();
1695 }
1696
1697 /** This subroutine enables the the soft iron transformation process.
1698 */
inv_enable_compass_soft_iron_matrix(void)1699 void inv_enable_compass_soft_iron_matrix(void) {
1700 sensors.soft_iron.enable = 1;
1701 }
1702
1703 /** This subroutine disables the the soft iron transformation process.
1704 */
inv_disable_compass_soft_iron_matrix(void)1705 void inv_disable_compass_soft_iron_matrix(void) {
1706 sensors.soft_iron.enable = 0;
1707 }
1708
1709 /**
1710 * @}
1711 */
1712