mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-26 09:48:45 -05:00
DATV demod: experimental DVB-S2 support
This commit is contained in:
parent
c00ccf3b64
commit
c981d76e6e
@ -24,7 +24,10 @@ set(datv_HEADERS
|
||||
datvdemodsettings.h
|
||||
datvideostream.h
|
||||
datvideorender.h
|
||||
datvconstellation.h
|
||||
datvdvbs2constellation.h
|
||||
leansdr/dvb.h
|
||||
leansdr/dvbs2.h
|
||||
leansdr/filtergen.h
|
||||
leansdr/framework.h
|
||||
leansdr/math.h
|
||||
|
@ -29,7 +29,7 @@ namespace leansdr {
|
||||
|
||||
static const int DEFAULT_GUI_DECIMATION = 64;
|
||||
|
||||
static inline cstln_lut<eucl_ss, 256> * make_dvbs2_constellation(cstln_lut<eucl_ss, 256>::predef c,
|
||||
static inline cstln_lut<eucl_ss, 256> * make_dvbs_constellation(cstln_lut<eucl_ss, 256>::predef c,
|
||||
code_rate r)
|
||||
{
|
||||
float gamma1 = 1, gamma2 = 1, gamma3 = 1;
|
||||
@ -59,7 +59,7 @@ static inline cstln_lut<eucl_ss, 256> * make_dvbs2_constellation(cstln_lut<eucl_
|
||||
gamma1 = 2.57;
|
||||
break;
|
||||
default:
|
||||
fail("cstln_lut<256>::make_dvbs2_constellation: Code rate not supported with APSK16");
|
||||
fail("cstln_lut<256>::make_dvbs_constellation: Code rate not supported with APSK16");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
@ -88,7 +88,7 @@ static inline cstln_lut<eucl_ss, 256> * make_dvbs2_constellation(cstln_lut<eucl_
|
||||
gamma2 = 4.30;
|
||||
break;
|
||||
default:
|
||||
fail("cstln_lut<eucl_ss, 256>::make_dvbs2_constellation: Code rate not supported with APSK32");
|
||||
fail("cstln_lut<eucl_ss, 256>::make_dvbs_constellation: Code rate not supported with APSK32");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include "datvdemod.h"
|
||||
|
||||
#include "leansdr/dvbs2.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
#include <stdio.h>
|
||||
@ -368,6 +370,63 @@ void DATVDemod::CleanUpDATVFramework(bool blnRelease)
|
||||
//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(p_deframer != nullptr)
|
||||
{
|
||||
delete (leansdr::s2_deframer*) p_deframer;
|
||||
}
|
||||
|
||||
if(r_scope_symbols_dvbs2 != nullptr)
|
||||
{
|
||||
delete r_scope_symbols_dvbs2;
|
||||
}
|
||||
}
|
||||
|
||||
m_objScheduler=nullptr;
|
||||
@ -452,13 +511,26 @@ void DATVDemod::CleanUpDATVFramework(bool blnRelease)
|
||||
r_derand = nullptr;
|
||||
|
||||
|
||||
//OUTPUT : To remove
|
||||
//OUTPUT : To remove void *
|
||||
r_stdout = nullptr;
|
||||
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;
|
||||
p_deframer = nullptr;
|
||||
r_scope_symbols_dvbs2 = nullptr;
|
||||
}
|
||||
|
||||
void DATVDemod::InitDATVFramework()
|
||||
@ -468,6 +540,7 @@ void DATVDemod::InitDATVFramework()
|
||||
CleanUpDATVFramework(false);
|
||||
|
||||
qDebug() << "DATVDemod::InitDATVFramework:"
|
||||
<< " Standard: " << m_settings.m_standard
|
||||
<< " Symbol Rate: " << m_settings.m_symbolRate
|
||||
<< " Modulation: " << m_settings.m_modulation
|
||||
<< " Notch Filters: " << m_settings.m_notchFilters
|
||||
@ -477,7 +550,8 @@ void DATVDemod::InitDATVFramework()
|
||||
<< " HARD METRIC: " << m_settings.m_hardMetric
|
||||
<< " RollOff: " << m_settings.m_rollOff
|
||||
<< " Viterbi: " << m_settings.m_viterbi
|
||||
<< " Excursion: " << m_settings.m_excursion;
|
||||
<< " Excursion: " << m_settings.m_excursion
|
||||
<< " Sample rate: " << m_sampleRate;
|
||||
|
||||
m_objCfg.standard = m_settings.m_standard;
|
||||
|
||||
@ -670,7 +744,7 @@ void DATVDemod::InitDATVFramework()
|
||||
qDebug("DATVDemod::InitDATVFramework: DVB-S2: Testing symbol sampler only.");
|
||||
}
|
||||
|
||||
m_objDemodulator->cstln = make_dvbs2_constellation(m_objCfg.constellation, m_objCfg.fec);
|
||||
m_objDemodulator->cstln = make_dvbs_constellation(m_objCfg.constellation, m_objCfg.fec);
|
||||
|
||||
if (m_objCfg.hard_metric) {
|
||||
m_objDemodulator->cstln->harden();
|
||||
@ -706,6 +780,8 @@ void DATVDemod::InitDATVFramework()
|
||||
|
||||
if (m_objRegisteredTVScreen)
|
||||
{
|
||||
qDebug("DATVDemod::InitDATVFramework: Register DVBSTVSCREEN");
|
||||
|
||||
m_objRegisteredTVScreen->resizeTVScreen(256,256);
|
||||
r_scope_symbols = new leansdr::datvconstellation<leansdr::f32>(m_objScheduler, *p_sampled, -128,128, nullptr, m_objRegisteredTVScreen);
|
||||
r_scope_symbols->decimation = 1;
|
||||
@ -784,6 +860,292 @@ void DATVDemod::InitDATVFramework()
|
||||
m_blnDVBInitialized = true;
|
||||
}
|
||||
|
||||
//************ DVB-S2 Decoder ************
|
||||
void DATVDemod::InitDATVS2Framework()
|
||||
{
|
||||
leansdr::s2_frame_receiver<leansdr::f32, leansdr::llr_ss> * objDemodulatorDVBS2;
|
||||
|
||||
m_blnDVBInitialized = false;
|
||||
m_lngReadIQ = 0;
|
||||
CleanUpDATVFramework(false);
|
||||
|
||||
qDebug() << "DATVDemod::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
|
||||
<< " Sample rate: " << m_sampleRate ;
|
||||
|
||||
|
||||
|
||||
m_objCfg.standard = m_settings.m_standard;
|
||||
|
||||
m_objCfg.fec = m_settings.m_fec;
|
||||
m_objCfg.Fs = (float) m_sampleRate;
|
||||
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
|
||||
|
||||
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);
|
||||
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);
|
||||
r_cnr->decimation = decimation(m_objCfg.Fs, 1); // 1 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_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("DATVDemod::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 */ nullptr,
|
||||
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->omega = 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->Fm = m_objCfg.Fm;
|
||||
objDemodulatorDVBS2->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo);
|
||||
|
||||
objDemodulatorDVBS2->strongpls = false;
|
||||
|
||||
|
||||
objDemodulatorDVBS2->cstln = make_dvbs2_constellation(m_objCfg.constellation, m_objCfg.fec);
|
||||
|
||||
|
||||
//constellation
|
||||
|
||||
if (m_objRegisteredTVScreen)
|
||||
{
|
||||
qDebug("DATVDemod::InitDATVS2Framework: Register DVBS 2 TVSCREEN");
|
||||
|
||||
m_objRegisteredTVScreen->resizeTVScreen(256,256);
|
||||
r_scope_symbols_dvbs2 = new leansdr::datvdvbs2constellation<leansdr::f32>(m_objScheduler, *p_cstln /* *p_sampled */ /* *p_cstln */, -128,128, nullptr, m_objRegisteredTVScreen);
|
||||
r_scope_symbols_dvbs2->decimation = 1;
|
||||
r_scope_symbols_dvbs2->cstln = (leansdr::cstln_base**) &objDemodulatorDVBS2->cstln;
|
||||
r_scope_symbols_dvbs2->calculate_cstln_points();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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=0;
|
||||
|
||||
/*
|
||||
fecdec->bitflips = cfg.ldpc_bf; //int TODO
|
||||
if ( ! cfg.ldpc_bf )
|
||||
fprintf(stderr, "Warning: No LDPC error correction selected.\n")
|
||||
*/
|
||||
|
||||
// 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
|
||||
r_videoplayer = new leansdr::datvvideoplayer<leansdr::tspacket>(m_objScheduler, *p_tspackets, m_objVideoStream);
|
||||
|
||||
m_blnDVBInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
|
||||
{
|
||||
(void) firstOfBurst;
|
||||
@ -795,6 +1157,8 @@ void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVect
|
||||
int intRFOut;
|
||||
double magSq;
|
||||
|
||||
int lngWritable=0;
|
||||
|
||||
//********** Bis repetita : Let's rock and roll buddy ! **********
|
||||
|
||||
#ifdef EXTENDED_DIRECT_SAMPLE
|
||||
@ -832,9 +1196,21 @@ void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVect
|
||||
|
||||
if (m_blnNeedConfigUpdate)
|
||||
{
|
||||
qDebug("DATVDemod::feed: Settings applied. Standard : %d...", m_settings.m_standard);
|
||||
m_objSettingsMutex.lock();
|
||||
m_blnNeedConfigUpdate=false;
|
||||
InitDATVFramework();
|
||||
|
||||
if(m_settings.m_standard==DATVDemodSettings::DVB_S2)
|
||||
{
|
||||
printf("SWITCHING TO DVBS-2\r\n");
|
||||
InitDATVS2Framework();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("SWITCHING TO DVBS\r\n");
|
||||
InitDATVFramework();
|
||||
}
|
||||
|
||||
m_objSettingsMutex.unlock();
|
||||
}
|
||||
|
||||
@ -863,14 +1239,17 @@ void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVect
|
||||
p_rawiq_writer->write(objIQ);
|
||||
m_lngReadIQ++;
|
||||
|
||||
lngWritable = p_rawiq_writer->writable();
|
||||
|
||||
//Leave +1 by safety
|
||||
if((m_lngReadIQ+1)>=p_rawiq_writer->writable())
|
||||
if(((m_lngReadIQ+1)>=lngWritable) || (m_lngReadIQ>=768))
|
||||
//if((m_lngReadIQ+1)>=lngWritable)
|
||||
{
|
||||
m_objScheduler->step();
|
||||
|
||||
m_lngReadIQ=0;
|
||||
delete p_rawiq_writer;
|
||||
p_rawiq_writer = new leansdr::pipewriter<leansdr::cf32>(*p_rawiq);
|
||||
//delete p_rawiq_writer;
|
||||
//p_rawiq_writer = new leansdr::pipewriter<leansdr::cf32>(*p_rawiq);
|
||||
}
|
||||
}
|
||||
|
||||
@ -896,7 +1275,9 @@ bool DATVDemod::handleMessage(const Message& cmd)
|
||||
qDebug() << "DATVDemod::handleMessage: MsgChannelizerNotification:"
|
||||
<< " m_intSampleRate: " << objNotif.getSampleRate()
|
||||
<< " m_intFrequencyOffset: " << objNotif.getFrequencyOffset();
|
||||
applyChannelSettings(objNotif.getSampleRate(), objNotif.getFrequencyOffset());
|
||||
|
||||
m_inputFrequencyOffset = objNotif.getFrequencyOffset();
|
||||
applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -911,6 +1292,9 @@ bool DATVDemod::handleMessage(const Message& cmd)
|
||||
qDebug() << "DATVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << m_channelizer->getInputSampleRate()
|
||||
<< " centerFrequency: " << cfg.getCenterFrequency();
|
||||
|
||||
m_sampleRate = m_channelizer->getInputSampleRate();
|
||||
applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureDATVDemod::match(cmd))
|
||||
@ -921,6 +1305,14 @@ bool DATVDemod::handleMessage(const Message& cmd)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(DSPSignalNotification::match(cmd))
|
||||
{
|
||||
m_sampleRate = m_channelizer->getInputSampleRate();
|
||||
qDebug("DATVDemod::handleMessage: DSPSignalNotification: sent sample rate: %d", m_sampleRate);
|
||||
applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -937,21 +1329,25 @@ void DATVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffs
|
||||
(m_sampleRate != inputSampleRate) || force)
|
||||
{
|
||||
m_objNCO.setFreq(-(float) inputFrequencyOffset, (float) inputSampleRate);
|
||||
qDebug("DATVDemod::applyChannelSettings: NCO: IF: %d <> TF: %d ISR: %d",
|
||||
inputFrequencyOffset, m_settings.m_centerFrequency, inputSampleRate);
|
||||
}
|
||||
|
||||
if ((m_sampleRate != inputSampleRate) || force)
|
||||
{
|
||||
m_objSettingsMutex.lock();
|
||||
//m_objSettingsMutex.lock();
|
||||
//Bandpass filter shaping
|
||||
Real fltLowCut = -((float) m_settings.m_rfBandwidth / 2.0) / (float) inputSampleRate;
|
||||
Real fltHiCut = ((float) m_settings.m_rfBandwidth / 2.0) / (float) inputSampleRate;
|
||||
m_objRFFilter->create_filter(fltLowCut, fltHiCut);
|
||||
m_blnNeedConfigUpdate = true;
|
||||
m_objSettingsMutex.unlock();
|
||||
//m_blnNeedConfigUpdate = true;
|
||||
//applySettings(m_settings,true);
|
||||
//m_objSettingsMutex.unlock();
|
||||
}
|
||||
|
||||
m_sampleRate = inputSampleRate;
|
||||
m_settings.m_centerFrequency = inputFrequencyOffset;
|
||||
applySettings(m_settings,true);
|
||||
}
|
||||
|
||||
void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force)
|
||||
@ -959,6 +1355,8 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force)
|
||||
QString msg = tr("DATVDemod::applySettings: force: %1").arg(force);
|
||||
settings.debug(msg);
|
||||
|
||||
qDebug("DATVDemod::applySettings: m_sampleRate: %d", m_sampleRate);
|
||||
|
||||
if (m_sampleRate == 0) {
|
||||
return;
|
||||
}
|
||||
@ -998,7 +1396,7 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force)
|
||||
|
||||
if (m_settings.isDifferent(settings) || force)
|
||||
{
|
||||
m_objSettingsMutex.lock();
|
||||
//m_objSettingsMutex.lock();
|
||||
|
||||
if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth)
|
||||
|| force)
|
||||
@ -1016,7 +1414,7 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force)
|
||||
m_objNCO.setFreq(-(float) settings.m_centerFrequency, (float) m_sampleRate);
|
||||
}
|
||||
|
||||
m_objSettingsMutex.unlock();
|
||||
//m_objSettingsMutex.unlock();
|
||||
m_blnNeedConfigUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -29,16 +29,14 @@ class DownChannelizer;
|
||||
//LeanSDR
|
||||
#include "leansdr/framework.h"
|
||||
#include "leansdr/generic.h"
|
||||
#include "leansdr/dsp.h"
|
||||
#include "leansdr/sdr.h"
|
||||
#include "leansdr/dvb.h"
|
||||
#include "leansdr/rs.h"
|
||||
#include "leansdr/filtergen.h"
|
||||
|
||||
#include "leansdr/hdlc.h"
|
||||
#include "leansdr/iess.h"
|
||||
|
||||
#include "datvconstellation.h"
|
||||
#include "datvdvbs2constellation.h"
|
||||
#include "datvvideoplayer.h"
|
||||
#include "datvideostream.h"
|
||||
#include "datvideorender.h"
|
||||
@ -185,6 +183,7 @@ public:
|
||||
void CleanUpDATVFramework(bool blnRelease);
|
||||
int GetSampleRate();
|
||||
void InitDATVFramework();
|
||||
void InitDATVS2Framework();
|
||||
double getMagSq() const { return m_objMagSqAverage; } //!< Beware this is scaled to 2^30
|
||||
|
||||
static const QString m_channelIdURI;
|
||||
@ -246,6 +245,13 @@ private:
|
||||
unsigned long BUF_PACKETS;
|
||||
unsigned long BUF_SLOW;
|
||||
|
||||
|
||||
//dvbs2
|
||||
unsigned long BUF_SLOTS;
|
||||
unsigned long BUF_FRAMES;
|
||||
unsigned long BUF_S2PACKETS;
|
||||
unsigned long S2_MAX_SYMBOLS;
|
||||
|
||||
//************** LEANDBV Scheduler ***************
|
||||
|
||||
leansdr::scheduler * m_objScheduler;
|
||||
@ -290,6 +296,18 @@ private:
|
||||
leansdr::pipebuf<leansdr::f32> *p_mer;
|
||||
leansdr::pipebuf<leansdr::cf32> *p_sampled;
|
||||
|
||||
//dvb-s2
|
||||
void *p_slots_dvbs2;
|
||||
leansdr::pipebuf<leansdr::cf32> *p_cstln;
|
||||
leansdr::pipebuf<leansdr::cf32> *p_cstln_pls;
|
||||
leansdr::pipebuf<int> *p_framelock;
|
||||
void *m_objDemodulatorDVBS2;
|
||||
void *p_fecframes;
|
||||
void *p_bbframes;
|
||||
void *p_s2_deinterleaver;
|
||||
void *r_fecdec;
|
||||
void *p_deframer;
|
||||
|
||||
//DECIMATION
|
||||
leansdr::pipebuf<leansdr::cf32> *p_decimated;
|
||||
leansdr::decimator<leansdr::cf32> *p_decim;
|
||||
@ -341,6 +359,7 @@ private:
|
||||
|
||||
//CONSTELLATION
|
||||
leansdr::datvconstellation<leansdr::f32> *r_scope_symbols;
|
||||
leansdr::datvdvbs2constellation<leansdr::f32> *r_scope_symbols_dvbs2;
|
||||
|
||||
DeviceAPI* m_deviceAPI;
|
||||
|
||||
@ -368,6 +387,7 @@ private:
|
||||
//DATVConfig m_objRunning;
|
||||
DATVDemodSettings m_settings;
|
||||
int m_sampleRate;
|
||||
int m_inputFrequencyOffset;
|
||||
MovingAverageUtil<double, double, 32> m_objMagSqAverage;
|
||||
|
||||
QMutex m_objSettingsMutex;
|
||||
|
@ -249,6 +249,8 @@ void DATVDemodGUI::applySettings(bool force)
|
||||
|
||||
if (m_blnDoApplySettings)
|
||||
{
|
||||
qDebug("DATVDemodGUI::applySettings");
|
||||
|
||||
//Bandwidth and center frequency
|
||||
m_objChannelMarker.setCenterFrequency(ui->deltaFrequency->getValueNew());
|
||||
m_objChannelMarker.setBandwidth(ui->rfBandwidth->getValueNew());
|
||||
@ -440,6 +442,25 @@ void DATVDemodGUI::tick()
|
||||
void DATVDemodGUI::on_cmbStandard_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
(void) arg1;
|
||||
QString strStandard;
|
||||
|
||||
strStandard = ui->cmbStandard->currentText();
|
||||
|
||||
if(strStandard=="DVB-S2")
|
||||
{
|
||||
ui->cmbFEC->addItem("4/5");
|
||||
ui->cmbFEC->addItem("4/6");
|
||||
ui->cmbFEC->addItem("8/9");
|
||||
ui->cmbFEC->addItem("9/10");
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->cmbFEC->removeItem(8);
|
||||
ui->cmbFEC->removeItem(7);
|
||||
ui->cmbFEC->removeItem(6);
|
||||
ui->cmbFEC->removeItem(5);
|
||||
}
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,11 @@
|
||||
<string>DVB-S</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DVB-S2</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="cmbModulation">
|
||||
<property name="geometry">
|
||||
|
@ -28,7 +28,7 @@
|
||||
const PluginDescriptor DATVDemodPlugin::m_ptrPluginDescriptor =
|
||||
{
|
||||
QString("DATV Demodulator"),
|
||||
QString("4.5.1"),
|
||||
QString("4.11.1"),
|
||||
QString("(c) F4HKW for SDRAngel using LeanSDR framework (c) F4DAV"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
@ -156,6 +156,7 @@ bool DATVDemodSettings::deserialize(const QByteArray& data)
|
||||
void DATVDemodSettings::debug(const QString& msg) const
|
||||
{
|
||||
qDebug() << msg
|
||||
<< " m_standard: " << m_standard
|
||||
<< " m_allowDrift: " << m_allowDrift
|
||||
<< " m_rfBandwidth: " << m_rfBandwidth
|
||||
<< " m_centerFrequency: " << m_centerFrequency
|
||||
@ -191,5 +192,6 @@ bool DATVDemodSettings::isDifferent(const DATVDemodSettings& other)
|
||||
|| (m_standard != other.m_standard)
|
||||
|| (m_notchFilters != other.m_notchFilters)
|
||||
|| (m_symbolRate != other.m_symbolRate)
|
||||
|| (m_excursion != other.m_excursion));
|
||||
|| (m_excursion != other.m_excursion)
|
||||
|| (m_standard != other.m_standard));
|
||||
}
|
219
plugins/channelrx/demoddatv/datvdvbs2constellation.h
Normal file
219
plugins/channelrx/demoddatv/datvdvbs2constellation.h
Normal file
@ -0,0 +1,219 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 F4HKW //
|
||||
// for F4EXB / SDRAngel //
|
||||
// using LeanSDR Framework (C) 2016 F4DAV //
|
||||
// //
|
||||
// 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 DATVDVBS2CONSTELLATION_H
|
||||
#define DATVDVBS2CONSTELLATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "leansdr/framework.h"
|
||||
#include "gui/tvscreen.h"
|
||||
|
||||
namespace leansdr {
|
||||
|
||||
static const int DEFAULT_GUI_DVBS2_DECIMATION = 64;
|
||||
|
||||
static inline cstln_lut<llr_ss, 256> * make_dvbs2_constellation(cstln_lut<llr_ss, 256>::predef c,
|
||||
code_rate r)
|
||||
{
|
||||
float gamma1 = 1, gamma2 = 1, gamma3 = 1;
|
||||
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case cstln_lut<llr_ss, 256>::APSK16:
|
||||
// EN 302 307, section 5.4.3, Table 9
|
||||
|
||||
switch (r)
|
||||
{
|
||||
case FEC23:
|
||||
case FEC46:
|
||||
gamma1 = 3.15;
|
||||
break;
|
||||
case FEC34:
|
||||
gamma1 = 2.85;
|
||||
break;
|
||||
case FEC45:
|
||||
gamma1 = 2.75;
|
||||
break;
|
||||
case FEC56:
|
||||
gamma1 = 2.70;
|
||||
break;
|
||||
case FEC89:
|
||||
gamma1 = 2.60;
|
||||
break;
|
||||
case FEC910:
|
||||
gamma1 = 2.57;
|
||||
break;
|
||||
default:
|
||||
fail("cstln_lut<256>::make_dvbs2_constellation: Code rate not supported with APSK16");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case cstln_lut<llr_ss, 256>::APSK32:
|
||||
// EN 302 307, section 5.4.4, Table 10
|
||||
switch (r)
|
||||
{
|
||||
case FEC34:
|
||||
gamma1 = 2.84;
|
||||
gamma2 = 5.27;
|
||||
break;
|
||||
case FEC45:
|
||||
gamma1 = 2.72;
|
||||
gamma2 = 4.87;
|
||||
break;
|
||||
case FEC56:
|
||||
gamma1 = 2.64;
|
||||
gamma2 = 4.64;
|
||||
break;
|
||||
case FEC89:
|
||||
gamma1 = 2.54;
|
||||
gamma2 = 4.33;
|
||||
break;
|
||||
case FEC910:
|
||||
gamma1 = 2.53;
|
||||
gamma2 = 4.30;
|
||||
break;
|
||||
default:
|
||||
fail("cstln_lut<llr_ss, 256>::make_dvbs2_constellation: Code rate not supported with APSK32");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case cstln_lut<llr_ss, 256>::APSK64E:
|
||||
// EN 302 307-2, section 5.4.5, Table 13f
|
||||
gamma1 = 2.4;
|
||||
gamma2 = 4.3;
|
||||
gamma3 = 7;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new cstln_lut<llr_ss, 256>(c, gamma1, gamma2, gamma3);
|
||||
}
|
||||
|
||||
template<typename T> struct datvdvbs2constellation: runnable
|
||||
{
|
||||
T xymin, xymax;
|
||||
unsigned long decimation;
|
||||
long pixels_per_frame;
|
||||
/*cstln_lut<llr_ss, 256>*/ cstln_base **cstln; // Optional ptr to optional constellation
|
||||
TVScreen *m_objDATVScreen;
|
||||
pipereader<complex<T> > in;
|
||||
unsigned long phase;
|
||||
std::vector<int> cstln_rows;
|
||||
std::vector<int> cstln_cols;
|
||||
|
||||
datvdvbs2constellation(
|
||||
scheduler *sch,
|
||||
pipebuf<complex<T> > &_in,
|
||||
T _xymin,
|
||||
T _xymax,
|
||||
const char *_name = nullptr,
|
||||
TVScreen *objDATVScreen = nullptr) :
|
||||
runnable(sch, _name ? _name : _in.name),
|
||||
xymin(_xymin),
|
||||
xymax(_xymax),
|
||||
decimation(DEFAULT_GUI_DVBS2_DECIMATION),
|
||||
pixels_per_frame(1024),
|
||||
cstln(0),
|
||||
m_objDATVScreen(objDATVScreen),
|
||||
in(_in),
|
||||
phase(0)
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
|
||||
phase=0;
|
||||
//Symbols
|
||||
while (in.readable() >= pixels_per_frame)
|
||||
{
|
||||
if ((!phase) && m_objDATVScreen)
|
||||
{
|
||||
m_objDATVScreen->resetImage();
|
||||
|
||||
complex<T> *p = in.rd(), *pend = p + pixels_per_frame;
|
||||
|
||||
for (; p < pend; ++p)
|
||||
{
|
||||
m_objDATVScreen->selectRow(256 * (p->re - xymin) / (xymax - xymin));
|
||||
m_objDATVScreen->setDataColor(
|
||||
256 - 256 * ((p->im - xymin) / (xymax - xymin)),
|
||||
255, 0, 255);
|
||||
}
|
||||
|
||||
if (cstln && (*cstln))
|
||||
{
|
||||
// Plot constellation points
|
||||
std::vector<int>::const_iterator row_it = cstln_rows.begin();
|
||||
std::vector<int>::const_iterator col_it = cstln_cols.begin();
|
||||
|
||||
for (;(row_it != cstln_rows.end()) && (col_it != cstln_cols.end()); ++row_it, ++col_it)
|
||||
{
|
||||
m_objDATVScreen->selectRow(*row_it);
|
||||
m_objDATVScreen->setDataColor(*col_it, 250, 250, 250);
|
||||
}
|
||||
}
|
||||
|
||||
m_objDATVScreen->renderImage(0);
|
||||
}
|
||||
|
||||
in.read(pixels_per_frame);
|
||||
|
||||
if (++phase >= decimation) {
|
||||
phase = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_begin()
|
||||
{
|
||||
}
|
||||
|
||||
void calculate_cstln_points()
|
||||
{
|
||||
if (!(*cstln)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cstln_rows.clear();
|
||||
cstln_cols.clear();
|
||||
|
||||
for (int i = 0; i < (*cstln)->nsymbols; ++i)
|
||||
{
|
||||
complex<signed char> *p = &(*cstln)->symbols[i];
|
||||
int x = 256 * (p->re - xymin) / (xymax - xymin);
|
||||
int y = 256 - 256 * (p->im - xymin) / (xymax - xymin);
|
||||
|
||||
for (int d = -4; d <= 4; ++d)
|
||||
{
|
||||
cstln_rows.push_back(x + d);
|
||||
cstln_cols.push_back(y);
|
||||
cstln_rows.push_back(x);
|
||||
cstln_cols.push_back(y + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // leansdr
|
||||
|
||||
#endif // DATVDVBS2CONSTELLATION_H
|
@ -17,12 +17,23 @@
|
||||
#ifndef LEANSDR_DVBS2_H
|
||||
#define LEANSDR_DVBS2_H
|
||||
|
||||
/*
|
||||
#include "leansdr/bch.h"
|
||||
#include "leansdr/crc.h"
|
||||
#include "leansdr/dvb.h"
|
||||
#include "leansdr/ldpc.h"
|
||||
#include "leansdr/sdr.h"
|
||||
#include "leansdr/softword.h"
|
||||
*/
|
||||
|
||||
#include "bch.h"
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
#include "dvb.h"
|
||||
#include "softword.h"
|
||||
#include "ldpc.h"
|
||||
#include "sdr.h"
|
||||
|
||||
namespace leansdr
|
||||
{
|
||||
@ -819,7 +830,7 @@ struct s2_frame_receiver : runnable
|
||||
// Slots to skip until next PL slot (pilot or sof)
|
||||
int till_next_pls = pls.pilots ? 16 : S;
|
||||
|
||||
for (int slots = S; slots--; ++pout, --till_next_pls)
|
||||
for (int leansdr_slots = S; leansdr_slots--; ++pout, --till_next_pls)
|
||||
{
|
||||
if (till_next_pls == 0)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user