PX4 Firmware
PX4 Autopilot Software http://px4.io
data_validator_group.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2015 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file data_validator_group.cpp
36  *
37  * A data validation group to identify anomalies in data streams
38  *
39  * @author Lorenz Meier <lorenz@px4.io>
40  */
41 
42 #include "data_validator_group.h"
43 #include <ecl.h>
44 #include <float.h>
45 
47 {
48  DataValidator *next = nullptr;
49  DataValidator *prev = nullptr;
50 
51  for (unsigned i = 0; i < siblings; i++) {
52  next = new DataValidator();
53 
54  if (i == 0) {
55  _first = next;
56 
57  } else {
58  prev->setSibling(next);
59  }
60 
61  prev = next;
62  }
63 
64  _last = next;
65 
66  if (_first) {
68  }
69 }
70 
72 {
73  while (_first) {
74  DataValidator *next = _first->sibling();
75  delete (_first);
76  _first = next;
77  }
78 }
79 
81 {
82  DataValidator *validator = new DataValidator();
83 
84  if (!validator) {
85  return nullptr;
86  }
87 
88  _last->setSibling(validator);
89  _last = validator;
91  return _last;
92 }
93 
94 void
95 DataValidatorGroup::set_timeout(uint32_t timeout_interval_us)
96 {
97  DataValidator *next = _first;
98 
99  while (next != nullptr) {
100  next->set_timeout(timeout_interval_us);
101  next = next->sibling();
102  }
103 
104  _timeout_interval_us = timeout_interval_us;
105 }
106 
107 void
109 {
110  DataValidator *next = _first;
111 
112  while (next != nullptr) {
113  next->set_equal_value_threshold(threshold);
114  next = next->sibling();
115  }
116 }
117 
118 
119 void
120 DataValidatorGroup::put(unsigned index, uint64_t timestamp, const float val[3], uint64_t error_count, int priority)
121 {
122  DataValidator *next = _first;
123  unsigned i = 0;
124 
125  while (next != nullptr) {
126  if (i == index) {
127  next->put(timestamp, val, error_count, priority);
128  break;
129  }
130 
131  next = next->sibling();
132  i++;
133  }
134 }
135 
136 float *
137 DataValidatorGroup::get_best(uint64_t timestamp, int *index)
138 {
139  DataValidator *next = _first;
140 
141  // XXX This should eventually also include voting
142  int pre_check_best = _curr_best;
143  float pre_check_confidence = 1.0f;
144  int pre_check_prio = -1;
145  float max_confidence = -1.0f;
146  int max_priority = -1000;
147  int max_index = -1;
148  DataValidator *best = nullptr;
149 
150  int i = 0;
151 
152  while (next != nullptr) {
153  float confidence = next->confidence(timestamp);
154 
155  if (i == pre_check_best) {
156  pre_check_prio = next->priority();
157  pre_check_confidence = confidence;
158  }
159 
160  /*
161  * Switch if:
162  * 1) the confidence is higher and priority is equal or higher
163  * 2) the confidence is no less than 1% different and the priority is higher
164  */
165  if ((((max_confidence < MIN_REGULAR_CONFIDENCE) && (confidence >= MIN_REGULAR_CONFIDENCE)) ||
166  (confidence > max_confidence && (next->priority() >= max_priority)) ||
167  (fabsf(confidence - max_confidence) < 0.01f && (next->priority() > max_priority))
168  ) && (confidence > 0.0f)) {
169 
170  max_index = i;
171  max_confidence = confidence;
172  max_priority = next->priority();
173  best = next;
174  }
175 
176  next = next->sibling();
177  i++;
178  }
179 
180  /* the current best sensor is not matching the previous best sensor,
181  * or the only sensor went bad */
182  if (max_index != _curr_best || ((max_confidence < FLT_EPSILON) && (_curr_best >= 0))) {
183 
184  bool true_failsafe = true;
185 
186  /* check whether the switch was a failsafe or preferring a higher priority sensor */
187  if (pre_check_prio != -1 && pre_check_prio < max_priority &&
188  fabsf(pre_check_confidence - max_confidence) < 0.1f) {
189 
190  /* this is not a failover */
191  true_failsafe = false;
192 
193  /* reset error flags, this is likely a hotplug sensor coming online late */
194  if (best != nullptr) {
195  best->reset_state();
196  }
197  }
198 
199  /* if we're no initialized, initialize the bookkeeping but do not count a failsafe */
200  if (_curr_best < 0) {
201  _prev_best = max_index;
202 
203  } else {
204  /* we were initialized before, this is a real failsafe */
205  _prev_best = pre_check_best;
206 
207  if (true_failsafe) {
208  _toggle_count++;
209 
210  /* if this is the first time, log when we failed */
211  if (_first_failover_time == 0) {
212  _first_failover_time = timestamp;
213  }
214  }
215  }
216 
217  /* for all cases we want to keep a record of the best index */
218  _curr_best = max_index;
219  }
220 
221  *index = max_index;
222  return (best) ? best->value() : nullptr;
223 }
224 
225 float
227 {
228  DataValidator *next = _first;
229 
230  float vibe = 0.0f;
231 
232  /* find the best RMS value of a non-timed out sensor */
233  while (next != nullptr) {
234 
235  if (next->confidence(timestamp) > 0.5f) {
236  float *rms = next->rms();
237 
238  for (unsigned j = 0; j < 3; j++) {
239  if (rms[j] > vibe) {
240  vibe = rms[j];
241  }
242  }
243  }
244 
245  next = next->sibling();
246  }
247 
248  return vibe;
249 }
250 
251 float
252 DataValidatorGroup::get_vibration_offset(uint64_t timestamp, int axis)
253 {
254  DataValidator *next = _first;
255 
256  float vibe = -1.0f;
257 
258  /* find the best vibration value of a non-timed out sensor */
259  while (next != nullptr) {
260 
261  if (next->confidence(timestamp) > 0.5f) {
262  float *vibration_offset = next->vibration_offset();
263 
264  if (vibe < 0.0f || vibration_offset[axis] < vibe) {
265  vibe = vibration_offset[axis];
266  }
267  }
268 
269  next = next->sibling();
270  }
271 
272  return vibe;
273 }
274 
275 void
277 {
278  /* print the group's state */
279  ECL_INFO("validator: best: %d, prev best: %d, failsafe: %s (%u events)",
280  _curr_best, _prev_best, (_toggle_count > 0) ? "YES" : "NO",
281  _toggle_count);
282 
283  DataValidator *next = _first;
284  unsigned i = 0;
285 
286  while (next != nullptr) {
287  if (next->used()) {
288  uint32_t flags = next->state();
289 
290  ECL_INFO("sensor #%u, prio: %d, state:%s%s%s%s%s%s", i, next->priority(),
291  ((flags & DataValidator::ERROR_FLAG_NO_DATA) ? " OFF" : ""),
292  ((flags & DataValidator::ERROR_FLAG_STALE_DATA) ? " STALE" : ""),
293  ((flags & DataValidator::ERROR_FLAG_TIMEOUT) ? " TOUT" : ""),
294  ((flags & DataValidator::ERROR_FLAG_HIGH_ERRCOUNT) ? " ECNT" : ""),
295  ((flags & DataValidator::ERROR_FLAG_HIGH_ERRDENSITY) ? " EDNST" : ""),
296  ((flags == DataValidator::ERROR_FLAG_NO_ERROR) ? " OK" : ""));
297 
298  next->print();
299  }
300 
301  next = next->sibling();
302  i++;
303  }
304 }
305 
306 int
308 {
309  DataValidator *next = _first;
310  unsigned i = 0;
311 
312  while (next != nullptr) {
313  if (next->used() && (next->state() != DataValidator::ERROR_FLAG_NO_ERROR) && (i == (unsigned)_prev_best)) {
314  return i;
315  }
316 
317  next = next->sibling();
318  i++;
319  }
320 
321  return -1;
322 }
323 
324 uint32_t
326 {
327  DataValidator *next = _first;
328  unsigned i = 0;
329 
330  while (next != nullptr) {
331  if (next->used() && (next->state() != DataValidator::ERROR_FLAG_NO_ERROR) && (i == (unsigned)_prev_best)) {
332  return next->state();
333  }
334 
335  next = next->sibling();
336  i++;
337  }
338 
340 }
int priority() const
Get the priority of this validator.
uint32_t get_timeout() const
Get the timeout value.
static constexpr uint32_t ERROR_FLAG_NO_ERROR
Data validator error states.
Adapter / shim layer for system calls needed by ECL.
bool used() const
Get the used status of this validator.
void put(unsigned index, uint64_t timestamp, const float val[3], uint64_t error_count, int priority)
Put an item into the validator group.
void set_timeout(uint32_t timeout_interval_us)
Set the timeout value for the whole group.
void reset_state()
Reset the error state of this validator.
DataValidator * _first
first node in the group
static constexpr uint32_t ERROR_FLAG_TIMEOUT
int failover_index()
Get the index of the failed sensor in the group.
DataValidator * _last
last node in the group
void print()
Print the validator value.
uint32_t failover_state()
Get the error state of the failed sensor in the group.
#define FLT_EPSILON
static constexpr uint32_t ERROR_FLAG_STALE_DATA
float * get_best(uint64_t timestamp, int *index)
Get the best data triplet of the group.
static constexpr float MIN_REGULAR_CONFIDENCE
static constexpr uint32_t ERROR_FLAG_HIGH_ERRCOUNT
float get_vibration_offset(uint64_t timestamp, int axis)
Get the vibration offset in the sensor unit.
float get_vibration_factor(uint64_t timestamp)
Get the RMS / vibration factor.
void print()
Print the validator value.
void set_timeout(uint32_t timeout_interval_us)
Set the timeout value.
void setSibling(DataValidator *new_sibling)
Set the sibling to the next node in the group.
void set_equal_value_threshold(uint32_t threshold)
Set the equal count threshold for the whole group.
DataValidator * add_new_validator()
Create a new Validator (with index equal to the number of currently existing validators) ...
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
A data validation group to identify anomalies in data streams.
uint32_t state() const
Get the error state of this validator.
float * rms()
Get the RMS values of this validator.
static constexpr uint32_t ERROR_FLAG_NO_DATA
float * value()
Get the values of this validator.
DataValidator * sibling()
Get the next sibling in the group.
static constexpr uint32_t ERROR_FLAG_HIGH_ERRDENSITY
void set_equal_value_threshold(uint32_t threshold)
Set the equal count threshold.
uint32_t _timeout_interval_us
currently set timeout
#define ECL_INFO
Definition: ecl.h:90
float * vibration_offset()
Get the vibration offset.
void put(uint64_t timestamp, float val, uint64_t error_count, int priority)
Put an item into the validator.
float confidence(uint64_t timestamp)
Get the confidence of this validator.
int _prev_best
the previous best index
int _curr_best
currently best index
unsigned _toggle_count
number of back and forth switches between two sensors
DataValidatorGroup(unsigned siblings)
uint64_t _first_failover_time
timestamp where the first failover occured or zero if none occured