diff --git a/doc/img/IEEE_802_15_4_Mod_plugin.png b/doc/img/IEEE_802_15_4_Mod_plugin.png new file mode 100644 index 000000000..ffbe333b6 Binary files /dev/null and b/doc/img/IEEE_802_15_4_Mod_plugin.png differ diff --git a/plugins/channeltx/CMakeLists.txt b/plugins/channeltx/CMakeLists.txt index 47bf45234..88cd7ae61 100644 --- a/plugins/channeltx/CMakeLists.txt +++ b/plugins/channeltx/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(modnfm) add_subdirectory(modssb) add_subdirectory(modwfm) add_subdirectory(modpacket) +add_subdirectory(mod802.15.4) add_subdirectory(udpsource) add_subdirectory(localsource) add_subdirectory(filesource) diff --git a/plugins/channeltx/mod802.15.4/CMakeLists.txt b/plugins/channeltx/mod802.15.4/CMakeLists.txt new file mode 100644 index 000000000..11dd722f6 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/CMakeLists.txt @@ -0,0 +1,66 @@ +project(mod_ieee_802_15_4) + +set(mod_ieee_802_15_4_SOURCES + ieee_802_15_4_mod.cpp + ieee_802_15_4_modbaseband.cpp + ieee_802_15_4_modsource.cpp + ieee_802_15_4_modplugin.cpp + ieee_802_15_4_modsettings.cpp + ieee_802_15_4_modwebapiadapter.cpp +) + +set(mod_ieee_802_15_4_HEADERS + ieee_802_15_4_mod.h + ieee_802_15_4_modbaseband.h + ieee_802_15_4_modsource.h + ieee_802_15_4_modplugin.h + ieee_802_15_4_modsettings.h + ieee_802_15_4_modwebapiadapter.h + ieee_802_15_4_macframe.h +) + +include_directories( + ${Boost_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(mod_ieee_802_15_4_SOURCES + ${mod_ieee_802_15_4_SOURCES} + ieee_802_15_4_modgui.cpp + ieee_802_15_4_modgui.ui + ieee_802_15_4_modrepeatdialog.cpp + ieee_802_15_4_modrepeatdialog.ui + ieee_802_15_4_modtxsettingsdialog.cpp + ieee_802_15_4_modtxsettingsdialog.ui + ) + set(mod_ieee_802_15_4_HEADERS + ${mod_ieee_802_15_4_HEADERS} + ieee_802_15_4_modgui.h + ieee_802_15_4_modrepeatdialog.h + ieee_802_15_4_modtxsettingsdialog.h + ) + set(TARGET_NAME modieee_802_15_4) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME modieee_802_15_4srv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${mod_ieee_802_15_4_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/mod802.15.4/ieee_802_15_4_macframe.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_macframe.h new file mode 100644 index 000000000..a2c8b282a --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_macframe.h @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MACFRAME_H +#define INCLUDE_IEEE_802_15_4_MACFRAME_H + +#include +#include + +// Frame control field values +#define IEEE_802_15_4_MAC_FRAME_TYPE_MASK 0x0003 +#define IEEE_802_15_4_MAC_BEACON 0x0000 +#define IEEE_802_15_4_MAC_DATA 0x0001 +#define IEEE_802_15_4_MAC_ACK 0x0002 +#define IEEE_802_15_4_MAC_COMMAND 0x0003 +#define IEEE_802_15_4_MAC_SECURITY_ENABLED 0x0008 +#define IEEE_802_15_4_MAC_FRAME_PENDING 0x0010 +#define IEEE_802_15_4_MAC_ACK_REQUIRED 0x0020 +#define IEEE_802_15_4_MAC_PAN_COMPRESSION 0x0040 +#define IEEE_802_15_4_MAC_NO_SEQ_NUMBER 0x0100 +#define IEEE_802_15_4_MAC_DEST_ADDRESS_MASK 0x0c00 +#define IEEE_802_15_4_MAC_DEST_ADDRESS_NONE 0x0000 +#define IEEE_802_15_4_MAC_DEST_ADDRESS_SHORT 0x0400 +#define IEEE_802_15_4_MAC_DEST_ADDRESS_EXT 0x0c00 +#define IEEE_802_15_4_MAC_SOURCE_ADDRESS_MASK 0xc000 +#define IEEE_802_15_4_MAC_SOURCE_ADDRESS_NONE 0x0000 +#define IEEE_802_15_4_MAC_SOURCE_ADDRESS_SHORT 0x4000 +#define IEEE_802_15_4_MAC_SOURCE_ADDRESS_EXT 0xc000 +#define IEEE_802_15_4_MAC_PAYLOAD_MAX_LENGTH 124 +#define IEEE_802_15_5_MAX_EXT_ADDRESS_LENGTH 8 + +typedef uint8_t ieee_802_15_4_address[IEEE_802_15_5_MAX_EXT_ADDRESS_LENGTH]; + +struct IEEE_802_15_4_MacFrame +{ + uint16_t m_frameControl; + uint8_t m_sequenceNumber; + uint16_t m_destPANID; + uint16_t m_destShortAddress; + ieee_802_15_4_address m_destAddress; + uint16_t m_sourcePANID; + uint16_t m_sourceShortAddress; + ieee_802_15_4_address m_sourceAddress; + uint8_t m_payload[IEEE_802_15_4_MAC_PAYLOAD_MAX_LENGTH]; + uint8_t m_payloadLength; + + IEEE_802_15_4_MacFrame() + { + if (false) + { + // Example ACK frame + m_frameControl = IEEE_802_15_4_MAC_ACK; + m_sequenceNumber = 0; + m_payloadLength = 0; + } + else + { + ieee_802_15_4_address dst = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + ieee_802_15_4_address src = {0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + + // Example data frame + m_frameControl = IEEE_802_15_4_MAC_DATA | IEEE_802_15_4_MAC_DEST_ADDRESS_EXT | IEEE_802_15_4_MAC_SOURCE_ADDRESS_EXT; + m_sequenceNumber = 0; + m_destPANID = 0xbabe; + memcpy(m_destAddress, dst, sizeof(m_destAddress)); + m_sourcePANID = 0xbabe; + memcpy(m_sourceAddress, src, sizeof(m_sourceAddress)); + strcpy((char *)m_payload, "SDR Angel does 15.4"); + m_payloadLength = strlen((char *)m_payload); + } + } + + char *bytesToHex(char *buf, uint8_t *data, int len) + { + for (int i = 0; i < len; i++) + buf += sprintf(buf, "%02x ", data[i]); + return buf; + } + + void toHexCharArray(char *buf) + { + buf += sprintf(buf, "%02x %02x %02x ", m_frameControl & 0xff, (m_frameControl >> 8) & 0xff, m_sequenceNumber); + + if ((m_frameControl & IEEE_802_15_4_MAC_FRAME_TYPE_MASK) != IEEE_802_15_4_MAC_ACK) + buf += sprintf(buf, "%02x %02x ", m_destPANID & 0xff, (m_destPANID >> 8) & 0xff); + if ((m_frameControl & IEEE_802_15_4_MAC_DEST_ADDRESS_MASK) == IEEE_802_15_4_MAC_DEST_ADDRESS_EXT) + buf = bytesToHex(buf, m_destAddress, sizeof(m_destAddress)); + else if ((m_frameControl & IEEE_802_15_4_MAC_DEST_ADDRESS_MASK) == IEEE_802_15_4_MAC_DEST_ADDRESS_SHORT) + buf += sprintf(buf, "%02x %02x ", m_destShortAddress & 0xff, (m_destShortAddress >> 8) & 0xff); + + if (((m_frameControl & IEEE_802_15_4_MAC_FRAME_TYPE_MASK) != IEEE_802_15_4_MAC_ACK) + && (!(m_frameControl & IEEE_802_15_4_MAC_PAN_COMPRESSION) || (m_destPANID != m_sourcePANID))) + buf += sprintf(buf, "%02x %02x ", m_sourcePANID & 0xff, (m_sourcePANID >> 8) & 0xff); + if ((m_frameControl & IEEE_802_15_4_MAC_SOURCE_ADDRESS_MASK) == IEEE_802_15_4_MAC_SOURCE_ADDRESS_EXT) + buf = bytesToHex(buf, m_sourceAddress, sizeof(m_sourceAddress)); + else if ((m_frameControl & IEEE_802_15_4_MAC_SOURCE_ADDRESS_MASK) == IEEE_802_15_4_MAC_SOURCE_ADDRESS_SHORT) + buf += sprintf(buf, "%02x %02x ", m_sourceShortAddress & 0xff, (m_sourceShortAddress >> 8) & 0xff); + + buf = bytesToHex(buf, m_payload, m_payloadLength); + } + +}; + +#endif /* INCLUDE_IEEE_802_15_4_MACFRAME_H */ diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp new file mode 100644 index 000000000..1003fb800 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -0,0 +1,525 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "SWGIEEE_802_15_4_ModReport.h" +#include "SWGIEEE_802_15_4_ModActions.h" +#include "SWGIEEE_802_15_4_ModActions_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 "ieee_802_15_4_modbaseband.h" +#include "ieee_802_15_4_mod.h" + +MESSAGE_CLASS_DEFINITION(IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod, Message) +MESSAGE_CLASS_DEFINITION(IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod, Message) + +const QString IEEE_802_15_4_Mod::m_channelIdURI = "sdrangel.channeltx.mod802.15.4"; +const QString IEEE_802_15_4_Mod::m_channelId = "IEEE_802_15_4_Mod"; + +IEEE_802_15_4_Mod::IEEE_802_15_4_Mod(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), + m_deviceAPI(deviceAPI), + m_spectrumVis(SDR_TX_SCALEF), + m_settingsMutex(QMutex::Recursive) +{ + setObjectName(m_channelId); + + m_thread = new QThread(this); + m_basebandSource = new IEEE_802_15_4_ModBaseband(); + 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*))); +} + +IEEE_802_15_4_Mod::~IEEE_802_15_4_Mod() +{ + 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 IEEE_802_15_4_Mod::start() +{ + qDebug("IEEE_802_15_4_Mod::start"); + m_basebandSource->reset(); + m_thread->start(); +} + +void IEEE_802_15_4_Mod::stop() +{ + qDebug("IEEE_802_15_4_Mod::stop"); + m_thread->exit(); + m_thread->wait(); +} + +void IEEE_802_15_4_Mod::pull(SampleVector::iterator& begin, unsigned int nbSamples) +{ + m_basebandSource->pull(begin, nbSamples); +} + +bool IEEE_802_15_4_Mod::handleMessage(const Message& cmd) +{ + if (MsgConfigureIEEE_802_15_4_Mod::match(cmd)) + { + MsgConfigureIEEE_802_15_4_Mod& cfg = (MsgConfigureIEEE_802_15_4_Mod&) cmd; + qDebug() << "IEEE_802_15_4_Mod::handleMessage: MsgConfigureIEEE_802_15_4_Mod"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgTXIEEE_802_15_4_Mod::match(cmd)) + { + // Forward a copy to baseband + MsgTXIEEE_802_15_4_Mod* rep = new MsgTXIEEE_802_15_4_Mod((MsgTXIEEE_802_15_4_Mod&)cmd); + qDebug() << "IEEE_802_15_4_Mod::handleMessage: MsgTXIEEE_802_15_4_Mod"; + 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() << "IEEE_802_15_4_Mod::handleMessage: DSPSignalNotification"; + m_basebandSource->getInputMessageQueue()->push(rep); + + // Forward to GUI + if (getMessageQueueToGUI()) + { + DSPSignalNotification *notifToGUI = new DSPSignalNotification(notif); + getMessageQueueToGUI()->push(notifToGUI); + } + + return true; + } + else + { + return false; + } +} + +void IEEE_802_15_4_Mod::applySettings(const IEEE_802_15_4_ModSettings& settings, bool force) +{ + qDebug() << "IEEE_802_15_4_Mod::applySettings:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " 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_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_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 (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"); + } + + IEEE_802_15_4_ModBaseband::MsgConfigureIEEE_802_15_4_ModBaseband *msg = IEEE_802_15_4_ModBaseband::MsgConfigureIEEE_802_15_4_ModBaseband::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 IEEE_802_15_4_Mod::serialize() const +{ + return m_settings.serialize(); +} + +bool IEEE_802_15_4_Mod::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureIEEE_802_15_4_Mod *msg = MsgConfigureIEEE_802_15_4_Mod::create(m_settings, true); + m_inputMessageQueue.push(msg); + + return success; +} + +int IEEE_802_15_4_Mod::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings()); + response.getIeee802154ModSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int IEEE_802_15_4_Mod::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + IEEE_802_15_4_ModSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigureIEEE_802_15_4_Mod *msg = MsgConfigureIEEE_802_15_4_Mod::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureIEEE_802_15_4_Mod *msgToGUI = MsgConfigureIEEE_802_15_4_Mod::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +void IEEE_802_15_4_Mod::webapiUpdateChannelSettings( + IEEE_802_15_4_ModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + if (channelSettingsKeys.contains("inputFrequencyOffset")) { + settings.m_inputFrequencyOffset = response.getIeee802154ModSettings()->getInputFrequencyOffset(); + } + if (channelSettingsKeys.contains("phy")) { + settings.setPHY(*response.getIeee802154ModSettings()->getPhy()); + } + if (channelSettingsKeys.contains("rfBandwidth")) { + settings.m_rfBandwidth = response.getIeee802154ModSettings()->getRfBandwidth(); + } + if (channelSettingsKeys.contains("gain")) { + settings.m_gain = response.getIeee802154ModSettings()->getGain(); + } + if (channelSettingsKeys.contains("channelMute")) { + settings.m_channelMute = response.getIeee802154ModSettings()->getChannelMute() != 0; + } + if (channelSettingsKeys.contains("repeat")) { + settings.m_repeat = response.getIeee802154ModSettings()->getRepeat() != 0; + } + if (channelSettingsKeys.contains("repeatDelay")) { + settings.m_repeatDelay = response.getIeee802154ModSettings()->getRepeatDelay(); + } + if (channelSettingsKeys.contains("repeatCount")) { + settings.m_repeatCount = response.getIeee802154ModSettings()->getRepeatCount(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getIeee802154ModSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getIeee802154ModSettings()->getTitle(); + } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.getIeee802154ModSettings()->getStreamIndex(); + } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getIeee802154ModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getIeee802154ModSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getIeee802154ModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getIeee802154ModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getIeee802154ModSettings()->getReverseApiChannelIndex(); + } +} + +int IEEE_802_15_4_Mod::webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIeee802154ModReport(new SWGSDRangel::SWGIEEE_802_15_4_ModReport()); + response.getIeee802154ModReport()->init(); + webapiFormatChannelReport(response); + return 200; +} + +int IEEE_802_15_4_Mod::webapiActionsPost( + const QStringList& channelActionsKeys, + SWGSDRangel::SWGChannelActions& query, + QString& errorMessage) +{ + SWGSDRangel::SWGIEEE_802_15_4_ModActions *swgIEEE_802_15_4_ModActions = query.getIeee802154ModActions(); + + if (swgIEEE_802_15_4_ModActions) + { + if (channelActionsKeys.contains("tx")) + { + SWGSDRangel::SWGIEEE_802_15_4_ModActions_tx* tx = swgIEEE_802_15_4_ModActions->getTx(); + QString *dataP = tx->getData(); + if (dataP != nullptr) + { + QString data(*dataP); + + IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod *msg = IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod::create(data); + m_basebandSource->getInputMessageQueue()->push(msg); + return 202; + } + else + { + errorMessage = "Missing data to transmit"; + return 400; + } + } + else + { + errorMessage = "Unknown action"; + return 400; + } + } + else + { + errorMessage = "Missing IEEE_802_15_4_ModActions in query"; + return 400; + } +} + +void IEEE_802_15_4_Mod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const IEEE_802_15_4_ModSettings& settings) +{ + response.getIeee802154ModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getIeee802154ModSettings()->setPhy(new QString(settings.getPHY())); + response.getIeee802154ModSettings()->setRfBandwidth(settings.m_rfBandwidth); + response.getIeee802154ModSettings()->setGain(settings.m_gain); + response.getIeee802154ModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0); + response.getIeee802154ModSettings()->setRepeat(settings.m_repeat ? 1 : 0); + response.getIeee802154ModSettings()->setRepeatDelay(settings.m_repeatDelay); + response.getIeee802154ModSettings()->setRepeatCount(settings.m_repeatCount); + response.getIeee802154ModSettings()->setRgbColor(settings.m_rgbColor); + + if (response.getIeee802154ModSettings()->getTitle()) { + *response.getIeee802154ModSettings()->getTitle() = settings.m_title; + } else { + response.getIeee802154ModSettings()->setTitle(new QString(settings.m_title)); + } + + response.getIeee802154ModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getIeee802154ModSettings()->getReverseApiAddress()) { + *response.getIeee802154ModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getIeee802154ModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getIeee802154ModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getIeee802154ModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getIeee802154ModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); +} + +void IEEE_802_15_4_Mod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +{ + response.getIeee802154ModReport()->setChannelPowerDb(CalcDb::dbPower(getMagSq())); + response.getIeee802154ModReport()->setChannelSampleRate(m_basebandSource->getChannelSampleRate()); +} + +void IEEE_802_15_4_Mod::webapiReverseSendSettings(QList& channelSettingsKeys, const IEEE_802_15_4_ModSettings& 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("IEEE_802_15_4_Mod")); + swgChannelSettings->setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings()); + SWGSDRangel::SWGIEEE_802_15_4_ModSettings *swgIEEE_802_15_4_ModSettings = swgChannelSettings->getIeee802154ModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgIEEE_802_15_4_ModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgIEEE_802_15_4_ModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("gain") || force) { + swgIEEE_802_15_4_ModSettings->setGain(settings.m_gain); + } + if (channelSettingsKeys.contains("channelMute") || force) { + swgIEEE_802_15_4_ModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("repeat") || force) { + swgIEEE_802_15_4_ModSettings->setRepeat(settings.m_repeat ? 1 : 0); + } + if (channelSettingsKeys.contains("repeatDelay") || force) { + swgIEEE_802_15_4_ModSettings->setRepeatDelay(settings.m_repeatDelay); + } + if (channelSettingsKeys.contains("repeatCount") || force) { + swgIEEE_802_15_4_ModSettings->setRepeatCount(settings.m_repeatCount); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgIEEE_802_15_4_ModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgIEEE_802_15_4_ModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgIEEE_802_15_4_ModSettings->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 IEEE_802_15_4_Mod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "IEEE_802_15_4_Mod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("IEEE_802_15_4_Mod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +double IEEE_802_15_4_Mod::getMagSq() const +{ + return m_basebandSource->getMagSq(); +} + +void IEEE_802_15_4_Mod::setLevelMeter(QObject *levelMeter) +{ + connect(m_basebandSource, SIGNAL(levelChanged(qreal, qreal, int)), levelMeter, SLOT(levelChanged(qreal, qreal, int))); +} + +uint32_t IEEE_802_15_4_Mod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSinkStreams(); +} + +void IEEE_802_15_4_Mod::setScopeSink(BasebandSampleSink* scopeSink) +{ + m_basebandSource->setScopeSink(scopeSink); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h new file mode 100644 index 000000000..3e3c82dcc --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 INCLUDE_IEEE_802_15_4_MOD_H +#define INCLUDE_IEEE_802_15_4_MOD_H + +#include +#include +#include + +#include +#include + +#include "dsp/basebandsamplesource.h" +#include "dsp/spectrumvis.h" +#include "channel/channelapi.h" +#include "util/message.h" + +#include "ieee_802_15_4_modsettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class DeviceAPI; +class IEEE_802_15_4_ModBaseband; + +class IEEE_802_15_4_Mod : public BasebandSampleSource, public ChannelAPI { + Q_OBJECT + +public: + class MsgConfigureIEEE_802_15_4_Mod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const IEEE_802_15_4_ModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureIEEE_802_15_4_Mod* create(const IEEE_802_15_4_ModSettings& settings, bool force) + { + return new MsgConfigureIEEE_802_15_4_Mod(settings, force); + } + + private: + IEEE_802_15_4_ModSettings m_settings; + bool m_force; + + MsgConfigureIEEE_802_15_4_Mod(const IEEE_802_15_4_ModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgTXIEEE_802_15_4_Mod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgTXIEEE_802_15_4_Mod* create(QString data) + { + return new MsgTXIEEE_802_15_4_Mod(data); + } + + QString m_data; + + private: + + MsgTXIEEE_802_15_4_Mod(QString data) : + Message(), + m_data(data) + { } + }; + + //================================================================= + + IEEE_802_15_4_Mod(DeviceAPI *deviceAPI); + ~IEEE_802_15_4_Mod(); + 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 const QString& getURI() const { return m_channelIdURI; } + 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 IEEE_802_15_4_ModSettings& settings); + + static void webapiUpdateChannelSettings( + IEEE_802_15_4_ModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } + void setScopeSink(BasebandSampleSink* scopeSink); + double getMagSq() const; + void setLevelMeter(QObject *levelMeter); + uint32_t getNumberOfDeviceStreams() const; + + static const QString m_channelIdURI; + static const QString m_channelId; + +private: + + DeviceAPI* m_deviceAPI; + QThread *m_thread; + IEEE_802_15_4_ModBaseband* m_basebandSource; + IEEE_802_15_4_ModSettings m_settings; + SpectrumVis m_spectrumVis; + + SampleVector m_sampleBuffer; + QMutex m_settingsMutex; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void applySettings(const IEEE_802_15_4_ModSettings& settings, bool force = false); + void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const IEEE_802_15_4_ModSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + + +#endif /* INCLUDE_IEEE_802_15_4_MOD_H */ diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.cpp new file mode 100644 index 000000000..50fcdf5c5 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.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 "ieee_802_15_4_modbaseband.h" +#include "ieee_802_15_4_mod.h" + +MESSAGE_CLASS_DEFINITION(IEEE_802_15_4_ModBaseband::MsgConfigureIEEE_802_15_4_ModBaseband, Message) + +IEEE_802_15_4_ModBaseband::IEEE_802_15_4_ModBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(48000)); + m_channelizer = new UpChannelizer(&m_source); + + qDebug("IEEE_802_15_4_ModBaseband::IEEE_802_15_4_ModBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSourceFifo::dataRead, + this, + &IEEE_802_15_4_ModBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +IEEE_802_15_4_ModBaseband::~IEEE_802_15_4_ModBaseband() +{ + delete m_channelizer; +} + +void IEEE_802_15_4_ModBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void IEEE_802_15_4_ModBaseband::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 IEEE_802_15_4_ModBaseband::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 IEEE_802_15_4_ModBaseband::processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd) +{ + m_channelizer->prefetch(iEnd - iBegin); + m_channelizer->pull(data.begin() + iBegin, iEnd - iBegin); +} + +void IEEE_802_15_4_ModBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool IEEE_802_15_4_ModBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureIEEE_802_15_4_ModBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureIEEE_802_15_4_ModBaseband& cfg = (MsgConfigureIEEE_802_15_4_ModBaseband&) cmd; + qDebug() << "IEEE_802_15_4_ModBaseband::handleMessage: MsgConfigureIEEE_802_15_4_ModBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod::match(cmd)) + { + IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod& tx = (IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod&) cmd; + m_source.addTXFrame(tx.m_data); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "IEEE_802_15_4_ModBaseband::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() << "IEEE_802_15_4_ModBaseband - Baseband got unknown message"; + return false; + } +} + +void IEEE_802_15_4_ModBaseband::applySettings(const IEEE_802_15_4_ModSettings& 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 IEEE_802_15_4_ModBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.h new file mode 100644 index 000000000..6ae89ceb6 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modbaseband.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MODBASEBAND_H +#define INCLUDE_IEEE_802_15_4_MODBASEBAND_H + +#include +#include + +#include "dsp/samplesourcefifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "ieee_802_15_4_modsource.h" + +class UpChannelizer; + +class IEEE_802_15_4_ModBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureIEEE_802_15_4_ModBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const IEEE_802_15_4_ModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureIEEE_802_15_4_ModBaseband* create(const IEEE_802_15_4_ModSettings& settings, bool force) + { + return new MsgConfigureIEEE_802_15_4_ModBaseband(settings, force); + } + + private: + IEEE_802_15_4_ModSettings m_settings; + bool m_force; + + MsgConfigureIEEE_802_15_4_ModBaseband(const IEEE_802_15_4_ModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + IEEE_802_15_4_ModBaseband(); + ~IEEE_802_15_4_ModBaseband(); + 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); } + void setScopeSink(BasebandSampleSink* scopeSink) { m_source.setScopeSink(scopeSink); } + + +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; + IEEE_802_15_4_ModSource m_source; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + IEEE_802_15_4_ModSettings m_settings; + QMutex m_mutex; + + void processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd); + bool handleMessage(const Message& cmd); + void applySettings(const IEEE_802_15_4_ModSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + + +#endif // INCLUDE_IEEE_802_15_4_MODBASEBAND_H diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp new file mode 100644 index 000000000..4cc273def --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -0,0 +1,546 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "device/deviceuiset.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "dsp/spectrumvis.h" +#include "dsp/scopevis.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicchannelsettingsdialog.h" +#include "gui/devicestreamselectiondialog.h" +#include "mainwindow.h" + +#include "ui_IEEE_802_15_4_Modgui.h" +#include "ieee_802_15_4_modgui.h" +#include "ieee_802_15_4_modrepeatdialog.h" +#include "ieee_802_15_4_modtxsettingsdialog.h" + + +IEEE_802_15_4_ModGUI* IEEE_802_15_4_ModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx) +{ + IEEE_802_15_4_ModGUI* gui = new IEEE_802_15_4_ModGUI(pluginAPI, deviceUISet, channelTx); + return gui; +} + +void IEEE_802_15_4_ModGUI::destroy() +{ + delete this; +} + +void IEEE_802_15_4_ModGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString IEEE_802_15_4_ModGUI::getName() const +{ + return objectName(); +} + +qint64 IEEE_802_15_4_ModGUI::getCenterFrequency() const { + return m_channelMarker.getCenterFrequency(); +} + +void IEEE_802_15_4_ModGUI::setCenterFrequency(qint64 centerFrequency) +{ + m_channelMarker.setCenterFrequency(centerFrequency); + applySettings(); +} + +void IEEE_802_15_4_ModGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray IEEE_802_15_4_ModGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool IEEE_802_15_4_ModGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applySettings(true); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool IEEE_802_15_4_ModGUI::handleMessage(const Message& message) +{ + if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_basebandSampleRate = notif.getSampleRate(); + m_scopeVis->setLiveRate(m_basebandSampleRate); + checkSampleRate(); + return true; + } + else if (IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod::match(message)) + { + const IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod& cfg = (IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +void IEEE_802_15_4_ModGUI::channelMarkerChangedByCursor() +{ + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + +void IEEE_802_15_4_ModGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +// Check sample rate is good enough and if not, display a warning in title +void IEEE_802_15_4_ModGUI::checkSampleRate() +{ + int cr = m_settings.getChipRate(); + if ((m_basebandSampleRate % cr) != 0) + setWindowTitle(m_channelMarker.getTitle() + " - Baseband sample rate is not an integer multiple of chip rate"); + else if ((m_basebandSampleRate / cr) <= 2) + setWindowTitle(m_channelMarker.getTitle() + " - Baseband sample rate is too low"); + else + setWindowTitle(m_channelMarker.getTitle()); +} + +void IEEE_802_15_4_ModGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_phy_currentIndexChanged(int value) +{ + QString phy = ui->phy->currentText(); + + // If m_doApplySettings is set, we are here from a call to displaySettings, + // so we only want to display the current settings, not update them + // as though a user had selected a new PHY + if (m_doApplySettings) + m_settings.setPHY(phy); + + ui->rfBWText->setText(QString("%1M").arg(m_settings.m_rfBandwidth / 1000000.0, 0, 'f', 1)); + ui->rfBW->setValue(m_settings.m_rfBandwidth / 1000.0); + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate); + checkSampleRate(); + applySettings(); + + // Remove custom PHY when deselected, as we no longer know how to set it + if (value < 6) + ui->phy->removeItem(6); +} + +void IEEE_802_15_4_ModGUI::on_rfBW_valueChanged(int value) +{ + float bw = value * 1000.0f; + ui->rfBWText->setText(QString("%1M").arg(value / 1000.0, 0, 'f', 1)); + m_channelMarker.setBandwidth(bw); + m_settings.m_rfBandwidth = bw; + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_gain_valueChanged(int value) +{ + ui->gainText->setText(QString("%1dB").arg(value)); + m_settings.m_gain = value; + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_channelMute_toggled(bool checked) +{ + m_settings.m_channelMute = checked; + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_txButton_clicked(bool checked) +{ + transmit(); +} + +void IEEE_802_15_4_ModGUI::on_frame_returnPressed() +{ + transmit(); +} + +void IEEE_802_15_4_ModGUI::on_frame_editingFinished() +{ + m_settings.m_data = ui->frame->text(); + applySettings(); +} + +void IEEE_802_15_4_ModGUI::on_repeat_toggled(bool checked) +{ + m_settings.m_repeat = checked; + applySettings(); +} + +void IEEE_802_15_4_ModGUI::repeatSelect() +{ + IEEE_802_15_4_ModRepeatDialog 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 IEEE_802_15_4_ModGUI::txSettingsSelect() +{ + IEEE_802_15_4_ModTXSettingsDialog dialog(m_settings.m_rampUpBits, m_settings.m_rampDownBits, + m_settings.m_rampRange, m_settings.m_modulateWhileRamping, + m_settings.m_modulation, m_settings.m_bitRate, + m_settings.m_pulseShaping, m_settings.m_beta, m_settings.m_symbolSpan, + m_settings.m_scramble, m_settings.m_polynomial, + m_settings.m_lpfTaps, + m_settings.m_bbNoise, + 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_modulation = static_cast(dialog.m_modulation); + m_settings.m_bitRate = dialog.m_bitRate; + m_settings.m_pulseShaping = static_cast(dialog.m_pulseShaping); + m_settings.m_beta = dialog.m_beta; + m_settings.m_symbolSpan = dialog.m_symbolSpan; + m_settings.m_scramble = dialog.m_scramble; + m_settings.m_polynomial = dialog.m_polynomial; + m_settings.m_lpfTaps = dialog.m_lpfTaps; + m_settings.m_bbNoise = dialog.m_bbNoise; + m_settings.m_writeToFile = dialog.m_writeToFile; + displaySettings(); + applySettings(); + } +} + +void IEEE_802_15_4_ModGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; +} + +void IEEE_802_15_4_ModGUI::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_IEEE_802_15_4_Mod->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(); +} + +IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent) : + ChannelGUI(parent), + ui(new Ui::IEEE_802_15_4_ModGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_doApplySettings(true), + m_basebandSampleRate(12000000) +{ + 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_IEEE_802_15_4_Mod = (IEEE_802_15_4_Mod*) channelTx; + m_IEEE_802_15_4_Mod->setMessageQueueToGUI(getInputMessageQueue()); + + connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + m_scopeVis = new ScopeVis(ui->glScope); + m_IEEE_802_15_4_Mod->setScopeSink(m_scopeVis); + ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); + connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms + ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); + + // Scope settings to display the IQ waveforms + ui->scopeGUI->setPreTrigger(1); + ScopeVis::TraceData traceDataI, traceDataQ; + traceDataI.m_projectionType = Projector::ProjectionReal; + traceDataI.m_amp = 1.0; // for -1 to +1 + traceDataI.m_ampIndex = 0; + traceDataI.m_ofs = 0.0; // vertical offset + traceDataI.m_ofsCoarse = 0; + traceDataQ.m_projectionType = Projector::ProjectionImag; + traceDataQ.m_amp = 1.0; + traceDataQ.m_ampIndex = 0; + traceDataQ.m_ofs = 0.0; + traceDataQ.m_ofsCoarse = 0; + ui->scopeGUI->changeTrace(0, traceDataI); + ui->scopeGUI->addTrace(traceDataQ); + ui->scopeGUI->setDisplayMode(GLScopeGUI::DisplayPol); + ui->scopeGUI->focusOnTrace(0); // re-focus to take changes into account in the GUI + + ScopeVis::TriggerData triggerData; + triggerData.m_triggerLevel = 0.1; + triggerData.m_triggerLevelCoarse = 10; + triggerData.m_triggerPositiveEdge = true; + ui->scopeGUI->changeTrigger(0, triggerData); + ui->scopeGUI->focusOnTrigger(0); // re-focus to take changes into account in the GUI + + m_scopeVis->setLiveRate(m_basebandSampleRate); + //m_scopeVis->setFreeRun(false); // FIXME: add method rather than call m_scopeVis->configure() + + m_spectrumVis = m_IEEE_802_15_4_Mod->getSpectrumVis(); + m_spectrumVis->setGLSpectrum(ui->glSpectrum); + + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate); + ui->glSpectrum->setSsbSpectrum(false); + 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())); + + 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("802.15.4 Modulator"); + m_channelMarker.setSourceOrSinkStream(false); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + 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_IEEE_802_15_4_Mod->setLevelMeter(ui->volumeMeter); + + m_settings.setChannelMarker(&m_channelMarker); + + ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); + + displaySettings(); + applySettings(); +} + +IEEE_802_15_4_ModGUI::~IEEE_802_15_4_ModGUI() +{ + delete m_IEEE_802_15_4_Mod; // TODO: check this: when the GUI closes it has to delete the modulator + delete m_scopeVis; + delete ui; +} + +void IEEE_802_15_4_ModGUI::transmit() +{ + QString data = ui->frame->text(); + ui->transmittedText->appendPlainText(data + "\n"); + IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod *msg = IEEE_802_15_4_Mod::MsgTXIEEE_802_15_4_Mod::create(data); + m_IEEE_802_15_4_Mod->getInputMessageQueue()->push(msg); +} + +void IEEE_802_15_4_ModGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void IEEE_802_15_4_ModGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod *msg = IEEE_802_15_4_Mod::MsgConfigureIEEE_802_15_4_Mod::create(m_settings, force); + m_IEEE_802_15_4_Mod->getInputMessageQueue()->push(msg); + } +} + +void IEEE_802_15_4_ModGUI::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()); + if ((m_settings.m_bitRate == 20000) + && m_settings.m_subGHzBand + && (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::RC) + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::BPSK)) + ui->phy->setCurrentIndex(0); + else if ((m_settings.m_bitRate == 40000) + && m_settings.m_subGHzBand + && (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::RC) + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::BPSK)) + ui->phy->setCurrentIndex(1); + else if ((m_settings.m_bitRate == 100000) + && m_settings.m_subGHzBand + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::OQPSK)) + ui->phy->setCurrentIndex(2); + else if ((m_settings.m_bitRate == 250000) + && m_settings.m_subGHzBand + && (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::SINE) + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::OQPSK)) + ui->phy->setCurrentIndex(3); + else if ((m_settings.m_bitRate == 250000) + && m_settings.m_subGHzBand + && (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::RC) + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::OQPSK)) + ui->phy->setCurrentIndex(4); + else if ((m_settings.m_bitRate == 250000) + && !m_settings.m_subGHzBand + && (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::SINE) + && (m_settings.m_modulation == IEEE_802_15_4_ModSettings::OQPSK)) + ui->phy->setCurrentIndex(5); + else + { + ui->phy->removeItem(6); + ui->phy->addItem(m_settings.getPHY()); + ui->phy->setCurrentIndex(6); + } + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate); + + ui->rfBWText->setText(QString("%1M").arg(m_settings.m_rfBandwidth / 1000000.0, 0, 'f', 1)); + ui->rfBW->setValue(m_settings.m_rfBandwidth / 1000.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->frame->setText(m_settings.m_data); + + blockApplySettings(false); +} + +void IEEE_802_15_4_ModGUI::displayStreamIndex() +{ + if (m_deviceUISet->m_deviceMIMOEngine) { + setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); + } else { + setStreamIndicator("S"); // single channel indicator + } +} + +void IEEE_802_15_4_ModGUI::leaveEvent(QEvent*) +{ + m_channelMarker.setHighlighted(false); +} + +void IEEE_802_15_4_ModGUI::enterEvent(QEvent*) +{ + m_channelMarker.setHighlighted(true); +} + +void IEEE_802_15_4_ModGUI::tick() +{ + double powDb = CalcDb::dbPower(m_IEEE_802_15_4_Mod->getMagSq()); + m_channelPowerDbAvg(powDb); + ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h new file mode 100644 index 000000000..fec671a95 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MODGUI_H +#define INCLUDE_IEEE_802_15_4_MODGUI_H + +#include "channel/channelgui.h" +#include "dsp/channelmarker.h" +#include "util/movingaverage.h" +#include "util/messagequeue.h" + +#include "ieee_802_15_4_mod.h" +#include "ieee_802_15_4_modsettings.h" + +class PluginAPI; +class DeviceUISet; +class BasebandSampleSource; +class SpectrumVis; +class ScopeVis; +class ScopeVisXY; + +namespace Ui { + class IEEE_802_15_4_ModGUI; +} + +class IEEE_802_15_4_ModGUI : public ChannelGUI { + Q_OBJECT + +public: + static IEEE_802_15_4_ModGUI* 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::IEEE_802_15_4_ModGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + IEEE_802_15_4_ModSettings m_settings; + bool m_doApplySettings; + SpectrumVis* m_spectrumVis; + ScopeVis* m_scopeVis; + int m_basebandSampleRate; + + IEEE_802_15_4_Mod* m_IEEE_802_15_4_Mod; + MovingAverageUtil m_channelPowerDbAvg; // Less than other mods, as frames are short + + MessageQueue m_inputMessageQueue; + + explicit IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent = 0); + virtual ~IEEE_802_15_4_ModGUI(); + + void checkSampleRate(); + 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_phy_currentIndexChanged(int value); + void on_rfBW_valueChanged(int index); + void on_gain_valueChanged(int value); + void on_channelMute_toggled(bool checked); + void on_txButton_clicked(bool checked); + void on_frame_editingFinished(); + void on_frame_returnPressed(); + void on_repeat_toggled(bool checked); + void repeatSelect(); + void txSettingsSelect(); + + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + + void tick(); +}; + +#endif /* INCLUDE_IEEE_802_15_4_MODGUI_H */ diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui new file mode 100644 index 000000000..6e269c2e6 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui @@ -0,0 +1,666 @@ + + + IEEE_802_15_4_ModGUI + + + + 0 + 0 + 363 + 937 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Liberation Sans + 9 + + + + Qt::StrongFocus + + + 802.15.4 Modulator + + + + true + + + + 2 + 2 + 341 + 121 + + + + + 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 + + + + + + + + + + + + 170 + 0 + + + + + 60 + 16777215 + + + + PHY bitrate and modulation + + + 20kbps BPSK + + + + 20kbps BPSK + + + + + 40kbps BPSK + + + + + 100kbps <1GHz O-QPSK + + + + + 250kbps <1GHz O-QPSK (Sine) + + + + + 250kbps <1GHz O-QPSK (RC) + + + + + 250kbps >2GHz O-QPSK + + + + + + + + Qt::Vertical + + + + + + + BW + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + RF bandwidth + + + 300 + + + 10000 + + + 100 + + + 100 + + + 300 + + + Qt::Horizontal + + + + + + + + 30 + 0 + + + + 2.0M + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + + + 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 + + + + + + + + + Qt::LeftToRight + + + Data + + + + + + + Enter data to transmit as a series of hex bytes. + + + 00 00 01 00 00 00 00 00 + + + 256 + + + + + + + Repeatedly transmit the frame. Right click for additional settings. + + + ... + + + + :/playloop.png:/playloop.png + + + + + + + Press to transmit the frame + + + TX + + + + + + + + + + + 0 + 190 + 351 + 141 + + + + Transmitted Frames + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + true + + + + + + + + + 0 + 340 + 351 + 284 + + + + Baseband Waveform + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 200 + 250 + + + + + Liberation Mono + 8 + + + + + + + + + + + + + 0 + 630 + 351 + 284 + + + + Baseband Spectrum + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 200 + 250 + + + + + Liberation Mono + 8 + + + + + + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + GLScope + QWidget +
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
+ 1 +
+ + GLSpectrum + QWidget +
gui/glspectrum.h
+ 1 +
+ + GLSpectrumGUI + QWidget +
gui/glspectrumgui.h
+ 1 +
+ + LevelMeterVU + QWidget +
gui/levelmeter.h
+ 1 +
+
+ + deltaFrequency + channelMute + phy + rfBW + gain + frame + repeat + txButton + transmittedText + + + + + +
diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.cpp new file mode 100644 index 000000000..51ce8f112 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.cpp @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "ieee_802_15_4_modgui.h" +#endif +#include "ieee_802_15_4_mod.h" +#include "ieee_802_15_4_modwebapiadapter.h" +#include "ieee_802_15_4_modplugin.h" + +const PluginDescriptor IEEE_802_15_4_ModPlugin::m_pluginDescriptor = { + IEEE_802_15_4_Mod::m_channelId, + QString("802.15.4 Modulator"), + QString("4.19.1"), + QString("(c) Jon Beniston, M7RCE"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +IEEE_802_15_4_ModPlugin::IEEE_802_15_4_ModPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& IEEE_802_15_4_ModPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void IEEE_802_15_4_ModPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + m_pluginAPI->registerTxChannel(IEEE_802_15_4_Mod::m_channelIdURI, IEEE_802_15_4_Mod::m_channelId, this); +} + +void IEEE_802_15_4_ModPlugin::createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const +{ + if (bs || cs) + { + IEEE_802_15_4_Mod *instance = new IEEE_802_15_4_Mod(deviceAPI); + + if (bs) { + *bs = instance; + } + + if (cs) { + *cs = instance; + } + } +} + +#ifdef SERVER_MODE +ChannelGUI* IEEE_802_15_4_ModPlugin::createTxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSource *txChannel) const +{ + return 0; +} +#else +ChannelGUI* IEEE_802_15_4_ModPlugin::createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) const +{ + return IEEE_802_15_4_ModGUI::create(m_pluginAPI, deviceUISet, txChannel); +} +#endif + +ChannelWebAPIAdapter* IEEE_802_15_4_ModPlugin::createChannelWebAPIAdapter() const +{ + return new IEEE_802_15_4_ModWebAPIAdapter(); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.h new file mode 100644 index 000000000..4ac8dda53 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modplugin.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MODPLUGIN_H +#define INCLUDE_IEEE_802_15_4_MODPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSource; + +class IEEE_802_15_4_ModPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.channeltx.mod802.15.4") + +public: + explicit IEEE_802_15_4_ModPlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const; + virtual ChannelGUI* createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *rxChannel) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_IEEE_802_15_4_MODPLUGIN_H diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.cpp new file mode 100644 index 000000000..5dd3afbd2 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.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 "ieee_802_15_4_modrepeatdialog.h" +#include "ieee_802_15_4_modsettings.h" +#include + +IEEE_802_15_4_ModRepeatDialog::IEEE_802_15_4_ModRepeatDialog(float repeatDelay, int repeatCount, QWidget* parent) : + QDialog(parent), + ui(new Ui::IEEE_802_15_4_ModRepeatDialog) +{ + ui->setupUi(this); + ui->repeatDelay->setValue(repeatDelay); + QLineEdit *edit = ui->repeatCount->lineEdit(); + if (edit) + { + if (repeatCount == IEEE_802_15_4_ModSettings::infinitePackets) + edit->setText("Infinite"); + else + edit->setText(QString("%1").arg(repeatCount)); + } +} + +IEEE_802_15_4_ModRepeatDialog::~IEEE_802_15_4_ModRepeatDialog() +{ + delete ui; +} + +void IEEE_802_15_4_ModRepeatDialog::accept() +{ + m_repeatDelay = ui->repeatDelay->value(); + QString text = ui->repeatCount->currentText(); + if (!text.compare(QString("Infinite"), Qt::CaseInsensitive)) + m_repeatCount = IEEE_802_15_4_ModSettings::infinitePackets; + else + m_repeatCount = text.toUInt(); + QDialog::accept(); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.h new file mode 100644 index 000000000..691e8dc07 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.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_IEEE_802_15_4_MODREPEATDIALOG_H +#define INCLUDE_IEEE_802_15_4_MODREPEATDIALOG_H + +#include "ui_IEEE_802_15_4_Modrepeatdialog.h" + +class IEEE_802_15_4_ModRepeatDialog : public QDialog { + Q_OBJECT + +public: + explicit IEEE_802_15_4_ModRepeatDialog(float repeatDelay, int repeatCount, QWidget* parent = 0); + ~IEEE_802_15_4_ModRepeatDialog(); + + float m_repeatDelay; // Delay in seconds between frames + int m_repeatCount; // Number of frames to transmit (-1 = infinite) + +private slots: + void accept(); + +private: + Ui::IEEE_802_15_4_ModRepeatDialog* ui; +}; + +#endif // INCLUDE_IEEE_802_15_4_MODREPEATDIALOG_H diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.ui new file mode 100644 index 000000000..bd347d8ad --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modrepeatdialog.ui @@ -0,0 +1,134 @@ + + + IEEE_802_15_4_ModRepeatDialog + + + + 0 + 0 + 351 + 115 + + + + + Liberation Sans + 9 + + + + Frame Repeat Settings + + + + + + + + + Delay between frames (s) + + + + + + + Frames to transmit + + + + + + + Number of frames to transmit + + + true + + + + Infinite + + + + + 10 + + + + + 100 + + + + + 1000 + + + + + + + + 3 + + + 1000000.000000000000000 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + repeatDelay + repeatCount + + + + + buttonBox + accepted() + IEEE_802_15_4_ModRepeatDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IEEE_802_15_4_ModRepeatDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp new file mode 100644 index 000000000..7914a0d16 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp @@ -0,0 +1,257 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "ieee_802_15_4_modsettings.h" +#include "ieee_802_15_4_macframe.h" + +IEEE_802_15_4_ModSettings::IEEE_802_15_4_ModSettings() +{ + resetToDefaults(); +} + +void IEEE_802_15_4_ModSettings::resetToDefaults() +{ + IEEE_802_15_4_MacFrame macFrame; + char frame[1024]; + + macFrame.toHexCharArray(frame); + + m_inputFrequencyOffset = 0; + m_modulation = BPSK; + m_bitRate = 20000; + m_subGHzBand = true; + m_rfBandwidth = 2.0f * 300000.0f; + m_gain = -1.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 = 0; + m_rampDownBits = 0; + m_rampRange = 0; + m_modulateWhileRamping = true; + m_lpfTaps = 301; + m_bbNoise = false; + m_writeToFile = false; + m_spectrumRate = m_rfBandwidth; + m_data = QString(frame); + m_rgbColor = QColor(255, 0, 0).rgb(); + m_title = "802.15.4 Modulator"; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; + m_scramble = false; + m_polynomial = 0x108; + m_pulseShaping = RC; + m_beta = 1.0f; + m_symbolSpan = 6; +} + +bool IEEE_802_15_4_ModSettings::setPHY(QString phy) +{ + int bitRate; + bool valid; + + // First part of phy string should give bitrate in kbps + bitRate = phy.split("k")[0].toInt(&valid) * 1000; + if (!valid) + return false; + + if (phy.contains("BPSK")) + { + m_bitRate = bitRate; + m_subGHzBand = true; + m_rfBandwidth = 2.0 * bitRate * 15.0; + m_spectrumRate = m_rfBandwidth; + m_modulation = IEEE_802_15_4_ModSettings::BPSK; + m_pulseShaping = RC; + m_beta = 1.0f; + m_symbolSpan = 6; + } + else if (phy.contains("O-QPSK")) + { + m_bitRate = bitRate; + m_subGHzBand = phy.contains("<1"); + m_rfBandwidth = 2.0 * (bitRate / 4.0) * (m_subGHzBand ? 16.0 : 32.0); + m_spectrumRate = m_rfBandwidth; + m_modulation = IEEE_802_15_4_ModSettings::OQPSK; + if (phy.contains("RC")) + { + m_pulseShaping = RC; + m_beta = 0.8f; + m_symbolSpan = 6; + } + else + m_pulseShaping = SINE; + } + else + return false; + return true; +} + +QString IEEE_802_15_4_ModSettings::getPHY() const +{ + return QString("%1kbps %2").arg(m_bitRate/1000).arg(m_modulation == IEEE_802_15_4_ModSettings::BPSK ? "BPSK" : "O-QPSK"); +} + +int IEEE_802_15_4_ModSettings::getChipRate() const +{ + int chipsPerSymbol, bitsPerSymbol; + + if (m_modulation == BPSK) + { + chipsPerSymbol = 15; + bitsPerSymbol = 1; + } + else + { + bitsPerSymbol = 4; + chipsPerSymbol = m_subGHzBand ? 16 : 32; + } + return m_bitRate * chipsPerSymbol / bitsPerSymbol; +} + +QByteArray IEEE_802_15_4_ModSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_inputFrequencyOffset); + s.writeS32(2, m_bitRate); + s.writeReal(3, m_rfBandwidth); + s.writeBool(4, m_subGHzBand); + 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_lpfTaps); + s.writeBool(15, m_bbNoise); + s.writeBool(16, m_writeToFile); + s.writeString(17, m_data); + + s.writeU32(18, m_rgbColor); + s.writeString(19, m_title); + if (m_channelMarker) { + s.writeBlob(20, m_channelMarker->serialize()); + } + s.writeS32(21, m_streamIndex); + s.writeBool(22, m_useReverseAPI); + s.writeString(23, m_reverseAPIAddress); + s.writeU32(24, m_reverseAPIPort); + s.writeU32(25, m_reverseAPIDeviceIndex); + s.writeU32(26, m_reverseAPIChannelIndex); + + s.writeBool(27, m_scramble); + s.writeS32(28, m_polynomial); + s.writeS32(29, m_pulseShaping); + s.writeReal(30, m_beta); + s.writeS32(31, m_symbolSpan); + s.writeS32(32, m_spectrumRate); + s.writeS32(33, m_modulation); + + return s.final(); +} + +bool IEEE_802_15_4_ModSettings::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_bitRate, 20000); + d.readReal(3, &m_rfBandwidth, 2.0f * 300000.0f); + d.readBool(4, &m_subGHzBand, m_bitRate <= 40000); + 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_lpfTaps, 301); + d.readBool(15, &m_bbNoise, false); + d.readBool(16, &m_writeToFile, false); + d.readString(17, &m_data, ""); + + d.readU32(18, &m_rgbColor); + d.readString(19, &m_title, "802.15.4 Modulator"); + + if (m_channelMarker) { + d.readBlob(20, &bytetmp); + m_channelMarker->deserialize(bytetmp); + } + + d.readS32(21, &m_streamIndex, 0); + + d.readBool(22, &m_useReverseAPI, false); + d.readString(23, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(24, &utmp, 0); + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + d.readU32(25, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(26, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + + d.readBool(27, &m_scramble, false); + d.readS32(28, &m_polynomial, 0x108); + d.readS32(29, (qint32 *)&m_pulseShaping, RC); + d.readReal(30, &m_beta, 1.0f); + d.readS32(31, &m_symbolSpan, 6); + d.readS32(32, &m_spectrumRate, m_rfBandwidth); + d.readS32(33, (qint32 *)&m_modulation, m_bitRate < 100000 ? IEEE_802_15_4_ModSettings::BPSK : IEEE_802_15_4_ModSettings::OQPSK); + + return true; + } + else + { + qDebug() << "IEEE_802_15_4_ModSettings::deserialize: ERROR"; + resetToDefaults(); + return false; + } +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h new file mode 100644 index 000000000..a41c9a789 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 INCLUDE_IEEE_802_15_4_MODSETTINGS_H +#define INCLUDE_IEEE_802_15_4_MODSETTINGS_H + +#include +#include +#include "dsp/dsptypes.h" + +class Serializable; + +struct IEEE_802_15_4_ModSettings +{ + static const int infinitePackets = -1; + + qint64 m_inputFrequencyOffset; + enum Modulation {BPSK, OQPSK} m_modulation; + int m_bitRate; + bool m_subGHzBand; + Real m_rfBandwidth; + 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_lpfTaps; + bool m_bbNoise; + bool m_writeToFile; + int m_spectrumRate; + 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; + bool m_scramble; + int m_polynomial; + enum PulseShaping {RC, SINE} m_pulseShaping; + float m_beta; + int m_symbolSpan; + + IEEE_802_15_4_ModSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + bool setPHY(QString phy); + QString getPHY() const; + int getChipRate() const; +}; + +#endif /* INCLUDE_IEEE_802_15_4_MODSETTINGS_H */ diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp new file mode 100644 index 000000000..7331f7b28 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.cpp @@ -0,0 +1,611 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "ieee_802_15_4_modsource.h" +#include "util/crc.h" + +IEEE_802_15_4_ModSource::IEEE_802_15_4_ModSource() : + m_channelSampleRate(3000000), + m_spectrumRate(0), + m_channelFrequencyOffset(0), + m_magsq(0.0), + m_levelCalcCount(0), + m_peakLevel(0.0f), + m_levelSum(0.0f), + m_bitCount(0), + m_byteIdx(0), + m_bitIdx(0), + m_state(idle), + m_sampleIdx(0), + m_chipsPerSymbol(15), + m_bitsPerSymbol(1), + m_chipRate(300000), + m_sinLUT(nullptr), + m_spectrumSink(nullptr), + m_scopeSink(nullptr), + m_scrambler(0x108, 0x1fe, 0) +{ + m_lowpass.create(301, m_channelSampleRate, 22000.0 / 2.0); + m_pulseShapeI.create(1, 6, m_channelSampleRate/300000, true); + m_pulseShapeQ.create(1, 6, m_channelSampleRate/300000, true); + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +IEEE_802_15_4_ModSource::~IEEE_802_15_4_ModSource() +{ + delete m_sinLUT; +} + +void IEEE_802_15_4_ModSource::pull(SampleVector::iterator begin, unsigned int nbSamples) +{ + std::for_each( + begin, + begin + nbSamples, + [this](Sample& s) { + pullOne(s); + } + ); +} + +void IEEE_802_15_4_ModSource::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 IEEE_802_15_4_ModSource::prefetch(unsigned int nbSamples) +{ +} + +void IEEE_802_15_4_ModSource::sampleToSpectrum(Complex sample) +{ + if (m_spectrumSink && (m_settings.m_spectrumRate > 0)) + { + Complex out; + + // Could use a simpler filter here, as currently m_spectrumRate is + // always an integer multiple of m_channelSampleRate + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, sample, &out)) + { + Real r = std::real(out) * SDR_TX_SCALEF; + Real i = std::imag(out) * SDR_TX_SCALEF; + m_sampleBuffer.push_back(Sample(r, i)); + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); + m_sampleBuffer.clear(); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } +} + +void IEEE_802_15_4_ModSource::sampleToScope(Complex sample) +{ + if (m_scopeSink) + { + Real r = std::real(sample) * SDR_RX_SCALEF; + Real i = std::imag(sample) * SDR_RX_SCALEF; + m_sampleBuffer.push_back(Sample(r, i)); + m_scopeSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true); + m_sampleBuffer.clear(); + } +} + +void IEEE_802_15_4_ModSource::modulateSample() +{ + Real linearRampGain; + Real i, q; + + if ((m_state == idle) || (m_state == wait)) + { + Real audioMod = 0.0f; + m_modSample.real(audioMod); + m_modSample.imag(0); + calculateLevel(audioMod); + sampleToSpectrum(m_modSample); + sampleToScope(m_modSample); + if (m_state == wait) + { + m_waitCounter--; + if (m_waitCounter == 0) + initTX(); + } + } + else + { + + if (m_sampleIdx == 0) + { + if (chipsValid()) + m_chips[m_chipOdd] = getChip(); + // 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_samplesPerChip); + } + } + + if (!m_settings.m_bbNoise) + { + if (m_settings.m_modulation == IEEE_802_15_4_ModSettings::BPSK) + { + // BPSK - Raised cosine pulse shaping + if ((m_sampleIdx == 1) && (m_state != ramp_down)) + i = m_pulseShapeI.filter(m_chips[0] ? 1.0f : -1.0f); + else + i = m_pulseShapeI.filter(0.0f); + q = 0.0f; + } + else + { + if (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::SINE) + { + // O-QPSK - Half-sine pulse shaping over 2 chips. Even chips on I, odd on Q. 1-chip out of phase. + i = (m_chips[0] ? 1.0f : -1.0f) * m_sinLUT[m_sampleIdx+(m_chipOdd ? m_samplesPerChip : 0)]; + q = (m_chips[1] ? 1.0f : -1.0f) * m_sinLUT[m_sampleIdx+(m_chipOdd ? 0 : m_samplesPerChip)]; + } + else + { + // O-QPSK - Raised cosine pulse shaping. Even chips on I, odd on Q. 1-chip out of phase. + if ((m_sampleIdx == 1) && (m_state != ramp_down) && !m_chipOdd) + i = m_pulseShapeI.filter(m_chips[0] ? 1.0f : -1.0f); + else + i = m_pulseShapeI.filter(0.0f); + if ((m_sampleIdx == 1) && (m_state != ramp_down) && m_chipOdd) + q = m_pulseShapeQ.filter(m_chips[1] ? 1.0f : -1.0f); + else + q = m_pulseShapeQ.filter(0.0f); + } + } + } + else + { + i = (Real)rand()/((Real)RAND_MAX)-0.5; // Noise to test filter frequency response + q = (Real)rand()/((Real)RAND_MAX)-0.5; + } + + if (m_basebandFile.is_open()) + m_basebandFile << m_chips[0] << "," << m_chips[1] << "," << m_chipOdd << "," << i << "," << q << "," << (m_sampleIdx+(m_chipOdd ? m_samplesPerChip : 0)) << "," << (m_sampleIdx+(m_chipOdd ? 0 : m_samplesPerChip)) << "\n"; + + m_sampleIdx++; + if (m_sampleIdx >= m_samplesPerChip) + { + m_sampleIdx = 0; + if (m_settings.m_modulation == IEEE_802_15_4_ModSettings::OQPSK) + m_chipOdd = !m_chipOdd; + } + + + linearRampGain = powf(10.0f, m_pow/20.0f); + + m_modSample.real(m_linearGain * linearRampGain * i); + m_modSample.imag(m_linearGain * linearRampGain * q); + + // Display baseband audio in spectrum analyser + sampleToSpectrum(m_modSample); + sampleToScope(m_modSample); + + // Apply low pass filter to limit RF BW + m_modSample = m_lowpass.filter(m_modSample); + + // Ramp up/down power at start/end of frame + 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 frame? + if (m_settings.m_repeat) + { + if (m_frameRepeatCount > 0) + m_frameRepeatCount--; + if ((m_frameRepeatCount == IEEE_802_15_4_ModSettings::infinitePackets) || (m_frameRepeatCount > 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 IEEE_802_15_4_ModSource::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 IEEE_802_15_4_ModSource::applySettings(const IEEE_802_15_4_ModSettings& settings, bool force) +{ + // Only recreate filters if settings have changed + if ((settings.m_lpfTaps != m_settings.m_lpfTaps) || (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + { + qDebug() << "IEEE_802_15_4_ModSource::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_spectrumRate != m_settings.m_spectrumRate) || force) + { + m_interpolatorDistanceRemain = 0; + m_interpolatorConsumed = false; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) settings.m_spectrumRate; + m_interpolator.create(48, settings.m_spectrumRate, settings.m_spectrumRate / 2.2, 3.0); + } + + if (settings.m_modulation == IEEE_802_15_4_ModSettings::BPSK) + { + m_chipsPerSymbol = 15; + m_bitsPerSymbol = 1; + } + else + { + m_bitsPerSymbol = 4; + m_chipsPerSymbol = settings.m_subGHzBand ? 16 : 32; + } + m_chipRate = settings.m_bitRate * m_chipsPerSymbol / m_bitsPerSymbol; + m_samplesPerChip = m_channelSampleRate / m_chipRate; + qDebug() << "m_samplesPerChip: " << m_samplesPerChip; + if (m_channelSampleRate % m_chipRate != 0) + qCritical("Sample rate is not an integer multiple of the chip rate"); + if (m_samplesPerChip <= 2) + qCritical("Sample rate is not a high enough multiple of the chip rate"); + + if ((settings.m_pulseShaping != m_settings.m_pulseShaping) || (settings.m_beta != m_settings.m_beta) || (settings.m_symbolSpan != m_settings.m_symbolSpan) + || (settings.m_bitRate != m_settings.m_bitRate) || (settings.m_modulation != m_settings.m_modulation) + || (settings.m_subGHzBand != m_settings.m_subGHzBand) + || force) + { + qDebug() << "IEEE_802_15_4_ModSource::applySettings: Recreating pulse shaping filter: " + << " pulseShaping: " << m_settings.m_pulseShaping + << " beta: " << settings.m_beta + << " symbolSpan: " << settings.m_symbolSpan + << " channelSampleRate:" << m_channelSampleRate + << " subGHzBand: " << settings.m_subGHzBand + << " bitRate:" << settings.m_bitRate + << " chipRate:" << m_chipRate; + if (settings.m_pulseShaping == IEEE_802_15_4_ModSettings::RC) + { + m_pulseShapeI.create(settings.m_beta, m_settings.m_symbolSpan, m_channelSampleRate/m_chipRate, true); + m_pulseShapeQ.create(settings.m_beta, m_settings.m_symbolSpan, m_channelSampleRate/m_chipRate, true); + } + else + createHalfSine(m_channelSampleRate, m_chipRate); + } + if ((settings.m_polynomial != m_settings.m_polynomial) || force) + m_scrambler.setPolynomial(settings.m_polynomial); + + m_settings = settings; + + // Precalculate linear gain to save doing it in the loop + m_linearGain = powf(10.0f, m_settings.m_gain/20.0f); +} + +void IEEE_802_15_4_ModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "IEEE_802_15_4_ModSource::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) + { + qDebug() << "IEEE_802_15_4_ModSource::applyChannelSettings: Recreating filters"; + m_lowpass.create(m_settings.m_lpfTaps, channelSampleRate, m_settings.m_rfBandwidth / 2.0); + qDebug() << "IEEE_802_15_4_ModSource::applyChannelSettings: Recreating pulse shaping filter: " + << " pulseShaping: " << m_settings.m_pulseShaping + << " beta: " << m_settings.m_beta + << " symbolSpan: " << m_settings.m_symbolSpan + << " channelSampleRate:" << channelSampleRate + << " subGHzBand: " << m_settings.m_subGHzBand + << " bitRate:" << m_settings.m_bitRate + << " chipRate:" << m_chipRate; + if (m_settings.m_pulseShaping == IEEE_802_15_4_ModSettings::RC) + { + m_pulseShapeI.create(m_settings.m_beta, m_settings.m_symbolSpan, channelSampleRate/m_chipRate, true); + m_pulseShapeQ.create(m_settings.m_beta, m_settings.m_symbolSpan, channelSampleRate/m_chipRate, true); + } + else + createHalfSine(channelSampleRate, m_chipRate); + } + + if ((m_channelSampleRate != channelSampleRate) || (m_spectrumRate != m_settings.m_spectrumRate) || force) + { + 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; + m_spectrumRate = m_settings.m_spectrumRate; + m_samplesPerChip = m_channelSampleRate / m_chipRate; + qDebug() << "m_samplesPerChip: " << m_samplesPerChip; +} + +// Half-sine pulse shaping for O-QPSK +void IEEE_802_15_4_ModSource::createHalfSine(int sampleRate, int chipRate) +{ + int samplesPerChip = sampleRate / chipRate; + double tc = 1.0 / chipRate; + + if (m_sinLUT) + delete m_sinLUT; + m_sinLUT = new double[2*samplesPerChip]; + for (int i = 0; i < 2*samplesPerChip; i++) + { + double t=i/(double)sampleRate; + m_sinLUT[i] = sin(M_PI*t/(2.0*tc)); + } +} + +bool IEEE_802_15_4_ModSource::chipsValid() +{ + return (m_bitCount > 0) || (m_chipIdx < m_chipsPerSymbol); +} + +// Symbol-to-chip mapping +int IEEE_802_15_4_ModSource::getChip() +{ + int chip = 0; + + if (m_chipIdx == 0) + m_symbol = getSymbol(); + + if (m_settings.m_bitRate <= 40000) + { + static const int chipsBpsk[2][15] = { + {1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1} + }; + chip = chipsBpsk[m_symbol][m_chipIdx]; + } + else if (m_settings.m_subGHzBand) + { + static const int chipsSubGHzOqpsk[16][16] = { + {0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1}, + {0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}, + {0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0}, + {1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + {0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0}, + {1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1}, + {1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1}, + {1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0}, + {0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0}, + {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1}, + {1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1}, + {1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0}, + {1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0}, + {1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1}, + }; + chip = chipsSubGHzOqpsk[m_symbol][m_chipIdx]; + } + else + { + static const int chipsOqpsk[16][32] = { + {1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0}, + {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0}, + {0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0}, + {0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1}, + {0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1}, + {1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1}, + {1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1}, + {1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1}, + {0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0}, + {0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1}, + {1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0}, + {1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0} + }; + chip = chipsOqpsk[m_symbol][m_chipIdx]; + } + + m_chipIdx++; + if (m_chipIdx >= m_chipsPerSymbol) + m_chipIdx = 0; + + return chip; +} + +int IEEE_802_15_4_ModSource::getSymbol() +{ + int symbol; + + if (m_bitCount > 0) + { + int mask = m_bitsPerSymbol == 1 ? 0x1 : 0xf; + symbol = (m_bits[m_byteIdx] >> m_bitIdx) & mask; + m_bitIdx += m_bitsPerSymbol; + m_bitCount -= m_bitsPerSymbol; + if (m_bitIdx == 8) + { + m_byteIdx++; + m_bitIdx = 0; + } + if (m_settings.m_modulation == IEEE_802_15_4_ModSettings::BPSK) + { + // Differential encoding + symbol = symbol ^ m_diffBit; + m_diffBit = symbol; + } + } + else + symbol = 0; + + return symbol; +} + +void IEEE_802_15_4_ModSource::initTX() +{ + m_sampleIdx = 0; + m_chipOdd = false; + m_chips[0] = 0; + m_chips[1] = 0; + m_chipIdx = 0; + m_diffBit = 0; + m_byteIdx = 0; + m_bitIdx = 0; + m_bitCount = m_bitCountTotal; // Reset to allow retransmission + m_symbol = 0; + 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_samplesPerChip); + } + m_scrambler.init(); +} + +uint8_t *IEEE_802_15_4_ModSource::hexToBin(uint8_t *p, QString data) +{ + // Convert string containing space separated list of hex values to binary + QStringList list = data.split(" "); + for (int i = 0; i < list.size(); i++) + { + *p++ = list[i].toInt(nullptr, 16); + } + return p; +} + +void IEEE_802_15_4_ModSource::addTXFrame(QString data) +{ + uint8_t *crcStart; + uint8_t *p; + uint8_t *pLength; + crc16itut crc; + uint16_t crcValue; + int len; + + // Create PHY frame + p = m_bits; + // Preamble + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x00; + // SFD - start of frame delimiter + *p++ = 0xa7; + // PHR - length + pLength = p; + *p++ = 0; + // PHY payload + crcStart = p; + // Data + p = hexToBin(p, data); + // MAC FCS + crc.calculate(crcStart, p-crcStart); + crcValue = crc.get(); + *p++ = crcValue & 0xff; + *p++ = (crcValue >> 8); + // Update length + *pLength = p - pLength - 1; + // Extra 0 to account for pulse shaping filter delay. + // Should probably just be a few chips + *p++ = 0x00; + + // Dump frame + QByteArray qb((char *)m_bits, p-m_bits); + qDebug() << "TX: " << qb.toHex(); + + // Save number of bits in frame + m_bitCount = m_bitCountTotal = (p-&m_bits[0]) * 8; + + m_frameRepeatCount = m_settings.m_repeatCount; + initTX(); + + if (m_settings.m_writeToFile) + m_basebandFile.open("IEEE_802_15_4_Mod.csv", std::ofstream::out); + else if (m_basebandFile.is_open()) + m_basebandFile.close(); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.h new file mode 100644 index 000000000..6d81b2a82 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsource.h @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MODSOURCE_H +#define INCLUDE_IEEE_802_15_4_MODSOURCE_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/highpass.h" +#include "dsp/raisedcosine.h" +#include "dsp/fmpreemphasis.h" +#include "util/lfsr.h" +#include "util/movingaverage.h" + +#include "ieee_802_15_4_modsettings.h" + +class BasebandSampleSink; + +class IEEE_802_15_4_ModSource : public ChannelSampleSource +{ +public: + IEEE_802_15_4_ModSource(); + virtual ~IEEE_802_15_4_ModSource(); + + 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 setScopeSink(BasebandSampleSink* scopeSink) { m_scopeSink = scopeSink; } + void applySettings(const IEEE_802_15_4_ModSettings& settings, bool force = false); + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + + uint8_t *hexToBin(uint8_t *p, QString data); + void addTXFrame(QString data); + +private: + int m_channelSampleRate; + int m_channelFrequencyOffset; + int m_spectrumRate; + IEEE_802_15_4_ModSettings m_settings; + + NCO m_carrierNco; + Real m_linearGain; + Complex m_modSample; + double *m_sinLUT; + + int m_chips[2]; // Chips. Odd/even for O-QPSK + bool m_chipOdd; + int m_diffBit; // Output of differential coder + RaisedCosine m_pulseShapeI; // Pulse shaping filters + RaisedCosine m_pulseShapeQ; + Lowpass m_lowpass; // Low pass filter to limit RF bandwidth + LFSR m_scrambler; // Scrambler + + BasebandSampleSink* m_spectrumSink; // Spectrum GUI to display baseband waveform + BasebandSampleSink* m_scopeSink; // Scope 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_sampleIdx; // Sample index in to chip + int m_samplesPerChip; // Number of samples per chip + int m_chipsPerSymbol; // Number of chips per symbol + int m_bitsPerSymbol; // Number of bits per symbol + int m_chipRate; + + int m_symbol; + int m_chipIdx; + + Real m_pow; // In dB + Real m_powRamp; // In dB + enum IEEE_802_15_4_ModState { + idle, ramp_up, tx, ramp_down, wait + } m_state; // States for sample modulation + int m_frameRepeatCount; + uint64_t m_waitCounter; // Samples to wait before retransmission + + uint8_t m_bits[4+1+1+127]; // Bits to transmit (preamble, SFD, length, payload) + int m_byteIdx; // Index in to m_bits + int m_bitIdx; // Index in to current byte of m_bits + int m_bitCount; // Count of number of valid bits in m_bits + int m_bitCountTotal; + + std::ofstream m_basebandFile; // For debug output of baseband waveform + + bool chipsValid(); // Are there any chips to transmit + int getSymbol(); + int getChip(); + void initTX(); + void createHalfSine(int sampleRate, int chipRate); + + void calculateLevel(Real& sample); + void modulateSample(); + void sampleToSpectrum(Complex sample); + void sampleToScope(Complex sample); + +}; + +#endif // INCLUDE_IEEE_802_15_4_MODSOURCE_H diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.cpp new file mode 100644 index 000000000..67b8af0fb --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "ieee_802_15_4_modtxsettingsdialog.h" + +IEEE_802_15_4_ModTXSettingsDialog::IEEE_802_15_4_ModTXSettingsDialog(int rampUpBits, int rampDownBits, + int rampRange, bool modulateWhileRamping, int modulation, int bitRate, + int pulseShaping, float beta, int symbolSpan, + bool scramble, int polynomial, + int lpfTaps, bool bbNoise, bool writeToFile, + QWidget* parent) : + QDialog(parent), + ui(new Ui::IEEE_802_15_4_ModTXSettingsDialog) +{ + ui->setupUi(this); + ui->rampUp->setValue(rampUpBits); + ui->rampDown->setValue(rampDownBits); + ui->rampRange->setValue(rampRange); + ui->modulateWhileRamping->setChecked(modulateWhileRamping); + ui->modulation->setCurrentIndex(modulation); + ui->bitRate->setValue(bitRate); + ui->pulseShaping->setCurrentIndex(pulseShaping); + ui->beta->setValue(beta); + ui->symbolSpan->setValue(symbolSpan); + ui->scramble->setChecked(scramble); + ui->polynomial->setValue(polynomial); + ui->lpfTaps->setValue(lpfTaps); + ui->bbNoise->setChecked(bbNoise); + ui->writeToFile->setChecked(writeToFile); +} + +IEEE_802_15_4_ModTXSettingsDialog::~IEEE_802_15_4_ModTXSettingsDialog() +{ + delete ui; +} + +void IEEE_802_15_4_ModTXSettingsDialog::accept() +{ + m_rampUpBits = ui->rampUp->value(); + m_rampDownBits = ui->rampDown->value(); + m_rampRange = ui->rampRange->value(); + m_modulateWhileRamping = ui->modulateWhileRamping->isChecked(); + m_modulation = ui->modulation->currentIndex(); + m_bitRate = ui->bitRate->value(); + m_pulseShaping = ui->pulseShaping->currentIndex(); + m_beta = ui->beta->value(); + m_symbolSpan = ui->symbolSpan->value(); + m_scramble = ui->scramble->isChecked(); + m_polynomial = ui->polynomial->value(); + m_lpfTaps = ui->lpfTaps->value(); + m_bbNoise = ui->bbNoise->isChecked(); + m_writeToFile = ui->writeToFile->isChecked(); + + QDialog::accept(); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.h new file mode 100644 index 000000000..9b4745bd2 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_IEEE_802_15_4_MODTXSETTINGSDIALOG_H +#define INCLUDE_IEEE_802_15_4_MODTXSETTINGSDIALOG_H + +#include "ui_IEEE_802_15_4_Modtxsettingsdialog.h" + +class IEEE_802_15_4_ModTXSettingsDialog : public QDialog { + Q_OBJECT + +public: + explicit IEEE_802_15_4_ModTXSettingsDialog(int rampUpBits, int rampDownBits, int rampRange, + bool modulateWhileRamping, int modulation, int bitRate, + int pulseShaping, float beta, int symbolSpan, + bool scramble, int polynomial, + int lpfTaps, bool bbNoise, bool writeToFile, + QWidget* parent = 0); + ~IEEE_802_15_4_ModTXSettingsDialog(); + + int m_rampUpBits; + int m_rampDownBits; + int m_rampRange; + bool m_modulateWhileRamping; + int m_modulation; + int m_bitRate; + int m_pulseShaping; + float m_beta; + int m_symbolSpan; + bool m_scramble; + int m_polynomial; + int m_lpfTaps; + bool m_bbNoise; + bool m_writeToFile; + +private slots: + void accept(); + +private: + Ui::IEEE_802_15_4_ModTXSettingsDialog* ui; +}; + +#endif // INCLUDE_IEEE_802_15_4_MODTXSETTINGSDIALOG_H diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.ui new file mode 100644 index 000000000..1edae7355 --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modtxsettingsdialog.ui @@ -0,0 +1,370 @@ + + + IEEE_802_15_4_ModTXSettingsDialog + + + + 0 + 0 + 351 + 592 + + + + + Liberation Sans + 9 + + + + 802.15.4 Extra Settings + + + + + + Modulation + + + + + + Bitrate + + + + + + + Bitrate (bits per second). + + + 500000 + + + 40000 + + + + + + + Modulaton type. + + + + BPSK + + + + + O-QPSK + + + + + + + + Modulation + + + + + + + RF BW limit LPF taps + + + + + + + Number of taps in LPF for RF BW filter. + + + 10000 + + + + + + + + + + Pulse shaping + + + + + + Pulse shaping + + + + + + + + Raised Cosine + + + + + Half sine + + + + + + + + Filter rolloff (beta) + + + + + + + Roll-off of the filter + + + 1.000000000000000 + + + 0.250000000000000 + + + + + + + Filter symbol span + + + + + + + Number of symbols over which filter is applied + + + 1 + + + 20 + + + + + + + + + + Scrambing + + + + + + Enabling scrambling. + + + Scramble + + + + + + + Polynomial + + + + + + + Polynomial of the scrambler. The +1 is implicit. + + + 0x + + + 2147483647 + + + 67584 + + + 16 + + + + + + + + + + Power Ramping + + + + + + 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 + + + + + + + + + + Debug + + + + + + Generate white noise as baseband signal. + + + Generate BB noise + + + + + + + + 0 + 0 + + + + Write baseband signal to a CSV file named IEEE_802_15_4_Mod.csv + + + Write baseband to CSV + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + modulation + bitRate + lpfTaps + pulseShaping + beta + symbolSpan + scramble + polynomial + rampUp + rampDown + rampRange + modulateWhileRamping + bbNoise + writeToFile + + + + + buttonBox + accepted() + IEEE_802_15_4_ModTXSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IEEE_802_15_4_ModTXSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.cpp new file mode 100644 index 000000000..a377186bd --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.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 "ieee_802_15_4_mod.h" +#include "ieee_802_15_4_modwebapiadapter.h" + +IEEE_802_15_4_ModWebAPIAdapter::IEEE_802_15_4_ModWebAPIAdapter() +{} + +IEEE_802_15_4_ModWebAPIAdapter::~IEEE_802_15_4_ModWebAPIAdapter() +{} + +int IEEE_802_15_4_ModWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings()); + response.getIeee802154ModSettings()->init(); + IEEE_802_15_4_Mod::webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int IEEE_802_15_4_ModWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + IEEE_802_15_4_Mod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + + IEEE_802_15_4_Mod::webapiFormatChannelSettings(response, m_settings); + return 200; +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.h new file mode 100644 index 000000000..58ab9cc7d --- /dev/null +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modwebapiadapter.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_IEEE_802_15_4_MODWEBAPIADAPTER_H +#define INCLUDE_IEEE_802_15_4_MODWEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "ieee_802_15_4_modsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class IEEE_802_15_4_ModWebAPIAdapter : public ChannelWebAPIAdapter { +public: + IEEE_802_15_4_ModWebAPIAdapter(); + virtual ~IEEE_802_15_4_ModWebAPIAdapter(); + + 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: + IEEE_802_15_4_ModSettings m_settings; +}; + +#endif // INCLUDE_IEEE_802_15_4_MODWEBAPIADAPTER_H diff --git a/plugins/channeltx/mod802.15.4/readme.md b/plugins/channeltx/mod802.15.4/readme.md new file mode 100644 index 000000000..75448cbca --- /dev/null +++ b/plugins/channeltx/mod802.15.4/readme.md @@ -0,0 +1,84 @@ +

IEEE 802.15.4 modulator plugin

+ +

Introduction

+ +This plugin can be used to transmit IEEE 802.15.4 frames. The 802.15.4 PHY & RF layers are used by networking layers such as ZigBee, 6LoWPAN and others. + +

Interface

+ +![802.15.4 Modulator plugin GUI](../../../doc/img/IEEE_802_15_4_Mod_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: PHY

+ +This specifies the parameters of the PHY (physical layer), including the bit rate and modulation that is used for the frame transmission. Supported PHYs are 20kbps BPSK, 40kbps BPSK, 100kpbs <1GHZ O-QPSK, 250kpbs <1GHz O-QPSK and 250kbps 2.4GHz O-QPSK. +These 802.15.4 PHYs use DSSS (Direct sequence spread spectrum), whereby multiple chips are transmitted per symbol. This means that a high sample rate (>3MSa/s) and large RF bandwidth is required. The baseband sample rate should be set to an integer multiple of the chip rate. +Each PHY is applicable only for specific frequency bands as detailed below: + +Channel Page | Channels | Frequencies (MHz) | Bit rate (kbps) | Chip rate (kcps) | Modulation | Shaping | Min sample rate (MSa/s) +-------------|----------|--------------------|-----------------|------------------|------------|---------|--------------- +0 | 0 | 868.3 | 20 | 300 | BPSK | RC r=1 | 1.2 +0 | 1-10 | 906, 908... 926 | 40 | 600 | BPSK | RC r=1 | 2.4 +0 | 11-26 | 2405, 2410... 2480 | 250 | 2000 | O-QPSK | Sine | +2 | 0 | 868.3 | 100 | 400 | O-QPSK | Sine/RC | 4 +2 | 1-10 | 906, 908... 926 | 250 | 1000 | O-QPSK | Sine | 6 +5 | 0-3 | 780, 782.. 786 | 250 | 1000 | O-QPSK | RC r=.8 | 6 +11 | 0-6 | 2363, 2368... 2393 | 250 | 2000 | O-QPSK | Sine | +11 | 7-13 | 2367, 2372... 2397 | 250 | 2000 | O-QPSK | Sine | +11 | 14 | 2395 | 250 | 2000 | O-QPSK | Sine | + +

5: RF Bandwidth

+ +This specifies the bandwidth of a LPF that is applied to the output signal to limit the RF bandwidth. + +PHY | Occupied BW (99%) | Mesaured Occupied BW +----------------------------|----------------------------------------- +20kbps BPSK | 400kHz | 440kHz +40kbps BPSK | 760kHz | 940kHz +100kbps O-QPSK | 330kHz | 500kHz +250kbps <1GHz O-QPSK (Sine) | 1.200MHz | 1.8MHz +250kbps <1GHz O-QPSK (RC) | 1.200MHz | 1.4MHz +250kbps >2GHz O-QPSK | | 3.2MHz + +

6: Gain

+ +Adjusts the gain in dB from -60 to 0dB. The gain should be set to ensure the level meter remains below 100%. If the baseband signal clips, this can lead to out-of-band spurious. + +

7: Level meter in %

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

8: Data

+ +The frame of data to send as hex bytes. For example, to send a MAC ACK frame, enter "02 00 00". The MAC CRC should not be included, as this is calculated and appended automatically. + +

9: Repeat

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

10: TX

+ +Transmits a frame containing the payload set in the data field. + +

API

+ +Full details of the API can be found in the Swagger documentation. Here is a quick example of how to transmit a frame from the command line: + + curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/actions" -d '{"channelType": "IEEE_802_15_4_Mod", "direction": 1, "IEEE_802_15_4_ModActions": { "tx": { "data": "02 00 00" }}}' + +Or to set the PHY to 20kbps BPSK: + + curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/settings" -d '{"channelType": "IEEE_802_15_4_Mod", "direction": 1, "IEEE_802_15_4_ModSettings": {"phy": "20kbps BPSK"}}' diff --git a/sdrbase/dsp/raisedcosine.h b/sdrbase/dsp/raisedcosine.h index 0816a912f..98a82bee2 100644 --- a/sdrbase/dsp/raisedcosine.h +++ b/sdrbase/dsp/raisedcosine.h @@ -34,10 +34,13 @@ public: // beta - roll-off factor // symbolSpan - number of symbols over which the filter is spread // samplesPerSymbol - number of samples per symbol - void create(double beta, int symbolSpan, int samplesPerSymbol) + // normaliseUpsampledAmplitude - when true, scale the filter such that an upsampled + // (by samplesPerSymbol) bipolar sequence (E.g. [1 0 0 -1 0 0..]) has maximum + // output values close to (1,-1) + void create(double beta, int symbolSpan, int samplesPerSymbol, bool normaliseUpsampledAmplitude = false) { int nTaps = symbolSpan * samplesPerSymbol + 1; - int i; + int i, j; // check constraints if(!(nTaps & 1)) { @@ -72,13 +75,36 @@ public: } // normalize - double sum = 0; - for(i = 0; i < (int)m_taps.size() - 1; i++) - sum += std::pow(m_taps[i], 2.0) * 2; - sum += std::pow(m_taps[i], 2.0); - sum = std::sqrt(sum); - for(i = 0; i < (int)m_taps.size(); i++) - m_taps[i] /= sum; + if (!normaliseUpsampledAmplitude) + { + // normalize energy + double sum = 0; + for(i = 0; i < (int)m_taps.size() - 1; i++) + sum += std::pow(m_taps[i], 2.0) * 2; + sum += std::pow(m_taps[i], 2.0); + sum = std::sqrt(sum); + for(i = 0; i < (int)m_taps.size(); i++) + m_taps[i] /= sum; + } + else + { + // Calculate maximum output of filter, assuming upsampled bipolar input E.g. [1 0 0 -1 0 0..] + // This doesn't necessarily include the centre tap, so we try each offset + double maxGain = 0.0; + for (i = 0; i < samplesPerSymbol; i++) + { + double g = 0.0; + for (j = 0; j < (int)m_taps.size() - 1; j += samplesPerSymbol) + g += std::fabs(2.0 * m_taps[j]); + if ((i & 1) == 0) + g += std::fabs(m_taps[j]); + if (g > maxGain) + maxGain = g; + } + // Scale up so maximum out is 1 + for(i = 0; i < (int)m_taps.size(); i++) + m_taps[i] /= maxGain; + } } Type filter(Type sample) diff --git a/sdrbase/util/crc.h b/sdrbase/util/crc.h index 1c4899403..3fcd44559 100644 --- a/sdrbase/util/crc.h +++ b/sdrbase/util/crc.h @@ -87,6 +87,12 @@ public: crc16ccitt() : crc(16, 0x1021, true, 0xffff, 0) {} }; +class SDRBASE_API crc16itut : public crc +{ +public: + crc16itut() : crc(16, 0x1021, false, 0x0000, 0) {} +}; + class SDRBASE_API crc16x25 : public crc { public: diff --git a/sdrbase/util/lfsr.h b/sdrbase/util/lfsr.h index b487fbbac..0f21c2cde 100644 --- a/sdrbase/util/lfsr.h +++ b/sdrbase/util/lfsr.h @@ -102,4 +102,12 @@ public: RandomizeCCSDS() : LFSR(0x95, 0xff, 7) {} }; +// 802.15.4 GFSK PHY +// Call randomize() +class SDRBASE_API Randomize_802_15_4_PN9 : public LFSR +{ +public: + Randomize_802_15_4_PN9() : LFSR(0x108, 0x1fe, 0) {} +}; + #endif diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index b025d68c5..376da9135 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -80,6 +80,7 @@ const QMap WebAPIRequestMapper::m_channelURIToSettingsKey = { {"sdrangel.channel.localsink", "LocalSinkSettings"}, // remap {"sdrangel.channel.localsource", "LocalSourceSettings"}, {"sdrangel.channeltx.modpacket", "PacketModSettings"}, + {"sdrangel.channeltx.mod802.15.4", "IEEE_802_15_4_ModSettings"}, {"sdrangel.demod.remotesink", "RemoteSinkSettings"}, {"sdrangel.channeltx.remotesource", "RemoteSourceSettings"}, {"sdrangel.channeltx.modssb", "SSBModSettings"}, @@ -148,6 +149,7 @@ const QMap WebAPIRequestMapper::m_channelTypeToSettingsKey = { {"FreeDVDemod", "FreeDVDemodSettings"}, {"FreeDVMod", "FreeDVModSettings"}, {"FreqTracker", "FreqTrackerSettings"}, + {"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModSettings"}, {"NFMDemod", "NFMDemodSettings"}, {"NFMMod", "NFMModSettings"}, {"PacketMod", "PacketModSettings"}, @@ -166,6 +168,7 @@ const QMap WebAPIRequestMapper::m_channelTypeToSettingsKey = { const QMap WebAPIRequestMapper::m_channelTypeToActionsKey = { {"FileSink", "FileSinkActions"}, {"FileSource", "FileSourceActions"}, + {"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModActions"}, {"PacketMod", "PacketModActions"} }; @@ -3776,6 +3779,11 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setFreqTrackerSettings(new SWGSDRangel::SWGFreqTrackerSettings()); channelSettings->getFreqTrackerSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "IEEE_802_15_4_ModSettings") + { + channelSettings->setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings()); + channelSettings->getIeee802154ModSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "NFMDemodSettings") { channelSettings->setNfmDemodSettings(new SWGSDRangel::SWGNFMDemodSettings()); @@ -3878,6 +3886,11 @@ bool WebAPIRequestMapper::getChannelActions( channelActions->setFileSourceActions(new SWGSDRangel::SWGFileSourceActions()); channelActions->getFileSourceActions()->fromJsonObject(actionsJsonObject); } + else if (channelActionsKey == "IEEE_802_15_4_ModActions") + { + channelActions->setIeee802154ModActions(new SWGSDRangel::SWGIEEE_802_15_4_ModActions()); + channelActions->getIeee802154ModActions()->fromJsonObject(actionsJsonObject); + } else if (channelActionsKey == "PacketModActions") { channelActions->setPacketModActions(new SWGSDRangel::SWGPacketModActions()); @@ -4320,6 +4333,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setAtvModSettings(nullptr); channelSettings.setBfmDemodSettings(nullptr); channelSettings.setDsdDemodSettings(nullptr); + channelSettings.setIeee802154ModSettings(nullptr); channelSettings.setNfmDemodSettings(nullptr); channelSettings.setNfmModSettings(nullptr); channelSettings.setPacketModSettings(nullptr); @@ -4344,6 +4358,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setDsdDemodReport(nullptr); channelReport.setNfmDemodReport(nullptr); channelReport.setNfmModReport(nullptr); + channelReport.setIeee802154ModReport(nullptr); channelReport.setPacketModReport(nullptr); channelReport.setRemoteSourceReport(nullptr); channelReport.setSsbDemodReport(nullptr); @@ -4359,6 +4374,7 @@ void WebAPIRequestMapper::resetChannelActions(SWGSDRangel::SWGChannelActions& ch channelActions.cleanup(); channelActions.setChannelType(nullptr); channelActions.setFileSourceActions(nullptr); + channelActions.setIeee802154ModActions(nullptr); channelActions.setPacketModActions(nullptr); } diff --git a/swagger/sdrangel/api/swagger/include/ChannelActions.yaml b/swagger/sdrangel/api/swagger/include/ChannelActions.yaml index 1853f43ef..7be1394d5 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelActions.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelActions.yaml @@ -21,5 +21,7 @@ ChannelActions: $ref: "http://swgserver:8081/api/swagger/include/FileSink.yaml#/FileSinkActions" FileSourceActions: $ref: "http://swgserver:8081/api/swagger/include/FileSource.yaml#/FileSourceActions" + IEEE_802_15_4_ModActions: + $ref: "http://swgserver:8081/api/swagger/include/IEEE_802_15_4_Mod.yaml#/IEEE_802_15_4_ModActions" PacketModActions: $ref: "http://swgserver:8081/api/swagger/include/PacketMod.yaml#/PacketModActions" diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index dff63ca63..21e76944b 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -21,6 +21,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/BFMDemod.yaml#/BFMDemodReport" DSDDemodReport: $ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodReport" + IEEE_802_15_4_ModReport: + $ref: "http://swgserver:8081/api/swagger/include/IEEE_802_15_4_Mod.yaml#/IEEE_802_15_4_ModReport" FileSinkReport: $ref: "http://swgserver:8081/api/swagger/include/FileSink.yaml#/FileSinkReport" FileSourceReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 235f6835b..e92e02d8f 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -43,6 +43,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/FreeDVMod.yaml#/FreeDVModSettings" FreqTrackerSettings: $ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerSettings" + IEEE_802_15_4_ModSettings: + $ref: "http://swgserver:8081/api/swagger/include/IEEE_802_15_4_Mod.yaml#/IEEE_802_15_4_ModSettings" NFMDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodSettings" NFMModSettings: diff --git a/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml b/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml new file mode 100644 index 000000000..d66fd9983 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/IEEE_802_15_4_Mod.yaml @@ -0,0 +1,67 @@ +IEEE_802_15_4_ModSettings: + description: IEEE_802_15_4_Mod + properties: + inputFrequencyOffset: + type: integer + format: int64 + phy: + description: Physical layer. "20kbps BPSK", "40kbps BPSK", "100kbps <1GHz O-QPSK", "250kbps <1GHz O-QPSK (Sine)", "250kbps <1GHz O-QPSK (RC)", or "250kbps >2GHz O-QPSK". + type: string + rfBandwidth: + type: number + format: float + gain: + type: number + format: float + channelMute: + type: integer + repeat: + description: Whether to repeated transmit the frame (1 for yes, 0 for no). + type: integer + repeatDelay: + description: Delay between repeated transmissions. + type: number + format: float + repeatCount: + description: Number of times to repeat the frame (-1 for infinite). + type: integer + 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 + +IEEE_802_15_4_ModReport: + description: IEEE_802_15_4_Mod + properties: + channelPowerDB: + description: power transmitted in channel (dB) + type: number + format: float + channelSampleRate: + type: integer + +IEEE_802_15_4_ModActions: + description: IEEE_802_15_4_Mod + properties: + tx: + type: object + properties: + data: + description: Hex coded bytes of data to transmit, excluding CRC (E.g for an ACK - 02 00 00). + type: string + description: > + Transmit a frame diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelActions.cpp index 88e69fd60..bcc8b5055 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; + ieee_802_15_4_mod_actions = nullptr; + m_ieee_802_15_4_mod_actions_isSet = false; packet_mod_actions = nullptr; m_packet_mod_actions_isSet = false; } @@ -62,6 +64,8 @@ SWGChannelActions::init() { m_file_sink_actions_isSet = false; file_source_actions = new SWGFileSourceActions(); m_file_source_actions_isSet = false; + ieee_802_15_4_mod_actions = new SWGIEEE_802_15_4_ModActions(); + m_ieee_802_15_4_mod_actions_isSet = false; packet_mod_actions = new SWGPacketModActions(); m_packet_mod_actions_isSet = false; } @@ -80,6 +84,9 @@ SWGChannelActions::cleanup() { if(file_source_actions != nullptr) { delete file_source_actions; } + if(ieee_802_15_4_mod_actions != nullptr) { + delete ieee_802_15_4_mod_actions; + } if(packet_mod_actions != nullptr) { delete packet_mod_actions; } @@ -108,6 +115,8 @@ SWGChannelActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&file_source_actions, pJson["FileSourceActions"], "SWGFileSourceActions", "SWGFileSourceActions"); + ::SWGSDRangel::setValue(&ieee_802_15_4_mod_actions, pJson["IEEE_802_15_4_ModActions"], "SWGIEEE_802_15_4_ModActions", "SWGIEEE_802_15_4_ModActions"); + ::SWGSDRangel::setValue(&packet_mod_actions, pJson["PacketModActions"], "SWGPacketModActions", "SWGPacketModActions"); } @@ -144,6 +153,9 @@ SWGChannelActions::asJsonObject() { if((file_source_actions != nullptr) && (file_source_actions->isSet())){ toJsonValue(QString("FileSourceActions"), file_source_actions, obj, QString("SWGFileSourceActions")); } + if((ieee_802_15_4_mod_actions != nullptr) && (ieee_802_15_4_mod_actions->isSet())){ + toJsonValue(QString("IEEE_802_15_4_ModActions"), ieee_802_15_4_mod_actions, obj, QString("SWGIEEE_802_15_4_ModActions")); + } if((packet_mod_actions != nullptr) && (packet_mod_actions->isSet())){ toJsonValue(QString("PacketModActions"), packet_mod_actions, obj, QString("SWGPacketModActions")); } @@ -211,6 +223,16 @@ SWGChannelActions::setFileSourceActions(SWGFileSourceActions* file_source_action this->m_file_source_actions_isSet = true; } +SWGIEEE_802_15_4_ModActions* +SWGChannelActions::getIeee802154ModActions() { + return ieee_802_15_4_mod_actions; +} +void +SWGChannelActions::setIeee802154ModActions(SWGIEEE_802_15_4_ModActions* ieee_802_15_4_mod_actions) { + this->ieee_802_15_4_mod_actions = ieee_802_15_4_mod_actions; + this->m_ieee_802_15_4_mod_actions_isSet = true; +} + SWGPacketModActions* SWGChannelActions::getPacketModActions() { return packet_mod_actions; @@ -244,6 +266,9 @@ SWGChannelActions::isSet(){ if(file_source_actions && file_source_actions->isSet()){ isObjectUpdated = true; break; } + if(ieee_802_15_4_mod_actions && ieee_802_15_4_mod_actions->isSet()){ + isObjectUpdated = true; break; + } if(packet_mod_actions && packet_mod_actions->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelActions.h b/swagger/sdrangel/code/qt5/client/SWGChannelActions.h index b7ec0cea8..888b6ac58 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 "SWGIEEE_802_15_4_ModActions.h" #include "SWGPacketModActions.h" #include @@ -63,6 +64,9 @@ public: SWGFileSourceActions* getFileSourceActions(); void setFileSourceActions(SWGFileSourceActions* file_source_actions); + SWGIEEE_802_15_4_ModActions* getIeee802154ModActions(); + void setIeee802154ModActions(SWGIEEE_802_15_4_ModActions* ieee_802_15_4_mod_actions); + SWGPacketModActions* getPacketModActions(); void setPacketModActions(SWGPacketModActions* packet_mod_actions); @@ -88,6 +92,9 @@ private: SWGFileSourceActions* file_source_actions; bool m_file_source_actions_isSet; + SWGIEEE_802_15_4_ModActions* ieee_802_15_4_mod_actions; + bool m_ieee_802_15_4_mod_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 c1caf8a89..0afdca202 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -42,6 +42,8 @@ SWGChannelReport::SWGChannelReport() { m_bfm_demod_report_isSet = false; dsd_demod_report = nullptr; m_dsd_demod_report_isSet = false; + ieee_802_15_4_mod_report = nullptr; + m_ieee_802_15_4_mod_report_isSet = false; file_sink_report = nullptr; m_file_sink_report_isSet = false; file_source_report = nullptr; @@ -94,6 +96,8 @@ SWGChannelReport::init() { m_bfm_demod_report_isSet = false; dsd_demod_report = new SWGDSDDemodReport(); m_dsd_demod_report_isSet = false; + ieee_802_15_4_mod_report = new SWGIEEE_802_15_4_ModReport(); + m_ieee_802_15_4_mod_report_isSet = false; file_sink_report = new SWGFileSinkReport(); m_file_sink_report_isSet = false; file_source_report = new SWGFileSourceReport(); @@ -147,6 +151,9 @@ SWGChannelReport::cleanup() { if(dsd_demod_report != nullptr) { delete dsd_demod_report; } + if(ieee_802_15_4_mod_report != nullptr) { + delete ieee_802_15_4_mod_report; + } if(file_sink_report != nullptr) { delete file_sink_report; } @@ -219,6 +226,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&dsd_demod_report, pJson["DSDDemodReport"], "SWGDSDDemodReport", "SWGDSDDemodReport"); + ::SWGSDRangel::setValue(&ieee_802_15_4_mod_report, pJson["IEEE_802_15_4_ModReport"], "SWGIEEE_802_15_4_ModReport", "SWGIEEE_802_15_4_ModReport"); + ::SWGSDRangel::setValue(&file_sink_report, pJson["FileSinkReport"], "SWGFileSinkReport", "SWGFileSinkReport"); ::SWGSDRangel::setValue(&file_source_report, pJson["FileSourceReport"], "SWGFileSourceReport", "SWGFileSourceReport"); @@ -286,6 +295,9 @@ SWGChannelReport::asJsonObject() { if((dsd_demod_report != nullptr) && (dsd_demod_report->isSet())){ toJsonValue(QString("DSDDemodReport"), dsd_demod_report, obj, QString("SWGDSDDemodReport")); } + if((ieee_802_15_4_mod_report != nullptr) && (ieee_802_15_4_mod_report->isSet())){ + toJsonValue(QString("IEEE_802_15_4_ModReport"), ieee_802_15_4_mod_report, obj, QString("SWGIEEE_802_15_4_ModReport")); + } if((file_sink_report != nullptr) && (file_sink_report->isSet())){ toJsonValue(QString("FileSinkReport"), file_sink_report, obj, QString("SWGFileSinkReport")); } @@ -405,6 +417,16 @@ SWGChannelReport::setDsdDemodReport(SWGDSDDemodReport* dsd_demod_report) { this->m_dsd_demod_report_isSet = true; } +SWGIEEE_802_15_4_ModReport* +SWGChannelReport::getIeee802154ModReport() { + return ieee_802_15_4_mod_report; +} +void +SWGChannelReport::setIeee802154ModReport(SWGIEEE_802_15_4_ModReport* ieee_802_15_4_mod_report) { + this->ieee_802_15_4_mod_report = ieee_802_15_4_mod_report; + this->m_ieee_802_15_4_mod_report_isSet = true; +} + SWGFileSinkReport* SWGChannelReport::getFileSinkReport() { return file_sink_report; @@ -581,6 +603,9 @@ SWGChannelReport::isSet(){ if(dsd_demod_report && dsd_demod_report->isSet()){ isObjectUpdated = true; break; } + if(ieee_802_15_4_mod_report && ieee_802_15_4_mod_report->isSet()){ + isObjectUpdated = true; break; + } if(file_sink_report && file_sink_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index f2dbb0c52..46ad8c44e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -32,6 +32,7 @@ #include "SWGFreeDVDemodReport.h" #include "SWGFreeDVModReport.h" #include "SWGFreqTrackerReport.h" +#include "SWGIEEE_802_15_4_ModReport.h" #include "SWGNFMDemodReport.h" #include "SWGNFMModReport.h" #include "SWGPacketModReport.h" @@ -83,6 +84,9 @@ public: SWGDSDDemodReport* getDsdDemodReport(); void setDsdDemodReport(SWGDSDDemodReport* dsd_demod_report); + SWGIEEE_802_15_4_ModReport* getIeee802154ModReport(); + void setIeee802154ModReport(SWGIEEE_802_15_4_ModReport* ieee_802_15_4_mod_report); + SWGFileSinkReport* getFileSinkReport(); void setFileSinkReport(SWGFileSinkReport* file_sink_report); @@ -153,6 +157,9 @@ private: SWGDSDDemodReport* dsd_demod_report; bool m_dsd_demod_report_isSet; + SWGIEEE_802_15_4_ModReport* ieee_802_15_4_mod_report; + bool m_ieee_802_15_4_mod_report_isSet; + SWGFileSinkReport* file_sink_report; bool m_file_sink_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index 0b0e32a83..e0519f71c 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -62,6 +62,8 @@ SWGChannelSettings::SWGChannelSettings() { m_free_dv_mod_settings_isSet = false; freq_tracker_settings = nullptr; m_freq_tracker_settings_isSet = false; + ieee_802_15_4_mod_settings = nullptr; + m_ieee_802_15_4_mod_settings_isSet = false; nfm_demod_settings = nullptr; m_nfm_demod_settings_isSet = false; nfm_mod_settings = nullptr; @@ -130,6 +132,8 @@ SWGChannelSettings::init() { m_free_dv_mod_settings_isSet = false; freq_tracker_settings = new SWGFreqTrackerSettings(); m_freq_tracker_settings_isSet = false; + ieee_802_15_4_mod_settings = new SWGIEEE_802_15_4_ModSettings(); + m_ieee_802_15_4_mod_settings_isSet = false; nfm_demod_settings = new SWGNFMDemodSettings(); m_nfm_demod_settings_isSet = false; nfm_mod_settings = new SWGNFMModSettings(); @@ -205,6 +209,9 @@ SWGChannelSettings::cleanup() { if(freq_tracker_settings != nullptr) { delete freq_tracker_settings; } + if(ieee_802_15_4_mod_settings != nullptr) { + delete ieee_802_15_4_mod_settings; + } if(nfm_demod_settings != nullptr) { delete nfm_demod_settings; } @@ -291,6 +298,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&freq_tracker_settings, pJson["FreqTrackerSettings"], "SWGFreqTrackerSettings", "SWGFreqTrackerSettings"); + ::SWGSDRangel::setValue(&ieee_802_15_4_mod_settings, pJson["IEEE_802_15_4_ModSettings"], "SWGIEEE_802_15_4_ModSettings", "SWGIEEE_802_15_4_ModSettings"); + ::SWGSDRangel::setValue(&nfm_demod_settings, pJson["NFMDemodSettings"], "SWGNFMDemodSettings", "SWGNFMDemodSettings"); ::SWGSDRangel::setValue(&nfm_mod_settings, pJson["NFMModSettings"], "SWGNFMModSettings", "SWGNFMModSettings"); @@ -384,6 +393,9 @@ SWGChannelSettings::asJsonObject() { if((freq_tracker_settings != nullptr) && (freq_tracker_settings->isSet())){ toJsonValue(QString("FreqTrackerSettings"), freq_tracker_settings, obj, QString("SWGFreqTrackerSettings")); } + if((ieee_802_15_4_mod_settings != nullptr) && (ieee_802_15_4_mod_settings->isSet())){ + toJsonValue(QString("IEEE_802_15_4_ModSettings"), ieee_802_15_4_mod_settings, obj, QString("SWGIEEE_802_15_4_ModSettings")); + } if((nfm_demod_settings != nullptr) && (nfm_demod_settings->isSet())){ toJsonValue(QString("NFMDemodSettings"), nfm_demod_settings, obj, QString("SWGNFMDemodSettings")); } @@ -597,6 +609,16 @@ SWGChannelSettings::setFreqTrackerSettings(SWGFreqTrackerSettings* freq_tracker_ this->m_freq_tracker_settings_isSet = true; } +SWGIEEE_802_15_4_ModSettings* +SWGChannelSettings::getIeee802154ModSettings() { + return ieee_802_15_4_mod_settings; +} +void +SWGChannelSettings::setIeee802154ModSettings(SWGIEEE_802_15_4_ModSettings* ieee_802_15_4_mod_settings) { + this->ieee_802_15_4_mod_settings = ieee_802_15_4_mod_settings; + this->m_ieee_802_15_4_mod_settings_isSet = true; +} + SWGNFMDemodSettings* SWGChannelSettings::getNfmDemodSettings() { return nfm_demod_settings; @@ -783,6 +805,9 @@ SWGChannelSettings::isSet(){ if(freq_tracker_settings && freq_tracker_settings->isSet()){ isObjectUpdated = true; break; } + if(ieee_802_15_4_mod_settings && ieee_802_15_4_mod_settings->isSet()){ + isObjectUpdated = true; break; + } if(nfm_demod_settings && nfm_demod_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index a47c57951..990208b03 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -35,6 +35,7 @@ #include "SWGFreeDVDemodSettings.h" #include "SWGFreeDVModSettings.h" #include "SWGFreqTrackerSettings.h" +#include "SWGIEEE_802_15_4_ModSettings.h" #include "SWGLocalSinkSettings.h" #include "SWGLocalSourceSettings.h" #include "SWGNFMDemodSettings.h" @@ -119,6 +120,9 @@ public: SWGFreqTrackerSettings* getFreqTrackerSettings(); void setFreqTrackerSettings(SWGFreqTrackerSettings* freq_tracker_settings); + SWGIEEE_802_15_4_ModSettings* getIeee802154ModSettings(); + void setIeee802154ModSettings(SWGIEEE_802_15_4_ModSettings* ieee_802_15_4_mod_settings); + SWGNFMDemodSettings* getNfmDemodSettings(); void setNfmDemodSettings(SWGNFMDemodSettings* nfm_demod_settings); @@ -213,6 +217,9 @@ private: SWGFreqTrackerSettings* freq_tracker_settings; bool m_freq_tracker_settings_isSet; + SWGIEEE_802_15_4_ModSettings* ieee_802_15_4_mod_settings; + bool m_ieee_802_15_4_mod_settings_isSet; + SWGNFMDemodSettings* nfm_demod_settings; bool m_nfm_demod_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.cpp new file mode 100644 index 000000000..689caddfd --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.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, USRP 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 "SWGIEEE_802_15_4_ModActions.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGIEEE_802_15_4_ModActions::SWGIEEE_802_15_4_ModActions(QString* json) { + init(); + this->fromJson(*json); +} + +SWGIEEE_802_15_4_ModActions::SWGIEEE_802_15_4_ModActions() { + tx = nullptr; + m_tx_isSet = false; +} + +SWGIEEE_802_15_4_ModActions::~SWGIEEE_802_15_4_ModActions() { + this->cleanup(); +} + +void +SWGIEEE_802_15_4_ModActions::init() { + tx = new SWGIEEE_802_15_4_ModActions_tx(); + m_tx_isSet = false; +} + +void +SWGIEEE_802_15_4_ModActions::cleanup() { + if(tx != nullptr) { + delete tx; + } +} + +SWGIEEE_802_15_4_ModActions* +SWGIEEE_802_15_4_ModActions::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGIEEE_802_15_4_ModActions::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&tx, pJson["tx"], "SWGIEEE_802_15_4_ModActions_tx", "SWGIEEE_802_15_4_ModActions_tx"); + +} + +QString +SWGIEEE_802_15_4_ModActions::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGIEEE_802_15_4_ModActions::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if((tx != nullptr) && (tx->isSet())){ + toJsonValue(QString("tx"), tx, obj, QString("SWGIEEE_802_15_4_ModActions_tx")); + } + + return obj; +} + +SWGIEEE_802_15_4_ModActions_tx* +SWGIEEE_802_15_4_ModActions::getTx() { + return tx; +} +void +SWGIEEE_802_15_4_ModActions::setTx(SWGIEEE_802_15_4_ModActions_tx* tx) { + this->tx = tx; + this->m_tx_isSet = true; +} + + +bool +SWGIEEE_802_15_4_ModActions::isSet(){ + bool isObjectUpdated = false; + do{ + if(tx && tx->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.h new file mode 100644 index 000000000..8286460e4 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions.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, USRP 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. + */ + +/* + * SWGIEEE_802_15_4_ModActions.h + * + * IEEE_802_15_4_Mod + */ + +#ifndef SWGIEEE_802_15_4_ModActions_H_ +#define SWGIEEE_802_15_4_ModActions_H_ + +#include + + +#include "SWGIEEE_802_15_4_ModActions_tx.h" + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGIEEE_802_15_4_ModActions: public SWGObject { +public: + SWGIEEE_802_15_4_ModActions(); + SWGIEEE_802_15_4_ModActions(QString* json); + virtual ~SWGIEEE_802_15_4_ModActions(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGIEEE_802_15_4_ModActions* fromJson(QString &jsonString) override; + + SWGIEEE_802_15_4_ModActions_tx* getTx(); + void setTx(SWGIEEE_802_15_4_ModActions_tx* tx); + + + virtual bool isSet() override; + +private: + SWGIEEE_802_15_4_ModActions_tx* tx; + bool m_tx_isSet; + +}; + +} + +#endif /* SWGIEEE_802_15_4_ModActions_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp new file mode 100644 index 000000000..0443b14e6 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.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, USRP 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 "SWGIEEE_802_15_4_ModActions_tx.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGIEEE_802_15_4_ModActions_tx::SWGIEEE_802_15_4_ModActions_tx(QString* json) { + init(); + this->fromJson(*json); +} + +SWGIEEE_802_15_4_ModActions_tx::SWGIEEE_802_15_4_ModActions_tx() { + data = nullptr; + m_data_isSet = false; +} + +SWGIEEE_802_15_4_ModActions_tx::~SWGIEEE_802_15_4_ModActions_tx() { + this->cleanup(); +} + +void +SWGIEEE_802_15_4_ModActions_tx::init() { + data = new QString(""); + m_data_isSet = false; +} + +void +SWGIEEE_802_15_4_ModActions_tx::cleanup() { + if(data != nullptr) { + delete data; + } +} + +SWGIEEE_802_15_4_ModActions_tx* +SWGIEEE_802_15_4_ModActions_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 +SWGIEEE_802_15_4_ModActions_tx::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&data, pJson["data"], "QString", "QString"); + +} + +QString +SWGIEEE_802_15_4_ModActions_tx::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGIEEE_802_15_4_ModActions_tx::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(data != nullptr && *data != QString("")){ + toJsonValue(QString("data"), data, obj, QString("QString")); + } + + return obj; +} + +QString* +SWGIEEE_802_15_4_ModActions_tx::getData() { + return data; +} +void +SWGIEEE_802_15_4_ModActions_tx::setData(QString* data) { + this->data = data; + this->m_data_isSet = true; +} + + +bool +SWGIEEE_802_15_4_ModActions_tx::isSet(){ + bool isObjectUpdated = false; + do{ + if(data && *data != QString("")){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h new file mode 100644 index 000000000..920a06336 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.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, USRP 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. + */ + +/* + * SWGIEEE_802_15_4_ModActions_tx.h + * + * Transmit a frame + */ + +#ifndef SWGIEEE_802_15_4_ModActions_tx_H_ +#define SWGIEEE_802_15_4_ModActions_tx_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGIEEE_802_15_4_ModActions_tx: public SWGObject { +public: + SWGIEEE_802_15_4_ModActions_tx(); + SWGIEEE_802_15_4_ModActions_tx(QString* json); + virtual ~SWGIEEE_802_15_4_ModActions_tx(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGIEEE_802_15_4_ModActions_tx* fromJson(QString &jsonString) override; + + QString* getData(); + void setData(QString* data); + + + virtual bool isSet() override; + +private: + QString* data; + bool m_data_isSet; + +}; + +} + +#endif /* SWGIEEE_802_15_4_ModActions_tx_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModReport.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModReport.cpp new file mode 100644 index 000000000..2c8f384e8 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModReport.cpp @@ -0,0 +1,131 @@ +/** + * 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, USRP 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 "SWGIEEE_802_15_4_ModReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGIEEE_802_15_4_ModReport::SWGIEEE_802_15_4_ModReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGIEEE_802_15_4_ModReport::SWGIEEE_802_15_4_ModReport() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +SWGIEEE_802_15_4_ModReport::~SWGIEEE_802_15_4_ModReport() { + this->cleanup(); +} + +void +SWGIEEE_802_15_4_ModReport::init() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +void +SWGIEEE_802_15_4_ModReport::cleanup() { + + +} + +SWGIEEE_802_15_4_ModReport* +SWGIEEE_802_15_4_ModReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGIEEE_802_15_4_ModReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); + + ::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", ""); + +} + +QString +SWGIEEE_802_15_4_ModReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGIEEE_802_15_4_ModReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_power_db_isSet){ + obj->insert("channelPowerDB", QJsonValue(channel_power_db)); + } + if(m_channel_sample_rate_isSet){ + obj->insert("channelSampleRate", QJsonValue(channel_sample_rate)); + } + + return obj; +} + +float +SWGIEEE_802_15_4_ModReport::getChannelPowerDb() { + return channel_power_db; +} +void +SWGIEEE_802_15_4_ModReport::setChannelPowerDb(float channel_power_db) { + this->channel_power_db = channel_power_db; + this->m_channel_power_db_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModReport::getChannelSampleRate() { + return channel_sample_rate; +} +void +SWGIEEE_802_15_4_ModReport::setChannelSampleRate(qint32 channel_sample_rate) { + this->channel_sample_rate = channel_sample_rate; + this->m_channel_sample_rate_isSet = true; +} + + +bool +SWGIEEE_802_15_4_ModReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_power_db_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/SWGIEEE_802_15_4_ModReport.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModReport.h new file mode 100644 index 000000000..517ef93f3 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModReport.h @@ -0,0 +1,64 @@ +/** + * 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, USRP 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. + */ + +/* + * SWGIEEE_802_15_4_ModReport.h + * + * IEEE_802_15_4_Mod + */ + +#ifndef SWGIEEE_802_15_4_ModReport_H_ +#define SWGIEEE_802_15_4_ModReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGIEEE_802_15_4_ModReport: public SWGObject { +public: + SWGIEEE_802_15_4_ModReport(); + SWGIEEE_802_15_4_ModReport(QString* json); + virtual ~SWGIEEE_802_15_4_ModReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGIEEE_802_15_4_ModReport* fromJson(QString &jsonString) override; + + float getChannelPowerDb(); + void setChannelPowerDb(float channel_power_db); + + qint32 getChannelSampleRate(); + void setChannelSampleRate(qint32 channel_sample_rate); + + + virtual bool isSet() override; + +private: + float channel_power_db; + bool m_channel_power_db_isSet; + + qint32 channel_sample_rate; + bool m_channel_sample_rate_isSet; + +}; + +} + +#endif /* SWGIEEE_802_15_4_ModReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp new file mode 100644 index 000000000..9b6c7c806 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.cpp @@ -0,0 +1,459 @@ +/** + * 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, USRP 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 "SWGIEEE_802_15_4_ModSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGIEEE_802_15_4_ModSettings::SWGIEEE_802_15_4_ModSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGIEEE_802_15_4_ModSettings::SWGIEEE_802_15_4_ModSettings() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + phy = nullptr; + m_phy_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_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; + 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; +} + +SWGIEEE_802_15_4_ModSettings::~SWGIEEE_802_15_4_ModSettings() { + this->cleanup(); +} + +void +SWGIEEE_802_15_4_ModSettings::init() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + phy = new QString(""); + m_phy_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_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; + 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 +SWGIEEE_802_15_4_ModSettings::cleanup() { + + if(phy != nullptr) { + delete phy; + } + + + + + + + + if(title != nullptr) { + delete title; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + +} + +SWGIEEE_802_15_4_ModSettings* +SWGIEEE_802_15_4_ModSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGIEEE_802_15_4_ModSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + + ::SWGSDRangel::setValue(&phy, pJson["phy"], "QString", "QString"); + + ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "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(&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 +SWGIEEE_802_15_4_ModSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGIEEE_802_15_4_ModSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_input_frequency_offset_isSet){ + obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); + } + if(phy != nullptr && *phy != QString("")){ + toJsonValue(QString("phy"), phy, obj, QString("QString")); + } + if(m_rf_bandwidth_isSet){ + obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); + } + 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_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 +SWGIEEE_802_15_4_ModSettings::getInputFrequencyOffset() { + return input_frequency_offset; +} +void +SWGIEEE_802_15_4_ModSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { + this->input_frequency_offset = input_frequency_offset; + this->m_input_frequency_offset_isSet = true; +} + +QString* +SWGIEEE_802_15_4_ModSettings::getPhy() { + return phy; +} +void +SWGIEEE_802_15_4_ModSettings::setPhy(QString* phy) { + this->phy = phy; + this->m_phy_isSet = true; +} + +float +SWGIEEE_802_15_4_ModSettings::getRfBandwidth() { + return rf_bandwidth; +} +void +SWGIEEE_802_15_4_ModSettings::setRfBandwidth(float rf_bandwidth) { + this->rf_bandwidth = rf_bandwidth; + this->m_rf_bandwidth_isSet = true; +} + +float +SWGIEEE_802_15_4_ModSettings::getGain() { + return gain; +} +void +SWGIEEE_802_15_4_ModSettings::setGain(float gain) { + this->gain = gain; + this->m_gain_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getChannelMute() { + return channel_mute; +} +void +SWGIEEE_802_15_4_ModSettings::setChannelMute(qint32 channel_mute) { + this->channel_mute = channel_mute; + this->m_channel_mute_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getRepeat() { + return repeat; +} +void +SWGIEEE_802_15_4_ModSettings::setRepeat(qint32 repeat) { + this->repeat = repeat; + this->m_repeat_isSet = true; +} + +float +SWGIEEE_802_15_4_ModSettings::getRepeatDelay() { + return repeat_delay; +} +void +SWGIEEE_802_15_4_ModSettings::setRepeatDelay(float repeat_delay) { + this->repeat_delay = repeat_delay; + this->m_repeat_delay_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getRepeatCount() { + return repeat_count; +} +void +SWGIEEE_802_15_4_ModSettings::setRepeatCount(qint32 repeat_count) { + this->repeat_count = repeat_count; + this->m_repeat_count_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getRgbColor() { + return rgb_color; +} +void +SWGIEEE_802_15_4_ModSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGIEEE_802_15_4_ModSettings::getTitle() { + return title; +} +void +SWGIEEE_802_15_4_ModSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getStreamIndex() { + return stream_index; +} +void +SWGIEEE_802_15_4_ModSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGIEEE_802_15_4_ModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGIEEE_802_15_4_ModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGIEEE_802_15_4_ModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGIEEE_802_15_4_ModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGIEEE_802_15_4_ModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGIEEE_802_15_4_ModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGIEEE_802_15_4_ModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + + +bool +SWGIEEE_802_15_4_ModSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_input_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(phy && *phy != QString("")){ + isObjectUpdated = true; break; + } + if(m_rf_bandwidth_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_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/SWGIEEE_802_15_4_ModSettings.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h new file mode 100644 index 000000000..22c482be5 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModSettings.h @@ -0,0 +1,149 @@ +/** + * 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, USRP 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. + */ + +/* + * SWGIEEE_802_15_4_ModSettings.h + * + * IEEE_802_15_4_Mod + */ + +#ifndef SWGIEEE_802_15_4_ModSettings_H_ +#define SWGIEEE_802_15_4_ModSettings_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGIEEE_802_15_4_ModSettings: public SWGObject { +public: + SWGIEEE_802_15_4_ModSettings(); + SWGIEEE_802_15_4_ModSettings(QString* json); + virtual ~SWGIEEE_802_15_4_ModSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGIEEE_802_15_4_ModSettings* fromJson(QString &jsonString) override; + + qint64 getInputFrequencyOffset(); + void setInputFrequencyOffset(qint64 input_frequency_offset); + + QString* getPhy(); + void setPhy(QString* phy); + + float getRfBandwidth(); + void setRfBandwidth(float rf_bandwidth); + + 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 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; + + QString* phy; + bool m_phy_isSet; + + float rf_bandwidth; + bool m_rf_bandwidth_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 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 /* SWGIEEE_802_15_4_ModSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 04a4514e4..867e0913e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -99,6 +99,10 @@ #include "SWGGain.h" #include "SWGHackRFInputSettings.h" #include "SWGHackRFOutputSettings.h" +#include "SWGIEEE_802_15_4_ModActions.h" +#include "SWGIEEE_802_15_4_ModActions_tx.h" +#include "SWGIEEE_802_15_4_ModReport.h" +#include "SWGIEEE_802_15_4_ModSettings.h" #include "SWGInstanceChannelsResponse.h" #include "SWGInstanceConfigResponse.h" #include "SWGInstanceDevicesResponse.h" @@ -454,6 +458,18 @@ namespace SWGSDRangel { if(QString("SWGHackRFOutputSettings").compare(type) == 0) { return new SWGHackRFOutputSettings(); } + if(QString("SWGIEEE_802_15_4_ModActions").compare(type) == 0) { + return new SWGIEEE_802_15_4_ModActions(); + } + if(QString("SWGIEEE_802_15_4_ModActions_tx").compare(type) == 0) { + return new SWGIEEE_802_15_4_ModActions_tx(); + } + if(QString("SWGIEEE_802_15_4_ModReport").compare(type) == 0) { + return new SWGIEEE_802_15_4_ModReport(); + } + if(QString("SWGIEEE_802_15_4_ModSettings").compare(type) == 0) { + return new SWGIEEE_802_15_4_ModSettings(); + } if(QString("SWGInstanceChannelsResponse").compare(type) == 0) { return new SWGInstanceChannelsResponse(); }