diff options
Diffstat (limited to 'common/gyro_still_det.c')
-rw-r--r-- | common/gyro_still_det.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/common/gyro_still_det.c b/common/gyro_still_det.c new file mode 100644 index 0000000000..4574e22e5f --- /dev/null +++ b/common/gyro_still_det.c @@ -0,0 +1,242 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gyro_still_det.h" +#include "vec3.h" + +/* Enforces the limits of an input value [0,1]. */ +static fp_t gyro_still_det_limit(fp_t value); + +void gyro_still_det_update(struct gyro_still_det *gyro_still_det, + uint32_t stillness_win_endtime, uint32_t sample_time, + fp_t x, fp_t y, fp_t z) +{ + fp_t delta = INT_TO_FP(0); + + /* + * 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 + */ + + /* 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] = INT_TO_FP(0); + gyro_still_det->win_mean[Y] = INT_TO_FP(0); + gyro_still_det->win_mean[Z] = INT_TO_FP(0); + gyro_still_det->acc_var[X] = INT_TO_FP(0); + gyro_still_det->acc_var[Y] = INT_TO_FP(0); + gyro_still_det->acc_var[Z] = INT_TO_FP(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] += fp_sq(delta); + + delta = (y - gyro_still_det->assumed_mean[Y]); + gyro_still_det->win_mean[Y] += delta; + gyro_still_det->acc_var[Y] += fp_sq(delta); + + delta = (z - gyro_still_det->assumed_mean[Z]); + gyro_still_det->win_mean[Z] += delta; + gyro_still_det->acc_var[Z] += fp_sq(delta); +} + +fp_t gyro_still_det_compute(struct gyro_still_det *gyro_still_det) +{ + fp_t tmp_denom = INT_TO_FP(1); + fp_t tmp_denom_mean = INT_TO_FP(1); + fp_t tmp; + fp_t upper_var_thresh, lower_var_thresh; + + /* Don't divide by zero (not likely, but a precaution). */ + if (gyro_still_det->num_acc_win_samples > 1) { + tmp_denom = fp_div( + tmp_denom, + INT_TO_FP(gyro_still_det->num_acc_win_samples - 1)); + tmp_denom_mean = + fp_div(tmp_denom_mean, + INT_TO_FP(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. */ + tmp = gyro_still_det->win_mean[X]; + gyro_still_det->win_mean[X] = + fp_mul(gyro_still_det->win_mean[X], tmp_denom_mean); + gyro_still_det->win_var[X] = + fp_mul((gyro_still_det->acc_var[X] - + fp_mul(gyro_still_det->win_mean[X], tmp)), + tmp_denom); + + tmp = gyro_still_det->win_mean[Y]; + gyro_still_det->win_mean[Y] = + fp_mul(gyro_still_det->win_mean[Y], tmp_denom_mean); + gyro_still_det->win_var[Y] = + fp_mul((gyro_still_det->acc_var[Y] - + fp_mul(gyro_still_det->win_mean[Y], tmp)), + tmp_denom); + + tmp = gyro_still_det->win_mean[Z]; + gyro_still_det->win_mean[Z] = + fp_mul(gyro_still_det->win_mean[Z], tmp_denom_mean); + gyro_still_det->win_var[Z] = + fp_mul((gyro_still_det->acc_var[Z] - + fp_mul(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. */ + upper_var_thresh = gyro_still_det->var_threshold + + gyro_still_det->confidence_delta; + + 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 = INT_TO_FP(1); + } else { + /* + * Motion detection thresholds not exceeded. Compute the + * stillness confidence score. + */ + fp_t var_thresh = gyro_still_det->var_threshold; + fpv3_t limit; + + /* + * Compute the stillness confidence score. + * Each axis score is limited [0,1]. + */ + tmp_denom = fp_div(INT_TO_FP(1), + (upper_var_thresh - lower_var_thresh)); + limit[X] = gyro_still_det_limit( + FLOAT_TO_FP(0.5f) - + fp_mul(gyro_still_det->win_var[X] - var_thresh, + tmp_denom)); + limit[Y] = gyro_still_det_limit( + FLOAT_TO_FP(0.5f) - + fp_mul(gyro_still_det->win_var[Y] - var_thresh, + tmp_denom)); + limit[Z] = gyro_still_det_limit( + FLOAT_TO_FP(0.5f) - + fp_mul(gyro_still_det->win_var[Z] - var_thresh, + tmp_denom)); + + gyro_still_det->stillness_confidence = + fp_mul(limit[X], fp_mul(limit[Y], limit[Z])); + } + + /* Return the stillness confidence. */ + return gyro_still_det->stillness_confidence; +} + +void gyro_still_det_reset(struct gyro_still_det *gyro_still_det, + bool reset_stats) +{ + fp_t tmp_denom = INT_TO_FP(1); + + /* 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 > INT_TO_FP(1)) + tmp_denom = + fp_div(INT_TO_FP(1), gyro_still_det->num_acc_samples); + + gyro_still_det->prev_mean[X] = + fp_mul(gyro_still_det->mean[X], tmp_denom); + gyro_still_det->prev_mean[Y] = + fp_mul(gyro_still_det->mean[Y], tmp_denom); + gyro_still_det->prev_mean[Z] = + fp_mul(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] = INT_TO_FP(0); + gyro_still_det->mean[Y] = INT_TO_FP(0); + gyro_still_det->mean[Z] = INT_TO_FP(0); + gyro_still_det->acc_var[X] = INT_TO_FP(0); + gyro_still_det->acc_var[Y] = INT_TO_FP(0); + gyro_still_det->acc_var[Z] = INT_TO_FP(0); + } +} + +fp_t gyro_still_det_limit(fp_t value) +{ + if (value < INT_TO_FP(0)) + value = INT_TO_FP(0); + else if (value > INT_TO_FP(1)) + value = INT_TO_FP(1); + + return value; +} |