mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-09 01:18:29 -04:00
e2ef0bd36e
new dsp peak detection algorithm add DSP sketch with frequency ascii art tool to generate gyro data frames from batch sampled DF logs add generated data from real Y6B flight allow fft_start() to use ObjectBuffer<float> for lock-free access allow ObjectBuffer to be resized
559 lines
16 KiB
C++
559 lines
16 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <stdint.h>
|
|
#include <AP_HAL/AP_HAL_Boards.h>
|
|
#include <AP_HAL/AP_HAL_Macros.h>
|
|
#include <AP_HAL/Semaphores.h>
|
|
|
|
/*
|
|
* Circular buffer of bytes.
|
|
*/
|
|
class ByteBuffer {
|
|
public:
|
|
ByteBuffer(uint32_t size);
|
|
~ByteBuffer(void);
|
|
|
|
// number of bytes available to be read
|
|
uint32_t available(void) const;
|
|
|
|
// Discards the buffer content, emptying it.
|
|
void clear(void);
|
|
|
|
// number of bytes space available to write
|
|
uint32_t space(void) const;
|
|
|
|
// true if available() is zero
|
|
bool empty(void) const;
|
|
|
|
// write bytes to ringbuffer. Returns number of bytes written
|
|
uint32_t write(const uint8_t *data, uint32_t len);
|
|
|
|
// read bytes from ringbuffer. Returns number of bytes read
|
|
uint32_t read(uint8_t *data, uint32_t len);
|
|
|
|
// read a byte from ring buffer. Returns true on success, false otherwise
|
|
bool read_byte(uint8_t *data);
|
|
|
|
/*
|
|
update bytes at the read pointer. Used to update an object without
|
|
popping it
|
|
*/
|
|
bool update(const uint8_t *data, uint32_t len);
|
|
|
|
// return size of ringbuffer
|
|
uint32_t get_size(void) const { return size; }
|
|
|
|
// set size of ringbuffer, caller responsible for locking
|
|
bool set_size(uint32_t size);
|
|
|
|
// advance the read pointer (discarding bytes)
|
|
bool advance(uint32_t n);
|
|
|
|
// Returns the pointer and size to a contiguous read of the next available data
|
|
const uint8_t *readptr(uint32_t &available_bytes);
|
|
|
|
// peek one byte without advancing read pointer. Return byte
|
|
// or -1 if none available
|
|
int16_t peek(uint32_t ofs) const;
|
|
|
|
/*
|
|
read len bytes without advancing the read pointer
|
|
*/
|
|
uint32_t peekbytes(uint8_t *data, uint32_t len);
|
|
|
|
// Similar to peekbytes(), but will fill out IoVec struct with
|
|
// both parts of the ring buffer if wraparound is happening, or
|
|
// just one part. Returns the number of parts written to.
|
|
struct IoVec {
|
|
uint8_t *data;
|
|
uint32_t len;
|
|
};
|
|
uint8_t peekiovec(IoVec vec[2], uint32_t len);
|
|
|
|
// Reserve `len` bytes and fills out `vec` with both parts of the
|
|
// ring buffer (if wraparound is happening), or just one contiguous
|
|
// part. Returns the number of `vec` elements filled out. Can be used
|
|
// with system calls such as `readv()`.
|
|
//
|
|
// After a call to 'reserve()', 'write()' should never be called
|
|
// until 'commit()' is called!
|
|
uint8_t reserve(IoVec vec[2], uint32_t len);
|
|
|
|
/*
|
|
* "Releases" the memory previously reserved by 'reserve()' to be read.
|
|
* Committer must inform how many bytes were actually written in 'len'.
|
|
*/
|
|
bool commit(uint32_t len);
|
|
|
|
private:
|
|
uint8_t *buf;
|
|
uint32_t size;
|
|
|
|
std::atomic<uint32_t> head{0}; // where to read data
|
|
std::atomic<uint32_t> tail{0}; // where to write data
|
|
};
|
|
|
|
/*
|
|
ring buffer class for objects of fixed size
|
|
!!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
*/
|
|
template <class T>
|
|
class ObjectBuffer {
|
|
public:
|
|
ObjectBuffer(uint32_t _size = 0) {
|
|
// we set size to 1 more than requested as the byte buffer
|
|
// gives one less byte than requested. We round up to a full
|
|
// multiple of the object size so that we always get aligned
|
|
// elements, which makes the readptr() method possible
|
|
buffer = new ByteBuffer(((_size+1) * sizeof(T)));
|
|
}
|
|
~ObjectBuffer(void) {
|
|
delete buffer;
|
|
}
|
|
|
|
// return size of ringbuffer
|
|
uint32_t get_size(void) const { return buffer->get_size() / sizeof(T); }
|
|
|
|
// set size of ringbuffer, caller responsible for locking
|
|
bool set_size(uint32_t size) { return buffer->set_size(((size+1) * sizeof(T))); }
|
|
|
|
// read len objects without advancing the read pointer
|
|
uint32_t peek(T *data, uint32_t len) { return buffer->peekbytes((uint8_t*)data, len * sizeof(T)) / sizeof(T); }
|
|
|
|
// Discards the buffer content, emptying it.
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
void clear(void)
|
|
{
|
|
buffer->clear();
|
|
}
|
|
|
|
// return number of objects available to be read from the front of the queue
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
uint32_t available(void) const {
|
|
return buffer->available() / sizeof(T);
|
|
}
|
|
|
|
// return number of objects that could be written to the back of the queue
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
uint32_t space(void) const {
|
|
return buffer->space() / sizeof(T);
|
|
}
|
|
|
|
// true is available() == 0
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool empty(void) const {
|
|
return buffer->empty();
|
|
}
|
|
|
|
// push one object onto the back of the queue
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool push(const T &object) {
|
|
if (buffer->space() < sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->write((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
// push N objects onto the back of the queue
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool push(const T *object, uint32_t n) {
|
|
if (buffer->space() < n*sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->write((uint8_t*)object, n*sizeof(T)) == n*sizeof(T);
|
|
}
|
|
|
|
/*
|
|
throw away an object from the front of the queue
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool pop(void) {
|
|
return buffer->advance(sizeof(T));
|
|
}
|
|
|
|
/*
|
|
pop earliest object off the front of the queue
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool pop(T &object) {
|
|
if (buffer->available() < sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->read((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
|
|
/*
|
|
* push_force() is semantically equivalent to:
|
|
* if (!push(t)) { pop(); push(t); }
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool push_force(const T &object) {
|
|
if (buffer->space() < sizeof(T)) {
|
|
buffer->advance(sizeof(T));
|
|
}
|
|
return push(object);
|
|
}
|
|
|
|
/*
|
|
* push_force() N objects
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool push_force(const T *object, uint32_t n) {
|
|
uint32_t _space = buffer->space();
|
|
if (_space < sizeof(T)*n) {
|
|
buffer->advance(sizeof(T)*(n-_space));
|
|
}
|
|
return push(object, n);
|
|
}
|
|
|
|
/*
|
|
peek copies an object out from the front of the queue without advancing the read pointer
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this update, in both places !!!
|
|
bool peek(T &object) {
|
|
return buffer->peekbytes((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
/*
|
|
return a pointer to first contiguous array of available
|
|
objects. Return nullptr if none available
|
|
*/
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this, update in both places !!!
|
|
const T *readptr(uint32_t &n) {
|
|
uint32_t avail_bytes = 0;
|
|
const T *ret = (const T *)buffer->readptr(avail_bytes);
|
|
if (!ret || avail_bytes < sizeof(T)) {
|
|
return nullptr;
|
|
}
|
|
n = avail_bytes / sizeof(T);
|
|
return ret;
|
|
}
|
|
|
|
// advance the read pointer (discarding objects)
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this, update in both places !!!
|
|
bool advance(uint32_t n) {
|
|
return buffer->advance(n * sizeof(T));
|
|
}
|
|
|
|
/* update the object at the front of the queue (the one that would
|
|
be fetched by pop()) */
|
|
// !!! Note ObjectBuffer_TS is a duplicate of this, update in both places !!!
|
|
bool update(const T &object) {
|
|
return buffer->update((uint8_t*)&object, sizeof(T));
|
|
}
|
|
|
|
private:
|
|
ByteBuffer *buffer = nullptr;
|
|
};
|
|
|
|
/*
|
|
Thread safe ring buffer class for objects of fixed size
|
|
!!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
*/
|
|
template <class T>
|
|
class ObjectBuffer_TS {
|
|
public:
|
|
ObjectBuffer_TS(uint32_t _size = 0) {
|
|
// we set size to 1 more than requested as the byte buffer
|
|
// gives one less byte than requested. We round up to a full
|
|
// multiple of the object size so that we always get aligned
|
|
// elements, which makes the readptr() method possible
|
|
buffer = new ByteBuffer(((_size+1) * sizeof(T)));
|
|
}
|
|
~ObjectBuffer_TS(void) {
|
|
delete buffer;
|
|
}
|
|
|
|
// return size of ringbuffer
|
|
uint32_t get_size(void) const {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->get_size() / sizeof(T);
|
|
}
|
|
|
|
// set size of ringbuffer, caller responsible for locking
|
|
bool set_size(uint32_t size) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->set_size(((size+1) * sizeof(T)));
|
|
}
|
|
|
|
// read len objects without advancing the read pointer
|
|
uint32_t peek(T *data, uint32_t len) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->peekbytes((uint8_t*)data, len * sizeof(T)) / sizeof(T);
|
|
}
|
|
|
|
|
|
// Discards the buffer content, emptying it.
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
void clear(void)
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
buffer->clear();
|
|
}
|
|
|
|
// return number of objects available to be read from the front of the queue
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
uint32_t available(void) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->available() / sizeof(T);
|
|
}
|
|
|
|
// return number of objects that could be written to the back of the queue
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
uint32_t space(void) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->space() / sizeof(T);
|
|
}
|
|
|
|
// true is available() == 0
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool empty(void) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->empty();
|
|
}
|
|
|
|
// push one object onto the back of the queue
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool push(const T &object) {
|
|
WITH_SEMAPHORE(sem);
|
|
if (buffer->space() < sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->write((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
// push N objects onto the back of the queue
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool push(const T *object, uint32_t n) {
|
|
WITH_SEMAPHORE(sem);
|
|
if (buffer->space() < n*sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->write((uint8_t*)object, n*sizeof(T)) == n*sizeof(T);
|
|
}
|
|
|
|
/*
|
|
throw away an object from the front of the queue
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool pop(void) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->advance(sizeof(T));
|
|
}
|
|
|
|
/*
|
|
pop earliest object off the front of the queue
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool pop(T &object) {
|
|
WITH_SEMAPHORE(sem);
|
|
if (buffer->available() < sizeof(T)) {
|
|
return false;
|
|
}
|
|
return buffer->read((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
/*
|
|
* push_force() is semantically equivalent to:
|
|
* if (!push(t)) { pop(); push(t); }
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool push_force(const T &object) {
|
|
WITH_SEMAPHORE(sem);
|
|
if (buffer->space() < sizeof(T)) {
|
|
buffer->advance(sizeof(T));
|
|
}
|
|
return push(object);
|
|
}
|
|
|
|
/*
|
|
* push_force() N objects
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool push_force(const T *object, uint32_t n) {
|
|
WITH_SEMAPHORE(sem);
|
|
uint32_t _space = buffer->space();
|
|
if (_space < sizeof(T)*n) {
|
|
buffer->advance(sizeof(T)*(n-_space));
|
|
}
|
|
return push(object, n);
|
|
}
|
|
|
|
/*
|
|
peek copies an object out from the front of the queue without advancing the read pointer
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool peek(T &object) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->peekbytes((uint8_t*)&object, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
/*
|
|
return a pointer to first contiguous array of available
|
|
objects. Return nullptr if none available
|
|
*/
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
const T *readptr(uint32_t &n) {
|
|
WITH_SEMAPHORE(sem);
|
|
uint32_t avail_bytes = 0;
|
|
const T *ret = (const T *)buffer->readptr(avail_bytes);
|
|
if (!ret || avail_bytes < sizeof(T)) {
|
|
return nullptr;
|
|
}
|
|
n = avail_bytes / sizeof(T);
|
|
return ret;
|
|
}
|
|
|
|
// advance the read pointer (discarding objects)
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool advance(uint32_t n) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->advance(n * sizeof(T));
|
|
}
|
|
|
|
/* update the object at the front of the queue (the one that would
|
|
be fetched by pop()) */
|
|
// !!! Note this is a duplicate of ObjectBuffer with semaphore, update in both places !!!
|
|
bool update(const T &object) {
|
|
WITH_SEMAPHORE(sem);
|
|
return buffer->update((uint8_t*)&object, sizeof(T));
|
|
}
|
|
|
|
private:
|
|
ByteBuffer *buffer = nullptr;
|
|
HAL_Semaphore sem;
|
|
};
|
|
|
|
/*
|
|
ring buffer class for objects of fixed size with pointer
|
|
access. Note that this is not thread safe, buf offers efficient
|
|
array-like access
|
|
*/
|
|
template <class T>
|
|
class ObjectArray {
|
|
public:
|
|
ObjectArray(uint16_t size_) {
|
|
_size = size_;
|
|
_head = _count = 0;
|
|
_buffer = new T[_size];
|
|
}
|
|
~ObjectArray(void) {
|
|
delete[] _buffer;
|
|
}
|
|
|
|
// return total number of objects
|
|
uint16_t size(void) const {
|
|
return _size;
|
|
}
|
|
|
|
// return number of objects available to be read
|
|
uint16_t available(void) const {
|
|
return _count;
|
|
}
|
|
|
|
// return number of objects that could be written
|
|
uint16_t space(void) const {
|
|
return _size - _count;
|
|
}
|
|
|
|
// true is available() == 0
|
|
bool empty(void) const {
|
|
return _count == 0;
|
|
}
|
|
|
|
// push one object
|
|
bool push(const T &object) {
|
|
if (space() == 0) {
|
|
return false;
|
|
}
|
|
_buffer[(_head+_count)%_size] = object;
|
|
_count++;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
throw away an object
|
|
*/
|
|
bool pop(void) {
|
|
if (empty()) {
|
|
return false;
|
|
}
|
|
_head = (_head+1) % _size;
|
|
_count--;
|
|
return true;
|
|
}
|
|
|
|
// Discards the buffer content, emptying it.
|
|
void clear(void)
|
|
{
|
|
_head = _count = 0;
|
|
}
|
|
|
|
/*
|
|
pop earliest object off the queue
|
|
*/
|
|
bool pop(T &object) {
|
|
if (empty()) {
|
|
return false;
|
|
}
|
|
object = _buffer[_head];
|
|
return pop();
|
|
}
|
|
|
|
|
|
/*
|
|
* push_force() is semantically equivalent to:
|
|
* if (!push(t)) { pop(); push(t); }
|
|
*/
|
|
bool push_force(const T &object) {
|
|
if (space() == 0) {
|
|
pop();
|
|
}
|
|
return push(object);
|
|
}
|
|
|
|
/*
|
|
remove the Nth element from the array. First element is zero
|
|
*/
|
|
bool remove(uint16_t n) {
|
|
if (n >= _count) {
|
|
return false;
|
|
}
|
|
if (n == _count-1) {
|
|
// remove last element
|
|
_count--;
|
|
return true;
|
|
}
|
|
if (n == 0) {
|
|
// remove first element
|
|
return pop();
|
|
}
|
|
// take advantage of the [] operator for simple shift of the array elements
|
|
for (uint16_t i=n; i<_count-1; i++) {
|
|
*(*this)[i] = *(*this)[i+1];
|
|
}
|
|
_count--;
|
|
return true;
|
|
}
|
|
|
|
// allow array indexing, based on current head. Returns a pointer
|
|
// to the object or nullptr
|
|
T * operator[](uint16_t i) {
|
|
if (i >= _count) {
|
|
return nullptr;
|
|
}
|
|
return &_buffer[(_head+i)%_size];
|
|
}
|
|
|
|
private:
|
|
T *_buffer;
|
|
uint16_t _size; // total buffer size
|
|
uint16_t _count; // number in buffer now
|
|
uint16_t _head; // first element
|
|
};
|
|
|
|
typedef ObjectBuffer<float> FloatBuffer;
|
|
typedef ObjectBuffer_TS<float> FloatBuffer_TS;
|
|
typedef ObjectArray<float> FloatArray; |