diff --git a/doc/img/PacketMod_plugin.png b/doc/img/PacketMod_plugin.png new file mode 100644 index 000000000..72899037b Binary files /dev/null and b/doc/img/PacketMod_plugin.png differ diff --git a/plugins/channeltx/CMakeLists.txt b/plugins/channeltx/CMakeLists.txt index 2af0701e3..47bf45234 100644 --- a/plugins/channeltx/CMakeLists.txt +++ b/plugins/channeltx/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(modam) add_subdirectory(modnfm) add_subdirectory(modssb) add_subdirectory(modwfm) +add_subdirectory(modpacket) add_subdirectory(udpsource) add_subdirectory(localsource) add_subdirectory(filesource) diff --git a/plugins/channeltx/modpacket/CMakeLists.txt b/plugins/channeltx/modpacket/CMakeLists.txt new file mode 100644 index 000000000..69a4297c4 --- /dev/null +++ b/plugins/channeltx/modpacket/CMakeLists.txt @@ -0,0 +1,64 @@ +project(modpacket) + +set(modpacket_SOURCES + packetmod.cpp + packetmodbaseband.cpp + packetmodsource.cpp + packetmodplugin.cpp + packetmodsettings.cpp + packetmodwebapiadapter.cpp +) + +set(modpacket_HEADERS + packetmod.h + packetmodbaseband.h + packetmodsource.h + packetmodplugin.h + packetmodsettings.h + packetmodwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(modpacket_SOURCES + ${modpacket_SOURCES} + packetmodgui.cpp + packetmodgui.ui + packetmodrepeatdialog.cpp + packetmodrepeatdialog.ui + packetmodtxsettingsdialog.cpp + packetmodtxsettingsdialog.ui + ) + set(modpacket_HEADERS + ${modpacket_HEADERS} + packetmodgui.h + packetmodrepeatdialog.h + packetmodtxsettingsdialog.h + ) + set(TARGET_NAME modpacket) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME modpacketsrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${modpacket_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp new file mode 100644 index 000000000..30d59284c --- /dev/null +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -0,0 +1,573 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include "SWGChannelSettings.h" +#include "SWGChannelReport.h" +#include "SWGChannelActions.h" +#include "SWGPacketModReport.h" +#include "SWGPacketModActions.h" +#include "SWGPacketModActions_tx.h" + +#include +#include +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "device/deviceapi.h" +#include "util/db.h" +#include "util/crc.h" + +#include "packetmodbaseband.h" +#include "packetmod.h" + +MESSAGE_CLASS_DEFINITION(PacketMod::MsgConfigurePacketMod, Message) +MESSAGE_CLASS_DEFINITION(PacketMod::MsgTXPacketMod, Message) + +const QString PacketMod::m_channelIdURI = "sdrangel.channeltx.modpacket"; +const QString PacketMod::m_channelId = "PacketMod"; + +PacketMod::PacketMod(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), + m_deviceAPI(deviceAPI), + m_spectrumVis(SDR_TX_SCALEF), + m_settingsMutex(QMutex::Recursive), + m_sampleRate(48000) +{ + setObjectName(m_channelId); + + m_thread = new QThread(this); + m_basebandSource = new PacketModBaseband(); + m_basebandSource->setSpectrumSampleSink(&m_spectrumVis); + m_basebandSource->moveToThread(m_thread); + + applySettings(m_settings, true); + + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSourceAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +PacketMod::~PacketMod() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + delete m_basebandSource; + delete m_thread; +} + +void PacketMod::start() +{ + qDebug("PacketMod::start"); + m_basebandSource->reset(); + m_thread->start(); +} + +void PacketMod::stop() +{ + qDebug("PacketMod::stop"); + m_thread->exit(); + m_thread->wait(); +} + +void PacketMod::pull(SampleVector::iterator& begin, unsigned int nbSamples) +{ + m_basebandSource->pull(begin, nbSamples); +} + +bool PacketMod::handleMessage(const Message& cmd) +{ + if (MsgConfigurePacketMod::match(cmd)) + { + MsgConfigurePacketMod& cfg = (MsgConfigurePacketMod&) cmd; + qDebug() << "PacketMod::handleMessage: MsgConfigurePacketMod"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgTXPacketMod::match(cmd)) + { + // Forward a copy to baseband + MsgTXPacketMod* rep = new MsgTXPacketMod((MsgTXPacketMod&)cmd); + qDebug() << "PacketMod::handleMessage: MsgTXPacketMod"; + m_basebandSource->getInputMessageQueue()->push(rep); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + // Forward to the source + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "PacketMod::handleMessage: DSPSignalNotification"; + m_basebandSource->getInputMessageQueue()->push(rep); + + return true; + } + else + { + return false; + } +} + +void PacketMod::applySettings(const PacketModSettings& settings, bool force) +{ + qDebug() << "PacketMod::applySettings:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " m_fmDeviation: " << settings.m_fmDeviation + << " m_gain: " << settings.m_gain + << " m_channelMute: " << settings.m_channelMute + << " m_repeat: " << settings.m_repeat + << " m_repeatDelay: " << settings.m_repeatDelay + << " m_repeatCount: " << settings.m_repeatCount + << " m_ax25PreFlags: " << settings.m_ax25PreFlags + << " m_ax25PostFlags: " << settings.m_ax25PostFlags + << " m_preEmphasis: " << settings.m_preEmphasis + << " m_preEmphasisTau: " << settings.m_preEmphasisTau + << " m_preEmphasisHighFreq: " << settings.m_preEmphasisHighFreq + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIAddress: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex + << " force: " << force; + + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + + if ((settings.m_gain != m_settings.m_gain) || force) { + reverseAPIKeys.append("gain"); + } + + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + + if ((settings.m_repeat != m_settings.m_repeat) || force) { + reverseAPIKeys.append("repeat"); + } + + if ((settings.m_repeatDelay != m_settings.m_repeatDelay) || force) { + reverseAPIKeys.append("repeatDelay"); + } + + if ((settings.m_repeatCount != m_settings.m_repeatCount) || force) { + reverseAPIKeys.append("repeatCount"); + } + + if((settings.m_ax25PreFlags != m_settings.m_ax25PreFlags) || force) { + reverseAPIKeys.append("ax25PreFlags"); + } + + if((settings.m_ax25PostFlags != m_settings.m_ax25PostFlags) || force) { + reverseAPIKeys.append("ax25PostFlags"); + } + + if((settings.m_preEmphasis != m_settings.m_preEmphasis) || force) { + reverseAPIKeys.append("preEmphasis"); + } + + if((settings.m_preEmphasisTau != m_settings.m_preEmphasisTau) || force) { + reverseAPIKeys.append("preEmphasisTau"); + } + + if((settings.m_preEmphasisHighFreq != m_settings.m_preEmphasisHighFreq) || force) { + reverseAPIKeys.append("preEmphasisHighFreq"); + } + + if (m_settings.m_streamIndex != settings.m_streamIndex) + { + if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSource(this, settings.m_streamIndex); + m_deviceAPI->addChannelSourceAPI(this); + } + + reverseAPIKeys.append("streamIndex"); + } + + PacketModBaseband::MsgConfigurePacketModBaseband *msg = PacketModBaseband::MsgConfigurePacketModBaseband::create(settings, force); + m_basebandSource->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_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; +} + +QByteArray PacketMod::serialize() const +{ + return m_settings.serialize(); +} + +bool PacketMod::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigurePacketMod *msg = MsgConfigurePacketMod::create(m_settings, true); + m_inputMessageQueue.push(msg); + + return success; +} + +int PacketMod::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setPacketModSettings(new SWGSDRangel::SWGPacketModSettings()); + response.getPacketModSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int PacketMod::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + PacketModSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigurePacketMod *msg = MsgConfigurePacketMod::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigurePacketMod *msgToGUI = MsgConfigurePacketMod::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +void PacketMod::webapiUpdateChannelSettings( + PacketModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + if (channelSettingsKeys.contains("inputFrequencyOffset")) { + settings.m_inputFrequencyOffset = response.getPacketModSettings()->getInputFrequencyOffset(); + } + if (channelSettingsKeys.contains("rfBandwidth")) { + settings.m_rfBandwidth = response.getPacketModSettings()->getRfBandwidth(); + } + if (channelSettingsKeys.contains("fmDeviation")) { + settings.m_fmDeviation = response.getPacketModSettings()->getFmDeviation(); + } + if (channelSettingsKeys.contains("gain")) { + settings.m_gain = response.getPacketModSettings()->getGain(); + } + if (channelSettingsKeys.contains("channelMute")) { + settings.m_channelMute = response.getPacketModSettings()->getChannelMute() != 0; + } + if (channelSettingsKeys.contains("repeat")) { + settings.m_repeat = response.getPacketModSettings()->getRepeat() != 0; + } + if (channelSettingsKeys.contains("repeatDelay")) { + settings.m_repeatDelay = response.getPacketModSettings()->getRepeatDelay(); + } + if (channelSettingsKeys.contains("repeatCount")) { + settings.m_repeatCount = response.getPacketModSettings()->getRepeatCount(); + } + if (channelSettingsKeys.contains("ax25PreFlags")) { + settings.m_ax25PreFlags = response.getPacketModSettings()->getAx25PreFlags(); + } + if (channelSettingsKeys.contains("ax25PostFlags")) { + settings.m_ax25PostFlags = response.getPacketModSettings()->getAx25PostFlags(); + } + if (channelSettingsKeys.contains("preEmphasis")) { + settings.m_preEmphasis = response.getPacketModSettings()->getPreEmphasis() != 0; + } + if (channelSettingsKeys.contains("preEmphasisTau")) { + settings.m_preEmphasisTau = response.getPacketModSettings()->getPreEmphasisTau(); + } + if (channelSettingsKeys.contains("preEmphasisHighFreq")) { + settings.m_preEmphasisHighFreq = response.getPacketModSettings()->getPreEmphasisHighFreq(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getPacketModSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getPacketModSettings()->getTitle(); + } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.getPacketModSettings()->getStreamIndex(); + } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getPacketModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getPacketModSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getPacketModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getPacketModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getPacketModSettings()->getReverseApiChannelIndex(); + } +} + +int PacketMod::webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setPacketModReport(new SWGSDRangel::SWGPacketModReport()); + response.getPacketModReport()->init(); + webapiFormatChannelReport(response); + return 200; +} + +int PacketMod::webapiActionsPost( + const QStringList& channelActionsKeys, + SWGSDRangel::SWGChannelActions& query, + QString& errorMessage) +{ + SWGSDRangel::SWGPacketModActions *swgPacketModActions = query.getPacketModActions(); + + if (swgPacketModActions) + { + if (channelActionsKeys.contains("tx")) + { + SWGSDRangel::SWGPacketModActions_tx* tx = swgPacketModActions->getTx(); + QString callsign(*tx->getCallsign()); + QString to(*tx->getTo()); + QString via(*tx->getVia()); + QString data(*tx->getData()); + + PacketMod::MsgTXPacketMod *msg = PacketMod::MsgTXPacketMod::create(callsign, to, via, data); + m_basebandSource->getInputMessageQueue()->push(msg); + } + + return 202; + } + else + { + errorMessage = "Missing PacketModActions in query"; + return 400; + } +} + +void PacketMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const PacketModSettings& settings) +{ + response.getPacketModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getPacketModSettings()->setRfBandwidth(settings.m_rfBandwidth); + response.getPacketModSettings()->setFmDeviation(settings.m_fmDeviation); + response.getPacketModSettings()->setGain(settings.m_gain); + response.getPacketModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0); + response.getPacketModSettings()->setRepeat(settings.m_repeat ? 1 : 0); + response.getPacketModSettings()->setRepeatDelay(settings.m_repeatDelay); + response.getPacketModSettings()->setRepeatCount(settings.m_repeatCount); + response.getPacketModSettings()->setAx25PreFlags(settings.m_ax25PreFlags); + response.getPacketModSettings()->setAx25PostFlags(settings.m_ax25PostFlags); + response.getPacketModSettings()->setPreEmphasis(settings.m_preEmphasis ? 1 : 0); + response.getPacketModSettings()->setPreEmphasisTau(settings.m_preEmphasisTau); + response.getPacketModSettings()->setPreEmphasisHighFreq(settings.m_preEmphasisHighFreq); + response.getPacketModSettings()->setRgbColor(settings.m_rgbColor); + + if (response.getPacketModSettings()->getTitle()) { + *response.getPacketModSettings()->getTitle() = settings.m_title; + } else { + response.getPacketModSettings()->setTitle(new QString(settings.m_title)); + } + + response.getPacketModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getPacketModSettings()->getReverseApiAddress()) { + *response.getPacketModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getPacketModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getPacketModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getPacketModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getPacketModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); +} + +void PacketMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +{ + response.getPacketModReport()->setChannelPowerDb(CalcDb::dbPower(getMagSq())); + response.getPacketModReport()->setChannelSampleRate(m_basebandSource->getChannelSampleRate()); +} + +void PacketMod::webapiReverseSendSettings(QList& channelSettingsKeys, const PacketModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setDirection(1); // single source (Tx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString("PacketMod")); + swgChannelSettings->setPacketModSettings(new SWGSDRangel::SWGPacketModSettings()); + SWGSDRangel::SWGPacketModSettings *swgPacketModSettings = swgChannelSettings->getPacketModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgPacketModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgPacketModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgPacketModSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("gain") || force) { + swgPacketModSettings->setGain(settings.m_gain); + } + if (channelSettingsKeys.contains("channelMute") || force) { + swgPacketModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("repeat") || force) { + swgPacketModSettings->setRepeat(settings.m_repeat ? 1 : 0); + } + if (channelSettingsKeys.contains("repeatDelay") || force) { + swgPacketModSettings->setRepeatDelay(settings.m_repeatDelay); + } + if (channelSettingsKeys.contains("repeatCount") || force) { + swgPacketModSettings->setRepeatCount(settings.m_repeatCount); + } + if (channelSettingsKeys.contains("ax25PreFlags") || force) { + swgPacketModSettings->setAx25PreFlags(settings.m_ax25PreFlags); + } + if (channelSettingsKeys.contains("ax25PostFlags") || force) { + swgPacketModSettings->setAx25PostFlags(settings.m_ax25PostFlags); + } + if (channelSettingsKeys.contains("preEmphasis") || force) { + swgPacketModSettings->setPreEmphasis(settings.m_preEmphasis); + } + if (channelSettingsKeys.contains("preEmphasisTau") || force) { + swgPacketModSettings->setPreEmphasisTau(settings.m_preEmphasisTau); + } + if (channelSettingsKeys.contains("preEmphasisHighFreq") || force) { + swgPacketModSettings->setPreEmphasisHighFreq(settings.m_preEmphasisHighFreq); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgPacketModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgPacketModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgPacketModSettings->setStreamIndex(settings.m_streamIndex); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->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 swgChannelSettings; +} + +void PacketMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "PacketMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("PacketMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +double PacketMod::getMagSq() const +{ + return m_basebandSource->getMagSq(); +} + +void PacketMod::setLevelMeter(QObject *levelMeter) +{ + connect(m_basebandSource, SIGNAL(levelChanged(qreal, qreal, int)), levelMeter, SLOT(levelChanged(qreal, qreal, int))); +} + +uint32_t PacketMod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSinkStreams(); +} + diff --git a/plugins/channeltx/modpacket/packetmod.h b/plugins/channeltx/modpacket/packetmod.h new file mode 100644 index 000000000..12b15ca0d --- /dev/null +++ b/plugins/channeltx/modpacket/packetmod.h @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016-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 PLUGINS_CHANNELTX_MODPACKET_PACKETMOD_H_ +#define PLUGINS_CHANNELTX_MODPACKET_PACKETMOD_H_ + +#include +#include +#include + +#include +#include + +#include "dsp/basebandsamplesource.h" +#include "dsp/spectrumvis.h" +#include "channel/channelapi.h" +#include "util/message.h" + +#include "packetmodsettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class DeviceAPI; +class PacketModBaseband; + +class PacketMod : public BasebandSampleSource, public ChannelAPI { + Q_OBJECT + +public: + class MsgConfigurePacketMod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const PacketModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigurePacketMod* create(const PacketModSettings& settings, bool force) + { + return new MsgConfigurePacketMod(settings, force); + } + + private: + PacketModSettings m_settings; + bool m_force; + + MsgConfigurePacketMod(const PacketModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgTXPacketMod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgTXPacketMod* create(QString callsign, QString to, QString via, QString data) + { + return new MsgTXPacketMod(callsign, to, via, data); + } + + QString m_callsign; + QString m_to; + QString m_via; + QString m_data; + + private: + + MsgTXPacketMod(QString callsign, QString to, QString via, QString data) : + Message(), + m_callsign(callsign), + m_to(to), + m_via(via), + m_data(data) + { } + }; + + //================================================================= + + PacketMod(DeviceAPI *deviceAPI); + ~PacketMod(); + virtual void destroy() { delete this; } + + virtual void start(); + virtual void stop(); + virtual void pull(SampleVector::iterator& begin, unsigned int nbSamples); + virtual bool handleMessage(const Message& cmd); + + virtual void getIdentifier(QString& id) { id = objectName(); } + virtual void getTitle(QString& title) { title = m_settings.m_title; } + virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 1; } + virtual int getNbSourceStreams() const { return 0; } + + virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const + { + (void) streamIndex; + (void) sinkElseSource; + return m_settings.m_inputFrequencyOffset; + } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage); + + virtual int webapiActionsPost( + const QStringList& channelActionsKeys, + SWGSDRangel::SWGChannelActions& query, + QString& errorMessage); + + static void webapiFormatChannelSettings( + SWGSDRangel::SWGChannelSettings& response, + const PacketModSettings& settings); + + static void webapiUpdateChannelSettings( + PacketModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } + double getMagSq() const; + void setLevelMeter(QObject *levelMeter); + uint32_t getNumberOfDeviceStreams() const; + + static const QString m_channelIdURI; + static const QString m_channelId; + +private: + enum RateState { + RSInitialFill, + RSRunning + }; + + DeviceAPI* m_deviceAPI; + QThread *m_thread; + PacketModBaseband* m_basebandSource; + PacketModSettings m_settings; + SpectrumVis m_spectrumVis; + + SampleVector m_sampleBuffer; + QMutex m_settingsMutex; + + int m_sampleRate; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void applySettings(const PacketModSettings& settings, bool force = false); + void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const PacketModSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + + +#endif /* PLUGINS_CHANNELTX_MODPACKET_PACKETMOD_H_ */ diff --git a/plugins/channeltx/modpacket/packetmodbaseband.cpp b/plugins/channeltx/modpacket/packetmodbaseband.cpp new file mode 100644 index 000000000..810cc3807 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodbaseband.cpp @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/upchannelizer.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" + +#include "packetmodbaseband.h" +#include "packetmod.h" + +MESSAGE_CLASS_DEFINITION(PacketModBaseband::MsgConfigurePacketModBaseband, Message) + +PacketModBaseband::PacketModBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(48000)); + m_channelizer = new UpChannelizer(&m_source); + + qDebug("PacketModBaseband::PacketModBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSourceFifo::dataRead, + this, + &PacketModBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +PacketModBaseband::~PacketModBaseband() +{ + delete m_channelizer; +} + +void PacketModBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void PacketModBaseband::pull(const SampleVector::iterator& begin, unsigned int nbSamples) +{ + unsigned int part1Begin, part1End, part2Begin, part2End; + m_sampleFifo.read(nbSamples, part1Begin, part1End, part2Begin, part2End); + SampleVector& data = m_sampleFifo.getData(); + + if (part1Begin != part1End) + { + std::copy( + data.begin() + part1Begin, + data.begin() + part1End, + begin + ); + } + + unsigned int shift = part1End - part1Begin; + + if (part2Begin != part2End) + { + std::copy( + data.begin() + part2Begin, + data.begin() + part2End, + begin + shift + ); + } +} + +void PacketModBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + SampleVector& data = m_sampleFifo.getData(); + unsigned int ipart1begin; + unsigned int ipart1end; + unsigned int ipart2begin; + unsigned int ipart2end; + qreal rmsLevel, peakLevel; + int numSamples; + + unsigned int remainder = m_sampleFifo.remainder(); + + while ((remainder > 0) && (m_inputMessageQueue.size() == 0)) + { + m_sampleFifo.write(remainder, ipart1begin, ipart1end, ipart2begin, ipart2end); + + if (ipart1begin != ipart1end) { // first part of FIFO data + processFifo(data, ipart1begin, ipart1end); + } + + if (ipart2begin != ipart2end) { // second part of FIFO data (used when block wraps around) + processFifo(data, ipart2begin, ipart2end); + } + + remainder = m_sampleFifo.remainder(); + } + + m_source.getLevels(rmsLevel, peakLevel, numSamples); + emit levelChanged(rmsLevel, peakLevel, numSamples); +} + +void PacketModBaseband::processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd) +{ + m_channelizer->prefetch(iEnd - iBegin); + m_channelizer->pull(data.begin() + iBegin, iEnd - iBegin); +} + +void PacketModBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool PacketModBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigurePacketModBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigurePacketModBaseband& cfg = (MsgConfigurePacketModBaseband&) cmd; + qDebug() << "PacketModBaseband::handleMessage: MsgConfigurePacketModBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (PacketMod::MsgTXPacketMod::match(cmd)) + { + PacketMod::MsgTXPacketMod& tx = (PacketMod::MsgTXPacketMod&) cmd; + m_source.addTXPacket(tx.m_callsign, tx.m_to, tx.m_via, tx.m_data); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "PacketModBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + return true; + } + else + { + qDebug() << "PacketModBaseband - Baseband got unknown message"; + return false; + } +} + +void PacketModBaseband::applySettings(const PacketModSettings& settings, bool force) +{ + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + m_channelizer->setChannelization(m_channelizer->getChannelSampleRate(), settings.m_inputFrequencyOffset); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + } + + m_source.applySettings(settings, force); + + m_settings = settings; +} + +int PacketModBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} diff --git a/plugins/channeltx/modpacket/packetmodbaseband.h b/plugins/channeltx/modpacket/packetmodbaseband.h new file mode 100644 index 000000000..5ed3388e1 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodbaseband.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PACKETMODBASEBAND_H +#define INCLUDE_PACKETMODBASEBAND_H + +#include +#include + +#include "dsp/samplesourcefifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "packetmodsource.h" + +class UpChannelizer; + +class PacketModBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigurePacketModBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const PacketModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigurePacketModBaseband* create(const PacketModSettings& settings, bool force) + { + return new MsgConfigurePacketModBaseband(settings, force); + } + + private: + PacketModSettings m_settings; + bool m_force; + + MsgConfigurePacketModBaseband(const PacketModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + PacketModBaseband(); + ~PacketModBaseband(); + void reset(); + void pull(const SampleVector::iterator& begin, unsigned int nbSamples); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + double getMagSq() const { return m_source.getMagSq(); } + int getChannelSampleRate() const; + void setSpectrumSampleSink(BasebandSampleSink* sampleSink) { m_source.setSpectrumSink(sampleSink); } + + +signals: + /** + * Level changed + * \param rmsLevel RMS level in range 0.0 - 1.0 + * \param peakLevel Peak level in range 0.0 - 1.0 + * \param numSamples Number of audio samples analyzed + */ + void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); + +private: + SampleSourceFifo m_sampleFifo; + UpChannelizer *m_channelizer; + PacketModSource m_source; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + PacketModSettings m_settings; + QMutex m_mutex; + + void processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd); + bool handleMessage(const Message& cmd); + void applySettings(const PacketModSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + + +#endif // INCLUDE_PACKETMODBASEBAND_H diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp new file mode 100644 index 000000000..7c755ca3c --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -0,0 +1,527 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "dsp/spectrumvis.h" +#include "device/deviceuiset.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "dsp/dspengine.h" +#include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicchannelsettingsdialog.h" +#include "gui/devicestreamselectiondialog.h" +#include "gui/fmpreemphasisdialog.h" +#include "mainwindow.h" + +#include "ui_packetmodgui.h" +#include "packetmodgui.h" +#include "packetmodrepeatdialog.h" +#include "packetmodtxsettingsdialog.h" + + +PacketModGUI* PacketModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx) +{ + PacketModGUI* gui = new PacketModGUI(pluginAPI, deviceUISet, channelTx); + return gui; +} + +void PacketModGUI::destroy() +{ + delete this; +} + +void PacketModGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString PacketModGUI::getName() const +{ + return objectName(); +} + +qint64 PacketModGUI::getCenterFrequency() const { + return m_channelMarker.getCenterFrequency(); +} + +void PacketModGUI::setCenterFrequency(qint64 centerFrequency) +{ + m_channelMarker.setCenterFrequency(centerFrequency); + applySettings(); +} + +void PacketModGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray PacketModGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool PacketModGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applySettings(true); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool PacketModGUI::handleMessage(const Message& message) +{ + if (PacketMod::MsgConfigurePacketMod::match(message)) + { + const PacketMod::MsgConfigurePacketMod& cfg = (PacketMod::MsgConfigurePacketMod&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +void PacketModGUI::channelMarkerChangedByCursor() +{ + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + +void PacketModGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void PacketModGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + +void PacketModGUI::on_rfBW_valueChanged(int value) +{ + float bw = value * 100.0f; + ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1)); + m_channelMarker.setBandwidth(bw); + m_settings.m_rfBandwidth = bw; + applySettings(); +} + +void PacketModGUI::on_fmDev_valueChanged(int value) +{ + ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_fmDeviation = value * 100.0; + applySettings(); +} + +void PacketModGUI::on_gain_valueChanged(int value) +{ + ui->gainText->setText(QString("%1dB").arg(value)); + m_settings.m_gain = value; + applySettings(); +} + +void PacketModGUI::on_channelMute_toggled(bool checked) +{ + m_settings.m_channelMute = checked; + applySettings(); +} + +void PacketModGUI::on_txButton_clicked(bool checked) +{ + transmit(); +} + +void PacketModGUI::on_packet_returnPressed() +{ + transmit(); +} + +void PacketModGUI::on_callsign_editingFinished() +{ + m_settings.m_callsign = ui->callsign->text(); + applySettings(); +} + +void PacketModGUI::on_to_currentTextChanged(const QString &text) +{ + m_settings.m_to = text; + applySettings(); +} + +void PacketModGUI::on_via_currentTextChanged(const QString &text) +{ + m_settings.m_via = text; + applySettings(); +} + +void PacketModGUI::on_packet_editingFinished() +{ + m_settings.m_data = ui->packet->text(); + applySettings(); +} + +void PacketModGUI::on_insertPosition_clicked(bool checked) +{ + float latitude = MainWindow::getInstance()->getMainSettings().getLatitude(); + float longitude = MainWindow::getInstance()->getMainSettings().getLongitude(); + + int latDeg, latMin, latFrac, latNorth; + int longDeg, longMin, longFrac, longEast; + char latBuf[10]; + char longBuf[11]; + + // Convert decimal latitude to degrees, min and hundreths of a minute + latNorth = latitude >= 0.0f; + latitude = abs(latitude); + latDeg = (int)latitude; + latitude -= (float)latDeg; + latitude *= 60.0f; + latMin = (int)latitude; + latitude -= (float)latMin; + latitude *= 100.0f; + latFrac = round(latitude); + + // Convert decimal longitude + longEast = longitude >= 0.0f; + longitude = abs(longitude); + longDeg = (int)longitude; + longitude -= (float)longDeg; + longitude *= 60.0f; + longMin = (int)longitude; + longitude -= (float)longMin; + longitude *= 100.0f; + longFrac = round(longitude); + + // Insert position with house symbol (-) in to data field + sprintf(latBuf, "%02d%02d.%02d%c", latDeg, latMin, latFrac, latNorth ? 'N' : 'S'); + sprintf(longBuf, "%03d%02d.%02d%c", longDeg, longMin, longFrac, longEast ? 'E' : 'W'); + QString packet = QString("%1/%2-").arg(latBuf).arg(longBuf); + ui->packet->insert(packet); +} + +void PacketModGUI::on_repeat_toggled(bool checked) +{ + m_settings.m_repeat = checked; + applySettings(); +} + +void PacketModGUI::on_preEmphasis_toggled(bool checked) +{ + m_settings.m_preEmphasis = checked; + applySettings(); +} + +void PacketModGUI::preEmphasisSelect() +{ + FMPreemphasisDialog dialog(m_settings.m_preEmphasisTau, m_settings.m_preEmphasisHighFreq); + if (dialog.exec() == QDialog::Accepted) + { + m_settings.m_preEmphasisTau = dialog.m_tau; + m_settings.m_preEmphasisHighFreq = dialog.m_highFreq; + applySettings(); + } +} + +void PacketModGUI::repeatSelect() +{ + PacketModRepeatDialog dialog(m_settings.m_repeatDelay, m_settings.m_repeatCount); + if (dialog.exec() == QDialog::Accepted) + { + m_settings.m_repeatDelay = dialog.m_repeatDelay; + m_settings.m_repeatCount = dialog.m_repeatCount; + applySettings(); + } +} + +void PacketModGUI::txSettingsSelect() +{ + PacketModTXSettingsDialog dialog(m_settings.m_rampUpBits, m_settings.m_rampDownBits, + m_settings.m_rampRange, m_settings.m_modulateWhileRamping, + m_settings.m_markFrequency, m_settings.m_spaceFrequency, + m_settings.m_ax25PreFlags, m_settings.m_ax25PostFlags, + m_settings.m_ax25Control, m_settings.m_ax25PID, + m_settings.m_lpfTaps, + m_settings.m_bbNoise, m_settings.m_rfNoise, + m_settings.m_writeToFile); + if (dialog.exec() == QDialog::Accepted) + { + m_settings.m_rampUpBits = dialog.m_rampUpBits; + m_settings.m_rampDownBits = dialog.m_rampDownBits; + m_settings.m_rampRange = dialog.m_rampRange; + m_settings.m_modulateWhileRamping = dialog.m_modulateWhileRamping; + m_settings.m_markFrequency = dialog.m_markFrequency; + m_settings.m_spaceFrequency = dialog.m_spaceFrequency; + m_settings.m_ax25PreFlags = dialog.m_ax25PreFlags; + m_settings.m_ax25PostFlags = dialog.m_ax25PostFlags; + m_settings.m_ax25Control = dialog.m_ax25Control; + m_settings.m_ax25PID = dialog.m_ax25PID; + m_settings.m_lpfTaps = dialog.m_lpfTaps; + m_settings.m_bbNoise = dialog.m_bbNoise; + m_settings.m_rfNoise = dialog.m_rfNoise; + m_settings.m_writeToFile = dialog.m_writeToFile; + applySettings(); + } +} + +void PacketModGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; +} + +void PacketModGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); + + setWindowTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); + } + else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) + { + DeviceStreamSelectionDialog dialog(this); + dialog.setNumberOfStreams(m_packetMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + displayStreamIndex(); + applySettings(); + } + + resetContextMenuType(); +} + +PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::PacketModGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_doApplySettings(true) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_packetMod = (PacketMod*) channelTx; + m_packetMod->setMessageQueueToGUI(getInputMessageQueue()); + + connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + m_spectrumVis = m_packetMod->getSpectrumVis(); + m_spectrumVis->setGLSpectrum(ui->glSpectrum); + + // Extra /2 here because SSB? + ui->glSpectrum->setCenterFrequency(m_settings.m_spectrumRate/4); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate/2); + ui->glSpectrum->setSsbSpectrum(true); + ui->glSpectrum->setDisplayCurrent(true); + ui->glSpectrum->setLsbDisplay(false); + ui->glSpectrum->setDisplayWaterfall(false); + ui->glSpectrum->setDisplayMaxHold(false); + ui->glSpectrum->setDisplayHistogram(false); + ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); + + CRightClickEnabler *repeatRightClickEnabler = new CRightClickEnabler(ui->repeat); + connect(repeatRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(repeatSelect())); + + CRightClickEnabler *txRightClickEnabler = new CRightClickEnabler(ui->txButton); + connect(txRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(txSettingsSelect())); + + CRightClickEnabler *preempRightClickEnabler = new CRightClickEnabler(ui->preEmphasis); + connect(preempRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(preEmphasisSelect())); + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(Qt::red); + m_channelMarker.setBandwidth(12500); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("Packet Modulator"); + m_channelMarker.setSourceOrSinkStream(false); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + m_deviceUISet->registerTxChannelInstance(PacketMod::m_channelIdURI, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); + + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); + m_packetMod->setLevelMeter(ui->volumeMeter); + + m_settings.setChannelMarker(&m_channelMarker); + + ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); + + displaySettings(); + applySettings(); +} + +PacketModGUI::~PacketModGUI() +{ + m_deviceUISet->removeTxChannelInstance(this); + delete m_packetMod; // TODO: check this: when the GUI closes it has to delete the modulator + delete ui; +} + +void PacketModGUI::transmit() +{ + QString callsign = ui->callsign->text(); + QString to = ui->to->currentText(); + QString via = ui->via->currentText(); + QString data = ui->packet->text(); + // TODO: Any validation? + QString str = callsign + ">" + to + "," + via + ":" + data; + ui->transmittedText->appendPlainText(str + "\n"); + PacketMod::MsgTXPacketMod *msg = PacketMod::MsgTXPacketMod::create(callsign, to, via, data); + m_packetMod->getInputMessageQueue()->push(msg); +} + +void PacketModGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void PacketModGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + PacketMod::MsgConfigurePacketMod *msg = PacketMod::MsgConfigurePacketMod::create(m_settings, force); + m_packetMod->getInputMessageQueue()->push(msg); + } +} + +void PacketModGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.setBandwidth(m_settings.m_rfBandwidth); + m_channelMarker.blockSignals(false); + m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only + + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_channelMarker.getTitle()); + displayStreamIndex(); + + blockApplySettings(true); + + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + + ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); + ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0); + + ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1)); + ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0); + + ui->gainText->setText(QString("%1").arg((double)m_settings.m_gain, 0, 'f', 1)); + ui->gain->setValue(m_settings.m_gain); + + ui->channelMute->setChecked(m_settings.m_channelMute); + ui->repeat->setChecked(m_settings.m_repeat); + + ui->callsign->setText(m_settings.m_callsign); + ui->to->lineEdit()->setText(m_settings.m_to); + ui->via->lineEdit()->setText(m_settings.m_via); + ui->packet->setText(m_settings.m_data); + + blockApplySettings(false); +} + +void PacketModGUI::displayStreamIndex() +{ + if (m_deviceUISet->m_deviceMIMOEngine) { + setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); + } else { + setStreamIndicator("S"); // single channel indicator + } +} + +void PacketModGUI::leaveEvent(QEvent*) +{ + m_channelMarker.setHighlighted(false); +} + +void PacketModGUI::enterEvent(QEvent*) +{ + m_channelMarker.setHighlighted(true); +} + +void PacketModGUI::tick() +{ + double powDb = CalcDb::dbPower(m_packetMod->getMagSq()); + m_channelPowerDbAvg(powDb); + ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); +} diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h new file mode 100644 index 000000000..0c0cd4813 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 PLUGINS_CHANNELTX_MODPACKET_PACKETMODGUI_H_ +#define PLUGINS_CHANNELTX_MODPACKET_PACKETMODGUI_H_ + +#include +#include "gui/rollupwidget.h" +#include "dsp/channelmarker.h" +#include "util/movingaverage.h" +#include "util/messagequeue.h" + +#include "packetmod.h" +#include "packetmodsettings.h" + +class PluginAPI; +class DeviceUISet; +class BasebandSampleSource; +class SpectrumVis; + +namespace Ui { + class PacketModGUI; +} + +class PacketModGUI : public RollupWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + static PacketModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx); + virtual void destroy(); + + void setName(const QString& name); + QString getName() const; + virtual qint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual bool handleMessage(const Message& message); + +public slots: + void channelMarkerChangedByCursor(); + +private: + Ui::PacketModGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + PacketModSettings m_settings; + bool m_doApplySettings; + SpectrumVis* m_spectrumVis; + + PacketMod* m_packetMod; + MovingAverageUtil m_channelPowerDbAvg; // Less than other mods, as packets are short + + MessageQueue m_inputMessageQueue; + + explicit PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent = 0); + virtual ~PacketModGUI(); + + void transmit(); + void blockApplySettings(bool block); + void applySettings(bool force = false); + void displaySettings(); + void displayStreamIndex(); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + +private slots: + void handleSourceMessages(); + + void on_deltaFrequency_changed(qint64 value); + void on_rfBW_valueChanged(int index); + void on_fmDev_valueChanged(int value); + void on_gain_valueChanged(int value); + void on_channelMute_toggled(bool checked); + void on_txButton_clicked(bool checked); + void on_callsign_editingFinished(); + void on_to_currentTextChanged(const QString &text); + void on_via_currentTextChanged(const QString &text); + void on_packet_editingFinished(); + void on_insertPosition_clicked(bool checked); + void on_packet_returnPressed(); + void on_repeat_toggled(bool checked); + void on_preEmphasis_toggled(bool checked); + void preEmphasisSelect(); + void repeatSelect(); + void txSettingsSelect(); + + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + + void tick(); +}; + +#endif /* PLUGINS_CHANNELTX_MODPACKET_PACKETMODGUI_H_ */ diff --git a/plugins/channeltx/modpacket/packetmodgui.ui b/plugins/channeltx/modpacket/packetmodgui.ui new file mode 100644 index 000000000..6ae31b79b --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodgui.ui @@ -0,0 +1,817 @@ + + + PacketModGUI + + + + 0 + 0 + 363 + 610 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Liberation Sans + 9 + + + + Qt::StrongFocus + + + Packet Modulator + + + + true + + + + 2 + 2 + 341 + 181 + + + + + 280 + 0 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + Channel power + + + Qt::RightToLeft + + + -100.0 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Mute/Unmute channel + + + ... + + + + :/txon.png + :/txoff.png:/txon.png + + + true + + + + + + + + + + + + 90 + 0 + + + + + 60 + 16777215 + + + + Baud rate and modulation + + + + 1200 AFSK + + + + + + + + Qt::Vertical + + + + + + + BW + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + RF bandwidth + + + 10 + + + 400 + + + 1 + + + 100 + + + Qt::Horizontal + + + + + + + + 30 + 0 + + + + 10.0k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Dev + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Frequency deviation + + + 10 + + + 60 + + + 1 + + + 50 + + + Qt::Horizontal + + + + + + + + 30 + 0 + + + + 5.0k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + Gain + + + + + + + + 24 + 24 + + + + Gain + + + -60 + + + 0 + + + 5 + + + 1 + + + 0 + + + + + + + + 30 + 0 + + + + Audio input gain value + + + + + + -80.0dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + Liberation Mono + 8 + + + + Level (% full range) top trace: average, bottom trace: instantaneous peak, tip: peak hold + + + + + + + + + Qt::Horizontal + + + + + + + + + Callsign + + + + + + + Enter your amateur radio callsign and optionally a SSID. E.g. M7RCE or M7RCE-1 + + + MYCALL + + + 10 + + + + + + + Preemphasis filter. Right click for additional settings. + + + ... + + + + :/filter_highpass.png:/filter_highpass.png + + + + + + + Repeatedly transmit the packet. Right click for additional settings. + + + ... + + + + :/playloop.png:/playloop.png + + + + + + + Insert position (latitude and longitude) + + + ... + + + + :/gps.png + + + + + + + + + + + + + 0 + 0 + + + + To + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + Enter destination + + + true + + + APRS + + + 0 + + + + APRS + + + + + APZ + + + + + CQ + + + + + BEACON + + + + + CALLSIGN-SSID + + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + Via + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + Enter routing + + + true + + + + WIDE2-2 + + + + + ARISS + + + + + + + + + + + + Qt::LeftToRight + + + Data + + + + + + + Enter data to transmit. + +APRS examples: +>Status +:Addressee:Message +!Position + + + >Using SDRangel + + + 256 + + + + + + + Press to transmit the packet + + + TX + + + + + + + + + + + 0 + 190 + 351 + 141 + + + + Transmitted Packets + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + true + + + + + + + + + 0 + 340 + 351 + 284 + + + + Baseband Spectrum + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 200 + 250 + + + + + Liberation Mono + 8 + + + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + GLSpectrum + QWidget +
gui/glspectrum.h
+ 1 +
+ + GLSpectrumGUI + QWidget +
gui/glspectrumgui.h
+ 1 +
+ + LevelMeterVU + QWidget +
gui/levelmeter.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + deltaFrequency + channelMute + mode + rfBW + fmDev + gain + callsign + preEmphasis + repeat + insertPosition + to + via + packet + txButton + transmittedText + + + + + +
diff --git a/plugins/channeltx/modpacket/packetmodplugin.cpp b/plugins/channeltx/modpacket/packetmodplugin.cpp new file mode 100644 index 000000000..86c423717 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodplugin.cpp @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "packetmodgui.h" +#endif +#include "packetmod.h" +#include "packetmodwebapiadapter.h" +#include "packetmodplugin.h" + +const PluginDescriptor PacketModPlugin::m_pluginDescriptor = { + PacketMod::m_channelId, + QString("Packet Modulator"), + QString("4.16.1"), + QString("(c) Jon Beniston, M7RCE"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +PacketModPlugin::PacketModPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& PacketModPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void PacketModPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + m_pluginAPI->registerTxChannel(PacketMod::m_channelIdURI, PacketMod::m_channelId, this); +} + +#ifdef SERVER_MODE +PluginInstanceGUI* PacketModPlugin::createTxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSource *txChannel) const +{ + return 0; +} +#else +PluginInstanceGUI* PacketModPlugin::createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) const +{ + return PacketModGUI::create(m_pluginAPI, deviceUISet, txChannel); +} +#endif + +BasebandSampleSource* PacketModPlugin::createTxChannelBS(DeviceAPI *deviceAPI) const +{ + return new PacketMod(deviceAPI); +} + +ChannelAPI* PacketModPlugin::createTxChannelCS(DeviceAPI *deviceAPI) const +{ + return new PacketMod(deviceAPI); +} + +ChannelWebAPIAdapter* PacketModPlugin::createChannelWebAPIAdapter() const +{ + return new PacketModWebAPIAdapter(); +} diff --git a/plugins/channeltx/modpacket/packetmodplugin.h b/plugins/channeltx/modpacket/packetmodplugin.h new file mode 100644 index 000000000..16c317e66 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodplugin.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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_PACKETMODPLUGIN_H +#define INCLUDE_PACKETMODPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSource; + +class PacketModPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.channeltx.packetmod") + +public: + explicit PacketModPlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual PluginInstanceGUI* createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *rxChannel) const; + virtual BasebandSampleSource* createTxChannelBS(DeviceAPI *deviceAPI) const; + virtual ChannelAPI* createTxChannelCS(DeviceAPI *deviceAPI) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_PACKETMODPLUGIN_H diff --git a/plugins/channeltx/modpacket/packetmodrepeatdialog.cpp b/plugins/channeltx/modpacket/packetmodrepeatdialog.cpp new file mode 100644 index 000000000..3a9106982 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodrepeatdialog.cpp @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "packetmodrepeatdialog.h" +#include "packetmodsettings.h" +#include + +PacketModRepeatDialog::PacketModRepeatDialog(float repeatDelay, int repeatCount, QWidget* parent) : + QDialog(parent), + ui(new Ui::PacketModRepeatDialog) +{ + ui->setupUi(this); + ui->repeatDelay->setValue(repeatDelay); + QLineEdit *edit = ui->repeatCount->lineEdit(); + if (edit) + { + if (repeatCount == PacketModSettings::infinitePackets) + edit->setText("Infinite"); + else + edit->setText(QString("%1").arg(repeatCount)); + } +} + +PacketModRepeatDialog::~PacketModRepeatDialog() +{ + delete ui; +} + +void PacketModRepeatDialog::accept() +{ + m_repeatDelay = ui->repeatDelay->value(); + QString text = ui->repeatCount->currentText(); + if (!text.compare(QString("Infinite"), Qt::CaseInsensitive)) + m_repeatCount = PacketModSettings::infinitePackets; + else + m_repeatCount = text.toUInt(); + QDialog::accept(); +} diff --git a/plugins/channeltx/modpacket/packetmodrepeatdialog.h b/plugins/channeltx/modpacket/packetmodrepeatdialog.h new file mode 100644 index 000000000..2398d5787 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodrepeatdialog.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PACKETMODREPEATDIALOG_H +#define INCLUDE_PACKETMODREPEATDIALOG_H + +#include "ui_packetmodrepeatdialog.h" + +class PacketModRepeatDialog : public QDialog { + Q_OBJECT + +public: + explicit PacketModRepeatDialog(float repeatDelay, int repeatCount, QWidget* parent = 0); + ~PacketModRepeatDialog(); + + float m_repeatDelay; // Delay in seconds between packets + int m_repeatCount; // Number of packets to transmit (-1 = infinite) + +private slots: + void accept(); + +private: + Ui::PacketModRepeatDialog* ui; +}; + +#endif // INCLUDE_PACKETMODREPEATDIALOG_H diff --git a/plugins/channeltx/modpacket/packetmodrepeatdialog.ui b/plugins/channeltx/modpacket/packetmodrepeatdialog.ui new file mode 100644 index 000000000..cb0bf8f15 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodrepeatdialog.ui @@ -0,0 +1,134 @@ + + + PacketModRepeatDialog + + + + 0 + 0 + 351 + 115 + + + + + Liberation Sans + 9 + + + + Packet Repeat Settings + + + + + + + + + Delay between packets (s) + + + + + + + Packets to transmit + + + + + + + Number of packets to transmit + + + true + + + + Infinite + + + + + 10 + + + + + 100 + + + + + 1000 + + + + + + + + 3 + + + 1000000.000000000000000 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + repeatDelay + repeatCount + + + + + buttonBox + accepted() + PacketModRepeatDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PacketModRepeatDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/channeltx/modpacket/packetmodsettings.cpp b/plugins/channeltx/modpacket/packetmodsettings.cpp new file mode 100644 index 000000000..68f0ae8dc --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodsettings.cpp @@ -0,0 +1,204 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "dsp/dspengine.h" +#include "util/simpleserializer.h" +#include "settings/serializable.h" +#include "packetmodsettings.h" + +PacketModSettings::PacketModSettings() +{ + resetToDefaults(); +} + +void PacketModSettings::resetToDefaults() +{ + m_inputFrequencyOffset = 0; + m_baud = 1200; + m_rfBandwidth = 10000.0f; + m_fmDeviation = 2500.0f; + m_gain = -2.0f; // To avoid overflow, which results in out-of-band RF + m_channelMute = false; + m_repeat = false; + m_repeatDelay = 1.0f; + m_repeatCount = infinitePackets; + m_rampUpBits = 8; + m_rampDownBits = 8; + m_rampRange = 60; + m_modulateWhileRamping = true; + m_markFrequency = 2200; + m_spaceFrequency = 1200; + m_ax25PreFlags = 5; + m_ax25PostFlags = 2; + m_ax25Control = 3; + m_ax25PID = 0xf0; + m_preEmphasis = false; + m_preEmphasisTau = 50e-6f; + m_preEmphasisHighFreq = 12000.0f; + m_lpfTaps = 301; + m_bbNoise = false; + m_rfNoise = false; + m_writeToFile = false; + m_spectrumRate = 8000; + m_callsign = "MYCALL"; + m_to = "APRS"; + m_via = "WIDE2-2"; + m_data = ">Using SDRangel"; + m_rgbColor = QColor(255, 0, 0).rgb(); + m_title = "Packet Modulator"; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; +} + +QByteArray PacketModSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_inputFrequencyOffset); + s.writeS32(2, m_baud); + s.writeReal(3, m_rfBandwidth); + s.writeReal(4, m_fmDeviation); + s.writeReal(5, m_gain); + s.writeBool(6, m_channelMute); + s.writeBool(7, m_repeat); + s.writeReal(8, m_repeatDelay); + s.writeS32(9, m_repeatCount); + s.writeS32(10, m_rampUpBits); + s.writeS32(11, m_rampDownBits); + s.writeS32(12, m_rampRange); + s.writeBool(13, m_modulateWhileRamping); + s.writeS32(14, m_markFrequency); + s.writeS32(15, m_spaceFrequency); + s.writeS32(16, m_ax25PreFlags); + s.writeS32(17, m_ax25PostFlags); + s.writeS32(18, m_ax25Control); + s.writeS32(19, m_ax25PID); + s.writeBool(20, m_preEmphasis); + s.writeReal(21, m_preEmphasisTau); + s.writeReal(22, m_preEmphasisHighFreq); + s.writeS32(23, m_lpfTaps); + s.writeBool(24, m_bbNoise); + s.writeBool(25, m_rfNoise); + s.writeBool(26, m_writeToFile); + s.writeString(27, m_callsign); + s.writeString(28, m_to); + s.writeString(29, m_via); + s.writeString(30, m_data); + + s.writeU32(31, m_rgbColor); + s.writeString(32, m_title); + if (m_channelMarker) { + s.writeBlob(33, m_channelMarker->serialize()); + } + s.writeS32(34, m_streamIndex); + s.writeBool(35, m_useReverseAPI); + s.writeString(36, m_reverseAPIAddress); + s.writeU32(37, m_reverseAPIPort); + s.writeU32(38, m_reverseAPIDeviceIndex); + s.writeU32(39, m_reverseAPIChannelIndex); + + return s.final(); +} + +bool PacketModSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + qint32 tmp; + uint32_t utmp; + + d.readS32(1, &tmp, 0); + m_inputFrequencyOffset = tmp; + d.readS32(2, &m_baud, 1200); + d.readReal(3, &m_rfBandwidth, 12500.0f); + d.readReal(4, &m_fmDeviation, 2500.0f); + d.readReal(5, &m_gain, 0.0f); + d.readBool(6, &m_channelMute, false); + d.readBool(7, &m_repeat, false); + d.readReal(8, &m_repeatDelay, 1.0f); + d.readS32(9, &m_repeatCount, -1); + d.readS32(10, &m_rampUpBits, 8); + d.readS32(11, &m_rampDownBits, 8); + d.readS32(12, &m_rampRange, 8); + d.readBool(13, &m_modulateWhileRamping, true); + d.readS32(14, &m_markFrequency, 5); + d.readS32(15, &m_spaceFrequency, 5); + d.readS32(16, &m_ax25PreFlags, 5); + d.readS32(17, &m_ax25PostFlags, 2); + d.readS32(18, &m_ax25Control, 3); + d.readS32(19, &m_ax25PID, 0xf0); + d.readBool(20, &m_preEmphasis, false); + d.readReal(21, &m_preEmphasisTau, 50e-6f); + d.readReal(22, &m_preEmphasisHighFreq, 12000.0f); + d.readS32(23, &m_lpfTaps, 301); + d.readBool(24, &m_bbNoise, false); + d.readBool(25, &m_rfNoise, false); + d.readBool(26, &m_writeToFile, false); + d.readString(27, &m_callsign, "MYCALL"); + d.readString(28, &m_to, "APRS"); + d.readString(29, &m_via, "WIDE2-2"); + d.readString(30, &m_data, ">Using SDRangel"); + + d.readU32(31, &m_rgbColor); + d.readString(32, &m_title, "Packet Modulator"); + + if (m_channelMarker) { + d.readBlob(33, &bytetmp); + m_channelMarker->deserialize(bytetmp); + } + + d.readS32(34, &m_streamIndex, 0); + + d.readBool(35, &m_useReverseAPI, false); + d.readString(36, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(37, &utmp, 0); + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + d.readU32(38, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(39, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + + return true; + } + else + { + qDebug() << "PacketModSettings::deserialize: ERROR"; + resetToDefaults(); + return false; + } +} diff --git a/plugins/channeltx/modpacket/packetmodsettings.h b/plugins/channeltx/modpacket/packetmodsettings.h new file mode 100644 index 000000000..2514bd2c0 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodsettings.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 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 PLUGINS_CHANNELTX_MODPACKET_PACKETMODSETTINGS_H +#define PLUGINS_CHANNELTX_MODPACKET_PACKETMODSETTINGS_H + +#include +#include +#include "dsp/dsptypes.h" + +class Serializable; + +struct PacketModSettings +{ + static const int infinitePackets = -1; + + qint64 m_inputFrequencyOffset; + int m_baud; + Real m_rfBandwidth; + Real m_fmDeviation; + Real m_gain; + bool m_channelMute; + bool m_repeat; + Real m_repeatDelay; + int m_repeatCount; + int m_rampUpBits; + int m_rampDownBits; + int m_rampRange; + bool m_modulateWhileRamping; + int m_markFrequency; + int m_spaceFrequency; + int m_ax25PreFlags; + int m_ax25PostFlags; + int m_ax25Control; + int m_ax25PID; + bool m_preEmphasis; + float m_preEmphasisTau; + float m_preEmphasisHighFreq; + int m_lpfTaps; + bool m_bbNoise; + bool m_rfNoise; + bool m_writeToFile; + int m_spectrumRate; + QString m_callsign; + QString m_to; + QString m_via; + QString m_data; + quint32 m_rgbColor; + QString m_title; + Serializable *m_channelMarker; + int m_streamIndex; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + + PacketModSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_CHANNELTX_MODPACKET_PACKETMODSETTINGS_H */ diff --git a/plugins/channeltx/modpacket/packetmodsource.cpp b/plugins/channeltx/modpacket/packetmodsource.cpp new file mode 100644 index 000000000..2e0d9f43b --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodsource.cpp @@ -0,0 +1,482 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "dsp/basebandsamplesink.h" +#include "packetmodsource.h" +#include "util/crc.h" + +PacketModSource::PacketModSource() : + m_channelSampleRate(48000), + m_preemphasisFilter(48000, FMPREEMPHASIS_TAU_US), + m_channelFrequencyOffset(0), + m_magsq(0.0), + m_audioPhase(0.0f), + m_fmPhase(0.0f), + m_levelCalcCount(0), + m_peakLevel(0.0f), + m_levelSum(0.0f), + m_bitCount(0), + m_byteIdx(0), + m_bitIdx(0), + m_last5Bits(0), + m_state(idle) +{ + m_lowpass.create(301, m_channelSampleRate, 22000.0 / 2.0); + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +PacketModSource::~PacketModSource() +{ +} + +void PacketModSource::pull(SampleVector::iterator begin, unsigned int nbSamples) +{ + std::for_each( + begin, + begin + nbSamples, + [this](Sample& s) { + pullOne(s); + } + ); +} + +void PacketModSource::pullOne(Sample& sample) +{ + if (m_settings.m_channelMute) + { + sample.m_real = 0.0f; + sample.m_imag = 0.0f; + return; + } + + // Calculate next sample + modulateSample(); + + // Shift to carrier frequency + Complex ci = m_modSample; + ci *= m_carrierNco.nextIQ(); + + // Calculate power + double magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); + m_movingAverage(magsq); + m_magsq = m_movingAverage.asDouble(); + + // Convert from float to fixed point + sample.m_real = (FixReal) (ci.real() * SDR_TX_SCALEF); + sample.m_imag = (FixReal) (ci.imag() * SDR_TX_SCALEF); +} + +void PacketModSource::prefetch(unsigned int nbSamples) +{ +} + +void PacketModSource::sampleToSpectrum(Real sample) +{ + if (m_spectrumSink) + { + Complex out; + Complex in; + in.real(sample); + in.imag(0.0f); + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, in, &out)) + { + sample = std::real(out); + m_sampleBuffer.push_back(Sample(sample * 0.891235351562f * SDR_TX_SCALEF, 0.0f)); + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true); + m_sampleBuffer.clear(); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } +} + +void PacketModSource::modulateSample() +{ + Real audioMod; + Real theta; + Real f_delta; + Real linearGain; + Real linearRampGain; + Real emphasis; + + if ((m_state == idle) || (m_state == wait)) + { + audioMod = 0.0f; + m_modSample.real(audioMod); + m_modSample.imag(0); + calculateLevel(audioMod); + sampleToSpectrum(audioMod); + if (m_state == wait) + { + m_waitCounter--; + if (m_waitCounter == 0) + initTX(); + } + } + else + { + // Bell 202 AFSK + if (m_sampleIdx == 0) + { + if (bitsValid()) + { + // NRZI encoding - encode 0 as change of freq, 1 no change + if (getBit() == 0) + m_f = m_f == m_settings.m_markFrequency ? m_settings.m_spaceFrequency : m_settings.m_markFrequency; + } + // Should we start ramping down power? + if ((m_bitCount < m_settings.m_rampDownBits) || ((m_bitCount == 0) && !m_settings.m_rampDownBits)) + { + m_state = ramp_down; + if (m_settings.m_rampDownBits > 0) + m_powRamp = -m_settings.m_rampRange/(m_settings.m_rampDownBits * (Real)m_samplesPerSymbol); + } + } + m_sampleIdx++; + if (m_sampleIdx > m_samplesPerSymbol) + m_sampleIdx = 0; + + if (!m_settings.m_bbNoise) + audioMod = sin(m_audioPhase); + else + audioMod = (Real)rand()/((Real)RAND_MAX)-0.5; // Noise to test filter frequency response + if ((m_state == tx) || m_settings.m_modulateWhileRamping) + m_audioPhase += (M_PI * 2.0f * m_f) / (m_channelSampleRate); + if (m_audioPhase > M_PI) + m_audioPhase -= (2.0f * M_PI); + + // Preemphasis filter + if (m_settings.m_preEmphasis) + audioMod = m_preemphasisFilter.filter(audioMod); + + if (m_audioFile.is_open()) + m_audioFile << audioMod << "\n"; + + // Display baseband audio in spectrum analyser + sampleToSpectrum(audioMod); + + // FM + m_fmPhase += audioMod; + if (m_fmPhase > M_PI) + m_fmPhase -= (2.0f * M_PI); + f_delta = m_settings.m_fmDeviation / m_channelSampleRate; + theta = 2.0f * M_PI * f_delta * m_fmPhase; + + linearRampGain = powf(10.0f, m_pow/20.0f); + linearGain = powf(10.0f, m_settings.m_gain/20.0f); + + if (!m_settings.m_rfNoise) + { + m_modSample.real(linearGain * linearRampGain * cos(theta)); + m_modSample.imag(linearGain * linearRampGain * sin(theta)); + } + else + { + // Noise to test filter frequency response + m_modSample.real(linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); + m_modSample.imag(linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); + } + + // Apply low pass filter to limit RF BW + m_modSample = m_lowpass.filter(m_modSample); + + // Ramp up/down power at start/end of packet + if ((m_state == ramp_up) || (m_state == ramp_down)) + { + m_pow += m_powRamp; + if ((m_state == ramp_up) && (m_pow >= 0.0f)) + { + // Finished ramp up, transmit at full gain + m_state = tx; + m_pow = 0.0f; + } + else if ((m_state == ramp_down) && ( (m_settings.m_rampRange == 0) + || (m_settings.m_rampDownBits == 0) + || (m_pow <= -(Real)m_settings.m_rampRange) + )) + { + m_state = idle; + // Do we need to retransmit the packet? + if (m_settings.m_repeat) + { + if (m_packetRepeatCount > 0) + m_packetRepeatCount--; + if ((m_packetRepeatCount == PacketModSettings::infinitePackets) || (m_packetRepeatCount > 0)) + { + if (m_settings.m_repeatDelay > 0.0f) + { + // Wait before retransmitting + m_state = wait; + m_waitCounter = m_settings.m_repeatDelay * m_channelSampleRate; + } + else + { + // Retransmit immediately + initTX(); + } + } + } + } + } + + Real s = std::real(m_modSample); + calculateLevel(s); + } +} + +void PacketModSource::calculateLevel(Real& sample) +{ + if (m_levelCalcCount < m_levelNbSamples) + { + m_peakLevel = std::max(std::fabs(m_peakLevel), sample); + m_levelSum += sample * sample; + m_levelCalcCount++; + } + else + { + m_rmsLevel = sqrt(m_levelSum / m_levelNbSamples); + m_peakLevelOut = m_peakLevel; + m_peakLevel = 0.0f; + m_levelSum = 0.0f; + m_levelCalcCount = 0; + } +} + +void PacketModSource::applySettings(const PacketModSettings& settings, bool force) +{ + if ((settings.m_lpfTaps != m_settings.m_lpfTaps) || (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + { + qDebug() << "PacketModSource::applySettings: Creating new lpf with taps " << settings.m_lpfTaps << " rfBW " << settings.m_rfBandwidth; + m_lowpass.create(settings.m_lpfTaps, m_channelSampleRate, settings.m_rfBandwidth / 2.0); + } + if ((settings.m_preEmphasisTau != m_settings.m_preEmphasisTau) || (settings.m_preEmphasisHighFreq != m_settings.m_preEmphasisHighFreq) || force) + { + qDebug() << "PacketModSource::applySettings: Creating new preemphasis filter with tau " << settings.m_preEmphasisTau << " highFreq " << settings.m_preEmphasisHighFreq << " sampleRate " << m_channelSampleRate; + m_preemphasisFilter.configure(m_channelSampleRate, settings.m_preEmphasisTau, settings.m_preEmphasisHighFreq); + } + + m_settings = settings; +} + +void PacketModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "PacketModSource::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset + << " rfBandwidth: " << m_settings.m_rfBandwidth + << " spectrumRate: " << m_settings.m_spectrumRate; + + if ((channelFrequencyOffset != m_channelFrequencyOffset) + || (channelSampleRate != m_channelSampleRate) || force) + { + m_carrierNco.setFreq(channelFrequencyOffset, channelSampleRate); + } + + if ((m_channelSampleRate != channelSampleRate) || force) + { + m_preemphasisFilter.configure(channelSampleRate, m_settings.m_preEmphasisTau); + m_lowpass.create(m_settings.m_lpfTaps, channelSampleRate, m_settings.m_rfBandwidth / 2.0); + m_interpolatorDistanceRemain = 0; + m_interpolatorConsumed = false; + m_interpolatorDistance = (Real) channelSampleRate / (Real) m_settings.m_spectrumRate; + m_interpolator.create(48, m_settings.m_spectrumRate, m_settings.m_spectrumRate / 2.2, 3.0); + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +static uint8_t *ax25_address(uint8_t *p, QString address, uint8_t crrl) +{ + int len; + int i; + QByteArray b; + int ssid; + + len = address.length(); + b = address.toUtf8(); + ssid = 0; + for (i = 0; i < 6; i++) + { + if ((i < len) && (ssid == 0)) + { + if (b[i] == '-') + { + if (len > i + 1) + { + ssid = b[i+1] - '0'; + if ((len > i + 2) && isdigit(b[i+2])) { + ssid = (ssid*10) + (b[i+1] - '0'); + } + if (ssid >= 16) + qDebug() << "ax25_address: SSID greater than 15 not supported"; + } + else + qDebug() << "ax25_address: SSID number missing"; + *p++ = ' ' << 1; + } + else + { + *p++ = b[i] << 1; + } + } + else + { + *p++ = ' ' << 1; + } + } + *p++ = crrl | (ssid << 1); + + return p; +} + +bool PacketModSource::bitsValid() +{ + return m_bitCount > 0; +} + +int PacketModSource::getBit() +{ + int bit; + + if (m_bitCount > 0) + { + bit = (m_bits[m_byteIdx] >> m_bitIdx) & 1; + m_bitIdx++; + m_bitCount--; + if (m_bitIdx == 8) + { + m_byteIdx++; + m_bitIdx = 0; + } + } + else + bit = 0; + + return bit; +} + +void PacketModSource::addBit(int bit) +{ + // Transmit LSB first + m_bits[m_byteIdx] |= bit << m_bitIdx; + m_bitIdx++; + m_bitCount++; + m_bitCountTotal++; + if (m_bitIdx == 8) + { + m_byteIdx++; + m_bits[m_byteIdx] = 0; + m_bitIdx = 0; + } + m_last5Bits = ((m_last5Bits << 1) | bit) & 0x1f; +} + +void PacketModSource::initTX() +{ + m_byteIdx = 0; + m_bitIdx = 0; + m_bitCount = m_bitCountTotal; // Reset to allow retransmission + m_f = m_settings.m_spaceFrequency; + if (m_settings.m_rampUpBits == 0) + { + m_state = tx; + m_pow = 0.0f; + } + else + { + m_state = ramp_up; + m_pow = -(Real)m_settings.m_rampRange; + m_powRamp = m_settings.m_rampRange/(m_settings.m_rampUpBits * (Real)m_samplesPerSymbol); + } +} + +void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QString data) +{ + uint8_t packet[AX25_MAX_BYTES]; + uint8_t *crc_start; + uint8_t *p; + crc16x25 crc; + uint16_t crcValue; + int len; + 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; + // Dest + p = ax25_address(p, to, 0xe0); + // From + p = ax25_address(p, callsign, 0x60); + // Via + p = ax25_address(p, via, 0x61); + // Control + *p++ = m_settings.m_ax25Control; + // PID + *p++ = m_settings.m_ax25PID; + // Data + len = data.length(); + memcpy(p, data.toUtf8(), len); + p += len; + // CRC (do not include flags) + crc.calculate(crc_start, p-crc_start); + crcValue = crc.get(); + *p++ = crcValue & 0xff; + *p++ = (crcValue >> 8); + // Flag + for (int i = 0; i < std::min(m_settings.m_ax25PostFlags, AX25_MAX_FLAGS); i++) + *p++ = AX25_FLAG; + + packet_length = p-&packet[0]; + + // HDLC bit stuffing + m_byteIdx = 0; + m_bitIdx = 0; + m_last5Bits = 0; + m_bitCount = 0; + m_bitCountTotal = 0; + for (int i = 0; i < packet_length; i++) + { + for (int j = 0; j < 8; j++) + { + int tx_bit = (packet[i] >> j) & 1; + // Stuff 0 if last 5 bits are 1s + if ((packet[i] != AX25_FLAG) && (m_last5Bits == 0x1f)) + addBit(0); + addBit(tx_bit); + } + } + m_samplesPerSymbol = m_channelSampleRate / m_settings.m_baud; + m_packetRepeatCount = m_settings.m_repeatCount; + initTX(); + // Only reset phases at start of new packet TX, not in initTX(), so that + // there isn't a discontinuity in phase when repeatedly transmitting a + // single tone + m_sampleIdx = 0; + m_audioPhase = 0.0f; + m_fmPhase = 0.0f; + + if (m_settings.m_writeToFile) + m_audioFile.open("packetmod.csv", std::ofstream::out); +} diff --git a/plugins/channeltx/modpacket/packetmodsource.h b/plugins/channeltx/modpacket/packetmodsource.h new file mode 100644 index 000000000..d48cd36a0 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodsource.h @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PACKETMODSOURCE_H +#define INCLUDE_PACKETMODSOURCE_H + +#include +#include + +#include +#include + +#include "dsp/channelsamplesource.h" +#include "dsp/nco.h" +#include "dsp/ncof.h" +#include "dsp/interpolator.h" +#include "dsp/lowpass.h" +#include "dsp/bandpass.h" +#include "dsp/fmpreemphasis.h" +#include "util/movingaverage.h" + +#include "packetmodsettings.h" + +#define AX25_MAX_FLAGS 1024 +#define AX25_MAX_BYTES (2*AX25_MAX_FLAGS+1+28+2+256+2+1) +#define AX25_MAX_BITS (AX25_MAX_BYTES*2) +#define AX25_FLAG 0x7e +#define AX25_NO_L3 0xf0 + +class BasebandSampleSink; + +class PacketModSource : public ChannelSampleSource +{ +public: + PacketModSource(); + virtual ~PacketModSource(); + + virtual void pull(SampleVector::iterator begin, unsigned int nbSamples); + virtual void pullOne(Sample& sample); + virtual void prefetch(unsigned int nbSamples); + + double getMagSq() const { return m_magsq; } + void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const + { + rmsLevel = m_rmsLevel; + peakLevel = m_peakLevelOut; + numSamples = m_levelNbSamples; + } + void setSpectrumSink(BasebandSampleSink *sampleSink) { m_spectrumSink = sampleSink; } + 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); + +private: + int m_channelSampleRate; + int m_channelFrequencyOffset; + PacketModSettings m_settings; + + NCO m_carrierNco; + Real m_audioPhase; + Real m_fmPhase; + Complex m_modSample; + + Lowpass m_lowpass; // Low pass filter to limit RF bandwidth + FMPreemphasis m_preemphasisFilter; // FM preemphasis filter to amplify high frequencies + + BasebandSampleSink* m_spectrumSink; // Spectrum GUI to display baseband waveform + SampleVector m_sampleBuffer; + Interpolator m_interpolator; // Interpolator to downsample to 4k in spectrum + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + bool m_interpolatorConsumed; + + double m_magsq; + MovingAverageUtil m_movingAverage; + + quint32 m_levelCalcCount; + qreal m_rmsLevel; + qreal m_peakLevelOut; + Real m_peakLevel; + Real m_levelSum; + + static const int m_levelNbSamples = 480; // every 10ms assuming 48k Sa/s + + int m_f; // Current frequency + int m_sampleIdx; // Sample index in to symbol + int m_samplesPerSymbol; // Number of samples per symbol + Real m_pow; // In dB + Real m_powRamp; // In dB + enum PacketModState { + idle, ramp_up, tx, ramp_down, wait + } m_state; // States for sample modulation + int m_packetRepeatCount; + uint64_t m_waitCounter; // Samples to wait before retransmission + + uint8_t m_bits[AX25_MAX_BITS]; // HDLC encoded bits to transmit + int m_byteIdx; // Index in to m_bits + int m_bitIdx; // Index in to current byte of m_bits + int m_last5Bits; // Last 5 bits to be HDLC encoded + int m_bitCount; // Count of number of valid bits in m_bits + int m_bitCountTotal; + + std::ofstream m_audioFile; // For debug output of baseband waveform + + bool bitsValid(); // Are there and bits to transmit + int getBit(); // Get bit from m_bits + void addBit(int bit); // Add bit to m_bits, with zero stuffing + void initTX(); + + void calculateLevel(Real& sample); + void modulateSample(); + void sampleToSpectrum(Real sample); + +}; + +#endif // INCLUDE_PACKETMODSOURCE_H diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp new file mode 100644 index 000000000..d3a09193a --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "packetmodtxsettingsdialog.h" + +PacketModTXSettingsDialog::PacketModTXSettingsDialog(int rampUpBits, int rampDownBits, + int rampRange, bool modulateWhileRamping, + int markFrequency, int spaceFrequency, + int ax25PreFlags, int ax25PostFlags, + int ax25Control, int ax25PID, + int lpfTaps, bool bbNoise, bool rfNoise, bool writeToFile, + QWidget* parent) : + QDialog(parent), + ui(new Ui::PacketModTXSettingsDialog) +{ + ui->setupUi(this); + ui->rampUp->setValue(rampUpBits); + ui->rampDown->setValue(rampDownBits); + ui->rampRange->setValue(rampRange); + ui->modulateWhileRamping->setChecked(modulateWhileRamping); + ui->markFrequency->setValue(markFrequency); + ui->spaceFrequency->setValue(spaceFrequency); + ui->ax25PreFlags->setValue(ax25PreFlags); + ui->ax25PostFlags->setValue(ax25PostFlags); + ui->ax25Control->setValue(ax25Control); + ui->ax25PID->setValue(ax25PID); + ui->lpfTaps->setValue(lpfTaps); + ui->bbNoise->setChecked(bbNoise); + ui->rfNoise->setChecked(rfNoise); + ui->writeToFile->setChecked(writeToFile); +} + +PacketModTXSettingsDialog::~PacketModTXSettingsDialog() +{ + delete ui; +} + +void PacketModTXSettingsDialog::accept() +{ + m_rampUpBits = ui->rampUp->value(); + m_rampDownBits = ui->rampDown->value(); + m_rampRange = ui->rampRange->value(); + m_modulateWhileRamping = ui->modulateWhileRamping->isChecked(); + m_markFrequency = ui->markFrequency->value(); + m_spaceFrequency = ui->spaceFrequency->value(); + m_ax25PreFlags = ui->ax25PreFlags->value(); + m_ax25PostFlags = ui->ax25PostFlags->value(); + m_ax25Control = ui->ax25Control->value(); + m_ax25PID = ui->ax25PID->value(); + m_lpfTaps = ui->lpfTaps->value(); + m_bbNoise = ui->bbNoise->isChecked(); + m_rfNoise = ui->rfNoise->isChecked(); + m_writeToFile = ui->writeToFile->isChecked(); + + QDialog::accept(); +} diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h new file mode 100644 index 000000000..7889ba625 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PACKETMODTXSETTINGSDIALOG_H +#define INCLUDE_PACKETMODTXSETTINGSDIALOG_H + +#include "ui_packetmodtxsettingsdialog.h" + +class PacketModTXSettingsDialog : public QDialog { + Q_OBJECT + +public: + explicit PacketModTXSettingsDialog(int rampUpBits, int rampDownBits, int rampRange, + bool modulateWhileRamping, + int markFrequency, int spaceFrequency, + int ax25PreFlags, int ax25PostFlags, + int ax25Control, int ax25PID, + int lpfTaps, bool bbNoise, bool rfNoise, bool writeToFile, + QWidget* parent = 0); + ~PacketModTXSettingsDialog(); + + int m_rampUpBits; + int m_rampDownBits; + int m_rampRange; + bool m_modulateWhileRamping; + int m_markFrequency; + int m_spaceFrequency; + int m_ax25PreFlags; + int m_ax25PostFlags; + int m_ax25Control; + int m_ax25PID; + int m_lpfTaps; + bool m_bbNoise; + bool m_rfNoise; + bool m_writeToFile; + +private slots: + void accept(); + +private: + Ui::PacketModTXSettingsDialog* ui; +}; + +#endif // INCLUDE_PACKETMODTXSETTINGSDIALOG_H diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui new file mode 100644 index 000000000..f9a28efb7 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui @@ -0,0 +1,332 @@ + + + PacketModTXSettingsDialog + + + + 0 + 0 + 351 + 449 + + + + + Liberation Sans + 9 + + + + Packet TX Extra Settings + + + + + + + + + Ramp up bits + + + + + + + Number of bits at start of frame during which output power is ramped up. + + + + + + + Ramp down bits + + + + + + + Number of bits at end of frame during which output power is ramped down. + + + + + + + Ramp range (dB) + + + + + + + Range in dB over which power is ramped up or down. E.g. a value of 60 causes power to be ramped from -60dB to 0dB. + + + 120 + + + + + + + Modulate during ramping. + + + Modulate while ramping + + + + + + + Mark frequency (Hz) + + + + + + + Frequency of tone to generate for a mark (1). + + + 24000 + + + 100 + + + + + + + Space frequency (Hz) + + + + + + + Frequency of tone to generate for a space (0). + + + 24000 + + + 100 + + + + + + + AX.25 preamble flags + + + + + + + Number of flags to be transmitted before the frame. This gives more time for a receiver to unmute. + + + 1 + + + 1024 + + + + + + + AX.25 postamble flags + + + + + + + Number of flags to be transmitted after the frame. + + + 1 + + + 1024 + + + + + + + AX.25 control + + + + + + + Value of control field in AX.25 frame. + + + 0x + + + 255 + + + 3 + + + 16 + + + + + + + AX.25 PID + + + + + + + Value of PID field in AX.25 frame. Use 0xf0 for no L3. + + + 0x + + + 255 + + + 240 + + + 16 + + + + + + + Lowpass taps + + + + + + + Number of taps in LPF for RF BW filter. + + + 10000 + + + + + + + Generate white noise as RF signal. + + + Generate RF noise + + + + + + + + 0 + 0 + + + + Write baseband signal to a CSV file named packetmod.csv + + + Write baseband to CSV + + + + + + + Generate white noise as baseband signal. + + + Generate BB noise + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + rampUp + rampDown + rampRange + modulateWhileRamping + markFrequency + spaceFrequency + ax25PreFlags + ax25PostFlags + ax25Control + ax25PID + lpfTaps + bbNoise + rfNoise + writeToFile + + + + + buttonBox + accepted() + PacketModTXSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PacketModTXSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/channeltx/modpacket/packetmodwebapiadapter.cpp b/plugins/channeltx/modpacket/packetmodwebapiadapter.cpp new file mode 100644 index 000000000..ca8f2be83 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodwebapiadapter.cpp @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGChannelSettings.h" +#include "packetmod.h" +#include "packetmodwebapiadapter.h" + +PacketModWebAPIAdapter::PacketModWebAPIAdapter() +{} + +PacketModWebAPIAdapter::~PacketModWebAPIAdapter() +{} + +int PacketModWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setPacketModSettings(new SWGSDRangel::SWGPacketModSettings()); + response.getPacketModSettings()->init(); + PacketMod::webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int PacketModWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + PacketMod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + + PacketMod::webapiFormatChannelSettings(response, m_settings); + return 200; +} diff --git a/plugins/channeltx/modpacket/packetmodwebapiadapter.h b/plugins/channeltx/modpacket/packetmodwebapiadapter.h new file mode 100644 index 000000000..bb13b137c --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodwebapiadapter.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PACKETMOD_WEBAPIADAPTER_H +#define INCLUDE_PACKETMOD_WEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "packetmodsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class PacketModWebAPIAdapter : public ChannelWebAPIAdapter { +public: + PacketModWebAPIAdapter(); + virtual ~PacketModWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + +private: + PacketModSettings m_settings; +}; + +#endif // INCLUDE_PACKETMOD_WEBAPIADAPTER_H diff --git a/plugins/channeltx/modpacket/readme.md b/plugins/channeltx/modpacket/readme.md new file mode 100644 index 000000000..6305f8520 --- /dev/null +++ b/plugins/channeltx/modpacket/readme.md @@ -0,0 +1,85 @@ +

Packet radio modulator plugin

+ +

Introduction

+ +This plugin can be used to modulate packet radio (APRS/AX.25) data packets. + +

Interface

+ +![Packet Modulator plugin GUI](../../../doc/img/PacketMod_plugin.png) + +

1: Frequency shift from center frequency of transmission

+ +Use the wheels to adjust the frequency shift in Hz from the center frequency of transmission. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

2: Channel power

+ +Average total power in dB relative to a +/- 1.0 amplitude signal generated in the pass band. + +

3: Channel mute

+ +Use this button to toggle mute for this channel. + +

4: Modulation

+ +This specifies the baud rate and modulation that is used for the packet transmission. Currently 1200 baud AFSK is supported. + +

5: RF Bandwidth

+ +This specifies the bandwidth of a LPF that is applied to the output signal to limit the RF bandwidth. + +

6: Frequency deviation

+ +Adjusts the frequency deviation in 0.1 kHz steps from 1 to 6 kHz. Typical values are 2.5 kHz and 5 kHz. + +

7: Gain

+ +Adjusts the gain in dB from -60 to 0dB. The gain should be set to ensure the level meter remains below 100%. + +

8: Level meter in %

+ + - top bar (beige): average value + - bottom bar (brown): instantaneous peak value + - tip vertical bar (bright red): peak hold value + +

9: Callsign

+ +Enter your amateur radio callsign and optionally a sub-station ID (SSID). E.g. M7RCE or M7RCE-1 + +

10: Preemphaisis Filter

+ +Check this button to enable a FM preemphasis filter, which amplifiers higher frequencies. Right click to open the dialog to adjust settings for the filter. + +

11: Repeat

+ +Check this button to repeated transmit a packet. Right click to open the dialog to adjust the delay between retransmission and number of times the packet should be repeated. + +

12: Insertion position

+ +Inserts position as APRS formatted latitude and longitude in to the current cursor position within the data field. Lattitude and longitude can be specified under Preferences > My position. + +

13: To

+ +Enter the destination for the packet. To send the packet to the APRS network, use APRS or APZ. + +

14: Via

+ +Enter the routing for the packet. To have the packet repeated by digipeaters, use WIDE2-2. To have the packet repeated by the International Space Station (ISS), use ARISS. + +

15: Data

+ +The packet of data to send. To send an APRS status message, use the format >Status. APRS messages can be tracked on https://aprs.fi + +

16: TX

+ +Transmits a packet based on the current values in callsign, to, via and data. + +

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: + + curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/actions" -d '{"channelType": "PacketMod", "direction": 1, "PacketModActions": { "tx": { "callsign": "MYCALL", "to": "APRS", "via": "WIDE2-2", "data": ">Using SDRangel API to transmit" }}}' + +Or to set the frequency deviation: + + curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/settings" -d '{"channelType": "PacketMod", "direction": 1, "PacketModSettings": {"fmDeviation": 5000}}' diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 92cb17bcc..1dc6811ee 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -101,6 +101,7 @@ set(sdrbase_SOURCES dsp/filtermbe.cpp dsp/filerecord.cpp dsp/filerecordinterface.cpp + dsp/fmpreemphasis.cpp dsp/freqlockcomplex.cpp dsp/interpolator.cpp dsp/glscopesettings.cpp @@ -142,6 +143,7 @@ set(sdrbase_SOURCES settings/preset.cpp settings/mainsettings.cpp + util/crc.cpp util/CRC64.cpp util/db.cpp util/fixedtraits.cpp @@ -232,6 +234,7 @@ set(sdrbase_HEADERS dsp/filtermbe.h dsp/filerecord.h dsp/filerecordinterface.h + dsp/fmpreemphasis.h dsp/freqlockcomplex.h dsp/gfft.h dsp/glscopesettings.h diff --git a/sdrbase/dsp/fmpreemphasis.cpp b/sdrbase/dsp/fmpreemphasis.cpp new file mode 100644 index 000000000..6e33ef431 --- /dev/null +++ b/sdrbase/dsp/fmpreemphasis.cpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2005,2007,2012 Free Software Foundation, Inc. +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#define _USE_MATH_DEFINES +#include +#include +#include "dsp/fmpreemphasis.h" + +FMPreemphasis::FMPreemphasis(int sampleRate, Real tau, Real highFreq) +{ + configure(sampleRate, tau, highFreq); +} + +void FMPreemphasis::configure(int sampleRate, Real tau, Real highFreq) +{ + // Based on: https://github.com/gnuradio/gnuradio/blob/master/gr-analog/python/analog/fm_emph.py + // Compare to freq response in https://www.mathworks.com/help/comm/ref/comm.fmbroadcastmodulator-system-object.html + + // High frequency corner at which to flatten the gain + double fh = std::min((double)highFreq, 0.925 * sampleRate/2.0); + + // Digital corner frequencies + double w_cl = 1.0 / tau; + double w_ch = 2.0 * M_PI * fh; + + // Prewarped analog corner frequencies + double w_cla = 2.0 * sampleRate * std::tan(w_cl / (2.0 * sampleRate)); + double w_cha = 2.0 * sampleRate * std::tan(w_ch / (2.0 * sampleRate)); + + // Resulting digital pole, zero, and gain term from the bilinear + // transformation of H(s) = (s + w_cla) / (s + w_cha) to + // H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) + double kl = -w_cla / (2.0 * sampleRate); + double kh = -w_cha / (2.0 * sampleRate); + double z1 = (1.0 + kl) / (1.0 - kl); + double p1 = (1.0 + kh) / (1.0 - kh); + double b0 = (1.0 - kl) / (1.0 - kh); + + // Adjust with a gain, g, so 0 dB gain at DC + double g = std::abs(1.0 - p1) / (b0 * std::abs(1.0 - z1)); + + // Caclulate IIR taps + m_b0 = (Real)(g * b0 * 1.0); + m_b1 = (Real)(g * b0 * -z1); + m_a1 = (Real)-p1; + // Zero delay line so we get reproducible results + m_z = 0; + + qDebug() << "FMPreemphasis::configure: tau: " << tau + << " sampleRate: " << sampleRate + << " b0: " << m_b0 + << " b1: " << m_b1 + << " a1: " << m_a1; +} + +Real FMPreemphasis::filter(const Real sampleIn) +{ + Real sampleOut; + + // See Transposed Direct form 2 - https://en.wikipedia.org/wiki/Digital_biquad_filter + sampleOut = sampleIn * m_b0 + m_z; + m_z = sampleIn * m_b1 + sampleOut * -m_a1; + + return sampleOut; +} diff --git a/sdrbase/dsp/fmpreemphasis.h b/sdrbase/dsp/fmpreemphasis.h new file mode 100644 index 000000000..cb6725053 --- /dev/null +++ b/sdrbase/dsp/fmpreemphasis.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_DSP_FMPREEMPHASIS_H_ +#define INCLUDE_DSP_FMPREEMPHASIS_H_ + +#include "dsp/dsptypes.h" +#include "export.h" + +#define FMPREEMPHASIS_TAU_EU 50e-6f +#define FMPREEMPHASIS_TAU_US 75e-6f + +/** FM preemphasis filter. + * Amplifies frequencies above ~3.2k (tau=50e-6 in EU) or ~2.1k (tau=75e-6 in US) + * at ~6dB per octave, and then flattens at 12k (highFreq). + * Frequency response: + * highFreq + * ------- + * / + * / + * -------/ + * 1/(2*pi*tau) + */ +class SDRBASE_API FMPreemphasis +{ +public: + + FMPreemphasis(int sampleRate, Real tau = FMPREEMPHASIS_TAU_EU, Real highFreq = 12000.0); + + void configure(int sampleRate, Real tau = FMPREEMPHASIS_TAU_EU, Real highFreq = 12000.0); + + Real filter(Real sampleIn); + +private: + Real m_z; // Delay element + Real m_b0; // IIR numerator taps + Real m_b1; + Real m_a1; // IIR denominator taps +}; + +#endif /* INCLUDE_DSP_FMPREEMPHASIS_H_ */ diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index b20392c52..994b0eb4f 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -2036,6 +2036,9 @@ margin-bottom: 20px; }, "FileSourceActions" : { "$ref" : "#/definitions/FileSourceActions" + }, + "PacketModActions" : { + "$ref" : "#/definitions/PacketModActions" } }, "description" : "Base channel actions. Only the channel actions corresponding to the channel specified in the channelType field is or should be present." @@ -2192,6 +2195,9 @@ margin-bottom: 20px; "NFMModReport" : { "$ref" : "#/definitions/NFMModReport" }, + "PacketModReport" : { + "$ref" : "#/definitions/PacketModReport" + }, "SSBDemodReport" : { "$ref" : "#/definitions/SSBDemodReport" }, @@ -2287,6 +2293,9 @@ margin-bottom: 20px; "LocalSourceSettings" : { "$ref" : "#/definitions/LocalSourceSettings" }, + "PacketModSettings" : { + "$ref" : "#/definitions/PacketModSettings" + }, "RemoteSinkSettings" : { "$ref" : "#/definitions/RemoteSinkSettings" }, @@ -4875,6 +4884,124 @@ margin-bottom: 20px; } }, "description" : "Enumeration with name for values" +}; + defs.PacketModActions = { + "properties" : { + "tx" : { + "$ref" : "#/definitions/PacketModActions_tx" + } + }, + "description" : "PacketMod" +}; + defs.PacketModActions_tx = { + "properties" : { + "callsign" : { + "type" : "string" + }, + "to" : { + "type" : "string" + }, + "via" : { + "type" : "string" + }, + "data" : { + "type" : "string" + } + }, + "description" : "Transmit a packet\n" +}; + defs.PacketModReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power transmitted in channel (dB)" + }, + "audioSampleRate" : { + "type" : "integer" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "PacketMod" +}; + defs.PacketModSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "fmDeviation" : { + "type" : "number", + "format" : "float" + }, + "gain" : { + "type" : "number", + "format" : "float" + }, + "channelMute" : { + "type" : "integer" + }, + "repeat" : { + "type" : "integer" + }, + "repeatDelay" : { + "type" : "number", + "format" : "float" + }, + "repeatCount" : { + "type" : "integer" + }, + "ax25PreFlags" : { + "type" : "integer" + }, + "ax25PostFlags" : { + "type" : "integer" + }, + "preEmphasis" : { + "type" : "integer" + }, + "preEmphasisTau" : { + "type" : "number", + "format" : "float" + }, + "preEmphasisHighFreq" : { + "type" : "number", + "format" : "float" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "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" : "PacketMod" }; defs.PerseusReport = { "properties" : { @@ -33327,7 +33454,7 @@ except ApiException as e:
- Generated 2020-09-14T23:30:27.272+02:00 + Generated 2020-09-18T15:59:26.503+02:00
diff --git a/sdrbase/util/crc.cpp b/sdrbase/util/crc.cpp new file mode 100644 index 000000000..fe848e35e --- /dev/null +++ b/sdrbase/util/crc.cpp @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "crc.h" +#include "QDebug.h" + +// Reverse bit ordering +uint32_t crc::reverse(uint32_t val, int bits) +{ + uint32_t temp; + int i; + + temp = 0; + for (i = 0; i < bits; i++) + temp |= ((val >> i) & 1) << (bits - 1 - i); + return temp; +} + +// Calculate CRC for specified number of bits +void crc::calculate(uint32_t data, int data_bits) +{ + uint32_t tmp; + uint32_t mask; + int bit, i; + + // Reverse data order. + if (m_msb_first) + data = reverse (data, data_bits); + + // Compute CRC. + tmp = m_crc; + for (i = 0; i < data_bits; i++) { + bit = ((data >> i) & 1) ^ (tmp & 1); + if (bit) + tmp = (tmp >> 1) ^ m_polynomial_rev; + else + tmp = tmp >> 1; + } + m_crc = tmp; +} + +// Calculate CRC for specified array +void crc::calculate(const uint8_t *data, int length) +{ + for(int i = 0; i < length; i++) + calculate(data[i], 8); +} diff --git a/sdrbase/util/crc.h b/sdrbase/util/crc.h new file mode 100644 index 000000000..1c4899403 --- /dev/null +++ b/sdrbase/util/crc.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_CRC_H +#define INCLUDE_CRC_H + +#include + +#include "export.h" + +// Class to calculate arbitrary CRCs (up to 32-bits) +class SDRBASE_API crc +{ +public: + // Create and initialise CRC with specified polynomial and parameters + crc(int poly_bits, uint32_t polynomial, bool msb_first, uint32_t init_value, uint32_t final_xor) : + m_poly_bits(poly_bits), + m_polynomial(polynomial), + m_msb_first(msb_first), + m_init_value(init_value), + m_final_xor(final_xor) + { + int shift; + + shift = 32 - m_poly_bits; + m_polynomial_rev = reverse (m_polynomial << shift, 32); + init(); + } + + // Initialise CRC state + void init() + { + m_crc = m_init_value; + } + + // Calculate CRC for supplied data + void calculate(uint32_t data, int data_bits); + void calculate(const uint8_t *data, int length); + + // Get final CRC + uint32_t get() + { + uint32_t t; + + t = m_final_xor ^ m_crc; + if (m_msb_first) + return reverse(t, m_poly_bits); + else + return t; + } + +private: + static uint32_t reverse(uint32_t val, int bits); + + uint32_t m_crc; + uint32_t m_polynomial; + uint32_t m_polynomial_rev; + uint32_t m_poly_bits; + bool m_msb_first; + uint32_t m_init_value; + uint32_t m_final_xor; +}; + +class SDRBASE_API crc16ansi : public crc +{ +public: + crc16ansi() : crc(16, 0x8005, false, 0x0000, 0) {} +}; + +class SDRBASE_API crc16ccitt : public crc +{ +public: + crc16ccitt() : crc(16, 0x1021, true, 0xffff, 0) {} +}; + +class SDRBASE_API crc16x25 : public crc +{ +public: + crc16x25() : crc(16, 0x1021, false, 0xffff, 0xffff) {} +}; + +class SDRBASE_API crc32 : public crc +{ +public: + crc32() : crc(32, 0x04C11DB7, false, 0xffffffff, 0xffffffff) {} +}; + +class SDRBASE_API crc32c : public crc +{ +public: + crc32c() : crc(32, 0x1EDC6F41, false, 0xffffffff, 0) {} +}; + +#endif diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index a29abe57b..499e4b28d 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -75,6 +75,7 @@ const QMap WebAPIRequestMapper::m_channelURIToSettingsKey = { {"sdrangel.demod.localsink", "LocalSinkSettings"}, {"sdrangel.channel.localsink", "LocalSinkSettings"}, // remap {"sdrangel.channel.localsource", "LocalSourceSettings"}, + {"sdrangel.channeltx.modpacket", "PacketModSettings"}, {"sdrangel.demod.remotesink", "RemoteSinkSettings"}, {"sdrangel.channeltx.remotesource", "RemoteSourceSettings"}, {"sdrangel.channeltx.modssb", "SSBModSettings"}, @@ -143,6 +144,7 @@ const QMap WebAPIRequestMapper::m_channelTypeToSettingsKey = { {"FreqTracker", "FreqTrackerSettings"}, {"NFMDemod", "NFMDemodSettings"}, {"NFMMod", "NFMModSettings"}, + {"PacketMod", "PacketModSettings"}, {"LocalSink", "LocalSinkSettings"}, {"LocalSource", "LocalSourceSettings"}, {"RemoteSink", "RemoteSinkSettings"}, @@ -157,7 +159,8 @@ const QMap WebAPIRequestMapper::m_channelTypeToSettingsKey = { const QMap WebAPIRequestMapper::m_channelTypeToActionsKey = { {"FileSink", "FileSinkActions"}, - {"FileSource", "FileSourceActions"} + {"FileSource", "FileSourceActions"}, + {"PacketMod", "PacketModActions"} }; const QMap WebAPIRequestMapper::m_sourceDeviceHwIdToSettingsKey = { @@ -3173,6 +3176,11 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setLocalSourceSettings(new SWGSDRangel::SWGLocalSourceSettings()); channelSettings->getLocalSourceSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "PacketModSettings") + { + channelSettings->setPacketModSettings(new SWGSDRangel::SWGPacketModSettings()); + channelSettings->getPacketModSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "RemoteSinkSettings") { channelSettings->setRemoteSinkSettings(new SWGSDRangel::SWGRemoteSinkSettings()); @@ -3250,6 +3258,11 @@ bool WebAPIRequestMapper::getChannelActions( channelActions->setFileSourceActions(new SWGSDRangel::SWGFileSourceActions()); channelActions->getFileSourceActions()->fromJsonObject(actionsJsonObject); } + else if (channelActionsKey == "PacketModActions") + { + channelActions->setPacketModActions(new SWGSDRangel::SWGPacketModActions()); + channelActions->getPacketModActions()->fromJsonObject(actionsJsonObject); + } else { return false; @@ -3608,6 +3621,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setDsdDemodSettings(nullptr); channelSettings.setNfmDemodSettings(nullptr); channelSettings.setNfmModSettings(nullptr); + channelSettings.setPacketModSettings(nullptr); channelSettings.setRemoteSinkSettings(nullptr); channelSettings.setRemoteSourceSettings(nullptr); channelSettings.setSsbDemodSettings(nullptr); @@ -3629,6 +3643,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setDsdDemodReport(nullptr); channelReport.setNfmDemodReport(nullptr); channelReport.setNfmModReport(nullptr); + channelReport.setPacketModReport(nullptr); channelReport.setRemoteSourceReport(nullptr); channelReport.setSsbDemodReport(nullptr); channelReport.setSsbModReport(nullptr); @@ -3643,6 +3658,7 @@ void WebAPIRequestMapper::resetChannelActions(SWGSDRangel::SWGChannelActions& ch channelActions.cleanup(); channelActions.setChannelType(nullptr); channelActions.setFileSourceActions(nullptr); + channelActions.setPacketModActions(nullptr); } void WebAPIRequestMapper::resetAudioInputDevice(SWGSDRangel::SWGAudioInputDevice& audioInputDevice) diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index b8187f86f..827597405 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -30,6 +30,7 @@ set(sdrgui_SOURCES gui/editcommanddialog.cpp gui/externalclockbutton.cpp gui/externalclockdialog.cpp + gui/fmpreemphasisdialog.cpp gui/glscope.cpp gui/glscopegui.cpp gui/glshadercolors.cpp @@ -104,6 +105,7 @@ set(sdrgui_HEADERS gui/editcommanddialog.h gui/externalclockbutton.h gui/externalclockdialog.h + gui/fmpreemphasisdialog.h gui/glscope.h gui/glscopegui.h gui/glshadercolors.h @@ -165,6 +167,7 @@ set(sdrgui_FORMS gui/deviceuserargsdialog.ui gui/editcommanddialog.ui gui/externalclockdialog.ui + gui/fmpreemphasisdialog.ui gui/glscopegui.ui gui/glspectrumgui.ui gui/pluginsdialog.ui diff --git a/sdrgui/gui/fmpreemphasisdialog.cpp b/sdrgui/gui/fmpreemphasisdialog.cpp new file mode 100644 index 000000000..f265095b8 --- /dev/null +++ b/sdrgui/gui/fmpreemphasisdialog.cpp @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#define _USE_MATH_DEFINES +#include +#include "fmpreemphasisdialog.h" +#include "ui_fmpreemphasisdialog.h" + +// Convert from s to us +#define TAU_SCALE 1000000.0 + +FMPreemphasisDialog::FMPreemphasisDialog(float tau, float highFreq, QWidget* parent) : + QDialog(parent), + ui(new Ui::FMPreemphasisDialog) +{ + ui->setupUi(this); + ui->tau->setValue(tau*TAU_SCALE); + ui->lowFreq->setValue(1.0/(2.0*M_PI*tau)); + ui->highFreq->setValue(highFreq); +} + +FMPreemphasisDialog::~FMPreemphasisDialog() +{ + delete ui; +} + +void FMPreemphasisDialog::accept() +{ + m_tau = ui->tau->value()/TAU_SCALE; + m_highFreq = ui->highFreq->value(); + + QDialog::accept(); +} + +void FMPreemphasisDialog::on_tau_valueChanged(double value) +{ + // Set corresponding low frequency + ui->lowFreq->blockSignals(true); + ui->lowFreq->setValue(1.0/(2.0*M_PI*(value/TAU_SCALE))); + ui->lowFreq->blockSignals(false); +} + +void FMPreemphasisDialog::on_lowFreq_valueChanged(double value) +{ + // Set corresponding tau + ui->tau->blockSignals(true); + ui->tau->setValue(TAU_SCALE*1.0/(2.0*M_PI*value)); + ui->tau->blockSignals(false); +} diff --git a/sdrgui/gui/fmpreemphasisdialog.h b/sdrgui/gui/fmpreemphasisdialog.h new file mode 100644 index 000000000..780c5cf42 --- /dev/null +++ b/sdrgui/gui/fmpreemphasisdialog.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_FMPREEMPHASISTDIALOG_H +#define INCLUDE_FMPREEMPHASISTDIALOG_H + +#include + +#include "export.h" + +namespace Ui { + class FMPreemphasisDialog; +} + +class SDRGUI_API FMPreemphasisDialog : public QDialog { + Q_OBJECT + +public: + explicit FMPreemphasisDialog(float tau, float highFreq, QWidget* parent = 0); + ~FMPreemphasisDialog(); + + float m_tau; + float m_highFreq; + +private slots: + void accept(); + void on_tau_valueChanged(double value); + void on_lowFreq_valueChanged(double value); + +private: + Ui::FMPreemphasisDialog* ui; +}; + +#endif // INCLUDE_FMPREEMPHASISTDIALOG_H diff --git a/sdrgui/gui/fmpreemphasisdialog.ui b/sdrgui/gui/fmpreemphasisdialog.ui new file mode 100644 index 000000000..d87f83b92 --- /dev/null +++ b/sdrgui/gui/fmpreemphasisdialog.ui @@ -0,0 +1,144 @@ + + + FMPreemphasisDialog + + + + 0 + 0 + 351 + 142 + + + + + Liberation Sans + 9 + + + + FM Preemphasis Settings + + + + + + + + + Time constant (Tau) (us) + + + + + + + Low frequency corner (Hz) + + + + + + + Low frequency corner. Automatically calculated based on tau. + + + 10000.000000000000000 + + + 1000.000000000000000 + + + + + + + High frequency corner (Hz) + + + + + + + High frequency corner. + + + 1000000.000000000000000 + + + 1000.000000000000000 + + + + + + + Time constant in us. 50 for EU. 75 for USA. + + + 0 + + + 5000.000000000000000 + + + 10.000000000000000 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + tau + lowFreq + highFreq + + + + + buttonBox + accepted() + FMPreemphasisDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FMPreemphasisDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/swagger/sdrangel/api/swagger/include/ChannelActions.yaml b/swagger/sdrangel/api/swagger/include/ChannelActions.yaml index bb5ed9c5a..1853f43ef 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelActions.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelActions.yaml @@ -21,3 +21,5 @@ ChannelActions: $ref: "http://swgserver:8081/api/swagger/include/FileSink.yaml#/FileSinkActions" FileSourceActions: $ref: "http://swgserver:8081/api/swagger/include/FileSource.yaml#/FileSourceActions" + PacketModActions: + $ref: "http://swgserver:8081/api/swagger/include/PacketMod.yaml#/PacketModActions" diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index cd2fca850..235f6835b 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -51,6 +51,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/LocalSink.yaml#/LocalSinkSettings" LocalSourceSettings: $ref: "http://swgserver:8081/api/swagger/include/LocalSource.yaml#/LocalSourceSettings" + PacketModSettings: + $ref: "http://swgserver:8081/api/swagger/include/PacketMod.yaml#/PacketModSettings" RemoteSinkSettings: $ref: "http://swgserver:8081/api/swagger/include/RemoteSink.yaml#/RemoteSinkSettings" RemoteSourceSettings: diff --git a/swagger/sdrangel/api/swagger/include/PacketMod.yaml b/swagger/sdrangel/api/swagger/include/PacketMod.yaml new file mode 100644 index 000000000..06e3236fe --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/PacketMod.yaml @@ -0,0 +1,83 @@ +PacketModSettings: + description: PacketMod + properties: + inputFrequencyOffset: + type: integer + format: int64 + rfBandwidth: + type: number + format: float + fmDeviation: + type: number + format: float + gain: + type: number + format: float + channelMute: + type: integer + repeat: + type: integer + repeatDelay: + type: number + format: float + repeatCount: + type: integer + ax25PreFlags: + type: integer + ax25PostFlags: + type: integer + preEmphasis: + type: integer + preEmphasisTau: + type: number + format: float + preEmphasisHighFreq: + type: number + format: float + rgbColor: + type: integer + title: + type: string + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + 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 + +PacketModReport: + description: PacketMod + properties: + channelPowerDB: + description: power transmitted in channel (dB) + type: number + format: float + audioSampleRate: + type: integer + channelSampleRate: + type: integer + +PacketModActions: + description: PacketMod + properties: + tx: + type: object + properties: + callsign: + type: string + to: + type: string + via: + type: string + data: + type: string + description: > + Transmit a packet diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index 8eac5dde0..84c5c81c5 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -2356,6 +2356,8 @@ definitions: $ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodReport" NFMModReport: $ref: "http://swgserver:8081/api/swagger/include/NFMMod.yaml#/NFMModReport" + PacketModReport: + $ref: "http://swgserver:8081/api/swagger/include/PacketMod.yaml#/PacketModReport" SSBDemodReport: $ref: "http://swgserver:8081/api/swagger/include/SSBDemod.yaml#/SSBDemodReport" RemoteSourceReport: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index b20392c52..994b0eb4f 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -2036,6 +2036,9 @@ margin-bottom: 20px; }, "FileSourceActions" : { "$ref" : "#/definitions/FileSourceActions" + }, + "PacketModActions" : { + "$ref" : "#/definitions/PacketModActions" } }, "description" : "Base channel actions. Only the channel actions corresponding to the channel specified in the channelType field is or should be present." @@ -2192,6 +2195,9 @@ margin-bottom: 20px; "NFMModReport" : { "$ref" : "#/definitions/NFMModReport" }, + "PacketModReport" : { + "$ref" : "#/definitions/PacketModReport" + }, "SSBDemodReport" : { "$ref" : "#/definitions/SSBDemodReport" }, @@ -2287,6 +2293,9 @@ margin-bottom: 20px; "LocalSourceSettings" : { "$ref" : "#/definitions/LocalSourceSettings" }, + "PacketModSettings" : { + "$ref" : "#/definitions/PacketModSettings" + }, "RemoteSinkSettings" : { "$ref" : "#/definitions/RemoteSinkSettings" }, @@ -4875,6 +4884,124 @@ margin-bottom: 20px; } }, "description" : "Enumeration with name for values" +}; + defs.PacketModActions = { + "properties" : { + "tx" : { + "$ref" : "#/definitions/PacketModActions_tx" + } + }, + "description" : "PacketMod" +}; + defs.PacketModActions_tx = { + "properties" : { + "callsign" : { + "type" : "string" + }, + "to" : { + "type" : "string" + }, + "via" : { + "type" : "string" + }, + "data" : { + "type" : "string" + } + }, + "description" : "Transmit a packet\n" +}; + defs.PacketModReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power transmitted in channel (dB)" + }, + "audioSampleRate" : { + "type" : "integer" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "PacketMod" +}; + defs.PacketModSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "fmDeviation" : { + "type" : "number", + "format" : "float" + }, + "gain" : { + "type" : "number", + "format" : "float" + }, + "channelMute" : { + "type" : "integer" + }, + "repeat" : { + "type" : "integer" + }, + "repeatDelay" : { + "type" : "number", + "format" : "float" + }, + "repeatCount" : { + "type" : "integer" + }, + "ax25PreFlags" : { + "type" : "integer" + }, + "ax25PostFlags" : { + "type" : "integer" + }, + "preEmphasis" : { + "type" : "integer" + }, + "preEmphasisTau" : { + "type" : "number", + "format" : "float" + }, + "preEmphasisHighFreq" : { + "type" : "number", + "format" : "float" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "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" : "PacketMod" }; defs.PerseusReport = { "properties" : { @@ -33327,7 +33454,7 @@ except ApiException as e:
- Generated 2020-09-14T23:30:27.272+02:00 + Generated 2020-09-18T15:59:26.503+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp index 1a8bc46c0..db87e42d9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp @@ -40,6 +40,8 @@ SWGChannelActions::SWGChannelActions() { m_file_sink_actions_isSet = false; file_source_actions = nullptr; m_file_source_actions_isSet = false; + packet_mod_actions = nullptr; + m_packet_mod_actions_isSet = false; } SWGChannelActions::~SWGChannelActions() { @@ -60,6 +62,8 @@ SWGChannelActions::init() { m_file_sink_actions_isSet = false; file_source_actions = new SWGFileSourceActions(); m_file_source_actions_isSet = false; + packet_mod_actions = new SWGPacketModActions(); + m_packet_mod_actions_isSet = false; } void @@ -76,6 +80,9 @@ SWGChannelActions::cleanup() { if(file_source_actions != nullptr) { delete file_source_actions; } + if(packet_mod_actions != nullptr) { + delete packet_mod_actions; + } } SWGChannelActions* @@ -101,6 +108,8 @@ SWGChannelActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&file_source_actions, pJson["FileSourceActions"], "SWGFileSourceActions", "SWGFileSourceActions"); + ::SWGSDRangel::setValue(&packet_mod_actions, pJson["PacketModActions"], "SWGPacketModActions", "SWGPacketModActions"); + } QString @@ -135,6 +144,9 @@ SWGChannelActions::asJsonObject() { if((file_source_actions != nullptr) && (file_source_actions->isSet())){ toJsonValue(QString("FileSourceActions"), file_source_actions, obj, QString("SWGFileSourceActions")); } + if((packet_mod_actions != nullptr) && (packet_mod_actions->isSet())){ + toJsonValue(QString("PacketModActions"), packet_mod_actions, obj, QString("SWGPacketModActions")); + } return obj; } @@ -199,6 +211,16 @@ SWGChannelActions::setFileSourceActions(SWGFileSourceActions* file_source_action this->m_file_source_actions_isSet = true; } +SWGPacketModActions* +SWGChannelActions::getPacketModActions() { + return packet_mod_actions; +} +void +SWGChannelActions::setPacketModActions(SWGPacketModActions* packet_mod_actions) { + this->packet_mod_actions = packet_mod_actions; + this->m_packet_mod_actions_isSet = true; +} + bool SWGChannelActions::isSet(){ @@ -222,6 +244,9 @@ SWGChannelActions::isSet(){ if(file_source_actions && file_source_actions->isSet()){ isObjectUpdated = true; break; } + if(packet_mod_actions && packet_mod_actions->isSet()){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelActions.h b/swagger/sdrangel/code/qt5/client/SWGChannelActions.h index 006e85de4..1186cfb3b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelActions.h @@ -24,6 +24,7 @@ #include "SWGFileSinkActions.h" #include "SWGFileSourceActions.h" +#include "SWGPacketModActions.h" #include #include "SWGObject.h" @@ -62,6 +63,9 @@ public: SWGFileSourceActions* getFileSourceActions(); void setFileSourceActions(SWGFileSourceActions* file_source_actions); + SWGPacketModActions* getPacketModActions(); + void setPacketModActions(SWGPacketModActions* packet_mod_actions); + virtual bool isSet() override; @@ -84,6 +88,9 @@ private: SWGFileSourceActions* file_source_actions; bool m_file_source_actions_isSet; + SWGPacketModActions* packet_mod_actions; + bool m_packet_mod_actions_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index c7cc33d26..b7277d3af 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -56,6 +56,8 @@ SWGChannelReport::SWGChannelReport() { m_nfm_demod_report_isSet = false; nfm_mod_report = nullptr; m_nfm_mod_report_isSet = false; + packet_mod_report = nullptr; + m_packet_mod_report_isSet = false; ssb_demod_report = nullptr; m_ssb_demod_report_isSet = false; remote_source_report = nullptr; @@ -106,6 +108,8 @@ SWGChannelReport::init() { m_nfm_demod_report_isSet = false; nfm_mod_report = new SWGNFMModReport(); m_nfm_mod_report_isSet = false; + packet_mod_report = new SWGPacketModReport(); + m_packet_mod_report_isSet = false; ssb_demod_report = new SWGSSBDemodReport(); m_ssb_demod_report_isSet = false; remote_source_report = new SWGRemoteSourceReport(); @@ -164,6 +168,9 @@ SWGChannelReport::cleanup() { if(nfm_mod_report != nullptr) { delete nfm_mod_report; } + if(packet_mod_report != nullptr) { + delete packet_mod_report; + } if(ssb_demod_report != nullptr) { delete ssb_demod_report; } @@ -226,6 +233,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&nfm_mod_report, pJson["NFMModReport"], "SWGNFMModReport", "SWGNFMModReport"); + ::SWGSDRangel::setValue(&packet_mod_report, pJson["PacketModReport"], "SWGPacketModReport", "SWGPacketModReport"); + ::SWGSDRangel::setValue(&ssb_demod_report, pJson["SSBDemodReport"], "SWGSSBDemodReport", "SWGSSBDemodReport"); ::SWGSDRangel::setValue(&remote_source_report, pJson["RemoteSourceReport"], "SWGRemoteSourceReport", "SWGRemoteSourceReport"); @@ -298,6 +307,9 @@ SWGChannelReport::asJsonObject() { if((nfm_mod_report != nullptr) && (nfm_mod_report->isSet())){ toJsonValue(QString("NFMModReport"), nfm_mod_report, obj, QString("SWGNFMModReport")); } + if((packet_mod_report != nullptr) && (packet_mod_report->isSet())){ + toJsonValue(QString("PacketModReport"), packet_mod_report, obj, QString("SWGPacketModReport")); + } if((ssb_demod_report != nullptr) && (ssb_demod_report->isSet())){ toJsonValue(QString("SSBDemodReport"), ssb_demod_report, obj, QString("SWGSSBDemodReport")); } @@ -463,6 +475,16 @@ SWGChannelReport::setNfmModReport(SWGNFMModReport* nfm_mod_report) { this->m_nfm_mod_report_isSet = true; } +SWGPacketModReport* +SWGChannelReport::getPacketModReport() { + return packet_mod_report; +} +void +SWGChannelReport::setPacketModReport(SWGPacketModReport* packet_mod_report) { + this->packet_mod_report = packet_mod_report; + this->m_packet_mod_report_isSet = true; +} + SWGSSBDemodReport* SWGChannelReport::getSsbDemodReport() { return ssb_demod_report; @@ -580,6 +602,9 @@ SWGChannelReport::isSet(){ if(nfm_mod_report && nfm_mod_report->isSet()){ isObjectUpdated = true; break; } + if(packet_mod_report && packet_mod_report->isSet()){ + isObjectUpdated = true; break; + } if(ssb_demod_report && ssb_demod_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 0115a27f0..dcae92524 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -34,6 +34,7 @@ #include "SWGFreqTrackerReport.h" #include "SWGNFMDemodReport.h" #include "SWGNFMModReport.h" +#include "SWGPacketModReport.h" #include "SWGRemoteSourceReport.h" #include "SWGSSBDemodReport.h" #include "SWGSSBModReport.h" @@ -103,6 +104,9 @@ public: SWGNFMModReport* getNfmModReport(); void setNfmModReport(SWGNFMModReport* nfm_mod_report); + SWGPacketModReport* getPacketModReport(); + void setPacketModReport(SWGPacketModReport* packet_mod_report); + SWGSSBDemodReport* getSsbDemodReport(); void setSsbDemodReport(SWGSSBDemodReport* ssb_demod_report); @@ -170,6 +174,9 @@ private: SWGNFMModReport* nfm_mod_report; bool m_nfm_mod_report_isSet; + SWGPacketModReport* packet_mod_report; + bool m_packet_mod_report_isSet; + SWGSSBDemodReport* ssb_demod_report; bool m_ssb_demod_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index ac8f28fe4..e71c97a2a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -70,6 +70,8 @@ SWGChannelSettings::SWGChannelSettings() { m_local_sink_settings_isSet = false; local_source_settings = nullptr; m_local_source_settings_isSet = false; + packet_mod_settings = nullptr; + m_packet_mod_settings_isSet = false; remote_sink_settings = nullptr; m_remote_sink_settings_isSet = false; remote_source_settings = nullptr; @@ -136,6 +138,8 @@ SWGChannelSettings::init() { m_local_sink_settings_isSet = false; local_source_settings = new SWGLocalSourceSettings(); m_local_source_settings_isSet = false; + packet_mod_settings = new SWGPacketModSettings(); + m_packet_mod_settings_isSet = false; remote_sink_settings = new SWGRemoteSinkSettings(); m_remote_sink_settings_isSet = false; remote_source_settings = new SWGRemoteSourceSettings(); @@ -213,6 +217,9 @@ SWGChannelSettings::cleanup() { if(local_source_settings != nullptr) { delete local_source_settings; } + if(packet_mod_settings != nullptr) { + delete packet_mod_settings; + } if(remote_sink_settings != nullptr) { delete remote_sink_settings; } @@ -292,6 +299,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&local_source_settings, pJson["LocalSourceSettings"], "SWGLocalSourceSettings", "SWGLocalSourceSettings"); + ::SWGSDRangel::setValue(&packet_mod_settings, pJson["PacketModSettings"], "SWGPacketModSettings", "SWGPacketModSettings"); + ::SWGSDRangel::setValue(&remote_sink_settings, pJson["RemoteSinkSettings"], "SWGRemoteSinkSettings", "SWGRemoteSinkSettings"); ::SWGSDRangel::setValue(&remote_source_settings, pJson["RemoteSourceSettings"], "SWGRemoteSourceSettings", "SWGRemoteSourceSettings"); @@ -387,6 +396,9 @@ SWGChannelSettings::asJsonObject() { if((local_source_settings != nullptr) && (local_source_settings->isSet())){ toJsonValue(QString("LocalSourceSettings"), local_source_settings, obj, QString("SWGLocalSourceSettings")); } + if((packet_mod_settings != nullptr) && (packet_mod_settings->isSet())){ + toJsonValue(QString("PacketModSettings"), packet_mod_settings, obj, QString("SWGPacketModSettings")); + } if((remote_sink_settings != nullptr) && (remote_sink_settings->isSet())){ toJsonValue(QString("RemoteSinkSettings"), remote_sink_settings, obj, QString("SWGRemoteSinkSettings")); } @@ -625,6 +637,16 @@ SWGChannelSettings::setLocalSourceSettings(SWGLocalSourceSettings* local_source_ this->m_local_source_settings_isSet = true; } +SWGPacketModSettings* +SWGChannelSettings::getPacketModSettings() { + return packet_mod_settings; +} +void +SWGChannelSettings::setPacketModSettings(SWGPacketModSettings* packet_mod_settings) { + this->packet_mod_settings = packet_mod_settings; + this->m_packet_mod_settings_isSet = true; +} + SWGRemoteSinkSettings* SWGChannelSettings::getRemoteSinkSettings() { return remote_sink_settings; @@ -773,6 +795,9 @@ SWGChannelSettings::isSet(){ if(local_source_settings && local_source_settings->isSet()){ isObjectUpdated = true; break; } + if(packet_mod_settings && packet_mod_settings->isSet()){ + isObjectUpdated = true; break; + } if(remote_sink_settings && remote_sink_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index f56358935..8590568a9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -39,6 +39,7 @@ #include "SWGLocalSourceSettings.h" #include "SWGNFMDemodSettings.h" #include "SWGNFMModSettings.h" +#include "SWGPacketModSettings.h" #include "SWGRemoteSinkSettings.h" #include "SWGRemoteSourceSettings.h" #include "SWGSSBDemodSettings.h" @@ -130,6 +131,9 @@ public: SWGLocalSourceSettings* getLocalSourceSettings(); void setLocalSourceSettings(SWGLocalSourceSettings* local_source_settings); + SWGPacketModSettings* getPacketModSettings(); + void setPacketModSettings(SWGPacketModSettings* packet_mod_settings); + SWGRemoteSinkSettings* getRemoteSinkSettings(); void setRemoteSinkSettings(SWGRemoteSinkSettings* remote_sink_settings); @@ -221,6 +225,9 @@ private: SWGLocalSourceSettings* local_source_settings; bool m_local_source_settings_isSet; + SWGPacketModSettings* packet_mod_settings; + bool m_packet_mod_settings_isSet; + SWGRemoteSinkSettings* remote_sink_settings; bool m_remote_sink_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 15e1b76a0..ecd70d7b3 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -118,6 +118,10 @@ #include "SWGNFMModReport.h" #include "SWGNFMModSettings.h" #include "SWGNamedEnum.h" +#include "SWGPacketModActions.h" +#include "SWGPacketModActions_tx.h" +#include "SWGPacketModReport.h" +#include "SWGPacketModSettings.h" #include "SWGPerseusReport.h" #include "SWGPerseusSettings.h" #include "SWGPlutoSdrInputReport.h" @@ -491,6 +495,18 @@ namespace SWGSDRangel { if(QString("SWGNamedEnum").compare(type) == 0) { return new SWGNamedEnum(); } + if(QString("SWGPacketModActions").compare(type) == 0) { + return new SWGPacketModActions(); + } + if(QString("SWGPacketModActions_tx").compare(type) == 0) { + return new SWGPacketModActions_tx(); + } + if(QString("SWGPacketModReport").compare(type) == 0) { + return new SWGPacketModReport(); + } + if(QString("SWGPacketModSettings").compare(type) == 0) { + return new SWGPacketModSettings(); + } if(QString("SWGPerseusReport").compare(type) == 0) { return new SWGPerseusReport(); } diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModActions.cpp new file mode 100644 index 000000000..b03a5242a --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModActions.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: 4.15.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 "SWGPacketModActions.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGPacketModActions::SWGPacketModActions(QString* json) { + init(); + this->fromJson(*json); +} + +SWGPacketModActions::SWGPacketModActions() { + tx = nullptr; + m_tx_isSet = false; +} + +SWGPacketModActions::~SWGPacketModActions() { + this->cleanup(); +} + +void +SWGPacketModActions::init() { + tx = new SWGPacketModActions_tx(); + m_tx_isSet = false; +} + +void +SWGPacketModActions::cleanup() { + if(tx != nullptr) { + delete tx; + } +} + +SWGPacketModActions* +SWGPacketModActions::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGPacketModActions::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&tx, pJson["tx"], "SWGPacketModActions_tx", "SWGPacketModActions_tx"); + +} + +QString +SWGPacketModActions::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGPacketModActions::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if((tx != nullptr) && (tx->isSet())){ + toJsonValue(QString("tx"), tx, obj, QString("SWGPacketModActions_tx")); + } + + return obj; +} + +SWGPacketModActions_tx* +SWGPacketModActions::getTx() { + return tx; +} +void +SWGPacketModActions::setTx(SWGPacketModActions_tx* tx) { + this->tx = tx; + this->m_tx_isSet = true; +} + + +bool +SWGPacketModActions::isSet(){ + bool isObjectUpdated = false; + do{ + if(tx && tx->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions.h b/swagger/sdrangel/code/qt5/client/SWGPacketModActions.h new file mode 100644 index 000000000..c21a2b902 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModActions.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: 4.15.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. + */ + +/* + * SWGPacketModActions.h + * + * PacketMod + */ + +#ifndef SWGPacketModActions_H_ +#define SWGPacketModActions_H_ + +#include + + +#include "SWGPacketModActions_tx.h" + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGPacketModActions: public SWGObject { +public: + SWGPacketModActions(); + SWGPacketModActions(QString* json); + virtual ~SWGPacketModActions(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGPacketModActions* fromJson(QString &jsonString) override; + + SWGPacketModActions_tx* getTx(); + void setTx(SWGPacketModActions_tx* tx); + + + virtual bool isSet() override; + +private: + SWGPacketModActions_tx* tx; + bool m_tx_isSet; + +}; + +} + +#endif /* SWGPacketModActions_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp new file mode 100644 index 000000000..ad9fddbeb --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp @@ -0,0 +1,185 @@ +/** + * 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: 4.15.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 "SWGPacketModActions_tx.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGPacketModActions_tx::SWGPacketModActions_tx(QString* json) { + init(); + this->fromJson(*json); +} + +SWGPacketModActions_tx::SWGPacketModActions_tx() { + callsign = nullptr; + m_callsign_isSet = false; + to = nullptr; + m_to_isSet = false; + via = nullptr; + m_via_isSet = false; + data = nullptr; + m_data_isSet = false; +} + +SWGPacketModActions_tx::~SWGPacketModActions_tx() { + this->cleanup(); +} + +void +SWGPacketModActions_tx::init() { + callsign = new QString(""); + m_callsign_isSet = false; + to = new QString(""); + m_to_isSet = false; + via = new QString(""); + m_via_isSet = false; + data = new QString(""); + m_data_isSet = false; +} + +void +SWGPacketModActions_tx::cleanup() { + if(callsign != nullptr) { + delete callsign; + } + if(to != nullptr) { + delete to; + } + if(via != nullptr) { + delete via; + } + if(data != nullptr) { + delete data; + } +} + +SWGPacketModActions_tx* +SWGPacketModActions_tx::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGPacketModActions_tx::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&callsign, pJson["callsign"], "QString", "QString"); + + ::SWGSDRangel::setValue(&to, pJson["to"], "QString", "QString"); + + ::SWGSDRangel::setValue(&via, pJson["via"], "QString", "QString"); + + ::SWGSDRangel::setValue(&data, pJson["data"], "QString", "QString"); + +} + +QString +SWGPacketModActions_tx::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGPacketModActions_tx::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(callsign != nullptr && *callsign != QString("")){ + toJsonValue(QString("callsign"), callsign, obj, QString("QString")); + } + if(to != nullptr && *to != QString("")){ + toJsonValue(QString("to"), to, obj, QString("QString")); + } + if(via != nullptr && *via != QString("")){ + toJsonValue(QString("via"), via, obj, QString("QString")); + } + if(data != nullptr && *data != QString("")){ + toJsonValue(QString("data"), data, obj, QString("QString")); + } + + return obj; +} + +QString* +SWGPacketModActions_tx::getCallsign() { + return callsign; +} +void +SWGPacketModActions_tx::setCallsign(QString* callsign) { + this->callsign = callsign; + this->m_callsign_isSet = true; +} + +QString* +SWGPacketModActions_tx::getTo() { + return to; +} +void +SWGPacketModActions_tx::setTo(QString* to) { + this->to = to; + this->m_to_isSet = true; +} + +QString* +SWGPacketModActions_tx::getVia() { + return via; +} +void +SWGPacketModActions_tx::setVia(QString* via) { + this->via = via; + this->m_via_isSet = true; +} + +QString* +SWGPacketModActions_tx::getData() { + return data; +} +void +SWGPacketModActions_tx::setData(QString* data) { + this->data = data; + this->m_data_isSet = true; +} + + +bool +SWGPacketModActions_tx::isSet(){ + bool isObjectUpdated = false; + do{ + if(callsign && *callsign != QString("")){ + isObjectUpdated = true; break; + } + if(to && *to != QString("")){ + isObjectUpdated = true; break; + } + if(via && *via != QString("")){ + isObjectUpdated = true; break; + } + if(data && *data != QString("")){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h new file mode 100644 index 000000000..4a34c656c --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h @@ -0,0 +1,77 @@ +/** + * 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: 4.15.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. + */ + +/* + * SWGPacketModActions_tx.h + * + * Transmit a packet + */ + +#ifndef SWGPacketModActions_tx_H_ +#define SWGPacketModActions_tx_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGPacketModActions_tx: public SWGObject { +public: + SWGPacketModActions_tx(); + SWGPacketModActions_tx(QString* json); + virtual ~SWGPacketModActions_tx(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGPacketModActions_tx* fromJson(QString &jsonString) override; + + QString* getCallsign(); + void setCallsign(QString* callsign); + + QString* getTo(); + void setTo(QString* to); + + QString* getVia(); + void setVia(QString* via); + + QString* getData(); + void setData(QString* data); + + + virtual bool isSet() override; + +private: + QString* callsign; + bool m_callsign_isSet; + + QString* to; + bool m_to_isSet; + + QString* via; + bool m_via_isSet; + + QString* data; + bool m_data_isSet; + +}; + +} + +#endif /* SWGPacketModActions_tx_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModReport.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModReport.cpp new file mode 100644 index 000000000..94bdea8d5 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModReport.cpp @@ -0,0 +1,154 @@ +/** + * 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: 4.15.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 "SWGPacketModReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGPacketModReport::SWGPacketModReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGPacketModReport::SWGPacketModReport() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +SWGPacketModReport::~SWGPacketModReport() { + this->cleanup(); +} + +void +SWGPacketModReport::init() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +void +SWGPacketModReport::cleanup() { + + + +} + +SWGPacketModReport* +SWGPacketModReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGPacketModReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); + + ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", ""); + +} + +QString +SWGPacketModReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGPacketModReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_power_db_isSet){ + obj->insert("channelPowerDB", QJsonValue(channel_power_db)); + } + if(m_audio_sample_rate_isSet){ + obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); + } + if(m_channel_sample_rate_isSet){ + obj->insert("channelSampleRate", QJsonValue(channel_sample_rate)); + } + + return obj; +} + +float +SWGPacketModReport::getChannelPowerDb() { + return channel_power_db; +} +void +SWGPacketModReport::setChannelPowerDb(float channel_power_db) { + this->channel_power_db = channel_power_db; + this->m_channel_power_db_isSet = true; +} + +qint32 +SWGPacketModReport::getAudioSampleRate() { + return audio_sample_rate; +} +void +SWGPacketModReport::setAudioSampleRate(qint32 audio_sample_rate) { + this->audio_sample_rate = audio_sample_rate; + this->m_audio_sample_rate_isSet = true; +} + +qint32 +SWGPacketModReport::getChannelSampleRate() { + return channel_sample_rate; +} +void +SWGPacketModReport::setChannelSampleRate(qint32 channel_sample_rate) { + this->channel_sample_rate = channel_sample_rate; + this->m_channel_sample_rate_isSet = true; +} + + +bool +SWGPacketModReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_power_db_isSet){ + isObjectUpdated = true; break; + } + if(m_audio_sample_rate_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_sample_rate_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModReport.h b/swagger/sdrangel/code/qt5/client/SWGPacketModReport.h new file mode 100644 index 000000000..089c92b4b --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModReport.h @@ -0,0 +1,70 @@ +/** + * 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: 4.15.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. + */ + +/* + * SWGPacketModReport.h + * + * PacketMod + */ + +#ifndef SWGPacketModReport_H_ +#define SWGPacketModReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGPacketModReport: public SWGObject { +public: + SWGPacketModReport(); + SWGPacketModReport(QString* json); + virtual ~SWGPacketModReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGPacketModReport* fromJson(QString &jsonString) override; + + float getChannelPowerDb(); + void setChannelPowerDb(float channel_power_db); + + qint32 getAudioSampleRate(); + void setAudioSampleRate(qint32 audio_sample_rate); + + qint32 getChannelSampleRate(); + void setChannelSampleRate(qint32 channel_sample_rate); + + + virtual bool isSet() override; + +private: + float channel_power_db; + bool m_channel_power_db_isSet; + + qint32 audio_sample_rate; + bool m_audio_sample_rate_isSet; + + qint32 channel_sample_rate; + bool m_channel_sample_rate_isSet; + +}; + +} + +#endif /* SWGPacketModReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp new file mode 100644 index 000000000..ea40165fe --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp @@ -0,0 +1,572 @@ +/** + * 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: 4.15.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 "SWGPacketModSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGPacketModSettings::SWGPacketModSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGPacketModSettings::SWGPacketModSettings() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + fm_deviation = 0.0f; + m_fm_deviation_isSet = false; + gain = 0.0f; + m_gain_isSet = false; + channel_mute = 0; + m_channel_mute_isSet = false; + repeat = 0; + m_repeat_isSet = false; + repeat_delay = 0.0f; + m_repeat_delay_isSet = false; + repeat_count = 0; + m_repeat_count_isSet = false; + ax25_pre_flags = 0; + m_ax25_pre_flags_isSet = false; + ax25_post_flags = 0; + m_ax25_post_flags_isSet = false; + pre_emphasis = 0; + m_pre_emphasis_isSet = false; + pre_emphasis_tau = 0.0f; + m_pre_emphasis_tau_isSet = false; + pre_emphasis_high_freq = 0.0f; + m_pre_emphasis_high_freq_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + stream_index = 0; + m_stream_index_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; +} + +SWGPacketModSettings::~SWGPacketModSettings() { + this->cleanup(); +} + +void +SWGPacketModSettings::init() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + fm_deviation = 0.0f; + m_fm_deviation_isSet = false; + gain = 0.0f; + m_gain_isSet = false; + channel_mute = 0; + m_channel_mute_isSet = false; + repeat = 0; + m_repeat_isSet = false; + repeat_delay = 0.0f; + m_repeat_delay_isSet = false; + repeat_count = 0; + m_repeat_count_isSet = false; + ax25_pre_flags = 0; + m_ax25_pre_flags_isSet = false; + ax25_post_flags = 0; + m_ax25_post_flags_isSet = false; + pre_emphasis = 0; + m_pre_emphasis_isSet = false; + pre_emphasis_tau = 0.0f; + m_pre_emphasis_tau_isSet = false; + pre_emphasis_high_freq = 0.0f; + m_pre_emphasis_high_freq_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + stream_index = 0; + m_stream_index_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 +SWGPacketModSettings::cleanup() { + + + + + + + + + + + + + + + if(title != nullptr) { + delete title; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + +} + +SWGPacketModSettings* +SWGPacketModSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGPacketModSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + + ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", ""); + + ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "float", ""); + + ::SWGSDRangel::setValue(&gain, pJson["gain"], "float", ""); + + ::SWGSDRangel::setValue(&channel_mute, pJson["channelMute"], "qint32", ""); + + ::SWGSDRangel::setValue(&repeat, pJson["repeat"], "qint32", ""); + + ::SWGSDRangel::setValue(&repeat_delay, pJson["repeatDelay"], "float", ""); + + ::SWGSDRangel::setValue(&repeat_count, pJson["repeatCount"], "qint32", ""); + + ::SWGSDRangel::setValue(&ax25_pre_flags, pJson["ax25PreFlags"], "qint32", ""); + + ::SWGSDRangel::setValue(&ax25_post_flags, pJson["ax25PostFlags"], "qint32", ""); + + ::SWGSDRangel::setValue(&pre_emphasis, pJson["preEmphasis"], "qint32", ""); + + ::SWGSDRangel::setValue(&pre_emphasis_tau, pJson["preEmphasisTau"], "float", ""); + + ::SWGSDRangel::setValue(&pre_emphasis_high_freq, pJson["preEmphasisHighFreq"], "float", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "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 +SWGPacketModSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGPacketModSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_input_frequency_offset_isSet){ + obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); + } + if(m_rf_bandwidth_isSet){ + obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); + } + if(m_fm_deviation_isSet){ + obj->insert("fmDeviation", QJsonValue(fm_deviation)); + } + if(m_gain_isSet){ + obj->insert("gain", QJsonValue(gain)); + } + if(m_channel_mute_isSet){ + obj->insert("channelMute", QJsonValue(channel_mute)); + } + if(m_repeat_isSet){ + obj->insert("repeat", QJsonValue(repeat)); + } + if(m_repeat_delay_isSet){ + obj->insert("repeatDelay", QJsonValue(repeat_delay)); + } + if(m_repeat_count_isSet){ + obj->insert("repeatCount", QJsonValue(repeat_count)); + } + if(m_ax25_pre_flags_isSet){ + obj->insert("ax25PreFlags", QJsonValue(ax25_pre_flags)); + } + if(m_ax25_post_flags_isSet){ + obj->insert("ax25PostFlags", QJsonValue(ax25_post_flags)); + } + if(m_pre_emphasis_isSet){ + obj->insert("preEmphasis", QJsonValue(pre_emphasis)); + } + if(m_pre_emphasis_tau_isSet){ + obj->insert("preEmphasisTau", QJsonValue(pre_emphasis_tau)); + } + if(m_pre_emphasis_high_freq_isSet){ + obj->insert("preEmphasisHighFreq", QJsonValue(pre_emphasis_high_freq)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } + 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; +} + +qint64 +SWGPacketModSettings::getInputFrequencyOffset() { + return input_frequency_offset; +} +void +SWGPacketModSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { + this->input_frequency_offset = input_frequency_offset; + this->m_input_frequency_offset_isSet = true; +} + +float +SWGPacketModSettings::getRfBandwidth() { + return rf_bandwidth; +} +void +SWGPacketModSettings::setRfBandwidth(float rf_bandwidth) { + this->rf_bandwidth = rf_bandwidth; + this->m_rf_bandwidth_isSet = true; +} + +float +SWGPacketModSettings::getFmDeviation() { + return fm_deviation; +} +void +SWGPacketModSettings::setFmDeviation(float fm_deviation) { + this->fm_deviation = fm_deviation; + this->m_fm_deviation_isSet = true; +} + +float +SWGPacketModSettings::getGain() { + return gain; +} +void +SWGPacketModSettings::setGain(float gain) { + this->gain = gain; + this->m_gain_isSet = true; +} + +qint32 +SWGPacketModSettings::getChannelMute() { + return channel_mute; +} +void +SWGPacketModSettings::setChannelMute(qint32 channel_mute) { + this->channel_mute = channel_mute; + this->m_channel_mute_isSet = true; +} + +qint32 +SWGPacketModSettings::getRepeat() { + return repeat; +} +void +SWGPacketModSettings::setRepeat(qint32 repeat) { + this->repeat = repeat; + this->m_repeat_isSet = true; +} + +float +SWGPacketModSettings::getRepeatDelay() { + return repeat_delay; +} +void +SWGPacketModSettings::setRepeatDelay(float repeat_delay) { + this->repeat_delay = repeat_delay; + this->m_repeat_delay_isSet = true; +} + +qint32 +SWGPacketModSettings::getRepeatCount() { + return repeat_count; +} +void +SWGPacketModSettings::setRepeatCount(qint32 repeat_count) { + this->repeat_count = repeat_count; + this->m_repeat_count_isSet = true; +} + +qint32 +SWGPacketModSettings::getAx25PreFlags() { + return ax25_pre_flags; +} +void +SWGPacketModSettings::setAx25PreFlags(qint32 ax25_pre_flags) { + this->ax25_pre_flags = ax25_pre_flags; + this->m_ax25_pre_flags_isSet = true; +} + +qint32 +SWGPacketModSettings::getAx25PostFlags() { + return ax25_post_flags; +} +void +SWGPacketModSettings::setAx25PostFlags(qint32 ax25_post_flags) { + this->ax25_post_flags = ax25_post_flags; + this->m_ax25_post_flags_isSet = true; +} + +qint32 +SWGPacketModSettings::getPreEmphasis() { + return pre_emphasis; +} +void +SWGPacketModSettings::setPreEmphasis(qint32 pre_emphasis) { + this->pre_emphasis = pre_emphasis; + this->m_pre_emphasis_isSet = true; +} + +float +SWGPacketModSettings::getPreEmphasisTau() { + return pre_emphasis_tau; +} +void +SWGPacketModSettings::setPreEmphasisTau(float pre_emphasis_tau) { + this->pre_emphasis_tau = pre_emphasis_tau; + this->m_pre_emphasis_tau_isSet = true; +} + +float +SWGPacketModSettings::getPreEmphasisHighFreq() { + return pre_emphasis_high_freq; +} +void +SWGPacketModSettings::setPreEmphasisHighFreq(float pre_emphasis_high_freq) { + this->pre_emphasis_high_freq = pre_emphasis_high_freq; + this->m_pre_emphasis_high_freq_isSet = true; +} + +qint32 +SWGPacketModSettings::getRgbColor() { + return rgb_color; +} +void +SWGPacketModSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGPacketModSettings::getTitle() { + return title; +} +void +SWGPacketModSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGPacketModSettings::getStreamIndex() { + return stream_index; +} +void +SWGPacketModSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGPacketModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGPacketModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGPacketModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGPacketModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGPacketModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGPacketModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGPacketModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGPacketModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGPacketModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGPacketModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + + +bool +SWGPacketModSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_input_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(m_rf_bandwidth_isSet){ + isObjectUpdated = true; break; + } + if(m_fm_deviation_isSet){ + isObjectUpdated = true; break; + } + if(m_gain_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_mute_isSet){ + isObjectUpdated = true; break; + } + if(m_repeat_isSet){ + isObjectUpdated = true; break; + } + if(m_repeat_delay_isSet){ + isObjectUpdated = true; break; + } + if(m_repeat_count_isSet){ + isObjectUpdated = true; break; + } + if(m_ax25_pre_flags_isSet){ + isObjectUpdated = true; break; + } + if(m_ax25_post_flags_isSet){ + isObjectUpdated = true; break; + } + if(m_pre_emphasis_isSet){ + isObjectUpdated = true; break; + } + if(m_pre_emphasis_tau_isSet){ + isObjectUpdated = true; break; + } + if(m_pre_emphasis_high_freq_isSet){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_stream_index_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/SWGPacketModSettings.h b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h new file mode 100644 index 000000000..15b1afae8 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h @@ -0,0 +1,179 @@ +/** + * 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: 4.15.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. + */ + +/* + * SWGPacketModSettings.h + * + * PacketMod + */ + +#ifndef SWGPacketModSettings_H_ +#define SWGPacketModSettings_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGPacketModSettings: public SWGObject { +public: + SWGPacketModSettings(); + SWGPacketModSettings(QString* json); + virtual ~SWGPacketModSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGPacketModSettings* fromJson(QString &jsonString) override; + + qint64 getInputFrequencyOffset(); + void setInputFrequencyOffset(qint64 input_frequency_offset); + + float getRfBandwidth(); + void setRfBandwidth(float rf_bandwidth); + + float getFmDeviation(); + void setFmDeviation(float fm_deviation); + + float getGain(); + void setGain(float gain); + + qint32 getChannelMute(); + void setChannelMute(qint32 channel_mute); + + qint32 getRepeat(); + void setRepeat(qint32 repeat); + + float getRepeatDelay(); + void setRepeatDelay(float repeat_delay); + + qint32 getRepeatCount(); + void setRepeatCount(qint32 repeat_count); + + qint32 getAx25PreFlags(); + void setAx25PreFlags(qint32 ax25_pre_flags); + + qint32 getAx25PostFlags(); + void setAx25PostFlags(qint32 ax25_post_flags); + + qint32 getPreEmphasis(); + void setPreEmphasis(qint32 pre_emphasis); + + float getPreEmphasisTau(); + void setPreEmphasisTau(float pre_emphasis_tau); + + float getPreEmphasisHighFreq(); + void setPreEmphasisHighFreq(float pre_emphasis_high_freq); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getStreamIndex(); + void setStreamIndex(qint32 stream_index); + + 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: + qint64 input_frequency_offset; + bool m_input_frequency_offset_isSet; + + float rf_bandwidth; + bool m_rf_bandwidth_isSet; + + float fm_deviation; + bool m_fm_deviation_isSet; + + float gain; + bool m_gain_isSet; + + qint32 channel_mute; + bool m_channel_mute_isSet; + + qint32 repeat; + bool m_repeat_isSet; + + float repeat_delay; + bool m_repeat_delay_isSet; + + qint32 repeat_count; + bool m_repeat_count_isSet; + + qint32 ax25_pre_flags; + bool m_ax25_pre_flags_isSet; + + qint32 ax25_post_flags; + bool m_ax25_post_flags_isSet; + + qint32 pre_emphasis; + bool m_pre_emphasis_isSet; + + float pre_emphasis_tau; + bool m_pre_emphasis_tau_isSet; + + float pre_emphasis_high_freq; + bool m_pre_emphasis_high_freq_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + qint32 stream_index; + bool m_stream_index_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 /* SWGPacketModSettings_H_ */