diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt
index 0a0e7bd02..e054d9e74 100644
--- a/devices/CMakeLists.txt
+++ b/devices/CMakeLists.txt
@@ -6,6 +6,7 @@ if (BUILD_DEBIAN)
add_subdirectory(bladerf)
add_subdirectory(hackrf)
add_subdirectory(limesdr)
+ add_subdirectory(sdrdaemon)
else(BUILD_DEBIAN)
find_package(LibBLADERF)
if(LIBUSB_FOUND AND LIBBLADERF_FOUND)
@@ -21,4 +22,10 @@ else(BUILD_DEBIAN)
if(LIBUSB_FOUND AND LIMESUITE_FOUND)
add_subdirectory(limesdr)
endif(LIBUSB_FOUND AND LIMESUITE_FOUND)
+
+ find_package(CM256cc)
+ if(CM256CC_FOUND)
+ add_subdirectory(sdrdaemon)
+ endif(CM256CC_FOUND AND LIBNANOMSG_FOUND)
+
endif (BUILD_DEBIAN)
diff --git a/devices/sdrdaemon/CMakeLists.txt b/devices/sdrdaemon/CMakeLists.txt
new file mode 100644
index 000000000..51bc08d8c
--- /dev/null
+++ b/devices/sdrdaemon/CMakeLists.txt
@@ -0,0 +1,55 @@
+project(sdrdaemondevice)
+
+if (HAS_SSSE3)
+ message(STATUS "SDRdaemonDevice: use SSSE3 SIMD" )
+elseif (HAS_NEON)
+ message(STATUS "SDRdaemonDevice: use Neon SIMD" )
+else()
+ message(STATUS "SDRdaemonDevice: Unsupported architecture")
+ return()
+endif()
+
+set(sdrdaemondevice_SOURCES
+ sdrdaemonfecbuffer.cpp
+)
+
+set(sdrdaemondevice_HEADERS
+ sdrdaemonfecbuffer.h
+)
+
+add_definitions(${QT_DEFINITIONS})
+add_definitions(-DQT_SHARED)
+
+if (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${LIBCM256CCSRC}
+)
+else (BUILD_DEBIAN)
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CM256CC_INCLUDE_DIR}
+)
+endif (BUILD_DEBIAN)
+
+add_library(sdrdaemondevice SHARED
+ ${sdrdaemondevice_SOURCES}
+)
+
+if (BUILD_DEBIAN)
+target_link_libraries(sdrdaemondevice
+ ${QT_LIBRARIES}
+ cm256cc
+)
+else (BUILD_DEBIAN)
+target_link_libraries(sdrdaemondevice
+ ${QT_LIBRARIES}
+ ${CM256CC_LIBRARIES}
+)
+endif (BUILD_DEBIAN)
+
+qt5_use_modules(sdrdaemondevice Core)
+
+install(TARGETS sdrdaemondevice DESTINATION lib)
diff --git a/devices/sdrdaemon/sdrdaemonfecbuffer.cpp b/devices/sdrdaemon/sdrdaemonfecbuffer.cpp
new file mode 100644
index 000000000..aa187bad8
--- /dev/null
+++ b/devices/sdrdaemon/sdrdaemonfecbuffer.cpp
@@ -0,0 +1,453 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sdrdaemonfecbuffer.h"
+
+
+const int SDRdaemonFECBuffer::m_sampleSize = 2;
+const int SDRdaemonFECBuffer::m_iqSampleSize = 2 * m_sampleSize;
+
+SDRdaemonFECBuffer::SDRdaemonFECBuffer(uint32_t throttlems) :
+ m_frameHead(0),
+ m_decoderIndexHead(nbDecoderSlots/2),
+ m_curNbBlocks(0),
+ m_minNbBlocks(256),
+ m_minOriginalBlocks(128),
+ m_curNbRecovery(0),
+ m_maxNbRecovery(0),
+ m_framesDecoded(true),
+ m_throttlemsNominal(throttlems),
+ m_readIndex(0),
+ m_readBuffer(0),
+ m_readSize(0),
+ m_bufferLenSec(0.0f),
+ m_nbReads(0),
+ m_nbWrites(0),
+ m_balCorrection(0),
+ m_balCorrLimit(0),
+ m_curOriginalBlocks(0)
+{
+ m_currentMeta.init();
+ m_framesNbBytes = nbDecoderSlots * sizeof(BufferFrame);
+ m_wrDeltaEstimate = m_framesNbBytes / 2;
+ m_tvOut_sec = 0;
+ m_tvOut_usec = 0;
+ m_readNbBytes = 1;
+ m_paramsCM256.BlockBytes = sizeof(ProtectedBlock); // never changes
+ m_paramsCM256.OriginalCount = m_nbOriginalBlocks; // never changes
+
+ if (!m_cm256.isInitialized()) {
+ m_cm256_OK = false;
+ qDebug() << "SDRdaemonFECBuffer::SDRdaemonFECBuffer: cannot initialize CM256 library";
+ } else {
+ m_cm256_OK = true;
+ }
+}
+
+SDRdaemonFECBuffer::~SDRdaemonFECBuffer()
+{
+ if (m_readBuffer) {
+ delete[] m_readBuffer;
+ }
+}
+
+void SDRdaemonFECBuffer::initDecodeAllSlots()
+{
+ for (int i = 0; i < nbDecoderSlots; i++)
+ {
+ m_decoderSlots[i].m_blockCount = 0;
+ m_decoderSlots[i].m_originalCount = 0;
+ m_decoderSlots[i].m_recoveryCount = 0;
+ m_decoderSlots[i].m_decoded = false;
+ m_decoderSlots[i].m_metaRetrieved = false;
+ resetOriginalBlocks(i);
+ memset((void *) m_decoderSlots[i].m_recoveryBlocks, 0, m_nbOriginalBlocks * sizeof(ProtectedBlock));
+ }
+}
+
+void SDRdaemonFECBuffer::initDecodeSlot(int slotIndex)
+{
+ // collect stats before voiding the slot
+
+ m_curNbBlocks = m_decoderSlots[slotIndex].m_blockCount;
+ m_curOriginalBlocks = m_decoderSlots[slotIndex].m_originalCount;
+ m_curNbRecovery = m_decoderSlots[slotIndex].m_recoveryCount;
+ m_avgNbBlocks(m_curNbBlocks);
+ m_avgOrigBlocks(m_curOriginalBlocks);
+ m_avgNbRecovery(m_curNbRecovery);
+ m_framesDecoded = m_framesDecoded && m_decoderSlots[slotIndex].m_decoded;
+
+ if (m_curNbBlocks < m_minNbBlocks) {
+ m_minNbBlocks = m_curNbBlocks;
+ }
+
+ if (m_curOriginalBlocks < m_minOriginalBlocks) {
+ m_minOriginalBlocks = m_curOriginalBlocks;
+ }
+
+ if (m_curNbRecovery > m_maxNbRecovery) {
+ m_maxNbRecovery = m_curNbRecovery;
+ }
+
+ // void the slot
+
+ m_decoderSlots[slotIndex].m_blockCount = 0;
+ m_decoderSlots[slotIndex].m_originalCount = 0;
+ m_decoderSlots[slotIndex].m_recoveryCount = 0;
+ m_decoderSlots[slotIndex].m_decoded = false;
+ m_decoderSlots[slotIndex].m_metaRetrieved = false;
+
+ resetOriginalBlocks(slotIndex);
+ memset((void *) m_decoderSlots[slotIndex].m_recoveryBlocks, 0, m_nbOriginalBlocks * sizeof(ProtectedBlock));
+}
+
+void SDRdaemonFECBuffer::initReadIndex()
+{
+ m_readIndex = ((m_decoderIndexHead + (nbDecoderSlots/2)) % nbDecoderSlots) * sizeof(BufferFrame);
+ m_wrDeltaEstimate = m_framesNbBytes / 2;
+ m_nbReads = 0;
+ m_nbWrites = 0;
+}
+
+void SDRdaemonFECBuffer::rwCorrectionEstimate(int slotIndex)
+{
+ if (m_nbReads >= 40) // check every ~1s as tick is ~50ms
+ {
+ int targetPivotSlot = (slotIndex + (nbDecoderSlots/2)) % nbDecoderSlots; // slot at half buffer opposite of current write slot
+ int targetPivotIndex = targetPivotSlot * sizeof(BufferFrame); // buffer index corresponding to start of above slot
+ int normalizedReadIndex = (m_readIndex < targetPivotIndex ? m_readIndex + nbDecoderSlots * sizeof(BufferFrame) : m_readIndex)
+ - (targetPivotSlot * sizeof(BufferFrame)); // normalize read index so it is positive and zero at start of pivot slot
+ int dBytes;
+ int rwDelta = (m_nbReads * m_readNbBytes) - (m_nbWrites * sizeof(BufferFrame));
+
+ if (normalizedReadIndex < (nbDecoderSlots/ 2) * sizeof(BufferFrame)) // read leads
+ {
+ dBytes = - normalizedReadIndex - rwDelta;
+ }
+ else // read lags
+ {
+ dBytes = (nbDecoderSlots * sizeof(BufferFrame)) - normalizedReadIndex - rwDelta;
+ }
+
+ m_balCorrection = (m_balCorrection / 4) + (dBytes / (int) (m_iqSampleSize * m_nbReads)); // 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;
+ }
+
+ float rwRatio = (float) (m_nbWrites * sizeof(BufferFrame)) / (float) (m_nbReads * m_readNbBytes);
+
+ m_nbReads = 0;
+ m_nbWrites = 0;
+ }
+}
+
+void SDRdaemonFECBuffer::checkSlotData(int slotIndex)
+{
+ int pseudoWriteIndex = slotIndex * sizeof(BufferFrame);
+ m_wrDeltaEstimate = pseudoWriteIndex - m_readIndex;
+ m_nbWrites++;
+
+ int rwDelayBytes = (m_wrDeltaEstimate > 0 ? m_wrDeltaEstimate : sizeof(BufferFrame) * nbDecoderSlots + m_wrDeltaEstimate);
+ int sampleRate = m_currentMeta.m_sampleRate;
+
+ if (sampleRate > 0)
+ {
+ int64_t ts = m_currentMeta.m_tv_sec * 1000000LL + m_currentMeta.m_tv_usec;
+ ts -= (rwDelayBytes * 1000000LL) / (sampleRate * sizeof(Sample));
+ m_tvOut_sec = ts / 1000000LL;
+ m_tvOut_usec = ts - (m_tvOut_sec * 1000000LL);
+ }
+
+ if (!m_decoderSlots[slotIndex].m_decoded)
+ {
+ qDebug() << "SDRdaemonFECBuffer::checkSlotData: incomplete frame:"
+ << " m_blockCount: " << m_decoderSlots[slotIndex].m_blockCount
+ << " m_recoveryCount: " << m_decoderSlots[slotIndex].m_recoveryCount;
+ }
+}
+
+void SDRdaemonFECBuffer::writeData(char *array, uint32_t length)
+{
+ SuperBlock *superBlock = (SuperBlock *) array;
+ int frameIndex = superBlock->header.frameIndex;
+ int decoderIndex = frameIndex % nbDecoderSlots;
+
+ // frame break
+
+ if (m_frameHead == -1) // initial state
+ {
+ m_decoderIndexHead = decoderIndex; // new decoder slot head
+ m_frameHead = frameIndex;
+ initReadIndex(); // reset read index
+ initDecodeAllSlots(); // initialize all slots
+ }
+ else if (m_frameHead != frameIndex) // frame break => new frame starts
+ {
+ m_decoderIndexHead = decoderIndex; // new decoder slot head
+ m_frameHead = frameIndex; // new frame head
+ checkSlotData(decoderIndex); // check slot before re-init
+ rwCorrectionEstimate(decoderIndex);
+ initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
+ }
+
+ // Block processing
+
+ if (m_decoderSlots[decoderIndex].m_blockCount < m_nbOriginalBlocks) // not enough blocks to decode -> store data
+ {
+ int blockIndex = superBlock->header.blockIndex;
+ int blockCount = m_decoderSlots[decoderIndex].m_blockCount;
+ int recoveryCount = m_decoderSlots[decoderIndex].m_recoveryCount;
+ m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockCount].Index = blockIndex;
+
+ if (blockIndex == 0) // first block with meta
+ {
+ m_decoderSlots[decoderIndex].m_metaRetrieved = true;
+ }
+
+ if (blockIndex < m_nbOriginalBlocks) // original data
+ {
+ m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockCount].Block = (void *) storeOriginalBlock(decoderIndex, blockIndex, superBlock->protectedBlock);
+ m_decoderSlots[decoderIndex].m_originalCount++;
+ }
+ else // recovery data
+ {
+ m_decoderSlots[decoderIndex].m_recoveryBlocks[recoveryCount] = superBlock->protectedBlock;
+ m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockCount].Block = (void *) &m_decoderSlots[decoderIndex].m_recoveryBlocks[recoveryCount];
+ m_decoderSlots[decoderIndex].m_recoveryCount++;
+ }
+ }
+
+ m_decoderSlots[decoderIndex].m_blockCount++;
+
+ if (m_decoderSlots[decoderIndex].m_blockCount == m_nbOriginalBlocks) // ready to decode
+ {
+ m_decoderSlots[decoderIndex].m_decoded = true;
+
+ if (m_cm256_OK && (m_decoderSlots[decoderIndex].m_recoveryCount > 0)) // recovery data used => need to decode FEC
+ {
+ m_paramsCM256.BlockBytes = sizeof(ProtectedBlock); // never changes
+ m_paramsCM256.OriginalCount = m_nbOriginalBlocks; // never changes
+
+ if (m_decoderSlots[decoderIndex].m_metaRetrieved) {
+ m_paramsCM256.RecoveryCount = m_currentMeta.m_nbFECBlocks;
+ } else {
+ m_paramsCM256.RecoveryCount = m_decoderSlots[decoderIndex].m_recoveryCount;
+ }
+
+ if (m_cm256.cm256_decode(m_paramsCM256, m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks)) // CM256 decode
+ {
+ qDebug() << "SDRdaemonFECBuffer::writeData: decode CM256 error:"
+ << " m_originalCount: " << m_decoderSlots[decoderIndex].m_originalCount
+ << " m_recoveryCount: " << m_decoderSlots[decoderIndex].m_recoveryCount;
+ }
+ else
+ {
+ qDebug() << "SDRdaemonFECBuffer::writeData: decode CM256 success:"
+ << " m_originalCount: " << m_decoderSlots[decoderIndex].m_originalCount
+ << " m_recoveryCount: " << m_decoderSlots[decoderIndex].m_recoveryCount;
+
+ for (int ir = 0; ir < m_decoderSlots[decoderIndex].m_recoveryCount; ir++) // restore missing blocks
+ {
+ int recoveryIndex = m_nbOriginalBlocks - m_decoderSlots[decoderIndex].m_recoveryCount + ir;
+ int blockIndex = m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[recoveryIndex].Index;
+ ProtectedBlock *recoveredBlock = (ProtectedBlock *) m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[recoveryIndex].Block;
+
+ if (blockIndex == 0) // first block with meta
+ {
+ MetaDataFEC *metaData = (MetaDataFEC *) recoveredBlock;
+
+ boost::crc_32_type crc32;
+ crc32.process_bytes(metaData, 20);
+
+ if (crc32.checksum() == metaData->m_crc32)
+ {
+ m_decoderSlots[decoderIndex].m_metaRetrieved = true;
+ printMeta("SDRdaemonFECBuffer::writeData: recovered meta", metaData);
+ }
+ else
+ {
+ qDebug() << "SDRdaemonFECBuffer::writeData: recovered meta: invalid CRC32";
+ }
+ }
+
+ storeOriginalBlock(decoderIndex, blockIndex, *recoveredBlock);
+
+ qDebug() << "SDRdaemonFECBuffer::writeData: recovered block #" << blockIndex;
+ } // restore missing blocks
+ } // CM256 decode
+ } // revovery
+
+ if (m_decoderSlots[decoderIndex].m_metaRetrieved) // block zero with its meta data has been received
+ {
+ MetaDataFEC *metaData = getMetaData(decoderIndex);
+
+ if (!(*metaData == m_currentMeta))
+ {
+ int sampleRate = metaData->m_sampleRate;
+
+ if (sampleRate > 0) {
+ m_bufferLenSec = (float) m_framesNbBytes / (float) (sampleRate * m_iqSampleSize);
+ m_balCorrLimit = sampleRate / 1000; // +/- 1 ms correction max per read
+ m_readNbBytes = (sampleRate * m_iqSampleSize) / 20;
+ }
+
+ printMeta("SDRdaemonFECBuffer::writeData: new meta", metaData); // print for change other than timestamp
+ }
+
+ m_currentMeta = *metaData; // renew current meta
+ } // check block 0
+ } // decode
+}
+
+void SDRdaemonFECBuffer::writeData0(char *array, uint32_t length)
+{
+// Kept as comments for the out of sync blocks algorithms
+// assert(length == m_udpPayloadSize);
+//
+// bool dataAvailable = false;
+// SuperBlock *superBlock = (SuperBlock *) array;
+// int frameIndex = superBlock->header.frameIndex;
+// int decoderIndex = frameIndex % nbDecoderSlots;
+// int blockIndex = superBlock->header.blockIndex;
+//
+//// qDebug() << "SDRdaemonFECBuffer::writeData:"
+//// << " frameIndex: " << frameIndex
+//// << " decoderIndex: " << decoderIndex
+//// << " blockIndex: " << blockIndex;
+//
+// if (m_frameHead == -1) // initial state
+// {
+// m_decoderIndexHead = 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)
+// {
+// if (-frameDelta < nbDecoderSlots) // new frame head not too new
+// {
+// //qDebug() << "SDRdaemonFECBuffer::writeData: new frame head (1): " << frameIndex << ":" << frameDelta << ":" << decoderIndex;
+// m_decoderIndexHead = decoderIndex; // new decoder slot head
+// m_frameHead = frameIndex;
+// checkSlotData(decoderIndex);
+// dataAvailable = true;
+// initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
+// }
+// else if (-frameDelta <= 65536 - nbDecoderSlots) // loss of sync start over
+// {
+// //qDebug() << "SDRdaemonFECBuffer::writeData: loss of sync start over (1)" << frameIndex << ":" << frameDelta << ":" << decoderIndex;
+// m_decoderIndexHead = decoderIndex; // new decoder slot head
+// m_frameHead = frameIndex;
+// initReadIndex(); // reset read index
+// initDecodeAllSlots(); // re-initialize all slots
+// }
+// }
+// else
+// {
+// if (frameDelta > 65536 - nbDecoderSlots) // new frame head not too new
+// {
+// //qDebug() << "SDRdaemonFECBuffer::writeData: new frame head (2): " << frameIndex << ":" << frameDelta << ":" << decoderIndex;
+// m_decoderIndexHead = decoderIndex; // new decoder slot head
+// m_frameHead = frameIndex;
+// checkSlotData(decoderIndex);
+// dataAvailable = true;
+// initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
+// }
+// else if (frameDelta >= nbDecoderSlots) // loss of sync start over
+// {
+// //qDebug() << "SDRdaemonFECBuffer::writeData: loss of sync start over (2)" << frameIndex << ":" << frameDelta << ":" << decoderIndex;
+// m_decoderIndexHead = decoderIndex; // new decoder slot head
+// m_frameHead = frameIndex;
+// initReadIndex(); // reset read index
+// initDecodeAllSlots(); // re-initialize all slots
+// }
+// }
+// }
+//
+// // decoderIndex should now be correctly set
+//
+}
+
+uint8_t *SDRdaemonFECBuffer::readData(int32_t length)
+{
+ uint8_t *buffer = (uint8_t *) m_frames;
+ uint32_t readIndex = m_readIndex;
+
+ m_nbReads++;
+
+ // SEGFAULT FIX: arbitratily truncate so that it does not exceed buffer length
+ if (length > framesSize) {
+ length = framesSize;
+ }
+
+ if (m_readIndex + length < m_framesNbBytes) // ends before buffer bound
+ {
+ 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;
+ }
+
+ m_readBuffer = new uint8_t[length];
+ m_readSize = length;
+ }
+
+ 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::printMeta(const QString& header, MetaDataFEC *metaData)
+{
+ qDebug() << header << ": "
+ << "|" << 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/devices/sdrdaemon/sdrdaemonfecbuffer.h b/devices/sdrdaemon/sdrdaemonfecbuffer.h
new file mode 100644
index 000000000..b4dbe2dc2
--- /dev/null
+++ b/devices/sdrdaemon/sdrdaemonfecbuffer.h
@@ -0,0 +1,274 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 DEVICES_SDRDAEMON_SDRDAEMONFECBUFFER_H_
+#define DEVICES_SDRDAEMON_SDRDAEMONFECBUFFER_H_
+
+#include
+#include
+#include
+#include "cm256.h"
+#include "util/movingaverage.h"
+
+
+#define SDRDAEMONFEC_UDPSIZE 512 // UDP payload size
+#define SDRDAEMONFEC_NBORIGINALBLOCKS 128 // number of sample blocks per frame excluding FEC blocks
+#define SDRDAEMONFEC_NBDECODERSLOTS 16 // power of two sub multiple of uint16_t size. A too large one is superfluous.
+
+class SDRdaemonFECBuffer
+{
+public:
+#pragma pack(push, 1)
+ 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
+ uint32_t m_crc32; //!< 24 CRC32 of the above
+
+ 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(MetaDataFEC));
+ }
+ };
+
+ struct Sample
+ {
+ int16_t i;
+ int16_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 framesSize = SDRDAEMONFEC_NBDECODERSLOTS * (SDRDAEMONFEC_NBORIGINALBLOCKS - 1) * (SDRDAEMONFEC_UDPSIZE - sizeof(Header));
+
+ struct ProtectedBlock
+ {
+ Sample samples[samplesPerBlock];
+ };
+
+ struct SuperBlock
+ {
+ Header header;
+ ProtectedBlock protectedBlock;
+ };
+#pragma pack(pop)
+
+ SDRdaemonFECBuffer(uint32_t throttlems);
+ ~SDRdaemonFECBuffer();
+
+ // R/W operations
+ void writeData(char *array, uint32_t length); //!< Write data into buffer.
+ void writeData0(char *array, uint32_t length); //!< Write data into buffer.
+ uint8_t *readData(int32_t length); //!< Read data from buffer
+
+ // meta data
+ const MetaDataFEC& getCurrentMeta() const { return m_currentMeta; }
+
+ // samples timestamp
+ uint32_t getTVOutSec() const { return m_tvOut_sec; }
+ uint32_t getTVOutUsec() const { return m_tvOut_usec; }
+
+ // stats
+
+ int getCurNbBlocks() const { return m_curNbBlocks; }
+ int getCurOriginalBlocks() const { return m_curOriginalBlocks; }
+ int getCurNbRecovery() const { return m_curNbRecovery; }
+ float getAvgNbBlocks() const { return m_avgNbBlocks; }
+ float getAvgOriginalBlocks() const { return m_avgOrigBlocks; }
+ float getAvgNbRecovery() const { return m_avgNbRecovery; }
+
+ int getMinNbBlocks()
+ {
+ int minNbBlocks = m_minNbBlocks;
+ m_minNbBlocks = 256;
+ return minNbBlocks;
+ }
+
+ int getMinOriginalBlocks()
+ {
+ int minOriginalBlocks = m_minOriginalBlocks;
+ m_minOriginalBlocks = 128;
+ return minOriginalBlocks;
+ }
+
+ int getMaxNbRecovery()
+ {
+ int maxNbRecovery = m_maxNbRecovery;
+ m_maxNbRecovery = 0;
+ return maxNbRecovery;
+ }
+
+ bool allFramesDecoded()
+ {
+ bool framesDecoded = m_framesDecoded;
+ m_framesDecoded = true;
+ return framesDecoded;
+ }
+
+ float getBufferLengthInSecs() const { return m_bufferLenSec; }
+ int32_t getRWBalanceCorrection() const { return m_balCorrection; }
+
+ /** Get buffer gauge value in % of buffer size ([-50:50])
+ * [-50:0] : write leads or read lags
+ * [0:50] : read leads or write lags
+ */
+ inline int32_t getBufferGauge() const
+ {
+ if (m_framesNbBytes)
+ {
+ int32_t val = (m_wrDeltaEstimate * 100) / (int32_t) m_framesNbBytes;
+ int32_t ret = val < 0 ? -val - 50 : 50 -val;
+ int32_t rp = (m_readIndex * 100) / (int32_t) m_framesNbBytes;
+ return ret;
+ }
+ else
+ {
+ return 0; // default position
+ }
+ }
+
+ static const int m_udpPayloadSize = SDRDAEMONFEC_UDPSIZE;
+ static const int m_nbOriginalBlocks = SDRDAEMONFEC_NBORIGINALBLOCKS;
+ static const int m_sampleSize;
+ static const int m_iqSampleSize;
+
+private:
+ static const int nbDecoderSlots = SDRDAEMONFEC_NBDECODERSLOTS;
+
+#pragma pack(push, 1)
+ struct BufferFrame
+ {
+ ProtectedBlock m_blocks[m_nbOriginalBlocks - 1];
+ };
+#pragma pack(pop)
+
+ struct DecoderSlot
+ {
+ ProtectedBlock m_blockZero; //!< First block of a frame. Has meta data.
+ ProtectedBlock m_originalBlocks[m_nbOriginalBlocks]; //!< Original blocks retrieved directly or by later FEC
+ ProtectedBlock m_recoveryBlocks[m_nbOriginalBlocks]; //!< Recovery blocks (FEC blocks) with max size
+ CM256::cm256_block m_cm256DescriptorBlocks[m_nbOriginalBlocks]; //!< CM256 decoder descriptors (block addresses and block indexes)
+ int m_blockCount; //!< number of blocks received for this frame
+ int m_originalCount; //!< number of original blocks received
+ int m_recoveryCount; //!< number of recovery blocks received
+ bool m_decoded; //!< true if decoded
+ bool m_metaRetrieved; //!< true if meta data (block zero) was retrieved
+ };
+
+ MetaDataFEC m_currentMeta; //!< Stored current meta data
+ CM256::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_decoderIndexHead; //!< 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_minNbBlocks; //!< (stats) minimum number of blocks received since last poll
+ int m_curOriginalBlocks; //!< (stats) instantanous number of original blocks received
+ int m_minOriginalBlocks; //!< (stats) minimum number of original blocks received since last poll
+ int m_curNbRecovery; //!< (stats) instantaneous number of recovery blocks used
+ int m_maxNbRecovery; //!< (stats) maximum number of recovery blocks used since last poll
+ MovingAverage m_avgNbBlocks; //!< (stats) average number of blocks received
+ MovingAverage m_avgOrigBlocks; //!< (stats) average number of original blocks received
+ MovingAverage m_avgNbRecovery; //!< (stats) average number of recovery blocks used
+ bool m_framesDecoded; //!< [stats] true if all frames were decoded since last poll
+ int m_readIndex; //!< current byte read index in frames buffer
+ int m_wrDeltaEstimate; //!< Sampled estimate of write to read indexes difference
+ uint32_t m_tvOut_sec; //!< Estimated returned samples timestamp (seconds)
+ uint32_t m_tvOut_usec; //!< Estimated returned samples timestamp (microseconds)
+ int m_readNbBytes; //!< Nominal number of bytes per read (50ms)
+
+ uint32_t m_throttlemsNominal; //!< Initial throttle in ms
+ uint8_t* m_readBuffer; //!< Read buffer to hold samples when looping back to beginning of raw buffer
+ uint32_t m_readSize; //!< Read buffer size
+
+ float m_bufferLenSec;
+
+ int m_nbReads; //!< Number of buffer reads since start of auto R/W balance correction period
+ int m_nbWrites; //!< Number of buffer writes since start of auto R/W balance correction period
+ int m_balCorrection; //!< R/W balance correction in number of samples
+ int m_balCorrLimit; //!< Correction absolute value limit in number of samples
+ CM256 m_cm256; //!< CM256 library
+ bool m_cm256_OK; //!< CM256 library initialized OK
+
+ inline ProtectedBlock* storeOriginalBlock(int slotIndex, int blockIndex, const ProtectedBlock& protectedBlock)
+ {
+ if (blockIndex == 0) {
+ // m_decoderSlots[slotIndex].m_originalBlocks[0] = protectedBlock;
+ // return &m_decoderSlots[slotIndex].m_originalBlocks[0];
+ m_decoderSlots[slotIndex].m_blockZero = protectedBlock;
+ return &m_decoderSlots[slotIndex].m_blockZero;
+ } else {
+ // m_decoderSlots[slotIndex].m_originalBlocks[blockIndex] = protectedBlock;
+ // return &m_decoderSlots[slotIndex].m_originalBlocks[blockIndex];
+ m_frames[slotIndex].m_blocks[blockIndex - 1] = protectedBlock;
+ return &m_frames[slotIndex].m_blocks[blockIndex - 1];
+ }
+ }
+
+ inline ProtectedBlock& getOriginalBlock(int slotIndex, int blockIndex)
+ {
+ if (blockIndex == 0) {
+ // return m_decoderSlots[slotIndex].m_originalBlocks[0];
+ return m_decoderSlots[slotIndex].m_blockZero;
+ } else {
+ // return m_decoderSlots[slotIndex].m_originalBlocks[blockIndex];
+ return m_frames[slotIndex].m_blocks[blockIndex - 1];
+ }
+ }
+
+ inline MetaDataFEC *getMetaData(int slotIndex)
+ {
+ // return (MetaDataFEC *) &m_decoderSlots[slotIndex].m_originalBlocks[0];
+ return (MetaDataFEC *) &m_decoderSlots[slotIndex].m_blockZero;
+ }
+
+ inline void resetOriginalBlocks(int slotIndex)
+ {
+ // memset((void *) m_decoderSlots[slotIndex].m_originalBlocks, 0, m_nbOriginalBlocks * sizeof(ProtectedBlock));
+ memset((void *) &m_decoderSlots[slotIndex].m_blockZero, 0, sizeof(ProtectedBlock));
+ memset((void *) m_frames[slotIndex].m_blocks, 0, (m_nbOriginalBlocks - 1) * sizeof(ProtectedBlock));
+ }
+
+ void initDecodeAllSlots();
+ void initReadIndex();
+ void rwCorrectionEstimate(int slotIndex);
+ void checkSlotData(int slotIndex);
+ void initDecodeSlot(int slotIndex);
+
+ static void printMeta(const QString& header, MetaDataFEC *metaData);
+};
+
+
+
+#endif /* DEVICES_SDRDAEMON_SDRDAEMONFECBUFFER_H_ */