sdrangel/sdrbase/websockets/wsspectrum.cpp

219 lines
6.7 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020-2021 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 "util/timeutil.h"
#include "wsspectrum.h"
WSSpectrum::WSSpectrum(QObject *parent) :
QObject(parent),
m_listeningAddress(QHostAddress::LocalHost),
m_port(8887),
m_webSocketServer(nullptr)
{
connect(this,
SIGNAL(payloadToSend(const QByteArray&)),
this,
SLOT(sendPayload(const QByteArray&)),
Qt::QueuedConnection);
m_timer.start();
}
WSSpectrum::~WSSpectrum()
{
disconnect(this,
SIGNAL(payloadToSend(const QByteArray&)),
this,
SLOT(sendPayload(const QByteArray&)));
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)
{
qDebug() << "WSSpectrum::closeSocket: stopping spectrum server listening at " << m_listeningAddress.toString() << " on port " << m_port;
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
}
bool WSSpectrum::socketOpened() const
{
return m_webSocketServer && m_webSocketServer->isListening();
}
void WSSpectrum::getPeers(QList<QHostAddress>& hosts, QList<quint16>& ports) const
{
hosts.clear();
ports.clear();
for (auto pSocket : m_clients)
{
hosts.push_back(pSocket->peerAddress());
ports.push_back(pSocket->peerPort());
}
}
void WSSpectrum::setListeningAddress(const QString& address)
{
if (address == "127.0.0.1") {
m_listeningAddress.setAddress(QHostAddress::LocalHost);
} else if (address == "0.0.0.0") {
m_listeningAddress.setAddress(QHostAddress::Any);
} else {
m_listeningAddress.setAddress(address);
}
}
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();
}
}
QHostAddress WSSpectrum::getListeningAddress() const
{
if (m_webSocketServer) {
return m_webSocketServer->serverAddress();
} else {
return QHostAddress::Null;
}
}
uint16_t WSSpectrum::getListeningPort() const
{
if (m_webSocketServer) {
return m_webSocketServer->serverPort();
} else {
return 0;
}
}
void WSSpectrum::newSpectrum(
const std::vector<Real>& spectrum,
int fftSize,
uint64_t centerFrequency,
int bandwidth,
bool linear,
bool ssb,
bool usb
)
{
qint64 elapsed = m_timer.restart();
uint64_t nowMs = TimeUtil::nowms();
QByteArray payload;
buildPayload(
payload,
spectrum,
fftSize,
elapsed,
nowMs,
centerFrequency,
bandwidth,
linear,
ssb,
usb
);
//qDebug() << "WSSpectrum::newSpectrum: " << payload.size() << " bytes in " << elapsed << " ms";
emit payloadToSend(payload);
}
void WSSpectrum::sendPayload(const QByteArray& payload)
{
//qDebug() << "WSSpectrum::sendPayload: " << payload.size() << " bytes";
for (QWebSocket *pClient : qAsConst(m_clients)) {
pClient->sendBinaryMessage(payload);
}
}
void WSSpectrum::buildPayload(
QByteArray& bytes,
const std::vector<Real>& spectrum,
int fftSize,
int64_t fftTimeMs,
uint64_t timestampMs,
uint64_t centerFrequency,
int bandwidth,
bool linear,
bool ssb,
bool usb
)
{
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
buffer.write((char*) &centerFrequency, sizeof(uint64_t)); // 0
buffer.write((char*) &fftTimeMs, sizeof(int64_t)); // 8
buffer.write((char*) &timestampMs, sizeof(uint64_t)); // 16
buffer.write((char*) &fftSize, sizeof(int)); // 24
buffer.write((char*) &bandwidth, sizeof(int)); // 28
int indicators = (linear ? 1 : 0) + (ssb ? 2 : 0) + (usb ? 4 : 0);
buffer.write((char*) &indicators, sizeof(int)); // 32
buffer.write((char*) spectrum.data(), fftSize*sizeof(Real)); // 36
buffer.close();
}