mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 16:08:39 -05:00
SDRdaemonFEC: removed useless devices/sdrdaemonfec subdirectory
This commit is contained in:
parent
e7b7e77d4d
commit
c2d9e64c0b
@ -1,55 +0,0 @@
|
|||||||
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)
|
|
@ -1,377 +0,0 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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/>. //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cmath>
|
|
||||||
#include <boost/crc.hpp>
|
|
||||||
#include <boost/cstdint.hpp>
|
|
||||||
|
|
||||||
#include "sdrdaemonfecbuffer.h"
|
|
||||||
|
|
||||||
|
|
||||||
const int SDRdaemonFECBuffer::m_sampleSize = 2;
|
|
||||||
const int SDRdaemonFECBuffer::m_iqSampleSize = 2 * m_sampleSize;
|
|
||||||
|
|
||||||
SDRdaemonFECBuffer::SDRdaemonFECBuffer(uint32_t throttlems) :
|
|
||||||
m_decoderIndexHead(nbDecoderSlots/2),
|
|
||||||
m_frameHead(0),
|
|
||||||
m_curNbBlocks(0),
|
|
||||||
m_minNbBlocks(256),
|
|
||||||
m_curOriginalBlocks(0),
|
|
||||||
m_minOriginalBlocks(128),
|
|
||||||
m_curNbRecovery(0),
|
|
||||||
m_maxNbRecovery(0),
|
|
||||||
m_framesDecoded(true),
|
|
||||||
m_readIndex(0),
|
|
||||||
m_throttlemsNominal(throttlems),
|
|
||||||
m_readBuffer(0),
|
|
||||||
m_readSize(0),
|
|
||||||
m_bufferLenSec(0.0f),
|
|
||||||
m_nbReads(0),
|
|
||||||
m_nbWrites(0),
|
|
||||||
m_balCorrection(0),
|
|
||||||
m_balCorrLimit(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
|
|
||||||
uint32_t 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *SDRdaemonFECBuffer::readData(uint32_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
|
|
||||||
<< "|";
|
|
||||||
}
|
|
@ -1,272 +0,0 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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 DEVICES_SDRDAEMON_SDRDAEMONFECBUFFER_H_
|
|
||||||
#define DEVICES_SDRDAEMON_SDRDAEMONFECBUFFER_H_
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include "cm256.h"
|
|
||||||
#include "util/movingaverage.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define SDRDAEMONFEC_UDPSIZE 512 // UDP payload size
|
|
||||||
#define SDRDAEMONSOURCE_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 * (SDRDAEMONSOURCE_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); //!< Write data into buffer.
|
|
||||||
uint8_t *readData(uint32_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;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0; // default position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const int m_udpPayloadSize = SDRDAEMONFEC_UDPSIZE;
|
|
||||||
static const int m_nbOriginalBlocks = SDRDAEMONSOURCE_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
|
|
||||||
uint32_t 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<int, int, 10> m_avgNbBlocks; //!< (stats) average number of blocks received
|
|
||||||
MovingAverage<int, int, 10> m_avgOrigBlocks; //!< (stats) average number of original blocks received
|
|
||||||
MovingAverage<int, int, 10> 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_ */
|
|
Loading…
Reference in New Issue
Block a user