C++程序  |  821行  |  28.8 KB

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <algos/gyro_cal.h>

/////// DEFINITIONS AND MACROS ///////////////////////////////////////

// Maximum gyro bias correction (should be set based on
// expected max bias of the given sensor).
#define MAX_GYRO_BIAS 0.096f // [rad/sec]

// Helper constants for converting units.
#define RAD_TO_MDEG (float)(1e3*180.0/M_PI)
#define GRAVITY_TO_G (float)(1.0/9.80665)

/////// FORWARD DECLARATIONS /////////////////////////////////////////

static void deviceStillnessCheck(struct gyroCal_t* gyro_cal,
                                 uint64_t sample_time);

static void computeGyroCal(struct gyroCal_t* gyro_cal,
                           uint64_t calibration_time);

static void checkWatchdog(struct gyroCal_t* gyro_cal,
                          uint64_t sample_time);

#ifdef GYRO_CAL_DBG_ENABLED
static void gyroCalUpdateDebug(struct gyroCal_t* gyro_cal,
                               struct debugGyroCal_t* debug_gyro_cal);

static void gyroCalTuneDebugPrint(struct gyroCal_t* gyro_cal,
                                  uint64_t sample_time);

static void gyroCalDebugPrintData(int count,
                                  struct debugGyroCal_t *debug_data);

// Information print macro.
#define GYRO_INFO_PRINT(fmt, ...) do { \
        osLog(LOG_INFO, "%s " fmt, "[GYRO CAL]", ##__VA_ARGS__); \
    } while (0);

// Macro used to print out floating point numbers.
#define GYRO_ENCODE_FLOAT(x, num_digits) ((x < 0) ? "-" : ""), \
    (int)floorf(fabsf(x)),                                \
    (int)((fabsf(x) - floorf(fabsf(x))) * powf(10,num_digits))
#endif

/////// FUNCTION DEFINITIONS /////////////////////////////////////////

// Initialize the gyro calibration data structure.
void gyroCalInit(struct gyroCal_t* gyro_cal, uint64_t min_still_duration,
                 uint64_t max_still_duration,
                 float bias_x, float bias_y, float bias_z,
                 uint64_t calibration_time,
                 uint64_t window_time_duration,
                 float gyro_var_threshold,
                 float gyro_confidence_delta,
                 float accel_var_threshold,
                 float accel_confidence_delta,
                 float mag_var_threshold,
                 float mag_confidence_delta,
                 float stillness_threshold,
                 int remove_bias_enable) {

  // Clear gyro_cal structure memory.
  memset(gyro_cal, 0, sizeof(struct gyroCal_t));

  // Initialize the stillness detectors.
  // Gyro parameter input units are [rad/sec]
  // Accel parameter input units are [m/sec^2]
  // Magnetometer parameter input units are [uT]
  gyroStillDetInit(&gyro_cal->gyro_stillness_detect,
                 gyro_var_threshold,
                 gyro_confidence_delta);
  gyroStillDetInit(&gyro_cal->accel_stillness_detect,
                 accel_var_threshold,
                 accel_confidence_delta);
  gyroStillDetInit(&gyro_cal->mag_stillness_detect,
                 mag_var_threshold,
                 mag_confidence_delta);

  // Reset stillness flag and start timestamp.
  gyro_cal->prev_still = false;
  gyro_cal->start_still_time = 0;

  // Set the min and max window stillness duration.
  gyro_cal->min_still_duration = min_still_duration;
  gyro_cal->max_still_duration = max_still_duration;

  // Sets the duration of the stillness processing windows.
  gyro_cal->window_time_duration = window_time_duration;

  // Set the watchdog timeout duration.
  gyro_cal->gyro_watchdog_timeout_duration =
      2 * window_time_duration;

  // Load the last valid cal from system memory.
  gyro_cal->bias_x = bias_x; // [rad/sec]
  gyro_cal->bias_y = bias_y; // [rad/sec]
  gyro_cal->bias_z = bias_z; // [rad/sec]
  gyro_cal->calibration_time = calibration_time;

  // Set the stillness threshold required for gyro bias calibration.
  gyro_cal->stillness_threshold = stillness_threshold;

  // Current window end time used to assist in keeping sensor data
  // collection in sync. Setting this to zero signals that sensor data
  // will be dropped until a valid end time is set from the first gyro
  // timestamp received.
  gyro_cal->stillness_win_endtime = 0;

  // Gyro calibrations will be applied (see, gyroCalRemoveBias()).
  gyro_cal->gyro_calibration_enable = (remove_bias_enable > 0);

#ifdef GYRO_CAL_DBG_ENABLED
  GYRO_INFO_PRINT("Gyro Cal: Initialized.");
#endif
}

// Void all pointers in the gyro calibration data structure
// (prevents compiler warnings).
void gyroCalDestroy(struct gyroCal_t* gyro_cal) {
  (void)gyro_cal;
}

// Get the most recent bias calibration value.
void gyroCalGetBias(struct gyroCal_t* gyro_cal,
                    float* bias_x, float* bias_y, float* bias_z) {
  if (gyro_cal->gyro_calibration_enable) {
    *bias_x = gyro_cal->bias_x;
    *bias_y = gyro_cal->bias_y;
    *bias_z = gyro_cal->bias_z;
  }
}

// Set an initial bias calibration value.
void gyroCalSetBias(struct gyroCal_t* gyro_cal,
                    float bias_x, float bias_y, float bias_z,
                    uint64_t calibration_time) {
  gyro_cal->bias_x = bias_x;
  gyro_cal->bias_y = bias_y;
  gyro_cal->bias_z = bias_z;
  gyro_cal->calibration_time = calibration_time;
}

// Remove bias from a gyro measurement [rad/sec].
void gyroCalRemoveBias(struct gyroCal_t* gyro_cal,
                       float xi, float yi, float zi,
                       float* xo, float* yo, float* zo) {
  if (gyro_cal->gyro_calibration_enable) {
    *xo = xi - gyro_cal->bias_x;
    *yo = yi - gyro_cal->bias_y;
    *zo = zi - gyro_cal->bias_z;
  }
}

// Returns true when a new gyro calibration is available.
bool gyroCalNewBiasAvailable(struct gyroCal_t* gyro_cal) {
  bool new_gyro_cal_available =
      (gyro_cal->gyro_calibration_enable &&
       gyro_cal->new_gyro_cal_available);

  // Clear the flag.
  gyro_cal->new_gyro_cal_available = false;

  return new_gyro_cal_available;
}

// Update the gyro calibration with gyro data [rad/sec].
void gyroCalUpdateGyro(struct gyroCal_t* gyro_cal,
                       uint64_t sample_time,
                       float x, float y, float z,
                       float temperature) {

  // Make sure that a valid window end time is set,
  // and start the watchdog timer.
  if (gyro_cal->stillness_win_endtime <= 0) {
    gyro_cal->stillness_win_endtime =
        sample_time + gyro_cal->window_time_duration;

    // Start the watchdog timer.
    gyro_cal->gyro_watchdog_start = sample_time;
  }

  // Record the latest temperture sample.
  gyro_cal->latest_temperature_celcius = temperature;

  // Pass gyro data to stillness detector
  gyroStillDetUpdate(&gyro_cal->gyro_stillness_detect,
                     gyro_cal->stillness_win_endtime,
                     sample_time, x, y, z);

  // Perform a device stillness check, set next window end time, and
  // possibly do a gyro bias calibration and stillness detector reset.
  deviceStillnessCheck(gyro_cal, sample_time);
}

// Update the gyro calibration with mag data [micro Tesla].
void gyroCalUpdateMag(struct gyroCal_t* gyro_cal,
                      uint64_t sample_time,
                      float x, float y, float z) {

  // Pass magnetometer data to stillness detector.
  gyroStillDetUpdate(&gyro_cal->mag_stillness_detect,
                     gyro_cal->stillness_win_endtime,
                     sample_time, x, y, z);

  // Received a magnetometer sample; incorporate it into detection.
  gyro_cal->using_mag_sensor = true;

  // Perform a device stillness check, set next window end time, and
  // possibly do a gyro bias calibration and stillness detector reset.
  deviceStillnessCheck(gyro_cal, sample_time);
}

// Update the gyro calibration with accel data [m/sec^2].
void gyroCalUpdateAccel(struct gyroCal_t* gyro_cal,
                        uint64_t sample_time,
                        float x, float y, float z) {
  // Pass accelerometer data to stillnesss detector.
  gyroStillDetUpdate(&gyro_cal->accel_stillness_detect,
                     gyro_cal->stillness_win_endtime,
                     sample_time, x, y, z);

  // Perform a device stillness check, set next window end time, and
  // possibly do a gyro bias calibration and stillness detector reset.
  deviceStillnessCheck(gyro_cal, sample_time);
}

// Checks the state of all stillness detectors to determine
// whether the device is "still".
void deviceStillnessCheck(struct gyroCal_t* gyro_cal,
                          uint64_t sample_time) {

  bool stillness_duration_exceeded = false;
  bool stillness_duration_too_short = false;
  bool device_is_still = false;
  float conf_not_rot = 0;
  float conf_not_accel = 0;
  float conf_still = 0;

  // Check the watchdog timer.
  checkWatchdog(gyro_cal, sample_time);

  // Is there enough data to do a stillness calculation?
  if ((!gyro_cal->mag_stillness_detect.stillness_window_ready &&
       gyro_cal->using_mag_sensor) ||
      !gyro_cal->accel_stillness_detect.stillness_window_ready ||
      !gyro_cal->gyro_stillness_detect.stillness_window_ready) {
    return; // Not yet, wait for more data.
  }

  // Set the next window end time for the stillness detectors.
  gyro_cal->stillness_win_endtime =
      sample_time + gyro_cal->window_time_duration;

  // Update the confidence scores for all sensors.
  gyroStillDetCompute(&gyro_cal->accel_stillness_detect);
  gyroStillDetCompute(&gyro_cal->gyro_stillness_detect);
  if (gyro_cal->using_mag_sensor) {
    gyroStillDetCompute(&gyro_cal->mag_stillness_detect);
  } else {
    // Not using magnetometer, force stillness confidence to 100%.
    gyro_cal->mag_stillness_detect.stillness_confidence = 1.0f;
  }

  // Determine motion confidence scores (rotation, accelerating, and stillness).
  conf_not_rot = gyro_cal->gyro_stillness_detect.stillness_confidence *
      gyro_cal->mag_stillness_detect.stillness_confidence;
  conf_not_accel = gyro_cal->accel_stillness_detect.stillness_confidence;
  conf_still = conf_not_rot * conf_not_accel;

  // determine if the device is currently still.
  device_is_still = (conf_still > gyro_cal->stillness_threshold);

  if (device_is_still) {

    // Device is still logic:
    // If not previously still, then record the start time.
    // If stillness period is too long, then do a calibration.
    // Otherwise, continue collecting stillness data.

    // If device was not previously still, set new start timestamp.
    if (!gyro_cal->prev_still) {

      // Record the starting timestamp of the current stillness window.
      // This enables the calculation of total duration of the stillness period.
      gyro_cal->start_still_time =
          gyro_cal->gyro_stillness_detect.window_start_time;
    }

    // Check to see if current stillness period exceeds the desired limit
    // to avoid corrupting the .
    stillness_duration_exceeded =
        ((gyro_cal->gyro_stillness_detect.last_sample_time -
          gyro_cal->start_still_time) >
          gyro_cal->max_still_duration);

    if (stillness_duration_exceeded) {

      // The current stillness has gone too long. Do a calibration with the
      // current data and reset.

      // Update the gyro bias estimate with the current window data and
      // reset the stats.
      gyroStillDetReset(&gyro_cal->accel_stillness_detect, true);
      gyroStillDetReset(&gyro_cal->gyro_stillness_detect, true);
      gyroStillDetReset(&gyro_cal->mag_stillness_detect, true);

      // Perform calibration.
      computeGyroCal(gyro_cal,
                     gyro_cal->gyro_stillness_detect.last_sample_time);

      // Update stillness flag. Force the start of a new stillness period.
      gyro_cal->prev_still = false;
    } else {

      // Continue collecting stillness data.

      // Reset stillness detectors, and extend stillness period.
      gyroStillDetReset(&gyro_cal->accel_stillness_detect, false);
      gyroStillDetReset(&gyro_cal->gyro_stillness_detect, false);
      gyroStillDetReset(&gyro_cal->mag_stillness_detect, false);

      // Update stillness flag.
      gyro_cal->prev_still = true;
    }
  } else {

    // Device is NOT still; motion detected.

    // If device was previously still and the total stillness duration is not
    // "too short", then do a calibration with the data accumulated thus far.
    stillness_duration_too_short =
        ((gyro_cal->gyro_stillness_detect.window_start_time -
          gyro_cal->start_still_time) < gyro_cal->min_still_duration);

    if (gyro_cal->prev_still && !stillness_duration_too_short) {
      computeGyroCal(gyro_cal,
                     gyro_cal->gyro_stillness_detect.window_start_time);
    }

    // Reset stillness detectors and the stats.
    gyroStillDetReset(&gyro_cal->accel_stillness_detect, true);
    gyroStillDetReset(&gyro_cal->gyro_stillness_detect, true);
    gyroStillDetReset(&gyro_cal->mag_stillness_detect, true);

    // Update stillness flag.
    gyro_cal->prev_still = false;
  }

  // Reset the watchdog timer after we have processed data.
  gyro_cal->gyro_watchdog_start = sample_time;

#ifdef GYRO_CAL_DBG_ENABLED
  // Debug information available.
  gyro_cal->debug_processed_data_available = true;
  gyro_cal->debug_processed_data_time = sample_time;
#endif
}

// Calculates a new gyro bias offset calibration value.
void computeGyroCal(struct gyroCal_t* gyro_cal,
                    uint64_t calibration_time) {

  // Current calibration duration.
  uint64_t cur_cal_dur =
      calibration_time - gyro_cal->start_still_time;

  // Check to see if new calibration values is within acceptable range.
  if (!(gyro_cal->gyro_stillness_detect.prev_mean_x < MAX_GYRO_BIAS &&
        gyro_cal->gyro_stillness_detect.prev_mean_x >-MAX_GYRO_BIAS &&
        gyro_cal->gyro_stillness_detect.prev_mean_y < MAX_GYRO_BIAS &&
        gyro_cal->gyro_stillness_detect.prev_mean_y >-MAX_GYRO_BIAS &&
        gyro_cal->gyro_stillness_detect.prev_mean_z < MAX_GYRO_BIAS &&
        gyro_cal->gyro_stillness_detect.prev_mean_z >-MAX_GYRO_BIAS)) {
    // Outside of range. Ignore, reset, and continue.
    return;
  }

  // Record new gyro bias offset calibration.
  gyro_cal->bias_x = gyro_cal->gyro_stillness_detect.prev_mean_x;
  gyro_cal->bias_y = gyro_cal->gyro_stillness_detect.prev_mean_y;
  gyro_cal->bias_z = gyro_cal->gyro_stillness_detect.prev_mean_z;

  // Record final stillness confidence.
  gyro_cal->stillness_confidence =
      gyro_cal->gyro_stillness_detect.prev_stillness_confidence *
      gyro_cal->accel_stillness_detect.prev_stillness_confidence *
      gyro_cal->mag_stillness_detect.prev_stillness_confidence;

  // Store calibration stillness duration.
  gyro_cal->calibration_time_duration = cur_cal_dur;

  // Store calibration time stamp.
  gyro_cal->calibration_time = calibration_time;

  // Set flag to indicate a new gyro calibration value is available.
  gyro_cal->new_gyro_cal_available = true;

#ifdef GYRO_CAL_DBG_ENABLED
  // Increment the total count of calibration updates.
  gyro_cal->debug_calibration_count++;

  // Store the last 'DEBUG_GYRO_SHORTTERM_NUM_CAL' calibration debug data
  // in a circular buffer, 'debug_cal_data[]'. 'debug_head' is the index
  // of the last valid calibration.
  gyro_cal->debug_head++;
  if (gyro_cal->debug_head >= DEBUG_GYRO_SHORTTERM_NUM_CAL) {
    gyro_cal->debug_head = 0;
  }
  if (gyro_cal->debug_num_cals < DEBUG_GYRO_SHORTTERM_NUM_CAL) {
    gyro_cal->debug_num_cals++;
  }

  // Update the calibration debug information.
  gyroCalUpdateDebug(gyro_cal,
                     &gyro_cal->debug_cal_data[gyro_cal->debug_head]);

  // Improve the collection of historic calibrations. Limit frequency to
  // every N hours.
  if ((gyro_cal->debug_num_cals_hist <= 0) ||
      (calibration_time -
       gyro_cal->
       debug_cal_data_hist[gyro_cal->debug_head_hist].calibration_time)
      >= DEBUG_GYRO_CAL_LIMIT) {
    gyro_cal->debug_head_hist++;
    if (gyro_cal->debug_head_hist >= DEBUG_GYRO_LONGTERM_NUM_CAL) {
      gyro_cal->debug_head_hist = 0;
    }
    if (gyro_cal->debug_num_cals_hist < DEBUG_GYRO_LONGTERM_NUM_CAL) {
      gyro_cal->debug_num_cals_hist++;
    }

    // Update the calibration debug information.
    gyroCalUpdateDebug(gyro_cal,
                       &gyro_cal->
                       debug_cal_data_hist[gyro_cal->debug_head_hist]);
  }
#endif
}

// Check for a watchdog timeout condition.
void checkWatchdog(struct gyroCal_t* gyro_cal, uint64_t sample_time) {

  bool watchdog_timeout;

  // Check for initialization of the watchdog time (=0).
  if (gyro_cal->gyro_watchdog_start <= 0) {
    return;
  }

  // Check for timeout condition of watchdog.
  watchdog_timeout = ((sample_time - gyro_cal->gyro_watchdog_start) >
                      gyro_cal->gyro_watchdog_timeout_duration);

  // If a timeout occurred then reset to known good state.
  if (watchdog_timeout) {

    // Reset stillness detectors and restart data capture.
    gyroStillDetReset(&gyro_cal->accel_stillness_detect, true);
    gyroStillDetReset(&gyro_cal->gyro_stillness_detect, true);
    gyroStillDetReset(&gyro_cal->mag_stillness_detect, true);
    gyro_cal->mag_stillness_detect.stillness_confidence = 0;
    gyro_cal->stillness_win_endtime = 0;

    // Force stillness confidence to zero.
    gyro_cal->accel_stillness_detect.prev_stillness_confidence = 0;
    gyro_cal->gyro_stillness_detect.prev_stillness_confidence = 0;
    gyro_cal->mag_stillness_detect.prev_stillness_confidence = 0;
    gyro_cal->stillness_confidence = 0;
    gyro_cal->prev_still = false;

    // If there are no magnetometer samples being received then
    // operate the calibration algorithm without this sensor.
    if (!gyro_cal->mag_stillness_detect.stillness_window_ready &&
       gyro_cal->using_mag_sensor) {
      gyro_cal->using_mag_sensor = false;
    }

    // Assert watchdog timeout flags.
    gyro_cal->gyro_watchdog_timeout |= watchdog_timeout;
    gyro_cal->gyro_watchdog_start = 0;
#ifdef GYRO_CAL_DBG_ENABLED
    gyro_cal->debug_watchdog_count++;
#endif
  }
}

#ifdef GYRO_CAL_DBG_ENABLED
// Update the calibration debug information.
void gyroCalUpdateDebug(struct gyroCal_t* gyro_cal,
                        struct debugGyroCal_t* debug_gyro_cal) {
  // Probability of stillness (acc, rot, still), duration, timestamp.
  debug_gyro_cal->accel_stillness_conf =
      gyro_cal->accel_stillness_detect.prev_stillness_confidence;
  debug_gyro_cal->gyro_stillness_conf =
      gyro_cal->gyro_stillness_detect.prev_stillness_confidence;
  debug_gyro_cal->mag_stillness_conf =
      gyro_cal->mag_stillness_detect.prev_stillness_confidence;

  // Magnetometer usage.
  debug_gyro_cal->used_mag_sensor = gyro_cal->using_mag_sensor;

  // Temperature at calibration time.
  debug_gyro_cal->temperature_celcius =
      gyro_cal->latest_temperature_celcius;

  // Calibration time and stillness duration.
  debug_gyro_cal->calibration_time_duration =
      gyro_cal->calibration_time_duration;
  debug_gyro_cal->calibration_time =
      gyro_cal->calibration_time;

  // Record the current calibration values
  debug_gyro_cal->calibration[0] = gyro_cal->bias_x;
  debug_gyro_cal->calibration[1] = gyro_cal->bias_y;
  debug_gyro_cal->calibration[2] = gyro_cal->bias_z;

  // Record the previous window means
  debug_gyro_cal->accel_mean[0] =
      gyro_cal->accel_stillness_detect.prev_mean_x;
  debug_gyro_cal->accel_mean[1] =
      gyro_cal->accel_stillness_detect.prev_mean_y;
  debug_gyro_cal->accel_mean[2] =
      gyro_cal->accel_stillness_detect.prev_mean_z;

  debug_gyro_cal->gyro_mean[0] =
      gyro_cal->gyro_stillness_detect.prev_mean_x;
  debug_gyro_cal->gyro_mean[1] =
      gyro_cal->gyro_stillness_detect.prev_mean_y;
  debug_gyro_cal->gyro_mean[2] =
      gyro_cal->gyro_stillness_detect.prev_mean_z;

  debug_gyro_cal->mag_mean[0] =
      gyro_cal->mag_stillness_detect.prev_mean_x;
  debug_gyro_cal->mag_mean[1] =
      gyro_cal->mag_stillness_detect.prev_mean_y;
  debug_gyro_cal->mag_mean[2] =
      gyro_cal->mag_stillness_detect.prev_mean_z;

  // Record the variance data.
  debug_gyro_cal->accel_var[0] =
      gyro_cal->accel_stillness_detect.win_var_x;
  debug_gyro_cal->accel_var[1] =
      gyro_cal->accel_stillness_detect.win_var_y;
  debug_gyro_cal->accel_var[2] =
      gyro_cal->accel_stillness_detect.win_var_z;

  debug_gyro_cal->gyro_var[0] =
      gyro_cal->gyro_stillness_detect.win_var_x;
  debug_gyro_cal->gyro_var[1] =
      gyro_cal->gyro_stillness_detect.win_var_y;
  debug_gyro_cal->gyro_var[2] =
      gyro_cal->gyro_stillness_detect.win_var_z;

  debug_gyro_cal->mag_var[0] =
      gyro_cal->mag_stillness_detect.win_var_x;
  debug_gyro_cal->mag_var[1] =
      gyro_cal->mag_stillness_detect.win_var_y;
  debug_gyro_cal->mag_var[2] =
      gyro_cal->mag_stillness_detect.win_var_z;
}

// Helper function to print debug statements.
void gyroCalDebugPrintData(int count,
                       struct debugGyroCal_t *debug_data) {
  GYRO_INFO_PRINT(
      "#%d Gyro Bias Calibration = {%s%d.%06d, %s%d.%06d, %s%d.%06d} [mdps]\n",
      count,
      GYRO_ENCODE_FLOAT(debug_data->calibration[0] * RAD_TO_MDEG, 6),
      GYRO_ENCODE_FLOAT(debug_data->calibration[1] * RAD_TO_MDEG, 6),
      GYRO_ENCODE_FLOAT(debug_data->calibration[2] * RAD_TO_MDEG, 6)
                  );

  if (debug_data->used_mag_sensor) {
    GYRO_INFO_PRINT("   Using Magnetometer.\n");
  } else {
    GYRO_INFO_PRINT("   Not Using Magnetometer.\n");
  }

  GYRO_INFO_PRINT("   Temperature = %s%d.%06d [C]\n",
                  GYRO_ENCODE_FLOAT(debug_data->temperature_celcius, 6));

  GYRO_INFO_PRINT("   Calibration Timestamp = %llu [nsec]\n",
                  debug_data->calibration_time);

  GYRO_INFO_PRINT("   Stillness Duration = %llu [nsec]\n",
                  debug_data->calibration_time_duration);

  GYRO_INFO_PRINT("   Accel Mean = {%s%d.%06d, %s%d.%06d, %s%d.%06d} [g]\n",
                  GYRO_ENCODE_FLOAT(debug_data->accel_mean[0] *
                                    GRAVITY_TO_G, 6),
                  GYRO_ENCODE_FLOAT(debug_data->accel_mean[1] *
                                    GRAVITY_TO_G, 6),
                  GYRO_ENCODE_FLOAT(debug_data->accel_mean[2] *
                                    GRAVITY_TO_G, 6));

  GYRO_INFO_PRINT("   Mag Mean = {%s%d.%06d, %s%d.%06d, %s%d.%06d} [uT]\n",
                  GYRO_ENCODE_FLOAT(debug_data->mag_mean[0], 6),
                  GYRO_ENCODE_FLOAT(debug_data->mag_mean[1], 6),
                  GYRO_ENCODE_FLOAT(debug_data->mag_mean[2], 6)
                  );
}

// Print Debug data useful for tuning the stillness detectors.
void gyroCalTuneDebugPrint(struct gyroCal_t* gyro_cal, uint64_t sample_time) {

  static uint64_t counter = 0;

  // Output sensor variance levels to assist with tuning thresholds
  // every N seconds.
  if ((sample_time - counter) > 10000000000) { //nsec
    GYRO_INFO_PRINT("");
    GYRO_INFO_PRINT(
        "   Gyro Variance = {%s%d.%08d, %s%d.%08d, %s%d.%08d} [rad/sec]^2\n",
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->gyro_stillness_detect.win_var_x, 8),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->gyro_stillness_detect.win_var_y, 8),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->gyro_stillness_detect.win_var_z, 8));
    GYRO_INFO_PRINT(
        "   Accel Variance = {%s%d.%08d, %s%d.%08d, %s%d.%08d} [m/sec^2]^2\n",
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->accel_stillness_detect.win_var_x, 8),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->accel_stillness_detect.win_var_y, 8),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->accel_stillness_detect.win_var_z, 8));
    GYRO_INFO_PRINT(
        "   Mag Variance = {%s%d.%06d, %s%d.%06d, %s%d.%06d} [uT]^2\n",
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->mag_stillness_detect.win_var_x, 6),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->mag_stillness_detect.win_var_y, 6),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->mag_stillness_detect.win_var_z, 6));
    GYRO_INFO_PRINT(
        "   Stillness = {G%s%d.%06d, A%s%d.%06d, M%s%d.%06d}\n",
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->
                        gyro_stillness_detect.stillness_confidence, 6),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->
                        accel_stillness_detect.stillness_confidence, 6),
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->
                        mag_stillness_detect.stillness_confidence, 6));
    GYRO_INFO_PRINT(
        "   Temperature = %s%d.%06d [C]\n",
                    GYRO_ENCODE_FLOAT(
                        gyro_cal->latest_temperature_celcius, 6));
    GYRO_INFO_PRINT("Total Gyro Calibrations: %lu\n",
                    gyro_cal->debug_calibration_count);
    counter = sample_time; //reset
  }
}

// Print Debug data report.
void gyroCalDebugPrint(struct gyroCal_t* gyro_cal,
                       int* state,
                       uint64_t sample_time) {

  static int head_index;
  static int i, j;

  if (gyro_cal->debug_num_cals == 0) {
    *state = -1; // idle state.
  }

  // State machine to control reporting out debug print data.
  switch (*state) {

    case 0:
      // STATE 0 : Print out header
      GYRO_INFO_PRINT("");
      GYRO_INFO_PRINT(
          "---DEBUG REPORT-------------------------------------\n");
      GYRO_INFO_PRINT("Gyro Calibrations To Report (newest to oldest): %d\n",
                      gyro_cal->debug_num_cals);
      head_index = gyro_cal->debug_head;
      i = 1; // initialize report count
      *state = 1; // set next state.
      break;

    case 1:
      // STATE 1: Print out past N recent calibrations.
      if (i <= gyro_cal->debug_num_cals) {

        // Print the data.
        gyroCalDebugPrintData(i, &gyro_cal->debug_cal_data[head_index]);

        // Move to the previous calibration data set.
        head_index--;
        if (head_index < 0) {
          head_index = DEBUG_GYRO_SHORTTERM_NUM_CAL-1;
        }

        // Increment the report count.
        i++;

        // Go to wait state.
        j = 0;
        *state = 2;

      } else {
        // Print out the footer data.
        *state = 3;
      }
      break;

    case 2:
      // STATE 2 : Wait state.
      // Wait for N cycles of gyro samples before printing out more data.
      // This helps throttle the print statements.
      if (j <= 50) {
        j++;
      } else {
        *state = 1;
      }
      break;

    case 3:
      // STATE 3 : Print the end of the debug report.
      GYRO_INFO_PRINT("Total Gyro Calibrations: %lu\n",
                      gyro_cal->debug_calibration_count);

      GYRO_INFO_PRINT("Total Watchdog Timeouts: %lu\n",
                      gyro_cal->debug_watchdog_count);

      GYRO_INFO_PRINT(
          "---END DEBUG REPORT---------------------------------\n");
      *state = 4;
      break;

    case 4:
      // STATE 4 : Print out header (historic data)
      GYRO_INFO_PRINT("");
      GYRO_INFO_PRINT(
          "---DEBUG REPORT (HISTORY)---------------------------\n");
      GYRO_INFO_PRINT("Gyro Calibrations To Report (newest to oldest): %d\n",
                      gyro_cal->debug_num_cals_hist);
      head_index = gyro_cal->debug_head_hist;
      i = 1; // initialize report count
      *state = 5; // set next state.
      break;

    case 5:
      // STATE 5: Print out past N historic calibrations.
      if (i <= gyro_cal->debug_num_cals_hist) {

        // Print the data.
        gyroCalDebugPrintData(i, &gyro_cal->debug_cal_data_hist[head_index]);

        // Move to the previous calibration data set.
        head_index--;
        if (head_index < 0) {
          head_index = DEBUG_GYRO_LONGTERM_NUM_CAL-1;
        }

        // Increment the report count.
        i++;

        // Go to wait state.
        j = 0;
        *state = 6;

      } else {
        // Print out the footer data.
        *state = 7;
      }
      break;

    case 6:
      // STATE 6 : Wait state.
      // Wait for N cycles of gyro samples before printing out more data.
      // This helps throttle the print statements.
      if (j <= 50) {
        j++;
      } else {
        *state = 5;
      }
      break;

    case 7:
      // STATE 3 : Print the end of the debug report.
      GYRO_INFO_PRINT("Total Gyro Calibrations: %lu\n",
                      gyro_cal->debug_calibration_count);

      GYRO_INFO_PRINT("Total Watchdog Timeouts: %lu\n",
                      gyro_cal->debug_watchdog_count);

      GYRO_INFO_PRINT(
          "---END DEBUG REPORT---------------------------------\n");
      *state = -1;
      break;

    default :
      // Idle state.
      *state = -1;

      // Report periodic data useful for tuning the stillness detectors.
      //gyroCalTuneDebugPrint(gyro_cal, sample_time);
    }
}
#endif