kopia lustrzana https://github.com/f4exb/sdrangel
SDRdaemonFEC plugin: cleanup and readme updates
rodzic
8b82547b86
commit
72fe72a529
10
Readme.md
10
Readme.md
|
@ -89,6 +89,16 @@ There is an automated skew rate compensation in place. During rate readjustemnt
|
|||
|
||||
Note that this plugin does not require any of the hardware support libraries nor the libusb library. It is alwasys available in the list of devices as `SDRdaemon[0]` even if no physical device is connected.
|
||||
|
||||
<h2>SDRdaemonFEC input</h2>
|
||||
|
||||
This is a specialized client for the SDRdaemon server that matches the SDRdaemon with FEC option (`sdrdaemonfec` binary). The frame format is quite different from what it is without FEC and it also does not have the exact same functionnalities so a different plugin is more practiclal than trying to fit both versions in one plugin.
|
||||
|
||||
Using this scheme the remote operation is more robust in case conditions are not optimal. While SDRdaemon without FEC will work fine on copper or fiber lines the FEC version is recommended for WiFi links where even in good conditions some UDP packets might get lost.
|
||||
|
||||
This plugin will be built only if the [CM256cc library](https://github.com/f4exb/cm256cc) is installed in your system. You will then have to specify the include and library paths on the cmake command line. Say if you install cm256cc in `/opt/install/cm256cc` you will have to add `-DCM256CC_INCLUDE_DIR=/opt/install/cm256cc/include/cm256cc -DCM256CC_LIBRARIES=/opt/install/cm256cc/lib/libcm256cc.so` to the cmake commands.
|
||||
|
||||
Note that this plugin does not require any of the hardware support libraries nor the libusb library. It is alwasys available in the list of devices as `SDRdaemonFEC[0]` even if no physical device is connected.
|
||||
|
||||
<h1>Channel plugins with special conditions</h1>
|
||||
|
||||
<h2>DSD (Digital Speech Decoder)</h2>
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
This input sample source plugin gets its samples over tbe network from a SDRdaemon server using UDP connection. SDRdaemon refers to the SDRdaemon utility found in [this](https://github.com/f4exb/sdrdaemon) Github repostory. This plugin is specialized in the version of SDRdaemon that sends data with FEC (Forward Erasure Correction). When FEC is used the format of the data is completely different from what it is without FEC.
|
||||
|
||||
The addition of FEC blocks and the sequence tagging of frames and blocks make the transmission more robust. While it is unlikely to be beneficial with copper or fiber links it can improve links over WiFi particularly on distant links.
|
||||
|
||||
Please note that there is no provision for handling out of sync UDP blocks. It is assumed that frames and block numbers always increase with possible blocks missing.
|
||||
|
||||
<h2>Build</h2>
|
||||
|
||||
The plugin will be built only if the [CM256cc library](https://github.com/f4exb/cm256cc) is installed in your system. You will then have to specify the include and library paths on the cmake command line. Say if you install cm256cc in `/opt/install/cm256cc` you will have to add `-DCM256CC_INCLUDE_DIR=/opt/install/cm256cc/include/cm256cc -DCM256CC_LIBRARIES=/opt/install/cm256cc/lib/libcm256cc.so` to the cmake commands.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![SDR Daemon with FEC plugin GUI](../../../doc/img/SDRdaemonFEC_plugin.png)
|
||||
|
@ -60,7 +68,7 @@ The system tries to compensate read / write unbalance however at start or when a
|
|||
The color of the icon indicates stream status:
|
||||
|
||||
- Green: all original blocks have been received for all frames during the last polling timeframe
|
||||
- Blue: some original blocks were reconstructed from FEC blocks for some frames during the last polling timeframe
|
||||
- Magenta/Pink: some original blocks were reconstructed from FEC blocks for some frames during the last polling timeframe
|
||||
- No color: some original blocks were definitely lost for some frames during the last polling timeframe
|
||||
|
||||
<h4>4.2: Minimum number of blocks received by frame</h4>
|
||||
|
@ -73,7 +81,7 @@ Moving average over the last 10 frames of the number of blocks received per fram
|
|||
|
||||
<h4>4.4: Maximum number of FEC blocks used by frame</h4>
|
||||
|
||||
Maximum number of FEC blocks used for original blocks recovery during the last polling timeframe. Ideally this should be 0 when no blocks are lost but the system is able to correct lost blocks up to the nominal number of FEC blocks (blue lock icon).
|
||||
Maximum number of FEC blocks used for original blocks recovery during the last polling timeframe. Ideally this should be 0 when no blocks are lost but the system is able to correct lost blocks up to the nominal number of FEC blocks (Magenta/Pink lock icon).
|
||||
|
||||
<h4>4.5: Average number of FEC blocks used for original blocks recovery by frame</h4>
|
||||
|
||||
|
|
|
@ -151,18 +151,6 @@ void SDRdaemonFECBuffer::rwCorrectionEstimate(int slotIndex)
|
|||
|
||||
float rwRatio = (float) (m_nbWrites * sizeof(BufferFrame)) / (float) (m_nbReads * m_readNbBytes);
|
||||
|
||||
// qDebug() << "SDRdaemonFECBuffer::rwCorrectionEstimate: "
|
||||
// << " m_nbReads: " << m_nbReads
|
||||
// << " m_nbWrites: " << m_nbWrites
|
||||
// << " rwDelta: " << rwDelta
|
||||
// << " targetPivotSlot: " << targetPivotSlot
|
||||
// << " targetPivotIndex: " << targetPivotIndex
|
||||
// << " m_readIndex: " << m_readIndex
|
||||
// << " normalizedReadIndex: " << normalizedReadIndex
|
||||
// << " dBytes: " << dBytes
|
||||
// << " m_balCorrection: " << m_balCorrection;
|
||||
|
||||
//m_balCorrection = dBytes / (int) (m_iqSampleSize * m_nbReads);
|
||||
m_nbReads = 0;
|
||||
m_nbWrites = 0;
|
||||
}
|
||||
|
@ -304,12 +292,6 @@ void SDRdaemonFECBuffer::writeData(char *array, uint32_t length)
|
|||
m_decoderSlots[decoderIndex].m_originalBlocks[blockIndex] = *recoveredBlock;
|
||||
|
||||
qDebug() << "SDRdaemonFECBuffer::writeData: recovered block #" << blockIndex;
|
||||
// << " i[0]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[0].i
|
||||
// << " q[0]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[0].q
|
||||
// << " i[1]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[1].i
|
||||
// << " q[1]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[1].q
|
||||
// << " i[2]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[2].i
|
||||
// << " q[2]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[2].q;
|
||||
} // restore missing blocks
|
||||
} // CM256 decode
|
||||
} // revovery
|
||||
|
@ -338,6 +320,7 @@ void SDRdaemonFECBuffer::writeData(char *array, uint32_t length)
|
|||
|
||||
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;
|
||||
|
@ -406,107 +389,6 @@ void SDRdaemonFECBuffer::writeData0(char *array, uint32_t length)
|
|||
//
|
||||
// // decoderIndex should now be correctly set
|
||||
//
|
||||
// int blockHead = m_decoderSlots[decoderIndex].m_blockCount;
|
||||
// int recoveryHead = m_decoderSlots[decoderIndex].m_recoveryCount;
|
||||
//
|
||||
// if (blockHead < m_nbOriginalBlocks) // not enough blocks to decode -> store data
|
||||
// {
|
||||
// if (blockIndex == 0) // first block with meta
|
||||
// {
|
||||
//// ProtectedBlockZero *blockZero = (ProtectedBlockZero *) &superBlock->protectedBlock;
|
||||
//// m_decoderSlots[decoderIndex].m_blockZero = *blockZero;
|
||||
// m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_decoderSlots[decoderIndex].m_blockZero;
|
||||
// m_decoderSlots[decoderIndex].m_metaRetrieved = true;
|
||||
// }
|
||||
// else if (blockIndex < m_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[recoveryHead] = superBlock->protectedBlock;
|
||||
// m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_decoderSlots[decoderIndex].m_recoveryBlocks[recoveryHead];
|
||||
// m_decoderSlots[decoderIndex].m_recoveryCount++;
|
||||
// }
|
||||
//
|
||||
// m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Index = blockIndex;
|
||||
// m_decoderSlots[decoderIndex].m_blockCount++;
|
||||
// }
|
||||
// else if (!m_decoderSlots[decoderIndex].m_decoded) // ready to decode
|
||||
// {
|
||||
// if (m_decoderSlots[decoderIndex].m_recoveryCount > 0) // recovery data used
|
||||
// {
|
||||
// if (m_decoderSlots[decoderIndex].m_metaRetrieved) // block zero with its meta data has been received
|
||||
// {
|
||||
//// m_paramsCM256.RecoveryCount = m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_paramsCM256.RecoveryCount = m_currentMeta.m_nbFECBlocks; // take last stored value for number of FEC blocks
|
||||
// }
|
||||
//
|
||||
// if (cm256_decode(m_paramsCM256, m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks)) // failure to decode
|
||||
// {
|
||||
// qDebug() << "SDRdaemonFECBuffer::writeData: CM256 decode error:"
|
||||
// << " BlockBytes: " << m_paramsCM256.BlockBytes
|
||||
// << " OriginalCount: " << m_paramsCM256.OriginalCount
|
||||
// << " RecoveryCount: " << m_paramsCM256.RecoveryCount
|
||||
// << " m_recoveryCount: " << m_decoderSlots[decoderIndex].m_recoveryCount;
|
||||
// }
|
||||
// else // success to decode
|
||||
// {
|
||||
// int nbOriginalBlocks = m_decoderSlots[decoderIndex].m_blockCount - m_decoderSlots[decoderIndex].m_recoveryCount;
|
||||
//
|
||||
// qDebug() << "SDRdaemonFECBuffer::writeData: CM256 decode success:"
|
||||
// << " nbOriginalBlocks: " << nbOriginalBlocks
|
||||
// << " m_recoveryCount: " << 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];
|
||||
//// printMeta("SDRdaemonFECBuffer::writeData: recovered meta", &recoveredBlockZero->m_metaData);
|
||||
// // FEC does not work
|
||||
//// m_decoderSlots[decoderIndex].m_blockZero.m_metaData = recoveredBlockZero->m_metaData;
|
||||
//// m_decoderSlots[decoderIndex].m_metaRetrieved = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_frames[decoderIndex].m_blocks[blockIndex - 1] = m_decoderSlots[decoderIndex].m_recoveryBlocks[ir];
|
||||
// }
|
||||
//
|
||||
// qDebug() << "SDRdaemonFECBuffer::writeData:"
|
||||
// << " recovered block #" << blockIndex
|
||||
// << " i[0]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[0].i
|
||||
// << " q[0]: " << m_decoderSlots[decoderIndex].m_recoveryBlocks[ir].samples[0].q;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //printMeta("SDRdaemonFECBuffer::writeData", &m_decoderSlots[decoderIndex].m_blockZero.m_metaData);
|
||||
//
|
||||
//// if (m_decoderSlots[decoderIndex].m_metaRetrieved) // meta data has been retrieved (direct or recovery)
|
||||
//// {
|
||||
//// 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: new meta", &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;
|
||||
// }
|
||||
}
|
||||
|
||||
uint8_t *SDRdaemonFECBuffer::readData(int32_t length)
|
||||
|
|
|
@ -139,8 +139,6 @@ public:
|
|||
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;
|
||||
//qDebug() << "getBufferLengthInSecs: " << val << ":" << ret << ":" << rp;
|
||||
// conversion: [-100:-50[ : read leads (+) / [-50:0[ : read lags (-) / [0:50[ : read leads (+) / [50:100{ : read lags (-)
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -605,9 +605,8 @@ void SDRdaemonFECGui::updateWithStreamTime()
|
|||
if (m_framesDecodingStatus == 2) {
|
||||
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else if (m_framesDecodingStatus == 1) {
|
||||
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : blue; }");
|
||||
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : magenta; }");
|
||||
} else {
|
||||
// ui->allFramesDecoded->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
ui->allFramesDecoded->setStyleSheet("QToolButton { background:rgb(56,56,56); }");
|
||||
}
|
||||
|
||||
|
|
|
@ -309,7 +309,7 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frames status: green = all original received, blue = some recovered by FEC, none = some lost</string>
|
||||
<string>Frames status: green = all original received, pink = some recovered by FEC, none = some lost</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
|
Ładowanie…
Reference in New Issue