From fb242d15d3efec64671a6a4dfc1168032e2c45d9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 6 Dec 2019 08:45:00 +0100 Subject: [PATCH] Frequency Tracker: refactoring of classes --- plugins/channelrx/freqtracker/CMakeLists.txt | 8 +- plugins/channelrx/freqtracker/freqtracker.cpp | 416 +++--------------- plugins/channelrx/freqtracker/freqtracker.h | 158 +------ .../freqtracker/freqtrackerbaseband.cpp | 183 ++++++++ .../freqtracker/freqtrackerbaseband.h | 97 ++++ .../channelrx/freqtracker/freqtrackergui.cpp | 10 +- .../freqtracker/freqtrackerplugin.cpp | 2 +- .../freqtracker/freqtrackerreport.cpp | 28 ++ .../channelrx/freqtracker/freqtrackerreport.h | 94 ++++ .../channelrx/freqtracker/freqtrackersink.cpp | 376 ++++++++++++++++ .../channelrx/freqtracker/freqtrackersink.h | 141 ++++++ 11 files changed, 1004 insertions(+), 509 deletions(-) create mode 100644 plugins/channelrx/freqtracker/freqtrackerbaseband.cpp create mode 100644 plugins/channelrx/freqtracker/freqtrackerbaseband.h create mode 100644 plugins/channelrx/freqtracker/freqtrackerreport.cpp create mode 100644 plugins/channelrx/freqtracker/freqtrackerreport.h create mode 100644 plugins/channelrx/freqtracker/freqtrackersink.cpp create mode 100644 plugins/channelrx/freqtracker/freqtrackersink.h diff --git a/plugins/channelrx/freqtracker/CMakeLists.txt b/plugins/channelrx/freqtracker/CMakeLists.txt index 453e501e8..ab63a3350 100644 --- a/plugins/channelrx/freqtracker/CMakeLists.txt +++ b/plugins/channelrx/freqtracker/CMakeLists.txt @@ -1,7 +1,10 @@ project(freqtracker) set(freqtracker_SOURCES - freqtracker.cpp + freqtracker.cpp + freqtrackerbaseband.cpp + freqtrackersink.cpp + freqtrackerreport.cpp freqtrackersettings.cpp freqtrackerwebapiadapter.cpp freqtrackerplugin.cpp @@ -9,6 +12,9 @@ set(freqtracker_SOURCES set(freqtracker_HEADERS freqtracker.h + freqtrackerbaseband.h + freqtrackersink.h + freqtrackerreport.h freqtrackersettings.h freqtrackerwebapiadapter.h freqtrackerplugin.h diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index 48eb9f6bd..246193682 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -43,9 +44,9 @@ #include "util/db.h" #include "util/stepfunctions.h" +#include "freqtrackerreport.h" + MESSAGE_CLASS_DEFINITION(FreqTracker::MsgConfigureFreqTracker, Message) -MESSAGE_CLASS_DEFINITION(FreqTracker::MsgSampleRateNotification, Message) -MESSAGE_CLASS_DEFINITION(FreqTracker::MsgConfigureChannelizer, Message) const QString FreqTracker::m_channelIdURI = "sdrangel.channel.freqtracker"; const QString FreqTracker::m_channelId = "FreqTracker"; @@ -58,22 +59,14 @@ FreqTracker::FreqTracker(DeviceAPI *deviceAPI) : { setObjectName(m_channelId); -#ifdef USE_INTERNAL_TIMER -#warning "Uses internal timer" - m_timer = new QTimer(); - m_timer->start(50); -#else - m_timer = &DSPEngine::instance()->getMasterTimer(); -#endif - m_magsq = 0.0; + m_thread = new QThread(this); + m_basebandSink = new FreqTrackerBaseband(); + propagateMessageQueue(getInputMessageQueue()); + m_basebandSink->moveToThread(m_thread); - m_rrcFilter = new fftfilt(m_settings.m_rfBandwidth / m_channelSampleRate, 2*1024); - m_pll.computeCoefficients(0.002f, 0.5f, 10.0f); // bandwidth, damping factor, loop gain - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); + applySettings(m_settings, true); - m_channelizer = new DownChannelizer(this); - m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); - m_deviceAPI->addChannelSink(m_threadedChannelizer); + m_deviceAPI->addChannelSink(this); m_deviceAPI->addChannelSinkAPI(this); m_networkManager = new QNetworkAccessManager(); @@ -82,18 +75,13 @@ FreqTracker::FreqTracker(DeviceAPI *deviceAPI) : FreqTracker::~FreqTracker() { - disconnectTimer(); -#ifdef USE_INTERNAL_TIMER - m_timer->stop(); - delete m_timer; -#endif disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; + m_deviceAPI->removeChannelSinkAPI(this); - m_deviceAPI->removeChannelSink(m_threadedChannelizer); - delete m_threadedChannelizer; - delete m_channelizer; - delete m_rrcFilter; + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; } uint32_t FreqTracker::getNumberOfDeviceStreams() const @@ -104,142 +92,26 @@ uint32_t FreqTracker::getNumberOfDeviceStreams() const void FreqTracker::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; - Complex ci; - - if (!m_running) { - return; - } - - m_settingsMutex.lock(); - - for (SampleVector::const_iterator it = begin; it != end; ++it) - { - Complex c(it->real(), it->imag()); - c *= m_nco.nextIQ(); - - if (m_interpolatorDistance < 1.0f) // interpolate - { - processOneSample(ci); - - while (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) - { - processOneSample(ci); - } - - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - else // decimate - { - if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) - { - processOneSample(ci); - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - } - } - - m_settingsMutex.unlock(); -} - -void FreqTracker::processOneSample(Complex &ci) -{ - fftfilt::cmplx *sideband; - int n_out; - - if (m_settings.m_rrc) - { - n_out = m_rrcFilter->runFilt(ci, &sideband); - } - else - { - n_out = 1; - sideband = &ci; - } - - for (int i = 0; i < n_out; i++) - { - Real re = sideband[i].real() / SDR_RX_SCALEF; - Real im = sideband[i].imag() / SDR_RX_SCALEF; - Real magsq = re*re + im*im; - m_movingAverage(magsq); - m_magsq = m_movingAverage.asDouble(); - m_magsqSum += magsq; - - if (magsq > m_magsqPeak) - { - m_magsqPeak = magsq; - } - - m_magsqCount++; - - if (m_magsq < m_squelchLevel) - { - if (m_squelchGate > 0) - { - if (m_squelchCount > 0) { - m_squelchCount--; - } - - m_squelchOpen = m_squelchCount >= m_squelchGate; - } - else - { - m_squelchOpen = false; - } - } - else - { - if (m_squelchGate > 0) - { - if (m_squelchCount < 2*m_squelchGate) { - m_squelchCount++; - } - - m_squelchOpen = m_squelchCount >= m_squelchGate; - } - else - { - m_squelchOpen = true; - } - } - - if (m_squelchOpen) - { - if (m_settings.m_trackerType == FreqTrackerSettings::TrackerFLL) - { - m_fll.feed(re, im); - } - else if (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) - { - m_pll.feed(re, im); - } - } - } + m_basebandSink->feed(begin, end); } void FreqTracker::start() { qDebug("FreqTracker::start"); - m_squelchCount = 0; - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); - m_running = true; + + if (m_basebandSampleRate != 0) { + m_basebandSink->setBasebandSampleRate(m_basebandSampleRate); + } + + m_basebandSink->reset(); + m_thread->start(); } void FreqTracker::stop() { qDebug("FreqTracker::stop"); - m_running = false; -} - -Real FreqTracker::getFrequency() const -{ - if (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) { - return (m_pll.getFreq() * m_channelSampleRate) / (2.0 * M_PI); - } else if (m_settings.m_trackerType == FreqTrackerSettings::TrackerFLL) { - return (m_fll.getFreq() * m_channelSampleRate) / (2.0 * M_PI); - } else { - return 0; - } + m_thread->exit(); + m_thread->wait(); } bool FreqTracker::handleMessage(const Message& cmd) @@ -247,31 +119,14 @@ bool FreqTracker::handleMessage(const Message& cmd) if (DSPSignalNotification::match(cmd)) { DSPSignalNotification& notif = (DSPSignalNotification&) cmd; - m_deviceSampleRate = notif.getSampleRate(); - - qDebug() << "FreqTracker::handleMessage: DSPSignalNotification:" - << " m_deviceSampleRate: " << m_deviceSampleRate - << " centerFrequency: " << notif.getCenterFrequency(); - - configureChannelizer(); + m_basebandSampleRate = notif.getSampleRate(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "FreqTracker::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); return true; } - else if (DownChannelizer::MsgChannelizerNotification::match(cmd)) - { - DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; - - if (!m_settings.m_tracking) { - qDebug() << "FreqTracker::handleMessage: MsgChannelizerNotification:" - << " inputSampleRate: " << notif.getSampleRate() - << " inputFrequencyOffset: " << notif.getFrequencyOffset(); - } - - applyChannelSettings(notif.getSampleRate(), notif.getFrequencyOffset()); - setInterpolator(); - - return true; - } else if (MsgConfigureFreqTracker::match(cmd)) { MsgConfigureFreqTracker& cfg = (MsgConfigureFreqTracker&) cmd; @@ -280,34 +135,21 @@ bool FreqTracker::handleMessage(const Message& cmd) return true; } + else if (FreqTrackerReport::MsgSinkFrequencyOffsetNotification::match(cmd)) + { + FreqTrackerReport::MsgSinkFrequencyOffsetNotification& cfg = (FreqTrackerReport::MsgSinkFrequencyOffsetNotification&) cmd; + FreqTrackerSettings settings = m_settings; + settings.m_inputFrequencyOffset = cfg.getFrequencyOffset(); + applySettings(settings, false); + + return true; + } else { return false; } } -void FreqTracker::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force) -{ - if (!m_settings.m_tracking) { - qDebug() << "FreqTracker::applyChannelSettings:" - << " inputSampleRate: " << inputSampleRate - << " inputFrequencyOffset: " << inputFrequencyOffset; - } - - if ((m_inputFrequencyOffset != inputFrequencyOffset) || - (m_inputSampleRate != inputSampleRate) || force) - { - m_nco.setFreq(-inputFrequencyOffset, inputSampleRate); - } - - if ((m_inputSampleRate != inputSampleRate) || force) { - setInterpolator(); - } - - m_inputSampleRate = inputSampleRate; - m_inputFrequencyOffset = inputFrequencyOffset; -} - void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force) { if (!settings.m_tracking) @@ -338,30 +180,18 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force) bool updateChannelizer = false; bool updateInterpolator = false; - if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) - { + if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) { reverseAPIKeys.append("inputFrequencyOffset"); - updateChannelizer = true; } - - if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) - { + if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { reverseAPIKeys.append("log2Decim"); - updateChannelizer = true; } - - if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) - { - updateInterpolator = true; + if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) { reverseAPIKeys.append("rfBandwidth"); } - - if ((m_settings.m_squelch != settings.m_squelch) || force) - { - m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch); + if ((m_settings.m_squelch != settings.m_squelch) || force) { reverseAPIKeys.append("squelch"); } - if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) { reverseAPIKeys.append("rgbColor"); } @@ -371,60 +201,23 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force) if ((m_settings.m_alphaEMA != settings.m_alphaEMA) || force) { reverseAPIKeys.append("alphaEMA"); } - - if ((m_settings.m_tracking != settings.m_tracking) || force) - { + if ((m_settings.m_tracking != settings.m_tracking) || force) { reverseAPIKeys.append("tracking"); - m_avgDeltaFreq = 0.0; - m_lastCorrAbs = 0; - - if (settings.m_tracking) - { - m_pll.reset(); - m_fll.reset(); - } } - - if ((m_settings.m_trackerType != settings.m_trackerType) || force) - { + if ((m_settings.m_trackerType != settings.m_trackerType) || force) { reverseAPIKeys.append("trackerType"); - m_lastCorrAbs = 0; - m_avgDeltaFreq = 0.0; - - if (settings.m_trackerType == FreqTrackerSettings::TrackerFLL) { - m_fll.reset(); - } else if (settings.m_trackerType == FreqTrackerSettings::TrackerPLL) { - m_pll.reset(); - } - - if (settings.m_trackerType == FreqTrackerSettings::TrackerNone) { - disconnectTimer(); - } else { - connectTimer(); - } } - - if ((m_settings.m_pllPskOrder != settings.m_pllPskOrder) || force) - { + if ((m_settings.m_pllPskOrder != settings.m_pllPskOrder) || force) { reverseAPIKeys.append("pllPskOrder"); - - if (settings.m_pllPskOrder < 32) { - m_pll.setPskOrder(settings.m_pllPskOrder); - } } - if ((m_settings.m_rrc != settings.m_rrc) || force) { reverseAPIKeys.append("rrc"); } - if ((m_settings.m_rrcRolloff != settings.m_rrcRolloff) || force) - { + if ((m_settings.m_rrcRolloff != settings.m_rrcRolloff) || force) { reverseAPIKeys.append("rrcRolloff"); - updateInterpolator = true; } - if ((m_settings.m_squelchGate != settings.m_squelchGate) || force) - { + if ((m_settings.m_squelchGate != settings.m_squelchGate) || force) { reverseAPIKeys.append("squelchGate"); - updateInterpolator = true; } if (m_settings.m_streamIndex != settings.m_streamIndex) @@ -432,16 +225,17 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force) if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only { m_deviceAPI->removeChannelSinkAPI(this, m_settings.m_streamIndex); - m_deviceAPI->removeChannelSink(m_threadedChannelizer, m_settings.m_streamIndex); - m_deviceAPI->addChannelSink(m_threadedChannelizer, settings.m_streamIndex); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); m_deviceAPI->addChannelSinkAPI(this, settings.m_streamIndex); - // apply stream sample rate to itself - applyChannelSettings(m_deviceAPI->getSampleMIMO()->getSourceSampleRate(settings.m_streamIndex), m_inputFrequencyOffset); } reverseAPIKeys.append("streamIndex"); } + FreqTrackerBaseband::MsgConfigureFreqTrackerBaseband *msg = FreqTrackerBaseband::MsgConfigureFreqTrackerBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + if (settings.m_useReverseAPI) { bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || @@ -453,71 +247,8 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force) } m_settings = settings; - - if (updateChannelizer) { - configureChannelizer(); - } else if (updateInterpolator) { - setInterpolator(); - } } -void FreqTracker::setInterpolator() -{ - m_settingsMutex.lock(); - m_interpolator.create(16, m_inputSampleRate, m_settings.m_rfBandwidth / 2.2f); - m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_channelSampleRate; - m_rrcFilter->create_rrc_filter(m_settings.m_rfBandwidth / m_channelSampleRate, m_settings.m_rrcRolloff / 100.0); - m_squelchGate = (m_channelSampleRate / 100) * m_settings.m_squelchGate; // gate is given in 10s of ms at channel sample rate - m_settingsMutex.unlock(); -} - -void FreqTracker::configureChannelizer() -{ - if (m_channelSampleRate != m_deviceSampleRate / (1<configure(m_channelizer->getInputMessageQueue(), - m_channelSampleRate, - m_settings.m_inputFrequencyOffset); - - if (m_guiMessageQueue) - { - MsgSampleRateNotification *msg = MsgSampleRateNotification::create( - m_deviceSampleRate / (1<push(msg); - } -} - -void FreqTracker::connectTimer() -{ - if (!m_timerConnected) - { - m_tickCount = 0; - connect(m_timer, SIGNAL(timeout()), this, SLOT(tick())); - m_timerConnected = true; - } -} - -void FreqTracker::disconnectTimer() -{ - if (m_timerConnected) - { - disconnect(m_timer, SIGNAL(timeout()), this, SLOT(tick())); - m_timerConnected = false; - } -} QByteArray FreqTracker::serialize() const { @@ -699,9 +430,9 @@ void FreqTracker::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respo getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); response.getFreqTrackerReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getFreqTrackerReport()->setSquelch(m_squelchOpen ? 1 : 0); - response.getFreqTrackerReport()->setSampleRate(m_channelSampleRate); - response.getFreqTrackerReport()->setChannelSampleRate(m_inputSampleRate); + response.getFreqTrackerReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); + response.getFreqTrackerReport()->setSampleRate(m_basebandSink->getSampleRate()); + response.getFreqTrackerReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); } void FreqTracker::webapiReverseSendSettings(QList& channelSettingsKeys, const FreqTrackerSettings& settings, bool force) @@ -778,42 +509,3 @@ void FreqTracker::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } - -void FreqTracker::tick() -{ - if (getSquelchOpen()) { - m_avgDeltaFreq = m_settings.m_alphaEMA*getFrequency() + (1.0 - m_settings.m_alphaEMA)*m_avgDeltaFreq; - } - - if (m_tickCount < 9) - { - m_tickCount++; - } - else - { - if ((m_settings.m_tracking) && getSquelchOpen()) - { - uint32_t decayDivider = 200.0 * m_settings.m_alphaEMA; - int decayAmount = m_channelSampleRate < decayDivider ? 1 : m_channelSampleRate / decayDivider; - int trim = m_channelSampleRate / 1000; - - if (m_lastCorrAbs < decayAmount) - { - m_lastCorrAbs = m_avgDeltaFreq < 0 ? -m_avgDeltaFreq : m_avgDeltaFreq; - - if (m_lastCorrAbs > trim) - { - FreqTrackerSettings settings = m_settings; - settings.m_inputFrequencyOffset += m_avgDeltaFreq; - applySettings(settings); - } - } - else - { - m_lastCorrAbs -= decayAmount; - } - } - - m_tickCount = 0; - } -} \ No newline at end of file diff --git a/plugins/channelrx/freqtracker/freqtracker.h b/plugins/channelrx/freqtracker/freqtracker.h index d16388103..6c7f93514 100644 --- a/plugins/channelrx/freqtracker/freqtracker.h +++ b/plugins/channelrx/freqtracker/freqtracker.h @@ -25,26 +25,13 @@ #include "dsp/basebandsamplesink.h" #include "channel/channelapi.h" -#include "dsp/ncof.h" -#include "dsp/interpolator.h" -#include "util/movingaverage.h" -#include "dsp/agc.h" -#include "dsp/bandpass.h" -#include "dsp/lowpass.h" -#include "dsp/phaselockcomplex.h" -#include "dsp/freqlockcomplex.h" #include "util/message.h" -#include "util/doublebufferfifo.h" -#include "freqtrackersettings.h" +#include "freqtrackerbaseband.h" class QNetworkAccessManager; class QNetworkReply; class DeviceAPI; -class DownChannelizer; -class ThreadedBasebandSampleSink; -class QTimer; -class fftfilt; class FreqTracker : public BasebandSampleSink, public ChannelAPI { Q_OBJECT @@ -72,51 +59,6 @@ public: { } }; - 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 MsgSampleRateNotification : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgSampleRateNotification* create(int sampleRate, int frequencyOffset) { - return new MsgSampleRateNotification(sampleRate, frequencyOffset); - } - - int getSampleRate() const { return m_sampleRate; } - int getFrequencyOffset() const { return m_frequencyOffset; } - - private: - MsgSampleRateNotification(int sampleRate, int frequencyOffset) : - Message(), - m_sampleRate(sampleRate), - m_frequencyOffset(frequencyOffset) - { } - - int m_sampleRate; - int m_frequencyOffset; - }; - FreqTracker(DeviceAPI *deviceAPI); ~FreqTracker(); virtual void destroy() { delete this; } @@ -166,29 +108,20 @@ public: const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); - uint32_t getSampleRate() const { return m_channelSampleRate; } - double getMagSq() const { return m_magsq; } - bool getSquelchOpen() const { return m_squelchOpen; } - bool getPllLocked() const { return (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) && m_pll.locked(); } - Real getFrequency() const; - Real getAvgDeltaFreq() const { return m_avgDeltaFreq; } + uint32_t getSampleRate() const { return m_basebandSink->getSampleRate(); } + double getMagSq() const { return m_basebandSink->getMagSq(); } + bool getSquelchOpen() const { return m_basebandSink->getSquelchOpen(); } + bool getPllLocked() const { return m_basebandSink->getPllLocked(); } + Real getFrequency() const { return m_basebandSink->getFrequency(); }; + Real getAvgDeltaFreq() const { return m_basebandSink->getAvgDeltaFreq(); } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_basebandSink->getMagSqLevels(avg, peak, nbSamples); } - void getMagSqLevels(double& avg, double& peak, int& nbSamples) - { - if (m_magsqCount > 0) - { - m_magsq = m_magsqSum / m_magsqCount; - m_magSqLevelStore.m_magsq = m_magsq; - m_magSqLevelStore.m_magsqPeak = m_magsqPeak; - } + void propagateMessageQueue(MessageQueue *messageQueueToInput) { + m_basebandSink->setMessageQueueToInput(messageQueueToInput); + } - avg = m_magSqLevelStore.m_magsq; - peak = m_magSqLevelStore.m_magsqPeak; - nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; - - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; + void propagateMessageQueueToGUI(MessageQueue *messageQueueToGUI) { + m_basebandSink->setMessageQueueToGUI(messageQueueToGUI); } uint32_t getNumberOfDeviceStreams() const; @@ -197,78 +130,21 @@ public: static const QString m_channelId; private: - struct MagSqLevelsStore - { - MagSqLevelsStore() : - m_magsq(1e-12), - m_magsqPeak(1e-12) - {} - double m_magsq; - double m_magsqPeak; - }; - - enum RateState { - RSInitialFill, - RSRunning - }; - - DeviceAPI *m_deviceAPI; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; + DeviceAPI* m_deviceAPI; + QThread *m_thread; + FreqTrackerBaseband* m_basebandSink; FreqTrackerSettings m_settings; - - uint32_t m_deviceSampleRate; - int m_inputSampleRate; - int m_inputFrequencyOffset; - uint32_t m_channelSampleRate; - bool m_running; - - NCOF m_nco; - PhaseLockComplex m_pll; - FreqLockComplex m_fll; - Interpolator m_interpolator; - Real m_interpolatorDistance; - Real m_interpolatorDistanceRemain; - - fftfilt* m_rrcFilter; - - Real m_squelchLevel; - uint32_t m_squelchCount; - bool m_squelchOpen; - uint32_t m_squelchGate; //!< Squelch gate in samples - double m_magsq; - double m_magsqSum; - double m_magsqPeak; - int m_magsqCount; - MagSqLevelsStore m_magSqLevelStore; - - MovingAverageUtil m_movingAverage; - + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink static const int m_udpBlockSize; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; - const QTimer *m_timer; - bool m_timerConnected; - uint32_t m_tickCount; - int m_lastCorrAbs; - Real m_avgDeltaFreq; - QMutex m_settingsMutex; - void applySettings(const FreqTrackerSettings& settings, bool force = false); - void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); - void setInterpolator(); - void configureChannelizer(); - void connectTimer(); - void disconnectTimer(); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const FreqTrackerSettings& settings, bool force); - void processOneSample(Complex &ci); - private slots: void networkManagerFinished(QNetworkReply *reply); - void tick(); }; #endif // INCLUDE_FREQTRACKER_H diff --git a/plugins/channelrx/freqtracker/freqtrackerbaseband.cpp b/plugins/channelrx/freqtracker/freqtrackerbaseband.cpp new file mode 100644 index 000000000..4e670aabd --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackerbaseband.cpp @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "freqtrackerreport.h" +#include "freqtrackerbaseband.h" + +MESSAGE_CLASS_DEFINITION(FreqTrackerBaseband::MsgConfigureFreqTrackerBaseband, Message) + +FreqTrackerBaseband::FreqTrackerBaseband() : + m_mutex(QMutex::Recursive), + m_messageQueueToGUI(nullptr) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + qDebug("FreqTrackerBaseband::FreqTrackerBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &FreqTrackerBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +FreqTrackerBaseband::~FreqTrackerBaseband() +{ + delete m_channelizer; +} + +void FreqTrackerBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void FreqTrackerBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void FreqTrackerBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void FreqTrackerBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool FreqTrackerBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureFreqTrackerBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureFreqTrackerBaseband& cfg = (MsgConfigureFreqTrackerBaseband&) cmd; + qDebug() << "FreqTrackerBaseband::handleMessage: MsgConfigureFreqTrackerBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + qDebug() << "FreqTrackerBaseband::handleMessage: DSPSignalNotification:" + << "basebandSampleRate:" << m_basebandSampleRate; + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(m_basebandSampleRate)); + m_channelizer->setBasebandSampleRate(m_basebandSampleRate); + m_sink.applyChannelSettings( + m_basebandSampleRate / (1<getChannelSampleRate(), + m_channelizer->getChannelFrequencyOffset() + ); + + if (getMessageQueueToGUI()) + { + FreqTrackerReport::MsgNotificationToGUI *msg = FreqTrackerReport::MsgNotificationToGUI::create( + m_basebandSampleRate / (1<push(msg); + } + + return true; + } + else + { + return false; + } +} + +void FreqTrackerBaseband::applySettings(const FreqTrackerSettings& settings, bool force) +{ + if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) + || (m_settings.m_log2Decim != settings.m_log2Decim)|| force) + { + m_channelizer->setChannelization(m_basebandSampleRate/(1<getChannelSampleRate(), + m_channelizer->getChannelFrequencyOffset() + ); + + if (getMessageQueueToGUI()) + { + FreqTrackerReport::MsgNotificationToGUI *msg = FreqTrackerReport::MsgNotificationToGUI::create( + m_basebandSampleRate / (1<push(msg); + } + } + + m_sink.applySettings(settings, force); + + m_settings = settings; +} + +int FreqTrackerBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void FreqTrackerBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); +} diff --git a/plugins/channelrx/freqtracker/freqtrackerbaseband.h b/plugins/channelrx/freqtracker/freqtrackerbaseband.h new file mode 100644 index 000000000..d2579df9a --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackerbaseband.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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_FREQTRACKERBASEBAND_H +#define INCLUDE_FREQTRACKERBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "freqtrackersink.h" + +class DownSampleChannelizer; + +class FreqTrackerBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureFreqTrackerBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FreqTrackerSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureFreqTrackerBaseband* create(const FreqTrackerSettings& settings, bool force) + { + return new MsgConfigureFreqTrackerBaseband(settings, force); + } + + private: + FreqTrackerSettings m_settings; + bool m_force; + + MsgConfigureFreqTrackerBaseband(const FreqTrackerSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + FreqTrackerBaseband(); + ~FreqTrackerBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setBasebandSampleRate(int sampleRate); + void setMessageQueueToInput(MessageQueue *messageQueue) { m_sink.setMessageQueueToInput(messageQueue); } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } + + double getMagSq() const { return m_sink.getMagSq(); } + bool getSquelchOpen() const { return m_sink.getSquelchOpen(); } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); } + uint32_t getSampleRate() const { return m_sink.getSampleRate(); } + bool getPllLocked() const { return m_sink.getPllLocked(); } + Real getFrequency() const { return m_sink.getFrequency(); }; + Real getAvgDeltaFreq() const { return m_sink.getAvgDeltaFreq(); } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + FreqTrackerSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + FreqTrackerSettings m_settings; + unsigned int m_basebandSampleRate; + QMutex m_mutex; + + MessageQueue *m_messageQueueToGUI; + + MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; } + bool handleMessage(const Message& cmd); + void applySettings(const FreqTrackerSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_FREQTRACKERBASEBAND_H diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index dd7f9b431..3826982ff 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -37,6 +37,7 @@ #include "gui/audioselectdialog.h" #include "freqtracker.h" +#include "freqtrackerreport.h" FreqTrackerGUI* FreqTrackerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) { @@ -105,13 +106,13 @@ bool FreqTrackerGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } - else if (FreqTracker::MsgSampleRateNotification::match(message)) + else if (FreqTrackerReport::MsgNotificationToGUI::match(message)) { if (!m_settings.m_tracking) { - qDebug("FreqTrackerGUI::handleMessage: FreqTracker::MsgSampleRateNotification"); + qDebug("FreqTrackerGUI::handleMessage: FreqTrackerReport::MsgNotificationToGUI"); } - const FreqTracker::MsgSampleRateNotification& cfg = (FreqTracker::MsgSampleRateNotification&) message; - m_channelSampleRate = cfg.getSampleRate(); + const FreqTrackerReport::MsgNotificationToGUI& cfg = (FreqTrackerReport::MsgNotificationToGUI&) message; + m_channelSampleRate = cfg.getSinkSampleRate(); ui->channelSampleRateText->setText(tr("%1k").arg(QString::number(m_channelSampleRate / 1000.0f, 'g', 5))); blockApplySettings(true); ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset); @@ -306,6 +307,7 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_freqTracker = reinterpret_cast(rxChannel); //new FreqTracker(m_deviceUISet->m_deviceSourceAPI); m_freqTracker->setMessageQueueToGUI(getInputMessageQueue()); + m_freqTracker->propagateMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms diff --git a/plugins/channelrx/freqtracker/freqtrackerplugin.cpp b/plugins/channelrx/freqtracker/freqtrackerplugin.cpp index bd598fd75..76232a53e 100644 --- a/plugins/channelrx/freqtracker/freqtrackerplugin.cpp +++ b/plugins/channelrx/freqtracker/freqtrackerplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor FreqTrackerPlugin::m_pluginDescriptor = { QString("Frequency Tracker"), - QString("4.11.6"), + QString("4.12.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/freqtracker/freqtrackerreport.cpp b/plugins/channelrx/freqtracker/freqtrackerreport.cpp new file mode 100644 index 000000000..47b0c767b --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackerreport.cpp @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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 "freqtrackerreport.h" + +MESSAGE_CLASS_DEFINITION(FreqTrackerReport::MsgNotificationToGUI, Message) +MESSAGE_CLASS_DEFINITION(FreqTrackerReport::MsgConfigureChannelizer, Message) +MESSAGE_CLASS_DEFINITION(FreqTrackerReport::MsgSinkFrequencyOffsetNotification, Message) + +FreqTrackerReport::FreqTrackerReport() +{} + +FreqTrackerReport::~FreqTrackerReport() +{} diff --git a/plugins/channelrx/freqtracker/freqtrackerreport.h b/plugins/channelrx/freqtracker/freqtrackerreport.h new file mode 100644 index 000000000..6220a3c77 --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackerreport.h @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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_FREQTRACKERREPORT_H +#define INCLUDE_FREQTRACKERREPORT_H + +#include "util/message.h" + +class FreqTrackerReport +{ +public: + class MsgNotificationToGUI : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgNotificationToGUI* create(int sinkSampleRate, int sinkFrequencyOffset) { + return new MsgNotificationToGUI(sinkSampleRate, sinkFrequencyOffset); + } + + int getSinkSampleRate() const { return m_sinkSampleRate; } + int getSinkFrequencyOffset() const { return m_sinkFrequencyOffset; } + + private: + MsgNotificationToGUI(int sinkSampleRate, int sinkFrequencyOffset) : + Message(), + m_sinkSampleRate(sinkSampleRate), + m_sinkFrequencyOffset(sinkFrequencyOffset) + { } + + int m_sinkSampleRate; + int m_sinkFrequencyOffset; + }; + + 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 MsgSinkFrequencyOffsetNotification : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgSinkFrequencyOffsetNotification* create(int frequencyOffset) { + return new MsgSinkFrequencyOffsetNotification(frequencyOffset); + } + + int getFrequencyOffset() const { return m_frequencyOffset; } + + private: + MsgSinkFrequencyOffsetNotification(int frequencyOffset) : + Message(), + m_frequencyOffset(frequencyOffset) + { } + + int m_frequencyOffset; + }; + + FreqTrackerReport(); + ~FreqTrackerReport(); +}; + +#endif // INCLUDE_FREQTRACKERREPORT_H diff --git a/plugins/channelrx/freqtracker/freqtrackersink.cpp b/plugins/channelrx/freqtracker/freqtrackersink.cpp new file mode 100644 index 000000000..390ae77ce --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackersink.cpp @@ -0,0 +1,376 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/fftfilt.h" +#include "util/db.h" +#include "util/stepfunctions.h" +#include "util/messagequeue.h" + +#include "freqtrackerreport.h" +#include "freqtrackersink.h" + +FreqTrackerSink::FreqTrackerSink() : + m_channelSampleRate(48000), + m_inputFrequencyOffset(0), + m_sinkSampleRate(48000), + m_squelchOpen(false), + m_squelchGate(0), + m_magsqSum(0.0f), + m_magsqPeak(0.0f), + m_magsqCount(0), + m_timerConnected(false), + m_tickCount(0), + m_lastCorrAbs(0), + m_avgDeltaFreq(0.0), + m_messageQueueToInput(nullptr) +{ +#ifdef USE_INTERNAL_TIMER +#warning "Uses internal timer" + m_timer = new QTimer(); + m_timer->start(50); +#else + m_timer = &DSPEngine::instance()->getMasterTimer(); +#endif + m_magsq = 0.0; + + m_rrcFilter = new fftfilt(m_settings.m_rfBandwidth / m_sinkSampleRate, 2*1024); + m_pll.computeCoefficients(0.002f, 0.5f, 10.0f); // bandwidth, damping factor, loop gain + applyChannelSettings(m_channelSampleRate, m_inputFrequencyOffset, true); +} + +FreqTrackerSink::~FreqTrackerSink() +{ + disconnectTimer(); +#ifdef USE_INTERNAL_TIMER + m_timer->stop(); + delete m_timer; +#endif + delete m_rrcFilter; +} + +void FreqTrackerSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci; + + for (SampleVector::const_iterator it = begin; it != end; ++it) + { + Complex c(it->real(), it->imag()); + c *= m_nco.nextIQ(); + + if (m_interpolatorDistance < 1.0f) // interpolate + { + processOneSample(ci); + + while (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { + processOneSample(ci); + } + + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + else // decimate + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } +} + +void FreqTrackerSink::processOneSample(Complex &ci) +{ + fftfilt::cmplx *sideband; + int n_out; + + if (m_settings.m_rrc) + { + n_out = m_rrcFilter->runFilt(ci, &sideband); + } + else + { + n_out = 1; + sideband = &ci; + } + + for (int i = 0; i < n_out; i++) + { + Real re = sideband[i].real() / SDR_RX_SCALEF; + Real im = sideband[i].imag() / SDR_RX_SCALEF; + Real magsq = re*re + im*im; + m_movingAverage(magsq); + m_magsq = m_movingAverage.asDouble(); + m_magsqSum += magsq; + + if (magsq > m_magsqPeak) + { + m_magsqPeak = magsq; + } + + m_magsqCount++; + + if (m_magsq < m_squelchLevel) + { + if (m_squelchGate > 0) + { + if (m_squelchCount > 0) { + m_squelchCount--; + } + + m_squelchOpen = m_squelchCount >= m_squelchGate; + } + else + { + m_squelchOpen = false; + } + } + else + { + if (m_squelchGate > 0) + { + if (m_squelchCount < 2*m_squelchGate) { + m_squelchCount++; + } + + m_squelchOpen = m_squelchCount >= m_squelchGate; + } + else + { + m_squelchOpen = true; + } + } + + if (m_squelchOpen) + { + if (m_settings.m_trackerType == FreqTrackerSettings::TrackerFLL) { + m_fll.feed(re, im); + } else if (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) { + m_pll.feed(re, im); + } + } + } +} + +Real FreqTrackerSink::getFrequency() const +{ + if (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) { + return (m_pll.getFreq() * m_sinkSampleRate) / (2.0 * M_PI); + } else if (m_settings.m_trackerType == FreqTrackerSettings::TrackerFLL) { + return (m_fll.getFreq() * m_sinkSampleRate) / (2.0 * M_PI); + } else { + return 0; + } +} + +void FreqTrackerSink::applyChannelSettings(int sinkSampleRate, int channelSampleRate, int inputFrequencyOffset, bool force) +{ + if (!m_settings.m_tracking) + { + qDebug() << "FreqTracker::applyChannelSettings:" + << " sinkSampleRate: " << sinkSampleRate + << " channelSampleRate: " << channelSampleRate + << " inputFrequencyOffset: " << inputFrequencyOffset; + } + + bool useInterpolator = false; + + if ((m_inputFrequencyOffset != inputFrequencyOffset) || + (m_channelSampleRate != channelSampleRate) || force) + { + m_nco.setFreq(-inputFrequencyOffset, channelSampleRate); + } + + if ((m_channelSampleRate != channelSampleRate) + || (m_sinkSampleRate != sinkSampleRate) || force) + { + m_pll.setSampleRate(sinkSampleRate); + m_fll.setSampleRate(sinkSampleRate); + useInterpolator = true; + } + + m_sinkSampleRate = sinkSampleRate; + m_channelSampleRate = channelSampleRate; + m_inputFrequencyOffset = inputFrequencyOffset; + + if (useInterpolator) { + setInterpolator(); + } +} + +void FreqTrackerSink::applySettings(const FreqTrackerSettings& settings, bool force) +{ + if (!settings.m_tracking) + { + qDebug() << "FreqTrackerSink::applySettings:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " m_log2Decim: " << settings.m_log2Decim + << " m_squelch: " << settings.m_squelch + << " m_rgbColor: " << settings.m_rgbColor + << " m_title: " << settings.m_title + << " m_alphaEMA: " << settings.m_alphaEMA + << " m_tracking: " << settings.m_tracking + << " m_trackerType: " << settings.m_trackerType + << " m_pllPskOrder: " << settings.m_pllPskOrder + << " m_rrc: " << settings.m_rrc + << " m_rrcRolloff: " << settings.m_rrcRolloff + << " m_streamIndex: " << settings.m_streamIndex + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIPort: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex + << " force: " << force; + } + + + if ((m_settings.m_squelch != settings.m_squelch) || force) { + m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch); + } + + if ((m_settings.m_tracking != settings.m_tracking) || force) + { + m_avgDeltaFreq = 0.0; + m_lastCorrAbs = 0; + + if (settings.m_tracking) + { + m_pll.reset(); + m_fll.reset(); + } + } + + if ((m_settings.m_trackerType != settings.m_trackerType) || force) + { + m_lastCorrAbs = 0; + m_avgDeltaFreq = 0.0; + + if (settings.m_trackerType == FreqTrackerSettings::TrackerFLL) { + m_fll.reset(); + } else if (settings.m_trackerType == FreqTrackerSettings::TrackerPLL) { + m_pll.reset(); + } + + if (settings.m_trackerType == FreqTrackerSettings::TrackerNone) { + disconnectTimer(); + } else { + connectTimer(); + } + } + + if ((m_settings.m_pllPskOrder != settings.m_pllPskOrder) || force) + { + if (settings.m_pllPskOrder < 32) { + m_pll.setPskOrder(settings.m_pllPskOrder); + } + } + + bool useInterpolator = false; + + if ((m_settings.m_rrcRolloff != settings.m_rrcRolloff) + || (m_settings.m_rfBandwidth != settings.m_rfBandwidth) + || (m_settings.m_squelchGate != settings.m_squelchGate) || force) { + useInterpolator = true; + } + + m_settings = settings; + + if (useInterpolator) { + setInterpolator(); + } +} + +void FreqTrackerSink::setInterpolator() +{ + qDebug("FreqTrackerSink::setInterpolator: m_sinkSampleRate: %u m_channelSampleRate: %d", + m_sinkSampleRate, m_channelSampleRate); + + m_interpolator.create(16, m_channelSampleRate, m_settings.m_rfBandwidth / 2.2f); + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_sinkSampleRate; + m_rrcFilter->create_rrc_filter(m_settings.m_rfBandwidth / m_sinkSampleRate, m_settings.m_rrcRolloff / 100.0); + m_squelchGate = (m_sinkSampleRate / 100) * m_settings.m_squelchGate; // gate is given in 10s of ms at channel sample rate +} + +void FreqTrackerSink::connectTimer() +{ + if (!m_timerConnected) + { + m_tickCount = 0; + connect(m_timer, SIGNAL(timeout()), this, SLOT(tick())); + m_timerConnected = true; + } +} + +void FreqTrackerSink::disconnectTimer() +{ + if (m_timerConnected) + { + disconnect(m_timer, SIGNAL(timeout()), this, SLOT(tick())); + m_timerConnected = false; + } +} + +void FreqTrackerSink::tick() +{ + if (getSquelchOpen()) { + m_avgDeltaFreq = m_settings.m_alphaEMA*getFrequency() + (1.0 - m_settings.m_alphaEMA)*m_avgDeltaFreq; + } + + if (m_tickCount < 9) + { + m_tickCount++; + } + else + { + if ((m_settings.m_tracking) && getSquelchOpen()) + { + uint32_t decayDivider = 200.0 * m_settings.m_alphaEMA; + int decayAmount = m_sinkSampleRate < decayDivider ? 1 : m_sinkSampleRate / decayDivider; + int trim = m_sinkSampleRate / 1000; + + if (m_lastCorrAbs < decayAmount) + { + m_lastCorrAbs = m_avgDeltaFreq < 0 ? -m_avgDeltaFreq : m_avgDeltaFreq; + + if (m_lastCorrAbs > trim) + { + FreqTrackerSettings settings = m_settings; + settings.m_inputFrequencyOffset += m_avgDeltaFreq; + if (getMessageQueueToInput()) + { + FreqTrackerReport::MsgSinkFrequencyOffsetNotification *msg + = FreqTrackerReport::MsgSinkFrequencyOffsetNotification::create(settings.m_inputFrequencyOffset); + getMessageQueueToInput()->push(msg); + } + } + } + else + { + m_lastCorrAbs -= decayAmount; + } + } + + m_tickCount = 0; + } +} diff --git a/plugins/channelrx/freqtracker/freqtrackersink.h b/plugins/channelrx/freqtracker/freqtrackersink.h new file mode 100644 index 000000000..be732bcd4 --- /dev/null +++ b/plugins/channelrx/freqtracker/freqtrackersink.h @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 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 // +// (at your option) any later version. // +// // +// 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_FREQTRACKERSINK_H +#define INCLUDE_FREQTRACKERSINK_H + +#include + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/ncof.h" +#include "dsp/interpolator.h" +#include "util/movingaverage.h" +#include "dsp/agc.h" +#include "dsp/bandpass.h" +#include "dsp/lowpass.h" +#include "dsp/phaselockcomplex.h" +#include "dsp/freqlockcomplex.h" +#include "util/message.h" +#include "util/doublebufferfifo.h" + +#include "freqtrackersettings.h" + +class fftfilt; +class MessageQueue; +class QTimer; + +class FreqTrackerSink : public QObject, public ChannelSampleSink { + Q_OBJECT +public: + FreqTrackerSink(); + ~FreqTrackerSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void applySettings(const FreqTrackerSettings& settings, bool force = false); + void applyChannelSettings(int sinkSampleRate, int channelSampleRate, int channelFrequencyOffset, bool force = false); + void setMessageQueueToInput(MessageQueue *messageQueue) { m_messageQueueToInput = messageQueue;} + + uint32_t getSampleRate() const { return m_sinkSampleRate; } + double getMagSq() const { return m_magsq; } + bool getSquelchOpen() const { return m_squelchOpen; } + bool getPllLocked() const { return (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) && m_pll.locked(); } + Real getFrequency() const; + Real getAvgDeltaFreq() const { return m_avgDeltaFreq; } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) + { + if (m_magsqCount > 0) + { + m_magsq = m_magsqSum / m_magsqCount; + m_magSqLevelStore.m_magsq = m_magsq; + m_magSqLevelStore.m_magsqPeak = m_magsqPeak; + } + + avg = m_magSqLevelStore.m_magsq; + peak = m_magSqLevelStore.m_magsqPeak; + nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; + + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + } + +private: + struct MagSqLevelsStore + { + MagSqLevelsStore() : + m_magsq(1e-12), + m_magsqPeak(1e-12) + {} + double m_magsq; + double m_magsqPeak; + }; + + enum RateState { + RSInitialFill, + RSRunning + }; + + FreqTrackerSettings m_settings; + + int m_channelSampleRate; + int m_inputFrequencyOffset; + uint32_t m_sinkSampleRate; + + NCOF m_nco; + PhaseLockComplex m_pll; + FreqLockComplex m_fll; + Interpolator m_interpolator; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + + fftfilt* m_rrcFilter; + + Real m_squelchLevel; + uint32_t m_squelchCount; + bool m_squelchOpen; + uint32_t m_squelchGate; //!< Squelch gate in samples + double m_magsq; + double m_magsqSum; + double m_magsqPeak; + int m_magsqCount; + MagSqLevelsStore m_magSqLevelStore; + + MovingAverageUtil m_movingAverage; + + const QTimer *m_timer; + bool m_timerConnected; + uint32_t m_tickCount; + int m_lastCorrAbs; + Real m_avgDeltaFreq; + + MessageQueue *m_messageQueueToInput; + + MessageQueue *getMessageQueueToInput() { return m_messageQueueToInput; } + void setInterpolator(); + void connectTimer(); + void disconnectTimer(); + void processOneSample(Complex &ci); + +private slots: + void tick(); +}; + +#endif // INCLUDE_FREQTRACKERSINK_H \ No newline at end of file