SDRdaemonFEC support: interim state (1). Compile except UI. Bumped version to 2.1.0 where necessary

pull/27/head
f4exb 2016-06-20 00:45:24 +02:00
rodzic 4386e0fbab
commit e418e68bd9
15 zmienionych plików z 575 dodań i 848 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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
<< "|";
}

Wyświetl plik

@ -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);
};

Wyświetl plik

@ -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;

Wyświetl plik

@ -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">

Wyświetl plik

@ -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),

Wyświetl plik

@ -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)
{ }
};

Wyświetl plik

@ -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,

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -84,7 +84,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Version 2.0.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. &lt;/p&gt;&lt;p&gt;Code at &lt;a href=&quot;https://github.com/f4exb/sdrangel&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/f4exb/sdrangel&lt;/span&gt;&lt;/a&gt; This is a complete redesign from RTL-SDRangelove at &lt;a href=&quot;https://github.com/hexameron/rtl-sdrangelove&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/hexameron/rtl-sdrangelove&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Many thanks to the original developers:&lt;/p&gt;&lt;p&gt;The osmocom developer team - especially horizon, Hoernchen &amp;amp; tnt.&lt;/p&gt;&lt;p&gt;Christian Daniel from maintech GmbH.&lt;/p&gt;&lt;p&gt;John Greb (hexameron) for the contributions in RTL-SDRangelove&lt;/p&gt;&lt;p&gt;The following rules apply to the SDRangel main application and libsdrbase:&lt;br/&gt;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 &lt;a href=&quot;http://www.gnu.org/licenses/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.gnu.org/licenses/&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the license of installed plugins, look into the plugin list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Version 2.1.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. &lt;/p&gt;&lt;p&gt;Code at &lt;a href=&quot;https://github.com/f4exb/sdrangel&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/f4exb/sdrangel&lt;/span&gt;&lt;/a&gt; This is a complete redesign from RTL-SDRangelove at &lt;a href=&quot;https://github.com/hexameron/rtl-sdrangelove&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/hexameron/rtl-sdrangelove&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Many thanks to the original developers:&lt;/p&gt;&lt;p&gt;The osmocom developer team - especially horizon, Hoernchen &amp;amp; tnt.&lt;/p&gt;&lt;p&gt;Christian Daniel from maintech GmbH.&lt;/p&gt;&lt;p&gt;John Greb (hexameron) for the contributions in RTL-SDRangelove&lt;/p&gt;&lt;p&gt;The following rules apply to the SDRangel main application and libsdrbase:&lt;br/&gt;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 &lt;a href=&quot;http://www.gnu.org/licenses/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://www.gnu.org/licenses/&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the license of installed plugins, look into the plugin list.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>

Wyświetl plik

@ -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);

Wyświetl plik

@ -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_ */