mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	DATV Demod: Add support for LDPC on Windows. Use Qt worker thread instead of external ldpc_tool process.
This commit is contained in:
		
							parent
							
								
									a65c9458ed
								
							
						
					
					
						commit
						ff26ece347
					
				| @ -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.
 | ||||
| @ -212,7 +220,7 @@ int main(int argc, char **argv) | ||||
| 
 | ||||
| 	delete ldpc; | ||||
| 
 | ||||
| 	free(aligned_buffer); | ||||
| 	ldpctool::LDPCUtil::aligned_free(aligned_buffer); | ||||
| 	delete[] code; | ||||
| 
 | ||||
| 	return 0; | ||||
|  | ||||
| @ -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