1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-15 12:51:49 -05:00

Add UDP ports for packet forwarding in Packet mod and demod.

This commit is contained in:
Jon Beniston 2021-04-07 21:06:00 +01:00
parent 300c428f8c
commit e15470494a
25 changed files with 751 additions and 47 deletions

View File

@ -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);
}

View File

@ -22,6 +22,7 @@
#include <vector>
#include <QNetworkRequest>
#include <QUdpSocket>
#include <QThread>
#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;

View File

@ -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++)

View File

@ -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);

View File

@ -43,7 +43,7 @@
<x>0</x>
<y>0</y>
<width>390</width>
<height>101</height>
<height>131</height>
</rect>
</property>
<property name="minimumSize">
@ -174,7 +174,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<layout class="QHBoxLayout" name="powerLayout">
<item>
<widget class="QLabel" name="channelPowerMeterUnits">
<property name="text">
@ -210,7 +210,14 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="phySettingsLayout">
<item>
<widget class="QComboBox" name="mode">
<property name="minimumSize">
@ -373,7 +380,111 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="udpLayout">
<item>
<widget class="QCheckBox" name="udpEnabled">
<property name="toolTip">
<string>Send packets via UDP</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="udpAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP address</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="udpSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="udpPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Destination UDP port</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9998</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="filterLayout">
<item>
<widget class="QLabel" name="filterFromLabel">
<property name="text">
@ -450,7 +561,7 @@
<property name="geometry">
<rect>
<x>0</x>
<y>110</y>
<y>140</y>
<width>391</width>
<height>261</height>
</rect>
@ -556,18 +667,18 @@
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterSignalDB</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterSignalDB</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>packets</tabstop>

View File

@ -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++)

View File

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

View File

@ -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.
<h3>11: UDP</h3>
When checked, received packets are forwarded to the specified UDP address (12) and port (13).
<h3>12: UDP address</h3>
IP address of the host to forward received packets to via UDP.
<h3>11: UDP port</h3>
UDP port number to forward received packets to.
<h3>Received Packets Table</h3>
The received packets table displays the contexts of the packets that have been received. Only packets with valid CRCs are displayed.

View File

@ -20,8 +20,10 @@
#include <QDebug>
#include <QMutexLocker>
#include <QNetworkAccessManager>
#include <QNetworkDatagram>
#include <QNetworkReply>
#include <QBuffer>
#include <QUdpSocket>
#include <QThread>
#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);
}
}

View File

@ -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();
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>610</height>
<height>702</height>
</rect>
</property>
<property name="sizePolicy">
@ -43,7 +43,7 @@
<x>2</x>
<y>2</y>
<width>341</width>
<height>181</height>
<height>211</height>
</rect>
</property>
<property name="minimumSize">
@ -435,6 +435,103 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="udpLayout">
<item>
<widget class="QCheckBox" name="udpEnabled">
<property name="toolTip">
<string>Forward packets received via UDP</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="udpAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>UDP address to listen for packets to forward on</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="udpSeparator">
<property name="text">
<string>:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="udpPort">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>UDP port to listen for packets to forward on</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>9997</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="callsignLayout">
<item>
@ -686,7 +783,7 @@ APRS examples:
<property name="geometry">
<rect>
<x>0</x>
<y>190</y>
<y>220</y>
<width>351</width>
<height>141</height>
</rect>
@ -723,7 +820,7 @@ APRS examples:
<property name="geometry">
<rect>
<x>0</x>
<y>340</y>
<y>370</y>
<width>351</width>
<height>284</height>
</rect>
@ -789,9 +886,9 @@ APRS examples:
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterVU</class>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
<customwidget>
@ -800,9 +897,9 @@ APRS examples:
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<class>LevelMeterVU</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@ -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;
}

View File

@ -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();

View File

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

View File

@ -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:

View File

@ -78,6 +78,18 @@ The packet of data to send. To send an APRS status message, use the format <tt>>
Transmits a packet containing the current values in callsign, to, via and data fields.
<h3>18: UDP</h3>
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.
<h3>19: UDP address</h3>
IP address of the interface open the UDP port on, to receive packets to be transmitted.
<h3>20: UDP port</h3>
UDP port number to receive packets to be transmitted on.
<h2>API</h2>
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:

View File

@ -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:

View File

@ -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:

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

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