From f69e69a799c47324a26890e7a0f0bbb99bacdccc Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 6 Dec 2015 19:47:55 +0100 Subject: [PATCH] BFM demod: added phase lock class. Updated copyright notices --- CMakeLists.txt | 4 +- include/dsp/phaselock.h | 67 ++++++++++ plugins/channel/bfm/bfmdemod.cpp | 19 ++- plugins/channel/bfm/bfmdemod.h | 7 +- plugins/channel/bfm/bfmdemodgui.cpp | 47 ++++++- plugins/channel/bfm/bfmdemodgui.h | 22 +++- plugins/channel/bfm/bfmdemodgui.ui | 54 ++++++++- plugins/channel/bfm/bfmplugin.cpp | 17 +++ plugins/channel/bfm/bfmplugin.h | 17 +++ plugins/channel/udpsrc/udpsrc.cpp | 5 +- plugins/channel/udpsrc/udpsrc.h | 17 +++ plugins/channel/udpsrc/udpsrcgui.cpp | 17 +++ plugins/channel/udpsrc/udpsrcgui.h | 17 +++ plugins/channel/udpsrc/udpsrcplugin.cpp | 17 +++ plugins/channel/udpsrc/udpsrcplugin.h | 17 +++ sdrbase/dsp/phaselock.cpp | 155 ++++++++++++++++++++++++ 16 files changed, 477 insertions(+), 22 deletions(-) create mode 100644 include/dsp/phaselock.h create mode 100644 sdrbase/dsp/phaselock.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ffd934f0c..7ed5aacbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ set(sdrbase_SOURCES sdrbase/dsp/movingaverage.cpp sdrbase/dsp/nco.cpp sdrbase/dsp/pidcontroller.cpp + sdrbase/dsp/phaselock.cpp sdrbase/dsp/samplefifo.cpp sdrbase/dsp/samplesink.cpp sdrbase/dsp/nullsink.cpp @@ -164,7 +165,8 @@ set(sdrbase_HEADERS include/dsp/misc.h include/dsp/movingaverage.h include/dsp/nco.h - sdrbase/dsp/pidcontroller.h + include/dsp/phaselock.h + sdrbase/dsp/pidcontroller.h include/dsp/samplefifo.h include/dsp/samplesink.h include/dsp/nullsink.h diff --git a/include/dsp/phaselock.h b/include/dsp/phaselock.h new file mode 100644 index 000000000..1bb389ae3 --- /dev/null +++ b/include/dsp/phaselock.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "dsp/dsptypes.h" + +/** Phase-locked loop mainly for broadcadt FM stereo pilot. */ +class PhaseLock +{ +public: + + /** Expected pilot frequency (used for PPS events). */ + static const int pilot_frequency = 19000; + + /** + * Construct phase-locked loop. + * + * freq :: 19 kHz center frequency relative to sample freq + * (0.5 is Nyquist) + * bandwidth :: bandwidth relative to sample frequency + * minsignal :: minimum pilot amplitude + */ + PhaseLock(Real freq, Real bandwidth, Real minsignal); + + /** + * Process samples and extract 19 kHz pilot tone. + * Generate phase-locked 38 kHz tone with unit amplitude. + */ + void process(const std::vector& samples_in, std::vector& samples_out); + + /** Return true if the phase-locked loop is locked. */ + bool locked() const + { + return m_lock_cnt >= m_lock_delay; + } + + /** Return detected amplitude of pilot signal. */ + Real get_pilot_level() const + { + return 2 * m_pilot_level; + } + +private: + Real m_minfreq, m_maxfreq; + Real m_phasor_b0, m_phasor_a1, m_phasor_a2; + Real m_phasor_i1, m_phasor_i2, m_phasor_q1, m_phasor_q2; + Real m_loopfilter_b0, m_loopfilter_b1; + Real m_loopfilter_x1; + Real m_freq, m_phase; + Real m_minsignal; + Real m_pilot_level; + int m_lock_delay; + int m_lock_cnt; +}; diff --git a/plugins/channel/bfm/bfmdemod.cpp b/plugins/channel/bfm/bfmdemod.cpp index 7bca8fe89..0ac49a49e 100644 --- a/plugins/channel/bfm/bfmdemod.cpp +++ b/plugins/channel/bfm/bfmdemod.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// written by Christian Daniel // +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // // // // 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 // @@ -41,7 +41,7 @@ BFMDemod::BFMDemod(SampleSink* sampleSink) : m_config.m_afBandwidth = 15000; m_config.m_squelch = -60.0; m_config.m_volume = 2.0; - m_config.m_audioSampleRate = DSPEngine::instance()->getAudioSampleRate(); + m_config.m_audioSampleRate = DSPEngine::instance()->getAudioSampleRate(); // normally 48 kHz m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, rfFilterFftLength); apply(); @@ -77,6 +77,8 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto int rf_out; Real msq, demod; + m_sampleBuffer.clear(); + m_settingsMutex.lock(); for (SampleVector::const_iterator it = begin; it != end; ++it) @@ -116,12 +118,13 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto m_m2Sample = m_m1Sample; m_m1Sample = rf[i]; + m_sampleBuffer.push_back(Sample(demod * (1<<15), 0.0)); Complex e(demod, 0); if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, e, &ci)) { quint16 sample = (qint16)(ci.real() * 3000 * m_running.m_volume); - m_sampleBuffer.push_back(Sample(sample, sample)); + //m_sampleBuffer.push_back(Sample(sample, sample)); m_audioBuffer[m_audioBufferFill].l = sample; m_audioBuffer[m_audioBufferFill].r = sample; ++m_audioBufferFill; @@ -157,7 +160,7 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto if(m_sampleSink != 0) { - m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); + m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true); } m_sampleBuffer.clear(); @@ -251,11 +254,15 @@ void BFMDemod::apply() (m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset)) { m_settingsMutex.lock(); - qDebug() << "BFMDemod::handleMessage: m_rfFilter->create_filter"; Real lowCut = -(m_config.m_rfBandwidth / 2.0) / m_config.m_inputSampleRate; Real hiCut = (m_config.m_rfBandwidth / 2.0) / m_config.m_inputSampleRate; m_rfFilter->create_filter(lowCut, hiCut); m_settingsMutex.unlock(); + + qDebug() << "BFMDemod::handleMessage: m_rfFilter->create_filter: sampleRate: " + << m_config.m_inputSampleRate + << " lowCut: " << lowCut * m_config.m_inputSampleRate + << " hiCut: " << hiCut * m_config.m_inputSampleRate; } if((m_config.m_afBandwidth != m_running.m_afBandwidth) || diff --git a/plugins/channel/bfm/bfmdemod.h b/plugins/channel/bfm/bfmdemod.h index b650e5bc7..6419d3f7e 100644 --- a/plugins/channel/bfm/bfmdemod.h +++ b/plugins/channel/bfm/bfmdemod.h @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// written by Christian Daniel // +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // // // // 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 // @@ -38,6 +38,7 @@ public: void configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch); + int getSampleRate() const { return m_config.m_inputSampleRate; } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); virtual void start(); virtual void stop(); @@ -110,7 +111,7 @@ private: Config m_running; NCO m_nco; - Interpolator m_interpolator; //!< Interpolator between sample rate sent from DSP engine and requested RF bandwidth (rational) + Interpolator m_interpolator; //!< Interpolator between fixed demod bandwidth and audio bandwidth (rational) Real m_interpolatorDistance; Real m_interpolatorDistanceRemain; Lowpass m_lowpass; diff --git a/plugins/channel/bfm/bfmdemodgui.cpp b/plugins/channel/bfm/bfmdemodgui.cpp index a2cbb5818..95de9835c 100644 --- a/plugins/channel/bfm/bfmdemodgui.cpp +++ b/plugins/channel/bfm/bfmdemodgui.cpp @@ -1,9 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 #include #include "dsp/threadedsamplesink.h" #include "dsp/channelizer.h" #include "dsp/dspengine.h" +#include "dsp/spectrumvis.h" #include "gui/glspectrum.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" @@ -17,7 +35,7 @@ #include "bfmdemod.h" const int BFMDemodGUI::m_rfBW[] = { - 48000, 80000, 120000, 140000, 160000, 180000, 200000, 220000, 250000 + 80000, 100000, 120000, 140000, 160000, 180000, 200000, 220000, 250000 }; int requiredBW(int rfBW) @@ -85,6 +103,7 @@ QByteArray BFMDemodGUI::serialize() const s.writeS32(4, ui->volume->value()); s.writeS32(5, ui->squelch->value()); s.writeU32(7, m_channelMarker.getColor().rgb()); + s.writeBlob(8, ui->spectrumGUI->serialize()); return s.final(); } @@ -123,6 +142,9 @@ bool BFMDemodGUI::deserialize(const QByteArray& data) m_channelMarker.setColor(u32tmp); } + d.readBlob(8, &bytetmp); + ui->spectrumGUI->deserialize(bytetmp); + blockApplySettings(false); m_channelMarker.blockSignals(false); @@ -225,11 +247,22 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_wfmDemod = new BFMDemod(0); - m_channelizer = new Channelizer(m_wfmDemod); + m_spectrumVis = new SpectrumVis(ui->glSpectrum); + m_bfmDemod = new BFMDemod(m_spectrumVis); + m_channelizer = new Channelizer(m_bfmDemod); m_threadedChannelizer = new ThreadedSampleSink(m_channelizer, this); DSPEngine::instance()->addThreadedSink(m_threadedChannelizer); + //ui->glSpectrum->setCenterFrequency(BFMDemodGUI::m_rfBW[ui->rfBW->value()] / 4); + //ui->glSpectrum->setSampleRate(BFMDemodGUI::m_rfBW[ui->rfBW->value()] / 2); + ui->glSpectrum->setCenterFrequency(625000 / 4); + ui->glSpectrum->setSampleRate(625000 / 2); + //ui->glSpectrum->setCenterFrequency(48000 / 4); + //ui->glSpectrum->setSampleRate(48000 / 2); + ui->glSpectrum->setDisplayWaterfall(false); + ui->glSpectrum->setDisplayMaxHold(false); + ui->glSpectrum->setSsbSpectrum(true); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, FFTWindow::BlackmanHarris); connect(&m_pluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); //m_channelMarker = new ChannelMarker(this); @@ -240,6 +273,8 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); m_pluginAPI->addChannelMarker(&m_channelMarker); + ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); + applySettings(); } @@ -249,7 +284,7 @@ BFMDemodGUI::~BFMDemodGUI() DSPEngine::instance()->removeThreadedSink(m_threadedChannelizer); delete m_threadedChannelizer; delete m_channelizer; - delete m_wfmDemod; + delete m_bfmDemod; //delete m_channelMarker; delete ui; } @@ -272,7 +307,7 @@ void BFMDemodGUI::applySettings() ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency())); ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0); - m_wfmDemod->configure(m_wfmDemod->getInputMessageQueue(), + m_bfmDemod->configure(m_bfmDemod->getInputMessageQueue(), m_rfBW[ui->rfBW->value()], ui->afBW->value() * 1000.0, ui->volume->value() / 10.0, @@ -296,7 +331,7 @@ void BFMDemodGUI::enterEvent(QEvent*) void BFMDemodGUI::tick() { - Real powDb = CalcDb::dbPower(m_wfmDemod->getMagSq()); + Real powDb = CalcDb::dbPower(m_bfmDemod->getMagSq()); m_channelPowerDbAvg.feed(powDb); ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1)); } diff --git a/plugins/channel/bfm/bfmdemodgui.h b/plugins/channel/bfm/bfmdemodgui.h index a2eef8490..d95ae89b0 100644 --- a/plugins/channel/bfm/bfmdemodgui.h +++ b/plugins/channel/bfm/bfmdemodgui.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 INCLUDE_BFMDEMODGUI_H #define INCLUDE_BFMDEMODGUI_H @@ -10,6 +27,7 @@ class PluginAPI; class ThreadedSampleSink; class Channelizer; +class SpectrumVis; class BFMDemod; namespace Ui { @@ -55,7 +73,9 @@ private: ThreadedSampleSink* m_threadedChannelizer; Channelizer* m_channelizer; - BFMDemod* m_wfmDemod; + SpectrumVis* m_spectrumVis; + + BFMDemod* m_bfmDemod; MovingAverage m_channelPowerDbAvg; static const int m_rfBW[]; diff --git a/plugins/channel/bfm/bfmdemodgui.ui b/plugins/channel/bfm/bfmdemodgui.ui index 69bff07ac..c7fe66770 100644 --- a/plugins/channel/bfm/bfmdemodgui.ui +++ b/plugins/channel/bfm/bfmdemodgui.ui @@ -6,8 +6,8 @@ 0 0 - 302 - 373 + 252 + 324 @@ -16,7 +16,7 @@ - 20 + 10 20 235 121 @@ -314,6 +314,42 @@ + + + + 10 + 150 + 231 + 156 + + + + Channel Spectrum + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + @@ -328,6 +364,18 @@
gui/valuedial.h
1
+ + GLSpectrum + QWidget +
gui/glspectrum.h
+ 1 +
+ + GLSpectrumGUI + QWidget +
gui/glspectrumgui.h
+ 1 +
diff --git a/plugins/channel/bfm/bfmplugin.cpp b/plugins/channel/bfm/bfmplugin.cpp index d6ed37728..d68dc9da2 100644 --- a/plugins/channel/bfm/bfmplugin.cpp +++ b/plugins/channel/bfm/bfmplugin.cpp @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 #include "plugin/pluginapi.h" diff --git a/plugins/channel/bfm/bfmplugin.h b/plugins/channel/bfm/bfmplugin.h index f3e198bbe..b33b847b7 100644 --- a/plugins/channel/bfm/bfmplugin.h +++ b/plugins/channel/bfm/bfmplugin.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 INCLUDE_BFMPLUGIN_H #define INCLUDE_BFMPLUGIN_H diff --git a/plugins/channel/udpsrc/udpsrc.cpp b/plugins/channel/udpsrc/udpsrc.cpp index b2b10f78c..0dd557e18 100644 --- a/plugins/channel/udpsrc/udpsrc.cpp +++ b/plugins/channel/udpsrc/udpsrc.cpp @@ -1,5 +1,6 @@ -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// (C) 2015 John Greb // +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // // // // 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 // diff --git a/plugins/channel/udpsrc/udpsrc.h b/plugins/channel/udpsrc/udpsrc.h index 69958bcac..d24b6faee 100644 --- a/plugins/channel/udpsrc/udpsrc.h +++ b/plugins/channel/udpsrc/udpsrc.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 INCLUDE_UDPSRC_H #define INCLUDE_UDPSRC_H diff --git a/plugins/channel/udpsrc/udpsrcgui.cpp b/plugins/channel/udpsrc/udpsrcgui.cpp index 292d670f8..8d873d263 100644 --- a/plugins/channel/udpsrc/udpsrcgui.cpp +++ b/plugins/channel/udpsrc/udpsrcgui.cpp @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "udpsrcgui.h" #include "plugin/pluginapi.h" diff --git a/plugins/channel/udpsrc/udpsrcgui.h b/plugins/channel/udpsrc/udpsrcgui.h index 8fe04c0f6..5c1884a66 100644 --- a/plugins/channel/udpsrc/udpsrcgui.h +++ b/plugins/channel/udpsrc/udpsrcgui.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 INCLUDE_UDPSRCGUI_H #define INCLUDE_UDPSRCGUI_H diff --git a/plugins/channel/udpsrc/udpsrcplugin.cpp b/plugins/channel/udpsrc/udpsrcplugin.cpp index 60a529a02..0262a7cbe 100644 --- a/plugins/channel/udpsrc/udpsrcplugin.cpp +++ b/plugins/channel/udpsrc/udpsrcplugin.cpp @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "udpsrcplugin.h" #include diff --git a/plugins/channel/udpsrc/udpsrcplugin.h b/plugins/channel/udpsrc/udpsrcplugin.h index 5da0e9ad9..74116a394 100644 --- a/plugins/channel/udpsrc/udpsrcplugin.h +++ b/plugins/channel/udpsrc/udpsrcplugin.h @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 INCLUDE_UDPSRCPLUGIN_H #define INCLUDE_UDPSRCPLUGIN_H diff --git a/sdrbase/dsp/phaselock.cpp b/sdrbase/dsp/phaselock.cpp new file mode 100644 index 000000000..72cf5b63d --- /dev/null +++ b/sdrbase/dsp/phaselock.cpp @@ -0,0 +1,155 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "dsp/phaselock.h" + +// Construct phase-locked loop. +PhaseLock::PhaseLock(Real freq, Real bandwidth, Real minsignal) +{ + /* + * This is a type-2, 4th order phase-locked loop. + * + * Open-loop transfer function: + * G(z) = K * (z - q1) / ((z - p1) * (z - p2) * (z - 1) * (z - 1)) + * K = 3.788 * (bandwidth * 2 * Pi)**3 + * q1 = exp(-0.1153 * bandwidth * 2*Pi) + * p1 = exp(-1.146 * bandwidth * 2*Pi) + * p2 = exp(-5.331 * bandwidth * 2*Pi) + * + * I don't understand what I'm doing; hopefully it will work. + */ + + // Set min/max locking frequencies. + m_minfreq = (freq - bandwidth) * 2.0 * M_PI; + m_maxfreq = (freq + bandwidth) * 2.0 * M_PI; + + // Set valid signal threshold. + m_minsignal = minsignal; + m_lock_delay = int(20.0 / bandwidth); + m_lock_cnt = 0; + m_pilot_level = 0; + + // Create 2nd order filter for I/Q representation of phase error. + // Filter has two poles, unit DC gain. + Real p1 = exp(-1.146 * bandwidth * 2.0 * M_PI); + Real p2 = exp(-5.331 * bandwidth * 2.0 * M_PI); + m_phasor_a1 = - p1 - p2; + m_phasor_a2 = p1 * p2; + m_phasor_b0 = 1 + m_phasor_a1 + m_phasor_a2; + + // Create loop filter to stabilize the loop. + Real q1 = exp(-0.1153 * bandwidth * 2.0 * M_PI); + m_loopfilter_b0 = 0.62 * bandwidth * 2.0 * M_PI; + m_loopfilter_b1 = - m_loopfilter_b0 * q1; + + // After the loop filter, the phase error is integrated to produce + // the frequency. Then the frequency is integrated to produce the phase. + // These integrators form the two remaining poles, both at z = 1. + + // Initialize frequency and phase. + m_freq = freq * 2.0 * M_PI; + m_phase = 0; + + m_phasor_i1 = 0; + m_phasor_i2 = 0; + m_phasor_q1 = 0; + m_phasor_q2 = 0; + m_loopfilter_x1 = 0; +} + + +// Process samples. +void PhaseLock::process(const std::vector& samples_in, std::vector& samples_out) +{ + unsigned int n = samples_in.size(); + + samples_out.resize(n); + + bool was_locked = (m_lock_cnt >= m_lock_delay); + + if (n > 0) + m_pilot_level = 1000.0; + + for (unsigned int i = 0; i < n; i++) { + + // Generate locked pilot tone. + Real psin = sin(m_phase); + Real pcos = cos(m_phase); + + // Generate double-frequency output. + // sin(2*x) = 2 * sin(x) * cos(x) + samples_out[i] = 2 * psin * pcos; + + // Multiply locked tone with input. + Real x = samples_in[i]; + Real phasor_i = psin * x; + Real phasor_q = pcos * x; + + // Run IQ phase error through low-pass filter. + phasor_i = m_phasor_b0 * phasor_i + - m_phasor_a1 * m_phasor_i1 + - m_phasor_a2 * m_phasor_i2; + phasor_q = m_phasor_b0 * phasor_q + - m_phasor_a1 * m_phasor_q1 + - m_phasor_a2 * m_phasor_q2; + m_phasor_i2 = m_phasor_i1; + m_phasor_i1 = phasor_i; + m_phasor_q2 = m_phasor_q1; + m_phasor_q1 = phasor_q; + + // Convert I/Q ratio to estimate of phase error. + Real phase_err; + if (phasor_i > abs(phasor_q)) { + // We are within +/- 45 degrees from lock. + // Use simple linear approximation of arctan. + phase_err = phasor_q / phasor_i; + } else if (phasor_q > 0) { + // We are lagging more than 45 degrees behind the input. + phase_err = 1; + } else { + // We are more than 45 degrees ahead of the input. + phase_err = -1; + } + + // Detect pilot level (conservative). + m_pilot_level = std::min(m_pilot_level, phasor_i); + + // Run phase error through loop filter and update frequency estimate. + m_freq += m_loopfilter_b0 * phase_err + + m_loopfilter_b1 * m_loopfilter_x1; + m_loopfilter_x1 = phase_err; + + // Limit frequency to allowable range. + m_freq = std::max(m_minfreq, std::min(m_maxfreq, m_freq)); + + // Update locked phase. + m_phase += m_freq; + if (m_phase > 2.0 * M_PI) { + m_phase -= 2.0 * M_PI; + } + } + + // Update lock status. + if (2 * m_pilot_level > m_minsignal) { + if (m_lock_cnt < m_lock_delay) + m_lock_cnt += n; + } else { + m_lock_cnt = 0; + } + +}