mirror of https://github.com/ArduPilot/ardupilot
AP_NavEKF: re-implemented EKF ring buffer
this fixes a bug where elemnts being pushed into the buffer more slowly than we recall can be lost for example, if you push a single element in then try a recall it will fail
This commit is contained in:
parent
19da623077
commit
a941e4cd41
|
@ -10,22 +10,21 @@
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
ekf_ring_buffer::ekf_ring_buffer(uint8_t _elsize) :
|
ekf_ring_buffer::ekf_ring_buffer(uint8_t _elsize) :
|
||||||
elsize(_elsize)
|
elsize(_elsize),
|
||||||
|
buffer(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool ekf_ring_buffer::init(uint8_t size)
|
bool ekf_ring_buffer::init(uint8_t _size)
|
||||||
{
|
{
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
buffer = calloc(size, elsize);
|
buffer = calloc(_size, elsize);
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_size = size;
|
size = _size;
|
||||||
_head = 0;
|
reset();
|
||||||
_tail = 0;
|
|
||||||
_new_data = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ void *ekf_ring_buffer::get_offset(uint8_t idx) const
|
||||||
/*
|
/*
|
||||||
get a reference to the timestamp for an index
|
get a reference to the timestamp for an index
|
||||||
*/
|
*/
|
||||||
uint32_t &ekf_ring_buffer::time_ms(uint8_t idx)
|
uint32_t ekf_ring_buffer::time_ms(uint8_t idx) const
|
||||||
{
|
{
|
||||||
EKF_obs_element_t *el = (EKF_obs_element_t *)get_offset(idx);
|
EKF_obs_element_t *el = (EKF_obs_element_t *)get_offset(idx);
|
||||||
return el->time_ms;
|
return el->time_ms;
|
||||||
|
@ -48,53 +47,30 @@ uint32_t &ekf_ring_buffer::time_ms(uint8_t idx)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Search through a ring buffer and return the newest data that is
|
Search through a ring buffer and return the newest data that is
|
||||||
older than the time specified by sample_time_ms Zeros old data
|
older than the time specified by sample_time_ms
|
||||||
so it cannot not be used again Returns false if no data can be
|
Returns false if no data can be found that is less than 100msec old
|
||||||
found that is less than 100msec old
|
|
||||||
*/
|
*/
|
||||||
bool ekf_ring_buffer::recall(void *element,uint32_t sample_time)
|
bool ekf_ring_buffer::recall(void *element, const uint32_t sample_time_ms)
|
||||||
{
|
{
|
||||||
if (!_new_data) {
|
bool ret = false;
|
||||||
return false;
|
while (count > 0) {
|
||||||
}
|
const uint32_t toldest = time_ms(oldest);
|
||||||
bool success = false;
|
const int32_t dt = sample_time_ms - toldest;
|
||||||
uint8_t tail = _tail, bestIndex;
|
const bool matches = dt >= 0 && dt < 100;
|
||||||
|
if (matches) {
|
||||||
if (_head == tail) {
|
memcpy(element, get_offset(oldest), elsize);
|
||||||
if (time_ms(tail) != 0 && time_ms(tail) <= sample_time) {
|
ret = true;
|
||||||
// if head is equal to tail just check if the data is unused and within time horizon window
|
|
||||||
if (((sample_time - time_ms(tail)) < 100)) {
|
|
||||||
bestIndex = tail;
|
|
||||||
success = true;
|
|
||||||
_new_data = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
if (dt < 0) {
|
||||||
while(_head != tail) {
|
// the oldest element is younger than we want, stop
|
||||||
// find a measurement older than the fusion time horizon that we haven't checked before
|
// searching and don't consume this element
|
||||||
if (time_ms(tail) != 0 && time_ms(tail) <= sample_time) {
|
break;
|
||||||
// Find the most recent non-stale measurement that meets the time horizon criteria
|
|
||||||
if (((sample_time - time_ms(tail)) < 100)) {
|
|
||||||
bestIndex = tail;
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
} else if (time_ms(tail) > sample_time){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tail = (tail+1) % _size;
|
|
||||||
}
|
}
|
||||||
|
// discard the sample
|
||||||
|
count--;
|
||||||
|
oldest = (oldest+1) % size;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
if (!success) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(element, get_offset(bestIndex), elsize);
|
|
||||||
_tail = (bestIndex+1) % _size;
|
|
||||||
// make time zero to stop using it again,
|
|
||||||
// resolves corner case of reusing the element when head == tail
|
|
||||||
time_ms(bestIndex) = 0;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -106,21 +82,26 @@ void ekf_ring_buffer::push(const void *element)
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance head to next available index
|
// Advance head to next available index
|
||||||
_head = (_head+1) % _size;
|
const uint8_t head = (oldest+count) % size;
|
||||||
|
|
||||||
// New data is written at the head
|
// New data is written at the head
|
||||||
memcpy(get_offset(_head), element, elsize);
|
memcpy(get_offset(head), element, elsize);
|
||||||
_new_data = true;
|
|
||||||
|
if (count < size) {
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
oldest = (oldest+1) % size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// zeroes all data in the ring buffer
|
// zeroes all data in the ring buffer
|
||||||
void ekf_ring_buffer::reset()
|
void ekf_ring_buffer::reset()
|
||||||
{
|
{
|
||||||
_head = 0;
|
count = 0;
|
||||||
_tail = 0;
|
oldest = 0;
|
||||||
_new_data = false;
|
|
||||||
memset((void *)buffer,0,_size*uint32_t(elsize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
* Zeros old data so it cannot not be used again
|
* Zeros old data so it cannot not be used again
|
||||||
* Returns false if no data can be found that is less than 100msec old
|
* Returns false if no data can be found that is less than 100msec old
|
||||||
*/
|
*/
|
||||||
bool recall(void *element, uint32_t sample_time);
|
bool recall(void *element, const uint32_t sample_time_ms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writes data and timestamp to a Ring buffer and advances indices that
|
* Writes data and timestamp to a Ring buffer and advances indices that
|
||||||
|
@ -42,9 +42,17 @@ public:
|
||||||
private:
|
private:
|
||||||
const uint8_t elsize;
|
const uint8_t elsize;
|
||||||
void *buffer;
|
void *buffer;
|
||||||
uint8_t _size, _head, _tail, _new_data;
|
|
||||||
|
|
||||||
uint32_t &time_ms(uint8_t idx);
|
// size of allocated buffer in elsize units
|
||||||
|
uint8_t size;
|
||||||
|
|
||||||
|
// index of the oldest element in the buffer
|
||||||
|
uint8_t oldest;
|
||||||
|
|
||||||
|
// total number of elements in the buffer
|
||||||
|
uint8_t count;
|
||||||
|
|
||||||
|
uint32_t time_ms(uint8_t idx) const;
|
||||||
void *get_offset(uint8_t idx) const;
|
void *get_offset(uint8_t idx) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,15 +71,15 @@ public:
|
||||||
ekf_ring_buffer(sizeof(element_type))
|
ekf_ring_buffer(sizeof(element_type))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool init(uint8_t size) {
|
bool init(uint8_t _size) {
|
||||||
return ekf_ring_buffer::init(size);
|
return ekf_ring_buffer::init(_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recall(element_type &element,uint32_t sample_time) {
|
bool recall(element_type &element,uint32_t sample_time) {
|
||||||
return ekf_ring_buffer::recall(&element, sample_time);
|
return ekf_ring_buffer::recall(&element, sample_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(element_type element) {
|
void push(const element_type &element) {
|
||||||
return ekf_ring_buffer::push(&element);
|
return ekf_ring_buffer::push(&element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue