diff --git a/plugins/channelrx/chanalyzerng/chanalyzerng.cpp b/plugins/channelrx/chanalyzerng/chanalyzerng.cpp
index 63994c9c0..546985628 100644
--- a/plugins/channelrx/chanalyzerng/chanalyzerng.cpp
+++ b/plugins/channelrx/chanalyzerng/chanalyzerng.cpp
@@ -197,8 +197,6 @@ bool ChannelAnalyzerNG::handleMessage(const Message& cmd)
}
}
-
-
void ChannelAnalyzerNG::apply(bool force)
{
if ((m_running.m_frequency != m_config.m_frequency) ||
@@ -253,6 +251,12 @@ void ChannelAnalyzerNG::apply(bool force)
m_settingsMutex.unlock();
}
+ if ((m_running.m_channelSampleRate != m_config.m_channelSampleRate) ||
+ (m_running.m_spanLog2 != m_config.m_spanLog2) || force)
+ {
+ m_pll.setSampleRate(m_running.m_channelSampleRate / (1<. //
-///////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDE_CHANALYZERNG_H
-#define INCLUDE_CHANALYZERNG_H
-
-#include
-#include
-
-#include "dsp/basebandsamplesink.h"
-#include "channel/channelsinkapi.h"
-#include "dsp/interpolator.h"
-#include "dsp/ncof.h"
-#include "dsp/fftfilt.h"
-#include "dsp/phaselockcomplex.h"
-#include "audio/audiofifo.h"
-#include "util/message.h"
-
-#define ssbFftLen 1024
-
-class DeviceSourceAPI;
-class ThreadedBasebandSampleSink;
-class DownChannelizer;
-
-class ChannelAnalyzerNG : public BasebandSampleSink, public ChannelSinkAPI {
-public:
- class MsgConfigureChannelAnalyzer : public Message {
- MESSAGE_CLASS_DECLARATION
-
- public:
- int getChannelSampleRate() const { return m_channelSampleRate; }
- Real getBandwidth() const { return m_Bandwidth; }
- Real getLoCutoff() const { return m_LowCutoff; }
- int getSpanLog2() const { return m_spanLog2; }
- bool getSSB() const { return m_ssb; }
- bool getPLL() const { return m_pll; }
- unsigned int getPLLPSKOrder() const { return m_pllPskOrder; }
-
- static MsgConfigureChannelAnalyzer* create(
- int channelSampleRate,
- Real Bandwidth,
- Real LowCutoff,
- int spanLog2,
- bool ssb,
- bool pll,
- unsigned int pllPskOrder)
- {
- return new MsgConfigureChannelAnalyzer(
- channelSampleRate,
- Bandwidth,
- LowCutoff,
- spanLog2,
- ssb,
- pll,
- pllPskOrder);
- }
-
- private:
- int m_channelSampleRate;
- Real m_Bandwidth;
- Real m_LowCutoff;
- int m_spanLog2;
- bool m_ssb;
- bool m_pll;
- unsigned int m_pllPskOrder;
-
- MsgConfigureChannelAnalyzer(
- int channelSampleRate,
- Real Bandwidth,
- Real LowCutoff,
- int spanLog2,
- bool ssb,
- bool pll,
- unsigned int pllPskOrder) :
- Message(),
- m_channelSampleRate(channelSampleRate),
- m_Bandwidth(Bandwidth),
- m_LowCutoff(LowCutoff),
- m_spanLog2(spanLog2),
- m_ssb(ssb),
- m_pll(pll),
- m_pllPskOrder(pllPskOrder)
- { }
- };
-
- class MsgConfigureChannelizer : public Message {
- MESSAGE_CLASS_DECLARATION
-
- public:
- int getSampleRate() const { return m_sampleRate; }
- int getCenterFrequency() const { return m_centerFrequency; }
-
- static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency)
- {
- return new MsgConfigureChannelizer(sampleRate, centerFrequency);
- }
-
- private:
- int m_sampleRate;
- int m_centerFrequency;
-
- MsgConfigureChannelizer(int sampleRate, int centerFrequency) :
- Message(),
- m_sampleRate(sampleRate),
- m_centerFrequency(centerFrequency)
- { }
- };
-
- class MsgReportChannelSampleRateChanged : public Message {
- MESSAGE_CLASS_DECLARATION
-
- public:
-
- static MsgReportChannelSampleRateChanged* create()
- {
- return new MsgReportChannelSampleRateChanged();
- }
-
- private:
-
- MsgReportChannelSampleRateChanged() :
- Message()
- { }
- };
-
- ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI);
- virtual ~ChannelAnalyzerNG();
- virtual void destroy() { delete this; }
- void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; }
-
- void configure(MessageQueue* messageQueue,
- int channelSampleRate,
- Real Bandwidth,
- Real LowCutoff,
- int spanLog2,
- bool ssb,
- bool pll,
- unsigned int pllPskOrder);
-
- 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(); }
- Real getPllFrequency() const { return m_pll.getFrequency(); }
- Real getPllDeltaPhase() const { return m_pll.getDeltaPhi(); }
- Real getPllPhase() const { return m_pll.getPhiHat(); }
-
- virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
- virtual void start();
- virtual void stop();
- virtual bool handleMessage(const Message& cmd);
-
- virtual void getIdentifier(QString& id) { id = objectName(); }
- virtual void getTitle(QString& title) { title = objectName(); }
- virtual qint64 getCenterFrequency() const { return m_running.m_frequency; }
-
- virtual QByteArray serialize() const { return QByteArray(); }
- virtual bool deserialize(const QByteArray& data __attribute__((unused))) { return false; }
-
- static const QString m_channelIdURI;
- static const QString m_channelId;
-
-private:
-
- struct Config
- {
- int m_frequency;
- int m_inputSampleRate;
- int m_channelSampleRate;
- Real m_Bandwidth;
- Real m_LowCutoff;
- int m_spanLog2;
- bool m_ssb;
- bool m_pll;
- unsigned int m_pllPskOrder;
-
- Config() :
- m_frequency(0),
- m_inputSampleRate(96000),
- m_channelSampleRate(96000),
- m_Bandwidth(5000),
- m_LowCutoff(300),
- m_spanLog2(3),
- m_ssb(false),
- m_pll(false),
- m_pllPskOrder(1)
- {}
- };
-
- Config m_config;
- Config m_running;
-
- DeviceSourceAPI *m_deviceAPI;
- ThreadedBasebandSampleSink* m_threadedChannelizer;
- DownChannelizer* m_channelizer;
-
- int m_undersampleCount;
- fftfilt::cmplx m_sum;
- bool m_usb;
- double m_magsq;
- bool m_useInterpolator;
-
- NCOF m_nco;
- PhaseLockComplex m_pll;
- Interpolator m_interpolator;
- Real m_interpolatorDistance;
- Real m_interpolatorDistanceRemain;
-
- fftfilt* SSBFilter;
- fftfilt* DSBFilter;
-
- BasebandSampleSink* m_sampleSink;
- SampleVector m_sampleBuffer;
- QMutex m_settingsMutex;
-
- void apply(bool force = false);
-
- void processOneSample(Complex& c, fftfilt::cmplx *sideband)
- {
- int n_out;
- int decim = 1<runSSB(c, &sideband, m_usb);
- }
- else
- {
- n_out = DSBFilter->runDSB(c, &sideband);
- }
-
- for (int i = 0; i < n_out; i++)
- {
- // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
- // smart decimation with bit gain using float arithmetic (23 bits significand)
-
- m_sum += sideband[i];
-
- if (!(m_undersampleCount++ & (decim - 1))) // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
- {
- m_sum /= decim;
- Real re = m_sum.real() / SDR_RX_SCALED;
- Real im = m_sum.imag() / SDR_RX_SCALED;
- m_magsq = re*re + im*im;
-
- if (m_running.m_pll)
- {
- m_pll.feed(re, im);
-
- // Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
- // Real mixI = m_sum.real() * m_pll.getImag() - m_sum.imag() * m_pll.getReal();
- // Real mixQ = m_sum.real() * m_pll.getReal() + m_sum.imag() * m_pll.getImag();
- Real mixI = m_pll.getReal() * SDR_RX_SCALED;
- Real mixQ = m_pll.getImag() * SDR_RX_SCALED;
-
- 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
- {
- 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;
- }
- }
- }
-};
-
-#endif // INCLUDE_CHANALYZERNG_H
+///////////////////////////////////////////////////////////////////////////////////
+// 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 INCLUDE_CHANALYZERNG_H
+#define INCLUDE_CHANALYZERNG_H
+
+#include
+#include
+
+#include "dsp/basebandsamplesink.h"
+#include "channel/channelsinkapi.h"
+#include "dsp/interpolator.h"
+#include "dsp/ncof.h"
+#include "dsp/fftfilt.h"
+#include "dsp/phaselockcomplex.h"
+#include "audio/audiofifo.h"
+#include "util/message.h"
+
+#define ssbFftLen 1024
+
+class DeviceSourceAPI;
+class ThreadedBasebandSampleSink;
+class DownChannelizer;
+
+class ChannelAnalyzerNG : public BasebandSampleSink, public ChannelSinkAPI {
+public:
+ class MsgConfigureChannelAnalyzer : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ int getChannelSampleRate() const { return m_channelSampleRate; }
+ Real getBandwidth() const { return m_Bandwidth; }
+ Real getLoCutoff() const { return m_LowCutoff; }
+ int getSpanLog2() const { return m_spanLog2; }
+ bool getSSB() const { return m_ssb; }
+ bool getPLL() const { return m_pll; }
+ unsigned int getPLLPSKOrder() const { return m_pllPskOrder; }
+
+ static MsgConfigureChannelAnalyzer* create(
+ int channelSampleRate,
+ Real Bandwidth,
+ Real LowCutoff,
+ int spanLog2,
+ bool ssb,
+ bool pll,
+ unsigned int pllPskOrder)
+ {
+ return new MsgConfigureChannelAnalyzer(
+ channelSampleRate,
+ Bandwidth,
+ LowCutoff,
+ spanLog2,
+ ssb,
+ pll,
+ pllPskOrder);
+ }
+
+ private:
+ int m_channelSampleRate;
+ Real m_Bandwidth;
+ Real m_LowCutoff;
+ int m_spanLog2;
+ bool m_ssb;
+ bool m_pll;
+ unsigned int m_pllPskOrder;
+
+ MsgConfigureChannelAnalyzer(
+ int channelSampleRate,
+ Real Bandwidth,
+ Real LowCutoff,
+ int spanLog2,
+ bool ssb,
+ bool pll,
+ unsigned int pllPskOrder) :
+ Message(),
+ m_channelSampleRate(channelSampleRate),
+ m_Bandwidth(Bandwidth),
+ m_LowCutoff(LowCutoff),
+ m_spanLog2(spanLog2),
+ m_ssb(ssb),
+ m_pll(pll),
+ m_pllPskOrder(pllPskOrder)
+ { }
+ };
+
+ class MsgConfigureChannelizer : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ int getSampleRate() const { return m_sampleRate; }
+ int getCenterFrequency() const { return m_centerFrequency; }
+
+ static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency)
+ {
+ return new MsgConfigureChannelizer(sampleRate, centerFrequency);
+ }
+
+ private:
+ int m_sampleRate;
+ int m_centerFrequency;
+
+ MsgConfigureChannelizer(int sampleRate, int centerFrequency) :
+ Message(),
+ m_sampleRate(sampleRate),
+ m_centerFrequency(centerFrequency)
+ { }
+ };
+
+ class MsgReportChannelSampleRateChanged : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+
+ static MsgReportChannelSampleRateChanged* create()
+ {
+ return new MsgReportChannelSampleRateChanged();
+ }
+
+ private:
+
+ MsgReportChannelSampleRateChanged() :
+ Message()
+ { }
+ };
+
+ ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI);
+ virtual ~ChannelAnalyzerNG();
+ virtual void destroy() { delete this; }
+ void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; }
+
+ void configure(MessageQueue* messageQueue,
+ int channelSampleRate,
+ Real Bandwidth,
+ Real LowCutoff,
+ int spanLog2,
+ bool ssb,
+ bool pll,
+ unsigned int pllPskOrder);
+
+ 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(); }
+ Real getPllDeltaPhase() const { return m_pll.getDeltaPhi(); }
+ Real getPllPhase() const { return m_pll.getPhiHat(); }
+
+ virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
+ virtual void start();
+ virtual void stop();
+ virtual bool handleMessage(const Message& cmd);
+
+ virtual void getIdentifier(QString& id) { id = objectName(); }
+ virtual void getTitle(QString& title) { title = objectName(); }
+ virtual qint64 getCenterFrequency() const { return m_running.m_frequency; }
+
+ virtual QByteArray serialize() const { return QByteArray(); }
+ virtual bool deserialize(const QByteArray& data __attribute__((unused))) { return false; }
+
+ static const QString m_channelIdURI;
+ static const QString m_channelId;
+
+private:
+
+ struct Config
+ {
+ int m_frequency;
+ int m_inputSampleRate;
+ int m_channelSampleRate;
+ Real m_Bandwidth;
+ Real m_LowCutoff;
+ int m_spanLog2;
+ bool m_ssb;
+ bool m_pll;
+ unsigned int m_pllPskOrder;
+
+ Config() :
+ m_frequency(0),
+ m_inputSampleRate(96000),
+ m_channelSampleRate(96000),
+ m_Bandwidth(5000),
+ m_LowCutoff(300),
+ m_spanLog2(3),
+ m_ssb(false),
+ m_pll(false),
+ m_pllPskOrder(1)
+ {}
+ };
+
+ Config m_config;
+ Config m_running;
+
+ DeviceSourceAPI *m_deviceAPI;
+ ThreadedBasebandSampleSink* m_threadedChannelizer;
+ DownChannelizer* m_channelizer;
+
+ int m_undersampleCount;
+ fftfilt::cmplx m_sum;
+ bool m_usb;
+ double m_magsq;
+ bool m_useInterpolator;
+
+ NCOF m_nco;
+ PhaseLockComplex m_pll;
+ Interpolator m_interpolator;
+ Real m_interpolatorDistance;
+ Real m_interpolatorDistanceRemain;
+
+ fftfilt* SSBFilter;
+ fftfilt* DSBFilter;
+
+ BasebandSampleSink* m_sampleSink;
+ SampleVector m_sampleBuffer;
+ QMutex m_settingsMutex;
+
+ void apply(bool force = false);
+
+ void processOneSample(Complex& c, fftfilt::cmplx *sideband)
+ {
+ int n_out;
+ int decim = 1<runSSB(c, &sideband, m_usb);
+ }
+ else
+ {
+ n_out = DSBFilter->runDSB(c, &sideband);
+ }
+
+ for (int i = 0; i < n_out; i++)
+ {
+ // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
+ // smart decimation with bit gain using float arithmetic (23 bits significand)
+
+ m_sum += sideband[i];
+
+ if (!(m_undersampleCount++ & (decim - 1))) // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
+ {
+ m_sum /= decim;
+ Real re = m_sum.real() / SDR_RX_SCALED;
+ Real im = m_sum.imag() / SDR_RX_SCALED;
+ m_magsq = re*re + im*im;
+
+ if (m_running.m_pll)
+ {
+ m_pll.feed(re, im);
+
+ // Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
+ Real mixI = m_sum.real() * m_pll.getImag() - m_sum.imag() * m_pll.getReal();
+ Real mixQ = m_sum.real() * m_pll.getReal() + m_sum.imag() * m_pll.getImag();
+// Real mixI = m_pll.getReal() * SDR_RX_SCALED;
+// Real mixQ = m_pll.getImag() * SDR_RX_SCALED;
+
+ 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
+ {
+ 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;
+ }
+ }
+ }
+};
+
+#endif // INCLUDE_CHANALYZERNG_H
diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp
index afc23019b..e8a862580 100644
--- a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp
+++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp
@@ -237,15 +237,6 @@ void ChannelAnalyzerNGGUI::tick()
} else {
ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
}
-
- if (ui->pll->isChecked())
- {
- int fHz = round(m_channelAnalyzer->getPllFrequency()*m_rate);
- ui->pll->setToolTip(tr("PLL lock (f:%1 Hz e:%2 rad p:%3 rad)")
- .arg(fHz)
- .arg(m_channelAnalyzer->getPllDeltaPhase())
- .arg(m_channelAnalyzer->getPllPhase()));
- }
}
void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
@@ -262,10 +253,6 @@ void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
void ChannelAnalyzerNGGUI::on_pll_toggled(bool checked)
{
- if (!checked && m_usePll) {
- ui->pll->setToolTip("PLL lock");
- }
-
m_usePll = checked;
applySettings();
}
diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp
index 4a9611666..e81436311 100644
--- a/plugins/channelrx/demodam/amdemod.cpp
+++ b/plugins/channelrx/demodam/amdemod.cpp
@@ -82,7 +82,7 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
m_deviceAPI->addThreadedSink(m_threadedChannelizer);
m_deviceAPI->addChannelAPI(this);
- m_pllFilt.create(101, m_audioSampleRate, 500.0);
+ m_pllFilt.create(101, m_audioSampleRate, 200.0);
m_pll.computeCoefficients(0.05, 0.707, 1000);
m_syncAMBuffIndex = 0;
}
@@ -374,7 +374,7 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
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_pllFilt.create(101, sampleRate, 200.0);
if (m_settings.m_pll) {
m_volumeAGC.resizeNew(sampleRate, 0.003);
@@ -383,6 +383,7 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
}
m_syncAMAGC.resize(sampleRate/4, sampleRate/8, 0.1);
+ m_pll.setSampleRate(sampleRate);
m_settingsMutex.unlock();
m_audioSampleRate = sampleRate;
diff --git a/sdrbase/dsp/phaselockcomplex.cpp b/sdrbase/dsp/phaselockcomplex.cpp
index c3440c0f2..b9d2fe255 100644
--- a/sdrbase/dsp/phaselockcomplex.cpp
+++ b/sdrbase/dsp/phaselockcomplex.cpp
@@ -44,9 +44,14 @@ PhaseLockComplex::PhaseLockComplex() :
m_yRe(1.0),
m_yIm(0.0),
m_freq(0.0),
+ m_freqPrev(0.0),
m_lock(0.0),
m_lockCount(0),
- m_pskOrder(1)
+ m_pskOrder(1),
+ m_lockTime1(480),
+ m_lockTime(2400),
+ m_lockTimef(2400.0f),
+ m_lockThreshold(4.8f)
{
}
@@ -83,6 +88,16 @@ void PhaseLockComplex::computeCoefficients(Real wn, Real zeta, Real K)
void PhaseLockComplex::setPskOrder(unsigned int order)
{
m_pskOrder = order > 0 ? order : 1;
+ reset();
+}
+
+void PhaseLockComplex::setSampleRate(unsigned int sampleRate)
+{
+ m_lockTime1 = sampleRate / 100; // 10ms for order 1
+ m_lockTime = sampleRate / 20; // 50ms for order > 1
+ m_lockTimef = (float) m_lockTime;
+ m_lockThreshold = m_lockTime * 0.002f; // threshold of 0.002 taking division by lock time into account
+ reset();
}
void PhaseLockComplex::reset()
@@ -103,6 +118,7 @@ void PhaseLockComplex::reset()
m_yRe = 1.0f;
m_yIm = 0.0f;
m_freq = 0.0f;
+ m_freqPrev = 0.0f;
m_lock = 0.0f;
m_lockCount = 0;
}
@@ -148,40 +164,69 @@ void PhaseLockComplex::feed(float re, float im)
m_phiHat += 2.0*M_PI;
}
- float dPhi = normalizeAngle(m_phiHat - m_phiHatPrev);
- m_phiHatPrev = m_phiHat;
-
- if (m_phiHatCount < 9)
+ // lock estimation
+ if (m_pskOrder > 1)
{
- m_dPhiHatAccum += dPhi;
+ float dPhi = normalizeAngle(m_phiHat - m_phiHatPrev);
+
+ if (m_phiHatCount < (m_lockTime-1))
+ {
+ m_dPhiHatAccum += dPhi; // re-accumulate phase for differential calculation
+ m_phiHatCount++;
+ }
+ else
+ {
+ float dPhi11 = (m_dPhiHatAccum - m_phiHat1); // optimized out division by lock time
+ float dPhi12 = (m_phiHat1 - m_phiHat2);
+ m_lock = dPhi11 - dPhi12; // second derivative of phase to get lock status
+
+ if ((m_lock > -m_lockThreshold) && (m_lock < m_lockThreshold)) // includes re-multiplication by lock time
+ {
+ if (m_lockCount < 20) { // [0..20]
+ m_lockCount++;
+ }
+ }
+ else
+ {
+ if (m_lockCount > 0) {
+ m_lockCount -= 2;
+ }
+ }
+
+ m_phiHat2 = m_phiHat1;
+ m_phiHat1 = m_dPhiHatAccum;
+ m_dPhiHatAccum = 0.0f;
+ m_phiHatCount = 0;
+ }
+
+ m_phiHatPrev = m_phiHat;
}
else
{
- float dPhi1 = (m_phiHat1 - m_dPhiHatAccum) / 10.0f;
- float dPhi1Prev = (m_phiHat2 - m_phiHat1) / 10.0f;
- m_lock = dPhi1 - dPhi1Prev; // second derivative of phase
+ m_freq = (m_phiHat - m_phiHatPrev) / (2.0*M_PI);
- if ((m_lock > -0.01) && (m_lock < 0.01))
+ if (m_freq < -1.0f) {
+ m_freq += 2.0f;
+ } else if (m_freq > 1.0f) {
+ m_freq -= 2.0f;
+ }
+
+ float dFreq = m_freq - m_freqPrev;
+
+ if ((dFreq > -0.01) && (dFreq < 0.01))
{
- if (m_lockCount < 1000) {
+ if (m_lockCount < (m_lockTime1-1)) { // [0..479]
m_lockCount++;
}
}
else
{
- if (m_lockCount > 0) {
- m_lockCount--;
- }
+ m_lockCount = 0;
}
- m_freq = dPhi1 / 2.0*M_PI; // first derivative of phase
- m_phiHat2 = m_phiHat1;
- m_phiHat1 = m_dPhiHatAccum;
- m_dPhiHatAccum = 0.0f;
- m_phiHatCount = 0;
+ m_phiHatPrev = m_phiHat;
+ m_freqPrev = m_freq;
}
-
- m_dPhiHatAccum += dPhi;
}
float PhaseLockComplex::normalizeAngle(float angle)
diff --git a/sdrbase/dsp/phaselockcomplex.h b/sdrbase/dsp/phaselockcomplex.h
index 675c12bb8..340d83dec 100644
--- a/sdrbase/dsp/phaselockcomplex.h
+++ b/sdrbase/dsp/phaselockcomplex.h
@@ -41,13 +41,14 @@ public:
* \param order 0,1: no PSK (CW), 2: BPSK, 4: QPSK, 8: 8-PSK, ... use powers of two for real cases
*/
void setPskOrder(unsigned int order);
+ /** Set sample rate information only for frequency and lock condition calculation */
+ void setSampleRate(unsigned int sampleRate);
void reset();
void feed(float re, float im);
const std::complex& getComplex() const { return m_y; }
float getReal() const { return m_yRe; }
float getImag() const { return m_yIm; }
- bool locked() const { return m_lockCount > 500; }
- float getFrequency() const { return m_freq; }
+ bool locked() const { return m_lockCount > (m_pskOrder > 1 ? 15 : (m_lockTime1-2)); } // 6
float getDeltaPhi() const { return m_deltaPhi; }
float getPhiHat() const { return m_phiHat; }
@@ -75,9 +76,14 @@ private:
float m_yRe;
float m_yIm;
float m_freq;
+ float m_freqPrev;
float m_lock;
int m_lockCount;
unsigned int m_pskOrder;
+ int m_lockTime1;
+ int m_lockTime;
+ float m_lockTimef;
+ float m_lockThreshold;
};