Add PER tester feature

This commit is contained in:
Jon Beniston 2021-04-07 20:25:12 +01:00
parent 8ecdb3fdc1
commit 300c428f8c
36 changed files with 4812 additions and 48 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -14,6 +14,7 @@ endif()
add_subdirectory(afc)
add_subdirectory(aprs)
add_subdirectory(demodanalyzer)
add_subdirectory(pertester)
add_subdirectory(rigctlserver)
add_subdirectory(simpleptt)
if (SGP4_FOUND)

View File

@ -0,0 +1,57 @@
project(pertester)
set(pertester_SOURCES
pertester.cpp
pertestersettings.cpp
pertesterplugin.cpp
pertesterworker.cpp
pertesterwebapiadapter.cpp
)
set(pertester_HEADERS
pertester.h
pertestersettings.h
pertesterplugin.h
pertesterreport.h
pertesterworker.h
pertesterwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(pertester_SOURCES
${pertester_SOURCES}
pertestergui.cpp
pertestergui.ui
)
set(pertester_HEADERS
${pertester_HEADERS}
pertestergui.h
)
set(TARGET_NAME featurepertester)
set(TARGET_LIB Qt5::Widgets)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME featurepertestersrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${pertester_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,556 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QTimer>
#include "SWGFeatureSettings.h"
#include "SWGFeatureReport.h"
#include "SWGFeatureActions.h"
#include "SWGDeviceState.h"
#include "SWGPERTesterActions.h"
#include "dsp/dspengine.h"
#include "device/deviceset.h"
#include "channel/channelapi.h"
#include "feature/featureset.h"
#include "maincore.h"
#include "pertester.h"
#include "pertesterworker.h"
#include "pertesterreport.h"
MESSAGE_CLASS_DEFINITION(PERTester::MsgConfigurePERTester, Message)
MESSAGE_CLASS_DEFINITION(PERTester::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(PERTester::MsgResetStats, Message)
MESSAGE_CLASS_DEFINITION(PERTester::MsgReportWorker, Message)
const char* const PERTester::m_featureIdURI = "sdrangel.feature.pertester";
const char* const PERTester::m_featureId = "PERTester";
PERTester::PERTester(WebAPIAdapterInterface *webAPIAdapterInterface) :
Feature(m_featureIdURI, webAPIAdapterInterface)
{
qDebug("PERTester::PERTester: webAPIAdapterInterface: %p", webAPIAdapterInterface);
setObjectName(m_featureId);
m_worker = new PERTesterWorker();
m_state = StIdle;
m_errorMessage = "PERTester error";
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
PERTester::~PERTester()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
if (m_worker->isRunning()) {
stop();
}
delete m_worker;
}
void PERTester::start()
{
qDebug("PERTester::start");
m_worker->setMessageQueueToFeature(getInputMessageQueue());
m_worker->setMessageQueueToGUI(getMessageQueueToGUI());
m_worker->getInputMessageQueue()->push(PERTesterWorker::MsgConfigurePERTesterWorker::create(m_settings, true));
if (m_settings.m_start == PERTesterSettings::START_IMMEDIATELY)
{
bool ok = m_worker->startWork();
m_state = ok ? StRunning : StError;
}
else
{
// Wait for AOS
m_state = StIdle;
}
m_thread.start();
}
void PERTester::stop()
{
qDebug("PERTester::stop");
m_worker->stopWork();
m_state = StIdle;
m_thread.quit();
m_thread.wait();
}
bool PERTester::handleMessage(const Message& cmd)
{
if (MsgConfigurePERTester::match(cmd))
{
MsgConfigurePERTester& cfg = (MsgConfigurePERTester&) cmd;
qDebug() << "PERTester::handleMessage: MsgConfigurePERTester";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (MsgStartStop::match(cmd))
{
MsgStartStop& cfg = (MsgStartStop&) cmd;
qDebug() << "PERTester::handleMessage: MsgStartStop: start:" << cfg.getStartStop();
if (cfg.getStartStop()) {
start();
} else {
stop();
}
return true;
}
else if (MsgResetStats::match(cmd))
{
m_worker->getInputMessageQueue()->push(MsgResetStats::create());
return true;
}
else if (MsgReportWorker::match(cmd))
{
MsgReportWorker& report = (MsgReportWorker&) cmd;
if (report.getMessage() == "Complete")
m_state = StIdle;
else
{
m_state = StError;
m_errorMessage = report.getMessage();
}
return true;
}
else
{
return false;
}
}
QByteArray PERTester::serialize() const
{
return m_settings.serialize();
}
bool PERTester::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigurePERTester *msg = MsgConfigurePERTester::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigurePERTester *msg = MsgConfigurePERTester::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void PERTester::applySettings(const PERTesterSettings& settings, bool force)
{
qDebug() << "PERTester::applySettings:"
<< " m_packetCount: " << settings.m_packetCount
<< " m_interval: " << settings.m_interval
<< " m_start: " << settings.m_start
<< " m_satellites: " << settings.m_satellites
<< " m_packet: " << settings.m_packet
<< " m_ignoreLeadingBytes: " << settings.m_ignoreLeadingBytes
<< " m_ignoreTrailingBytes: " << settings.m_ignoreTrailingBytes
<< " m_txUDPAddress: " << settings.m_txUDPAddress
<< " m_txUDPPort: " << settings.m_txUDPPort
<< " m_rxUDPAddress: " << settings.m_rxUDPAddress
<< " m_rxUDPPort: " << settings.m_rxUDPPort
<< " m_title: " << settings.m_title
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIFeatureSetIndex: " << settings.m_reverseAPIFeatureSetIndex
<< " m_reverseAPIFeatureIndex: " << settings.m_reverseAPIFeatureIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((m_settings.m_packetCount != settings.m_packetCount) || force) {
reverseAPIKeys.append("packetCount");
}
if ((m_settings.m_interval != settings.m_interval) || force) {
reverseAPIKeys.append("interval");
}
if ((m_settings.m_start != settings.m_start) || force) {
reverseAPIKeys.append("start");
}
if ((m_settings.m_satellites != settings.m_satellites) || force) {
reverseAPIKeys.append("satellites");
}
if ((m_settings.m_packet != settings.m_packet) || force) {
reverseAPIKeys.append("packet");
}
if ((m_settings.m_ignoreLeadingBytes != settings.m_ignoreLeadingBytes) || force) {
reverseAPIKeys.append("ignoreLeadingBytes");
}
if ((m_settings.m_ignoreTrailingBytes != settings.m_ignoreTrailingBytes) || force) {
reverseAPIKeys.append("ignoreTrailingBytes");
}
if ((m_settings.m_txUDPAddress != settings.m_txUDPAddress) || force) {
reverseAPIKeys.append("txUDPAddress");
}
if ((m_settings.m_txUDPPort != settings.m_txUDPPort) || force) {
reverseAPIKeys.append("txUDPPort");
}
if ((m_settings.m_rxUDPAddress != settings.m_rxUDPAddress) || force) {
reverseAPIKeys.append("rxUDPAddress");
}
if ((m_settings.m_rxUDPPort != settings.m_rxUDPPort) || force) {
reverseAPIKeys.append("rxUDPPort");
}
if ((m_settings.m_title != settings.m_title) || force) {
reverseAPIKeys.append("title");
}
if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) {
reverseAPIKeys.append("rgbColor");
}
PERTesterWorker::MsgConfigurePERTesterWorker *msg = PERTesterWorker::MsgConfigurePERTesterWorker::create(
settings, force
);
m_worker->getInputMessageQueue()->push(msg);
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIFeatureSetIndex != settings.m_reverseAPIFeatureSetIndex) ||
(m_settings.m_reverseAPIFeatureIndex != settings.m_reverseAPIFeatureIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
m_settings = settings;
}
int PERTester::webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
getFeatureStateStr(*response.getState());
MsgStartStop *msg = MsgStartStop::create(run);
getInputMessageQueue()->push(msg);
return 202;
}
int PERTester::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setPerTesterSettings(new SWGSDRangel::SWGPERTesterSettings());
response.getPerTesterSettings()->init();
webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int PERTester::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
PERTesterSettings settings = m_settings;
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
MsgConfigurePERTester *msg = MsgConfigurePERTester::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigurePERTester *msgToGUI = MsgConfigurePERTester::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatFeatureSettings(response, settings);
return 200;
}
static QList<QString *> *convertStringListToPtrs(QStringList listIn)
{
QList<QString *> *listOut = new QList<QString *>();
for (int i = 0; i < listIn.size(); i++)
listOut->append(new QString(listIn[i]));
return listOut;
}
static QStringList convertPtrsToStringList(QList<QString *> *listIn)
{
QStringList listOut;
for (int i = 0; i < listIn->size(); i++)
listOut.append(*listIn->at(i));
return listOut;
}
void PERTester::webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const PERTesterSettings& settings)
{
response.getPerTesterSettings()->setPacketCount(settings.m_packetCount);
response.getPerTesterSettings()->setInterval(settings.m_interval);
response.getPerTesterSettings()->setStart((int)settings.m_start);
response.getPerTesterSettings()->setSatellites(convertStringListToPtrs(settings.m_satellites));
response.getPerTesterSettings()->setPacket(new QString(settings.m_packet));
response.getPerTesterSettings()->setIgnoreLeadingBytes(settings.m_ignoreLeadingBytes);
response.getPerTesterSettings()->setIgnoreTrailingBytes(settings.m_ignoreTrailingBytes);
response.getPerTesterSettings()->setTxUdpAddress(new QString(settings.m_txUDPAddress));
response.getPerTesterSettings()->setTxUdpPort(settings.m_txUDPPort);
response.getPerTesterSettings()->setRxUdpAddress(new QString(settings.m_rxUDPAddress));
response.getPerTesterSettings()->setRxUdpPort(settings.m_rxUDPPort);
if (response.getPerTesterSettings()->getTitle()) {
*response.getPerTesterSettings()->getTitle() = settings.m_title;
} else {
response.getPerTesterSettings()->setTitle(new QString(settings.m_title));
}
response.getPerTesterSettings()->setRgbColor(settings.m_rgbColor);
response.getPerTesterSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getPerTesterSettings()->getReverseApiAddress()) {
*response.getPerTesterSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getPerTesterSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getPerTesterSettings()->setReverseApiPort(settings.m_reverseAPIPort);
}
void PERTester::webapiUpdateFeatureSettings(
PERTesterSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response)
{
if (featureSettingsKeys.contains("packetCount")) {
settings.m_packetCount = response.getPerTesterSettings()->getPacketCount();
}
if (featureSettingsKeys.contains("interval")) {
settings.m_interval = response.getPerTesterSettings()->getInterval();
}
if (featureSettingsKeys.contains("start")) {
settings.m_start = (PERTesterSettings::Start)response.getPerTesterSettings()->getStart();
}
if (featureSettingsKeys.contains("satellites")) {
settings.m_satellites = convertPtrsToStringList(response.getPerTesterSettings()->getSatellites());
}
if (featureSettingsKeys.contains("packet")) {
settings.m_packet = *response.getPerTesterSettings()->getPacket();
}
if (featureSettingsKeys.contains("ignoreLeadingBytes")) {
settings.m_ignoreLeadingBytes = response.getPerTesterSettings()->getIgnoreLeadingBytes();
}
if (featureSettingsKeys.contains("ignoreTrailingBytes")) {
settings.m_ignoreTrailingBytes = response.getPerTesterSettings()->getIgnoreTrailingBytes();
}
if (featureSettingsKeys.contains("txUDPAddress")) {
settings.m_txUDPAddress = *response.getPerTesterSettings()->getTxUdpAddress();
}
if (featureSettingsKeys.contains("txUDPPort")) {
settings.m_txUDPPort = response.getPerTesterSettings()->getTxUdpPort();
}
if (featureSettingsKeys.contains("rxUDPAddress")) {
settings.m_txUDPAddress = *response.getPerTesterSettings()->getRxUdpAddress();
}
if (featureSettingsKeys.contains("rxUDPPort")) {
settings.m_rxUDPPort = response.getPerTesterSettings()->getRxUdpPort();
}
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getPerTesterSettings()->getTitle();
}
if (featureSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getPerTesterSettings()->getRgbColor();
}
if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getPerTesterSettings()->getUseReverseApi() != 0;
}
if (featureSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getPerTesterSettings()->getReverseApiAddress();
}
if (featureSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getPerTesterSettings()->getReverseApiPort();
}
}
void PERTester::webapiReverseSendSettings(QList<QString>& featureSettingsKeys, const PERTesterSettings& settings, bool force)
{
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
swgFeatureSettings->setFeatureType(new QString("PERTester"));
swgFeatureSettings->setPerTesterSettings(new SWGSDRangel::SWGPERTesterSettings());
SWGSDRangel::SWGPERTesterSettings *swgPERTesterSettings = swgFeatureSettings->getPerTesterSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (featureSettingsKeys.contains("packetCount") || force) {
swgPERTesterSettings->setPacketCount(settings.m_packetCount);
}
if (featureSettingsKeys.contains("interval") || force) {
swgPERTesterSettings->setInterval(settings.m_interval);
}
if (featureSettingsKeys.contains("start") || force) {
swgPERTesterSettings->setStart((int)settings.m_start);
}
if (featureSettingsKeys.contains("satellites") || force) {
swgPERTesterSettings->setSatellites(convertStringListToPtrs(settings.m_satellites));
}
if (featureSettingsKeys.contains("packet") || force) {
swgPERTesterSettings->setPacket(new QString(settings.m_packet));
}
if (featureSettingsKeys.contains("ignoreLeadingBytes") || force) {
swgPERTesterSettings->setIgnoreLeadingBytes(settings.m_ignoreLeadingBytes);
}
if (featureSettingsKeys.contains("ignoreTrailingBytes") || force) {
swgPERTesterSettings->setIgnoreTrailingBytes(settings.m_ignoreTrailingBytes);
}
if (featureSettingsKeys.contains("txUDPAddress") || force) {
swgPERTesterSettings->setTxUdpAddress(new QString(settings.m_txUDPAddress));
}
if (featureSettingsKeys.contains("txUDPPort") || force) {
swgPERTesterSettings->setTxUdpPort(settings.m_txUDPPort);
}
if (featureSettingsKeys.contains("rxUDPAddress") || force) {
swgPERTesterSettings->setRxUdpAddress(new QString(settings.m_rxUDPAddress));
}
if (featureSettingsKeys.contains("rxUDPPort") || force) {
swgPERTesterSettings->setRxUdpPort(settings.m_rxUDPPort);
}
if (featureSettingsKeys.contains("title") || force) {
swgPERTesterSettings->setTitle(new QString(settings.m_title));
}
if (featureSettingsKeys.contains("rgbColor") || force) {
swgPERTesterSettings->setRgbColor(settings.m_rgbColor);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIFeatureSetIndex)
.arg(settings.m_reverseAPIFeatureIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgFeatureSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgFeatureSettings;
}
int PERTester::webapiActionsPost(
const QStringList& featureActionsKeys,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage)
{
SWGSDRangel::SWGPERTesterActions *swgPERTesterActions = query.getPerTesterActions();
if (swgPERTesterActions)
{
if (featureActionsKeys.contains("aos"))
{
SWGSDRangel::SWGPERTesterActions_aos* aos = swgPERTesterActions->getAos();
QString *satelliteName = aos->getSatelliteName();
if (satelliteName != nullptr)
{
if (m_settings.m_satellites.contains(*satelliteName))
{
if (m_settings.m_start == PERTesterSettings::START_ON_AOS)
{
bool ok = m_worker->startWork();
m_state = ok ? StRunning : StError;
}
else if (m_settings.m_start == PERTesterSettings::START_ON_MID_PASS)
{
QString aosTimeString = *aos->getAosTime();
QString losTimeString = *aos->getLosTime();
QDateTime aosTime = QDateTime::fromString(aosTimeString);
QDateTime losTime = QDateTime::fromString(losTimeString);
qint64 msecs = aosTime.msecsTo(losTime) / 2;
QTimer::singleShot(msecs, [this] {
bool ok = m_worker->startWork();
m_state = ok ? StRunning : StError;
});
}
}
return 202;
}
else
{
errorMessage = "Missing satellite name";
return 400;
}
}
else
{
errorMessage = "Unknown action";
return 400;
}
}
else
{
errorMessage = "Missing PERTesterActions in query";
return 400;
}
}
void PERTester::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "PERTester::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("PERTester::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

View File

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_FEATURE_PERTESTER_H_
#define INCLUDE_FEATURE_PERTESTER_H_
#include <QThread>
#include <QNetworkRequest>
#include <QTimer>
#include "feature/feature.h"
#include "util/message.h"
#include "pertestersettings.h"
class WebAPIAdapterInterface;
class PERTesterWorker;
class QNetworkAccessManager;
class QNetworkReply;
namespace SWGSDRangel {
class SWGDeviceState;
}
class PERTester : public Feature
{
Q_OBJECT
public:
class MsgConfigurePERTester : public Message {
MESSAGE_CLASS_DECLARATION
public:
const PERTesterSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigurePERTester* create(const PERTesterSettings& settings, bool force) {
return new MsgConfigurePERTester(settings, force);
}
private:
PERTesterSettings m_settings;
bool m_force;
MsgConfigurePERTester(const PERTesterSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgStartStop* create(bool startStop) {
return new MsgStartStop(startStop);
}
protected:
bool m_startStop;
MsgStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
class MsgResetStats : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgResetStats* create() {
return new MsgResetStats();
}
protected:
MsgResetStats() :
Message()
{ }
};
class MsgReportWorker : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getMessage() { return m_message; }
static MsgReportWorker* create(QString message) {
return new MsgReportWorker(message);
}
private:
QString m_message;
MsgReportWorker(QString message) :
Message(),
m_message(message)
{}
};
PERTester(WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~PERTester();
virtual void destroy() { delete this; }
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) const { id = objectName(); }
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiActionsPost(
const QStringList& featureActionsKeys,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage);
static void webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const PERTesterSettings& settings);
static void webapiUpdateFeatureSettings(
PERTesterSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response);
static const char* const m_featureIdURI;
static const char* const m_featureId;
private:
QThread m_thread;
PERTesterWorker *m_worker;
PERTesterSettings m_settings;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void start();
void stop();
void applySettings(const PERTesterSettings& settings, bool force = false);
void webapiReverseSendSettings(QList<QString>& featureSettingsKeys, const PERTesterSettings& settings, bool force);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_FEATURE_PERTESTER_H_

View File

@ -0,0 +1,349 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 <cmath>
#include <QMessageBox>
#include <QLineEdit>
#include "feature/featureuiset.h"
#include "gui/basicfeaturesettingsdialog.h"
#include "mainwindow.h"
#include "device/deviceuiset.h"
#include "ui_pertestergui.h"
#include "pertester.h"
#include "pertestergui.h"
#include "pertesterreport.h"
PERTesterGUI* PERTesterGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
{
PERTesterGUI* gui = new PERTesterGUI(pluginAPI, featureUISet, feature);
return gui;
}
void PERTesterGUI::destroy()
{
delete this;
}
void PERTesterGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray PERTesterGUI::serialize() const
{
return m_settings.serialize();
}
bool PERTesterGUI::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
displaySettings();
applySettings(true);
return true;
}
else
{
resetToDefaults();
return false;
}
}
bool PERTesterGUI::handleMessage(const Message& message)
{
if (PERTester::MsgConfigurePERTester::match(message))
{
qDebug("PERTesterGUI::handleMessage: PERTester::MsgConfigurePERTester");
const PERTester::MsgConfigurePERTester& cfg = (PERTester::MsgConfigurePERTester&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (PERTesterReport::MsgReportStats::match(message))
{
PERTesterReport::MsgReportStats& stats = (PERTesterReport::MsgReportStats&) message;
int tx = stats.getTx();
int rxMatched = stats.getRxMatched();
double per = tx == 0 ? 0.0 : 100.0 - (rxMatched/(double)tx)*100.0;
ui->transmittedText->setText(QString("%1").arg(tx));
ui->receivedMatchedText->setText(QString("%1").arg(rxMatched));
ui->receivedUnmatchedText->setText(QString("%1").arg(stats.getRxUnmatched()));
ui->perText->setText(QString("%1%").arg(per, 0, 'f', 1));
return true;
}
return false;
}
void PERTesterGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()))
{
if (handleMessage(*message)) {
delete message;
}
}
}
void PERTesterGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
FeatureGUI(parent),
ui(new Ui::PERTesterGUI),
m_pluginAPI(pluginAPI),
m_featureUISet(featureUISet),
m_doApplySettings(true),
m_lastFeatureState(0)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
setChannelWidget(false);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
m_perTester = reinterpret_cast<PERTester*>(feature);
m_perTester->setMessageQueueToGUI(&m_inputMessageQueue);
m_featureUISet->addRollupWidget(this);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
displaySettings();
applySettings(true);
}
PERTesterGUI::~PERTesterGUI()
{
delete ui;
}
void PERTesterGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void PERTesterGUI::displaySettings()
{
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_settings.m_title);
blockApplySettings(true);
ui->packetCount->setValue(m_settings.m_packetCount);
ui->start->setCurrentIndex((int)m_settings.m_start);
ui->satellites->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY);
ui->satellitesLabel->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY);
ui->satellites->setText(m_settings.m_satellites.join(" "));
ui->interval->setValue(m_settings.m_interval);
ui->packet->setPlainText(m_settings.m_packet);
ui->leading->setValue(m_settings.m_ignoreLeadingBytes);
ui->trailing->setValue(m_settings.m_ignoreTrailingBytes);
ui->txUDPAddress->setText(m_settings.m_txUDPAddress);
ui->txUDPPort->setText(QString::number(m_settings.m_txUDPPort));
ui->rxUDPAddress->setText(m_settings.m_rxUDPAddress);
ui->rxUDPPort->setText(QString::number(m_settings.m_rxUDPPort));
blockApplySettings(false);
arrangeRollups();
}
void PERTesterGUI::leaveEvent(QEvent*)
{
}
void PERTesterGUI::enterEvent(QEvent*)
{
}
void PERTesterGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicFeatureSettingsDialog dialog(this);
dialog.setTitle(m_settings.m_title);
dialog.setColor(m_settings.m_rgbColor);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
dialog.move(p);
dialog.exec();
m_settings.m_rgbColor = dialog.getColor().rgb();
m_settings.m_title = dialog.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
resetContextMenuType();
}
void PERTesterGUI::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
PERTester::MsgStartStop *message = PERTester::MsgStartStop::create(checked);
m_perTester->getInputMessageQueue()->push(message);
}
}
void PERTesterGUI::on_resetStats_clicked()
{
if (m_doApplySettings)
{
PERTester::MsgResetStats *message = PERTester::MsgResetStats::create();
m_perTester->getInputMessageQueue()->push(message);
}
}
void PERTesterGUI::on_packetCount_valueChanged(int value)
{
m_settings.m_packetCount = value;
applySettings();
}
void PERTesterGUI::on_start_currentIndexChanged(int index)
{
m_settings.m_start = (PERTesterSettings::Start)index;
ui->satellites->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY);
ui->satellitesLabel->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY);
applySettings();
arrangeRollups();
}
void PERTesterGUI::on_satellites_editingFinished()
{
m_settings.m_satellites = ui->satellites->text().trimmed().split(" ");
applySettings();
}
void PERTesterGUI::on_interval_valueChanged(double value)
{
m_settings.m_interval = value;
applySettings();
}
void PERTesterGUI::on_packet_textChanged()
{
m_settings.m_packet = ui->packet->toPlainText();
applySettings();
}
void PERTesterGUI::on_leading_valueChanged(int value)
{
m_settings.m_ignoreLeadingBytes = value;
applySettings();
}
void PERTesterGUI::on_trailing_valueChanged(int value)
{
m_settings.m_ignoreTrailingBytes = value;
applySettings();
}
void PERTesterGUI::on_txUDPAddress_editingFinished()
{
m_settings.m_txUDPAddress = ui->txUDPAddress->text();
applySettings();
}
void PERTesterGUI::on_txUDPPort_editingFinished()
{
m_settings.m_txUDPPort = ui->txUDPPort->text().toInt();
applySettings();
}
void PERTesterGUI::on_rxUDPAddress_editingFinished()
{
m_settings.m_rxUDPAddress = ui->rxUDPAddress->text();
applySettings();
}
void PERTesterGUI::on_rxUDPPort_editingFinished()
{
m_settings.m_rxUDPPort = ui->rxUDPPort->text().toInt();
applySettings();
}
void PERTesterGUI::updateStatus()
{
int state = m_perTester->getState();
if (m_lastFeatureState != state)
{
// We set checked state of start/stop button, in case it was changed via API
bool oldState;
switch (state)
{
case Feature::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case Feature::StIdle:
oldState = ui->startStop->blockSignals(true);
ui->startStop->setChecked(false);
ui->startStop->blockSignals(oldState);
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case Feature::StRunning:
oldState = ui->startStop->blockSignals(true);
ui->startStop->setChecked(true);
ui->startStop->blockSignals(oldState);
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case Feature::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_perTester->getErrorMessage());
break;
default:
break;
}
m_lastFeatureState = state;
}
}
void PERTesterGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
PERTester::MsgConfigurePERTester* message = PERTester::MsgConfigurePERTester::create(m_settings, force);
m_perTester->getInputMessageQueue()->push(message);
}
}

View File

@ -0,0 +1,91 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_FEATURE_PERTESTERGUI_H_
#define INCLUDE_FEATURE_PERTESTERGUI_H_
#include <QTimer>
#include "feature/featuregui.h"
#include "util/messagequeue.h"
#include "pipes/pipeendpoint.h"
#include "pertestersettings.h"
class PluginAPI;
class FeatureUISet;
class PERTester;
namespace Ui {
class PERTesterGUI;
}
class PERTesterGUI : public FeatureGUI {
Q_OBJECT
public:
static PERTesterGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
private:
Ui::PERTesterGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
PERTesterSettings m_settings;
bool m_doApplySettings;
PERTester* m_perTester;
MessageQueue m_inputMessageQueue;
QTimer m_statusTimer;
int m_lastFeatureState;
explicit PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~PERTesterGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
bool handleMessage(const Message& message);
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_resetStats_clicked();
void on_packetCount_valueChanged(int value);
void on_start_currentIndexChanged(int index);
void on_satellites_editingFinished();
void on_interval_valueChanged(double value);
void on_packet_textChanged();
void on_leading_valueChanged(int value);
void on_trailing_valueChanged(int value);
void on_txUDPAddress_editingFinished();
void on_txUDPPort_editingFinished();
void on_rxUDPAddress_editingFinished();
void on_rxUDPPort_editingFinished();
void updateStatus();
};
#endif // INCLUDE_FEATURE_PERTESTERGUI_H_

View File

@ -0,0 +1,576 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PERTesterGUI</class>
<widget class="RollupWidget" name="PERTesterGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>430</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>350</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>GS-232 Rotator Controller</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>331</width>
<height>291</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="controlLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>Start/stop PER Test</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="resetStats">
<property name="toolTip">
<string>Reset statistics</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="buttonsHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="startLabel">
<property name="text">
<string>Start test</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="packetCount">
<property name="toolTip">
<string>Number of packets to transmit</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="start">
<property name="toolTip">
<string>When to start the test, after the start button is pressed</string>
</property>
<item>
<property name="text">
<string>Immediately</string>
</property>
</item>
<item>
<property name="text">
<string>On satellite AOS</string>
</property>
</item>
<item>
<property name="text">
<string>On satellite mid pass</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="packetCountLabel">
<property name="text">
<string>Packets</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="interval">
<property name="toolTip">
<string>Interval in seconds between packets</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="intervalLabel">
<property name="text">
<string>Interval (s)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="satellitesLabel">
<property name="text">
<string>Satellites</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="satellites">
<property name="toolTip">
<string>Space separated list of satellite names to start the PER test on</string>
</property>
<property name="text">
<string>ISS</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="packetLabel">
<property name="text">
<string>Packet</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="packet">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>80</height>
</size>
</property>
<property name="toolTip">
<string>Hex encoded packet to transmit.
Substitutions:
%{ax25.src=CALLSIGN}
%{ax25.dst=CALLSIGN}
%{num}
%{data=min,max}</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ignoreLayout">
<item>
<widget class="QLabel" name="ignoreLabel">
<property name="text">
<string>Ignore</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="leadingLabel">
<property name="text">
<string>Leading</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="leading">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="trailingLabel">
<property name="text">
<string>Trailing</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="trailing">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bytesLabel">
<property name="text">
<string>bytes</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="txLayout">
<item>
<widget class="QLabel" name="txUDPLabel">
<property name="text">
<string>TX UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txUDPAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP address</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="txUDPSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txUDPPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP port</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9997</string>
</property>
</widget>
</item>
<item>
<spacer name="txHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="channelLayout">
<item>
<widget class="QLabel" name="rxUDPLabel">
<property name="text">
<string>RX UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rxUDPAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP address</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rxUDPSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rxUDPPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP port</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9998</string>
</property>
</widget>
</item>
<item>
<spacer name="rxHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="statsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>310</y>
<width>331</width>
<height>91</height>
</rect>
</property>
<property name="windowTitle">
<string>Statistics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<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="QGridLayout" name="statsLayout">
<item row="2" column="1">
<widget class="QLabel" name="receivedMatchedText">
<property name="toolTip">
<string>Number of packets received that match a transmitted packet</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="receivedMatchedLabel">
<property name="text">
<string>Received and Matched</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="perLabel">
<property name="text">
<string>PER</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="perText">
<property name="text">
<string>0%</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="transmittedText">
<property name="toolTip">
<string>Number of packets that have been transmitted</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="transmittedLabel">
<property name="text">
<string>Transmitted</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="receivedUnmatchedLabel">
<property name="text">
<string>Received and Unmatched</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="receivedUnmatchedText">
<property name="toolTip">
<string>Number of packets received that do not match a transmitted packet</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>startStop</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,576 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PERTesterGUI</class>
<widget class="RollupWidget" name="PERTesterGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>430</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>350</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>GS-232 Rotator Controller</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>331</width>
<height>291</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="controlLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>Start/stop PER Test</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="resetStats">
<property name="toolTip">
<string>Reset statistics</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="buttonsHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="startLabel">
<property name="text">
<string>Start test</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="packetCount">
<property name="toolTip">
<string>Number of packets to transmit</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="start">
<property name="toolTip">
<string>When to start the test, after the start button is pressed</string>
</property>
<item>
<property name="text">
<string>Immediately</string>
</property>
</item>
<item>
<property name="text">
<string>On satellite AOS</string>
</property>
</item>
<item>
<property name="text">
<string>On satellite mid pass</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="packetCountLabel">
<property name="text">
<string>Packets</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="interval">
<property name="toolTip">
<string>Interval in seconds between packets</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="intervalLabel">
<property name="text">
<string>Interval (s)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="satellitesLabel">
<property name="text">
<string>Satellites</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="satellites">
<property name="toolTip">
<string>Space separated list of satellite names to start the PER test on</string>
</property>
<property name="text">
<string>ISS</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="packetLabel">
<property name="text">
<string>Packet</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="packet">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Hex encoded packet to transmit.
Substitutions:
%{ax25.src=CALLSIGN}
%{ax25.dst=CALLSIGN}
%{num}
%{data=min,max}</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ignoreLayout">
<item>
<widget class="QLabel" name="ignoreLabel">
<property name="text">
<string>Ignore</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="leadingLabel">
<property name="text">
<string>Leading</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="leading">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="trailingLabel">
<property name="text">
<string>Trailing</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="trailing">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bytesLabel">
<property name="text">
<string>bytes</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="txLayout">
<item>
<widget class="QLabel" name="txUDPLabel">
<property name="text">
<string>TX UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txUDPAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP address</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="txUDPSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txUDPPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP port</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9997</string>
</property>
</widget>
</item>
<item>
<spacer name="txHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="channelLayout">
<item>
<widget class="QLabel" name="rxUDPLabel">
<property name="text">
<string>RX UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rxUDPAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP address</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rxUDPSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rxUDPPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP port</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9998</string>
</property>
</widget>
</item>
<item>
<spacer name="rxHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="statsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>310</y>
<width>331</width>
<height>91</height>
</rect>
</property>
<property name="windowTitle">
<string>Statistics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<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="QGridLayout" name="statsLayout">
<item row="2" column="1">
<widget class="QLabel" name="receivedMatchedText">
<property name="toolTip">
<string>Number of packets received that match a transmitted packet</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="receivedMatchedLabel">
<property name="text">
<string>Received and Matched</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="perLabel">
<property name="text">
<string>PER</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="perText">
<property name="text">
<string>0%</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="transmittedText">
<property name="toolTip">
<string>Number of packets that have been transmitted</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="transmittedLabel">
<property name="text">
<string>Transmitted</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="receivedUnmatchedLabel">
<property name="text">
<string>Received and Unmatched</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="receivedUnmatchedText">
<property name="toolTip">
<string>Number of packets received that do not match a transmitted packet</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>startStop</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,80 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "pertestergui.h"
#endif
#include "pertester.h"
#include "pertesterplugin.h"
#include "pertesterwebapiadapter.h"
const PluginDescriptor PERTesterPlugin::m_pluginDescriptor = {
PERTester::m_featureId,
QStringLiteral("Packet Error Rate Tester"),
QStringLiteral("6.7.1"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
PERTesterPlugin::PERTesterPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& PERTesterPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void PERTesterPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerFeature(PERTester::m_featureIdURI, PERTester::m_featureId, this);
}
#ifdef SERVER_MODE
FeatureGUI* PERTesterPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
(void) featureUISet;
(void) feature;
return nullptr;
}
#else
FeatureGUI* PERTesterPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return PERTesterGUI::create(m_pluginAPI, featureUISet, feature);
}
#endif
Feature* PERTesterPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
{
return new PERTester(webAPIAdapterInterface);
}
FeatureWebAPIAdapter* PERTesterPlugin::createFeatureWebAPIAdapter() const
{
return new PERTesterWebAPIAdapter();
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_FEATURE_PERTESTERPLUGIN_H
#define INCLUDE_FEATURE_PERTESTERPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FeatureGUI;
class WebAPIAdapterInterface;
class PERTesterPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.feature.pertester")
public:
explicit PERTesterPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FEATURE_PERTESTERPLUGIN_H

View File

@ -0,0 +1,63 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_FEATURE_PERTESTERREPORT_H_
#define INCLUDE_FEATURE_PERTESTERREPORT_H_
#include <QObject>
#include "util/message.h"
class PERTesterReport : public QObject
{
Q_OBJECT
public:
class MsgReportStats : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTx() const { return m_tx; }
int getRxMatched() const { return m_rxMatched; }
int getRxUnmatched() const { return m_rxUnmatched; }
static MsgReportStats* create(int tx, int rxMatched, int rxUnmatched)
{
return new MsgReportStats(tx, rxMatched, rxUnmatched);
}
private:
int m_tx;
int m_rxMatched;
int m_rxUnmatched;
MsgReportStats(int tx, int rxMatched, int rxUnmatched) :
Message(),
m_tx(tx),
m_rxMatched(rxMatched),
m_rxUnmatched(rxUnmatched)
{
}
};
public:
PERTesterReport() {}
~PERTesterReport() {}
};
#endif // INCLUDE_FEATURE_PERTESTERREPORT_H_

View File

@ -0,0 +1,159 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 <QColor>
#include <QDataStream>
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "pertestersettings.h"
PERTesterSettings::PERTesterSettings()
{
resetToDefaults();
}
void PERTesterSettings::resetToDefaults()
{
m_packetCount = 10;
m_interval = 1.0f;
m_packet = "%{ax25.dst=MYCALL} %{ax25.src=MYCALL} 03 f0 %{num} %{data=0,100}";
m_txUDPAddress = "127.0.0.1";
m_txUDPPort = 9998;
m_rxUDPAddress = "127.0.0.1";
m_rxUDPPort = 9999;
m_ignoreLeadingBytes = 0;
m_ignoreTrailingBytes = 2; // Ignore CRC
m_start = START_IMMEDIATELY;
m_satellites = {QString("ISS")};
m_title = "Packet Error Rate Tester";
m_rgbColor = QColor(225, 25, 99).rgb();
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIFeatureSetIndex = 0;
m_reverseAPIFeatureIndex = 0;
}
QByteArray PERTesterSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_packetCount);
s.writeFloat(2, m_interval);
s.writeString(3, m_txUDPAddress);
s.writeU32(4, m_txUDPPort);
s.writeString(5, m_rxUDPAddress);
s.writeU32(6, m_rxUDPPort);
s.writeS32(7, m_ignoreLeadingBytes);
s.writeS32(8, m_ignoreTrailingBytes);
s.writeS32(9, (int)m_start);
s.writeBlob(10, serializeStringList(m_satellites));
s.writeString(20, m_title);
s.writeU32(21, m_rgbColor);
s.writeBool(22, m_useReverseAPI);
s.writeString(23, m_reverseAPIAddress);
s.writeU32(24, m_reverseAPIPort);
s.writeU32(25, m_reverseAPIFeatureSetIndex);
s.writeU32(26, m_reverseAPIFeatureIndex);
return s.final();
}
bool PERTesterSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
QByteArray blob;
d.readS32(1, &m_packetCount);
d.readFloat(2, &m_interval, 1.0f);
d.readString(3, &m_txUDPAddress);
d.readU32(4, &utmp);
if ((utmp > 1023) && (utmp < 65535)) {
m_txUDPPort = utmp;
} else {
m_txUDPPort = 8888;
}
d.readString(5, &m_rxUDPAddress);
d.readU32(6, &utmp);
if ((utmp > 1023) && (utmp < 65535)) {
m_rxUDPPort = utmp;
} else {
m_rxUDPPort = 8888;
}
d.readS32(7, &m_ignoreLeadingBytes, 0);
d.readS32(8, &m_ignoreTrailingBytes, 2);
d.readS32(9, (int*)&m_start, (int)START_IMMEDIATELY);
d.readBlob(10, &blob);
deserializeStringList(blob, m_satellites);
d.readString(20, &m_title, "Packet Error Rate Tester");
d.readU32(21, &m_rgbColor, QColor(225, 25, 99).rgb());
d.readBool(22, &m_useReverseAPI, false);
d.readString(23, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(24, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(25, &utmp, 0);
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
d.readU32(26, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
return true;
}
else
{
resetToDefaults();
return false;
}
}
QByteArray PERTesterSettings::serializeStringList(const QList<QString>& strings) const
{
QByteArray data;
QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly);
(*stream) << strings;
delete stream;
return data;
}
void PERTesterSettings::deserializeStringList(const QByteArray& data, QList<QString>& strings)
{
QDataStream *stream = new QDataStream(data);
(*stream) >> strings;
delete stream;
}

View File

@ -0,0 +1,60 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_FEATURE_PERTESTERSETTINGS_H_
#define INCLUDE_FEATURE_PERTESTERSETTINGS_H_
#include <QByteArray>
#include <QString>
#include "util/message.h"
class Serializable;
struct PERTesterSettings
{
int m_packetCount; //!< How many packets to send
float m_interval; //!< Interval between packets in seconds
QString m_packet; //!< Contents of the packet
QString m_txUDPAddress; //!< UDP port of modulator to send packets too
uint16_t m_txUDPPort;
QString m_rxUDPAddress; //!< UDP port to receive packets from demodulator on
uint16_t m_rxUDPPort;
int m_ignoreLeadingBytes; //!< Number of bytes to ignore at start of received frame (E.g. header)
int m_ignoreTrailingBytes; //!< Number of bytes to ignore at end of received frame (E.g. CRC)
enum Start {START_IMMEDIATELY, START_ON_AOS, START_ON_MID_PASS} m_start; //!< When to start test
QList<QString> m_satellites; //!< Which satellites to start test on
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIFeatureSetIndex;
uint16_t m_reverseAPIFeatureIndex;
PERTesterSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
QByteArray serializeStringList(const QList<QString>& strings) const;
void deserializeStringList(const QByteArray& data, QList<QString>& strings);
};
#endif // INCLUDE_FEATURE_PERTESTERSETTINGS_H_

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 "SWGFeatureSettings.h"
#include "pertester.h"
#include "pertesterwebapiadapter.h"
PERTesterWebAPIAdapter::PERTesterWebAPIAdapter()
{}
PERTesterWebAPIAdapter::~PERTesterWebAPIAdapter()
{}
int PERTesterWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setPerTesterSettings(new SWGSDRangel::SWGPERTesterSettings());
response.getPerTesterSettings()->init();
PERTester::webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int PERTesterWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
PERTester::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_PERTESTER_WEBAPIADAPTER_H
#define INCLUDE_PERTESTER_WEBAPIADAPTER_H
#include "feature/featurewebapiadapter.h"
#include "pertestersettings.h"
/**
* Standalone API adapter only for the settings
*/
class PERTesterWebAPIAdapter : public FeatureWebAPIAdapter {
public:
PERTesterWebAPIAdapter();
virtual ~PERTesterWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
private:
PERTesterSettings m_settings;
};
#endif // INCLUDE_PERTESTER_WEBAPIADAPTER_H

View File

@ -0,0 +1,321 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 <algorithm>
#include <random>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkDatagram>
#include <QEventLoop>
#include <QTimer>
#include <QRegExp>
#include "util/ax25.h"
#include "pertester.h"
#include "pertesterworker.h"
#include "pertesterreport.h"
MESSAGE_CLASS_DEFINITION(PERTesterWorker::MsgConfigurePERTesterWorker, Message)
MESSAGE_CLASS_DEFINITION(PERTesterReport::MsgReportStats, Message)
PERTesterWorker::PERTesterWorker() :
m_msgQueueToFeature(nullptr),
m_msgQueueToGUI(nullptr),
m_running(false),
m_mutex(QMutex::Recursive),
m_rxUDPSocket(nullptr),
m_tx(0),
m_rxMatched(0),
m_rxUnmatched(0)
{
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
PERTesterWorker::~PERTesterWorker()
{
closeUDP();
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_inputMessageQueue.clear();
}
void PERTesterWorker::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
}
bool PERTesterWorker::startWork()
{
qDebug() << "PERTesterWorker::startWork";
QMutexLocker mutexLocker(&m_mutex);
openUDP(m_settings);
// Automatically restart if previous run had finished, otherwise continue
if (m_tx >= m_settings.m_packetCount)
resetStats();
connect(&m_txTimer, SIGNAL(timeout()), this, SLOT(tx()));
m_txTimer.start(m_settings.m_interval * 1000.0);
m_running = true;
return m_running;
}
void PERTesterWorker::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
m_txTimer.stop();
closeUDP();
disconnect(&m_txTimer, SIGNAL(timeout()), this, SLOT(tx()));
m_running = false;
}
void PERTesterWorker::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
void PERTesterWorker::resetStats()
{
m_tx = 0;
m_rxMatched = 0;
m_rxUnmatched = 0;
if (getMessageQueueToGUI())
getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
}
bool PERTesterWorker::handleMessage(const Message& cmd)
{
if (MsgConfigurePERTesterWorker::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigurePERTesterWorker& cfg = (MsgConfigurePERTesterWorker&) cmd;
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (PERTester::MsgResetStats::match(cmd))
{
resetStats();
return true;
}
else
{
return false;
}
}
void PERTesterWorker::applySettings(const PERTesterSettings& settings, bool force)
{
qDebug() << "PERTesterWorker::applySettings:"
<< " force: " << force;
if ( (settings.m_rxUDPAddress != m_settings.m_rxUDPAddress)
|| (settings.m_rxUDPPort != m_settings.m_rxUDPPort)
|| force)
{
openUDP(settings);
}
if ((settings.m_interval != m_settings.m_interval) || force)
m_txTimer.setInterval(settings.m_interval * 1000.0);
m_settings = settings;
}
void PERTesterWorker::openUDP(const PERTesterSettings& settings)
{
closeUDP();
m_rxUDPSocket = new QUdpSocket();
if (!m_rxUDPSocket->bind(QHostAddress(settings.m_rxUDPAddress), settings.m_rxUDPPort))
{
qCritical() << "PERTesterWorker::openUDP: Failed to bind to port " << settings.m_rxUDPAddress << ":" << settings.m_rxUDPPort << ". Error: " << m_rxUDPSocket->error();
if (m_msgQueueToFeature)
m_msgQueueToFeature->push(PERTester::MsgReportWorker::create(QString("Failed to bind to port %1:%2 - %3").arg(settings.m_rxUDPAddress).arg(settings.m_rxUDPPort).arg(m_rxUDPSocket->error())));
}
else
qDebug() << "PERTesterWorker::openUDP: Listening on port " << settings.m_rxUDPAddress << ":" << settings.m_rxUDPPort << ".";
connect(m_rxUDPSocket, &QUdpSocket::readyRead, this, &PERTesterWorker::rx);
}
void PERTesterWorker::closeUDP()
{
if (m_rxUDPSocket != nullptr)
{
qDebug() << "PERTesterWorker::closeUDP: Closing port " << m_settings.m_rxUDPAddress << ":" << m_settings.m_rxUDPPort << ".";
disconnect(m_rxUDPSocket, &QUdpSocket::readyRead, this, &PERTesterWorker::rx);
delete m_rxUDPSocket;
m_rxUDPSocket = nullptr;
}
}
void PERTesterWorker::rx()
{
while (m_rxUDPSocket->hasPendingDatagrams())
{
QNetworkDatagram datagram = m_rxUDPSocket->receiveDatagram();
QByteArray packet = datagram.data();
// Ignore header and CRC, if requested
packet = packet.mid(m_settings.m_ignoreLeadingBytes, packet.size() - m_settings.m_ignoreLeadingBytes - m_settings.m_ignoreTrailingBytes);
// Remove from list of transmitted packets
int i;
for (i = 0; i < m_txPackets.size(); i++)
{
if (packet == m_txPackets[i])
{
m_rxMatched++;
m_txPackets.removeAt(i);
break;
}
}
if (i == m_txPackets.size())
{
qDebug() << "PERTesterWorker::rx: Received packet that was not transmitted: " << packet.toHex();
m_rxUnmatched++;
}
}
if (getMessageQueueToGUI())
getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
}
void PERTesterWorker::tx()
{
QRegExp ax25Dst("^%\\{ax25\\.dst=([A-Za-z0-9-]+)\\}");
QRegExp ax25Src("^%\\{ax25\\.src=([A-Za-z0-9-]+)\\}");
QRegExp num("^%\\{num\\}");
QRegExp data("^%\\{data=([0-9]+),([0-9]+)\\}");
QRegExp hex("^(0x)?([0-9a-fA-F]?[0-9a-fA-F])");
QByteArray bytes;
int pos = 0;
while (pos < m_settings.m_packet.size())
{
if (m_settings.m_packet[pos] == '%')
{
if (ax25Dst.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
{
// AX.25 destination callsign & SSID
QString address = ax25Dst.capturedTexts()[1];
bytes.append(AX25Packet::encodeAddress(address));
pos += ax25Dst.matchedLength();
}
else if (ax25Src.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
{
// AX.25 source callsign & SSID
QString address = ax25Src.capturedTexts()[1];
bytes.append(AX25Packet::encodeAddress(address, 1));
pos += ax25Src.matchedLength();
}
else if (num.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
{
// Big endian packet number
bytes.append((m_tx >> 24) & 0xff);
bytes.append((m_tx >> 16) & 0xff);
bytes.append((m_tx >> 8) & 0xff);
bytes.append(m_tx & 0xff);
pos += num.matchedLength();
}
else if (data.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
{
// Constrained random number of random bytes
int minBytes = data.capturedTexts()[1].toInt();
int maxBytes = data.capturedTexts()[2].toInt();
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr0(minBytes, maxBytes);
std::uniform_int_distribution<> distr1(0, 255);
int count = distr0(gen);
for (int i = 0; i < count; i++)
bytes.append(distr1(gen));
pos += data.matchedLength();
}
else
{
qWarning() << "PERTester: Unsupported substitution in packet: pos=" << pos << " in " << m_settings.m_packet;
break;
}
}
else if (m_settings.m_packet[pos] == '\"')
{
// ASCII string in double quotes
int startIdx = pos + 1;
int endQuoteIdx = m_settings.m_packet.indexOf('\"', startIdx);
if (endQuoteIdx != -1)
{
int len = endQuoteIdx - startIdx;
QString string = m_settings.m_packet.mid(startIdx, len);
bytes.append(string.toLocal8Bit());
pos = endQuoteIdx + 1;
}
else
{
qWarning() << "PERTester: Unterminated string: pos=" << pos << " in " << m_settings.m_packet;
break;
}
}
else if (hex.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
{
// Hex byte
int value = hex.capturedTexts()[2].toInt(nullptr, 16);
bytes.append(value);
pos += hex.matchedLength();
}
else if ((m_settings.m_packet[pos] == ' ') || (m_settings.m_packet[pos] == ',') || (m_settings.m_packet[pos] == ':'))
{
pos++;
}
else
{
qWarning() << "PERTester: Unexpected character in packet: pos=" << pos << " in " << m_settings.m_packet;
break;
}
}
if ((pos >= m_settings.m_packet.size()) && (m_tx < m_settings.m_packetCount))
{
// Send packet via UDP
m_txUDPSocket.writeDatagram(bytes.data(), bytes.size(), QHostAddress(m_settings.m_txUDPAddress), m_settings.m_txUDPPort);
m_tx++;
m_txPackets.append(bytes);
if (getMessageQueueToGUI())
getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
}
if (m_tx >= m_settings.m_packetCount)
{
// Test complete
m_txTimer.stop();
// Wait for a couple of seconds for the last packet to be received
QTimer::singleShot(2000, this, &PERTesterWorker::testComplete);
}
}
void PERTesterWorker::testComplete()
{
stopWork();
if (m_msgQueueToFeature != nullptr)
m_msgQueueToFeature->push(PERTester::MsgReportWorker::create("Complete"));
qDebug() << "PERTesterWorker::tx: Test complete";
}

View File

@ -0,0 +1,98 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// 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 INCLUDE_FEATURE_PERTESTERWORKER_H_
#define INCLUDE_FEATURE_PERTESTERWORKER_H_
#include <QObject>
#include <QTimer>
#include <QUdpSocket>
#include "util/message.h"
#include "util/messagequeue.h"
#include "pertestersettings.h"
class PERTesterWorker : public QObject
{
Q_OBJECT
public:
class MsgConfigurePERTesterWorker : public Message {
MESSAGE_CLASS_DECLARATION
public:
const PERTesterSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigurePERTesterWorker* create(const PERTesterSettings& settings, bool force)
{
return new MsgConfigurePERTesterWorker(settings, force);
}
private:
PERTesterSettings m_settings;
bool m_force;
MsgConfigurePERTesterWorker(const PERTesterSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
PERTesterWorker();
~PERTesterWorker();
void reset();
bool startWork();
void stopWork();
bool isRunning() const { return m_running; }
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; }
private:
MessageQueue m_inputMessageQueue;
MessageQueue *m_msgQueueToFeature;
MessageQueue *m_msgQueueToGUI;
PERTesterSettings m_settings;
bool m_running;
QMutex m_mutex;
QUdpSocket *m_rxUDPSocket; //!< UDP socket to receive packets on
QUdpSocket m_txUDPSocket;
QTimer m_txTimer;
int m_tx; //!< Number of packets we've transmitted
int m_rxMatched; //!< Number of packets we've received that match a transmitted packet
int m_rxUnmatched; //!< Number of packets received that didn't match a transmitted packet
QList<QByteArray> m_txPackets; //!< Packets we've transmitted, but not yet received
bool handleMessage(const Message& cmd);
void applySettings(const PERTesterSettings& settings, bool force = false);
MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; }
void openUDP(const PERTesterSettings& settings);
void closeUDP();
void resetStats();
private slots:
void handleInputMessages();
void rx();
void tx();
void testComplete();
};
#endif // INCLUDE_FEATURE_PERTESTERWORKER_H_

View File

@ -0,0 +1,103 @@
<h1>Packer Error Rate (PER) Tester Feature Plugin</h1>
<h2>Introduction</h2>
The Packet Error Rate (PER) Tester feature can be used to measure the packet error rate over digital, packet based protcols such as AX.25 (Packet mod/demod), LoRa (ChipChat mod/demod) and 802.15.4.
The PER Tester feature allows you to define the contents of the packets to transmit, which can include a per-packet 32-bit identifier, as well as a user-defined or random payload, how many packets are sent and the interval between them.
Packets are sent and received from the feature to modulators and demodulators via UDP. This allows the transmitter or receiver to be connected to remote computer.
<h2>Interface</h2>
![PER Tester feature plugin GUI](../../../doc/img/PERTester_plugin.png)
<h3>1: Start/Stop plugin</h3>
This button starts or stops the PER test. If when starting the test, the number of transmitted packets is equal to the number of packets to transmit, the statistics will be reset and the test restarted. Otherwise, the test will be resumed.
<h3>2: Reset Statistics</h3>
Pressing the reset statistics button will clear the number of packets transmitted and received.
<h3>3: Packets</h3>
Specify the number of packets to transmit.
<h3>4: Start test</h3>
Controls when the test is started, after the start button (1) is pressed. This can be either:
* Immediately
* On satellite AOS (Acquistion of signal)
* On satellite mid pass ((AOS-LOS)/2)
When either satellite option is selected, the Satellites field appears, allowing you to enter the names of satellites which should start the test.
<h3>5: Interval</h3>
Specify the interval in seconds between packet transmissions.
<h3>6: Packet</h3>
Specify the contents of the packet to transmit and expect to be received. Data should be entered in hexidecimal bytes (E.g: 00 11 22 33 44).
The exact format required will depend on the underlying protocol being used. For AX.25 using the Packet modulator, LoRo using the ChirpChat modulator and 802.15.4, it is not necessary to include the trailing CRC, as this is appended automatically by the SDRangel modulators.
Aside from hex values, a number of variables can be used:
* %{num} - Inserts a 32-bit packet identifier, that counts from 0 to the number of packets to be transmitted minus 1.
* %{data=n,m} - Inserts a random number of bytes, ranging in length from n to m bytes.
* %{ax25.dst=MYCALL} - Encodes the callsign MYCALL as an AX.25 destination address.
* %{ax25.src=MYCALL} - Encodes the callsign MYCALL as an AX.25 source address.
* "some text" - Encode the string contained within the quotes as ASCII.
As an example, for AX.25, the following packet could be used:
%{ax25.dst=MYCALL} %{ax25.src=MYCALL} 03 f0 %{num} "Hello" %{data=0,100}
This:
* Encodes MYCALL as a 7-byte AX.25 destination address.
* Encodes MYCALL as a 7-byte AX.25 source address.
* Inserts hex value 0x03.
* Inserts hex value 0xf0.
* Inserts a 32-bit packet identifer.
* Inserts the 5 bytes of the ASCII encoding of Hello.
* Inserts a random payload of between 0 and 100 bytes.
<h3>7: Ignore Leading and Trailing bytes</h3>
When the PER Tester receives a packet, it compares it against a list of packets that it has transmitted. The ignore Leading and Trailing bytes field, reduce the number of bytes in the received packet, used for this comparison.
This can be used in cases where the demodulator outputs a different byte sequence than what the modulator requires. For example, the Packet Modulator does not require the CRC to be included, but the Packet Demodulator includes the CRC in the packet it outputs. Therefore, for the packet to match, the trailing two bytes should be ignored.
* For AX.25 (with Packet mod/demod), set Leading to 0 and Trailing to 2.
* For LoRa (with ChirpChat mod/demod), set Leading to 0 and Trailing to 0.
<h3>8: TX UDP port</h3>
Specifies the IP address and UDP port number of the modulator to send to packets to, to be transmitted.
<h3>9: RX UDP port</h3>
Specifies the IP address and UDP port number of the demodulator to receive packets from.
<h2>Statistics</h2>
The statistics fields display the statistics for the current test:
* Transmitted - number of packets transmitted.
* Received and Matched - number of packets received that matched a packet that was transmitted.
* Received and Unmatched - number of packets received that did not match a packet that was transmitted.
* PER - packet error rate - calculated as 100-(matched/transmitted)*100.
<h2>API</h2>
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the azimuth and elevation from the command line:
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "PERTester", "PERTesterSettings": { "packetCount": 100 }}'
To start sending the test:
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"

View File

@ -4410,6 +4410,11 @@ bool WebAPIRequestMapper::getFeatureSettings(
featureSettings->setMapSettings(new SWGSDRangel::SWGMapSettings());
featureSettings->getMapSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "PERTesterSettings")
{
featureSettings->setPerTesterSettings(new SWGSDRangel::SWGPERTesterSettings());
featureSettings->getPerTesterSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "SimplePTTSettings")
{
featureSettings->setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings());
@ -4655,6 +4660,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
featureSettings.setAprsSettings(nullptr);
featureSettings.setGs232ControllerSettings(nullptr);
featureSettings.setMapSettings(nullptr);
featureSettings.setPerTesterSettings(nullptr);
featureSettings.setSimplePttSettings(nullptr);
featureSettings.setStarTrackerSettings(nullptr);
featureSettings.setRigCtlServerSettings(nullptr);

View File

@ -247,6 +247,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToSettingsKey = {
{"APRS", "APRSSettings"},
{"GS232Controller", "GS232ControllerSettings"},
{"Map", "MapSettings"},
{"PERTester", "PERTesterSettings"},
{"SimplePTT", "SimplePTTSettings"},
{"StarTracker", "StarTrackerSettings"},
{"RigCtlServer", "RigCtlServerSettings"}
@ -261,6 +262,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureURIToSettingsKey = {
{"sdrangel.feature.aprs", "APRSSettings"},
{"sdrangel.feature.gs232controller", "GS232ControllerSettings"},
{"sdrangel.feature.map", "MapSettings"},
{"sdrangel.feature.pertester", "PERTesterSettings"},
{"sdrangel.feature.simpleptt", "SimplePTTSettings"},
{"sdrangel.feature.startracker", "StarTrackerSettings"},
{"sdrangel.feature.rigctlserver", "RigCtlServerSettings"}

View File

@ -17,5 +17,7 @@ FeatureActions:
$ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCActions"
MapActions:
$ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapActions"
PERTesterActions:
$ref: "http://swgserver:8081/api/swagger/include/PERTester.yaml#/PERTesterActions"
SimplePTTActions:
$ref: "http://swgserver:8081/api/swagger/include/SimplePTT.yaml#/SimplePTTActions"

View File

@ -23,6 +23,8 @@ FeatureSettings:
$ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerSettings"
MapSettings:
$ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapSettings"
PERTesterSettings:
$ref: "http://swgserver:8081/api/swagger/include/PERTester.yaml#/PERTesterSettings"
RigCtlServerSettings:
$ref: "http://swgserver:8081/api/swagger/include/RigCtlServer.yaml#/RigCtlServerSettings"
SatelliteTrackerSettings:

View File

@ -0,0 +1,67 @@
PERTesterSettings:
description: "PER Tester settings"
properties:
packetCount:
type: integer
interval:
type: number
format: float
start:
description: "0 - immediately, 1 - on satellite AOS, 2 - on satellite mid pass"
type: integer
satellites:
description: "Satellite names to start test on"
type: array
items:
type: string
packet:
description: "Packet of data to send"
type: string
ignoreLeadingBytes:
type: integer
ignoreTrailingBytes:
type: integer
txUDPAddress:
description: "UDP address to sent packets to be transmitted to"
type: string
txUDPPort:
description: "UDP port to sent packets to be transmitted to"
type: integer
rxUDPAddress:
description: "UDP address to receive packets via"
type: string
rxUDPPort:
description: "UDP port to receive packets via"
type: integer
title:
type: string
rgbColor:
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
PERTesterActions:
description: PERTester
properties:
aos:
description: "Acquisition of signal"
type: object
properties:
satelliteName:
description: "Name of the satellite"
type: string
aosTime:
description: "Time of AOS"
type: string
losTime:
description: "Time of LOS"
type: string

View File

@ -3256,6 +3256,18 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "number of repetitions of the same message (0 for infinite)"
},
"udpEnabled" : {
"type" : "integer",
"description" : "boolean 1 to enable forwarding messages via UDP else 0"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP address to listen for messages to transmit on"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port to listen for messages to transmit on"
},
"rgbColor" : {
"type" : "integer"
},
@ -3346,35 +3358,19 @@ margin-bottom: 20px;
},
"standard" : {
"type" : "integer",
"description" : "DVB bersion (see DATVDemodSettings::dvb_version)\n * 0 - DVB-S\n * 1 - DVB-S2\n"
"description" : "see DATVDemodSettings::dvb_version"
},
"modulation" : {
"type" : "integer",
"description" : "Modulation type (DATVDemodSettings::DATVModulation)\n * 0 - BPSK\n * 1 - QPSK\n * 2 - PSK8\n * 3 - APSK16\n * 4 - APSK32\n * 5 - APSK64E\n * 6 - QAM16\n * 7 - QAM64\n * 8 - QAM256\n"
"description" : "see DATVDemodSettings::DATVModulation"
},
"fec" : {
"type" : "integer",
"description" : "FEC rate (see DATVDemodSettings::DATVCodeRate)\n * 0 - 1/2\n * 1 - 2/3\n * 2 - 4/6\n * 3 - 3/4\n * 4 - 4/6\n * 5 - 7/8\n * 6 - 4/5\n * 7 - 8/9\n * 8 - 9/10\n * 9 - 1/4\n * 10 - 1/3\n * 11 - 2/5\n * 12 - 3/5\n"
},
"softLDPC" : {
"type" : "integer",
"description" : "(boolean) engage sodt LDPC with LDPC tool sub processes (Linux only)"
},
"softLDPCToolPath" : {
"type" : "string",
"description" : "O/S path to the LDPC tool binary"
},
"softLDPCMaxTrials" : {
"type" : "integer",
"description" : "maximum number of trials in the soft LDPC algorithm (LDPC tool parameter)"
},
"maxBitflips" : {
"type" : "integer",
"description" : "maximum number of bit flips allowed in hard LDPC algorithm"
"description" : "see DATVDemodSettings::DATVCodeRate"
},
"audioMute" : {
"type" : "integer",
"description" : "(boolean) mute audio output"
"description" : "boolean"
},
"audioDeviceName" : {
"type" : "string"
@ -3387,28 +3383,27 @@ margin-bottom: 20px;
},
"allowDrift" : {
"type" : "integer",
"description" : "(boolean) allow a larger frequency drift (DVB-S)"
"description" : "boolean"
},
"fastLock" : {
"type" : "integer",
"description" : "(boolean) faster locking algorithm (DVB-S)"
"description" : "boolean"
},
"filter" : {
"type" : "integer",
"description" : "Type of sumbol filtering (see DATVDemodSettings::dvb_sampler)\n * 0 - Nearest\n * 1 - Linear\n * 2 - Root Raised Cosine\n"
"description" : "see DATVDemodSettings::dvb_sampler"
},
"hardMetric" : {
"type" : "integer",
"description" : "(boolean) (DVB-S)"
"description" : "boolean"
},
"rollOff" : {
"type" : "number",
"format" : "float",
"description" : "RRC filter roll-off factor (0..1)"
"format" : "float"
},
"viterbi" : {
"type" : "integer",
"description" : "(boolean) (DVB-S)"
"description" : "boolean"
},
"excursion" : {
"type" : "integer"
@ -3429,26 +3424,6 @@ margin-bottom: 20px;
"udpTS" : {
"type" : "integer",
"description" : "boolean"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "DATVDemod"
@ -4352,6 +4327,9 @@ margin-bottom: 20px;
"MapActions" : {
"$ref" : "#/definitions/MapActions"
},
"PERTesterActions" : {
"$ref" : "#/definitions/PERTesterActions"
},
"SimplePTTActions" : {
"$ref" : "#/definitions/SimplePTTActions"
}
@ -4468,6 +4446,9 @@ margin-bottom: 20px;
"MapSettings" : {
"$ref" : "#/definitions/MapSettings"
},
"PERTesterSettings" : {
"$ref" : "#/definitions/PERTesterSettings"
},
"RigCtlServerSettings" : {
"$ref" : "#/definitions/RigCtlServerSettings"
},
@ -5466,6 +5447,18 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "Number of times to repeat the frame (-1 for infinite)."
},
"udpEnabled" : {
"type" : "integer",
"description" : "Enable forwarding of frames via UDP"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP address to listen for frames to transmit on"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port to listen for frames to transmit on"
},
"rgbColor" : {
"type" : "integer"
},
@ -7092,6 +7085,102 @@ margin-bottom: 20px;
}
},
"description" : "Enumeration with name for values"
};
defs.PERTesterActions = {
"properties" : {
"aos" : {
"$ref" : "#/definitions/PERTesterActions_aos"
}
},
"description" : "PERTester"
};
defs.PERTesterActions_aos = {
"properties" : {
"satelliteName" : {
"type" : "string",
"description" : "Name of the satellite"
},
"aosTime" : {
"type" : "string",
"description" : "Time of AOS"
},
"losTime" : {
"type" : "string",
"description" : "Time of LOS"
}
},
"description" : "Acquisition of signal"
};
defs.PERTesterSettings = {
"properties" : {
"packetCount" : {
"type" : "integer"
},
"interval" : {
"type" : "number",
"format" : "float"
},
"start" : {
"type" : "integer",
"description" : "0 - immediately, 1 - on satellite AOS, 2 - on satellite mid pass"
},
"satellites" : {
"type" : "array",
"description" : "Satellite names to start test on",
"items" : {
"type" : "string"
}
},
"packet" : {
"type" : "string",
"description" : "Packet of data to send"
},
"ignoreLeadingBytes" : {
"type" : "integer"
},
"ignoreTrailingBytes" : {
"type" : "integer"
},
"txUDPAddress" : {
"type" : "string",
"description" : "UDP address to sent packets to be transmitted to"
},
"txUDPPort" : {
"type" : "integer",
"description" : "UDP port to sent packets to be transmitted to"
},
"rxUDPAddress" : {
"type" : "string",
"description" : "UDP address to receive packets via"
},
"rxUDPPort" : {
"type" : "integer",
"description" : "UDP port to receive packets via"
},
"title" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "PER Tester settings"
};
defs.PacketDemodSettings = {
"properties" : {
@ -7111,6 +7200,18 @@ margin-bottom: 20px;
"type" : "number",
"format" : "float"
},
"udpEnabled" : {
"type" : "integer",
"description" : "Whether to forward received packets to specified UDP port"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP address to forward received packets to"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port to forward received packets to"
},
"rgbColor" : {
"type" : "integer"
},
@ -7320,6 +7421,18 @@ margin-bottom: 20px;
"symbolSpan" : {
"type" : "integer"
},
"udpEnabled" : {
"type" : "integer",
"description" : "Whether to receive packets to transmit on specified UDP port"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP address to receive packets to transmit via"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port to receive packets to transmit via"
},
"rgbColor" : {
"type" : "integer"
},
@ -45779,7 +45892,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-04-01T20:39:21.587+02:00
Generated 2021-04-02T23:43:06.310+02:00
</div>
</div>
</div>

View File

@ -38,6 +38,8 @@ SWGFeatureActions::SWGFeatureActions() {
m_afc_actions_isSet = false;
map_actions = nullptr;
m_map_actions_isSet = false;
per_tester_actions = nullptr;
m_per_tester_actions_isSet = false;
simple_ptt_actions = nullptr;
m_simple_ptt_actions_isSet = false;
}
@ -58,6 +60,8 @@ SWGFeatureActions::init() {
m_afc_actions_isSet = false;
map_actions = new SWGMapActions();
m_map_actions_isSet = false;
per_tester_actions = new SWGPERTesterActions();
m_per_tester_actions_isSet = false;
simple_ptt_actions = new SWGSimplePTTActions();
m_simple_ptt_actions_isSet = false;
}
@ -75,6 +79,9 @@ SWGFeatureActions::cleanup() {
if(map_actions != nullptr) {
delete map_actions;
}
if(per_tester_actions != nullptr) {
delete per_tester_actions;
}
if(simple_ptt_actions != nullptr) {
delete simple_ptt_actions;
}
@ -101,6 +108,8 @@ SWGFeatureActions::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&map_actions, pJson["MapActions"], "SWGMapActions", "SWGMapActions");
::SWGSDRangel::setValue(&per_tester_actions, pJson["PERTesterActions"], "SWGPERTesterActions", "SWGPERTesterActions");
::SWGSDRangel::setValue(&simple_ptt_actions, pJson["SimplePTTActions"], "SWGSimplePTTActions", "SWGSimplePTTActions");
}
@ -134,6 +143,9 @@ SWGFeatureActions::asJsonObject() {
if((map_actions != nullptr) && (map_actions->isSet())){
toJsonValue(QString("MapActions"), map_actions, obj, QString("SWGMapActions"));
}
if((per_tester_actions != nullptr) && (per_tester_actions->isSet())){
toJsonValue(QString("PERTesterActions"), per_tester_actions, obj, QString("SWGPERTesterActions"));
}
if((simple_ptt_actions != nullptr) && (simple_ptt_actions->isSet())){
toJsonValue(QString("SimplePTTActions"), simple_ptt_actions, obj, QString("SWGSimplePTTActions"));
}
@ -191,6 +203,16 @@ SWGFeatureActions::setMapActions(SWGMapActions* map_actions) {
this->m_map_actions_isSet = true;
}
SWGPERTesterActions*
SWGFeatureActions::getPerTesterActions() {
return per_tester_actions;
}
void
SWGFeatureActions::setPerTesterActions(SWGPERTesterActions* per_tester_actions) {
this->per_tester_actions = per_tester_actions;
this->m_per_tester_actions_isSet = true;
}
SWGSimplePTTActions*
SWGFeatureActions::getSimplePttActions() {
return simple_ptt_actions;
@ -221,6 +243,9 @@ SWGFeatureActions::isSet(){
if(map_actions && map_actions->isSet()){
isObjectUpdated = true; break;
}
if(per_tester_actions && per_tester_actions->isSet()){
isObjectUpdated = true; break;
}
if(simple_ptt_actions && simple_ptt_actions->isSet()){
isObjectUpdated = true; break;
}

View File

@ -24,6 +24,7 @@
#include "SWGAFCActions.h"
#include "SWGMapActions.h"
#include "SWGPERTesterActions.h"
#include "SWGSimplePTTActions.h"
#include <QString>
@ -60,6 +61,9 @@ public:
SWGMapActions* getMapActions();
void setMapActions(SWGMapActions* map_actions);
SWGPERTesterActions* getPerTesterActions();
void setPerTesterActions(SWGPERTesterActions* per_tester_actions);
SWGSimplePTTActions* getSimplePttActions();
void setSimplePttActions(SWGSimplePTTActions* simple_ptt_actions);
@ -82,6 +86,9 @@ private:
SWGMapActions* map_actions;
bool m_map_actions_isSet;
SWGPERTesterActions* per_tester_actions;
bool m_per_tester_actions_isSet;
SWGSimplePTTActions* simple_ptt_actions;
bool m_simple_ptt_actions_isSet;

View File

@ -44,6 +44,8 @@ SWGFeatureSettings::SWGFeatureSettings() {
m_gs232_controller_settings_isSet = false;
map_settings = nullptr;
m_map_settings_isSet = false;
per_tester_settings = nullptr;
m_per_tester_settings_isSet = false;
rig_ctl_server_settings = nullptr;
m_rig_ctl_server_settings_isSet = false;
satellite_tracker_settings = nullptr;
@ -78,6 +80,8 @@ SWGFeatureSettings::init() {
m_gs232_controller_settings_isSet = false;
map_settings = new SWGMapSettings();
m_map_settings_isSet = false;
per_tester_settings = new SWGPERTesterSettings();
m_per_tester_settings_isSet = false;
rig_ctl_server_settings = new SWGRigCtlServerSettings();
m_rig_ctl_server_settings_isSet = false;
satellite_tracker_settings = new SWGSatelliteTrackerSettings();
@ -112,6 +116,9 @@ SWGFeatureSettings::cleanup() {
if(map_settings != nullptr) {
delete map_settings;
}
if(per_tester_settings != nullptr) {
delete per_tester_settings;
}
if(rig_ctl_server_settings != nullptr) {
delete rig_ctl_server_settings;
}
@ -156,6 +163,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&map_settings, pJson["MapSettings"], "SWGMapSettings", "SWGMapSettings");
::SWGSDRangel::setValue(&per_tester_settings, pJson["PERTesterSettings"], "SWGPERTesterSettings", "SWGPERTesterSettings");
::SWGSDRangel::setValue(&rig_ctl_server_settings, pJson["RigCtlServerSettings"], "SWGRigCtlServerSettings", "SWGRigCtlServerSettings");
::SWGSDRangel::setValue(&satellite_tracker_settings, pJson["SatelliteTrackerSettings"], "SWGSatelliteTrackerSettings", "SWGSatelliteTrackerSettings");
@ -206,6 +215,9 @@ SWGFeatureSettings::asJsonObject() {
if((map_settings != nullptr) && (map_settings->isSet())){
toJsonValue(QString("MapSettings"), map_settings, obj, QString("SWGMapSettings"));
}
if((per_tester_settings != nullptr) && (per_tester_settings->isSet())){
toJsonValue(QString("PERTesterSettings"), per_tester_settings, obj, QString("SWGPERTesterSettings"));
}
if((rig_ctl_server_settings != nullptr) && (rig_ctl_server_settings->isSet())){
toJsonValue(QString("RigCtlServerSettings"), rig_ctl_server_settings, obj, QString("SWGRigCtlServerSettings"));
}
@ -305,6 +317,16 @@ SWGFeatureSettings::setMapSettings(SWGMapSettings* map_settings) {
this->m_map_settings_isSet = true;
}
SWGPERTesterSettings*
SWGFeatureSettings::getPerTesterSettings() {
return per_tester_settings;
}
void
SWGFeatureSettings::setPerTesterSettings(SWGPERTesterSettings* per_tester_settings) {
this->per_tester_settings = per_tester_settings;
this->m_per_tester_settings_isSet = true;
}
SWGRigCtlServerSettings*
SWGFeatureSettings::getRigCtlServerSettings() {
return rig_ctl_server_settings;
@ -384,6 +406,9 @@ SWGFeatureSettings::isSet(){
if(map_settings && map_settings->isSet()){
isObjectUpdated = true; break;
}
if(per_tester_settings && per_tester_settings->isSet()){
isObjectUpdated = true; break;
}
if(rig_ctl_server_settings && rig_ctl_server_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -27,6 +27,7 @@
#include "SWGDemodAnalyzerSettings.h"
#include "SWGGS232ControllerSettings.h"
#include "SWGMapSettings.h"
#include "SWGPERTesterSettings.h"
#include "SWGRigCtlServerSettings.h"
#include "SWGSatelliteTrackerSettings.h"
#include "SWGSimplePTTSettings.h"
@ -76,6 +77,9 @@ public:
SWGMapSettings* getMapSettings();
void setMapSettings(SWGMapSettings* map_settings);
SWGPERTesterSettings* getPerTesterSettings();
void setPerTesterSettings(SWGPERTesterSettings* per_tester_settings);
SWGRigCtlServerSettings* getRigCtlServerSettings();
void setRigCtlServerSettings(SWGRigCtlServerSettings* rig_ctl_server_settings);
@ -119,6 +123,9 @@ private:
SWGMapSettings* map_settings;
bool m_map_settings_isSet;
SWGPERTesterSettings* per_tester_settings;
bool m_per_tester_settings_isSet;
SWGRigCtlServerSettings* rig_ctl_server_settings;
bool m_rig_ctl_server_settings_isSet;

View File

@ -162,6 +162,9 @@
#include "SWGNFMModReport.h"
#include "SWGNFMModSettings.h"
#include "SWGNamedEnum.h"
#include "SWGPERTesterActions.h"
#include "SWGPERTesterActions_aos.h"
#include "SWGPERTesterSettings.h"
#include "SWGPacketDemodSettings.h"
#include "SWGPacketModActions.h"
#include "SWGPacketModActions_tx.h"
@ -701,6 +704,15 @@ namespace SWGSDRangel {
if(QString("SWGNamedEnum").compare(type) == 0) {
return new SWGNamedEnum();
}
if(QString("SWGPERTesterActions").compare(type) == 0) {
return new SWGPERTesterActions();
}
if(QString("SWGPERTesterActions_aos").compare(type) == 0) {
return new SWGPERTesterActions_aos();
}
if(QString("SWGPERTesterSettings").compare(type) == 0) {
return new SWGPERTesterSettings();
}
if(QString("SWGPacketDemodSettings").compare(type) == 0) {
return new SWGPacketDemodSettings();
}

View File

@ -0,0 +1,110 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGPERTesterActions.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGPERTesterActions::SWGPERTesterActions(QString* json) {
init();
this->fromJson(*json);
}
SWGPERTesterActions::SWGPERTesterActions() {
aos = nullptr;
m_aos_isSet = false;
}
SWGPERTesterActions::~SWGPERTesterActions() {
this->cleanup();
}
void
SWGPERTesterActions::init() {
aos = new SWGPERTesterActions_aos();
m_aos_isSet = false;
}
void
SWGPERTesterActions::cleanup() {
if(aos != nullptr) {
delete aos;
}
}
SWGPERTesterActions*
SWGPERTesterActions::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGPERTesterActions::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&aos, pJson["aos"], "SWGPERTesterActions_aos", "SWGPERTesterActions_aos");
}
QString
SWGPERTesterActions::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGPERTesterActions::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if((aos != nullptr) && (aos->isSet())){
toJsonValue(QString("aos"), aos, obj, QString("SWGPERTesterActions_aos"));
}
return obj;
}
SWGPERTesterActions_aos*
SWGPERTesterActions::getAos() {
return aos;
}
void
SWGPERTesterActions::setAos(SWGPERTesterActions_aos* aos) {
this->aos = aos;
this->m_aos_isSet = true;
}
bool
SWGPERTesterActions::isSet(){
bool isObjectUpdated = false;
do{
if(aos && aos->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,59 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGPERTesterActions.h
*
* PERTester
*/
#ifndef SWGPERTesterActions_H_
#define SWGPERTesterActions_H_
#include <QJsonObject>
#include "SWGPERTesterActions_aos.h"
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGPERTesterActions: public SWGObject {
public:
SWGPERTesterActions();
SWGPERTesterActions(QString* json);
virtual ~SWGPERTesterActions();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGPERTesterActions* fromJson(QString &jsonString) override;
SWGPERTesterActions_aos* getAos();
void setAos(SWGPERTesterActions_aos* aos);
virtual bool isSet() override;
private:
SWGPERTesterActions_aos* aos;
bool m_aos_isSet;
};
}
#endif /* SWGPERTesterActions_H_ */

View File

@ -0,0 +1,160 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGPERTesterActions_aos.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGPERTesterActions_aos::SWGPERTesterActions_aos(QString* json) {
init();
this->fromJson(*json);
}
SWGPERTesterActions_aos::SWGPERTesterActions_aos() {
satellite_name = nullptr;
m_satellite_name_isSet = false;
aos_time = nullptr;
m_aos_time_isSet = false;
los_time = nullptr;
m_los_time_isSet = false;
}
SWGPERTesterActions_aos::~SWGPERTesterActions_aos() {
this->cleanup();
}
void
SWGPERTesterActions_aos::init() {
satellite_name = new QString("");
m_satellite_name_isSet = false;
aos_time = new QString("");
m_aos_time_isSet = false;
los_time = new QString("");
m_los_time_isSet = false;
}
void
SWGPERTesterActions_aos::cleanup() {
if(satellite_name != nullptr) {
delete satellite_name;
}
if(aos_time != nullptr) {
delete aos_time;
}
if(los_time != nullptr) {
delete los_time;
}
}
SWGPERTesterActions_aos*
SWGPERTesterActions_aos::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGPERTesterActions_aos::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&satellite_name, pJson["satelliteName"], "QString", "QString");
::SWGSDRangel::setValue(&aos_time, pJson["aosTime"], "QString", "QString");
::SWGSDRangel::setValue(&los_time, pJson["losTime"], "QString", "QString");
}
QString
SWGPERTesterActions_aos::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGPERTesterActions_aos::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(satellite_name != nullptr && *satellite_name != QString("")){
toJsonValue(QString("satelliteName"), satellite_name, obj, QString("QString"));
}
if(aos_time != nullptr && *aos_time != QString("")){
toJsonValue(QString("aosTime"), aos_time, obj, QString("QString"));
}
if(los_time != nullptr && *los_time != QString("")){
toJsonValue(QString("losTime"), los_time, obj, QString("QString"));
}
return obj;
}
QString*
SWGPERTesterActions_aos::getSatelliteName() {
return satellite_name;
}
void
SWGPERTesterActions_aos::setSatelliteName(QString* satellite_name) {
this->satellite_name = satellite_name;
this->m_satellite_name_isSet = true;
}
QString*
SWGPERTesterActions_aos::getAosTime() {
return aos_time;
}
void
SWGPERTesterActions_aos::setAosTime(QString* aos_time) {
this->aos_time = aos_time;
this->m_aos_time_isSet = true;
}
QString*
SWGPERTesterActions_aos::getLosTime() {
return los_time;
}
void
SWGPERTesterActions_aos::setLosTime(QString* los_time) {
this->los_time = los_time;
this->m_los_time_isSet = true;
}
bool
SWGPERTesterActions_aos::isSet(){
bool isObjectUpdated = false;
do{
if(satellite_name && *satellite_name != QString("")){
isObjectUpdated = true; break;
}
if(aos_time && *aos_time != QString("")){
isObjectUpdated = true; break;
}
if(los_time && *los_time != QString("")){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,71 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGPERTesterActions_aos.h
*
* Acquisition of signal
*/
#ifndef SWGPERTesterActions_aos_H_
#define SWGPERTesterActions_aos_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGPERTesterActions_aos: public SWGObject {
public:
SWGPERTesterActions_aos();
SWGPERTesterActions_aos(QString* json);
virtual ~SWGPERTesterActions_aos();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGPERTesterActions_aos* fromJson(QString &jsonString) override;
QString* getSatelliteName();
void setSatelliteName(QString* satellite_name);
QString* getAosTime();
void setAosTime(QString* aos_time);
QString* getLosTime();
void setLosTime(QString* los_time);
virtual bool isSet() override;
private:
QString* satellite_name;
bool m_satellite_name_isSet;
QString* aos_time;
bool m_aos_time_isSet;
QString* los_time;
bool m_los_time_isSet;
};
}
#endif /* SWGPERTesterActions_aos_H_ */

View File

@ -0,0 +1,515 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGPERTesterSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGPERTesterSettings::SWGPERTesterSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGPERTesterSettings::SWGPERTesterSettings() {
packet_count = 0;
m_packet_count_isSet = false;
interval = 0.0f;
m_interval_isSet = false;
start = 0;
m_start_isSet = false;
satellites = nullptr;
m_satellites_isSet = false;
packet = nullptr;
m_packet_isSet = false;
ignore_leading_bytes = 0;
m_ignore_leading_bytes_isSet = false;
ignore_trailing_bytes = 0;
m_ignore_trailing_bytes_isSet = false;
tx_udp_address = nullptr;
m_tx_udp_address_isSet = false;
tx_udp_port = 0;
m_tx_udp_port_isSet = false;
rx_udp_address = nullptr;
m_rx_udp_address_isSet = false;
rx_udp_port = 0;
m_rx_udp_port_isSet = false;
title = nullptr;
m_title_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
SWGPERTesterSettings::~SWGPERTesterSettings() {
this->cleanup();
}
void
SWGPERTesterSettings::init() {
packet_count = 0;
m_packet_count_isSet = false;
interval = 0.0f;
m_interval_isSet = false;
start = 0;
m_start_isSet = false;
satellites = new QList<QString*>();
m_satellites_isSet = false;
packet = new QString("");
m_packet_isSet = false;
ignore_leading_bytes = 0;
m_ignore_leading_bytes_isSet = false;
ignore_trailing_bytes = 0;
m_ignore_trailing_bytes_isSet = false;
tx_udp_address = new QString("");
m_tx_udp_address_isSet = false;
tx_udp_port = 0;
m_tx_udp_port_isSet = false;
rx_udp_address = new QString("");
m_rx_udp_address_isSet = false;
rx_udp_port = 0;
m_rx_udp_port_isSet = false;
title = new QString("");
m_title_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
void
SWGPERTesterSettings::cleanup() {
if(satellites != nullptr) {
auto arr = satellites;
for(auto o: *arr) {
delete o;
}
delete satellites;
}
if(packet != nullptr) {
delete packet;
}
if(tx_udp_address != nullptr) {
delete tx_udp_address;
}
if(rx_udp_address != nullptr) {
delete rx_udp_address;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGPERTesterSettings*
SWGPERTesterSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGPERTesterSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&packet_count, pJson["packetCount"], "qint32", "");
::SWGSDRangel::setValue(&interval, pJson["interval"], "float", "");
::SWGSDRangel::setValue(&start, pJson["start"], "qint32", "");
::SWGSDRangel::setValue(&satellites, pJson["satellites"], "QList", "QString");
::SWGSDRangel::setValue(&packet, pJson["packet"], "QString", "QString");
::SWGSDRangel::setValue(&ignore_leading_bytes, pJson["ignoreLeadingBytes"], "qint32", "");
::SWGSDRangel::setValue(&ignore_trailing_bytes, pJson["ignoreTrailingBytes"], "qint32", "");
::SWGSDRangel::setValue(&tx_udp_address, pJson["txUDPAddress"], "QString", "QString");
::SWGSDRangel::setValue(&tx_udp_port, pJson["txUDPPort"], "qint32", "");
::SWGSDRangel::setValue(&rx_udp_address, pJson["rxUDPAddress"], "QString", "QString");
::SWGSDRangel::setValue(&rx_udp_port, pJson["rxUDPPort"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
}
QString
SWGPERTesterSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGPERTesterSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_packet_count_isSet){
obj->insert("packetCount", QJsonValue(packet_count));
}
if(m_interval_isSet){
obj->insert("interval", QJsonValue(interval));
}
if(m_start_isSet){
obj->insert("start", QJsonValue(start));
}
if(satellites && satellites->size() > 0){
toJsonArray((QList<void*>*)satellites, obj, "satellites", "QString");
}
if(packet != nullptr && *packet != QString("")){
toJsonValue(QString("packet"), packet, obj, QString("QString"));
}
if(m_ignore_leading_bytes_isSet){
obj->insert("ignoreLeadingBytes", QJsonValue(ignore_leading_bytes));
}
if(m_ignore_trailing_bytes_isSet){
obj->insert("ignoreTrailingBytes", QJsonValue(ignore_trailing_bytes));
}
if(tx_udp_address != nullptr && *tx_udp_address != QString("")){
toJsonValue(QString("txUDPAddress"), tx_udp_address, obj, QString("QString"));
}
if(m_tx_udp_port_isSet){
obj->insert("txUDPPort", QJsonValue(tx_udp_port));
}
if(rx_udp_address != nullptr && *rx_udp_address != QString("")){
toJsonValue(QString("rxUDPAddress"), rx_udp_address, obj, QString("QString"));
}
if(m_rx_udp_port_isSet){
obj->insert("rxUDPPort", QJsonValue(rx_udp_port));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
return obj;
}
qint32
SWGPERTesterSettings::getPacketCount() {
return packet_count;
}
void
SWGPERTesterSettings::setPacketCount(qint32 packet_count) {
this->packet_count = packet_count;
this->m_packet_count_isSet = true;
}
float
SWGPERTesterSettings::getInterval() {
return interval;
}
void
SWGPERTesterSettings::setInterval(float interval) {
this->interval = interval;
this->m_interval_isSet = true;
}
qint32
SWGPERTesterSettings::getStart() {
return start;
}
void
SWGPERTesterSettings::setStart(qint32 start) {
this->start = start;
this->m_start_isSet = true;
}
QList<QString*>*
SWGPERTesterSettings::getSatellites() {
return satellites;
}
void
SWGPERTesterSettings::setSatellites(QList<QString*>* satellites) {
this->satellites = satellites;
this->m_satellites_isSet = true;
}
QString*
SWGPERTesterSettings::getPacket() {
return packet;
}
void
SWGPERTesterSettings::setPacket(QString* packet) {
this->packet = packet;
this->m_packet_isSet = true;
}
qint32
SWGPERTesterSettings::getIgnoreLeadingBytes() {
return ignore_leading_bytes;
}
void
SWGPERTesterSettings::setIgnoreLeadingBytes(qint32 ignore_leading_bytes) {
this->ignore_leading_bytes = ignore_leading_bytes;
this->m_ignore_leading_bytes_isSet = true;
}
qint32
SWGPERTesterSettings::getIgnoreTrailingBytes() {
return ignore_trailing_bytes;
}
void
SWGPERTesterSettings::setIgnoreTrailingBytes(qint32 ignore_trailing_bytes) {
this->ignore_trailing_bytes = ignore_trailing_bytes;
this->m_ignore_trailing_bytes_isSet = true;
}
QString*
SWGPERTesterSettings::getTxUdpAddress() {
return tx_udp_address;
}
void
SWGPERTesterSettings::setTxUdpAddress(QString* tx_udp_address) {
this->tx_udp_address = tx_udp_address;
this->m_tx_udp_address_isSet = true;
}
qint32
SWGPERTesterSettings::getTxUdpPort() {
return tx_udp_port;
}
void
SWGPERTesterSettings::setTxUdpPort(qint32 tx_udp_port) {
this->tx_udp_port = tx_udp_port;
this->m_tx_udp_port_isSet = true;
}
QString*
SWGPERTesterSettings::getRxUdpAddress() {
return rx_udp_address;
}
void
SWGPERTesterSettings::setRxUdpAddress(QString* rx_udp_address) {
this->rx_udp_address = rx_udp_address;
this->m_rx_udp_address_isSet = true;
}
qint32
SWGPERTesterSettings::getRxUdpPort() {
return rx_udp_port;
}
void
SWGPERTesterSettings::setRxUdpPort(qint32 rx_udp_port) {
this->rx_udp_port = rx_udp_port;
this->m_rx_udp_port_isSet = true;
}
QString*
SWGPERTesterSettings::getTitle() {
return title;
}
void
SWGPERTesterSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGPERTesterSettings::getRgbColor() {
return rgb_color;
}
void
SWGPERTesterSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
qint32
SWGPERTesterSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGPERTesterSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGPERTesterSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGPERTesterSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGPERTesterSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGPERTesterSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGPERTesterSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGPERTesterSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGPERTesterSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGPERTesterSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
bool
SWGPERTesterSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_packet_count_isSet){
isObjectUpdated = true; break;
}
if(m_interval_isSet){
isObjectUpdated = true; break;
}
if(m_start_isSet){
isObjectUpdated = true; break;
}
if(satellites && (satellites->size() > 0)){
isObjectUpdated = true; break;
}
if(packet && *packet != QString("")){
isObjectUpdated = true; break;
}
if(m_ignore_leading_bytes_isSet){
isObjectUpdated = true; break;
}
if(m_ignore_trailing_bytes_isSet){
isObjectUpdated = true; break;
}
if(tx_udp_address && *tx_udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_tx_udp_port_isSet){
isObjectUpdated = true; break;
}
if(rx_udp_address && *rx_udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_rx_udp_port_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,162 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGPERTesterSettings.h
*
* PER Tester settings
*/
#ifndef SWGPERTesterSettings_H_
#define SWGPERTesterSettings_H_
#include <QJsonObject>
#include <QList>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGPERTesterSettings: public SWGObject {
public:
SWGPERTesterSettings();
SWGPERTesterSettings(QString* json);
virtual ~SWGPERTesterSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGPERTesterSettings* fromJson(QString &jsonString) override;
qint32 getPacketCount();
void setPacketCount(qint32 packet_count);
float getInterval();
void setInterval(float interval);
qint32 getStart();
void setStart(qint32 start);
QList<QString*>* getSatellites();
void setSatellites(QList<QString*>* satellites);
QString* getPacket();
void setPacket(QString* packet);
qint32 getIgnoreLeadingBytes();
void setIgnoreLeadingBytes(qint32 ignore_leading_bytes);
qint32 getIgnoreTrailingBytes();
void setIgnoreTrailingBytes(qint32 ignore_trailing_bytes);
QString* getTxUdpAddress();
void setTxUdpAddress(QString* tx_udp_address);
qint32 getTxUdpPort();
void setTxUdpPort(qint32 tx_udp_port);
QString* getRxUdpAddress();
void setRxUdpAddress(QString* rx_udp_address);
qint32 getRxUdpPort();
void setRxUdpPort(qint32 rx_udp_port);
QString* getTitle();
void setTitle(QString* title);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
virtual bool isSet() override;
private:
qint32 packet_count;
bool m_packet_count_isSet;
float interval;
bool m_interval_isSet;
qint32 start;
bool m_start_isSet;
QList<QString*>* satellites;
bool m_satellites_isSet;
QString* packet;
bool m_packet_isSet;
qint32 ignore_leading_bytes;
bool m_ignore_leading_bytes_isSet;
qint32 ignore_trailing_bytes;
bool m_ignore_trailing_bytes_isSet;
QString* tx_udp_address;
bool m_tx_udp_address_isSet;
qint32 tx_udp_port;
bool m_tx_udp_port_isSet;
QString* rx_udp_address;
bool m_rx_udp_address_isSet;
qint32 rx_udp_port;
bool m_rx_udp_port_isSet;
QString* title;
bool m_title_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
};
}
#endif /* SWGPERTesterSettings_H_ */