From e9f64a05f2c44bfda6395c98ccf3c5c23f719245 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 13 May 2018 17:27:24 +0200 Subject: [PATCH] AM demod: basic synchronous AM detection option --- plugins/channelrx/demodam/amdemod.cpp | 10 +++++ plugins/channelrx/demodam/amdemod.h | 43 +++++++++++++++++-- plugins/channelrx/demodam/amdemodgui.cpp | 20 +++++++++ plugins/channelrx/demodam/amdemodgui.h | 1 + plugins/channelrx/demodam/amdemodgui.ui | 25 +++++++++++ plugins/channelrx/demodam/amdemodplugin.cpp | 2 +- plugins/channelrx/demodam/amdemodsettings.cpp | 3 ++ plugins/channelrx/demodam/amdemodsettings.h | 1 + sdrbase/dsp/fftfilt.cxx | 5 ++- sdrbase/dsp/fftfilt.h | 2 +- 10 files changed, 106 insertions(+), 6 deletions(-) diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index fafa7fb69..b93284476 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -66,6 +66,7 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) : DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); + DSBFilter = new fftfilt((2.0f * m_settings.m_rfBandwidth) / m_audioSampleRate, 2 * 1024); applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); applySettings(m_settings, true); @@ -74,6 +75,10 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_pllFilt.create(101, m_audioSampleRate, 500.0); + m_pll.computeCoefficients(0.05, 0.707, 1000); + m_syncAMBuffIndex = 0; } AMDemod::~AMDemod() @@ -83,6 +88,7 @@ AMDemod::~AMDemod() m_deviceAPI->removeThreadedSink(m_threadedChannelizer); delete m_threadedChannelizer; delete m_channelizer; + delete DSBFilter; } void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused))) @@ -233,6 +239,8 @@ void AMDemod::applyAudioSampleRate(int sampleRate) m_bandpass.create(301, sampleRate, 300.0, m_settings.m_rfBandwidth / 2.0f); m_audioFifo.setSize(sampleRate); m_squelchDelayLine.resize(sampleRate/5); + DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate); + m_pllFilt.create(101, sampleRate, 500.0); m_settingsMutex.unlock(); m_audioSampleRate = sampleRate; @@ -273,6 +281,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) << " m_audioMute: " << settings.m_audioMute << " m_bandpassEnable: " << settings.m_bandpassEnable << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_pll: " << settings.m_pll << " force: " << force; if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || @@ -283,6 +292,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_rfBandwidth / 2.0f); + DSBFilter->create_dsb_filter((2.0f * settings.m_rfBandwidth) / (float) m_audioSampleRate); m_settingsMutex.unlock(); } diff --git a/plugins/channelrx/demodam/amdemod.h b/plugins/channelrx/demodam/amdemod.h index 9c32eb8ff..e9e380898 100644 --- a/plugins/channelrx/demodam/amdemod.h +++ b/plugins/channelrx/demodam/amdemod.h @@ -27,6 +27,9 @@ #include "util/movingaverage.h" #include "dsp/agc.h" #include "dsp/bandpass.h" +#include "dsp/lowpass.h" +#include "dsp/phaselockcomplex.h" +#include "dsp/fftfilt.h" #include "audio/audiofifo.h" #include "util/message.h" #include "util/doublebufferfifo.h" @@ -118,6 +121,7 @@ public: uint32_t getAudioSampleRate() const { return m_audioSampleRate; } double getMagSq() const { return m_magsq; } bool getSquelchOpen() const { return m_squelchOpen; } + bool getPllLocked() const { return m_settings.m_pll && m_pll.locked(); } void getMagSqLevels(double& avg, double& peak, int& nbSamples) { @@ -165,6 +169,11 @@ private: MovingAverageUtil m_movingAverage; SimpleAGC<4096> m_volumeAGC; Bandpass m_bandpass; + Lowpass > m_pllFilt; + PhaseLockComplex m_pll; + fftfilt* DSBFilter; + Real m_syncAMBuff[2*1024]; + uint32_t m_syncAMBuffIndex; AudioVector m_audioBuffer; uint32_t m_audioBufferFill; @@ -217,9 +226,37 @@ private: if (m_squelchOpen && !m_settings.m_audioMute) { - Real demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20)); - m_volumeAGC.feed(demod); - demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue(); + Real demod; + + if (m_settings.m_pll) + { + std::complex s(re, im); + s = m_pllFilt.filter(s); + m_pll.feed(s.real(), s.imag()); + float yr = re * m_pll.getImag() - im * m_pll.getReal(); + float yi = re * m_pll.getReal() + im * m_pll.getImag(); + + fftfilt::cmplx *sideband; + std::complex cs(yr, yi); + int n_out = DSBFilter->runDSB(cs, &sideband, false); + + for (int i = 0; i < n_out; i++) + { + m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag()); + m_syncAMBuffIndex = 0; + } + + m_syncAMBuffIndex = m_syncAMBuffIndex < 2*1024 ? m_syncAMBuffIndex : 0; + demod = m_syncAMBuff[m_syncAMBuffIndex++]*0.7*(SDR_RX_SCALEF/602.0f); + m_volumeAGC.feed(demod); + demod /= (10.0*m_volumeAGC.getValue()); + } + else + { + demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20)); + m_volumeAGC.feed(demod); + demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue(); + } if (m_settings.m_bandpassEnable) { diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index d9488350d..4cc4faf8d 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -139,6 +139,16 @@ void AMDemodGUI::on_deltaFrequency_changed(qint64 value) applySettings(); } +void AMDemodGUI::on_pll_toggled(bool checked) +{ + if (!checked) { + ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_settings.m_pll = checked; + applySettings(); +} + void AMDemodGUI::on_bandpassEnable_toggled(bool checked) { m_settings.m_bandpassEnable = checked; @@ -302,6 +312,7 @@ void AMDemodGUI::displaySettings() ui->audioMute->setChecked(m_settings.m_audioMute); ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable); + ui->pll->setChecked(m_settings.m_pll); blockApplySettings(false); } @@ -359,6 +370,15 @@ void AMDemodGUI::tick() } } + if (m_settings.m_pll) + { + if (m_amDemod->getPllLocked()) { + ui->pll->setStyleSheet("QToolButton { background-color : green; }"); + } else { + ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + } + m_tickCount++; } diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 14efddf93..464474a89 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -65,6 +65,7 @@ private: private slots: void on_deltaFrequency_changed(qint64 value); + void on_pll_toggled(bool checked); void on_bandpassEnable_toggled(bool checked); void on_rfBW_valueChanged(int value); void on_volume_valueChanged(int value); diff --git a/plugins/channelrx/demodam/amdemodgui.ui b/plugins/channelrx/demodam/amdemodgui.ui index b2d4fc427..6efd1cb09 100644 --- a/plugins/channelrx/demodam/amdemodgui.ui +++ b/plugins/channelrx/demodam/amdemodgui.ui @@ -129,6 +129,31 @@ + + + + Qt::Vertical + + + + + + + PLL for synchronous AM + + + + + + + :/unlocked.png + :/locked.png:/unlocked.png + + + true + + + diff --git a/plugins/channelrx/demodam/amdemodplugin.cpp b/plugins/channelrx/demodam/amdemodplugin.cpp index 1cffd61c3..3760131c7 100644 --- a/plugins/channelrx/demodam/amdemodplugin.cpp +++ b/plugins/channelrx/demodam/amdemodplugin.cpp @@ -9,7 +9,7 @@ const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = { QString("AM Demodulator"), - QString("3.14.5"), + QString("3.14.7"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodam/amdemodsettings.cpp b/plugins/channelrx/demodam/amdemodsettings.cpp index 53d7f8832..fb5192cb0 100644 --- a/plugins/channelrx/demodam/amdemodsettings.cpp +++ b/plugins/channelrx/demodam/amdemodsettings.cpp @@ -38,6 +38,7 @@ void AMDemodSettings::resetToDefaults() m_rgbColor = QColor(255, 255, 0).rgb(); m_title = "AM Demodulator"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_pll = false; } QByteArray AMDemodSettings::serialize() const @@ -56,6 +57,7 @@ QByteArray AMDemodSettings::serialize() const s.writeBool(8, m_bandpassEnable); s.writeString(9, m_title); s.writeString(11, m_audioDeviceName); + s.writeBool(12, m_pll); return s.final(); } @@ -93,6 +95,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data) d.readBool(8, &m_bandpassEnable, false); d.readString(9, &m_title, "AM Demodulator"); d.readString(11, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(12, &m_pll, false); return true; } diff --git a/plugins/channelrx/demodam/amdemodsettings.h b/plugins/channelrx/demodam/amdemodsettings.h index 54cb97a5b..166875394 100644 --- a/plugins/channelrx/demodam/amdemodsettings.h +++ b/plugins/channelrx/demodam/amdemodsettings.h @@ -33,6 +33,7 @@ struct AMDemodSettings QString m_title; Serializable *m_channelMarker; QString m_audioDeviceName; + bool m_pll; AMDemodSettings(); void resetToDefaults(); diff --git a/sdrbase/dsp/fftfilt.cxx b/sdrbase/dsp/fftfilt.cxx index fe6af7ab8..edae8deaa 100644 --- a/sdrbase/dsp/fftfilt.cxx +++ b/sdrbase/dsp/fftfilt.cxx @@ -298,7 +298,7 @@ int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb, bool getDC) } // Version for double sideband. You have to double the FFT size used for SSB. -int fftfilt::runDSB(const cmplx & in, cmplx **out) +int fftfilt::runDSB(const cmplx & in, cmplx **out, bool getDC) { data[inptr++] = in; if (inptr < flen2) @@ -312,6 +312,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out) data[flen2 + i] *= filter[flen2 + i]; } + // get or reject DC component + data[0] = getDC ? data[0] : 0; + // in-place FFT: freqdata overwritten with filtered timedata fft->InverseComplexFFT(data); diff --git a/sdrbase/dsp/fftfilt.h b/sdrbase/dsp/fftfilt.h index b52d2bd83..a9db21f11 100644 --- a/sdrbase/dsp/fftfilt.h +++ b/sdrbase/dsp/fftfilt.h @@ -32,7 +32,7 @@ public: 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 runDSB(const cmplx& in, cmplx **out, bool getDC = true); int runAsym(const cmplx & in, cmplx **out, bool usb); //!< Asymmetrical fitering can be used for vestigial sideband protected: