From b079fae8432eeedd34c2330c15d8664c897889d7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 16 Mar 2017 10:11:03 +0100 Subject: [PATCH] ATV Modulator: use assymetrical filter for vestigial sideband modulation --- plugins/channeltx/modatv/atvmod.cpp | 31 +++++++---- plugins/channeltx/modatv/atvmod.h | 36 +++++++------ plugins/channeltx/modatv/atvmodgui.cpp | 4 +- sdrbase/dsp/fftfilt.cxx | 74 +++++++++++++++++++------- sdrbase/dsp/fftfilt.h | 4 +- 5 files changed, 102 insertions(+), 47 deletions(-) diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index 2082ccc07..5add752a4 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -98,6 +98,7 @@ ATVMod::~ATVMod() void ATVMod::configure(MessageQueue* messageQueue, Real rfBandwidth, + Real rfOppBandwidth, ATVStd atvStd, ATVModInput atvModInput, Real uniformLevel, @@ -105,11 +106,11 @@ void ATVMod::configure(MessageQueue* messageQueue, bool videoPlayLoop, bool videoPlay, bool cameraPlay, - bool channelMute, - Real vestigialRatio) + bool channelMute) { Message* cmd = MsgConfigureATVMod::create( rfBandwidth, + rfOppBandwidth, atvStd, atvModInput, uniformLevel, @@ -117,8 +118,7 @@ void ATVMod::configure(MessageQueue* messageQueue, videoPlayLoop, videoPlay, cameraPlay, - channelMute, - vestigialRatio); + channelMute); messageQueue->push(cmd); } @@ -240,7 +240,7 @@ Complex& ATVMod::modulateVestigialSSB(Real& sample) Complex ci(sample, 0.0f); fftfilt::cmplx *filtered; - n_out = m_DSBFilter->runVestigial(ci, &filtered, m_running.m_atvModulation == ATVModulationVestigialUSB, m_running.m_vestigialRatio); + n_out = m_DSBFilter->runAsym(ci, &filtered, m_running.m_atvModulation == ATVModulationVestigialUSB); if (n_out > 0) { @@ -512,6 +512,7 @@ bool ATVMod::handleMessage(const Message& cmd) MsgConfigureATVMod& cfg = (MsgConfigureATVMod&) cmd; m_config.m_rfBandwidth = cfg.getRFBandwidth(); + m_config.m_rfOppBandwidth = cfg.getRFOppBandwidth(); m_config.m_atvModInput = cfg.getATVModInput(); m_config.m_atvStd = cfg.getATVStd(); m_config.m_uniformLevel = cfg.getUniformLevel(); @@ -520,12 +521,12 @@ bool ATVMod::handleMessage(const Message& cmd) m_config.m_videoPlay = cfg.getVideoPlay(); m_config.m_cameraPlay = cfg.getCameraPlay(); m_config.m_channelMute = cfg.getChannelMute(); - m_config.m_vestigialRatio = cfg.getVestigialRatio(); apply(); qDebug() << "ATVMod::handleMessage: MsgConfigureATVMod:" << " m_rfBandwidth: " << m_config.m_rfBandwidth + << " m_rfOppBandwidth: " << m_config.m_rfOppBandwidth << " m_atvStd: " << (int) m_config.m_atvStd << " m_atvModInput: " << (int) m_config.m_atvModInput << " m_uniformLevel: " << m_config.m_uniformLevel @@ -533,8 +534,7 @@ bool ATVMod::handleMessage(const Message& cmd) << " m_videoPlayLoop: " << m_config.m_videoPlayLoop << " m_videoPlay: " << m_config.m_videoPlay << " m_cameraPlay: " << m_config.m_cameraPlay - << " m_channelMute: " << m_config.m_channelMute - << " m_vestigialRatio: " << m_config.m_vestigialRatio; + << " m_channelMute: " << m_config.m_channelMute; return true; } @@ -654,11 +654,20 @@ void ATVMod::apply(bool force) memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1)); m_SSBFilterBufferIndex = 0; - m_DSBFilter->create_dsb_filter(m_config.m_rfBandwidth / m_config.m_outputSampleRate); + applyStandard(); // set all timings + m_settingsMutex.unlock(); + } + + if ((m_config.m_outputSampleRate != m_running.m_outputSampleRate) || + (m_config.m_rfOppBandwidth != m_running.m_rfOppBandwidth) || + (m_config.m_rfBandwidth != m_running.m_rfBandwidth) || force) + { + m_settingsMutex.lock(); + + m_DSBFilter->create_asym_filter(m_config.m_rfOppBandwidth / m_config.m_outputSampleRate, m_config.m_rfBandwidth / m_config.m_outputSampleRate); memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen)); m_DSBFilterBufferIndex = 0; - applyStandard(); // set all timings m_settingsMutex.unlock(); } @@ -673,6 +682,7 @@ void ATVMod::apply(bool force) m_running.m_outputSampleRate = m_config.m_outputSampleRate; m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; m_running.m_rfBandwidth = m_config.m_rfBandwidth; + m_running.m_rfOppBandwidth = m_config.m_rfOppBandwidth; m_running.m_atvModInput = m_config.m_atvModInput; m_running.m_atvStd = m_config.m_atvStd; m_running.m_uniformLevel = m_config.m_uniformLevel; @@ -681,7 +691,6 @@ void ATVMod::apply(bool force) m_running.m_videoPlay = m_config.m_videoPlay; m_running.m_cameraPlay = m_config.m_cameraPlay; m_running.m_channelMute = m_config.m_channelMute; - m_running.m_vestigialRatio = m_config.m_vestigialRatio; } int ATVMod::getSampleRateUnits(ATVStd std) diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h index 1b61fde6d..b368037b9 100644 --- a/plugins/channeltx/modatv/atvmod.h +++ b/plugins/channeltx/modatv/atvmod.h @@ -308,6 +308,7 @@ public: void configure(MessageQueue* messageQueue, Real rfBandwidth, + Real rfOppBandwidth, ATVStd atvStd, ATVModInput atvModInput, Real uniformLevel, @@ -315,8 +316,7 @@ public: bool videoPlayLoop, bool videoPlay, bool cameraPLay, - bool channelMute, - Real vestigialRatio); + bool channelMute); virtual void pull(Sample& sample); virtual void pullAudio(int nbSamples); // this is used for video signal actually @@ -346,6 +346,7 @@ private: public: Real getRFBandwidth() const { return m_rfBandwidth; } + Real getRFOppBandwidth() const { return m_rfOppBandwidth; } ATVStd getATVStd() const { return m_atvStd; } ATVModInput getATVModInput() const { return m_atvModInput; } Real getUniformLevel() const { return m_uniformLevel; } @@ -354,10 +355,10 @@ private: bool getVideoPlay() const { return m_videoPlay; } bool getCameraPlay() const { return m_cameraPlay; } bool getChannelMute() const { return m_channelMute; } - Real getVestigialRatio() const { return m_vestigialRatio; } static MsgConfigureATVMod* create( Real rfBandwidth, + Real rfOppBandwidth, ATVStd atvStd, ATVModInput atvModInput, Real uniformLevel, @@ -365,11 +366,11 @@ private: bool videoPlayLoop, bool videoPlay, bool cameraPlay, - bool channelMute, - Real vestigialRatio) + bool channelMute) { return new MsgConfigureATVMod( rfBandwidth, + rfOppBandwidth, atvStd, atvModInput, uniformLevel, @@ -377,12 +378,12 @@ private: videoPlayLoop, videoPlay, cameraPlay, - channelMute, - vestigialRatio); + channelMute); } private: Real m_rfBandwidth; + Real m_rfOppBandwidth; ATVStd m_atvStd; ATVModInput m_atvModInput; Real m_uniformLevel; @@ -391,10 +392,10 @@ private: bool m_videoPlay; bool m_cameraPlay; bool m_channelMute; - Real m_vestigialRatio; MsgConfigureATVMod( Real rfBandwidth, + Real rfOppBandwidth, ATVStd atvStd, ATVModInput atvModInput, Real uniformLevel, @@ -402,10 +403,10 @@ private: bool videoPlayLoop, bool videoPlay, bool cameraPlay, - bool channelMute, - Real vestigialRatio) : + bool channelMute) : Message(), m_rfBandwidth(rfBandwidth), + m_rfOppBandwidth(rfOppBandwidth), m_atvStd(atvStd), m_atvModInput(atvModInput), m_uniformLevel(uniformLevel), @@ -413,8 +414,7 @@ private: m_videoPlayLoop(videoPlayLoop), m_videoPlay(videoPlay), m_cameraPlay(cameraPlay), - m_channelMute(channelMute), - m_vestigialRatio(vestigialRatio) + m_channelMute(channelMute) { } }; @@ -450,7 +450,8 @@ private: { int m_outputSampleRate; //!< sample rate from channelizer qint64 m_inputFrequencyOffset; //!< offset from baseband center frequency - Real m_rfBandwidth; //!< Bandwidth of modulated signal + Real m_rfBandwidth; //!< Bandwidth of modulated signal or direct sideband for SSB / vestigial SSB + Real m_rfOppBandwidth; //!< Bandwidth of opposite sideband for vestigial SSB ATVStd m_atvStd; //!< Standard ATVModInput m_atvModInput; //!< Input source type Real m_uniformLevel; //!< Percentage between black and white for uniform screen display @@ -465,6 +466,7 @@ private: m_outputSampleRate(-1), m_inputFrequencyOffset(0), m_rfBandwidth(0), + m_rfOppBandwidth(0), m_atvStd(ATVStdPAL625), m_atvModInput(ATVModInputHBars), m_uniformLevel(0.5f), @@ -542,12 +544,16 @@ private: std::string m_overlayText; bool m_showOverlayText; + // Used for standard SSB fftfilt* m_SSBFilter; - fftfilt* m_DSBFilter; Complex* m_SSBFilterBuffer; - Complex* m_DSBFilterBuffer; int m_SSBFilterBufferIndex; + + // Used for vestigial SSB with asymmetrical filtering (needs double sideband scheme) + fftfilt* m_DSBFilter; + Complex* m_DSBFilterBuffer; int m_DSBFilterBufferIndex; + static const int m_ssbFftLen; static const float m_blackLevel; diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 81ed69083..b871f03be 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -512,6 +512,7 @@ void ATVModGUI::applySettings() m_atvMod->configure(m_atvMod->getInputMessageQueue(), ui->rfBW->value() * 100000.0f, + (ui->vestigial->value() / 100.0f) * ui->rfBW->value() * 100000.0f, (ATVMod::ATVStd) ui->standard->currentIndex(), (ATVMod::ATVModInput) ui->inputSelect->currentIndex(), ui->uniformLevel->value() / 100.0f, @@ -519,8 +520,7 @@ void ATVModGUI::applySettings() ui->playLoop->isChecked(), ui->playVideo->isChecked(), ui->playCamera->isChecked(), - ui->channelMute->isChecked(), - ui->vestigial->value() / 100.0f); + ui->channelMute->isChecked()); } } diff --git a/sdrbase/dsp/fftfilt.cxx b/sdrbase/dsp/fftfilt.cxx index a2f31616f..735767f42 100644 --- a/sdrbase/dsp/fftfilt.cxx +++ b/sdrbase/dsp/fftfilt.cxx @@ -54,11 +54,13 @@ void fftfilt::init_filter() fft = new g_fft(flen); filter = new cmplx[flen]; + filterOpp = new cmplx[flen]; data = new cmplx[flen]; output = new cmplx[flen2]; ovlbuf = new cmplx[flen2]; memset(filter, 0, flen * sizeof(cmplx)); + memset(filterOpp, 0, flen * sizeof(cmplx)); memset(data, 0, flen * sizeof(cmplx)); memset(output, 0, flen2 * sizeof(cmplx)); memset(ovlbuf, 0, flen2 * sizeof(cmplx)); @@ -92,6 +94,7 @@ fftfilt::~fftfilt() if (fft) delete fft; if (filter) delete [] filter; + if (filterOpp) delete [] filterOpp; if (data) delete [] data; if (output) delete [] output; if (ovlbuf) delete [] ovlbuf; @@ -162,6 +165,55 @@ void fftfilt::create_dsb_filter(float f2) } } +// Double the size of FFT used for equivalent SSB filter or assume FFT is half the size of the one used for SSB +// used with runAsym for in band / opposite band asymmetrical filtering. Can be used for vestigial sideband modulation. +void fftfilt::create_asym_filter(float fopp, float fin) +{ + // in band + // initialize the filter to zero + memset(filter, 0, flen * sizeof(cmplx)); + + for (int i = 0; i < flen2; i++) { + filter[i] = fsinc(fin, i, flen2); + filter[i] *= _blackman(i, flen2); + } + + fft->ComplexFFT(filter); + + // normalize the output filter for unity gain + float scale = 0, mag; + for (int i = 0; i < flen2; i++) { + mag = abs(filter[i]); + if (mag > scale) scale = mag; + } + if (scale != 0) { + for (int i = 0; i < flen; i++) + filter[i] /= scale; + } + + // opposite band + // initialize the filter to zero + memset(filterOpp, 0, flen * sizeof(cmplx)); + + for (int i = 0; i < flen2; i++) { + filterOpp[i] = fsinc(fopp, i, flen2); + filterOpp[i] *= _blackman(i, flen2); + } + + fft->ComplexFFT(filterOpp); + + // normalize the output filter for unity gain + scale = 0; + for (int i = 0; i < flen2; i++) { + mag = abs(filterOpp[i]); + if (mag > scale) scale = mag; + } + if (scale != 0) { + for (int i = 0; i < flen; i++) + filterOpp[i] /= scale; + } +} + // test bypass int fftfilt::noFilt(const cmplx & in, cmplx **out) { @@ -271,13 +323,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out) return flen2; } -// Version for vestigial sideband. You have to double the FFT size used for SSB. -int fftfilt::runVestigial(const cmplx & in, cmplx **out, bool usb, float vf) +// Version for asymmetrical sidebands. You have to double the FFT size used for SSB. +int fftfilt::runAsym(const cmplx & in, cmplx **out, bool usb) { - if (vf < 0.0f) vf = 0.0f; - if (vf > 1.0f) vf = 1.0f; - if (usb) vf = 1.0f - vf; - data[inptr++] = in; if (inptr < flen2) return 0; @@ -292,24 +340,14 @@ int fftfilt::runVestigial(const cmplx & in, cmplx **out, bool usb, float vf) for (int i = 1; i < flen2; i++) { data[i] *= filter[i]; // usb - - if (i > (int) (vf * flen2)) { // vestigial lsb - data[flen2 + i] *= filter[flen2 + i]; - } else { - data[flen2 + i] = 0; - } + data[flen2 + i] *= filterOpp[flen2 + i]; // lsb is the opposite } } else { for (int i = 1; i < flen2; i++) { - if (i < (int) (vf * flen2)) { // vestigial usb - data[i] *= filter[i]; - } else { - data[i] = 0; - } - + data[i] *= filterOpp[i]; // usb is the opposite data[flen2 + i] *= filter[flen2 + i]; // lsb } } diff --git a/sdrbase/dsp/fftfilt.h b/sdrbase/dsp/fftfilt.h index b1d0ecdf6..132160dae 100644 --- a/sdrbase/dsp/fftfilt.h +++ b/sdrbase/dsp/fftfilt.h @@ -23,12 +23,13 @@ public: // f1 > f2 ==> band reject void create_filter(float f1, float f2); void create_dsb_filter(float f2); + void create_asym_filter(float fopp, float fin); //!< two different filters for in band and opposite band int noFilt(const cmplx& in, cmplx **out); int runFilt(const cmplx& in, cmplx **out); int runSSB(const cmplx& in, cmplx **out, bool usb, bool getDC = true); int runDSB(const cmplx& in, cmplx **out); - int runVestigial(const cmplx & in, cmplx **out, bool usb, float vf); + int runAsym(const cmplx & in, cmplx **out, bool usb); //!< Asymmetrical fitering can be used for vestigial sideband protected: int flen; @@ -36,6 +37,7 @@ protected: g_fft *fft; g_fft *ift; cmplx *filter; + cmplx *filterOpp; cmplx *data; cmplx *ovlbuf; cmplx *output;