1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-02-20 04:32:17 -05:00

DATV Demod: Add support for LDPC on Windows. Use Qt worker thread instead of external ldpc_tool process.

This commit is contained in:
Jon Beniston 2022-07-18 16:40:00 +01:00
parent a65c9458ed
commit ff26ece347
10 changed files with 353 additions and 73 deletions

View File

@ -25,6 +25,7 @@ set(datv_SOURCES
set(ldpc_SOURCES set(ldpc_SOURCES
ldpctool/tables_handler.cpp ldpctool/tables_handler.cpp
ldpctool/ldpcworker.cpp
) )
set(datv_HEADERS set(datv_HEADERS
@ -55,6 +56,7 @@ set(ldpc_HEADERS
ldpctool/dvb_s2_tables.h ldpctool/dvb_s2_tables.h
ldpctool/dvb_s2x_tables.h ldpctool/dvb_s2x_tables.h
ldpctool/dvb_t2_tables.h ldpctool/dvb_t2_tables.h
ldpctool/ldpcworker.h
) )
include_directories( include_directories(
@ -69,16 +71,10 @@ include_directories(
set(TARGET_NAME demoddatv) set(TARGET_NAME demoddatv)
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
if (LINUX) add_library(${TARGET_NAME} SHARED
add_library(${TARGET_NAME} SHARED ${datv_SOURCES}
${datv_SOURCES} ${ldpc_SOURCES}
${ldpc_SOURCES} )
)
else()
add_library(${TARGET_NAME} SHARED
${datv_SOURCES}
)
endif()
target_link_libraries(${TARGET_NAME} target_link_libraries(${TARGET_NAME}
Qt5::Core Qt5::Core
@ -94,13 +90,11 @@ target_link_libraries(${TARGET_NAME}
${SWRESAMPLE_LIBRARIES} ${SWRESAMPLE_LIBRARIES}
) )
if (LINUX) add_executable(ldpctool
add_executable(ldpctool ldpctool/ldpc_tool.cpp
ldpctool/ldpc_tool.cpp ldpctool/tables_handler.cpp
ldpctool/tables_handler.cpp )
) install(TARGETS ldpctool DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS ldpctool DESTINATION ${INSTALL_BIN_DIR})
endif()
if(FFMPEG_EXTERNAL) if(FFMPEG_EXTERNAL)
add_dependencies(${TARGET_NAME} ffmpeg) add_dependencies(${TARGET_NAME} ffmpeg)

View File

@ -300,13 +300,8 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect()));
#ifdef LINUX
CRightClickEnabler *ldpcToolRightClickEnabler = new CRightClickEnabler(ui->softLDPC); CRightClickEnabler *ldpcToolRightClickEnabler = new CRightClickEnabler(ui->softLDPC);
connect(ldpcToolRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(ldpcToolSelect())); 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->playerIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }");
ui->udpIndicator->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 }"); ui->maxBitflipsLabel->setStyleSheet("QLabel { color: white }");
} }
#ifdef LINUX
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S) if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
{ {
ui->softLDPC->setEnabled(false); ui->softLDPC->setEnabled(false);
@ -388,7 +382,6 @@ void DATVDemodGUI::displaySettings()
ui->softLDPC->setEnabled(true); ui->softLDPC->setEnabled(true);
ui->softLDPC->setStyleSheet("QCheckBox { color: white }"); ui->softLDPC->setStyleSheet("QCheckBox { color: white }");
} }
#endif
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S) 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 }"); ui->maxBitflipsLabel->setStyleSheet("QLabel { color: white }");
} }
#ifdef LINUX
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S) if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S)
{ {
ui->softLDPC->setEnabled(false); ui->softLDPC->setEnabled(false);
@ -669,7 +661,6 @@ void DATVDemodGUI::on_cmbStandard_currentIndexChanged(int index)
ui->softLDPC->setEnabled(true); ui->softLDPC->setEnabled(true);
ui->softLDPC->setStyleSheet("QCheckBox { color: white }"); ui->softLDPC->setStyleSheet("QCheckBox { color: white }");
} }
#endif
if (m_settings.m_standard == DATVDemodSettings::dvb_version::DVB_S) 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() void DATVDemodGUI::on_softLDPC_clicked()
{ {
#ifdef LINUX
m_settings.m_softLDPC = ui->softLDPC->isChecked(); m_settings.m_softLDPC = ui->softLDPC->isChecked();
applySettings(); applySettings();
#endif
} }
void DATVDemodGUI::on_maxBitflips_valueChanged(int value) void DATVDemodGUI::on_maxBitflips_valueChanged(int value)
{ {
#ifdef LINUX
m_settings.m_maxBitflips = value; m_settings.m_maxBitflips = value;
applySettings(); applySettings();
#endif
} }
void DATVDemodGUI::on_chkViterbi_clicked() void DATVDemodGUI::on_chkViterbi_clicked()

View File

@ -27,6 +27,12 @@
#include "datvdemodsettings.h" #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() : DATVDemodSettings::DATVDemodSettings() :
m_channelMarker(nullptr), m_channelMarker(nullptr),
m_rollupState(nullptr) m_rollupState(nullptr)
@ -44,7 +50,7 @@ void DATVDemodSettings::resetToDefaults()
m_modulation = BPSK; m_modulation = BPSK;
m_fec = FEC12; m_fec = FEC12;
m_softLDPC = false; m_softLDPC = false;
m_softLDPCToolPath = "/opt/install/sdrangel/bin/ldpctool"; m_softLDPCToolPath = DEFAULT_LDPCTOOLPATH;
m_softLDPCMaxTrials = 8; m_softLDPCMaxTrials = 8;
m_maxBitflips = 0; m_maxBitflips = 0;
m_symbolRate = 250000; m_symbolRate = 250000;
@ -208,7 +214,7 @@ bool DATVDemodSettings::deserialize(const QByteArray& data)
d.readBool(32, &m_softLDPC, false); d.readBool(32, &m_softLDPC, false);
d.readS32(33, &m_maxBitflips, 0); 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); d.readS32(35, &tmp, 8);
m_softLDPCMaxTrials = tmp < 1 ? 1 : tmp > m_softLDPCMaxMaxTrials ? m_softLDPCMaxMaxTrials : tmp; m_softLDPCMaxTrials = tmp < 1 ? 1 : tmp > m_softLDPCMaxMaxTrials ? m_softLDPCMaxMaxTrials : tmp;
d.readBool(36, &m_playerEnable, true); 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; delete (leansdr::s2_fecdec<bool, leansdr::hard_sb>*) r_fecdec;
} }
#ifdef LINUX
if (r_fecdecsoft != nullptr) { if (r_fecdecsoft != nullptr) {
delete (leansdr::s2_fecdec_soft<leansdr::llr_t,leansdr::llr_sb>*) r_fecdecsoft; delete (leansdr::s2_fecdec_soft<leansdr::llr_t,leansdr::llr_sb>*) r_fecdecsoft;
} }
@ -405,7 +404,6 @@ void DATVDemodSink::CleanUpDATVFramework()
if (r_fecdechelper != nullptr) { if (r_fecdechelper != nullptr) {
delete (leansdr::s2_fecdec_helper<leansdr::llr_t,leansdr::llr_sb>*) r_fecdechelper; delete (leansdr::s2_fecdec_helper<leansdr::llr_t,leansdr::llr_sb>*) r_fecdechelper;
} }
#endif
if (p_deframer != nullptr) { if (p_deframer != nullptr) {
delete (leansdr::s2_deframer*) p_deframer; delete (leansdr::s2_deframer*) p_deframer;
@ -515,10 +513,8 @@ void DATVDemodSink::ResetDATVFrameworkPointers()
p_bbframes = nullptr; p_bbframes = nullptr;
p_s2_deinterleaver = nullptr; p_s2_deinterleaver = nullptr;
r_fecdec = nullptr; r_fecdec = nullptr;
#ifdef LINUX
r_fecdecsoft = nullptr; r_fecdecsoft = nullptr;
r_fecdechelper = nullptr; r_fecdechelper = nullptr;
#endif
p_deframer = nullptr; p_deframer = nullptr;
r_scope_symbols_dvbs2 = 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_vbitcount= new leansdr::pipebuf<int>(m_objScheduler, "Bits processed", BUF_S2PACKETS);
p_verrcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits corrected", BUF_S2PACKETS); p_verrcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits corrected", BUF_S2PACKETS);
#ifdef LINUX
bool commandFileValid = false; bool commandFileValid = false;
if (QFileInfo::exists(m_settings.m_softLDPCToolPath)) if (QFileInfo::exists(m_settings.m_softLDPCToolPath))
@ -1110,7 +1105,7 @@ void DATVDemodSink::InitDATVS2Framework()
commandFileValid = fileInfo.isExecutable(); commandFileValid = fileInfo.isExecutable();
} }
if (m_settings.m_softLDPC && commandFileValid) if (m_settings.m_softLDPC /*&& commandFileValid*/)
{ {
#if 0 #if 0
// Doesn't work... // 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; leansdr::s2_fecdec<bool, leansdr::hard_sb> *fecdec = (leansdr::s2_fecdec<bool, leansdr::hard_sb> * ) r_fecdec;
fecdec->bitflips=m_settings.m_maxBitflips; 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 // Deframe BB frames to TS packets
p_lock = new leansdr::pipebuf<int> (m_objScheduler, "lock", BUF_SLOW); 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"); QFileDialog fileDialog(this, "Select LDPC tool");
fileDialog.setOption(QFileDialog::DontUseNativeDialog, true); fileDialog.setOption(QFileDialog::DontUseNativeDialog, true);
#ifdef _MSC_VER
fileDialog.setNameFilter("*.exe");
#else
fileDialog.setFilter(QDir::Executable | QDir::Files); fileDialog.setFilter(QDir::Executable | QDir::Files);
#endif
fileDialog.selectFile(m_fileName); fileDialog.selectFile(m_fileName);
if (fileDialog.exec() == QDialog::Accepted) if (fileDialog.exec() == QDialog::Accepted)

View File

@ -8,10 +8,39 @@ Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
#define LAYERED_DECODER_HH #define LAYERED_DECODER_HH
#include <stdlib.h> #include <stdlib.h>
#ifdef _MSC_VER
#include <malloc.h>
#endif
#include "ldpc.h" #include "ldpc.h"
namespace ldpctool { 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> template <typename TYPE, typename ALG>
class LDPCDecoder class LDPCDecoder
{ {
@ -113,8 +142,8 @@ public:
} }
LT = ldpc->links_total(); LT = ldpc->links_total();
delete ldpc; delete ldpc;
bnl = reinterpret_cast<TYPE *>(aligned_alloc(sizeof(TYPE), sizeof(TYPE) * LT)); bnl = reinterpret_cast<TYPE *>(LDPCUtil::aligned_malloc(sizeof(TYPE), sizeof(TYPE) * LT));
pty = reinterpret_cast<TYPE *>(aligned_alloc(sizeof(TYPE), sizeof(TYPE) * R)); pty = reinterpret_cast<TYPE *>(LDPCUtil::aligned_malloc(sizeof(TYPE), sizeof(TYPE) * R));
uint16_t *tmp = new uint16_t[R * CNL]; uint16_t *tmp = new uint16_t[R * CNL];
for (int i = 0; i < q; ++i) for (int i = 0; i < q; ++i)
for (int j = 0; j < M; ++j) for (int j = 0; j < M; ++j)
@ -139,8 +168,8 @@ public:
~LDPCDecoder() ~LDPCDecoder()
{ {
if (initialized) { if (initialized) {
free(bnl); LDPCUtil::aligned_free(bnl);
free(pty); LDPCUtil::aligned_free(pty);
delete[] cnc; delete[] cnc;
delete[] pos; delete[] pos;
delete[] inp; delete[] inp;

View File

@ -7,7 +7,14 @@ Copyright 2019 <pabr@pabr.org>
*/ */
#include <stdlib.h> #include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h> #include <unistd.h>
#else
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#include <io.h>
#include <malloc.h>
#endif
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <random> #include <random>
@ -21,6 +28,7 @@ Copyright 2019 <pabr@pabr.org>
#include "algorithms.h" #include "algorithms.h"
#include "ldpc.h" #include "ldpc.h"
#if 0 #if 0
#include "flooding_decoder.h" #include "flooding_decoder.h"
static const int DEFAULT_TRIALS = 50; static const int DEFAULT_TRIALS = 50;
@ -129,7 +137,7 @@ int main(int argc, char **argv)
int BLOCKS = batch_size; int BLOCKS = batch_size;
ldpctool::code_type *code = new ldpctool::code_type[BLOCKS * CODE_LEN]; 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); ldpctool::simd_type *simd = reinterpret_cast<ldpctool::simd_type *>(aligned_buffer);
// Expect LLR values in int8_t format. // Expect LLR values in int8_t format.
@ -212,7 +220,7 @@ int main(int argc, char **argv)
delete ldpc; delete ldpc;
free(aligned_buffer); ldpctool::LDPCUtil::aligned_free(aligned_buffer);
delete[] code; delete[] code;
return 0; return 0;

View File

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

View File

@ -54,13 +54,18 @@
#include "ldpc.h" #include "ldpc.h"
#include "sdr.h" #include "sdr.h"
#ifdef LINUX
#include <signal.h> #include <signal.h>
#ifdef LINUX
#include <sys/wait.h> #include <sys/wait.h>
#endif
#ifdef _MSC_VER
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
#include "ldpctool/layered_decoder.h" #include "ldpctool/layered_decoder.h"
#include "ldpctool/testbench.h" #include "ldpctool/testbench.h"
#include "ldpctool/algorithms.h" #include "ldpctool/algorithms.h"
#endif #include "ldpctool/ldpcworker.h"
namespace leansdr namespace leansdr
{ {
@ -3041,8 +3046,6 @@ struct s2_fecdec : runnable
pipewriter<int> *bitcount, *errcount; pipewriter<int> *bitcount, *errcount;
}; // s2_fecdec }; // s2_fecdec
#ifdef LINUX
// Soft LDPC decoder // Soft LDPC decoder
// Internally implemented LDPC tool. Replaces external LDPC decoder // Internally implemented LDPC tool. Replaces external LDPC decoder
@ -3230,6 +3233,8 @@ private:
T q[SIZE]; T q[SIZE];
}; };
#if defined(USE_LDPC_TOOL) && !defined(_MSC_VER)
template <typename SOFTBIT, typename SOFTBYTE> template <typename SOFTBIT, typename SOFTBYTE>
struct s2_fecdec_helper : runnable struct s2_fecdec_helper : runnable
{ {
@ -3610,7 +3615,280 @@ struct s2_fecdec_helper : runnable
std::deque<int> errcount_q; std::deque<int> errcount_q;
pipewriter<int> *bitcount, *errcount; pipewriter<int> *bitcount, *errcount;
}; // s2_fecdec_helper }; // 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 #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 // S2 FRAMER
// EN 302 307-1 section 5.1 Mode adaptation // EN 302 307-1 section 5.1 Mode adaptation
@ -3854,7 +4132,6 @@ private:
{ {
handle_ts(data, dfl, syncd, sync); handle_ts(data, dfl, syncd, sync);
} }
#ifdef LINUX
else if (streamtype == 1) else if (streamtype == 1)
{ {
if (fd_gse >= 0) if (fd_gse >= 0)
@ -3874,7 +4151,6 @@ private:
fprintf(stderr, "Unrecognized bbframe\n"); fprintf(stderr, "Unrecognized bbframe\n");
} }
} }
#endif
} }
void handle_ts(uint8_t *data, uint16_t dfl, uint16_t syncd, uint8_t sync) 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> <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. 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.
Right clicking on this control opens a dialog where you can choose: 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. - 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> <h5>B.2b.7: DVB-S2 specific - LDPC maximum number of bit flips allowed</h5>