2021-05-21 22:29:17 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <condition_variable>
|
|
|
|
|
#include <volk/volk.h>
|
|
|
|
|
#include <string.h>
|
2022-11-21 18:54:40 +01:00
|
|
|
#include "common/dsp/complex.h"
|
2023-01-31 21:11:17 +01:00
|
|
|
#include "dll_export.h"
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
namespace dsp
|
|
|
|
|
{
|
2023-01-31 20:40:37 +01:00
|
|
|
// Default buffer sizes
|
2023-01-31 21:11:17 +01:00
|
|
|
SATDUMP_DLL extern int STREAM_BUFFER_SIZE;
|
|
|
|
|
SATDUMP_DLL extern int RING_BUF_SZ;
|
2022-12-04 01:09:54 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Util function to create a volk aligned buffer
|
|
|
|
|
*/
|
|
|
|
|
template <typename T>
|
|
|
|
|
T *create_volk_buffer(int size, bool zero = true)
|
|
|
|
|
{
|
|
|
|
|
T *buffer = (T *)volk_malloc(size * sizeof(T), volk_get_alignment());
|
|
|
|
|
if (zero)
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
|
buffer[i] = 0;
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
class stream
|
|
|
|
|
{
|
|
|
|
|
public:
|
2021-11-16 20:19:46 +01:00
|
|
|
stream(int stream_size = STREAM_BUFFER_SIZE)
|
2021-05-21 22:29:17 +02:00
|
|
|
{
|
2022-12-04 01:09:54 +01:00
|
|
|
writeBuf = create_volk_buffer<T>(stream_size);
|
|
|
|
|
readBuf = create_volk_buffer<T>(stream_size);
|
2022-11-21 15:38:11 +01:00
|
|
|
|
|
|
|
|
for (int i = 0; i < stream_size; i++)
|
|
|
|
|
{
|
|
|
|
|
writeBuf[i] = 0;
|
|
|
|
|
readBuf[i] = 0;
|
|
|
|
|
}
|
2021-05-21 22:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~stream()
|
|
|
|
|
{
|
|
|
|
|
volk_free(writeBuf);
|
|
|
|
|
volk_free(readBuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool swap(int size)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
// Wait to either swap or stop
|
|
|
|
|
std::unique_lock<std::mutex> lck(swapMtx);
|
2022-03-28 11:51:13 +02:00
|
|
|
swapCV.wait(lck, [this]
|
|
|
|
|
{ return (canSwap || writerStop); });
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
// If writer was stopped, abandon operation
|
|
|
|
|
if (writerStop)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Swap buffers
|
|
|
|
|
dataSize = size;
|
|
|
|
|
T *temp = writeBuf;
|
|
|
|
|
writeBuf = readBuf;
|
|
|
|
|
readBuf = temp;
|
|
|
|
|
canSwap = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify reader that some data is ready
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
|
|
|
|
dataReady = true;
|
|
|
|
|
}
|
|
|
|
|
rdyCV.notify_all();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int read()
|
|
|
|
|
{
|
|
|
|
|
// Wait for data to be ready or to be stopped
|
|
|
|
|
std::unique_lock<std::mutex> lck(rdyMtx);
|
2022-03-28 11:51:13 +02:00
|
|
|
rdyCV.wait(lck, [this]
|
|
|
|
|
{ return (dataReady || readerStop); });
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
return (readerStop ? -1 : dataSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void flush()
|
|
|
|
|
{
|
|
|
|
|
// Clear data ready
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
|
|
|
|
dataReady = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify writer that buffers can be swapped
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lck(swapMtx);
|
|
|
|
|
canSwap = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swapCV.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopWriter()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lck(swapMtx);
|
|
|
|
|
writerStop = true;
|
|
|
|
|
}
|
|
|
|
|
swapCV.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void clearWriteStop()
|
|
|
|
|
{
|
|
|
|
|
writerStop = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopReader()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
|
|
|
|
readerStop = true;
|
|
|
|
|
}
|
|
|
|
|
rdyCV.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 01:09:54 +01:00
|
|
|
void clearReadStop() { readerStop = false; }
|
|
|
|
|
int getDataSize() { return dataSize; }
|
|
|
|
|
bool getReady() { return dataReady; }
|
2022-03-28 11:51:13 +02:00
|
|
|
|
2021-05-21 22:29:17 +02:00
|
|
|
T *writeBuf;
|
|
|
|
|
T *readBuf;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::mutex swapMtx;
|
|
|
|
|
std::condition_variable swapCV;
|
|
|
|
|
bool canSwap = true;
|
|
|
|
|
|
|
|
|
|
std::mutex rdyMtx;
|
|
|
|
|
std::condition_variable rdyCV;
|
|
|
|
|
bool dataReady = false;
|
|
|
|
|
|
|
|
|
|
bool readerStop = false;
|
|
|
|
|
bool writerStop = false;
|
|
|
|
|
|
|
|
|
|
int dataSize = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
class RingBuffer
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
RingBuffer()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RingBuffer(int maxLatency) { init(maxLatency); }
|
|
|
|
|
|
2022-11-27 14:18:01 +01:00
|
|
|
~RingBuffer()
|
|
|
|
|
{
|
|
|
|
|
if (size != 0)
|
|
|
|
|
delete[] _buffer;
|
|
|
|
|
size = 0;
|
|
|
|
|
}
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
void init(int maxLatency)
|
|
|
|
|
{
|
|
|
|
|
size = RING_BUF_SZ;
|
|
|
|
|
_buffer = new T[size];
|
|
|
|
|
_stopReader = false;
|
|
|
|
|
_stopWriter = false;
|
|
|
|
|
this->maxLatency = maxLatency;
|
|
|
|
|
writec = 0;
|
|
|
|
|
readc = 0;
|
|
|
|
|
readable = 0;
|
|
|
|
|
writable = size;
|
2022-03-28 11:51:13 +02:00
|
|
|
memset((void *)_buffer, 0, size * sizeof(T));
|
2021-05-21 22:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int read(T *data, int len)
|
|
|
|
|
{
|
|
|
|
|
int dataRead = 0;
|
|
|
|
|
int toRead = 0;
|
|
|
|
|
while (dataRead < len)
|
|
|
|
|
{
|
|
|
|
|
toRead = std::min<int>(waitUntilReadable(), len - dataRead);
|
|
|
|
|
if (toRead < 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if ((toRead + readc) > size)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&data[dataRead], &_buffer[readc], (size - readc) * sizeof(T));
|
|
|
|
|
memcpy(&data[dataRead + (size - readc)], &_buffer[0], (toRead - (size - readc)) * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy(&data[dataRead], &_buffer[readc], toRead * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataRead += toRead;
|
|
|
|
|
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
readable -= toRead;
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
_writable_mtx.lock();
|
|
|
|
|
writable += toRead;
|
|
|
|
|
_writable_mtx.unlock();
|
|
|
|
|
readc = (readc + toRead) % size;
|
|
|
|
|
canWriteVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int readAndSkip(T *data, int len, int skip)
|
|
|
|
|
{
|
|
|
|
|
int dataRead = 0;
|
|
|
|
|
int toRead = 0;
|
|
|
|
|
while (dataRead < len)
|
|
|
|
|
{
|
|
|
|
|
toRead = std::min<int>(waitUntilReadable(), len - dataRead);
|
|
|
|
|
if (toRead < 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if ((toRead + readc) > size)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&data[dataRead], &_buffer[readc], (size - readc) * sizeof(T));
|
|
|
|
|
memcpy(&data[dataRead + (size - readc)], &_buffer[0], (toRead - (size - readc)) * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy(&data[dataRead], &_buffer[readc], toRead * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataRead += toRead;
|
|
|
|
|
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
readable -= toRead;
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
_writable_mtx.lock();
|
|
|
|
|
writable += toRead;
|
|
|
|
|
_writable_mtx.unlock();
|
|
|
|
|
readc = (readc + toRead) % size;
|
|
|
|
|
canWriteVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
dataRead = 0;
|
|
|
|
|
while (dataRead < skip)
|
|
|
|
|
{
|
|
|
|
|
toRead = std::min<int>(waitUntilReadable(), skip - dataRead);
|
|
|
|
|
if (toRead < 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
dataRead += toRead;
|
|
|
|
|
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
readable -= toRead;
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
_writable_mtx.lock();
|
|
|
|
|
writable += toRead;
|
|
|
|
|
_writable_mtx.unlock();
|
|
|
|
|
readc = (readc + toRead) % size;
|
|
|
|
|
canWriteVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int waitUntilReadable()
|
|
|
|
|
{
|
|
|
|
|
if (_stopReader)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int _r = getReadable();
|
|
|
|
|
if (_r != 0)
|
|
|
|
|
{
|
|
|
|
|
return _r;
|
|
|
|
|
}
|
|
|
|
|
std::unique_lock<std::mutex> lck(_readable_mtx);
|
2022-03-28 11:51:13 +02:00
|
|
|
canReadVar.wait(lck, [=]()
|
|
|
|
|
{ return ((this->getReadable(false) > 0) || this->getReadStop()); });
|
2021-05-21 22:29:17 +02:00
|
|
|
if (_stopReader)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return getReadable(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getReadable(bool lock = true)
|
|
|
|
|
{
|
|
|
|
|
if (lock)
|
|
|
|
|
{
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
};
|
|
|
|
|
int _r = readable;
|
|
|
|
|
if (lock)
|
|
|
|
|
{
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
};
|
|
|
|
|
return _r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int write(T *data, int len)
|
|
|
|
|
{
|
|
|
|
|
int dataWritten = 0;
|
|
|
|
|
int toWrite = 0;
|
|
|
|
|
while (dataWritten < len)
|
|
|
|
|
{
|
|
|
|
|
toWrite = std::min<int>(waitUntilwritable(), len - dataWritten);
|
|
|
|
|
if (toWrite < 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if ((toWrite + writec) > size)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&_buffer[writec], &data[dataWritten], (size - writec) * sizeof(T));
|
|
|
|
|
memcpy(&_buffer[0], &data[dataWritten + (size - writec)], (toWrite - (size - writec)) * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy(&_buffer[writec], &data[dataWritten], toWrite * sizeof(T));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataWritten += toWrite;
|
|
|
|
|
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
readable += toWrite;
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
_writable_mtx.lock();
|
|
|
|
|
writable -= toWrite;
|
|
|
|
|
_writable_mtx.unlock();
|
|
|
|
|
writec = (writec + toWrite) % size;
|
|
|
|
|
|
|
|
|
|
canReadVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int waitUntilwritable()
|
|
|
|
|
{
|
|
|
|
|
if (_stopWriter)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int _w = getWritable();
|
|
|
|
|
if (_w != 0)
|
|
|
|
|
{
|
|
|
|
|
return _w;
|
|
|
|
|
}
|
|
|
|
|
std::unique_lock<std::mutex> lck(_writable_mtx);
|
2022-03-28 11:51:13 +02:00
|
|
|
canWriteVar.wait(lck, [=]()
|
|
|
|
|
{ return ((this->getWritable(false) > 0) || this->getWriteStop()); });
|
2021-05-21 22:29:17 +02:00
|
|
|
if (_stopWriter)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return getWritable(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getWritable(bool lock = true)
|
|
|
|
|
{
|
|
|
|
|
if (lock)
|
|
|
|
|
{
|
|
|
|
|
_writable_mtx.lock();
|
|
|
|
|
};
|
|
|
|
|
int _w = writable;
|
|
|
|
|
if (lock)
|
|
|
|
|
{
|
|
|
|
|
_writable_mtx.unlock();
|
|
|
|
|
_readable_mtx.lock();
|
|
|
|
|
};
|
|
|
|
|
int _r = readable;
|
|
|
|
|
if (lock)
|
|
|
|
|
{
|
|
|
|
|
_readable_mtx.unlock();
|
|
|
|
|
};
|
|
|
|
|
return std::max<int>(std::min<int>(_w, maxLatency - _r), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopReader()
|
|
|
|
|
{
|
|
|
|
|
_stopReader = true;
|
|
|
|
|
canReadVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopWriter()
|
|
|
|
|
{
|
|
|
|
|
_stopWriter = true;
|
|
|
|
|
canWriteVar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 01:09:54 +01:00
|
|
|
bool getReadStop() { return _stopReader; }
|
|
|
|
|
bool getWriteStop() { return _stopWriter; }
|
|
|
|
|
void clearReadStop() { _stopReader = false; }
|
|
|
|
|
void clearWriteStop() { _stopWriter = false; }
|
|
|
|
|
void setMaxLatency(int maxLatency) { this->maxLatency = maxLatency; }
|
2021-05-21 22:29:17 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
T *_buffer;
|
2022-11-27 14:18:01 +01:00
|
|
|
int size = 0;
|
2021-05-21 22:29:17 +02:00
|
|
|
int readc;
|
|
|
|
|
int writec;
|
|
|
|
|
int readable;
|
|
|
|
|
int writable;
|
|
|
|
|
int maxLatency;
|
|
|
|
|
bool _stopReader;
|
|
|
|
|
bool _stopWriter;
|
|
|
|
|
std::mutex _readable_mtx;
|
|
|
|
|
std::mutex _writable_mtx;
|
|
|
|
|
std::condition_variable canReadVar;
|
|
|
|
|
std::condition_variable canWriteVar;
|
|
|
|
|
};
|
|
|
|
|
};
|