diff --git a/app/main.cpp b/app/main.cpp index 964e327ed..809d3f48f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -35,7 +35,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo */ QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangel"); - QCoreApplication::setApplicationVersion("3.14.6"); + QCoreApplication::setApplicationVersion("3.14.7"); #if 1 qApp->setStyle(QStyleFactory::create("fusion")); diff --git a/appbench/main.cpp b/appbench/main.cpp index 4c9c1554d..8f1214937 100644 --- a/appbench/main.cpp +++ b/appbench/main.cpp @@ -57,7 +57,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangelBench"); - QCoreApplication::setApplicationVersion("3.14.6"); + QCoreApplication::setApplicationVersion("3.14.7"); int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP}; std::vector vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int)); diff --git a/appsrv/main.cpp b/appsrv/main.cpp index 298db8ea7..c4ec4d5d7 100644 --- a/appsrv/main.cpp +++ b/appsrv/main.cpp @@ -56,7 +56,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangelSrv"); - QCoreApplication::setApplicationVersion("3.14.6"); + QCoreApplication::setApplicationVersion("3.14.7"); int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP}; std::vector vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int)); diff --git a/debian/changelog b/debian/changelog index e338fc591..a71e6207f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sdrangel (3.14.7-1) unstable; urgency=medium + + * ChanelAnalyzerNG: added PLL option + + -- Edouard Griffiths, F4EXB Sun, 13 May 2018 20:14:18 +0200 + sdrangel (3.14.6-1) unstable; urgency=medium * Fixed keyboard input for negative values on realtive integer value dials diff --git a/plugins/channelrx/chanalyzerng/chanalyzerng.cpp b/plugins/channelrx/chanalyzerng/chanalyzerng.cpp index fed5b3e2f..63f654cfc 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerng.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzerng.cpp @@ -35,6 +35,7 @@ const QString ChannelAnalyzerNG::m_channelId = "ChannelAnalyzerNG"; ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) : ChannelSinkAPI(m_channelIdURI), m_deviceAPI(deviceAPI), + m_pll(0,0.05,0.01), m_sampleSink(0), m_settingsMutex(QMutex::Recursive) { @@ -73,9 +74,10 @@ void ChannelAnalyzerNG::configure(MessageQueue* messageQueue, Real Bandwidth, Real LowCutoff, int spanLog2, - bool ssb) + bool ssb, + bool pll) { - Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb); + Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb, pll); messageQueue->push(cmd); } @@ -165,13 +167,15 @@ bool ChannelAnalyzerNG::handleMessage(const Message& cmd) m_config.m_LowCutoff = cfg.getLoCutoff(); m_config.m_spanLog2 = cfg.getSpanLog2(); m_config.m_ssb = cfg.getSSB(); + m_config.m_pll = cfg.getPLL(); qDebug() << "ChannelAnalyzerNG::handleMessage: MsgConfigureChannelAnalyzer:" << " m_channelSampleRate: " << m_config.m_channelSampleRate << " m_Bandwidth: " << m_config.m_Bandwidth << " m_LowCutoff: " << m_config.m_LowCutoff << " m_spanLog2: " << m_config.m_spanLog2 - << " m_ssb: " << m_config.m_ssb; + << " m_ssb: " << m_config.m_ssb + << " m_pll: " << m_config.m_pll; apply(); return true; @@ -254,5 +258,6 @@ void ChannelAnalyzerNG::apply(bool force) //m_settingsMutex.lock(); m_running.m_spanLog2 = m_config.m_spanLog2; m_running.m_ssb = m_config.m_ssb; + m_running.m_pll = m_config.m_pll; //m_settingsMutex.unlock(); } diff --git a/plugins/channelrx/chanalyzerng/chanalyzerng.h b/plugins/channelrx/chanalyzerng/chanalyzerng.h index aea652558..f9f194bd2 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerng.h +++ b/plugins/channelrx/chanalyzerng/chanalyzerng.h @@ -25,6 +25,7 @@ #include "dsp/interpolator.h" #include "dsp/ncof.h" #include "dsp/fftfilt.h" +#include "dsp/phaselock.h" #include "audio/audiofifo.h" #include "util/message.h" @@ -45,20 +46,23 @@ public: Real getLoCutoff() const { return m_LowCutoff; } int getSpanLog2() const { return m_spanLog2; } bool getSSB() const { return m_ssb; } + bool getPLL() const { return m_pll; } static MsgConfigureChannelAnalyzer* create( int channelSampleRate, Real Bandwidth, Real LowCutoff, int spanLog2, - bool ssb) + bool ssb, + bool pll) { return new MsgConfigureChannelAnalyzer( channelSampleRate, Bandwidth, LowCutoff, spanLog2, - ssb); + ssb, + pll); } private: @@ -67,19 +71,22 @@ public: Real m_LowCutoff; int m_spanLog2; bool m_ssb; + bool m_pll; MsgConfigureChannelAnalyzer( int channelSampleRate, Real Bandwidth, Real LowCutoff, int spanLog2, - bool ssb) : + bool ssb, + bool pll) : Message(), m_channelSampleRate(channelSampleRate), m_Bandwidth(Bandwidth), m_LowCutoff(LowCutoff), m_spanLog2(spanLog2), - m_ssb(ssb) + m_ssb(ssb), + m_pll(pll) { } }; @@ -133,12 +140,14 @@ public: Real Bandwidth, Real LowCutoff, int spanLog2, - bool ssb); + bool ssb, + bool pll); DownChannelizer *getChannelizer() { return m_channelizer; } int getInputSampleRate() const { return m_running.m_inputSampleRate; } int getChannelSampleRate() const { return m_running.m_channelSampleRate; } double getMagSq() const { return m_magsq; } + bool isPllLocked() const { return m_running.m_pll && m_pll.locked(); } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); virtual void start(); @@ -166,6 +175,7 @@ private: Real m_LowCutoff; int m_spanLog2; bool m_ssb; + bool m_pll; Config() : m_frequency(0), @@ -174,7 +184,8 @@ private: m_Bandwidth(5000), m_LowCutoff(300), m_spanLog2(3), - m_ssb(false) + m_ssb(false), + m_pll(false) {} }; @@ -192,6 +203,7 @@ private: bool m_useInterpolator; NCOF m_nco; + SimplePhaseLock m_pll; Interpolator m_interpolator; Real m_interpolatorDistance; Real m_interpolatorDistanceRemain; @@ -233,13 +245,33 @@ private: Real im = m_sum.imag() / SDR_RX_SCALED; m_magsq = re*re + im*im; - if (m_running.m_ssb & !m_usb) - { // invert spectrum for LSB - m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); + if (m_running.m_pll) + { + Real ncopll[2]; + m_pll.process(re, im, ncopll); + + Real mixI = m_sum.real() * ncopll[0] - m_sum.imag() * ncopll[1]; + Real mixQ = m_sum.real() * ncopll[1] + m_sum.imag() * ncopll[0]; + + if (m_running.m_ssb & !m_usb) + { // invert spectrum for LSB + m_sampleBuffer.push_back(Sample(mixQ, mixI)); + } + else + { + m_sampleBuffer.push_back(Sample(mixI, mixQ)); + } } else { - m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); + if (m_running.m_ssb & !m_usb) + { // invert spectrum for LSB + m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); + } + else + { + m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); + } } m_sum = 0; diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp index 72be0e3bf..14fba25a3 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp @@ -237,6 +237,12 @@ void ChannelAnalyzerNGGUI::tick() double powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq()); m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg((Real) m_channelPowerDbAvg, 0, 'f', 1)); + + if (m_channelAnalyzer->isPllLocked()) { + ui->pll->setStyleSheet("QToolButton { background-color : green; }"); + } else { + ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } } void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value) @@ -251,6 +257,11 @@ void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value) } } +void ChannelAnalyzerNGGUI::on_pll_toggled(bool checked __attribute__((unused))) +{ + applySettings(); +} + void ChannelAnalyzerNGGUI::on_useRationalDownsampler_toggled(bool checked __attribute__((unused))) { setNewFinalRate(m_spanLog2); @@ -578,7 +589,8 @@ void ChannelAnalyzerNGGUI::applySettings() ui->BW->value() * 100.0, ui->lowCut->value() * 100.0, m_spanLog2, - ui->ssb->isChecked()); + ui->ssb->isChecked(), + ui->pll->isChecked()); } } diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.h b/plugins/channelrx/chanalyzerng/chanalyzernggui.h index e620b00fe..c30a6087d 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.h +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.h @@ -92,6 +92,7 @@ private: private slots: void on_deltaFrequency_changed(qint64 value); void on_channelSampleRate_changed(quint64 value); + void on_pll_toggled(bool checked); void on_useRationalDownsampler_toggled(bool checked); void on_BW_valueChanged(int value); void on_lowCut_valueChanged(int value); diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.ui b/plugins/channelrx/chanalyzerng/chanalyzernggui.ui index c6e712619..1c72fa3a1 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.ui +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.ui @@ -179,6 +179,24 @@ + + + + PLL lock + + + + + + + :/unlocked.png + :/locked.png:/unlocked.png + + + true + + + diff --git a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp index 076d9b967..b2450da84 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = { QString("Channel Analyzer NG"), - QString("3.14.5"), + QString("3.14.7"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/sdrbase/dsp/phaselock.cpp b/sdrbase/dsp/phaselock.cpp index ce6c57a97..86de423ae 100644 --- a/sdrbase/dsp/phaselock.cpp +++ b/sdrbase/dsp/phaselock.cpp @@ -262,61 +262,85 @@ void PhaseLock::process(const Real& sample_in, Real *samples_out) processPhase(samples_out); // Multiply locked tone with input. - Real x = sample_in; - Real phasor_i = m_psin * x; - Real phasor_q = m_pcos * x; + Real phasor_i = m_psin * sample_in; + Real phasor_q = m_pcos * sample_in; - // 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; + // Actual PLL + process_phasor(phasor_i, phasor_q); +} - // Convert I/Q ratio to estimate of phase error. - Real phase_err; +void PhaseLock::process(const Real& real_in, const Real& imag_in, Real *samples_out) +{ + m_pps_events.clear(); + + // Generate locked pilot tone. + m_psin = sin(m_phase); + m_pcos = cos(m_phase); + + // Generate output + processPhase(samples_out); + + // Multiply locked tone with input. + Real phasor_i = m_psin * real_in - m_pcos * imag_in; + Real phasor_q = m_pcos * real_in + m_psin * imag_in; + + // Actual PLL + process_phasor(phasor_i, phasor_q); +} + +void PhaseLock::process_phasor(Real& phasor_i, Real& phasor_q) +{ + // 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 > std::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; - } + // 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); - m_pilot_level = phasor_i; + // Detect pilot level (conservative). + // m_pilot_level = std::min(m_pilot_level, phasor_i); + 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; + // 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)); + // 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; - m_pilot_periods++; + // Update locked phase. + m_phase += m_freq; + if (m_phase > 2.0 * M_PI) + { + m_phase -= 2.0 * M_PI; + m_pilot_periods++; - // Generate pulse-per-second. - if (m_pilot_periods == pilot_frequency) - { - m_pilot_periods = 0; - } - } + // Generate pulse-per-second. + if (m_pilot_periods == pilot_frequency) + { + m_pilot_periods = 0; + } + } // Update lock status. if (2 * m_pilot_level > m_minsignal) @@ -328,7 +352,7 @@ void PhaseLock::process(const Real& sample_in, Real *samples_out) } else { - m_lock_cnt = 0; + m_lock_cnt = 0; } // Drop PPS events when pilot not locked. diff --git a/sdrbase/dsp/phaselock.h b/sdrbase/dsp/phaselock.h index 06196c5e7..ab1b2af03 100644 --- a/sdrbase/dsp/phaselock.h +++ b/sdrbase/dsp/phaselock.h @@ -73,6 +73,7 @@ public: * This is the in flow version */ void process(const Real& sample_in, Real *samples_out); + void process(const Real& real_in, const Real& imag_in, Real *samples_out); /** Return true if the phase-locked loop is locked. */ bool locked() const @@ -111,6 +112,8 @@ private: quint64 m_pps_cnt; quint64 m_sample_cnt; std::vector m_pps_events; + + void process_phasor(Real& phasor_i, Real& phasor_q); }; class SimplePhaseLock : public PhaseLock