mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-02-03 09:44:01 -05:00
SDRdaemonFEC support: interim state (1). Compile except UI. Bumped version to 2.1.0 where necessary
This commit is contained in:
parent
4386e0fbab
commit
e418e68bd9
@ -240,6 +240,7 @@ set(sdrbase_HEADERS
|
||||
sdrbase/util/export.h
|
||||
sdrbase/util/message.h
|
||||
sdrbase/util/messagequeue.h
|
||||
sdrbase/util/movingaverage.h
|
||||
sdrbase/util/prettyprint.h
|
||||
sdrbase/util/syncmessenger.h
|
||||
sdrbase/util/samplesourceserializer.h
|
||||
|
35
cmake/Modules/FindCM256.cmake
Normal file
35
cmake/Modules/FindCM256.cmake
Normal file
@ -0,0 +1,35 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_CM256 "libcm256")
|
||||
|
||||
FIND_PATH(CM256_INCLUDE_DIR
|
||||
NAMES cm256/cm256.h
|
||||
HINTS ${PC_CM256_INCLUDE_DIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
${LIBCM256_INSTALL_PREFIX}/include
|
||||
PATHS
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(CM256_LIBRARIES
|
||||
NAMES cm256 libcm256
|
||||
HINTS ${PC_CM256_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
PATHS
|
||||
${CM256_INCLUDE_DIR}/../lib
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
|
||||
if(CM256_INCLUDE_DIR AND CM256_LIBRARIES)
|
||||
set(CM256_FOUND TRUE CACHE INTERNAL "CM256 found")
|
||||
message(STATUS "Found CM256: ${CM256_INCLUDE_DIR}, ${CM256_LIBRARIES}")
|
||||
else(CM256_INCLUDE_DIR AND CM256_LIBRARIES)
|
||||
set(CM256_FOUND FALSE CACHE INTERNAL "CM256 found")
|
||||
message(STATUS "CM256 not found")
|
||||
endif(CM256_INCLUDE_DIR AND CM256_LIBRARIES)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CM256 DEFAULT_MSG CM256_LIBRARIES CM256_INCLUDE_DIR)
|
||||
MARK_AS_ADVANCED(CM256_LIBRARIES CM256_INCLUDE_DIR)
|
@ -47,7 +47,11 @@ endif(LIBUSB_FOUND AND LIBHACKRF_FOUND)
|
||||
# add_subdirectory(sdrdaemon)
|
||||
#endif(LIBNANOMSG_FOUND)
|
||||
|
||||
find_package(CM256)
|
||||
if(CM256_FOUND)
|
||||
add_subdirectory(sdrdaemonfec)
|
||||
endif(CM256_FOUND)
|
||||
|
||||
add_subdirectory(filesource)
|
||||
add_subdirectory(sdrdaemon)
|
||||
add_subdirectory(sdrdaemonfec)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
project(sdrdaemonfec)
|
||||
|
||||
find_package(LZ4)
|
||||
find_package(LibNANOMSG)
|
||||
|
||||
set(sdrdaemonfec_SOURCES
|
||||
@ -43,13 +42,13 @@ add_library(inputsdrdaemonfec SHARED
|
||||
)
|
||||
|
||||
target_include_directories(inputsdrdaemonfec PUBLIC
|
||||
${LZ4_INCLUDE_DIRS}
|
||||
${CM256_INCLUDE_DIR}
|
||||
${LIBNANOMSG_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(inputsdrdaemonfec
|
||||
${QT_LIBRARIES}
|
||||
${LZ4_LIBRARIES}
|
||||
${CM256_LIBRARIES}
|
||||
${LIBNANOMSG_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
@ -29,425 +29,268 @@ const int SDRdaemonFECBuffer::m_rawBufferLengthSeconds = 8; // should be even
|
||||
const int SDRdaemonFECBuffer::m_rawBufferMinNbFrames = 50;
|
||||
|
||||
SDRdaemonFECBuffer::SDRdaemonFECBuffer(uint32_t throttlems) :
|
||||
m_throttlemsNominal(throttlems),
|
||||
m_rawSize(0),
|
||||
m_rawBuffer(0),
|
||||
m_sampleRateStream(0),
|
||||
m_sampleRate(0),
|
||||
m_sampleBytes(2),
|
||||
m_sampleBits(12),
|
||||
m_sync(false),
|
||||
m_syncLock(false),
|
||||
m_lz4(false),
|
||||
m_nbBlocks(0),
|
||||
m_bytesInBlock(0),
|
||||
m_dataCRC(0),
|
||||
m_inCount(0),
|
||||
m_lz4InCount(0),
|
||||
m_lz4InSize(0),
|
||||
m_lz4InBuffer(0),
|
||||
m_lz4OutBuffer(0),
|
||||
m_frameSize(0),
|
||||
m_bufferLenSec(0.0),
|
||||
m_nbLz4Decodes(0),
|
||||
m_nbLz4SuccessfulDecodes(0),
|
||||
m_nbLz4CRCOK(0),
|
||||
m_nbLastLz4SuccessfulDecodes(0),
|
||||
m_nbLastLz4CRCOK(0),
|
||||
m_writeIndex(0),
|
||||
m_readIndex(0),
|
||||
m_readSize(0),
|
||||
m_readBuffer(0),
|
||||
m_autoFollowRate(false),
|
||||
m_autoCorrBuffer(false),
|
||||
m_skewTest(false),
|
||||
m_skewCorrection(false),
|
||||
m_resetIndexes(false),
|
||||
m_readCount(0),
|
||||
m_writeCount(0),
|
||||
m_nbCycles(0),
|
||||
m_nbReads(0),
|
||||
m_balCorrection(0),
|
||||
m_balCorrLimit(0)
|
||||
m_frameHead(0),
|
||||
m_decoderSlotHead(nbDecoderSlots/2),
|
||||
m_curNbBlocks(0),
|
||||
m_curNbRecovery(0),
|
||||
m_throttlemsNominal(throttlems),
|
||||
m_readIndex(0),
|
||||
m_readBuffer(0),
|
||||
m_readSize(0),
|
||||
m_bufferLenSec(0.0f)
|
||||
{
|
||||
m_currentMeta.init();
|
||||
m_framesNbBytes = nbDecoderSlots * sizeof(BufferFrame) * m_iqSampleSize;
|
||||
m_wrDeltaEstimate = m_framesNbBytes / 2;
|
||||
}
|
||||
|
||||
SDRdaemonFECBuffer::~SDRdaemonFECBuffer()
|
||||
{
|
||||
if (m_rawBuffer) {
|
||||
delete[] m_rawBuffer;
|
||||
}
|
||||
|
||||
if (m_lz4InBuffer) {
|
||||
delete[] m_lz4InBuffer;
|
||||
}
|
||||
|
||||
if (m_lz4OutBuffer) {
|
||||
delete[] m_lz4OutBuffer;
|
||||
}
|
||||
|
||||
if (m_readBuffer) {
|
||||
delete[] m_readBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::updateBufferSize(uint32_t sampleRate)
|
||||
void SDRdaemonFECBuffer::initDecoderSlotsAddresses()
|
||||
{
|
||||
uint32_t rawSize = sampleRate * m_iqSampleSize * m_rawBufferLengthSeconds; // store worth of this seconds of samples at this sample rate
|
||||
|
||||
if ((m_frameSize > 0) && (rawSize / m_frameSize < m_rawBufferMinNbFrames))
|
||||
{
|
||||
rawSize = m_rawBufferMinNbFrames * m_frameSize; // ensure a minimal size of this times the write block size so that auto follow ups work fine
|
||||
}
|
||||
|
||||
if (rawSize != m_rawSize)
|
||||
{
|
||||
m_rawSize = rawSize;
|
||||
m_balCorrLimit = sampleRate / 50; // +/- 20 ms correction max per read
|
||||
m_bufferLenSec = m_rawSize / (sampleRate * m_iqSampleSize);
|
||||
|
||||
if (m_rawBuffer) {
|
||||
delete[] m_rawBuffer;
|
||||
}
|
||||
|
||||
m_rawBuffer = new uint8_t[m_rawSize];
|
||||
resetIndexes();
|
||||
|
||||
qDebug() << "SDRdaemonBuffer::updateBufferSize:"
|
||||
<< " sampleRate: " << sampleRate
|
||||
<< " m_frameSize: " << m_frameSize
|
||||
<< " m_rawSize: " << m_rawSize;
|
||||
}
|
||||
for (int i = 0; i < nbDecoderSlots; i++)
|
||||
{
|
||||
for (int j = 0; j < nbOriginalBlocks - 1; j++)
|
||||
{
|
||||
m_decoderSlots[i].m_originalBlockPtrs[j] = &m_frames[i].m_blocks[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::updateLZ4Sizes(uint32_t frameSize)
|
||||
void SDRdaemonFECBuffer::initDecodeAllSlots()
|
||||
{
|
||||
uint32_t maxInputSize = LZ4_compressBound(frameSize);
|
||||
|
||||
if (m_lz4InBuffer) {
|
||||
delete[] m_lz4InBuffer;
|
||||
}
|
||||
|
||||
m_lz4InBuffer = new uint8_t[maxInputSize];
|
||||
|
||||
if (m_lz4OutBuffer) {
|
||||
delete[] m_lz4OutBuffer;
|
||||
}
|
||||
|
||||
m_lz4OutBuffer = new uint8_t[frameSize];
|
||||
for (int i = 0; i < nbDecoderSlots; i++)
|
||||
{
|
||||
m_decoderSlots[i].m_blockCount = 0;
|
||||
m_decoderSlots[i].m_recoveryCount = 0;
|
||||
m_decoderSlots[i].m_decoded = false;
|
||||
m_decoderSlots[i].m_blockZero.m_metaData.init();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::updateReadBufferSize(uint32_t length)
|
||||
void SDRdaemonFECBuffer::initReadIndex()
|
||||
{
|
||||
if (m_readBuffer) {
|
||||
delete[] m_readBuffer;
|
||||
}
|
||||
|
||||
m_readBuffer = new uint8_t[length];
|
||||
m_readIndex = ((m_decoderSlotHead + (nbDecoderSlots/2)) % nbDecoderSlots) * sizeof(BufferFrame);
|
||||
m_wrDeltaEstimate = m_framesNbBytes / 2;
|
||||
}
|
||||
|
||||
bool SDRdaemonFECBuffer::readMeta(char *array, uint32_t length)
|
||||
void SDRdaemonFECBuffer::initDecodeSlot(int slotIndex)
|
||||
{
|
||||
assert(length >= sizeof(MetaData) + 8);
|
||||
MetaData *metaData = (MetaData *) array;
|
||||
|
||||
if (m_crc64.calculate_crc((uint8_t *) array, sizeof(MetaData) - 8) == metaData->m_crc)
|
||||
{
|
||||
// sync condition:
|
||||
if (m_currentMeta.m_blockSize > 0)
|
||||
{
|
||||
uint32_t nbBlocks = m_currentMeta.m_nbBytes / m_currentMeta.m_blockSize;
|
||||
m_syncLock = nbBlocks + (m_lz4 ? 2 : 1) == m_nbBlocks;
|
||||
//qDebug("SDRdaemonBuffer::readMeta: m_nbBlocks: %d:%d %s", nbBlocks, m_nbBlocks, (m_syncLock ? "locked" : "unlocked"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncLock = false;
|
||||
}
|
||||
|
||||
memcpy((void *) &m_dataCRC, (const void *) &array[sizeof(MetaData)], 8);
|
||||
m_nbBlocks = 0;
|
||||
m_inCount = 0;
|
||||
|
||||
if (!m_lz4 && !(m_currentMeta == *metaData))
|
||||
{
|
||||
printMeta(QString("SDRdaemonBuffer::readMeta"), metaData);
|
||||
}
|
||||
|
||||
m_currentMeta = *metaData;
|
||||
|
||||
// sanity checks
|
||||
if (metaData->m_blockSize == m_udpPayloadSize) // sent blocksize matches given blocksize
|
||||
{
|
||||
m_sampleBytes = metaData->m_sampleBytes & 0x0F;
|
||||
uint32_t frameSize = m_iqSampleSize * metaData->m_nbSamples * metaData->m_nbBlocks;
|
||||
int sampleRate = metaData->m_sampleRate;
|
||||
|
||||
if (sampleRate != m_sampleRateStream) // change of nominal stream sample rate
|
||||
{
|
||||
updateBufferSize(sampleRate);
|
||||
m_sampleRateStream = sampleRate;
|
||||
m_sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
// auto skew rate compensation
|
||||
if (m_autoFollowRate)
|
||||
{
|
||||
if (m_skewCorrection)
|
||||
{
|
||||
int64_t deltaRate = (m_writeCount - m_readCount) / (m_nbCycles * m_rawBufferLengthSeconds * m_iqSampleSize);
|
||||
m_sampleRate = ((m_sampleRate + deltaRate) / m_iqSampleSize) * m_iqSampleSize; // ensure it is a multiple of the I/Q sample size
|
||||
resetIndexes();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
// Reset indexes if requested
|
||||
if (m_resetIndexes)
|
||||
{
|
||||
resetIndexes();
|
||||
m_resetIndexes = false;
|
||||
}
|
||||
|
||||
if (metaData->m_sampleBytes & 0x10)
|
||||
{
|
||||
m_lz4 = true;
|
||||
m_lz4InSize = metaData->m_nbBytes; // compressed input size
|
||||
m_lz4InCount = 0;
|
||||
|
||||
if (frameSize != m_frameSize)
|
||||
{
|
||||
updateLZ4Sizes(frameSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lz4 = false;
|
||||
}
|
||||
|
||||
if (frameSize != m_frameSize) {
|
||||
m_frameSize = frameSize;
|
||||
updateBufferSize(m_sampleRate);
|
||||
}
|
||||
|
||||
m_sync = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sync = false;
|
||||
}
|
||||
|
||||
return m_sync;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int pseudoWriteIndex = slotIndex * sizeof(BufferFrame);
|
||||
m_wrDeltaEstimate = pseudoWriteIndex - m_readIndex;
|
||||
// collect stats before voiding the slot
|
||||
m_curNbBlocks = m_decoderSlots[slotIndex].m_blockCount;
|
||||
m_curNbRecovery = m_decoderSlots[slotIndex].m_recoveryCount;
|
||||
m_avgNbBlocks(m_curNbBlocks);
|
||||
m_avgNbRecovery(m_curNbRecovery);
|
||||
// void the slot
|
||||
m_decoderSlots[slotIndex].m_blockCount = 0;
|
||||
m_decoderSlots[slotIndex].m_recoveryCount = 0;
|
||||
m_decoderSlots[slotIndex].m_decoded = false;
|
||||
m_decoderSlots[slotIndex].m_blockZero.m_metaData.init();
|
||||
memset((void *) m_decoderSlots[slotIndex].m_blockZero.m_samples, 0, samplesPerBlockZero * sizeof(Sample));
|
||||
memset((void *) m_frames[slotIndex].m_blocks, 0, (nbOriginalBlocks - 1) * samplesPerBlock * sizeof(Sample));
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::writeData(char *array, uint32_t length)
|
||||
{
|
||||
if ((m_sync) && (m_nbBlocks > 0))
|
||||
{
|
||||
if (m_lz4)
|
||||
{
|
||||
writeDataLZ4(array, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeToRawBufferUncompressed(array, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(length == udpSize);
|
||||
|
||||
uint8_t *SDRdaemonFECBuffer::readData(int32_t length)
|
||||
{
|
||||
// auto compensation calculations
|
||||
if (m_skewTest && ((m_readIndex + length) > (m_rawSize / 2)))
|
||||
bool dataAvailable = false;
|
||||
SuperBlock *superBlock = (SuperBlock *) array;
|
||||
int frameIndex = superBlock->header.frameIndex;
|
||||
int decoderIndex = frameIndex % nbDecoderSlots;
|
||||
|
||||
if (m_frameHead == -1) // initial state
|
||||
{
|
||||
// auto follow sample rate calculation
|
||||
int dIndex = (m_readIndex - m_writeIndex > 0 ? m_readIndex - m_writeIndex : m_writeIndex - m_readIndex); // absolute delta
|
||||
m_skewCorrection = (dIndex < m_rawSize / 10); // close by 10%
|
||||
m_nbCycles++;
|
||||
// auto R/W balance calculation
|
||||
if ((m_nbReads > 5*m_rawBufferLengthSeconds) && m_autoCorrBuffer)
|
||||
m_decoderSlotHead = decoderIndex; // new decoder slot head
|
||||
m_frameHead = frameIndex;
|
||||
initReadIndex(); // reset read index
|
||||
initDecodeAllSlots(); // initialize all slots
|
||||
}
|
||||
else
|
||||
{
|
||||
int frameDelta = m_frameHead - frameIndex;
|
||||
|
||||
if (frameDelta < 0)
|
||||
{
|
||||
int32_t dBytes;
|
||||
int32_t dI = (m_rawSize / 2) - m_readIndex; // delta of read index to the middle of buffer (positive)
|
||||
|
||||
if (m_readIndex > m_writeIndex) { // write leads
|
||||
dBytes = m_writeIndex + dI; // positive from start of buffer + delta read index
|
||||
} else { // read leads
|
||||
dBytes = m_writeIndex - (int32_t) m_rawSize + dI; // negative from end of buffer minus delta read index
|
||||
if (-frameDelta < nbDecoderSlots) // new frame head not too new
|
||||
{
|
||||
m_decoderSlotHead = decoderIndex; // new decoder slot head
|
||||
m_frameHead = frameIndex;
|
||||
dataAvailable = true;
|
||||
initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
|
||||
}
|
||||
|
||||
m_balCorrection = (m_balCorrection / 4) + ((int32_t) dBytes / (int32_t) (m_nbReads * m_iqSampleSize)); // correction is in number of samples. Alpha = 0.25
|
||||
|
||||
if (m_balCorrection < -m_balCorrLimit) {
|
||||
m_balCorrection = -m_balCorrLimit;
|
||||
} else if (m_balCorrection > m_balCorrLimit) {
|
||||
m_balCorrection = m_balCorrLimit;
|
||||
else if (-frameDelta <= sizeof(uint16_t) - nbDecoderSlots) // loss of sync start over
|
||||
{
|
||||
m_decoderSlotHead = frameIndex % nbDecoderSlots; // new decoder slot head
|
||||
decoderIndex = m_decoderSlotHead;
|
||||
m_frameHead = frameIndex;
|
||||
initReadIndex(); // reset read index
|
||||
initDecodeAllSlots(); // re-initialize all slots
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_balCorrection = 0;
|
||||
if (frameDelta > sizeof(uint16_t) - nbDecoderSlots) // new frame head not too new
|
||||
{
|
||||
m_decoderSlotHead = decoderIndex; // new decoder slot head
|
||||
m_frameHead = frameIndex;
|
||||
dataAvailable = true;
|
||||
initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
|
||||
}
|
||||
else if (frameDelta >= nbDecoderSlots) // loss of sync start over
|
||||
{
|
||||
m_decoderSlotHead = frameIndex % nbDecoderSlots; // new decoder slot head
|
||||
decoderIndex = m_decoderSlotHead;
|
||||
m_frameHead = frameIndex;
|
||||
initReadIndex(); // reset read index
|
||||
initDecodeAllSlots(); // re-initialize all slots
|
||||
}
|
||||
}
|
||||
|
||||
m_nbReads = 0;
|
||||
// un-arm
|
||||
m_skewTest = false;
|
||||
}
|
||||
|
||||
m_readCount += length;
|
||||
m_nbReads++;
|
||||
// decoderIndex should now be correctly set
|
||||
|
||||
if (m_readIndex + length < m_rawSize)
|
||||
{
|
||||
uint32_t readIndex = m_readIndex;
|
||||
m_readIndex += length;
|
||||
return &m_rawBuffer[readIndex];
|
||||
}
|
||||
else if (m_readIndex + length == m_rawSize)
|
||||
{
|
||||
uint32_t readIndex = m_readIndex;
|
||||
m_readIndex = 0;
|
||||
m_skewTest = true; // re-arm
|
||||
return &m_rawBuffer[readIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (length > m_readSize)
|
||||
{
|
||||
updateReadBufferSize(length);
|
||||
m_readSize = length;
|
||||
}
|
||||
int blockIndex = superBlock->header.blockIndex;
|
||||
int blockHead = m_decoderSlots[decoderIndex].m_blockCount;
|
||||
|
||||
std::memcpy((void *) m_readBuffer, (const void *) &m_rawBuffer[m_readIndex], m_rawSize - m_readIndex);
|
||||
length -= m_rawSize - m_readIndex;
|
||||
std::memcpy((void *) &m_readBuffer[m_rawSize - m_readIndex], (const void *) m_rawBuffer, length);
|
||||
m_readIndex = length;
|
||||
m_skewTest = true; // re-arm
|
||||
return m_readBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::writeDataLZ4(const char *array, uint32_t length)
|
||||
{
|
||||
if (m_lz4InCount + length < m_lz4InSize)
|
||||
if (blockHead < nbOriginalBlocks) // not enough blocks to decode -> store data
|
||||
{
|
||||
std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, length);
|
||||
m_lz4InCount += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, m_lz4InSize - m_lz4InCount); // copy rest of data in compressed Buffer
|
||||
m_lz4InCount += length;
|
||||
}
|
||||
|
||||
if (m_lz4InCount >= m_lz4InSize) // full input compressed block retrieved
|
||||
{
|
||||
if (m_nbLz4Decodes == 100)
|
||||
if (blockIndex == 0) // first block with meta
|
||||
{
|
||||
qDebug() << "SDRdaemonBuffer::writeAndReadLZ4:"
|
||||
<< " decoding: " << m_nbLz4CRCOK
|
||||
<< ":" << m_nbLz4SuccessfulDecodes
|
||||
<< "/" << m_nbLz4Decodes;
|
||||
|
||||
m_nbLastLz4SuccessfulDecodes = m_nbLz4SuccessfulDecodes;
|
||||
m_nbLastLz4CRCOK = m_nbLz4CRCOK;
|
||||
m_nbLz4Decodes = 0;
|
||||
m_nbLz4SuccessfulDecodes = 0;
|
||||
m_nbLz4CRCOK = 0;
|
||||
SuperBlockZero *superBlockZero = (SuperBlockZero *) array;
|
||||
m_decoderSlots[decoderIndex].m_blockZero = superBlockZero->protectedBlock;
|
||||
m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_decoderSlots[decoderIndex].m_blockZero;
|
||||
memcpy((void *) m_frames[decoderIndex].m_blockZero.m_samples,
|
||||
(const void *) m_decoderSlots[decoderIndex].m_blockZero.m_samples,
|
||||
samplesPerBlockZero * sizeof(Sample));
|
||||
}
|
||||
else if (blockIndex < nbOriginalBlocks) // normal block
|
||||
{
|
||||
m_frames[decoderIndex].m_blocks[blockIndex - 1] = superBlock->protectedBlock;
|
||||
m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_frames[decoderIndex].m_blocks[blockIndex - 1];
|
||||
}
|
||||
else // redundancy block
|
||||
{
|
||||
m_decoderSlots[decoderIndex].m_recoveryBlocks[m_decoderSlots[decoderIndex].m_recoveryCount] = superBlock->protectedBlock;
|
||||
m_decoderSlots[decoderIndex].m_recoveryCount++;
|
||||
}
|
||||
|
||||
writeToRawBufferLZ4();
|
||||
m_lz4InCount = 0;
|
||||
m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Index = blockIndex;
|
||||
m_decoderSlots[decoderIndex].m_blockCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::writeToRawBufferLZ4()
|
||||
{
|
||||
uint64_t crc64 = m_crc64.calculate_crc(m_lz4InBuffer, m_lz4InSize);
|
||||
|
||||
if (memcmp(&crc64, &m_dataCRC, 8) == 0)
|
||||
else if (!m_decoderSlots[decoderIndex].m_decoded) // ready to decode
|
||||
{
|
||||
m_nbLz4CRCOK++;
|
||||
if (m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks < 0) // block zero has not been received
|
||||
{
|
||||
m_paramsCM256.RecoveryCount = m_currentMeta.m_nbFECBlocks; // take last value for number of FEC blocks
|
||||
}
|
||||
else
|
||||
{
|
||||
m_paramsCM256.RecoveryCount = m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks;
|
||||
}
|
||||
|
||||
if (m_decoderSlots[decoderIndex].m_recoveryCount > 0) // recovery data used
|
||||
{
|
||||
if (cm256_decode(m_paramsCM256, m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks)) // failure to decode
|
||||
{
|
||||
qDebug("SDRdaemonFECBuffer::writeAndRead: CM256 decode error");
|
||||
}
|
||||
else // success to decode
|
||||
{
|
||||
int nbOriginalBlocks = m_decoderSlots[decoderIndex].m_blockCount - m_decoderSlots[decoderIndex].m_recoveryCount;
|
||||
|
||||
for (int ir = 0; ir < m_decoderSlots[decoderIndex].m_recoveryCount; ir++) // recover lost blocks
|
||||
{
|
||||
int blockIndex = m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[nbOriginalBlocks+ir].Index;
|
||||
|
||||
if (blockIndex == 0)
|
||||
{
|
||||
ProtectedBlockZero *recoveredBlockZero = (ProtectedBlockZero *) &m_decoderSlots[decoderIndex].m_recoveryBlocks[ir];
|
||||
m_decoderSlots[decoderIndex].m_blockZero.m_metaData = recoveredBlockZero->m_metaData;
|
||||
memcpy((void *) m_frames[decoderIndex].m_blockZero.m_samples,
|
||||
(const void *) recoveredBlockZero->m_samples,
|
||||
samplesPerBlockZero * sizeof(Sample));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frames[decoderIndex].m_blocks[blockIndex - 1] = m_decoderSlots[decoderIndex].m_recoveryBlocks[ir];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks >= 0) // meta data valid
|
||||
{
|
||||
if (!(m_decoderSlots[decoderIndex].m_blockZero.m_metaData == m_currentMeta))
|
||||
{
|
||||
int sampleRate = m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_sampleRate;
|
||||
|
||||
if (sampleRate > 0) {
|
||||
m_bufferLenSec = (float) m_framesNbBytes / (float) sampleRate;
|
||||
}
|
||||
|
||||
printMeta("SDRdaemonFECBuffer::writeData", &m_decoderSlots[decoderIndex].m_blockZero.m_metaData); // print for change other than timestamp
|
||||
}
|
||||
|
||||
m_currentMeta = m_decoderSlots[decoderIndex].m_blockZero.m_metaData; // renew current meta
|
||||
}
|
||||
|
||||
m_decoderSlots[decoderIndex].m_decoded = true;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
uint8_t *SDRdaemonFECBuffer::readData(int32_t length)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *) m_frames;
|
||||
uint32_t readIndex = m_readIndex;
|
||||
|
||||
if (m_readIndex + length < m_framesNbBytes) // ends before buffer bound
|
||||
{
|
||||
return;
|
||||
m_readIndex += length;
|
||||
return &buffer[readIndex];
|
||||
}
|
||||
else if (m_readIndex + length == m_framesNbBytes) // ends at buffer bound
|
||||
{
|
||||
m_readIndex = 0;
|
||||
return &buffer[readIndex];
|
||||
}
|
||||
else // ends after buffer bound
|
||||
{
|
||||
if (length > m_readSize) // reallocate composition buffer if necessary
|
||||
{
|
||||
if (m_readBuffer) {
|
||||
delete[] m_readBuffer;
|
||||
}
|
||||
|
||||
int compressedSize = LZ4_decompress_fast((const char*) m_lz4InBuffer, (char*) m_lz4OutBuffer, m_frameSize);
|
||||
m_nbLz4Decodes++;
|
||||
m_readBuffer = new uint8_t[length];
|
||||
m_readSize = length;
|
||||
}
|
||||
|
||||
if (compressedSize == m_lz4InSize)
|
||||
{
|
||||
m_nbLz4SuccessfulDecodes++;
|
||||
writeToRawBufferUncompressed((const char *) m_lz4OutBuffer, m_frameSize);
|
||||
}
|
||||
std::memcpy((void *) m_readBuffer, (const void *) &buffer[m_readIndex], m_framesNbBytes - m_readIndex); // copy end of buffer
|
||||
length -= m_framesNbBytes - m_readIndex;
|
||||
std::memcpy((void *) &m_readBuffer[m_framesNbBytes - m_readIndex], (const void *) buffer, length); // copy start of buffer
|
||||
m_readIndex = length;
|
||||
return m_readBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::writeToRawBufferUncompressed(const char *array, uint32_t length)
|
||||
{
|
||||
// TODO: handle the 1 byte per I or Q sample
|
||||
if (m_writeIndex + length < m_rawSize)
|
||||
{
|
||||
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length);
|
||||
m_writeIndex += length;
|
||||
}
|
||||
else if (m_writeIndex + length == m_rawSize)
|
||||
{
|
||||
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length);
|
||||
m_writeIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, m_rawSize - m_writeIndex);
|
||||
length -= m_rawSize - m_writeIndex;
|
||||
std::memcpy((void *) m_rawBuffer, (const void *) &array[m_rawSize - m_writeIndex], length);
|
||||
m_writeIndex = length;
|
||||
}
|
||||
|
||||
m_writeCount += length;
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::resetIndexes()
|
||||
{
|
||||
m_writeIndex = 0;
|
||||
m_readIndex = m_rawSize / 2;
|
||||
m_readCount = 0;
|
||||
m_writeCount = 0;
|
||||
m_nbCycles = 0;
|
||||
m_skewTest = false;
|
||||
m_skewCorrection = false;
|
||||
m_nbReads = 0;
|
||||
m_balCorrection = 0;
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::updateBlockCounts(uint32_t nbBytesReceived)
|
||||
{
|
||||
m_nbBlocks += m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? 1 : 0;
|
||||
m_bytesInBlock = m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? nbBytesReceived : m_bytesInBlock + nbBytesReceived;
|
||||
}
|
||||
|
||||
void SDRdaemonFECBuffer::printMeta(const QString& header, MetaData *metaData)
|
||||
void SDRdaemonFECBuffer::printMeta(const QString& header, MetaDataFEC *metaData)
|
||||
{
|
||||
qDebug() << header << ": "
|
||||
<< "|" << metaData->m_centerFrequency
|
||||
<< ":" << metaData->m_sampleRate
|
||||
<< ":" << (int) (metaData->m_sampleBytes & 0xF)
|
||||
<< ":" << (int) metaData->m_sampleBits
|
||||
<< ":" << metaData->m_blockSize
|
||||
<< ":" << metaData->m_nbSamples
|
||||
<< "||" << metaData->m_nbBlocks
|
||||
<< ":" << metaData->m_nbBytes
|
||||
<< "|" << metaData->m_tv_sec
|
||||
<< ":" << metaData->m_tv_usec;
|
||||
<< "|" << metaData->m_centerFrequency
|
||||
<< ":" << metaData->m_sampleRate
|
||||
<< ":" << (int) (metaData->m_sampleBytes & 0xF)
|
||||
<< ":" << (int) metaData->m_sampleBits
|
||||
<< ":" << (int) metaData->m_nbOriginalBlocks
|
||||
<< ":" << (int) metaData->m_nbFECBlocks
|
||||
<< "|" << metaData->m_tv_sec
|
||||
<< ":" << metaData->m_tv_usec
|
||||
<< "|";
|
||||
}
|
||||
|
||||
|
@ -19,70 +19,100 @@
|
||||
|
||||
#include <QString>
|
||||
#include <cstdlib>
|
||||
#include "cm256.h"
|
||||
#include "util/movingaverage.h"
|
||||
|
||||
#include "util/CRC64.h"
|
||||
|
||||
#define SDRDAEMONFEC_UDPSIZE 512 // UDP payload size
|
||||
#define SDRDAEMONFEC_NBORIGINALBLOCKS 128 // number of sample blocks per frame excluding FEC blocks
|
||||
#define SDRDAEMONFEC_NBDECODERSLOTS 4 // power of two sub multiple of uint16_t size. A too large one is superfluous.
|
||||
|
||||
class SDRdaemonFECBuffer
|
||||
{
|
||||
public:
|
||||
public:
|
||||
#pragma pack(push, 1)
|
||||
struct MetaData
|
||||
{
|
||||
// critical data
|
||||
uint32_t m_centerFrequency; //!< center frequency in kHz
|
||||
uint32_t m_sampleRate; //!< sample rate in Hz
|
||||
uint8_t m_sampleBytes; //!< MSB(4): indicators, LSB(4) number of bytes per sample
|
||||
uint8_t m_sampleBits; //!< number of effective bits per sample
|
||||
uint16_t m_blockSize; //!< payload size
|
||||
uint32_t m_nbSamples; //!< number of samples in a hardware block
|
||||
// end of critical data
|
||||
uint16_t m_nbBlocks; //!< number of hardware blocks in the frame
|
||||
uint32_t m_nbBytes; //!< total number of bytes in the frame
|
||||
uint32_t m_tv_sec; //!< seconds of timestamp at start time of frame processing
|
||||
uint32_t m_tv_usec; //!< microseconds of timestamp at start time of frame processing
|
||||
uint64_t m_crc; //!< 64 bit CRC of the above
|
||||
struct MetaDataFEC
|
||||
{
|
||||
uint32_t m_centerFrequency; //!< 4 center frequency in kHz
|
||||
uint32_t m_sampleRate; //!< 8 sample rate in Hz
|
||||
uint8_t m_sampleBytes; //!< 9 MSB(4): indicators, LSB(4) number of bytes per sample
|
||||
uint8_t m_sampleBits; //!< 10 number of effective bits per sample
|
||||
uint8_t m_nbOriginalBlocks; //!< 11 number of blocks with original (protected) data
|
||||
uint8_t m_nbFECBlocks; //!< 12 number of blocks carrying FEC
|
||||
uint32_t m_tv_sec; //!< 16 seconds of timestamp at start time of super-frame processing
|
||||
uint32_t m_tv_usec; //!< 20 microseconds of timestamp at start time of super-frame processing
|
||||
|
||||
bool operator==(const MetaData& rhs)
|
||||
{
|
||||
return (memcmp((const void *) this, (const void *) &rhs, 20) == 0); // Only the 20 first bytes are relevant (critical)
|
||||
}
|
||||
bool operator==(const MetaDataFEC& rhs)
|
||||
{
|
||||
return (memcmp((const void *) this, (const void *) &rhs, 12) == 0); // Only the 12 first bytes are relevant
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
memset((void *) this, 0, sizeof(MetaData));
|
||||
}
|
||||
void init()
|
||||
{
|
||||
memset((void *) this, 0, sizeof(MetaDataFEC));
|
||||
m_nbFECBlocks = -1;
|
||||
}
|
||||
};
|
||||
|
||||
void operator=(const MetaData& rhs)
|
||||
{
|
||||
memcpy((void *) this, (const void *) &rhs, sizeof(MetaData));
|
||||
}
|
||||
};
|
||||
struct Sample
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t q;
|
||||
};
|
||||
|
||||
struct Header
|
||||
{
|
||||
uint16_t frameIndex;
|
||||
uint8_t blockIndex;
|
||||
uint8_t filler;
|
||||
};
|
||||
|
||||
static const int samplesPerBlock = (SDRDAEMONFEC_UDPSIZE - sizeof(Header)) / sizeof(Sample);
|
||||
static const int samplesPerBlockZero = samplesPerBlock - (sizeof(MetaDataFEC) / sizeof(Sample));
|
||||
|
||||
struct ProtectedBlock
|
||||
{
|
||||
Sample samples[samplesPerBlock];
|
||||
};
|
||||
|
||||
struct SuperBlock
|
||||
{
|
||||
Header header;
|
||||
ProtectedBlock protectedBlock;
|
||||
};
|
||||
|
||||
struct ProtectedBlockZero
|
||||
{
|
||||
MetaDataFEC m_metaData;
|
||||
Sample m_samples[samplesPerBlockZero];
|
||||
};
|
||||
|
||||
struct SuperBlockZero
|
||||
{
|
||||
Header header;
|
||||
ProtectedBlockZero protectedBlock;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
SDRdaemonFECBuffer(uint32_t throttlems);
|
||||
~SDRdaemonFECBuffer();
|
||||
|
||||
bool readMeta(char *array, uint32_t length); //!< Attempt to read meta. Returns true if meta block
|
||||
// R/W operations
|
||||
void writeData(char *array, uint32_t length); //!< Write data into buffer.
|
||||
uint8_t *readData(int32_t length);
|
||||
void updateBlockCounts(uint32_t nbBytesReceived);
|
||||
uint8_t *readData(int32_t length); //!< Read data from buffer
|
||||
|
||||
const MetaData& getCurrentMeta() const { return m_currentMeta; }
|
||||
uint32_t getSampleRateStream() const { return m_sampleRateStream; }
|
||||
uint32_t getSampleRate() const { return m_sampleRate; }
|
||||
bool isSync() const { return m_sync; }
|
||||
bool isSyncLocked() const { return m_syncLock; }
|
||||
uint32_t getFrameSize() const { return m_frameSize; }
|
||||
bool isLz4Compressed() const { return m_lz4; }
|
||||
float getCompressionRatio() const { return (m_frameSize > 0 ? (float) m_lz4InSize / (float) m_frameSize : 1.0); }
|
||||
uint32_t getLz4DataCRCOK() const { return m_nbLastLz4CRCOK; }
|
||||
uint32_t getLz4SuccessfulDecodes() const { return m_nbLastLz4SuccessfulDecodes; }
|
||||
float getBufferLengthInSecs() const { return m_bufferLenSec; }
|
||||
void setAutoFollowRate(bool autoFollowRate) { m_autoFollowRate = autoFollowRate; }
|
||||
void setAutoCorrBuffer(bool autoCorrBuffer) { m_autoCorrBuffer = autoCorrBuffer; }
|
||||
void setResetIndexes() { m_resetIndexes = true; }
|
||||
int32_t getRWBalanceCorrection() const { return m_balCorrection; }
|
||||
// meta data
|
||||
const MetaDataFEC& getCurrentMeta() const { return m_currentMeta; }
|
||||
const MetaDataFEC& getOutputMeta() const { return m_outputMeta; }
|
||||
|
||||
// stats
|
||||
int getCurNbBlocks() const { return m_curNbBlocks; }
|
||||
int getCurNbRecovery() const { return m_curNbRecovery; }
|
||||
float getAvgNbBlocks() const { return m_avgNbBlocks; }
|
||||
float getAvgNbRecovery() const { return m_avgNbRecovery; }
|
||||
|
||||
|
||||
float getBufferLengthInSecs() const { return m_bufferLenSec; }
|
||||
|
||||
/** Get buffer gauge value in % of buffer size ([-50:50])
|
||||
* [-50:0] : write leads or read lags
|
||||
@ -90,9 +120,9 @@ public:
|
||||
*/
|
||||
inline int32_t getBufferGauge() const
|
||||
{
|
||||
if (m_rawSize)
|
||||
if (m_framesNbBytes)
|
||||
{
|
||||
int32_t val = ((m_writeIndex - m_readIndex) * 100) / (int32_t) m_rawSize;
|
||||
int32_t val = (m_wrDeltaEstimate * 100) / (int32_t) m_framesNbBytes;
|
||||
|
||||
if (val < -50) {
|
||||
return val + 100; // read leads (positive)
|
||||
@ -115,63 +145,61 @@ public:
|
||||
static const int m_rawBufferMinNbFrames; //!< Minimum number of frames for the length of buffer
|
||||
|
||||
private:
|
||||
void updateBufferSize(uint32_t sampleRate);
|
||||
void updateLZ4Sizes(uint32_t frameSize);
|
||||
void updateReadBufferSize(uint32_t length);
|
||||
void writeDataLZ4(const char *array, uint32_t length);
|
||||
void writeToRawBufferLZ4();
|
||||
void writeToRawBufferUncompressed(const char *array, uint32_t length);
|
||||
void resetIndexes();
|
||||
static const int udpSize = SDRDAEMONFEC_UDPSIZE;
|
||||
static const int nbOriginalBlocks = SDRDAEMONFEC_NBORIGINALBLOCKS;
|
||||
static const int nbDecoderSlots = SDRDAEMONFEC_NBDECODERSLOTS;
|
||||
|
||||
static void printMeta(const QString& header, MetaData *metaData);
|
||||
#pragma pack(push, 1)
|
||||
struct BufferBlockZero
|
||||
{
|
||||
Sample m_samples[samplesPerBlockZero];
|
||||
};
|
||||
|
||||
struct BufferFrame
|
||||
{
|
||||
BufferBlockZero m_blockZero;
|
||||
ProtectedBlock m_blocks[nbOriginalBlocks - 1];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct DecoderSlot
|
||||
{
|
||||
ProtectedBlockZero m_blockZero;
|
||||
ProtectedBlock* m_originalBlockPtrs[nbOriginalBlocks];
|
||||
ProtectedBlock m_recoveryBlocks[nbOriginalBlocks]; // max size
|
||||
cm256_block m_cm256DescriptorBlocks[nbOriginalBlocks];
|
||||
int m_blockCount; //!< total number of blocks received for this frame
|
||||
int m_recoveryCount; //!< number of recovery blocks received
|
||||
bool m_decoded; //!< true if decoded
|
||||
};
|
||||
|
||||
MetaDataFEC m_currentMeta; //!< Stored current meta data
|
||||
MetaDataFEC m_outputMeta; //!< Meta data corresponding to currently served frame
|
||||
cm256_encoder_params m_paramsCM256; //!< CM256 decoder parameters block
|
||||
DecoderSlot m_decoderSlots[nbDecoderSlots]; //!< CM256 decoding control/buffer slots
|
||||
BufferFrame m_frames[nbDecoderSlots]; //!< Samples buffer
|
||||
int m_framesNbBytes; //!< Number of bytes in samples buffer
|
||||
int m_decoderSlotHead; //!< index of the current head frame slot in decoding slots
|
||||
int m_frameHead; //!< index of the current head frame sent
|
||||
int m_curNbBlocks; //!< (stats) instantaneous number of blocks received
|
||||
int m_curNbRecovery; //!< (stats) instantaneous number of recovery blocks used
|
||||
MovingAverage<int, int, 10> m_avgNbBlocks; //!< (stats) average number of blocks received
|
||||
MovingAverage<int, int, 10> m_avgNbRecovery; //!< (stats) average number of recovery blocks used
|
||||
int m_readIndex; //!< current byte read index in frames buffer
|
||||
int m_wrDeltaEstimate; //!< Sampled estimate of write to read indexes difference
|
||||
|
||||
uint32_t m_throttlemsNominal; //!< Initial throttle in ms
|
||||
uint32_t m_rawSize; //!< Size of the raw samples buffer in bytes
|
||||
uint8_t *m_rawBuffer; //!< Buffer for raw samples obtained from UDP (I/Q not in a formal I/Q structure)
|
||||
uint32_t m_sampleRateStream; //!< Current sample rate from the stream meta data
|
||||
uint32_t m_sampleRate; //!< Current actual sample rate in Hz
|
||||
uint8_t m_sampleBytes; //!< Current number of bytes per I or Q sample
|
||||
uint8_t m_sampleBits; //!< Current number of effective bits per sample
|
||||
uint8_t* m_readBuffer; //!< Read buffer to hold samples when looping back to beginning of raw buffer
|
||||
uint32_t m_readSize; //!< Read buffer size
|
||||
|
||||
bool m_sync; //!< Meta data acquired
|
||||
bool m_syncLock; //!< Meta data expected (Stream synchronized)
|
||||
bool m_lz4; //!< Stream is compressed with LZ4
|
||||
MetaData m_currentMeta; //!< Stored current meta data
|
||||
CRC64 m_crc64; //!< CRC64 calculator
|
||||
uint32_t m_nbBlocks; //!< Number of UDP blocks received in the current frame
|
||||
uint32_t m_bytesInBlock; //!< Number of bytes received in the current UDP block
|
||||
uint64_t m_dataCRC; //!< CRC64 of the data block
|
||||
uint32_t m_inCount; //!< Current position of uncompressed input
|
||||
uint32_t m_lz4InCount; //!< Current position in LZ4 input buffer
|
||||
uint32_t m_lz4InSize; //!< Size in bytes of the LZ4 input data
|
||||
uint8_t *m_lz4InBuffer; //!< Buffer for LZ4 compressed input
|
||||
uint8_t *m_lz4OutBuffer; //!< Buffer for LZ4 uncompressed output
|
||||
uint32_t m_frameSize; //!< Size in bytes of one uncompressed frame
|
||||
float m_bufferLenSec; //!< Raw buffer length in seconds
|
||||
uint32_t m_nbLz4Decodes;
|
||||
uint32_t m_nbLz4SuccessfulDecodes;
|
||||
uint32_t m_nbLz4CRCOK;
|
||||
uint32_t m_nbLastLz4SuccessfulDecodes;
|
||||
uint32_t m_nbLastLz4CRCOK;
|
||||
float m_bufferLenSec;
|
||||
|
||||
int32_t m_writeIndex; //!< Current write position in the raw samples buffer
|
||||
int32_t m_readIndex; //!< Current read position in the raw samples buffer
|
||||
uint32_t m_readSize; //!< Read buffer size
|
||||
uint8_t *m_readBuffer; //!< Read buffer to hold samples when looping back to beginning of raw buffer
|
||||
void initDecoderSlotsAddresses();
|
||||
void initDecodeAllSlots();
|
||||
void initReadIndex();
|
||||
void initDecodeSlot(int slotIndex);
|
||||
|
||||
bool m_autoFollowRate; //!< Auto follow stream sample rate else stick with meta data sample rate
|
||||
bool m_autoCorrBuffer; //!< Auto correct buffer read / write balance (attempt to ...)
|
||||
bool m_skewTest;
|
||||
bool m_skewCorrection; //!< Do a skew rate correction at next meta data reception
|
||||
bool m_resetIndexes; //!< Do a reset indexes at next meta data reception
|
||||
|
||||
int64_t m_readCount; //!< Number of bytes read for auto skew compensation
|
||||
int64_t m_writeCount; //!< Number of bytes written for auto skew compensation
|
||||
uint32_t m_nbCycles; //!< Number of buffer cycles since start of auto skew compensation byte counting
|
||||
|
||||
uint32_t m_nbReads; //!< Number of buffer reads since start of auto R/W balance correction period
|
||||
int32_t m_balCorrection; //!< R/W balance correction in number of samples
|
||||
int32_t m_balCorrLimit; //!< Correction absolute value limit in number of samples
|
||||
static void printMeta(const QString& header, MetaDataFEC *metaData);
|
||||
};
|
||||
|
||||
|
||||
|
@ -303,24 +303,24 @@ bool SDRdaemonFECGui::handleMessage(const Message& message)
|
||||
updateWithStreamData();
|
||||
return true;
|
||||
}
|
||||
else if (SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming::match(message))
|
||||
else if (SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming::match(message))
|
||||
{
|
||||
m_startingTimeStamp.tv_sec = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_sec();
|
||||
m_startingTimeStamp.tv_usec = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_usec();
|
||||
m_syncLocked = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getSyncLock();
|
||||
m_frameSize = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getFrameSize();
|
||||
m_lz4 = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4Compression();
|
||||
m_startingTimeStamp.tv_sec = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).get_tv_sec();
|
||||
m_startingTimeStamp.tv_usec = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).get_tv_usec();
|
||||
m_syncLocked = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getSyncLock();
|
||||
m_frameSize = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getFrameSize();
|
||||
m_lz4 = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4Compression();
|
||||
|
||||
if (m_lz4) {
|
||||
m_compressionRatio = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4CompressionRatio();
|
||||
m_compressionRatio = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4CompressionRatio();
|
||||
} else {
|
||||
m_compressionRatio = 1.0;
|
||||
}
|
||||
|
||||
m_nbLz4DataCRCOK = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4DataCRCOK();
|
||||
m_nbLz4SuccessfulDecodes = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4SuccessfulDecodes();
|
||||
m_bufferLengthInSecs = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getBufferLengthInSecs();
|
||||
m_bufferGauge = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getBufferGauge();
|
||||
m_nbLz4DataCRCOK = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4DataCRCOK();
|
||||
m_nbLz4SuccessfulDecodes = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4SuccessfulDecodes();
|
||||
m_bufferLengthInSecs = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getBufferLengthInSecs();
|
||||
m_bufferGauge = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getBufferGauge();
|
||||
|
||||
updateWithStreamTime();
|
||||
return true;
|
||||
|
@ -304,12 +304,12 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="streamLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="streamLocked">
|
||||
<widget class="QToolButton" name="framesComplete">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream locked status i.e. synced with meta data</string>
|
||||
<string>Frames complete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
@ -320,58 +320,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="frameSizeText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frame size in kB</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0000</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sampleRateStreamText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>65</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Nominal sample rate from stream meta data (kS/s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>00000.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sampleRateText">
|
||||
<property name="minimumSize">
|
||||
@ -392,33 +340,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="skewRateText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sample rate skew from stream nominal rate (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-00.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream4">
|
||||
<widget class="Line" name="lineStream1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
@ -443,6 +365,74 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="curNbBlocksText">
|
||||
<property name="toolTip">
|
||||
<string>Total number of blocks retrieved per frame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>128</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="avgNbBlocksText">
|
||||
<property name="toolTip">
|
||||
<string>Average number of blocks retrieved per frame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>000.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="curNbRecoveryText">
|
||||
<property name="toolTip">
|
||||
<string>Number of recovery blocks used per frame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineStream5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="avgNbRecoveryText">
|
||||
<property name="toolTip">
|
||||
<string>Average number of recovery blocks used per frame</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>000.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -465,177 +455,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lz4Layout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="lz4Compressed">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream is compressed with LZ4</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrbase/resources/res.qrc">
|
||||
<normaloff>:/compressed.png</normaloff>:/compressed.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="compressionRatioText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stream comopression ratio (compressed / original)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz41">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="dataCRCOKText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data CRC OK (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz42">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lz4DecodesOKText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>LZ4 successful decodes (%)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineLz43">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="bufferLenSecsText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Main buffer lenth in seconds</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>00.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetIndexes">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset buffer indexes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrbase/resources/res.qrc">
|
||||
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="autoCorrBuffer">
|
||||
<property name="toolTip">
|
||||
<string>Auto maintain buffer read / write balance</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="autoFollowRate">
|
||||
<property name="toolTip">
|
||||
<string>Auto follow actual stream sample rate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>R</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
|
@ -37,7 +37,7 @@ MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgConfigureSDRdaemonResetIndexes, M
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgConfigureSDRdaemonStreamTiming, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonAcquisition, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonStreamData, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming, Message)
|
||||
|
||||
SDRdaemonFECInput::SDRdaemonFECInput(const QTimer& masterTimer, DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
|
@ -200,77 +200,65 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportSDRdaemonStreamTiming : public Message {
|
||||
class MsgReportSDRdaemonFECStreamTiming : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
uint32_t get_tv_sec() const { return m_tv_sec; }
|
||||
uint32_t get_tv_usec() const { return m_tv_usec; }
|
||||
bool getSyncLock() const { return m_syncLock; }
|
||||
uint32_t getFrameSize() const { return m_frameSize; }
|
||||
float getBufferLengthInSecs() const { return m_bufferLenSec; }
|
||||
bool getLz4Compression() const { return m_lz4; }
|
||||
float getLz4CompressionRatio() const { return m_compressionRatio; }
|
||||
uint32_t getLz4DataCRCOK() const { return m_nbLz4CRCOK; }
|
||||
uint32_t getLz4SuccessfulDecodes() const { return m_nbLz4SuccessfulDecodes; }
|
||||
int32_t getBufferGauge() const { return m_bufferGauge; }
|
||||
int getCurNbBlocks() const { return m_curNbBlocks; }
|
||||
int getCurNbRecovery() const { return m_curNbRecovery; }
|
||||
float getAvgNbBlocks() const { return m_avgNbBlocks; }
|
||||
float getAvgNbRecovery() const { return m_avgNbRecovery; }
|
||||
|
||||
static MsgReportSDRdaemonStreamTiming* create(uint32_t tv_sec,
|
||||
static MsgReportSDRdaemonFECStreamTiming* create(uint32_t tv_sec,
|
||||
uint32_t tv_usec,
|
||||
bool syncLock,
|
||||
uint32_t frameSize,
|
||||
float bufferLenSec,
|
||||
bool lz4,
|
||||
float compressionRatio,
|
||||
uint32_t nbLz4CRCOK,
|
||||
uint32_t nbLz4SuccessfulDecodes,
|
||||
int32_t bufferGauge)
|
||||
int32_t bufferGauge,
|
||||
int curNbBlocks,
|
||||
int curNbRecovery,
|
||||
float avgNbBlocks,
|
||||
float avgNbRecovery)
|
||||
{
|
||||
return new MsgReportSDRdaemonStreamTiming(tv_sec,
|
||||
return new MsgReportSDRdaemonFECStreamTiming(tv_sec,
|
||||
tv_usec,
|
||||
syncLock,
|
||||
frameSize,
|
||||
bufferLenSec,
|
||||
lz4,
|
||||
compressionRatio,
|
||||
nbLz4CRCOK,
|
||||
nbLz4SuccessfulDecodes,
|
||||
bufferGauge);
|
||||
bufferGauge,
|
||||
curNbBlocks,
|
||||
curNbRecovery,
|
||||
avgNbBlocks,
|
||||
avgNbRecovery);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t m_tv_sec;
|
||||
uint32_t m_tv_usec;
|
||||
bool m_syncLock;
|
||||
uint32_t m_frameSize;
|
||||
float m_bufferLenSec;
|
||||
bool m_lz4;
|
||||
float m_compressionRatio;
|
||||
uint32_t m_nbLz4CRCOK;
|
||||
uint32_t m_nbLz4SuccessfulDecodes;
|
||||
int32_t m_bufferGauge;
|
||||
float m_bufferLenSec;
|
||||
int32_t m_bufferGauge;
|
||||
int m_curNbBlocks;
|
||||
int m_curNbRecovery;
|
||||
float m_avgNbBlocks;
|
||||
float m_avgNbRecovery;
|
||||
|
||||
MsgReportSDRdaemonStreamTiming(uint32_t tv_sec,
|
||||
MsgReportSDRdaemonFECStreamTiming(uint32_t tv_sec,
|
||||
uint32_t tv_usec,
|
||||
bool syncLock,
|
||||
uint32_t frameSize,
|
||||
float bufferLenSec,
|
||||
bool lz4,
|
||||
float compressionRatio,
|
||||
uint32_t nbLz4CRCOK,
|
||||
uint32_t nbLz4SuccessfulDecodes,
|
||||
int32_t bufferGauge) :
|
||||
int32_t bufferGauge,
|
||||
int curNbBlocks,
|
||||
int curNbRecovery,
|
||||
float avgNbBlocks,
|
||||
float avgNbRecovery) :
|
||||
Message(),
|
||||
m_tv_sec(tv_sec),
|
||||
m_tv_usec(tv_usec),
|
||||
m_syncLock(syncLock),
|
||||
m_frameSize(frameSize),
|
||||
m_bufferLenSec(bufferLenSec),
|
||||
m_lz4(lz4),
|
||||
m_compressionRatio(compressionRatio),
|
||||
m_nbLz4CRCOK(nbLz4CRCOK),
|
||||
m_nbLz4SuccessfulDecodes(nbLz4SuccessfulDecodes),
|
||||
m_bufferGauge(bufferGauge)
|
||||
m_bufferGauge(bufferGauge),
|
||||
m_curNbBlocks(curNbBlocks),
|
||||
m_curNbRecovery(curNbRecovery),
|
||||
m_avgNbBlocks(avgNbBlocks),
|
||||
m_avgNbRecovery(avgNbRecovery)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
const PluginDescriptor SDRdaemonFECPlugin::m_pluginDescriptor = {
|
||||
QString("SDRdaemon input"),
|
||||
QString("2.0.0"),
|
||||
QString("2.1.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
@ -133,60 +133,16 @@ void SDRdaemonFECUDPHandler::dataReadyRead()
|
||||
{
|
||||
qint64 pendingDataSize = m_dataSocket->pendingDatagramSize();
|
||||
m_udpReadBytes = m_dataSocket->readDatagram(m_udpBuf, pendingDataSize, &m_remoteAddress, 0);
|
||||
processData();
|
||||
|
||||
if (m_udpReadBytes == SDRdaemonFECBuffer::udpSize) {
|
||||
processData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDRdaemonFECUDPHandler::processData()
|
||||
{
|
||||
if (m_udpReadBytes < 0)
|
||||
{
|
||||
qDebug() << "SDRdaemonThread::processData: read failed";
|
||||
}
|
||||
else if (m_udpReadBytes > 0)
|
||||
{
|
||||
m_sdrDaemonBuffer.updateBlockCounts(m_udpReadBytes);
|
||||
|
||||
if (m_sdrDaemonBuffer.readMeta(m_udpBuf, m_udpReadBytes))
|
||||
{
|
||||
const SDRdaemonFECBuffer::MetaData& metaData = m_sdrDaemonBuffer.getCurrentMeta();
|
||||
bool change = false;
|
||||
m_tv_sec = metaData.m_tv_sec;
|
||||
m_tv_usec = metaData.m_tv_usec;
|
||||
|
||||
uint32_t sampleRate = m_sdrDaemonBuffer.getSampleRate();
|
||||
|
||||
if (m_samplerate != sampleRate)
|
||||
{
|
||||
setSamplerate(sampleRate);
|
||||
m_samplerate = sampleRate;
|
||||
change = true;
|
||||
}
|
||||
|
||||
if (m_centerFrequency != metaData.m_centerFrequency)
|
||||
{
|
||||
m_centerFrequency = metaData.m_centerFrequency;
|
||||
change = true;
|
||||
}
|
||||
|
||||
if (change)
|
||||
{
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(m_samplerate, m_centerFrequency * 1000); // Frequency in Hz for the DSP engine
|
||||
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
|
||||
SDRdaemonFECInput::MsgReportSDRdaemonStreamData *report = SDRdaemonFECInput::MsgReportSDRdaemonStreamData::create(
|
||||
m_sdrDaemonBuffer.getSampleRateStream(),
|
||||
m_samplerate,
|
||||
m_centerFrequency * 1000, // Frequency in Hz for the GUI
|
||||
m_tv_sec,
|
||||
m_tv_usec);
|
||||
m_outputMessageQueueToGUI->push(report);
|
||||
}
|
||||
}
|
||||
else if (m_sdrDaemonBuffer.isSync())
|
||||
{
|
||||
m_sdrDaemonBuffer.writeData(m_udpBuf, m_udpReadBytes);
|
||||
}
|
||||
}
|
||||
m_sdrDaemonBuffer.writeData(m_udpBuf, m_udpReadBytes);
|
||||
}
|
||||
|
||||
void SDRdaemonFECUDPHandler::setSamplerate(uint32_t samplerate)
|
||||
@ -223,15 +179,10 @@ void SDRdaemonFECUDPHandler::tick()
|
||||
{
|
||||
m_throttlems = throttlems;
|
||||
m_readLengthSamples = (m_sdrDaemonBuffer.getSampleRate() * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000;
|
||||
m_readLengthSamples += m_sdrDaemonBuffer.getRWBalanceCorrection();
|
||||
m_readLength = m_readLengthSamples * SDRdaemonFECBuffer::m_iqSampleSize;
|
||||
m_throttleToggle = !m_throttleToggle;
|
||||
}
|
||||
|
||||
if (m_autoCorrBuffer) {
|
||||
m_readLengthSamples += m_sdrDaemonBuffer.getRWBalanceCorrection();
|
||||
}
|
||||
|
||||
// read samples directly feeding the SampleFifo (no callback)
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(m_sdrDaemonBuffer.readData(m_readLength)), m_readLength);
|
||||
m_samplesCount += m_readLengthSamples;
|
||||
@ -243,17 +194,15 @@ void SDRdaemonFECUDPHandler::tick()
|
||||
else
|
||||
{
|
||||
m_tickCount = 0;
|
||||
SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming *report = SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming::create(
|
||||
SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming *report = SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming::create(
|
||||
m_tv_sec,
|
||||
m_tv_usec,
|
||||
m_sdrDaemonBuffer.isSyncLocked(),
|
||||
m_sdrDaemonBuffer.getFrameSize(),
|
||||
m_sdrDaemonBuffer.getBufferLengthInSecs(),
|
||||
m_sdrDaemonBuffer.isLz4Compressed(),
|
||||
m_sdrDaemonBuffer.getCompressionRatio(),
|
||||
m_sdrDaemonBuffer.getLz4DataCRCOK(),
|
||||
m_sdrDaemonBuffer.getLz4SuccessfulDecodes(),
|
||||
m_sdrDaemonBuffer.getBufferGauge());
|
||||
m_sdrDaemonBuffer.getBufferGauge(),
|
||||
m_sdrDaemonBuffer.getCurNbBlocks(),
|
||||
m_sdrDaemonBuffer.getCurNbRecovery(),
|
||||
m_sdrDaemonBuffer.getAvgNbBlocks(),
|
||||
m_sdrDaemonBuffer.getAvgNbRecovery());
|
||||
m_outputMessageQueueToGUI->push(report);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Version 2.0.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
<string><html><head/><body><p>Version 2.1.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
@ -333,7 +333,7 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex)
|
||||
void MainWindow::createStatusBar()
|
||||
{
|
||||
QString qtVersionStr = QString("Qt %1 ").arg(QT_VERSION_STR);
|
||||
m_showSystemWidget = new QLabel("SDRangel v2.0.0 " + qtVersionStr + QSysInfo::prettyProductName(), this);
|
||||
m_showSystemWidget = new QLabel("SDRangel v2.1.0 " + qtVersionStr + QSysInfo::prettyProductName(), this);
|
||||
statusBar()->addPermanentWidget(m_showSystemWidget);
|
||||
|
||||
m_dateTimeWidget = new QLabel(tr("Date"), this);
|
||||
|
61
sdrbase/util/movingaverage.h
Normal file
61
sdrbase/util/movingaverage.h
Normal file
@ -0,0 +1,61 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// SDRdaemon - send I/Q samples read from a SDR device over the network via UDP //
|
||||
// with FEC protection. GNUradio interface. //
|
||||
// //
|
||||
// http://stackoverflow.com/questions/10990618/calculate-rolling-moving-average-in-c //
|
||||
// //
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_
|
||||
#define GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
template <typename T, typename Total, int N>
|
||||
class MovingAverage
|
||||
{
|
||||
public:
|
||||
MovingAverage()
|
||||
: m_num_samples(0), m_total(0)
|
||||
{ }
|
||||
|
||||
void operator()(T sample)
|
||||
{
|
||||
if (m_num_samples < N)
|
||||
{
|
||||
m_samples[m_num_samples++] = sample;
|
||||
m_total += sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
T& oldest = m_samples[m_num_samples++ % N];
|
||||
m_total += sample - oldest;
|
||||
oldest = sample;
|
||||
}
|
||||
}
|
||||
|
||||
operator double() const { return m_num_samples > 0 ? m_total / std::min(m_num_samples, N) : 0.0d; }
|
||||
operator float() const { return m_num_samples > 0 ? m_total / std::min(m_num_samples, N) : 0.0f; }
|
||||
|
||||
private:
|
||||
T m_samples[N];
|
||||
int m_num_samples;
|
||||
Total m_total;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ */
|
Loading…
Reference in New Issue
Block a user