From c9f99907648054130c8ef3acb24ccecee362c132 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 22 Jun 2015 04:16:27 +0200 Subject: [PATCH] Added a scope widget in the channel analyzer plugin --- CMakeLists.txt | 3 + Readme.md | 1 + include-gpl/gui/glscopegui.h | 60 ++++ plugins/channel/chanalyzer/chanalyzer.cpp | 18 +- plugins/channel/chanalyzer/chanalyzer.h | 5 +- plugins/channel/chanalyzer/chanalyzergui.cpp | 27 +- plugins/channel/chanalyzer/chanalyzergui.h | 6 +- plugins/channel/chanalyzer/chanalyzergui.ui | 65 +++- sdrbase/gui/glscopegui.cpp | 198 ++++++++++++ sdrbase/gui/glscopegui.ui | 321 +++++++++++++++++++ 10 files changed, 680 insertions(+), 24 deletions(-) create mode 100644 include-gpl/gui/glscopegui.h create mode 100644 sdrbase/gui/glscopegui.cpp create mode 100644 sdrbase/gui/glscopegui.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e629fa2a..ec353f715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ set(sdrbase_SOURCES sdrbase/gui/buttonswitch.cpp sdrbase/gui/channelwindow.cpp sdrbase/gui/glscope.cpp + sdrbase/gui/glscopegui.cpp sdrbase/gui/glspectrum.cpp sdrbase/gui/glspectrumgui.cpp sdrbase/gui/indicator.cpp @@ -147,6 +148,7 @@ set(sdrbase_HEADERS include-gpl/gui/buttonswitch.h include-gpl/gui/channelwindow.h include-gpl/gui/glscope.h + include-gpl/gui/glscopegui.h include-gpl/gui/glspectrum.h include-gpl/gui/glspectrumgui.h include-gpl/gui/indicator.h @@ -190,6 +192,7 @@ set(sdrbase_FORMS sdrbase/gui/aboutdialog.ui sdrbase/gui/addpresetdialog.ui sdrbase/gui/basicchannelsettingswidget.ui + sdrbase/gui/glscopegui.ui sdrbase/gui/glspectrumgui.ui sdrbase/gui/pluginsdialog.ui sdrbase/gui/preferencesdialog.ui diff --git a/Readme.md b/Readme.md index 7ef20c1d7..94d2635c4 100644 --- a/Readme.md +++ b/Readme.md @@ -109,6 +109,7 @@ Done since the fork - Filter out CTCSS tones for audio and full CTCSS support in NFMDemod - Enhancement of the NFM squelch - Added a channel analyzer plugin focusing on measurement (DSA/DSO functionnality). Basic functions. + - Added a scope widget in the channel analyzer plugin ===== To Do diff --git a/include-gpl/gui/glscopegui.h b/include-gpl/gui/glscopegui.h new file mode 100644 index 000000000..b2a62e5b6 --- /dev/null +++ b/include-gpl/gui/glscopegui.h @@ -0,0 +1,60 @@ +#ifndef INCLUDE_GLSCOPEGUI_H +#define INCLUDE_GLSCOPEGUI_H + +#include +#include "dsp/dsptypes.h" +#include "util/export.h" +#include "util/message.h" + +namespace Ui { + class GLScopeGUI; +} + +class MessageQueue; +class ScopeVis; +class GLScope; + +class SDRANGELOVE_API GLScopeGUI : public QWidget { + Q_OBJECT + +public: + explicit GLScopeGUI(QWidget* parent = NULL); + ~GLScopeGUI(); + + void setBuddies(MessageQueue* messageQueue, ScopeVis* scopeVis, GLScope* glScope); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + bool handleMessage(Message* message); + +private: + Ui::GLScopeGUI* ui; + + MessageQueue* m_messageQueue; + ScopeVis* m_scopeVis; + GLScope* m_glScope; + + int m_sampleRate; + + qint32 m_displayData; + qint32 m_displayOrientation; + qint32 m_timeBase; + qint32 m_timeOffset; + qint32 m_amplification; + + void applySettings(); + +private slots: + void on_amp_valueChanged(int value); + void on_scope_traceSizeChanged(int value); + void on_time_valueChanged(int value); + void on_timeOfs_valueChanged(int value); + void on_displayMode_currentIndexChanged(int index); + + void on_horizView_clicked(); + void on_vertView_clicked(); +}; + +#endif // INCLUDE_GLSCOPEGUI_H diff --git a/plugins/channel/chanalyzer/chanalyzer.cpp b/plugins/channel/chanalyzer/chanalyzer.cpp index 4ee3bc271..2c6ede749 100644 --- a/plugins/channel/chanalyzer/chanalyzer.cpp +++ b/plugins/channel/chanalyzer/chanalyzer.cpp @@ -26,8 +26,9 @@ MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzer, Message) -ChannelAnalyzer::ChannelAnalyzer(SampleSink* sampleSink) : - m_sampleSink(sampleSink) +ChannelAnalyzer::ChannelAnalyzer(SampleSink* spectrumSink, SampleSink* scopeSink) : + m_spectrumSink(spectrumSink), + m_scopeSink(scopeSink) { m_Bandwidth = 5000; m_LowCutoff = 300; @@ -101,9 +102,14 @@ void ChannelAnalyzer::feed(SampleVector::const_iterator begin, SampleVector::con } } - if(m_sampleSink != NULL) + if(m_spectrumSink != NULL) { - m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only + } + + if(m_scopeSink != NULL) + { + m_scopeSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); // positive only is unused } m_sampleBuffer.clear(); @@ -160,8 +166,8 @@ bool ChannelAnalyzer::handleMessage(Message* cmd) cmd->completed(); return true; } else { - if(m_sampleSink != NULL) - return m_sampleSink->handleMessage(cmd); + if(m_spectrumSink != NULL) + return m_spectrumSink->handleMessage(cmd); else return false; } } diff --git a/plugins/channel/chanalyzer/chanalyzer.h b/plugins/channel/chanalyzer/chanalyzer.h index 59887fa48..46c619485 100644 --- a/plugins/channel/chanalyzer/chanalyzer.h +++ b/plugins/channel/chanalyzer/chanalyzer.h @@ -32,7 +32,7 @@ class ChannelAnalyzer : public SampleSink { public: - ChannelAnalyzer(SampleSink* sampleSink); + ChannelAnalyzer(SampleSink* spectrumSink, SampleSink* scopeSink); ~ChannelAnalyzer(); void configure(MessageQueue* messageQueue, @@ -100,7 +100,8 @@ private: fftfilt* SSBFilter; fftfilt* DSBFilter; - SampleSink* m_sampleSink; + SampleSink* m_spectrumSink; + SampleSink* m_scopeSink; SampleVector m_sampleBuffer; }; diff --git a/plugins/channel/chanalyzer/chanalyzergui.cpp b/plugins/channel/chanalyzer/chanalyzergui.cpp index 689609480..00677a86f 100644 --- a/plugins/channel/chanalyzer/chanalyzergui.cpp +++ b/plugins/channel/chanalyzer/chanalyzergui.cpp @@ -4,7 +4,9 @@ #include "dsp/threadedsamplesink.h" #include "dsp/channelizer.h" #include "dsp/spectrumvis.h" +#include "dsp/scopevis.h" #include "gui/glspectrum.h" +#include "gui/glscope.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "gui/basicchannelsettingswidget.h" @@ -245,13 +247,12 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent) : connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - //m_audioFifo = new AudioFifo(4, 48000 / 4); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_channelAnalyzer = new ChannelAnalyzer(m_spectrumVis); + m_scopeVis = new ScopeVis(ui->glScope); + m_channelAnalyzer = new ChannelAnalyzer(m_spectrumVis, m_scopeVis); m_channelizer = new Channelizer(m_channelAnalyzer); - m_threadedSampleSink = new ThreadedSampleSink(m_channelizer); - //m_pluginAPI->addAudioSource(m_audioFifo); - m_pluginAPI->addSampleSink(m_threadedSampleSink); + m_threadedSpectrumSampleSink = new ThreadedSampleSink(m_channelizer); + m_pluginAPI->addSampleSink(m_threadedSpectrumSampleSink); ui->glSpectrum->setCenterFrequency(m_rate/2); ui->glSpectrum->setSampleRate(m_rate); @@ -268,7 +269,8 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent) : connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); m_pluginAPI->addChannelMarker(m_channelMarker); - ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum); + ui->spectrumGUI->setBuddies(m_threadedSpectrumSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum); + ui->scopeGUI->setBuddies(m_threadedScopeSampleSink->getMessageQueue(), m_scopeVis, ui->glScope); applySettings(); } @@ -276,13 +278,14 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent) : ChannelAnalyzerGUI::~ChannelAnalyzerGUI() { m_pluginAPI->removeChannelInstance(this); - //m_pluginAPI->removeAudioSource(m_audioFifo); - m_pluginAPI->removeSampleSink(m_threadedSampleSink); - delete m_threadedSampleSink; + m_pluginAPI->removeSampleSink(m_threadedSpectrumSampleSink); + m_pluginAPI->removeSampleSink(m_threadedScopeSampleSink); + delete m_threadedSpectrumSampleSink; + delete m_threadedScopeSampleSink; delete m_channelizer; delete m_channelAnalyzer; delete m_spectrumVis; - //delete m_audioFifo; + delete m_scopeVis; delete m_channelMarker; delete ui; } @@ -348,10 +351,10 @@ void ChannelAnalyzerGUI::applySettings() setTitleColor(m_channelMarker->getColor()); ui->deltaFrequency->setValue(abs(m_channelMarker->getCenterFrequency())); ui->deltaMinus->setChecked(m_channelMarker->getCenterFrequency() < 0); - m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), + m_channelizer->configure(m_threadedSpectrumSampleSink->getMessageQueue(), 48000, m_channelMarker->getCenterFrequency()); - m_channelAnalyzer->configure(m_threadedSampleSink->getMessageQueue(), + m_channelAnalyzer->configure(m_threadedSpectrumSampleSink->getMessageQueue(), ui->BW->value() * 100.0, ui->lowCut->value() * 100.0, m_spanLog2, diff --git a/plugins/channel/chanalyzer/chanalyzergui.h b/plugins/channel/chanalyzer/chanalyzergui.h index 13ce581b5..1ba5d9c0c 100644 --- a/plugins/channel/chanalyzer/chanalyzergui.h +++ b/plugins/channel/chanalyzer/chanalyzergui.h @@ -12,6 +12,7 @@ class ThreadedSampleSink; class Channelizer; class ChannelAnalyzer; class SpectrumVis; +class ScopeVis; namespace Ui { class ChannelAnalyzerGUI; @@ -52,11 +53,12 @@ private: int m_rate; int m_spanLog2; - //AudioFifo* m_audioFifo; - ThreadedSampleSink* m_threadedSampleSink; + ThreadedSampleSink* m_threadedSpectrumSampleSink; + ThreadedSampleSink* m_threadedScopeSampleSink; Channelizer* m_channelizer; ChannelAnalyzer* m_channelAnalyzer; SpectrumVis* m_spectrumVis; + ScopeVis* m_scopeVis; explicit ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent = NULL); ~ChannelAnalyzerGUI(); diff --git a/plugins/channel/chanalyzer/chanalyzergui.ui b/plugins/channel/chanalyzer/chanalyzergui.ui index f199f60d9..ca1bbc7f9 100644 --- a/plugins/channel/chanalyzer/chanalyzergui.ui +++ b/plugins/channel/chanalyzer/chanalyzergui.ui @@ -7,7 +7,7 @@ 0 0 302 - 439 + 719 @@ -347,7 +347,7 @@ Channel Spectrum - + 2 @@ -384,6 +384,55 @@ + + + + 40 + 430 + 241 + 284 + + + + Channel Scope + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 200 + 250 + + + + + Courier + 8 + + + + + + + + + @@ -410,6 +459,18 @@
gui/valuedial.h
1
+ + GLScope + QWidget +
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
+ 1 +
diff --git a/sdrbase/gui/glscopegui.cpp b/sdrbase/gui/glscopegui.cpp new file mode 100644 index 000000000..d08739978 --- /dev/null +++ b/sdrbase/gui/glscopegui.cpp @@ -0,0 +1,198 @@ +#include "gui/glscopegui.h" +#include "dsp/scopevis.h" +#include "dsp/dspcommands.h" +#include "gui/glscope.h" +#include "util/simpleserializer.h" +#include "ui_glscopegui.h" + + +GLScopeGUI::GLScopeGUI(QWidget* parent) : + QWidget(parent), + ui(new Ui::GLScopeGUI), + m_messageQueue(NULL), + m_scopeVis(NULL), + m_glScope(NULL), + m_sampleRate(0), + m_displayData(GLScope::ModeIQ), + m_displayOrientation(Qt::Horizontal), + m_timeBase(1), + m_timeOffset(0), + m_amplification(0) +{ + ui->setupUi(this); +} + +GLScopeGUI::~GLScopeGUI() +{ + delete ui; +} + +void GLScopeGUI::setBuddies(MessageQueue* messageQueue, ScopeVis* scopeVis, GLScope* glScope) +{ + m_messageQueue = messageQueue; + m_scopeVis = scopeVis; + m_glScope = glScope; + applySettings(); +} + +void GLScopeGUI::resetToDefaults() +{ + m_displayData = GLScope::ModeIQ; + m_displayOrientation = Qt::Horizontal; + m_timeBase = 1; + m_timeOffset = 0; + m_amplification = 0; + applySettings(); +} + +QByteArray GLScopeGUI::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_displayData); + s.writeS32(2, m_displayOrientation); + s.writeS32(3, m_timeBase); + s.writeS32(4, m_timeOffset); + s.writeS32(5, m_amplification); + + return s.final(); +} + +bool GLScopeGUI::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) { + d.readS32(1, &m_displayData, GLScope::ModeIQ); + d.readS32(2, &m_displayOrientation, Qt::Horizontal); + d.readS32(3, &m_timeBase, 1); + d.readS32(4, &m_timeOffset, 0); + d.readS32(5, &m_amplification, 0); + if(m_timeBase < 0) + m_timeBase = 1; + applySettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +void GLScopeGUI::applySettings() +{ + ui->dataMode->setCurrentIndex(m_displayData); + if(m_displayOrientation == Qt::Horizontal) { + m_glScope->setOrientation(Qt::Horizontal); + ui->horizView->setChecked(true); + ui->vertView->setChecked(false); + } else { + m_glScope->setOrientation(Qt::Vertical); + ui->horizView->setChecked(false); + ui->vertView->setChecked(true); + } + ui->time->setValue(m_timeBase); + ui->timeOfs->setValue(m_timeOffset); + ui->amp->setValue(m_amplification); +} + +void GLScopeGUI::on_amp_valueChanged(int value) +{ + static qreal amps[11] = { 0.2, 0.1, 0.05, 0.02, 0.01, 0.005, 0.002, 0.001, 0.0005, 0.0002, 0.0001 }; + ui->ampText->setText(tr("%1\n/div").arg(amps[value], 0, 'f', 4)); + m_glScope->setAmp(0.2 / amps[value]); + m_amplification = value; +} + +void GLScopeGUI::on_scope_traceSizeChanged(int) +{ + qreal t = (m_glScope->getTraceSize() * 0.1 / m_sampleRate) / (qreal)m_timeBase; + if(t < 0.000001) + ui->timeText->setText(tr("%1\nns/div").arg(t * 1000000000.0)); + else if(t < 0.001) + ui->timeText->setText(tr("%1\nµs/div").arg(t * 1000000.0)); + else if(t < 1.0) + ui->timeText->setText(tr("%1\nms/div").arg(t * 1000.0)); + else ui->timeText->setText(tr("%1\ns/div").arg(t * 1.0)); +} + +void GLScopeGUI::on_time_valueChanged(int value) +{ + m_timeBase = value; + on_scope_traceSizeChanged(0); + m_glScope->setTimeBase(m_timeBase); +} + +void GLScopeGUI::on_timeOfs_valueChanged(int value) +{ + m_timeOffset = value; + m_glScope->setTimeOfsProMill(value); +} + +void GLScopeGUI::on_displayMode_currentIndexChanged(int index) +{ + m_displayData = index; + switch(index) { + case 0: // i+q + m_glScope->setMode(GLScope::ModeIQ); + break; + case 1: // mag(lin)+pha + m_glScope->setMode(GLScope::ModeMagLinPha); + break; + case 2: // mag(dB)+pha + m_glScope->setMode(GLScope::ModeMagdBPha); + break; + case 3: // derived1+derived2 + m_glScope->setMode(GLScope::ModeDerived12); + break; + case 4: // clostationary + m_glScope->setMode(GLScope::ModeCyclostationary); + break; + + default: + break; + } +} + +void GLScopeGUI::on_horizView_clicked() +{ + m_displayOrientation = Qt::Horizontal; + if(ui->horizView->isChecked()) { + ui->vertView->setChecked(false); + m_glScope->setOrientation(Qt::Horizontal); + } else { + ui->horizView->setChecked(true); + } +} + +void GLScopeGUI::on_vertView_clicked() +{ + m_displayOrientation = Qt::Vertical; + if(ui->vertView->isChecked()) { + ui->horizView->setChecked(false); + m_glScope->setOrientation(Qt::Vertical); + } else { + ui->vertView->setChecked(true); + m_glScope->setOrientation(Qt::Vertical); + } +} + +bool GLScopeGUI::handleMessage(Message* cmd) +{ + if(DSPSignalNotification::match(cmd)) + { + DSPSignalNotification* signal = (DSPSignalNotification*)cmd; + //fprintf(stderr, "%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset()); + m_sampleRate = signal->getSampleRate(); + cmd->completed(); + return true; + } + else + { + return false; + } +} diff --git a/sdrbase/gui/glscopegui.ui b/sdrbase/gui/glscopegui.ui new file mode 100644 index 000000000..fdb386ecc --- /dev/null +++ b/sdrbase/gui/glscopegui.ui @@ -0,0 +1,321 @@ + + + GLScopeGUI + + + + 0 + 0 + 798 + 40 + + + + Oscilloscope + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Data + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContentsOnFirstShow + + + + I+Q Level (linear) + + + + + Magnitude (linear) + Phase + + + + + Magnitude (dB) + Phase + + + + + Derived 1st + 2nd order + + + + + Cyclostationary + + + + + + + + Qt::Vertical + + + + + + + 0 + + + + + Horizontal display arrangement + + + + + + + :/horizontal.png:/horizontal.png + + + + 16 + 16 + + + + true + + + true + + + + + + + Vertical display arrangement + + + + + + + :/vertical.png:/vertical.png + + + + 16 + 16 + + + + true + + + + + + + + + Qt::Vertical + + + + + + + Time + + + + + + + + 0 + 0 + + + + 1 + + + 100 + + + 5 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 5 + + + + + + + + 40 + 0 + + + + 0.1000 +/div + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + 0 + + + 1000 + + + 10 + + + Qt::Horizontal + + + QSlider::NoTicks + + + 50 + + + + + + + 0 + + + + + + + Qt::Vertical + + + + + + + Amp + + + + + + + + 0 + 0 + + + + 0 + + + 8 + + + 1 + + + 0 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1 + + + + + + + + 40 + 0 + + + + 0.2000 +/div + + + Qt::AlignCenter + + + + + + + + + + + +