diff --git a/doc/img/FreqTracker_plugin.png b/doc/img/FreqTracker_plugin.png index a3304a78a..8ea6cbbee 100644 Binary files a/doc/img/FreqTracker_plugin.png and b/doc/img/FreqTracker_plugin.png differ diff --git a/doc/img/FreqTracker_plugin.xcf b/doc/img/FreqTracker_plugin.xcf index b84be5bfe..c542504ee 100644 Binary files a/doc/img/FreqTracker_plugin.xcf and b/doc/img/FreqTracker_plugin.xcf differ diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index 40b60679b..73e66fb6c 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -55,12 +55,14 @@ const int FreqTracker::m_udpBlockSize = 512; FreqTracker::FreqTracker(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), + m_spectrumVis(SDR_RX_SCALEF), m_basebandSampleRate(0) { setObjectName(m_channelId); m_thread = new QThread(this); m_basebandSink = new FreqTrackerBaseband(); + m_basebandSink->setSpectrumSink(&m_spectrumVis); propagateMessageQueue(getInputMessageQueue()); m_basebandSink->moveToThread(m_thread); diff --git a/plugins/channelrx/freqtracker/freqtracker.h b/plugins/channelrx/freqtracker/freqtracker.h index e820ec619..c374384ae 100644 --- a/plugins/channelrx/freqtracker/freqtracker.h +++ b/plugins/channelrx/freqtracker/freqtracker.h @@ -24,6 +24,7 @@ #include #include "dsp/basebandsamplesink.h" +#include "dsp/spectrumvis.h" #include "channel/channelapi.h" #include "util/message.h" @@ -109,6 +110,7 @@ public: const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); + SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } uint32_t getSampleRate() const { return m_basebandSink->getSampleRate(); } double getMagSq() const { return m_basebandSink->getMagSq(); } bool getSquelchOpen() const { return m_basebandSink->getSquelchOpen(); } @@ -131,6 +133,7 @@ private: QThread *m_thread; FreqTrackerBaseband* m_basebandSink; FreqTrackerSettings m_settings; + SpectrumVis m_spectrumVis; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink static const int m_udpBlockSize; QNetworkAccessManager *m_networkManager; diff --git a/plugins/channelrx/freqtracker/freqtrackerbaseband.h b/plugins/channelrx/freqtracker/freqtrackerbaseband.h index ae3ac94ea..716b9ac7a 100644 --- a/plugins/channelrx/freqtracker/freqtrackerbaseband.h +++ b/plugins/channelrx/freqtracker/freqtrackerbaseband.h @@ -28,6 +28,7 @@ #include "freqtrackersink.h" class DownChannelizer; +class SpectrumVis; class FreqTrackerBaseband : public QObject { @@ -61,6 +62,7 @@ public: 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 + void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumVis = spectrumSink; m_sink.setSpectrumSink(spectrumSink); } int getChannelSampleRate() const; void setBasebandSampleRate(int sampleRate); void setMessageQueueToInput(MessageQueue *messageQueue) { m_sink.setMessageQueueToInput(messageQueue); } @@ -80,6 +82,7 @@ private: MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication FreqTrackerSettings m_settings; unsigned int m_basebandSampleRate; + SpectrumVis *m_spectrumVis; QMutex m_mutex; bool handleMessage(const Message& cmd); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 91d143a5e..b876a872e 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -91,6 +91,8 @@ bool FreqTrackerGUI::handleMessage(const Message& message) m_basebandSampleRate = cfg.getSampleRate(); int sinkSampleRate = m_basebandSampleRate / (1<channelSampleRateText->setText(tr("%1k").arg(QString::number(sinkSampleRate / 1000.0f, 'g', 5))); + ui->glSpectrum->setSampleRate(sinkSampleRate); + m_pllChannelMarker.setBandwidth(sinkSampleRate/1000); if (sinkSampleRate > 1000) { ui->rfBW->setMaximum(sinkSampleRate/100); @@ -151,6 +153,8 @@ void FreqTrackerGUI::on_log2Decim_currentIndexChanged(int index) m_settings.m_log2Decim = index < 0 ? 0 : index > 6 ? 6 : index; int sinkSampleRate = m_basebandSampleRate / (1<channelSampleRateText->setText(tr("%1k").arg(QString::number(sinkSampleRate / 1000.0f, 'g', 5))); + ui->glSpectrum->setSampleRate(sinkSampleRate); + m_pllChannelMarker.setBandwidth(sinkSampleRate/1000); if (sinkSampleRate > 1000) { ui->rfBW->setMaximum(sinkSampleRate/100); @@ -288,6 +292,7 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_pllChannelMarker(this), m_basebandSampleRate(0), m_doApplySettings(true), m_squelchOpen(false), @@ -300,6 +305,8 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_freqTracker = reinterpret_cast(rxChannel); m_freqTracker->setMessageQueueToGUI(getInputMessageQueue()); + m_spectrumVis = m_freqTracker->getSpectrumVis(); + m_spectrumVis->setGLSpectrum(ui->glSpectrum); connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms @@ -318,10 +325,23 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B setTitleColor(m_channelMarker.getColor()); m_settings.setChannelMarker(&m_channelMarker); + m_settings.setSpectrumGUI(ui->spectrumGUI); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); + ui->glSpectrum->setCenterFrequency(0); + m_pllChannelMarker.blockSignals(true); + m_pllChannelMarker.setColor(Qt::gray); + m_pllChannelMarker.setCenterFrequency(0); + m_pllChannelMarker.setBandwidth(35); + m_pllChannelMarker.setTitle("Tracker"); + m_pllChannelMarker.setMovable(false); + m_pllChannelMarker.blockSignals(false); + m_pllChannelMarker.setVisible(true); + ui->glSpectrum->addChannelMarker(&m_pllChannelMarker); + ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -445,6 +465,7 @@ void FreqTrackerGUI::tick() int freq = m_freqTracker->getAvgDeltaFreq(); QLocale loc; ui->trackingFrequencyText->setText(tr("%1 Hz").arg(loc.toString(freq))); + m_pllChannelMarker.setCenterFrequency(freq); if (m_settings.m_tracking) { ui->tracking->setToolTip("Tracking on"); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index a0fa5b636..6e9b6e78f 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -28,9 +28,9 @@ class PluginAPI; class DeviceUISet; - class FreqTracker; class BasebandSampleSink; +class SpectrumVis; namespace Ui { class FreqTrackerGUI; @@ -57,11 +57,13 @@ private: PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; + ChannelMarker m_pllChannelMarker; FreqTrackerSettings m_settings; int m_basebandSampleRate; bool m_doApplySettings; FreqTracker* m_freqTracker; + SpectrumVis* m_spectrumVis; bool m_squelchOpen; uint32_t m_tickCount; MessageQueue m_inputMessageQueue; diff --git a/plugins/channelrx/freqtracker/freqtrackergui.ui b/plugins/channelrx/freqtracker/freqtrackergui.ui index 3927ef6d2..1419b08ed 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.ui +++ b/plugins/channelrx/freqtracker/freqtrackergui.ui @@ -6,7 +6,7 @@ 0 0 - 360 + 400 327 @@ -18,16 +18,10 @@ - 360 + 400 100 - - - 360 - 16777215 - - Liberation Sans @@ -45,7 +39,7 @@ 0 0 - 358 + 401 140 @@ -686,7 +680,7 @@ 0 150 - 351 + 391 171 diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.cpp b/plugins/channelrx/freqtracker/freqtrackersettings.cpp index 65f2c4f53..bf187540d 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.cpp +++ b/plugins/channelrx/freqtracker/freqtrackersettings.cpp @@ -23,7 +23,8 @@ #include "freqtrackersettings.h" FreqTrackerSettings::FreqTrackerSettings() : - m_channelMarker(0) + m_channelMarker(0), + m_spectrumGUI(0) { resetToDefaults(); } @@ -57,6 +58,10 @@ QByteArray FreqTrackerSettings::serialize() const s.writeS32(1, m_inputFrequencyOffset); s.writeS32(2, m_rfBandwidth/100); s.writeU32(3, m_log2Decim); + + if (m_spectrumGUI) { + s.writeBlob(4, m_spectrumGUI->serialize()); + } s.writeS32(5, m_squelch); if (m_channelMarker) { @@ -105,7 +110,13 @@ bool FreqTrackerSettings::deserialize(const QByteArray& data) m_rfBandwidth = 100 * tmp; d.readU32(3, &utmp, 0); m_log2Decim = utmp > 6 ? 6 : utmp; - d.readS32(4, &tmp, 20); + + if (m_spectrumGUI) + { + d.readBlob(4, &bytetmp); + m_spectrumGUI->deserialize(bytetmp); + } + d.readS32(5, &tmp, -40); m_squelch = tmp; d.readBlob(6, &bytetmp); diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.h b/plugins/channelrx/freqtracker/freqtrackersettings.h index aabd85f7e..71e287f1e 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.h +++ b/plugins/channelrx/freqtracker/freqtrackersettings.h @@ -41,6 +41,7 @@ struct FreqTrackerSettings quint32 m_rgbColor; QString m_title; Serializable *m_channelMarker; + Serializable *m_spectrumGUI; float m_alphaEMA; //!< alpha factor for delta frequency EMA bool m_tracking; TrackerType m_trackerType; @@ -58,6 +59,7 @@ struct FreqTrackerSettings FreqTrackerSettings(); void resetToDefaults(); void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + void setSpectrumGUI(Serializable *spectrumGUI) { m_spectrumGUI = spectrumGUI; } QByteArray serialize() const; bool deserialize(const QByteArray& data); }; diff --git a/plugins/channelrx/freqtracker/freqtrackersink.cpp b/plugins/channelrx/freqtracker/freqtrackersink.cpp index 390ae77ce..4cbb8e85b 100644 --- a/plugins/channelrx/freqtracker/freqtrackersink.cpp +++ b/plugins/channelrx/freqtracker/freqtrackersink.cpp @@ -22,6 +22,7 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "dsp/fftfilt.h" +#include "dsp/spectrumvis.h" #include "util/db.h" #include "util/stepfunctions.h" #include "util/messagequeue.h" @@ -33,6 +34,8 @@ FreqTrackerSink::FreqTrackerSink() : m_channelSampleRate(48000), m_inputFrequencyOffset(0), m_sinkSampleRate(48000), + m_spectrumSink(nullptr), + m_sampleBufferCount(0), m_squelchOpen(false), m_squelchGate(0), m_magsqSum(0.0f), @@ -52,6 +55,8 @@ FreqTrackerSink::FreqTrackerSink() : m_timer = &DSPEngine::instance()->getMasterTimer(); #endif m_magsq = 0.0; + m_sampleBufferSize = m_sinkSampleRate / 20; // 50 ms + m_sampleBuffer.resize(m_sampleBufferSize); 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 @@ -102,6 +107,7 @@ void FreqTrackerSink::processOneSample(Complex &ci) { fftfilt::cmplx *sideband; int n_out; + m_sampleBuffer[m_sampleBufferCount++] = Sample(ci.real(), ci.imag()); if (m_settings.m_rrc) { @@ -168,7 +174,15 @@ void FreqTrackerSink::processOneSample(Complex &ci) m_pll.feed(re, im); } } + } + + if (m_spectrumSink && (m_sampleBufferCount == m_sampleBufferSize)) + { + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); + m_sampleBufferCount = 0; + } + } Real FreqTrackerSink::getFrequency() const @@ -215,6 +229,10 @@ void FreqTrackerSink::applyChannelSettings(int sinkSampleRate, int channelSample if (useInterpolator) { setInterpolator(); } + + m_sampleBufferSize = m_sinkSampleRate / 20; // 50 ms + m_sampleBuffer.resize(m_sampleBufferSize); + m_sampleBufferCount = 0; } void FreqTrackerSink::applySettings(const FreqTrackerSettings& settings, bool force) diff --git a/plugins/channelrx/freqtracker/freqtrackersink.h b/plugins/channelrx/freqtracker/freqtrackersink.h index be732bcd4..3a9369f71 100644 --- a/plugins/channelrx/freqtracker/freqtrackersink.h +++ b/plugins/channelrx/freqtracker/freqtrackersink.h @@ -36,6 +36,7 @@ #include "freqtrackersettings.h" +class SpectrumVis; class fftfilt; class MessageQueue; class QTimer; @@ -48,6 +49,7 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; } 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;} @@ -99,6 +101,11 @@ private: int m_inputFrequencyOffset; uint32_t m_sinkSampleRate; + SpectrumVis* m_spectrumSink; + SampleVector m_sampleBuffer; + unsigned int m_sampleBufferCount; + unsigned int m_sampleBufferSize; + NCOF m_nco; PhaseLockComplex m_pll; FreqLockComplex m_fll; @@ -138,4 +145,4 @@ private slots: void tick(); }; -#endif // INCLUDE_FREQTRACKERSINK_H \ No newline at end of file +#endif // INCLUDE_FREQTRACKERSINK_H diff --git a/plugins/channelrx/freqtracker/readme.md b/plugins/channelrx/freqtracker/readme.md index 4f9b6a7e6..e72ae67b9 100644 --- a/plugins/channelrx/freqtracker/readme.md +++ b/plugins/channelrx/freqtracker/readme.md @@ -98,4 +98,8 @@ This is the squelch threshold in dB. The average total power received in the sig

10: Squelch time gate

-Number of milliseconds following squelch gate opening after which the signal is declared open. 0 means squelch is declared open with no delay and is suitable for burst signals. The value can be varied in steps of 10 ms from 0 to 990 ms. \ No newline at end of file +Number of milliseconds following squelch gate opening after which the signal is declared open. 0 means squelch is declared open with no delay and is suitable for burst signals. The value can be varied in steps of 10 ms from 0 to 990 ms. + +

11: Channel spectrum

+ +This is the spectrum display of the tracker channel. When the tracker is locked to the signal the center of the channel should fall almost in the middle of the signal spectrum (ideally in the middle when the tracker error is zero). Thus the locking can be followed dynamically and it can be more reliable than the lock indicator. A channel marker shows the tracker offset from the channel center frequency (tracker error). Its width is the tracker error tolerance but is hardly visible since it is 1/1000th of the channel width. Controls on the bottom of the panel are identical to the ones of the main spectrum display.