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  *   @defgroup  Results_Holder results_holder
9  *   @brief     Motion Library - Results Holder
10  *              Holds the data for MPL
11  *
12  *   @{
13  *       @file results_holder.c
14  *       @brief Results Holder for HAL.
15  */
16 
17 #include <string.h>
18 
19 #include "results_holder.h"
20 #include "ml_math_func.h"
21 #include "mlmath.h"
22 #include "start_manager.h"
23 #include "data_builder.h"
24 #include "message_layer.h"
25 #include "log.h"
26 
27 struct results_t {
28     float nav_quat[4];
29     float game_quat[4];
30     long gam_quat[4];
31     float geomag_quat[4];
32     long accel_quat[4];
33     inv_time_t nav_timestamp;
34     inv_time_t gam_timestamp;
35     inv_time_t geomag_timestamp;
36     long mag_scale[3]; /**< scale factor to apply to magnetic field reading */
37     long compass_correction[4]; /**< quaternion going from gyro,accel quaternion to 9 axis */
38     long geomag_compass_correction[4]; /**< quaternion going from accel quaternion to geomag sensor fusion */
39     int acc_state; /**< Describes accel state */
40     int got_accel_bias; /**< Flag describing if accel bias is known */
41     long compass_bias_error[3]; /**< Error Squared */
42     unsigned char motion_state;
43     unsigned int motion_state_counter; /**< Incremented for each no motion event in a row */
44     long compass_count; /**< compass state internal counter */
45     int got_compass_bias; /**< Flag describing if compass bias is known */
46     int large_mag_field; /**< Flag describing if there is a large magnetic field */
47     int compass_state; /**< Internal compass state */
48     long status;
49     struct inv_sensor_cal_t *sensor;
50     float quat_confidence_interval;
51     float geo_mag_confidence_interval;
52     struct local_field_t mag_local_field;
53     struct local_field_t mpl_compass_cal;
54     int quat_validity;
55 #ifdef WIN32
56     long last_quat[4];
57 #endif
58 };
59 static struct results_t rh;
60 
61 /** @internal
62 * Store a quaternion more suitable for gaming. This quaternion is often determined
63 * using only gyro and accel.
64 * @param[in] quat Length 4, Quaternion scaled by 2^30
65 * @param[in] timestamp Timestamp of the 6-axis quaternion
66 */
inv_store_gaming_quaternion(const long * quat,inv_time_t timestamp)67 void inv_store_gaming_quaternion(const long *quat, inv_time_t timestamp)
68 {
69     rh.status |= INV_6_AXIS_QUAT_SET;
70     memcpy(&rh.gam_quat, quat, sizeof(rh.gam_quat));
71     rh.gam_timestamp = timestamp;
72 }
73 
74 /** @internal
75 * Store a 9-axis quaternion.
76 * @param[in] quat Length 4 in floating-point numbers
77 * @param[in] timestamp Timestamp of the 9-axis quaternion
78 */
inv_store_nav_quaternion(const float * quat,inv_time_t timestamp)79 void inv_store_nav_quaternion(const float *quat, inv_time_t timestamp)
80 {
81     memcpy(&rh.nav_quat, quat, sizeof(rh.nav_quat));
82     rh.nav_timestamp = timestamp;
83 }
84 
85 /** @internal
86 * Store a 6-axis geomagnetic quaternion.
87 * @param[in] quat Length 4 in floating-point numbers
88 * @param[in] timestamp Timestamp of the geomag quaternion
89 */
inv_store_geomag_quaternion(const float * quat,inv_time_t timestamp)90 void inv_store_geomag_quaternion(const float *quat, inv_time_t timestamp)
91 {
92     memcpy(&rh.geomag_quat, quat, sizeof(rh.geomag_quat));
93     rh.geomag_timestamp = timestamp;
94 }
95 
96 /** @internal
97 * Store a floating-point quaternion more suitable for gaming.
98 * @param[in] quat Length 4 in floating-point numbers
99 * @param[in] timestamp Timestamp of the 6-axis quaternion
100 */
inv_store_game_quaternion(const float * quat,inv_time_t timestamp)101 void inv_store_game_quaternion(const float *quat, inv_time_t timestamp)
102 {
103     rh.status |= INV_6_AXIS_QUAT_SET;
104     memcpy(&rh.game_quat, quat, sizeof(rh.game_quat));
105     rh.gam_timestamp = timestamp;
106 }
107 
108 /** @internal
109 * Store a quaternion computed from accelerometer correction. This quaternion is
110 * determined * using only accel, and used for geomagnetic fusion.
111 * @param[in] quat Length 4, Quaternion scaled by 2^30
112 */
inv_store_accel_quaternion(const long * quat,inv_time_t timestamp)113 void inv_store_accel_quaternion(const long *quat, inv_time_t timestamp)
114 {
115    // rh.status |= INV_6_AXIS_QUAT_SET;
116     memcpy(&rh.accel_quat, quat, sizeof(rh.accel_quat));
117     rh.geomag_timestamp = timestamp;
118 }
119 /** @internal
120 * Sets the quaternion adjustment from 6 axis (accel, gyro) to 9 axis quaternion.
121 * @param[in] data Quaternion Adjustment
122 * @param[in] timestamp Timestamp of when this is valid
123 */
inv_set_compass_correction(const long * data,inv_time_t timestamp)124 void inv_set_compass_correction(const long *data, inv_time_t timestamp)
125 {
126     rh.status |= INV_COMPASS_CORRECTION_SET;
127     memcpy(rh.compass_correction, data, sizeof(rh.compass_correction));
128     rh.nav_timestamp = timestamp;
129 }
130 
131 /** @internal
132 * Sets the quaternion adjustment from 3 axis (accel) to 6 axis (with compass) quaternion.
133 * @param[in] data Quaternion Adjustment
134 * @param[in] timestamp Timestamp of when this is valid
135 */
inv_set_geomagnetic_compass_correction(const long * data,inv_time_t timestamp)136 void inv_set_geomagnetic_compass_correction(const long *data, inv_time_t timestamp)
137 {
138     rh.status |= INV_GEOMAGNETIC_CORRECTION_SET;
139     memcpy(rh.geomag_compass_correction, data, sizeof(rh.geomag_compass_correction));
140     rh.geomag_timestamp = timestamp;
141 }
142 
143 /** @internal
144 * Gets the quaternion adjustment from 6 axis (accel, gyro) to 9 axis quaternion.
145 * @param[out] data Quaternion Adjustment
146 * @param[out] timestamp Timestamp of when this is valid
147 */
inv_get_compass_correction(long * data,inv_time_t * timestamp)148 void inv_get_compass_correction(long *data, inv_time_t *timestamp)
149 {
150     memcpy(data, rh.compass_correction, sizeof(rh.compass_correction));
151     *timestamp = rh.nav_timestamp;
152 }
153 
154 /** @internal
155 * Gets the quaternion adjustment from 3 axis (accel) to 6 axis (with compass) quaternion.
156 * @param[out] data Quaternion Adjustment
157 * @param[out] timestamp Timestamp of when this is valid
158 */
inv_get_geomagnetic_compass_correction(long * data,inv_time_t * timestamp)159 void inv_get_geomagnetic_compass_correction(long *data, inv_time_t *timestamp)
160 {
161     memcpy(data, rh.geomag_compass_correction, sizeof(rh.geomag_compass_correction));
162     *timestamp = rh.geomag_timestamp;
163 }
164 
165 /** Returns non-zero if there is a large magnetic field. See inv_set_large_mag_field() for setting this variable.
166  * @return Returns non-zero if there is a large magnetic field.
167  */
inv_get_large_mag_field()168 int inv_get_large_mag_field()
169 {
170     return rh.large_mag_field;
171 }
172 
173 /** Set to non-zero if there as a large magnetic field. See inv_get_large_mag_field() for getting this variable.
174  * @param[in] state value to set for magnetic field strength. Should be non-zero if it is large.
175  */
inv_set_large_mag_field(int state)176 void inv_set_large_mag_field(int state)
177 {
178     rh.large_mag_field = state;
179 }
180 
181 /** Gets the accel state set by inv_set_acc_state()
182  * @return accel state.
183  */
inv_get_acc_state()184 int inv_get_acc_state()
185 {
186     return rh.acc_state;
187 }
188 
189 /** Sets the accel state. See inv_get_acc_state() to get the value.
190  * @param[in] state value to set accel state to.
191  */
inv_set_acc_state(int state)192 void inv_set_acc_state(int state)
193 {
194     rh.acc_state = state;
195     return;
196 }
197 
198 /** Returns the motion state
199 * @param[out] cntr Number of previous times a no motion event has occured in a row.
200 * @return Returns INV_SUCCESS if successful or an error code if not.
201 */
inv_get_motion_state(unsigned int * cntr)202 int inv_get_motion_state(unsigned int *cntr)
203 {
204     *cntr = rh.motion_state_counter;
205     return rh.motion_state;
206 }
207 
208 /** Sets the motion state
209  * @param[in] state motion state where INV_NO_MOTION is not moving
210  *            and INV_MOTION is moving.
211  */
inv_set_motion_state(unsigned char state)212 void inv_set_motion_state(unsigned char state)
213 {
214     long set;
215     if (state == rh.motion_state) {
216         if (state == INV_NO_MOTION) {
217             rh.motion_state_counter++;
218         } else {
219             rh.motion_state_counter = 0;
220         }
221         return;
222     }
223     rh.motion_state_counter = 0;
224     rh.motion_state = state;
225     /* Equivalent to set = state, but #define's may change. */
226     if (state == INV_MOTION)
227         set = INV_MSG_MOTION_EVENT;
228     else
229         set = INV_MSG_NO_MOTION_EVENT;
230     inv_set_message(set, (INV_MSG_MOTION_EVENT | INV_MSG_NO_MOTION_EVENT), 0);
231 }
232 
233 /** Sets the compass sensitivity
234  * @param[in] data Length 3, sensitivity for each compass axis
235  *  scaled such that 1.0 = 2^30.
236  */
inv_set_mag_scale(const long * data)237 void inv_set_mag_scale(const long *data)
238 {
239     memcpy(rh.mag_scale, data, sizeof(rh.mag_scale));
240 }
241 
242 /** Gets the compass sensitivity
243  * @param[out] data Length 3, sensitivity for each compass axis
244  *  scaled such that 1.0 = 2^30.
245  */
inv_get_mag_scale(long * data)246 void inv_get_mag_scale(long *data)
247 {
248     memcpy(data, rh.mag_scale, sizeof(rh.mag_scale));
249 }
250 
251 /** Gets gravity vector
252  * @param[out] data gravity vector in body frame scaled such that 1.0 = 2^30.
253  * @return Returns INV_SUCCESS if successful or an error code if not.
254  */
inv_get_gravity(long * data)255 inv_error_t inv_get_gravity(long *data)
256 {
257     long ldata[4];
258     inv_error_t result = inv_get_quaternion(ldata);
259 
260     data[0] =
261         inv_q29_mult(ldata[1], ldata[3]) - inv_q29_mult(ldata[2], ldata[0]);
262     data[1] =
263         inv_q29_mult(ldata[2], ldata[3]) + inv_q29_mult(ldata[1], ldata[0]);
264     data[2] =
265         (inv_q29_mult(ldata[3], ldata[3]) + inv_q29_mult(ldata[0], ldata[0])) -
266         1073741824L;
267 
268     return result;
269 }
270 
271 /** Returns a quaternion based only on accel.
272  * @param[out] data 3-axis  accel quaternion scaled such that 1.0 = 2^30.
273  * @return Returns INV_SUCCESS if successful or an error code if not.
274  */
inv_get_accel_quaternion(long * data)275 inv_error_t inv_get_accel_quaternion(long *data)
276 {
277     memcpy(data, rh.accel_quat, sizeof(rh.accel_quat));
278     return INV_SUCCESS;
279 }
inv_get_gravity_6x(long * data)280 inv_error_t inv_get_gravity_6x(long *data)
281 {
282     data[0] =
283         inv_q29_mult(rh.gam_quat[1], rh.gam_quat[3]) - inv_q29_mult(rh.gam_quat[2], rh.gam_quat[0]);
284     data[1] =
285         inv_q29_mult(rh.gam_quat[2], rh.gam_quat[3]) + inv_q29_mult(rh.gam_quat[1], rh.gam_quat[0]);
286     data[2] =
287         (inv_q29_mult(rh.gam_quat[3], rh.gam_quat[3]) + inv_q29_mult(rh.gam_quat[0], rh.gam_quat[0])) -
288         1073741824L;
289     return INV_SUCCESS;
290 }
291 /** Returns a quaternion based only on gyro and accel.
292  * @param[out] data 6-axis  gyro and accel quaternion scaled such that 1.0 = 2^30.
293  * @return Returns INV_SUCCESS if successful or an error code if not.
294  */
inv_get_6axis_quaternion(long * data,inv_time_t * timestamp)295 inv_error_t inv_get_6axis_quaternion(long *data, inv_time_t *timestamp)
296 {
297     data[0] = (long)MIN(MAX(rh.game_quat[0] * ((float)(1L << 30)), -2147483648.), 2147483647.);
298     data[1] = (long)MIN(MAX(rh.game_quat[1] * ((float)(1L << 30)), -2147483648.), 2147483647.);
299     data[2] = (long)MIN(MAX(rh.game_quat[2] * ((float)(1L << 30)), -2147483648.), 2147483647.);
300     data[3] = (long)MIN(MAX(rh.game_quat[3] * ((float)(1L << 30)), -2147483648.), 2147483647.);
301     *timestamp = rh.gam_timestamp;
302     return INV_SUCCESS;
303 }
304 
305 /** Returns a quaternion.
306  * @param[out] data 9-axis quaternion scaled such that 1.0 = 2^30.
307  * @return Returns INV_SUCCESS if successful or an error code if not.
308  */
inv_get_quaternion(long * data)309 inv_error_t inv_get_quaternion(long *data)
310 {
311     data[0] = (long)MIN(MAX(rh.nav_quat[0] * ((float)(1L << 30)), -2147483648.), 2147483647.);
312     data[1] = (long)MIN(MAX(rh.nav_quat[1] * ((float)(1L << 30)), -2147483648.), 2147483647.);
313     data[2] = (long)MIN(MAX(rh.nav_quat[2] * ((float)(1L << 30)), -2147483648.), 2147483647.);
314     data[3] = (long)MIN(MAX(rh.nav_quat[3] * ((float)(1L << 30)), -2147483648.), 2147483647.);
315 
316     return INV_SUCCESS;
317 }
318 
319 #ifdef WIN32
320 /** Returns the last 9-axis quaternion genearted by MPL, so that
321  * it can be written to the DMP.
322  * @param[out] data 9-axis quaternion scaled such that 1.0 = 2^30.
323  * @return Returns INV_SUCCESS if successful or an error code if not.
324  */
inv_get_last_quaternion(long * data)325 inv_error_t inv_get_last_quaternion(long *data)
326 {
327     memcpy(data, rh.last_quat, sizeof(rh.last_quat));
328     return INV_SUCCESS;
329 }
330 
331 /** Saves the last 9-axis quaternion generated by MPL.
332  * @param[in] data 9-axis quaternion data.
333  */
inv_set_last_quaternion(long * data)334 inv_error_t inv_set_last_quaternion(long *data)
335 {
336     memcpy(rh.last_quat, data, sizeof(rh.last_quat));
337     return INV_SUCCESS;
338 }
339 #endif
340 
341 /** Returns the status of the result holder.
342  * @param[out] rh_status Result holder status.
343  * @return Returns INV_SUCCESS if successful or an error code if not.
344  */
inv_get_result_holder_status(long * rh_status)345 inv_error_t inv_get_result_holder_status(long *rh_status)
346 {
347     *rh_status = rh.status;
348     return INV_SUCCESS;
349 }
350 
351 /** Set the status of the result holder.
352  * @return Returns INV_SUCCESS if successful or an error code if not.
353  */
inv_set_result_holder_status(long rh_status)354 inv_error_t inv_set_result_holder_status(long rh_status)
355 {
356     rh.status = rh_status;
357     return INV_SUCCESS;
358 }
359 
360 /** Returns the status of the authenticity of the quaternion data.
361  * @param[out] value Authenticity of the quaternion data.
362  * @return Returns INV_SUCCESS if successful or an error code if not.
363  */
inv_get_quaternion_validity(int * value)364 inv_error_t inv_get_quaternion_validity(int *value)
365 {
366     *value = rh.quat_validity;
367     return INV_SUCCESS;
368 }
369 
370 /** Set the status of the authenticity of the quaternion data.
371  * @return Returns INV_SUCCESS if successful or an error code if not.
372  */
inv_set_quaternion_validity(int value)373 inv_error_t inv_set_quaternion_validity(int value)
374 {
375     rh.quat_validity = value;
376     return INV_SUCCESS;
377 }
378 
379 /** Returns a quaternion based only on compass and accel.
380  * @param[out] data 6-axis  compass and accel quaternion scaled such that 1.0 = 2^30.
381  * @return Returns INV_SUCCESS if successful or an error code if not.
382  */
inv_get_geomagnetic_quaternion(long * data,inv_time_t * timestamp)383 inv_error_t inv_get_geomagnetic_quaternion(long *data, inv_time_t *timestamp)
384 {
385     data[0] = (long)MIN(MAX(rh.geomag_quat[0] * ((float)(1L << 30)), -2147483648.), 2147483647.);
386     data[1] = (long)MIN(MAX(rh.geomag_quat[1] * ((float)(1L << 30)), -2147483648.), 2147483647.);
387     data[2] = (long)MIN(MAX(rh.geomag_quat[2] * ((float)(1L << 30)), -2147483648.), 2147483647.);
388     data[3] = (long)MIN(MAX(rh.geomag_quat[3] * ((float)(1L << 30)), -2147483648.), 2147483647.);
389     *timestamp = rh.geomag_timestamp;
390     return INV_SUCCESS;
391 }
392 
393 /** Returns a quaternion.
394  * @param[out] data 9-axis quaternion.
395  * @return Returns INV_SUCCESS if successful or an error code if not.
396  */
inv_get_quaternion_float(float * data)397 inv_error_t inv_get_quaternion_float(float *data)
398 {
399     memcpy(data, rh.nav_quat, sizeof(rh.nav_quat));
400     return INV_SUCCESS;
401 }
402 
403 /** Returns a quaternion based only on gyro and accel.
404  * @param[out] data 6-axis  gyro and accel quaternion.
405  * @return Returns INV_SUCCESS if successful or an error code if not.
406  */
inv_get_6axis_quaternion_float(float * data,inv_time_t * timestamp)407 inv_error_t inv_get_6axis_quaternion_float(float *data, inv_time_t *timestamp)
408 {
409     memcpy(data, rh.game_quat, sizeof(rh.game_quat));
410     *timestamp = rh.gam_timestamp;
411     return INV_SUCCESS;
412 }
413 
414 /** Returns a quaternion based only on compass and accel.
415  * @param[out] data 6-axis  compass and accel quaternion.
416  * @return Returns INV_SUCCESS if successful or an error code if not.
417  */
inv_get_geomagnetic_quaternion_float(float * data,inv_time_t * timestamp)418 inv_error_t inv_get_geomagnetic_quaternion_float(float *data, inv_time_t *timestamp)
419 {
420     memcpy(data, rh.geomag_quat, sizeof(rh.geomag_quat));
421     *timestamp = rh.geomag_timestamp;
422     return INV_SUCCESS;
423 }
424 
425 /** Returns a quaternion with accuracy and timestamp.
426  * @param[out] data 9-axis quaternion scaled such that 1.0 = 2^30.
427  * @param[out] accuracy Accuracy of quaternion, 0-3, where 3 is most accurate.
428  * @param[out] timestamp Timestamp of this quaternion in nanoseconds
429  */
inv_get_quaternion_set(long * data,int * accuracy,inv_time_t * timestamp)430 void inv_get_quaternion_set(long *data, int *accuracy, inv_time_t *timestamp)
431 {
432     inv_get_quaternion(data);
433     *timestamp = inv_get_last_timestamp();
434     if (inv_get_compass_on()) {
435         *accuracy = inv_get_mag_accuracy();
436     } else if (inv_get_gyro_on()) {
437         *accuracy = inv_get_gyro_accuracy();
438     }else if (inv_get_accel_on()) {
439         *accuracy = inv_get_accel_accuracy();
440     } else {
441         *accuracy = 0;
442     }
443 }
444 
445 /** Callback that gets called everytime there is new data. It is
446  * registered by inv_start_results_holder().
447  * @param[in] sensor_cal New sensor data to process.
448  * @return Returns INV_SUCCESS if successful or an error code if not.
449  */
inv_generate_results(struct inv_sensor_cal_t * sensor_cal)450 inv_error_t inv_generate_results(struct inv_sensor_cal_t *sensor_cal)
451 {
452     rh.sensor = sensor_cal;
453     return INV_SUCCESS;
454 }
455 
456 /** Function to turn on this module. This is automatically called by
457  *  inv_enable_results_holder(). Typically not called by users.
458  * @return Returns INV_SUCCESS if successful or an error code if not.
459  */
inv_start_results_holder(void)460 inv_error_t inv_start_results_holder(void)
461 {
462     inv_register_data_cb(inv_generate_results, INV_PRIORITY_RESULTS_HOLDER,
463         INV_GYRO_NEW | INV_ACCEL_NEW | INV_MAG_NEW);
464     return INV_SUCCESS;
465 }
466 
467 /** Initializes results holder. This is called automatically by the
468 * enable function inv_enable_results_holder(). It may be called any time the feature is enabled, but
469 * is typically not needed to be called by outside callers.
470 * @return Returns INV_SUCCESS if successful or an error code if not.
471 */
inv_init_results_holder(void)472 inv_error_t inv_init_results_holder(void)
473 {
474     memset(&rh, 0, sizeof(rh));
475     rh.mag_scale[0] = 1L<<30;
476     rh.mag_scale[1] = 1L<<30;
477     rh.mag_scale[2] = 1L<<30;
478     rh.compass_correction[0] = 1L<<30;
479     rh.gam_quat[0] = 1L<<30;
480     rh.nav_quat[0] = 1.;
481     rh.geomag_quat[0] = 1.;
482     rh.game_quat[0] = 1.;
483     rh.accel_quat[0] = 1L<<30;
484     rh.geomag_compass_correction[0] = 1L<<30;
485     rh.quat_confidence_interval = (float)M_PI;
486 #ifdef WIN32
487     rh.last_quat[0] = 1L<<30;
488 #endif
489 
490 
491 #ifdef TEST
492     {
493         struct local_field_t local_field;
494         local_field.intensity = 48.0f;   // uT
495         local_field.inclination = 60.0f; // degree
496         local_field.declination = 0.0f;  // degree
497         inv_set_earth_magnetic_local_field_parameter(&local_field);
498     //    inv_set_local_field_status(LOCAL_FILED_NOT_SET_BY_USER);
499         inv_set_local_field_status(LOCAL_FILED_SET_BY_USER);
500    	    inv_set_local_magnetic_field(48.0f, 59.0f, 0.0f);
501 
502         // set default mpl calibration result for testing
503         local_field.intensity = 50.0f;   // uT
504         local_field.inclination = 59.0f; // degree
505         local_field.declination = 0.0f;  // degree
506         inv_set_mpl_magnetic_local_field_parameter(&local_field);
507         inv_set_mpl_mag_field_status(LOCAL_FIELD_SET_BUT_NOT_MATCH_WITH_MPL);
508     }
509 #endif
510 
511     return INV_SUCCESS;
512 }
513 
514 /** Turns on storage of results.
515 */
inv_enable_results_holder()516 inv_error_t inv_enable_results_holder()
517 {
518     inv_error_t result;
519     result = inv_init_results_holder();
520     if ( result ) {
521         return result;
522     }
523 
524     result = inv_register_mpl_start_notification(inv_start_results_holder);
525     return result;
526 }
527 
528 /** Sets state of if we know the accel bias.
529  * @return return 1 if we know the accel bias, 0 if not.
530  *            it is set with inv_set_accel_bias_found()
531  */
inv_got_accel_bias()532 int inv_got_accel_bias()
533 {
534     return rh.got_accel_bias;
535 }
536 
537 /** Sets whether we know the accel bias
538  * @param[in] state Set to 1 if we know the accel bias.
539  *            Can be retrieved with inv_got_accel_bias()
540  */
inv_set_accel_bias_found(int state)541 void inv_set_accel_bias_found(int state)
542 {
543     rh.got_accel_bias = state;
544 }
545 
546 /** Sets state of if we know the compass bias.
547  * @return return 1 if we know the compass bias, 0 if not.
548  *            it is set with inv_set_compass_bias_found()
549  */
inv_got_compass_bias()550 int inv_got_compass_bias()
551 {
552     return rh.got_compass_bias;
553 }
554 
555 /** Sets whether we know the compass bias
556  * @param[in] state Set to 1 if we know the compass bias.
557  *            Can be retrieved with inv_got_compass_bias()
558  */
inv_set_compass_bias_found(int state)559 void inv_set_compass_bias_found(int state)
560 {
561     rh.got_compass_bias = state;
562 }
563 
564 /** Sets the compass state.
565  * @param[in] state Compass state. It can be retrieved with inv_get_compass_state().
566  */
inv_set_compass_state(int state)567 void inv_set_compass_state(int state)
568 {
569     rh.compass_state = state;
570 }
571 
572 /** Get's the compass state
573  * @return the compass state that was set with inv_set_compass_state()
574  */
inv_get_compass_state()575 int inv_get_compass_state()
576 {
577     return rh.compass_state;
578 }
579 
580 /** Set compass bias error. See inv_get_compass_bias_error()
581  * @param[in] bias_error Set's how accurate we know the compass bias. It is the
582  * error squared.
583  */
inv_set_compass_bias_error(const long * bias_error)584 void inv_set_compass_bias_error(const long *bias_error)
585 {
586     memcpy(rh.compass_bias_error, bias_error, sizeof(rh.compass_bias_error));
587 }
588 
589 /** Get's compass bias error. See inv_set_compass_bias_error() for setting.
590  * @param[out] bias_error Accuracy as to how well the compass bias is known. It is the error squared.
591  */
inv_get_compass_bias_error(long * bias_error)592 void inv_get_compass_bias_error(long *bias_error)
593 {
594     memcpy(bias_error, rh.compass_bias_error, sizeof(rh.compass_bias_error));
595 }
596 
597 /**
598  *  @brief      Returns 3-element vector of accelerometer data in body frame
599  *                with gravity removed
600  *  @param[out] data    3-element vector of accelerometer data in body frame
601  *                with gravity removed
602  *  @return     INV_SUCCESS if successful
603  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
604  */
inv_get_linear_accel(long * data)605 inv_error_t inv_get_linear_accel(long *data)
606 {
607     long gravity[3];
608 
609     if (data != NULL)
610     {
611         inv_get_accel_set(data, NULL, NULL);
612         inv_get_gravity(gravity);
613         data[0] -= gravity[0] >> 14;
614         data[1] -= gravity[1] >> 14;
615         data[2] -= gravity[2] >> 14;
616         return INV_SUCCESS;
617     }
618     else {
619         return INV_ERROR_INVALID_PARAMETER;
620     }
621 }
622 
623 /**
624  *  @brief      Returns 3-element vector of accelerometer data in body frame
625  *  @param[out] data    3-element vector of accelerometer data in body frame
626  *  @return     INV_SUCCESS if successful
627  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
628  */
inv_get_accel(long * data)629 inv_error_t inv_get_accel(long *data)
630 {
631     if (data != NULL) {
632         inv_get_accel_set(data, NULL, NULL);
633         return INV_SUCCESS;
634     }
635     else {
636         return INV_ERROR_INVALID_PARAMETER;
637     }
638 }
639 
640 /**
641  *  @brief      Returns 3-element vector of accelerometer float data
642  *  @param[out] data    3-element vector of accelerometer float data
643  *  @return     INV_SUCCESS if successful
644  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
645  */
inv_get_accel_float(float * data)646 inv_error_t inv_get_accel_float(float *data)
647 {
648     long tdata[3];
649     unsigned char i;
650 
651     if (data != NULL && !inv_get_accel(tdata)) {
652         for (i = 0; i < 3; ++i) {
653             data[i] = ((float)tdata[i] / (1L << 16));
654         }
655         return INV_SUCCESS;
656     }
657     else {
658         return INV_ERROR_INVALID_PARAMETER;
659     }
660 }
661 
662 /**
663  *  @brief      Returns 3-element vector of gyro float data
664  *  @param[out] data    3-element vector of gyro float data
665  *  @return     INV_SUCCESS if successful
666  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
667  */
inv_get_gyro_float(float * data)668 inv_error_t inv_get_gyro_float(float *data)
669 {
670     long tdata[3];
671     unsigned char i;
672 
673     if (data != NULL) {
674         inv_get_gyro_set(tdata, NULL, NULL);
675         for (i = 0; i < 3; ++i) {
676             data[i] = ((float)tdata[i] / (1L << 16));
677         }
678         return INV_SUCCESS;
679     }
680     else {
681         return INV_ERROR_INVALID_PARAMETER;
682     }
683 }
684 
685 /** Set 9 axis 95% heading confidence interval for quaternion
686 * @param[in] ci Confidence interval in radians.
687 */
inv_set_heading_confidence_interval(float ci)688 void inv_set_heading_confidence_interval(float ci)
689 {
690     rh.quat_confidence_interval = ci;
691 }
692 
693 /** Get 9 axis 95% heading confidence interval for quaternion
694 * @return Confidence interval in radians.
695 */
inv_get_heading_confidence_interval(void)696 float inv_get_heading_confidence_interval(void)
697 {
698     return rh.quat_confidence_interval;
699 }
700 
701 /** Set 6 axis (accel and compass) 95% heading confidence interval for quaternion
702 * @param[in] ci Confidence interval in radians.
703 */
inv_set_accel_compass_confidence_interval(float ci)704 void inv_set_accel_compass_confidence_interval(float ci)
705 {
706     rh.geo_mag_confidence_interval = ci;
707 }
708 
709 /** Get 6 axis (accel and compass) 95% heading confidence interval for quaternion
710 * @return Confidence interval in radians.
711 */
inv_get_accel_compass_confidence_interval(void)712 float inv_get_accel_compass_confidence_interval(void)
713 {
714     return rh.geo_mag_confidence_interval;
715 }
716 
717 /**
718  *  @brief      Returns 3-element vector of linear accel float data
719  *  @param[out] data    3-element vector of linear aceel float data
720  *  @return     INV_SUCCESS if successful
721  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
722  */
inv_get_linear_accel_float(float * data)723 inv_error_t inv_get_linear_accel_float(float *data)
724 {
725     long tdata[3];
726     unsigned char i;
727 
728     if (data != NULL && !inv_get_linear_accel(tdata)) {
729         for (i = 0; i < 3; ++i) {
730             data[i] = ((float)tdata[i] / (1L << 16));
731         }
732         return INV_SUCCESS;
733     }
734     else {
735         return INV_ERROR_INVALID_PARAMETER;
736     }
737 }
738 
739 /**
740  *  @brief      Returns the status of earth magnetic field local field parameters
741  *  @param[out] N/A
742  *  @return     status of local field, defined in enum compass_local_field_e
743  */
inv_get_local_field_status(void)744 enum compass_local_field_e inv_get_local_field_status(void)
745 {
746     return rh.mag_local_field.mpl_match_status;
747 }
748 
749 /** Set the status of earth magnetic field local field parameters
750 * @param[in] status of earth magnetic field local field parameters.
751 */
inv_set_local_field_status(enum compass_local_field_e status)752 void inv_set_local_field_status(enum compass_local_field_e status)
753 {
754     rh.mag_local_field.mpl_match_status = status;
755 }
756 
757 /** Set the parameters of earth magnetic field local field
758 * @param[in] the earth magnetic field local field parameters.
759 */
inv_set_earth_magnetic_local_field_parameter(struct local_field_t * parameters)760 void inv_set_earth_magnetic_local_field_parameter(struct local_field_t *parameters)
761 {
762     rh.mag_local_field.intensity = parameters->intensity;        // radius
763     rh.mag_local_field.inclination = parameters->inclination;    // dip angle
764     rh.mag_local_field.declination = parameters->declination;    // yaw deviation angle from true north
765 
766     inv_set_local_field_status(LOCAL_FILED_SET_BY_USER);
767 }
768 
769 /**
770  *  @brief      Returns the parameters of earth magnetic field local field
771  *  @param[out] the parameters of earth magnetic field local field
772  *  @return     N/A
773  */
inv_get_earth_magnetic_local_field_parameter(struct local_field_t * parameters)774 void inv_get_earth_magnetic_local_field_parameter(struct local_field_t *parameters)
775 {
776     parameters->intensity = rh.mag_local_field.intensity;        // radius
777     parameters->inclination = rh.mag_local_field.inclination;    // dip angle
778     parameters->declination = rh.mag_local_field.declination;    // yaw deviation angle from true north
779     parameters->mpl_match_status = rh.mag_local_field.mpl_match_status;
780 }
781 
782 /**
783  *  @brief      Returns the status of mpl calibrated magnetic field local field parameters
784  *  @param[out] N/A
785  *  @return     status of local field, defined in enum compass_local_field_e
786  */
inv_get_mpl_mag_field_status(void)787 enum compass_local_field_e inv_get_mpl_mag_field_status(void)
788 {
789     return rh.mpl_compass_cal.mpl_match_status;
790 }
791 
792 /** Set the status of mpl calibrated magnetic field local field parameters
793 * @param[in] status of earth magnetic field local field parameters.
794 */
inv_set_mpl_mag_field_status(enum compass_local_field_e status)795 void inv_set_mpl_mag_field_status(enum compass_local_field_e status)
796 {
797     rh.mpl_compass_cal.mpl_match_status = status;
798 }
799 
800 /** Set the magnetic field local field struct object
801 * @param[in] status of earth magnetic field local field parameters.
802 */
inv_set_local_magnetic_field(float intensity,float inclination,float declination)803 inv_error_t inv_set_local_magnetic_field(float intensity, float inclination, float declination)
804 {
805 	struct local_field_t local_field;
806 	local_field.intensity = intensity;  // radius
807     local_field.inclination = inclination; // dip angle angle degree
808     local_field.declination = declination; // yaw deviation angle from true north, eastward as positive
809     local_field.mpl_match_status = LOCAL_FILED_SET_BY_USER;
810 
811 	inv_set_earth_magnetic_local_field_parameter(&local_field);
812 	return 0;
813 }
814 
815 /** Set the parameters of mpl calibrated magnetic field local field
816 *   This API is used by mpl only.
817 * @param[in] the earth magnetic field local field parameters.
818  *  @return     INV_SUCCESS if successful
819  *              INV_ERROR_INVALID_PARAMETER if invalid input pointer
820  */
inv_set_mpl_magnetic_local_field_parameter(struct local_field_t * parameters)821 inv_error_t inv_set_mpl_magnetic_local_field_parameter(struct local_field_t *parameters)
822 {
823     enum compass_local_field_e mpl_status;
824     struct local_field_t local_field;
825     inv_error_t status;
826 
827     rh.mpl_compass_cal.intensity = parameters->intensity;        // radius
828     rh.mpl_compass_cal.inclination = parameters->inclination;    // dip angle
829     rh.mpl_compass_cal.declination = parameters->declination;    // yaw deviation angle from true north
830 
831     mpl_status = inv_get_mpl_mag_field_status();
832     inv_get_earth_magnetic_local_field_parameter(&local_field);
833 
834     status = INV_SUCCESS;
835 
836     switch (inv_get_local_field_status())  {
837     case LOCAL_FILED_NOT_SET_BY_USER:
838         if (mpl_status == LOCAL_FILED_NOT_SET_BY_USER)  {
839             inv_set_mpl_mag_field_status(LOCAL_FILED_NOT_SET_BY_USER_BUT_SET_BY_MPL);
840         } else {
841             // illegal status
842             status = INV_ERROR_INVALID_PARAMETER;
843         }
844         break;
845     case LOCAL_FILED_SET_BY_USER:
846         switch (mpl_status) {
847             case LOCAL_FIELD_SET_BUT_NOT_MATCH_WITH_MPL:
848             case LOCAL_FIELD_SET_MATCH_WITH_MPL:
849                 if  ( (ABS(local_field.intensity - parameters->intensity) < 5.0f) &&
850                       (ABS(local_field.intensity - parameters->intensity) < 5.0f)  )  {
851                     inv_set_mpl_mag_field_status(LOCAL_FIELD_SET_MATCH_WITH_MPL);
852                 } else {
853                     inv_set_mpl_mag_field_status(LOCAL_FIELD_SET_BUT_NOT_MATCH_WITH_MPL);
854                 }
855             break;
856             case LOCAL_FILED_NOT_SET_BY_USER_BUT_SET_BY_MPL:
857             // no status update
858             break;
859             case LOCAL_FILED_NOT_SET_BY_USER:
860             case LOCAL_FILED_SET_BY_USER:
861             default:
862             // illegal status
863             status = INV_ERROR_INVALID_PARAMETER;
864             break;
865         }
866         break;
867     case LOCAL_FILED_NOT_SET_BY_USER_BUT_SET_BY_MPL:
868     case LOCAL_FIELD_SET_BUT_NOT_MATCH_WITH_MPL:
869     case LOCAL_FIELD_SET_MATCH_WITH_MPL:
870     default:
871         // illegal status
872         status = INV_ERROR_INVALID_PARAMETER;
873         break;
874     }
875     return status;
876 }
877 
878 /**
879  *  @brief      Returns the parameters of mpl calibrated magnetic field local field
880  *  @param[out] the parameters of earth magnetic field local field
881  *  @return     N/A
882  */
inv_get_mpl_magnetic_local_field_parameter(struct local_field_t * parameters)883 void inv_get_mpl_magnetic_local_field_parameter(struct local_field_t *parameters)
884 {
885     parameters->intensity = rh.mpl_compass_cal.intensity;        // radius
886     parameters->inclination = rh.mpl_compass_cal.inclination;    // dip angle
887     parameters->declination = rh.mpl_compass_cal.declination;    // yaw deviation angle from true north
888     parameters->mpl_match_status = rh.mpl_compass_cal.mpl_match_status;
889 }
890 
891 /**
892  * @}
893  */
894