mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 08:04:49 -05:00
New scope: interim state (1)
This commit is contained in:
parent
6d4c000107
commit
f0f7838765
@ -127,7 +127,9 @@ set(sdrbase_SOURCES
|
||||
sdrbase/dsp/basebandsamplesource.cpp
|
||||
sdrbase/dsp/nullsink.cpp
|
||||
sdrbase/dsp/spectrumscopecombovis.cpp
|
||||
sdrbase/dsp/spectrumscopengcombovis.cpp
|
||||
sdrbase/dsp/scopevis.cpp
|
||||
sdrbase/dsp/scopevisng.cpp
|
||||
sdrbase/dsp/spectrumvis.cpp
|
||||
sdrbase/dsp/threadedbasebandsamplesink.cpp
|
||||
sdrbase/dsp/threadedbasebandsamplesource.cpp
|
||||
@ -141,6 +143,8 @@ set(sdrbase_SOURCES
|
||||
sdrbase/gui/cwkeyergui.cpp
|
||||
sdrbase/gui/glscope.cpp
|
||||
sdrbase/gui/glscopegui.cpp
|
||||
sdrbase/gui/glscopeng.cpp
|
||||
sdrbase/gui/glscopenggui.cpp
|
||||
sdrbase/gui/glshadersimple.cpp
|
||||
sdrbase/gui/glshadertextured.cpp
|
||||
sdrbase/gui/glspectrum.cpp
|
||||
@ -237,7 +241,10 @@ set(sdrbase_HEADERS
|
||||
sdrbase/dsp/basebandsamplesink.h
|
||||
sdrbase/dsp/basebandsamplesource.h
|
||||
sdrbase/dsp/nullsink.h
|
||||
sdrbase/dsp/spectrumscopecombovis.h
|
||||
sdrbase/dsp/spectrumscopengcombovis.h
|
||||
sdrbase/dsp/scopevis.h
|
||||
sdrbase/dsp/scopevisng.h
|
||||
sdrbase/dsp/spectrumvis.h
|
||||
sdrbase/dsp/threadedbasebandsamplesink.h
|
||||
sdrbase/dsp/threadedbasebandsamplesource.h
|
||||
@ -251,6 +258,8 @@ set(sdrbase_HEADERS
|
||||
sdrbase/gui/cwkeyergui.h
|
||||
sdrbase/gui/glscope.h
|
||||
sdrbase/gui/glscopegui.h
|
||||
sdrbase/gui/glscopeng.h
|
||||
sdrbase/gui/glscopenggui.h
|
||||
sdrbase/gui/glshadersimple.h
|
||||
sdrbase/gui/glshadertextured.h
|
||||
sdrbase/gui/glspectrum.h
|
||||
@ -282,6 +291,7 @@ set(sdrbase_HEADERS
|
||||
|
||||
sdrbase/util/CRC64.h
|
||||
sdrbase/util/db.h
|
||||
sdrbase/util/doublebuffer.h
|
||||
sdrbase/util/export.h
|
||||
sdrbase/util/message.h
|
||||
sdrbase/util/messagequeue.h
|
||||
@ -309,6 +319,7 @@ set(sdrbase_FORMS
|
||||
sdrbase/gui/basicchannelsettingswidget.ui
|
||||
sdrbase/gui/cwkeyergui.ui
|
||||
sdrbase/gui/glscopegui.ui
|
||||
sdrbase/gui/glscopenggui.ui
|
||||
sdrbase/gui/glspectrumgui.ui
|
||||
sdrbase/gui/pluginsdialog.ui
|
||||
sdrbase/gui/audiodialog.ui
|
||||
|
@ -2,6 +2,7 @@ project(demod)
|
||||
|
||||
add_subdirectory(demodlora)
|
||||
add_subdirectory(demodam)
|
||||
add_subdirectory(demodatv)
|
||||
add_subdirectory(demodbfm)
|
||||
add_subdirectory(demodnfm)
|
||||
add_subdirectory(demodssb)
|
||||
@ -9,6 +10,7 @@ add_subdirectory(tcpsrc)
|
||||
add_subdirectory(udpsrc)
|
||||
add_subdirectory(demodwfm)
|
||||
add_subdirectory(chanalyzer)
|
||||
add_subdirectory(chanalyzerng)
|
||||
|
||||
if(LIBDSDCC_FOUND AND LIBMBE_FOUND)
|
||||
add_subdirectory(demoddsd)
|
||||
|
45
plugins/channelrx/chanalyzerng/CMakeLists.txt
Normal file
45
plugins/channelrx/chanalyzerng/CMakeLists.txt
Normal file
@ -0,0 +1,45 @@
|
||||
project(chanalyzerng)
|
||||
|
||||
set(chanalyzerng_SOURCES
|
||||
chanalyzerng.cpp
|
||||
chanalyzernggui.cpp
|
||||
chanalyzerngplugin.cpp
|
||||
)
|
||||
|
||||
set(chanalyzerng_HEADERS
|
||||
chanalyzerng.h
|
||||
chanalyzernggui.h
|
||||
chanalyzerngplugin.h
|
||||
)
|
||||
|
||||
set(chanalyzerng_FORMS
|
||||
chanalyzernggui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt5_wrap_cpp(chanalyzer_HEADERS_MOC ${chanalyzer_HEADERS})
|
||||
qt5_wrap_ui(chanalyzerng_FORMS_HEADERS ${chanalyzerng_FORMS})
|
||||
|
||||
add_library(chanalyzerng SHARED
|
||||
${chanalyzerng_SOURCES}
|
||||
${chanalyzerng_HEADERS_MOC}
|
||||
${chanalyzerng_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(chanalyzerng
|
||||
${QT_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(chanalyzerng Core Widgets )
|
||||
|
||||
install(TARGETS chanalyzerng DESTINATION lib/plugins/channelrx)
|
206
plugins/channelrx/chanalyzerng/chanalyzerng.cpp
Normal file
206
plugins/channelrx/chanalyzerng/chanalyzerng.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "chanalyzerng.h"
|
||||
|
||||
#include <dsp/downchannelizer.h>
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
#include <stdio.h>
|
||||
#include "audio/audiooutput.h"
|
||||
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgConfigureChannelAnalyzer, Message)
|
||||
|
||||
ChannelAnalyzerNG::ChannelAnalyzerNG(BasebandSampleSink* sampleSink) :
|
||||
m_sampleSink(sampleSink),
|
||||
m_settingsMutex(QMutex::Recursive)
|
||||
{
|
||||
m_Bandwidth = 5000;
|
||||
m_LowCutoff = 300;
|
||||
m_spanLog2 = 3;
|
||||
m_sampleRate = 96000;
|
||||
m_frequency = 0;
|
||||
m_nco.setFreq(m_frequency, m_sampleRate);
|
||||
m_undersampleCount = 0;
|
||||
m_sum = 0;
|
||||
m_usb = true;
|
||||
m_ssb = true;
|
||||
m_magsq = 0;
|
||||
SSBFilter = new fftfilt(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate, ssbFftLen);
|
||||
DSBFilter = new fftfilt(m_Bandwidth / m_sampleRate, 2*ssbFftLen);
|
||||
}
|
||||
|
||||
ChannelAnalyzerNG::~ChannelAnalyzerNG()
|
||||
{
|
||||
if (SSBFilter) delete SSBFilter;
|
||||
if (DSBFilter) delete DSBFilter;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNG::configure(MessageQueue* messageQueue,
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb)
|
||||
{
|
||||
Message* cmd = MsgConfigureChannelAnalyzer::create(Bandwidth, LowCutoff, spanLog2, ssb);
|
||||
messageQueue->push(cmd);
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNG::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly)
|
||||
{
|
||||
fftfilt::cmplx *sideband;
|
||||
int n_out;
|
||||
int decim = 1<<m_spanLog2;
|
||||
unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
|
||||
|
||||
m_settingsMutex.lock();
|
||||
|
||||
for(SampleVector::const_iterator it = begin; it < end; ++it)
|
||||
{
|
||||
//Complex c(it->real() / 32768.0f, it->imag() / 32768.0f);
|
||||
Complex c(it->real(), it->imag());
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
if (m_ssb)
|
||||
{
|
||||
n_out = SSBFilter->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_mask))
|
||||
{
|
||||
m_sum /= decim;
|
||||
m_magsq = (m_sum.real() * m_sum.real() + m_sum.imag() * m_sum.imag())/ (1<<30);
|
||||
|
||||
if (m_ssb & !m_usb)
|
||||
{ // invert spectrum for LSB
|
||||
//m_sampleBuffer.push_back(Sample(m_sum.imag() * 32768.0, m_sum.real() * 32768.0));
|
||||
m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real()));
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_sampleBuffer.push_back(Sample(m_sum.real() * 32768.0, m_sum.imag() * 32768.0));
|
||||
m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag()));
|
||||
}
|
||||
|
||||
m_sum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_sampleSink != NULL)
|
||||
{
|
||||
m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only
|
||||
}
|
||||
|
||||
m_sampleBuffer.clear();
|
||||
|
||||
m_settingsMutex.unlock();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNG::start()
|
||||
{
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNG::stop()
|
||||
{
|
||||
}
|
||||
|
||||
bool ChannelAnalyzerNG::handleMessage(const Message& cmd)
|
||||
{
|
||||
float band, lowCutoff;
|
||||
|
||||
qDebug() << "ChannelAnalyzerNG::handleMessage";
|
||||
|
||||
if (DownChannelizer::MsgChannelizerNotification::match(cmd))
|
||||
{
|
||||
DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd;
|
||||
|
||||
m_sampleRate = notif.getSampleRate();
|
||||
m_nco.setFreq(-notif.getFrequencyOffset(), m_sampleRate);
|
||||
|
||||
qDebug() << "ChannelAnalyzerNG::handleMessage: MsgChannelizerNotification: m_sampleRate: " << m_sampleRate
|
||||
<< " frequencyOffset: " << notif.getFrequencyOffset();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureChannelAnalyzer::match(cmd))
|
||||
{
|
||||
MsgConfigureChannelAnalyzer& cfg = (MsgConfigureChannelAnalyzer&) cmd;
|
||||
|
||||
band = cfg.getBandwidth();
|
||||
lowCutoff = cfg.getLoCutoff();
|
||||
|
||||
if (band < 0)
|
||||
{
|
||||
band = -band;
|
||||
lowCutoff = -lowCutoff;
|
||||
m_usb = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_usb = true;
|
||||
}
|
||||
|
||||
if (band < 100.0f)
|
||||
{
|
||||
band = 100.0f;
|
||||
lowCutoff = 0;
|
||||
}
|
||||
|
||||
m_settingsMutex.lock();
|
||||
|
||||
m_Bandwidth = band;
|
||||
m_LowCutoff = lowCutoff;
|
||||
|
||||
SSBFilter->create_filter(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate);
|
||||
DSBFilter->create_dsb_filter(m_Bandwidth / m_sampleRate);
|
||||
|
||||
m_spanLog2 = cfg.getSpanLog2();
|
||||
m_ssb = cfg.getSSB();
|
||||
|
||||
m_settingsMutex.unlock();
|
||||
|
||||
qDebug() << " - MsgConfigureChannelAnalyzer: m_Bandwidth: " << m_Bandwidth
|
||||
<< " m_LowCutoff: " << m_LowCutoff
|
||||
<< " m_spanLog2: " << m_spanLog2
|
||||
<< " m_ssb: " << m_ssb;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_sampleSink != 0)
|
||||
{
|
||||
return m_sampleSink->handleMessage(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
105
plugins/channelrx/chanalyzerng/chanalyzerng.h
Normal file
105
plugins/channelrx/chanalyzerng/chanalyzerng.h
Normal file
@ -0,0 +1,105 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_CHANALYZERNG_H
|
||||
#define INCLUDE_CHANALYZERNG_H
|
||||
|
||||
#include <dsp/basebandsamplesink.h>
|
||||
#include <QMutex>
|
||||
#include <vector>
|
||||
#include "dsp/ncof.h"
|
||||
#include "dsp/fftfilt.h"
|
||||
#include "audio/audiofifo.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#define ssbFftLen 1024
|
||||
|
||||
class ChannelAnalyzerNG : public BasebandSampleSink {
|
||||
public:
|
||||
ChannelAnalyzerNG(BasebandSampleSink* m_sampleSink);
|
||||
virtual ~ChannelAnalyzerNG();
|
||||
|
||||
void configure(MessageQueue* messageQueue,
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb);
|
||||
|
||||
int getSampleRate() const { return m_sampleRate; }
|
||||
Real getMagSq() const { return m_magsq; }
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
class MsgConfigureChannelAnalyzer : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
Real getBandwidth() const { return m_Bandwidth; }
|
||||
Real getLoCutoff() const { return m_LowCutoff; }
|
||||
int getSpanLog2() const { return m_spanLog2; }
|
||||
bool getSSB() const { return m_ssb; }
|
||||
|
||||
static MsgConfigureChannelAnalyzer* create(Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb)
|
||||
{
|
||||
return new MsgConfigureChannelAnalyzer(Bandwidth, LowCutoff, spanLog2, ssb);
|
||||
}
|
||||
|
||||
private:
|
||||
Real m_Bandwidth;
|
||||
Real m_LowCutoff;
|
||||
int m_spanLog2;
|
||||
bool m_ssb;
|
||||
|
||||
MsgConfigureChannelAnalyzer(Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb) :
|
||||
Message(),
|
||||
m_Bandwidth(Bandwidth),
|
||||
m_LowCutoff(LowCutoff),
|
||||
m_spanLog2(spanLog2),
|
||||
m_ssb(ssb)
|
||||
{ }
|
||||
};
|
||||
|
||||
Real m_Bandwidth;
|
||||
Real m_LowCutoff;
|
||||
int m_spanLog2;
|
||||
int m_undersampleCount;
|
||||
fftfilt::cmplx m_sum;
|
||||
int m_sampleRate;
|
||||
int m_frequency;
|
||||
bool m_usb;
|
||||
bool m_ssb;
|
||||
Real m_magsq;
|
||||
|
||||
NCOF m_nco;
|
||||
fftfilt* SSBFilter;
|
||||
fftfilt* DSBFilter;
|
||||
|
||||
BasebandSampleSink* m_sampleSink;
|
||||
SampleVector m_sampleBuffer;
|
||||
QMutex m_settingsMutex;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_CHANALYZERNG_H
|
41
plugins/channelrx/chanalyzerng/chanalyzerng.pro
Normal file
41
plugins/channelrx/chanalyzerng/chanalyzerng.pro
Normal file
@ -0,0 +1,41 @@
|
||||
#--------------------------------------------------------
|
||||
#
|
||||
# Pro file for Android and Windows builds with Qt Creator
|
||||
#
|
||||
#--------------------------------------------------------
|
||||
|
||||
TEMPLATE = lib
|
||||
CONFIG += plugin
|
||||
|
||||
QT += core gui widgets multimedia opengl
|
||||
|
||||
TARGET = chanalyzerng
|
||||
|
||||
DEFINES += USE_SSE2=1
|
||||
QMAKE_CXXFLAGS += -msse2
|
||||
DEFINES += USE_SSE4_1=1
|
||||
QMAKE_CXXFLAGS += -msse4.1
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
INCLUDEPATH += ../../../sdrbase
|
||||
|
||||
CONFIG(ANDROID):INCLUDEPATH += /opt/softs/boost_1_60_0
|
||||
CONFIG(MINGW32):INCLUDEPATH += "D:\boost_1_58_0"
|
||||
CONFIG(MINGW64):INCLUDEPATH += "D:\boost_1_58_0"
|
||||
|
||||
CONFIG(Release):build_subdir = release
|
||||
CONFIG(Debug):build_subdir = debug
|
||||
|
||||
SOURCES += chanalyzerng.cpp\
|
||||
chanalyzernggui.cpp\
|
||||
chanalyzerngplugin.cpp
|
||||
|
||||
HEADERS += chanalyzerng.h\
|
||||
chanalyzernggui.h\
|
||||
chanalyzerngplugin.h
|
||||
|
||||
FORMS += chanalyzernggui.ui
|
||||
|
||||
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
|
||||
|
||||
RESOURCES = ../../../sdrbase/resources/res.qrc
|
485
plugins/channelrx/chanalyzerng/chanalyzernggui.cpp
Normal file
485
plugins/channelrx/chanalyzerng/chanalyzernggui.cpp
Normal file
@ -0,0 +1,485 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "chanalyzernggui.h"
|
||||
|
||||
#include <device/devicesourceapi.h>
|
||||
#include <dsp/downchannelizer.h>
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "dsp/threadedbasebandsamplesink.h"
|
||||
#include "ui_chanalyzernggui.h"
|
||||
#include "dsp/spectrumscopengcombovis.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "dsp/scopevis.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/glscopeng.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "util/db.h"
|
||||
#include "gui/basicchannelsettingswidget.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "chanalyzerng.h"
|
||||
|
||||
const QString ChannelAnalyzerNGGUI::m_channelID = "sdrangel.channel.chanalyzerng";
|
||||
|
||||
ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI)
|
||||
{
|
||||
ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceAPI);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
QString ChannelAnalyzerNGGUI::getName() const
|
||||
{
|
||||
return objectName();
|
||||
}
|
||||
|
||||
qint64 ChannelAnalyzerNGGUI::getCenterFrequency() const
|
||||
{
|
||||
return m_channelMarker.getCenterFrequency();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::setCenterFrequency(qint64 centerFrequency)
|
||||
{
|
||||
m_channelMarker.setCenterFrequency(centerFrequency);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::resetToDefaults()
|
||||
{
|
||||
blockApplySettings(true);
|
||||
|
||||
ui->BW->setValue(30);
|
||||
ui->deltaFrequency->setValue(0);
|
||||
ui->spanLog2->setValue(3);
|
||||
|
||||
blockApplySettings(false);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
QByteArray ChannelAnalyzerNGGUI::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeS32(1, m_channelMarker.getCenterFrequency());
|
||||
s.writeS32(2, ui->BW->value());
|
||||
s.writeBlob(3, ui->spectrumGUI->serialize());
|
||||
s.writeU32(4, m_channelMarker.getColor().rgb());
|
||||
s.writeS32(5, ui->lowCut->value());
|
||||
s.writeS32(6, ui->spanLog2->value());
|
||||
s.writeBool(7, ui->ssb->isChecked());
|
||||
s.writeBlob(8, ui->scopeGUI->serialize());
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool ChannelAnalyzerNGGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
quint32 u32tmp;
|
||||
qint32 tmp, bw, lowCut;
|
||||
bool tmpBool;
|
||||
|
||||
blockApplySettings(true);
|
||||
m_channelMarker.blockSignals(true);
|
||||
|
||||
d.readS32(1, &tmp, 0);
|
||||
m_channelMarker.setCenterFrequency(tmp);
|
||||
d.readS32(2, &bw, 30);
|
||||
ui->BW->setValue(bw);
|
||||
d.readBlob(3, &bytetmp);
|
||||
ui->spectrumGUI->deserialize(bytetmp);
|
||||
|
||||
if(d.readU32(4, &u32tmp))
|
||||
{
|
||||
m_channelMarker.setColor(u32tmp);
|
||||
}
|
||||
|
||||
d.readS32(5, &lowCut, 3);
|
||||
ui->lowCut->setValue(lowCut);
|
||||
d.readS32(6, &tmp, 20);
|
||||
ui->spanLog2->setValue(tmp);
|
||||
setNewRate(tmp);
|
||||
d.readBool(7, &tmpBool, false);
|
||||
ui->ssb->setChecked(tmpBool);
|
||||
d.readBlob(8, &bytetmp);
|
||||
ui->scopeGUI->deserialize(bytetmp);
|
||||
|
||||
blockApplySettings(false);
|
||||
m_channelMarker.blockSignals(false);
|
||||
|
||||
ui->BW->setValue(bw);
|
||||
ui->lowCut->setValue(lowCut); // does applySettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelAnalyzerNGGUI::handleMessage(const Message& message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::viewChanged()
|
||||
{
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::tick()
|
||||
{
|
||||
Real powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq());
|
||||
m_channelPowerDbAvg.feed(powDb);
|
||||
ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1));
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::channelSampleRateChanged()
|
||||
{
|
||||
setNewRate(m_spanLog2);
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_deltaMinus_toggled(bool minus)
|
||||
{
|
||||
int deltaFrequency = m_channelMarker.getCenterFrequency();
|
||||
bool minusDelta = (deltaFrequency < 0);
|
||||
|
||||
if (minus ^ minusDelta) // sign change
|
||||
{
|
||||
m_channelMarker.setCenterFrequency(-deltaFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_deltaFrequency_changed(quint64 value)
|
||||
{
|
||||
if (ui->deltaMinus->isChecked()) {
|
||||
m_channelMarker.setCenterFrequency(-value);
|
||||
} else {
|
||||
m_channelMarker.setCenterFrequency(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_BW_valueChanged(int value)
|
||||
{
|
||||
QString s = QString::number(value/10.0, 'f', 1);
|
||||
ui->BWText->setText(tr("%1k").arg(s));
|
||||
m_channelMarker.setBandwidth(value * 100 * 2);
|
||||
|
||||
if (ui->ssb->isChecked())
|
||||
{
|
||||
if (value < 0) {
|
||||
m_channelMarker.setSidebands(ChannelMarker::lsb);
|
||||
} else {
|
||||
m_channelMarker.setSidebands(ChannelMarker::usb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_channelMarker.setSidebands(ChannelMarker::dsb);
|
||||
}
|
||||
|
||||
on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100);
|
||||
}
|
||||
|
||||
int ChannelAnalyzerNGGUI::getEffectiveLowCutoff(int lowCutoff)
|
||||
{
|
||||
int ssbBW = m_channelMarker.getBandwidth() / 2;
|
||||
int effectiveLowCutoff = lowCutoff;
|
||||
const int guard = 100;
|
||||
|
||||
if (ssbBW < 0) {
|
||||
if (effectiveLowCutoff < ssbBW + guard) {
|
||||
effectiveLowCutoff = ssbBW + guard;
|
||||
}
|
||||
if (effectiveLowCutoff > 0) {
|
||||
effectiveLowCutoff = 0;
|
||||
}
|
||||
} else {
|
||||
if (effectiveLowCutoff > ssbBW - guard) {
|
||||
effectiveLowCutoff = ssbBW - guard;
|
||||
}
|
||||
if (effectiveLowCutoff < 0) {
|
||||
effectiveLowCutoff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveLowCutoff;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_lowCut_valueChanged(int value)
|
||||
{
|
||||
int lowCutoff = getEffectiveLowCutoff(value * 100);
|
||||
m_channelMarker.setLowCutoff(lowCutoff);
|
||||
QString s = QString::number(lowCutoff/1000.0, 'f', 1);
|
||||
ui->lowCutText->setText(tr("%1k").arg(s));
|
||||
ui->lowCut->setValue(lowCutoff/100);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_spanLog2_valueChanged(int value)
|
||||
{
|
||||
if (setNewRate(value)) {
|
||||
applySettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_ssb_toggled(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
if (ui->BW->value() < 0) {
|
||||
m_channelMarker.setSidebands(ChannelMarker::lsb);
|
||||
} else {
|
||||
m_channelMarker.setSidebands(ChannelMarker::usb);
|
||||
}
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(m_rate/4);
|
||||
ui->glSpectrum->setSampleRate(m_rate/2);
|
||||
ui->glSpectrum->setSsbSpectrum(true);
|
||||
|
||||
on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_channelMarker.setSidebands(ChannelMarker::dsb);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(0);
|
||||
ui->glSpectrum->setSampleRate(m_rate);
|
||||
ui->glSpectrum->setSsbSpectrum(false);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
/*
|
||||
if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL))
|
||||
m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown);
|
||||
*/
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::onMenuDoubleClicked()
|
||||
{
|
||||
if(!m_basicSettingsShown) {
|
||||
m_basicSettingsShown = true;
|
||||
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this);
|
||||
bcsw->show();
|
||||
}
|
||||
}
|
||||
|
||||
ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) :
|
||||
RollupWidget(parent),
|
||||
ui(new Ui::ChannelAnalyzerNGGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_channelMarker(this),
|
||||
m_basicSettingsShown(false),
|
||||
m_doApplySettings(true),
|
||||
m_rate(6000),
|
||||
m_spanLog2(3),
|
||||
m_channelPowerDbAvg(40,0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
|
||||
|
||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||
m_scopeVis = new ScopeVisNG(ui->glScope);
|
||||
m_spectrumScopeComboVis = new SpectrumScopeNGComboVis(m_spectrumVis, m_scopeVis);
|
||||
m_channelAnalyzer = new ChannelAnalyzerNG(m_spectrumScopeComboVis);
|
||||
m_channelizer = new DownChannelizer(m_channelAnalyzer);
|
||||
m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
|
||||
connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelSampleRateChanged()));
|
||||
m_deviceAPI->addThreadedSink(m_threadedChannelizer);
|
||||
|
||||
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
|
||||
ui->deltaFrequency->setValueRange(7, 0U, 9999999U);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(m_rate/2);
|
||||
ui->glSpectrum->setSampleRate(m_rate);
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
ui->glSpectrum->setDisplayMaxHold(true);
|
||||
ui->glSpectrum->setSsbSpectrum(true);
|
||||
|
||||
ui->glSpectrum->connectTimer(m_pluginAPI->getMainWindow()->getMasterTimer());
|
||||
ui->glScope->connectTimer(m_pluginAPI->getMainWindow()->getMasterTimer());
|
||||
connect(&m_pluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
|
||||
|
||||
//m_channelMarker = new ChannelMarker(this);
|
||||
m_channelMarker.setColor(Qt::gray);
|
||||
m_channelMarker.setBandwidth(m_rate);
|
||||
m_channelMarker.setSidebands(ChannelMarker::usb);
|
||||
m_channelMarker.setCenterFrequency(0);
|
||||
m_channelMarker.setVisible(true);
|
||||
|
||||
connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
|
||||
|
||||
m_deviceAPI->registerChannelInstance(m_channelID, this);
|
||||
m_deviceAPI->addChannelMarker(&m_channelMarker);
|
||||
m_deviceAPI->addRollupWidget(this);
|
||||
|
||||
ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
||||
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
|
||||
|
||||
applySettings();
|
||||
setNewRate(m_spanLog2);
|
||||
}
|
||||
|
||||
ChannelAnalyzerNGGUI::~ChannelAnalyzerNGGUI()
|
||||
{
|
||||
m_deviceAPI->removeChannelInstance(this);
|
||||
m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
|
||||
delete m_threadedChannelizer;
|
||||
delete m_channelizer;
|
||||
delete m_channelAnalyzer;
|
||||
delete m_spectrumVis;
|
||||
delete m_scopeVis;
|
||||
delete m_spectrumScopeComboVis;
|
||||
//delete m_channelMarker;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool ChannelAnalyzerNGGUI::setNewRate(int spanLog2)
|
||||
{
|
||||
qDebug("ChannelAnalyzerNGGUI::setNewRate");
|
||||
|
||||
if ((spanLog2 < 0) || (spanLog2 > 6)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_spanLog2 = spanLog2;
|
||||
//m_rate = 48000 / (1<<spanLog2);
|
||||
m_rate = m_channelAnalyzer->getSampleRate() / (1<<spanLog2);
|
||||
|
||||
if (ui->BW->value() < -m_rate/200) {
|
||||
ui->BW->setValue(-m_rate/200);
|
||||
m_channelMarker.setBandwidth(-m_rate*2);
|
||||
} else if (ui->BW->value() > m_rate/200) {
|
||||
ui->BW->setValue(m_rate/200);
|
||||
m_channelMarker.setBandwidth(m_rate*2);
|
||||
}
|
||||
|
||||
if (ui->lowCut->value() < -m_rate/200) {
|
||||
ui->lowCut->setValue(-m_rate/200);
|
||||
m_channelMarker.setLowCutoff(-m_rate);
|
||||
} else if (ui->lowCut->value() > m_rate/200) {
|
||||
ui->lowCut->setValue(m_rate/200);
|
||||
m_channelMarker.setLowCutoff(m_rate);
|
||||
}
|
||||
|
||||
ui->BW->setMinimum(-m_rate/200);
|
||||
ui->lowCut->setMinimum(-m_rate/200);
|
||||
ui->BW->setMaximum(m_rate/200);
|
||||
ui->lowCut->setMaximum(m_rate/200);
|
||||
|
||||
QString s = QString::number(m_rate/1000.0, 'f', 1);
|
||||
ui->spanText->setText(tr("%1k").arg(s));
|
||||
|
||||
if (ui->ssb->isChecked())
|
||||
{
|
||||
if (ui->BW->value() < 0) {
|
||||
m_channelMarker.setSidebands(ChannelMarker::lsb);
|
||||
} else {
|
||||
m_channelMarker.setSidebands(ChannelMarker::usb);
|
||||
}
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(m_rate/4);
|
||||
ui->glSpectrum->setSampleRate(m_rate/2);
|
||||
ui->glSpectrum->setSsbSpectrum(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_channelMarker.setSidebands(ChannelMarker::dsb);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(0);
|
||||
ui->glSpectrum->setSampleRate(m_rate);
|
||||
ui->glSpectrum->setSsbSpectrum(false);
|
||||
}
|
||||
|
||||
ui->glScope->setSampleRate(m_rate);
|
||||
//m_scopeVis->setSampleRate(m_rate); TODO: not needed anymore?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::blockApplySettings(bool block)
|
||||
{
|
||||
ui->glScope->blockSignals(block);
|
||||
ui->glSpectrum->blockSignals(block);
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::applySettings()
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
setTitleColor(m_channelMarker.getColor());
|
||||
ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency()));
|
||||
ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0);
|
||||
|
||||
m_channelizer->configure(m_channelizer->getInputMessageQueue(),
|
||||
m_channelizer->getInputSampleRate(),
|
||||
m_channelMarker.getCenterFrequency());
|
||||
|
||||
m_channelAnalyzer->configure(m_channelAnalyzer->getInputMessageQueue(),
|
||||
ui->BW->value() * 100.0,
|
||||
ui->lowCut->value() * 100.0,
|
||||
m_spanLog2,
|
||||
ui->ssb->isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::leaveEvent(QEvent*)
|
||||
{
|
||||
blockApplySettings(true);
|
||||
m_channelMarker.setHighlighted(false);
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::enterEvent(QEvent*)
|
||||
{
|
||||
blockApplySettings(true);
|
||||
m_channelMarker.setHighlighted(true);
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
103
plugins/channelrx/chanalyzerng/chanalyzernggui.h
Normal file
103
plugins/channelrx/chanalyzerng/chanalyzernggui.h
Normal file
@ -0,0 +1,103 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_CHANNELANALYZERNGGUI_H
|
||||
#define INCLUDE_CHANNELANALYZERNGGUI_H
|
||||
|
||||
#include "gui/rollupwidget.h"
|
||||
#include "plugin/plugingui.h"
|
||||
#include "dsp/channelmarker.h"
|
||||
#include "dsp/movingaverage.h"
|
||||
|
||||
class PluginAPI;
|
||||
class DeviceSourceAPI;
|
||||
|
||||
class ThreadedBasebandSampleSink;
|
||||
class DownChannelizer;
|
||||
class ChannelAnalyzerNG;
|
||||
class SpectrumScopeNGComboVis;
|
||||
class SpectrumVis;
|
||||
class ScopeVisNG;
|
||||
|
||||
namespace Ui {
|
||||
class ChannelAnalyzerNGGUI;
|
||||
}
|
||||
|
||||
class ChannelAnalyzerNGGUI : public RollupWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ChannelAnalyzerNGGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI);
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
QString getName() const;
|
||||
virtual qint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
static const QString m_channelID;
|
||||
|
||||
private slots:
|
||||
void viewChanged();
|
||||
void channelSampleRateChanged();
|
||||
void on_deltaFrequency_changed(quint64 value);
|
||||
void on_deltaMinus_toggled(bool minus);
|
||||
void on_BW_valueChanged(int value);
|
||||
void on_lowCut_valueChanged(int value);
|
||||
void on_spanLog2_valueChanged(int value);
|
||||
void on_ssb_toggled(bool checked);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDoubleClicked();
|
||||
void tick();
|
||||
|
||||
private:
|
||||
Ui::ChannelAnalyzerNGGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
DeviceSourceAPI* m_deviceAPI;
|
||||
ChannelMarker m_channelMarker;
|
||||
bool m_basicSettingsShown;
|
||||
bool m_doApplySettings;
|
||||
int m_rate;
|
||||
int m_spanLog2;
|
||||
MovingAverage<Real> m_channelPowerDbAvg;
|
||||
|
||||
ThreadedBasebandSampleSink* m_threadedChannelizer;
|
||||
DownChannelizer* m_channelizer;
|
||||
ChannelAnalyzerNG* m_channelAnalyzer;
|
||||
SpectrumScopeNGComboVis* m_spectrumScopeComboVis;
|
||||
SpectrumVis* m_spectrumVis;
|
||||
ScopeVisNG* m_scopeVis;
|
||||
|
||||
explicit ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL);
|
||||
virtual ~ChannelAnalyzerNGGUI();
|
||||
|
||||
int getEffectiveLowCutoff(int lowCutoff);
|
||||
bool setNewRate(int spanLog2);
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings();
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_CHANNELANALYZERNGGUI_H
|
540
plugins/channelrx/chanalyzerng/chanalyzernggui.ui
Normal file
540
plugins/channelrx/chanalyzerng/chanalyzernggui.ui
Normal file
@ -0,0 +1,540 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ChannelAnalyzerNGGUI</class>
|
||||
<widget class="RollupWidget" name="ChannelAnalyzerNGGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>640</width>
|
||||
<height>814</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>640</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Sans Serif</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Channel Analyzer NG</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>10</y>
|
||||
<width>301</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="DeltaFreqPowLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="DeltaFrequencyLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="deltaMinus">
|
||||
<property name="toolTip">
|
||||
<string>Frequency shift direction</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<selectedoff>:/plus.png</selectedoff>
|
||||
<selectedon>:/minus.png</selectedon>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="deltaFrequency" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>SizeVerCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Demod shift frequency from center in Hz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="deltaUnits">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>26</red>
|
||||
<green>26</green>
|
||||
<blue>26</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="BrightText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>26</red>
|
||||
<green>26</green>
|
||||
<blue>26</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="BrightText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>118</red>
|
||||
<green>118</green>
|
||||
<blue>117</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="BrightText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Hz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ChannelPowerLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPower">
|
||||
<property name="toolTip">
|
||||
<string>Channel power</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPowerUnits">
|
||||
<property name="text">
|
||||
<string> dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="SpanLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="spanLabel">
|
||||
<property name="text">
|
||||
<string>Rate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="spanLog2">
|
||||
<property name="toolTip">
|
||||
<string>Channel sample rate</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="sliderPosition">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="spanText">
|
||||
<property name="text">
|
||||
<string>6.0k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ssb">
|
||||
<property name="toolTip">
|
||||
<string>SSB/DSB togggle</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SSB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="BWLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="BWLabel">
|
||||
<property name="text">
|
||||
<string>BW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="BW">
|
||||
<property name="toolTip">
|
||||
<string>Lowpass filter cutoff frequency</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-60</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>60</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="BWText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3.0k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="LowCutLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lowCutLabel">
|
||||
<property name="text">
|
||||
<string>Low cut.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="lowCut">
|
||||
<property name="toolTip">
|
||||
<string>Highpass filter cutoff frequency (SSB)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-60</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>60</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lowCutText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.3k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>180</y>
|
||||
<width>636</width>
|
||||
<height>284</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>636</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Channel Spectrum</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutSpectrum">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GLSpectrum" name="glSpectrum" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>250</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="scopeContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>470</y>
|
||||
<width>636</width>
|
||||
<height>284</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>636</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Channel Scope</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutScope">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GLScopeNG" name="glScope" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>250</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLScopeNGGUI" name="scopeGUI" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RollupWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLSpectrum</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrum.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLSpectrumGUI</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrumgui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLScopeNG</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glscopeng.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLScopeNGGUI</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glscopenggui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
64
plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp
Normal file
64
plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "chanalyzerngplugin.h"
|
||||
#include "chanalyzernggui.h"
|
||||
|
||||
const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = {
|
||||
QString("Channel Analyzer NG"),
|
||||
QString("3.2.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
ChannelAnalyzerNGPlugin::ChannelAnalyzerNGPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& ChannelAnalyzerNGPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
// register demodulator
|
||||
m_pluginAPI->registerRxChannel(ChannelAnalyzerNGGUI::m_channelID, this);
|
||||
}
|
||||
|
||||
PluginGUI* ChannelAnalyzerNGPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI)
|
||||
{
|
||||
if(channelName == ChannelAnalyzerNGGUI::m_channelID)
|
||||
{
|
||||
ChannelAnalyzerNGGUI* gui = ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceAPI);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGPlugin::createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI)
|
||||
{
|
||||
ChannelAnalyzerNGGUI* gui = ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceAPI);
|
||||
}
|
48
plugins/channelrx/chanalyzerng/chanalyzerngplugin.h
Normal file
48
plugins/channelrx/chanalyzerng/chanalyzerngplugin.h
Normal file
@ -0,0 +1,48 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_CHANALYZERNGPLUGIN_H
|
||||
#define INCLUDE_CHANALYZERNGPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class DeviceSourceAPI;
|
||||
|
||||
class ChannelAnalyzerNGPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.channel.chanalyzerng")
|
||||
|
||||
public:
|
||||
explicit ChannelAnalyzerNGPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
PluginGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
|
||||
private slots:
|
||||
void createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_CHANALYZERNGPLUGIN_H
|
299
sdrbase/dsp/scopevisng.cpp
Normal file
299
sdrbase/dsp/scopevisng.cpp
Normal file
@ -0,0 +1,299 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
#include "scopevisng.h"
|
||||
#include "gui/glscopeng.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ScopeVisNG::MsgConfigureScopeVisNG, Message)
|
||||
MESSAGE_CLASS_DEFINITION(ScopeVisNG::MsgScopeVisNGAddTrigger, Message)
|
||||
MESSAGE_CLASS_DEFINITION(ScopeVisNG::MsgScopeVisNGRemoveTrigger, Message)
|
||||
|
||||
const uint ScopeVisNG::m_traceChunkSize = 4800;
|
||||
const Real ScopeVisNG::ProjectorMagDB::mult = (10.0f / log2f(10.0f));
|
||||
|
||||
|
||||
ScopeVisNG::ScopeVisNG(GLScopeNG* glScope) :
|
||||
m_glScope(glScope),
|
||||
m_preTriggerDelay(0),
|
||||
m_currentTriggerIndex(0),
|
||||
m_triggerState(TriggerUntriggered),
|
||||
m_traceSize(m_traceChunkSize),
|
||||
m_traceStart(true),
|
||||
m_traceFill(0),
|
||||
m_zTraceIndex(-1),
|
||||
m_traceCompleteCount(0)
|
||||
{
|
||||
setObjectName("ScopeVisNG");
|
||||
m_tracebackBuffers.resize(1);
|
||||
m_tracebackBuffers[0].resize(4*m_traceChunkSize);
|
||||
}
|
||||
|
||||
ScopeVisNG::~ScopeVisNG()
|
||||
{
|
||||
std::vector<TriggerCondition>::iterator it = m_triggerConditions.begin();
|
||||
|
||||
for (; it != m_triggerConditions.end(); ++it)
|
||||
{
|
||||
delete it->m_projector;
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeVisNG::configure(MessageQueue* msgQueue,
|
||||
uint traceSize)
|
||||
{
|
||||
Message* cmd = MsgConfigureScopeVisNG::create(traceSize);
|
||||
msgQueue->push(cmd);
|
||||
}
|
||||
|
||||
|
||||
void ScopeVisNG::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly)
|
||||
{
|
||||
uint32_t feedIndex = 0; // TODO: redefine feed interface so it can be passed a feed index
|
||||
|
||||
if (m_triggerState == TriggerFreeRun) {
|
||||
m_triggerPoint = cbegin;
|
||||
}
|
||||
else if (m_triggerState == TriggerTriggered) {
|
||||
m_triggerPoint = cbegin;
|
||||
}
|
||||
else if (m_triggerState == TriggerUntriggered) {
|
||||
m_triggerPoint = end;
|
||||
}
|
||||
else if (m_triggerState == TriggerWait) {
|
||||
m_triggerPoint = end;
|
||||
}
|
||||
else {
|
||||
m_triggerPoint = cbegin;
|
||||
}
|
||||
|
||||
if (m_triggerState == TriggerNewConfig)
|
||||
{
|
||||
m_triggerState = TriggerUntriggered;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_triggerConditions.size() > 0) && (m_triggerState == TriggerWait)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_tracebackBuffers[feedIndex].write(cbegin, end);
|
||||
SampleVector::const_iterator begin(cbegin);
|
||||
TriggerCondition& triggerCondition = m_triggerConditions[m_currentTriggerIndex];
|
||||
|
||||
// trigger process
|
||||
if ((m_triggerConditions.size() > 0) && (feedIndex == triggerCondition.m_inputIndex))
|
||||
{
|
||||
while (begin < end)
|
||||
{
|
||||
if (m_triggerState == TriggerUntriggered)
|
||||
{
|
||||
bool condition = triggerCondition.m_projector->run(*begin) > triggerCondition.m_triggerLevel;
|
||||
bool trigger;
|
||||
|
||||
if (triggerCondition.m_triggerBothEdges) {
|
||||
trigger = triggerCondition.m_prevCondition ^ condition;
|
||||
} else {
|
||||
trigger = condition ^ !triggerCondition.m_triggerPositiveEdge;
|
||||
}
|
||||
|
||||
if (trigger)
|
||||
{
|
||||
if (triggerCondition.m_triggerDelay > 0)
|
||||
{
|
||||
triggerCondition.m_triggerDelayCount = triggerCondition.m_triggerDelay;
|
||||
m_triggerState == TriggerDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (triggerCondition.m_triggerCounts > 0)
|
||||
{
|
||||
triggerCondition.m_triggerCounts--;
|
||||
m_triggerState = TriggerUntriggered;
|
||||
}
|
||||
else
|
||||
{
|
||||
// next trigger
|
||||
m_currentTriggerIndex++;
|
||||
|
||||
if (m_currentTriggerIndex == m_triggerConditions.size())
|
||||
{
|
||||
m_currentTriggerIndex = 0;
|
||||
m_triggerState = TriggerTriggered;
|
||||
m_triggerPoint = begin;
|
||||
m_traceStart = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_triggerState = TriggerUntriggered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_triggerState == TriggerDelay)
|
||||
{
|
||||
if (triggerCondition.m_triggerDelayCount > 0)
|
||||
{
|
||||
triggerCondition.m_triggerDelayCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerCondition.m_triggerDelayCount = 0;
|
||||
|
||||
// next trigger
|
||||
m_currentTriggerIndex++;
|
||||
|
||||
if (m_currentTriggerIndex == m_triggerConditions.size())
|
||||
{
|
||||
m_currentTriggerIndex = 0;
|
||||
m_triggerState = TriggerTriggered;
|
||||
m_triggerPoint = begin;
|
||||
m_traceStart = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize a new trace
|
||||
m_triggerState = TriggerUntriggered;
|
||||
m_traceCompleteCount = 0;
|
||||
m_triggerState = TriggerUntriggered;
|
||||
|
||||
feed(begin, end, positiveOnly); // process the rest of samples
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
++begin;
|
||||
} // begin < end
|
||||
}
|
||||
|
||||
// trace process
|
||||
if ((m_triggerConditions.size() == 0) || (m_triggerState == TriggerTriggered))
|
||||
{
|
||||
// trace back
|
||||
|
||||
if (m_traceStart)
|
||||
{
|
||||
int count = begin - cbegin; // number of samples consumed since begin
|
||||
std::vector<Trace>::iterator itTrace = m_traces.begin();
|
||||
|
||||
for (;itTrace != m_traces.end(); ++itTrace)
|
||||
{
|
||||
if (itTrace->m_inputIndex == feedIndex)
|
||||
{
|
||||
SampleVector::const_iterator startPoint = m_tracebackBuffers[feedIndex].getCurrent() - count;
|
||||
SampleVector::const_iterator prevPoint = m_tracebackBuffers[feedIndex].getCurrent() - count - m_preTriggerDelay - itTrace->m_traceDelay;
|
||||
processPrevTrace(prevPoint, startPoint, itTrace);
|
||||
}
|
||||
}
|
||||
|
||||
m_traceStart = false;
|
||||
}
|
||||
|
||||
// live trace
|
||||
|
||||
int shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
|
||||
|
||||
while (begin < end)
|
||||
{
|
||||
for (std::vector<Trace>::iterator itTrace = m_traces.begin(); itTrace != m_traces.end(); ++itTrace)
|
||||
{
|
||||
if (itTrace->m_inputIndex == feedIndex)
|
||||
{
|
||||
float posLimit = 1.0 / itTrace->m_amp;
|
||||
float negLimit = -1.0 / itTrace->m_amp;
|
||||
|
||||
if (itTrace->m_traceCount < m_traceSize)
|
||||
{
|
||||
float v = itTrace->m_projector->run(*begin) * itTrace->m_amp + itTrace->m_shift;
|
||||
|
||||
if(v > posLimit) {
|
||||
v = posLimit;
|
||||
} else if (v < negLimit) {
|
||||
v = negLimit;
|
||||
}
|
||||
|
||||
itTrace->m_trace[2*(itTrace->m_traceCount)] = itTrace->m_traceCount - shift;
|
||||
itTrace->m_trace[2*(itTrace->m_traceCount)+1] = v;
|
||||
|
||||
itTrace->m_traceCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itTrace->m_traceCount = 0;
|
||||
|
||||
if (m_traceCompleteCount < m_traces.size())
|
||||
{
|
||||
m_traceCompleteCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_glScope->newTraces((DisplayTraces&) m_traces); // TODO: glScopeNG new traces
|
||||
m_traceCompleteCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeVisNG::processPrevTrace(SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, std::vector<Trace>::iterator& trace)
|
||||
{
|
||||
int shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
|
||||
float posLimit = 1.0 / trace->m_amp;
|
||||
float negLimit = -1.0 / trace->m_amp;
|
||||
|
||||
while (begin < end)
|
||||
{
|
||||
float v = trace->m_projector->run(*begin) * trace->m_amp + trace->m_shift;
|
||||
|
||||
if(v > posLimit) {
|
||||
v = posLimit;
|
||||
} else if (v < negLimit) {
|
||||
v = negLimit;
|
||||
}
|
||||
|
||||
trace->m_trace[2*(trace->m_traceCount)] = (trace->m_traceCount - shift); // display x
|
||||
trace->m_trace[2*(trace->m_traceCount) + 1] = v; // display y
|
||||
|
||||
trace->m_traceCount++;
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
|
||||
void ScopeVisNG::start()
|
||||
{
|
||||
}
|
||||
|
||||
void ScopeVisNG::stop()
|
||||
{
|
||||
}
|
||||
|
||||
bool ScopeVisNG::handleMessage(const Message& message)
|
||||
{
|
||||
qDebug() << "ScopeVisNG::handleMessage" << message.getIdentifier();
|
||||
}
|
||||
|
285
sdrbase/dsp/scopevisng.h
Normal file
285
sdrbase/dsp/scopevisng.h
Normal file
@ -0,0 +1,285 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SDRBASE_DSP_SCOPEVISNG_H_
|
||||
#define SDRBASE_DSP_SCOPEVISNG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "util/export.h"
|
||||
#include "util/message.h"
|
||||
#include "util/doublebuffer.h"
|
||||
|
||||
class GLScopeNG;
|
||||
|
||||
class SDRANGEL_API ScopeVisNG : public BasebandSampleSink {
|
||||
|
||||
public:
|
||||
enum ProjectionType
|
||||
{
|
||||
ProjectionReal, //!< Extract real part
|
||||
ProjectionImag, //!< Extract imaginary part
|
||||
ProjectionMagLin, //!< Calculate linear magnitude or modulus
|
||||
ProjectionMagDB, //!< Calculate logarithmic (dB) of squared magnitude
|
||||
ProjectionPhase, //!< Calculate phase
|
||||
ProjectionDPhase //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate
|
||||
};
|
||||
|
||||
struct DisplayTrace
|
||||
{
|
||||
float *m_trace; //!< Displayable trace (interleaved x,y of GLfloat)
|
||||
ProjectionType m_projectionType; //!< Complex to real projection type
|
||||
float m_amp; //!< Amplification factor
|
||||
float m_ofs; //!< Offset factor
|
||||
};
|
||||
|
||||
typedef std::vector<DisplayTrace> DisplayTraces;
|
||||
|
||||
static const uint m_traceChunkSize;
|
||||
static const uint m_nbTriggers = 10;
|
||||
|
||||
ScopeVisNG(GLScopeNG* glScope = 0);
|
||||
virtual ~ScopeVisNG();
|
||||
|
||||
void configure(MessageQueue* msgQueue,
|
||||
uint32_t traceSize);
|
||||
|
||||
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& message);
|
||||
SampleVector::const_iterator getTriggerPoint() const { return m_triggerPoint; }
|
||||
|
||||
private:
|
||||
typedef DoubleBufferSimple<Sample> TraceBuffer;
|
||||
|
||||
class MsgConfigureScopeVisNG : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgConfigureScopeVisNG* create(
|
||||
uint32_t traceSize)
|
||||
{
|
||||
return new MsgConfigureScopeVisNG(traceSize);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_traceSize;
|
||||
|
||||
MsgConfigureScopeVisNG(uint32_t traceSize) :
|
||||
m_traceSize(traceSize)
|
||||
{}
|
||||
};
|
||||
|
||||
class MsgScopeVisNGAddTrigger : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgScopeVisNGAddTrigger* create(
|
||||
ProjectionType projectionType)
|
||||
{
|
||||
return new MsgScopeVisNGAddTrigger(projectionType);
|
||||
}
|
||||
|
||||
private:
|
||||
ProjectionType m_projectionType;
|
||||
|
||||
MsgScopeVisNGAddTrigger(ProjectionType projectionType) :
|
||||
m_projectionType(projectionType)
|
||||
{}
|
||||
};
|
||||
|
||||
class MsgScopeVisNGRemoveTrigger : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgScopeVisNGRemoveTrigger* create(
|
||||
uint32_t triggerIndex)
|
||||
{
|
||||
return new MsgScopeVisNGRemoveTrigger(triggerIndex);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_triggerIndex;
|
||||
|
||||
MsgScopeVisNGRemoveTrigger(uint32_t triggerIndex) :
|
||||
m_triggerIndex(triggerIndex)
|
||||
{}
|
||||
};
|
||||
|
||||
class Projector
|
||||
{
|
||||
public:
|
||||
Projector(ProjectionType projectionType) : m_projectionType(projectionType) {}
|
||||
|
||||
ProjectionType getProjectionType() const { return m_projectionType; }
|
||||
virtual Real run(const Sample& s) = 0;
|
||||
private:
|
||||
ProjectionType m_projectionType;
|
||||
};
|
||||
|
||||
class ProjectorReal : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorReal() : Projector(ProjectionReal) {}
|
||||
virtual Real run(const Sample& s) { return s.m_real / 32768.0f; }
|
||||
};
|
||||
|
||||
class ProjectorImag : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorImag() : Projector(ProjectionImag) {}
|
||||
virtual Real run(const Sample& s) { return s.m_imag / 32768.0f; }
|
||||
};
|
||||
|
||||
class ProjectorMagLin : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorMagLin() : Projector(ProjectionMagLin) {}
|
||||
virtual Real run(const Sample& s)
|
||||
{
|
||||
uint32_t magsq = s.m_real*s.m_real + s.m_imag*s.m_imag;
|
||||
return std::sqrt(magsq/1073741824.0f);
|
||||
}
|
||||
};
|
||||
|
||||
class ProjectorMagDB : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorMagDB() : Projector(ProjectionMagDB) {}
|
||||
virtual Real run(const Sample& s)
|
||||
{
|
||||
uint32_t magsq = s.m_real*s.m_real + s.m_imag*s.m_imag;
|
||||
return mult * log2f(magsq/1073741824.0f);
|
||||
}
|
||||
private:
|
||||
static const Real mult;
|
||||
};
|
||||
|
||||
class ProjectorPhase : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorPhase() : Projector(ProjectionPhase) {}
|
||||
virtual Real run(const Sample& s) { return std::atan2((float) s.m_imag, (float) s.m_real) / M_PI; }
|
||||
};
|
||||
|
||||
class ProjectorDPhase : public Projector
|
||||
{
|
||||
public:
|
||||
ProjectorDPhase() : Projector(ProjectionDPhase), m_prevArg(0.0f) {}
|
||||
virtual Real run(const Sample& s)
|
||||
{
|
||||
Real curArg = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI;
|
||||
Real dPhi = curArg - m_prevArg;
|
||||
m_prevArg = curArg;
|
||||
|
||||
if (dPhi < -M_PI) {
|
||||
dPhi += 2.0 * M_PI;
|
||||
} else if (dPhi > M_PI) {
|
||||
dPhi -= 2.0 * M_PI;
|
||||
}
|
||||
|
||||
return dPhi;
|
||||
}
|
||||
|
||||
private:
|
||||
Real m_prevArg;
|
||||
};
|
||||
|
||||
enum TriggerState
|
||||
{
|
||||
TriggerFreeRun, //!< Trigger is disabled
|
||||
TriggerUntriggered, //!< Trigger is not kicked off yet (or trigger list is empty)
|
||||
TriggerTriggered, //!< Trigger has been kicked off
|
||||
TriggerWait, //!< In one shot mode trigger waits for manual re-enabling
|
||||
TriggerDelay, //!< Trigger conditions have been kicked off but it is waiting for delay before final kick off
|
||||
TriggerNewConfig, //!< Special condition when a new configuration has been received
|
||||
};
|
||||
|
||||
struct TriggerCondition
|
||||
{
|
||||
public:
|
||||
Projector *m_projector; //!< Projector transform from complex trace to reaL trace usable for triggering
|
||||
uint32_t m_inputIndex; //!< Input or feed index this trigger is associated with
|
||||
Real m_triggerLevel; //!< Level in real units
|
||||
bool m_triggerPositiveEdge; //!< Trigger on the positive edge (else negative)
|
||||
bool m_triggerBothEdges; //!< Trigger on both edges (else only one)
|
||||
bool m_prevCondition; //!< Condition (above threshold) at previous sample
|
||||
uint32_t m_triggerDelay; //!< Delay before the trigger is kicked off in number of samples
|
||||
uint32_t m_triggerDelayCount; //!< Counter of samples for delay
|
||||
uint32_t m_triggerCounts; //!< Number of trigger conditions before the final decisive trigger
|
||||
|
||||
TriggerCondition(Projector *projector) :
|
||||
m_projector(projector),
|
||||
m_inputIndex(0),
|
||||
m_triggerLevel(0.0f),
|
||||
m_triggerPositiveEdge(true),
|
||||
m_triggerBothEdges(false),
|
||||
m_prevCondition(false),
|
||||
m_triggerDelay(0),
|
||||
m_triggerDelayCount(0),
|
||||
m_triggerCounts(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct Trace : public DisplayTrace
|
||||
{
|
||||
Projector *m_projector; //!< Projector transform from complex trace to real (displayable) trace
|
||||
uint32_t m_inputIndex; //!< Input or feed index this trace is associated with
|
||||
int m_traceDelay; //!< Trace delay in number of samples
|
||||
int m_traceCount; //!< Count of samples processed
|
||||
float m_amp; //!< Linear trace amplifier factor
|
||||
float m_shift; //!< Linear trace shift
|
||||
|
||||
Trace(Projector *projector, Real *displayTraceBuffer) :
|
||||
m_projector(projector),
|
||||
m_inputIndex(0),
|
||||
m_traceDelay(0),
|
||||
m_traceCount(0),
|
||||
m_amp(1.0f),
|
||||
m_shift(0.0f)
|
||||
{
|
||||
m_projectionType = m_projector->getProjectionType();
|
||||
m_trace = displayTraceBuffer;
|
||||
}
|
||||
};
|
||||
|
||||
GLScopeNG* m_glScope;
|
||||
std::vector<TraceBuffer> m_tracebackBuffers; //!< One complex (Sample type) trace buffer per input source or feed
|
||||
DoubleBufferSimple<Sample> m_traceback; //!< FIFO to handle delayed processes
|
||||
int m_preTriggerDelay; //!< Pre-trigger delay in number of samples
|
||||
std::vector<TriggerCondition> m_triggerConditions; //!< Chain of triggers
|
||||
int m_currentTriggerIndex; //!< Index of current index in the chain
|
||||
TriggerState m_triggerState; //!< Current trigger state
|
||||
std::vector<Trace> m_traces; //!< One trace control object per display trace allocated to X, Y[n] or Z
|
||||
int m_traceSize; //!< Size of traces in number of samples
|
||||
int m_timeOfsProMill; //!< Start trace shift in 1/1000 trace size
|
||||
bool m_traceStart; //!< Trace is at start point
|
||||
int m_traceFill; //!< Count of samples accumulated into trace
|
||||
int m_zTraceIndex; //!< Index of the trace used for Z input (luminance or false colors)
|
||||
int m_traceCompleteCount; //!< Count of completed traces
|
||||
SampleVector::const_iterator m_triggerPoint; //!< Trigger start location in the samples vector
|
||||
|
||||
void processPrevTrace(SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, std::vector<Trace>::iterator& trace);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDRBASE_DSP_SCOPEVISNG_H_ */
|
41
sdrbase/dsp/spectrumscopengcombovis.cpp
Normal file
41
sdrbase/dsp/spectrumscopengcombovis.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "dsp/spectrumscopengcombovis.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
SpectrumScopeNGComboVis::SpectrumScopeNGComboVis(SpectrumVis* spectrumVis, ScopeVisNG* scopeVis) :
|
||||
m_spectrumVis(spectrumVis),
|
||||
m_scopeVis(scopeVis)
|
||||
{
|
||||
setObjectName("SpectrumScopeNGComboVis");
|
||||
}
|
||||
|
||||
SpectrumScopeNGComboVis::~SpectrumScopeNGComboVis()
|
||||
{
|
||||
}
|
||||
|
||||
void SpectrumScopeNGComboVis::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly)
|
||||
{
|
||||
m_scopeVis->feed(begin, end, false);
|
||||
SampleVector::const_iterator triggerPoint = m_scopeVis->getTriggerPoint();
|
||||
m_spectrumVis->feedTriggered(triggerPoint, begin, end, positiveOnly);
|
||||
}
|
||||
|
||||
void SpectrumScopeNGComboVis::start()
|
||||
{
|
||||
m_spectrumVis->start();
|
||||
m_scopeVis->start();
|
||||
}
|
||||
|
||||
void SpectrumScopeNGComboVis::stop()
|
||||
{
|
||||
m_spectrumVis->stop();
|
||||
m_scopeVis->stop();
|
||||
}
|
||||
|
||||
bool SpectrumScopeNGComboVis::handleMessage(const Message& message)
|
||||
{
|
||||
bool spectDone = m_spectrumVis->handleMessage(message);
|
||||
bool scopeDone = m_scopeVis->handleMessage(message);
|
||||
|
||||
return (spectDone || scopeDone);
|
||||
}
|
27
sdrbase/dsp/spectrumscopengcombovis.h
Normal file
27
sdrbase/dsp/spectrumscopengcombovis.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef INCLUDE_SPECTRUMSCOPENGCOMBOVIS_H
|
||||
#define INCLUDE_SPECTRUMSCOPENGCOMBOVIS_H
|
||||
|
||||
#include <dsp/basebandsamplesink.h>
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "dsp/scopevisng.h"
|
||||
#include "util/export.h"
|
||||
|
||||
class Message;
|
||||
|
||||
class SDRANGEL_API SpectrumScopeNGComboVis : public BasebandSampleSink {
|
||||
public:
|
||||
|
||||
SpectrumScopeNGComboVis(SpectrumVis* spectrumVis, ScopeVisNG* scopeVis);
|
||||
virtual ~SpectrumScopeNGComboVis();
|
||||
|
||||
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& message);
|
||||
|
||||
private:
|
||||
SpectrumVis* m_spectrumVis;
|
||||
ScopeVisNG* m_scopeVis;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SPECTRUMSCOPENGCOMBOVIS_H
|
996
sdrbase/gui/glscopeng.cpp
Normal file
996
sdrbase/gui/glscopeng.cpp
Normal file
@ -0,0 +1,996 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QSurface>
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
|
||||
#include "glscopeng.h"
|
||||
|
||||
GLScopeNG::GLScopeNG(QWidget* parent) :
|
||||
QGLWidget(parent),
|
||||
m_displayMode(DisplayX),
|
||||
m_dataChanged(false),
|
||||
m_configChanged(false),
|
||||
m_traces(0),
|
||||
m_displayGridIntensity(10),
|
||||
m_displayTraceIntensity(50),
|
||||
m_timeBase(1),
|
||||
m_traceSize(0),
|
||||
m_sampleRate(0),
|
||||
m_triggerPre(0),
|
||||
m_timeOfsProMill(0),
|
||||
m_highlightedTraceIndex(0)
|
||||
{
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
|
||||
m_timer.start(50);
|
||||
|
||||
m_y1Scale.setFont(font());
|
||||
m_y1Scale.setOrientation(Qt::Vertical);
|
||||
m_y2Scale.setFont(font());
|
||||
m_y2Scale.setOrientation(Qt::Vertical);
|
||||
m_x1Scale.setFont(font());
|
||||
m_x1Scale.setOrientation(Qt::Horizontal);
|
||||
m_x2Scale.setFont(font());
|
||||
m_x2Scale.setOrientation(Qt::Horizontal);
|
||||
|
||||
m_powerOverlayFont.setBold(true);
|
||||
m_powerOverlayFont.setPointSize(font().pointSize()+1);
|
||||
}
|
||||
|
||||
GLScopeNG::~GLScopeNG()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void GLScopeNG::newTraces()
|
||||
{
|
||||
if (m_traces)
|
||||
{
|
||||
if(!m_mutex.tryLock(2))
|
||||
return;
|
||||
|
||||
m_dataChanged = true;
|
||||
|
||||
m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void GLScopeNG::initializeGL()
|
||||
{
|
||||
QOpenGLContext *glCurrentContext = QOpenGLContext::currentContext();
|
||||
|
||||
if (glCurrentContext) {
|
||||
if (QOpenGLContext::currentContext()->isValid()) {
|
||||
qDebug() << "GLScopeNG::initializeGL: context:"
|
||||
<< " major: " << (QOpenGLContext::currentContext()->format()).majorVersion()
|
||||
<< " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion()
|
||||
<< " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no");
|
||||
}
|
||||
else {
|
||||
qDebug() << "GLScopeNG::initializeGL: current context is invalid";
|
||||
}
|
||||
} else {
|
||||
qCritical() << "GLScopeNG::initializeGL: no current context";
|
||||
return;
|
||||
}
|
||||
|
||||
QSurface *surface = glCurrentContext->surface();
|
||||
|
||||
if (surface == 0)
|
||||
{
|
||||
qCritical() << "GLScopeNG::initializeGL: no surface attached";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (surface->surfaceType() != QSurface::OpenGLSurface)
|
||||
{
|
||||
qCritical() << "GLScopeNG::initializeGL: surface is not an OpenGLSurface: " << surface->surfaceType()
|
||||
<< " cannot use an OpenGL context";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "GLScopeNG::initializeGL: OpenGL surface:"
|
||||
<< " class: " << (surface->surfaceClass() == QSurface::Window ? "Window" : "Offscreen");
|
||||
}
|
||||
}
|
||||
|
||||
connect(glCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this, &GLScopeNG::cleanup); // TODO: when migrating to QOpenGLWidget
|
||||
|
||||
QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
|
||||
glFunctions->initializeOpenGLFunctions();
|
||||
|
||||
//glDisable(GL_DEPTH_TEST);
|
||||
m_glShaderSimple.initializeGL();
|
||||
m_glShaderLeft1Scale.initializeGL();
|
||||
m_glShaderBottom1Scale.initializeGL();
|
||||
m_glShaderLeft2Scale.initializeGL();
|
||||
m_glShaderBottom2Scale.initializeGL();
|
||||
m_glShaderPowerOverlay.initializeGL();
|
||||
}
|
||||
|
||||
void GLScopeNG::resizeGL(int width, int height)
|
||||
{
|
||||
QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
|
||||
glFunctions->glViewport(0, 0, width, height);
|
||||
m_configChanged = true;
|
||||
}
|
||||
|
||||
void GLScopeNG::paintGL()
|
||||
{
|
||||
if(!m_mutex.tryLock(2))
|
||||
return;
|
||||
|
||||
if(m_configChanged)
|
||||
applyConfig();
|
||||
|
||||
QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
|
||||
glFunctions->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glFunctions->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (m_displayMode == DisplayX) // display only trace #0
|
||||
{
|
||||
// draw rect around
|
||||
{
|
||||
GLfloat q3[] {
|
||||
1, 1,
|
||||
0, 1,
|
||||
0, 0,
|
||||
1, 0
|
||||
};
|
||||
|
||||
QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
m_glShaderSimple.drawContour(m_glScopeMatrix1, color, q3, 4);
|
||||
}
|
||||
|
||||
// paint grid
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
// Y1 (X trace or trace #0)
|
||||
{
|
||||
tickList = &m_y1Scale.getTickList();
|
||||
|
||||
GLfloat q3[4*tickList->count()];
|
||||
int effectiveTicks = 0;
|
||||
|
||||
for (int i= 0; i < tickList->count(); i++)
|
||||
{
|
||||
tick = &(*tickList)[i];
|
||||
|
||||
if (tick->major)
|
||||
{
|
||||
if (tick->textSize > 0)
|
||||
{
|
||||
float y = 1 - (tick->pos / m_y1Scale.getSize());
|
||||
q3[4*effectiveTicks] = 0;
|
||||
q3[4*effectiveTicks+1] = y;
|
||||
q3[4*effectiveTicks+2] = 1;
|
||||
q3[4*effectiveTicks+3] = y;
|
||||
effectiveTicks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float blue = 1.0f;
|
||||
QVector4D color(1.0f, 1.0f, blue, (float) m_displayGridIntensity / 100.0f);
|
||||
m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
|
||||
}
|
||||
|
||||
// X1 (time)
|
||||
{
|
||||
tickList = &m_x1Scale.getTickList();
|
||||
|
||||
GLfloat q3[4*tickList->count()];
|
||||
int effectiveTicks = 0;
|
||||
for(int i= 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
float x = tick->pos / m_x1Scale.getSize();
|
||||
q3[4*effectiveTicks] = x;
|
||||
q3[4*effectiveTicks+1] = 0;
|
||||
q3[4*effectiveTicks+2] = x;
|
||||
q3[4*effectiveTicks+3] = 1;
|
||||
effectiveTicks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
|
||||
m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
|
||||
}
|
||||
|
||||
// paint left #1 scale
|
||||
{
|
||||
GLfloat vtx1[] = {
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0,
|
||||
0, 0
|
||||
};
|
||||
GLfloat tex1[] = {
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0,
|
||||
0, 0
|
||||
};
|
||||
m_glShaderLeft1Scale.drawSurface(m_glLeft1ScaleMatrix, tex1, vtx1, 4);
|
||||
}
|
||||
|
||||
// paint bottom #1 scale
|
||||
{
|
||||
GLfloat vtx1[] = {
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0,
|
||||
0, 0
|
||||
};
|
||||
GLfloat tex1[] = {
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0,
|
||||
0, 0
|
||||
};
|
||||
m_glShaderBottom1Scale.drawSurface(m_glBot1ScaleMatrix, tex1, vtx1, 4);
|
||||
}
|
||||
|
||||
// TODO: paint trigger level #1
|
||||
|
||||
// paint trace #1
|
||||
if (m_traceSize > 0)
|
||||
{
|
||||
const ScopeVisNG::DisplayTrace& trace = (*m_traces)[0];
|
||||
int start = (m_timeOfsProMill/1000.0) * m_traceSize;
|
||||
int end = std::min(start + m_traceSize/m_timeBase, m_traceSize);
|
||||
if(end - start < 2)
|
||||
start--;
|
||||
|
||||
float rectX = m_glScopeRect1.x();
|
||||
float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f;
|
||||
float rectW = m_glScopeRect1.width() * (float)m_timeBase / (float)(m_traceSize - 1);
|
||||
float rectH = -(m_glScopeRect1.height() / 2.0f) * trace.m_amp;
|
||||
|
||||
QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f);
|
||||
QMatrix4x4 mat;
|
||||
mat.setToIdentity();
|
||||
mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
|
||||
mat.scale(2.0f * rectW, -2.0f * rectH);
|
||||
m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace.m_trace[2*start], end - start);
|
||||
}
|
||||
}
|
||||
|
||||
m_dataChanged = false;
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void GLScopeNG::setSampleRate(int sampleRate)
|
||||
{
|
||||
m_sampleRate = sampleRate;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setTimeBase(int timeBase)
|
||||
{
|
||||
m_timeBase = timeBase;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setTriggerPre(Real triggerPre)
|
||||
{
|
||||
m_triggerPre = triggerPre;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setTimeOfsProMill(int timeOfsProMill)
|
||||
{
|
||||
m_timeOfsProMill = timeOfsProMill;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setHighlightedTraceIndex(uint32_t traceIndex)
|
||||
{
|
||||
m_highlightedTraceIndex = traceIndex;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setDisplayMode(DisplayMode displayMode)
|
||||
{
|
||||
m_displayMode = displayMode;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::setTraceSize(int traceSize)
|
||||
{
|
||||
m_traceSize = traceSize;
|
||||
m_configChanged = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::applyConfig()
|
||||
{
|
||||
m_configChanged = false;
|
||||
|
||||
QFontMetrics fm(font());
|
||||
int M = fm.width("-");
|
||||
float t_start = ((m_timeOfsProMill / 1000.0) - m_triggerPre) * ((float) m_traceSize / m_sampleRate);
|
||||
float t_len = ((float) m_traceSize / m_sampleRate) / (float) m_timeBase;
|
||||
|
||||
m_x1Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
|
||||
m_x2Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
|
||||
|
||||
if (m_traces)
|
||||
{
|
||||
if (m_traces->size() > 0)
|
||||
{
|
||||
setYScale(m_y1Scale, 0); // This is always the X trace (trace #0)
|
||||
}
|
||||
|
||||
if ((m_traces->size() > 1) && (m_highlightedTraceIndex < m_traces->size()))
|
||||
{
|
||||
setYScale(m_y2Scale, m_highlightedTraceIndex > 0 ? m_highlightedTraceIndex : 1); // if Highlighted trace is #0 (X trace) set it to first Y trace (trace #1)
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_displayMode == DisplayX) || (m_displayMode == DisplayY)) // unique display
|
||||
{
|
||||
int scopeHeight = height() - m_topMargin - m_botMargin;
|
||||
int scopeWidth = width() - m_leftMargin - m_rightMargin;
|
||||
|
||||
m_glScopeRect1 = QRectF(
|
||||
(float) m_leftMargin / (float) width(),
|
||||
(float) m_topMargin / (float) height(),
|
||||
(float) scopeWidth / (float) width(),
|
||||
(float) scopeHeight / (float) height()
|
||||
);
|
||||
m_glScopeMatrix1.setToIdentity();
|
||||
m_glScopeMatrix1.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glScopeMatrix1.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot1ScaleMatrix.setToIdentity();
|
||||
m_glBot1ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot1ScaleMatrix.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft1ScaleMatrix.setToIdentity();
|
||||
m_glLeft1ScaleMatrix.translate (
|
||||
-1.0f,
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glLeft1ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
{ // X1 scale
|
||||
m_x1Scale.setSize(scopeWidth);
|
||||
|
||||
m_bot1ScalePixmap = QPixmap(
|
||||
scopeWidth,
|
||||
m_botMargin - 1
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_bot1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_bot1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_x1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
|
||||
|
||||
} // X1 scale
|
||||
|
||||
if (m_displayMode == DisplayX) // use Y1 scale
|
||||
{
|
||||
m_y1Scale.setSize(scopeHeight);
|
||||
|
||||
m_left1ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
|
||||
}
|
||||
else if (m_displayMode == DisplayY) // use Y2 scale
|
||||
{
|
||||
m_y2Scale.setSize(scopeHeight);
|
||||
|
||||
m_left2ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left2ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left2ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y2Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
|
||||
} // Y scales
|
||||
} // single display
|
||||
else // dual display (X+Y or polar)
|
||||
{
|
||||
// left (first) display
|
||||
if ((m_displayMode == DisplayXYH) || (m_displayMode == DisplayPol)) // horizontal split of first display
|
||||
{
|
||||
int scopeHeight = height() - m_topMargin - m_botMargin;
|
||||
int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin;
|
||||
|
||||
m_glScopeRect1 = QRectF(
|
||||
(float) m_leftMargin / (float) width(),
|
||||
(float) m_topMargin / (float) height(),
|
||||
(float) scopeWidth / (float) width(),
|
||||
(float) scopeHeight / (float) height()
|
||||
);
|
||||
m_glScopeMatrix1.setToIdentity();
|
||||
m_glScopeMatrix1.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glScopeMatrix1.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot1ScaleMatrix.setToIdentity();
|
||||
m_glBot1ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot1ScaleMatrix.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft1ScaleMatrix.setToIdentity();
|
||||
m_glLeft1ScaleMatrix.translate (
|
||||
-1.0f,
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glLeft1ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
{ // Y1 scale
|
||||
m_y1Scale.setSize(scopeHeight);
|
||||
|
||||
m_left1ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
|
||||
|
||||
} // Y1 scale
|
||||
{ // X1 scale
|
||||
m_x1Scale.setSize(scopeWidth);
|
||||
|
||||
m_bot1ScalePixmap = QPixmap(
|
||||
scopeWidth,
|
||||
m_botMargin - 1
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_bot1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_bot1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_x1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
|
||||
|
||||
} // X1 scale
|
||||
}
|
||||
else // vertical split of first display
|
||||
{
|
||||
int scopeHeight = (height() - m_topMargin) / 2 - m_botMargin;
|
||||
int scopeWidth = width() - m_leftMargin - m_rightMargin;
|
||||
|
||||
m_glScopeRect1 = QRectF(
|
||||
(float) m_leftMargin / (float) width(),
|
||||
(float) m_topMargin / (float) height(),
|
||||
(float) scopeWidth / (float) width(),
|
||||
(float) scopeHeight / (float) height()
|
||||
);
|
||||
m_glScopeMatrix1.setToIdentity();
|
||||
m_glScopeMatrix1.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glScopeMatrix1.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot1ScaleMatrix.setToIdentity();
|
||||
m_glBot1ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot1ScaleMatrix.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft1ScaleMatrix.setToIdentity();
|
||||
m_glLeft1ScaleMatrix.translate (
|
||||
-1.0f,
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glLeft1ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
{ // Y1 scale
|
||||
m_y1Scale.setSize(scopeHeight);
|
||||
|
||||
m_left1ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
|
||||
|
||||
} // Y1 scale
|
||||
{ // X1 scale
|
||||
m_x1Scale.setSize(scopeWidth);
|
||||
|
||||
m_bot1ScalePixmap = QPixmap(
|
||||
scopeWidth,
|
||||
m_botMargin - 1
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_bot1ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_bot1ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_x1Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
|
||||
|
||||
} // X1 scale
|
||||
} // hotizontal or vertical split of first display
|
||||
// right (second) display
|
||||
if (m_displayMode == DisplayPol) // in Polar mode second display is square and split is horizontal
|
||||
{
|
||||
int scopeHeight = height() - m_topMargin - m_botMargin;
|
||||
int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin;
|
||||
|
||||
int scopeDim = std::min(scopeWidth, scopeHeight);
|
||||
|
||||
m_glScopeRect2 = QRectF(
|
||||
(float)(m_leftMargin + scopeWidth + m_leftMargin) / (float)width(),
|
||||
(float)m_topMargin / (float)height(),
|
||||
(float) scopeDim / (float)width(),
|
||||
(float)(height() - m_topMargin - m_botMargin) / (float)height()
|
||||
);
|
||||
m_glScopeMatrix2.setToIdentity();
|
||||
m_glScopeMatrix2.translate (
|
||||
-1.0f + ((float) 2*(m_leftMargin + scopeWidth + m_leftMargin) / (float) width()),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glScopeMatrix2.scale (
|
||||
(float) 2*scopeDim / (float) width(),
|
||||
(float) -2*(height() - m_topMargin - m_botMargin) / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft2ScaleMatrix.setToIdentity();
|
||||
m_glLeft2ScaleMatrix.translate (
|
||||
-1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glLeft2ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot2ScaleMatrix.setToIdentity();
|
||||
m_glBot2ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot2ScaleMatrix.scale (
|
||||
(float) 2*scopeDim / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
}
|
||||
else // both displays are similar and share space equally
|
||||
{
|
||||
if (m_displayMode == DisplayXYH) // horizontal split of second display
|
||||
{
|
||||
int scopeHeight = height() - m_topMargin - m_botMargin;
|
||||
int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin;
|
||||
|
||||
m_glScopeRect2 = QRectF(
|
||||
(float)(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float)width(),
|
||||
(float)m_topMargin / (float)height(),
|
||||
(float)((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float)width(),
|
||||
(float)(height() - m_topMargin - m_botMargin) / (float)height()
|
||||
);
|
||||
m_glScopeMatrix2.setToIdentity();
|
||||
m_glScopeMatrix2.translate (
|
||||
-1.0f + ((float) 2*(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float) width()),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glScopeMatrix2.scale (
|
||||
(float) 2*((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float) width(),
|
||||
(float) -2*(height() - m_topMargin - m_botMargin) / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft2ScaleMatrix.setToIdentity();
|
||||
m_glLeft2ScaleMatrix.translate (
|
||||
-1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(),
|
||||
1.0f - ((float) 2*m_topMargin / (float) height())
|
||||
);
|
||||
m_glLeft2ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot2ScaleMatrix.setToIdentity();
|
||||
m_glBot2ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot2ScaleMatrix.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
|
||||
{ // Y2 scale
|
||||
m_y2Scale.setSize(scopeHeight);
|
||||
|
||||
m_left2ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left2ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left2ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y2Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
|
||||
|
||||
} // Y2 scale
|
||||
{ // X2 scale
|
||||
m_x2Scale.setSize(scopeWidth);
|
||||
m_bot2ScalePixmap = QPixmap(
|
||||
scopeWidth,
|
||||
m_botMargin - 1
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_bot2ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_bot2ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_x2Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
|
||||
|
||||
} // X2 scale
|
||||
}
|
||||
else // vertical split of second display
|
||||
{
|
||||
int scopeHeight = (height() - m_topMargin) / 2 - m_botMargin;
|
||||
int scopeWidth = width() - m_leftMargin - m_rightMargin;
|
||||
|
||||
m_glScopeRect2 = QRectF(
|
||||
(float) m_leftMargin / (float)width(),
|
||||
(float) (m_botMargin + m_topMargin + scopeHeight) / (float)height(),
|
||||
(float) scopeWidth / (float)width(),
|
||||
(float) scopeHeight / (float)height()
|
||||
);
|
||||
m_glScopeMatrix2.setToIdentity();
|
||||
m_glScopeMatrix2.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*(m_botMargin + m_topMargin + scopeHeight) / (float) height())
|
||||
);
|
||||
m_glScopeMatrix2.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glLeft2ScaleMatrix.setToIdentity();
|
||||
m_glLeft2ScaleMatrix.translate (
|
||||
-1.0f,
|
||||
1.0f - ((float) 2*(m_topMargin + scopeHeight + m_botMargin) / (float) height())
|
||||
);
|
||||
m_glLeft2ScaleMatrix.scale (
|
||||
(float) 2*(m_leftMargin-1) / (float) width(),
|
||||
(float) -2*scopeHeight / (float) height()
|
||||
);
|
||||
|
||||
m_glBot2ScaleMatrix.setToIdentity();
|
||||
m_glBot2ScaleMatrix.translate (
|
||||
-1.0f + ((float) 2*m_leftMargin / (float) width()),
|
||||
1.0f - ((float) 2*(scopeHeight + m_topMargin + scopeHeight + m_botMargin + 1) / (float) height())
|
||||
);
|
||||
m_glBot2ScaleMatrix.scale (
|
||||
(float) 2*scopeWidth / (float) width(),
|
||||
(float) -2*(m_botMargin - 1) / (float) height()
|
||||
);
|
||||
|
||||
{ // Y2 scale
|
||||
m_y2Scale.setSize(scopeHeight);
|
||||
|
||||
m_left2ScalePixmap = QPixmap(
|
||||
m_leftMargin - 1,
|
||||
scopeHeight
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_left2ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_left2ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_y2Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
|
||||
|
||||
} // Y2 scale
|
||||
{ // X2 scale
|
||||
m_x2Scale.setSize(scopeWidth);
|
||||
m_bot2ScalePixmap = QPixmap(
|
||||
scopeWidth,
|
||||
m_botMargin - 1
|
||||
);
|
||||
|
||||
const ScaleEngine::TickList* tickList;
|
||||
const ScaleEngine::Tick* tick;
|
||||
|
||||
m_bot2ScalePixmap.fill(Qt::black);
|
||||
QPainter painter(&m_bot2ScalePixmap);
|
||||
painter.setPen(QColor(0xf0, 0xf0, 0xff));
|
||||
painter.setFont(font());
|
||||
tickList = &m_x2Scale.getTickList();
|
||||
|
||||
for(int i = 0; i < tickList->count(); i++) {
|
||||
tick = &(*tickList)[i];
|
||||
if(tick->major) {
|
||||
if(tick->textSize > 0) {
|
||||
painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
|
||||
} // X2 scale
|
||||
} // vertical or horizontal split of second display
|
||||
} // second display square (polar mode) or half space
|
||||
} // single or dual display
|
||||
}
|
||||
|
||||
void GLScopeNG::setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex)
|
||||
{
|
||||
ScopeVisNG::DisplayTrace trace = (*m_traces)[highlightedTraceIndex];
|
||||
float amp_range = 2.0 / trace.m_amp;
|
||||
float amp_ofs = trace.m_ofs;
|
||||
float pow_floor = -100.0 + trace.m_ofs * 100.0;
|
||||
float pow_range = 100.0 / trace.m_amp;
|
||||
|
||||
switch (trace.m_projectionType)
|
||||
{
|
||||
case ScopeVisNG::ProjectionMagDB: // dB scale
|
||||
scale.setRange(Unit::Decibel, pow_floor, pow_floor + pow_range);
|
||||
break;
|
||||
case ScopeVisNG::ProjectionPhase: // Phase or frequency
|
||||
case ScopeVisNG::ProjectionDPhase:
|
||||
scale.setRange(Unit::None, -1.0/trace.m_amp + amp_ofs, 1.0/trace.m_amp + amp_ofs);
|
||||
break;
|
||||
case ScopeVisNG::ProjectionReal: // Linear generic
|
||||
case ScopeVisNG::ProjectionImag:
|
||||
case ScopeVisNG::ProjectionMagLin:
|
||||
default:
|
||||
if (amp_range < 2.0) {
|
||||
scale.setRange(Unit::None, - amp_range * 500.0 + amp_ofs * 1000.0, amp_range * 500.0 + amp_ofs * 1000.0);
|
||||
} else {
|
||||
scale.setRange(Unit::None, - amp_range * 0.5 + amp_ofs, amp_range * 0.5 + amp_ofs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLScopeNG::tick()
|
||||
{
|
||||
if(m_dataChanged)
|
||||
update();
|
||||
}
|
||||
|
||||
void GLScopeNG::connectTimer(const QTimer& timer)
|
||||
{
|
||||
qDebug() << "GLScopeNG::connectTimer";
|
||||
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(tick()));
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
void GLScopeNG::cleanup()
|
||||
{
|
||||
//makeCurrent();
|
||||
m_glShaderSimple.cleanup();
|
||||
m_glShaderBottom1Scale.cleanup();
|
||||
m_glShaderBottom2Scale.cleanup();
|
||||
m_glShaderLeft1Scale.cleanup();
|
||||
m_glShaderPowerOverlay.cleanup();
|
||||
//doneCurrent();
|
||||
}
|
||||
|
131
sdrbase/gui/glscopeng.h
Normal file
131
sdrbase/gui/glscopeng.h
Normal file
@ -0,0 +1,131 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SDRBASE_GUI_GLSCOPENG_H_
|
||||
#define SDRBASE_GUI_GLSCOPENG_H_
|
||||
|
||||
#include <QGLWidget>
|
||||
#include <QPen>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QFont>
|
||||
#include <QMatrix4x4>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "dsp/scopevisng.h"
|
||||
#include "gui/scaleengine.h"
|
||||
#include "gui/glshadersimple.h"
|
||||
#include "gui/glshadertextured.h"
|
||||
#include "util/export.h"
|
||||
#include "util/bitfieldindex.h"
|
||||
|
||||
class QPainter;
|
||||
|
||||
class SDRANGEL_API GLScopeNG: public QGLWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum DisplayMode {
|
||||
DisplayXYH,
|
||||
DisplayXYV,
|
||||
DisplayX,
|
||||
DisplayY,
|
||||
DisplayPol
|
||||
};
|
||||
|
||||
GLScopeNG(QWidget* parent = 0);
|
||||
virtual ~GLScopeNG();
|
||||
|
||||
void connectTimer(const QTimer& timer);
|
||||
|
||||
void setTraces(const ScopeVisNG::DisplayTraces *traces) { m_traces = traces; m_configChanged = true; }
|
||||
void newTraces();
|
||||
|
||||
void setTriggerPre(Real triggerPre);
|
||||
void setTimeOfsProMill(int timeOfsProMill);
|
||||
void setSampleRate(int sampleRate);
|
||||
void setTimeBase(int timeBase);
|
||||
void setHighlightedTraceIndex(uint32_t traceIndex);
|
||||
void setDisplayMode(DisplayMode displayMode);
|
||||
void setTraceSize(int trceSize);
|
||||
|
||||
private:
|
||||
DisplayMode m_displayMode;
|
||||
QTimer m_timer;
|
||||
QMutex m_mutex;
|
||||
bool m_dataChanged;
|
||||
bool m_configChanged;
|
||||
const ScopeVisNG::DisplayTraces *m_traces;
|
||||
int m_sampleRate;
|
||||
int m_timeOfsProMill;
|
||||
Real m_triggerPre;
|
||||
int m_traceSize;
|
||||
int m_timeBase;
|
||||
uint32_t m_highlightedTraceIndex;
|
||||
|
||||
// graphics stuff
|
||||
QRectF m_glScopeRect1;
|
||||
QRectF m_glScopeRect2;
|
||||
QMatrix4x4 m_glScopeMatrix1;
|
||||
QMatrix4x4 m_glScopeMatrix2;
|
||||
QMatrix4x4 m_glLeft1ScaleMatrix;
|
||||
QMatrix4x4 m_glRight1ScaleMatrix;
|
||||
QMatrix4x4 m_glLeft2ScaleMatrix;
|
||||
QMatrix4x4 m_glBot1ScaleMatrix;
|
||||
QMatrix4x4 m_glBot2ScaleMatrix;
|
||||
|
||||
QPixmap m_left1ScalePixmap;
|
||||
QPixmap m_left2ScalePixmap;
|
||||
QPixmap m_bot1ScalePixmap;
|
||||
QPixmap m_bot2ScalePixmap;
|
||||
QPixmap m_powerOverlayPixmap1;
|
||||
|
||||
int m_displayGridIntensity;
|
||||
int m_displayTraceIntensity;
|
||||
|
||||
ScaleEngine m_x1Scale; //!< Display #1 X scale. Time scale
|
||||
ScaleEngine m_x2Scale; //!< Display #2 X scale. Time scale
|
||||
ScaleEngine m_y1Scale; //!< Display #1 Y scale. Always connected to trace #0 (X trace)
|
||||
ScaleEngine m_y2Scale; //!< Display #2 Y scale. Connected to highlighted Y trace (#1..n)
|
||||
|
||||
QFont m_powerOverlayFont;
|
||||
|
||||
GLShaderSimple m_glShaderSimple;
|
||||
GLShaderTextured m_glShaderLeft1Scale;
|
||||
GLShaderTextured m_glShaderBottom1Scale;
|
||||
GLShaderTextured m_glShaderLeft2Scale;
|
||||
GLShaderTextured m_glShaderBottom2Scale;
|
||||
GLShaderTextured m_glShaderPowerOverlay;
|
||||
|
||||
static const int m_topMargin = 5;
|
||||
static const int m_botMargin = 20;
|
||||
static const int m_leftMargin = 35;
|
||||
static const int m_rightMargin = 5;
|
||||
|
||||
void initializeGL();
|
||||
void resizeGL(int width, int height);
|
||||
void paintGL();
|
||||
|
||||
void applyConfig();
|
||||
void setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex);
|
||||
|
||||
protected slots:
|
||||
void cleanup();
|
||||
void tick();
|
||||
|
||||
};
|
||||
|
||||
#endif /* SDRBASE_GUI_GLSCOPENG_H_ */
|
88
sdrbase/gui/glscopenggui.cpp
Normal file
88
sdrbase/gui/glscopenggui.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "glscopenggui.h"
|
||||
#include "ui_glscopenggui.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
GLScopeNGGUI::GLScopeNGGUI(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::GLScopeNGGUI),
|
||||
m_messageQueue(0),
|
||||
m_glScope(0),
|
||||
m_scopeVis(0),
|
||||
m_sampleRate(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
GLScopeNGGUI::~GLScopeNGGUI()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void GLScopeNGGUI::setBuddies(MessageQueue* messageQueue, ScopeVisNG* scopeVis, GLScopeNG* glScope)
|
||||
{
|
||||
m_messageQueue = messageQueue;
|
||||
m_scopeVis = scopeVis;
|
||||
m_glScope = glScope;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void GLScopeNGGUI::setSampleRate(int sampleRate)
|
||||
{
|
||||
m_sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
void GLScopeNGGUI::resetToDefaults()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QByteArray GLScopeNGGUI::serialize() const
|
||||
{
|
||||
// TODO
|
||||
SimpleSerializer s(1);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool GLScopeNGGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
// TODO
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GLScopeNGGUI::applySettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool GLScopeNGGUI::handleMessage(Message* message)
|
||||
{
|
||||
return false;
|
||||
}
|
64
sdrbase/gui/glscopenggui.h
Normal file
64
sdrbase/gui/glscopenggui.h
Normal file
@ -0,0 +1,64 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SDRBASE_GUI_GLSCOPENGGUI_H_
|
||||
#define SDRBASE_GUI_GLSCOPENGGUI_H_
|
||||
|
||||
#include <QWidget>
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "util/export.h"
|
||||
#include "util/message.h"
|
||||
|
||||
namespace Ui {
|
||||
class GLScopeNGGUI;
|
||||
}
|
||||
|
||||
class MessageQueue;
|
||||
class GLScopeNG;
|
||||
class ScopeVisNG;
|
||||
|
||||
class SDRANGEL_API GLScopeNGGUI : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GLScopeNGGUI(QWidget* parent = 0);
|
||||
~GLScopeNGGUI();
|
||||
|
||||
void setBuddies(MessageQueue* messageQueue, ScopeVisNG* scopeVis, GLScopeNG* glScope);
|
||||
|
||||
void setSampleRate(int sampleRate);
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
Ui::GLScopeNGGUI* ui;
|
||||
|
||||
MessageQueue* m_messageQueue;
|
||||
ScopeVisNG* m_scopeVis;
|
||||
GLScopeNG* m_glScope;
|
||||
|
||||
int m_sampleRate;
|
||||
|
||||
void applySettings();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* SDRBASE_GUI_GLSCOPENGGUI_H_ */
|
1481
sdrbase/gui/glscopenggui.ui
Normal file
1481
sdrbase/gui/glscopenggui.ui
Normal file
File diff suppressed because it is too large
Load Diff
@ -72,6 +72,7 @@ SOURCES += mainwindow.cpp\
|
||||
dsp/basebandsamplesource.cpp\
|
||||
dsp/nullsink.cpp\
|
||||
dsp/spectrumscopecombovis.cpp\
|
||||
dsp/spectrumscopengcombovis.cpp\
|
||||
dsp/scopevis.cpp\
|
||||
dsp/spectrumvis.cpp\
|
||||
dsp/threadedbasebandsamplesink.cpp\
|
||||
@ -85,6 +86,8 @@ SOURCES += mainwindow.cpp\
|
||||
gui/cwkeyergui.cpp\
|
||||
gui/glscope.cpp\
|
||||
gui/glscopegui.cpp\
|
||||
gui/glscopeng.cpp\
|
||||
gui/glscopenggui.cpp\
|
||||
gui/glshadersimple.cpp\
|
||||
gui/glshadertextured.cpp\
|
||||
gui/glspectrum.cpp\
|
||||
@ -172,6 +175,8 @@ HEADERS += mainwindow.h\
|
||||
dsp/basebandsamplesink.h\
|
||||
dsp/basebandsamplesource.h\
|
||||
dsp/nullsink.h\
|
||||
dsp/spectrumscopecombovis.h\
|
||||
dsp/spectrumscopengcombovis.h\
|
||||
dsp/scopevis.h\
|
||||
dsp/spectrumvis.h\
|
||||
dsp/threadedbasebandsamplesink.h\
|
||||
@ -186,6 +191,8 @@ HEADERS += mainwindow.h\
|
||||
gui/cwkeyergui.h\
|
||||
gui/glscope.h\
|
||||
gui/glscopegui.h\
|
||||
gui/glscopeng.h\
|
||||
gui/glscopenggui.h\
|
||||
gui/glshadersimple.h\
|
||||
gui/glshadertextured.h\
|
||||
gui/glspectrum.h\
|
||||
@ -227,6 +234,7 @@ FORMS += mainwindow.ui\
|
||||
gui/cwkeyergui.ui\
|
||||
gui/audiodialog.ui\
|
||||
gui/glscopegui.ui\
|
||||
gui/glscopenggui.ui\
|
||||
gui/aboutdialog.ui\
|
||||
gui/pluginsdialog.ui\
|
||||
gui/samplingdevicecontrol.ui\
|
||||
|
77
sdrbase/util/doublebuffer.h
Normal file
77
sdrbase/util/doublebuffer.h
Normal file
@ -0,0 +1,77 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SDRBASE_UTIL_DOUBLEBUFFER_H_
|
||||
#define SDRBASE_UTIL_DOUBLEBUFFER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
template<typename T>
|
||||
class DoubleBufferSimple
|
||||
{
|
||||
public:
|
||||
DoubleBufferSimple()
|
||||
{
|
||||
m_size = 0;
|
||||
m_current = m_data.end();
|
||||
}
|
||||
|
||||
~DoubleBufferSimple() {}
|
||||
|
||||
void resize(int size)
|
||||
{
|
||||
m_size = size;
|
||||
m_data.resize(2*size);
|
||||
m_current = m_data.begin();
|
||||
}
|
||||
|
||||
void write(const typename std::vector<T>::const_iterator& begin, const typename std::vector<T>::const_iterator& cend)
|
||||
{
|
||||
typename std::vector<T>::const_iterator end = cend;
|
||||
|
||||
if ((end - begin) > m_size)
|
||||
{
|
||||
end = begin + m_size;
|
||||
}
|
||||
|
||||
int insize = end - begin;
|
||||
|
||||
std::copy(begin, end, m_current);
|
||||
|
||||
if (((m_current - m_data.begin()) + insize) > m_size)
|
||||
{
|
||||
int sizeLeft = m_size - (m_current - m_data.begin());
|
||||
std::copy(begin, begin + sizeLeft, m_current + m_size);
|
||||
std::copy(begin + sizeLeft, end, m_data.begin());
|
||||
m_current = m_data.begin() + (insize - sizeLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(begin, end, m_current + m_size);
|
||||
m_current += insize;
|
||||
}
|
||||
}
|
||||
|
||||
typename std::vector<T>::iterator getCurrent() const { return m_current + m_size; }
|
||||
|
||||
private:
|
||||
int m_size;
|
||||
std::vector<T> m_data;
|
||||
typename std::vector<T>::iterator m_current;
|
||||
};
|
||||
|
||||
#endif /* SDRBASE_UTIL_DOUBLEBUFFER_H_ */
|
Loading…
Reference in New Issue
Block a user