From e8ce2234f3759c994e920a07c5c89927c969b14b Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Sun, 28 Jun 2015 13:33:40 +0200 Subject: [PATCH] Data validator: Better vibration level estimation, better failover --- src/lib/ecl/validation/data_validator.h | 42 +++++++-- src/lib/ecl/validation/data_validator_group.h | 93 ++++++++++++++++++- 2 files changed, 122 insertions(+), 13 deletions(-) diff --git a/src/lib/ecl/validation/data_validator.h b/src/lib/ecl/validation/data_validator.h index c006d7c50c..26d55af11e 100644 --- a/src/lib/ecl/validation/data_validator.h +++ b/src/lib/ecl/validation/data_validator.h @@ -73,11 +73,17 @@ public: uint64_t error_count() { return _error_count; } /** - * Get the value of this validator + * Get the values of this validator * @return the stored value */ float* value() { return _value; } + /** + * Get the RMS values of this validator + * @return the stored RMS + */ + float* rms() { return _rms; } + /** * Print the validator value * @@ -95,8 +101,10 @@ private: float _M2[3]; /**< RMS component value */ float _rms[3]; /**< root mean square error */ float _value[3]; /**< last value */ + float _value_equal_count; /**< equal values in a row */ DataValidator *_sibling; /**< sibling in the group */ - const unsigned _noreturn_errcount = 1000; /**< if the error count reaches this value, return sensor as invalid */ + const unsigned NORETURN_ERRCOUNT = 1000; /**< if the error count reaches this value, return sensor as invalid */ + const unsigned VALUE_EQUAL_COUNT_MAX = 100; /**< if the sensor value is the same (accumulated also between axes) this many times, flag it */ /* we don't want this class to be copied */ DataValidator(const DataValidator&); @@ -113,6 +121,7 @@ DataValidator::DataValidator(DataValidator *prev_sibling) : _M2{0.0f}, _rms{0.0f}, _value{0.0f}, + _value_equal_count(0), _sibling(prev_sibling) { @@ -131,7 +140,7 @@ DataValidator::put(uint64_t timestamp, float val[3], uint64_t error_count_in) for (unsigned i = 0; i < _dimensions; i++) { if (_time_last == 0) { - _mean[i] = val[i]; + _mean[i] = 0; _lp[i] = val[i]; _M2[i] = 0; } else { @@ -141,10 +150,16 @@ DataValidator::put(uint64_t timestamp, float val[3], uint64_t error_count_in) _mean[i] += delta_val / _event_count; _M2[i] += delta_val * (lp_val - _mean[i]); _rms[i] = sqrtf(_M2[i] / (_event_count - 1)); + + if (fabsf(_value[i] - val[i]) < 0.000001f) { + _value_equal_count++; + } else { + _value_equal_count = 0; + } } // XXX replace with better filter, make it auto-tune to update rate - _lp[i] = _lp[i] * 0.95f + val[i] * 0.05f; + _lp[i] = _lp[i] * 0.5f + val[i] * 0.5f; _value[i] = val[i]; } @@ -161,7 +176,12 @@ DataValidator::confidence(uint64_t timestamp) } /* check error count limit */ - if (_error_count > _noreturn_errcount) { + if (_error_count > NORETURN_ERRCOUNT) { + return 0.0f; + } + + /* we got the exact same sensor value N times in a row */ + if (_value_equal_count > VALUE_EQUAL_COUNT_MAX) { return 0.0f; } @@ -170,17 +190,19 @@ DataValidator::confidence(uint64_t timestamp) return 0.0f; } - // XXX work out scaling between confidence and RMS - //return _rms / _mean; - return 1.0f; } void DataValidator::print() { + if (_time_last == 0) { + printf("\tno data\n"); + return; + } + for (unsigned i = 0; i < _dimensions; i++) { - printf("\tmean: %8.4f lp: %8.4f RMS: %8.4f\n", - (double)(_lp[i] + _mean[i]), (double)_lp[i], (double)_rms[i]); + printf("\tval: %8.4f, lp: %8.4f mean dev: %8.4f RMS: %8.4f\n", + (double) _value[i], (double)_lp[i], (double)_mean[i], (double)_rms[i]); } } diff --git a/src/lib/ecl/validation/data_validator_group.h b/src/lib/ecl/validation/data_validator_group.h index f68a891532..6b420b3f27 100644 --- a/src/lib/ecl/validation/data_validator_group.h +++ b/src/lib/ecl/validation/data_validator_group.h @@ -65,6 +65,20 @@ public: */ float* get_best(uint64_t timestamp, int *index); + /** + * Get the RMS / vibration factor + * + * @return float value representing the RMS, which a valid indicator for vibration + */ + float get_vibration_factor(uint64_t timestamp); + + /** + * Get the number of failover events + * + * @return the number of failovers + */ + unsigned failover_count(); + /** * Print the validator value * @@ -73,6 +87,10 @@ public: private: DataValidator *_first; /**< sibling in the group */ + int _curr_best; /**< currently best index */ + int _prev_best; /**< the previous best index */ + uint64_t _first_failover_time; /**< timestamp where the first failover occured or zero if none occured */ + unsigned _toggle_count; /**< number of back and forth switches between two sensors */ /* we don't want this class to be copied */ DataValidatorGroup(const DataValidatorGroup&); @@ -80,13 +98,19 @@ private: }; DataValidatorGroup::DataValidatorGroup(unsigned siblings) : - _first(nullptr) + _first(nullptr), + _curr_best(-1), + _prev_best(-1), + _first_failover_time(0), + _toggle_count(0) { DataValidator *next = _first; for (unsigned i = 0; i < siblings; i++) { next = new DataValidator(next); } + + _first = next; } DataValidatorGroup::~DataValidatorGroup() @@ -116,6 +140,7 @@ DataValidatorGroup::get_best(uint64_t timestamp, int *index) DataValidator *next = _first; // XXX This should eventually also include voting + int pre_check_best = _curr_best; float max_confidence = 0.0f; int max_index = -1; uint64_t min_error_count = 30000; @@ -125,28 +150,90 @@ DataValidatorGroup::get_best(uint64_t timestamp, int *index) while (next != nullptr) { float confidence = next->confidence(timestamp); - if (confidence > max_confidence && - next->error_count() < min_error_count) { + if (confidence > max_confidence || + (fabsf(confidence - max_confidence) < 0.01f && next->error_count() < min_error_count)) { max_index = i; max_confidence = confidence; min_error_count = next->error_count(); best = next; } + next = next->sibling(); i++; } + /* the current best sensor is not matching the previous best sensor */ + if (max_index != _curr_best) { + + /* if we're no initialized, initialize the bookkeeping but do not count a failsafe */ + if (_curr_best < 0) { + _prev_best = max_index; + } else { + /* we were initialized before, this is a real failsafe */ + _prev_best = pre_check_best; + _toggle_count++; + + /* if this is the first time, log when we failed */ + if (_first_failover_time == 0) { + _first_failover_time = timestamp; + } + } + + /* for all cases we want to keep a record of the best index */ + _curr_best = max_index; + } *index = max_index; return (best) ? best->value() : nullptr; } +float +DataValidatorGroup::get_vibration_factor(uint64_t timestamp) +{ + DataValidator *next = _first; + + float vibe = 0.0f; + + /* find the best RMS value of a non-timed out sensor */ + while (next != nullptr) { + + if (next->confidence(timestamp) > 0.5f) { + float* rms = next->rms(); + + for (unsigned j = 0; j < 3; j++) { + if (rms[j] > vibe) { + vibe = rms[j]; + } + } + } + + next = next->sibling(); + } + + return vibe; +} + void DataValidatorGroup::print() { + /* print the group's state */ + warnx("validator: best: %d, prev best: %d, failsafe: %s (# %u)", + _curr_best, _prev_best, (_toggle_count > 0) ? "YES" : "NO", + _toggle_count); + + DataValidator *next = _first; + unsigned i = 0; while (next != nullptr) { + printf("sensor #%u:\n", i); next->print(); next = next->sibling(); + i++; } } + +unsigned +DataValidatorGroup::failover_count() +{ + return _toggle_count; +}