Home | History | Annotate | Download | only in mllite
      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  HAL_Outputs hal_outputs
     10  *   @brief     Motion Library - HAL Outputs
     11  *              Sets up common outputs for HAL
     12  *
     13  *   @{
     14  *       @file  hal_outputs.c
     15  *       @brief HAL Outputs.
     16  */
     17 
     18 #undef MPL_LOG_NDEBUG
     19 #define MPL_LOG_NDEBUG 1 /* Use 0 to turn on MPL_LOGV output */
     20 #undef MPL_LOG_TAG
     21 #define MPL_LOG_TAG "MLLITE"
     22 //#define MPL_LOG_9AXIS_DEBUG 1
     23 
     24 #include <string.h>
     25 
     26 #include "hal_outputs.h"
     27 #include "log.h"
     28 #include "ml_math_func.h"
     29 #include "mlmath.h"
     30 #include "start_manager.h"
     31 #include "data_builder.h"
     32 #include "results_holder.h"
     33 
     34 /* commenting this define out will bypass the low pass filter noise reduction
     35    filter for compass data.
     36    Disable this only for testing purpose (e.g. comparing the raw and calibrated
     37    compass data, since the former is unfiltered and the latter is filtered,
     38    leading to a small difference in the readings sample by sample).
     39    Android specifications require this filter to be enabled to have the Magnetic
     40    Field output's standard deviation fall below 0.5 uT.
     41    */
     42 #define CALIB_COMPASS_NOISE_REDUCTION
     43 
     44 struct hal_output_t {
     45     int accuracy_mag;    /**< Compass accuracy */
     46     //int accuracy_gyro;   /**< Gyro Accuracy */
     47     //int accuracy_accel;  /**< Accel Accuracy */
     48     int accuracy_quat;   /**< quat Accuracy */
     49 
     50     inv_time_t nav_timestamp;
     51     inv_time_t gam_timestamp;
     52     //inv_time_t accel_timestamp;
     53     inv_time_t mag_timestamp;
     54     long nav_quat[4];
     55     int gyro_status;
     56     int accel_status;
     57     int compass_status;
     58     int nine_axis_status;
     59     int quat_status;
     60     inv_biquad_filter_t lp_filter[3];
     61     float compass_float[3];
     62 };
     63 
     64 static struct hal_output_t hal_out;
     65 
     66 /** Acceleration (m/s^2) in body frame.
     67 * @param[out] values Acceleration in m/s^2 includes gravity. So while not in motion, it
     68 *             should return a vector of magnitude near 9.81 m/s^2
     69 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate, while 3 is most accurate.
     70 * @param[out] timestamp The timestamp for this sensor. Derived from the timestamp sent to
     71 *             inv_build_accel().
     72 * @return     Returns 1 if the data was updated or 0 if it was not updated.
     73 */
     74 int inv_get_sensor_type_accelerometer(float *values, int8_t *accuracy,
     75                                        inv_time_t * timestamp)
     76 {
     77     int status;
     78     /* Converts fixed point to m/s^2. Fixed point has 1g = 2^16.
     79      * So this 9.80665 / 2^16 */
     80 #define ACCEL_CONVERSION 0.000149637603759766f
     81     long accel[3];
     82     inv_get_accel_set(accel, accuracy, timestamp);
     83     values[0] = accel[0] * ACCEL_CONVERSION;
     84     values[1] = accel[1] * ACCEL_CONVERSION;
     85     values[2] = accel[2] * ACCEL_CONVERSION;
     86     if (hal_out.accel_status & INV_NEW_DATA)
     87         status = 1;
     88     else
     89         status = 0;
     90     MPL_LOGV("accel values:%f %f %f -%d -%lld", values[0], values[1],
     91                               values[2], status, *timestamp);
     92     return status;
     93 }
     94 
     95 /** Linear Acceleration (m/s^2) in Body Frame.
     96 * @param[out] values Linear Acceleration in body frame, length 3, (m/s^2). May show
     97 *             accel biases while at rest.
     98 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate, while 3 is most accurate.
     99 * @param[out] timestamp The timestamp for this sensor. Derived from the timestamp sent to
    100 *             inv_build_accel().
    101 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    102 */
    103 int inv_get_sensor_type_linear_acceleration(float *values, int8_t *accuracy,
    104         inv_time_t * timestamp)
    105 {
    106     long gravity[3], accel[3];
    107     int status;
    108 
    109     inv_get_accel_set(accel, accuracy, timestamp);
    110     inv_get_gravity(gravity);
    111     accel[0] -= gravity[0] >> 14;
    112     accel[1] -= gravity[1] >> 14;
    113     accel[2] -= gravity[2] >> 14;
    114     values[0] = accel[0] * ACCEL_CONVERSION;
    115     values[1] = accel[1] * ACCEL_CONVERSION;
    116     values[2] = accel[2] * ACCEL_CONVERSION;
    117 
    118     if (hal_out.accel_status & INV_NEW_DATA)
    119         status = 1;
    120     else
    121         status = 0;
    122     return status;
    123 }
    124 
    125 /** Gravity vector (m/s^2) in Body Frame.
    126 * @param[out] values Gravity vector in body frame, length 3, (m/s^2)
    127 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate, while 3 is most accurate.
    128 * @param[out] timestamp The timestamp for this sensor. Derived from the timestamp sent to
    129 *             inv_build_accel().
    130 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    131 */
    132 int inv_get_sensor_type_gravity(float *values, int8_t *accuracy,
    133                                  inv_time_t * timestamp)
    134 {
    135     long gravity[3];
    136     int status;
    137 
    138     *accuracy = (int8_t) hal_out.accuracy_quat;
    139     *timestamp = hal_out.nav_timestamp;
    140     inv_get_gravity(gravity);
    141     values[0] = (gravity[0] >> 14) * ACCEL_CONVERSION;
    142     values[1] = (gravity[1] >> 14) * ACCEL_CONVERSION;
    143     values[2] = (gravity[2] >> 14) * ACCEL_CONVERSION;
    144     if (hal_out.accel_status & INV_NEW_DATA)
    145         status = 1;
    146     else
    147         status = 0;
    148     return status;
    149 }
    150 
    151 /* Converts fixed point to rad/sec. Fixed point has 1 dps = 2^16.
    152  * So this is: pi / 2^16 / 180 */
    153 #define GYRO_CONVERSION 2.66316109007924e-007f
    154 
    155 /** Gyroscope calibrated data (rad/s) in body frame.
    156 * @param[out] values Rotation Rate in rad/sec.
    157 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate, while 3 is most accurate.
    158 * @param[out] timestamp The timestamp for this sensor. Derived from the timestamp sent to
    159 *             inv_build_gyro().
    160 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    161 */
    162 int inv_get_sensor_type_gyroscope(float *values, int8_t *accuracy,
    163                                    inv_time_t * timestamp)
    164 {
    165     long gyro[3];
    166     int status;
    167 
    168     inv_get_gyro_set(gyro, accuracy, timestamp);
    169 
    170     values[0] = gyro[0] * GYRO_CONVERSION;
    171     values[1] = gyro[1] * GYRO_CONVERSION;
    172     values[2] = gyro[2] * GYRO_CONVERSION;
    173     if (hal_out.gyro_status & INV_NEW_DATA)
    174         status = 1;
    175     else
    176         status = 0;
    177     return status;
    178 }
    179 
    180 /** Gyroscope raw data (rad/s) in body frame.
    181 * @param[out] values Rotation Rate in rad/sec.
    182 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate,
    183 *                      while 3 is most accurate.
    184 * @param[out] timestamp The timestamp for this sensor. Derived from the
    185 *                       timestamp sent to inv_build_gyro().
    186 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    187 */
    188 int inv_get_sensor_type_gyroscope_raw(float *values, int8_t *accuracy,
    189                                       inv_time_t * timestamp)
    190 {
    191     long gyro[3];
    192     int status;
    193 
    194     inv_get_gyro_set_raw(gyro, accuracy, timestamp);
    195     values[0] = gyro[0] * GYRO_CONVERSION;
    196     values[1] = gyro[1] * GYRO_CONVERSION;
    197     values[2] = gyro[2] * GYRO_CONVERSION;
    198     if (hal_out.gyro_status & INV_NEW_DATA)
    199         status = 1;
    200     else
    201         status = 0;
    202     return status;
    203 }
    204 
    205 /**
    206 * This corresponds to Sensor.TYPE_ROTATION_VECTOR.
    207 * The rotation vector represents the orientation of the device as a combination
    208 * of an angle and an axis, in which the device has rotated through an angle @f$\theta@f$
    209 * around an axis {x, y, z}. <br>
    210 * The three elements of the rotation vector are
    211 * {x*sin(@f$\theta@f$/2), y*sin(@f$\theta@f$/2), z*sin(@f$\theta@f$/2)}, such that the magnitude of the rotation
    212 * vector is equal to sin(@f$\theta@f$/2), and the direction of the rotation vector is
    213 * equal to the direction of the axis of rotation.
    214 *
    215 * The three elements of the rotation vector are equal to the last three components of a unit quaternion
    216 * {x*sin(@f$\theta@f$/2), y*sin(@f$\theta@f$/2), z*sin(@f$\theta@f$/2)>. The 4th element is cos(@f$\theta@f$/2).
    217 *
    218 * Elements of the rotation vector are unitless. The x,y and z axis are defined in the same way as the acceleration sensor.
    219 * The reference coordinate system is defined as a direct orthonormal basis, where:
    220 *
    221 *   -X is defined as the vector product Y.Z (It is tangential to the ground at the device's current location and roughly points East).
    222 *   -Y is tangential to the ground at the device's current location and points towards the magnetic North Pole.
    223 *   -Z points towards the sky and is perpendicular to the ground.
    224 * @param[out] values
    225 *               Length 5, 4th element being the w angle of the originating 4
    226 *               elements quaternion and 5th element being the heading accuracy
    227 *               at 95%.
    228 * @param[out] accuracy Accuracy is not defined
    229 * @param[out] timestamp Timestamp. In (ns) for Android.
    230 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    231 */
    232 int inv_get_sensor_type_rotation_vector(float *values, int8_t *accuracy,
    233         inv_time_t * timestamp)
    234 {
    235     *accuracy = (int8_t) hal_out.accuracy_quat;
    236     *timestamp = hal_out.nav_timestamp;
    237 
    238     if (hal_out.nav_quat[0] >= 0) {
    239         values[0] = hal_out.nav_quat[1] * INV_TWO_POWER_NEG_30;
    240         values[1] = hal_out.nav_quat[2] * INV_TWO_POWER_NEG_30;
    241         values[2] = hal_out.nav_quat[3] * INV_TWO_POWER_NEG_30;
    242         values[3] = hal_out.nav_quat[0] * INV_TWO_POWER_NEG_30;
    243     } else {
    244         values[0] = -hal_out.nav_quat[1] * INV_TWO_POWER_NEG_30;
    245         values[1] = -hal_out.nav_quat[2] * INV_TWO_POWER_NEG_30;
    246         values[2] = -hal_out.nav_quat[3] * INV_TWO_POWER_NEG_30;
    247         values[3] = -hal_out.nav_quat[0] * INV_TWO_POWER_NEG_30;
    248     }
    249     values[4] = inv_get_heading_confidence_interval();
    250     return hal_out.nine_axis_status;
    251 }
    252 
    253 int inv_get_sensor_type_rotation_vector_6_axis(float *values, int8_t *accuracy,
    254         inv_time_t * timestamp)
    255 {
    256     int status;
    257     long accel[3], quat_6_axis[4];
    258     inv_get_accel_set(accel, accuracy, timestamp);
    259     inv_get_6axis_quaternion(quat_6_axis, timestamp);
    260 
    261     if (quat_6_axis[0] >= 0) {
    262         values[0] = quat_6_axis[1] * INV_TWO_POWER_NEG_30;
    263         values[1] = quat_6_axis[2] * INV_TWO_POWER_NEG_30;
    264         values[2] = quat_6_axis[3] * INV_TWO_POWER_NEG_30;
    265         values[3] = quat_6_axis[0] * INV_TWO_POWER_NEG_30;
    266     } else {
    267         values[0] = -quat_6_axis[1] * INV_TWO_POWER_NEG_30;
    268         values[1] = -quat_6_axis[2] * INV_TWO_POWER_NEG_30;
    269         values[2] = -quat_6_axis[3] * INV_TWO_POWER_NEG_30;
    270         values[3] = -quat_6_axis[0] * INV_TWO_POWER_NEG_30;
    271     }
    272     //This sensor does not report an estimated heading accuracy
    273     values[4] = 0;
    274     if (hal_out.quat_status & INV_QUAT_3AXIS)
    275     {
    276         status = hal_out.quat_status & INV_NEW_DATA? 1 : 0;
    277     }
    278     else {
    279         status = hal_out.accel_status & INV_NEW_DATA? 1 : 0;
    280     }
    281     MPL_LOGV("values:%f %f %f %f %f -%d -%lld", values[0], values[1],
    282                               values[2], values[3], values[4], status, *timestamp);
    283     return status;
    284 }
    285 
    286 /**
    287 * This corresponds to Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR.
    288 * Similar to SENSOR_TYPE_ROTATION_VECTOR, but using a magnetometer
    289 * instead of using a gyroscope.
    290 * Fourth element = estimated_accuracy in radians (heading confidence).
    291 * @param[out] values Length 4.
    292 * @param[out] accuracy is not defined.
    293 * @param[out] timestamp in (ns) for Android.
    294 * @return     Returns 1 if the data was updated, 0 otherwise.
    295 */
    296 int inv_get_sensor_type_geomagnetic_rotation_vector(float *values, int8_t *accuracy,
    297         inv_time_t * timestamp)
    298 {
    299     long compass[3], quat_geomagnetic[4];
    300     int status;
    301     inv_get_compass_set(compass, accuracy, timestamp);
    302     inv_get_geomagnetic_quaternion(quat_geomagnetic, timestamp);
    303     if (quat_geomagnetic[0] >= 0) {
    304         values[0] = quat_geomagnetic[1] * INV_TWO_POWER_NEG_30;
    305         values[1] = quat_geomagnetic[2] * INV_TWO_POWER_NEG_30;
    306         values[2] = quat_geomagnetic[3] * INV_TWO_POWER_NEG_30;
    307         values[3] = quat_geomagnetic[0] * INV_TWO_POWER_NEG_30;
    308     } else {
    309         values[0] = -quat_geomagnetic[1] * INV_TWO_POWER_NEG_30;
    310         values[1] = -quat_geomagnetic[2] * INV_TWO_POWER_NEG_30;
    311         values[2] = -quat_geomagnetic[3] * INV_TWO_POWER_NEG_30;
    312         values[3] = -quat_geomagnetic[0] * INV_TWO_POWER_NEG_30;
    313     }
    314     values[4] = inv_get_accel_compass_confidence_interval();
    315     status = hal_out.accel_status & INV_NEW_DATA? 1 : 0;
    316     MPL_LOGV("values:%f %f %f %f %f -%d", values[0], values[1],
    317                               values[2], values[3], values[4], status);
    318     return (status);
    319 }
    320 
    321 /** Compass data (uT) in body frame.
    322 * @param[out] values Compass data in (uT), length 3. May be calibrated by having
    323 *             biases removed and sensitivity adjusted
    324 * @param[out] accuracy Accuracy 0 to 3, 3 = most accurate
    325 * @param[out] timestamp Timestamp. In (ns) for Android.
    326 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    327 */
    328 int inv_get_sensor_type_magnetic_field(float *values, int8_t *accuracy,
    329                                         inv_time_t * timestamp)
    330 {
    331     int status;
    332     int i;
    333     /* Converts fixed point to uT. Fixed point has 1 uT = 2^16.
    334      * So this is: 1 / 2^16*/
    335 //#define COMPASS_CONVERSION 1.52587890625e-005f
    336 
    337     *timestamp = hal_out.mag_timestamp;
    338     *accuracy = (int8_t) hal_out.accuracy_mag;
    339 
    340     for (i = 0; i < 3; i++)
    341         values[i] = hal_out.compass_float[i];
    342     if (hal_out.compass_status & INV_NEW_DATA)
    343         status = 1;
    344     else
    345         status = 0;
    346     return status;
    347 }
    348 
    349 /** Compass raw data (uT) in body frame.
    350 * @param[out] values Compass data in (uT), length 3. May be calibrated by having
    351 *             biases removed and sensitivity adjusted
    352 * @param[out] accuracy Accuracy 0 to 3, 3 = most accurate
    353 * @param[out] timestamp Timestamp. In (ns) for Android.
    354 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    355 */
    356 int inv_get_sensor_type_magnetic_field_raw(float *values, int8_t *accuracy,
    357                                            inv_time_t * timestamp)
    358 {
    359     long mag[3];
    360     int status;
    361     int i;
    362 
    363     inv_get_compass_set_raw(mag, accuracy, timestamp);
    364 
    365     /* Converts fixed point to uT. Fixed point has 1 uT = 2^16.
    366      * So this is: 1 / 2^16*/
    367 #define COMPASS_CONVERSION 1.52587890625e-005f
    368 
    369     for (i = 0; i < 3; i++) {
    370         values[i] = (float)mag[i] * COMPASS_CONVERSION;
    371     }
    372     if (hal_out.compass_status & INV_NEW_DATA)
    373         status = 1;
    374     else
    375         status = 0;
    376     return status;
    377 }
    378 static void inv_get_rotation_geomagnetic(float r[3][3])
    379 {
    380     long rot[9], quat_geo[4];
    381     float conv = 1.f / (1L<<30);
    382     inv_time_t timestamp;
    383     inv_get_geomagnetic_quaternion(quat_geo, &timestamp);
    384     inv_quaternion_to_rotation(quat_geo, rot);
    385     r[0][0] = rot[0]*conv;
    386     r[0][1] = rot[1]*conv;
    387     r[0][2] = rot[2]*conv;
    388     r[1][0] = rot[3]*conv;
    389     r[1][1] = rot[4]*conv;
    390     r[1][2] = rot[5]*conv;
    391     r[2][0] = rot[6]*conv;
    392     r[2][1] = rot[7]*conv;
    393     r[2][2] = rot[8]*conv;
    394 }
    395 static void google_orientation_geomagnetic(float *g)
    396 {
    397     float rad2deg = (float)(180.0 / M_PI);
    398     float R[3][3];
    399     inv_get_rotation_geomagnetic(R);
    400     g[0] = atan2f(-R[1][0], R[0][0]) * rad2deg;
    401     g[1] = atan2f(-R[2][1], R[2][2]) * rad2deg;
    402     g[2] = asinf ( R[2][0])          * rad2deg;
    403     if (g[0] < 0)
    404         g[0] += 360;
    405 }
    406 
    407 static void inv_get_rotation_6_axis(float r[3][3])
    408 {
    409     long rot[9], quat_6_axis[4];
    410     float conv = 1.f / (1L<<30);
    411     inv_time_t timestamp;
    412 
    413     inv_get_6axis_quaternion(quat_6_axis, &timestamp);
    414     inv_quaternion_to_rotation(quat_6_axis, rot);
    415     r[0][0] = rot[0]*conv;
    416     r[0][1] = rot[1]*conv;
    417     r[0][2] = rot[2]*conv;
    418     r[1][0] = rot[3]*conv;
    419     r[1][1] = rot[4]*conv;
    420     r[1][2] = rot[5]*conv;
    421     r[2][0] = rot[6]*conv;
    422     r[2][1] = rot[7]*conv;
    423     r[2][2] = rot[8]*conv;
    424 }
    425 
    426 static void google_orientation_6_axis(float *g)
    427 {
    428     float rad2deg = (float)(180.0 / M_PI);
    429     float R[3][3];
    430 
    431     inv_get_rotation_6_axis(R);
    432 
    433     g[0] = atan2f(-R[1][0], R[0][0]) * rad2deg;
    434     g[1] = atan2f(-R[2][1], R[2][2]) * rad2deg;
    435     g[2] = asinf ( R[2][0])          * rad2deg;
    436     if (g[0] < 0)
    437         g[0] += 360;
    438 }
    439 
    440 static void inv_get_rotation(float r[3][3])
    441 {
    442     long rot[9];
    443     float conv = 1.f / (1L<<30);
    444 
    445     inv_quaternion_to_rotation(hal_out.nav_quat, rot);
    446     r[0][0] = rot[0]*conv;
    447     r[0][1] = rot[1]*conv;
    448     r[0][2] = rot[2]*conv;
    449     r[1][0] = rot[3]*conv;
    450     r[1][1] = rot[4]*conv;
    451     r[1][2] = rot[5]*conv;
    452     r[2][0] = rot[6]*conv;
    453     r[2][1] = rot[7]*conv;
    454     r[2][2] = rot[8]*conv;
    455 }
    456 
    457 static void google_orientation(float *g)
    458 {
    459     float rad2deg = (float)(180.0 / M_PI);
    460     float R[3][3];
    461 
    462     inv_get_rotation(R);
    463 
    464     g[0] = atan2f(-R[1][0], R[0][0]) * rad2deg;
    465     g[1] = atan2f(-R[2][1], R[2][2]) * rad2deg;
    466     g[2] = asinf ( R[2][0])          * rad2deg;
    467     if (g[0] < 0)
    468         g[0] += 360;
    469 }
    470 
    471 /** This corresponds to Sensor.TYPE_ORIENTATION. All values are angles in degrees.
    472 * @param[out] values Length 3, Degrees.<br>
    473 *        - values[0]: Azimuth, angle between the magnetic north direction
    474 *         and the y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South, 270=West<br>
    475 *        - values[1]: Pitch, rotation around x-axis (-180 to 180), with positive values
    476 *         when the z-axis moves toward the y-axis.<br>
    477 *        - values[2]: Roll, rotation around y-axis (-90 to 90), with positive
    478 *          values when the x-axis moves toward the z-axis.<br>
    479 *
    480 * @note  This definition is different from yaw, pitch and roll used in aviation
    481 *        where the X axis is along the long side of the plane (tail to nose).
    482 *        Note: This sensor type exists for legacy reasons, please use getRotationMatrix()
    483 *        in conjunction with remapCoordinateSystem() and getOrientation() to compute
    484 *        these values instead.
    485 *        Important note: For historical reasons the roll angle is positive in the
    486 *        clockwise direction (mathematically speaking, it should be positive in
    487 *        the counter-clockwise direction).
    488 * @param[out] accuracy Accuracy of the measurment, 0 is least accurate, while 3 is most accurate.
    489 * @param[out] timestamp The timestamp for this sensor.
    490 * @return     Returns 1 if the data was updated or 0 if it was not updated.
    491 */
    492 int inv_get_sensor_type_orientation(float *values, int8_t *accuracy,
    493                                      inv_time_t * timestamp)
    494 {
    495     *accuracy = (int8_t) hal_out.accuracy_quat;
    496     *timestamp = hal_out.nav_timestamp;
    497 
    498     google_orientation(values);
    499 
    500     return hal_out.nine_axis_status;
    501 }
    502 
    503 int inv_get_sensor_type_orientation_6_axis(float *values, int8_t *accuracy,
    504                                      inv_time_t * timestamp)
    505 {
    506     long accel[3];
    507     inv_get_accel_set(accel, accuracy, timestamp);
    508 
    509     hal_out.gam_timestamp = hal_out.nav_timestamp;
    510     *timestamp = hal_out.gam_timestamp;
    511 
    512     google_orientation_6_axis(values);
    513 
    514     return hal_out.accel_status;
    515 }
    516 
    517 int inv_get_sensor_type_orientation_geomagnetic(float *values, int8_t *accuracy,
    518                                      inv_time_t * timestamp)
    519 {
    520     long compass[3], quat_geomagnetic[4];
    521     inv_get_compass_set(compass, accuracy, timestamp);
    522     inv_get_geomagnetic_quaternion(quat_geomagnetic, timestamp);
    523 
    524     google_orientation_geomagnetic(values);
    525     return hal_out.accel_status & hal_out.compass_status;
    526 }
    527 /** Main callback to generate HAL outputs. Typically not called by library users.
    528 * @param[in] sensor_cal Input variable to take sensor data whenever there is new
    529 * sensor data.
    530 * @return Returns INV_SUCCESS if successful or an error code if not.
    531 */
    532 inv_error_t inv_generate_hal_outputs(struct inv_sensor_cal_t *sensor_cal)
    533 {
    534     int use_sensor = 0;
    535     long sr = 1000;
    536     long compass[3];
    537     int8_t accuracy;
    538     int i;
    539     (void) sensor_cal;
    540 
    541     inv_get_quaternion_set(hal_out.nav_quat, &hal_out.accuracy_quat,
    542                            &hal_out.nav_timestamp);
    543     hal_out.gyro_status = sensor_cal->gyro.status;
    544     hal_out.accel_status = sensor_cal->accel.status;
    545     hal_out.compass_status = sensor_cal->compass.status;
    546     hal_out.quat_status = sensor_cal->quat.status;
    547 #if MPL_LOG_9AXIS_DEBUG
    548     MPL_LOGV("hal_out:g=%d", hal_out.gyro_status);
    549 #endif
    550     // Find the highest sample rate and tie generating 9-axis to that one.
    551     if (sensor_cal->gyro.status & INV_SENSOR_ON) {
    552         sr = sensor_cal->gyro.sample_rate_ms;
    553         use_sensor = 0;
    554     }
    555     if ((sensor_cal->accel.status & INV_SENSOR_ON) && (sr > sensor_cal->accel.sample_rate_ms)) {
    556         sr = sensor_cal->accel.sample_rate_ms;
    557         use_sensor = 1;
    558     }
    559     if ((sensor_cal->compass.status & INV_SENSOR_ON) && (sr > sensor_cal->compass.sample_rate_ms)) {
    560         sr = sensor_cal->compass.sample_rate_ms;
    561         use_sensor = 2;
    562     }
    563     if ((sensor_cal->quat.status & INV_SENSOR_ON) && (sr > sensor_cal->quat.sample_rate_ms)) {
    564         sr = sensor_cal->quat.sample_rate_ms;
    565         use_sensor = 3;
    566     }
    567 
    568     // Only output 9-axis if all 9 sensors are on.
    569     if (sensor_cal->quat.status & INV_SENSOR_ON) {
    570         // If quaternion sensor is on, gyros are not required as quaternion already has that part
    571         if ((sensor_cal->accel.status & sensor_cal->compass.status & INV_SENSOR_ON) == 0) {
    572             use_sensor = -1;
    573         }
    574     } else {
    575         if ((sensor_cal->gyro.status & sensor_cal->accel.status & sensor_cal->compass.status & INV_SENSOR_ON) == 0) {
    576             use_sensor = -1;
    577         }
    578     }
    579 #if MPL_LOG_9AXIS_DEBUG
    580     MPL_LOGI("use_sensor=%d", use_sensor);
    581 #endif
    582     switch (use_sensor) {
    583     case 0:
    584         hal_out.nine_axis_status = (sensor_cal->gyro.status & INV_NEW_DATA) ? 1 : 0;
    585         hal_out.nav_timestamp = sensor_cal->gyro.timestamp;
    586         break;
    587     case 1:
    588         hal_out.nine_axis_status = (sensor_cal->accel.status & INV_NEW_DATA) ? 1 : 0;
    589         hal_out.nav_timestamp = sensor_cal->accel.timestamp;
    590         break;
    591     case 2:
    592         hal_out.nine_axis_status = (sensor_cal->compass.status & INV_NEW_DATA) ? 1 : 0;
    593         hal_out.nav_timestamp = sensor_cal->compass.timestamp;
    594         break;
    595     case 3:
    596         hal_out.nine_axis_status = (sensor_cal->quat.status & INV_NEW_DATA) ? 1 : 0;
    597         hal_out.nav_timestamp = sensor_cal->quat.timestamp;
    598         break;
    599     default:
    600         hal_out.nine_axis_status = 0; // Don't output quaternion related info
    601         break;
    602     }
    603 #if MPL_LOG_9AXIS_DEBUG
    604     MPL_LOGI("nav ts: %lld", hal_out.nav_timestamp);
    605 #endif
    606     /* Converts fixed point to uT. Fixed point has 1 uT = 2^16.
    607      * So this is: 1 / 2^16*/
    608     #define COMPASS_CONVERSION 1.52587890625e-005f
    609 
    610     inv_get_compass_set(compass, &accuracy, &(hal_out.mag_timestamp) );
    611     hal_out.accuracy_mag = (int)accuracy;
    612 
    613 #ifndef CALIB_COMPASS_NOISE_REDUCTION
    614     for (i = 0; i < 3; i++) {
    615         hal_out.compass_float[i] = (float)compass[i] * COMPASS_CONVERSION;
    616     }
    617 #else
    618     for (i = 0; i < 3; i++) {
    619         if ((sensor_cal->compass.status & (INV_NEW_DATA | INV_CONTIGUOUS)) ==
    620                                                              INV_NEW_DATA)  {
    621             // set the state variables to match output with input
    622             inv_calc_state_to_match_output(&hal_out.lp_filter[i],
    623                                            (float)compass[i]);
    624         }
    625         if ((sensor_cal->compass.status & (INV_NEW_DATA | INV_RAW_DATA)) ==
    626                                           (INV_NEW_DATA | INV_RAW_DATA)) {
    627             hal_out.compass_float[i] =
    628                 inv_biquad_filter_process(&hal_out.lp_filter[i],
    629                                           (float)compass[i]) *
    630                                           COMPASS_CONVERSION;
    631         } else if ((sensor_cal->compass.status & INV_NEW_DATA) == INV_NEW_DATA) {
    632             hal_out.compass_float[i] = (float)compass[i] * COMPASS_CONVERSION;
    633         }
    634     }
    635 #endif // CALIB_COMPASS_NOISE_REDUCTION
    636     return INV_SUCCESS;
    637 }
    638 
    639 /** Turns off generation of HAL outputs.
    640 * @return Returns INV_SUCCESS if successful or an error code if not.
    641  */
    642 inv_error_t inv_stop_hal_outputs(void)
    643 {
    644     inv_error_t result;
    645     result = inv_unregister_data_cb(inv_generate_hal_outputs);
    646     return result;
    647 }
    648 
    649 /** Turns on generation of HAL outputs. This should be called after inv_stop_hal_outputs()
    650 * to turn generation of HAL outputs back on. It is automatically called by inv_enable_hal_outputs().
    651 * @return Returns INV_SUCCESS if successful or an error code if not.
    652 */
    653 inv_error_t inv_start_hal_outputs(void)
    654 {
    655     inv_error_t result;
    656     result =
    657         inv_register_data_cb(inv_generate_hal_outputs,
    658                              INV_PRIORITY_HAL_OUTPUTS,
    659                              INV_GYRO_NEW | INV_ACCEL_NEW | INV_MAG_NEW | INV_QUAT_NEW | INV_PRESSURE_NEW);
    660     return result;
    661 }
    662 
    663 /* file name: lowPassFilterCoeff_1_6.c */
    664 float compass_low_pass_filter_coeff[5] =
    665 {+2.000000000000f, +1.000000000000f, -1.279632424998f, +0.477592250073f, +0.049489956269f};
    666 
    667 /** Initializes hal outputs class. This is called automatically by the
    668 * enable function. It may be called any time the feature is enabled, but
    669 * is typically not needed to be called by outside callers.
    670 * @return Returns INV_SUCCESS if successful or an error code if not.
    671 */
    672 inv_error_t inv_init_hal_outputs(void)
    673 {
    674     int i;
    675     memset(&hal_out, 0, sizeof(hal_out));
    676     for (i=0; i<3; i++)  {
    677         inv_init_biquad_filter(&hal_out.lp_filter[i], compass_low_pass_filter_coeff);
    678     }
    679 
    680     return INV_SUCCESS;
    681 }
    682 
    683 /** Turns on creation and storage of HAL type results.
    684 * @return Returns INV_SUCCESS if successful or an error code if not.
    685 */
    686 inv_error_t inv_enable_hal_outputs(void)
    687 {
    688     inv_error_t result;
    689 
    690         // don't need to check the result for inv_init_hal_outputs
    691         // since it's always INV_SUCCESS
    692         inv_init_hal_outputs();
    693 
    694     result = inv_register_mpl_start_notification(inv_start_hal_outputs);
    695     return result;
    696 }
    697 
    698 /** Turns off creation and storage of HAL type results.
    699 */
    700 inv_error_t inv_disable_hal_outputs(void)
    701 {
    702     inv_error_t result;
    703 
    704     inv_stop_hal_outputs(); // Ignore error if we have already stopped this
    705     result = inv_unregister_mpl_start_notification(inv_start_hal_outputs);
    706     return result;
    707 }
    708 
    709 /**
    710  * @}
    711  */
    712 
    713 
    714 
    715