mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-07-27 12:34:18 -04:00
e86e95feab
The original leansdr code uses fatal() and fail() for unrecoverable errors by terminating execution. When integrated into SDRangel, these error paths did not provide a mechanism for the plugin to handle failures locally, allowing initialization failures to escape the normal plugin lifecycle. Convert leansdr fatal() and fail() handling into C++ exceptions so DATV can detect framework initialization failures, report them, and cleanly return control to SDRangel instead of allowing the error to terminate the application. This behavior is desirable because a DATV framework configuration failure should not bring down the entire application. Catch initialization exceptions in DATVDemodSink::feed(), report the failure, clean up the partially initialized framework, and return to the caller. Add noreturn annotations to the leansdr error functions and include the leansdr source in exception messages to make failures easier to diagnose. This does not redesign leansdr error handling or provide recovery from runtime DSP failures. It only adds an exception boundary between the leansdr library code and the SDRangel plugin lifecycle. noticed via cppcheck indicating many Array indexes could go out of bounds due to fail and fatal returning. Signed-off-by: Robin Getz <rgetz503@gmail.com>
1533 lines
50 KiB
C++
1533 lines
50 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2018-2021, 2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
|
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
|
|
// Copyright (C) 2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
// //
|
|
// 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/>. //
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef SERVER_MODE
|
|
#include "datvideorender.h"
|
|
#endif
|
|
#include "datvcstlnlut.h"
|
|
#include "datvdemodsink.h"
|
|
|
|
#include "leansdr/dvbs2.h"
|
|
|
|
#include <QDebug>
|
|
#include <QObject>
|
|
#include <QFileInfo>
|
|
#include <QMutexLocker>
|
|
|
|
#include "datvdemodreport.h"
|
|
#ifndef SERVER_MODE
|
|
#include "datvideorender.h"
|
|
#endif
|
|
|
|
const unsigned int DATVDemodSink::m_rfFilterFftLength = 512;
|
|
|
|
DATVDemodSink::DATVDemodSink() :
|
|
m_blnNeedConfigUpdate(false),
|
|
m_tvScreen(nullptr),
|
|
#ifndef SERVER_MODE
|
|
m_videoRender(nullptr),
|
|
#endif
|
|
m_videoStream(new DATVideostream()),
|
|
m_udpStream(leansdr::tspacket::SIZE),
|
|
m_videoThread(nullptr),
|
|
m_audioFifo(48000),
|
|
m_blnRenderingVideo(false),
|
|
m_cstlnSetByModcod(false),
|
|
m_modcodModulation(-1),
|
|
m_modcodCodeRate(-1),
|
|
m_enmModulation(DATVDemodSettings::BPSK /*DATV_FM1*/),
|
|
m_channelSampleRate(1024000),
|
|
m_messageQueueToGUI(nullptr)
|
|
{
|
|
//*************** DATV PARAMETERS ***************
|
|
m_blnInitialized=false;
|
|
ResetDATVFrameworkPointers();
|
|
}
|
|
|
|
DATVDemodSink::~DATVDemodSink()
|
|
{
|
|
m_blnInitialized = false;
|
|
|
|
//Immediately exit from DATVideoStream if waiting for data before killing thread
|
|
m_videoStream->setThreadTimeout(0);
|
|
m_videoStream->deleteLater();
|
|
|
|
stopVideo();
|
|
CleanUpDATVFramework();
|
|
|
|
#ifndef SERVER_MODE
|
|
if (m_videoThread) {
|
|
delete m_videoThread;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DATVDemodSink::stopVideo()
|
|
{
|
|
#ifndef SERVER_MODE
|
|
if (m_videoThread)
|
|
{
|
|
if (m_videoThread->isRunning())
|
|
{
|
|
m_videoThread->stopRendering();
|
|
m_videoThread->quit();
|
|
m_videoThread->wait();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DATVDemodSink::setTVScreen(TVScreen *tvScreen)
|
|
{
|
|
m_tvScreen = tvScreen;
|
|
}
|
|
|
|
void DATVDemodSink::SetVideoRender(DATVideoRender *screen)
|
|
{
|
|
#ifndef SERVER_MODE
|
|
m_videoRender = screen;
|
|
m_videoRender->setAudioFIFO(&m_audioFifo);
|
|
m_videoThread = new DATVideoRenderThread(m_videoRender, m_videoStream);
|
|
m_videoThread->setObjectName("vtDATVDemodSink");
|
|
#else
|
|
(void) screen;
|
|
#endif
|
|
}
|
|
|
|
bool DATVDemodSink::audioActive()
|
|
{
|
|
#ifndef SERVER_MODE
|
|
if (m_videoRender) {
|
|
return m_videoRender->getAudioStreamIndex() >= 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DATVDemodSink::videoActive()
|
|
{
|
|
#ifndef SERVER_MODE
|
|
if (m_videoRender) {
|
|
return m_videoRender->getVideoStreamIndex() >= 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DATVDemodSink::audioDecodeOK()
|
|
{
|
|
#ifndef SERVER_MODE
|
|
if (m_videoRender) {
|
|
return m_videoRender->getAudioDecodeOK();
|
|
} else {
|
|
return false;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DATVDemodSink::videoDecodeOK()
|
|
{
|
|
#ifndef SERVER_MODE
|
|
if (m_videoRender) {
|
|
return m_videoRender->getVideoDecodeOK();
|
|
} else {
|
|
return false;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DATVDemodSink::udpRunning()
|
|
{
|
|
if (!r_videoplayer) {
|
|
return false;
|
|
}
|
|
|
|
bool udpRunning = r_videoplayer->isUDPRunning();
|
|
|
|
return udpRunning;
|
|
}
|
|
|
|
bool DATVDemodSink::playVideo()
|
|
{
|
|
QMutexLocker mlock(&m_mutex);
|
|
|
|
#ifndef SERVER_MODE
|
|
if (m_videoStream == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (m_videoRender == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (m_videoThread == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (m_videoThread->isRunning()) {
|
|
return true;
|
|
}
|
|
|
|
if (m_videoStream->bytesAvailable() > 0)
|
|
{
|
|
m_videoStream->setMultiThreaded(true);
|
|
m_videoStream->setThreadTimeout(DATVideoRenderThread::videoThreadTimeoutMs);
|
|
m_videoThread->start();
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void DATVDemodSink::CleanUpDATVFramework()
|
|
{
|
|
if (m_videoStream) {
|
|
m_videoStream->cleanUp();
|
|
}
|
|
|
|
if (m_objScheduler != nullptr)
|
|
{
|
|
m_objScheduler->shutdown();
|
|
delete m_objScheduler;
|
|
}
|
|
|
|
// NOTCH FILTER
|
|
|
|
if (r_auto_notch != nullptr) {
|
|
delete r_auto_notch;
|
|
}
|
|
if (p_autonotched != nullptr) {
|
|
delete p_autonotched;
|
|
}
|
|
|
|
// FREQUENCY CORRECTION : DEROTATOR
|
|
if (p_derot != nullptr) {
|
|
delete p_derot;
|
|
}
|
|
if (r_derot != nullptr) {
|
|
delete r_derot;
|
|
}
|
|
|
|
// CNR ESTIMATION
|
|
if (p_cnr != nullptr) {
|
|
delete p_cnr;
|
|
}
|
|
if (r_cnr != nullptr) {
|
|
delete r_cnr;
|
|
}
|
|
if (r_cnrMeter != nullptr) {
|
|
delete r_cnrMeter;
|
|
}
|
|
|
|
//FILTERING
|
|
if (r_resample != nullptr) {
|
|
delete r_resample;
|
|
}
|
|
if (p_resampled != nullptr) {
|
|
delete p_resampled;
|
|
}
|
|
if (coeffs != nullptr) {
|
|
delete coeffs;
|
|
}
|
|
|
|
// OUTPUT PREPROCESSED DATA
|
|
if (sampler != nullptr) {
|
|
delete sampler;
|
|
}
|
|
if (coeffs_sampler != nullptr) {
|
|
delete coeffs_sampler;
|
|
}
|
|
if (p_symbols != nullptr) {
|
|
delete p_symbols;
|
|
}
|
|
if (p_freq != nullptr) {
|
|
delete p_freq;
|
|
}
|
|
if (p_ss != nullptr) {
|
|
delete p_ss;
|
|
}
|
|
if (p_mer != nullptr) {
|
|
delete p_mer;
|
|
}
|
|
if (r_merMeter != nullptr) {
|
|
delete r_merMeter;
|
|
}
|
|
if (p_sampled != nullptr) {
|
|
delete p_sampled;
|
|
}
|
|
|
|
//DECIMATION
|
|
if (p_decimated != nullptr) {
|
|
delete p_decimated;
|
|
}
|
|
if (p_decim != nullptr) {
|
|
delete p_decim;
|
|
}
|
|
if (r_ppout != nullptr) {
|
|
delete r_ppout;
|
|
}
|
|
|
|
//GENERIC CONSTELLATION RECEIVER
|
|
if (m_objDemodulator != nullptr) {
|
|
delete m_objDemodulator;
|
|
}
|
|
|
|
//DECONVOLUTION AND SYNCHRONIZATION
|
|
if (p_bytes != nullptr) {
|
|
delete p_bytes;
|
|
}
|
|
if (r_deconv != nullptr) {
|
|
delete r_deconv;
|
|
}
|
|
if (r != nullptr) {
|
|
delete r;
|
|
}
|
|
if (p_descrambled != nullptr) {
|
|
delete p_descrambled;
|
|
}
|
|
if (p_frames != nullptr) {
|
|
delete p_frames;
|
|
}
|
|
if (r_etr192_descrambler != nullptr) {
|
|
delete r_etr192_descrambler;
|
|
}
|
|
if (r_sync != nullptr) {
|
|
delete r_sync;
|
|
}
|
|
if (p_mpegbytes != nullptr) {
|
|
delete p_mpegbytes;
|
|
}
|
|
if (p_lock != nullptr) {
|
|
delete p_lock;
|
|
}
|
|
if (p_locktime != nullptr) {
|
|
delete p_locktime;
|
|
}
|
|
if (r_sync_mpeg != nullptr) {
|
|
delete r_sync_mpeg;
|
|
}
|
|
|
|
// DEINTERLEAVING
|
|
if (p_rspackets != nullptr) {
|
|
delete p_rspackets;
|
|
}
|
|
if (r_deinter != nullptr) {
|
|
delete r_deinter;
|
|
}
|
|
if (p_vbitcount != nullptr) {
|
|
delete p_vbitcount;
|
|
}
|
|
if (p_verrcount != nullptr) {
|
|
delete p_verrcount;
|
|
}
|
|
if (p_rtspackets != nullptr) {
|
|
delete p_rtspackets;
|
|
}
|
|
if (r_rsdec != nullptr) {
|
|
delete r_rsdec;
|
|
}
|
|
|
|
//BER ESTIMATION
|
|
if (p_vber != nullptr) {
|
|
delete p_vber;
|
|
}
|
|
if (r_vber != nullptr) {
|
|
delete r_vber;
|
|
}
|
|
|
|
// DERANDOMIZATION
|
|
if (p_tspackets != nullptr) {
|
|
delete p_tspackets;
|
|
}
|
|
if (r_derand != nullptr) {
|
|
delete r_derand;
|
|
}
|
|
|
|
//OUTPUT
|
|
if (r_videoplayer != nullptr) {
|
|
delete r_videoplayer;
|
|
}
|
|
|
|
//CONSTELLATION
|
|
#ifndef SERVER_MODE
|
|
if (r_scope_symbols != nullptr) {
|
|
delete r_scope_symbols;
|
|
}
|
|
#endif
|
|
// INPUT
|
|
if (p_rawiq != nullptr) {
|
|
delete p_rawiq;
|
|
}
|
|
if (p_rawiq_writer != nullptr) {
|
|
delete p_rawiq_writer;
|
|
}
|
|
//if(p_preprocessed!=nullptr) delete p_preprocessed;
|
|
|
|
//DVB-S2
|
|
|
|
if (p_slots_dvbs2 != nullptr) {
|
|
delete (leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> >*) p_slots_dvbs2;
|
|
}
|
|
|
|
if (p_cstln != nullptr) {
|
|
delete p_cstln;
|
|
}
|
|
|
|
if (p_cstln_pls != nullptr) {
|
|
delete p_cstln_pls;
|
|
}
|
|
|
|
if (p_framelock != nullptr) {
|
|
delete p_framelock;
|
|
}
|
|
|
|
if (m_objDemodulatorDVBS2 != nullptr) {
|
|
delete (leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss>*) m_objDemodulatorDVBS2;
|
|
}
|
|
|
|
if (p_fecframes != nullptr) {
|
|
delete (leansdr::pipebuf< leansdr::fecframe<leansdr::hard_sb> >*) p_fecframes;
|
|
}
|
|
|
|
if (p_bbframes != nullptr) {
|
|
delete (leansdr::pipebuf<leansdr::bbframe>*) p_bbframes;
|
|
}
|
|
|
|
if (p_s2_deinterleaver != nullptr) {
|
|
delete (leansdr::s2_deinterleaver<leansdr::llr_ss,leansdr::hard_sb>*) p_s2_deinterleaver;
|
|
}
|
|
|
|
if (r_fecdec != nullptr) {
|
|
delete (leansdr::s2_fecdec<bool, leansdr::hard_sb>*) r_fecdec;
|
|
}
|
|
|
|
#if 0
|
|
if (r_fecdecsoft != nullptr) {
|
|
delete (leansdr::s2_fecdec_soft<leansdr::llr_t,leansdr::llr_sb>*) r_fecdecsoft;
|
|
}
|
|
#endif
|
|
|
|
if (r_fecdechelper != nullptr) {
|
|
delete (leansdr::s2_fecdec_helper<leansdr::llr_t,leansdr::llr_sb>*) r_fecdechelper;
|
|
}
|
|
|
|
if (p_deframer != nullptr) {
|
|
delete (leansdr::s2_deframer*) p_deframer;
|
|
}
|
|
#ifndef SERVER_MODE
|
|
if (r_scope_symbols_dvbs2 != nullptr) {
|
|
delete r_scope_symbols_dvbs2;
|
|
}
|
|
#endif
|
|
ResetDATVFrameworkPointers();
|
|
}
|
|
|
|
void DATVDemodSink::ResetDATVFrameworkPointers()
|
|
{
|
|
// INPUT
|
|
m_objScheduler = nullptr;
|
|
|
|
p_rawiq = nullptr;
|
|
p_rawiq_writer = nullptr;
|
|
|
|
p_preprocessed = nullptr;
|
|
|
|
// NOTCH FILTER
|
|
r_auto_notch = nullptr;
|
|
p_autonotched = nullptr;
|
|
|
|
// FREQUENCY CORRECTION : DEROTATOR
|
|
p_derot = nullptr;
|
|
r_derot=nullptr;
|
|
|
|
// CNR ESTIMATION
|
|
p_cnr = nullptr;
|
|
r_cnr = nullptr;
|
|
r_cnrMeter = nullptr;
|
|
|
|
//FILTERING
|
|
r_resample = nullptr;
|
|
p_resampled = nullptr;
|
|
coeffs = nullptr;
|
|
ncoeffs=0;
|
|
|
|
// OUTPUT PREPROCESSED DATA
|
|
sampler = nullptr;
|
|
coeffs_sampler = nullptr;
|
|
ncoeffs_sampler = 0;
|
|
|
|
p_symbols = nullptr;
|
|
p_freq = nullptr;
|
|
p_ss = nullptr;
|
|
p_mer = nullptr;
|
|
r_merMeter = nullptr;
|
|
p_sampled = nullptr;
|
|
|
|
//DECIMATION
|
|
p_decimated = nullptr;
|
|
p_decim = nullptr;
|
|
r_ppout = nullptr;
|
|
|
|
//GENERIC CONSTELLATION RECEIVER
|
|
m_objDemodulator = nullptr;
|
|
|
|
//DECONVOLUTION AND SYNCHRONIZATION
|
|
p_bytes = nullptr;
|
|
r_deconv = nullptr;
|
|
r = nullptr;
|
|
|
|
p_descrambled = nullptr;
|
|
p_frames = nullptr;
|
|
r_etr192_descrambler = nullptr;
|
|
r_sync = nullptr;
|
|
|
|
p_mpegbytes = nullptr;
|
|
p_lock = nullptr;
|
|
p_locktime = nullptr;
|
|
r_sync_mpeg = nullptr;
|
|
|
|
// DEINTERLEAVING
|
|
p_rspackets = nullptr;
|
|
r_deinter = nullptr;
|
|
|
|
p_vbitcount = nullptr;
|
|
p_verrcount = nullptr;
|
|
p_rtspackets = nullptr;
|
|
r_rsdec = nullptr;
|
|
|
|
//BER ESTIMATION
|
|
p_vber = nullptr;
|
|
r_vber = nullptr;
|
|
|
|
// DERANDOMIZATION
|
|
p_tspackets = nullptr;
|
|
r_derand = nullptr;
|
|
|
|
//OUTPUT : To remove void *
|
|
r_videoplayer = nullptr;
|
|
|
|
//CONSTELLATION
|
|
#ifndef SERVER_MODE
|
|
r_scope_symbols = nullptr;
|
|
#endif
|
|
|
|
//DVB-S2
|
|
p_slots_dvbs2 = nullptr;
|
|
p_cstln = nullptr;
|
|
p_cstln_pls = nullptr;
|
|
p_framelock = nullptr;
|
|
m_objDemodulatorDVBS2 = nullptr;
|
|
p_fecframes = nullptr;
|
|
p_bbframes = nullptr;
|
|
p_s2_deinterleaver = nullptr;
|
|
r_fecdec = nullptr;
|
|
r_fecdecsoft = nullptr;
|
|
r_fecdechelper = nullptr;
|
|
p_deframer = nullptr;
|
|
#ifndef SERVER_MODE
|
|
r_scope_symbols_dvbs2 = nullptr;
|
|
#endif
|
|
}
|
|
|
|
void DATVDemodSink::InitDATVFramework()
|
|
{
|
|
m_blnDVBInitialized = false;
|
|
m_lngReadIQ = 0;
|
|
CleanUpDATVFramework();
|
|
|
|
qDebug() << "DATVDemodSink::InitDATVFramework:"
|
|
<< " Standard: " << m_settings.m_standard
|
|
<< " Symbol Rate: " << m_settings.m_symbolRate
|
|
<< " Modulation: " << m_settings.m_modulation
|
|
<< " Notch Filters: " << m_settings.m_notchFilters
|
|
<< " Allow Drift: " << m_settings.m_allowDrift
|
|
<< " Fast Lock: " << m_settings.m_fastLock
|
|
<< " Filter: " << m_settings.m_filter
|
|
<< " HARD METRIC: " << m_settings.m_hardMetric
|
|
<< " RollOff: " << m_settings.m_rollOff
|
|
<< " Viterbi: " << m_settings.m_viterbi
|
|
<< " Excursion: " << m_settings.m_excursion
|
|
<< " channel Sample rate: " << m_channelSampleRate
|
|
<< " Input sample rate: " << 2 * m_settings.m_symbolRate;
|
|
|
|
m_objCfg.standard = m_settings.m_standard;
|
|
|
|
m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec);
|
|
m_objCfg.Fs = (float) 2 * m_settings.m_symbolRate; // maintained at twice the symbol rate
|
|
m_objCfg.Fm = (float) m_settings.m_symbolRate;
|
|
m_objCfg.fastlock = m_settings.m_fastLock;
|
|
|
|
m_objCfg.sampler = m_settings.m_filter;
|
|
m_objCfg.rolloff = m_settings.m_rollOff; //0...1
|
|
m_objCfg.rrc_rej = (float) m_settings.m_excursion; //dB
|
|
m_objCfg.rrc_steps = 0; //auto
|
|
|
|
m_videoStream->resetTotalReceived();
|
|
m_udpStream.resetTotalReceived();
|
|
|
|
switch(m_settings.m_modulation)
|
|
{
|
|
case DATVDemodSettings::BPSK:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::BPSK;
|
|
break;
|
|
case DATVDemodSettings::QPSK:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::QPSK;
|
|
break;
|
|
case DATVDemodSettings::PSK8:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::PSK8;
|
|
break;
|
|
case DATVDemodSettings::APSK16:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::APSK16;
|
|
break;
|
|
case DATVDemodSettings::APSK32:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::APSK32;
|
|
break;
|
|
case DATVDemodSettings::APSK64E:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::APSK64E;
|
|
break;
|
|
case DATVDemodSettings::QAM16:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::QAM16;
|
|
break;
|
|
case DATVDemodSettings::QAM64:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::QAM64;
|
|
break;
|
|
case DATVDemodSettings::QAM256:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::QAM256;
|
|
break;
|
|
default:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::eucl_ss, 256>::BPSK;
|
|
break;
|
|
}
|
|
|
|
m_objCfg.allow_drift = m_settings.m_allowDrift;
|
|
m_objCfg.anf = m_settings.m_notchFilters;
|
|
m_objCfg.hard_metric = m_settings.m_hardMetric;
|
|
m_objCfg.sampler = m_settings.m_filter;
|
|
m_objCfg.viterbi = m_settings.m_viterbi;
|
|
|
|
// Min buffer size for baseband data
|
|
// scopes: 1024
|
|
// ss_estimator: 1024
|
|
// anf: 4096
|
|
// cstln_receiver: reads in chunks of 128+1
|
|
BUF_BASEBAND = 4096 * m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for IQ symbols
|
|
// cstln_receiver: writes in chunks of 128/omega symbols (margin 128)
|
|
// deconv_sync: reads at least 64+32
|
|
// A larger buffer improves performance significantly.
|
|
BUF_SYMBOLS = 1024 * m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for unsynchronized bytes
|
|
// deconv_sync: writes 32 bytes
|
|
// mpeg_sync: reads up to 204*scan_syncs = 1632 bytes
|
|
BUF_BYTES = 2048 * m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for synchronized (but interleaved) bytes
|
|
// mpeg_sync: writes 1 rspacket
|
|
// deinterleaver: reads 17*11*12+204 = 2448 bytes
|
|
BUF_MPEGBYTES = 2448 * m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for packets: 1
|
|
BUF_PACKETS = m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for misc measurements: 1
|
|
BUF_SLOW = m_objCfg.buf_factor;
|
|
|
|
m_lngExpectedReadIQ = BUF_BASEBAND;
|
|
|
|
m_objScheduler = new leansdr::scheduler();
|
|
|
|
//***************
|
|
p_rawiq = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "rawiq", BUF_BASEBAND);
|
|
p_rawiq_writer = new leansdr::pipewriter<leansdr::cf32>(*p_rawiq, m_RawIQMinWrite);
|
|
p_preprocessed = p_rawiq;
|
|
|
|
// NOTCH FILTER
|
|
|
|
if (m_objCfg.anf>0)
|
|
{
|
|
p_autonotched = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "autonotched", BUF_BASEBAND);
|
|
r_auto_notch = new leansdr::auto_notch<leansdr::f32>(m_objScheduler, *p_preprocessed, *p_autonotched, m_objCfg.anf, 0);
|
|
p_preprocessed = p_autonotched;
|
|
}
|
|
|
|
// FREQUENCY CORRECTION
|
|
|
|
//******** -> if ( m_objCfg.Fderot>0 )
|
|
|
|
// CNR ESTIMATION
|
|
|
|
p_cnr = new leansdr::pipebuf<leansdr::f32>(m_objScheduler, "cnr", BUF_SLOW);
|
|
|
|
if (m_objCfg.cnr == true)
|
|
{
|
|
r_cnr = new leansdr::cnr_fft<leansdr::f32>(m_objScheduler, *p_preprocessed, *p_cnr, m_objCfg.Fm/m_objCfg.Fs, 1024);
|
|
r_cnr->decimation = decimation(m_objCfg.Fs, 5); // 5 Hz
|
|
}
|
|
|
|
// FILTERING
|
|
|
|
int decim = 1;
|
|
|
|
//******** -> if ( m_objCfg.resample )
|
|
|
|
// DECIMATION
|
|
// (Unless already done in resampler)
|
|
|
|
//******** -> if ( !m_objCfg.resample && m_objCfg.decim>1 )
|
|
|
|
//Resampling FS
|
|
|
|
// Generic constellation receiver
|
|
|
|
p_symbols = new leansdr::pipebuf<leansdr::eucl_ss>(m_objScheduler, "PSK soft-symbols", BUF_SYMBOLS);
|
|
p_freq = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "freq", BUF_SLOW);
|
|
p_ss = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "SS", BUF_SLOW);
|
|
p_mer = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "MER", BUF_SLOW);
|
|
p_sampled = new leansdr::pipebuf<leansdr::cf32> (m_objScheduler, "PSK symbols", BUF_BASEBAND);
|
|
|
|
switch (m_objCfg.sampler)
|
|
{
|
|
case DATVDemodSettings::SAMP_NEAREST:
|
|
sampler = new leansdr::nearest_sampler<float>();
|
|
break;
|
|
case DATVDemodSettings::SAMP_LINEAR:
|
|
sampler = new leansdr::linear_sampler<float>();
|
|
break;
|
|
case DATVDemodSettings::SAMP_RRC:
|
|
{
|
|
if (m_objCfg.rrc_steps == 0)
|
|
{
|
|
// At least 64 discrete sampling points between symbols
|
|
m_objCfg.rrc_steps = std::max(1, (int)(64*m_objCfg.Fm / m_objCfg.Fs));
|
|
}
|
|
|
|
float Frrc = m_objCfg.Fs * m_objCfg.rrc_steps; // Sample freq of the RRC filter
|
|
float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff;
|
|
int order = m_objCfg.rrc_rej * Frrc / (22*transition);
|
|
ncoeffs_sampler = leansdr::filtergen::root_raised_cosine(order, m_objCfg.Fm/Frrc, m_objCfg.rolloff, &coeffs_sampler);
|
|
sampler = new leansdr::fir_sampler<float,float>(ncoeffs_sampler, coeffs_sampler, m_objCfg.rrc_steps);
|
|
break;
|
|
}
|
|
default:
|
|
qCritical("DATVDemodSink::InitDATVFramework: Interpolator not implemented");
|
|
return;
|
|
}
|
|
|
|
m_objDemodulator = new leansdr::cstln_receiver<leansdr::f32, leansdr::eucl_ss>(
|
|
m_objScheduler,
|
|
sampler,
|
|
*p_preprocessed,
|
|
*p_symbols,
|
|
p_freq,
|
|
p_ss,
|
|
p_mer,
|
|
p_sampled);
|
|
|
|
if (m_objCfg.standard == DATVDemodSettings::DVB_S)
|
|
{
|
|
if ( m_objCfg.constellation != leansdr::cstln_lut<leansdr::eucl_ss, 256>::QPSK
|
|
&& m_objCfg.constellation != leansdr::cstln_lut<leansdr::eucl_ss, 256>::BPSK )
|
|
{
|
|
qWarning("DATVDemodSink::InitDATVFramework: non-standard constellation for DVB-S");
|
|
}
|
|
}
|
|
|
|
if (m_objCfg.standard == DATVDemodSettings::DVB_S2)
|
|
{
|
|
// For DVB-S2 testing only.
|
|
// Constellation should be determined from PL signalling.
|
|
qDebug("DATVDemodSink::InitDATVFramework: DVB-S2: Testing symbol sampler only.");
|
|
}
|
|
|
|
m_objDemodulator->cstln = make_dvbs_constellation(m_objCfg.constellation, m_objCfg.fec);
|
|
|
|
if (m_objCfg.hard_metric) {
|
|
m_objDemodulator->cstln->harden();
|
|
}
|
|
|
|
m_objDemodulator->set_omega(m_objCfg.Fs/m_objCfg.Fm);
|
|
|
|
//******** if ( m_objCfg.Ftune )
|
|
//{
|
|
// m_objDemodulator->set_freq(m_objCfg.Ftune/m_objCfg.Fs);
|
|
//}
|
|
|
|
if (m_objCfg.allow_drift) {
|
|
m_objDemodulator->set_allow_drift(true);
|
|
}
|
|
|
|
//******** -> if ( m_objCfg.viterbi )
|
|
if (m_objCfg.viterbi) {
|
|
m_objDemodulator->pll_adjustment /= 6;
|
|
}
|
|
|
|
m_objDemodulator->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo);
|
|
|
|
// TRACKING FILTERS
|
|
|
|
if (r_cnr)
|
|
{
|
|
r_cnr->freq_tap = &m_objDemodulator->freq_tap;
|
|
r_cnr->tap_multiplier = 1.0 / decim;
|
|
}
|
|
|
|
//constellation
|
|
#ifndef SERVER_MODE
|
|
if (m_tvScreen)
|
|
{
|
|
qDebug("DATVDemodSink::InitDATVFramework: Register DVB constellation TV screen");
|
|
|
|
r_scope_symbols = new leansdr::datvconstellation<leansdr::f32>(m_objScheduler, *p_sampled, -128,128, nullptr, m_tvScreen);
|
|
r_scope_symbols->decimation = 1;
|
|
r_scope_symbols->cstln = &m_objDemodulator->cstln;
|
|
r_scope_symbols->calculate_cstln_points();
|
|
}
|
|
#endif
|
|
r_merMeter = new leansdr::datvmeter(m_objScheduler, *p_mer);
|
|
r_cnrMeter = new leansdr::datvmeter(m_objScheduler, *p_cnr);
|
|
|
|
// DECONVOLUTION AND SYNCHRONIZATION
|
|
|
|
p_bytes = new leansdr::pipebuf<leansdr::u8>(m_objScheduler, "bytes", BUF_BYTES);
|
|
|
|
r_deconv = nullptr;
|
|
|
|
//******** -> if ( m_objCfg.viterbi )
|
|
|
|
if (m_objCfg.viterbi)
|
|
{
|
|
if (m_objCfg.fec == leansdr::FEC23 && (m_objDemodulator->cstln->nsymbols == 4 || m_objDemodulator->cstln->nsymbols == 64)) {
|
|
m_objCfg.fec = leansdr::FEC46;
|
|
}
|
|
|
|
//To uncomment -> Linking Problem : undefined symbol: _ZN7leansdr21viterbi_dec_interfaceIhhiiE6updateEPiS2_
|
|
r = new leansdr::viterbi_sync(m_objScheduler, (*p_symbols), (*p_bytes), m_objDemodulator->cstln, m_objCfg.fec);
|
|
|
|
if (m_objCfg.fastlock) {
|
|
r->resync_period = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r_deconv = make_deconvol_sync_simple(m_objScheduler, (*p_symbols), (*p_bytes), m_objCfg.fec);
|
|
r_deconv->fastlock = m_objCfg.fastlock;
|
|
}
|
|
|
|
//******* -> if ( m_objCfg.hdlc )
|
|
|
|
p_mpegbytes = new leansdr::pipebuf<leansdr::u8> (m_objScheduler, "mpegbytes", BUF_MPEGBYTES);
|
|
p_lock = new leansdr::pipebuf<int> (m_objScheduler, "lock", BUF_SLOW);
|
|
p_locktime = new leansdr::pipebuf<leansdr::u32> (m_objScheduler, "locktime", BUF_PACKETS);
|
|
|
|
r_sync_mpeg = new leansdr::mpeg_sync<leansdr::u8, 0>(m_objScheduler, *p_bytes, *p_mpegbytes, r_deconv, p_lock, p_locktime);
|
|
r_sync_mpeg->fastlock = m_objCfg.fastlock;
|
|
|
|
// DEINTERLEAVING
|
|
|
|
p_rspackets = new leansdr::pipebuf<leansdr::rspacket<leansdr::u8> >(m_objScheduler, "RS-enc packets", BUF_PACKETS);
|
|
r_deinter = new leansdr::deinterleaver<leansdr::u8>(m_objScheduler, *p_mpegbytes, *p_rspackets);
|
|
|
|
// REED-SOLOMON
|
|
|
|
p_vbitcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits processed", BUF_PACKETS);
|
|
p_verrcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits corrected", BUF_PACKETS);
|
|
p_rtspackets = new leansdr::pipebuf<leansdr::tspacket>(m_objScheduler, "rand TS packets", BUF_PACKETS);
|
|
r_rsdec = new leansdr::rs_decoder<leansdr::u8, 0>(m_objScheduler, *p_rspackets, *p_rtspackets, p_vbitcount, p_verrcount);
|
|
|
|
// BER ESTIMATION
|
|
|
|
/*
|
|
p_vber = new pipebuf<float> (m_objScheduler, "VBER", BUF_SLOW);
|
|
r_vber = new rate_estimator<float> (m_objScheduler, *p_verrcount, *p_vbitcount, *p_vber);
|
|
r_vber->sample_size = m_objCfg.Fm/2; // About twice per second, depending on CR
|
|
// Require resolution better than 2E-5
|
|
if ( r_vber->sample_size < 50000 )
|
|
{
|
|
r_vber->sample_size = 50000;
|
|
}
|
|
*/
|
|
|
|
// DERANDOMIZATION
|
|
p_tspackets = new leansdr::pipebuf<leansdr::tspacket>(m_objScheduler, "TS packets", BUF_PACKETS);
|
|
r_derand = new leansdr::derandomizer(m_objScheduler, *p_rtspackets, *p_tspackets);
|
|
|
|
// OUTPUT
|
|
if (m_settings.m_playerEnable) {
|
|
r_videoplayer = new leansdr::datvvideoplayer<leansdr::tspacket>(m_objScheduler, *p_tspackets, m_videoStream, &m_udpStream);
|
|
} else {
|
|
r_videoplayer = new leansdr::datvvideoplayer<leansdr::tspacket>(m_objScheduler, *p_tspackets, nullptr, &m_udpStream);
|
|
}
|
|
|
|
r_videoplayer->setSymbolRate(m_settings.m_symbolRate);
|
|
m_blnDVBInitialized = true;
|
|
}
|
|
|
|
//************ DVB-S2 Decoder ************
|
|
void DATVDemodSink::InitDATVS2Framework()
|
|
{
|
|
leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss> * objDemodulatorDVBS2;
|
|
|
|
m_blnDVBInitialized = false;
|
|
m_lngReadIQ = 0;
|
|
CleanUpDATVFramework();
|
|
|
|
qDebug() << "DATVDemodSink::InitDATVS2Framework:"
|
|
<< " Standard: " << m_settings.m_standard
|
|
<< " Symbol Rate: " << m_settings.m_symbolRate
|
|
<< " Modulation: " << m_settings.m_modulation
|
|
<< " Notch Filters: " << m_settings.m_notchFilters
|
|
<< " Allow Drift: " << m_settings.m_allowDrift
|
|
<< " Fast Lock: " << m_settings.m_fastLock
|
|
<< " Filter: " << m_settings.m_filter
|
|
<< " HARD METRIC: " << m_settings.m_hardMetric
|
|
<< " RollOff: " << m_settings.m_rollOff
|
|
<< " Viterbi: " << m_settings.m_viterbi
|
|
<< " Excursion: " << m_settings.m_excursion
|
|
<< " Channel sample rate: " << m_channelSampleRate
|
|
<< " Input sample rate: " << 2 * m_settings.m_symbolRate
|
|
<< " m_softLDPCMaxTrials: " << m_settings.m_softLDPCMaxTrials;
|
|
|
|
m_objCfg.standard = m_settings.m_standard;
|
|
|
|
m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec);
|
|
m_objCfg.Fs = (float) 2 * m_settings.m_symbolRate; // maintained at twice the symbol rate
|
|
m_objCfg.Fm = (float) m_settings.m_symbolRate;
|
|
m_objCfg.fastlock = m_settings.m_fastLock;
|
|
|
|
m_objCfg.sampler = m_settings.m_filter;
|
|
m_objCfg.rolloff = m_settings.m_rollOff * 0.999f; //0...1 and correct 0.2 bug
|
|
m_objCfg.rrc_rej = (float) m_settings.m_excursion; //dB
|
|
m_objCfg.rrc_steps = 0; //auto
|
|
|
|
m_videoStream->resetTotalReceived();
|
|
m_udpStream.resetTotalReceived();
|
|
|
|
switch(m_settings.m_modulation)
|
|
{
|
|
case DATVDemodSettings::BPSK:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::BPSK;
|
|
break;
|
|
case DATVDemodSettings::QPSK:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::QPSK;
|
|
break;
|
|
case DATVDemodSettings::PSK8:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::PSK8;
|
|
break;
|
|
case DATVDemodSettings::APSK16:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::APSK16;
|
|
break;
|
|
case DATVDemodSettings::APSK32:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::APSK32;
|
|
break;
|
|
case DATVDemodSettings::APSK64E:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::APSK64E;
|
|
break;
|
|
case DATVDemodSettings::QAM16:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::QAM16;
|
|
break;
|
|
case DATVDemodSettings::QAM64:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::QAM64;
|
|
break;
|
|
case DATVDemodSettings::QAM256:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::QAM256;
|
|
break;
|
|
default:
|
|
m_objCfg.constellation = leansdr::cstln_lut<leansdr::llr_ss, 256>::BPSK;
|
|
break;
|
|
}
|
|
|
|
m_objCfg.allow_drift = m_settings.m_allowDrift;
|
|
m_objCfg.anf = m_settings.m_notchFilters;
|
|
m_objCfg.hard_metric = m_settings.m_hardMetric;
|
|
m_objCfg.sampler = m_settings.m_filter;
|
|
m_objCfg.viterbi = m_settings.m_viterbi;
|
|
|
|
// Min buffer size for baseband data
|
|
S2_MAX_SYMBOLS = (90*(1+360)+36*((360-1)/16));
|
|
|
|
BUF_BASEBAND = S2_MAX_SYMBOLS * 2 * (m_objCfg.Fs/m_objCfg.Fm) * m_objCfg.buf_factor;
|
|
// Min buffer size for IQ symbols
|
|
// cstln_receiver: writes in chunks of 128/omega symbols (margin 128)
|
|
// deconv_sync: reads at least 64+32
|
|
// A larger buffer improves performance significantly.
|
|
BUF_SYMBOLS = 1024 * m_objCfg.buf_factor;
|
|
|
|
|
|
// Min buffer size for misc measurements: 1
|
|
BUF_SLOW = m_objCfg.buf_factor;
|
|
|
|
// dvbs2 : Min buffer size for slots: 4 for deinterleaver
|
|
BUF_SLOTS = leansdr::modcod_info::MAX_SLOTS_PER_FRAME * m_objCfg.buf_factor;
|
|
|
|
BUF_FRAMES = m_objCfg.buf_factor;
|
|
|
|
// Min buffer size for TS packets: Up to 39 per BBFRAME
|
|
BUF_S2PACKETS = (leansdr::fec_info::KBCH_MAX/188/8+1) * m_objCfg.buf_factor;
|
|
|
|
m_lngExpectedReadIQ = BUF_BASEBAND;
|
|
|
|
m_objScheduler = new leansdr::scheduler();
|
|
|
|
//***************
|
|
p_rawiq = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "rawiq", BUF_BASEBAND);
|
|
p_rawiq_writer = new leansdr::pipewriter<leansdr::cf32>(*p_rawiq, m_RawIQMinWrite);
|
|
p_preprocessed = p_rawiq;
|
|
|
|
// NOTCH FILTER
|
|
|
|
if (m_objCfg.anf>0)
|
|
{
|
|
p_autonotched = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "autonotched", BUF_BASEBAND);
|
|
r_auto_notch = new leansdr::auto_notch<leansdr::f32>(m_objScheduler, *p_preprocessed, *p_autonotched, m_objCfg.anf, 0);
|
|
p_preprocessed = p_autonotched;
|
|
}
|
|
|
|
// FREQUENCY CORRECTION
|
|
|
|
//******** -> if ( m_objCfg.Fderot>0 )
|
|
|
|
// CNR ESTIMATION
|
|
|
|
p_cnr = new leansdr::pipebuf<leansdr::f32>(m_objScheduler, "cnr", BUF_SLOW);
|
|
|
|
if (m_objCfg.cnr == true)
|
|
{
|
|
r_cnr = new leansdr::cnr_fft<leansdr::f32>(m_objScheduler, *p_preprocessed, *p_cnr, m_objCfg.Fm/m_objCfg.Fs, 1024);
|
|
r_cnr->decimation = decimation(m_objCfg.Fs, 5); // 5 Hz
|
|
}
|
|
|
|
// FILTERING
|
|
|
|
//******** -> if ( m_objCfg.resample )
|
|
|
|
// DECIMATION
|
|
// (Unless already done in resampler)
|
|
|
|
//******** -> if ( !m_objCfg.resample && m_objCfg.decim>1 )
|
|
|
|
//Resampling FS
|
|
|
|
// Generic constellation receiver
|
|
|
|
p_freq = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "freq", BUF_SLOW);
|
|
p_ss = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "SS", BUF_SLOW);
|
|
p_mer = new leansdr::pipebuf<leansdr::f32> (m_objScheduler, "MER", BUF_SLOW);
|
|
|
|
switch (m_objCfg.sampler)
|
|
{
|
|
case DATVDemodSettings::SAMP_NEAREST:
|
|
sampler = new leansdr::nearest_sampler<float>();
|
|
break;
|
|
case DATVDemodSettings::SAMP_LINEAR:
|
|
sampler = new leansdr::linear_sampler<float>();
|
|
break;
|
|
case DATVDemodSettings::SAMP_RRC:
|
|
{
|
|
if (m_objCfg.rrc_steps == 0)
|
|
{
|
|
// At least 64 discrete sampling points between symbols
|
|
m_objCfg.rrc_steps = std::max(1, (int)(64*m_objCfg.Fm / m_objCfg.Fs));
|
|
}
|
|
|
|
float Frrc = m_objCfg.Fs * m_objCfg.rrc_steps; // Sample freq of the RRC filter
|
|
float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff;
|
|
int order = m_objCfg.rrc_rej * Frrc / (22*transition);
|
|
ncoeffs_sampler = leansdr::filtergen::root_raised_cosine(order, m_objCfg.Fm/Frrc, m_objCfg.rolloff, &coeffs_sampler);
|
|
sampler = new leansdr::fir_sampler<float,float>(ncoeffs_sampler, coeffs_sampler, m_objCfg.rrc_steps);
|
|
break;
|
|
}
|
|
default:
|
|
qCritical("DATVDemodSink::InitDATVS2Framework: Interpolator not implemented");
|
|
return;
|
|
}
|
|
|
|
p_slots_dvbs2 = new leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> > (m_objScheduler, "PL slots", BUF_SLOTS);
|
|
|
|
p_cstln = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "cstln", BUF_BASEBAND);
|
|
p_cstln_pls = new leansdr::pipebuf<leansdr::cf32>(m_objScheduler, "PLS cstln", BUF_BASEBAND);
|
|
p_framelock = new leansdr::pipebuf<int>(m_objScheduler, "frame lock", BUF_SLOW);
|
|
|
|
m_objDemodulatorDVBS2 = new leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss>(
|
|
m_objScheduler,
|
|
sampler,
|
|
*p_preprocessed,
|
|
*(leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> > *) p_slots_dvbs2,
|
|
/* p_freq */ nullptr,
|
|
/* p_ss */ nullptr,
|
|
p_mer,
|
|
p_cstln,
|
|
/* p_cstln_pls */ nullptr,
|
|
/*p_iqsymbols*/ nullptr,
|
|
/* p_framelock */nullptr
|
|
);
|
|
|
|
objDemodulatorDVBS2 = (leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss> *) m_objDemodulatorDVBS2;
|
|
objDemodulatorDVBS2->omega0 = m_objCfg.Fs/m_objCfg.Fm;
|
|
//objDemodulatorDVBS2->mu=1;
|
|
|
|
m_objCfg.Ftune=0.0f;
|
|
objDemodulatorDVBS2->Ftune = m_objCfg.Ftune / m_objCfg.Fm;
|
|
|
|
/*
|
|
demod.strongpls = cfg.strongpls;
|
|
*/
|
|
|
|
objDemodulatorDVBS2->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo);
|
|
objDemodulatorDVBS2->strongpls = false;
|
|
objDemodulatorDVBS2->cstln = make_dvbs2_constellation(m_objCfg.constellation, m_objCfg.fec);
|
|
m_cstlnSetByModcod = false;
|
|
|
|
//constellation
|
|
#ifndef SERVER_MODE
|
|
if (m_tvScreen)
|
|
{
|
|
qDebug("DATVDemodSink::InitDATVS2Framework: Register DVBS 2 TVSCREEN");
|
|
r_scope_symbols_dvbs2 = new leansdr::datvdvbs2constellation<leansdr::f32>(m_objScheduler, *p_cstln /* *p_sampled */ /* *p_cstln */, -128,128, nullptr, m_tvScreen);
|
|
r_scope_symbols_dvbs2->decimation = 1;
|
|
r_scope_symbols_dvbs2->cstln = (leansdr::cstln_base**) &objDemodulatorDVBS2->cstln;
|
|
r_scope_symbols_dvbs2->calculate_cstln_points();
|
|
}
|
|
#endif
|
|
r_merMeter = new leansdr::datvmeter(m_objScheduler, *p_mer);
|
|
r_cnrMeter = new leansdr::datvmeter(m_objScheduler, *p_cnr);
|
|
|
|
// Bit-flipping mode.
|
|
// Deinterleave into hard bits.
|
|
|
|
p_bbframes = new leansdr::pipebuf<leansdr::bbframe>(m_objScheduler, "BB frames", BUF_FRAMES);
|
|
|
|
// 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
|
|
// );
|
|
|
|
p_vbitcount= new leansdr::pipebuf<int>(m_objScheduler, "Bits processed", BUF_S2PACKETS);
|
|
p_verrcount = new leansdr::pipebuf<int>(m_objScheduler, "Bits corrected", BUF_S2PACKETS);
|
|
|
|
// bool commandFileValid = false;
|
|
|
|
if (m_settings.m_softLDPC /*&& commandFileValid*/)
|
|
{
|
|
#if 0
|
|
// Doesn't work...
|
|
// Soft LDPC decoder mode.
|
|
// Deinterleave into soft bits.
|
|
p_fecframes = new leansdr::pipebuf<leansdr::fecframe<leansdr::llr_sb> >(m_objScheduler, "FEC frames", BUF_FRAMES);
|
|
p_s2_deinterleaver = new leansdr::s2_deinterleaver<leansdr::llr_ss, leansdr::llr_sb>(
|
|
m_objScheduler,
|
|
*(leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> > *) p_slots_dvbs2,
|
|
*(leansdr::pipebuf< leansdr::fecframe<leansdr::llr_sb> > * ) p_fecframes
|
|
);
|
|
r_fecdecsoft = new leansdr::s2_fecdec_soft<leansdr::llr_t,leansdr::llr_sb>(
|
|
m_objScheduler, *(leansdr::pipebuf< leansdr::fecframe<leansdr::llr_sb> > * ) p_fecframes,
|
|
*(leansdr::pipebuf<leansdr::bbframe> *) p_bbframes,
|
|
m_modcodModulation < 0 ? 0 : m_modcodModulation,
|
|
true, 5,
|
|
p_vbitcount,
|
|
p_verrcount
|
|
);
|
|
#else
|
|
// External LDPC decoder mode.
|
|
// Deinterleave into soft bits.
|
|
// TBD Latency
|
|
p_fecframes = new leansdr::pipebuf<leansdr::fecframe<leansdr::llr_sb> >(m_objScheduler, "FEC frames", BUF_FRAMES);
|
|
p_s2_deinterleaver = new leansdr::s2_deinterleaver<leansdr::llr_ss, leansdr::llr_sb>(
|
|
m_objScheduler,
|
|
*(leansdr::pipebuf< leansdr::plslot<leansdr::llr_ss> > *) p_slots_dvbs2,
|
|
*(leansdr::pipebuf< leansdr::fecframe<leansdr::llr_sb> > *) p_fecframes
|
|
);
|
|
// Decode FEC-protected frames into plain BB frames.
|
|
r_fecdechelper = new leansdr::s2_fecdec_helper<leansdr::llr_t, leansdr::llr_sb>(
|
|
m_objScheduler,
|
|
*(leansdr::pipebuf< leansdr::fecframe<leansdr::llr_sb> > *) p_fecframes,
|
|
*(leansdr::pipebuf<leansdr::bbframe> *) p_bbframes,
|
|
p_vbitcount,
|
|
p_verrcount)
|
|
;
|
|
leansdr::s2_fecdec_helper<leansdr::llr_t, leansdr::llr_sb> *fecdec = (leansdr::s2_fecdec_helper<leansdr::llr_t, leansdr::llr_sb> *) r_fecdechelper;
|
|
const int nhelpers = 2;
|
|
fecdec->nhelpers = nhelpers;
|
|
fecdec->must_buffer = false;
|
|
fecdec->max_trials = m_settings.m_softLDPCMaxTrials;
|
|
#endif
|
|
}
|
|
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;
|
|
}
|
|
|
|
// Deframe BB frames to TS packets
|
|
p_lock = new leansdr::pipebuf<int> (m_objScheduler, "lock", BUF_SLOW);
|
|
p_locktime = new leansdr::pipebuf<leansdr::u32> (m_objScheduler, "locktime", BUF_S2PACKETS);
|
|
p_tspackets = new leansdr::pipebuf<leansdr::tspacket>(m_objScheduler, "TS packets", BUF_S2PACKETS);
|
|
p_deframer = new leansdr::s2_deframer(m_objScheduler,*(leansdr::pipebuf<leansdr::bbframe> *) p_bbframes, *p_tspackets, p_lock, p_locktime);
|
|
|
|
/*
|
|
if ( cfg.fd_gse >= 0 ) deframer.fd_gse = cfg.fd_gse;
|
|
*/
|
|
|
|
// OUTPUT
|
|
if (m_settings.m_playerEnable) {
|
|
r_videoplayer = new leansdr::datvvideoplayer<leansdr::tspacket>(m_objScheduler, *p_tspackets, m_videoStream, &m_udpStream);
|
|
} else {
|
|
r_videoplayer = new leansdr::datvvideoplayer<leansdr::tspacket>(m_objScheduler, *p_tspackets, nullptr, &m_udpStream);
|
|
}
|
|
|
|
r_videoplayer->setSymbolRate(m_settings.m_symbolRate);
|
|
m_blnDVBInitialized = true;
|
|
}
|
|
|
|
void DATVDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
|
{
|
|
leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss> *objDemodulatorDVBS2 =
|
|
(leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss> *) m_objDemodulatorDVBS2;
|
|
|
|
// DVBS2: Track change of constellation via MODCOD
|
|
if ((m_settings.m_standard == DATVDemodSettings::DVB_S2) && objDemodulatorDVBS2)
|
|
{
|
|
if (objDemodulatorDVBS2->cstln->m_setByModcod && !m_cstlnSetByModcod)
|
|
{
|
|
qDebug("DATVDemodSink::feed: change by MODCOD detected");
|
|
|
|
// Update constellation
|
|
#ifndef SERVER_MODE
|
|
if (r_scope_symbols_dvbs2) {
|
|
r_scope_symbols_dvbs2->calculate_cstln_points();
|
|
}
|
|
#endif
|
|
if (getMessageQueueToGUI())
|
|
{
|
|
DATVDemodReport::MsgReportModcodCstlnChange *msg = DATVDemodReport::MsgReportModcodCstlnChange::create(
|
|
DATVDemodSettings::getModulationFromLeanDVBCode(objDemodulatorDVBS2->cstln->m_typeCode),
|
|
DATVDemodSettings::getCodeRateFromLeanDVBCode(objDemodulatorDVBS2->cstln->m_rateCode)
|
|
);
|
|
|
|
getMessageQueueToGUI()->push(msg);
|
|
}
|
|
|
|
if (
|
|
(
|
|
(m_modcodModulation != objDemodulatorDVBS2->m_modcodType) &&
|
|
(m_modcodModulation >= 0) &&
|
|
(objDemodulatorDVBS2->m_modcodType >= 0)
|
|
) ||
|
|
(
|
|
(m_modcodCodeRate != objDemodulatorDVBS2->m_modcodRate) &&
|
|
(m_modcodCodeRate >= 0) &&
|
|
(objDemodulatorDVBS2->m_modcodRate >= 0)
|
|
)
|
|
)
|
|
{
|
|
m_blnNeedConfigUpdate = true;
|
|
}
|
|
}
|
|
|
|
m_cstlnSetByModcod = objDemodulatorDVBS2->cstln->m_setByModcod;
|
|
m_modcodModulation = objDemodulatorDVBS2->m_modcodType;
|
|
m_modcodCodeRate = objDemodulatorDVBS2->m_modcodRate;
|
|
} // DVBS2: Track change of constellation via MODCOD
|
|
|
|
//********** init leandvb framework **********
|
|
if (m_blnNeedConfigUpdate)
|
|
{
|
|
QMutexLocker mlock(&m_mutex);
|
|
stopVideo();
|
|
|
|
qDebug("DATVDemodSink::feed: Settings applied. Standard : %d...", m_settings.m_standard);
|
|
m_blnNeedConfigUpdate = false;
|
|
try
|
|
{
|
|
if(m_settings.m_standard==DATVDemodSettings::DVB_S2)
|
|
{
|
|
qDebug("DATVDemodSink::feed: init DVBS-2");
|
|
InitDATVS2Framework();
|
|
}
|
|
else
|
|
{
|
|
qDebug("DATVDemodSink::feed: init DVBS");
|
|
InitDATVFramework();
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
// leansdr uses fail() for unrecoverable internal errors.
|
|
// Convert these into a DATV initialization failure instead of
|
|
// allowing them to terminate the application.
|
|
qCritical("DATVDemodSink::feed: DATV framework initialization failed: %s", e.what());
|
|
|
|
CleanUpDATVFramework();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//********** Bis repetita : Let's rock and roll buddy ! **********
|
|
for (SampleVector::const_iterator it = begin; it != end; ++it /* ++it **/)
|
|
{
|
|
Complex c(it->real(), it->imag());
|
|
|
|
if (m_interpolatorDistance < 1.0f) // interpolate - should never get there...
|
|
{
|
|
Complex ci;
|
|
c *= m_nco.nextIQ();
|
|
|
|
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
|
|
{
|
|
processOneSample(ci);
|
|
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
|
}
|
|
}
|
|
else // decimate
|
|
{
|
|
Complex ci;
|
|
c *= m_nco.nextIQ();
|
|
|
|
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
|
|
{
|
|
processOneSample(ci);
|
|
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
|
}
|
|
}
|
|
} // Samples for loop
|
|
}
|
|
|
|
void DATVDemodSink::processOneSample(Complex &ci)
|
|
{
|
|
m_objMagSqAverage(norm(ci));
|
|
|
|
if (m_blnDVBInitialized
|
|
&& (p_rawiq_writer != nullptr)
|
|
&& (m_objScheduler != nullptr))
|
|
{
|
|
|
|
p_rawiq_writer->write(ci);
|
|
m_lngReadIQ++;
|
|
|
|
int writable = p_rawiq_writer->writable();
|
|
|
|
//Leave +1 by safety
|
|
//if(((m_lngReadIQ+1)>=lngWritable) || (m_lngReadIQ>=768))
|
|
if ((m_lngReadIQ + 1) >= writable)
|
|
{
|
|
m_objScheduler->step();
|
|
|
|
m_lngReadIQ = 0;
|
|
p_rawiq_writer->reset(m_RawIQMinWrite);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
|
|
{
|
|
qDebug() << "DATVDemodSink::applyChannelSettings:"
|
|
<< " channelSampleRate: " << channelSampleRate
|
|
<< " channelFrequencyOffset: " << channelFrequencyOffset;
|
|
|
|
bool callApplySettings = false;
|
|
|
|
if ((m_settings.m_centerFrequency != channelFrequencyOffset) ||
|
|
(m_channelSampleRate != channelSampleRate) || force)
|
|
{
|
|
m_nco.setFreq(-(float) channelFrequencyOffset, (float) channelSampleRate);
|
|
qDebug("DATVDemodSink::applyChannelSettings: NCO: IF: %d <> TF: %d ISR: %d",
|
|
channelFrequencyOffset, m_settings.m_centerFrequency, channelSampleRate);
|
|
callApplySettings = true;
|
|
}
|
|
|
|
if ((m_channelSampleRate != channelSampleRate) || force)
|
|
{
|
|
m_interpolator.create(m_interpolatorPhaseSteps, channelSampleRate, m_settings.m_rfBandwidth / 2.2, m_interpolatorTapsPerPhase);
|
|
m_interpolatorDistanceRemain = 0;
|
|
m_interpolatorDistance = (Real) channelSampleRate / (Real) (2 * m_settings.m_symbolRate);
|
|
qDebug("DATVDemodSink::applyChannelSettings: m_interpolatorDistance: %f", m_interpolatorDistance);
|
|
}
|
|
|
|
m_channelSampleRate = channelSampleRate;
|
|
m_settings.m_centerFrequency = channelFrequencyOffset;
|
|
|
|
if (callApplySettings) {
|
|
applySettings(QStringList(), m_settings, true);
|
|
}
|
|
}
|
|
|
|
void DATVDemodSink::applySettings(const QStringList& settingsKeys, const DATVDemodSettings& settings, bool force)
|
|
{
|
|
QString msg = QObject::tr("DATVDemodSink::applySettings: force: %1").arg(force);
|
|
settings.debug(msg);
|
|
|
|
qDebug("DATVDemodSink::applySettings: m_channelSampleRate: %d", m_channelSampleRate);
|
|
|
|
if (m_channelSampleRate == 0) {
|
|
return;
|
|
}
|
|
|
|
#ifndef SERVER_MODE
|
|
if ((settingsKeys.contains("audioVolume") && (settings.m_audioVolume != m_settings.m_audioVolume)) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setAudioVolume(settings.m_audioVolume);
|
|
}
|
|
}
|
|
|
|
if ((settingsKeys.contains("audioMute") && (settings.m_audioMute != m_settings.m_audioMute)) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setAudioMute(settings.m_audioMute);
|
|
}
|
|
}
|
|
|
|
if ((settingsKeys.contains("videoMute") && (settings.m_videoMute != m_settings.m_videoMute)) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setVideoMute(settings.m_videoMute);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (settingsKeys.contains("rfBandwidth") ||
|
|
settingsKeys.contains("symbolRate") ||
|
|
settingsKeys.contains("centerFrequency") || force)
|
|
{
|
|
m_interpolator.create(m_interpolatorPhaseSteps, m_channelSampleRate, settings.m_rfBandwidth / 2.2, m_interpolatorTapsPerPhase);
|
|
m_interpolatorDistanceRemain = 0;
|
|
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) (2 * settings.m_symbolRate);
|
|
m_nco.setFreq(-(float) settings.m_centerFrequency, (float) m_channelSampleRate);
|
|
}
|
|
|
|
if ((settingsKeys.contains("udpTS") && (m_settings.m_udpTS != settings.m_udpTS)) || force)
|
|
{
|
|
m_udpStream.setActive(settings.m_udpTS);
|
|
|
|
if (r_videoplayer && !settings.m_udpTS) {
|
|
r_videoplayer->resetUDPRunning();
|
|
}
|
|
}
|
|
|
|
if ((settingsKeys.contains("symbolRate") && (m_settings.m_symbolRate != settings.m_symbolRate)) || force) {
|
|
if (r_videoplayer) {
|
|
r_videoplayer->setSymbolRate(settings.m_symbolRate);
|
|
}
|
|
}
|
|
|
|
if ((settingsKeys.contains("udpTSAddress") && (m_settings.m_udpTSAddress != settings.m_udpTSAddress)) || force) {
|
|
m_udpStream.setAddress(settings.m_udpTSAddress);
|
|
}
|
|
|
|
if ((settingsKeys.contains("udpTSPort") && (m_settings.m_udpTSPort != settings.m_udpTSPort)) || force) {
|
|
m_udpStream.setPort(settings.m_udpTSPort);
|
|
}
|
|
|
|
if (m_settings.isDifferent(settings) || force) {
|
|
m_blnNeedConfigUpdate = true;
|
|
}
|
|
|
|
if (force) {
|
|
m_settings = settings;
|
|
} else {
|
|
m_settings.applySettings(settingsKeys, settings);
|
|
}
|
|
}
|
|
|
|
int DATVDemodSink::getLeanDVBCodeRateFromDATV(DATVDemodSettings::DATVCodeRate datvCodeRate)
|
|
{
|
|
if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC12) {
|
|
return (int) leansdr::code_rate::FEC12;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC13) {
|
|
return (int) leansdr::code_rate::FEC13;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC14) {
|
|
return (int) leansdr::code_rate::FEC14;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC23) {
|
|
return (int) leansdr::code_rate::FEC23;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC25) {
|
|
return (int) leansdr::code_rate::FEC25;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC34) {
|
|
return (int) leansdr::code_rate::FEC34;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC35) {
|
|
return (int) leansdr::code_rate::FEC35;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC45) {
|
|
return (int) leansdr::code_rate::FEC45;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC46) {
|
|
return (int) leansdr::code_rate::FEC46;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC56) {
|
|
return (int) leansdr::code_rate::FEC56;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC78) {
|
|
return (int) leansdr::code_rate::FEC78;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC89) {
|
|
return (int) leansdr::code_rate::FEC89;
|
|
} else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC910) {
|
|
return (int) leansdr::code_rate::FEC910;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int DATVDemodSink::getLeanDVBModulationFromDATV(DATVDemodSettings::DATVModulation datvModulation)
|
|
{
|
|
if (datvModulation == DATVDemodSettings::DATVModulation::APSK16) {
|
|
return (int) leansdr::cstln_base::predef::APSK16;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::APSK32) {
|
|
return (int) leansdr::cstln_base::predef::APSK32;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::APSK64E) {
|
|
return (int) leansdr::cstln_base::predef::APSK64E;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::BPSK) {
|
|
return (int) leansdr::cstln_base::predef::BPSK;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::PSK8) {
|
|
return (int) leansdr::cstln_base::predef::PSK8;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::QAM16) {
|
|
return (int) leansdr::cstln_base::predef::QAM16;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::QAM64) {
|
|
return (int) leansdr::cstln_base::predef::QAM64;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::QAM256) {
|
|
return (int) leansdr::cstln_base::predef::QAM256;
|
|
} else if (datvModulation == DATVDemodSettings::DATVModulation::QPSK) {
|
|
return (int) leansdr::cstln_base::predef::QPSK;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|