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

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