From 3354c774fccee8b07ca825ada5802e063f7bc009 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 25 Feb 2018 00:07:08 +0100 Subject: [PATCH] DATV demod: fixed compilation warnings --- plugins/channelrx/CMakeLists.txt | 4 +- plugins/channelrx/demoddatv/CMakeLists.txt | 2 - .../channelrx/demoddatv/datvconstellation.h | 120 +- plugins/channelrx/demoddatv/datvdemod.cpp | 308 +- plugins/channelrx/demoddatv/datvdemod.h | 489 ++-- plugins/channelrx/demoddatv/datvdemodgui.cpp | 36 +- plugins/channelrx/demoddatv/leansdr/dsp.h | 677 +++-- plugins/channelrx/demoddatv/leansdr/dvb.h | 2449 +++++++++------- .../channelrx/demoddatv/leansdr/framework.h | 603 ++-- plugins/channelrx/demoddatv/leansdr/hdlc.h | 520 ++-- plugins/channelrx/demoddatv/leansdr/sdr.h | 2580 +++++++++-------- 11 files changed, 4361 insertions(+), 3427 deletions(-) diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 6f1dd1ec6..e8d70170a 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -16,9 +16,9 @@ if(LIBDSDCC_FOUND AND LIBMBE_FOUND) add_subdirectory(demoddsd) endif(LIBDSDCC_FOUND AND LIBMBE_FOUND) -if (NOT RX_SAMPLE_24BIT) +#if (NOT RX_SAMPLE_24BIT) add_subdirectory(demoddatv) -endif() +#endif() if (BUILD_DEBIAN) add_subdirectory(demoddsd) diff --git a/plugins/channelrx/demoddatv/CMakeLists.txt b/plugins/channelrx/demoddatv/CMakeLists.txt index 3135612ef..5b4f5115a 100644 --- a/plugins/channelrx/demoddatv/CMakeLists.txt +++ b/plugins/channelrx/demoddatv/CMakeLists.txt @@ -24,8 +24,6 @@ set(datv_FORMS datvdemodgui.ui ) -set (CMAKE_CXX_FLAGS "-Wno-unused-variable -Wno-deprecated-declarations") - include_directories( . ${CMAKE_CURRENT_BINARY_DIR} diff --git a/plugins/channelrx/demoddatv/datvconstellation.h b/plugins/channelrx/demoddatv/datvconstellation.h index 9f95a7a15..a343cbfe2 100644 --- a/plugins/channelrx/demoddatv/datvconstellation.h +++ b/plugins/channelrx/demoddatv/datvconstellation.h @@ -27,72 +27,82 @@ namespace leansdr { - static const int DEFAULT_GUI_DECIMATION = 64; +static const int DEFAULT_GUI_DECIMATION = 64; - template struct datvconstellation : runnable +template struct datvconstellation: runnable +{ + T xymin; + T xymax; + unsigned long decimation; + unsigned long pixels_per_frame; + cstln_lut<256> **cstln; // Optional ptr to optional constellation + pipereader > in; + unsigned long phase; + DATVScreen *m_objDATVScreen; + + datvconstellation( + scheduler *sch, + pipebuf > &_in, + T _xymin, + T _xymax, + const char *_name = 0, + DATVScreen * objDATVScreen = 0) : + runnable(sch, _name ? _name : _in.name), + xymin(_xymin), + xymax(_xymax), + decimation(DEFAULT_GUI_DECIMATION), + pixels_per_frame(1024), + cstln(0), + in(_in), + phase(0), + m_objDATVScreen(objDATVScreen) { - T xymin, xymax; - unsigned long decimation; - unsigned long pixels_per_frame; - cstln_lut<256> **cstln; // Optional ptr to optional constellation - DATVScreen *m_objDATVScreen; + } - - datvconstellation(scheduler *sch, pipebuf< complex > &_in, T _xymin, T _xymax, const char *_name=NULL, DATVScreen * objDATVScreen=NULL) : - runnable(sch, _name?_name:_in.name), - xymin(_xymin), - xymax(_xymax), - decimation(DEFAULT_GUI_DECIMATION), - pixels_per_frame(1024), - cstln(NULL), - in(_in), - phase(0), - m_objDATVScreen(objDATVScreen) - { - - } - - void run() - { + void run() + { //Symbols - while ( in.readable() >= pixels_per_frame ) + while (in.readable() >= pixels_per_frame) { - if ( ! phase ) + if (!phase) { m_objDATVScreen->resetImage(); - complex *p = in.rd(), *pend = p+pixels_per_frame; + complex *p = in.rd(), *pend = p + pixels_per_frame; - for ( ; pselectRow(256*(p->re-xymin)/(xymax-xymin)); - m_objDATVScreen->setDataColor(256- 256*((p->im-xymin)/(xymax-xymin)),255,0,255); + 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) ) + if (cstln && (*cstln)) { - // Plot constellation points + // Plot constellation points - for ( int i=0; i<(*cstln)->nsymbols; ++i ) - { - complex *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 ) + for (int i = 0; i < (*cstln)->nsymbols; ++i) { - m_objDATVScreen->selectRow(x+d); - m_objDATVScreen->setDataColor(y,5,250,250); - m_objDATVScreen->selectRow(x); - m_objDATVScreen->setDataColor(y+d,5,250,250); + complex *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) + { + m_objDATVScreen->selectRow(x + d); + m_objDATVScreen->setDataColor(y, 5, 250, 250); + m_objDATVScreen->selectRow(x); + m_objDATVScreen->setDataColor(y + d, 5, 250, 250); + } } - } } m_objDATVScreen->renderImage(NULL); @@ -101,27 +111,25 @@ namespace leansdr in.read(pixels_per_frame); - if ( ++phase >= decimation ) + if (++phase >= decimation) { phase = 0; } - } - } + } + } - //private: - pipereader< complex > in; - unsigned long phase; - //gfx g; + //private: + //gfx g; - void draw_begin() - { + void draw_begin() + { //g.clear(); //g.setfg(0, 255, 0); //g.line(g.w/2,0, g.w/2, g.h); //g.line(0,g.h/2, g.w,g.h/2); - } + } - }; +}; } diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp index df1022e53..7b4e0ec99 100644 --- a/plugins/channelrx/demoddatv/datvdemod.cpp +++ b/plugins/channelrx/demoddatv/datvdemod.cpp @@ -38,21 +38,22 @@ MESSAGE_CLASS_DEFINITION(DATVDemod::MsgConfigureChannelizer, Message) DATVDemod::DATVDemod(DeviceSourceAPI *deviceAPI) : ChannelSinkAPI(m_channelIdURI), - m_deviceAPI(deviceAPI), - m_objSettingsMutex(QMutex::NonRecursive), - m_objRegisteredDATVScreen(NULL), - m_objVideoStream(NULL), - m_objRegisteredVideoRender(NULL), - m_objRenderThread(NULL), - m_enmModulation(BPSK /*DATV_FM1*/), m_blnNeedConfigUpdate(false), - m_blnRenderingVideo(false) + m_deviceAPI(deviceAPI), + m_objRegisteredDATVScreen(NULL), + m_objRegisteredVideoRender(NULL), + m_objVideoStream(NULL), + m_objRenderThread(NULL), + m_blnRenderingVideo(false), + m_enmModulation(BPSK /*DATV_FM1*/), + m_objSettingsMutex(QMutex::NonRecursive) { setObjectName("DATVDemod"); + qDebug("DATVDemod::DATVDemod: sizeof FixReal: %lu: SDR_RX_SAMP_SZ: %u", sizeof(FixReal), (unsigned int) SDR_RX_SAMP_SZ); //*************** DATV PARAMETERS *************** m_blnInitialized=false; - CleanUpDATVFramework(false); + CleanUpDATVFramework(); m_objVideoStream = new DATVideostream(); @@ -102,6 +103,7 @@ DATVDemod::~DATVDemod() bool DATVDemod::SetDATVScreen(DATVScreen *objScreen) { m_objRegisteredDATVScreen = objScreen; + return true; } DATVideostream * DATVDemod::SetVideoRender(DATVideoRender *objScreen) @@ -240,185 +242,185 @@ void DATVDemod::InitDATVParameters(int intMsps, m_blnInitialized=true; } -void DATVDemod::CleanUpDATVFramework(bool blnRelease) +void DATVDemod::CleanUpDATVFramework() { //if(blnRelease==true) - if(false) - { - if(m_objScheduler!=NULL) - { - m_objScheduler->shutdown(); - delete m_objScheduler; - } +// if (false) +// { +// if(m_objScheduler!=NULL) +// { +// m_objScheduler->shutdown(); +// delete m_objScheduler; +// } +// +// // INPUT +// if(p_rawiq!=NULL) delete p_rawiq; +// if(p_rawiq_writer!=NULL) delete p_rawiq_writer; +// if(p_preprocessed!=NULL) delete p_preprocessed; +// +// // NOTCH FILTER +// if(r_auto_notch!=NULL) delete r_auto_notch; +// if(p_autonotched!=NULL) delete p_autonotched; +// +// // FREQUENCY CORRECTION : DEROTATOR +// if(p_derot!=NULL) delete p_derot; +// if(r_derot!=NULL) delete r_derot; +// +// // CNR ESTIMATION +// if(p_cnr!=NULL) delete p_cnr; +// if(r_cnr!=NULL) delete r_cnr; +// +// //FILTERING +// if(r_resample!=NULL) delete r_resample; +// if(p_resampled!=NULL) delete p_resampled; +// if(coeffs!=NULL) delete coeffs; +// +// // OUTPUT PREPROCESSED DATA +// if(sampler!=NULL) delete sampler; +// if(coeffs_sampler!=NULL) delete coeffs_sampler; +// if(p_symbols!=NULL) delete p_symbols; +// if(p_freq!=NULL) delete p_freq; +// if(p_ss!=NULL) delete p_ss; +// if(p_mer!=NULL) delete p_mer; +// if(p_sampled!=NULL) delete p_sampled; +// +// //DECIMATION +// if(p_decimated!=NULL) delete p_decimated; +// if(p_decim!=NULL) delete p_decim; +// if(r_ppout!=NULL) delete r_ppout; +// +// //GENERIC CONSTELLATION RECEIVER +// if(m_objDemodulator!=NULL) delete m_objDemodulator; +// +// //DECONVOLUTION AND SYNCHRONIZATION +// if(p_bytes!=NULL) delete p_bytes; +// if(r_deconv!=NULL) delete r_deconv; +// if(r!=NULL) delete r; +// if(p_descrambled!=NULL) delete p_descrambled; +// if(p_frames!=NULL) delete p_frames; +// if(r_etr192_descrambler!=NULL) delete r_etr192_descrambler; +// if(r_sync!=NULL) delete r_sync; +// if(p_mpegbytes!=NULL) delete p_mpegbytes; +// if(p_lock!=NULL) delete p_lock; +// if(p_locktime!=NULL) delete p_locktime; +// if(r_sync_mpeg!=NULL) delete r_sync_mpeg; +// +// +// // DEINTERLEAVING +// if(p_rspackets!=NULL) delete p_rspackets; +// if(r_deinter!=NULL) delete r_deinter; +// if(p_vbitcount!=NULL) delete p_vbitcount; +// if(p_verrcount!=NULL) delete p_verrcount; +// if(p_rtspackets!=NULL) delete p_rtspackets; +// if(r_rsdec!=NULL) delete r_rsdec; +// +// //BER ESTIMATION +// if(p_vber!=NULL) delete p_vber; +// if(r_vber!=NULL) delete r_vber; +// +// // DERANDOMIZATION +// if(p_tspackets!=NULL) delete p_tspackets; +// if(r_derand!=NULL) delete r_derand; +// +// +// //OUTPUT : To remove +// if(r_stdout!=NULL) delete r_stdout; +// if(r_videoplayer!=NULL) delete r_videoplayer; +// +// //CONSTELLATION +// if(r_scope_symbols!=NULL) delete r_scope_symbols; +// +// } - // INPUT - if(p_rawiq!=NULL) delete p_rawiq; - if(p_rawiq_writer!=NULL) delete p_rawiq_writer; - if(p_preprocessed!=NULL) delete p_preprocessed; - - // NOTCH FILTER - if(r_auto_notch!=NULL) delete r_auto_notch; - if(p_autonotched!=NULL) delete p_autonotched; - - // FREQUENCY CORRECTION : DEROTATOR - if(p_derot!=NULL) delete p_derot; - if(r_derot!=NULL) delete r_derot; - - // CNR ESTIMATION - if(p_cnr!=NULL) delete p_cnr; - if(r_cnr!=NULL) delete r_cnr; - - //FILTERING - if(r_resample!=NULL) delete r_resample; - if(p_resampled!=NULL) delete p_resampled; - if(coeffs!=NULL) delete coeffs; - - // OUTPUT PREPROCESSED DATA - if(sampler!=NULL) delete sampler; - if(coeffs_sampler!=NULL) delete coeffs_sampler; - if(p_symbols!=NULL) delete p_symbols; - if(p_freq!=NULL) delete p_freq; - if(p_ss!=NULL) delete p_ss; - if(p_mer!=NULL) delete p_mer; - if(p_sampled!=NULL) delete p_sampled; - - //DECIMATION - if(p_decimated!=NULL) delete p_decimated; - if(p_decim!=NULL) delete p_decim; - if(r_ppout!=NULL) delete r_ppout; - - //GENERIC CONSTELLATION RECEIVER - if(m_objDemodulator!=NULL) delete m_objDemodulator; - - //DECONVOLUTION AND SYNCHRONIZATION - if(p_bytes!=NULL) delete p_bytes; - if(r_deconv!=NULL) delete r_deconv; - if(r!=NULL) delete r; - if(p_descrambled!=NULL) delete p_descrambled; - if(p_frames!=NULL) delete p_frames; - if(r_etr192_descrambler!=NULL) delete r_etr192_descrambler; - if(r_sync!=NULL) delete r_sync; - if(p_mpegbytes!=NULL) delete p_mpegbytes; - if(p_lock!=NULL) delete p_lock; - if(p_locktime!=NULL) delete p_locktime; - if(r_sync_mpeg!=NULL) delete r_sync_mpeg; - - - // DEINTERLEAVING - if(p_rspackets!=NULL) delete p_rspackets; - if(r_deinter!=NULL) delete r_deinter; - if(p_vbitcount!=NULL) delete p_vbitcount; - if(p_verrcount!=NULL) delete p_verrcount; - if(p_rtspackets!=NULL) delete p_rtspackets; - if(r_rsdec!=NULL) delete r_rsdec; - - //BER ESTIMATION - if(p_vber!=NULL) delete p_vber; - if(r_vber!=NULL) delete r_vber; - - // DERANDOMIZATION - if(p_tspackets!=NULL) delete p_tspackets; - if(r_derand!=NULL) delete r_derand; - - - //OUTPUT : To remove - if(r_stdout!=NULL) delete r_stdout; - if(r_videoplayer!=NULL) delete r_videoplayer; - - //CONSTELLATION - if(r_scope_symbols!=NULL) delete r_scope_symbols; - - } - - m_objScheduler=NULL; + m_objScheduler = 0; // INPUT - p_rawiq = NULL; - p_rawiq_writer = NULL; + p_rawiq = 0; + p_rawiq_writer = 0; - p_preprocessed = NULL; + p_preprocessed = 0; // NOTCH FILTER - r_auto_notch = NULL; - p_autonotched = NULL; + r_auto_notch = 0; + p_autonotched = 0; // FREQUENCY CORRECTION : DEROTATOR - p_derot = NULL; - r_derot=NULL; + p_derot = 0; + r_derot = 0; // CNR ESTIMATION - p_cnr = NULL; - r_cnr = NULL; + p_cnr = 0; + r_cnr = 0; //FILTERING - r_resample = NULL; - p_resampled = NULL; - coeffs = NULL; - ncoeffs=0; + r_resample = 0; + p_resampled = 0; + coeffs = 0; + ncoeffs = 0; // OUTPUT PREPROCESSED DATA - sampler = NULL; - coeffs_sampler=NULL; - ncoeffs_sampler=0; + sampler = 0; + coeffs_sampler = 0; + ncoeffs_sampler = 0; - p_symbols = NULL; - p_freq = NULL; - p_ss = NULL; - p_mer = NULL; - p_sampled = NULL; + p_symbols = 0; + p_freq = 0; + p_ss = 0; + p_mer = 0; + p_sampled = 0; //DECIMATION - p_decimated = NULL; - p_decim = NULL; - r_ppout = NULL; + p_decimated = 0; + p_decim = 0; + r_ppout = 0; //GENERIC CONSTELLATION RECEIVER - m_objDemodulator = NULL; + m_objDemodulator = 0; //DECONVOLUTION AND SYNCHRONIZATION - p_bytes=NULL; - r_deconv=NULL; - r = NULL; + p_bytes = 0; + r_deconv = 0; + r = 0; - p_descrambled = NULL; - p_frames = NULL; - r_etr192_descrambler = NULL; - r_sync = NULL; + p_descrambled = 0; + p_frames = 0; + r_etr192_descrambler = 0; + r_sync = 0; - p_mpegbytes = NULL; - p_lock = NULL; - p_locktime = NULL; - r_sync_mpeg = NULL; + p_mpegbytes = 0; + p_lock = 0; + p_locktime = 0; + r_sync_mpeg = 0; // DEINTERLEAVING - p_rspackets = NULL; - r_deinter = NULL; + p_rspackets = 0; + r_deinter = 0; - p_vbitcount = NULL; - p_verrcount = NULL; - p_rtspackets = NULL; - r_rsdec = NULL; + p_vbitcount = 0; + p_verrcount = 0; + p_rtspackets = 0; + r_rsdec = 0; //BER ESTIMATION - p_vber = NULL; - r_vber = NULL; + p_vber = 0; + r_vber = 0; // DERANDOMIZATION - p_tspackets = NULL; - r_derand = NULL; + p_tspackets = 0; + r_derand = 0; //OUTPUT : To remove - r_stdout = NULL; - r_videoplayer = NULL; + r_stdout = 0; + r_videoplayer = 0; //CONSTELLATION - r_scope_symbols = NULL; + r_scope_symbols = 0; } void DATVDemod::InitDATVFramework() @@ -516,7 +518,7 @@ void DATVDemod::InitDATVFramework() m_lngExpectedReadIQ = BUF_BASEBAND; - CleanUpDATVFramework(true); + CleanUpDATVFramework(); m_objScheduler = new scheduler(); @@ -819,7 +821,7 @@ void DATVDemod::InitDATVFramework() m_blnDVBInitialized=true; } -void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused))) { float fltI; float fltQ; @@ -964,12 +966,12 @@ bool DATVDemod::handleMessage(const Message& cmd) m_channelizer->configure(m_channelizer->getInputMessageQueue(), m_channelizer->getInputSampleRate(), - m_objRunning.intCenterFrequency); + cfg.getCenterFrequency()); + //m_objRunning.intCenterFrequency); - - - qDebug() << "ATVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << m_channelizer->getInputSampleRate() - << " centerFrequency: " << m_objRunning.intCenterFrequency; + qDebug() << "DATVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << m_channelizer->getInputSampleRate() + << " centerFrequency: " << cfg.getCenterFrequency(); + //<< " centerFrequency: " << m_objRunning.intCenterFrequency; return true; } diff --git a/plugins/channelrx/demoddatv/datvdemod.h b/plugins/channelrx/demoddatv/datvdemod.h index 399e596e9..8796f7229 100644 --- a/plugins/channelrx/demoddatv/datvdemod.h +++ b/plugins/channelrx/demoddatv/datvdemod.h @@ -43,8 +43,6 @@ class DownChannelizer; #endif - - #include "datvconstellation.h" #include "datvvideoplayer.h" @@ -70,68 +68,81 @@ class DownChannelizer; using namespace leansdr; -enum DATVModulation { BPSK, QPSK, PSK8, APSK16, APSK32, APSK64E, QAM16, QAM64, QAM256 }; -enum dvb_version { DVB_S, DVB_S2 }; -enum dvb_sampler { SAMP_NEAREST, SAMP_LINEAR, SAMP_RRC }; - -inline int decimation(float Fin, float Fout) { int d = Fin / Fout; return max(d, 1); } - -struct config -{ - dvb_version standard; - dvb_sampler sampler; - - int buf_factor; // Buffer sizing - float Fs; // Sampling frequency (Hz) - float Fderot; // Shift the signal (Hz). Note: Ftune is faster - int anf; // Number of auto notch filters - bool cnr; // Measure CNR - unsigned int decim; // Decimation, 0=auto - float Fm; // QPSK symbol rate (Hz) - cstln_lut<256>::predef constellation; - code_rate fec; - float Ftune; // Bias frequency for the QPSK demodulator (Hz) - bool allow_drift; - bool fastlock; - bool viterbi; - bool hard_metric; - bool resample; - float resample_rej; // Approx. filter rejection in dB - int rrc_steps; // Discrete steps between symbols, 0=auto - float rrc_rej; // Approx. RRC filter rejection in dB - float rolloff; // Roll-off 0..1 - bool hdlc; // Expect HDLC frames instead of MPEG packets - bool packetized; // Output frames with 16-bit BE length - float Finfo; // Desired refresh rate on fd_info (Hz) - - config() : buf_factor(4), - Fs(2.4e6), - Fderot(0), - anf(1), - cnr(false), - decim(0), - Fm(2e6), - standard(DVB_S), - constellation(cstln_lut<256>::QPSK), - fec(FEC12), - Ftune(0), - allow_drift(false), - fastlock(true), - viterbi(false), - hard_metric(false), - resample(false), - resample_rej(10), - sampler(SAMP_LINEAR), - rrc_steps(0), - rrc_rej(10), - rolloff(0.35), - hdlc(false), - packetized(false), - Finfo(5) - { - } +enum DATVModulation +{ + BPSK, QPSK, PSK8, APSK16, APSK32, APSK64E, QAM16, QAM64, QAM256 +}; +enum dvb_version +{ + DVB_S, DVB_S2 +}; +enum dvb_sampler +{ + SAMP_NEAREST, SAMP_LINEAR, SAMP_RRC }; +inline int decimation(float Fin, float Fout) +{ + int d = Fin / Fout; + return max(d, 1); +} + +struct config +{ + dvb_version standard; + dvb_sampler sampler; + + int buf_factor; // Buffer sizing + float Fs; // Sampling frequency (Hz) + float Fderot; // Shift the signal (Hz). Note: Ftune is faster + int anf; // Number of auto notch filters + bool cnr; // Measure CNR + unsigned int decim; // Decimation, 0=auto + float Fm; // QPSK symbol rate (Hz) + cstln_lut<256>::predef constellation; + code_rate fec; + float Ftune; // Bias frequency for the QPSK demodulator (Hz) + bool allow_drift; + bool fastlock; + bool viterbi; + bool hard_metric; + bool resample; + float resample_rej; // Approx. filter rejection in dB + int rrc_steps; // Discrete steps between symbols, 0=auto + float rrc_rej; // Approx. RRC filter rejection in dB + float rolloff; // Roll-off 0..1 + bool hdlc; // Expect HDLC frames instead of MPEG packets + bool packetized; // Output frames with 16-bit BE length + float Finfo; // Desired refresh rate on fd_info (Hz) + + config() : + standard(DVB_S), + sampler(SAMP_LINEAR), + buf_factor(4), + Fs(2.4e6), + Fderot(0), + anf(1), + cnr(false), + decim(0), + Fm(2e6), + constellation(cstln_lut<256>::QPSK), + fec(FEC12), + Ftune(0), + allow_drift(false), + fastlock(true), + viterbi(false), + hard_metric(false), + resample(false), + resample_rej(10), + rrc_steps(0), + rrc_rej(10), + rolloff(0.35), + hdlc(false), + packetized(false), + Finfo(5) + { + } +}; struct DATVConfig { @@ -152,118 +163,205 @@ struct DATVConfig bool blnViterbi; DATVConfig() : - intMsps(1024000), - intRFBandwidth(1024000), - intCenterFrequency(0), - enmStandard(DVB_S), - enmModulation(BPSK), - enmFEC(FEC12), - intSampleRate(1024000), - intSymbolRate(250000), - intNotchFilters(1), - blnAllowDrift(false), - blnFastLock(false), - blnHDLC(false), - blnHardMetric(false), - blnResample(false), - blnViterbi(false) + intMsps(1024000), + intRFBandwidth(1024000), + intCenterFrequency(0), + enmStandard(DVB_S), + enmModulation(BPSK), + enmFEC(FEC12), + intSampleRate(1024000), + intSymbolRate(250000), + intNotchFilters(1), + blnAllowDrift(false), + blnFastLock(false), + blnHDLC(false), + blnHardMetric(false), + blnResample(false), + blnViterbi(false) { } }; - -class DATVDemod : public BasebandSampleSink, public ChannelSinkAPI +class DATVDemod: public BasebandSampleSink, public ChannelSinkAPI { - Q_OBJECT +Q_OBJECT public: + class MsgConfigureChannelizer: public Message + { + MESSAGE_CLASS_DECLARATION + + public: + int getCenterFrequency() const + { + return m_centerFrequency; + } + + static MsgConfigureChannelizer* create(int centerFrequency) + { + return new MsgConfigureChannelizer(centerFrequency); + } + + private: + int m_centerFrequency; + + MsgConfigureChannelizer(int centerFrequency) : + Message(), m_centerFrequency(centerFrequency) + { + } + }; DATVDemod(DeviceSourceAPI *); ~DATVDemod(); - virtual void destroy() { delete this; } - virtual void getIdentifier(QString& id) { id = objectName(); } - virtual void getTitle(QString& title) { title = objectName(); } - virtual qint64 getCenterFrequency() const { return m_objRunning.intCenterFrequency; } + virtual void destroy() + { + delete this; + } + virtual void getIdentifier(QString& id) + { + id = objectName(); + } + virtual void getTitle(QString& title) + { + title = objectName(); + } + virtual qint64 getCenterFrequency() const + { + return m_objRunning.intCenterFrequency; + } - virtual QByteArray serialize() const { return QByteArray(); } - virtual bool deserialize(const QByteArray& data __attribute__((unused))) { return false; } + virtual QByteArray serialize() const + { + return QByteArray(); + } + virtual bool deserialize(const QByteArray& data __attribute__((unused))) + { + return false; + } + void configure( + MessageQueue* objMessageQueue, + int intRFBandwidth, + int intCenterFrequency, + dvb_version enmStandard, + DATVModulation enmModulation, + code_rate enmFEC, + int intSymbolRate, + int intNotchFilters, + bool blnAllowDrift, + bool blnFastLock, + bool blnHDLC, + bool blnHardMetric, + bool blnResample, + bool blnViterbi); - - void configure(MessageQueue* objMessageQueue, - int intRFBandwidth, - int intCenterFrequency, - dvb_version enmStandard, - DATVModulation enmModulation, - code_rate enmFEC, - int intSymbolRate, - int intNotchFilters, - bool blnAllowDrift, - bool blnFastLock, - bool blnHDLC, - bool blnHardMetric, - bool blnResample, - bool blnViterbi); - - virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); - virtual void start(); - virtual void stop(); - virtual bool handleMessage(const Message& cmd); + virtual void feed(const SampleVector::const_iterator& begin, + const SampleVector::const_iterator& end, bool po); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& cmd); bool SetDATVScreen(DATVScreen *objScreen); DATVideostream * SetVideoRender(DATVideoRender *objScreen); bool PlayVideo(bool blnStartStop); - void InitDATVParameters(int intMsps, - int intRFBandwidth, - int intCenterFrequency, - dvb_version enmStandard, - DATVModulation enmModulation, - code_rate enmFEC, - int intSampleRate, - int intSymbolRate, - int intNotchFilters, - bool blnAllowDrift, - bool blnFastLock, - bool blnHDLC, - bool blnHardMetric, - bool blnResample, - bool blnViterbi); + void InitDATVParameters( + int intMsps, + int intRFBandwidth, + int intCenterFrequency, + dvb_version enmStandard, + DATVModulation enmModulation, + code_rate enmFEC, + int intSampleRate, + int intSymbolRate, + int intNotchFilters, + bool blnAllowDrift, + bool blnFastLock, + bool blnHDLC, + bool blnHardMetric, + bool blnResample, + bool blnViterbi); - void CleanUpDATVFramework(bool blnRelease); + void CleanUpDATVFramework(); int GetSampleRate(); void InitDATVFramework(); static const QString m_channelIdURI; static const QString m_channelId; - - class MsgConfigureChannelizer : public Message - { - MESSAGE_CLASS_DECLARATION - - public: - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int centerFrequency) - { - return new MsgConfigureChannelizer(centerFrequency); - } - - private: - int m_centerFrequency; - - MsgConfigureChannelizer(int centerFrequency) : - Message(), - m_centerFrequency(centerFrequency) - { } - }; - - - private: + class MsgConfigureDATVDemod: public Message + { + MESSAGE_CLASS_DECLARATION + + public: + static MsgConfigureDATVDemod* create( + int intRFBandwidth, + int intCenterFrequency, + dvb_version enmStandard, + DATVModulation enmModulation, + code_rate enmFEC, + int intSymbolRate, + int intNotchFilters, + bool blnAllowDrift, + bool blnFastLock, + bool blnHDLC, + bool blnHardMetric, + bool blnResample, + bool blnViterbi) + { + return new MsgConfigureDATVDemod( + intRFBandwidth, + intCenterFrequency, + enmStandard, + enmModulation, + enmFEC, + intSymbolRate, + intNotchFilters, + blnAllowDrift, + blnFastLock, + blnHDLC, + blnHardMetric, + blnResample, + blnViterbi); + } + + DATVConfig m_objMsgConfig; + + private: + MsgConfigureDATVDemod( + int intRFBandwidth, + int intCenterFrequency, + dvb_version enmStandard, + DATVModulation enmModulation, + code_rate enmFEC, + int intSymbolRate, + int intNotchFilters, + bool blnAllowDrift, + bool blnFastLock, + bool blnHDLC, + bool blnHardMetric, + bool blnResample, + bool blnViterbi) : + Message() + { + m_objMsgConfig.intRFBandwidth = intRFBandwidth; + m_objMsgConfig.intCenterFrequency = intCenterFrequency; + m_objMsgConfig.enmStandard = enmStandard; + m_objMsgConfig.enmModulation = enmModulation; + m_objMsgConfig.enmFEC = enmFEC; + m_objMsgConfig.intSymbolRate = intSymbolRate; + m_objMsgConfig.intNotchFilters = intNotchFilters; + m_objMsgConfig.blnAllowDrift = blnAllowDrift; + m_objMsgConfig.blnFastLock = blnFastLock; + m_objMsgConfig.blnHDLC = blnHDLC; + m_objMsgConfig.blnHardMetric = blnHardMetric; + m_objMsgConfig.blnResample = blnResample; + m_objMsgConfig.blnViterbi = blnViterbi; + } + }; unsigned long m_lngExpectedReadIQ; unsigned long m_lngReadIQ; @@ -305,7 +403,7 @@ private: cnr_fft *r_cnr; //FILTERING - fir_filter *r_resample; + fir_filter *r_resample; pipebuf *p_resampled; float *coeffs; int ncoeffs; @@ -344,18 +442,17 @@ private: pipebuf *p_mpegbytes; pipebuf *p_lock; pipebuf *p_locktime; - mpeg_sync *r_sync_mpeg; - + mpeg_sync *r_sync_mpeg; // DEINTERLEAVING - pipebuf< rspacket > *p_rspackets; + pipebuf > *p_rspackets; deinterleaver *r_deinter; // REED-SOLOMON pipebuf *p_vbitcount; pipebuf *p_verrcount; pipebuf *p_rtspackets; - rs_decoder *r_rsdec; + rs_decoder *r_rsdec; // BER ESTIMATION pipebuf *p_vber; @@ -365,7 +462,6 @@ private: pipebuf *p_tspackets; derandomizer *r_derand; - //OUTPUT file_writer *r_stdout; datvvideoplayer *r_videoplayer; @@ -373,87 +469,26 @@ private: //CONSTELLATION datvconstellation *r_scope_symbols; + DeviceSourceAPI* m_deviceAPI; -private: + ThreadedBasebandSampleSink* m_threadedChannelizer; + DownChannelizer* m_channelizer; - DeviceSourceAPI* m_deviceAPI; + //*************** DATV PARAMETERS *************** + DATVScreen * m_objRegisteredDATVScreen; + DATVideoRender * m_objRegisteredVideoRender; + DATVideostream * m_objVideoStream; + DATVideoRenderThread * m_objRenderThread; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; + fftfilt * m_objRFFilter; + NCO m_objNCO; - //*************** DATV PARAMETERS *************** - DATVScreen * m_objRegisteredDATVScreen; - DATVideoRender * m_objRegisteredVideoRender; - DATVideostream * m_objVideoStream; - DATVideoRenderThread * m_objRenderThread; + bool m_blnInitialized; + bool m_blnRenderingVideo; - fftfilt * m_objRFFilter; - NCO m_objNCO; - - bool m_blnInitialized; - bool m_blnRenderingVideo; - - DATVModulation m_enmModulation; - - //QElapsedTimer m_objTimer; -private: - - class MsgConfigureDATVDemod : public Message - { - MESSAGE_CLASS_DECLARATION - - public: - static MsgConfigureDATVDemod* create(int intRFBandwidth, - int intCenterFrequency, - dvb_version enmStandard, - DATVModulation enmModulation, - code_rate enmFEC, - int intSymbolRate, - int intNotchFilters, - bool blnAllowDrift, - bool blnFastLock, - bool blnHDLC, - bool blnHardMetric, - bool blnResample, - bool blnViterbi) - { - return new MsgConfigureDATVDemod(intRFBandwidth,intCenterFrequency,enmStandard, enmModulation, enmFEC, intSymbolRate, intNotchFilters, blnAllowDrift,blnFastLock,blnHDLC,blnHardMetric,blnResample, blnViterbi); - } - - DATVConfig m_objMsgConfig; - - private: - MsgConfigureDATVDemod(int intRFBandwidth, - int intCenterFrequency, - dvb_version enmStandard, - DATVModulation enmModulation, - code_rate enmFEC, - int intSymbolRate, - int intNotchFilters, - bool blnAllowDrift, - bool blnFastLock, - bool blnHDLC, - bool blnHardMetric, - bool blnResample, - bool blnViterbi) : - Message() - { - m_objMsgConfig.intRFBandwidth = intRFBandwidth; - m_objMsgConfig.intCenterFrequency = intCenterFrequency; - m_objMsgConfig.enmStandard = enmStandard; - m_objMsgConfig.enmModulation = enmModulation; - m_objMsgConfig.enmFEC = enmFEC; - m_objMsgConfig.intSymbolRate = intSymbolRate; - m_objMsgConfig.intNotchFilters = intNotchFilters; - m_objMsgConfig.blnAllowDrift = blnAllowDrift; - m_objMsgConfig.blnFastLock = blnFastLock; - m_objMsgConfig.blnHDLC = blnHDLC; - m_objMsgConfig.blnHardMetric = blnHardMetric; - m_objMsgConfig.blnResample = blnResample; - m_objMsgConfig.blnViterbi = blnViterbi; - } - }; + DATVModulation m_enmModulation; + //QElapsedTimer m_objTimer; DATVConfig m_objRunning; diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 254d6049f..88ae39d06 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -47,7 +47,7 @@ DATVDemodGUI* DATVDemodGUI::create(PluginAPI* objPluginAPI, } void DATVDemodGUI::destroy() -{ +{ delete this; } @@ -208,7 +208,7 @@ bool DATVDemodGUI::deserialize(const QByteArray& arrData) } } -bool DATVDemodGUI::handleMessage(const Message& objMessage) +bool DATVDemodGUI::handleMessage(const Message& objMessage __attribute__((unused))) { return false; } @@ -233,7 +233,7 @@ void DATVDemodGUI::channelSampleRateChanged() applySettings(); } -void DATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) +void DATVDemodGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) { } @@ -258,7 +258,7 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba m_objChannelMarker(this), m_blnBasicSettingsShown(false), m_blnDoApplySettings(true) -{ +{ ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); @@ -491,9 +491,9 @@ void DATVDemodGUI::enterEvent(QEvent*) } void DATVDemodGUI::tick() -{ +{ if((m_intLastDecodedData-m_intPreviousDecodedData)>=0) - { + { m_intLastSpeed = 8*(m_intLastDecodedData-m_intPreviousDecodedData); ui->lblRate->setText(QString("Speed: %1b/s").arg(formatBytes(m_intLastSpeed))); } @@ -506,12 +506,12 @@ void DATVDemodGUI::tick() return; } -void DATVDemodGUI::on_cmbStandard_currentIndexChanged(const QString &arg1) -{ +void DATVDemodGUI::on_cmbStandard_currentIndexChanged(const QString &arg1 __attribute__((unused))) +{ applySettings(); } -void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1) +void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1 __attribute__((unused))) { QString strModulation; QString strFEC; @@ -563,8 +563,8 @@ void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1) } -void DATVDemodGUI::on_cmbFEC_currentIndexChanged(const QString &arg1) -{ +void DATVDemodGUI::on_cmbFEC_currentIndexChanged(const QString &arg1 __attribute__((unused))) +{ QString strFEC; strFEC = ui->cmbFEC->currentText(); @@ -625,7 +625,7 @@ void DATVDemodGUI::on_chkHardMetric_clicked() /* void DATVDemodGUI::on_pushButton_clicked() { - applySettings(); + applySettings(); } */ @@ -641,12 +641,12 @@ void DATVDemodGUI::on_spiSampleRate_valueChanged(int arg1) } */ -void DATVDemodGUI::on_spiSymbolRate_valueChanged(int arg1) +void DATVDemodGUI::on_spiSymbolRate_valueChanged(int arg1 __attribute__((unused))) { applySettings(); } -void DATVDemodGUI::on_spiNotchFilters_valueChanged(int arg1) +void DATVDemodGUI::on_spiNotchFilters_valueChanged(int arg1 __attribute__((unused))) { applySettings(); } @@ -701,8 +701,8 @@ void DATVDemodGUI::on_pushButton_4_clicked() } -void DATVDemodGUI::on_mouseEvent(QMouseEvent* obj) -{ +void DATVDemodGUI::on_mouseEvent(QMouseEvent* obj __attribute__((unused))) +{ } QString DATVDemodGUI::formatBytes(qint64 intBytes) @@ -724,7 +724,7 @@ QString DATVDemodGUI::formatBytes(qint64 intBytes) } -void DATVDemodGUI::on_StreamDataAvailable(int *intPackets, int *intBytes, int *intPercent, qint64 *intTotalReceived) +void DATVDemodGUI::on_StreamDataAvailable(int *intPackets __attribute__((unused)), int *intBytes, int *intPercent, qint64 *intTotalReceived) { ui->lblStatus->setText(QString("Decod: %1B").arg(formatBytes(*intTotalReceived))); m_intLastDecodedData = *intTotalReceived; @@ -742,7 +742,7 @@ void DATVDemodGUI::on_StreamDataAvailable(int *intPackets, int *intBytes, int *i } -void DATVDemodGUI::on_spiBandwidth_valueChanged(int arg1) +void DATVDemodGUI::on_spiBandwidth_valueChanged(int arg1 __attribute__((unused))) { applySettings(); } diff --git a/plugins/channelrx/demoddatv/leansdr/dsp.h b/plugins/channelrx/demoddatv/leansdr/dsp.h index cc66d73c5..e5a97b99e 100644 --- a/plugins/channelrx/demoddatv/leansdr/dsp.h +++ b/plugins/channelrx/demoddatv/leansdr/dsp.h @@ -5,347 +5,438 @@ #include "leansdr/framework.h" #include "leansdr/math.h" -namespace leansdr { +namespace leansdr +{ - ////////////////////////////////////////////////////////////////////// - // DSP blocks - ////////////////////////////////////////////////////////////////////// - - // [cconverter] converts complex streams between numric types, - // with optional ofsetting and rational scaling. - template - struct cconverter : runnable { - cconverter(scheduler *sch, pipebuf< complex > &_in, - pipebuf< complex > &_out) - : runnable(sch, "cconverter"), - in(_in), out(_out) { +////////////////////////////////////////////////////////////////////// +// DSP blocks +////////////////////////////////////////////////////////////////////// + +// [cconverter] converts complex streams between numric types, +// with optional ofsetting and rational scaling. +template +struct cconverter: runnable +{ + cconverter(scheduler *sch, pipebuf > &_in, + pipebuf > &_out) : + runnable(sch, "cconverter"), in(_in), out(_out) + { } - void run() { - unsigned long count = min(in.readable(), out.writable()); - complex *pin=in.rd(), *pend=pin+count; - complex *pout = out.wr(); - for ( ; pinre = Zout + (pin->re-(Tin)Zin)*Gn/Gd; - pout->im = Zout + (pin->im-(Tin)Zin)*Gn/Gd; - } - in.read(count); - out.written(count); + + void run() + { + unsigned long count = min(in.readable(), out.writable()); + complex *pin = in.rd(), *pend = pin + count; + complex *pout = out.wr(); + for (; pin < pend; ++pin, ++pout) + { + pout->re = Zout + (pin->re - (Tin) Zin) * Gn / Gd; + pout->im = Zout + (pin->im - (Tin) Zin) * Gn / Gd; + } + in.read(count); + out.written(count); } - private: - pipereader< complex > in; - pipewriter< complex > out; - }; - - template - struct cfft_engine { - const int n; - cfft_engine(int _n) : n(_n), invsqrtn(1.0/sqrt(n)) { - // Compute log2(n) - logn = 0; - for ( int t=n; t>1; t>>=1 ) ++logn; - // Bit reversal - bitrev = new int[n]; - for ( int i=0; i>b)&1); - } - // Float constants - omega = new complex[n]; - omega_rev = new complex[n]; - for ( int i=0; i > in; + pipewriter > out; +}; + +template +struct cfft_engine +{ + const unsigned int n; + + cfft_engine(unsigned int _n) : + n(_n), invsqrtn(1.0 / sqrt(n)) + { + // Compute log2(n) + logn = 0; + for (int t = n; t > 1; t >>= 1) + ++logn; + // Bit reversal + bitrev = new int[n]; + for (unsigned int i = 0; i < n; ++i) + { + bitrev[i] = 0; + for (int b = 0; b < logn; ++b) + bitrev[i] = (bitrev[i] << 1) | ((i >> b) & 1); + } + // Float constants + omega = new complex [n]; + omega_rev = new complex [n]; + for (unsigned int i = 0; i < n; ++i) + { + float a = 2.0 * M_PI * i / n; + omega_rev[i].re = (omega[i].re = cosf(a)); + omega_rev[i].im = -(omega[i].im = sinf(a)); + } } - void inplace(complex *data, bool reverse=false) { - // Bit-reversal permutation - for ( int i=0; i tmp=data[i]; data[i]=data[r]; data[r]=tmp; } - } - complex *om = reverse ? omega_rev : omega; - // Danielson-Lanczos - for ( int i=0; i &w = om[k*dom]; - complex &dqk = data[q+k]; - complex x(w.re*dqk.re - w.im*dqk.im, - w.re*dqk.im + w.im*dqk.re); - data[q+k].re = data[p+k].re - x.re; - data[q+k].im = data[p+k].im - x.im; - data[p+k].re = data[p+k].re + x.re; - data[p+k].im = data[p+k].im + x.im; - } - } - } - if ( reverse ) { - float invn = 1.0 / n; - for ( int i=0; i *data, bool reverse = false) + { + // Bit-reversal permutation + for (unsigned int i = 0; i < n; ++i) + { + unsigned int r = bitrev[i]; + if (r < i) + { + complex tmp = data[i]; + data[i] = data[r]; + data[r] = tmp; + } + } + complex *om = reverse ? omega_rev : omega; + // Danielson-Lanczos + for (int i = 0; i < logn; ++i) + { + int hbs = 1 << i; + int dom = 1 << (logn - 1 - i); + for (int j = 0; j < dom; ++j) + { + int p = j * hbs * 2, q = p + hbs; + for (int k = 0; k < hbs; ++k) + { + complex &w = om[k * dom]; + complex &dqk = data[q + k]; + complex x(w.re * dqk.re - w.im * dqk.im, + w.re * dqk.im + w.im * dqk.re); + data[q + k].re = data[p + k].re - x.re; + data[q + k].im = data[p + k].im - x.im; + data[p + k].re = data[p + k].re + x.re; + data[p + k].im = data[p + k].im + x.im; + } + } + } + if (reverse) + { + float invn = 1.0 / n; + for (unsigned int i = 0; i < n; ++i) + { + data[i].re *= invn; + data[i].im *= invn; + } + } } - private: + +private: int logn; int *bitrev; complex *omega, *omega_rev; float invsqrtn; - }; - - template - struct adder : runnable { - adder(scheduler *sch, - pipebuf &_in1, pipebuf &_in2, pipebuf &_out) - : runnable(sch, "adder"), - in1(_in1), in2(_in2), out(_out) { +}; + +template +struct adder: runnable +{ + adder(scheduler *sch, pipebuf &_in1, pipebuf &_in2, pipebuf &_out) : + runnable(sch, "adder"), + in1(_in1), + in2(_in2), + out(_out) + { } - void run() { - int n = out.writable(); - if ( in1.readable() < n ) n = in1.readable(); - if ( in2.readable() < n ) n = in2.readable(); - T *pin1=in1.rd(), *pin2=in2.rd(), *pout=out.wr(), *pend=pout+n; - while ( pout < pend ) *pout++ = *pin1++ + *pin2++; - in1.read(n); - in2.read(n); - out.written(n); + + void run() + { + int n = out.writable(); + if (in1.readable() < n) + n = in1.readable(); + if (in2.readable() < n) + n = in2.readable(); + T *pin1 = in1.rd(), *pin2 = in2.rd(), *pout = out.wr(), *pend = pout + + n; + while (pout < pend) + *pout++ = *pin1++ + *pin2++; + in1.read(n); + in2.read(n); + out.written(n); } - private: + +private: pipereader in1, in2; pipewriter out; - }; - - template - struct scaler : runnable { +}; + +template +struct scaler: runnable +{ Tscale scale; - scaler(scheduler *sch, Tscale _scale, - pipebuf &_in, pipebuf &_out) - : runnable(sch, "scaler"), - scale(_scale), - in(_in), out(_out) { + + scaler(scheduler *sch, Tscale _scale, pipebuf &_in, pipebuf &_out) : + runnable(sch, "scaler"), + scale(_scale), + in(_in), + out(_out) + { } - void run() { - unsigned long count = min(in.readable(), out.writable()); - Tin *pin=in.rd(), *pend=pin+count; - Tout *pout = out.wr(); - for ( ; pin in; pipewriter out; - }; - - // [awgb_c] generates complex white gaussian noise. - - template - struct wgn_c : runnable { - wgn_c(scheduler *sch, pipebuf< complex > &_out) - : runnable(sch, "awgn"), stddev(1.0), out(_out) { +}; + +// [awgb_c] generates complex white gaussian noise. + +template +struct wgn_c: runnable +{ + wgn_c(scheduler *sch, pipebuf > &_out) : + runnable(sch, "awgn"), + stddev(1.0), + out(_out) + { } - void run() { - int n = out.writable(); - complex *pout=out.wr(), *pend=pout+n; - while ( pout < pend ) { - // TAOCP - float x, y, r2; - do { - x = 2*drand48() - 1; - y = 2*drand48() - 1; - r2 = x*x + y*y; - } while ( r2==0 || r2>=1 ); - float k = sqrtf(-logf(r2)/r2) * stddev; - pout->re = k*x; - pout->im = k*y; - ++pout; - } - out.written(n); + + void run() + { + int n = out.writable(); + complex *pout = out.wr(), *pend = pout + n; + while (pout < pend) + { + // TAOCP + float x, y, r2; + do + { + x = 2 * drand48() - 1; + y = 2 * drand48() - 1; + r2 = x * x + y * y; + } while (r2 == 0 || r2 >= 1); + float k = sqrtf(-logf(r2) / r2) * stddev; + pout->re = k * x; + pout->im = k * y; + ++pout; + } + out.written(n); } + float stddev; - private: - pipewriter< complex > out; - }; - - template - struct naive_lowpass : runnable { - naive_lowpass(scheduler *sch, pipebuf &_in, pipebuf &_out, int _w) - : runnable(sch, "lowpass"), in(_in), out(_out), w(_w) { + +private: + pipewriter > out; +}; + +template +struct naive_lowpass: runnable +{ + naive_lowpass(scheduler *sch, pipebuf &_in, pipebuf &_out, int _w) : + runnable(sch, "lowpass"), + in(_in), + out(_out), + w(_w) + { } - - void run() { - if ( in.readable() < w ) return; - unsigned long count = min(in.readable()-w, out.writable()); - T *pin=in.rd(), *pend=pin+count; - T *pout = out.wr(); - float k = 1.0 / w; - for ( ; pin in; pipewriter out; int w; - }; +}; - template - struct fir_filter : runnable { - fir_filter(scheduler *sch, int _ncoeffs, Tc *_coeffs, - pipebuf &_in, pipebuf &_out, - unsigned int _decim=1) - : runnable(sch, "fir_filter"), - ncoeffs(_ncoeffs), coeffs(_coeffs), - in(_in), out(_out), - decim(_decim), - freq_tap(NULL), tap_multiplier(1), freq_tol(0.1) { - shifted_coeffs = new T[ncoeffs]; - set_freq(0); +template +struct fir_filter: runnable +{ + fir_filter(scheduler *sch, int _ncoeffs, Tc *_coeffs, pipebuf &_in, pipebuf &_out, unsigned int _decim = 1) : + runnable(sch, "fir_filter"), + freq_tap(NULL), + tap_multiplier(1), + freq_tol(0.1), + ncoeffs(_ncoeffs), + coeffs(_coeffs), + in(_in), + out(_out), + decim(_decim) + { + shifted_coeffs = new T[ncoeffs]; + set_freq(0); } - - void run() { - if ( in.readable() < ncoeffs ) return; - if ( freq_tap ) { - float new_freq = *freq_tap * tap_multiplier; - if ( fabs(current_freq-new_freq) > freq_tol ) { - if ( sch->verbose ) - fprintf(stderr, "Shifting filter %f -> %f\n", - current_freq, new_freq); - set_freq(new_freq); - } - } + void run() + { + if (in.readable() < ncoeffs) + return; - unsigned long count = min((in.readable()-ncoeffs)/decim, - out.writable()); - T *pin=in.rd()+ncoeffs, *pend=pin+count*decim, *pout=out.wr(); - // TBD use coeffs when current_freq=0 (fewer mults if float) - for ( ; pin freq_tol) + { + if (sch->verbose) + fprintf(stderr, "Shifting filter %f -> %f\n", current_freq, + new_freq); + set_freq(new_freq); + } + } + + unsigned long count = min((in.readable() - ncoeffs) / decim, + out.writable()); + T *pin = in.rd() + ncoeffs, *pend = pin + count * decim, *pout = + out.wr(); + // TBD use coeffs when current_freq=0 (fewer mults if float) + for (; pin < pend; pin += decim, ++pout) + { + T *pc = shifted_coeffs; + T *pi = pin; + T x = 0; + for (unsigned int i = ncoeffs; i--; ++pc, --pi) + x = x + (*pc) * (*pi); + *pout = x; + } + in.read(count * decim); + out.written(count); } - - private: + +public: + float *freq_tap; + float tap_multiplier; + float freq_tol; + +private: unsigned int ncoeffs; Tc *coeffs; pipereader in; pipewriter out; unsigned int decim; - - T *shifted_coeffs; + T *shifted_coeffs; float current_freq; - void set_freq(float f) { - for ( int i=0; i +struct fir_resampler: runnable +{ + fir_resampler(scheduler *sch, int _ncoeffs, Tc *_coeffs, pipebuf &_in, pipebuf &_out, int _interp = 1, int _decim = 1) : + runnable(sch, "fir_resampler"), + ncoeffs(_ncoeffs), + coeffs(_coeffs), + interp(_interp), + decim(_decim), + in(_in), + out(_out, interp), + freq_tap(NULL), + tap_multiplier(1), + freq_tol(0.1) + { + if (decim != 1) + fail("fir_resampler: decim not implemented"); // TBD + shifted_coeffs = new T[ncoeffs]; + set_freq(0); + } + + void run() + { + if (in.readable() < ncoeffs) + return; + + if (freq_tap) + { + float new_freq = *freq_tap * tap_multiplier; + if (fabs(current_freq - new_freq) > freq_tol) + { + if (sch->verbose) + fprintf(stderr, "Shifting filter %f -> %f\n", current_freq, + new_freq); + set_freq(new_freq); + } + } + + if (in.readable() * interp < ncoeffs) + return; + unsigned long count = min((in.readable() * interp - ncoeffs) / interp, + out.writable() / interp); + int latency = (ncoeffs + interp) / interp; + T *pin = in.rd() + latency, *pend = pin + count, *pout = out.wr(); + // TBD use coeffs when current_freq=0 (fewer mults if float) + for (; pin < pend; ++pin) + { + for (int i = 0; i < interp; ++i, ++pout) + { + T *pi = pin; + T *pc = shifted_coeffs + i, *pcend = shifted_coeffs + ncoeffs; + T x = 0; + for (; pc < pcend; pc += interp, --pi) + x = x + (*pc) * (*pi); + *pout = x; + } + } + in.read(count); + out.written(count * interp); + } + +public: float *freq_tap; float tap_multiplier; float freq_tol; - }; // fir_filter - - // FIR FILTER WITH INTERPOLATION AND DECIMATION - - template - struct fir_resampler : runnable { - fir_resampler(scheduler *sch, int _ncoeffs, Tc *_coeffs, - pipebuf &_in, pipebuf &_out, - int _interp=1, int _decim=1) - : runnable(sch, "fir_resampler"), - ncoeffs(_ncoeffs), coeffs(_coeffs), - interp(_interp), decim(_decim), - in(_in), out(_out,interp), - freq_tap(NULL), tap_multiplier(1), freq_tol(0.1) - { - if ( decim != 1 ) fail("fir_resampler: decim not implemented"); // TBD - shifted_coeffs = new T[ncoeffs]; - set_freq(0); - } - - void run() { - if ( in.readable() < ncoeffs ) return; - - if ( freq_tap ) { - float new_freq = *freq_tap * tap_multiplier; - if ( fabs(current_freq-new_freq) > freq_tol ) { - if ( sch->verbose ) - fprintf(stderr, "Shifting filter %f -> %f\n", - current_freq, new_freq); - set_freq(new_freq); - } - } - - if ( in.readable()*interp < ncoeffs ) return; - unsigned long count = min((in.readable()*interp-ncoeffs)/interp, - out.writable()/interp); - int latency = (ncoeffs+interp) / interp; - T *pin=in.rd()+latency, *pend=pin+count, *pout=out.wr(); - // TBD use coeffs when current_freq=0 (fewer mults if float) - for ( ; pin in; pipewriter out; - - public: - float *freq_tap; - float tap_multiplier; - float freq_tol; - - private: - T *shifted_coeffs; + T *shifted_coeffs; float current_freq; - void set_freq(float f) { - for ( int i=0; i * make_dvbs2_constellation(cstln_lut<256>::predef c, - code_rate r) { - float gamma1=1, gamma2=1, gamma3=1; - switch ( c ) { +inline cstln_lut<256> * make_dvbs2_constellation(cstln_lut<256>::predef c, + code_rate r) +{ + float gamma1 = 1, gamma2 = 1, gamma3 = 1; + switch (c) + { case cstln_lut<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("Code rate not supported with APSK16"); - } - break; + // 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("Code rate not supported with APSK16"); + } + break; case cstln_lut<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("Code rate not supported with APSK32"); - } - break; + // 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("Code rate not supported with APSK32"); + } + break; case cstln_lut<256>::APSK64E: - // EN 302 307-2, section 5.4.5, Table 13f - gamma1 = 2.4; gamma2 = 4.3; gamma3 = 7; - break; + // EN 302 307-2, section 5.4.5, Table 13f + gamma1 = 2.4; + gamma2 = 4.3; + gamma3 = 7; + break; default: - break; + break; } return new cstln_lut<256>(c, gamma1, gamma2, gamma3); - } +} - // EN 300 421, section 4.4.3, table 2 Punctured code, G1=0171, G2=0133 - static const int DVBS_G1 = 0171; - static const int DVBS_G2 = 0133; +// EN 300 421, section 4.4.3, table 2 Punctured code, G1=0171, G2=0133 +static const int DVBS_G1 = 0171; +static const int DVBS_G2 = 0133; -// G1 = 0b1111001 +// G1 = 0b1111001 // G2 = 0b1011011 // // G1 = [ 1 1 1 1 0 0 1 ] @@ -98,86 +137,95 @@ namespace leansdr { // IQ = [ Q1; I1; ... Q10; I10 ] = C * S // // D * C == [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ] -// +// // D = [ 0 1 0 1 1 1 0 1 1 1 0 0 0 0] -// D = 0x3ba +// D = 0x3ba - template - struct deconvol_sync : runnable { - deconvol_sync(scheduler *sch, - pipebuf &_in, - pipebuf &_out, - uint32_t gX, uint32_t gY, - uint32_t pX, uint32_t pY) - : runnable(sch, "deconvol_sync"), - fastlock(false), - in(_in), out(_out,SIZE_RSPACKET), - skip(0) { - conv = new uint32_t[2]; - conv[0] = gX; - conv[1] = gY; - nG = 2; - punct = new uint32_t[2]; - punct[0] = pX; - punct[1] = pY; - punctperiod = 0; - punctweight = 0; - for ( int i=0; i<2; ++i ) { - int nbits = log2(punct[i]) + 1; - if ( nbits > punctperiod ) punctperiod = nbits; - punctweight += hamming_weight(punct[i]); - } - if ( sch->verbose ) - fprintf(stderr, "puncturing %d/%d\n", punctperiod, punctweight); - deconv = new iq_t[punctperiod]; - deconv2 = new iq_t[punctperiod]; - inverse_convolution(); - init_syncs(); - locked = &syncs[0]; +template +struct deconvol_sync: runnable +{ + deconvol_sync(scheduler *sch, pipebuf &_in, + pipebuf &_out, uint32_t gX, uint32_t gY, uint32_t pX, + uint32_t pY) : + runnable(sch, "deconvol_sync"), fastlock(false), in(_in), out(_out, + SIZE_RSPACKET), skip(0) + { + conv = new uint32_t[2]; + conv[0] = gX; + conv[1] = gY; + nG = 2; + punct = new uint32_t[2]; + punct[0] = pX; + punct[1] = pY; + punctperiod = 0; + punctweight = 0; + for (int i = 0; i < 2; ++i) + { + int nbits = log2(punct[i]) + 1; + if (nbits > punctperiod) + punctperiod = nbits; + punctweight += hamming_weight(punct[i]); + } + if (sch->verbose) + fprintf(stderr, "puncturing %d/%d\n", punctperiod, punctweight); + deconv = new iq_t[punctperiod]; + deconv2 = new iq_t[punctperiod]; + inverse_convolution(); + init_syncs(); + locked = &syncs[0]; } typedef uint64_t signal_t; typedef uint64_t iq_t; - static int log2(uint64_t x) { - int n = -1; - for ( ; x; ++n,x>>=1 ) ; - return n; + static int log2(uint64_t x) + { + int n = -1; + for (; x; ++n, x >>= 1) + ; + return n; } - iq_t convolve(signal_t s) { - int sbits = log2(s) + 1; - iq_t iq = 0; - unsigned char state = 0; - for ( int b=sbits-1; b>=0; --b ) { // Feed into convolver, MSB first - unsigned char bit = (s>>b) & 1; - state = (state>>1) | (bit<<6); // Shift register - for ( int j=0; j= 0; --b) + { // Feed into convolver, MSB first + unsigned char bit = (s >> b) & 1; + state = (state >> 1) | (bit << 6); // Shift register + for (int j = 0; j < nG; ++j) + { + unsigned char xy = parity(state & conv[j]); // Taps + if (punct[j] & (1 << (b % punctperiod))) + iq = (iq << 1) | xy; + } + } + return iq; } - - void run() { - run_decoding(); + + void run() + { + run_decoding(); } - - void next_sync() { - if ( fastlock ) fail("Bug: next_sync() called with fastlock"); - ++locked; - if ( locked == &syncs[NSYNCS] ) { - locked = &syncs[0]; - // Try next symbol alignment (for FEC other than 1/2) - skip = 1; - } + + void next_sync() + { + if (fastlock) + fail("Bug: next_sync() called with fastlock"); + ++locked; + if (locked == &syncs[NSYNCS]) + { + locked = &syncs[0]; + // Try next symbol alignment (for FEC other than 1/2) + skip = 1; + } } bool fastlock; - private: +private: static const int maxsbits = 64; iq_t response[maxsbits]; @@ -185,161 +233,204 @@ namespace leansdr { //static const int traceback = 48; // For code rate 7/8 static const int traceback = 64; // For code rate 7/8 with fastlock - void solve_rec(iq_t prefix, int nprefix, signal_t exp, iq_t *best) { - if ( prefix > *best ) return; - if ( nprefix > sizeof(prefix)*8 ) return; - int solved = 1; - for ( int b=0; b>b)&1) ) { - // Current candidate does not solve this column. - if ( (response[b]>>nprefix) == 0 ) - // No more bits to trace back. - return; - solved = 0; - } - } - if ( solved ) { *best = prefix; return; } - solve_rec(prefix, nprefix+1, exp, best); - solve_rec(prefix|((iq_t)1< *best) + return; + if (nprefix > sizeof(prefix) * 8) + return; + int solved = 1; + for (int b = 0; b < maxsbits; ++b) + { + if (parity(prefix & response[b]) != ((exp >> b) & 1)) + { + // Current candidate does not solve this column. + if ((response[b] >> nprefix) == 0) + // No more bits to trace back. + return; + solved = 0; + } + } + if (solved) + { + *best = prefix; + return; + } + solve_rec(prefix, nprefix + 1, exp, best); + solve_rec(prefix | ((iq_t) 1 << nprefix), nprefix + 1, exp, best); } static const int LATENCY = 0; - void inverse_convolution() { - for ( int sbit=0; sbitdebug ) { - for ( int b=0; b sizeof(iq_t)*8 ) - fail("Bug: traceback exceeds register size"); - if ( log2(deconv[b])+1 > traceback ) - fail("traceback insufficient for deconvolution"); - if ( log2(deconv2[b])+1 > traceback ) - fail("traceback insufficient for deconvolution (alt)"); - } + if (sch->debug) + { + for (int b = 0; b < punctperiod; ++b) + fprintf(stderr, "deconv[%d]=0x%016llx %d taps / %d bits\n", b, + (unsigned long long) deconv[b], + hamming_weight(deconv[b]), log2(deconv[b]) + 1); + } + + // Sanity check + for (int b = 0; b < punctperiod; ++b) + { + for (int i = 0; i < maxsbits; ++i) + { + iq_t iq = convolve((iq_t) 1 << (LATENCY + i)); + int expect = (b == i) ? 1 : 0; + int d = parity(iq & deconv[b]); + if (d != expect) + fail("Failed to inverse convolutional coding"); + int d2 = parity(iq & deconv2[b]); + if (d2 != expect) + fail("Failed to inverse convolutional coding (alt)"); + } + if (traceback > sizeof(iq_t) * 8) + fail("Bug: traceback exceeds register size"); + if (log2(deconv[b]) + 1 > traceback) + fail("traceback insufficient for deconvolution"); + if (log2(deconv2[b]) + 1 > traceback) + fail("traceback insufficient for deconvolution (alt)"); + } } static const int NSYNCS = 4; - struct sync_t { - u8 lut[2][2]; // lut[(re>0)?1:0][(im>0)?1:0] = 0b000000IQ - iq_t in; - int n_in; - signal_t out; - int n_out; - // Auxiliary shift register for fastlock - iq_t in2; - int n_in2, n_out2; + struct sync_t + { + u8 lut[2][2]; // lut[(re>0)?1:0][(im>0)?1:0] = 0b000000IQ + iq_t in; + int n_in; + signal_t out; + int n_out; + // Auxiliary shift register for fastlock + iq_t in2; + int n_in2, n_out2; } syncs[NSYNCS]; - void init_syncs() { - // EN 300 421, section 4.5, Figure 5 QPSK constellation - // Four rotations * two conjugations. - // 180° rotation is detected as polarity inversion in mpeg_sync. - for ( int sync_id=0; sync_id 5 bytes // 7/8 32 symbols -> 7 bytes - inline Tbyte readbyte(sync_t *s, softsymbol *&p) { - while ( s->n_out < 8 ) { - iq_t iq = s->in; - while ( s->n_in < traceback ) { - u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1]; - ++p; - iq = (iq<<2) | iqbits; - s->n_in += 2; - } - s->in = iq; - for ( int b=punctperiod-1; b>=0; --b ) { - u8 bit = parity(iq&deconv[b]); - s->out = (s->out<<1) | bit; - } - s->n_out += punctperiod; - s->n_in -= punctweight; - } - Tbyte res = (s->out >> (s->n_out-8)) & 255; - s->n_out -= 8; - return res; + inline Tbyte readbyte(sync_t *s, softsymbol *&p) + { + while (s->n_out < 8) + { + iq_t iq = s->in; + while (s->n_in < traceback) + { + u8 iqbits = s->lut[(p->symbol & 2) ? 1 : 0][p->symbol & 1]; + ++p; + iq = (iq << 2) | iqbits; + s->n_in += 2; + } + s->in = iq; + for (int b = punctperiod - 1; b >= 0; --b) + { + u8 bit = parity(iq & deconv[b]); + s->out = (s->out << 1) | bit; + } + s->n_out += punctperiod; + s->n_in -= punctweight; + } + Tbyte res = (s->out >> (s->n_out - 8)) & 255; + s->n_out -= 8; + return res; } - inline unsigned long readerrors(sync_t *s, softsymbol *&p) { - unsigned long res = 0; - while ( s->n_out2 < 8 ) { - iq_t iq = s->in2; - while ( s->n_in2 < traceback ) { - u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1]; - ++p; - iq = (iq<<2) | iqbits; - s->n_in2 += 2; - } - s->in2 = iq; - for ( int b=punctperiod-1; b>=0; --b ) { - u8 bit = parity(iq&deconv[b]); - u8 bit2 = parity(iq&deconv2[b]); - if ( bit2 != bit ) ++res; - } - s->n_out2 += punctperiod; - s->n_in2 -= punctweight; - } - s->n_out2 -= 8; - return res; + inline unsigned long readerrors(sync_t *s, softsymbol *&p) + { + unsigned long res = 0; + while (s->n_out2 < 8) + { + iq_t iq = s->in2; + while (s->n_in2 < traceback) + { + u8 iqbits = s->lut[(p->symbol & 2) ? 1 : 0][p->symbol & 1]; + ++p; + iq = (iq << 2) | iqbits; + s->n_in2 += 2; + } + s->in2 = iq; + for (int b = punctperiod - 1; b >= 0; --b) + { + u8 bit = parity(iq & deconv[b]); + u8 bit2 = parity(iq & deconv2[b]); + if (bit2 != bit) + ++res; + } + s->n_out2 += punctperiod; + s->n_in2 -= punctweight; + } + s->n_out2 -= 8; + return res; } - void run_decoding() { - in.read(skip); - skip = 0; + void run_decoding() + { + in.read(skip); + skip = 0; - // 8 byte margin to fill the deconvolver - if ( in.readable() < 64 ) return; - int maxrd = (in.readable()-64) / (punctweight/2) * punctperiod / 8; - int maxwr = out.writable(); - int n = (maxrddebug ) - fprintf(stderr, "{%d->%d}\n", - (int)(locked-syncs), (int)(best-syncs)); - locked = best; - } - // If deconvolution bit error rate > 33%, try next sample alignment - if ( errors_best > n*8/3 ) { - // fprintf(stderr, ">"); - skip = 1; - } - } + // 8 byte margin to fill the deconvolver + if (in.readable() < 64) + return; + int maxrd = (in.readable() - 64) / (punctweight / 2) * punctperiod / 8; + int maxwr = out.writable(); + unsigned int n = (maxrd < maxwr) ? maxrd : maxwr; + if (!n) + return; + // Require enough symbols to discriminate in fastlock mode + // (threshold must be less than size of rspacket) + if (n < 32) + return; - softsymbol *pin=in.rd(), *pin0=pin; - Tbyte *pout=out.wr(), *pout0=pout; - while ( n-- ) - *pout++ = readbyte(locked, pin); - in.read(pin-pin0); - out.written(pout-pout0); - } + if (fastlock) + { + // Try all sync alignments + unsigned long errors_best = 1 << 30; + sync_t *best = &syncs[0]; + for (sync_t *s = syncs; s < syncs + NSYNCS; ++s) + { + softsymbol *pin = in.rd(); + unsigned long errors = 0; + for (int c = n; c--;) + errors += readerrors(s, pin); + if (errors < errors_best) + { + errors_best = errors; + best = s; + } + } + if (best != locked) + { + // Another alignment produces fewer bit errors + if (sch->debug) + fprintf(stderr, "{%d->%d}\n", (int) (locked - syncs), + (int) (best - syncs)); + locked = best; + } + // If deconvolution bit error rate > 33%, try next sample alignment + if (errors_best > n * 8 / 3) + { + // fprintf(stderr, ">"); + skip = 1; + } + } + + softsymbol *pin = in.rd(), *pin0 = pin; + Tbyte *pout = out.wr(), *pout0 = pout; + while (n--) + *pout++ = readbyte(locked, pin); + in.read(pin - pin0); + out.written(pout - pout0); + } pipereader in; pipewriter out; @@ -456,410 +565,479 @@ namespace leansdr { sync_t *locked; int skip; - }; +}; - typedef deconvol_sync deconvol_sync_simple; +typedef deconvol_sync deconvol_sync_simple; - inline deconvol_sync_simple *make_deconvol_sync_simple(scheduler *sch, - pipebuf &_in, - pipebuf &_out, - enum code_rate rate) { +inline deconvol_sync_simple *make_deconvol_sync_simple(scheduler *sch, + pipebuf &_in, pipebuf &_out, enum code_rate rate) +{ // EN 300 421, section 4.4.3 Inner coding uint32_t pX, pY; - switch ( rate ) { + switch (rate) + { case FEC12: - pX = 0x1; // 1 - pY = 0x1; // 1 - break; + pX = 0x1; // 1 + pY = 0x1; // 1 + break; case FEC23: case FEC46: - pX = 0xa; // 1010 (Handle as FEC4/6, no half-symbols) - pY = 0xf; // 1111 - break; + pX = 0xa; // 1010 (Handle as FEC4/6, no half-symbols) + pY = 0xf; // 1111 + break; case FEC34: - pX = 0x5; // 101 - pY = 0x6; // 110 - break; + pX = 0x5; // 101 + pY = 0x6; // 110 + break; case FEC56: - pX = 0x15; // 10101 - pY = 0x1a; // 11010 - break; + pX = 0x15; // 10101 + pY = 0x1a; // 11010 + break; case FEC78: - pX = 0x45; // 1000101 - pY = 0x7a; // 1111010 - break; + pX = 0x45; // 1000101 + pY = 0x7a; // 1111010 + break; default: - //fail("Code rate not implemented"); - // For testing DVB-S2 constellations. - fprintf(stderr, "Code rate not implemented; proceeding anyway\n"); - pX = pY = 1; + //fail("Code rate not implemented"); + // For testing DVB-S2 constellations. + fprintf(stderr, "Code rate not implemented; proceeding anyway\n"); + pX = pY = 1; } return new deconvol_sync_simple(sch, _in, _out, DVBS_G1, DVBS_G2, pX, pY); - } +} +// CONVOLUTIONAL ENCODER - // CONVOLUTIONAL ENCODER +static const uint16_t polys_fec12[] = +{ DVBS_G1, DVBS_G2 // X1Y1 + }; +static const uint16_t polys_fec23[] = +{ DVBS_G1, DVBS_G2, DVBS_G2 << 1 // X1Y1Y2 +}; +// Same code rate as 2/3, usable with QPSK +static const uint16_t polys_fec46[] = +{ DVBS_G1, DVBS_G2, DVBS_G2 << 1, // X1Y1Y2 +DVBS_G1 << 2, DVBS_G2 << 2, DVBS_G2 << 3 // X3Y3Y4 +}; +static const uint16_t polys_fec34[] = +{ DVBS_G1, DVBS_G2, // X1Y1 + DVBS_G2 << 1, DVBS_G1 << 2 // Y2X3 +}; +static const uint16_t polys_fec45[] = +{ // Non standard + DVBS_G1, DVBS_G2, // X1Y1 + DVBS_G2 << 1, DVBS_G1 << 2, // Y2X3 + DVBS_G1 << 3 // X4 + }; +static const uint16_t polys_fec56[] = +{ DVBS_G1, DVBS_G2, // X1Y1 + DVBS_G2 << 1, DVBS_G1 << 2, // Y2X3 + DVBS_G2 << 3, DVBS_G1 << 4 // Y4X5 +}; +static const uint16_t polys_fec78[] = +{ DVBS_G1, DVBS_G2, // X1Y1 + DVBS_G2 << 1, DVBS_G2 << 2, // Y2Y3 + DVBS_G2 << 3, DVBS_G1 << 4, // Y4X5 + DVBS_G2 << 5, DVBS_G1 << 6 // Y6X7 +}; - static const uint16_t polys_fec12[] = { - DVBS_G1, DVBS_G2 // X1Y1 - }; - static const uint16_t polys_fec23[] = { - DVBS_G1, DVBS_G2, DVBS_G2<<1 // X1Y1Y2 - }; - // Same code rate as 2/3, usable with QPSK - static const uint16_t polys_fec46[] = { - DVBS_G1, DVBS_G2, DVBS_G2<<1, // X1Y1Y2 - DVBS_G1<<2, DVBS_G2<<2, DVBS_G2<<3 // X3Y3Y4 - }; - static const uint16_t polys_fec34[] = { - DVBS_G1, DVBS_G2, // X1Y1 - DVBS_G2<<1, DVBS_G1<<2 // Y2X3 - }; - static const uint16_t polys_fec45[] = { // Non standard - DVBS_G1, DVBS_G2, // X1Y1 - DVBS_G2<<1, DVBS_G1<<2, // Y2X3 - DVBS_G1<<3 // X4 - }; - static const uint16_t polys_fec56[] = { - DVBS_G1, DVBS_G2, // X1Y1 - DVBS_G2<<1, DVBS_G1<<2, // Y2X3 - DVBS_G2<<3, DVBS_G1<<4 // Y4X5 - }; - static const uint16_t polys_fec78[] = { - DVBS_G1, DVBS_G2, // X1Y1 - DVBS_G2<<1, DVBS_G2<<2, // Y2Y3 - DVBS_G2<<3, DVBS_G1<<4, // Y4X5 - DVBS_G2<<5, DVBS_G1<<6 // Y6X7 - }; - - // FEC parameters, for convolutional coding only (not S2). - static struct fec_spec { +// FEC parameters, for convolutional coding only (not S2). +static struct fec_spec +{ int bits_in; // Entering the convolutional coder int bits_out; // Exiting the convolutional coder const uint16_t *polys; // [bits_out] - } fec_specs[FEC_MAX] = { - [FEC12] = { 1, 2, polys_fec12 }, - [FEC23] = { 2, 3, polys_fec23 }, - [FEC46] = { 4, 6, polys_fec46 }, - [FEC34] = { 3, 4, polys_fec34 }, - [FEC56] = { 5, 6, polys_fec56 }, - [FEC78] = { 7, 8, polys_fec78 }, - [FEC45] = { 4, 5, polys_fec45 }, // Non-standard - }; +} fec_specs[FEC_MAX] = +{ [FEC12] = +{ 1, 2, polys_fec12 }, [FEC23] = +{ 2, 3, polys_fec23 }, [FEC46] = +{ 4, 6, polys_fec46 }, [FEC34] = +{ 3, 4, polys_fec34 }, [FEC56] = +{ 5, 6, polys_fec56 }, [FEC78] = +{ 7, 8, polys_fec78 }, [FEC45] = +{ 4, 5, polys_fec45 }, // Non-standard + }; - struct dvb_convol : runnable { +struct dvb_convol: runnable +{ typedef u8 uncoded_byte; typedef u8 hardsymbol; - dvb_convol(scheduler *sch, - pipebuf &_in, - pipebuf &_out, - code_rate fec, - int bits_per_symbol) - : runnable(sch, "dvb_convol"), - in(_in), out(_out,64) // BPSK 7/8: 7 bytes in, 64 symbols out + dvb_convol(scheduler *sch, pipebuf &_in, + pipebuf &_out, code_rate fec, int bits_per_symbol) : + runnable(sch, "dvb_convol"), in(_in), out(_out, 64) // BPSK 7/8: 7 bytes in, 64 symbols out { - fec_spec *fs = &fec_specs[fec]; - if ( ! fs->bits_in ) fail("Unexpected FEC"); - convol.bits_in = fs->bits_in; - convol.bits_out = fs->bits_out; - convol.polys = fs->polys; - convol.bps = bits_per_symbol; - // FEC must output a whole number of IQ symbols - if ( convol.bits_out % convol.bps ) - fail("Code rate not suitable for this constellation"); + fec_spec *fs = &fec_specs[fec]; + if (!fs->bits_in) + fail("Unexpected FEC"); + convol.bits_in = fs->bits_in; + convol.bits_out = fs->bits_out; + convol.polys = fs->polys; + convol.bps = bits_per_symbol; + // FEC must output a whole number of IQ symbols + if (convol.bits_out % convol.bps) + fail("Code rate not suitable for this constellation"); } - void run() { - int count = min(in.readable(), out.writable()*convol.bps/ - convol.bits_out*convol.bits_in/8); - // Process in multiples of the puncturing period and of 8 bits. - int chunk = convol.bits_in; - count = (count/chunk) * chunk; - convol.encode(in.rd(), out.wr(), count); - in.read(count); - int nout = count*8/convol.bits_in*convol.bits_out/convol.bps; - out.written(nout); + void run() + { + int count = min(in.readable(), + out.writable() * convol.bps / convol.bits_out * convol.bits_in + / 8); + // Process in multiples of the puncturing period and of 8 bits. + int chunk = convol.bits_in; + count = (count / chunk) * chunk; + convol.encode(in.rd(), out.wr(), count); + in.read(count); + int nout = count * 8 / convol.bits_in * convol.bits_out / convol.bps; + out.written(nout); } - private: +private: pipereader in; pipewriter out; convol_multipoly convol; - }; // dvb_convol +}; +// dvb_convol +// NEW ALGEBRAIC DECONVOLUTION - // NEW ALGEBRAIC DECONVOLUTION +// QPSK 1/2 only; +// With DVB-S polynomials hardcoded. - // QPSK 1/2 only; - // With DVB-S polynomials hardcoded. - - template - struct dvb_deconvol_sync : runnable { +template +struct dvb_deconvol_sync: runnable +{ typedef u8 decoded_byte; int resync_period; static const int chunk_size = 64; // At least 2*sizeof(Thist)/8 - dvb_deconvol_sync(scheduler *sch, - pipebuf &_in, - pipebuf &_out) - : runnable(sch, "deconvol_sync_multipoly"), - resync_period(32), - in(_in), out(_out,chunk_size), - resync_phase(0) + dvb_deconvol_sync(scheduler *sch, pipebuf &_in, + pipebuf &_out) : + runnable(sch, "deconvol_sync_multipoly"), resync_period(32), in( + _in), out(_out, chunk_size), resync_phase(0) { - init_syncs(); - locked = &syncs[0]; + init_syncs(); + locked = &syncs[0]; } - void run() { + void run() + { - while ( in.readable() >= chunk_size*8 && - out.writable() >= chunk_size ) { - int errors_best = 1 << 30; - sync_t *best = NULL; - for ( sync_t *s=syncs; sdeconv.run(pin, s->lut, pout, chunk_size); - if ( nerrors < errors_best ) { errors_best=nerrors; best=s; } - } - in.read(chunk_size*8); - out.written(chunk_size); - if ( best != locked ) { - if ( sch->debug ) fprintf(stderr, "%%%d", (int)(best-syncs)); - locked = best; - } - if ( ++resync_phase >= resync_period ) resync_phase = 0; - } // Work to do + while (in.readable() >= chunk_size * 8 && out.writable() >= chunk_size) + { + int errors_best = 1 << 30; + sync_t *best = NULL; + for (sync_t *s = syncs; s < syncs + NSYNCS; ++s) + { + if (resync_phase != 0 && s != locked) + // Decode only the currently-locked alignment + continue; + Tin *pin = in.rd(); + static decoded_byte dummy[chunk_size]; + decoded_byte *pout = (s == locked) ? out.wr() : dummy; + int nerrors = s->deconv.run(pin, s->lut, pout, chunk_size); + if (nerrors < errors_best) + { + errors_best = nerrors; + best = s; + } + } + in.read(chunk_size * 8); + out.written(chunk_size); + if (best != locked) + { + if (sch->debug) + fprintf(stderr, "%%%d", (int) (best - syncs)); + locked = best; + } + if (++resync_phase >= resync_period) + resync_phase = 0; + } // Work to do } // run() - private: +private: pipereader in; pipewriter out; int resync_phase; - + static const int NSYNCS = 4; - struct sync_t { - deconvol_poly2 deconv; - u8 lut[4]; // TBD Swap and flip bits in the polynomials instead. + struct sync_t + { + deconvol_poly2 deconv; + u8 lut[4]; // TBD Swap and flip bits in the polynomials instead. } syncs[NSYNCS]; sync_t *locked; - void init_syncs() { - for ( int s=0; s dvb_deconvol_sync_soft; +typedef dvb_deconvol_sync dvb_deconvol_sync_hard; - typedef dvb_deconvol_sync dvb_deconvol_sync_soft; - typedef dvb_deconvol_sync dvb_deconvol_sync_hard; +// BIT ALIGNMENT AND MPEG SYNC DETECTION - - // BIT ALIGNMENT AND MPEG SYNC DETECTION - - template - struct mpeg_sync : runnable { +template +struct mpeg_sync: runnable +{ int scan_syncs, want_syncs; unsigned long lock_timeout; bool fastlock; int resync_period; - mpeg_sync(scheduler *sch, - pipebuf &_in, - pipebuf &_out, - deconvol_sync *_deconv, - pipebuf *_state_out=NULL, - pipebuf *_locktime_out=NULL) - : runnable(sch, "sync_detect"), - scan_syncs(8), want_syncs(4), - lock_timeout(4), - fastlock(false), - resync_period(1), - in(_in), out(_out, SIZE_RSPACKET*(scan_syncs+1)), - deconv(_deconv), - polarity(0), - resync_phase(0), - bitphase(0), synchronized(false), - next_sync_count(0), - report_state(true) { - state_out = _state_out ? new pipewriter(*_state_out) : NULL; - locktime_out = - _locktime_out ? new pipewriter(*_locktime_out) : NULL; - } - - void run() { - if ( report_state && state_out && state_out->writable()>=1 ) { - // Report unlocked state on first invocation. - state_out->write(0); - report_state = false; - } - if ( synchronized ) - run_decoding(); - else { - if ( fastlock ) run_searching_fast(); else run_searching(); - } + mpeg_sync( + scheduler *sch, + pipebuf &_in, + pipebuf &_out, + deconvol_sync *_deconv, + pipebuf *_state_out = NULL, + pipebuf *_locktime_out = NULL) : + runnable(sch, "sync_detect"), + scan_syncs(8), + want_syncs(4), + lock_timeout(4), + fastlock(false), + resync_period(1), + in(_in), + out(_out, SIZE_RSPACKET * (scan_syncs + 1)), + deconv(_deconv), + polarity(0), + resync_phase(0), + bitphase(0), + synchronized(false), + next_sync_count(0), + phase8(-1), + lock_timeleft(0), + locktime(0), + report_state(true) + { + state_out = _state_out ? new pipewriter(*_state_out) : NULL; + locktime_out = locktime_out ? new pipewriter(*_locktime_out) : NULL; } - void run_searching() { - bool next_sync = false; - int chunk = SIZE_RSPACKET * scan_syncs; - while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting - out.writable() >= chunk && // Use as temp buffer - ( !state_out || state_out->writable()>=1 ) ) { - if ( search_sync() ) return; - in.read(chunk); - // Switch to next bit alignment - ++bitphase; - if ( bitphase == 8 ) { - bitphase = 0; - next_sync = true; - } - } - - if ( next_sync ) { - // No lock this time - ++next_sync_count; - if ( next_sync_count >= 3 ) { - // After a few cycles without a lock, resync the deconvolver. - next_sync_count = 0; - if ( deconv ) deconv->next_sync(); - } - } + void run() + { + if (report_state && state_out && state_out->writable() >= 1) + { + // Report unlocked state on first invocation. + state_out->write(0); + report_state = false; + } + if (synchronized) + run_decoding(); + else + { + if (fastlock) + run_searching_fast(); + else + run_searching(); + } } - void run_searching_fast() { - int chunk = SIZE_RSPACKET * scan_syncs; - while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting - out.writable() >= chunk && // Use as temp buffer - ( !state_out || state_out->writable()>=1 ) ) { - if ( resync_phase == 0 ) { - // Try all bit alighments - for ( bitphase=0; bitphase<=7; ++bitphase ) { - if ( search_sync() ) return; - } - } - in.read(SIZE_RSPACKET); - if ( ++resync_phase >= resync_period ) resync_phase = 0; - } + void run_searching() + { + bool next_sync = false; + unsigned int chunk = SIZE_RSPACKET * scan_syncs; + + while (in.readable() >= chunk + 1 && // Need 1 ahead for bit shifting + out.writable() >= chunk && // Use as temp buffer + (!state_out || state_out->writable() >= 1)) + { + if (search_sync()) + return; + in.read(chunk); + // Switch to next bit alignment + ++bitphase; + if (bitphase == 8) + { + bitphase = 0; + next_sync = true; + } + } + + if (next_sync) + { + // No lock this time + ++next_sync_count; + if (next_sync_count >= 3) + { + // After a few cycles without a lock, resync the deconvolver. + next_sync_count = 0; + if (deconv) + deconv->next_sync(); + } + } } - bool search_sync() { - int chunk = SIZE_RSPACKET * scan_syncs; - // Bit-shift [scan_sync] packets according to current [bitphase] - Tbyte *pin = in.rd(), *pend = pin+chunk; - Tbyte *pout = out.wr(); - unsigned short w = *pin++; - for ( ; pin<=pend; ++pin,++pout ) { - w = (w<<8) | *pin; - *pout = w >> bitphase; - } - // Search for [want_sync] start codes at all 204 offsets - for ( int i=0; i nsyncs_n) - { polarity=0; nsyncs=nsyncs_p; phase8=phase8_p; } - else - { polarity=-1; nsyncs=nsyncs_n; phase8=phase8_n; } - if ( nsyncs>=want_syncs && phase8>=0 ) { - if ( sch->debug ) fprintf(stderr, "Locked\n"); - if ( ! i ) { // Avoid fixpoint detection in scheduler - i = SIZE_RSPACKET; - phase8 = (phase8+1) & 7; - } - in.read(i); // Skip to first start code - synchronized = true; - lock_timeleft = lock_timeout; - locktime = 0; - if ( state_out ) - state_out->write(1); - return true; - } - } - return false; + void run_searching_fast() + { + unsigned int chunk = SIZE_RSPACKET * scan_syncs; + + while (in.readable() >= chunk + 1 && // Need 1 ahead for bit shifting + out.writable() >= chunk && // Use as temp buffer + (!state_out || state_out->writable() >= 1)) + { + if (resync_phase == 0) + { + // Try all bit alighments + for (bitphase = 0; bitphase <= 7; ++bitphase) + { + if (search_sync()) + return; + } + } + in.read(SIZE_RSPACKET); + if (++resync_phase >= resync_period) + resync_phase = 0; + } } - void run_decoding() { - while ( in.readable() >= SIZE_RSPACKET+1 && // +1 for bit shifting - out.writable() >= SIZE_RSPACKET && - ( !state_out || state_out->writable()>=1 ) && - ( !locktime_out || locktime_out->writable()>=1 ) ) { - Tbyte *pin = in.rd(), *pend = pin+SIZE_RSPACKET; - Tbyte *pout = out.wr(); - unsigned short w = *pin++; - for ( ; pin<=pend; ++pin,++pout ) { - w = (w<<8) | *pin; - *pout = (w >> bitphase) ^ polarity; - } - in.read(SIZE_RSPACKET); - Tbyte syncbyte = *out.wr(); - out.written(SIZE_RSPACKET); - ++locktime; - if ( locktime_out ) - locktime_out->write(locktime); - // Reset timer if sync byte is correct - Tbyte expected = phase8 ? MPEG_SYNC : MPEG_SYNC_INV; - if ( syncbyte == expected ) lock_timeleft = lock_timeout; - phase8 = (phase8+1) & 7; - --lock_timeleft; - if ( ! lock_timeleft ) { - if ( sch->debug ) fprintf(stderr, "Unlocked\n"); - synchronized = false; - next_sync_count = 0; - if ( state_out ) - state_out->write(0); - return; - } - } + bool search_sync() + { + int chunk = SIZE_RSPACKET * scan_syncs; + // Bit-shift [scan_sync] packets according to current [bitphase] + Tbyte *pin = in.rd(), *pend = pin + chunk; + Tbyte *pout = out.wr(); + unsigned short w = *pin++; + for (; pin <= pend; ++pin, ++pout) + { + w = (w << 8) | *pin; + *pout = w >> bitphase; + } + // Search for [want_sync] start codes at all 204 offsets + for (int i = 0; i < SIZE_RSPACKET; ++i) + { + int nsyncs_p = 0, nsyncs_n = 0; // # start codes assuming pos/neg polarity + int phase8_p = -1, phase8_n = -1; // Position in sequence of 8 packets + Tbyte *p = &out.wr()[i]; + for (int j = 0; j < scan_syncs; ++j, p += SIZE_RSPACKET) + { + Tbyte b = *p; + if (b == MPEG_SYNC) + { + ++nsyncs_p; + phase8_n = (8 - j) & 7; + } + if (b == MPEG_SYNC_INV) + { + ++nsyncs_n; + phase8_p = (8 - j) & 7; + } + } + // Detect most likely polarity + int nsyncs; + if (nsyncs_p > nsyncs_n) + { + polarity = 0; + nsyncs = nsyncs_p; + phase8 = phase8_p; + } + else + { + polarity = -1; + nsyncs = nsyncs_n; + phase8 = phase8_n; + } + if (nsyncs >= want_syncs && phase8 >= 0) + { + if (sch->debug) + fprintf(stderr, "Locked\n"); + if (!i) + { // Avoid fixpoint detection in scheduler + i = SIZE_RSPACKET; + phase8 = (phase8 + 1) & 7; + } + in.read(i); // Skip to first start code + synchronized = true; + lock_timeleft = lock_timeout; + locktime = 0; + if (state_out) + state_out->write(1); + return true; + } + } + return false; } - private: + void run_decoding() + { + while (in.readable() >= SIZE_RSPACKET + 1 + && // +1 for bit shifting + out.writable() >= SIZE_RSPACKET + && (!state_out || state_out->writable() >= 1) + && (!locktime_out || locktime_out->writable() >= 1)) + { + Tbyte *pin = in.rd(), *pend = pin + SIZE_RSPACKET; + Tbyte *pout = out.wr(); + unsigned short w = *pin++; + for (; pin <= pend; ++pin, ++pout) + { + w = (w << 8) | *pin; + *pout = (w >> bitphase) ^ polarity; + } + in.read(SIZE_RSPACKET); + Tbyte syncbyte = *out.wr(); + out.written(SIZE_RSPACKET); + ++locktime; + if (locktime_out) + locktime_out->write(locktime); + // Reset timer if sync byte is correct + Tbyte expected = phase8 ? MPEG_SYNC : MPEG_SYNC_INV; + if (syncbyte == expected) + lock_timeleft = lock_timeout; + phase8 = (phase8 + 1) & 7; + --lock_timeleft; + if (!lock_timeleft) + { + if (sch->debug) + fprintf(stderr, "Unlocked\n"); + synchronized = false; + next_sync_count = 0; + if (state_out) + state_out->write(0); + return; + } + } + } + +private: pipereader in; pipewriter out; - deconvol_sync *deconv; + deconvol_sync *deconv; unsigned char polarity; // XOR mask, 0 or 0xff int resync_phase; int bitphase; @@ -871,330 +1049,376 @@ namespace leansdr { pipewriter *state_out; pipewriter *locktime_out; bool report_state; - }; +}; +template +struct rspacket +{ + Tbyte data[SIZE_RSPACKET]; +}; - template - struct rspacket { Tbyte data[SIZE_RSPACKET]; }; +// INTERLEAVER - - // INTERLEAVER - - struct interleaver : runnable { - interleaver(scheduler *sch, pipebuf< rspacket > &_in, - pipebuf< u8 > &_out) - : runnable(sch, "interleaver"), - in(_in), out(_out,SIZE_RSPACKET) { +struct interleaver: runnable +{ + interleaver(scheduler *sch, pipebuf > &_in, pipebuf &_out) : + runnable(sch, "interleaver"), in(_in), out(_out, SIZE_RSPACKET) + { } - void run() { - while ( in.readable() >= 12 && - out.writable() >= SIZE_RSPACKET ) { - rspacket *pin=in.rd(); - u8 *pout = out.wr(); - int delay = 0; - for ( int i=0; i= 12 && out.writable() >= SIZE_RSPACKET) + { + rspacket *pin = in.rd(); + u8 *pout = out.wr(); + int delay = 0; + for (int i = 0; i < SIZE_RSPACKET; + ++i, ++pout, delay = (delay + 1) % 12) + *pout = pin[11 - delay].data[i]; + in.read(1); + out.written(SIZE_RSPACKET); + } } - private: - pipereader< rspacket > in; +private: + pipereader > in; pipewriter out; - }; // interleaver +}; +// interleaver +// DEINTERLEAVER - // DEINTERLEAVER - - template - struct deinterleaver : runnable { +template +struct deinterleaver: runnable +{ deinterleaver(scheduler *sch, pipebuf &_in, - pipebuf< rspacket > &_out) - : runnable(sch, "deinterleaver"), - in(_in), out(_out) { + pipebuf > &_out) : + runnable(sch, "deinterleaver"), in(_in), out(_out) + { } - void run() { - while ( in.readable() >= 17*11*12+SIZE_RSPACKET && - out.writable() >= 1 ) { - Tbyte *pin = in.rd()+17*11*12, *pend=pin+SIZE_RSPACKET; - Tbyte *pout= out.wr()->data; - for ( int delay=17*11; pin= 17 * 11 * 12 + SIZE_RSPACKET + && out.writable() >= 1) + { + Tbyte *pin = in.rd() + 17 * 11 * 12, *pend = pin + SIZE_RSPACKET; + Tbyte *pout = out.wr()->data; + for (int delay = 17 * 11; pin < pend; + ++pin, ++pout, delay = (delay - 17 + 17 * 12) % (17 * 12)) + *pout = pin[-delay * 12]; + in.read(SIZE_RSPACKET); + out.written(1); + } } - private: +private: pipereader in; - pipewriter< rspacket > out; - }; // deinterleaver + pipewriter > out; +}; +// deinterleaver +static const int SIZE_TSPACKET = 188; +struct tspacket +{ + u8 data[SIZE_TSPACKET]; +}; - static const int SIZE_TSPACKET = 188; - struct tspacket { u8 data[SIZE_TSPACKET]; }; +// RS ENCODER - - // RS ENCODER - - struct rs_encoder : runnable { - rs_encoder(scheduler *sch, - pipebuf &_in, pipebuf< rspacket > &_out) - : runnable(sch, "RS encoder"), - in(_in), out(_out) { } - - void run() { - while ( in.readable()>=1 && out.writable()>=1 ) { - u8 *pin = in.rd()->data; - u8 *pout = out.wr()->data; - // The first 188 bytes are the uncoded message P(X) - memcpy(pout, pin, SIZE_TSPACKET); - // Append 16 RS parity bytes R(X) = - (P(X)*X^16 mod G(X)) - // so that G(X) divides the coded message S(X) = P(X)*X^16 - R(X). - rs.encode(pout); - in.read(1); - out.written(1); - } +struct rs_encoder: runnable +{ + rs_encoder(scheduler *sch, pipebuf &_in, + pipebuf > &_out) : + runnable(sch, "RS encoder"), in(_in), out(_out) + { } - private: + + void run() + { + while (in.readable() >= 1 && out.writable() >= 1) + { + u8 *pin = in.rd()->data; + u8 *pout = out.wr()->data; + // The first 188 bytes are the uncoded message P(X) + memcpy(pout, pin, SIZE_TSPACKET); + // Append 16 RS parity bytes R(X) = - (P(X)*X^16 mod G(X)) + // so that G(X) divides the coded message S(X) = P(X)*X^16 - R(X). + rs.encode(pout); + in.read(1); + out.written(1); + } + } +private: rs_engine rs; pipereader in; - pipewriter< rspacket > out; - }; // rs_encoder + pipewriter > out; +}; +// rs_encoder +// RS DECODER - // RS DECODER - - template - struct rs_decoder : runnable { +template +struct rs_decoder: runnable +{ rs_engine rs; - rs_decoder(scheduler *sch, - pipebuf< rspacket > &_in, - pipebuf &_out, - pipebuf *_bitcount=NULL, - pipebuf *_errcount=NULL) - : runnable(sch, "RS decoder"), - in(_in), out(_out) { - bitcount = _bitcount ? new pipewriter(*_bitcount) : NULL; - errcount = _errcount ? new pipewriter(*_errcount) : NULL; + rs_decoder(scheduler *sch, pipebuf > &_in, + pipebuf &_out, pipebuf *_bitcount = NULL, + pipebuf *_errcount = NULL) : + runnable(sch, "RS decoder"), in(_in), out(_out) + { + bitcount = _bitcount ? new pipewriter(*_bitcount) : NULL; + errcount = _errcount ? new pipewriter(*_errcount) : NULL; } - void run() { - if ( bitcount && bitcount->writable()<1 ) return; - if ( errcount && errcount->writable()<1 ) return; + void run() + { + if (bitcount && bitcount->writable() < 1) + return; + if (errcount && errcount->writable() < 1) + return; - int nbits=0, nerrs=0; + int nbits = 0, nerrs = 0; - while ( in.readable()>=1 && out.writable()>=1 ) { - Tbyte *pin = in.rd()->data; - u8 *pout = out.wr()->data; + while (in.readable() >= 1 && out.writable() >= 1) + { + Tbyte *pin = in.rd()->data; + u8 *pout = out.wr()->data; - nbits += SIZE_RSPACKET * 8; + nbits += SIZE_RSPACKET * 8; - // The message is the first 188 bytes. - if ( sizeof(Tbyte) == 1 ) - memcpy(pout, pin, SIZE_TSPACKET); - else - fail("Erasures not implemented"); + // The message is the first 188 bytes. + if (sizeof(Tbyte) == 1) + memcpy(pout, pin, SIZE_TSPACKET); + else + fail("Erasures not implemented"); - u8 synd[16]; - bool corrupted = rs.syndromes(pin, synd); + u8 synd[16]; + bool corrupted = rs.syndromes(pin, synd); #if 0 - if ( ! corrupted ) { - // Test BM - fprintf(stderr, "Simulating errors\n"); - pin[203] ^= 42; - pin[202] ^= 99; - corrupted = rs.syndromes(pin, synd); - } + if ( ! corrupted ) + { + // Test BM + fprintf(stderr, "Simulating errors\n"); + pin[203] ^= 42; + pin[202] ^= 99; + corrupted = rs.syndromes(pin, synd); + } #endif - if ( ! corrupted ) { - if ( sch->debug ) - fprintf(stderr, "_"); // Packet received without errors. - } else { - corrupted = rs.correct(synd, pout, pin, &nerrs); - if ( sch->debug ) { - if ( ! corrupted ) - fprintf(stderr, "."); // Errors were corrected. - else - fprintf(stderr, "!"); // Packet still corrupted. - } - } + if (!corrupted) + { + if (sch->debug) + fprintf(stderr, "_"); // Packet received without errors. + } + else + { + corrupted = rs.correct(synd, pout, pin, &nerrs); + if (sch->debug) + { + if (!corrupted) + fprintf(stderr, "."); // Errors were corrected. + else + fprintf(stderr, "!"); // Packet still corrupted. + } + } - in.read(1); - - // Output corrupted packets (with a special mark) - // otherwise the derandomizer will lose synchronization. - if ( corrupted ) pout[0] ^= MPEG_SYNC_CORRUPTED; - out.written(1); + in.read(1); - } - if ( nbits ) { - if ( bitcount ) bitcount->write(nbits); - if ( errcount ) errcount->write(nerrs); - } + // Output corrupted packets (with a special mark) + // otherwise the derandomizer will lose synchronization. + if (corrupted) + pout[0] ^= MPEG_SYNC_CORRUPTED; + out.written(1); + + } + if (nbits) + { + if (bitcount) + bitcount->write(nbits); + if (errcount) + errcount->write(nerrs); + } } - private: - pipereader< rspacket > in; +private: + pipereader > in; pipewriter out; pipewriter *bitcount, *errcount; - }; // rs_decoder +}; +// rs_decoder +// RANDOMIZER - // RANDOMIZER - - struct randomizer : runnable { - randomizer(scheduler *sch, - pipebuf &_in, pipebuf &_out) - : runnable(sch, "derandomizer"), - in(_in), out(_out) { - precompute_pattern(); - pos = pattern; - pattern_end = pattern + sizeof(pattern)/sizeof(pattern[0]); +struct randomizer: runnable +{ + randomizer(scheduler *sch, pipebuf &_in, pipebuf &_out) : + runnable(sch, "derandomizer"), in(_in), out(_out) + { + precompute_pattern(); + pos = pattern; + pattern_end = pattern + sizeof(pattern) / sizeof(pattern[0]); } - void precompute_pattern() { - // EN 300 421, section 4.4.1 Transport multiplex adaptation - pattern[0] = 0xff; // Invert one in eight sync bytes - unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) - for ( int i=1; i<188*8; ++i ) { - u8 out = 0; - for ( int n=8; n--; ) { - int bit = ((st>>13) ^ (st>>14)) & 1; // Taps - out = (out<<1) | bit; // MSB first - st = (st<<1) | bit; // Feedback - } - pattern[i] = (i%188) ? out : 0; // Inhibit on sync bytes - } + void precompute_pattern() + { + // EN 300 421, section 4.4.1 Transport multiplex adaptation + pattern[0] = 0xff; // Invert one in eight sync bytes + unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) + for (int i = 1; i < 188 * 8; ++i) + { + u8 out = 0; + for (int n = 8; n--;) + { + int bit = ((st >> 13) ^ (st >> 14)) & 1; // Taps + out = (out << 1) | bit; // MSB first + st = (st << 1) | bit; // Feedback + } + pattern[i] = (i % 188) ? out : 0; // Inhibit on sync bytes + } } - void run() { - while ( in.readable()>=1 && out.writable()>=1 ) { - u8 *pin = in.rd()->data, *pend = pin+SIZE_TSPACKET; - u8 *pout= out.wr()->data; - if ( pin[0] != MPEG_SYNC ) - fprintf(stderr, "randomizer: bad MPEG sync %02x\n", pin[0]); - for ( ; pin= 1 && out.writable() >= 1) + { + u8 *pin = in.rd()->data, *pend = pin + SIZE_TSPACKET; + u8 *pout = out.wr()->data; + if (pin[0] != MPEG_SYNC) + fprintf(stderr, "randomizer: bad MPEG sync %02x\n", pin[0]); + for (; pin < pend; ++pin, ++pout, ++pos) + *pout = *pin ^ *pos; + if (pos == pattern_end) + pos = pattern; + in.read(1); + out.written(1); + } } - private: - u8 pattern[188*8], *pattern_end, *pos; +private: + u8 pattern[188 * 8], *pattern_end, *pos; pipereader in; pipewriter out; - }; // randomizer +}; +// randomizer +// DERANDOMIZER - // DERANDOMIZER - - struct derandomizer : runnable { - derandomizer(scheduler *sch, - pipebuf &_in, pipebuf &_out) - : runnable(sch, "derandomizer"), - in(_in), out(_out) { - precompute_pattern(); - pos = pattern; - pattern_end = pattern + sizeof(pattern)/sizeof(pattern[0]); +struct derandomizer: runnable +{ + derandomizer(scheduler *sch, pipebuf &_in, + pipebuf &_out) : + runnable(sch, "derandomizer"), in(_in), out(_out) + { + precompute_pattern(); + pos = pattern; + pattern_end = pattern + sizeof(pattern) / sizeof(pattern[0]); } - void precompute_pattern() { - // EN 300 421, section 4.4.1 Transport multiplex adaptation - pattern[0] = 0xff; // Restore the inverted sync byte - unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) - for ( int i=1; i<188*8; ++i ) { - u8 out = 0; - for ( int n=8; n--; ) { - int bit = ((st>>13) ^ (st>>14)) & 1; // Taps - out = (out<<1) | bit; // MSB first - st = (st<<1) | bit; // Feedback - } - pattern[i] = (i%188) ? out : 0; // Inhibit on sync bytes - } + void precompute_pattern() + { + // EN 300 421, section 4.4.1 Transport multiplex adaptation + pattern[0] = 0xff; // Restore the inverted sync byte + unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) + for (int i = 1; i < 188 * 8; ++i) + { + u8 out = 0; + for (int n = 8; n--;) + { + int bit = ((st >> 13) ^ (st >> 14)) & 1; // Taps + out = (out << 1) | bit; // MSB first + st = (st << 1) | bit; // Feedback + } + pattern[i] = (i % 188) ? out : 0; // Inhibit on sync bytes + } } - void run() { - while ( in.readable()>=1 && out.writable()>=1 ) { - u8 *pin = in.rd()->data, *pend = pin+SIZE_TSPACKET; - u8 *pout= out.wr()->data; - if ( pin[0] == MPEG_SYNC_INV || - pin[0] == (MPEG_SYNC_INV^MPEG_SYNC_CORRUPTED) ) { - if ( pos != pattern ) { - if ( sch->debug ) - fprintf(stderr, "derandomizer: resynchronizing\n"); - pos = pattern; - } - } - for ( ; pin= 1 && out.writable() >= 1) + { + u8 *pin = in.rd()->data, *pend = pin + SIZE_TSPACKET; + u8 *pout = out.wr()->data; + if (pin[0] == MPEG_SYNC_INV + || pin[0] == (MPEG_SYNC_INV ^ MPEG_SYNC_CORRUPTED)) + { + if (pos != pattern) + { + if (sch->debug) + fprintf(stderr, "derandomizer: resynchronizing\n"); + pos = pattern; + } + } + for (; pin < pend; ++pin, ++pout, ++pos) + *pout = *pin ^ *pos; + if (pos == pattern_end) + pos = pattern; + in.read(1); - u8 sync = out.wr()->data[0]; - if ( sync == MPEG_SYNC ) { - out.written(1); - } else { - if ( sync != (MPEG_SYNC^MPEG_SYNC_CORRUPTED) ) - if ( sch->debug ) fprintf(stderr, "(%02x)", sync); - out.wr()->data[1] |= 0x80; // Set the Transport Error Indicator bit - // We could output corrupted packets here, in case the - // MPEG decoder can use them somehow. - //out.written(1); - } - } + u8 sync = out.wr()->data[0]; + if (sync == MPEG_SYNC) + { + out.written(1); + } + else + { + if (sync != (MPEG_SYNC ^ MPEG_SYNC_CORRUPTED)) + if (sch->debug) + fprintf(stderr, "(%02x)", sync); + out.wr()->data[1] |= 0x80; // Set the Transport Error Indicator bit + // We could output corrupted packets here, in case the + // MPEG decoder can use them somehow. + //out.written(1); + } + } } - private: - u8 pattern[188*8], *pattern_end, *pos; +private: + u8 pattern[188 * 8], *pattern_end, *pos; pipereader in; pipewriter out; - }; // derandomizer +}; +// derandomizer +// VITERBI DECODING +// Supports all code rates and constellations +// Simplified metric to support large constellations. - // VITERBI DECODING - // Supports all code rates and constellations - // Simplified metric to support large constellations. +// This version implements puncturing by expanding the trellis. +// TBD Compare performance vs skipping updates in a 1/2 trellis. - // This version implements puncturing by expanding the trellis. - // TBD Compare performance vs skipping updates in a 1/2 trellis. - - struct viterbi_sync : runnable { +struct viterbi_sync: runnable +{ typedef uint8_t TS, TCS, TUS; typedef int32_t TBM; // Only 16 bits per IQ, but several IQ per Viterbi CS typedef int32_t TPM; - typedef viterbi_dec_interface dvb_dec_interface; + typedef viterbi_dec_interface dvb_dec_interface; // 1/2: 6 bits of state, 1 bit in, 2 bits out - typedef bitpath path_12; - typedef trellis trellis_12; - typedef viterbi_dec dvb_dec_12; + typedef bitpath path_12; + typedef trellis trellis_12; + typedef viterbi_dec dvb_dec_12; // 2/3: 6 bits of state, 2 bits in, 3 bits out - typedef bitpath path_23; - typedef trellis trellis_23; - typedef viterbi_dec dvb_dec_23; + typedef bitpath path_23; + typedef trellis trellis_23; + typedef viterbi_dec dvb_dec_23; // 4/6: 6 bits of state, 4 bits in, 6 bits out - typedef bitpath path_46; - typedef trellis trellis_46; - typedef viterbi_dec dvb_dec_46; + typedef bitpath path_46; + typedef trellis trellis_46; + typedef viterbi_dec dvb_dec_46; // 3/4: 6 bits of state, 3 bits in, 4 bits out - typedef bitpath path_34; - typedef trellis trellis_34; - typedef viterbi_dec dvb_dec_34; + typedef bitpath path_34; + typedef trellis trellis_34; + typedef viterbi_dec dvb_dec_34; // 4/5: 6 bits of state, 4 bits in, 5 bits out (non-standard) - typedef bitpath path_45; - typedef trellis trellis_45; - typedef viterbi_dec dvb_dec_45; + typedef bitpath path_45; + typedef trellis trellis_45; + typedef viterbi_dec dvb_dec_45; // 5/6: 6 bits of state, 5 bits in, 6 bits out - typedef bitpath path_56; - typedef trellis trellis_56; - typedef viterbi_dec dvb_dec_56; + typedef bitpath path_56; + typedef trellis trellis_56; + typedef viterbi_dec dvb_dec_56; // QPSK 7/8: 6 bits of state, 7 bits in, 8 bits out - typedef bitpath path_78; - typedef trellis trellis_78; - typedef viterbi_dec dvb_dec_78; + typedef bitpath path_78; + typedef trellis trellis_78; + typedef viterbi_dec dvb_dec_78; - private: +private: pipereader in; pipewriter out; cstln_lut<256> *cstln; @@ -1202,204 +1426,243 @@ namespace leansdr { int bits_per_symbol; // Bits per IQ symbol (not per coded symbol) int nsyncs; int nshifts; - struct sync { - int shift; - dvb_dec_interface *dec; - TCS *map; // [nsymbols] - } *syncs; // [nsyncs] + struct sync + { + int shift; + dvb_dec_interface *dec; + TCS *map; // [nsymbols] + }*syncs; // [nsyncs] int current_sync; static const int chunk_size = 128; int resync_phase; - public: +public: int resync_period; - viterbi_sync(scheduler *sch, - pipebuf &_in, pipebuf &_out, - cstln_lut<256> *_cstln, code_rate cr) - : runnable(sch, "viterbi_sync"), - in(_in), out(_out, chunk_size), - cstln(_cstln), - current_sync(0), - resync_phase(0), - resync_period(32) // 1/32 = 9% synchronization overhead TBD + viterbi_sync(scheduler *sch, pipebuf &_in, + pipebuf &_out, cstln_lut<256> *_cstln, code_rate cr) : + runnable(sch, "viterbi_sync"), in(_in), out(_out, chunk_size), cstln( + _cstln), current_sync(0), resync_phase(0), resync_period(32) // 1/32 = 9% synchronization overhead TBD { - bits_per_symbol = log2i(cstln->nsymbols); - fec = &fec_specs[cr]; - { // Sanity check: FEC block size must be a multiple of label size. - int symbols_per_block = fec->bits_out / bits_per_symbol; - if ( bits_per_symbol*symbols_per_block != fec->bits_out ) - fail("Code rate not suitable for this constellation"); - } - int nconj; - switch ( cstln->nsymbols ) { - case 2: nconj = 1; break; // Conjugation is not relevant for BPSK - default: nconj = 2; break; - } + bits_per_symbol = log2i(cstln->nsymbols); + fec = &fec_specs[cr]; + { // Sanity check: FEC block size must be a multiple of label size. + int symbols_per_block = fec->bits_out / bits_per_symbol; + if (bits_per_symbol * symbols_per_block != fec->bits_out) + fail("Code rate not suitable for this constellation"); + } + int nconj; + switch (cstln->nsymbols) + { + case 2: + nconj = 1; + break; // Conjugation is not relevant for BPSK + default: + nconj = 2; + break; + } - int nrotations; - switch ( cstln->nsymbols ) { - case 2: - case 4: - // For BPSK and QPSK, 180° rotation is handled as - // polarity inversion in mpeg_sync. - nrotations = cstln->nrotations/2; - break; - default: - nrotations = cstln->nrotations; - break; - } - nshifts = fec->bits_out / bits_per_symbol; - nsyncs = nconj * nrotations * nshifts; + int nrotations; + switch (cstln->nsymbols) + { + case 2: + case 4: + // For BPSK and QPSK, 180° rotation is handled as + // polarity inversion in mpeg_sync. + nrotations = cstln->nrotations / 2; + break; + default: + nrotations = cstln->nrotations; + break; + } + nshifts = fec->bits_out / bits_per_symbol; + nsyncs = nconj * nrotations * nshifts; - // TBD Many HOM constellations are labelled in such a way - // that certain rot/conj combinations are equivalent to - // polarity inversion. We could reduce nsyncs. + // TBD Many HOM constellations are labelled in such a way + // that certain rot/conj combinations are equivalent to + // polarity inversion. We could reduce nsyncs. - syncs = new sync[nsyncs]; + syncs = new sync[nsyncs]; - for ( int s=0; snrotations); + for (int s = 0; s < nsyncs; ++s) + { + // Bit pattern [shift|conj|rot] + int rot = s % nrotations; + int conj = (s / nrotations) % nconj; + int shift = s / nrotations / nconj; + syncs[s].shift = shift; + if (shift) // Reuse identical map + syncs[s].map = syncs[conj * nrotations + rot].map; + else + syncs[s].map = init_map(conj, + 2 * M_PI * rot / cstln->nrotations); #if 0 - fprintf(stderr, "sync %3d: conj%d offs%d rot%d/%d map:", - s, conj, syncs[s].shift, rot, cstln->nrotations); - for ( int i=0; insymbols; ++i ) - fprintf(stderr, " %2d", syncs[s].map[i]); - fprintf(stderr, "\n"); + fprintf(stderr, "sync %3d: conj%d offs%d rot%d/%d map:", + s, conj, syncs[s].shift, rot, cstln->nrotations); + for ( int i=0; insymbols; ++i ) + fprintf(stderr, " %2d", syncs[s].map[i]); + fprintf(stderr, "\n"); #endif - } + } - if ( cr == FEC12 ) { - trellis_12 *trell = new trellis_12(); - trell->init_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); - for ( int s=0; sinit_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_12(trell); + } + else if (cr == FEC23) + { + trellis_23 *trell = new trellis_23(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_23(trell); + } + else if (cr == FEC46) + { + trellis_46 *trell = new trellis_46(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_46(trell); + } + else if (cr == FEC34) + { + trellis_34 *trell = new trellis_34(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_34(trell); + } + else if (cr == FEC45) + { + trellis_45 *trell = new trellis_45(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_45(trell); + } + else if (cr == FEC56) + { + trellis_56 *trell = new trellis_56(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_56(trell); + } + else if (cr == FEC78) + { + trellis_78 *trell = new trellis_78(); + trell->init_convolutional(fec->polys); + for (int s = 0; s < nsyncs; ++s) + syncs[s].dec = new dvb_dec_78(trell); + } + else + fail("CR not supported"); } - TCS *init_map(bool conj, float angle) { - // Each constellation has its own pattern for labels. - // Here we simply tabulate systematically. - TCS *map = new TCS[cstln->nsymbols]; - float ca=cosf(angle), sa=sinf(angle); - for ( int i=0; insymbols; ++i ) { - int8_t I = cstln->symbols[i].re; - int8_t Q = cstln->symbols[i].im; - if ( conj ) Q = -Q; - int8_t RI = I*ca - Q*sa; - int8_t RQ = I*sa + Q*ca; - cstln_lut<256>::result *pr = cstln->lookup(RI, RQ); - map[i] = pr->ss.symbol; - } - return map; + TCS *init_map(bool conj, float angle) + { + // Each constellation has its own pattern for labels. + // Here we simply tabulate systematically. + TCS *map = new TCS[cstln->nsymbols]; + float ca = cosf(angle), sa = sinf(angle); + for (int i = 0; i < cstln->nsymbols; ++i) + { + int8_t I = cstln->symbols[i].re; + int8_t Q = cstln->symbols[i].im; + if (conj) + Q = -Q; + int8_t RI = I * ca - Q * sa; + int8_t RQ = I * sa + Q * ca; + cstln_lut<256>::result *pr = cstln->lookup(RI, RQ); + map[i] = pr->ss.symbol; + } + return map; } - inline TUS update_sync(int s, softsymbol *pin, TPM *discr) { - // Read one FEC ouput block - pin += syncs[s].shift; - TCS cs = 0; - TBM cost = 0; - for ( int i=0; isymbol]; - cost += pin->cost; - } - return syncs[s].dec->update(cs, cost, discr); + inline TUS update_sync(int s, softsymbol *pin, TPM *discr) + { + // Read one FEC ouput block + pin += syncs[s].shift; + TCS cs = 0; + TBM cost = 0; + for (int i = 0; i < nshifts; ++i, ++pin) + { + cs = (cs << bits_per_symbol) | syncs[s].map[pin->symbol]; + cost += pin->cost; + } + return syncs[s].dec->update(cs, cost, discr); } - void run() { - // Number of FEC blocks to fill the bitpath depth. - // Before that we cannot discriminate between synchronizers - int discr_delay = 64 / fec->bits_in; + void run() + { + // Number of FEC blocks to fill the bitpath depth. + // Before that we cannot discriminate between synchronizers + int discr_delay = 64 / fec->bits_in; - // Process [chunk_size] FEC blocks at a time + // Process [chunk_size] FEC blocks at a time - while ( in.readable() >= nshifts*chunk_size + (nshifts-1) && - out.writable()*8 >= fec->bits_in*chunk_size ) { - TPM totaldiscr[nsyncs]; - for ( int s=0; s= nshifts * chunk_size + (nshifts - 1) + && ((long) out.writable() * 8) >= fec->bits_in * chunk_size) + { + TPM totaldiscr[nsyncs]; + for (int s = 0; s < nsyncs; ++s) + totaldiscr[s] = 0; - uint64_t outstream = 0; - int nout = 0; - softsymbol *pin = in.rd(); - for ( int blocknum=0; blocknumbits_in) | result; - nout += fec->bits_in; - if ( blocknum >= discr_delay ) totaldiscr[current_sync] += discr; - if ( ! resync_phase ) { - // Every [resync_period] chunks, also run the other decoders. - for ( int s=0; s= discr_delay ) totaldiscr[s] += discr; - } - } - while ( nout >= 8 ) { - out.write(outstream>>(nout-8)); - nout -= 8; - } - } // chunk_size - in.read(chunk_size*nshifts); - if ( nout ) fail("overlapping out"); - if ( ! resync_phase ) { - // Switch to another decoder ? - int best = current_sync; - for ( int s=0; s totaldiscr[best] ) best = s; - if ( best != current_sync ) { - if ( sch->debug ) fprintf(stderr, "{%d->%d}", current_sync, best); - current_sync = best; - } - } - if ( ++resync_phase >= resync_period ) resync_phase = 0; - } + uint64_t outstream = 0; + int nout = 0; + softsymbol *pin = in.rd(); + for (int blocknum = 0; blocknum < chunk_size; ++blocknum, pin += + nshifts) + { + TPM discr; + TUS result = update_sync(current_sync, pin, &discr); + outstream = (outstream << fec->bits_in) | result; + nout += fec->bits_in; + if (blocknum >= discr_delay) + totaldiscr[current_sync] += discr; + if (!resync_phase) + { + // Every [resync_period] chunks, also run the other decoders. + for (int s = 0; s < nsyncs; ++s) + { + if (s == current_sync) + continue; + TPM discr; + (void) update_sync(s, pin, &discr); + if (blocknum >= discr_delay) + totaldiscr[s] += discr; + } + } + while (nout >= 8) + { + out.write(outstream >> (nout - 8)); + nout -= 8; + } + } // chunk_size + in.read(chunk_size * nshifts); + if (nout) + fail("overlapping out"); + if (!resync_phase) + { + // Switch to another decoder ? + int best = current_sync; + for (int s = 0; s < nsyncs; ++s) + if (totaldiscr[s] > totaldiscr[best]) + best = s; + if (best != current_sync) + { + if (sch->debug) + fprintf(stderr, "{%d->%d}", current_sync, best); + current_sync = best; + } + } + if (++resync_phase >= resync_period) + resync_phase = 0; + } } - }; // viterbi_sync +}; +// viterbi_sync - - -} // namespace +}// namespace #endif // LEANSDR_DVB_H diff --git a/plugins/channelrx/demoddatv/leansdr/framework.h b/plugins/channelrx/demoddatv/leansdr/framework.h index 449c4864c..07e929b49 100644 --- a/plugins/channelrx/demoddatv/leansdr/framework.h +++ b/plugins/channelrx/demoddatv/leansdr/framework.h @@ -1,54 +1,104 @@ #ifndef LEANSDR_FRAMEWORK_H #define LEANSDR_FRAMEWORK_H +#include #include #include #include #include -namespace leansdr { - - inline void fatal(const char *s) { perror(s); exit(1); } - inline void fail(const char *s) { fprintf(stderr, "** %s\n", s); exit(1); } - - ////////////////////////////////////////////////////////////////////// - // DSP framework - ////////////////////////////////////////////////////////////////////// - - // [pipebuf] is a FIFO buffer with multiple readers. - // [pipewriter] is a client-side hook for writing into a [pipebuf]. - // [pipereader] is a client-side hook reading from a [pipebuf]. - // [runnable] is anything that moves data between [pipebufs]. - // [scheduler] is a global context which invokes [runnables] until fixpoint. - - static const int MAX_PIPES = 64; - static const int MAX_RUNNABLES = 64; - static const int MAX_READERS = 8; - - struct pipebuf_common { - virtual int sizeofT() { return 0; } - virtual long long hash() { return 0; } - virtual void dump(size_t *total_bufs) { } - const char *name; - pipebuf_common(const char *_name) : name(_name) { } - }; +namespace leansdr +{ + +inline void fatal(const char *s) +{ + perror(s); + exit(1); +} + +inline void fail(const char *s) +{ + fprintf(stderr, "leansdr::fail: %s\n", s); + exit(1); +} + +////////////////////////////////////////////////////////////////////// +// DSP framework +////////////////////////////////////////////////////////////////////// + +// [pipebuf] is a FIFO buffer with multiple readers. +// [pipewriter] is a client-side hook for writing into a [pipebuf]. +// [pipereader] is a client-side hook reading from a [pipebuf]. +// [runnable] is anything that moves data between [pipebufs]. +// [scheduler] is a global context which invokes [runnables] until fixpoint. + +static const int MAX_PIPES = 64; +static const int MAX_RUNNABLES = 64; +static const int MAX_READERS = 8; + +struct pipebuf_common +{ + virtual int sizeofT() + { + return 0; + } + + virtual long long hash() + { + return 0; + } + + virtual void dump(std::size_t *total_bufs __attribute__((unused))) + { + } + + pipebuf_common(const char *_name) : + name(_name) + { + } + + virtual ~pipebuf_common() + { + } - struct runnable_common { const char *name; - runnable_common(const char *_name) : name(_name) { } - virtual void run() { } - virtual void shutdown() { } +}; + +struct runnable_common +{ + runnable_common(const char *_name) : + name(_name) + { + } + + virtual ~runnable_common() + { + } + + virtual void run() + { + } + + virtual void shutdown() + { + } + #ifdef DEBUG - ~runnable_common() { fprintf(stderr, "Deallocating %s !\n", name); } + ~runnable_common() + { fprintf(stderr, "Deallocating %s !\n", name);} #endif - }; - - struct window_placement { + + const char *name; +}; + +struct window_placement +{ const char *name; // NULL to terminate int x, y, w, h; - }; +}; - struct scheduler { +struct scheduler +{ pipebuf_common *pipes[MAX_PIPES]; int npipes; runnable_common *runnables[MAX_RUNNABLES]; @@ -56,213 +106,362 @@ namespace leansdr { window_placement *windows; bool verbose, debug; - scheduler() - : npipes(0), nrunnables(0), windows(NULL), - verbose(false), debug(false) { + scheduler() : + npipes(0), nrunnables(0), windows(NULL), verbose(false), debug( + false) + { } - void add_pipe(pipebuf_common *p) { - if ( npipes == MAX_PIPES ) fail("MAX_PIPES"); - pipes[npipes++] = p; + + void add_pipe(pipebuf_common *p) + { + if (npipes == MAX_PIPES) { + fail("MAX_PIPES"); + } + pipes[npipes++] = p; } - void add_runnable(runnable_common *r) { - if ( nrunnables == MAX_RUNNABLES ) fail("MAX_RUNNABLES"); - runnables[nrunnables++] = r; + + void add_runnable(runnable_common *r) + { + if (nrunnables == MAX_RUNNABLES) { + fail("MAX_RUNNABLES"); + } + runnables[nrunnables++] = r; } - void step() { - for ( int i=0; irun(); + + void step() + { + for (int i = 0; i < nrunnables; ++i) { + runnables[i]->run(); + } } - void run() { - unsigned long long prev_hash = 0; - while ( 1 ) { - step(); - unsigned long long h = hash(); - if ( h == prev_hash ) break; - prev_hash = h; - } + + void run() + { + unsigned long long prev_hash = 0; + + while (1) + { + step(); + unsigned long long h = hash(); + if (h == prev_hash) { + break; + } + prev_hash = h; + } } - void shutdown() { - for ( int i=0; ishutdown(); + + void shutdown() + { + for (int i = 0; i < nrunnables; ++i) { + runnables[i]->shutdown(); + } } - unsigned long long hash() { - unsigned long long h = 0; - for ( int i=0; ihash(); - return h; + + unsigned long long hash() + { + unsigned long long h = 0; + for (int i = 0; i < npipes; ++i) { + h += (1 + i) * pipes[i]->hash(); + } + return h; } - - void dump() { - fprintf(stderr, "\n"); - size_t total_bufs = 0; - for ( int i=0; idump(&total_bufs); - fprintf(stderr, "Total buffer memory: %ld KiB\n", - (unsigned long)total_bufs/1024); + + void dump() + { + fprintf(stderr, "\n"); + std::size_t total_bufs = 0; + for (int i = 0; i < npipes; ++i) { + pipes[i]->dump(&total_bufs); + } + fprintf(stderr, "leansdr::scheduler::dump Total buffer memory: %ld KiB\n", (unsigned long) total_bufs / 1024); } - }; - - struct runnable : runnable_common { - runnable(scheduler *_sch, const char *name) - : runnable_common(name), sch(_sch) { - sch->add_runnable(this); +}; + +struct runnable: runnable_common +{ + runnable(scheduler *_sch, const char *name) : + runnable_common(name), sch(_sch) + { + sch->add_runnable(this); } - protected: +protected: scheduler *sch; - }; - - template - struct pipebuf : pipebuf_common { +}; + +template +struct pipebuf: pipebuf_common +{ T *buf; T *rds[MAX_READERS]; int nrd; T *wr; T *end; - int sizeofT() { return sizeof(T); } - pipebuf(scheduler *sch, const char *name, unsigned long size) - : pipebuf_common(name), - buf(new T[size]), nrd(0), wr(buf), end(buf+size), - min_write(1), - total_written(0), total_read(0) { - sch->add_pipe(this); + + int sizeofT() + { + return sizeof(T); } - int add_reader() { - if ( nrd == MAX_READERS ) fail("too many readers"); - rds[nrd] = wr; - return nrd++; + + pipebuf(scheduler *sch, const char *name, unsigned long size) : + pipebuf_common(name), buf(new T[size]), nrd(0), wr(buf), end( + buf + size), min_write(1), total_written(0), total_read(0) + { + sch->add_pipe(this); } - void pack() { - T *rd = wr; - for ( int i=0; i - struct pipewriter { +}; + +template +struct pipewriter +{ pipebuf &buf; - pipewriter(pipebuf &_buf, unsigned long min_write=1) - : buf(_buf) { - if ( min_write > buf.min_write ) buf.min_write = min_write; - } - // Return number of items writable at this->wr, 0 if full. - unsigned long writable() { - if ( buf.end-buf.wr < buf.min_write ) buf.pack(); - return buf.end - buf.wr; - } - T *wr() { return buf.wr; } - void written(unsigned long n) { - if ( buf.wr+n > buf.end ) { - fprintf(stderr, "Bug: overflow to %s\n", buf.name); - exit(1); - } - buf.wr += n; - buf.total_written += n; - } - void write(const T &e) { - *wr() = e; - written(1); - } - }; - // Convenience functions for working with optional pipes + pipewriter(pipebuf &_buf, unsigned long min_write = 1) : + buf(_buf) + { + if (min_write > buf.min_write) { + buf.min_write = min_write; + } + } - template - pipewriter *opt_writer(pipebuf *buf) { + /** Return number of items writable at this->wr, 0 if full. */ + unsigned long writable() + { + if (buf.end < buf.wr) + { + fprintf(stderr, "leansdr::pipewriter::writable: Bug: overflow to %s\n", buf.name); + exit(1); + } + + unsigned long delta = buf.end - buf.wr; + + if (delta < buf.min_write) { + buf.pack(); + } + + return delta; + } + + T *wr() + { + return buf.wr; + } + + void written(unsigned long n) + { + if (buf.wr + n > buf.end) + { + fprintf(stderr, "leansdr::pipewriter::written: Bug: overflow to %s\n", buf.name); + exit(1); + } + buf.wr += n; + buf.total_written += n; + } + + void write(const T &e) + { + *wr() = e; + written(1); + } +}; + +// Convenience functions for working with optional pipes + +template +pipewriter *opt_writer(pipebuf *buf) +{ return buf ? new pipewriter(*buf) : NULL; - } +} - template - bool opt_writable(pipewriter *p, int n=1) { - return (p==NULL) || p->writable()>=n; - } +template +bool opt_writable(pipewriter *p, unsigned int n = 1) +{ + return (p == NULL) || p->writable() >= n; +} - template - void opt_write(pipewriter *p, T val) { - if ( p ) p->write(val); - } +template +void opt_write(pipewriter *p, T val) +{ + if (p) { + p->write(val); + } +} - template - struct pipereader { +template +struct pipereader +{ pipebuf &buf; int id; - pipereader(pipebuf &_buf) : buf(_buf), id(_buf.add_reader()) { } - unsigned long readable() { return buf.wr - buf.rds[id]; } - T *rd() { return buf.rds[id]; } - void read(unsigned long n) { - if ( buf.rds[id]+n > buf.wr ) { - fprintf(stderr, "Bug: underflow from %s\n", buf.name); - exit(1); - } - buf.rds[id] += n; - buf.total_read += n; + + pipereader(pipebuf &_buf) : + buf(_buf), id(_buf.add_reader()) + { } - }; - - // Math functions for templates - - template T gen_sqrt(T x); - inline float gen_sqrt(float x) { return sqrtf(x); } - inline unsigned int gen_sqrt(unsigned int x) { return sqrtl(x); } - inline long double gen_sqrt(long double x) { return sqrtl(x); } - template T gen_abs(T x); - inline float gen_abs(float x) { return fabsf(x); } - inline int gen_abs(int x) { return abs(x); } - inline long int gen_abs(long int x) { return labs(x); } + unsigned long readable() + { + return buf.wr - buf.rds[id]; + } - template T gen_hypot(T x, T y); - inline float gen_hypot(float x, float y) { return hypotf(x,y); } - inline long double gen_hypot(long double x, long double y) - { return hypotl(x,y); } + T *rd() + { + return buf.rds[id]; + } - template T gen_atan2(T y, T x); - inline float gen_atan2(float y, float x) { return atan2f(y,x); } - inline long double gen_atan2(long double y, long double x) - { return atan2l(y,x); } + void read(unsigned long n) + { + if (buf.rds[id] + n > buf.wr) + { + fprintf(stderr, "leansdr::pipereader::read: Bug: underflow from %s\n", buf.name); + exit(1); + } + buf.rds[id] += n; + buf.total_read += n; + } +}; - template - T min(const T &x, const T &y) { return (x - T max(const T &x, const T &y) { return (x T gen_sqrt(T x); +inline float gen_sqrt(float x) +{ + return sqrtf(x); +} - // Abreviations for integer types +inline unsigned int gen_sqrt(unsigned int x) +{ + return sqrtl(x); +} - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned long u32; - typedef signed char s8; - typedef signed short s16; - typedef signed long s32; +inline long double gen_sqrt(long double x) +{ + return sqrtl(x); +} + +template T gen_abs(T x); +inline float gen_abs(float x) +{ + return fabsf(x); +} + +inline int gen_abs(int x) +{ + return abs(x); +} + +inline long int gen_abs(long int x) +{ + return labs(x); +} + +template T gen_hypot(T x, T y); +inline float gen_hypot(float x, float y) +{ + return hypotf(x, y); +} + +inline long double gen_hypot(long double x, long double y) +{ + return hypotl(x, y); +} + +template T gen_atan2(T y, T x); +inline float gen_atan2(float y, float x) +{ + return atan2f(y, x); +} + +inline long double gen_atan2(long double y, long double x) +{ + return atan2l(y, x); +} + +template +T min(const T &x, const T &y) +{ + return (x < y) ? x : y; +} + +template +T max(const T &x, const T &y) +{ + return (x < y) ? y : x; +} + +// Abreviations for integer types + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef signed char s8; +typedef signed short s16; +typedef signed long s32; } // namespace diff --git a/plugins/channelrx/demoddatv/leansdr/hdlc.h b/plugins/channelrx/demoddatv/leansdr/hdlc.h index efc24c685..7c8dc9f66 100644 --- a/plugins/channelrx/demoddatv/leansdr/hdlc.h +++ b/plugins/channelrx/demoddatv/leansdr/hdlc.h @@ -3,107 +3,144 @@ #include "leansdr/framework.h" -namespace leansdr { +namespace leansdr +{ - // HDLC deframer - - struct hdlc_dec { +// HDLC deframer + +struct hdlc_dec +{ hdlc_dec(int _minframesize, // Including CRC, excluding HDLC flags. - int _maxframesize, - bool _invert) - : minframesize(_minframesize), maxframesize(_maxframesize), - invertmask(_invert?0xff:0), - framebuf(new u8[maxframesize]), - debug(false) + int _maxframesize, bool _invert) : + minframesize(_minframesize), maxframesize(_maxframesize), invertmask( + _invert ? 0xff : 0), framebuf(new u8[maxframesize]), debug( + false) { - reset(); + reset(); + } + + void reset() + { + shiftreg = 0; + inframe = false; + } + + void begin_frame() + { + framesize = 0; + crc16 = crc16_init; } - - void reset() { shiftreg=0; inframe=false; } - void begin_frame() { framesize=0; crc16=crc16_init; } - // Decode (*ppin)[count] as MSB-packed HDLC bitstream. // Return pointer to buffer[*pdatasize], or NULL if no valid frame. // Return number of discarded bytes in *discarded. // Return number of checksum errors in *fcs_errors. // *ppin will have increased by at least 1 (unless count==0). - u8 *decode(u8 **ppin, int count, - int *pdatasize, int *hdlc_errors, int *fcs_errors) { - *hdlc_errors = 0; - *fcs_errors = 0; - *pdatasize = -1; - u8 *pin=*ppin, *pend=pin+count; - for ( ; pin>1) | bit_in; - if ( ! inframe ) { - if ( shiftreg == 0x7e ) { // HDLC flag 01111110 - inframe = true; - nbits_out = 0; - begin_frame(); - } - } else { - if ( (shiftreg&0xfe) == 0x7c ) { // 0111110x HDLC stuffing - // Unstuff this 0 - } else if ( shiftreg == 0x7e ) { // 01111110 HDLC flag - if ( nbits_out != 7 ) { - // Not at byte boundary - if ( debug ) fprintf(stderr, "^"); - ++*hdlc_errors; - } else { - // Checksum - crc16 ^= 0xffff; - if ( framesize<2 || framesize= minframesize ) ++*fcs_errors; - } else { - if ( debug ) fprintf(stderr, "_"); - // This will trigger output, but we finish the byte first. - *pdatasize = framesize-2; - } - } - nbits_out = 0; - begin_frame(); - // Keep processing up to 7 remaining bits from byte_in. - // Special cases 0111111 and 1111111 cannot affect *pdatasize. - } else if ( shiftreg == 0xfe ) { // 11111110 HDLC invalid - if ( framesize ) { - if ( debug ) fprintf(stderr, "^"); - ++*hdlc_errors; - } - inframe = false; - } else { // Data bit - byte_out = (byte_out>>1) | bit_in; // HDLC is LSB first - ++nbits_out; - if ( nbits_out == 8 ) { - if ( framesize < maxframesize ) { - framebuf[framesize++] = byte_out; - crc16_byte(byte_out); - } - nbits_out = 0; - } - } - } // inframe - } // bits - if ( *pdatasize != -1 ) { - // Found a complete frame - *ppin = pin+1; - return framebuf; - } - } - *ppin = pin; - return NULL; + u8 *decode(u8 **ppin, int count, int *pdatasize, int *hdlc_errors, + int *fcs_errors) + { + *hdlc_errors = 0; + *fcs_errors = 0; + *pdatasize = -1; + u8 *pin = *ppin, *pend = pin + count; + for (; pin < pend; ++pin) + { + u8 byte_in = (*pin) ^ invertmask; + for (int bits = 8; bits--; byte_in <<= 1) + { + u8 bit_in = byte_in & 128; + shiftreg = (shiftreg >> 1) | bit_in; + if (!inframe) + { + if (shiftreg == 0x7e) + { // HDLC flag 01111110 + inframe = true; + nbits_out = 0; + begin_frame(); + } + } + else + { + if ((shiftreg & 0xfe) == 0x7c) + { // 0111110x HDLC stuffing + // Unstuff this 0 + } + else if (shiftreg == 0x7e) + { // 01111110 HDLC flag + if (nbits_out != 7) + { + // Not at byte boundary + if (debug) + fprintf(stderr, "^"); + ++*hdlc_errors; + } + else + { + // Checksum + crc16 ^= 0xffff; + if (framesize < 2 || framesize < minframesize + || crc16 != crc16_check) + { + if (debug) + fprintf(stderr, "!"); + ++*hdlc_errors; + // Do not report random noise as FCS errors + if (framesize >= minframesize) + ++*fcs_errors; + } + else + { + if (debug) + fprintf(stderr, "_"); + // This will trigger output, but we finish the byte first. + *pdatasize = framesize - 2; + } + } + nbits_out = 0; + begin_frame(); + // Keep processing up to 7 remaining bits from byte_in. + // Special cases 0111111 and 1111111 cannot affect *pdatasize. + } + else if (shiftreg == 0xfe) + { // 11111110 HDLC invalid + if (framesize) + { + if (debug) + fprintf(stderr, "^"); + ++*hdlc_errors; + } + inframe = false; + } + else + { // Data bit + byte_out = (byte_out >> 1) | bit_in; // HDLC is LSB first + ++nbits_out; + if (nbits_out == 8) + { + if (framesize < maxframesize) + { + framebuf[framesize++] = byte_out; + crc16_byte(byte_out); + } + nbits_out = 0; + } + } + } // inframe + } // bits + if (*pdatasize != -1) + { + // Found a complete frame + *ppin = pin + 1; + return framebuf; + } + } + *ppin = pin; + return NULL; } - private: +private: // Config int minframesize, maxframesize; u8 invertmask; @@ -119,151 +156,170 @@ namespace leansdr { static const u16 crc16_init = 0xffff; static const u16 crc16_poly = 0x8408; // 0x1021 MSB-first static const u16 crc16_check = 0x0f47; - void crc16_byte(u8 data) { - crc16 ^= data; - for ( int bit=8; bit--; ) - crc16 = (crc16&1) ? (crc16>>1)^crc16_poly : (crc16>>1); - } - - public: - bool debug; - }; // hdlc_dec - - - // HDLC synchronizer with polarity detection - - struct hdlc_sync : runnable { - hdlc_sync(scheduler *sch, - pipebuf &_in, // Packed bits - pipebuf &_out, // Bytes - int _minframesize, // Including CRC, excluding HDLC flags. - int _maxframesize, - // Status - pipebuf *_lock_out=NULL, - pipebuf *_framecount_out=NULL, - pipebuf *_fcserrcount_out=NULL, - pipebuf *_hdlcbytecount_out=NULL, - pipebuf *_databytecount_out=NULL) - : runnable(sch, "hdlc_sync"), - minframesize(_minframesize), - maxframesize(_maxframesize), - chunk_size(maxframesize+2), - in(_in), out(_out, _maxframesize+chunk_size), - lock_out(opt_writer(_lock_out)), - framecount_out(opt_writer(_framecount_out)), - fcserrcount_out(opt_writer(_fcserrcount_out)), - hdlcbytecount_out(opt_writer(_hdlcbytecount_out)), - databytecount_out(opt_writer(_databytecount_out)), - cur_sync(0), resync_phase(0), - lock_state(false), - resync_period(32), - header16(false) + void crc16_byte(u8 data) { - for ( int s=0; sdebug = sch->debug; - errslot = 0; + crc16 ^= data; + for (int bit = 8; bit--;) + crc16 = (crc16 & 1) ? (crc16 >> 1) ^ crc16_poly : (crc16 >> 1); } - void run() { - if ( ! opt_writable(lock_out) || - ! opt_writable(framecount_out) || - ! opt_writable(fcserrcount_out) || - ! opt_writable(hdlcbytecount_out) || - ! opt_writable(databytecount_out) ) return; +public: + bool debug; +}; +// hdlc_dec - bool previous_lock_state = lock_state; - int fcserrcount=0, framecount=0; - int hdlcbytecount=0, databytecount=0; +// HDLC synchronizer with polarity detection - // Note: hdlc_dec may already hold one frame ready for output. - while ( in.readable() >= chunk_size && - out.writable() >= maxframesize+chunk_size ) { - if ( ! resync_phase ) { - // Once every resync_phase, try all decoders - for ( int s=0; sreset(); - syncs[s].errhist[errslot] = 0; - for ( u8 *pin=in.rd(), *pend=pin+chunk_size; pindecode(&pin, pend-pin, &datasize, - &hdlc_errors, &fcs_errors); - syncs[s].errhist[errslot] += hdlc_errors; - if ( s == cur_sync ) { - if ( f ) { - lock_state = true; - output_frame(f, datasize); - databytecount += datasize; - ++framecount; - } - fcserrcount += fcs_errors; - framecount += fcs_errors; - } - } - } - errslot = (errslot+1) % NERRHIST; - // Switch to another sync option ? - // Compare total error counts over about NERRHIST frames. - int total_errors[NSYNCS]; - for ( int s=0; sdebug ) fprintf(stderr, "[%d:%d->%d:%d]", - cur_sync, total_errors[cur_sync], - best, total_errors[best]); - // No verbose messages on candidate syncs - syncs[cur_sync].dec->debug = false; - cur_sync = best; - syncs[cur_sync].dec->debug = sch->debug; - } - } else { - // Use only the currently selected decoder - for ( u8 *pin=in.rd(), *pend=pin+chunk_size; pindecode(&pin, pend-pin, &datasize, - &hdlc_errors, &fcs_errors); - if ( f ) { - lock_state = true; - output_frame(f, datasize); - databytecount += datasize; - ++framecount; - } - fcserrcount += fcs_errors; - framecount += fcs_errors; - } - } // resync_phase - in.read(chunk_size); - hdlcbytecount += chunk_size; - if ( ++resync_phase >= resync_period ) resync_phase = 0; - } // Work to do - - if ( lock_state != previous_lock_state ) - opt_write(lock_out, lock_state?1:0); - opt_write(framecount_out, framecount); - opt_write(fcserrcount_out, fcserrcount); - opt_write(hdlcbytecount_out, hdlcbytecount); - opt_write(databytecount_out, databytecount); +struct hdlc_sync: runnable +{ + hdlc_sync(scheduler *sch, + pipebuf &_in, // Packed bits + pipebuf &_out, // Bytes + int _minframesize, // Including CRC, excluding HDLC flags. + int _maxframesize, + // Status + pipebuf *_lock_out = NULL, + pipebuf *_framecount_out = NULL, + pipebuf *_fcserrcount_out = NULL, + pipebuf *_hdlcbytecount_out = NULL, + pipebuf *_databytecount_out = NULL) : + runnable(sch, "hdlc_sync"), minframesize(_minframesize), maxframesize( + _maxframesize), chunk_size(maxframesize + 2), in(_in), out( + _out, _maxframesize + chunk_size), lock_out( + opt_writer(_lock_out)), framecount_out( + opt_writer(_framecount_out)), fcserrcount_out( + opt_writer(_fcserrcount_out)), hdlcbytecount_out( + opt_writer(_hdlcbytecount_out)), databytecount_out( + opt_writer(_databytecount_out)), cur_sync(0), resync_phase( + 0), lock_state(false), resync_period(32), header16(false) + { + for (int s = 0; s < NSYNCS; ++s) + { + syncs[s].dec = new hdlc_dec(minframesize, maxframesize, s != 0); + for (int h = 0; h < NERRHIST; ++h) + syncs[s].errhist[h] = 0; + } + syncs[cur_sync].dec->debug = sch->debug; + errslot = 0; } - private: - void output_frame(u8 *f, int size) { - if ( header16 ) { - // Removed 16-bit CRC, add 16-bit prefix -> Still <= maxframesize. - out.write(size >> 8); - out.write(size & 255); - } - memcpy(out.wr(), f, size); - out.written(size); - opt_write(framecount_out, 1); + void run() + { + if (!opt_writable(lock_out) || !opt_writable(framecount_out) + || !opt_writable(fcserrcount_out) + || !opt_writable(hdlcbytecount_out) + || !opt_writable(databytecount_out)) + return; + + bool previous_lock_state = lock_state; + int fcserrcount = 0, framecount = 0; + int hdlcbytecount = 0, databytecount = 0; + + // Note: hdlc_dec may already hold one frame ready for output. + while ((long) in.readable() >= chunk_size + && (long) out.writable() >= maxframesize + chunk_size) + { + if (!resync_phase) + { + // Once every resync_phase, try all decoders + for (int s = 0; s < NSYNCS; ++s) + { + if (s != cur_sync) + syncs[s].dec->reset(); + syncs[s].errhist[errslot] = 0; + for (u8 *pin = in.rd(), *pend = pin + chunk_size; + pin < pend;) + { + int datasize, hdlc_errors, fcs_errors; + u8 *f = syncs[s].dec->decode(&pin, pend - pin, + &datasize, &hdlc_errors, &fcs_errors); + syncs[s].errhist[errslot] += hdlc_errors; + if (s == cur_sync) + { + if (f) + { + lock_state = true; + output_frame(f, datasize); + databytecount += datasize; + ++framecount; + } + fcserrcount += fcs_errors; + framecount += fcs_errors; + } + } + } + errslot = (errslot + 1) % NERRHIST; + // Switch to another sync option ? + // Compare total error counts over about NERRHIST frames. + int total_errors[NSYNCS]; + for (int s = 0; s < NSYNCS; ++s) + { + total_errors[s] = 0; + for (int h = 0; h < NERRHIST; ++h) + total_errors[s] += syncs[s].errhist[h]; + } + int best = cur_sync; + for (int s = 0; s < NSYNCS; ++s) + if (total_errors[s] < total_errors[best]) + best = s; + if (best != cur_sync) + { + lock_state = false; + if (sch->debug) + fprintf(stderr, "[%d:%d->%d:%d]", cur_sync, + total_errors[cur_sync], best, + total_errors[best]); + // No verbose messages on candidate syncs + syncs[cur_sync].dec->debug = false; + cur_sync = best; + syncs[cur_sync].dec->debug = sch->debug; + } + } + else + { + // Use only the currently selected decoder + for (u8 *pin = in.rd(), *pend = pin + chunk_size; pin < pend;) + { + int datasize, hdlc_errors, fcs_errors; + u8 *f = syncs[cur_sync].dec->decode(&pin, pend - pin, + &datasize, &hdlc_errors, &fcs_errors); + if (f) + { + lock_state = true; + output_frame(f, datasize); + databytecount += datasize; + ++framecount; + } + fcserrcount += fcs_errors; + framecount += fcs_errors; + } + } // resync_phase + in.read(chunk_size); + hdlcbytecount += chunk_size; + if (++resync_phase >= resync_period) + resync_phase = 0; + } // Work to do + + if (lock_state != previous_lock_state) + opt_write(lock_out, lock_state ? 1 : 0); + opt_write(framecount_out, framecount); + opt_write(fcserrcount_out, fcserrcount); + opt_write(hdlcbytecount_out, hdlcbytecount); + opt_write(databytecount_out, databytecount); + } + +private: + void output_frame(u8 *f, int size) + { + if (header16) + { + // Removed 16-bit CRC, add 16-bit prefix -> Still <= maxframesize. + out.write(size >> 8); + out.write(size & 255); + } + memcpy(out.wr(), f, size); + out.written(size); + opt_write(framecount_out, 1); } int minframesize, maxframesize; @@ -275,19 +331,21 @@ namespace leansdr { pipewriter *hdlcbytecount_out, *databytecount_out; static const int NSYNCS = 2; // Two possible polarities static const int NERRHIST = 2; // Compare error counts over two frames - struct { - hdlc_dec *dec; - int errhist[NERRHIST]; + struct + { + hdlc_dec *dec; + int errhist[NERRHIST]; } syncs[NSYNCS]; int errslot; int cur_sync; int resync_phase; bool lock_state; - public: +public: int resync_period; bool header16; // Output length prefix - }; // hdlc_sync +}; +// hdlc_sync -} // namespace +}// namespace #endif // LEANSDR_HDLC_H diff --git a/plugins/channelrx/demoddatv/leansdr/sdr.h b/plugins/channelrx/demoddatv/leansdr/sdr.h index fbe0161c7..781daa140 100644 --- a/plugins/channelrx/demoddatv/leansdr/sdr.h +++ b/plugins/channelrx/demoddatv/leansdr/sdr.h @@ -4,684 +4,838 @@ #include "leansdr/math.h" #include "leansdr/dsp.h" -namespace leansdr { +namespace leansdr +{ - // Abbreviations for floating-point types +// Abbreviations for floating-point types - typedef float f32; +typedef float f32; - typedef complex cu8; - typedef complex cs8; - typedef complex cu16; - typedef complex cs16; - typedef complex cf32; +typedef complex cu8; +typedef complex cs8; +typedef complex cu16; +typedef complex cs16; +typedef complex cf32; +////////////////////////////////////////////////////////////////////// +// SDR blocks +////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////// - // SDR blocks - ////////////////////////////////////////////////////////////////////// - - // AUTO-NOTCH FILTER +// AUTO-NOTCH FILTER - // Periodically detects the [n__slots] strongest peaks with a FFT, - // removes them with a first-order filter. +// Periodically detects the [n__slots] strongest peaks with a FFT, +// removes them with a first-order filter. - - - template - struct auto_notch : runnable { +template +struct auto_notch: runnable +{ int decimation; float k; - auto_notch(scheduler *sch, pipebuf< complex > &_in, - pipebuf< complex > &_out, int _n__slots, - T _agc_rms_setpoint) - : runnable(sch, "auto_notch"), - decimation(1024*4096), k(0.002), // k(0.01) - fft(4096), - in(_in), out(_out,fft.n), - n__slots(_n__slots), __slots(new slot[n__slots]), - phase(0), gain(1), agc_rms_setpoint(_agc_rms_setpoint) { - for ( int s=0; s[fft.n]; - } + + auto_notch(scheduler *sch, pipebuf > &_in, pipebuf > &_out, int _n__slots, T _agc_rms_setpoint) : + runnable(sch, "auto_notch"), + decimation(1024 * 4096), + k(0.002), // k(0.01) + fft(4096), + in(_in), + out(_out, fft.n), + n__slots(_n__slots), + __slots(new slot[n__slots]), + phase(0), + gain(1), + agc_rms_setpoint(_agc_rms_setpoint) + { + for (int s = 0; s < n__slots; ++s) + { + __slots[s].i = -1; + __slots[s].expj = new complex [fft.n]; + } } - void run() { - while ( in.readable()>=fft.n && out.writable()>=fft.n ) { - phase += fft.n; - if ( phase >= decimation ) { - phase -= decimation; - detect(); - } - process(); - in.read(fft.n); - out.written(fft.n); - } + + void run() + { + while (in.readable() >= fft.n && out.writable() >= fft.n) + { + phase += fft.n; + if (phase >= decimation) + { + phase -= decimation; + detect(); + } + process(); + in.read(fft.n); + out.written(fft.n); + } } - void detect() { - complex *pin = in.rd(); - complex data[fft.n]; - float m0=0, m2=0; - for ( int i=0; i m0 ) m0 = gen_abs(pin[i].re); - if ( gen_abs(pin[i].im) > m0 ) m0 = gen_abs(pin[i].im); - } - if ( agc_rms_setpoint && m2 ) { - float rms = gen_sqrt(m2/fft.n); - if ( sch->debug ) fprintf(stderr, "(pow %f max %f)", rms, m0); - float new_gain = agc_rms_setpoint / rms; - gain = gain*0.9 + new_gain*0.1; - } - fft.inplace(data, true); - float amp[fft.n]; - for ( int i=0; i amp[iamax] ) iamax=i; - if ( iamax != s->i ) { - if ( sch->debug ) - fprintf(stderr, "%s: slot %d new peak %d -> %d\n", - name, (int)(s-__slots), s->i, iamax); - s->i = iamax; - s->estim.re = 0; - s->estim.im = 0; - s->estt = 0; - for ( int i=0; ii * i / fft.n; - s->expj[i].re = cosf(a); - s->expj[i].im = sinf(a); - } - } - amp[iamax] = 0; - if ( iamax-1 >= 0 ) amp[iamax-1] = 0; - if ( iamax+1 < fft.n ) amp[iamax+1] = 0; - } + + void detect() + { + complex *pin = in.rd(); + complex data[fft.n]; + float m0 = 0, m2 = 0; + + for (unsigned int i = 0; i < fft.n; ++i) + { + data[i].re = pin[i].re; + data[i].im = pin[i].im; + m2 += (float) pin[i].re * pin[i].re + (float) pin[i].im * pin[i].im; + if (gen_abs(pin[i].re) > m0) + m0 = gen_abs(pin[i].re); + if (gen_abs(pin[i].im) > m0) + m0 = gen_abs(pin[i].im); + } + + if (agc_rms_setpoint && m2) + { + float rms = gen_sqrt(m2 / fft.n); + if (sch->debug) + fprintf(stderr, "(pow %f max %f)", rms, m0); + float new_gain = agc_rms_setpoint / rms; + gain = gain * 0.9 + new_gain * 0.1; + } + + fft.inplace(data, true); + float amp[fft.n]; + + for (unsigned int i = 0; i < fft.n; ++i) { + amp[i] = hypotf(data[i].re, data[i].im); + } + + for (slot *s = __slots; s < __slots + n__slots; ++s) + { + int iamax = 0; + for (unsigned int i = 0; i < fft.n; ++i) + { + if (amp[i] > amp[iamax]) { + iamax = i; + } + } + if (iamax != s->i) + { + if (sch->debug) + fprintf(stderr, "%s: slot %d new peak %d -> %d\n", name, + (int) (s - __slots), s->i, iamax); + s->i = iamax; + s->estim.re = 0; + s->estim.im = 0; + s->estt = 0; + for (unsigned int i = 0; i < fft.n; ++i) + { + float a = 2 * M_PI * s->i * i / fft.n; + s->expj[i].re = cosf(a); + s->expj[i].im = sinf(a); + } + } + amp[iamax] = 0; + if (iamax - 1 >= 0) { + amp[iamax - 1] = 0; + } + if (iamax + 1 < (int) fft.n) { + amp[iamax + 1] = 0; + } + } } - void process() { - complex *pin=in.rd(), *pend=pin+fft.n, *pout=out.wr(); - for ( slot *s=__slots; s<__slots+n__slots; ++s ) s->ej = s->expj; - for ( ; pin out = *pin; - // TODO Optimize for n__slots==1 ? - for ( slot *s=__slots; s<__slots+n__slots; ++s->ej,++s ) { - complex bb(pin->re*s->ej->re + pin->im*s->ej->im, - -pin->re*s->ej->im + pin->im*s->ej->re); - s->estim.re = bb.re*k + s->estim.re*(1-k); - s->estim.im = bb.im*k + s->estim.im*(1-k); - complex sub(s->estim.re*s->ej->re - s->estim.im*s->ej->im, - s->estim.re*s->ej->im + s->estim.im*s->ej->re); - out.re -= sub.re; - out.im -= sub.im; - } - pout->re = gain * out.re; - pout->im = gain * out.im; - } + + void process() + { + complex *pin = in.rd(), *pend = pin + fft.n, *pout = out.wr(); + for (slot *s = __slots; s < __slots + n__slots; ++s) + s->ej = s->expj; + for (; pin < pend; ++pin, ++pout) + { + complex out = *pin; + // TODO Optimize for n__slots==1 ? + for (slot *s = __slots; s < __slots + n__slots; ++s->ej, ++s) + { + complex bb(pin->re * s->ej->re + pin->im * s->ej->im, + -pin->re * s->ej->im + pin->im * s->ej->re); + s->estim.re = bb.re * k + s->estim.re * (1 - k); + s->estim.im = bb.im * k + s->estim.im * (1 - k); + complex sub( + s->estim.re * s->ej->re - s->estim.im * s->ej->im, + s->estim.re * s->ej->im + s->estim.im * s->ej->re); + out.re -= sub.re; + out.im -= sub.im; + } + pout->re = gain * out.re; + pout->im = gain * out.im; + } } - - private: + +private: cfft_engine fft; - pipereader< complex > in; - pipewriter< complex > out; + pipereader > in; + pipewriter > out; int n__slots; - struct slot { - int i; - complex estim; - complex *expj, *ej; - int estt; - } *__slots; + struct slot + { + int i; + complex estim; + complex *expj, *ej; + int estt; + }*__slots; + int phase; float gain; T agc_rms_setpoint; - }; +}; +// SIGNAL STRENGTH ESTIMATOR - // SIGNAL STRENGTH ESTIMATOR +// Outputs RMS values. - // Outputs RMS values. - - template - struct ss_estimator : runnable { +template +struct ss_estimator: runnable +{ unsigned long window_size; // Samples per estimation unsigned long decimation; // Output rate - ss_estimator(scheduler *sch, pipebuf< complex > &_in, pipebuf &_out) - : runnable(sch, "SS estimator"), - window_size(1024), decimation(1024), - in(_in), out(_out), - phase(0) { + + ss_estimator(scheduler *sch, pipebuf > &_in, pipebuf &_out) : + runnable(sch, "SS estimator"), + window_size(1024), + decimation(1024), + in(_in), + out(_out), + phase(0) + { } - void run() { - while ( in.readable()>=window_size && out.writable()>=1 ) { - phase += window_size; - if ( phase >= decimation ) { - phase -= decimation; - complex *p=in.rd(), *pend=p+window_size; - float s = 0; - for ( ; pre*p->re + (float)p->im*p->im; - out.write(sqrtf(s/window_size)); - } - in.read(window_size); - } + + void run() + { + while (in.readable() >= window_size && out.writable() >= 1) + { + phase += window_size; + if (phase >= decimation) + { + phase -= decimation; + complex *p = in.rd(), *pend = p + window_size; + float s = 0; + for (; p < pend; ++p) + s += (float) p->re * p->re + (float) p->im * p->im; + out.write(sqrtf(s / window_size)); + } + in.read(window_size); + } } - private: - pipereader< complex > in; + +private: + pipereader > in; pipewriter out; unsigned long phase; - }; +}; - template - struct ss_amp_estimator : runnable { +template +struct ss_amp_estimator: runnable +{ unsigned long window_size; // Samples per estimation unsigned long decimation; // Output rate - ss_amp_estimator(scheduler *sch, pipebuf< complex > &_in, - pipebuf &_out_ss, - pipebuf &_out_ampmin, pipebuf &_out_ampmax) - : runnable(sch, "SS estimator"), - window_size(1024), decimation(1024), - in(_in), out_ss(_out_ss), - out_ampmin(_out_ampmin), out_ampmax(_out_ampmax), - phase(0) { + + ss_amp_estimator(scheduler *sch, pipebuf > &_in, pipebuf &_out_ss, pipebuf &_out_ampmin, pipebuf &_out_ampmax) : + runnable(sch, "SS estimator"), + window_size(1024), + decimation(1024), + in(_in), + out_ss(_out_ss), + out_ampmin(_out_ampmin), + out_ampmax(_out_ampmax), + phase(0) + { } - void run() { - while ( in.readable() >= window_size && - out_ss.writable() >= 1 && - out_ampmin.writable() >= 1 && - out_ampmax.writable() >= 1 ) { - phase += window_size; - if ( phase >= decimation ) { - phase -= decimation; - complex *p=in.rd(), *pend=p+window_size; - float s2 = 0; - float amin=1e38, amax=0; - for ( ; pre*p->re + (float)p->im*p->im; - s2 += mag2; - float mag = sqrtf(mag2); - if ( mag < amin ) amin = mag; - if ( mag > amax ) amax = mag; - } - out_ss.write(sqrtf(s2/window_size)); - out_ampmin.write(amin); - out_ampmax.write(amax); - } - in.read(window_size); - } + + void run() + { + while (in.readable() >= window_size && out_ss.writable() >= 1 + && out_ampmin.writable() >= 1 && out_ampmax.writable() >= 1) + { + phase += window_size; + if (phase >= decimation) + { + phase -= decimation; + complex *p = in.rd(), *pend = p + window_size; + float s2 = 0; + float amin = 1e38, amax = 0; + for (; p < pend; ++p) + { + float mag2 = (float) p->re * p->re + (float) p->im * p->im; + s2 += mag2; + float mag = sqrtf(mag2); + if (mag < amin) + amin = mag; + if (mag > amax) + amax = mag; + } + out_ss.write(sqrtf(s2 / window_size)); + out_ampmin.write(amin); + out_ampmax.write(amax); + } + in.read(window_size); + } } - private: - pipereader< complex > in; + +private: + pipereader > in; pipewriter out_ss, out_ampmin, out_ampmax; unsigned long phase; - }; - - // AGC +}; - template - struct simple_agc : runnable { +// AGC + +template +struct simple_agc: runnable +{ float out_rms; // Desired RMS output power float bw; // Bandwidth float estimated; // Input power - simple_agc(scheduler *sch, - pipebuf< complex > &_in, - pipebuf< complex > &_out) - : runnable(sch, "AGC"), - out_rms(1), bw(0.001), estimated(0), - in(_in), out(_out) { + + simple_agc(scheduler *sch, pipebuf > &_in, pipebuf > &_out) : + runnable(sch, "AGC"), + out_rms(1), + bw(0.001), + estimated(0), + in(_in), + out(_out) + { } - private: - pipereader< complex > in; - pipewriter< complex > out; + +private: + pipereader > in; + pipewriter > out; static const int chunk_size = 128; - void run() { - while ( in.readable() >= chunk_size && - out.writable() >= chunk_size ) { - complex *pin=in.rd(), *pend=pin+chunk_size; - float amp2 = 0; - for ( ; pinre*pin->re + pin->im*pin->im; - amp2 /= chunk_size; - if ( ! estimated ) estimated = amp2; - estimated = estimated*(1-bw) + amp2*bw; - float gain = estimated ? out_rms / sqrtf(estimated) : 0; - pin = in.rd(); - complex *pout = out.wr(); - float bwcomp = 1 - bw; - for ( ; pinre = pin->re * gain; - pout->im = pin->im * gain; - } - in.read(chunk_size); - out.written(chunk_size); - } + + void run() + { + while (in.readable() >= chunk_size && out.writable() >= chunk_size) + { + complex *pin = in.rd(), *pend = pin + chunk_size; + float amp2 = 0; + for (; pin < pend; ++pin) + amp2 += pin->re * pin->re + pin->im * pin->im; + amp2 /= chunk_size; + if (!estimated) + estimated = amp2; + estimated = estimated * (1 - bw) + amp2 * bw; + float gain = estimated ? out_rms / sqrtf(estimated) : 0; + pin = in.rd(); + complex *pout = out.wr(); + float bwcomp = 1 - bw; + for (; pin < pend; ++pin, ++pout) + { + pout->re = pin->re * gain; + pout->im = pin->im * gain; + } + in.read(chunk_size); + out.written(chunk_size); + } } - }; // simple_agc +}; +// simple_agc +typedef uint16_t u_angle; // [0,2PI[ in 65536 steps +typedef int16_t s_angle; // [-PI,PI[ in 65536 steps - typedef uint16_t u_angle; // [0,2PI[ in 65536 steps - typedef int16_t s_angle; // [-PI,PI[ in 65536 steps +// GENERIC CONSTELLATION DECODING BY LOOK-UP TABLE. +// Metrics and phase errors are pre-computed on a RxR grid. +// R must be a power of 2. +// Up to 256 symbols. - // GENERIC CONSTELLATION DECODING BY LOOK-UP TABLE. - - // Metrics and phase errors are pre-computed on a RxR grid. - // R must be a power of 2. - // Up to 256 symbols. - - struct softsymbol { +struct softsymbol +{ int16_t cost; // For Viterbi with TBM=int16_t uint8_t symbol; - }; +}; - // Target RMS amplitude for AGC - //const float cstln_amp = 73; // Best for 32APSK 9/10 - //const float cstln_amp = 90; // Best for QPSK - //const float cstln_amp = 64; // Best for BPSK - //const float cstln_amp = 75; // Best for BPSK at 45° - const float cstln_amp = 75; // Trade-off +// Target RMS amplitude for AGC +//const float cstln_amp = 73; // Best for 32APSK 9/10 +//const float cstln_amp = 90; // Best for QPSK +//const float cstln_amp = 64; // Best for BPSK +//const float cstln_amp = 75; // Best for BPSK at 45° +const float cstln_amp = 75; // Trade-off - template - struct cstln_lut { +template +struct cstln_lut +{ complex *symbols; int nsymbols; int nrotations; - enum predef { - BPSK, // DVB-S2 (and DVB-S variant) - QPSK, // DVB-S - PSK8, APSK16, APSK32, // DVB-S2 - APSK64E, // DVB-S2X - QAM16, QAM64, QAM256 // For experimentation only + enum predef + { + BPSK, // DVB-S2 (and DVB-S variant) + QPSK, // DVB-S + PSK8, + APSK16, + APSK32, // DVB-S2 + APSK64E, // DVB-S2X + QAM16, + QAM64, + QAM256 // For experimentation only }; - cstln_lut(predef type, float gamma1=1, float gamma2=1, float gamma3=1) { - switch ( type ) { - case BPSK: - nrotations = 2; - nsymbols = 2; - symbols = new complex[nsymbols]; + cstln_lut(predef type, float gamma1 = 1, float gamma2 = 1, float gamma3 = 1) + { + switch (type) + { + case BPSK: + nrotations = 2; + nsymbols = 2; + symbols = new complex [nsymbols]; #if 0 // BPSK at 0° - symbols[0] = polar(1, 2, 0); - symbols[1] = polar(1, 2, 1); + symbols[0] = polar(1, 2, 0); + symbols[1] = polar(1, 2, 1); #else // BPSK at 45° - symbols[0] = polar(1, 8, 1); - symbols[1] = polar(1, 8, 5); + symbols[0] = polar(1, 8, 1); + symbols[1] = polar(1, 8, 5); #endif - make_lut_from_symbols(); - break; - case QPSK: - // EN 300 421, section 4.5 Baseband shaping and modulation - // EN 302 307, section 5.4.1 - nrotations = 4; - nsymbols = 4; - symbols = new complex[nsymbols]; - symbols[0] = polar(1, 4, 0.5); - symbols[1] = polar(1, 4, 3.5); - symbols[2] = polar(1, 4, 1.5); - symbols[3] = polar(1, 4, 2.5); - make_lut_from_symbols(); - break; - case PSK8: - // EN 302 307, section 5.4.2 - nrotations = 8; - nsymbols = 8; - symbols = new complex[nsymbols]; - symbols[0] = polar(1, 8, 1); - symbols[1] = polar(1, 8, 0); - symbols[2] = polar(1, 8, 4); - symbols[3] = polar(1, 8, 5); - symbols[4] = polar(1, 8, 2); - symbols[5] = polar(1, 8, 7); - symbols[6] = polar(1, 8, 3); - symbols[7] = polar(1, 8, 6); - make_lut_from_symbols(); - break; - case APSK16: { - // EN 302 307, section 5.4.3 - float r1 = sqrtf(4 / (1+3*gamma1*gamma1)); - float r2 = gamma1 * r1; - nrotations = 4; - nsymbols = 16; - symbols = new complex[nsymbols]; - symbols[0] = polar(r2, 12, 1.5); - symbols[1] = polar(r2, 12, 10.5); - symbols[2] = polar(r2, 12, 4.5); - symbols[3] = polar(r2, 12, 7.5); - symbols[4] = polar(r2, 12, 0.5); - symbols[5] = polar(r2, 12, 11.5); - symbols[6] = polar(r2, 12, 5.5); - symbols[7] = polar(r2, 12, 6.5); - symbols[8] = polar(r2, 12, 2.5); - symbols[9] = polar(r2, 12, 9.5); - symbols[10] = polar(r2, 12, 3.5); - symbols[11] = polar(r2, 12, 8.5); - symbols[12] = polar(r1, 4, 0.5); - symbols[13] = polar(r1, 4, 3.5); - symbols[14] = polar(r1, 4, 1.5); - symbols[15] = polar(r1, 4, 2.5); - make_lut_from_symbols(); - break; - } - case APSK32: { - // EN 302 307, section 5.4.3 - float r1 = sqrtf(8 / (1+3*gamma1*gamma1+4*gamma2*gamma2)); - float r2 = gamma1 * r1; - float r3 = gamma2 * r1; - nrotations = 4; - nsymbols = 32; - symbols = new complex[nsymbols]; - symbols[0] = polar(r2, 12, 1.5); - symbols[1] = polar(r2, 12, 2.5); - symbols[2] = polar(r2, 12, 10.5); - symbols[3] = polar(r2, 12, 9.5); - symbols[4] = polar(r2, 12, 4.5); - symbols[5] = polar(r2, 12, 3.5); - symbols[6] = polar(r2, 12, 7.5); - symbols[7] = polar(r2, 12, 8.5); - symbols[8] = polar(r3, 16, 1 ); - symbols[9] = polar(r3, 16, 3 ); - symbols[10] = polar(r3, 16, 14 ); - symbols[11] = polar(r3, 16, 12 ); - symbols[12] = polar(r3, 16, 6 ); - symbols[13] = polar(r3, 16, 4 ); - symbols[14] = polar(r3, 16, 9 ); - symbols[15] = polar(r3, 16, 11 ); - symbols[16] = polar(r2, 12, 0.5); - symbols[17] = polar(r1, 4, 0.5); - symbols[18] = polar(r2, 12, 11.5); - symbols[19] = polar(r1, 4, 3.5); - symbols[20] = polar(r2, 12, 5.5); - symbols[21] = polar(r1, 4, 1.5); - symbols[22] = polar(r2, 12, 6.5); - symbols[23] = polar(r1, 4, 2.5); - symbols[24] = polar(r3, 16, 0 ); - symbols[25] = polar(r3, 16, 2 ); - symbols[26] = polar(r3, 16, 15 ); - symbols[27] = polar(r3, 16, 13 ); - symbols[28] = polar(r3, 16, 7 ); - symbols[29] = polar(r3, 16, 5 ); - symbols[30] = polar(r3, 16, 8 ); - symbols[31] = polar(r3, 16, 10 ); - make_lut_from_symbols(); - break; - } - case APSK64E: { - // EN 302 307-2, section 5.4.5, Table 13e - float r1 = - sqrtf(64 / (4+12*gamma1*gamma1+20*gamma2*gamma2+28*gamma3*gamma3)); - float r2 = gamma1 * r1; - float r3 = gamma2 * r1; - float r4 = gamma3 * r1; - nrotations = 4; - nsymbols = 64; - symbols = new complex[nsymbols]; - polar2( 0, r4, 1.0/ 4, 7.0/4, 3.0/ 4, 5.0/ 4); - polar2( 4, r4, 13.0/28, 43.0/28, 15.0/28, 41.0/28); - polar2( 8, r4, 1.0/28, 55.0/28, 27.0/28, 29.0/28); - polar2(12, r1, 1.0/ 4, 7.0/ 4, 3.0/ 4, 5.0/ 4); - polar2(16, r4, 9.0/28, 47.0/28, 19.0/28, 37.0/28); - polar2(20, r4, 11.0/28, 45.0/28, 17.0/28, 39.0/28); - polar2(24, r3, 1.0/20, 39.0/20, 19.0/20, 21.0/20); - polar2(28, r2, 1.0/12, 23.0/12, 11.0/12, 13.0/12); - polar2(32, r4, 5.0/28, 51.0/28, 23.0/28, 33.0/28); - polar2(36, r3, 9.0/20, 31.0/20, 11.0/20, 29.0/20); - polar2(40, r4, 3.0/28, 53.0/28, 25.0/28, 31.0/28); - polar2(44, r2, 5.0/12, 19.0/12, 7.0/12, 17.0/12); - polar2(48, r3, 1.0/ 4, 7.0/ 4, 3.0/ 4, 5.0/ 4); - polar2(52, r3, 7.0/20, 33.0/20, 13.0/20, 27.0/20); - polar2(56, r3, 3.0/20, 37.0/20, 17.0/20, 23.0/20); - polar2(60, r2, 1.0/ 4, 7.0/ 4, 3.0/ 4, 5.0/ 4); - make_lut_from_symbols(); - break; - } - case QAM16: - make_qam(16); - break; - case QAM64: - make_qam(64); - break; - case QAM256: - make_qam(256); - break; - default: - fail("Constellation not implemented"); - } + make_lut_from_symbols(); + break; + case QPSK: + // EN 300 421, section 4.5 Baseband shaping and modulation + // EN 302 307, section 5.4.1 + nrotations = 4; + nsymbols = 4; + symbols = new complex [nsymbols]; + symbols[0] = polar(1, 4, 0.5); + symbols[1] = polar(1, 4, 3.5); + symbols[2] = polar(1, 4, 1.5); + symbols[3] = polar(1, 4, 2.5); + make_lut_from_symbols(); + break; + case PSK8: + // EN 302 307, section 5.4.2 + nrotations = 8; + nsymbols = 8; + symbols = new complex [nsymbols]; + symbols[0] = polar(1, 8, 1); + symbols[1] = polar(1, 8, 0); + symbols[2] = polar(1, 8, 4); + symbols[3] = polar(1, 8, 5); + symbols[4] = polar(1, 8, 2); + symbols[5] = polar(1, 8, 7); + symbols[6] = polar(1, 8, 3); + symbols[7] = polar(1, 8, 6); + make_lut_from_symbols(); + break; + case APSK16: + { + // EN 302 307, section 5.4.3 + float r1 = sqrtf(4 / (1 + 3 * gamma1 * gamma1)); + float r2 = gamma1 * r1; + nrotations = 4; + nsymbols = 16; + symbols = new complex [nsymbols]; + symbols[0] = polar(r2, 12, 1.5); + symbols[1] = polar(r2, 12, 10.5); + symbols[2] = polar(r2, 12, 4.5); + symbols[3] = polar(r2, 12, 7.5); + symbols[4] = polar(r2, 12, 0.5); + symbols[5] = polar(r2, 12, 11.5); + symbols[6] = polar(r2, 12, 5.5); + symbols[7] = polar(r2, 12, 6.5); + symbols[8] = polar(r2, 12, 2.5); + symbols[9] = polar(r2, 12, 9.5); + symbols[10] = polar(r2, 12, 3.5); + symbols[11] = polar(r2, 12, 8.5); + symbols[12] = polar(r1, 4, 0.5); + symbols[13] = polar(r1, 4, 3.5); + symbols[14] = polar(r1, 4, 1.5); + symbols[15] = polar(r1, 4, 2.5); + make_lut_from_symbols(); + break; + } + case APSK32: + { + // EN 302 307, section 5.4.3 + float r1 = sqrtf( + 8 / (1 + 3 * gamma1 * gamma1 + 4 * gamma2 * gamma2)); + float r2 = gamma1 * r1; + float r3 = gamma2 * r1; + nrotations = 4; + nsymbols = 32; + symbols = new complex [nsymbols]; + symbols[0] = polar(r2, 12, 1.5); + symbols[1] = polar(r2, 12, 2.5); + symbols[2] = polar(r2, 12, 10.5); + symbols[3] = polar(r2, 12, 9.5); + symbols[4] = polar(r2, 12, 4.5); + symbols[5] = polar(r2, 12, 3.5); + symbols[6] = polar(r2, 12, 7.5); + symbols[7] = polar(r2, 12, 8.5); + symbols[8] = polar(r3, 16, 1); + symbols[9] = polar(r3, 16, 3); + symbols[10] = polar(r3, 16, 14); + symbols[11] = polar(r3, 16, 12); + symbols[12] = polar(r3, 16, 6); + symbols[13] = polar(r3, 16, 4); + symbols[14] = polar(r3, 16, 9); + symbols[15] = polar(r3, 16, 11); + symbols[16] = polar(r2, 12, 0.5); + symbols[17] = polar(r1, 4, 0.5); + symbols[18] = polar(r2, 12, 11.5); + symbols[19] = polar(r1, 4, 3.5); + symbols[20] = polar(r2, 12, 5.5); + symbols[21] = polar(r1, 4, 1.5); + symbols[22] = polar(r2, 12, 6.5); + symbols[23] = polar(r1, 4, 2.5); + symbols[24] = polar(r3, 16, 0); + symbols[25] = polar(r3, 16, 2); + symbols[26] = polar(r3, 16, 15); + symbols[27] = polar(r3, 16, 13); + symbols[28] = polar(r3, 16, 7); + symbols[29] = polar(r3, 16, 5); + symbols[30] = polar(r3, 16, 8); + symbols[31] = polar(r3, 16, 10); + make_lut_from_symbols(); + break; + } + case APSK64E: + { + // EN 302 307-2, section 5.4.5, Table 13e + float r1 = sqrtf( + 64 + / (4 + 12 * gamma1 * gamma1 + 20 * gamma2 * gamma2 + + 28 * gamma3 * gamma3)); + float r2 = gamma1 * r1; + float r3 = gamma2 * r1; + float r4 = gamma3 * r1; + nrotations = 4; + nsymbols = 64; + symbols = new complex [nsymbols]; + polar2(0, r4, 1.0 / 4, 7.0 / 4, 3.0 / 4, 5.0 / 4); + polar2(4, r4, 13.0 / 28, 43.0 / 28, 15.0 / 28, 41.0 / 28); + polar2(8, r4, 1.0 / 28, 55.0 / 28, 27.0 / 28, 29.0 / 28); + polar2(12, r1, 1.0 / 4, 7.0 / 4, 3.0 / 4, 5.0 / 4); + polar2(16, r4, 9.0 / 28, 47.0 / 28, 19.0 / 28, 37.0 / 28); + polar2(20, r4, 11.0 / 28, 45.0 / 28, 17.0 / 28, 39.0 / 28); + polar2(24, r3, 1.0 / 20, 39.0 / 20, 19.0 / 20, 21.0 / 20); + polar2(28, r2, 1.0 / 12, 23.0 / 12, 11.0 / 12, 13.0 / 12); + polar2(32, r4, 5.0 / 28, 51.0 / 28, 23.0 / 28, 33.0 / 28); + polar2(36, r3, 9.0 / 20, 31.0 / 20, 11.0 / 20, 29.0 / 20); + polar2(40, r4, 3.0 / 28, 53.0 / 28, 25.0 / 28, 31.0 / 28); + polar2(44, r2, 5.0 / 12, 19.0 / 12, 7.0 / 12, 17.0 / 12); + polar2(48, r3, 1.0 / 4, 7.0 / 4, 3.0 / 4, 5.0 / 4); + polar2(52, r3, 7.0 / 20, 33.0 / 20, 13.0 / 20, 27.0 / 20); + polar2(56, r3, 3.0 / 20, 37.0 / 20, 17.0 / 20, 23.0 / 20); + polar2(60, r2, 1.0 / 4, 7.0 / 4, 3.0 / 4, 5.0 / 4); + make_lut_from_symbols(); + break; + } + case QAM16: + make_qam(16); + break; + case QAM64: + make_qam(64); + break; + case QAM256: + make_qam(256); + break; + default: + fail("Constellation not implemented"); + } } - struct result { - struct softsymbol ss; - s_angle phase_error; + struct result + { + struct softsymbol ss; + s_angle phase_error; }; - inline result *lookup(float I, float Q) { - // Handling of overflows beyond the lookup table: - // - For BPSK/QPSK/8PSK we only care about the phase, - // so the following is harmless and improves locking at low SNR. - // - For amplitude modulations this is not appropriate. - // However, if there is enough noise to cause overflow, - // demodulation would probably fail anyway. - // - // Comment-out for better throughput at high SNR. + inline result *lookup(float I, float Q) + { + // Handling of overflows beyond the lookup table: + // - For BPSK/QPSK/8PSK we only care about the phase, + // so the following is harmless and improves locking at low SNR. + // - For amplitude modulations this is not appropriate. + // However, if there is enough noise to cause overflow, + // demodulation would probably fail anyway. + // + // Comment-out for better throughput at high SNR. #if 1 - while ( I<-128 || I>127 || Q<-128 || Q>127 ) { - I *= 0.5; - Q *= 0.5; - } + while (I < -128 || I > 127 || Q < -128 || Q > 127) + { + I *= 0.5; + Q *= 0.5; + } #endif - return &lut[(u8)(s8)I][(u8)(s8)Q]; + return &lut[(u8) (s8) I][(u8) (s8) Q]; } - inline result *lookup(int I, int Q) { - // Ignore wrapping modulo 256 - return &lut[(u8)I][(u8)Q]; + + inline result *lookup(int I, int Q) + { + // Ignore wrapping modulo 256 + return &lut[(u8) I][(u8) Q]; } - private: - complex polar(float r, int n, float i) { - float a = i * 2*M_PI / n; - return complex(r*cosf(a)*cstln_amp, r*sinf(a)*cstln_amp); + +private: + complex polar(float r, int n, float i) + { + float a = i * 2 * M_PI / n; + return complex(r * cosf(a) * cstln_amp, + r * sinf(a) * cstln_amp); } + // Helper function for some constellation tables - void polar2(int i, float r, float a0, float a1, float a2, float a3) { - float a[] = { a0, a1, a2, a3 }; - for ( int j=0; j<4; ++j ) { - float phi = a[j] * M_PI; - symbols[i+j] = complex(r*cosf(phi)*cstln_amp, - r*sinf(phi)*cstln_amp); - } + void polar2(int i, float r, float a0, float a1, float a2, float a3) + { + float a[] = + { a0, a1, a2, a3 }; + for (int j = 0; j < 4; ++j) + { + float phi = a[j] * M_PI; + symbols[i + j] = complex(r * cosf(phi) * cstln_amp, + r * sinf(phi) * cstln_amp); + } } - void make_qam(int n) { - nrotations = 4; - nsymbols = n; - symbols = new complex[nsymbols]; - int m = sqrtl(n); - float scale; - { // Average power in first quadrant with unit grid - int q = m / 2; - float avgpower = 2*(q*0.25+(q-1)*q/2+(q-1)*q*(2*q-1)/6) / q; - scale = 1.0 / sqrtf(avgpower); - } - // Arbitrary mapping - int s = 0; - for ( int x=0; x [nsymbols]; + int m = sqrtl(n); + float scale; + { // Average power in first quadrant with unit grid + int q = m / 2; + float avgpower = 2 + * (q * 0.25 + (q - 1) * q / 2 + + (q - 1) * q * (2 * q - 1) / 6) / q; + scale = 1.0 / sqrtf(avgpower); + } + // Arbitrary mapping + int s = 0; + for (int x = 0; x < m; ++x) + for (int y = 0; y < m; ++y) + { + float I = x - (float) (m - 1) / 2; + float Q = y - (float) (m - 1) / 2; + symbols[s].re = I * scale * cstln_amp; + symbols[s].im = Q * scale * cstln_amp; + ++s; + } + make_lut_from_symbols(); } + result lut[R][R]; - void make_lut_from_symbols() { - for ( int I=-R/2; I Suitable for Viterbi with partial metrics. - uint8_t nearest = 0; - int32_t cost=R*R*2, cost2=R*R*2; - for ( int s=0; s 32767 ) cost = 32767; - if ( cost2 > 32767 ) cost2 = 32767; - pr->ss.cost = cost - cost2; - pr->ss.symbol = nearest; - float ph_symbol = atan2f(symbols[pr->ss.symbol].im, - symbols[pr->ss.symbol].re); - float ph_err = atan2f(Q,I) - ph_symbol; - pr->phase_error = (s32)(ph_err * 65536 / (2*M_PI)); // Mod 65536 - } + + void make_lut_from_symbols() + { + for (int I = -R / 2; I < R / 2; ++I) + for (int Q = -R / 2; Q < R / 2; ++Q) + { + result *pr = &lut[I & (R - 1)][Q & (R - 1)]; + // Simplified metric: + // Distance to nearest minus distance to second-nearest. + // Null at edge of decision regions + // => Suitable for Viterbi with partial metrics. + uint8_t nearest = 0; + int32_t cost = R * R * 2, cost2 = R * R * 2; + for (int s = 0; s < nsymbols; ++s) + { + int32_t d2 = (I - symbols[s].re) * (I - symbols[s].re) + + (Q - symbols[s].im) * (Q - symbols[s].im); + if (d2 < cost) + { + cost2 = cost; + cost = d2; + nearest = s; + } + else if (d2 < cost2) + { + cost2 = d2; + } + } + if (cost > 32767) + cost = 32767; + if (cost2 > 32767) + cost2 = 32767; + pr->ss.cost = cost - cost2; + pr->ss.symbol = nearest; + float ph_symbol = atan2f(symbols[pr->ss.symbol].im, + symbols[pr->ss.symbol].re); + float ph_err = atan2f(Q, I) - ph_symbol; + pr->phase_error = (s32) (ph_err * 65536 / (2 * M_PI)); // Mod 65536 + } } - public: +public: // Convert soft metric to Hamming distance - void harden() { - for ( int i=0; icost < 0 ) ss->cost = -1; - if ( ss->cost > 0 ) ss->cost = 1; - } // for I,Q - } + void harden() + { + for (int i = 0; i < R; ++i) + for (int q = 0; q < R; ++q) + { + softsymbol *ss = &lut[i][q].ss; + if (ss->cost < 0) + ss->cost = -1; + if (ss->cost > 0) + ss->cost = 1; + } // for I,Q + } - }; // cstln_lut +}; +// cstln_lut - static const char *cstln_names[] = { - [cstln_lut<256>::BPSK] = "BPSK", - [cstln_lut<256>::QPSK] = "QPSK", - [cstln_lut<256>::PSK8] = "8PSK", - [cstln_lut<256>::APSK16] = "16APSK", - [cstln_lut<256>::APSK32] = "32APSK", - [cstln_lut<256>::APSK64E] = "64APSKe", - [cstln_lut<256>::QAM16] = "16QAM", - [cstln_lut<256>::QAM64] = "64QAM", - [cstln_lut<256>::QAM256] = "256QAM" - }; +//static const char *cstln_names[] = +//{ [cstln_lut<256>::BPSK] = "BPSK", +// [cstln_lut<256>::QPSK] = "QPSK", +// [cstln_lut<256>::PSK8] = "8PSK", +// [cstln_lut<256>::APSK16] = "16APSK", +// [cstln_lut<256>::APSK32] = "32APSK", +// [cstln_lut<256>::APSK64E] = "64APSKe", +// [cstln_lut<256>::QAM16] = "16QAM", +// [cstln_lut<256>::QAM64] = "64QAM", +// [cstln_lut<256>::QAM256] = "256QAM" +//}; - // SAMPLER INTERFACE FOR CSTLN_RECEIVER - - template - struct sampler_interface { +// SAMPLER INTERFACE FOR CSTLN_RECEIVER + +template +struct sampler_interface +{ virtual complex interp(const complex *pin, float mu, float phase) = 0; - virtual void update_freq(float freqw) { } // 65536 = 1 Hz - virtual int readahead() { return 0; } - }; + virtual void update_freq(float freqw __attribute__((unused))) + { + } // 65536 = 1 Hz - // NEAREST-SAMPLE SAMPLER FOR CSTLN_RECEIVER - // Suitable for bandpass-filtered, oversampled signals only - - template - struct nearest_sampler : sampler_interface { - int readahead() { return 0; } - complex interp(const complex *pin, float mu, float phase) { - return pin[0]*trig.expi(-phase); + virtual int readahead() + { + return 0; } - private: + + virtual ~sampler_interface() + { + } +}; + +// NEAREST-SAMPLE SAMPLER FOR CSTLN_RECEIVER +// Suitable for bandpass-filtered, oversampled signals only + +template +struct nearest_sampler: sampler_interface +{ + int readahead() + { + return 0; + } + + complex interp(const complex *pin, float mu __attribute__((unused)), float phase) + { + return pin[0] * trig.expi(-phase); + } + +private: trig16 trig; - }; // nearest_sampler +}; +// nearest_sampler +// LINEAR SAMPLER FOR CSTLN_RECEIVER - // LINEAR SAMPLER FOR CSTLN_RECEIVER - - template - struct linear_sampler : sampler_interface { - int readahead() { return 1; } - - complex interp(const complex *pin, float mu, float phase) { - // Derotate pin[0] and pin[1] - complex s0 = pin[0]*trig.expi(-phase); - complex s1 = pin[1]*trig.expi(-(phase+freqw)); - // Interpolate linearly - return s0*(1-mu) + s1*mu; +template +struct linear_sampler: sampler_interface +{ + int readahead() + { + return 1; } - void update_freq(float _freqw) { freqw = _freqw; } + complex interp(const complex *pin, float mu, float phase) + { + // Derotate pin[0] and pin[1] + complex s0 = pin[0] * trig.expi(-phase); + complex s1 = pin[1] * trig.expi(-(phase + freqw)); + // Interpolate linearly + return s0 * (1 - mu) + s1 * mu; + } - private: + void update_freq(float _freqw) + { + freqw = _freqw; + } + +private: trig16 trig; float freqw; - }; // linear_sampler +}; +// linear_sampler +// FIR SAMPLER FOR CSTLN_RECEIVER - // FIR SAMPLER FOR CSTLN_RECEIVER - - template - struct fir_sampler : sampler_interface { - fir_sampler(int _ncoeffs, Tc *_coeffs, int _subsampling=1) - : ncoeffs(_ncoeffs), coeffs(_coeffs), subsampling(_subsampling), - shifted_coeffs(new complex[ncoeffs]), - update_freq_phase(0) +template +struct fir_sampler: sampler_interface +{ + fir_sampler(int _ncoeffs, Tc *_coeffs, int _subsampling = 1) : + ncoeffs(_ncoeffs), coeffs(_coeffs), subsampling(_subsampling), shifted_coeffs( + new complex [ncoeffs]), update_freq_phase(0) { } - int readahead() { return ncoeffs-1; } - - complex interp(const complex *pin, float mu, float phase) { - // Apply FIR filter with subsampling - complex acc(0, 0); - complex *pc = shifted_coeffs + (int)((1-mu)*subsampling); - complex *pcend = shifted_coeffs + ncoeffs; - if ( subsampling == 1 ) { - // Special case for heavily oversampled signals, - // where filtering is expensive. - // gcc-4.9.2 can vectorize this form with NEON on ARM. - while ( pc < pcend ) - acc += (*pc++)*(*pin++); - } else { - // Not vectorized because the coefficients are not - // guaranteed to be contiguous in memory. - for ( ; pc interp(const complex *pin, float mu, float phase) + { + // Apply FIR filter with subsampling + complex acc(0, 0); + complex *pc = shifted_coeffs + (int) ((1 - mu) * subsampling); + complex *pcend = shifted_coeffs + ncoeffs; + if (subsampling == 1) + { + // Special case for heavily oversampled signals, + // where filtering is expensive. + // gcc-4.9.2 can vectorize this form with NEON on ARM. + while (pc < pcend) + acc += (*pc++) * (*pin++); + } + else + { + // Not vectorized because the coefficients are not + // guaranteed to be contiguous in memory. + for (; pc < pcend; pc += subsampling, ++pin) + acc += (*pc) * (*pin); + } + // Derotate + return trig.expi(-phase) * acc; } - private: - void do_update_freq(float freqw) { - float f = freqw / subsampling; - for ( int i=0; i - struct cstln_receiver : runnable { +template +struct cstln_receiver: runnable +{ sampler_interface *sampler; cstln_lut<256> *cstln; unsigned long meas_decimation; // Measurement rate @@ -691,226 +845,267 @@ namespace leansdr { bool allow_drift; // Follow carrier beyond safe limits static const unsigned int chunk_size = 128; float kest; - - cstln_receiver(scheduler *sch, - sampler_interface *_sampler, - pipebuf< complex > &_in, - pipebuf &_out, - pipebuf *_freq_out=NULL, - pipebuf *_ss_out=NULL, - pipebuf *_mer_out=NULL, - pipebuf *_cstln_out=NULL) - : runnable(sch, "Constellation receiver"), - sampler(_sampler), - cstln(NULL), - meas_decimation(1048576), - pll_adjustment(1.0), - allow_drift(false), - kest(0.01), - in(_in), out(_out, chunk_size), - est_insp(cstln_amp*cstln_amp), agc_gain(1), - mu(0), phase(0), - est_sp(0), est_ep(0), - meas_count(0) { - set_omega(1); - set_freq(0); - freq_out = _freq_out ? new pipewriter(*_freq_out) : NULL; - ss_out = _ss_out ? new pipewriter(*_ss_out) : NULL; - mer_out = _mer_out ? new pipewriter(*_mer_out) : NULL; - cstln_out = _cstln_out ? new pipewriter(*_cstln_out) : NULL; - memset(hist, 0, sizeof(hist)); - } - - void set_omega(float _omega, float tol=10e-6) { - omega = _omega; - min_omega = omega * (1-tol); - max_omega = omega * (1+tol); - update_freq_limits(); - } - - void set_freq(float freq) { - freqw = freq * 65536; - update_freq_limits(); - refresh_freq_tap(); + + cstln_receiver( + scheduler *sch, + sampler_interface *_sampler, + pipebuf > &_in, + pipebuf &_out, + pipebuf *_freq_out = NULL, + pipebuf *_ss_out = NULL, + pipebuf *_mer_out = NULL, + pipebuf *_cstln_out = NULL) : + runnable(sch, "Constellation receiver"), + sampler(_sampler), + cstln(NULL), + meas_decimation(1048576), + pll_adjustment(1.0), + allow_drift(false), + kest(0.01), + in(_in), + out(_out, chunk_size), + est_insp(cstln_amp * cstln_amp), + agc_gain(1), + mu(0), + phase(0), + est_sp(0), + est_ep(0), + meas_count(0) + { + set_omega(1); + set_freq(0); + freq_out = _freq_out ? new pipewriter(*_freq_out) : NULL; + ss_out = _ss_out ? new pipewriter(*_ss_out) : NULL; + mer_out = _mer_out ? new pipewriter(*_mer_out) : NULL; + cstln_out = _cstln_out ? new pipewriter(*_cstln_out) : NULL; + memset(hist, 0, sizeof(hist)); } - void set_allow_drift(bool d) { - allow_drift = d; + void set_omega(float _omega, float tol = 10e-6) + { + omega = _omega; + min_omega = omega * (1 - tol); + max_omega = omega * (1 + tol); + update_freq_limits(); } - void update_freq_limits() { - // Prevent PLL from crossing +-SR/n/2 and locking at +-SR/n. - int n = 4; - if ( cstln ) { - switch ( cstln->nsymbols ) { - case 2: n = 2; break; // BPSK - case 4: n = 4; break; // QPSK - case 8: n = 8; break; // 8PSK - case 16: n = 12; break; // 16APSK - case 32: n = 16; break; // 32APSK - default: n = 4; break; - } - } - min_freqw = freqw - 65536/max_omega/n/2; - max_freqw = freqw + 65536/max_omega/n/2; + void set_freq(float freq) + { + freqw = freq * 65536; + update_freq_limits(); + refresh_freq_tap(); } - - void run() { - if ( ! cstln ) fail("constellation not set"); - - // Magic constants that work with the qa recordings. - float freq_alpha = 0.04; - float freq_beta = 0.0012 / omega * pll_adjustment; - float gain_mu = 0.02 / (cstln_amp*cstln_amp) * 2; - int max_meas = chunk_size/meas_decimation + 1; - // Large margin on output_size because mu adjustments - // can lead to more than chunk_size/min_omega symbols. - while ( in.readable() >= chunk_size+sampler->readahead() && - out.writable() >= chunk_size && - ( !freq_out || freq_out ->writable()>=max_meas ) && - ( !ss_out || ss_out ->writable()>=max_meas ) && - ( !mer_out || mer_out ->writable()>=max_meas ) && - ( !cstln_out || cstln_out->writable()>=max_meas ) ) { - - sampler->update_freq(freqw); - - complex *pin=in.rd(), *pin0=pin, *pend=pin+chunk_size; - softsymbol *pout=out.wr(), *pout0=pout; - - // These are scoped outside the loop for SS and MER estimation. - complex sg; // Symbol before AGC; - complex s; // For MER estimation and constellation viewer - complex *cstln_point = NULL; - - while ( pin < pend ) { - // Here mu is the time of the next symbol counted from 0 at pin. - if ( mu < 1 ) { - // Here 0<=mu<1 is the fractional time of the next symbol - // between pin and pin+1. - sg = sampler->interp(pin, mu, phase); - s = sg * agc_gain; - - // Constellation look-up - cstln_lut<256>::result *cr = cstln->lookup(s.re, s.im); - *pout = cr->ss; - ++pout; - - // PLL - phase += cr->phase_error * freq_alpha; - freqw += cr->phase_error * freq_beta; - - // Modified Mueller and Müller - // mu[k]=real((c[k]-c[k-2])*conj(p[k-1])-(p[k]-p[k-2])*conj(c[k-1])) - // =dot(c[k]-c[k-2],p[k-1]) - dot(p[k]-p[k-2],c[k-1]) - // p = received signals - // c = decisions (constellation points) - hist[2] = hist[1]; - hist[1] = hist[0]; - hist[0].p.re = s.re; - hist[0].p.im = s.im; - cstln_point = &cstln->symbols[cr->ss.symbol]; - hist[0].c.re = cstln_point->re; - hist[0].c.im = cstln_point->im; - float muerr = - ( (hist[0].p.re-hist[2].p.re)*hist[1].c.re + - (hist[0].p.im-hist[2].p.im)*hist[1].c.im ) - - ( (hist[0].c.re-hist[2].c.re)*hist[1].p.re + - (hist[0].c.im-hist[2].c.im)*hist[1].p.im ); - float mucorr = muerr * gain_mu; - const float max_mucorr = 0.1; - // TBD Optimize out statically - if ( mucorr < -max_mucorr ) mucorr = -max_mucorr; - if ( mucorr > max_mucorr ) mucorr = max_mucorr; - mu += mucorr; - mu += omega; // Next symbol time; - } // mu<1 - - // Next sample - ++pin; - --mu; - phase += freqw; - } // chunk_size - - in.read(pin-pin0); - out.written(pout-pout0); - - // Normalize phase so that it never exceeds 32 bits. - // Max freqw is 2^31/65536/chunk_size = 256 Hz - // (this may happen with leandvb --drift --decim). - phase = fmodf(phase, 65536); - - if ( cstln_point ) { - - // Output the last interpolated PSK symbol, max once per chunk_size - if ( cstln_out ) - cstln_out->write(s); - - // AGC - // For APSK we must do AGC on the symbols, not the whole signal. - // TODO Use a better estimator at low SNR. - float insp = sg.re*sg.re + sg.im*sg.im; - est_insp = insp*kest + est_insp*(1-kest); - if ( est_insp ) - agc_gain = cstln_amp / gen_sqrt(est_insp); - - // SS and MER - complex ev(s.re-cstln_point->re, s.im-cstln_point->im); - float sig_power, ev_power; - if ( cstln->nsymbols == 2 ) { - // Special case for BPSK: Ignore quadrature component of noise. - // TBD Projection on I axis assumes BPSK at 45° - float sig_real = (cstln_point->re+cstln_point->im) * 0.707; - float ev_real = (ev.re+ev.im) * 0.707; - sig_power = sig_real * sig_real; - ev_power = ev_real * ev_real; - } else { - sig_power = - (int)cstln_point->re*cstln_point->re + - (int)cstln_point->im*cstln_point->im; - ev_power = ev.re*ev.re + ev.im*ev.im; - } - est_sp = sig_power*kest + est_sp*(1-kest); - est_ep = ev_power*kest + est_ep*(1-kest); - - } - - // This is best done periodically ouside the inner loop, - // but will cause non-deterministic output. - - if ( ! allow_drift ) { - if ( freqw < min_freqw || freqw > max_freqw ) - freqw = (max_freqw+min_freqw) / 2; - } - - // Output measurements - - refresh_freq_tap(); - - meas_count += pin-pin0; - while ( meas_count >= meas_decimation ) { - meas_count -= meas_decimation; - if ( freq_out ) - freq_out->write(freq_tap); - if ( ss_out ) - ss_out->write(sqrtf(est_insp)); - if ( mer_out ) - mer_out->write(est_ep ? 10*logf(est_sp/est_ep)/logf(10) : 0); - } - - } // Work to do + void set_allow_drift(bool d) + { + allow_drift = d; } - + + void update_freq_limits() + { + // Prevent PLL from crossing +-SR/n/2 and locking at +-SR/n. + int n = 4; + if (cstln) + { + switch (cstln->nsymbols) + { + case 2: + n = 2; + break; // BPSK + case 4: + n = 4; + break; // QPSK + case 8: + n = 8; + break; // 8PSK + case 16: + n = 12; + break; // 16APSK + case 32: + n = 16; + break; // 32APSK + default: + n = 4; + break; + } + } + min_freqw = freqw - 65536 / max_omega / n / 2; + max_freqw = freqw + 65536 / max_omega / n / 2; + } + + void run() + { + if (!cstln) + fail("constellation not set"); + + // Magic constants that work with the qa recordings. + float freq_alpha = 0.04; + float freq_beta = 0.0012 / omega * pll_adjustment; + float gain_mu = 0.02 / (cstln_amp * cstln_amp) * 2; + + unsigned int max_meas = chunk_size / meas_decimation + 1; + // Large margin on output_size because mu adjustments + // can lead to more than chunk_size/min_omega symbols. + while (in.readable() >= chunk_size + sampler->readahead() + && out.writable() >= chunk_size + && (!freq_out || freq_out->writable() >= max_meas) + && (!ss_out || ss_out->writable() >= max_meas) + && (!mer_out || mer_out->writable() >= max_meas) + && (!cstln_out || cstln_out->writable() >= max_meas)) + { + + sampler->update_freq(freqw); + + complex *pin = in.rd(), *pin0 = pin, *pend = pin + chunk_size; + softsymbol *pout = out.wr(), *pout0 = pout; + + // These are scoped outside the loop for SS and MER estimation. + complex sg; // Symbol before AGC; + complex s; // For MER estimation and constellation viewer + complex *cstln_point = NULL; + + while (pin < pend) + { + // Here mu is the time of the next symbol counted from 0 at pin. + if (mu < 1) + { + // Here 0<=mu<1 is the fractional time of the next symbol + // between pin and pin+1. + sg = sampler->interp(pin, mu, phase); + s = sg * agc_gain; + + // Constellation look-up + cstln_lut<256>::result *cr = cstln->lookup(s.re, s.im); + *pout = cr->ss; + ++pout; + + // PLL + phase += cr->phase_error * freq_alpha; + freqw += cr->phase_error * freq_beta; + + // Modified Mueller and Müller + // mu[k]=real((c[k]-c[k-2])*conj(p[k-1])-(p[k]-p[k-2])*conj(c[k-1])) + // =dot(c[k]-c[k-2],p[k-1]) - dot(p[k]-p[k-2],c[k-1]) + // p = received signals + // c = decisions (constellation points) + hist[2] = hist[1]; + hist[1] = hist[0]; + hist[0].p.re = s.re; + hist[0].p.im = s.im; + cstln_point = &cstln->symbols[cr->ss.symbol]; + hist[0].c.re = cstln_point->re; + hist[0].c.im = cstln_point->im; + float muerr = ((hist[0].p.re - hist[2].p.re) * hist[1].c.re + + (hist[0].p.im - hist[2].p.im) * hist[1].c.im) + - ((hist[0].c.re - hist[2].c.re) * hist[1].p.re + + (hist[0].c.im - hist[2].c.im) + * hist[1].p.im); + float mucorr = muerr * gain_mu; + const float max_mucorr = 0.1; + // TBD Optimize out statically + if (mucorr < -max_mucorr) + mucorr = -max_mucorr; + if (mucorr > max_mucorr) + mucorr = max_mucorr; + mu += mucorr; + mu += omega; // Next symbol time; + } // mu<1 + + // Next sample + ++pin; + --mu; + phase += freqw; + } // chunk_size + + in.read(pin - pin0); + out.written(pout - pout0); + + // Normalize phase so that it never exceeds 32 bits. + // Max freqw is 2^31/65536/chunk_size = 256 Hz + // (this may happen with leandvb --drift --decim). + phase = fmodf(phase, 65536); + + if (cstln_point) + { + + // Output the last interpolated PSK symbol, max once per chunk_size + if (cstln_out) + cstln_out->write(s); + + // AGC + // For APSK we must do AGC on the symbols, not the whole signal. + // TODO Use a better estimator at low SNR. + float insp = sg.re * sg.re + sg.im * sg.im; + est_insp = insp * kest + est_insp * (1 - kest); + if (est_insp) + agc_gain = cstln_amp / gen_sqrt(est_insp); + + // SS and MER + complex ev(s.re - cstln_point->re, + s.im - cstln_point->im); + float sig_power, ev_power; + if (cstln->nsymbols == 2) + { + // Special case for BPSK: Ignore quadrature component of noise. + // TBD Projection on I axis assumes BPSK at 45° + float sig_real = (cstln_point->re + cstln_point->im) + * 0.707; + float ev_real = (ev.re + ev.im) * 0.707; + sig_power = sig_real * sig_real; + ev_power = ev_real * ev_real; + } + else + { + sig_power = (int) cstln_point->re * cstln_point->re + + (int) cstln_point->im * cstln_point->im; + ev_power = ev.re * ev.re + ev.im * ev.im; + } + est_sp = sig_power * kest + est_sp * (1 - kest); + est_ep = ev_power * kest + est_ep * (1 - kest); + + } + + // This is best done periodically ouside the inner loop, + // but will cause non-deterministic output. + + if (!allow_drift) + { + if (freqw < min_freqw || freqw > max_freqw) + freqw = (max_freqw + min_freqw) / 2; + } + + // Output measurements + + refresh_freq_tap(); + + meas_count += pin - pin0; + while (meas_count >= meas_decimation) + { + meas_count -= meas_decimation; + if (freq_out) + freq_out->write(freq_tap); + if (ss_out) + ss_out->write(sqrtf(est_insp)); + if (mer_out) + mer_out->write( + est_ep ? 10 * logf(est_sp / est_ep) / logf(10) : 0); + } + + } // Work to do + } + float freq_tap; - void refresh_freq_tap() { - freq_tap = freqw / 65536; + void refresh_freq_tap() + { + freq_tap = freqw / 65536; } - private: - struct { - complex p; // Received symbol - complex c; // Matched constellation point +private: + struct + { + complex p; // Received symbol + complex c; // Matched constellation point } hist[3]; - pipereader< complex > in; + pipereader > in; pipewriter out; float est_insp, agc_gain; float mu; // PSK time expressed in clock ticks @@ -921,16 +1116,16 @@ namespace leansdr { unsigned long meas_count; pipewriter *freq_out, *ss_out, *mer_out; pipewriter *cstln_out; - }; - - - // FAST QPSK RECEIVER +}; - // Optimized for u8 input, no AGC, uses phase information only. - // Outputs hard symbols. +// FAST QPSK RECEIVER - template - struct fast_qpsk_receiver : runnable { +// Optimized for u8 input, no AGC, uses phase information only. +// Outputs hard symbols. + +template +struct fast_qpsk_receiver: runnable +{ typedef u8 hardsymbol; unsigned long meas_decimation; // Measurement rate float omega, min_omega, max_omega; // Samples per symbol @@ -938,231 +1133,272 @@ namespace leansdr { float pll_adjustment; bool allow_drift; // Follow carrier beyond safe limits static const unsigned int chunk_size = 128; - - fast_qpsk_receiver(scheduler *sch, - pipebuf< complex > &_in, - pipebuf &_out, - pipebuf *_freq_out=NULL, - pipebuf< complex > *_cstln_out=NULL) - : runnable(sch, "Fast QPSK receiver"), - meas_decimation(1048576), - pll_adjustment(1.0), - allow_drift(false), - in(_in), out(_out, chunk_size), - mu(0), phase(0), - meas_count(0) + + fast_qpsk_receiver( + scheduler *sch, + pipebuf > &_in, + pipebuf &_out, + pipebuf *_freq_out = NULL, + pipebuf > *_cstln_out = NULL) : + runnable(sch, "Fast QPSK receiver"), + meas_decimation(1048576), + pll_adjustment(1.0), + allow_drift(false), + in(_in), + out(_out, chunk_size), + mu(0), + phase(0), + meas_count(0) { - set_omega(1); - set_freq(0); - freq_out = _freq_out ? new pipewriter(*_freq_out) : NULL; - cstln_out = _cstln_out ? new pipewriter< complex >(*_cstln_out) : NULL; - memset(hist, 0, sizeof(hist)); - init_lookup_tables(); - } - - void set_omega(float _omega, float tol=10e-6) { - omega = _omega; - min_omega = omega * (1-tol); - max_omega = omega * (1+tol); - update_freq_limits(); - } - - void set_freq(float freq) { - freqw = freq * 65536; - update_freq_limits(); + set_omega(1); + set_freq(0); + freq_out = _freq_out ? new pipewriter(*_freq_out) : NULL; + cstln_out = + _cstln_out ? new pipewriter >(*_cstln_out) : NULL; + memset(hist, 0, sizeof(hist)); + init_lookup_tables(); } - void update_freq_limits() { - // Prevent PLL from locking at +-symbolrate/4. - // TODO The +-SR/8 limit is suitable for QPSK only. - min_freqw = freqw - 65536/max_omega/8; - max_freqw = freqw + 65536/max_omega/8; + void set_omega(float _omega, float tol = 10e-6) + { + omega = _omega; + min_omega = omega * (1 - tol); + max_omega = omega * (1 + tol); + update_freq_limits(); + } + + void set_freq(float freq) + { + freqw = freq * 65536; + update_freq_limits(); + } + + void update_freq_limits() + { + // Prevent PLL from locking at +-symbolrate/4. + // TODO The +-SR/8 limit is suitable for QPSK only. + min_freqw = freqw - 65536 / max_omega / 8; + max_freqw = freqw + 65536 / max_omega / 8; } static const int RLUT_BITS = 8; static const int RLUT_ANGLES = 1 << RLUT_BITS; - void run() { - // Magic constants that work with the qa recordings. - signed long freq_alpha = 0.04 * 65536; - signed long freq_beta = 0.0012 * 256 * 65536 / omega * pll_adjustment; - if ( ! freq_beta ) fail("Excessive oversampling"); + void run() + { + // Magic constants that work with the qa recordings. + signed long freq_alpha = 0.04 * 65536; + signed long freq_beta = 0.0012 * 256 * 65536 / omega * pll_adjustment; + if (!freq_beta) + fail("Excessive oversampling"); - float gain_mu = 0.02 / (cstln_amp*cstln_amp) * 2; + float gain_mu = 0.02 / (cstln_amp * cstln_amp) * 2; - int max_meas = chunk_size/meas_decimation + 1; - // Largin margin on output_size because mu adjustments - // can lead to more than chunk_size/min_omega symbols. - while ( in.readable() >= chunk_size+1 && // +1 for interpolation - out.writable() >= chunk_size && - ( !freq_out || freq_out ->writable()>=max_meas ) && - ( !cstln_out || cstln_out->writable()>=max_meas ) ) { - - complex *pin=in.rd(), *pin0=pin, *pend=pin+chunk_size; - hardsymbol *pout=out.wr(), *pout0=pout; + int max_meas = chunk_size / meas_decimation + 1; + // Largin margin on output_size because mu adjustments + // can lead to more than chunk_size/min_omega symbols. + while (in.readable() >= chunk_size + 1 + && // +1 for interpolation + out.writable() >= chunk_size + && (!freq_out || freq_out->writable() >= max_meas) + && (!cstln_out || cstln_out->writable() >= max_meas)) + { - cu8 s; - u_angle symbol_arg = 0; // Exported for constellation viewer + complex *pin = in.rd(), *pin0 = pin, *pend = pin + chunk_size; + hardsymbol *pout = out.wr(), *pout0 = pout; - while ( pin < pend ) { - // Here mu is the time of the next symbol counted from 0 at pin. - if ( mu < 1 ) { - // Here 0<=mu<1 is the fractional time of the next symbol - // between pin and pin+1. + cu8 s; + u_angle symbol_arg = 0; // Exported for constellation viewer - // Derotate and interpolate + while (pin < pend) + { + // Here mu is the time of the next symbol counted from 0 at pin. + if (mu < 1) + { + // Here 0<=mu<1 is the fractional time of the next symbol + // between pin and pin+1. + + // Derotate and interpolate #if 0 // Phase only (does not work) - // Careful with the float/signed/unsigned casts - u_angle a0 = fast_arg(pin[0]) - phase; - u_angle a1 = fast_arg(pin[1]) - (phase+freqw); - s_angle da = a1 - a0; - symbol_arg = a0 + (s_angle)(da*mu); - s = arg_to_symbol(symbol_arg); + // Careful with the float/signed/unsigned casts + u_angle a0 = fast_arg(pin[0]) - phase; + u_angle a1 = fast_arg(pin[1]) - (phase+freqw); + s_angle da = a1 - a0; + symbol_arg = a0 + (s_angle)(da*mu); + s = arg_to_symbol(symbol_arg); #elif 1 // Linear by lookup-table. 1.2M on bench3bishs - polar *p0 = &lut_polar[pin[0].re][pin[0].im]; - u_angle a0 = (u_angle)(p0->a-phase) >> (16-RLUT_BITS); - cu8 *p0r = &lut_rect[a0][p0->r>>1]; - polar *p1 = &lut_polar[pin[1].re][pin[1].im]; - u_angle a1 = (u_angle)(p1->a-(phase+freqw)) >> (16-RLUT_BITS); - cu8 *p1r = &lut_rect[a1][p1->r>>1]; - s.re = (int)(p0r->re + (p1r->re-p0r->re)*mu); - s.im = (int)(p0r->im + (p1r->im-p0r->im)*mu); - symbol_arg = fast_arg(s); + polar *p0 = &lut_polar[pin[0].re][pin[0].im]; + u_angle a0 = (u_angle) (p0->a - phase) >> (16 - RLUT_BITS); + cu8 *p0r = &lut_rect[a0][p0->r >> 1]; + polar *p1 = &lut_polar[pin[1].re][pin[1].im]; + u_angle a1 = (u_angle) (p1->a - (phase + freqw)) + >> (16 - RLUT_BITS); + cu8 *p1r = &lut_rect[a1][p1->r >> 1]; + s.re = (int) (p0r->re + (p1r->re - p0r->re) * mu); + s.im = (int) (p0r->im + (p1r->im - p0r->im) * mu); + symbol_arg = fast_arg(s); #else // Linear floating-point, for reference - float a0 = -(int)phase*M_PI/32768; - float cosa0=cosf(a0), sina0=sinf(a0); - complex - p0r(((float)pin[0].re-128)*cosa0 - ((float)pin[0].im-128)*sina0, - ((float)pin[0].re-128)*sina0 + ((float)pin[0].im-128)*cosa0); - float a1 = -(int)(phase+freqw)*M_PI/32768; - float cosa1=cosf(a1), sina1=sinf(a1); - complex - p1r(((float)pin[1].re-128)*cosa1 - ((float)pin[1].im-128)*sina1, - ((float)pin[1].re-128)*sina1 + ((float)pin[1].im-128)*cosa1); - s.re = (int)(128 + p0r.re + (p1r.re-p0r.re)*mu); - s.im = (int)(128 + p0r.im + (p1r.im-p0r.im)*mu); - symbol_arg = fast_arg(s); + float a0 = -(int)phase*M_PI/32768; + float cosa0=cosf(a0), sina0=sinf(a0); + complex + p0r(((float)pin[0].re-128)*cosa0 - ((float)pin[0].im-128)*sina0, + ((float)pin[0].re-128)*sina0 + ((float)pin[0].im-128)*cosa0); + float a1 = -(int)(phase+freqw)*M_PI/32768; + float cosa1=cosf(a1), sina1=sinf(a1); + complex + p1r(((float)pin[1].re-128)*cosa1 - ((float)pin[1].im-128)*sina1, + ((float)pin[1].re-128)*sina1 + ((float)pin[1].im-128)*cosa1); + s.re = (int)(128 + p0r.re + (p1r.re-p0r.re)*mu); + s.im = (int)(128 + p0r.im + (p1r.im-p0r.im)*mu); + symbol_arg = fast_arg(s); #endif - int quadrant = symbol_arg >> 14; - static unsigned char quadrant_to_symbol[4] = { 0, 2, 3, 1 }; - *pout = quadrant_to_symbol[quadrant]; - ++pout; + int quadrant = symbol_arg >> 14; + static unsigned char quadrant_to_symbol[4] = + { 0, 2, 3, 1 }; + *pout = quadrant_to_symbol[quadrant]; + ++pout; - // PLL - s_angle phase_error = (s_angle)(symbol_arg&16383) - 8192; - phase += (phase_error * freq_alpha + 32768) >> 16; - freqw += (phase_error * freq_beta + 32768*256) >> 24; - - // Modified Mueller and Müller - // mu[k]=real((c[k]-c[k-2])*conj(p[k-1])-(p[k]-p[k-2])*conj(c[k-1])) - // =dot(c[k]-c[k-2],p[k-1]) - dot(p[k]-p[k-2],c[k-1]) - // p = received signals - // c = decisions (constellation points) - hist[2] = hist[1]; - hist[1] = hist[0]; + // PLL + s_angle phase_error = (s_angle) (symbol_arg & 16383) - 8192; + phase += (phase_error * freq_alpha + 32768) >> 16; + freqw += (phase_error * freq_beta + 32768 * 256) >> 24; + + // Modified Mueller and Müller + // mu[k]=real((c[k]-c[k-2])*conj(p[k-1])-(p[k]-p[k-2])*conj(c[k-1])) + // =dot(c[k]-c[k-2],p[k-1]) - dot(p[k]-p[k-2],c[k-1]) + // p = received signals + // c = decisions (constellation points) + hist[2] = hist[1]; + hist[1] = hist[0]; #define HIST_FLOAT 0 #if HIST_FLOAT - hist[0].p.re = (float)s.re - 128; - hist[0].p.im = (float)s.im - 128; + hist[0].p.re = (float)s.re - 128; + hist[0].p.im = (float)s.im - 128; - cu8 cp = arg_to_symbol((symbol_arg&49152)+8192); - hist[0].c.re = (float)cp.re - 128; - hist[0].c.im = (float)cp.im - 128; + cu8 cp = arg_to_symbol((symbol_arg&49152)+8192); + hist[0].c.re = (float)cp.re - 128; + hist[0].c.im = (float)cp.im - 128; - float muerr = - ( (hist[0].p.re-hist[2].p.re)*hist[1].c.re + - (hist[0].p.im-hist[2].p.im)*hist[1].c.im ) - - ( (hist[0].c.re-hist[2].c.re)*hist[1].p.re + - (hist[0].c.im-hist[2].c.im)*hist[1].p.im ); + float muerr = + ( (hist[0].p.re-hist[2].p.re)*hist[1].c.re + + (hist[0].p.im-hist[2].p.im)*hist[1].c.im ) - + ( (hist[0].c.re-hist[2].c.re)*hist[1].p.re + + (hist[0].c.im-hist[2].c.im)*hist[1].p.im ); #else - hist[0].p = s; - hist[0].c = arg_to_symbol((symbol_arg&49152)+8192); + hist[0].p = s; + hist[0].c = arg_to_symbol((symbol_arg & 49152) + 8192); - int muerr = - ( (signed char)(hist[0].p.re-hist[2].p.re)*((int)hist[1].c.re-128) + - (signed char)(hist[0].p.im-hist[2].p.im)*((int)hist[1].c.im-128) ) - - ( (signed char)(hist[0].c.re-hist[2].c.re)*((int)hist[1].p.re-128) + - (signed char)(hist[0].c.im-hist[2].c.im)*((int)hist[1].p.im-128) ); + int muerr = + ((signed char) (hist[0].p.re - hist[2].p.re) + * ((int) hist[1].c.re - 128) + + (signed char) (hist[0].p.im - hist[2].p.im) + * ((int) hist[1].c.im - 128)) + - ((signed char) (hist[0].c.re + - hist[2].c.re) + * ((int) hist[1].p.re - 128) + + (signed char) (hist[0].c.im + - hist[2].c.im) + * ((int) hist[1].p.im - 128)); #endif - float mucorr = muerr * gain_mu; - const float max_mucorr = 0.1; - // TBD Optimize out statically - if ( mucorr < -max_mucorr ) mucorr = -max_mucorr; - if ( mucorr > max_mucorr ) mucorr = max_mucorr; - mu += mucorr; - mu += omega; // Next symbol time; - } // mu<1 - - // Next sample - ++pin; - --mu; - phase += freqw; - } // chunk_size - - in.read(pin-pin0); - out.written(pout-pout0); + float mucorr = muerr * gain_mu; + const float max_mucorr = 0.1; + // TBD Optimize out statically + if (mucorr < -max_mucorr) + mucorr = -max_mucorr; + if (mucorr > max_mucorr) + mucorr = max_mucorr; + mu += mucorr; + mu += omega; // Next symbol time; + } // mu<1 - if ( symbol_arg && cstln_out ) - // Output the last interpolated PSK symbol, max once per chunk_size - cstln_out->write(s); - - // This is best done periodically ouside the inner loop, - // but will cause non-deterministic output. - - if ( ! allow_drift ) { - if ( freqw < min_freqw || freqw > max_freqw ) - freqw = (max_freqw+min_freqw) / 2; - } - - // Output measurements - - meas_count += pin-pin0; - while ( meas_count >= meas_decimation ) { - meas_count -= meas_decimation; - if ( freq_out ) - freq_out->write((float)freqw / 65536); - } - - } // Work to do - } - - private: + // Next sample + ++pin; + --mu; + phase += freqw; + } // chunk_size - struct polar { u_angle a; unsigned char r; } lut_polar[256][256]; - u_angle fast_arg(const cu8 &c) { - // TBD read cu8 as u16 index, same endianness as in init() - return lut_polar[c.re][c.im].a; + in.read(pin - pin0); + out.written(pout - pout0); + + if (symbol_arg && cstln_out) + // Output the last interpolated PSK symbol, max once per chunk_size + cstln_out->write(s); + + // This is best done periodically ouside the inner loop, + // but will cause non-deterministic output. + + if (!allow_drift) + { + if (freqw < min_freqw || freqw > max_freqw) + freqw = (max_freqw + min_freqw) / 2; + } + + // Output measurements + + meas_count += pin - pin0; + while (meas_count >= meas_decimation) + { + meas_count -= meas_decimation; + if (freq_out) + freq_out->write((float) freqw / 65536); + } + + } // Work to do + } + +private: + + struct polar + { + u_angle a; + unsigned char r; + } lut_polar[256][256]; + u_angle fast_arg(const cu8 &c) + { + // TBD read cu8 as u16 index, same endianness as in init() + return lut_polar[c.re][c.im].a; } cu8 lut_rect[RLUT_ANGLES][256]; cu8 lut_sincos[65536]; - cu8 arg_to_symbol(u_angle a) { return lut_sincos[a]; } - void init_lookup_tables() { - for ( int i=0; i<256; ++i ) - for ( int q=0; q<256; ++q ) { - // Don't cast float to unsigned directly - lut_polar[i][q].a = (s_angle)(atan2f(q-128,i-128)*65536/(2*M_PI)); - lut_polar[i][q].r = (int)hypotf(i-128,q-128); - } - for ( unsigned long a=0; a<65536; ++a ) { - float f = 2*M_PI * a / 65536; - lut_sincos[a].re = 128 + cstln_amp*cosf(f); - lut_sincos[a].im = 128 + cstln_amp*sinf(f); - } - for ( int a=0; a p; // Received symbol - complex c; // Matched constellation point + complex p; // Received symbol + complex c;// Matched constellation point #else - cu8 p; // Received symbol - cu8 c; // Matched constellation point + cu8 p; // Received symbol + cu8 c; // Matched constellation point #endif } hist[3]; pipereader in; @@ -1172,224 +1408,268 @@ namespace leansdr { unsigned long meas_count; pipewriter *freq_out, *mer_out; pipewriter *cstln_out; - }; // fast_qpsk_receiver - +}; +// fast_qpsk_receiver - // CONSTELLATION TRANSMITTER +// CONSTELLATION TRANSMITTER - // Maps symbols to I/Q points. +// Maps symbols to I/Q points. - template - struct cstln_transmitter : runnable { +template +struct cstln_transmitter: runnable +{ cstln_lut<256> *cstln; - cstln_transmitter(scheduler *sch, - pipebuf &_in, pipebuf< complex > &_out) - : runnable(sch, "cstln_transmitter"), - in(_in), out(_out) + + cstln_transmitter(scheduler *sch, pipebuf &_in, pipebuf > &_out) : + runnable(sch, "cstln_transmitter"), + cstln(0), + in(_in), + out(_out) { } - void run() { - if ( ! cstln ) fail("constellation not set"); - int count = min(in.readable(), out.writable()); - u8 *pin=in.rd(), *pend=pin+count; - complex *pout = out.wr(); - for ( ; pin *cp = &cstln->symbols[*pin]; - pout->re = Zout + cp->re; - pout->im = Zout + cp->im; - } - in.read(count); - out.written(count); + + void run() + { + if (!cstln) + fail("constellation not set"); + int count = min(in.readable(), out.writable()); + u8 *pin = in.rd(), *pend = pin + count; + complex *pout = out.wr(); + for (; pin < pend; ++pin, ++pout) + { + complex *cp = &cstln->symbols[*pin]; + pout->re = Zout + cp->re; + pout->im = Zout + cp->im; + } + in.read(count); + out.written(count); } - private: + +private: pipereader in; - pipewriter< complex > out; - }; // cstln_transmitter + pipewriter > out; +}; +// cstln_transmitter +// FREQUENCY SHIFTER - // FREQUENCY SHIFTER +// Resolution is sample_freq/65536. - // Resolution is sample_freq/65536. - - template - struct rotator : runnable { - rotator(scheduler *sch, pipebuf< complex > &_in, - pipebuf< complex > &_out, float freq) - : runnable(sch, "rotator"), - in(_in), out(_out), index(0) { - int ifreq = freq * 65536; - if ( sch->debug ) - fprintf(stderr, "Rotate: req=%f real=%f\n", freq, ifreq/65536.0); - for ( int i=0; i<65536; ++i ) { - lut_cos[i] = cosf(2*M_PI * i * ifreq / 65536); - lut_sin[i] = sinf(2*M_PI * i * ifreq / 65536); - } +template +struct rotator: runnable +{ + rotator(scheduler *sch, pipebuf > &_in, pipebuf > &_out, float freq) : + runnable(sch, "rotator"), + in(_in), + out(_out), + index(0) + { + int ifreq = freq * 65536; + if (sch->debug) + fprintf(stderr, "Rotate: req=%f real=%f\n", freq, ifreq / 65536.0); + for (int i = 0; i < 65536; ++i) + { + lut_cos[i] = cosf(2 * M_PI * i * ifreq / 65536); + lut_sin[i] = sinf(2 * M_PI * i * ifreq / 65536); + } } - void run() { - unsigned long count = min(in.readable(), out.writable()); - complex *pin = in.rd(), *pend = pin+count; - complex *pout = out.wr(); - for ( ; pinre = pin->re*c - pin->im*s; - pout->im = pin->re*s + pin->im*c; - } - in.read(count); - out.written(count); + + void run() + { + unsigned long count = min(in.readable(), out.writable()); + complex *pin = in.rd(), *pend = pin + count; + complex *pout = out.wr(); + for (; pin < pend; ++pin, ++pout, ++index) + { + float c = lut_cos[index]; + float s = lut_sin[index]; + pout->re = pin->re * c - pin->im * s; + pout->im = pin->re * s + pin->im * c; + } + in.read(count); + out.written(count); } - private: - pipereader< complex > in; - pipewriter< complex > out; + +private: + pipereader > in; + pipewriter > out; float lut_cos[65536]; float lut_sin[65536]; unsigned short index; // Current phase - }; // rotator +}; +// rotator +// SPECTRUM-BASED CNR ESTIMATOR - // SPECTRUM-BASED CNR ESTIMATOR +// Assumes that the spectrum is as follows: +// +// ---|--noise---|-roll-off-|---carrier+noise----|-roll-off-|---noise--|--- +// | (bw/2) | (bw) | (bw/2) | (bw) | (bw/2) | +// +// Maximum roll-off 0.5 - // Assumes that the spectrum is as follows: - // - // ---|--noise---|-roll-off-|---carrier+noise----|-roll-off-|---noise--|--- - // | (bw/2) | (bw) | (bw/2) | (bw) | (bw/2) | - // - // Maximum roll-off 0.5 - - template - struct cnr_fft : runnable { - cnr_fft(scheduler *sch, pipebuf< complex > &_in, pipebuf &_out, - float _bandwidth, int nfft=4096) - : runnable(sch, "cnr_fft"), - bandwidth(_bandwidth), freq_tap(NULL), tap_multiplier(1), - decimation(1048576), kavg(0.1), - in(_in), out(_out), - fft(nfft), avgpower(NULL), phase(0) { - if ( bandwidth > 0.25 ) - fail("CNR estimator requires Fsampling > 4x Fsignal"); +template +struct cnr_fft: runnable +{ + cnr_fft(scheduler *sch, pipebuf > &_in, pipebuf &_out, float _bandwidth, int nfft = 4096) : + runnable(sch, "cnr_fft"), + bandwidth(_bandwidth), + freq_tap(NULL), + tap_multiplier(1), + decimation(1048576), + kavg(0.1), + in(_in), + out(_out), + fft(nfft), + avgpower(NULL), + phase(0) + { + if (bandwidth > 0.25) + fail("CNR estimator requires Fsampling > 4x Fsignal"); } float bandwidth; - float *freq_tap, tap_multiplier; + float *freq_tap, tap_multiplier; int decimation; float kavg; - void run() { - while ( in.readable()>=fft.n && out.writable()>=1 ) { - phase += fft.n; - if ( phase >= decimation ) { - phase -= decimation; - do_cnr(); - } - in.read(fft.n); - } - } - - private: - - void do_cnr() { - float center_freq = freq_tap ? *freq_tap * tap_multiplier : 0; - int icf = floor(center_freq*fft.n+0.5); - complex data[fft.n]; - memcpy(data, in.rd(), fft.n*sizeof(data[0])); - fft.inplace(data, true); - T power[fft.n]; - for ( int i=0; i0 && n2>0) ? 10 * logf(c2/n2)/logf(10) : -50; - out.write(cnr); + void run() + { + while (in.readable() >= fft.n && out.writable() >= 1) + { + phase += fft.n; + if (phase >= decimation) + { + phase -= decimation; + do_cnr(); + } + in.read(fft.n); + } } - float avg__slots(int i0, int i1) { // i0 <= i1 - T s = 0; - for ( int i=i0; i<=i1; ++i ) s += avgpower[i&(fft.n-1)]; - return s / (i1-i0+1); +private: + + void do_cnr() + { + float center_freq = freq_tap ? *freq_tap * tap_multiplier : 0; + int icf = floor(center_freq * fft.n + 0.5); + complex data[fft.n]; + memcpy(data, in.rd(), fft.n * sizeof(data[0])); + fft.inplace(data, true); + T power[fft.n]; + for (unsigned int i = 0; i < fft.n; ++i) + power[i] = data[i].re * data[i].re + data[i].im * data[i].im; + if (!avgpower) + { + // Initialize with first spectrum + avgpower = new T[fft.n]; + memcpy(avgpower, power, fft.n * sizeof(avgpower[0])); + } + // Accumulate and low-pass filter + for (unsigned int i = 0; i < fft.n; ++i) + avgpower[i] = avgpower[i] * (1 - kavg) + power[i] * kavg; + + int bw__slots = (bandwidth / 4) * fft.n; + if (!bw__slots) + return; + // Measure carrier+noise in center band + float c2plusn2 = avg__slots(icf - bw__slots, icf + bw__slots); + // Measure noise left and right of roll-off zones + float n2 = (avg__slots(icf - bw__slots * 4, icf - bw__slots * 3) + + avg__slots(icf + bw__slots * 3, icf + bw__slots * 4)) / 2; + float c2 = c2plusn2 - n2; + float cnr = (c2 > 0 && n2 > 0) ? 10 * logf(c2 / n2) / logf(10) : -50; + out.write(cnr); } - - pipereader< complex > in; - pipewriter< float > out; + + float avg__slots(int i0, int i1) + { // i0 <= i1 + T s = 0; + for (int i = i0; i <= i1; ++i) + s += avgpower[i & (fft.n - 1)]; + return s / (i1 - i0 + 1); + } + + pipereader > in; + pipewriter out; cfft_engine fft; T *avgpower; int phase; - }; // cnr_fft +}; +// cnr_fft - template - struct spectrum : runnable { +template +struct spectrum: runnable +{ static const int nfft = 1024; - spectrum(scheduler *sch, pipebuf< complex > &_in, - pipebuf &_out) - : runnable(sch, "spectrum"), - decimation(1048576), kavg(0.1), - in(_in), out(_out), - fft(nfft), avgpower(NULL), phase(0) { + spectrum(scheduler *sch, pipebuf > &_in, pipebuf &_out) : + runnable(sch, "spectrum"), + decimation(1048576), + kavg(0.1), + in(_in), + out(_out), + fft(nfft), + avgpower(NULL), + phase(0) + { } int decimation; float kavg; - void run() { - while ( in.readable()>=fft.n && out.writable()>=1 ) { - phase += fft.n; - if ( phase >= decimation ) { - phase -= decimation; - do_spectrum(); - } - in.read(fft.n); - } + void run() + { + while (in.readable() >= fft.n && out.writable() >= 1) + { + phase += fft.n; + if (phase >= decimation) + { + phase -= decimation; + do_spectrum(); + } + in.read(fft.n); + } } - private: +private: - void do_spectrum() { - complex data[fft.n]; - memcpy(data, in.rd(), fft.n*sizeof(data[0])); - fft.inplace(data, true); - float power[nfft]; - for ( int i=0; i data[fft.n]; + memcpy(data, in.rd(), fft.n * sizeof(data[0])); + fft.inplace(data, true); + float power[nfft]; + for (int i = 0; i < fft.n; ++i) + power[i] = (float) data[i].re * data[i].re + + (float) data[i].im * data[i].im; + if (!avgpower) + { + // Initialize with first spectrum + avgpower = new float[fft.n]; + memcpy(avgpower, power, fft.n * sizeof(avgpower[0])); + } + // Accumulate and low-pass filter + for (int i = 0; i < fft.n; ++i) + avgpower[i] = avgpower[i] * (1 - kavg) + power[i] * kavg; - // Reuse power[] - for ( int i=0; i > in; - pipewriter< float[nfft] > out; + pipereader > in; + pipewriter out; cfft_engine fft; T *avgpower; int phase; - }; // spectrum +}; +// spectrum - -} // namespace +}// namespace #endif // LEANSDR_SDR_H