mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-07-29 20:22:26 -04:00
Merge pull request #1345 from srcejon/fix_1341
DATV Demod: Add support for LDPC on Windows
This commit is contained in:
commit
1057bc2882
@ -25,6 +25,7 @@ set(datv_SOURCES
|
||||
|
||||
set(ldpc_SOURCES
|
||||
ldpctool/tables_handler.cpp
|
||||
ldpctool/ldpcworker.cpp
|
||||
)
|
||||
|
||||
set(datv_HEADERS
|
||||
@ -55,6 +56,7 @@ set(ldpc_HEADERS
|
||||
ldpctool/dvb_s2_tables.h
|
||||
ldpctool/dvb_s2x_tables.h
|
||||
ldpctool/dvb_t2_tables.h
|
||||
ldpctool/ldpcworker.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
@ -69,16 +71,10 @@ include_directories(
|
||||
set(TARGET_NAME demoddatv)
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
|
||||
if (LINUX)
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${datv_SOURCES}
|
||||
${ldpc_SOURCES}
|
||||
)
|
||||
else()
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${datv_SOURCES}
|
||||
)
|
||||
endif()
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${datv_SOURCES}
|
||||
${ldpc_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
@ -94,13 +90,11 @@ target_link_libraries(${TARGET_NAME}
|
||||
${SWRESAMPLE_LIBRARIES}
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
add_executable(ldpctool
|
||||
ldpctool/ldpc_tool.cpp
|
||||
ldpctool/tables_handler.cpp
|
||||
)
|
||||
install(TARGETS ldpctool DESTINATION ${INSTALL_BIN_DIR})
|
||||
endif()
|
||||
add_executable(ldpctool
|
||||
ldpctool/ldpc_tool.cpp
|
||||
ldpctool/tables_handler.cpp
|
||||
)
|
||||
install(TARGETS ldpctool DESTINATION ${INSTALL_BIN_DIR})
|
||||
|
||||
if(FFMPEG_EXTERNAL)
|
||||
add_dependencies(${TARGET_NAME} ffmpeg)
|
||||
|
@ -300,13 +300,8 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba
|
||||
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
|
||||
connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect()));
|
||||
|
||||
#ifdef LINUX
|
||||
CRightClickEnabler *ldpcToolRightClickEnabler = new CRightClickEnabler(ui->softLDPC);
|
||||
connect(ldpcToolRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(ldpcToolSelect()));
|
||||
#else
|
||||
ui->softLDPC->setEnabled(false);
|
||||
ui->softLDPC->setStyleSheet("QCheckBox { color: gray }");
|
||||
#endif
|
||||
|
||||
ui->playerIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }");
|
||||
ui->udpIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }");
|
||||
@ -377,7 +372,6 @@ void DATVDemodGUI::displaySettings()
|
||||
ui->maxBitflipsLabel->setStyleSheet("QLabel { color: white }");
|
||||
}
|
||||
|
||||
#ifdef LINUX
|
||||
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
|
||||
{
|
||||
ui->softLDPC->setEnabled(false);
|
||||
@ -388,7 +382,6 @@ void DATVDemodGUI::displaySettings()
|
||||
ui->softLDPC->setEnabled(true);
|
||||
ui->softLDPC->setStyleSheet("QCheckBox { color: white }");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
|
||||
{
|
||||
@ -658,7 +651,6 @@ void DATVDemodGUI::on_cmbStandard_currentIndexChanged(int index)
|
||||
ui->maxBitflipsLabel->setStyleSheet("QLabel { color: white }");
|
||||
}
|
||||
|
||||
#ifdef LINUX
|
||||
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
|
||||
{
|
||||
ui->softLDPC->setEnabled(false);
|
||||
@ -669,7 +661,6 @@ void DATVDemodGUI::on_cmbStandard_currentIndexChanged(int index)
|
||||
ui->softLDPC->setEnabled(true);
|
||||
ui->softLDPC->setStyleSheet("QCheckBox { color: white }");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
|
||||
{
|
||||
@ -710,18 +701,14 @@ void DATVDemodGUI::on_cmbFEC_currentIndexChanged(int arg1)
|
||||
|
||||
void DATVDemodGUI::on_softLDPC_clicked()
|
||||
{
|
||||
#ifdef LINUX
|
||||
m_settings.m_softLDPC = ui->softLDPC->isChecked();
|
||||
applySettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DATVDemodGUI::on_maxBitflips_valueChanged(int value)
|
||||
{
|
||||
#ifdef LINUX
|
||||
m_settings.m_maxBitflips = value;
|
||||
applySettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DATVDemodGUI::on_chkViterbi_clicked()
|
||||
|
@ -27,6 +27,12 @@
|
||||
|
||||
#include "datvdemodsettings.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DEFAULT_LDPCTOOLPATH "C:/Program Files/SDRangel/ldpctool.exe"
|
||||
#else
|
||||
#define DEFAULT_LDPCTOOLPATH "/opt/install/sdrangel/bin/ldpctool"
|
||||
#endif
|
||||
|
||||
DATVDemodSettings::DATVDemodSettings() :
|
||||
m_channelMarker(nullptr),
|
||||
m_rollupState(nullptr)
|
||||
@ -44,7 +50,7 @@ void DATVDemodSettings::resetToDefaults()
|
||||
m_modulation = BPSK;
|
||||
m_fec = FEC12;
|
||||
m_softLDPC = false;
|
||||
m_softLDPCToolPath = "/opt/install/sdrangel/bin/ldpctool";
|
||||
m_softLDPCToolPath = DEFAULT_LDPCTOOLPATH;
|
||||
m_softLDPCMaxTrials = 8;
|
||||
m_maxBitflips = 0;
|
||||
m_symbolRate = 250000;
|
||||
@ -208,7 +214,7 @@ bool DATVDemodSettings::deserialize(const QByteArray& data)
|
||||
|
||||
d.readBool(32, &m_softLDPC, false);
|
||||
d.readS32(33, &m_maxBitflips, 0);
|
||||
d.readString(34, &m_softLDPCToolPath, "/opt/install/sdrangel/bin/ldpctool");
|
||||
d.readString(34, &m_softLDPCToolPath, DEFAULT_LDPCTOOLPATH);
|
||||
d.readS32(35, &tmp, 8);
|
||||
m_softLDPCMaxTrials = tmp < 1 ? 1 : tmp > m_softLDPCMaxMaxTrials ? m_softLDPCMaxMaxTrials : tmp;
|
||||
d.readBool(36, &m_playerEnable, true);
|
||||
|
@ -397,7 +397,6 @@ void DATVDemodSink::CleanUpDATVFramework()
|
||||
delete (leansdr::s2_fecdec<bool, leansdr::hard_sb>*) r_fecdec;
|
||||
}
|
||||
|
||||
#ifdef LINUX
|
||||
if (r_fecdecsoft != nullptr) {
|
||||
delete (leansdr::s2_fecdec_soft<leansdr::llr_t,leansdr::llr_sb>*) r_fecdecsoft;
|
||||
}
|
||||
@ -405,7 +404,6 @@ void DATVDemodSink::CleanUpDATVFramework()
|
||||
if (r_fecdechelper != nullptr) {
|
||||
delete (leansdr::s2_fecdec_helper<leansdr::llr_t,leansdr::llr_sb>*) r_fecdechelper;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_deframer != nullptr) {
|
||||
delete (leansdr::s2_deframer*) p_deframer;
|
||||
@ -515,10 +513,8 @@ void DATVDemodSink::ResetDATVFrameworkPointers()
|
||||
p_bbframes = nullptr;
|
||||
p_s2_deinterleaver = nullptr;
|
||||
r_fecdec = nullptr;
|
||||
#ifdef LINUX
|
||||
r_fecdecsoft = nullptr;
|
||||
r_fecdechelper = nullptr;
|
||||
#endif
|
||||
p_deframer = nullptr;
|
||||
r_scope_symbols_dvbs2 = nullptr;
|
||||
}
|
||||
@ -1101,7 +1097,6 @@ void DATVDemodSink::InitDATVS2Framework()
|
||||
p_vbitcount= new leansdr::pipebuf<int>(m_objScheduler, "Bits processed", BUF_S2PACKETS);
|
||||
p_verrcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits corrected", BUF_S2PACKETS);
|
||||
|
||||
#ifdef LINUX
|
||||
bool commandFileValid = false;
|
||||
|
||||
if (QFileInfo::exists(m_settings.m_softLDPCToolPath))
|
||||
@ -1110,7 +1105,7 @@ void DATVDemodSink::InitDATVS2Framework()
|
||||
commandFileValid = fileInfo.isExecutable();
|
||||
}
|
||||
|
||||
if (m_settings.m_softLDPC && commandFileValid)
|
||||
if (m_settings.m_softLDPC /*&& commandFileValid*/)
|
||||
{
|
||||
#if 0
|
||||
// Doesn't work...
|
||||
@ -1178,25 +1173,6 @@ void DATVDemodSink::InitDATVS2Framework()
|
||||
leansdr::s2_fecdec<bool, leansdr::hard_sb> *fecdec = (leansdr::s2_fecdec<bool, leansdr::hard_sb> * ) r_fecdec;
|
||||
fecdec->bitflips=m_settings.m_maxBitflips;
|
||||
}
|
||||
#else
|
||||
// Bit-flipping mode.
|
||||
// Deinterleave into hard bits.
|
||||
p_fecframes = new leansdr::pipebuf< leansdr::fecframe<leansdr::hard_sb> >(m_objScheduler, "FEC frames", BUF_FRAMES);
|
||||
p_s2_deinterleaver = new leansdr::s2_deinterleaver<leansdr::llr_ss,leansdr::hard_sb>(
|
||||
m_objScheduler,
|
||||
*(leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> > *) p_slots_dvbs2,
|
||||
*(leansdr::pipebuf< leansdr::fecframe<leansdr::hard_sb> > * ) p_fecframes
|
||||
);
|
||||
r_fecdec = new leansdr::s2_fecdec<bool, leansdr::hard_sb>(
|
||||
m_objScheduler,
|
||||
*(leansdr::pipebuf< leansdr::fecframe<leansdr::hard_sb> > * ) p_fecframes,
|
||||
*(leansdr::pipebuf<leansdr::bbframe> *) p_bbframes,
|
||||
p_vbitcount,
|
||||
p_verrcount
|
||||
);
|
||||
leansdr::s2_fecdec<bool, leansdr::hard_sb> *fecdec = (leansdr::s2_fecdec<bool, leansdr::hard_sb> * ) r_fecdec;
|
||||
fecdec->bitflips=m_settings.m_maxBitflips;
|
||||
#endif
|
||||
|
||||
// Deframe BB frames to TS packets
|
||||
p_lock = new leansdr::pipebuf<int> (m_objScheduler, "lock", BUF_SLOW);
|
||||
|
@ -58,7 +58,11 @@ void DatvDvbS2LdpcDialog::on_showFileDialog_clicked(bool checked)
|
||||
|
||||
QFileDialog fileDialog(this, "Select LDPC tool");
|
||||
fileDialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
#ifdef _MSC_VER
|
||||
fileDialog.setNameFilter("*.exe");
|
||||
#else
|
||||
fileDialog.setFilter(QDir::Executable | QDir::Files);
|
||||
#endif
|
||||
fileDialog.selectFile(m_fileName);
|
||||
|
||||
if (fileDialog.exec() == QDialog::Accepted)
|
||||
|
@ -8,10 +8,39 @@ Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
|
||||
#define LAYERED_DECODER_HH
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include "ldpc.h"
|
||||
|
||||
namespace ldpctool {
|
||||
|
||||
class LDPCUtil
|
||||
{
|
||||
public:
|
||||
#ifndef _MSC_VER
|
||||
static void *aligned_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
return aligned_alloc(alignment, size);
|
||||
}
|
||||
|
||||
static void aligned_free(void *mem)
|
||||
{
|
||||
free(mem);
|
||||
}
|
||||
#else
|
||||
static void *aligned_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
return _aligned_malloc(size, alignment);
|
||||
}
|
||||
|
||||
static void aligned_free(void *mem)
|
||||
{
|
||||
_aligned_free(mem);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename TYPE, typename ALG>
|
||||
class LDPCDecoder
|
||||
{
|
||||
@ -113,8 +142,8 @@ public:
|
||||
}
|
||||
LT = ldpc->links_total();
|
||||
delete ldpc;
|
||||
bnl = reinterpret_cast<TYPE *>(aligned_alloc(sizeof(TYPE), sizeof(TYPE) * LT));
|
||||
pty = reinterpret_cast<TYPE *>(aligned_alloc(sizeof(TYPE), sizeof(TYPE) * R));
|
||||
bnl = reinterpret_cast<TYPE *>(LDPCUtil::aligned_malloc(sizeof(TYPE), sizeof(TYPE) * LT));
|
||||
pty = reinterpret_cast<TYPE *>(LDPCUtil::aligned_malloc(sizeof(TYPE), sizeof(TYPE) * R));
|
||||
uint16_t *tmp = new uint16_t[R * CNL];
|
||||
for (int i = 0; i < q; ++i)
|
||||
for (int j = 0; j < M; ++j)
|
||||
@ -139,8 +168,8 @@ public:
|
||||
~LDPCDecoder()
|
||||
{
|
||||
if (initialized) {
|
||||
free(bnl);
|
||||
free(pty);
|
||||
LDPCUtil::aligned_free(bnl);
|
||||
LDPCUtil::aligned_free(pty);
|
||||
delete[] cnc;
|
||||
delete[] pos;
|
||||
delete[] inp;
|
||||
|
@ -7,7 +7,14 @@ Copyright 2019 <pabr@pabr.org>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
@ -21,6 +28,7 @@ Copyright 2019 <pabr@pabr.org>
|
||||
#include "algorithms.h"
|
||||
#include "ldpc.h"
|
||||
|
||||
|
||||
#if 0
|
||||
#include "flooding_decoder.h"
|
||||
static const int DEFAULT_TRIALS = 50;
|
||||
@ -129,7 +137,7 @@ int main(int argc, char **argv)
|
||||
|
||||
int BLOCKS = batch_size;
|
||||
ldpctool::code_type *code = new ldpctool::code_type[BLOCKS * CODE_LEN];
|
||||
void *aligned_buffer = aligned_alloc(sizeof(ldpctool::simd_type), sizeof(ldpctool::simd_type) * CODE_LEN);
|
||||
void *aligned_buffer = ldpctool::LDPCUtil::aligned_malloc(sizeof(ldpctool::simd_type), sizeof(ldpctool::simd_type) * CODE_LEN);
|
||||
ldpctool::simd_type *simd = reinterpret_cast<ldpctool::simd_type *>(aligned_buffer);
|
||||
|
||||
// Expect LLR values in int8_t format.
|
||||
@ -193,9 +201,6 @@ int main(int argc, char **argv)
|
||||
code[(j + n) * CODE_LEN + i] = reinterpret_cast<ldpctool::code_type *>(simd + i)[n];
|
||||
}
|
||||
|
||||
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||||
assert(!std::isnan(code[i]));
|
||||
|
||||
for (ssize_t pos = 0; pos < iosize;)
|
||||
{
|
||||
ssize_t nw = write(1, code + pos, iosize - pos);
|
||||
@ -212,7 +217,7 @@ int main(int argc, char **argv)
|
||||
|
||||
delete ldpc;
|
||||
|
||||
free(aligned_buffer);
|
||||
ldpctool::LDPCUtil::aligned_free(aligned_buffer);
|
||||
delete[] code;
|
||||
|
||||
return 0;
|
||||
|
156
plugins/channelrx/demoddatv/ldpctool/ldpcworker.cpp
Normal file
156
plugins/channelrx/demoddatv/ldpctool/ldpcworker.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
LDPC testbench
|
||||
Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
|
||||
|
||||
Transformed into external decoder for third-party applications
|
||||
Copyright 2019 <pabr@pabr.org>
|
||||
|
||||
Transformed again in to a Qt worker.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include "ldpcworker.h"
|
||||
|
||||
LDPCWorker::LDPCWorker(int modcod, int maxTrials, int batchSize, bool shortFrames) :
|
||||
m_maxTrials(maxTrials),
|
||||
m_aligned_buffer(nullptr),
|
||||
m_ldpc(nullptr),
|
||||
m_code(nullptr),
|
||||
m_simd(nullptr)
|
||||
{
|
||||
// DVB-S2 MODCOD definitions
|
||||
static const char *mc_tabnames[2][32] = { // [shortframes][modcod]
|
||||
{// Normal frames
|
||||
0, "B1", "B2", "B3", "B4", "B5", "B6", "B7",
|
||||
"B8", "B9", "B10", "B11", "B5", "B6", "B7", "B9",
|
||||
"B10", "B11", "B6", "B7", "B8", "B9", "B10", "B11",
|
||||
"B7", "B8", "B8", "B10", "B11", 0, 0, 0},
|
||||
{// Short frames
|
||||
0, "C1", "C2", "C3", "C4", "C5", "C6", "C7",
|
||||
"C8", "C9", "C10", 0, "C5", "C6", "C7", "C9",
|
||||
"C10", 0, "C6", "C7", "C8", "C9", "C10", 0,
|
||||
"C7", "C8", "C8", "C10", 0, 0, 0, 0}};
|
||||
|
||||
const char *tabname = mc_tabnames[shortFrames][modcod];
|
||||
if (!tabname)
|
||||
{
|
||||
qCritical() << "LDPCWorker::LDPCWorker: unsupported modcod";
|
||||
return;
|
||||
}
|
||||
|
||||
m_ldpc = ldpctool::create_ldpc((char *)"S2", tabname[0], atoi(tabname + 1));
|
||||
|
||||
if (!m_ldpc)
|
||||
{
|
||||
qCritical() << "LDPCWorker::LDPCWorker: no such table!";
|
||||
return;
|
||||
}
|
||||
|
||||
m_codeLen = m_ldpc->code_len();
|
||||
m_dataLen = m_ldpc->data_len();
|
||||
|
||||
m_decode.init(m_ldpc);
|
||||
|
||||
BLOCKS = batchSize;
|
||||
m_code = new ldpctool::code_type[BLOCKS * m_codeLen];
|
||||
m_aligned_buffer = ldpctool::LDPCUtil::aligned_malloc(sizeof(ldpctool::simd_type), sizeof(ldpctool::simd_type) * m_codeLen);
|
||||
m_simd = reinterpret_cast<ldpctool::simd_type *>(m_aligned_buffer);
|
||||
|
||||
// Expect LLR values in int8_t format.
|
||||
if (sizeof(ldpctool::code_type) != 1) {
|
||||
qCritical() << "LDPCWorker::LDPCWorker: Unsupported code_type";
|
||||
}
|
||||
}
|
||||
|
||||
LDPCWorker::~LDPCWorker()
|
||||
{
|
||||
m_condOut.wakeAll();
|
||||
delete m_ldpc;
|
||||
|
||||
ldpctool::LDPCUtil::aligned_free(m_aligned_buffer);
|
||||
delete[] m_code;
|
||||
}
|
||||
|
||||
void LDPCWorker::process(QByteArray data)
|
||||
{
|
||||
int trials_count = 0;
|
||||
int max_count = 0;
|
||||
int num_decodes = 0;
|
||||
|
||||
int iosize = m_codeLen * sizeof(*m_code);
|
||||
|
||||
m_mutexIn.lock();
|
||||
m_dataIn.append(data);
|
||||
|
||||
if (m_dataIn.size() >= BLOCKS)
|
||||
{
|
||||
for (int j = 0; j < BLOCKS; j++)
|
||||
{
|
||||
QByteArray inputData = m_dataIn.takeAt(0);
|
||||
memcpy(m_code + j * m_codeLen, inputData.data(), inputData.size());
|
||||
}
|
||||
m_mutexIn.unlock();
|
||||
|
||||
for (int j = 0; j < BLOCKS; j += ldpctool::SIMD_WIDTH)
|
||||
{
|
||||
int blocks = j + ldpctool::SIMD_WIDTH > BLOCKS ? BLOCKS - j : ldpctool::SIMD_WIDTH;
|
||||
|
||||
for (int n = 0; n < blocks; ++n)
|
||||
for (int i = 0; i < m_codeLen; ++i)
|
||||
reinterpret_cast<ldpctool::code_type *>(m_simd + i)[n] = m_code[(j + n) * m_codeLen + i];
|
||||
|
||||
int count = m_decode(m_simd, m_simd + m_dataLen, m_maxTrials, blocks);
|
||||
num_decodes++;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
trials_count += m_maxTrials;
|
||||
max_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
trials_count += m_maxTrials - count;
|
||||
}
|
||||
|
||||
if (num_decodes == 20)
|
||||
{
|
||||
qDebug() << "ldpc_tool: trials: " << ((trials_count*100)/(num_decodes*m_maxTrials)) << "% max: "
|
||||
<< ((max_count*100)/num_decodes) << "% at converging to a code word (max trials: " << m_maxTrials << ")";
|
||||
trials_count = 0;
|
||||
max_count = 0;
|
||||
num_decodes = 0;
|
||||
}
|
||||
|
||||
for (int n = 0; n < blocks; ++n)
|
||||
for (int i = 0; i < m_codeLen; ++i)
|
||||
m_code[(j + n) * m_codeLen + i] = reinterpret_cast<ldpctool::code_type *>(m_simd + i)[n];
|
||||
}
|
||||
|
||||
m_mutexOut.lock();
|
||||
for (int j = 0; j < BLOCKS; j++)
|
||||
{
|
||||
QByteArray outputData((const char *)m_code + j * m_codeLen, iosize);
|
||||
m_dataOut.append(outputData);
|
||||
}
|
||||
m_condOut.wakeAll();
|
||||
m_mutexOut.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mutexIn.unlock();
|
||||
}
|
||||
}
|
94
plugins/channelrx/demoddatv/ldpctool/ldpcworker.h
Normal file
94
plugins/channelrx/demoddatv/ldpctool/ldpcworker.h
Normal file
@ -0,0 +1,94 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 //
|
||||
// (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. 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 <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "testbench.h"
|
||||
#include "algorithms.h"
|
||||
#include "ldpc.h"
|
||||
#include "layered_decoder.h"
|
||||
|
||||
class LDPCWorker : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LDPCWorker(int modcod, int maxTrials, int batchSize, bool shortFrames);
|
||||
~LDPCWorker();
|
||||
|
||||
// Are we busy
|
||||
bool busy()
|
||||
{
|
||||
QMutexLocker locker(&m_mutexIn);
|
||||
return m_dataIn.size() >= BLOCKS;
|
||||
}
|
||||
|
||||
// Is processed data available?
|
||||
bool dataAvailable()
|
||||
{
|
||||
QMutexLocker locker(&m_mutexOut);
|
||||
return !m_dataOut.isEmpty();
|
||||
}
|
||||
|
||||
// Get processed data
|
||||
QByteArray data()
|
||||
{
|
||||
m_mutexOut.lock();
|
||||
if (m_dataOut.isEmpty()) {
|
||||
m_condOut.wait(&m_mutexOut);
|
||||
}
|
||||
QByteArray data = m_dataOut.takeAt(0);
|
||||
m_mutexOut.unlock();
|
||||
return data;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void process(QByteArray data);
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
QMutex m_mutexIn;
|
||||
QMutex m_mutexOut;
|
||||
QWaitCondition m_condOut;
|
||||
QList<QByteArray> m_dataIn;
|
||||
QList<QByteArray> m_dataOut;
|
||||
int m_maxTrials;
|
||||
int BLOCKS;
|
||||
int m_codeLen;
|
||||
int m_dataLen;
|
||||
void *m_aligned_buffer;
|
||||
|
||||
ldpctool::LDPCInterface *m_ldpc;
|
||||
ldpctool::code_type *m_code;
|
||||
ldpctool::simd_type *m_simd;
|
||||
|
||||
typedef ldpctool::NormalUpdate<ldpctool::simd_type> update_type;
|
||||
//typedef SelfCorrectedUpdate<simd_type> update_type;
|
||||
|
||||
//typedef MinSumAlgorithm<simd_type, update_type> algorithm_type;
|
||||
//typedef OffsetMinSumAlgorithm<simd_type, update_type, FACTOR> algorithm_type;
|
||||
typedef ldpctool::MinSumCAlgorithm<ldpctool::simd_type, update_type, ldpctool::FACTOR> algorithm_type;
|
||||
//typedef LogDomainSPA<simd_type, update_type> algorithm_type;
|
||||
//typedef LambdaMinAlgorithm<simd_type, update_type, 3> algorithm_type;
|
||||
//typedef SumProductAlgorithm<simd_type, update_type> algorithm_type;
|
||||
|
||||
ldpctool::LDPCDecoder<ldpctool::simd_type, algorithm_type> m_decode;
|
||||
};
|
@ -4,6 +4,8 @@ LDPC testbench
|
||||
Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
#include "simd.h"
|
||||
|
@ -54,13 +54,18 @@
|
||||
#include "ldpc.h"
|
||||
#include "sdr.h"
|
||||
|
||||
#ifdef LINUX
|
||||
#include <signal.h>
|
||||
#ifdef LINUX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
#include "ldpctool/layered_decoder.h"
|
||||
#include "ldpctool/testbench.h"
|
||||
#include "ldpctool/algorithms.h"
|
||||
#endif
|
||||
#include "ldpctool/ldpcworker.h"
|
||||
|
||||
namespace leansdr
|
||||
{
|
||||
@ -3041,8 +3046,6 @@ struct s2_fecdec : runnable
|
||||
pipewriter<int> *bitcount, *errcount;
|
||||
}; // s2_fecdec
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
// Soft LDPC decoder
|
||||
// Internally implemented LDPC tool. Replaces external LDPC decoder
|
||||
|
||||
@ -3230,6 +3233,8 @@ private:
|
||||
T q[SIZE];
|
||||
};
|
||||
|
||||
#if defined(USE_LDPC_TOOL) && !defined(_MSC_VER)
|
||||
|
||||
template <typename SOFTBIT, typename SOFTBYTE>
|
||||
struct s2_fecdec_helper : runnable
|
||||
{
|
||||
@ -3610,7 +3615,280 @@ struct s2_fecdec_helper : runnable
|
||||
std::deque<int> errcount_q;
|
||||
pipewriter<int> *bitcount, *errcount;
|
||||
}; // s2_fecdec_helper
|
||||
|
||||
#else // USE_LDPC_TOOL
|
||||
|
||||
template <typename SOFTBIT, typename SOFTBYTE>
|
||||
struct s2_fecdec_helper : runnable
|
||||
{
|
||||
int batch_size;
|
||||
int nhelpers;
|
||||
bool must_buffer;
|
||||
int max_trials;
|
||||
|
||||
s2_fecdec_helper(
|
||||
scheduler *sch,
|
||||
pipebuf<fecframe<SOFTBYTE>> &_in,
|
||||
pipebuf<bbframe> &_out,
|
||||
const char *_command,
|
||||
pipebuf<int> *_bitcount = nullptr,
|
||||
pipebuf<int> *_errcount = nullptr
|
||||
) :
|
||||
runnable(sch, "S2 fecdec io"),
|
||||
batch_size(16),
|
||||
nhelpers(1),
|
||||
must_buffer(false),
|
||||
max_trials(8),
|
||||
in(_in),
|
||||
out(_out),
|
||||
bitcount(opt_writer(_bitcount, 1)),
|
||||
errcount(opt_writer(_errcount, 1))
|
||||
{
|
||||
command = strdup(_command);
|
||||
|
||||
for (int mc = 0; mc < 32; ++mc) {
|
||||
for (int sf = 0; sf < 2; ++sf) {
|
||||
pools[mc][sf].procs = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~s2_fecdec_helper()
|
||||
{
|
||||
free(command);
|
||||
killall(); // also deletes pools[mc][sf].procs if necessary
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
// Send work until all helpers block.
|
||||
while (in.readable() >= 1 && !jobs.full())
|
||||
{
|
||||
if ((bbframe_q.size() != 0) && (out.writable() >= 1))
|
||||
{
|
||||
bbframe *pout = out.wr();
|
||||
pout->pls = bbframe_q.front().pls;
|
||||
std::copy(bbframe_q.front().bytes, bbframe_q.front().bytes + (58192 / 8), pout->bytes);
|
||||
bbframe_q.pop_front();
|
||||
out.written(1);
|
||||
}
|
||||
|
||||
if ((bitcount_q.size() != 0) && opt_writable(bitcount, 1))
|
||||
{
|
||||
opt_write(bitcount, bitcount_q.front());
|
||||
bitcount_q.pop_front();
|
||||
}
|
||||
|
||||
if ((errcount_q.size() != 0) && opt_writable(errcount, 1))
|
||||
{
|
||||
opt_write(errcount, errcount_q.front());
|
||||
errcount_q.pop_front();
|
||||
}
|
||||
|
||||
if (!jobs.empty() && jobs.peek()->h->b_out) {
|
||||
receive_frame(jobs.get());
|
||||
}
|
||||
|
||||
send_frame(in.rd());
|
||||
in.read(1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct helper_instance
|
||||
{
|
||||
QThread *m_thread;
|
||||
LDPCWorker *m_worker;
|
||||
int batch_size;
|
||||
int b_in; // Jobs in input queue
|
||||
int b_out; // Jobs in output queue
|
||||
};
|
||||
struct pool
|
||||
{
|
||||
helper_instance *procs; // nullptr or [nprocs]
|
||||
int nprocs;
|
||||
int shift;
|
||||
} pools[32][2]; // [modcod][sf]
|
||||
struct helper_job
|
||||
{
|
||||
s2_pls pls;
|
||||
helper_instance *h;
|
||||
};
|
||||
|
||||
simplequeue<helper_job, 1024> jobs;
|
||||
|
||||
// Try to send a frame. Return false if helper was busy.
|
||||
bool send_frame(fecframe<SOFTBYTE> *pin)
|
||||
{
|
||||
pool *p = get_pool(&pin->pls);
|
||||
|
||||
for (int j = 0; j < p->nprocs; ++j)
|
||||
{
|
||||
int i = (p->shift + j) % p->nprocs;
|
||||
helper_instance *h = &p->procs[i];
|
||||
int iosize = (pin->pls.framebits() / 8) * sizeof(SOFTBYTE);
|
||||
|
||||
if (h->m_worker->busy()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray data((char *)pin->bytes, iosize);
|
||||
QMetaObject::invokeMethod(h->m_worker, "process", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
||||
|
||||
p->shift = i;
|
||||
helper_job *job = jobs.put();
|
||||
job->pls = pin->pls;
|
||||
job->h = h;
|
||||
++h->b_in;
|
||||
|
||||
if (h->b_in >= h->batch_size)
|
||||
{
|
||||
h->b_in -= h->batch_size;
|
||||
h->b_out += h->batch_size;
|
||||
}
|
||||
|
||||
return true; // done sent to worker
|
||||
}
|
||||
|
||||
fprintf(stderr, "s2_fecdec_helper::send_frame: WARNING: all %d workers were busy: modcod=%d sf=%d)\n",
|
||||
p->nprocs, pin->pls.modcod, pin->pls.sf);
|
||||
return false; // all workers were busy
|
||||
}
|
||||
|
||||
// Return a pool of running helpers for a given modcod.
|
||||
pool *get_pool(const s2_pls *pls)
|
||||
{
|
||||
pool *p = &pools[pls->modcod][pls->sf];
|
||||
|
||||
if (!p->procs)
|
||||
{
|
||||
fprintf(stderr, "s2_fecdec_helper::get_pool: allocate %d workers: modcod=%d sf=%d\n",
|
||||
nhelpers, pls->modcod, pls->sf);
|
||||
p->procs = new helper_instance[nhelpers];
|
||||
|
||||
for (int i = 0; i < nhelpers; ++i) {
|
||||
spawn_helper(&p->procs[i], pls);
|
||||
}
|
||||
|
||||
p->nprocs = nhelpers;
|
||||
p->shift = 0;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void killall()
|
||||
{
|
||||
qDebug() << "s2_fecdec_helper::killall";
|
||||
|
||||
for (int i = 0; i < 32; i++) // all MODCODs
|
||||
{
|
||||
for (int j = 0; j < 2; j++) // long and short frames
|
||||
{
|
||||
pool *p = &pools[i][j];
|
||||
|
||||
if (p->procs)
|
||||
{
|
||||
for (int i = 0; i < p->nprocs; ++i)
|
||||
{
|
||||
helper_instance *h = &p->procs[i];
|
||||
h->m_thread->quit();
|
||||
h->m_thread->wait();
|
||||
delete h->m_thread;
|
||||
h->m_thread = nullptr;
|
||||
delete h->m_worker;
|
||||
h->m_worker = nullptr;
|
||||
}
|
||||
|
||||
delete p->procs;
|
||||
p->procs = nullptr;
|
||||
p->nprocs = 0;
|
||||
}
|
||||
} // long and short frames
|
||||
} // all MODCODs
|
||||
}
|
||||
|
||||
// Spawn a helper thread.
|
||||
void spawn_helper(helper_instance *h, const s2_pls *pls)
|
||||
{
|
||||
qDebug() << "s2_fecdec_helper: Spawning LDPC thread: modcod=" << pls->modcod << " sf=" << pls->sf;
|
||||
h->m_thread = new QThread();
|
||||
h->m_worker = new LDPCWorker(pls->modcod, max_trials, batch_size, pls->sf);
|
||||
h->m_worker->moveToThread(h->m_thread);
|
||||
h->batch_size = batch_size;
|
||||
h->b_in = h->b_out = 0;
|
||||
h->m_thread->start();
|
||||
}
|
||||
|
||||
// Receive a finished job.
|
||||
void receive_frame(const helper_job *job)
|
||||
{
|
||||
// Read corrected frame from helper
|
||||
const s2_pls *pls = &job->pls;
|
||||
int iosize = (pls->framebits() / 8) * sizeof(ldpc_buf[0]);
|
||||
|
||||
// Blocking read - do we need to return faster?
|
||||
// If so, call m_worker->dataAvailable()
|
||||
QByteArray data = job->h->m_worker->data();
|
||||
memcpy(ldpc_buf, data.data(), data.size());
|
||||
|
||||
--job->h->b_out;
|
||||
// Decode BCH.
|
||||
const modcod_info *mcinfo = check_modcod(job->pls.modcod);
|
||||
const fec_info *fi = &fec_infos[job->pls.sf][mcinfo->rate];
|
||||
uint8_t *hardbytes = softbytes_harden(ldpc_buf, fi->kldpc / 8, bch_buf);
|
||||
size_t cwbytes = fi->kldpc / 8;
|
||||
//size_t msgbytes = fi->Kbch / 8;
|
||||
//size_t chkbytes = cwbytes - msgbytes;
|
||||
bch_interface *bch = s2bch.bchs[job->pls.sf][mcinfo->rate];
|
||||
int ncorr = bch->decode(hardbytes, cwbytes);
|
||||
|
||||
if (sch->debug2) {
|
||||
fprintf(stderr, "BCHCORR = %d\n", ncorr);
|
||||
}
|
||||
|
||||
bool corrupted = (ncorr < 0);
|
||||
// Report VBER
|
||||
bitcount_q.push_back(fi->Kbch);
|
||||
//opt_write(bitcount, fi->Kbch);
|
||||
errcount_q.push_front((ncorr >= 0) ? ncorr : fi->Kbch);
|
||||
//opt_write(errcount, (ncorr >= 0) ? ncorr : fi->Kbch);
|
||||
#if 0
|
||||
// TBD Some decoders want the bad packets.
|
||||
if ( corrupted ) {
|
||||
fprintf(stderr, "Passing bad frame\n");
|
||||
corrupted = false;
|
||||
}
|
||||
#endif
|
||||
if (!corrupted)
|
||||
{
|
||||
// Descramble and output
|
||||
bbframe_q.emplace_back();
|
||||
//bbframe *pout = out.wr();
|
||||
bbframe_q.back().pls = job->pls;
|
||||
bbscrambling.transform(hardbytes, fi->Kbch / 8, bbframe_q.back().bytes);
|
||||
//out.written(1);
|
||||
}
|
||||
|
||||
if (sch->debug) {
|
||||
fprintf(stderr, "%c", corrupted ? '!' : ncorr ? '.' : '_');
|
||||
}
|
||||
}
|
||||
|
||||
pipereader<fecframe<SOFTBYTE>> in;
|
||||
pipewriter<bbframe> out;
|
||||
char *command;
|
||||
SOFTBYTE ldpc_buf[64800 / 8];
|
||||
uint8_t bch_buf[64800 / 8]; // Temp storage for hardening before BCH
|
||||
s2_bch_engines s2bch;
|
||||
s2_bbscrambling bbscrambling;
|
||||
std::deque<bbframe> bbframe_q;
|
||||
std::deque<int> bitcount_q;
|
||||
std::deque<int> errcount_q;
|
||||
pipewriter<int> *bitcount, *errcount;
|
||||
}; // s2_fecdec_helper
|
||||
|
||||
#endif // USE_LDPC_TOOL
|
||||
|
||||
// S2 FRAMER
|
||||
// EN 302 307-1 section 5.1 Mode adaptation
|
||||
@ -3854,7 +4132,6 @@ private:
|
||||
{
|
||||
handle_ts(data, dfl, syncd, sync);
|
||||
}
|
||||
#ifdef LINUX
|
||||
else if (streamtype == 1)
|
||||
{
|
||||
if (fd_gse >= 0)
|
||||
@ -3874,7 +4151,6 @@ private:
|
||||
fprintf(stderr, "Unrecognized bbframe\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void handle_ts(uint8_t *data, uint16_t dfl, uint16_t syncd, uint8_t sync)
|
||||
|
@ -183,13 +183,11 @@ The controls specific to DVB-S are disabled and greyed out. These are: Fast Lock
|
||||
|
||||
<h5>B.2b.6: DVB-S2 specific - Soft LDPC decoder</h5>
|
||||
|
||||
This is for experimenters only working in Linux. It can be used to decode signals lower that ~10 db MER which is the limit of LDPC hard decoding as explained next (B.2b.7). Video degrades progressively down to about 7.5 dB MER and drops below this limit.
|
||||
|
||||
Runs the `ldpctool` program for soft LDPC decoding. Frames are sent on its standard input and decoded frames retrieved from its standard output. Two processes executing `ldpctool` are spawned but so far it seems that only one is effectively used.
|
||||
It can be used to decode signals lower that ~10 db MER which is the limit of LDPC hard decoding as explained next (B.2b.7). Video degrades progressively down to about 7.5 dB MER and drops below this limit.
|
||||
|
||||
Right clicking on this control opens a dialog where you can choose:
|
||||
|
||||
- The `ldpctool` executable. You have to use the `ldpctool` binary produced by the build of SDRangel.
|
||||
- The `ldpctool` executable. Obsolete.
|
||||
- The maximum of retries in LDPC decoding from 1 to 8.
|
||||
|
||||
<h5>B.2b.7: DVB-S2 specific - LDPC maximum number of bit flips allowed</h5>
|
||||
|
Loading…
x
Reference in New Issue
Block a user