mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 10:05:46 -05:00
1472 lines
48 KiB
C++
1472 lines
48 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/>. //
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "datvdemodsink.h"
|
|
|
|
#include "leansdr/dvbs2.h"
|
|
|
|
#include <QDebug>
|
|
#include <QObject>
|
|
#include <QFileInfo>
|
|
#include <QMutexLocker>
|
|
|
|
#include "datvdemodreport.h"
|
|
|
|
const unsigned int DATVDemodSink::m_rfFilterFftLength = 512;
|
|
|
|
DATVDemodSink::DATVDemodSink() :
|
|
m_blnNeedConfigUpdate(false),
|
|
m_tvScreen(nullptr),
|
|
m_videoRender(nullptr),
|
|
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();
|
|
|
|
if (m_videoThread) {
|
|
delete m_videoThread;
|
|
}
|
|
}
|
|
|
|
void DATVDemodSink::stopVideo()
|
|
{
|
|
if (m_videoThread)
|
|
{
|
|
if (m_videoThread->isRunning())
|
|
{
|
|
m_videoThread->stopRendering();
|
|
m_videoThread->quit();
|
|
m_videoThread->wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DATVDemodSink::setTVScreen(TVScreen *tvScreen)
|
|
{
|
|
m_tvScreen = tvScreen;
|
|
}
|
|
|
|
void DATVDemodSink::SetVideoRender(DATVideoRender *screen)
|
|
{
|
|
m_videoRender = screen;
|
|
m_videoRender->setAudioFIFO(&m_audioFifo);
|
|
m_videoThread = new DATVideoRenderThread(m_videoRender, m_videoStream);
|
|
m_videoThread->setObjectName("vtDATVDemodSink");
|
|
}
|
|
|
|
bool DATVDemodSink::audioActive()
|
|
{
|
|
if (m_videoRender) {
|
|
return m_videoRender->getAudioStreamIndex() >= 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DATVDemodSink::videoActive()
|
|
{
|
|
if (m_videoRender) {
|
|
return m_videoRender->getVideoStreamIndex() >= 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DATVDemodSink::audioDecodeOK()
|
|
{
|
|
if (m_videoRender) {
|
|
return m_videoRender->getAudioDecodeOK();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DATVDemodSink::videoDecodeOK()
|
|
{
|
|
if (m_videoRender) {
|
|
return m_videoRender->getVideoDecodeOK();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DATVDemodSink::udpRunning()
|
|
{
|
|
if (!r_videoplayer) {
|
|
return false;
|
|
}
|
|
|
|
bool udpRunning = r_videoplayer->isUDPRunning();
|
|
r_videoplayer->resetUDPRunning();
|
|
|
|
return udpRunning;
|
|
}
|
|
|
|
bool DATVDemodSink::playVideo()
|
|
{
|
|
QMutexLocker mlock(&m_mutex);
|
|
|
|
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();
|
|
}
|
|
|
|
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
|
|
if (r_scope_symbols != nullptr) {
|
|
delete r_scope_symbols;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (r_scope_symbols_dvbs2 != nullptr) {
|
|
delete r_scope_symbols_dvbs2;
|
|
}
|
|
|
|
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
|
|
r_scope_symbols = nullptr;
|
|
|
|
//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;
|
|
r_scope_symbols_dvbs2 = nullptr;
|
|
}
|
|
|
|
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
|
|
|
|
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();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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_softLDPCToolPath: " << m_settings.m_softLDPCToolPath;
|
|
|
|
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
|
|
|
|
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();
|
|
}
|
|
|
|
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 (QFileInfo::exists(m_settings.m_softLDPCToolPath))
|
|
{
|
|
QFileInfo fileInfo = QFileInfo(m_settings.m_softLDPCToolPath);
|
|
// commandFileValid = fileInfo.isExecutable();
|
|
}
|
|
|
|
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
|
|
QByteArray ba = m_settings.m_softLDPCToolPath.toLocal8Bit();
|
|
const char *c_str2 = ba.data();
|
|
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,
|
|
c_str2,
|
|
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);
|
|
}
|
|
|
|
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");
|
|
|
|
if (r_scope_symbols_dvbs2) {
|
|
r_scope_symbols_dvbs2->calculate_cstln_points();
|
|
}
|
|
|
|
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;
|
|
|
|
if(m_settings.m_standard==DATVDemodSettings::DVB_S2)
|
|
{
|
|
qDebug("DATVDemodSink::feed: init DVBS-2");
|
|
InitDATVS2Framework();
|
|
}
|
|
else
|
|
{
|
|
qDebug("DATVDemodSink::feed: init DVBS");
|
|
InitDATVFramework();
|
|
}
|
|
}
|
|
|
|
//********** 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(m_settings, true);
|
|
}
|
|
}
|
|
|
|
void DATVDemodSink::applySettings(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;
|
|
}
|
|
|
|
if ((settings.m_audioVolume) != (m_settings.m_audioVolume) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setAudioVolume(settings.m_audioVolume);
|
|
}
|
|
}
|
|
|
|
if ((settings.m_audioMute) != (m_settings.m_audioMute) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setAudioMute(settings.m_audioMute);
|
|
}
|
|
}
|
|
|
|
if ((settings.m_videoMute) != (m_settings.m_videoMute) || force)
|
|
{
|
|
if (m_videoRender) {
|
|
m_videoRender->setVideoMute(settings.m_videoMute);
|
|
}
|
|
}
|
|
|
|
if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth)
|
|
|| (m_settings.m_symbolRate != settings.m_symbolRate)
|
|
|| (m_settings.m_centerFrequency != settings.m_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 ((m_settings.m_udpTS != settings.m_udpTS) || force) {
|
|
m_udpStream.setActive(settings.m_udpTS);
|
|
}
|
|
|
|
if ((m_settings.m_udpTSAddress != settings.m_udpTSAddress) || force) {
|
|
m_udpStream.setAddress(settings.m_udpTSAddress);
|
|
}
|
|
|
|
if ((m_settings.m_udpTSPort != settings.m_udpTSPort) || force) {
|
|
m_udpStream.setPort(settings.m_udpTSPort);
|
|
}
|
|
|
|
if (m_settings.isDifferent(settings) || force) {
|
|
m_blnNeedConfigUpdate = true;
|
|
}
|
|
|
|
m_settings = 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;
|
|
}
|
|
}
|