| /* 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; |
| } |