diff --git a/doc/img/PERTester_plugin.png b/doc/img/PERTester_plugin.png new file mode 100644 index 000000000..7865dc541 Binary files /dev/null and b/doc/img/PERTester_plugin.png differ diff --git a/plugins/channelrx/demodapt/aptdemod.cpp b/plugins/channelrx/demodapt/aptdemod.cpp index 86f9f65c2..82cb70390 100644 --- a/plugins/channelrx/demodapt/aptdemod.cpp +++ b/plugins/channelrx/demodapt/aptdemod.cpp @@ -572,15 +572,12 @@ int APTDemod::webapiActionsPost( { if (channelActionsKeys.contains("aos")) { - qDebug() << "Aos action"; SWGSDRangel::SWGAPTDemodActions_aos* aos = swgAPTDemodActions->getAos(); QString *satelliteName = aos->getSatelliteName(); if (satelliteName != nullptr) { - qDebug() << "sat " << *satelliteName; if (matchSatellite(*satelliteName)) { - qDebug() << "Matched sat"; // Reset for new pass resetDecoder(); m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create()); @@ -592,7 +589,7 @@ int APTDemod::webapiActionsPost( APTDemodSettings settings = m_settings; settings.m_decodeEnabled = true; settings.m_flip = !aos->getNorthToSouthPass(); - qDebug() << "Sending settings"; + m_inputMessageQueue.push(MsgConfigureAPTDemod::create(settings, false)); if (m_guiMessageQueue) m_guiMessageQueue->push(MsgConfigureAPTDemod::create(settings, false)); diff --git a/plugins/channelrx/demodpacket/packetdemod.cpp b/plugins/channelrx/demodpacket/packetdemod.cpp index b3b4c256f..ea7dbf386 100644 --- a/plugins/channelrx/demodpacket/packetdemod.cpp +++ b/plugins/channelrx/demodpacket/packetdemod.cpp @@ -167,6 +167,14 @@ bool PacketDemod::handleMessage(const Message& cmd) } } + // Forward via UDP + if (m_settings.m_udpEnabled) + { + qDebug() << "Forwarding to " << m_settings.m_udpAddress << ":" << m_settings.m_udpPort; + m_udpSocket.writeDatagram(report.getPacket().data(), report.getPacket().size(), + QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort); + } + return true; } else @@ -197,7 +205,15 @@ void PacketDemod::applySettings(const PacketDemodSettings& settings, bool force) if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { reverseAPIKeys.append("fmDeviation"); } - + if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) { + reverseAPIKeys.append("udpEnabled"); + } + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -299,6 +315,15 @@ void PacketDemod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("rfBandwidth")) { settings.m_rfBandwidth = response.getPacketDemodSettings()->getRfBandwidth(); } + if (channelSettingsKeys.contains("udpEnabled")) { + settings.m_udpEnabled = response.getPacketDemodSettings()->getUdpEnabled(); + } + if (channelSettingsKeys.contains("udpAddress")) { + settings.m_udpAddress = *response.getPacketDemodSettings()->getUdpAddress(); + } + if (channelSettingsKeys.contains("udpPort")) { + settings.m_udpPort = response.getPacketDemodSettings()->getUdpPort(); + } if (channelSettingsKeys.contains("rgbColor")) { settings.m_rgbColor = response.getPacketDemodSettings()->getRgbColor(); } @@ -330,8 +355,11 @@ void PacketDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& r response.getPacketDemodSettings()->setFmDeviation(settings.m_fmDeviation); response.getPacketDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); response.getPacketDemodSettings()->setRfBandwidth(settings.m_rfBandwidth); - response.getPacketDemodSettings()->setRgbColor(settings.m_rgbColor); + response.getPacketDemodSettings()->setUdpEnabled(settings.m_udpEnabled); + response.getPacketDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress)); + response.getPacketDemodSettings()->setUdpPort(settings.m_udpPort); + response.getPacketDemodSettings()->setRgbColor(settings.m_rgbColor); if (response.getPacketDemodSettings()->getTitle()) { *response.getPacketDemodSettings()->getTitle() = settings.m_title; } else { @@ -402,6 +430,15 @@ void PacketDemod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("rfBandwidth") || force) { swgPacketDemodSettings->setRfBandwidth(settings.m_rfBandwidth); } + if (channelSettingsKeys.contains("udpEnabled") || force) { + swgPacketDemodSettings->setUdpEnabled(settings.m_udpEnabled); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgPacketDemodSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgPacketDemodSettings->setUdpPort(settings.m_udpPort); + } if (channelSettingsKeys.contains("rgbColor") || force) { swgPacketDemodSettings->setRgbColor(settings.m_rgbColor); } diff --git a/plugins/channelrx/demodpacket/packetdemod.h b/plugins/channelrx/demodpacket/packetdemod.h index 87570da94..c005da56c 100644 --- a/plugins/channelrx/demodpacket/packetdemod.h +++ b/plugins/channelrx/demodpacket/packetdemod.h @@ -22,6 +22,7 @@ #include #include +#include #include #include "dsp/basebandsamplesink.h" @@ -131,6 +132,7 @@ private: PacketDemodSettings m_settings; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink qint64 m_centerFrequency; + QUdpSocket m_udpSocket; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index 464ad61df..0f354d87e 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -306,6 +306,24 @@ void PacketDemodGUI::on_clearTable_clicked() ui->packets->setRowCount(0); } +void PacketDemodGUI::on_udpEnabled_clicked(bool checked) +{ + m_settings.m_udpEnabled = checked; + applySettings(); +} + +void PacketDemodGUI::on_udpAddress_editingFinished() +{ + m_settings.m_udpAddress = ui->udpAddress->text(); + applySettings(); +} + +void PacketDemodGUI::on_udpPort_editingFinished() +{ + m_settings.m_udpPort = ui->udpPort->text().toInt(); + applySettings(); +} + void PacketDemodGUI::filterRow(int row) { bool hidden = false; @@ -503,6 +521,10 @@ void PacketDemodGUI::displaySettings() ui->filterTo->setText(m_settings.m_filterTo); ui->filterPID->setChecked(m_settings.m_filterPID == "f0"); + ui->udpEnabled->setChecked(m_settings.m_udpEnabled); + ui->udpAddress->setText(m_settings.m_udpAddress); + ui->udpPort->setText(QString::number(m_settings.m_udpPort)); + // Order and size columns QHeaderView *header = ui->packets->horizontalHeader(); for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index 898df575b..4f230f0c5 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -104,6 +104,9 @@ private slots: void on_filterTo_editingFinished(); void on_filterPID_stateChanged(int state); void on_clearTable_clicked(); + void on_udpEnabled_clicked(bool checked); + void on_udpAddress_editingFinished(); + void on_udpPort_editingFinished(); void filterRow(int row); void filter(); void packets_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.ui b/plugins/channelrx/demodpacket/packetdemodgui.ui index b4c67fada..adcea90ed 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.ui +++ b/plugins/channelrx/demodpacket/packetdemodgui.ui @@ -43,7 +43,7 @@ 0 0 390 - 101 + 131 @@ -174,7 +174,7 @@ - + @@ -210,7 +210,14 @@ - + + + Qt::Horizontal + + + + + @@ -373,7 +380,111 @@ - + + + Qt::Horizontal + + + + + + + + + Send packets via UDP + + + Qt::RightToLeft + + + UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9998 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + @@ -450,7 +561,7 @@ 0 - 110 + 140 391 261 @@ -556,18 +667,18 @@
gui/rollupwidget.h
1 - - LevelMeterSignalDB - QWidget -
gui/levelmeter.h
- 1 -
ValueDialZ QWidget
gui/valuedialz.h
1
+ + LevelMeterSignalDB + QWidget +
gui/levelmeter.h
+ 1 +
packets diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.cpp b/plugins/channelrx/demodpacket/packetdemodsettings.cpp index b771202ea..6b97bc58c 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.cpp +++ b/plugins/channelrx/demodpacket/packetdemodsettings.cpp @@ -38,6 +38,9 @@ void PacketDemodSettings::resetToDefaults() m_filterFrom = ""; m_filterTo = ""; m_filterPID = ""; + m_udpEnabled = false; + m_udpAddress = "127.0.0.1"; + m_udpPort = 9999; m_rgbColor = QColor(0, 105, 2).rgb(); m_title = "Packet Demodulator"; @@ -78,6 +81,9 @@ QByteArray PacketDemodSettings::serialize() const s.writeFloat(20, m_rfBandwidth); s.writeFloat(21, m_fmDeviation); + s.writeBool(22, m_udpEnabled); + s.writeString(23, m_udpAddress); + s.writeU32(24, m_udpPort); for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) s.writeS32(100 + i, m_columnIndexes[i]); @@ -134,6 +140,15 @@ bool PacketDemodSettings::deserialize(const QByteArray& data) d.readFloat(20, &m_rfBandwidth, 12500.0f); d.readFloat(21, &m_fmDeviation, 2500.0f); + d.readBool(22, &m_udpEnabled); + d.readString(23, &m_udpAddress); + d.readU32(24, &utmp); + if ((utmp > 1023) && (utmp < 65535)) { + m_udpPort = utmp; + } else { + m_udpPort = 9999; + } + for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) d.readS32(100 + i, &m_columnIndexes[i], i); for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.h b/plugins/channelrx/demodpacket/packetdemodsettings.h index 326f09fa3..eaaeec2ef 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.h +++ b/plugins/channelrx/demodpacket/packetdemodsettings.h @@ -36,6 +36,9 @@ struct PacketDemodSettings QString m_filterFrom; QString m_filterTo; QString m_filterPID; + bool m_udpEnabled; + QString m_udpAddress; + uint16_t m_udpPort; quint32 m_rgbColor; QString m_title; diff --git a/plugins/channelrx/demodpacket/readme.md b/plugins/channelrx/demodpacket/readme.md index 803f074fd..13c39c222 100644 --- a/plugins/channelrx/demodpacket/readme.md +++ b/plugins/channelrx/demodpacket/readme.md @@ -50,6 +50,18 @@ Checking this option displays only packets where the PID (Protocol ID) field is Pressing this button clears all packets from the table. +

11: UDP

+ +When checked, received packets are forwarded to the specified UDP address (12) and port (13). + +

12: UDP address

+ +IP address of the host to forward received packets to via UDP. + +

11: UDP port

+ +UDP port number to forward received packets to. +

Received Packets Table

The received packets table displays the contexts of the packets that have been received. Only packets with valid CRCs are displayed. diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp index fef87319c..7e054b43e 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -56,7 +58,8 @@ IEEE_802_15_4_Mod::IEEE_802_15_4_Mod(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), m_deviceAPI(deviceAPI), m_spectrumVis(SDR_TX_SCALEF), - m_settingsMutex(QMutex::Recursive) + m_settingsMutex(QMutex::Recursive), + m_udpSocket(nullptr) { setObjectName(m_channelId); @@ -76,6 +79,7 @@ IEEE_802_15_4_Mod::IEEE_802_15_4_Mod(DeviceAPI *deviceAPI) : IEEE_802_15_4_Mod::~IEEE_802_15_4_Mod() { + closeUDP(); disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; m_deviceAPI->removeChannelSourceAPI(this); @@ -193,6 +197,29 @@ void IEEE_802_15_4_Mod::applySettings(const IEEE_802_15_4_ModSettings& settings, reverseAPIKeys.append("repeatCount"); } + if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) { + reverseAPIKeys.append("udpEnabled"); + } + + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } + + if ( (settings.m_udpEnabled != m_settings.m_udpEnabled) + || (settings.m_udpAddress != m_settings.m_udpAddress) + || (settings.m_udpPort != m_settings.m_udpPort) + || force) + { + if (settings.m_udpEnabled) + openUDP(settings); + else + closeUDP(); + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -314,6 +341,15 @@ void IEEE_802_15_4_Mod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("repeatCount")) { settings.m_repeatCount = response.getIeee802154ModSettings()->getRepeatCount(); } + if (channelSettingsKeys.contains("udpEnabled")) { + settings.m_udpEnabled = response.getPacketDemodSettings()->getUdpEnabled(); + } + if (channelSettingsKeys.contains("udpAddress")) { + settings.m_udpAddress = *response.getPacketDemodSettings()->getUdpAddress(); + } + if (channelSettingsKeys.contains("udpPort")) { + settings.m_udpPort = response.getPacketDemodSettings()->getUdpPort(); + } if (channelSettingsKeys.contains("rgbColor")) { settings.m_rgbColor = response.getIeee802154ModSettings()->getRgbColor(); } @@ -401,6 +437,9 @@ void IEEE_802_15_4_Mod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSetti response.getIeee802154ModSettings()->setRepeat(settings.m_repeat ? 1 : 0); response.getIeee802154ModSettings()->setRepeatDelay(settings.m_repeatDelay); response.getIeee802154ModSettings()->setRepeatCount(settings.m_repeatCount); + response.getIeee802154ModSettings()->setUdpEnabled(settings.m_udpEnabled); + response.getIeee802154ModSettings()->setUdpAddress(new QString(settings.m_udpAddress)); + response.getIeee802154ModSettings()->setUdpPort(settings.m_udpPort); response.getIeee802154ModSettings()->setRgbColor(settings.m_rgbColor); if (response.getIeee802154ModSettings()->getTitle()) { @@ -512,6 +551,15 @@ void IEEE_802_15_4_Mod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("repeatCount") || force) { swgIEEE_802_15_4_ModSettings->setRepeatCount(settings.m_repeatCount); } + if (channelSettingsKeys.contains("udpEnabled") || force) { + swgIEEE_802_15_4_ModSettings->setUdpEnabled(settings.m_udpEnabled); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgIEEE_802_15_4_ModSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgIEEE_802_15_4_ModSettings->setUdpPort(settings.m_udpPort); + } if (channelSettingsKeys.contains("rgbColor") || force) { swgIEEE_802_15_4_ModSettings->setRgbColor(settings.m_rgbColor); } @@ -563,3 +611,36 @@ void IEEE_802_15_4_Mod::setScopeSink(BasebandSampleSink* scopeSink) { m_basebandSource->setScopeSink(scopeSink); } + +void IEEE_802_15_4_Mod::openUDP(const IEEE_802_15_4_ModSettings& settings) +{ + closeUDP(); + m_udpSocket = new QUdpSocket(); + if (!m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort)) + qCritical() << "IEEE_802_15_4_Mod::openUDP: Failed to bind to port " << settings.m_udpAddress << ":" << settings.m_udpPort << ". Error: " << m_udpSocket->error(); + else + qDebug() << "IEEE_802_15_4_Mod::openUDP: Listening for packets on " << settings.m_udpAddress << ":" << settings.m_udpPort; + connect(m_udpSocket, &QUdpSocket::readyRead, this, &IEEE_802_15_4_Mod::udpRx); +} + +void IEEE_802_15_4_Mod::closeUDP() +{ + if (m_udpSocket != nullptr) + { + disconnect(m_udpSocket, &QUdpSocket::readyRead, this, &IEEE_802_15_4_Mod::udpRx); + delete m_udpSocket; + m_udpSocket = nullptr; + } +} + +void IEEE_802_15_4_Mod::udpRx() +{ + while (m_udpSocket->hasPendingDatagrams()) + { + QNetworkDatagram datagram = m_udpSocket->receiveDatagram(); + // Convert from binary to hex string + QString string = datagram.data().toHex(' '); + IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod *msg = IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod::create(string); + m_basebandSource->getInputMessageQueue()->push(msg); + } +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h index 0a57f783d..380fbaa78 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h @@ -36,6 +36,7 @@ class QNetworkAccessManager; class QNetworkReply; class QThread; +class QUdpSocket; class DeviceAPI; class IEEE_802_15_4_ModBaseband; @@ -163,6 +164,7 @@ private: QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; + QUdpSocket *m_udpSocket; void applySettings(const IEEE_802_15_4_ModSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); @@ -179,9 +181,12 @@ private: const IEEE_802_15_4_ModSettings& settings, bool force ); + void openUDP(const IEEE_802_15_4_ModSettings& settings); + void closeUDP(); private slots: void networkManagerFinished(QNetworkReply *reply); + void udpRx(); }; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index 0a8ee3868..83a8f528b 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -269,6 +269,24 @@ void IEEE_802_15_4_ModGUI::txSettingsSelect() } } +void IEEE_802_15_4_ModGUI::on_udpEnabled_clicked(bool checked) +{ + m_settings.m_udpEnabled = checked; + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_udpAddress_editingFinished() +{ + m_settings.m_udpAddress = ui->udpAddress->text(); + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_udpPort_editingFinished() +{ + m_settings.m_udpPort = ui->udpPort->text().toInt(); + applySettings(); +} + void IEEE_802_15_4_ModGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -514,6 +532,10 @@ void IEEE_802_15_4_ModGUI::displaySettings() ui->frame->setText(m_settings.m_data); + ui->udpEnabled->setChecked(m_settings.m_udpEnabled); + ui->udpAddress->setText(m_settings.m_udpAddress); + ui->udpPort->setText(QString::number(m_settings.m_udpPort)); + blockApplySettings(false); } diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index e679fa0fd..ae6cb71d5 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -102,6 +102,9 @@ private slots: void on_repeat_toggled(bool checked); void repeatSelect(); void txSettingsSelect(); + void on_udpEnabled_clicked(bool checked); + void on_udpAddress_editingFinished(); + void on_udpPort_editingFinished(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui index a945d2939..fb03165da 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui @@ -43,7 +43,7 @@ 2 2 341 - 121 + 151
@@ -410,6 +410,103 @@
+ + + + + + Forward frames received via UDP + + + Qt::RightToLeft + + + UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9997 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + @@ -600,35 +697,12 @@ - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
RollupWidget QWidget
gui/rollupwidget.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
- - GLScope - QWidget -
gui/glscope.h
- 1 -
- - GLScopeGUI - QWidget -
gui/glscopegui.h
- 1 -
GLSpectrum QWidget @@ -641,12 +715,35 @@
gui/glspectrumgui.h
1
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
LevelMeterVU QWidget
gui/levelmeter.h
1
+ + GLScope + QWidget +
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
+ 1 +
deltaFrequency diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp index 7914a0d16..fd984cdc0 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp @@ -69,6 +69,9 @@ void IEEE_802_15_4_ModSettings::resetToDefaults() m_pulseShaping = RC; m_beta = 1.0f; m_symbolSpan = 6; + m_udpEnabled = false; + m_udpAddress = "127.0.0.1"; + m_udpPort = 9998; } bool IEEE_802_15_4_ModSettings::setPHY(QString phy) @@ -176,6 +179,9 @@ QByteArray IEEE_802_15_4_ModSettings::serialize() const s.writeS32(31, m_symbolSpan); s.writeS32(32, m_spectrumRate); s.writeS32(33, m_modulation); + s.writeBool(34, m_udpEnabled); + s.writeString(35, m_udpAddress); + s.writeU32(36, m_udpPort); return s.final(); } @@ -245,6 +251,14 @@ bool IEEE_802_15_4_ModSettings::deserialize(const QByteArray& data) d.readS32(31, &m_symbolSpan, 6); d.readS32(32, &m_spectrumRate, m_rfBandwidth); d.readS32(33, (qint32 *)&m_modulation, m_bitRate < 100000 ? IEEE_802_15_4_ModSettings::BPSK : IEEE_802_15_4_ModSettings::OQPSK); + d.readBool(34, &m_udpEnabled); + d.readString(35, &m_udpAddress, "127.0.0.1"); + d.readU32(36, &utmp); + if ((utmp > 1023) && (utmp < 65535)) { + m_udpPort = utmp; + } else { + m_udpPort = 9998; + } return true; } diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h index a41c9a789..554fed6af 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h @@ -62,6 +62,9 @@ struct IEEE_802_15_4_ModSettings enum PulseShaping {RC, SINE} m_pulseShaping; float m_beta; int m_symbolSpan; + bool m_udpEnabled; + QString m_udpAddress; + uint16_t m_udpPort; IEEE_802_15_4_ModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp index 184a18e74..3a1b9a67a 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp @@ -577,7 +577,7 @@ void IEEE_802_15_4_ModSource::addTXFrame(QString data) // PHY payload crcStart = p; // Data - p = hexToBin(p, data); + p = hexToBin(p, data.trimmed()); // MAC FCS crc.calculate(crcStart, p-crcStart); crcValue = crc.get(); diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.cpp b/plugins/channeltx/modchirpchat/chirpchatmod.cpp index c5d19a572..0adbbfe15 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmod.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -52,7 +54,8 @@ ChirpChatMod::ChirpChatMod(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_currentPayloadTime(0.0), m_settingsMutex(QMutex::Recursive), - m_sampleRate(48000) + m_sampleRate(48000), + m_udpSocket(nullptr) { setObjectName(m_channelId); @@ -327,6 +330,27 @@ void ChirpChatMod::applySettings(const ChirpChatModSettings& settings, bool forc } } + if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) { + reverseAPIKeys.append("udpEnabled"); + } + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } + + if ( (settings.m_udpEnabled != m_settings.m_udpEnabled) + || (settings.m_udpAddress != m_settings.m_udpAddress) + || (settings.m_udpPort != m_settings.m_udpPort) + || force) + { + if (settings.m_udpEnabled) + openUDP(settings); + else + closeUDP(); + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -524,6 +548,15 @@ void ChirpChatMod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("messageRepeat")) { settings.m_messageRepeat = response.getChirpChatModSettings()->getMessageRepeat(); } + if (channelSettingsKeys.contains("udpEnabled")) { + settings.m_udpEnabled = response.getPacketDemodSettings()->getUdpEnabled(); + } + if (channelSettingsKeys.contains("udpAddress")) { + settings.m_udpAddress = *response.getPacketDemodSettings()->getUdpAddress(); + } + if (channelSettingsKeys.contains("udpPort")) { + settings.m_udpPort = response.getPacketDemodSettings()->getUdpPort(); + } if (channelSettingsKeys.contains("rgbColor")) { settings.m_rgbColor = response.getChirpChatModSettings()->getRgbColor(); } @@ -665,6 +698,10 @@ void ChirpChatMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& bytesStr->push_back(new QString(tr("%1").arg(b, 2, 16, QChar('0')))); } + response.getChirpChatModSettings()->setUdpEnabled(settings.m_udpEnabled); + response.getChirpChatModSettings()->setUdpAddress(new QString(settings.m_udpAddress)); + response.getChirpChatModSettings()->setUdpPort(settings.m_udpPort); + response.getChirpChatModSettings()->setRgbColor(settings.m_rgbColor); if (response.getChirpChatModSettings()->getTitle()) { @@ -855,6 +892,16 @@ void ChirpChatMod::webapiFormatChannelSettings( swgChirpChatModSettings->setMessageRepeat(settings.m_messageRepeat); } + if (channelSettingsKeys.contains("udpEnabled") || force) { + swgChirpChatModSettings->setUdpEnabled(settings.m_udpEnabled); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgChirpChatModSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgChirpChatModSettings->setUdpPort(settings.m_udpPort); + } + if (channelSettingsKeys.contains("rgbColor") || force) { swgChirpChatModSettings->setRgbColor(settings.m_rgbColor); } @@ -903,3 +950,49 @@ bool ChirpChatMod::getModulatorActive() const { return m_basebandSource->getActive(); } + +void ChirpChatMod::openUDP(const ChirpChatModSettings& settings) +{ + closeUDP(); + m_udpSocket = new QUdpSocket(); + if (!m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort)) + qCritical() << "ChirpChatMod::openUDP: Failed to bind to port " << settings.m_udpAddress << ":" << settings.m_udpPort << ". Error: " << m_udpSocket->error(); + else + qDebug() << "ChirpChatMod::openUDP: Listening for packets on " << settings.m_udpAddress << ":" << settings.m_udpPort; + connect(m_udpSocket, &QUdpSocket::readyRead, this, &ChirpChatMod::udpRx); +} + +void ChirpChatMod::closeUDP() +{ + if (m_udpSocket != nullptr) + { + disconnect(m_udpSocket, &QUdpSocket::readyRead, this, &ChirpChatMod::udpRx); + delete m_udpSocket; + m_udpSocket = nullptr; + } +} + +void ChirpChatMod::udpRx() +{ + while (m_udpSocket->hasPendingDatagrams()) + { + QNetworkDatagram datagram = m_udpSocket->receiveDatagram(); + ChirpChatModBaseband::MsgConfigureChirpChatModPayload *payloadMsg = nullptr; + std::vector symbols; + + m_encoder.encodeBytes(datagram.data(), symbols); + payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols); + + if (payloadMsg) + { + m_basebandSource->getInputMessageQueue()->push(payloadMsg); + m_currentPayloadTime = (symbols.size()*(1<push(rpt); + } + } + } +} diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.h b/plugins/channeltx/modchirpchat/chirpchatmod.h index 5731d313a..008cca810 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.h +++ b/plugins/channeltx/modchirpchat/chirpchatmod.h @@ -35,6 +35,7 @@ class QNetworkAccessManager; class QNetworkReply; class QThread; +class QUdpSocket; class DeviceAPI; class CWKeyer; class ChirpChatModBaseband; @@ -159,6 +160,7 @@ private: QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; + QUdpSocket *m_udpSocket; void applySettings(const ChirpChatModSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); @@ -175,9 +177,12 @@ private: const ChirpChatModSettings& settings, bool force ); + void openUDP(const ChirpChatModSettings& settings); + void closeUDP(); private slots: void networkManagerFinished(QNetworkReply *reply); + void udpRx(); }; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index c73eab1f1..65c97f11c 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -333,6 +333,24 @@ void ChirpChatModGUI::on_hexText_editingFinished() applySettings(); } +void ChirpChatModGUI::on_udpEnabled_clicked(bool checked) +{ + m_settings.m_udpEnabled = checked; + applySettings(); +} + +void ChirpChatModGUI::on_udpAddress_editingFinished() +{ + m_settings.m_udpAddress = ui->udpAddress->text(); + applySettings(); +} + +void ChirpChatModGUI::on_udpPort_editingFinished() +{ + m_settings.m_udpPort = ui->udpPort->text().toInt(); + applySettings(); +} + void ChirpChatModGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -498,6 +516,9 @@ void ChirpChatModGUI::displaySettings() ui->repeatMessage->setValue(m_settings.m_messageRepeat); ui->repeatText->setText(tr("%1").arg(m_settings.m_messageRepeat)); ui->msgType->setCurrentIndex((int) m_settings.m_messageType); + ui->udpEnabled->setChecked(m_settings.m_udpEnabled); + ui->udpAddress->setText(m_settings.m_udpAddress); + ui->udpPort->setText(QString::number(m_settings.m_udpPort)); blockApplySettings(false); } diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index 5c40ccc63..8e53d680b 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -104,6 +104,9 @@ private slots: void on_generateMessages_clicked(bool checked); void on_messageText_editingFinished(); void on_hexText_editingFinished(); + void on_udpEnabled_clicked(bool checked); + void on_udpAddress_editingFinished(); + void on_udpPort_editingFinished(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void tick(); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui index f001176ed..5f75c16fd 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui @@ -7,7 +7,7 @@ 0 0 402 - 373 + 461
@@ -497,16 +497,16 @@ - 1 - 150 + 0 + 160 400 - 221 + 250 0 - 220 + 250 @@ -1168,6 +1168,103 @@ Sync + + + + 2 + 230 + 50 + 16 + + + + Forward messages received via UDP + + + Qt::RightToLeft + + + UDP + + + + + + 199 + 230 + 50 + 16 + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + UDP port to listen for messages to forward on + + + 00000 + + + 9997 + + + + + + 184 + 230 + 10 + 16 + + + + : + + + Qt::AlignCenter + + + + + + 60 + 230 + 120 + 16 + + + + + 120 + 0 + + + + Qt::ClickFocus + + + UDP address to listen for messages to forward on + + + 000.000.000.000 + + + 127.0.0.1 + + diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp index a371d87a9..f6e942fca 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp @@ -81,6 +81,9 @@ void ChirpChatModSettings::resetToDefaults() m_syncWord = 0x34; m_channelMute = false; m_messageRepeat = 1; + m_udpEnabled = false; + m_udpAddress = "127.0.0.1"; + m_udpPort = 9998; m_rgbColor = QColor(255, 0, 255).rgb(); m_title = "ChirpChat Modulator"; m_streamIndex = 0; @@ -189,6 +192,9 @@ QByteArray ChirpChatModSettings::serialize() const s.writeU32(53, m_reverseAPIDeviceIndex); s.writeU32(54, m_reverseAPIChannelIndex); s.writeS32(55, m_streamIndex); + s.writeBool(56, m_udpEnabled); + s.writeString(57, m_udpAddress); + s.writeU32(58, m_udpPort); return s.final(); } @@ -279,6 +285,15 @@ bool ChirpChatModSettings::deserialize(const QByteArray& data) m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(55, &m_streamIndex, 0); + d.readBool(56, &m_udpEnabled); + d.readString(57, &m_udpAddress, "127.0.0.1"); + d.readU32(58, &utmp); + if ((utmp > 1023) && (utmp < 65535)) { + m_udpPort = utmp; + } else { + m_udpPort = 9998; + } + return true; } else diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h index 451b9c102..97cbf7b08 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h @@ -77,6 +77,9 @@ struct ChirpChatModSettings QString m_textMessage; QByteArray m_bytesMessage; int m_messageRepeat; + bool m_udpEnabled; + QString m_udpAddress; + uint16_t m_udpPort; uint32_t m_rgbColor; QString m_title; int m_streamIndex; diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index b84b73403..ad13ed7d5 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include #include #include "SWGChannelSettings.h" @@ -48,6 +50,7 @@ MESSAGE_CLASS_DEFINITION(PacketMod::MsgConfigurePacketMod, Message) MESSAGE_CLASS_DEFINITION(PacketMod::MsgTXPacketMod, Message) +MESSAGE_CLASS_DEFINITION(PacketMod::MsgTXPacketBytes, Message) const char* const PacketMod::m_channelIdURI = "sdrangel.channeltx.modpacket"; const char* const PacketMod::m_channelId = "PacketMod"; @@ -57,7 +60,8 @@ PacketMod::PacketMod(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_spectrumVis(SDR_TX_SCALEF), m_settingsMutex(QMutex::Recursive), - m_sampleRate(48000) + m_sampleRate(48000), + m_udpSocket(nullptr) { setObjectName(m_channelId); @@ -78,6 +82,7 @@ PacketMod::PacketMod(DeviceAPI *deviceAPI) : PacketMod::~PacketMod() { + closeUDP(); disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; m_deviceAPI->removeChannelSourceAPI(this); @@ -225,102 +230,125 @@ void PacketMod::applySettings(const PacketModSettings& settings, bool force) reverseAPIKeys.append("spaceFrequency"); } - if((settings.m_ax25PreFlags != m_settings.m_ax25PreFlags) || force) { + if ((settings.m_ax25PreFlags != m_settings.m_ax25PreFlags) || force) { reverseAPIKeys.append("ax25PreFlags"); } - if((settings.m_ax25PostFlags != m_settings.m_ax25PostFlags) || force) { + if ((settings.m_ax25PostFlags != m_settings.m_ax25PostFlags) || force) { reverseAPIKeys.append("ax25PostFlags"); } - if((settings.m_ax25Control != m_settings.m_ax25Control) || force) { + if ((settings.m_ax25Control != m_settings.m_ax25Control) || force) { reverseAPIKeys.append("ax25Control"); } - if((settings.m_ax25PID != m_settings.m_ax25PID) || force) { + if ((settings.m_ax25PID != m_settings.m_ax25PID) || force) { reverseAPIKeys.append("ax25PID"); } - if((settings.m_preEmphasis != m_settings.m_preEmphasis) || force) { + if ((settings.m_preEmphasis != m_settings.m_preEmphasis) || force) { reverseAPIKeys.append("preEmphasis"); } - if((settings.m_preEmphasisTau != m_settings.m_preEmphasisTau) || force) { + if ((settings.m_preEmphasisTau != m_settings.m_preEmphasisTau) || force) { reverseAPIKeys.append("preEmphasisTau"); } - if((settings.m_preEmphasisHighFreq != m_settings.m_preEmphasisHighFreq) || force) { + if ((settings.m_preEmphasisHighFreq != m_settings.m_preEmphasisHighFreq) || force) { reverseAPIKeys.append("preEmphasisHighFreq"); } - if((settings.m_lpfTaps != m_settings.m_lpfTaps) || force) { + if ((settings.m_lpfTaps != m_settings.m_lpfTaps) || force) { reverseAPIKeys.append("lpfTaps"); } - if((settings.m_bbNoise != m_settings.m_bbNoise) || force) { + if ((settings.m_bbNoise != m_settings.m_bbNoise) || force) { reverseAPIKeys.append("bbNoise"); } - if((settings.m_rfNoise != m_settings.m_rfNoise) || force) { + if ((settings.m_rfNoise != m_settings.m_rfNoise) || force) { reverseAPIKeys.append("rfNoise"); } - if((settings.m_writeToFile != m_settings.m_writeToFile) || force) { + if ((settings.m_writeToFile != m_settings.m_writeToFile) || force) { reverseAPIKeys.append("writeToFile"); } - if((settings.m_spectrumRate != m_settings.m_spectrumRate) || force) { + if ((settings.m_spectrumRate != m_settings.m_spectrumRate) || force) { reverseAPIKeys.append("spectrumRate"); } - if((settings.m_callsign != m_settings.m_callsign) || force) { + if ((settings.m_callsign != m_settings.m_callsign) || force) { reverseAPIKeys.append("callsign"); } - if((settings.m_to != m_settings.m_to) || force) { + if ((settings.m_to != m_settings.m_to) || force) { reverseAPIKeys.append("to"); } - if((settings.m_via != m_settings.m_via) || force) { + if ((settings.m_via != m_settings.m_via) || force) { reverseAPIKeys.append("via"); } - if((settings.m_data != m_settings.m_data) || force) { + if ((settings.m_data != m_settings.m_data) || force) { reverseAPIKeys.append("data"); } - if((settings.m_bpf != m_settings.m_bpf) || force) { + if ((settings.m_bpf != m_settings.m_bpf) || force) { reverseAPIKeys.append("bpf"); } - if((settings.m_bpfLowCutoff != m_settings.m_bpfLowCutoff) || force) { + if ((settings.m_bpfLowCutoff != m_settings.m_bpfLowCutoff) || force) { reverseAPIKeys.append("bpfLowCutoff"); } - if((settings.m_bpfHighCutoff != m_settings.m_bpfHighCutoff) || force) { + if ((settings.m_bpfHighCutoff != m_settings.m_bpfHighCutoff) || force) { reverseAPIKeys.append("bpfHighCutoff"); } - if((settings.m_bpfTaps != m_settings.m_bpfTaps) || force) { + if ((settings.m_bpfTaps != m_settings.m_bpfTaps) || force) { reverseAPIKeys.append("bpfTaps"); } - if((settings.m_scramble != m_settings.m_scramble) || force) { + if ((settings.m_scramble != m_settings.m_scramble) || force) { reverseAPIKeys.append("scramble"); } - if((settings.m_polynomial != m_settings.m_polynomial) || force) { + if ((settings.m_polynomial != m_settings.m_polynomial) || force) { reverseAPIKeys.append("polynomial"); } - if((settings.m_beta != m_settings.m_beta) || force) { + if ((settings.m_beta != m_settings.m_beta) || force) { reverseAPIKeys.append("beta"); } - if((settings.m_symbolSpan != m_settings.m_symbolSpan) || force) { + if ((settings.m_symbolSpan != m_settings.m_symbolSpan) || force) { reverseAPIKeys.append("symbolSpan"); } + if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) { + reverseAPIKeys.append("udpEnabled"); + } + + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } + + if ( (settings.m_udpEnabled != m_settings.m_udpEnabled) + || (settings.m_udpAddress != m_settings.m_udpAddress) + || (settings.m_udpPort != m_settings.m_udpPort) + || force) + { + if (settings.m_udpEnabled) + openUDP(settings); + else + closeUDP(); + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -559,6 +587,15 @@ void PacketMod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { settings.m_reverseAPIChannelIndex = response.getPacketModSettings()->getReverseApiChannelIndex(); } + if (channelSettingsKeys.contains("udpEnabled")) { + settings.m_udpEnabled = response.getPacketDemodSettings()->getUdpEnabled(); + } + if (channelSettingsKeys.contains("udpAddress")) { + settings.m_udpAddress = *response.getPacketDemodSettings()->getUdpAddress(); + } + if (channelSettingsKeys.contains("udpPort")) { + settings.m_udpPort = response.getPacketDemodSettings()->getUdpPort(); + } } int PacketMod::webapiReportGet( @@ -681,6 +718,9 @@ void PacketMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res response.getPacketModSettings()->setPulseShaping(settings.m_pulseShaping ? 1 : 0); response.getPacketModSettings()->setBeta(settings.m_beta); response.getPacketModSettings()->setSymbolSpan(settings.m_symbolSpan); + response.getPacketModSettings()->setUdpEnabled(settings.m_udpEnabled); + response.getPacketModSettings()->setUdpAddress(new QString(settings.m_udpAddress)); + response.getPacketModSettings()->setUdpPort(settings.m_udpPort); response.getPacketModSettings()->setRgbColor(settings.m_rgbColor); @@ -895,6 +935,15 @@ void PacketMod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("streamIndex") || force) { swgPacketModSettings->setStreamIndex(settings.m_streamIndex); } + if (channelSettingsKeys.contains("udpEnabled") || force) { + swgPacketModSettings->setUdpEnabled(settings.m_udpEnabled); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgPacketModSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgPacketModSettings->setUdpPort(settings.m_udpPort); + } } void PacketMod::networkManagerFinished(QNetworkReply *reply) @@ -932,3 +981,34 @@ uint32_t PacketMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); } + +void PacketMod::openUDP(const PacketModSettings& settings) +{ + closeUDP(); + m_udpSocket = new QUdpSocket(); + if (!m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort)) + qCritical() << "PacketMod::openUDP: Failed to bind to port " << settings.m_udpAddress << ":" << settings.m_udpPort << ". Error: " << m_udpSocket->error(); + else + qDebug() << "PacketMod::openUDP: Listening for packets on " << settings.m_udpAddress << ":" << settings.m_udpPort; + connect(m_udpSocket, &QUdpSocket::readyRead, this, &PacketMod::udpRx); +} + +void PacketMod::closeUDP() +{ + if (m_udpSocket != nullptr) + { + disconnect(m_udpSocket, &QUdpSocket::readyRead, this, &PacketMod::udpRx); + delete m_udpSocket; + m_udpSocket = nullptr; + } +} + +void PacketMod::udpRx() +{ + while (m_udpSocket->hasPendingDatagrams()) + { + QNetworkDatagram datagram = m_udpSocket->receiveDatagram(); + MsgTXPacketBytes *msg = MsgTXPacketBytes::create(datagram.data()); + m_basebandSource->getInputMessageQueue()->push(msg); + } +} diff --git a/plugins/channeltx/modpacket/packetmod.h b/plugins/channeltx/modpacket/packetmod.h index ec6cd764b..e040661d9 100644 --- a/plugins/channeltx/modpacket/packetmod.h +++ b/plugins/channeltx/modpacket/packetmod.h @@ -36,6 +36,7 @@ class QNetworkAccessManager; class QNetworkReply; class QThread; +class QUdpSocket; class DeviceAPI; class PacketModBaseband; @@ -91,6 +92,25 @@ public: { } }; + class MsgTXPacketBytes : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgTXPacketBytes* create(QByteArray data) + { + return new MsgTXPacketBytes(data); + } + + QByteArray m_data; + + private: + + MsgTXPacketBytes(QByteArray data) : + Message(), + m_data(data) + { } + }; + //================================================================= PacketMod(DeviceAPI *deviceAPI); @@ -174,6 +194,7 @@ private: QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; + QUdpSocket *m_udpSocket; void applySettings(const PacketModSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); @@ -190,9 +211,12 @@ private: const PacketModSettings& settings, bool force ); + void openUDP(const PacketModSettings& settings); + void closeUDP(); private slots: void networkManagerFinished(QNetworkReply *reply); + void udpRx(); }; diff --git a/plugins/channeltx/modpacket/packetmodbaseband.cpp b/plugins/channeltx/modpacket/packetmodbaseband.cpp index 292ab8bc7..bc1e164da 100644 --- a/plugins/channeltx/modpacket/packetmodbaseband.cpp +++ b/plugins/channeltx/modpacket/packetmodbaseband.cpp @@ -157,6 +157,13 @@ bool PacketModBaseband::handleMessage(const Message& cmd) return true; } + else if (PacketMod::MsgTXPacketBytes::match(cmd)) + { + PacketMod::MsgTXPacketBytes& tx = (PacketMod::MsgTXPacketBytes&) cmd; + m_source.addTXPacket(tx.m_data); + + return true; + } else if (DSPSignalNotification::match(cmd)) { QMutexLocker mutexLocker(&m_mutex); diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index ffd6b55d5..7dbb7a6ce 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -338,6 +338,24 @@ void PacketModGUI::txSettingsSelect() } } +void PacketModGUI::on_udpEnabled_clicked(bool checked) +{ + m_settings.m_udpEnabled = checked; + applySettings(); +} + +void PacketModGUI::on_udpAddress_editingFinished() +{ + m_settings.m_udpAddress = ui->udpAddress->text(); + applySettings(); +} + +void PacketModGUI::on_udpPort_editingFinished() +{ + m_settings.m_udpPort = ui->udpPort->text().toInt(); + applySettings(); +} + void PacketModGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -532,6 +550,10 @@ void PacketModGUI::displaySettings() ui->gainText->setText(QString("%1").arg((double)m_settings.m_gain, 0, 'f', 1)); ui->gain->setValue(m_settings.m_gain); + ui->udpEnabled->setChecked(m_settings.m_udpEnabled); + ui->udpAddress->setText(m_settings.m_udpAddress); + ui->udpPort->setText(QString::number(m_settings.m_udpPort)); + ui->channelMute->setChecked(m_settings.m_channelMute); ui->repeat->setChecked(m_settings.m_repeat); diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index 18b7d41da..2112370d9 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -101,6 +101,9 @@ private slots: void bpfSelect(); void repeatSelect(); void txSettingsSelect(); + void on_udpEnabled_clicked(bool checked); + void on_udpAddress_editingFinished(); + void on_udpPort_editingFinished(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); diff --git a/plugins/channeltx/modpacket/packetmodgui.ui b/plugins/channeltx/modpacket/packetmodgui.ui index fb6d46944..10a4da95d 100644 --- a/plugins/channeltx/modpacket/packetmodgui.ui +++ b/plugins/channeltx/modpacket/packetmodgui.ui @@ -7,7 +7,7 @@ 0 0 350 - 610 + 702 @@ -43,7 +43,7 @@ 2 2 341 - 181 + 211 @@ -435,6 +435,103 @@ + + + + + + Forward packets received via UDP + + + Qt::RightToLeft + + + UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + UDP address to listen for packets to forward on + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + UDP port to listen for packets to forward on + + + 00000 + + + 9997 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + @@ -686,7 +783,7 @@ APRS examples: 0 - 190 + 220 351 141 @@ -723,7 +820,7 @@ APRS examples: 0 - 340 + 370 351 284 @@ -789,9 +886,9 @@ APRS examples: 1 - LevelMeterVU + ValueDialZ QWidget -
gui/levelmeter.h
+
gui/valuedialz.h
1
@@ -800,9 +897,9 @@ APRS examples:
gui/buttonswitch.h
- ValueDialZ + LevelMeterVU QWidget -
gui/valuedialz.h
+
gui/levelmeter.h
1
diff --git a/plugins/channeltx/modpacket/packetmodsettings.cpp b/plugins/channeltx/modpacket/packetmodsettings.cpp index 4cc882cd3..a31cd2d06 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.cpp +++ b/plugins/channeltx/modpacket/packetmodsettings.cpp @@ -63,7 +63,7 @@ void PacketModSettings::resetToDefaults() m_to = "APRS"; m_via = "WIDE2-2"; m_data = ">Using SDRangel"; - m_rgbColor = QColor(255, 0, 0).rgb(); + m_rgbColor = QColor(0, 105, 2).rgb(); m_title = "Packet Modulator"; m_streamIndex = 0; m_useReverseAPI = false; @@ -80,6 +80,9 @@ void PacketModSettings::resetToDefaults() m_pulseShaping = true; m_beta = 0.5f; m_symbolSpan = 6; + m_udpEnabled = false; + m_udpAddress = "127.0.0.1"; + m_udpPort = 9998; } bool PacketModSettings::setMode(QString mode) @@ -181,6 +184,9 @@ QByteArray PacketModSettings::serialize() const s.writeS32(48, m_symbolSpan); s.writeS32(49, m_spectrumRate); s.writeS32(50, m_modulation); + s.writeBool(51, m_udpEnabled); + s.writeString(52, m_udpAddress); + s.writeU32(53, m_udpPort); return s.final(); } @@ -267,6 +273,14 @@ bool PacketModSettings::deserialize(const QByteArray& data) d.readS32(48, &m_symbolSpan, 6); d.readS32(49, &m_spectrumRate, m_baud == 1200 ? 8000 : 24000); d.readS32(50, (qint32 *)&m_modulation, m_baud == 1200 ? PacketModSettings::AFSK : PacketModSettings::FSK); + d.readBool(51, &m_udpEnabled); + d.readString(52, &m_udpAddress, "127.0.0.1"); + d.readU32(53, &utmp); + if ((utmp > 1023) && (utmp < 65535)) { + m_udpPort = utmp; + } else { + m_udpPort = 9998; + } return true; } diff --git a/plugins/channeltx/modpacket/packetmodsettings.h b/plugins/channeltx/modpacket/packetmodsettings.h index 264eb8d55..3a4c3d729 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.h +++ b/plugins/channeltx/modpacket/packetmodsettings.h @@ -79,6 +79,9 @@ struct PacketModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + bool m_udpEnabled; + QString m_udpAddress; + uint16_t m_udpPort; PacketModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modpacket/packetmodsource.cpp b/plugins/channeltx/modpacket/packetmodsource.cpp index 379600074..e3dcc27e1 100644 --- a/plugins/channeltx/modpacket/packetmodsource.cpp +++ b/plugins/channeltx/modpacket/packetmodsource.cpp @@ -601,6 +601,45 @@ void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QSt packet_length = p-&packet[0]; + encodePacket(packet, packet_length, crc_start, packet_end); +} + +void PacketModSource::addTXPacket(QByteArray data) +{ + uint8_t packet[AX25_MAX_BYTES]; + uint8_t *crc_start; + uint8_t *packet_end; + uint8_t *p; + crc16x25 crc; + uint16_t crcValue; + int packet_length; + + // Create AX.25 packet + p = packet; + // Flag + for (int i = 0; i < std::min(m_settings.m_ax25PreFlags, AX25_MAX_FLAGS); i++) + *p++ = AX25_FLAG; + crc_start = p; + // Copy packet payload + for (int i = 0; i < data.size(); i++) + *p++ = data[i]; + // CRC (do not include flags) + crc.calculate(crc_start, p-crc_start); + crcValue = crc.get(); + *p++ = crcValue & 0xff; + *p++ = (crcValue >> 8); + packet_end = p; + // Flag + for (int i = 0; i < std::min(m_settings.m_ax25PostFlags, AX25_MAX_FLAGS); i++) + *p++ = AX25_FLAG; + + packet_length = p-&packet[0]; + + encodePacket(packet, packet_length, crc_start, packet_end); +} + +void PacketModSource::encodePacket(uint8_t *packet, int packet_length, uint8_t *crc_start, uint8_t *packet_end) +{ // HDLC bit stuffing m_byteIdx = 0; m_bitIdx = 0; diff --git a/plugins/channeltx/modpacket/packetmodsource.h b/plugins/channeltx/modpacket/packetmodsource.h index bc3eb63f8..abd548dfd 100644 --- a/plugins/channeltx/modpacket/packetmodsource.h +++ b/plugins/channeltx/modpacket/packetmodsource.h @@ -68,6 +68,8 @@ public: void applySettings(const PacketModSettings& settings, bool force = false); void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); void addTXPacket(QString callsign, QString to, QString via, QString data); + void addTXPacket(QByteArray data); + void encodePacket(uint8_t *packet, int packet_length, uint8_t *crc_start, uint8_t *packet_end); void setChannel(ChannelAPI *channel) { m_channel = channel; } private: diff --git a/plugins/channeltx/modpacket/readme.md b/plugins/channeltx/modpacket/readme.md index 8becdc427..cd75adee4 100644 --- a/plugins/channeltx/modpacket/readme.md +++ b/plugins/channeltx/modpacket/readme.md @@ -78,6 +78,18 @@ The packet of data to send. To send an APRS status message, use the format > Transmits a packet containing the current values in callsign, to, via and data fields. +

18: UDP

+ +When checked, a UDP port is opened to receive packets from other features or applications that will be transmitted. These packets do not need to contain the CRC, as it is appended automatically. + +

19: UDP address

+ +IP address of the interface open the UDP port on, to receive packets to be transmitted. + +

20: UDP port

+ +UDP port number to receive packets to be transmitted on. +

API

Full details of the API can be found in the Swagger documentation. Here is a quick example of how to transmit a packet from the command line: diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index df3ba354d..2e95e860d 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -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) diff --git a/plugins/feature/pertester/CMakeLists.txt b/plugins/feature/pertester/CMakeLists.txt new file mode 100644 index 000000000..3546ef793 --- /dev/null +++ b/plugins/feature/pertester/CMakeLists.txt @@ -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}) diff --git a/plugins/feature/pertester/pertester.cpp b/plugins/feature/pertester/pertester.cpp new file mode 100644 index 000000000..e6e38fec7 --- /dev/null +++ b/plugins/feature/pertester/pertester.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#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 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 *convertStringListToPtrs(QStringList listIn) +{ + QList *listOut = new QList(); + + for (int i = 0; i < listIn.size(); i++) + listOut->append(new QString(listIn[i])); + + return listOut; +} + +static QStringList convertPtrsToStringList(QList *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& 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(); +} diff --git a/plugins/feature/pertester/pertester.h b/plugins/feature/pertester/pertester.h new file mode 100644 index 000000000..2732b79b2 --- /dev/null +++ b/plugins/feature/pertester/pertester.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTER_H_ +#define INCLUDE_FEATURE_PERTESTER_H_ + +#include +#include +#include + +#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& featureSettingsKeys, const PERTesterSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif // INCLUDE_FEATURE_PERTESTER_H_ diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp new file mode 100644 index 000000000..2ddd75c78 --- /dev/null +++ b/plugins/feature/pertester/pertestergui.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#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(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); + } +} diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h new file mode 100644 index 000000000..45039f877 --- /dev/null +++ b/plugins/feature/pertester/pertestergui.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTERGUI_H_ +#define INCLUDE_FEATURE_PERTESTERGUI_H_ + +#include + +#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_ diff --git a/plugins/feature/pertester/pertestergui.ui b/plugins/feature/pertester/pertestergui.ui new file mode 100644 index 000000000..ff1e2d391 --- /dev/null +++ b/plugins/feature/pertester/pertestergui.ui @@ -0,0 +1,576 @@ + + + PERTesterGUI + + + + 0 + 0 + 350 + 430 + + + + + 0 + 0 + + + + + 320 + 100 + + + + + 350 + 16777215 + + + + + Liberation Sans + 9 + + + + GS-232 Rotator Controller + + + + + 10 + 10 + 331 + 291 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Start/stop PER Test + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Reset statistics + + + + + + + :/recycle.png:/recycle.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Start test + + + + + + + Number of packets to transmit + + + 1 + + + 2000000000 + + + + + + + When to start the test, after the start button is pressed + + + + Immediately + + + + + On satellite AOS + + + + + On satellite mid pass + + + + + + + + Packets + + + + + + + Interval in seconds between packets + + + 3 + + + 1000000.000000000000000 + + + + + + + Interval (s) + + + + + + + Satellites + + + + + + + Space separated list of satellite names to start the PER test on + + + ISS + + + + + + + + + + + Packet + + + + + + + + 0 + 0 + + + + + 0 + 80 + + + + Hex encoded packet to transmit. + +Substitutions: +%{ax25.src=CALLSIGN} +%{ax25.dst=CALLSIGN} +%{num} +%{data=min,max} + + + + + + + + + + + Ignore + + + + + + + Leading + + + + + + + 10000 + + + + + + + Trailing + + + + + + + 10000 + + + + + + + bytes + + + + + + + + + + + TX UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9997 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + RX UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9998 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 10 + 310 + 331 + 91 + + + + Statistics + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Number of packets received that match a transmitted packet + + + 0 + + + + + + + Received and Matched + + + + + + + PER + + + + + + + 0% + + + + + + + Number of packets that have been transmitted + + + 0 + + + + + + + Transmitted + + + + + + + Received and Unmatched + + + + + + + Number of packets received that do not match a transmitted packet + + + 0 + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + startStop + + + + + +
diff --git a/plugins/feature/pertester/pertestergui.ui.autosave b/plugins/feature/pertester/pertestergui.ui.autosave new file mode 100644 index 000000000..d7b16de17 --- /dev/null +++ b/plugins/feature/pertester/pertestergui.ui.autosave @@ -0,0 +1,576 @@ + + + PERTesterGUI + + + + 0 + 0 + 350 + 430 + + + + + 0 + 0 + + + + + 320 + 100 + + + + + 350 + 16777215 + + + + + Liberation Sans + 9 + + + + GS-232 Rotator Controller + + + + + 10 + 10 + 331 + 291 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Start/stop PER Test + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Reset statistics + + + + + + + :/recycle.png:/recycle.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Start test + + + + + + + Number of packets to transmit + + + 1 + + + 2000000000 + + + + + + + When to start the test, after the start button is pressed + + + + Immediately + + + + + On satellite AOS + + + + + On satellite mid pass + + + + + + + + Packets + + + + + + + Interval in seconds between packets + + + 3 + + + 1000000.000000000000000 + + + + + + + Interval (s) + + + + + + + Satellites + + + + + + + Space separated list of satellite names to start the PER test on + + + ISS + + + + + + + + + + + Packet + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Hex encoded packet to transmit. + +Substitutions: +%{ax25.src=CALLSIGN} +%{ax25.dst=CALLSIGN} +%{num} +%{data=min,max} + + + + + + + + + + + Ignore + + + + + + + Leading + + + + + + + 10000 + + + + + + + Trailing + + + + + + + 10000 + + + + + + + bytes + + + + + + + + + + + TX UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9997 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + RX UDP + + + + + + + + 120 + 0 + + + + Qt::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::ClickFocus + + + Destination UDP port + + + 00000 + + + 9998 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 10 + 310 + 331 + 91 + + + + Statistics + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Number of packets received that match a transmitted packet + + + 0 + + + + + + + Received and Matched + + + + + + + PER + + + + + + + 0% + + + + + + + Number of packets that have been transmitted + + + 0 + + + + + + + Transmitted + + + + + + + Received and Unmatched + + + + + + + Number of packets received that do not match a transmitted packet + + + 0 + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + startStop + + + + + +
diff --git a/plugins/feature/pertester/pertesterplugin.cpp b/plugins/feature/pertester/pertesterplugin.cpp new file mode 100644 index 000000000..d8fa9d389 --- /dev/null +++ b/plugins/feature/pertester/pertesterplugin.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + + +#include +#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(); +} diff --git a/plugins/feature/pertester/pertesterplugin.h b/plugins/feature/pertester/pertesterplugin.h new file mode 100644 index 000000000..dc8f7c2ad --- /dev/null +++ b/plugins/feature/pertester/pertesterplugin.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTERPLUGIN_H +#define INCLUDE_FEATURE_PERTESTERPLUGIN_H + +#include +#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 diff --git a/plugins/feature/pertester/pertesterreport.h b/plugins/feature/pertester/pertesterreport.h new file mode 100644 index 000000000..f8f565bd3 --- /dev/null +++ b/plugins/feature/pertester/pertesterreport.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTERREPORT_H_ +#define INCLUDE_FEATURE_PERTESTERREPORT_H_ + +#include + +#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_ diff --git a/plugins/feature/pertester/pertestersettings.cpp b/plugins/feature/pertester/pertestersettings.cpp new file mode 100644 index 000000000..523ed1675 --- /dev/null +++ b/plugins/feature/pertester/pertestersettings.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#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& strings) const +{ + QByteArray data; + QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); + (*stream) << strings; + delete stream; + return data; +} + +void PERTesterSettings::deserializeStringList(const QByteArray& data, QList& strings) +{ + QDataStream *stream = new QDataStream(data); + (*stream) >> strings; + delete stream; +} diff --git a/plugins/feature/pertester/pertestersettings.h b/plugins/feature/pertester/pertestersettings.h new file mode 100644 index 000000000..76741e0fa --- /dev/null +++ b/plugins/feature/pertester/pertestersettings.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTERSETTINGS_H_ +#define INCLUDE_FEATURE_PERTESTERSETTINGS_H_ + +#include +#include + +#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 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& strings) const; + void deserializeStringList(const QByteArray& data, QList& strings); +}; + +#endif // INCLUDE_FEATURE_PERTESTERSETTINGS_H_ diff --git a/plugins/feature/pertester/pertesterwebapiadapter.cpp b/plugins/feature/pertester/pertesterwebapiadapter.cpp new file mode 100644 index 000000000..e8ec0f819 --- /dev/null +++ b/plugins/feature/pertester/pertesterwebapiadapter.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; +} diff --git a/plugins/feature/pertester/pertesterwebapiadapter.h b/plugins/feature/pertester/pertesterwebapiadapter.h new file mode 100644 index 000000000..6afc6c150 --- /dev/null +++ b/plugins/feature/pertester/pertesterwebapiadapter.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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 diff --git a/plugins/feature/pertester/pertesterworker.cpp b/plugins/feature/pertester/pertesterworker.cpp new file mode 100644 index 000000000..ab888f0ac --- /dev/null +++ b/plugins/feature/pertester/pertesterworker.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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"; +} diff --git a/plugins/feature/pertester/pertesterworker.h b/plugins/feature/pertester/pertesterworker.h new file mode 100644 index 000000000..7f6c0110c --- /dev/null +++ b/plugins/feature/pertester/pertesterworker.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_PERTESTERWORKER_H_ +#define INCLUDE_FEATURE_PERTESTERWORKER_H_ + +#include +#include +#include + +#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 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_ diff --git a/plugins/feature/pertester/readme.md b/plugins/feature/pertester/readme.md new file mode 100644 index 000000000..100b1e6fd --- /dev/null +++ b/plugins/feature/pertester/readme.md @@ -0,0 +1,103 @@ +

Packer Error Rate (PER) Tester Feature Plugin

+ +

Introduction

+ +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. + +

Interface

+ +![PER Tester feature plugin GUI](../../../doc/img/PERTester_plugin.png) + +

1: Start/Stop plugin

+ +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. + +

2: Reset Statistics

+ +Pressing the reset statistics button will clear the number of packets transmitted and received. + +

3: Packets

+ +Specify the number of packets to transmit. + +

4: Start test

+ +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. + +

5: Interval

+ +Specify the interval in seconds between packet transmissions. + +

6: Packet

+ +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. + +

7: Ignore Leading and Trailing bytes

+ +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. + +

8: TX UDP port

+ +Specifies the IP address and UDP port number of the modulator to send to packets to, to be transmitted. + +

9: RX UDP port

+ +Specifies the IP address and UDP port number of the demodulator to receive packets from. + +

Statistics

+ +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. + +

API

+ +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" diff --git a/plugins/feature/satellitetracker/satellitetrackerworker.cpp b/plugins/feature/satellitetracker/satellitetrackerworker.cpp index 17e4f2426..eb342cf55 100644 --- a/plugins/feature/satellitetracker/satellitetrackerworker.cpp +++ b/plugins/feature/satellitetracker/satellitetrackerworker.cpp @@ -36,6 +36,7 @@ #include "device/deviceset.h" #include "device/deviceapi.h" #include "channel/channelwebapiutils.h" +#include "feature/featurewebapiutils.h" #include "maincore.h" #include "satellitetracker.h" @@ -460,10 +461,22 @@ void SatelliteTrackerWorker::update() text = text.append("\nSatellite is visible"); else text = text.append("\nAOS in: %1 mins").arg((int)round((satWorkerState->m_satState.m_passes[0]->m_aos.toSecsSinceEpoch() - qdt.toSecsSinceEpoch())/60.0)); + QString aosDateTime; + QString losDateTime; + if (m_settings.m_utc) + { + aosDateTime = satWorkerState->m_satState.m_passes[0]->m_aos.toString(m_settings.m_dateFormat + " hh:mm"); + losDateTime = satWorkerState->m_satState.m_passes[0]->m_los.toString(m_settings.m_dateFormat + " hh:mm"); + } + else + { + aosDateTime = satWorkerState->m_satState.m_passes[0]->m_aos.toLocalTime().toString(m_settings.m_dateFormat + " hh:mm"); + losDateTime = satWorkerState->m_satState.m_passes[0]->m_los.toLocalTime().toString(m_settings.m_dateFormat + " hh:mm"); + } text = QString("%1\nAOS: %2\nLOS: %3\nMax El: %4%5") .arg(text) - .arg(satWorkerState->m_satState.m_passes[0]->m_aos.toString(m_settings.m_dateFormat + " hh:mm")) - .arg(satWorkerState->m_satState.m_passes[0]->m_los.toString(m_settings.m_dateFormat + " hh:mm")) + .arg(aosDateTime) + .arg(losDateTime) .arg((int)round(satWorkerState->m_satState.m_passes[0]->m_maxElevation)) .arg(QChar(0xb0)); } @@ -631,9 +644,10 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name) } } - // Send AOS message to channels + // Send AOS message to channels/features SatWorkerState *satWorkerState = m_workerState.value(name); ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth); + FeatureWebAPIUtils::satelliteAOS(name, satWorkerState->m_aos, satWorkerState->m_los); // Start Doppler correction, if needed satWorkerState->m_initFrequencyOffset.clear(); @@ -690,9 +704,10 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name) } else { - // Send AOS message to channels + // Send AOS message to channels/features SatWorkerState *satWorkerState = m_workerState.value(name); ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth); + FeatureWebAPIUtils::satelliteAOS(name, satWorkerState->m_aos, satWorkerState->m_los); } } @@ -775,8 +790,9 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) } } - // Send LOS message to channels + // Send LOS message to channels/features ChannelWebAPIUtils::satelliteLOS(satWorkerState->m_name); + FeatureWebAPIUtils::satelliteLOS(satWorkerState->m_name); // Stop acquisition for (int i = 0; i < m_deviceSettingsList->size(); i++) diff --git a/sdrbase/feature/featurewebapiutils.cpp b/sdrbase/feature/featurewebapiutils.cpp index ec8e09bd7..61fa02782 100644 --- a/sdrbase/feature/featurewebapiutils.cpp +++ b/sdrbase/feature/featurewebapiutils.cpp @@ -19,6 +19,7 @@ #include "SWGFeatureActions.h" #include "SWGMapActions.h" +#include "SWGPERTesterActions.h" #include "maincore.h" #include "feature/featureset.h" @@ -99,3 +100,49 @@ Feature* FeatureWebAPIUtils::getFeature(int featureSetIndex, int featureIndex, c return nullptr; } } + +// Send AOS actions to all features that support it +bool FeatureWebAPIUtils::satelliteAOS(const QString name, const QDateTime aos, const QDateTime los) +{ + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + + for (std::vector::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it) + { + for (int fi = 0; fi < (*it)->getNumberOfFeatures(); fi++) + { + Feature *feature = (*it)->getFeatureAt(fi); + if (feature->getURI() == "sdrangel.feature.pertester") + { + QStringList featureActionKeys = {"aos"}; + SWGSDRangel::SWGFeatureActions featureActions; + SWGSDRangel::SWGPERTesterActions *perTesterFeatureAction = new SWGSDRangel::SWGPERTesterActions(); + SWGSDRangel::SWGPERTesterActions_aos *aosAction = new SWGSDRangel::SWGPERTesterActions_aos(); + QString errorResponse; + int httpRC; + + aosAction->setSatelliteName(new QString(name)); + aosAction->setAosTime(new QString(aos.toString(Qt::ISODate))); + aosAction->setLosTime(new QString(los.toString(Qt::ISODate))); + perTesterFeatureAction->setAos(aosAction); + + featureActions.setPerTesterActions(perTesterFeatureAction); + httpRC = feature->webapiActionsPost(featureActionKeys, featureActions, errorResponse); + if (httpRC/100 != 2) + { + qWarning("FeatureWebAPIUtils::satelliteAOS: webapiActionsPost error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + } + } + } + return true; +} + +// Send LOS actions to all features that support it +bool FeatureWebAPIUtils::satelliteLOS(const QString name) +{ + (void) name; + // Not currently required by any features + return true; +} diff --git a/sdrbase/feature/featurewebapiutils.h b/sdrbase/feature/featurewebapiutils.h index 808ac6665..caf1d459a 100644 --- a/sdrbase/feature/featurewebapiutils.h +++ b/sdrbase/feature/featurewebapiutils.h @@ -18,6 +18,8 @@ #ifndef SDRBASE_FEATURE_FEATUREWEBAPIUTILS_H_ #define SDRBASE_FEATURE_FEATUREWEBAPIUTILS_H_ +#include + #include "export.h" class Feature; @@ -27,6 +29,8 @@ class SDRBASE_API FeatureWebAPIUtils public: static bool mapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1); static Feature *getFeature(int featureSetIndex, int featureIndex, const QString& uri); + static bool satelliteAOS(const QString name, const QDateTime aos, const QDateTime los); + static bool satelliteLOS(const QString name); }; #endif // SDRBASE_FEATURE_FEATUREWEBAPIUTILS_H_ diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index deb13f9bc..0c16ec371 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -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); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index f30c70cb4..b5b1d5214 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -247,6 +247,7 @@ const QMap WebAPIUtils::m_featureTypeToSettingsKey = { {"APRS", "APRSSettings"}, {"GS232Controller", "GS232ControllerSettings"}, {"Map", "MapSettings"}, + {"PERTester", "PERTesterSettings"}, {"SimplePTT", "SimplePTTSettings"}, {"StarTracker", "StarTrackerSettings"}, {"RigCtlServer", "RigCtlServerSettings"} @@ -261,6 +262,7 @@ const QMap 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"} diff --git a/swagger/sdrangel/api/swagger/include/ChirpChatMod.yaml b/swagger/sdrangel/api/swagger/include/ChirpChatMod.yaml index 933cd11af..f33edaf6e 100644 --- a/swagger/sdrangel/api/swagger/include/ChirpChatMod.yaml +++ b/swagger/sdrangel/api/swagger/include/ChirpChatMod.yaml @@ -130,6 +130,15 @@ ChirpChatModSettings: messageRepeat: description: number of repetitions of the same message (0 for infinite) type: integer + udpEnabled: + description: boolean 1 to enable forwarding messages via UDP else 0 + type: integer + udpAddress: + description: UDP address to listen for messages to transmit on + type: string + udpPort: + description: UDP port to listen for messages to transmit on + type: integer rgbColor: type: integer title: diff --git a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml index 0d9b38e3a..32a5fc094 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml @@ -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" diff --git a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml index 9cbaa9f35..ceaf98c74 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml @@ -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: diff --git a/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml b/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml index d66fd9983..fe6d3ea39 100644 --- a/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml +++ b/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml @@ -25,6 +25,15 @@ IEEE_802_15_4_ModSettings: repeatCount: description: Number of times to repeat the frame (-1 for infinite). type: integer + udpEnabled: + description: Enable forwarding of frames via UDP + type: integer + udpAddress: + description: UDP address to listen for frames to transmit on + type: string + udpPort: + description: UDP port to listen for frames to transmit on + type: integer rgbColor: type: integer title: diff --git a/swagger/sdrangel/api/swagger/include/PERTester.yaml b/swagger/sdrangel/api/swagger/include/PERTester.yaml new file mode 100644 index 000000000..20e5d3fbb --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/PERTester.yaml @@ -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 diff --git a/swagger/sdrangel/api/swagger/include/PacketDemod.yaml b/swagger/sdrangel/api/swagger/include/PacketDemod.yaml index fe919fdfd..7abe7fa6d 100644 --- a/swagger/sdrangel/api/swagger/include/PacketDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/PacketDemod.yaml @@ -15,6 +15,15 @@ PacketDemodSettings: fmDeviation: type: number format: float + udpEnabled: + description: "Whether to forward received packets to specified UDP port" + type: integer + udpAddress: + description: "UDP address to forward received packets to" + type: string + udpPort: + description: "UDP port to forward received packets to" + type: integer rgbColor: type: integer title: diff --git a/swagger/sdrangel/api/swagger/include/PacketMod.yaml b/swagger/sdrangel/api/swagger/include/PacketMod.yaml index 053018c3f..a769c904d 100644 --- a/swagger/sdrangel/api/swagger/include/PacketMod.yaml +++ b/swagger/sdrangel/api/swagger/include/PacketMod.yaml @@ -119,6 +119,15 @@ PacketModSettings: format: float symbolSpan: type: integer + udpEnabled: + description: "Whether to receive packets to transmit on specified UDP port" + type: integer + udpAddress: + description: "UDP address to receive packets to transmit via" + type: string + udpPort: + description: "UDP port to receive packets to transmit via" + type: integer rgbColor: type: integer title: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 9a1f4a958..635abc699 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -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:
- Generated 2021-04-01T20:39:21.587+02:00 + Generated 2021-04-02T23:43:06.310+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.cpp index 9756cdaa6..39f893043 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.cpp @@ -84,6 +84,12 @@ SWGChirpChatModSettings::SWGChirpChatModSettings() { m_bytes_message_isSet = false; message_repeat = 0; m_message_repeat_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = nullptr; + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = nullptr; @@ -164,6 +170,12 @@ SWGChirpChatModSettings::init() { m_bytes_message_isSet = false; message_repeat = 0; m_message_repeat_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = new QString(""); + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = new QString(""); @@ -245,6 +257,11 @@ SWGChirpChatModSettings::cleanup() { } + if(udp_address != nullptr) { + delete udp_address; + } + + if(title != nullptr) { delete title; } @@ -325,6 +342,12 @@ SWGChirpChatModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&bytes_message, pJson["bytesMessage"], "QList", "QString"); ::SWGSDRangel::setValue(&message_repeat, pJson["messageRepeat"], "qint32", ""); + ::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", ""); + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); @@ -441,6 +464,15 @@ SWGChirpChatModSettings::asJsonObject() { if(m_message_repeat_isSet){ obj->insert("messageRepeat", QJsonValue(message_repeat)); } + if(m_udp_enabled_isSet){ + obj->insert("udpEnabled", QJsonValue(udp_enabled)); + } + if(udp_address != nullptr && *udp_address != QString("")){ + toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString")); + } + if(m_udp_port_isSet){ + obj->insert("udpPort", QJsonValue(udp_port)); + } if(m_rgb_color_isSet){ obj->insert("rgbColor", QJsonValue(rgb_color)); } @@ -749,6 +781,36 @@ SWGChirpChatModSettings::setMessageRepeat(qint32 message_repeat) { this->m_message_repeat_isSet = true; } +qint32 +SWGChirpChatModSettings::getUdpEnabled() { + return udp_enabled; +} +void +SWGChirpChatModSettings::setUdpEnabled(qint32 udp_enabled) { + this->udp_enabled = udp_enabled; + this->m_udp_enabled_isSet = true; +} + +QString* +SWGChirpChatModSettings::getUdpAddress() { + return udp_address; +} +void +SWGChirpChatModSettings::setUdpAddress(QString* udp_address) { + this->udp_address = udp_address; + this->m_udp_address_isSet = true; +} + +qint32 +SWGChirpChatModSettings::getUdpPort() { + return udp_port; +} +void +SWGChirpChatModSettings::setUdpPort(qint32 udp_port) { + this->udp_port = udp_port; + this->m_udp_port_isSet = true; +} + qint32 SWGChirpChatModSettings::getRgbColor() { return rgb_color; @@ -918,6 +980,15 @@ SWGChirpChatModSettings::isSet(){ if(m_message_repeat_isSet){ isObjectUpdated = true; break; } + if(m_udp_enabled_isSet){ + isObjectUpdated = true; break; + } + if(udp_address && *udp_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_udp_port_isSet){ + isObjectUpdated = true; break; + } if(m_rgb_color_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.h b/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.h index 0ac1e7838..aa24fbc7b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChirpChatModSettings.h @@ -127,6 +127,15 @@ public: qint32 getMessageRepeat(); void setMessageRepeat(qint32 message_repeat); + qint32 getUdpEnabled(); + void setUdpEnabled(qint32 udp_enabled); + + QString* getUdpAddress(); + void setUdpAddress(QString* udp_address); + + qint32 getUdpPort(); + void setUdpPort(qint32 udp_port); + qint32 getRgbColor(); void setRgbColor(qint32 rgb_color); @@ -239,6 +248,15 @@ private: qint32 message_repeat; bool m_message_repeat_isSet; + qint32 udp_enabled; + bool m_udp_enabled_isSet; + + QString* udp_address; + bool m_udp_address_isSet; + + qint32 udp_port; + bool m_udp_port_isSet; + qint32 rgb_color; bool m_rgb_color_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp index f4f0a2fe7..d61db2da1 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h index 1d980a669..9c047f7b9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h @@ -24,6 +24,7 @@ #include "SWGAFCActions.h" #include "SWGMapActions.h" +#include "SWGPERTesterActions.h" #include "SWGSimplePTTActions.h" #include @@ -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; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp index a7b1fd4a4..68846af33 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h index b746d3c67..c9fb52814 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h @@ -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; diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp index e1c869c59..83376e39a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp @@ -44,6 +44,12 @@ SWGIEEE_802_15_4_ModSettings::SWGIEEE_802_15_4_ModSettings() { m_repeat_delay_isSet = false; repeat_count = 0; m_repeat_count_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = nullptr; + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = nullptr; @@ -84,6 +90,12 @@ SWGIEEE_802_15_4_ModSettings::init() { m_repeat_delay_isSet = false; repeat_count = 0; m_repeat_count_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = new QString(""); + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = new QString(""); @@ -115,6 +127,11 @@ SWGIEEE_802_15_4_ModSettings::cleanup() { + if(udp_address != nullptr) { + delete udp_address; + } + + if(title != nullptr) { delete title; } @@ -155,6 +172,12 @@ SWGIEEE_802_15_4_ModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&repeat_count, pJson["repeatCount"], "qint32", ""); + ::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", ""); + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); @@ -211,6 +234,15 @@ SWGIEEE_802_15_4_ModSettings::asJsonObject() { if(m_repeat_count_isSet){ obj->insert("repeatCount", QJsonValue(repeat_count)); } + if(m_udp_enabled_isSet){ + obj->insert("udpEnabled", QJsonValue(udp_enabled)); + } + if(udp_address != nullptr && *udp_address != QString("")){ + toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString")); + } + if(m_udp_port_isSet){ + obj->insert("udpPort", QJsonValue(udp_port)); + } if(m_rgb_color_isSet){ obj->insert("rgbColor", QJsonValue(rgb_color)); } @@ -319,6 +351,36 @@ SWGIEEE_802_15_4_ModSettings::setRepeatCount(qint32 repeat_count) { this->m_repeat_count_isSet = true; } +qint32 +SWGIEEE_802_15_4_ModSettings::getUdpEnabled() { + return udp_enabled; +} +void +SWGIEEE_802_15_4_ModSettings::setUdpEnabled(qint32 udp_enabled) { + this->udp_enabled = udp_enabled; + this->m_udp_enabled_isSet = true; +} + +QString* +SWGIEEE_802_15_4_ModSettings::getUdpAddress() { + return udp_address; +} +void +SWGIEEE_802_15_4_ModSettings::setUdpAddress(QString* udp_address) { + this->udp_address = udp_address; + this->m_udp_address_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getUdpPort() { + return udp_port; +} +void +SWGIEEE_802_15_4_ModSettings::setUdpPort(qint32 udp_port) { + this->udp_port = udp_port; + this->m_udp_port_isSet = true; +} + qint32 SWGIEEE_802_15_4_ModSettings::getRgbColor() { return rgb_color; @@ -428,6 +490,15 @@ SWGIEEE_802_15_4_ModSettings::isSet(){ if(m_repeat_count_isSet){ isObjectUpdated = true; break; } + if(m_udp_enabled_isSet){ + isObjectUpdated = true; break; + } + if(udp_address && *udp_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_udp_port_isSet){ + isObjectUpdated = true; break; + } if(m_rgb_color_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h index 9eb3247c0..e32326b64 100644 --- a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h @@ -66,6 +66,15 @@ public: qint32 getRepeatCount(); void setRepeatCount(qint32 repeat_count); + qint32 getUdpEnabled(); + void setUdpEnabled(qint32 udp_enabled); + + QString* getUdpAddress(); + void setUdpAddress(QString* udp_address); + + qint32 getUdpPort(); + void setUdpPort(qint32 udp_port); + qint32 getRgbColor(); void setRgbColor(qint32 rgb_color); @@ -118,6 +127,15 @@ private: qint32 repeat_count; bool m_repeat_count_isSet; + qint32 udp_enabled; + bool m_udp_enabled_isSet; + + QString* udp_address; + bool m_udp_address_isSet; + + qint32 udp_port; + bool m_udp_port_isSet; + qint32 rgb_color; bool m_rgb_color_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 6f2c27c15..c2c54478e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -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(); } diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.cpp b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.cpp new file mode 100644 index 000000000..268f21859 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.cpp @@ -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 +#include +#include +#include + +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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.h b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.h new file mode 100644 index 000000000..c7418b74a --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions.h @@ -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 + + +#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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.cpp b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.cpp new file mode 100644 index 000000000..dbfb30db6 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.cpp @@ -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 +#include +#include +#include + +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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.h b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.h new file mode 100644 index 000000000..990c6cf2d --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterActions_aos.h @@ -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 + + +#include + +#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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.cpp new file mode 100644 index 000000000..dc88e7702 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.cpp @@ -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 +#include +#include +#include + +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(); + 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*)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* +SWGPERTesterSettings::getSatellites() { + return satellites; +} +void +SWGPERTesterSettings::setSatellites(QList* 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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.h b/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.h new file mode 100644 index 000000000..3f5dfac95 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPERTesterSettings.h @@ -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 + + +#include +#include + +#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* getSatellites(); + void setSatellites(QList* 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* 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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.cpp index 0e6a75bc3..01daab472 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.cpp @@ -36,6 +36,12 @@ SWGPacketDemodSettings::SWGPacketDemodSettings() { m_rf_bandwidth_isSet = false; fm_deviation = 0.0f; m_fm_deviation_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = nullptr; + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = nullptr; @@ -68,6 +74,12 @@ SWGPacketDemodSettings::init() { m_rf_bandwidth_isSet = false; fm_deviation = 0.0f; m_fm_deviation_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = new QString(""); + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = new QString(""); @@ -95,6 +107,11 @@ SWGPacketDemodSettings::cleanup() { + if(udp_address != nullptr) { + delete udp_address; + } + + if(title != nullptr) { delete title; } @@ -127,6 +144,12 @@ SWGPacketDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "float", ""); + ::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", ""); + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); @@ -171,6 +194,15 @@ SWGPacketDemodSettings::asJsonObject() { if(m_fm_deviation_isSet){ obj->insert("fmDeviation", QJsonValue(fm_deviation)); } + if(m_udp_enabled_isSet){ + obj->insert("udpEnabled", QJsonValue(udp_enabled)); + } + if(udp_address != nullptr && *udp_address != QString("")){ + toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString")); + } + if(m_udp_port_isSet){ + obj->insert("udpPort", QJsonValue(udp_port)); + } if(m_rgb_color_isSet){ obj->insert("rgbColor", QJsonValue(rgb_color)); } @@ -239,6 +271,36 @@ SWGPacketDemodSettings::setFmDeviation(float fm_deviation) { this->m_fm_deviation_isSet = true; } +qint32 +SWGPacketDemodSettings::getUdpEnabled() { + return udp_enabled; +} +void +SWGPacketDemodSettings::setUdpEnabled(qint32 udp_enabled) { + this->udp_enabled = udp_enabled; + this->m_udp_enabled_isSet = true; +} + +QString* +SWGPacketDemodSettings::getUdpAddress() { + return udp_address; +} +void +SWGPacketDemodSettings::setUdpAddress(QString* udp_address) { + this->udp_address = udp_address; + this->m_udp_address_isSet = true; +} + +qint32 +SWGPacketDemodSettings::getUdpPort() { + return udp_port; +} +void +SWGPacketDemodSettings::setUdpPort(qint32 udp_port) { + this->udp_port = udp_port; + this->m_udp_port_isSet = true; +} + qint32 SWGPacketDemodSettings::getRgbColor() { return rgb_color; @@ -336,6 +398,15 @@ SWGPacketDemodSettings::isSet(){ if(m_fm_deviation_isSet){ isObjectUpdated = true; break; } + if(m_udp_enabled_isSet){ + isObjectUpdated = true; break; + } + if(udp_address && *udp_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_udp_port_isSet){ + isObjectUpdated = true; break; + } if(m_rgb_color_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.h index d7bab93dd..79387a2a7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGPacketDemodSettings.h @@ -54,6 +54,15 @@ public: float getFmDeviation(); void setFmDeviation(float fm_deviation); + qint32 getUdpEnabled(); + void setUdpEnabled(qint32 udp_enabled); + + QString* getUdpAddress(); + void setUdpAddress(QString* udp_address); + + qint32 getUdpPort(); + void setUdpPort(qint32 udp_port); + qint32 getRgbColor(); void setRgbColor(qint32 rgb_color); @@ -94,6 +103,15 @@ private: float fm_deviation; bool m_fm_deviation_isSet; + qint32 udp_enabled; + bool m_udp_enabled_isSet; + + QString* udp_address; + bool m_udp_address_isSet; + + qint32 udp_port; + bool m_udp_port_isSet; + qint32 rgb_color; bool m_rgb_color_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp index 204e05734..46384f24b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp @@ -108,6 +108,12 @@ SWGPacketModSettings::SWGPacketModSettings() { m_beta_isSet = false; symbol_span = 0; m_symbol_span_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = nullptr; + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = nullptr; @@ -212,6 +218,12 @@ SWGPacketModSettings::init() { m_beta_isSet = false; symbol_span = 0; m_symbol_span_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = new QString(""); + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = new QString(""); @@ -283,6 +295,11 @@ SWGPacketModSettings::cleanup() { + if(udp_address != nullptr) { + delete udp_address; + } + + if(title != nullptr) { delete title; } @@ -387,6 +404,12 @@ SWGPacketModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&symbol_span, pJson["symbolSpan"], "qint32", ""); + ::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", ""); + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); @@ -539,6 +562,15 @@ SWGPacketModSettings::asJsonObject() { if(m_symbol_span_isSet){ obj->insert("symbolSpan", QJsonValue(symbol_span)); } + if(m_udp_enabled_isSet){ + obj->insert("udpEnabled", QJsonValue(udp_enabled)); + } + if(udp_address != nullptr && *udp_address != QString("")){ + toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString")); + } + if(m_udp_port_isSet){ + obj->insert("udpPort", QJsonValue(udp_port)); + } if(m_rgb_color_isSet){ obj->insert("rgbColor", QJsonValue(rgb_color)); } @@ -967,6 +999,36 @@ SWGPacketModSettings::setSymbolSpan(qint32 symbol_span) { this->m_symbol_span_isSet = true; } +qint32 +SWGPacketModSettings::getUdpEnabled() { + return udp_enabled; +} +void +SWGPacketModSettings::setUdpEnabled(qint32 udp_enabled) { + this->udp_enabled = udp_enabled; + this->m_udp_enabled_isSet = true; +} + +QString* +SWGPacketModSettings::getUdpAddress() { + return udp_address; +} +void +SWGPacketModSettings::setUdpAddress(QString* udp_address) { + this->udp_address = udp_address; + this->m_udp_address_isSet = true; +} + +qint32 +SWGPacketModSettings::getUdpPort() { + return udp_port; +} +void +SWGPacketModSettings::setUdpPort(qint32 udp_port) { + this->udp_port = udp_port; + this->m_udp_port_isSet = true; +} + qint32 SWGPacketModSettings::getRgbColor() { return rgb_color; @@ -1172,6 +1234,15 @@ SWGPacketModSettings::isSet(){ if(m_symbol_span_isSet){ isObjectUpdated = true; break; } + if(m_udp_enabled_isSet){ + isObjectUpdated = true; break; + } + if(udp_address && *udp_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_udp_port_isSet){ + isObjectUpdated = true; break; + } if(m_rgb_color_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h index 5bd5fe751..254689c99 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h @@ -162,6 +162,15 @@ public: qint32 getSymbolSpan(); void setSymbolSpan(qint32 symbol_span); + qint32 getUdpEnabled(); + void setUdpEnabled(qint32 udp_enabled); + + QString* getUdpAddress(); + void setUdpAddress(QString* udp_address); + + qint32 getUdpPort(); + void setUdpPort(qint32 udp_port); + qint32 getRgbColor(); void setRgbColor(qint32 rgb_color); @@ -310,6 +319,15 @@ private: qint32 symbol_span; bool m_symbol_span_isSet; + qint32 udp_enabled; + bool m_udp_enabled_isSet; + + QString* udp_address; + bool m_udp_address_isSet; + + qint32 udp_port; + bool m_udp_port_isSet; + qint32 rgb_color; bool m_rgb_color_isSet;