/* * 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 "calibration/gyroscope/gyro_stillness_detect.h" #include /////// FORWARD DECLARATIONS ///////////////////////////////////////// // Enforces the limits of an input value [0,1]. static float gyroStillDetLimit(float value); /////// FUNCTION DEFINITIONS ///////////////////////////////////////// // Initialize the GyroStillDet structure. void gyroStillDetInit(struct GyroStillDet* gyro_still_det, float var_threshold, float confidence_delta) { // Clear all data structure variables to 0. memset(gyro_still_det, 0, sizeof(struct GyroStillDet)); // Set the delta about the variance threshold for calculation // of the stillness confidence score. if (confidence_delta < var_threshold) { gyro_still_det->confidence_delta = confidence_delta; } else { gyro_still_det->confidence_delta = var_threshold; } // Set the variance threshold parameter for the stillness // confidence score. gyro_still_det->var_threshold = var_threshold; // Signal to start capture of next stillness data window. gyro_still_det->start_new_window = true; } // Update the stillness detector with a new sample. void gyroStillDetUpdate(struct GyroStillDet* gyro_still_det, uint64_t stillness_win_endtime, uint64_t sample_time, float x, float y, float z) { // Using the method of the assumed mean to preserve some numerical // stability while avoiding per-sample divisions that the more // numerically stable Welford method would afford. // Reference for the numerical method used below to compute the // online mean and variance statistics: // 1). en.wikipedia.org/wiki/assumed_mean float delta = 0; // If the window end time is not valid then wait till it is. if (stillness_win_endtime <= 0) { return; } // Increment the number of samples. gyro_still_det->num_acc_samples++; // Online computation of mean for the running stillness period. gyro_still_det->mean_x += x; gyro_still_det->mean_y += y; gyro_still_det->mean_z += z; // Is this the first sample of a new window? if (gyro_still_det->start_new_window) { // Record the window start time. gyro_still_det->window_start_time = sample_time; gyro_still_det->start_new_window = false; // Update assumed mean values. gyro_still_det->assumed_mean_x = x; gyro_still_det->assumed_mean_y = y; gyro_still_det->assumed_mean_z = z; // Reset current window mean and variance. gyro_still_det->num_acc_win_samples = 0; gyro_still_det->win_mean_x = 0; gyro_still_det->win_mean_y = 0; gyro_still_det->win_mean_z = 0; gyro_still_det->acc_var_x = 0; gyro_still_det->acc_var_y = 0; gyro_still_det->acc_var_z = 0; } else { // Check to see if we have enough samples to compute a stillness // confidence score. gyro_still_det->stillness_window_ready = (sample_time >= stillness_win_endtime) && (gyro_still_det->num_acc_samples > 1); } // Record the most recent sample time stamp. gyro_still_det->last_sample_time = sample_time; // Online window mean and variance ("one-pass" accumulation). gyro_still_det->num_acc_win_samples++; delta = (x - gyro_still_det->assumed_mean_x); gyro_still_det->win_mean_x += delta; gyro_still_det->acc_var_x += delta * delta; delta = (y - gyro_still_det->assumed_mean_y); gyro_still_det->win_mean_y += delta; gyro_still_det->acc_var_y += delta * delta; delta = (z - gyro_still_det->assumed_mean_z); gyro_still_det->win_mean_z += delta; gyro_still_det->acc_var_z += delta * delta; } // Calculates and returns the stillness confidence score [0,1]. float gyroStillDetCompute(struct GyroStillDet* gyro_still_det) { float tmp_denom = 1.f; float tmp_denom_mean = 1.f; // Don't divide by zero (not likely, but a precaution). if (gyro_still_det->num_acc_win_samples > 1) { tmp_denom = 1.f / (gyro_still_det->num_acc_win_samples - 1); tmp_denom_mean = 1.f / gyro_still_det->num_acc_win_samples; } else { // Return zero stillness confidence. gyro_still_det->stillness_confidence = 0; return gyro_still_det->stillness_confidence; } // Update the final calculation of window mean and variance. float tmp = gyro_still_det->win_mean_x; gyro_still_det->win_mean_x *= tmp_denom_mean; gyro_still_det->win_var_x = (gyro_still_det->acc_var_x - gyro_still_det->win_mean_x * tmp) * tmp_denom; tmp = gyro_still_det->win_mean_y; gyro_still_det->win_mean_y *= tmp_denom_mean; gyro_still_det->win_var_y = (gyro_still_det->acc_var_y - gyro_still_det->win_mean_y * tmp) * tmp_denom; tmp = gyro_still_det->win_mean_z; gyro_still_det->win_mean_z *= tmp_denom_mean; gyro_still_det->win_var_z = (gyro_still_det->acc_var_z - gyro_still_det->win_mean_z * tmp) * tmp_denom; // Adds the assumed mean value back to the total mean calculation. gyro_still_det->win_mean_x += gyro_still_det->assumed_mean_x; gyro_still_det->win_mean_y += gyro_still_det->assumed_mean_y; gyro_still_det->win_mean_z += gyro_still_det->assumed_mean_z; // Define the variance thresholds. float upper_var_thresh = (gyro_still_det->var_threshold + gyro_still_det->confidence_delta); float lower_var_thresh = (gyro_still_det->var_threshold - gyro_still_det->confidence_delta); // Compute the stillness confidence score. if ((gyro_still_det->win_var_x > upper_var_thresh) || (gyro_still_det->win_var_y > upper_var_thresh) || (gyro_still_det->win_var_z > upper_var_thresh)) { // Sensor variance exceeds the upper threshold (i.e., motion detected). // Set stillness confidence equal to 0. gyro_still_det->stillness_confidence = 0; } else { if ((gyro_still_det->win_var_x <= lower_var_thresh) && (gyro_still_det->win_var_y <= lower_var_thresh) && (gyro_still_det->win_var_z <= lower_var_thresh)) { // Sensor variance is below the lower threshold (i.e., stillness // detected). // Set stillness confidence equal to 1. gyro_still_det->stillness_confidence = 1.f; } else { // Motion detection thresholds not exceeded. Compute the stillness // confidence score. float var_thresh = gyro_still_det->var_threshold; // Compute the stillness confidence score. // Each axis score is limited [0,1]. tmp_denom = 1.f / (upper_var_thresh - lower_var_thresh); gyro_still_det->stillness_confidence = gyroStillDetLimit(0.5f - (gyro_still_det->win_var_x - var_thresh) * tmp_denom) * gyroStillDetLimit(0.5f - (gyro_still_det->win_var_y - var_thresh) * tmp_denom) * gyroStillDetLimit(0.5f - (gyro_still_det->win_var_z - var_thresh) * tmp_denom); } } // Return the stillness confidence. return gyro_still_det->stillness_confidence; } // Resets the stillness detector and initiates a new detection window. // 'reset_stats' determines whether the stillness statistics are reset. void gyroStillDetReset(struct GyroStillDet* gyro_still_det, bool reset_stats) { float tmp_denom = 1.f; // Reset the stillness data ready flag. gyro_still_det->stillness_window_ready = false; // Signal to start capture of next stillness data window. gyro_still_det->start_new_window = true; // Track the stillness confidence (current->previous). gyro_still_det->prev_stillness_confidence = gyro_still_det->stillness_confidence; // Track changes in the mean estimate. if (gyro_still_det->num_acc_samples > 1) { tmp_denom = 1.f / gyro_still_det->num_acc_samples; } gyro_still_det->prev_mean_x = gyro_still_det->mean_x * tmp_denom; gyro_still_det->prev_mean_y = gyro_still_det->mean_y * tmp_denom; gyro_still_det->prev_mean_z = gyro_still_det->mean_z * tmp_denom; // Reset the current statistics to zero. if (reset_stats) { gyro_still_det->num_acc_samples = 0; gyro_still_det->mean_x = 0; gyro_still_det->mean_y = 0; gyro_still_det->mean_z = 0; gyro_still_det->acc_var_x = 0; gyro_still_det->acc_var_y = 0; gyro_still_det->acc_var_z = 0; } } // Enforces the limits of an input value [0,1]. float gyroStillDetLimit(float value) { // Fix limits [0,1]. if (value < 0) { value = 0; } else { if (value > 1.f) { value = 1.f; } } return value; }