1
0
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:
Edouard Griffiths 2022-07-19 15:15:03 +02:00 committed by GitHub
commit 1057bc2882
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 603 additions and 76 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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();
}
}

View 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;
};

View File

@ -4,6 +4,8 @@ LDPC testbench
Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
*/
#pragma once
#include <cstdint>
#include <complex>
#include "simd.h"

View File

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

View File

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