Websocket spectrum: first implementation

This commit is contained in:
f4exb 2020-04-29 17:41:17 +02:00
parent 6a6b5f8d7e
commit ac6c3b08f2
11 changed files with 623 additions and 33 deletions

View File

@ -399,6 +399,8 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban
m_spectrumVis->configure(
m_spectrumVis->getInputMessageQueue(),
64, // FFT size
0, // Ref level (dB)
100, // Power range (dB)
10, // overlapping %
0, // number of averaging samples
SpectrumVis::AvgModeNone, // no averaging

View File

@ -189,6 +189,8 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
ui->glSpectrum->setDisplayMaxHold(true);
m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(),
64, // FFT size
0, // Ref level (dB)
100, // Power range (dB)
10, // overlapping %
0, // number of averaging samples
SpectrumVis::AvgModeNone, // no averaging

View File

@ -146,6 +146,8 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
ui->glSpectrum->setDisplayMaxHold(true);
m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(),
64, // FFT size
0, // Ref level (dB)
100, // Power range (dB)
10, // overlapping %
0, // number of averaging samples
SpectrumVis::AvgModeNone, // no averaging

View File

@ -163,6 +163,8 @@ set(sdrbase_SOURCES
webapi/webapirequestmapper.cpp
webapi/webapiserver.cpp
websockets/wsspectrum.cpp
mainparser.cpp
resources/webapi.qrc
@ -310,7 +312,9 @@ set(sdrbase_HEADERS
webapi/webapiadapterbase.h
webapi/webapiadapterinterface.h
webapi/webapirequestmapper.h
webapi/webapiserver
webapi/webapiserver.h
websockets/wsspectrum.h
mainparser.h
)
@ -343,6 +347,7 @@ target_link_libraries(sdrbase
${sdrbase_LIMERFE_LIB}
Qt5::Core
Qt5::Multimedia
Qt5::WebSockets
httpserver
qrtplib
swagger

View File

@ -36,6 +36,7 @@ inline double log2f(double n)
#endif
MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureSpectrumVis, Message)
MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureDSP, Message)
MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureScalingFactor, Message)
const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f));
@ -53,12 +54,15 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrumInterface* glSpectrum) :
m_averageNb(0),
m_avgMode(AvgModeNone),
m_linear(false),
m_centerFrequency(0),
m_sampleRate(48000),
m_ofs(0),
m_powFFTDiv(1.0),
m_mutex(QMutex::Recursive)
{
setObjectName("SpectrumVis");
handleConfigure(1024, 0, 0, AvgModeNone, FFTWindow::BlackmanHarris, false);
handleConfigure(1024, 0, 100, 0, 0, AvgModeNone, FFTWindow::BlackmanHarris, false);
m_wsSpectrum.openSocket(); // FIXME: conditional
}
SpectrumVis::~SpectrumVis()
@ -69,17 +73,34 @@ SpectrumVis::~SpectrumVis()
void SpectrumVis::configure(MessageQueue* msgQueue,
int fftSize,
float refLevel,
float powerRange,
int overlapPercent,
unsigned int averagingNb,
AvgMode averagingMode,
FFTWindow::Function window,
bool linear)
{
MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window, linear);
MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(
fftSize,
refLevel,
powerRange,
overlapPercent,
averagingNb,
averagingMode,
window,
linear
);
msgQueue->push(cmd);
}
void SpectrumVis::setScalef(MessageQueue* msgQueue, Real scalef)
void SpectrumVis::configureDSP(uint64_t centerFrequency, int sampleRate)
{
MsgConfigureDSP* cmd = new MsgConfigureDSP(centerFrequency, sampleRate);
getInputMessageQueue()->push(cmd);
}
void SpectrumVis::setScalef(Real scalef)
{
MsgConfigureScalingFactor* cmd = new MsgConfigureScalingFactor(scalef);
getInputMessageQueue()->push(cmd);
@ -106,11 +127,189 @@ void SpectrumVis::feedTriggered(const SampleVector::const_iterator& triggerPoint
}*/
}
void SpectrumVis::feed(const Complex *begin, unsigned int length)
{
if (!m_glSpectrum && !m_wsSpectrum.socketOpened()) {
return;
}
if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
return;
}
Complex c;
Real v;
if (m_avgMode == AvgModeNone)
{
for (unsigned int i = 0; i < m_fftSize; i++)
{
if (i < length) {
c = begin[i];
} else {
c = Complex{0,0};
}
v = c.real() * c.real() + c.imag() * c.imag();
v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
}
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
else if (m_avgMode == AvgModeMovingAvg)
{
for (unsigned int i = 0; i < m_fftSize; i++)
{
if (i < length) {
c = begin[i];
} else {
c = Complex{0,0};
}
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i);
v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
}
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
m_movingAverage.nextAverage();
}
else if (m_avgMode == AvgModeFixedAvg)
{
double avg;
for (unsigned int i = 0; i < m_fftSize; i++)
{
if (i < length) {
c = begin[i];
} else {
c = Complex{0,0};
}
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg;
}
}
// result available
if (m_fixedAverage.nextAverage())
{
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
}
else if (m_avgMode == AvgModeMax)
{
double max;
for (unsigned int i = 0; i < m_fftSize; i++)
{
if (i < length) {
c = begin[i];
} else {
c = Complex{0,0};
}
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_max.storeAndGetMax(max, v, i))
{
max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i] = max;
}
}
// result available
if (m_max.nextMax())
{
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
}
m_mutex.unlock();
}
void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly)
{
// if no visualisation is set, send the samples to /dev/null
if (m_glSpectrum == 0) {
if (!m_glSpectrum && !m_wsSpectrum.socketOpened()) {
return;
}
@ -177,7 +376,23 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
}
// send new data to visualisation
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
else if (m_avgMode == AvgModeMovingAvg)
{
@ -212,7 +427,24 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
}
// send new data to visualisation
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
m_movingAverage.nextAverage();
}
else if (m_avgMode == AvgModeFixedAvg)
@ -241,8 +473,9 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
{ // result available
{
avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg;
}
@ -250,16 +483,36 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{ // result available
{
avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i + halfSize] = avg;
}
}
}
if (m_fixedAverage.nextAverage()) { // result available
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
// result available
if (m_fixedAverage.nextAverage())
{
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
}
else if (m_avgMode == AvgModeMax)
@ -288,8 +541,9 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_max.storeAndGetMax(max, v, i+halfSize))
{ // result available
{
max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i] = max;
}
@ -297,16 +551,36 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_max.storeAndGetMax(max, v, i))
{ // result available
{
max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i + halfSize] = max;
}
}
}
if (m_max.nextMax()) { // result available
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
// result available
if (m_max.nextMax())
{
// send new data to visualisation
if (m_glSpectrum) {
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_fftSize,
m_refLevel,
m_powerRange,
m_centerFrequency,
m_sampleRate,
m_linear
);
}
}
}
@ -347,6 +621,8 @@ bool SpectrumVis::handleMessage(const Message& message)
{
MsgConfigureSpectrumVis& conf = (MsgConfigureSpectrumVis&) message;
handleConfigure(conf.getFFTSize(),
conf.getRefLevel(),
conf.getPowerRange(),
conf.getOverlapPercent(),
conf.getAverageNb(),
conf.getAvgMode(),
@ -354,6 +630,12 @@ bool SpectrumVis::handleMessage(const Message& message)
conf.getLinear());
return true;
}
else if (MsgConfigureDSP::match(message))
{
MsgConfigureDSP& conf = (MsgConfigureDSP&) message;
handleConfigureDSP(conf.getCenterFrequency(), conf.getSampleRate());
return true;
}
else if (MsgConfigureScalingFactor::match(message))
{
MsgConfigureScalingFactor& conf = (MsgConfigureScalingFactor&) message;
@ -367,6 +649,8 @@ bool SpectrumVis::handleMessage(const Message& message)
}
void SpectrumVis::handleConfigure(int fftSize,
float refLevel,
float powerRange,
int overlapPercent,
unsigned int averageNb,
AvgMode averagingMode,
@ -377,25 +661,20 @@ void SpectrumVis::handleConfigure(int fftSize,
// fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false");
QMutexLocker mutexLocker(&m_mutex);
if (fftSize > MAX_FFT_SIZE)
{
if (fftSize > MAX_FFT_SIZE) {
fftSize = MAX_FFT_SIZE;
}
else if (fftSize < 64)
{
} else if (fftSize < 64) {
fftSize = 64;
}
if (overlapPercent > 100)
{
m_refLevel = refLevel;
m_powerRange = powerRange;
if (overlapPercent > 100) {
m_overlapPercent = 100;
}
else if (overlapPercent < 0)
{
} else if (overlapPercent < 0) {
m_overlapPercent = 0;
}
else
{
} else {
m_overlapPercent = overlapPercent;
}
@ -417,6 +696,13 @@ void SpectrumVis::handleConfigure(int fftSize,
m_powFFTDiv = m_fftSize*m_fftSize;
}
void SpectrumVis::handleConfigureDSP(uint64_t centerFrequency, int sampleRate)
{
QMutexLocker mutexLocker(&m_mutex);
m_centerFrequency = centerFrequency;
m_sampleRate = sampleRate;
}
void SpectrumVis::handleScalef(Real scalef)
{
QMutexLocker mutexLocker(&m_mutex);

View File

@ -21,8 +21,9 @@
#ifndef INCLUDE_SPECTRUMVIS_H
#define INCLUDE_SPECTRUMVIS_H
#include <dsp/basebandsamplesink.h>
#include <QMutex>
#include "dsp/basebandsamplesink.h"
#include "dsp/fftengine.h"
#include "dsp/fftwindow.h"
#include "export.h"
@ -30,6 +31,7 @@
#include "util/movingaverage2d.h"
#include "util/fixedaverage2d.h"
#include "util/max2d.h"
#include "websockets/wsspectrum.h"
class GLSpectrumInterface;
class MessageQueue;
@ -51,6 +53,8 @@ public:
public:
MsgConfigureSpectrumVis(
int fftSize,
float refLevel,
float powerRange,
int overlapPercent,
unsigned int averageNb,
AvgMode avgMode,
@ -59,6 +63,8 @@ public:
Message(),
m_fftSize(fftSize),
m_overlapPercent(overlapPercent),
m_refLevel(refLevel),
m_powerRange(powerRange),
m_averageNb(averageNb),
m_avgMode(avgMode),
m_window(window),
@ -66,6 +72,8 @@ public:
{}
int getFFTSize() const { return m_fftSize; }
float getRefLevel() const { return m_refLevel; }
float getPowerRange() const { return m_powerRange; }
int getOverlapPercent() const { return m_overlapPercent; }
unsigned int getAverageNb() const { return m_averageNb; }
SpectrumVis::AvgMode getAvgMode() const { return m_avgMode; }
@ -75,12 +83,33 @@ public:
private:
int m_fftSize;
int m_overlapPercent;
float m_refLevel;
float m_powerRange;
unsigned int m_averageNb;
SpectrumVis::AvgMode m_avgMode;
FFTWindow::Function m_window;
bool m_linear;
};
class MsgConfigureDSP : public Message
{
MESSAGE_CLASS_DECLARATION
public:
MsgConfigureDSP(uint64_t centerFrequency, int sampleRate) :
Message(),
m_centerFrequency(centerFrequency),
m_sampleRate(sampleRate)
{}
uint64_t getCenterFrequency() const { return m_centerFrequency; }
int getSampleRate() const { return m_sampleRate; }
private:
uint64_t m_centerFrequency;
int m_sampleRate;
};
class MsgConfigureScalingFactor : public Message
{
MESSAGE_CLASS_DECLARATION
@ -102,12 +131,15 @@ public:
void configure(MessageQueue* msgQueue,
int fftSize,
float refLevel,
float powerRange,
int overlapPercent,
unsigned int averagingNb,
AvgMode averagingMode,
FFTWindow::Function window,
bool m_linear);
void setScalef(MessageQueue* msgQueue, Real scalef);
void configureDSP(uint64_t centerFrequency, int sampleRate);
void setScalef(Real scalef);
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly);
@ -124,6 +156,8 @@ private:
std::vector<Real> m_powerSpectrum;
std::size_t m_fftSize;
float m_refLevel;
float m_powerRange;
std::size_t m_overlapPercent;
std::size_t m_overlapSize;
std::size_t m_refillSize;
@ -132,6 +166,7 @@ private:
Real m_scalef;
GLSpectrumInterface* m_glSpectrum;
WSSpectrum m_wsSpectrum;
MovingAverage2D<double> m_movingAverage;
FixedAverage2D<double> m_fixedAverage;
Max2D<double> m_max;
@ -139,6 +174,9 @@ private:
AvgMode m_avgMode;
bool m_linear;
uint64_t m_centerFrequency;
int m_sampleRate;
Real m_ofs;
Real m_powFFTDiv;
static const Real m_mult;
@ -146,11 +184,15 @@ private:
QMutex m_mutex;
void handleConfigure(int fftSize,
float refLevel,
float powerRange,
int overlapPercent,
unsigned int averageNb,
AvgMode averagingMode,
FFTWindow::Function window,
bool linear);
void handleConfigureDSP(uint64_t centerFrequency,
int sampleRate);
void handleScalef(Real scalef);
};

View File

@ -0,0 +1,164 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtWebSockets>
#include <QHostAddress>
#include <QDebug>
#include "wsspectrum.h"
WSSpectrum::WSSpectrum(QObject *parent) :
QObject(parent),
m_listeningAddress(QHostAddress::LocalHost),
m_port(8887),
m_webSocketServer(nullptr)
{
m_timer.start();
}
WSSpectrum::~WSSpectrum()
{
closeSocket();
}
void WSSpectrum::openSocket()
{
m_webSocketServer = new QWebSocketServer(
QStringLiteral("Spectrum Server"),
QWebSocketServer::NonSecureMode,
this);
if (m_webSocketServer->listen(m_listeningAddress, m_port))
{
qDebug() << "WSSpectrum::openSocket: spectrum server listening at " << m_listeningAddress.toString() << " on port " << m_port;
connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &WSSpectrum::onNewConnection);
}
else
{
qInfo("WSSpectrum::openSocket: cannot start spectrum server at %s on port %u", qPrintable(m_listeningAddress.toString()), m_port);
}
}
void WSSpectrum::closeSocket()
{
if (m_webSocketServer)
{
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
}
bool WSSpectrum::socketOpened()
{
return m_webSocketServer && m_webSocketServer->isListening();
}
QString WSSpectrum::getWebSocketIdentifier(QWebSocket *peer)
{
return QStringLiteral("%1:%2").arg(peer->peerAddress().toString(), QString::number(peer->peerPort()));
}
void WSSpectrum::onNewConnection()
{
auto pSocket = m_webSocketServer->nextPendingConnection();
qDebug() << " WSSpectrum::onNewConnection: " << getWebSocketIdentifier(pSocket) << " connected";
pSocket->setParent(this);
connect(pSocket, &QWebSocket::textMessageReceived, this, &WSSpectrum::processClientMessage);
connect(pSocket, &QWebSocket::disconnected, this, &WSSpectrum::socketDisconnected);
m_clients << pSocket;
}
void WSSpectrum::processClientMessage(const QString &message)
{
qDebug() << "WSSpectrum::processClientMessage: " << message;
}
void WSSpectrum::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
qDebug() << getWebSocketIdentifier(pClient) << " disconnected";
if (pClient)
{
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
void WSSpectrum::newSpectrum(
const std::vector<Real>& spectrum,
int fftSize,
float refLevel,
float powerRange,
uint64_t centerFrequency,
int bandwidth,
bool linear
)
{
if (m_timer.elapsed() < 200) { // Max 5 frames per second
return;
}
qint64 elapsed = m_timer.restart();
QWebSocket *pSender = qobject_cast<QWebSocket *>(sender());
QByteArray payload;
buildPayload(
payload,
spectrum,
fftSize,
elapsed,
refLevel,
powerRange,
centerFrequency,
bandwidth,
linear
);
//qDebug() << "WSSpectrum::newSpectrum: " << payload.size() << " bytes in " << elapsed << " ms";
for (QWebSocket *pClient : qAsConst(m_clients)) {
pClient->sendBinaryMessage(payload);
}
}
void WSSpectrum::buildPayload(
QByteArray& bytes,
const std::vector<Real>& spectrum,
int fftSize,
int64_t fftTimeMs,
float refLevel,
float powerRange,
uint64_t centerFrequency,
int bandwidth,
bool linear
)
{
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
buffer.write((char*) &fftSize, sizeof(int));
buffer.write((char*) &fftTimeMs, sizeof(int64_t));
buffer.write((char*) &refLevel, sizeof(float));
buffer.write((char*) &powerRange, sizeof(float));
buffer.write((char*) &centerFrequency, sizeof(uint64_t));
buffer.write((char*) &bandwidth, sizeof(int));
int linearInt = linear ? 1 : 0;
buffer.write((char*) &linearInt, sizeof(int));
buffer.write((char*) spectrum.data(), fftSize*sizeof(Real));
buffer.close();
}

View File

@ -0,0 +1,83 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_WEBSOCKETS_WSSPECTRUM_H_
#define SDRBASE_WEBSOCKETS_WSSPECTRUM_H_
#include <vector>
#include <QObject>
#include <QList>
#include <QElapsedTimer>
#include <QHostAddress>
#include "dsp/dsptypes.h"
#include "export.h"
class QWebSocketServer;
class QWebSocket;
class SDRBASE_API WSSpectrum : public QObject
{
Q_OBJECT
public:
explicit WSSpectrum(QObject *parent = nullptr);
~WSSpectrum() override;
void openSocket();
void closeSocket();
bool socketOpened();
void setListeningAddress(const QString& address) { m_listeningAddress.setAddress(address); }
void setPort(quint16 port) { m_port = port; }
void newSpectrum(
const std::vector<Real>& spectrum,
int fftSize,
float refLevel,
float powerRange,
uint64_t centerFrequency,
int bandwidth,
bool linear
);
private slots:
void onNewConnection();
void processClientMessage(const QString &message);
void socketDisconnected();
private:
QHostAddress m_listeningAddress;
quint16 m_port;
QWebSocketServer* m_webSocketServer;
QList<QWebSocket*> m_clients;
QElapsedTimer m_timer;
static QString getWebSocketIdentifier(QWebSocket *peer);
void buildPayload(
QByteArray& bytes,
const std::vector<Real>& spectrum,
int fftSize,
int64_t fftTimeMs,
float refLevel,
float powerRange,
uint64_t centerFrequency,
int bandwidth,
bool linear
);
};
#endif // SDRBASE_WEBSOCKETS_WSSPECTRUM_H_

View File

@ -74,7 +74,7 @@ DeviceUISet::~DeviceUISet()
void DeviceUISet::setSpectrumScalingFactor(float scalef)
{
m_spectrumVis->setScalef(m_spectrumVis->getInputMessageQueue(), scalef);
m_spectrumVis->setScalef(scalef);
}
void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker)

View File

@ -231,6 +231,8 @@ void GLSpectrumGUI::applySettingsVis()
{
m_spectrumVis->configure(m_messageQueueToVis,
m_fftSize,
m_refLevel,
m_powerRange,
m_fftOverlap,
m_averagingNb,
(SpectrumVis::AvgMode) m_averagingMode,
@ -338,6 +340,8 @@ void GLSpectrumGUI::on_refLevel_currentIndexChanged(int index)
m_glSpectrum->setReferenceLevel(refLevel);
m_glSpectrum->setPowerRange(powerRange);
}
applySettingsVis();
}
void GLSpectrumGUI::on_levelRange_currentIndexChanged(int index)
@ -352,6 +356,8 @@ void GLSpectrumGUI::on_levelRange_currentIndexChanged(int index)
m_glSpectrum->setReferenceLevel(refLevel);
m_glSpectrum->setPowerRange(powerRange);
}
applySettingsVis();
}
void GLSpectrumGUI::on_decay_valueChanged(int index)

View File

@ -37,8 +37,6 @@ class DSPEngine;
class DSPDeviceSourceEngine;
class DSPDeviceSinkEngine;
class Indicator;
class SpectrumVis;
class GLSpectrum;
class GLSpectrumGUI;
class ChannelWindow;
class PluginAPI;