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/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/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/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;