diff --git a/CMakeLists.txt b/CMakeLists.txt index 7172f7276..a19d9c475 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/cmake/Modules/FindCM256.cmake b/cmake/Modules/FindCM256.cmake new file mode 100644 index 000000000..4d38a8449 --- /dev/null +++ b/cmake/Modules/FindCM256.cmake @@ -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) diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index dff35146a..37cc13c7e 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -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) diff --git a/plugins/samplesource/sdrdaemonfec/CMakeLists.txt b/plugins/samplesource/sdrdaemonfec/CMakeLists.txt index c1b9184fc..7f9458acd 100644 --- a/plugins/samplesource/sdrdaemonfec/CMakeLists.txt +++ b/plugins/samplesource/sdrdaemonfec/CMakeLists.txt @@ -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 ) diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.cpp b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.cpp index 7bdeedbf5..c88474cba 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.cpp +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.cpp @@ -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 + << "|"; } - diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.h b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.h index f8af99178..a4c7de921 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.h +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecbuffer.h @@ -19,70 +19,100 @@ #include #include +#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 m_avgNbBlocks; //!< (stats) average number of blocks received + MovingAverage 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); }; diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.cpp b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.cpp index 18e70d853..bbfaec408 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.cpp +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.cpp @@ -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; diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.ui b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.ui index 7a3a3426a..2ac39e746 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.ui +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecgui.ui @@ -304,12 +304,12 @@ - + false - Stream locked status i.e. synced with meta data + Frames complete ... @@ -320,58 +320,6 @@ - - - - - 40 - 0 - - - - Frame size in kB - - - 0000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 65 - 0 - - - - Nominal sample rate from stream meta data (kS/s) - - - 00000.00 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - @@ -392,33 +340,7 @@ - - - Qt::Vertical - - - - - - - - 50 - 0 - - - - Sample rate skew from stream nominal rate (%) - - - -00.00 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + Qt::Vertical @@ -443,6 +365,74 @@ + + + + Qt::Vertical + + + + + + + Total number of blocks retrieved per frame + + + 128 + + + + + + + Qt::Vertical + + + + + + + Average number of blocks retrieved per frame + + + 000.0 + + + + + + + Qt::Vertical + + + + + + + Number of recovery blocks used per frame + + + 000 + + + + + + + Qt::Vertical + + + + + + + Average number of recovery blocks used per frame + + + 000.0 + + + @@ -465,177 +455,6 @@ - - - - - - false - - - Stream is compressed with LZ4 - - - ... - - - - :/compressed.png:/compressed.png - - - - - - - - 50 - 0 - - - - Stream comopression ratio (compressed / original) - - - 0.00 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 40 - 0 - - - - Data CRC OK (%) - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 40 - 0 - - - - LZ4 successful decodes (%) - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 30 - 0 - - - - Main buffer lenth in seconds - - - 00.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 22 - 20 - - - - Reset buffer indexes - - - - - - - :/recycle.png:/recycle.png - - - - - - - Auto maintain buffer read / write balance - - - B - - - - - - - Auto follow actual stream sample rate - - - R - - - - - diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.cpp b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.cpp index 3e4bdbb5d..58902290d 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.cpp +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.cpp @@ -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), diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.h b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.h index ed4932014..80538bfd7 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.h +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecinput.h @@ -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) { } }; diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecplugin.cpp b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecplugin.cpp index 3a26528a2..43f7cb744 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecplugin.cpp +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecplugin.cpp @@ -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, diff --git a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecudphandler.cpp b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecudphandler.cpp index f0ec4c877..662727aa0 100644 --- a/plugins/samplesource/sdrdaemonfec/sdrdaemonfecudphandler.cpp +++ b/plugins/samplesource/sdrdaemonfec/sdrdaemonfecudphandler.cpp @@ -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(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); } } diff --git a/sdrbase/gui/aboutdialog.ui b/sdrbase/gui/aboutdialog.ui index ea6b7c795..8ba817d2f 100644 --- a/sdrbase/gui/aboutdialog.ui +++ b/sdrbase/gui/aboutdialog.ui @@ -84,7 +84,7 @@ - <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> + <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> true diff --git a/sdrbase/mainwindow.cpp b/sdrbase/mainwindow.cpp index 0f5513382..cb5448ced 100644 --- a/sdrbase/mainwindow.cpp +++ b/sdrbase/mainwindow.cpp @@ -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); diff --git a/sdrbase/util/movingaverage.h b/sdrbase/util/movingaverage.h new file mode 100644 index 000000000..d5e647ca7 --- /dev/null +++ b/sdrbase/util/movingaverage.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////////// + +#ifndef GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ +#define GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ + +#include + +template +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_ */