181 lines
6.6 KiB
C++
181 lines
6.6 KiB
C++
#ifndef LMS_FIFO_BUFFER_H
|
|
#define LMS_FIFO_BUFFER_H
|
|
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <thread>
|
|
#include <condition_variable>
|
|
#include "dataTypes.h"
|
|
|
|
class LMS_SamplesFIFO
|
|
{
|
|
public:
|
|
struct BufferInfo
|
|
{
|
|
uint32_t size;
|
|
uint32_t itemsFilled;
|
|
};
|
|
|
|
BufferInfo GetInfo()
|
|
{
|
|
std::unique_lock<std::mutex> lck2(readLock);
|
|
std::unique_lock<std::mutex> lck(writeLock);
|
|
BufferInfo stats;
|
|
stats.size = (uint32_t)mBuffer.size();
|
|
stats.itemsFilled = mElementsFilled.load();
|
|
return stats;
|
|
}
|
|
|
|
LMS_SamplesFIFO(uint32_t bufLength)
|
|
{
|
|
Reset(bufLength);
|
|
}
|
|
|
|
~LMS_SamplesFIFO(){};
|
|
|
|
/** @brief inserts items to ring buffer
|
|
@param buffer data source
|
|
@param itemCount number of buffer items to insert
|
|
@param timeout_ms timeout duration for operation
|
|
@param overwrite enable to overwrite oldest items inside the buffer
|
|
@return number of items added
|
|
*/
|
|
uint32_t push_packet(SamplesPacket *buffer, const uint32_t itemCount, const uint32_t timeout_ms, const bool overwrite = true)
|
|
{
|
|
uint32_t addedItems = 0;
|
|
std::unique_lock<std::mutex> lck(writeLock);
|
|
while (addedItems < itemCount)
|
|
{
|
|
while (mElementsFilled.load() >= mBuffer.size()) //wait for free space to insert items
|
|
{
|
|
if (canWrite.wait_for(lck, std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout)
|
|
return addedItems; //dropped all items
|
|
}
|
|
|
|
uint32_t itemsToInsert = itemCount - addedItems;
|
|
uint32_t itemsToEnd = (uint32_t)mBuffer.size() - mTail.load(); //might need to split memcpy into two operations
|
|
if (itemsToInsert > itemsToEnd)
|
|
{
|
|
memcpy(&mBuffer[mTail], &buffer[addedItems], itemsToEnd*sizeof(SamplesPacket));
|
|
memcpy(&mBuffer[0], &buffer[addedItems+itemsToEnd], (itemsToInsert - itemsToEnd)*sizeof(SamplesPacket));
|
|
}
|
|
else
|
|
memcpy(&mBuffer[mTail], &buffer[addedItems], itemsToInsert*sizeof(SamplesPacket));
|
|
mTail.store((mTail.load() + itemsToInsert) & (mBuffer.size() - 1));
|
|
mElementsFilled.fetch_add(itemsToInsert);
|
|
canRead.notify_one();
|
|
addedItems += itemsToInsert;
|
|
}
|
|
return addedItems;
|
|
}
|
|
|
|
/** @brief inserts items to ring buffer
|
|
@param buffer data source
|
|
@param itemCount number of buffer items to insert
|
|
@param timeout_ms timeout duration for operation
|
|
@param overwrite enable to overwrite oldest items inside the buffer
|
|
@return number of items added
|
|
*/
|
|
uint32_t push_samples(const complex16_t *buffer, const uint32_t samplesCount, uint64_t timestamp, const uint32_t timeout_ms, const bool overwrite = true)
|
|
{
|
|
assert(buffer != nullptr);
|
|
const int samplesInPacket = SamplesPacket::samplesCount;
|
|
uint32_t samplesTaken = 0;
|
|
std::unique_lock<std::mutex> lck(writeLock);
|
|
while (samplesTaken < samplesCount)
|
|
{
|
|
while (mElementsFilled.load() >= mBuffer.size()) //buffer might be full, wait for free slots
|
|
{
|
|
if (canWrite.wait_for(lck, std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout)
|
|
return samplesTaken;
|
|
}
|
|
|
|
int tailIndex = mTail.load(); //which element to fill
|
|
while (mElementsFilled.load() < mBuffer.size() && samplesTaken < samplesCount) // not to release lock too often
|
|
{
|
|
mBuffer[tailIndex].timestamp = timestamp + samplesTaken;
|
|
mBuffer[tailIndex].first = 0;
|
|
mBuffer[tailIndex].last = 0;
|
|
while (mBuffer[tailIndex].last < samplesInPacket && samplesTaken < samplesCount)
|
|
{
|
|
mBuffer[tailIndex].samples[mBuffer[tailIndex].last++] = buffer[samplesTaken++];
|
|
}
|
|
mTail.store((tailIndex + 1) & (mBuffer.size() - 1));//advance to next one
|
|
tailIndex = mTail.load();
|
|
mElementsFilled.fetch_add(1);
|
|
canRead.notify_one();
|
|
}
|
|
}
|
|
return samplesTaken;
|
|
}
|
|
|
|
/** @brief Takes items out of ring buffer
|
|
@param buffer data destination
|
|
@param samplesCount number of samples to pop
|
|
@param timestamp returns timestamp of the first sample in buffer
|
|
@param timeout_ms timeout duration for operation
|
|
@return number of samples returned
|
|
*/
|
|
uint32_t pop_samples(complex16_t* buffer, const uint32_t samplesCount, uint64_t *timestamp, const uint32_t timeout_ms)
|
|
{
|
|
assert(buffer != nullptr);
|
|
const int samplesInPacket = SamplesPacket::samplesCount;
|
|
uint32_t samplesFilled = 0;
|
|
*timestamp = 0;
|
|
std::unique_lock<std::mutex> lck(readLock);
|
|
while (samplesFilled < samplesCount)
|
|
{
|
|
while (mElementsFilled.load() == 0) //buffer might be empty, wait for packets
|
|
{
|
|
if (canRead.wait_for(lck, std::chrono::milliseconds(timeout_ms)) == std::cv_status::timeout)
|
|
return samplesFilled;
|
|
}
|
|
if(samplesFilled == 0)
|
|
*timestamp = mBuffer[mHead.load()].timestamp + mBuffer[mHead.load()].first;
|
|
|
|
while(mElementsFilled.load() > 0 && samplesFilled < samplesCount)
|
|
{
|
|
int headIndex = mHead.load();
|
|
while (mBuffer[headIndex].first < mBuffer[headIndex].last && samplesFilled < samplesCount)
|
|
{
|
|
buffer[samplesFilled++] = mBuffer[headIndex].samples[mBuffer[headIndex].first++];
|
|
}
|
|
if (mBuffer[headIndex].first == mBuffer[headIndex].last) //packet depleated
|
|
{
|
|
mBuffer[headIndex].first = 0;
|
|
mBuffer[headIndex].last = 0;
|
|
mBuffer[headIndex].timestamp = 0;
|
|
mHead.store( (headIndex + 1) & (mBuffer.size() - 1) );//advance to next one
|
|
headIndex = mHead.load();
|
|
mElementsFilled.fetch_sub(1);
|
|
canWrite.notify_one();
|
|
}
|
|
}
|
|
}
|
|
return samplesFilled;
|
|
}
|
|
|
|
void Reset(uint32_t bufLength)
|
|
{
|
|
std::unique_lock<std::mutex> lck(writeLock);
|
|
std::unique_lock<std::mutex> lck2(readLock);
|
|
mBuffer.resize(bufLength);
|
|
mHead.store(0);
|
|
mTail.store(0);
|
|
mElementsFilled.store(0);
|
|
}
|
|
|
|
protected:
|
|
std::vector<SamplesPacket> mBuffer;
|
|
std::atomic<uint32_t> mHead;
|
|
std::atomic<uint32_t> mTail;
|
|
std::mutex writeLock;
|
|
std::mutex readLock;
|
|
std::atomic<uint32_t> mElementsFilled;
|
|
std::condition_variable canWrite;
|
|
std::condition_variable canRead;
|
|
};
|
|
|
|
#endif
|