diff --git a/CMakeLists.txt b/CMakeLists.txt index ecaf08b76..e1f288ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ set(sdrbase_SOURCES sdrbase/dsp/basebandsamplesink.cpp sdrbase/dsp/basebandsamplesource.cpp sdrbase/dsp/nullsink.cpp + sdrbase/dsp/recursivefilters.cpp sdrbase/dsp/spectrumscopecombovis.cpp sdrbase/dsp/spectrumscopengcombovis.cpp sdrbase/dsp/scopevis.cpp @@ -235,6 +236,7 @@ set(sdrbase_HEADERS sdrbase/dsp/phasediscri.h sdrbase/dsp/phaselock.h sdrbase/dsp/pidcontroller.h + sdrbase/dsp/recursivefilters.h sdrbase/dsp/samplesinkfifo.h sdrbase/dsp/samplesourcefifo.h sdrbase/dsp/samplesinkfifodoublebuffered.h diff --git a/plugins/channelrx/demodatv/atvdemod.cpp b/plugins/channelrx/demodatv/atvdemod.cpp index 81bc6557e..7d1bdd7e7 100644 --- a/plugins/channelrx/demodatv/atvdemod.cpp +++ b/plugins/channelrx/demodatv/atvdemod.cpp @@ -51,6 +51,8 @@ ATVDemod::ATVDemod() : m_fltAmpDelta(1.0), m_fltAmpLineAverage(0.0f), m_intNumberSamplePerTop(0), + m_bfoPLL(200/1000000, 100/1000000, 0.01), + m_bfoFilter(200.0, 1000000.0, 0.9), m_interpolatorDistanceRemain(0.0f), m_interpolatorDistance(1.0f), m_DSBFilter(0), @@ -113,14 +115,16 @@ void ATVDemod::configureRF( float fltRFBandwidth, float fltRFOppBandwidth, bool blnFFTFiltering, - bool blnDecimatorEnable) + bool blnDecimatorEnable, + float fltBFOFrequency) { Message* msgCmd = MsgConfigureRFATVDemod::create( enmModulation, fltRFBandwidth, fltRFOppBandwidth, blnFFTFiltering, - blnDecimatorEnable); + blnDecimatorEnable, + fltBFOFrequency); objMessageQueue->push(msgCmd); } @@ -238,7 +242,7 @@ void ATVDemod::demod(Complex& c) int n_out; fftfilt::cmplx *filtered; - n_out = m_DSBFilter->runAsym(c, &filtered, m_objRFRunning.m_enmModulation != ATV_VAML); // all usb except explicitely lsb + n_out = m_DSBFilter->runAsym(c, &filtered, m_objRFRunning.m_enmModulation != ATV_LSB); // all usb except explicitely lsb if (n_out > 0) { @@ -308,7 +312,7 @@ void ATVDemod::demod(Complex& c) m_fltBufferQ[0]=fltNormQ; } - else if ((m_objRFRunning.m_enmModulation == ATV_AM) || (m_objRFRunning.m_enmModulation == ATV_VAMU) || (m_objRFRunning.m_enmModulation == ATV_VAML)) + else if (m_objRFRunning.m_enmModulation == ATV_AM) { //Amplitude AM fltVal = fltNorm; @@ -329,6 +333,39 @@ void ATVDemod::demod(Complex& c) fltVal -= m_fltAmpMin; fltVal /=m_fltAmpDelta; } + else if ((m_objRFRunning.m_enmModulation == ATV_USB) || (m_objRFRunning.m_enmModulation == ATV_LSB)) + { + Real bfoValues[2]; + float fltFiltered = m_bfoFilter.run(fltI); + m_bfoPLL.process(fltFiltered, bfoValues); + + // do the mix + + float mixI = fltI * bfoValues[0] - fltQ * bfoValues[1]; + float mixQ = fltI * bfoValues[1] + fltQ * bfoValues[0]; + + if (m_objRFRunning.m_enmModulation == ATV_USB) { + fltVal = (mixI + mixQ); + } else { + fltVal = (mixI - mixQ); + } + + //********** Mini and Maxi Amplitude tracking ********** + + if(fltValm_fltEffMax) + { + m_fltEffMax=fltVal; + } + + //Normalisation + fltVal -= m_fltAmpMin; + fltVal /=m_fltAmpDelta; + } else { fltVal = 0.0f; @@ -560,14 +597,14 @@ bool ATVDemod::handleMessage(const Message& cmd) m_objConfig = objCfg.m_objMsgConfig; qDebug() << "ATVDemod::handleMessage: MsgConfigureATVDemod:" - << " m_fltVoltLevelSynchroBlack" << m_objConfig.m_fltVoltLevelSynchroBlack - << " m_fltVoltLevelSynchroTop" << m_objConfig.m_fltVoltLevelSynchroTop - << " m_fltFramePerS" << m_objConfig.m_fltFramePerS - << " m_fltLineDurationUs" << m_objConfig.m_fltLineDurationUs - << " m_fltRatioOfRowsToDisplay" << m_objConfig.m_fltRatioOfRowsToDisplay - << " m_fltTopDurationUs" << m_objConfig.m_fltTopDurationUs - << " m_blnHSync" << m_objConfig.m_blnHSync - << " m_blnVSync" << m_objConfig.m_blnVSync; + << " m_fltVoltLevelSynchroBlack:" << m_objConfig.m_fltVoltLevelSynchroBlack + << " m_fltVoltLevelSynchroTop:" << m_objConfig.m_fltVoltLevelSynchroTop + << " m_fltFramePerS:" << m_objConfig.m_fltFramePerS + << " m_fltLineDurationUs:" << m_objConfig.m_fltLineDurationUs + << " m_fltRatioOfRowsToDisplay:" << m_objConfig.m_fltRatioOfRowsToDisplay + << " m_fltTopDurationUs:" << m_objConfig.m_fltTopDurationUs + << " m_blnHSync:" << m_objConfig.m_blnHSync + << " m_blnVSync:" << m_objConfig.m_blnVSync; applySettings(); @@ -580,11 +617,12 @@ bool ATVDemod::handleMessage(const Message& cmd) m_objRFConfig = objCfg.m_objMsgConfig; qDebug() << "ATVDemod::handleMessage: MsgConfigureRFATVDemod:" - << " m_enmModulation" << m_objRFConfig.m_enmModulation - << " m_fltRFBandwidth" << m_objRFConfig.m_fltRFBandwidth - << " m_fltRFOppBandwidth" << m_objRFConfig.m_fltRFOppBandwidth - << " m_blnFFTFiltering" << m_objRFConfig.m_blnFFTFiltering - << " m_blnDecimatorEnable" << m_objRFConfig.m_blndecimatorEnable; + << " m_enmModulation:" << m_objRFConfig.m_enmModulation + << " m_fltRFBandwidth:" << m_objRFConfig.m_fltRFBandwidth + << " m_fltRFOppBandwidth:" << m_objRFConfig.m_fltRFOppBandwidth + << " m_blnFFTFiltering:" << m_objRFConfig.m_blnFFTFiltering + << " m_blnDecimatorEnable:" << m_objRFConfig.m_blndecimatorEnable + << " m_fltBFOFrequency:" << m_objRFConfig.m_fltBFOFrequency; applySettings(); @@ -677,6 +715,14 @@ void ATVDemod::applySettings() m_objSettingsMutex.unlock(); } + if ((m_objConfigPrivate.m_intTVSampleRate != m_objRunningPrivate.m_intTVSampleRate) + || (m_objRFConfig.m_fltBFOFrequency != m_objRFRunning.m_fltBFOFrequency)) + { + m_bfoPLL.configure(m_objRFConfig.m_fltBFOFrequency / m_objConfigPrivate.m_intTVSampleRate, + 100.0 / m_objConfigPrivate.m_intTVSampleRate, + 0.01); + m_bfoFilter.setFrequencies(m_objRFConfig.m_fltBFOFrequency, m_objConfigPrivate.m_intTVSampleRate); + } m_objRunning = m_objConfig; m_objRFRunning = m_objRFConfig; @@ -692,3 +738,15 @@ int ATVDemod::getEffectiveSampleRate() { return m_objRFRunning.m_blndecimatorEnable ? m_objRunningPrivate.m_intTVSampleRate : m_objRunning.m_intSampleRate; } + +bool ATVDemod::getBFOLocked() +{ + if ((m_objRFRunning.m_enmModulation == ATV_USB) || (m_objRFRunning.m_enmModulation == ATV_LSB)) + { + return m_bfoPLL.locked(); + } + else + { + return false; + } +} diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index aefe49ebb..cded66e58 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -31,6 +31,8 @@ #include "dsp/movingaverage.h" #include "dsp/fftfilt.h" #include "dsp/agc.h" +#include "dsp/phaselock.h" +#include "dsp/recursivefilters.h" #include "audio/audiofifo.h" #include "util/message.h" #include "atvscreen.h" @@ -46,8 +48,8 @@ public: ATV_FM1, //!< Classical frequency modulation with discriminator #1 ATV_FM2, //!< Classical frequency modulation with discriminator #2 ATV_AM, //!< Classical amplitude modulation - ATV_VAMU, //!< AM with vestigial lower side band (main signal is in the upper side) - ATV_VAML //!< AM with vestigial upper side band (main signal is in the lower side) + ATV_USB, //!< AM with vestigial lower side band (main signal is in the upper side) + ATV_LSB //!< AM with vestigial upper side band (main signal is in the lower side) }; struct ATVConfig @@ -84,6 +86,7 @@ public: float m_fltRFOppBandwidth; bool m_blnFFTFiltering; bool m_blndecimatorEnable; + float m_fltBFOFrequency; ATVRFConfig() : m_intFrequencyOffset(0), @@ -91,7 +94,8 @@ public: m_fltRFBandwidth(0), m_fltRFOppBandwidth(0), m_blnFFTFiltering(false), - m_blndecimatorEnable(false) + m_blndecimatorEnable(false), + m_fltBFOFrequency(0.0f) { } }; @@ -135,7 +139,8 @@ public: float fltRFBandwidth, float fltRFOppBandwidth, bool blnFFTFiltering, - bool blndecimatorEnable); + bool blndecimatorEnable, + float fltBFOFrequency); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); virtual void start(); @@ -146,6 +151,7 @@ public: int getSampleRate(); int getEffectiveSampleRate(); double getMagSq() const { return m_objMagSqAverage.average(); } //!< Beware this is scaled to 2^30 + bool getBFOLocked(); private: struct ATVConfigPrivate @@ -218,14 +224,16 @@ private: float fltRFBandwidth, float fltRFOppBandwidth, bool blnFFTFiltering, - bool blndecimatorEnable) + bool blndecimatorEnable, + int intBFOFrequency) { return new MsgConfigureRFATVDemod( enmModulation, fltRFBandwidth, fltRFOppBandwidth, blnFFTFiltering, - blndecimatorEnable); + blndecimatorEnable, + intBFOFrequency); } ATVRFConfig m_objMsgConfig; @@ -236,7 +244,8 @@ private: float fltRFBandwidth, float fltRFOppBandwidth, bool blnFFTFiltering, - bool blndecimatorEnable) : + bool blndecimatorEnable, + float fltBFOFrequency) : Message() { m_objMsgConfig.m_enmModulation = enmModulation; @@ -244,6 +253,7 @@ private: m_objMsgConfig.m_fltRFOppBandwidth = fltRFOppBandwidth; m_objMsgConfig.m_blnFFTFiltering = blnFFTFiltering; m_objMsgConfig.m_blndecimatorEnable = blndecimatorEnable; + m_objMsgConfig.m_fltBFOFrequency = fltBFOFrequency; } }; @@ -286,6 +296,8 @@ private: MovingAverage m_objMagSqAverage; NCO m_nco; + SimplePhaseLock m_bfoPLL; + SecondOrderRecursiveFilter m_bfoFilter; // Interpolator group for decimation and/or double sideband RF filtering Interpolator m_interpolator; diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 5eb987e06..ff34fe4fd 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -105,6 +105,7 @@ QByteArray ATVDemodGUI::serialize() const s.writeBool(11, ui->halfImage->isChecked()); s.writeS32(12, ui->rfBW->value()); s.writeS32(13, ui->rfOppBW->value()); + s.writeS32(14, ui->bfo->value()); return s.final(); } @@ -160,6 +161,8 @@ bool ATVDemodGUI::deserialize(const QByteArray& arrData) ui->rfBW->setValue(tmp); d.readS32(13, &tmp, 10); ui->rfOppBW->setValue(tmp); + d.readS32(14, &tmp, 10); + ui->bfo->setValue(tmp); blockApplySettings(false); m_objChannelMarker.blockSignals(false); @@ -336,7 +339,8 @@ void ATVDemodGUI::applyRFSettings() ui->rfBW->value() * 100000.0f, ui->rfOppBW->value() * 100000.0f, ui->rfFiltering->isChecked(), - ui->decimatorEnable->isChecked()); + ui->decimatorEnable->isChecked(), + ui->bfo->value() * 10.0f); } } @@ -349,9 +353,9 @@ void ATVDemodGUI::setChannelMarkerBandwidth() m_objChannelMarker.setBandwidth(ui->rfBW->value()*100000); m_objChannelMarker.setOppositeBandwidth(ui->rfOppBW->value()*100000); - if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_VAML) { + if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_LSB) { m_objChannelMarker.setSidebands(ChannelMarker::vlsb); - } else if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_VAMU) { + } else if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_USB) { m_objChannelMarker.setSidebands(ChannelMarker::vusb); } else { m_objChannelMarker.setSidebands(ChannelMarker::vusb); @@ -413,6 +417,12 @@ void ATVDemodGUI::tick() m_objMagSqAverage.feed(m_objATVDemod->getMagSq()); double magSqDB = CalcDb::dbPower(m_objMagSqAverage.average() / (1<<30)); ui->channePowerText->setText(tr("%1 dB").arg(magSqDB, 0, 'f', 1)); + + if (m_objATVDemod->getBFOLocked()) { + ui->bfoLockedLabel->setStyleSheet("QLabel { background-color : green; }"); + } else { + ui->bfoLockedLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } } m_intTickCount = 0; @@ -525,3 +535,9 @@ void ATVDemodGUI::on_deltaFrequencyMinus_toggled(bool minus) } } +void ATVDemodGUI::on_bfo_valueChanged(int value) +{ + ui->bfoText->setText(QString("%1").arg(value * 10.0, 0, 'f', 0)); + applyRFSettings(); +} + diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index cb1d1f3b7..94329d633 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -79,6 +79,7 @@ private slots: void on_decimatorEnable_toggled(bool checked); void on_deltaFrequency_changed(quint64 value); void on_deltaFrequencyMinus_toggled(bool minus); + void on_bfo_valueChanged(int value); private: Ui::ATVDemodGUI* ui; diff --git a/plugins/channelrx/demodatv/atvdemodgui.ui b/plugins/channelrx/demodatv/atvdemodgui.ui index 9513fb0b1..5c8653462 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.ui +++ b/plugins/channelrx/demodatv/atvdemodgui.ui @@ -40,7 +40,7 @@ 10 10 501 - 71 + 81 @@ -52,23 +52,6 @@ - - - - Toggle decimator on/off - - - - - - - :/arrow_down.png:/arrow_down.png - - - true - - - @@ -124,6 +107,30 @@ + + + + Qt::Vertical + + + + + + + Toggle decimator on/off + + + + + + + :/arrow_down.png:/arrow_down.png + + + true + + + @@ -144,35 +151,77 @@ - - - Modulation type + + + Qt::Vertical + + + + + + + + 22 + 22 + + + + + + + :/carrier.png + + + + + + + + 22 + 22 + + + + BFO frequency adjust + + + -500 + + + 500 + + + 1 + + + 0 + + + + + + + + 40 + 0 + + + + BFO frequency (Hz) + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical - - - FM 1 - - - - - FM 2 - - - - - AM - - - - - VAMU - - - - - VAML - - @@ -211,6 +260,38 @@ + + + + Modulation type + + + + FM 1 + + + + + FM 2 + + + + + AM + + + + + USB + + + + + LSB + + + + diff --git a/sdrbase/dsp/phaselock.h b/sdrbase/dsp/phaselock.h index 66092b153..0d1b5e3e4 100644 --- a/sdrbase/dsp/phaselock.h +++ b/sdrbase/dsp/phaselock.h @@ -112,6 +112,24 @@ private: std::vector m_pps_events; }; +class SimplePhaseLock : public PhaseLock +{ +public: + SimplePhaseLock(Real freq, Real bandwidth, Real minsignal) : + PhaseLock(freq, bandwidth, minsignal) + {} + + virtual ~SimplePhaseLock() + {} + +protected: + virtual void processPhase(Real *samples_out) const + { + samples_out[0] = m_psin; // f Pilot + samples_out[1] = m_pcos; // f Pilot + } +}; + class StereoPhaseLock : public PhaseLock { public: diff --git a/sdrbase/dsp/recursivefilters.cpp b/sdrbase/dsp/recursivefilters.cpp new file mode 100644 index 000000000..b52adc68b --- /dev/null +++ b/sdrbase/dsp/recursivefilters.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "recursivefilters.h" + +SecondOrderRecursiveFilter::SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r) : + m_r(r), + m_frequencyRatio(centerFrequency/samplingFrequency) +{ + init(); +} + +SecondOrderRecursiveFilter::~SecondOrderRecursiveFilter() +{} + +void SecondOrderRecursiveFilter::setFrequencies(float samplingFrequency, float centerFrequency) +{ + m_frequencyRatio = centerFrequency / samplingFrequency; + init(); +} + +void SecondOrderRecursiveFilter::setR(float r) +{ + m_r = r; + init(); +} + +short SecondOrderRecursiveFilter::run(short sample) +{ + m_v[0] = ((1.0f - m_r) * (float) sample) + (2.0f * m_r * cos(2.0*M_PI*m_frequencyRatio) * m_v[1]) - (m_r * m_r * m_v[2]); + float y = m_v[0] - m_v[2]; + m_v[2] = m_v[1]; + m_v[1] = m_v[0]; + + return (short) y; +} + +float SecondOrderRecursiveFilter::run(float sample) +{ + m_v[0] = ((1.0f - m_r) * sample) + (2.0f * m_r * cos(2.0*M_PI*m_frequencyRatio) * m_v[1]) - (m_r * m_r * m_v[2]); + float y = m_v[0] - m_v[2]; + m_v[2] = m_v[1]; + m_v[1] = m_v[0]; + + return y; +} + +void SecondOrderRecursiveFilter::init() +{ + for (int i = 0; i < 3; i++) + { + m_v[i] = 0.0f; + } +} + diff --git a/sdrbase/dsp/recursivefilters.h b/sdrbase/dsp/recursivefilters.h new file mode 100644 index 000000000..26f6577f8 --- /dev/null +++ b/sdrbase/dsp/recursivefilters.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_DSP_RECURSIVEFILTERS_H_ +#define SDRBASE_DSP_RECURSIVEFILTERS_H_ + +/** + * \Brief: This is a second order bandpass filter using recursive method. r is in range ]0..1[ the higher the steeper the filter. + * inspired by:http://www.ece.umd.edu/~tretter/commlab/c6713slides/FSKSlides.pdf + */ +class SecondOrderRecursiveFilter +{ +public: + SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r); + ~SecondOrderRecursiveFilter(); + + void setFrequencies(float samplingFrequency, float centerFrequency); + void setR(float r); + short run(short sample); + float run(float sample); + +private: + void init(); + + float m_r; + float m_frequencyRatio; + float m_v[3]; +}; + + + +#endif /* SDRBASE_DSP_RECURSIVEFILTERS_H_ */