From ede1b035280ece80066801399376f117245cb705 Mon Sep 17 00:00:00 2001 From: Daniele Forsi Date: Sun, 22 May 2022 11:53:14 +0200 Subject: [PATCH 01/29] Fix typing errors in readme's These are errors that codespell doesn't fix automatically because there is more than one fix. The following command now gives an empty list of possible fixes, as expected: find . -name '*.md' -exec codespell --ignore-words-list=cach,doas,ehr,hist,inout,lits,nd,ot,verry --write-changes --summary {} \+ --- plugins/channelrx/demodais/readme.md | 2 +- plugins/channelrx/demodvormc/readme.md | 2 +- plugins/feature/vorlocalizer/readme.md | 2 +- plugins/samplemimo/bladerf2mimo/readme.md | 2 +- plugins/samplesource/limesdrinput/readme.md | 4 ++-- plugins/samplesource/xtrxinput/readme.md | 4 ++-- sdrgui/readme.md | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/channelrx/demodais/readme.md b/plugins/channelrx/demodais/readme.md index e160a3541..674a7a80d 100644 --- a/plugins/channelrx/demodais/readme.md +++ b/plugins/channelrx/demodais/readme.md @@ -68,7 +68,7 @@ UDP port number to forward received messages to.

12: UDP format

-The format the messages are forwared via UDP in. This can be either binary (which is useful for SDRangel's PERTester feature) or NMEA (which is useful for 3rd party applications such as OpenCPN). +The format the messages are forwarded via UDP in. This can be either binary (which is useful for SDRangel's PERTester feature) or NMEA (which is useful for 3rd party applications such as OpenCPN).

13: Start/stop Logging Messages to .csv File

diff --git a/plugins/channelrx/demodvormc/readme.md b/plugins/channelrx/demodvormc/readme.md index 3053da335..4ab3aa259 100644 --- a/plugins/channelrx/demodvormc/readme.md +++ b/plugins/channelrx/demodvormc/readme.md @@ -42,7 +42,7 @@ Pressing this button downloads the OpenAIP.net Navaid database, which contains t

5: Draw Radials Adjusted for Magnetic Declination

-When checked, radials on the map will drawn adjusted for magnetic declination. For example, if a VOR has a magnetic declination of 5 degrees, and the radial is calculated at 0 degrees, the radial will be drawn to magnetic North, i.e. -5 degress from true North. If not checked, the same radial would be drawn to true North (i.e 0 degrees), which may result in a less accurate position estimate. +When checked, radials on the map will drawn adjusted for magnetic declination. For example, if a VOR has a magnetic declination of 5 degrees, and the radial is calculated at 0 degrees, the radial will be drawn to magnetic North, i.e. -5 degrees from true North. If not checked, the same radial would be drawn to true North (i.e 0 degrees), which may result in a less accurate position estimate.

6: Morse ident threshold

diff --git a/plugins/feature/vorlocalizer/readme.md b/plugins/feature/vorlocalizer/readme.md index f4f4fc18a..15fe66557 100644 --- a/plugins/feature/vorlocalizer/readme.md +++ b/plugins/feature/vorlocalizer/readme.md @@ -27,7 +27,7 @@ Pressing this button downloads the OpenAIP.net Navaid database, which contains t

3: Draw Radials Adjusted for Magnetic Declination

-When checked, radials on the map will drawn adjusted for magnetic declination. For example, if a VOR has a magnetic declination of 5 degrees, and the radial is calculated at 0 degrees, the radial will be drawn to magnetic North, i.e. -5 degress from true North. If not checked, the same radial would be drawn to true North (i.e 0 degrees), which may result in a less accurate position estimate. +When checked, radials on the map will drawn adjusted for magnetic declination. For example, if a VOR has a magnetic declination of 5 degrees, and the radial is calculated at 0 degrees, the radial will be drawn to magnetic North, i.e. -5 degrees from true North. If not checked, the same radial would be drawn to true North (i.e 0 degrees), which may result in a less accurate position estimate.

4: Round robin turn time

diff --git a/plugins/samplemimo/bladerf2mimo/readme.md b/plugins/samplemimo/bladerf2mimo/readme.md index f8dc2c303..517246477 100644 --- a/plugins/samplemimo/bladerf2mimo/readme.md +++ b/plugins/samplemimo/bladerf2mimo/readme.md @@ -104,7 +104,7 @@ With SR as the sample rate before decimation Fc is calculated as: For Rx streams the I/Q stream from the BladeRF ADC is downsampled by a power of two before being sent to the passband. -For Tx strams the baseband stream is interpolated by this value before being sent to the BladeRF device. +For Tx streams the baseband stream is interpolated by this value before being sent to the BladeRF device. Possible values are increasing powers of two: 1 (no decimation or interpolation), 2, 4, 8, 16, 32, 64. diff --git a/plugins/samplesource/limesdrinput/readme.md b/plugins/samplesource/limesdrinput/readme.md index 9c86f4687..ba80b268a 100644 --- a/plugins/samplesource/limesdrinput/readme.md +++ b/plugins/samplesource/limesdrinput/readme.md @@ -161,7 +161,7 @@ Use this button to adjust the global gain of the LNA, TIA and PGA. LimeSuite sof

8.3: LNA manual gain

-Use this button to adjust the gain of tha LNA when manual gain mode is set (8.1). Gain can be set between 1 and 30 dB in 1 dB steps. However the hardware has 3 dB steps for the lower gain values so increasing or decreasing by one step does not always produce a change. The value in dB appears at the right of the button. +Use this button to adjust the gain of the LNA when manual gain mode is set (8.1). Gain can be set between 1 and 30 dB in 1 dB steps. However the hardware has 3 dB steps for the lower gain values so increasing or decreasing by one step does not always produce a change. The value in dB appears at the right of the button.

8.4: TIA manual gain

@@ -169,7 +169,7 @@ Use this combo to select the TIA gain in dB when manual gain mode is set (8.1).

8.5: PGA manual gain

-Use this button to adjust the gain of tha PGA when manual gain mode is set (8.1). Gain can be set between 0 and 32 dB in 1 dB steps. The value in dB appears at the right of the button. +Use this button to adjust the gain of the PGA when manual gain mode is set (8.1). Gain can be set between 0 and 32 dB in 1 dB steps. The value in dB appears at the right of the button.

9: Antenna select

diff --git a/plugins/samplesource/xtrxinput/readme.md b/plugins/samplesource/xtrxinput/readme.md index b82329800..a3fc80942 100644 --- a/plugins/samplesource/xtrxinput/readme.md +++ b/plugins/samplesource/xtrxinput/readme.md @@ -179,7 +179,7 @@ Use this button to adjust the global gain of the LNA, TIA and PGA. LimeSuite sof

8.3: LNA manual gain

-Use this button to adjust the gain of tha LNA when manual gain mode is set (9.1). Gain can be set between 1 and 30 dB in 1 dB steps. However the hardware has 3 dB steps for the lower gain values so increasing or decreasing by one step does not always produce a change. The value in dB appears at the right of the button. +Use this button to adjust the gain of the LNA when manual gain mode is set (9.1). Gain can be set between 1 and 30 dB in 1 dB steps. However the hardware has 3 dB steps for the lower gain values so increasing or decreasing by one step does not always produce a change. The value in dB appears at the right of the button.

8.4: TIA manual gain

@@ -187,7 +187,7 @@ Use this combo to select the TIA gain in dB when manual gain mode is set (9.1).

8.5: PGA manual gain

-Use this button to adjust the gain of tha PGA when manual gain mode is set (9.1). Gain can be set between 0 and 32 dB in 1 dB steps. The value in dB appears at the right of the button. +Use this button to adjust the gain of the PGA when manual gain mode is set (9.1). Gain can be set between 0 and 32 dB in 1 dB steps. The value in dB appears at the right of the button.

9: Antenna select

diff --git a/sdrgui/readme.md b/sdrgui/readme.md index d304790bd..53bcf9a2a 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -53,7 +53,7 @@ The workspace has a top bar with the following controls:

1.1: Workspace index

-Shows the index of the workspaces in the list of workspaces as a "W" followd by the index. +Shows the index of the workspaces in the list of workspaces as a "W" followed by the index.

1.2: Create new receiver

From 539a03373f894869f14acc072f869394b2463a09 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 21 May 2022 11:20:38 +0200 Subject: [PATCH 02/29] LimeRFE feature. Implements #1251 --- plugins/feature/CMakeLists.txt | 4 + plugins/feature/limerfe/CMakeLists.txt | 67 ++ plugins/feature/limerfe/limerfe.cpp | 716 +++++++++++++ plugins/feature/limerfe/limerfe.h | 117 +++ plugins/feature/limerfe/limerfegui.cpp | 1009 +++++++++++++++++++ plugins/feature/limerfe/limerfegui.h | 135 +++ plugins/feature/limerfe/limerfegui.ui | 980 ++++++++++++++++++ plugins/feature/limerfe/limerfeplugin.cpp | 80 ++ plugins/feature/limerfe/limerfeplugin.h | 48 + plugins/feature/limerfe/limerfesettings.cpp | 185 ++++ plugins/feature/limerfe/limerfesettings.h | 122 +++ plugins/feature/limerfe/limerfeusbcalib.cpp | 71 ++ plugins/feature/limerfe/limerfeusbcalib.h | 59 ++ 13 files changed, 3593 insertions(+) create mode 100644 plugins/feature/limerfe/CMakeLists.txt create mode 100644 plugins/feature/limerfe/limerfe.cpp create mode 100644 plugins/feature/limerfe/limerfe.h create mode 100644 plugins/feature/limerfe/limerfegui.cpp create mode 100644 plugins/feature/limerfe/limerfegui.h create mode 100644 plugins/feature/limerfe/limerfegui.ui create mode 100644 plugins/feature/limerfe/limerfeplugin.cpp create mode 100644 plugins/feature/limerfe/limerfeplugin.h create mode 100644 plugins/feature/limerfe/limerfesettings.cpp create mode 100644 plugins/feature/limerfe/limerfesettings.h create mode 100644 plugins/feature/limerfe/limerfeusbcalib.cpp create mode 100644 plugins/feature/limerfe/limerfeusbcalib.h diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index 9a00b324d..ac1b031d5 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -35,3 +35,7 @@ if (Qt5Charts_FOUND) add_subdirectory(radiosonde) add_subdirectory(startracker) endif() + +if (ENABLE_LIMESUITE AND LIMESUITE_FOUND) + add_subdirectory(limerfe) +endif() diff --git a/plugins/feature/limerfe/CMakeLists.txt b/plugins/feature/limerfe/CMakeLists.txt new file mode 100644 index 000000000..bb1555046 --- /dev/null +++ b/plugins/feature/limerfe/CMakeLists.txt @@ -0,0 +1,67 @@ +project(limerfe) + +set(limerfe_SOURCES + limerfe.cpp + limerfesettings.cpp + limerfeusbcalib.cpp + limerfeplugin.cpp + # limerfeworker.cpp + # limerfereport.cpp + # limerfewebapiadapter.cpp +) + +set(limerfe_HEADERS + limerfe.h + limerfesettings.h + limerfeusbcalib.h + limerfeplugin.h + # limerfeworker.h + # limerfereport.h + # limerfewebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${LIMESUITE_INCLUDE_DIR} +) + +if(NOT SERVER_MODE) + set(limerfe_SOURCES + ${limerfe_SOURCES} + limerfegui.cpp + limerfegui.ui + ) + set(limerfe_HEADERS + ${limerfe_HEADERS} + limerfegui.h + ) + + set(TARGET_NAME featurelimerfe) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME featurelimerfesrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${limerfe_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + ${LIMESUITE_LIBRARY} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/feature/limerfe/limerfe.cpp b/plugins/feature/limerfe/limerfe.cpp new file mode 100644 index 000000000..1c108f023 --- /dev/null +++ b/plugins/feature/limerfe/limerfe.cpp @@ -0,0 +1,716 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "SWGDeviceState.h" +#include "SWGErrorResponse.h" + +#include "util/simpleserializer.h" +#include "util/serialutil.h" +#include "webapi/webapiadapterinterface.h" + +#include "limerfe.h" + +MESSAGE_CLASS_DEFINITION(LimeRFE::MsgConfigureLimeRFE, Message) + +const char* const LimeRFE::m_featureIdURI = "sdrangel.feature.limerfe"; +const char* const LimeRFE::m_featureId = "LimeRFE"; + +const std::map LimeRFE::m_errorCodesMap = { + { 0, "OK"}, + {-4, "Error synchronizing communication"}, + {-3, "Non-configurable GPIO pin specified. Only pins 4 and 5 are configurable."}, + {-2, "Problem with .ini configuration file"}, + {-1, "Communication error"}, + { 1, "Wrong TX connector - not possible to route TX of the selecrted channel to the specified port"}, + { 2, "Wrong RX connector - not possible to route RX of the selecrted channel to the specified port"}, + { 3, "Mode TXRX not allowed - when the same port is selected for RX and TX, it is not allowed to use mode RX & TX"}, + { 4, "Wrong mode for cellular channel - Cellular FDD bands (1, 2, 3, and 7) are only allowed mode RX & TX, while TDD band 38 is allowed only RX or TX mode"}, + { 5, "Cellular channels must be the same both for RX and TX"}, + { 6, "Requested channel code is wrong"} +}; + +LimeRFE::LimeRFE(WebAPIAdapterInterface *webAPIAdapterInterface) : + Feature(m_featureIdURI, webAPIAdapterInterface), + m_webAPIAdapterInterface(webAPIAdapterInterface), + m_rfeDevice(nullptr) +{ + setObjectName(m_featureId); + m_state = StIdle; + m_errorMessage = "LimeRFE error"; + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &LimeRFE::networkManagerFinished + ); + listComPorts(); +} + +LimeRFE::~LimeRFE() +{ + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &LimeRFE::networkManagerFinished + ); + delete m_networkManager; + closeDevice(); +} + +void LimeRFE::start() +{ + qDebug("LimeRFE::start"); + m_state = StRunning; +} + +void LimeRFE::stop() +{ + qDebug("LimeRFE::stop"); + m_state = StIdle; +} + +void LimeRFE::listComPorts() +{ + m_comPorts.clear(); + std::vector comPorts; + SerialUtil::getComPorts(comPorts, "ttyUSB[0-9]+"); // regex is for Linux only + + for (std::vector::const_iterator it = comPorts.begin(); it != comPorts.end(); ++it) { + m_comPorts.push_back(QString(it->c_str())); + } +} + +bool LimeRFE::handleMessage(const Message& cmd) +{ + (void) cmd; + return false; +} + +QByteArray LimeRFE::serialize() const +{ + SimpleSerializer s(1); + + s.writeBlob(1, m_settings.serialize()); + s.writeBlob(2, m_calib.serialize()); + + return s.final(); +} + +bool LimeRFE::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + m_settings.resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + QByteArray bytetmp; + bool ret; + + d.readBlob(1, &bytetmp); + + if (m_settings.deserialize(bytetmp)) + { + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, true); + m_inputMessageQueue.push(msg); + ret = true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, true); + m_inputMessageQueue.push(msg); + ret = false; + } + + d.readBlob(2, &bytetmp); + + if (!m_calib.deserialize(bytetmp)) { + ret = false; + } + + return ret; + } + else + { + return false; + } +} + +int LimeRFE::openDevice(const std::string& serialDeviceName) +{ + closeDevice(); + + rfe_dev_t *rfeDevice = RFE_Open(serialDeviceName.c_str(), nullptr); + + if (rfeDevice != (void *) -1) + { + m_rfeDevice = rfeDevice; + return 0; + } + else + { + return -1; + } +} + +void LimeRFE::closeDevice() +{ + if (m_rfeDevice) + { + RFE_Close(m_rfeDevice); + m_rfeDevice = nullptr; + } +} + +int LimeRFE::configure() +{ + if (!m_rfeDevice) { + return -1; + } + + qDebug() << "LimeRFE::configure: " + << "attValue: " << (int) m_rfeBoardState.attValue + << "channelIDRX: " << (int) m_rfeBoardState.channelIDRX + << "channelIDTX: " << (int) m_rfeBoardState.channelIDTX + << "mode: " << (int) m_rfeBoardState.mode + << "notchOnOff: " << (int) m_rfeBoardState.notchOnOff + << "selPortRX: " << (int) m_rfeBoardState.selPortRX + << "selPortTX: " << (int) m_rfeBoardState.selPortTX + << "enableSWR: " << (int) m_rfeBoardState.enableSWR + << "sourceSWR: " << (int) m_rfeBoardState.sourceSWR; + + int rc = RFE_ConfigureState(m_rfeDevice, m_rfeBoardState); + + if (rc != 0) { + qInfo("LimeRFE::configure: %s", getError(rc).c_str()); + } else { + qDebug() << "LimeRFE::configure: done"; + } + + return rc; +} + +int LimeRFE::getState() +{ + if (!m_rfeDevice) { + return -1; + } + + int rc = RFE_GetState(m_rfeDevice, &m_rfeBoardState); + + qDebug() << "LimeRFE::getState: " + << "attValue: " << (int) m_rfeBoardState.attValue + << "channelIDRX: " << (int) m_rfeBoardState.channelIDRX + << "channelIDTX: " << (int) m_rfeBoardState.channelIDTX + << "mode: " << (int) m_rfeBoardState.mode + << "notchOnOff: " << (int) m_rfeBoardState.notchOnOff + << "selPortRX: " << (int) m_rfeBoardState.selPortRX + << "selPortTX: " << (int) m_rfeBoardState.selPortTX + << "enableSWR: " << (int) m_rfeBoardState.enableSWR + << "sourceSWR: " << (int) m_rfeBoardState.sourceSWR; + + if (rc != 0) { + qInfo("LimeRFE::getState: %s", getError(rc).c_str()); + } + + return rc; +} + +std::string LimeRFE::getError(int errorCode) +{ + std::map::const_iterator it = m_errorCodesMap.find(errorCode); + + if (it == m_errorCodesMap.end()) { + return "Unknown error"; + } else { + return it->second; + } +} + +int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) +{ + if (!m_rfeDevice) { + return -1; + } + + int mode = rxOn && settings.m_txOn ? + RFE_MODE_TXRX : rxOn ? + RFE_MODE_RX : settings.m_txOn ? + RFE_MODE_TX : RFE_MODE_NONE; + + int rc = RFE_Mode(m_rfeDevice, mode); + + if (rc == 0) { + settings.m_rxOn = rxOn; + } + + return rc; +} + +int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) +{ + if (!m_rfeDevice) { + return -1; + } + + int mode = txOn && settings.m_rxOn ? + RFE_MODE_TXRX : txOn ? + RFE_MODE_TX : settings.m_rxOn ? + RFE_MODE_RX : RFE_MODE_NONE; + + int rc = RFE_Mode(m_rfeDevice, mode); + + if (rc == 0) { + settings.m_txOn = txOn; + } + + return rc; +} + +bool LimeRFE::turnDevice(int deviceSetIndex, bool on) +{ + qDebug("LimeRFE::turnDevice %d: %s", deviceSetIndex, on ? "on" : "off"); + SWGSDRangel::SWGDeviceState response; + SWGSDRangel::SWGErrorResponse error; + int httpCode; + + if (on) { + httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost(deviceSetIndex, response, error); + } else { + httpCode = m_webAPIAdapterInterface->devicesetDeviceRunDelete(deviceSetIndex, response, error); + } + + if (httpCode/100 == 2) + { + qDebug("LimeRFE::turnDevice: %s success", on ? "on" : "off"); + return true; + } + else + { + qWarning("LimeRFE::turnDevice: error: %s", qPrintable(*error.getMessage())); + return false; + } +} + +int LimeRFE::getFwdPower(int& powerDB) +{ + if (!m_rfeDevice) { + return -1; + } + + int power; + int rc = RFE_ReadADC(m_rfeDevice, RFE_ADC1, &power); + + if (rc == 0) { + powerDB = power; + } + + return rc; +} + +int LimeRFE::getRefPower(int& powerDB) +{ + if (!m_rfeDevice) { + return -1; + } + + int power; + int rc = RFE_ReadADC(m_rfeDevice, RFE_ADC2, &power); + + if (rc == 0) { + powerDB = power; + } + + return rc; +} + +void LimeRFE::settingsToState(const LimeRFESettings& settings) +{ + if (settings.m_rxChannels == LimeRFESettings::ChannelGroups::ChannelsCellular) + { + if (settings.m_rxCellularChannel == LimeRFESettings::CellularChannel::CellularBand1) + { + m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND01; + m_rfeBoardState.mode = RFE_MODE_TXRX; + } + else if (settings.m_rxCellularChannel == LimeRFESettings::CellularChannel::CellularBand2) + { + m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND02; + m_rfeBoardState.mode = RFE_MODE_TXRX; + } + else if (settings.m_rxCellularChannel == LimeRFESettings::CellularChannel::CellularBand3) + { + m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND03; + m_rfeBoardState.mode = RFE_MODE_TXRX; + } + else if (settings.m_rxCellularChannel == LimeRFESettings::CellularChannel::CellularBand38) + { + m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND38; + } + else if (settings.m_rxCellularChannel == LimeRFESettings::CellularChannel::CellularBand7) + { + m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND07; + m_rfeBoardState.mode = RFE_MODE_TXRX; + } + + m_rfeBoardState.selPortRX = RFE_PORT_1; + m_rfeBoardState.selPortTX = RFE_PORT_1; + m_rfeBoardState.channelIDTX = m_rfeBoardState.channelIDRX; + } + else + { + m_rfeBoardState.mode = settings.m_rxOn && settings.m_txOn ? + RFE_MODE_TXRX : + settings.m_rxOn ? + RFE_MODE_RX : + settings.m_txOn ? + RFE_MODE_TX : + RFE_MODE_NONE; + + if (settings.m_rxChannels == LimeRFESettings::ChannelGroups::ChannelsWideband) + { + if (settings.m_rxWidebandChannel == LimeRFESettings::WidebandChannel::WidebandLow) { + m_rfeBoardState.channelIDRX = RFE_CID_WB_1000; + } else if (settings.m_rxWidebandChannel == LimeRFESettings::WidebandChannel::WidebandHigh) { + m_rfeBoardState.channelIDRX = RFE_CID_WB_4000; + } + } + else if (settings.m_rxChannels == LimeRFESettings::ChannelGroups::ChannelsHAM) + { + if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_30M) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0030; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_50_70MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0070; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_144_146MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0145; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_220_225MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0220; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_430_440MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0435; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_902_928MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_0920; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_1240_1325MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_1280; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_2300_2450MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_2400; + } else if (settings.m_rxHAMChannel == LimeRFESettings::HAMChannel::HAM_3300_3500MHz) { + m_rfeBoardState.channelIDRX = RFE_CID_HAM_3500; + } + } + + if (settings.m_rxPort == LimeRFESettings::RxPort::RxPortJ3) { + m_rfeBoardState.selPortRX = RFE_PORT_1; + } else if (settings.m_rxPort == LimeRFESettings::RxPort::RxPortJ5) { + m_rfeBoardState.selPortRX = RFE_PORT_3; + } + + if (settings.m_txRxDriven) + { + m_rfeBoardState.channelIDTX = m_rfeBoardState.channelIDRX; + } + else + { + if (settings.m_txChannels == LimeRFESettings::ChannelGroups::ChannelsWideband) + { + if (settings.m_txWidebandChannel == LimeRFESettings::WidebandChannel::WidebandLow) { + m_rfeBoardState.channelIDTX = RFE_CID_WB_1000; + } else if (settings.m_txWidebandChannel == LimeRFESettings::WidebandChannel::WidebandHigh) { + m_rfeBoardState.channelIDTX = RFE_CID_WB_4000; + } + } + else if (settings.m_txChannels == LimeRFESettings::ChannelGroups::ChannelsHAM) + { + if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_30M) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0030; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_50_70MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0070; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_144_146MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0145; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_220_225MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0220; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_430_440MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0435; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_902_928MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_0920; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_1240_1325MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_1280; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_2300_2450MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_2400; + } else if (settings.m_txHAMChannel == LimeRFESettings::HAMChannel::HAM_3300_3500MHz) { + m_rfeBoardState.channelIDTX = RFE_CID_HAM_3500; + } + } + } + + if (settings.m_txPort == LimeRFESettings::TxPort::TxPortJ3) { + m_rfeBoardState.selPortTX = RFE_PORT_1; + } else if (settings.m_txPort == LimeRFESettings::TxPort::TxPortJ4) { + m_rfeBoardState.selPortTX = RFE_PORT_2; + } else if (settings.m_txPort == LimeRFESettings::TxPort::TxPortJ5) { + m_rfeBoardState.selPortTX = RFE_PORT_3; + } + } + + m_rfeBoardState.attValue = settings.m_attenuationFactor > 7 ? 7 : settings.m_attenuationFactor; + m_rfeBoardState.notchOnOff = settings.m_amfmNotch; + m_rfeBoardState.enableSWR = settings.m_swrEnable ? RFE_SWR_ENABLE : RFE_SWR_DISABLE; + + if (settings.m_swrSource == LimeRFESettings::SWRSource::SWRExternal) { + m_rfeBoardState.sourceSWR = RFE_SWR_SRC_EXT; + } else if (settings.m_swrSource == LimeRFESettings::SWRSource::SWRCellular) { + m_rfeBoardState.sourceSWR = RFE_SWR_SRC_CELL; + } +} + +void LimeRFE::stateToSettings(LimeRFESettings& settings) +{ + if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND01) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_rxCellularChannel = LimeRFESettings::CellularChannel::CellularBand1; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND02) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_rxCellularChannel = LimeRFESettings::CellularChannel::CellularBand2; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND03) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_rxCellularChannel = LimeRFESettings::CellularChannel::CellularBand3; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND07) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_rxCellularChannel = LimeRFESettings::CellularChannel::CellularBand7; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND38) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_rxCellularChannel = LimeRFESettings::CellularChannel::CellularBand38; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_WB_1000) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsWideband; + settings.m_rxWidebandChannel = LimeRFESettings::WidebandChannel::WidebandLow; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_WB_4000) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsWideband; + settings.m_rxWidebandChannel = LimeRFESettings::WidebandChannel::WidebandHigh; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0030) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_30M; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0070) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_50_70MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0145) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_144_146MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0220) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_220_225MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0435) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_430_440MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0920) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_902_928MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_1280) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_1240_1325MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_2400) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_2300_2450MHz; + } + else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_3500) + { + settings.m_rxChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_rxHAMChannel = LimeRFESettings::HAMChannel::HAM_3300_3500MHz; + } + + if (m_rfeBoardState.selPortRX == RFE_PORT_1) { + settings.m_rxPort = LimeRFESettings::RxPort::RxPortJ3; + } else if (m_rfeBoardState.selPortRX == RFE_PORT_3) { + settings.m_rxPort = LimeRFESettings::RxPort::RxPortJ5; + } + + if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND01) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_txCellularChannel = LimeRFESettings::CellularChannel::CellularBand1; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND02) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_txCellularChannel = LimeRFESettings::CellularChannel::CellularBand2; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND03) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_txCellularChannel = LimeRFESettings::CellularChannel::CellularBand3; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND07) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_txCellularChannel = LimeRFESettings::CellularChannel::CellularBand7; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND38) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsCellular; + settings.m_txCellularChannel = LimeRFESettings::CellularChannel::CellularBand38; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_WB_1000) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsWideband; + settings.m_txWidebandChannel = LimeRFESettings::WidebandChannel::WidebandLow; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_WB_4000) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsWideband; + settings.m_txWidebandChannel = LimeRFESettings::WidebandChannel::WidebandHigh; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0030) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_30M; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0070) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_50_70MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0145) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_144_146MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0220) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_220_225MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0435) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_430_440MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0920) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_902_928MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_1280) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_1240_1325MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_2400) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_2300_2450MHz; + } + else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_3500) + { + settings.m_txChannels = LimeRFESettings::ChannelGroups::ChannelsHAM; + settings.m_txHAMChannel = LimeRFESettings::HAMChannel::HAM_3300_3500MHz; + } + + if (m_rfeBoardState.selPortTX == RFE_PORT_1) { + settings.m_txPort = LimeRFESettings::TxPort::TxPortJ3; + } else if (m_rfeBoardState.selPortTX == RFE_PORT_2) { + settings.m_txPort = LimeRFESettings::TxPort::TxPortJ4; + } else if (m_rfeBoardState.selPortTX == RFE_PORT_3) { + settings.m_txPort = LimeRFESettings::TxPort::TxPortJ5; + } + + settings.m_attenuationFactor = m_rfeBoardState.attValue; + settings.m_amfmNotch = m_rfeBoardState.notchOnOff == RFE_NOTCH_ON; + + if (m_rfeBoardState.mode == RFE_MODE_RX) + { + settings.m_rxOn = true; + settings.m_txOn = false; + } + else if (m_rfeBoardState.mode == RFE_MODE_TX) + { + settings.m_rxOn = false; + settings.m_txOn = true; + } + else if (m_rfeBoardState.mode == RFE_MODE_NONE) + { + settings.m_rxOn = false; + settings.m_txOn = false; + } + else if (m_rfeBoardState.mode == RFE_MODE_TXRX) + { + settings.m_rxOn = true; + settings.m_txOn = true; + } + + settings.m_swrEnable = m_rfeBoardState.enableSWR == RFE_SWR_ENABLE; + settings.m_swrSource = m_rfeBoardState.sourceSWR == RFE_SWR_SRC_CELL ? + LimeRFESettings::SWRSource::SWRCellular : + LimeRFESettings::SWRSource::SWRExternal; +} + +void LimeRFE::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "LimeRFE::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("LimeRFE::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/feature/limerfe/limerfe.h b/plugins/feature/limerfe/limerfe.h new file mode 100644 index 000000000..e55c57956 --- /dev/null +++ b/plugins/feature/limerfe/limerfe.h @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_LIMERFE_H_ +#define INCLUDE_FEATURE_LIMERFE_H_ + +#include + +#include +#include +#include "lime/limeRFE.h" + +#include "feature/feature.h" +#include "util/message.h" + +#include "limerfesettings.h" +#include "limerfeusbcalib.h" + +class QNetworkReply; +class QNetworkAccessManager; + +class LimeRFE : public Feature +{ + Q_OBJECT +public: + class MsgConfigureLimeRFE : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const LimeRFESettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureLimeRFE* create(const LimeRFESettings& settings, bool force) { + return new MsgConfigureLimeRFE(settings, force); + } + + private: + LimeRFESettings m_settings; + bool m_force; + + MsgConfigureLimeRFE(const LimeRFESettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + LimeRFE(WebAPIAdapterInterface *webAPIAdapterInterface); + virtual ~LimeRFE(); + virtual void destroy() { delete this; } + virtual bool handleMessage(const Message& cmd); + + virtual void getIdentifier(QString& id) const { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual void getTitle(QString& title) const { title = m_settings.m_title; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + LimeRFEUSBCalib *getCalib() { return &m_calib; } + + int openDevice(const std::string& serialDeviceName); + void closeDevice(); + + const QStringList& getComPorts() { return m_comPorts; } + int configure(); + int getState(); + static std::string getError(int errorCode); + int setRx(LimeRFESettings& settings, bool rxOn); + int setTx(LimeRFESettings& settings, bool txOn); + bool turnDevice(int deviceSetIndex, bool on); + int getFwdPower(int& powerDB); + int getRefPower(int& powerDB); + + void settingsToState(const LimeRFESettings& settings); + void stateToSettings(LimeRFESettings& settings); + + static const char* const m_featureIdURI; + static const char* const m_featureId; + +private: + LimeRFESettings m_settings; + LimeRFEUSBCalib m_calib; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + WebAPIAdapterInterface *m_webAPIAdapterInterface; + + rfe_dev_t *m_rfeDevice; + rfe_boardState m_rfeBoardState; + static const std::map m_errorCodesMap; + QStringList m_comPorts; + + void start(); + void stop(); + void listComPorts(); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + +}; + +#endif diff --git a/plugins/feature/limerfe/limerfegui.cpp b/plugins/feature/limerfe/limerfegui.cpp new file mode 100644 index 000000000..496fa88df --- /dev/null +++ b/plugins/feature/limerfe/limerfegui.cpp @@ -0,0 +1,1009 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "feature/featureuiset.h" +#include "gui/basicfeaturesettingsdialog.h" +#include "gui/crightclickenabler.h" +#include "util/db.h" +#include "dsp/dspengine.h" +#include "dsp/dspdevicesourceengine.h" +#include "dsp/dspdevicesinkengine.h" +#include "device/deviceset.h" +#include "maincore.h" + +#include "limerfeusbcalib.h" +#include "ui_limerfegui.h" +#include "limerfe.h" +#include "limerfegui.h" + +LimeRFEGUI* LimeRFEGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature) +{ + LimeRFEGUI* gui = new LimeRFEGUI(pluginAPI, featureUISet, feature); + return gui; +} + +void LimeRFEGUI::destroy() +{ + delete this; +} + +void LimeRFEGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray LimeRFEGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool LimeRFEGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); + displaySettings(); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +void LimeRFEGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + +void LimeRFEGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + +void LimeRFEGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + getRollupContents()->saveState(m_rollupState); + applySettings(); +} + +void LimeRFEGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicFeatureSettingsDialog dialog(this); + dialog.setTitle(m_settings.m_title); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); + dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); + + dialog.move(p); + dialog.exec(); + + m_settings.m_title = dialog.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); + m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); + + setTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); + } + + resetContextMenuType(); +} + +LimeRFEGUI::LimeRFEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) : + FeatureGUI(parent), + ui(new Ui::LimeRFEGUI), + m_pluginAPI(pluginAPI), + m_featureUISet(featureUISet), + m_doApplySettings(true), + m_rxTxToggle(false), + m_currentPowerCorrection(0.0), + m_avgPower(false), + m_deviceSetSync(false) +{ + m_feature = feature; + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/feature/limerfe/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + + m_limeRFE = reinterpret_cast(feature); + m_limeRFE->setMessageQueueToGUI(&m_inputMessageQueue); + m_limeRFEUSBCalib = m_limeRFE->getCalib(); + + for (const auto& comPortName : m_limeRFE->getComPorts()) { + ui->device->addItem(comPortName); + } + + m_settings.setRollupState(&m_rollupState); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + updateDeviceSetList(); + displaySettings(); + highlightApplyButton(false); + m_timer.setInterval(500); + makeUIConnections(); +} + +LimeRFEGUI::~LimeRFEGUI() +{ + delete ui; +} + +void LimeRFEGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + LimeRFE::MsgConfigureLimeRFE* message = LimeRFE::MsgConfigureLimeRFE::create( m_settings, force); + m_limeRFE->getInputMessageQueue()->push(message); + } +} + +void LimeRFEGUI::displaySettings() +{ + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); + blockApplySettings(true); + setRxChannels(); + ui->rxPort->setCurrentIndex(m_settings.m_rxPort); + ui->attenuation->setCurrentIndex(m_settings.m_attenuationFactor); + ui->amFmNotchFilter->setChecked(m_settings.m_amfmNotch); + setTxChannels(); + ui->txPort->setCurrentIndex(m_settings.m_txPort); + ui->txFollowsRx->setChecked(m_settings.m_txRxDriven); + ui->rxTxToggle->setChecked(m_rxTxToggle); + displayMode(); + displayPower(); + blockApplySettings(false); +} + +void LimeRFEGUI::displayMode() +{ + QString s; + + if (m_settings.m_rxOn) + { + if (m_settings.m_txOn) { + s = "Rx/Tx"; + } else { + s = "Rx"; + } + } + else + { + if (m_settings.m_txOn) { + s = "Tx"; + } else { + s = "None"; + } + } + + ui->modeText->setText(s); + + ui->modeRx->blockSignals(true); + ui->modeTx->blockSignals(true); + + if (m_settings.m_rxOn) { + ui->modeRx->setStyleSheet("QToolButton { background-color : green; }"); + } else { + ui->modeRx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + if (m_settings.m_txOn) { + ui->modeTx->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->modeTx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + ui->modeRx->setChecked(m_settings.m_rxOn); + ui->modeTx->setChecked(m_settings.m_txOn); + + ui->modeRx->blockSignals(false); + ui->modeTx->blockSignals(false); +} + +void LimeRFEGUI::displayPower() +{ + ui->powerEnable->blockSignals(true); + ui->powerSource->blockSignals(true); + + ui->powerEnable->setChecked(m_settings.m_swrEnable); + ui->powerSource->setCurrentIndex((int) m_settings.m_swrSource); + + ui->powerEnable->blockSignals(false); + ui->powerSource->blockSignals(false); +} + +void LimeRFEGUI::refreshPower() +{ + int fwdPower, refPower; + int rc = m_limeRFE->getFwdPower(fwdPower); + + if (rc != 0) + { + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + return; + } + + rc = m_limeRFE->getRefPower(refPower); + + if (rc != 0) + { + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + return; + } + + double fwdPowerDB = fwdPower / 10.0; + double refPowerDB = refPower / 10.0; + double retLossDB = fwdPowerDB - refPowerDB; + + ui->powerFwdText->setText(QString::number(fwdPowerDB, 'f', 1)); + ui->powerRefText->setText(QString::number(refPowerDB, 'f', 1)); + ui->returnLossText->setText(QString::number(retLossDB, 'f', 1)); + + double denom = CalcDb::powerFromdB(retLossDB/2.0) - 1.0; + + if (denom == 0.0) + { + ui->swrText->setText("---"); + } + else + { + double vswr = (CalcDb::powerFromdB(retLossDB/2.0) + 1.0) / denom; + vswr = vswr < 0.0 ? 0.0 : vswr > 99.999 ? 99.999 : vswr; + ui->swrText->setText(QString::number(vswr, 'f', 3)); + } + + updateAbsPower(m_currentPowerCorrection); +} + +void LimeRFEGUI::setRxChannels() +{ + ui->rxChannel->blockSignals(true); + ui->rxPort->blockSignals(true); + ui->rxChannel->clear(); + ui->rxPort->clear(); + + if (m_settings.m_rxChannels == LimeRFESettings::ChannelsWideband) + { + ui->rxChannel->addItem("1-1000MHz"); + ui->rxChannel->addItem("1-4GHz"); + ui->rxChannel->setCurrentIndex((int) m_settings.m_rxWidebandChannel); + ui->rxPort->addItem("TX/RX (J3)"); + ui->rxPort->addItem("TX/RX 30M (J5)"); + ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); + ui->txFollowsRx->setEnabled(true); + ui->rxPort->setEnabled(true); + } + else if (m_settings.m_rxChannels == LimeRFESettings::ChannelsHAM) + { + ui->rxChannel->addItem("<30MHz"); + ui->rxChannel->addItem("50-70MHz"); + ui->rxChannel->addItem("144-146MHz"); + ui->rxChannel->addItem("220-225MHz"); + ui->rxChannel->addItem("430-440MHz"); + ui->rxChannel->addItem("902-928MHz"); + ui->rxChannel->addItem("1240-1325MHz"); + ui->rxChannel->addItem("2300-2450MHz"); + ui->rxChannel->addItem("3300-3500MHz"); + ui->rxChannel->setCurrentIndex((int) m_settings.m_rxHAMChannel); + ui->txFollowsRx->setEnabled(true); + + switch(m_settings.m_rxHAMChannel) + { + case LimeRFESettings::HAM_30M: + case LimeRFESettings::HAM_50_70MHz: + case LimeRFESettings::HAM_144_146MHz: + case LimeRFESettings::HAM_220_225MHz: + case LimeRFESettings::HAM_430_440MHz: + ui->rxPort->addItem("TX/RX (J3)"); + ui->rxPort->addItem("TX/RX 30M (J5)"); + ui->rxPort->setEnabled(true); + ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); + break; + case LimeRFESettings::HAM_902_928MHz: + case LimeRFESettings::HAM_1240_1325MHz: + case LimeRFESettings::HAM_2300_2450MHz: + case LimeRFESettings::HAM_3300_3500MHz: + ui->rxPort->addItem("TX/RX (J3)"); + ui->rxPort->setEnabled(false); + m_settings.m_rxPort = LimeRFESettings::RxPortJ3; + ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); + break; + default: + break; + } + } + else if (m_settings.m_rxChannels == LimeRFESettings::ChannelsCellular) + { + ui->rxChannel->addItem("Band1"); + ui->rxChannel->addItem("Band2"); + ui->rxChannel->addItem("Band3"); + ui->rxChannel->addItem("Band7"); + ui->rxChannel->addItem("Band38"); + ui->rxChannel->setCurrentIndex((int) m_settings.m_rxCellularChannel); + ui->rxPort->addItem("TX/RX (J3)"); + ui->rxPort->setEnabled(false); + m_settings.m_rxPort = LimeRFESettings::RxPortJ3; + ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); + m_settings.m_txRxDriven = true; + ui->txFollowsRx->setEnabled(false); + ui->txFollowsRx->setChecked(m_settings.m_txRxDriven); + } + + ui->rxChannelGroup->setCurrentIndex((int) m_settings.m_rxChannels); + ui->rxPort->blockSignals(false); + ui->rxChannel->blockSignals(false); +} + +void LimeRFEGUI::setTxChannels() +{ + ui->txChannel->blockSignals(true); + ui->txPort->blockSignals(true); + ui->powerCorrValue->blockSignals(true); + + ui->txChannel->clear(); + ui->txPort->clear(); + + if (m_settings.m_txChannels == LimeRFESettings::ChannelsWideband) + { + ui->txChannel->addItem("1-1000MHz"); + ui->txChannel->addItem("1-4GHz"); + ui->txChannel->setCurrentIndex((int) m_settings.m_txWidebandChannel); + ui->txPort->addItem("TX/RX (J3)"); + ui->txPort->addItem("TX (J4)"); + ui->txPort->setCurrentIndex((int) m_settings.m_txPort); + ui->txPort->setEnabled(true); + } + else if (m_settings.m_txChannels == LimeRFESettings::ChannelsHAM) + { + ui->txChannel->addItem("<30MHz"); + ui->txChannel->addItem("50-70MHz"); + ui->txChannel->addItem("144-146MHz"); + ui->txChannel->addItem("220-225MHz"); + ui->txChannel->addItem("430-440MHz"); + ui->txChannel->addItem("902-928MHz"); + ui->txChannel->addItem("1240-1325MHz"); + ui->txChannel->addItem("2300-2450MHz"); + ui->txChannel->addItem("3300-3500MHz"); + ui->txChannel->setCurrentIndex((int) m_settings.m_txHAMChannel); + + switch(m_settings.m_txHAMChannel) + { + case LimeRFESettings::HAM_30M: + case LimeRFESettings::HAM_50_70MHz: + ui->txPort->addItem("TX/RX (J3)"); + ui->txPort->addItem("TX (J4)"); + ui->txPort->addItem("TX/RX 30M (J5)"); + ui->txPort->setEnabled(false); + m_settings.m_txPort = LimeRFESettings::TxPortJ5; + ui->txPort->setCurrentIndex((int) m_settings.m_txPort); + break; + case LimeRFESettings::HAM_144_146MHz: + case LimeRFESettings::HAM_220_225MHz: + case LimeRFESettings::HAM_430_440MHz: + case LimeRFESettings::HAM_902_928MHz: + case LimeRFESettings::HAM_1240_1325MHz: + case LimeRFESettings::HAM_2300_2450MHz: + case LimeRFESettings::HAM_3300_3500MHz: + ui->txPort->addItem("TX/RX (J3)"); + ui->txPort->addItem("TX (J4)"); + ui->txPort->setCurrentIndex(m_settings.m_txPort < 2 ? m_settings.m_txPort : 1); + ui->txPort->setEnabled(true); + break; + default: + break; + } + } + else if (m_settings.m_txChannels == LimeRFESettings::ChannelsCellular) + { + ui->txChannel->addItem("Band1"); + ui->txChannel->addItem("Band2"); + ui->txChannel->addItem("Band3"); + ui->txChannel->addItem("Band7"); + ui->txChannel->addItem("Band38"); + ui->txChannel->setCurrentIndex((int) m_settings.m_txCellularChannel); + ui->txPort->addItem("TX/RX (J3)"); + m_settings.m_txPort = LimeRFESettings::TxPortJ3; + ui->txPort->setEnabled(false); + ui->txPort->setCurrentIndex((int) m_settings.m_txPort); + } + + ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); + m_currentPowerCorrection = getPowerCorrection(); + ui->powerCorrValue->setText(QString::number(m_currentPowerCorrection, 'f', 1)); + updateAbsPower(m_currentPowerCorrection); + + ui->powerCorrValue->blockSignals(false); + ui->txPort->blockSignals(false); + ui->txChannel->blockSignals(false); +} + +int LimeRFEGUI::getPowerCorectionIndex() +{ + LimeRFEUSBCalib::ChannelRange range; + + switch (m_settings.m_txChannels) + { + case LimeRFESettings::ChannelsWideband: + { + switch (m_settings.m_txWidebandChannel) + { + case LimeRFESettings::WidebandLow: + range = LimeRFEUSBCalib::WidebandLow; + break; + case LimeRFESettings::WidebandHigh: + range = LimeRFEUSBCalib::WidebandHigh; + break; + default: + return -1; + break; + } + break; + } + case LimeRFESettings::ChannelsHAM: + { + switch (m_settings.m_txHAMChannel) + { + case LimeRFESettings::HAM_30M: + range = LimeRFEUSBCalib::HAM_30MHz; + break; + case LimeRFESettings::HAM_50_70MHz: + range = LimeRFEUSBCalib::HAM_50_70MHz; + break; + case LimeRFESettings::HAM_144_146MHz: + range = LimeRFEUSBCalib::HAM_144_146MHz; + break; + case LimeRFESettings::HAM_220_225MHz: + range = LimeRFEUSBCalib::HAM_220_225MHz; + break; + case LimeRFESettings::HAM_430_440MHz: + range = LimeRFEUSBCalib::HAM_430_440MHz; + break; + case LimeRFESettings::HAM_902_928MHz: + range = LimeRFEUSBCalib::HAM_902_928MHz; + break; + case LimeRFESettings::HAM_1240_1325MHz: + range = LimeRFEUSBCalib::HAM_1240_1325MHz; + break; + case LimeRFESettings::HAM_2300_2450MHz: + range = LimeRFEUSBCalib::HAM_2300_2450MHz; + break; + case LimeRFESettings::HAM_3300_3500MHz: + range = LimeRFEUSBCalib::HAM_3300_3500MHz; + break; + default: + return -1; + break; + } + break; + } + case LimeRFESettings::ChannelsCellular: + { + switch (m_settings.m_txCellularChannel) + { + case LimeRFESettings::CellularBand1: + range = LimeRFEUSBCalib::CellularBand1; + break; + case LimeRFESettings::CellularBand2: + range = LimeRFEUSBCalib::CellularBand2; + break; + case LimeRFESettings::CellularBand3: + range = LimeRFEUSBCalib::CellularBand3; + break; + case LimeRFESettings::CellularBand7: + range = LimeRFEUSBCalib::CellularBand7; + break; + case LimeRFESettings::CellularBand38: + range = LimeRFEUSBCalib::CellularBand38; + break; + default: + return -1; + break; + } + break; + } + default: + return -1; + break; + } + + return (int) range; +} + +double LimeRFEGUI::getPowerCorrection() +{ + int index = getPowerCorectionIndex(); + + QMap::const_iterator it = m_limeRFEUSBCalib->m_calibrations.find(index); + + if (it != m_limeRFEUSBCalib->m_calibrations.end()) { + return it.value(); + } else { + return 0.0; + } +} + +void LimeRFEGUI::setPowerCorrection(double dbValue) +{ + int index = getPowerCorectionIndex(); + + if (index < 0) { + return; + } + + m_limeRFEUSBCalib->m_calibrations[index] = dbValue; +} + +void LimeRFEGUI::updateAbsPower(double powerCorrDB) +{ + bool ok; + double power = ui->powerFwdText->text().toDouble(&ok); + + if (ok) + { + double powerCorrected = power + powerCorrDB; + double powerDisplayed = powerCorrected; + + if (m_avgPower) + { + m_powerMovingAverage(powerCorrected); + powerDisplayed = m_powerMovingAverage.asDouble(); + } + + ui->powerAbsDbText->setText(tr("%1 dBm").arg(QString::number(powerDisplayed, 'f', 1))); + double powerWatts = CalcDb::powerFromdB(powerDisplayed - 30.0); + powerWatts = powerWatts > 8.0 ? 8.0 : powerWatts; + ui->powerAbsWText->setText(tr("%1 W").arg(QString::number(powerWatts, 'f', 3))); + } +} + +void LimeRFEGUI::updateDeviceSetList() +{ + MainCore *mainCore = MainCore::instance(); + std::vector& deviceSets = mainCore->getDeviceSets(); + std::vector::const_iterator it = deviceSets.begin(); + + ui->deviceSetRx->blockSignals(true); + ui->deviceSetTx->blockSignals(true); + + // Save current positions + int rxIndex = ui->deviceSetRx->currentIndex(); + int txIndex = ui->deviceSetTx->currentIndex(); + + ui->deviceSetRx->clear(); + ui->deviceSetTx->clear(); + unsigned int deviceSetIndex = 0; + + for (; it != deviceSets.end(); ++it, deviceSetIndex++) + { + DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; + DSPDeviceSinkEngine *deviceSinkEngine = (*it)->m_deviceSinkEngine; + + if (deviceSourceEngine) { + ui->deviceSetRx->addItem(QString("R:%1").arg(deviceSetIndex), deviceSetIndex); + } else if (deviceSinkEngine) { + ui->deviceSetTx->addItem(QString("T:%1").arg(deviceSetIndex), deviceSetIndex); + } + } + + // Restore current positions (if possible) + ui->deviceSetRx->setCurrentIndex(rxIndex < 0 ? 0 : rxIndex); + ui->deviceSetTx->setCurrentIndex(txIndex < 0 ? 0 : txIndex); + + ui->deviceSetRx->blockSignals(false); + ui->deviceSetTx->blockSignals(false); +} + +void LimeRFEGUI::highlightApplyButton(bool highlight) +{ + if (highlight) { + ui->apply->setStyleSheet("QPushButton { background-color : green; }"); + } else { + ui->apply->setStyleSheet("QPushButton { background:rgb(64, 64, 64); }"); + } +} + +void LimeRFEGUI::on_openDevice_clicked() +{ + int rc = m_limeRFE->openDevice(ui->device->currentText().toStdString()); + ui->statusText->append(QString("Open %1: %2").arg(ui->device->currentText()).arg(m_limeRFE->getError(rc).c_str())); + + if (rc != 0) { + return; + } + + rc = m_limeRFE->getState(); + ui->statusText->append(QString("Get state: %1").arg(m_limeRFE->getError(rc).c_str())); +} + +void LimeRFEGUI::on_closeDevice_clicked() +{ + ui->statusText->clear(); + m_limeRFE->closeDevice(); + ui->statusText->setText("Closed"); +} + +void LimeRFEGUI::on_deviceToGUI_clicked() +{ + int rc = m_limeRFE->getState(); + + if (rc != 0) + { + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + return; + } + + m_limeRFE->stateToSettings(m_settings); + displaySettings(); + highlightApplyButton(false); +} + +void LimeRFEGUI::on_rxChannelGroup_currentIndexChanged(int index) +{ + m_settings.m_rxChannels = (LimeRFESettings::ChannelGroups) index; + setRxChannels(); + + if (m_settings.m_txRxDriven) + { + m_settings.m_txChannels = m_settings.m_rxChannels; + ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); + } + + highlightApplyButton(true); +} + +void LimeRFEGUI::on_rxChannel_currentIndexChanged(int index) +{ + if (m_settings.m_rxChannels == LimeRFESettings::ChannelsWideband) { + m_settings.m_rxWidebandChannel = (LimeRFESettings::WidebandChannel) index; + } else if (m_settings.m_rxChannels == LimeRFESettings::ChannelsHAM) { + m_settings.m_rxHAMChannel = (LimeRFESettings::HAMChannel) index; + } else if (m_settings.m_rxChannels == LimeRFESettings::ChannelsCellular) { + m_settings.m_rxCellularChannel = (LimeRFESettings::CellularChannel) index; + } + + setRxChannels(); + + if (m_settings.m_txRxDriven) + { + m_settings.m_txWidebandChannel = m_settings.m_rxWidebandChannel; + m_settings.m_txHAMChannel = m_settings.m_rxHAMChannel; + m_settings.m_txCellularChannel = m_settings.m_rxCellularChannel; + setTxChannels(); + } + + highlightApplyButton(true); +} + +void LimeRFEGUI::on_rxPort_currentIndexChanged(int index) +{ + m_settings.m_rxPort = (LimeRFESettings::RxPort) index; + highlightApplyButton(true); +} + +void LimeRFEGUI::on_txFollowsRx_clicked() +{ + bool checked = ui->txFollowsRx->isChecked(); + m_settings.m_txRxDriven = checked; + ui->txChannelGroup->setEnabled(!checked); + ui->txChannel->setEnabled(!checked); + m_settings.m_txChannels = m_settings.m_rxChannels; + m_settings.m_txWidebandChannel = m_settings.m_rxWidebandChannel; + m_settings.m_txHAMChannel = m_settings.m_rxHAMChannel; + m_settings.m_txCellularChannel = m_settings.m_rxCellularChannel; + ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); + + if (checked) { + highlightApplyButton(true); + } +} + +void LimeRFEGUI::on_txChannelGroup_currentIndexChanged(int index) +{ + m_settings.m_txChannels = (LimeRFESettings::ChannelGroups) index; + setTxChannels(); + highlightApplyButton(true); +} + +void LimeRFEGUI::on_txChannel_currentIndexChanged(int index) +{ + if (m_settings.m_txChannels == LimeRFESettings::ChannelsWideband) { + m_settings.m_txWidebandChannel = (LimeRFESettings::WidebandChannel) index; + } else if (m_settings.m_txChannels == LimeRFESettings::ChannelsHAM) { + m_settings.m_txHAMChannel = (LimeRFESettings::HAMChannel) index; + } else if (m_settings.m_txChannels == LimeRFESettings::ChannelsCellular) { + m_settings.m_txCellularChannel = (LimeRFESettings::CellularChannel) index; + } + + setTxChannels(); + highlightApplyButton(true); +} + +void LimeRFEGUI::on_txPort_currentIndexChanged(int index) +{ + m_settings.m_txPort = (LimeRFESettings::TxPort) index; + highlightApplyButton(true); +} + +void LimeRFEGUI::on_powerEnable_clicked() +{ + m_settings.m_swrEnable = ui->powerEnable->isChecked(); + highlightApplyButton(true); +} + +void LimeRFEGUI::on_powerSource_currentIndexChanged(int index) +{ + m_settings.m_swrSource = (LimeRFESettings::SWRSource) index; + highlightApplyButton(true); +} + +void LimeRFEGUI::on_powerRefresh_clicked() +{ + refreshPower(); +} + +void LimeRFEGUI::on_powerAutoRefresh_toggled(bool checked) +{ + if (checked) + { + connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); + m_timer.start(); + } + else + { + m_timer.stop(); + disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); + } +} + +void LimeRFEGUI::on_powerAbsAvg_clicked() +{ + m_avgPower = ui->powerAbsAvg->isChecked(); +} + +void LimeRFEGUI::on_powerCorrValue_textEdited(const QString &text) +{ + bool ok; + double powerCorrection = text.toDouble(&ok); + + if (ok) + { + setPowerCorrection(powerCorrection); + m_currentPowerCorrection = powerCorrection; + updateAbsPower(powerCorrection); + } +} + +void LimeRFEGUI::on_deviceSetRefresh_clicked() +{ + updateDeviceSetList(); +} + +void LimeRFEGUI::on_deviceSetSync_clicked() +{ + m_deviceSetSync = ui->deviceSetSync->isChecked(); + + if (m_deviceSetSync) { + syncRxTx(); + } +} + +void LimeRFEGUI::syncRxTx() +{ + if (!m_settings.m_txOn) { + stopStartTx(m_settings.m_txOn); + } + + stopStartRx(m_settings.m_rxOn); + + if (m_settings.m_txOn) { + stopStartTx(m_settings.m_txOn); + } +} + +void LimeRFEGUI::stopStartRx(bool start) +{ + if (ui->deviceSetRx->currentIndex() < 0) { + return; + } + + int deviceSetIndex = ui->deviceSetRx->currentData().toInt(); + m_limeRFE->turnDevice(deviceSetIndex, start); +} + +void LimeRFEGUI::stopStartTx(bool start) +{ + if (ui->deviceSetTx->currentIndex() < 0) { + return; + } + + int deviceSetIndex = ui->deviceSetTx->currentData().toInt(); + m_limeRFE->turnDevice(deviceSetIndex, start); +} + +void LimeRFEGUI::on_modeRx_toggled(bool checked) +{ + int rc; + ui->statusText->clear(); + m_settings.m_rxOn = checked; + + if (m_rxTxToggle) + { + m_settings.m_txOn = !checked; + + if (checked) // Rx on + { + rc = m_limeRFE->setTx(m_settings, false); // stop Tx first + ui->statusText->append(QString("Stop TX: %1").arg(m_limeRFE->getError(rc).c_str())); + } + + rc = m_limeRFE->setRx(m_settings, m_settings.m_rxOn); // Rx on or off + ui->statusText->append(QString("RX: %1").arg(m_limeRFE->getError(rc).c_str())); + + if (!checked) // Rx off + { + rc = m_limeRFE->setTx(m_settings, true); // start Tx next + ui->statusText->append(QString("Start TX: %1").arg(m_limeRFE->getError(rc).c_str())); + } + } + else + { + rc = m_limeRFE->setRx(m_settings, m_settings.m_rxOn); + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + } + + if (m_deviceSetSync) { + syncRxTx(); + } + + displayMode(); +} + +void LimeRFEGUI::on_modeTx_toggled(bool checked) +{ + int rc; + ui->statusText->clear(); + m_settings.m_txOn = checked; + + if (m_rxTxToggle) + { + m_settings.m_rxOn = !checked; + + if (checked) // Tx on + { + rc = m_limeRFE->setRx(m_settings, false); // stop Rx first + ui->statusText->append(QString("Stop RX: %1").arg(m_limeRFE->getError(rc).c_str())); + } + + rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); // Tx on or off + ui->statusText->append(QString("TX: %1").arg(m_limeRFE->getError(rc).c_str())); + + if (!checked) // Tx off + { + rc = m_limeRFE->setRx(m_settings, true); // start Rx next + ui->statusText->append(QString("Start RX: %1").arg(m_limeRFE->getError(rc).c_str())); + } + } + else + { + rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + } + + if (m_deviceSetSync) { + syncRxTx(); + } + + displayMode(); +} + +void LimeRFEGUI::on_rxTxToggle_clicked() +{ + m_rxTxToggle = ui->rxTxToggle->isChecked(); + + if (m_rxTxToggle && m_settings.m_rxOn && m_settings.m_txOn) + { + m_settings.m_txOn = false; + int rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + displayMode(); + + if (m_deviceSetSync) { + syncRxTx(); + } + } +} + +void LimeRFEGUI::on_attenuation_currentIndexChanged(int index) +{ + m_settings.m_attenuationFactor = index; + highlightApplyButton(true); +} + +void LimeRFEGUI::on_amFmNotchFilter_clicked() +{ + m_settings.m_amfmNotch = ui->amFmNotchFilter->isChecked(); + highlightApplyButton(true); +} + +void LimeRFEGUI::on_apply_clicked() +{ + ui->statusText->clear(); + m_limeRFE->settingsToState(m_settings); + int rc = m_limeRFE->configure(); + ui->statusText->setText(m_limeRFE->getError(rc).c_str()); + highlightApplyButton(false); +} + +void LimeRFEGUI::tick() +{ + refreshPower(); +} + +void LimeRFEGUI::makeUIConnections() +{ + QObject::connect(ui->openDevice, &QPushButton::clicked, this, &LimeRFEGUI::on_openDevice_clicked); + QObject::connect(ui->closeDevice, &QPushButton::clicked, this, &LimeRFEGUI::on_closeDevice_clicked); + QObject::connect(ui->deviceToGUI, &QPushButton::clicked, this, &LimeRFEGUI::on_deviceToGUI_clicked); + QObject::connect(ui->rxChannelGroup, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_rxChannelGroup_currentIndexChanged); + QObject::connect(ui->rxChannel, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_rxChannel_currentIndexChanged); + QObject::connect(ui->rxPort, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_rxPort_currentIndexChanged); + QObject::connect(ui->attenuation, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_attenuation_currentIndexChanged); + QObject::connect(ui->amFmNotchFilter, &QCheckBox::clicked, this, &LimeRFEGUI::on_amFmNotchFilter_clicked); + QObject::connect(ui->txFollowsRx, &QCheckBox::clicked, this, &LimeRFEGUI::on_txFollowsRx_clicked); + QObject::connect(ui->txChannelGroup, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_txChannelGroup_currentIndexChanged); + QObject::connect(ui->txChannel, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_txChannel_currentIndexChanged); + QObject::connect(ui->txPort, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_txPort_currentIndexChanged); + QObject::connect(ui->powerEnable, &QCheckBox::clicked, this, &LimeRFEGUI::on_powerEnable_clicked); + QObject::connect(ui->powerSource, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeRFEGUI::on_powerSource_currentIndexChanged); + QObject::connect(ui->powerRefresh, &QPushButton::clicked, this, &LimeRFEGUI::on_powerRefresh_clicked); + QObject::connect(ui->powerAutoRefresh, &ButtonSwitch::toggled, this, &LimeRFEGUI::on_powerAutoRefresh_toggled); + QObject::connect(ui->powerAbsAvg, &QCheckBox::clicked, this, &LimeRFEGUI::on_powerAbsAvg_clicked); + QObject::connect(ui->powerCorrValue, &QLineEdit::textEdited, this, &LimeRFEGUI::on_powerCorrValue_textEdited); + QObject::connect(ui->modeRx, &QToolButton::toggled, this, &LimeRFEGUI::on_modeRx_toggled); + QObject::connect(ui->modeTx, &QToolButton::toggled, this, &LimeRFEGUI::on_modeTx_toggled); + QObject::connect(ui->rxTxToggle, &QCheckBox::clicked, this, &LimeRFEGUI::on_rxTxToggle_clicked); + QObject::connect(ui->deviceSetRefresh, &QPushButton::clicked, this, &LimeRFEGUI::on_deviceSetRefresh_clicked); + QObject::connect(ui->deviceSetSync, &QCheckBox::clicked, this, &LimeRFEGUI::on_deviceSetSync_clicked); + QObject::connect(ui->apply, &QPushButton::clicked, this, &LimeRFEGUI::on_apply_clicked); +} diff --git a/plugins/feature/limerfe/limerfegui.h b/plugins/feature/limerfe/limerfegui.h new file mode 100644 index 000000000..6646ce788 --- /dev/null +++ b/plugins/feature/limerfe/limerfegui.h @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_LIMERFEGUI_H_ +#define INCLUDE_FEATURE_LIMERFEGUI_H_ + +#include + +#include "feature/featuregui.h" +#include "util/messagequeue.h" +#include "util/movingaverage.h" +#include "settings/rollupstate.h" + +#include "limerfesettings.h" + +class PluginAPI; +class FeatureUISet; +class Feature; +class LimeRFE; +class LimeRFEUSBCalib; +class DSPDeviceSourceEngine; +class DSPDeviceSinkEngine; + +namespace Ui { + class LimeRFEGUI; +} + +class LimeRFEGUI : public FeatureGUI +{ + Q_OBJECT +public: + static LimeRFEGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index); + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + +protected: + void resizeEvent(QResizeEvent* size); + +private: + Ui::LimeRFEGUI* ui; + PluginAPI* m_pluginAPI; + FeatureUISet* m_featureUISet; + LimeRFESettings m_settings; + LimeRFEUSBCalib* m_limeRFEUSBCalib; + RollupState m_rollupState; + bool m_doApplySettings; + bool m_rxTxToggle; + QTimer m_timer; + double m_currentPowerCorrection; + bool m_avgPower; + MovingAverageUtil m_powerMovingAverage; + bool m_deviceSetSync; + std::vector m_sourceEngines; + std::vector m_rxDeviceSetIndex; + std::vector m_sinkEngines; + std::vector m_txDeviceSetIndex; + + LimeRFE* m_limeRFE; + MessageQueue m_inputMessageQueue; + + explicit LimeRFEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); + virtual ~LimeRFEGUI(); + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void applySettings(bool force = false); + void displaySettings(); + void displayMode(); + void displayPower(); + void refreshPower(); + void setRxChannels(); + void setTxChannels(); + int getPowerCorectionIndex(); + double getPowerCorrection(); + void setPowerCorrection(double dbValue); + void updateAbsPower(double powerCorrDB); + void updateDeviceSetList(); + void stopStartRx(bool start); + void stopStartTx(bool start); + void syncRxTx(); + void highlightApplyButton(bool highlight); + void makeUIConnections(); + +private slots: + void onMenuDialogCalled(const QPoint &p); + void onWidgetRolled(QWidget* widget, bool rollDown); + void on_openDevice_clicked(); + void on_closeDevice_clicked(); + void on_deviceToGUI_clicked(); + void on_rxChannelGroup_currentIndexChanged(int index); + void on_rxChannel_currentIndexChanged(int index); + void on_rxPort_currentIndexChanged(int index); + void on_attenuation_currentIndexChanged(int index); + void on_amFmNotchFilter_clicked(); + void on_txFollowsRx_clicked(); + void on_txChannelGroup_currentIndexChanged(int index); + void on_txChannel_currentIndexChanged(int index); + void on_txPort_currentIndexChanged(int index); + void on_powerEnable_clicked(); + void on_powerSource_currentIndexChanged(int index); + void on_powerRefresh_clicked(); + void on_powerAutoRefresh_toggled(bool checked); + void on_powerAbsAvg_clicked(); + void on_powerCorrValue_textEdited(const QString &text); + void on_modeRx_toggled(bool checked); + void on_modeTx_toggled(bool checked); + void on_rxTxToggle_clicked(); + void on_deviceSetRefresh_clicked(); + void on_deviceSetSync_clicked(); + void on_apply_clicked(); + void tick(); +}; + +#endif diff --git a/plugins/feature/limerfe/limerfegui.ui b/plugins/feature/limerfe/limerfegui.ui new file mode 100644 index 000000000..13574f3e3 --- /dev/null +++ b/plugins/feature/limerfe/limerfegui.ui @@ -0,0 +1,980 @@ + + + LimeRFEGUI + + + + 0 + 0 + 392 + 667 + + + + + 0 + 0 + + + + + 390 + 667 + + + + + 560 + 667 + + + + + Liberation Sans + 9 + + + + LimeRFE USB controller + + + + + 0 + 0 + 391 + 372 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Dev + + + + + + + + 220 + 0 + + + + Device + + + + + + + + 60 + 16777215 + + + + Open device + + + Open + + + + + + + + 60 + 16777215 + + + + Close Device + + + Close + + + + + + + + + + + Transfer data to GUI + + + to GUI + + + + + + + Apply changes + + + Apply + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Rx channel + + + + + + + + + + + + 120 + 0 + + + + Rx channel group + + + + Wideband + + + + + HAM + + + + + Cellular + + + + + + + + + 200 + 0 + + + + Rx channel range + + + + + + + + + + + Rx port + + + + + + + + 99 + 16777215 + + + + Rx port + + + + Tx/Rx (J3) + + + + + Tx/Rx HF (J5) + + + + + + + + Att + + + + + + + Rx attenuation + + + + 0 + + + + + 2 + + + + + 4 + + + + + 6 + + + + + 8 + + + + + 10 + + + + + 12 + + + + + 14 + + + + + + + + dB + + + + + + + AM/FM notch filter + + + Notch + + + + + + + + + Qt::Horizontal + + + + + + + + + Tx channel + + + + + + + Channel same as Rx + + + Same as Rx + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 120 + 0 + + + + Rx channel group + + + + Wideband + + + + + HAM + + + + + Cellular + + + + + + + + + 200 + 0 + + + + Rx channel range + + + + + + + + + + + + 47 + 0 + + + + Tx port + + + + + + + Tx port + + + + Tx/Rx (J3) + + + + + Tx (J4) + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 373 + 391 + 121 + + + + Power + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Enable power measurements + + + Pwr + + + + + + + Refresh power + + + + + + + :/recycle.png:/recycle.png + + + + + + + Power measurement source (EXTernal, CELlular) + + + + EXT + + + + + CEL + + + + + + + + Auto refresh power + + + + + + + :/play.png:/play.png + + + true + + + + + + + Corr + + + + + + + + 60 + 16777215 + + + + Qt::ClickFocus + + + Power correction in dBm + + + -00.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 10 + + + + + Fwd + + + + + + + Relative forward power in dB + + + 00.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Ref + + + + + + + Relative reflected power in dB + + + 00.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + RL + + + + + + + Return loss in dB + + + 00.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + VSWR + + + + + + + Voltage Standing Wave Ratio + + + 1.000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Abs power + + + + + + + Corrected forward power in dBm + + + -00.0 dBm + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Corrected forward power in Watts + + + 0.000 W + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Corrected power averaging + + + Avg + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 495 + 391 + 171 + + + + Control + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + Mode + + + + + + + + 110 + 0 + + + + Rx/Tx state + + + None + + + + + + + DeviceSet synchronization + + + Rx/Tx Sync + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Switch Rx + + + RX + + + true + + + + + + + Switch Tx + + + TX + + + true + + + + + + + Rx/Tx toggle + + + Toggle + + + + + + + Rx + + + + + + + Index of Rx DeviceSet + + + + + + + Tx + + + + + + + Index of Tx DeviceSet + + + + + + + Refresh DeviceSet indexes + + + + + + + :/recycle.png:/recycle.png + + + + + + + + + + + Messages + + + + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+
+ + + + +
diff --git a/plugins/feature/limerfe/limerfeplugin.cpp b/plugins/feature/limerfe/limerfeplugin.cpp new file mode 100644 index 000000000..491011da3 --- /dev/null +++ b/plugins/feature/limerfe/limerfeplugin.cpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "limerfegui.h" +#endif +#include "limerfe.h" +#include "limerfeplugin.h" +// #include "simplepttwebapiadapter.h" + +const PluginDescriptor LimeRFEPlugin::m_pluginDescriptor = { + LimeRFE::m_featureId, + QStringLiteral("LimeRFE USB Controller"), + QStringLiteral("7.1.0"), + QStringLiteral("(c) Edouard Griffiths, F4EXB"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +LimeRFEPlugin::LimeRFEPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(nullptr) +{ +} + +const PluginDescriptor& LimeRFEPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void LimeRFEPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register Simple PTT feature + m_pluginAPI->registerFeature(LimeRFE::m_featureIdURI, LimeRFE::m_featureId, this); +} + +#ifdef SERVER_MODE +FeatureGUI* LimeRFEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + (void) featureUISet; + (void) feature; + return nullptr; +} +#else +FeatureGUI* LimeRFEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + return LimeRFEGUI::create(m_pluginAPI, featureUISet, feature); +} +#endif + +Feature* LimeRFEPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const +{ + return new LimeRFE(webAPIAdapterInterface); +} + +FeatureWebAPIAdapter* LimeRFEPlugin::createFeatureWebAPIAdapter() const +{ + return nullptr; // TODO new SimplePTTWebAPIAdapter(); +} diff --git a/plugins/feature/limerfe/limerfeplugin.h b/plugins/feature/limerfe/limerfeplugin.h new file mode 100644 index 000000000..53aca7c26 --- /dev/null +++ b/plugins/feature/limerfe/limerfeplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_LIMERFEPLUGIN_H +#define INCLUDE_FEATURE_LIMERFEPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class FeatureGUI; +class WebAPIAdapterInterface; + +class LimeRFEPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.feature.limerfe") + +public: + explicit LimeRFEPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const; + virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const; + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_FEATURE_SIMPLEPTTPLUGIN_H diff --git a/plugins/feature/limerfe/limerfesettings.cpp b/plugins/feature/limerfe/limerfesettings.cpp new file mode 100644 index 000000000..e164cc96d --- /dev/null +++ b/plugins/feature/limerfe/limerfesettings.cpp @@ -0,0 +1,185 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" + +#include "limerfesettings.h" + +LimeRFESettings::LimeRFESettings() : + m_rollupState(nullptr) +{ + resetToDefaults(); +} + +void LimeRFESettings::resetToDefaults() +{ + m_devicePath = ""; + m_title = "Lime RFE"; + m_rgbColor = QColor(50, 205, 50).rgb(); + m_rxChannels = ChannelsWideband; + m_rxWidebandChannel = WidebandLow; + m_rxHAMChannel = HAM_144_146MHz; + m_rxCellularChannel = CellularBand38; + m_rxPort = RxPortJ3; + m_amfmNotch = false; + m_attenuationFactor = 0; + m_txChannels = ChannelsWideband; + m_txWidebandChannel = WidebandLow; + m_txHAMChannel = HAM_144_146MHz; + m_txCellularChannel = CellularBand38; + m_txPort = TxPortJ3; + m_swrEnable = false; + m_swrSource = SWRExternal; + m_txRxDriven = false; + m_rxOn = false; + m_txOn = false; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIFeatureSetIndex = 0; + m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; +} + +QByteArray LimeRFESettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, (int) m_rxChannels); + s.writeS32(2, (int) m_rxWidebandChannel); + s.writeS32(3, (int) m_rxHAMChannel); + s.writeS32(4, (int) m_rxCellularChannel); + s.writeS32(5, (int) m_rxPort); + s.writeBool(6, m_amfmNotch); + s.writeU32(7, m_attenuationFactor); + + s.writeS32(10, (int) m_txChannels); + s.writeS32(11, (int) m_txWidebandChannel); + s.writeS32(12, (int) m_txHAMChannel); + s.writeS32(13, (int) m_txCellularChannel); + s.writeS32(14, (int) m_txPort); + s.writeBool(15, m_swrEnable); + s.writeS32(16, (int) m_swrSource); + + s.writeBool(20, m_txRxDriven); + s.writeBool(21, m_rxOn); + s.writeBool(22, m_txOn); + + s.writeString(30, m_title); + s.writeU32(31, m_rgbColor); + s.writeBool(32, m_useReverseAPI); + s.writeString(33, m_reverseAPIAddress); + s.writeU32(34, m_reverseAPIPort); + s.writeU32(35, m_reverseAPIFeatureSetIndex); + s.writeU32(36, m_reverseAPIFeatureIndex); + + if (m_rollupState) { + s.writeBlob(37, m_rollupState->serialize()); + } + + s.writeS32(38, m_workspaceIndex); + s.writeBlob(39, m_geometryBytes); + s.writeString(40, m_devicePath); + + return s.final(); +} + +bool LimeRFESettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + uint32_t utmp; + int tmp; + + d.readS32(1, &tmp, (int) ChannelsWideband); + m_rxChannels = (ChannelGroups) tmp; + d.readS32(2, &tmp, (int) WidebandLow); + m_rxWidebandChannel = (WidebandChannel) tmp; + d.readS32(3, &tmp, (int) HAM_144_146MHz); + m_rxHAMChannel = (HAMChannel) tmp; + d.readS32(4, &tmp, (int) CellularBand38); + m_rxCellularChannel = (CellularChannel) tmp; + d.readS32(5, &tmp, (int) RxPortJ3); + m_rxPort = (RxPort) tmp; + d.readBool(6, &m_amfmNotch, false); + d.readU32(7, &m_attenuationFactor, 0); + + d.readS32(10, &tmp, (int) ChannelsWideband); + m_txChannels = (ChannelGroups) tmp; + d.readS32(11, &tmp, (int) WidebandLow); + m_txWidebandChannel = (WidebandChannel) tmp; + d.readS32(12, &tmp, (int) HAM_144_146MHz); + m_txHAMChannel = (HAMChannel) tmp; + d.readS32(13, &tmp, (int) CellularBand38); + m_txCellularChannel = (CellularChannel) tmp; + d.readS32(14, &tmp, (int) TxPortJ3); + m_txPort = (TxPort) tmp; + d.readBool(15, &m_swrEnable, false); + d.readS32(16, &tmp, (int) SWRExternal); + m_swrSource = (SWRSource) tmp; + + d.readBool(20, &m_txRxDriven, false); + d.readBool(21, &m_rxOn, false); + d.readBool(22, &m_txOn, false); + + d.readString(30, &m_title, "Lime RFE"); + d.readU32(31, &m_rgbColor, QColor(50, 205, 50).rgb()); + d.readBool(32, &m_useReverseAPI, false); + d.readString(33, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(34, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(35, &utmp, 0); + m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp; + d.readU32(36, &utmp, 0); + m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; + + if (m_rollupState) + { + d.readBlob(37, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + + d.readS32(38, &m_workspaceIndex, 0); + d.readBlob(39, &m_geometryBytes); + d.readString(40, &m_devicePath, ""); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/feature/limerfe/limerfesettings.h b/plugins/feature/limerfe/limerfesettings.h new file mode 100644 index 000000000..9e3a98180 --- /dev/null +++ b/plugins/feature/limerfe/limerfesettings.h @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_LIMERFESETTINGS_H_ +#define INCLUDE_FEATURE_LIMERFESETTINGS_H_ + +#include +#include + +class Serializable; + +struct LimeRFESettings +{ + enum ChannelGroups + { + ChannelsWideband, + ChannelsHAM, + ChannelsCellular + }; + + enum WidebandChannel + { + WidebandLow, //!< 1 - 1000 MHz + WidebandHigh //!< 1000 - 4000 MHz + }; + + enum HAMChannel + { + HAM_30M, + HAM_50_70MHz, + HAM_144_146MHz, + HAM_220_225MHz, + HAM_430_440MHz, + HAM_902_928MHz, + HAM_1240_1325MHz, + HAM_2300_2450MHz, + HAM_3300_3500MHz + }; + + enum CellularChannel + { + CellularBand1, + CellularBand2, + CellularBand3, + CellularBand7, + CellularBand38 + }; + + enum RxPort + { + RxPortJ3, //!< Rx/Tx + RxPortJ5 //!< Rx/Tx HF + }; + + enum TxPort + { + TxPortJ3, //!< Rx/Tx + TxPortJ4, //!< Tx + TxPortJ5 //!< Rx/Tx HF + }; + + enum SWRSource + { + SWRExternal, + SWRCellular + }; + + // Rx + ChannelGroups m_rxChannels; + WidebandChannel m_rxWidebandChannel; + HAMChannel m_rxHAMChannel; + CellularChannel m_rxCellularChannel; + RxPort m_rxPort; + unsigned int m_attenuationFactor; //!< Attenuation is 2 times this factor in dB (0..7 => 0..14dB) + bool m_amfmNotch; + // Tx + ChannelGroups m_txChannels; + WidebandChannel m_txWidebandChannel; + HAMChannel m_txHAMChannel; + CellularChannel m_txCellularChannel; + TxPort m_txPort; + bool m_swrEnable; + SWRSource m_swrSource; + // Rx/Tx + bool m_txRxDriven; //!< Tx settings set according to Rx settings + bool m_rxOn; + bool m_txOn; + // Common + QString m_devicePath; + QString m_title; + quint32 m_rgbColor; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIFeatureSetIndex; + uint16_t m_reverseAPIFeatureIndex; + Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + + LimeRFESettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } +}; + +#endif diff --git a/plugins/feature/limerfe/limerfeusbcalib.cpp b/plugins/feature/limerfe/limerfeusbcalib.cpp new file mode 100644 index 000000000..1502fe246 --- /dev/null +++ b/plugins/feature/limerfe/limerfeusbcalib.cpp @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "util/simpleserializer.h" + +#include "limerfeusbcalib.h" + +QByteArray LimeRFEUSBCalib::serialize() const +{ + SimpleSerializer s(1); + QByteArray data; + + serializeCalibMap(data); + s.writeBlob(1, data); + + return s.final(); +} + +bool LimeRFEUSBCalib::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) { + return false; + } + + if (d.getVersion() == 1) + { + QByteArray data; + + d.readBlob(1, &data); + deserializeCalibMap(data); + + return true; + } + else + { + return false; + } +} + +void LimeRFEUSBCalib::serializeCalibMap(QByteArray& data) const +{ + QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); + *stream << m_calibrations; + delete stream; +} + +void LimeRFEUSBCalib::deserializeCalibMap(QByteArray& data) +{ + QDataStream readStream(&data, QIODevice::ReadOnly); + readStream >> m_calibrations; +} diff --git a/plugins/feature/limerfe/limerfeusbcalib.h b/plugins/feature/limerfe/limerfeusbcalib.h new file mode 100644 index 000000000..80f419575 --- /dev/null +++ b/plugins/feature/limerfe/limerfeusbcalib.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ +#define SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ + +#include +#include "export.h" + +class QByteArray; + +class SDRBASE_API LimeRFEUSBCalib +{ +public: + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + enum ChannelRange + { + WidebandLow, //!< 1 - 1000 MHz + WidebandHigh, //!< 1000 - 4000 MHz + HAM_30MHz, //!< Up to 30 MHz + HAM_50_70MHz, + HAM_144_146MHz, + HAM_220_225MHz, + HAM_430_440MHz, + HAM_902_928MHz, + HAM_1240_1325MHz, + HAM_2300_2450MHz, + HAM_3300_3500MHz, + CellularBand1, + CellularBand2, + CellularBand3, + CellularBand7, + CellularBand38 + }; + + QMap m_calibrations; //!< Channel range to calibration value in floating point decibels + +private: + void serializeCalibMap(QByteArray& data) const; + void deserializeCalibMap(QByteArray& data); +}; + +#endif // SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ From 6d7ee189897bc8acb811dd20682653349019fa60 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 22 May 2022 11:59:02 +0200 Subject: [PATCH 03/29] LimeRFE feature: API updates --- plugins/feature/limerfe/limerfe.cpp | 404 +++++++++++++++++- plugins/feature/limerfe/limerfe.h | 68 +++ plugins/feature/limerfe/limerfegui.cpp | 27 ++ plugins/feature/limerfe/limerfegui.h | 2 + sdrbase/resources/webapi/doc/html2/index.html | 79 +++- .../doc/swagger/include/FeatureActions.yaml | 2 + .../doc/swagger/include/FeatureReport.yaml | 2 + .../doc/swagger/include/FeatureSettings.yaml | 2 + .../webapi/doc/swagger/include/LimeRFE.yaml | 65 ++- sdrbase/webapi/webapirequestmapper.cpp | 5 + sdrbase/webapi/webapiutils.cpp | 3 + .../api/swagger/include/FeatureActions.yaml | 2 + .../api/swagger/include/FeatureReport.yaml | 2 + .../api/swagger/include/FeatureSettings.yaml | 2 + .../sdrangel/api/swagger/include/LimeRFE.yaml | 65 ++- swagger/sdrangel/code/html2/index.html | 79 +++- .../code/qt5/client/SWGFeatureActions.cpp | 25 ++ .../code/qt5/client/SWGFeatureActions.h | 7 + .../code/qt5/client/SWGFeatureReport.cpp | 25 ++ .../code/qt5/client/SWGFeatureReport.h | 7 + .../code/qt5/client/SWGFeatureSettings.cpp | 25 ++ .../code/qt5/client/SWGFeatureSettings.h | 7 + .../code/qt5/client/SWGLimeRFEActions.cpp | 200 +++++++++ .../code/qt5/client/SWGLimeRFEActions.h | 82 ++++ .../code/qt5/client/SWGLimeRFEPower.h | 2 +- .../code/qt5/client/SWGLimeRFEReport.cpp | 131 ++++++ .../code/qt5/client/SWGLimeRFEReport.h | 64 +++ .../code/qt5/client/SWGLimeRFESettings.cpp | 213 +++++++++ .../code/qt5/client/SWGLimeRFESettings.h | 55 +++ .../code/qt5/client/SWGModelFactory.h | 12 + 30 files changed, 1646 insertions(+), 18 deletions(-) create mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h diff --git a/plugins/feature/limerfe/limerfe.cpp b/plugins/feature/limerfe/limerfe.cpp index 1c108f023..4a9c9127a 100644 --- a/plugins/feature/limerfe/limerfe.cpp +++ b/plugins/feature/limerfe/limerfe.cpp @@ -20,14 +20,20 @@ #include "SWGDeviceState.h" #include "SWGErrorResponse.h" +#include "SWGFeatureSettings.h" +#include "SWGFeatureReport.h" +#include "SWGFeatureActions.h" #include "util/simpleserializer.h" #include "util/serialutil.h" +#include "settings/serializable.h" #include "webapi/webapiadapterinterface.h" #include "limerfe.h" MESSAGE_CLASS_DEFINITION(LimeRFE::MsgConfigureLimeRFE, Message) +MESSAGE_CLASS_DEFINITION(LimeRFE::MsgReportSetRx, Message) +MESSAGE_CLASS_DEFINITION(LimeRFE::MsgReportSetTx, Message) const char* const LimeRFE::m_featureIdURI = "sdrangel.feature.limerfe"; const char* const LimeRFE::m_featureId = "LimeRFE"; @@ -99,9 +105,23 @@ void LimeRFE::listComPorts() } } +void LimeRFE::applySettings(const LimeRFESettings& settings, bool force) +{ + (void) force; + m_settings = settings; +} + bool LimeRFE::handleMessage(const Message& cmd) { - (void) cmd; + if (MsgConfigureLimeRFE::match(cmd)) + { + MsgConfigureLimeRFE& cfg = (MsgConfigureLimeRFE&) cmd; + qDebug() << "LimeRFE::handleMessage: MsgConfigureLimeRFE"; + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + return false; } @@ -257,15 +277,30 @@ int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) return -1; } - int mode = rxOn && settings.m_txOn ? - RFE_MODE_TXRX : rxOn ? - RFE_MODE_RX : settings.m_txOn ? - RFE_MODE_TX : RFE_MODE_NONE; + int mode = RFE_MODE_NONE; + if (rxOn) + { + if (settings.m_txOn) { + mode = RFE_MODE_TXRX; + } else { + mode = RFE_MODE_RX; + } + } + else + { + if (settings.m_txOn) { + mode = RFE_MODE_TX; + } + } + + qDebug("LimeRFE::setRx: switch %s mode: %d", rxOn ? "on" : "off", mode); int rc = RFE_Mode(m_rfeDevice, mode); if (rc == 0) { settings.m_rxOn = rxOn; + } else { + qInfo("LimeRFE::setRx: %s", getError(rc).c_str()); } return rc; @@ -277,15 +312,30 @@ int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) return -1; } - int mode = txOn && settings.m_rxOn ? - RFE_MODE_TXRX : txOn ? - RFE_MODE_TX : settings.m_rxOn ? - RFE_MODE_RX : RFE_MODE_NONE; + int mode = RFE_MODE_NONE; + if (txOn) + { + if (settings.m_rxOn) { + mode = RFE_MODE_TXRX; + } else { + mode = RFE_MODE_TX; + } + } + else + { + if (settings.m_rxOn) { + mode = RFE_MODE_RX; + } + } + + qDebug("LimeRFE::setTx: switch %s mode: %d", txOn ? "on" : "off", mode); int rc = RFE_Mode(m_rfeDevice, mode); if (rc == 0) { settings.m_txOn = txOn; + } else { + qInfo("LimeRFE::setTx: %s", getError(rc).c_str()); } return rc; @@ -714,3 +764,339 @@ void LimeRFE::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } + +int LimeRFE::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setLimeRfeSettings(new SWGSDRangel::SWGLimeRFESettings()); + response.getLimeRfeSettings()->init(); + webapiFormatFeatureSettings(response, m_settings); + return 200; +} + +int LimeRFE::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + LimeRFESettings settings = m_settings; + webapiUpdateFeatureSettings(settings, featureSettingsKeys, response); + + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(settings, force); + m_inputMessageQueue.push(msg); + + qDebug("LimeRFE::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureLimeRFE *msgToGUI = MsgConfigureLimeRFE::create(settings, true); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatFeatureSettings(response, settings); + + return 200; +} + +int LimeRFE::webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage) +{ + response.setLimeRfeReport(new SWGSDRangel::SWGLimeRFEReport()); + response.getLimeRfeReport()->init(); + return webapiFormatFeatureReport(response, errorMessage); +} + +int LimeRFE::webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage) +{ + SWGSDRangel::SWGLimeRFEActions *swgLimeRFEActions = query.getLimeRfeActions(); + + if (swgLimeRFEActions) + { + bool unknownAction = true; + int channel = -1; + int deviceSetIndex = -1; + + if (featureActionsKeys.contains("selectChannel")) + { + channel = swgLimeRFEActions->getSelectChannel(); + unknownAction = false; + } + + if (featureActionsKeys.contains("deviceSetIndex")) + { + deviceSetIndex = swgLimeRFEActions->getDeviceSetIndex(); + unknownAction = false; + } + + if (featureActionsKeys.contains("openCloseDevice") && (swgLimeRFEActions->getOpenCloseDevice() != 0)) + { + int rc = openDevice(m_settings.m_devicePath.toStdString()); + unknownAction = false; + + if (rc != 0) + { + errorMessage = QString("Open %1: %2").arg(m_settings.m_devicePath).arg(getError(rc).c_str()); + return 500; + } + } + + if (featureActionsKeys.contains("fromToSettings") && (swgLimeRFEActions->getFromToSettings() != 0)) + { + settingsToState(m_settings); + unknownAction = false; + } + + if ((channel >= 0) && featureActionsKeys.contains("switchChannel")) + { + if (channel == 0) + { + bool on = swgLimeRFEActions->getSwitchChannel() != 0; + int rc = setRx(m_settings, on); + + if (rc != 0) + { + errorMessage = QString("Set Rx %1 %2: %3").arg(m_settings.m_devicePath).arg(on).arg(getError(rc).c_str()); + return 500; + } + + if (getMessageQueueToGUI()) + { + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, false); + getMessageQueueToGUI()->push(msg); + } + } + else + { + bool on = swgLimeRFEActions->getSwitchChannel() != 0; + int rc = setTx(m_settings, swgLimeRFEActions->getSwitchChannel() != 0); + + if (rc != 0) + { + errorMessage = QString("Set Tx %1 %2: %3").arg(m_settings.m_devicePath).arg(on).arg(getError(rc).c_str()); + return 500; + } + + if (getMessageQueueToGUI()) + { + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, false); + getMessageQueueToGUI()->push(msg); + } + } + + if (deviceSetIndex >= 0) { + turnDevice(deviceSetIndex, swgLimeRFEActions->getSwitchChannel() != 0); + } + + unknownAction = false; + } + + if (featureActionsKeys.contains("fromToSettings") && (swgLimeRFEActions->getFromToSettings() == 0)) + { + stateToSettings(m_settings); + unknownAction = false; + + if (getMessageQueueToGUI()) + { + MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, false); + getMessageQueueToGUI()->push(msg); + } + } + + if (featureActionsKeys.contains("openCloseDevice") && (swgLimeRFEActions->getOpenCloseDevice() == 0)) + { + closeDevice(); + unknownAction = false; + } + + if (unknownAction) + { + errorMessage = "Unknown action"; + return 400; + } + else + { + return 202; + } + } + else + { + errorMessage = "Missing SimplePTTActions in query"; + return 400; + } +} + +void LimeRFE::webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const LimeRFESettings& settings) +{ + if (response.getLimeRfeSettings()->getTitle()) { + *response.getLimeRfeSettings()->getTitle() = settings.m_title; + } else { + response.getLimeRfeSettings()->setTitle(new QString(settings.m_title)); + } + + response.getLimeRfeSettings()->setRgbColor(settings.m_rgbColor); + response.getLimeRfeSettings()->setDevicePath(new QString(settings.m_devicePath)); + response.getLimeRfeSettings()->setRxChannels((int) settings.m_rxChannels); + response.getLimeRfeSettings()->setRxWidebandChannel((int) settings.m_rxWidebandChannel); + response.getLimeRfeSettings()->setRxHamChannel((int) settings.m_rxHAMChannel); + response.getLimeRfeSettings()->setRxCellularChannel((int) settings.m_rxCellularChannel); + response.getLimeRfeSettings()->setRxPort((int) settings.m_rxPort); + response.getLimeRfeSettings()->setRxOn(settings.m_rxOn ? 1 : 0); + response.getLimeRfeSettings()->setAmfmNotch(settings.m_amfmNotch ? 1 : 0); + response.getLimeRfeSettings()->setAttenuationFactor(settings.m_attenuationFactor); + response.getLimeRfeSettings()->setTxChannels((int) settings.m_txChannels); + response.getLimeRfeSettings()->setTxWidebandChannel((int) settings.m_txWidebandChannel); + response.getLimeRfeSettings()->setTxHamChannel((int) settings.m_txHAMChannel); + response.getLimeRfeSettings()->setTxCellularChannel((int) settings.m_txCellularChannel); + response.getLimeRfeSettings()->setTxPort((int) settings.m_txPort); + response.getLimeRfeSettings()->setTxOn(settings.m_txOn ? 1 : 0); + response.getLimeRfeSettings()->setSwrEnable(settings.m_swrEnable ? 1 : 0); + response.getLimeRfeSettings()->setSwrSource((int) settings.m_swrSource); + response.getLimeRfeSettings()->setTxRxDriven(settings.m_txRxDriven ? 1 : 0); + + response.getLimeRfeSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getLimeRfeSettings()->getReverseApiAddress()) { + *response.getLimeRfeSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getLimeRfeSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getLimeRfeSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getLimeRfeSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex); + response.getLimeRfeSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex); + + if (settings.m_rollupState) + { + if (response.getLimeRfeSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.getLimeRfeSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.getLimeRfeSettings()->setRollupState(swgRollupState); + } + } +} + +void LimeRFE::webapiUpdateFeatureSettings( + LimeRFESettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response) +{ + if (featureSettingsKeys.contains("title")) { + settings.m_title = *response.getLimeRfeSettings()->getTitle(); + } + if (featureSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getLimeRfeSettings()->getRgbColor(); + } + if (featureSettingsKeys.contains("devicePath")) { + settings.m_devicePath = *response.getLimeRfeSettings()->getDevicePath(); + } + if (featureSettingsKeys.contains("rxChannels")) { + settings.m_rxChannels = (LimeRFESettings::ChannelGroups) response.getLimeRfeSettings()->getRxChannels(); + } + if (featureSettingsKeys.contains("rxWidebandChannel")) { + settings.m_rxWidebandChannel = (LimeRFESettings::WidebandChannel) response.getLimeRfeSettings()->getRxWidebandChannel(); + } + if (featureSettingsKeys.contains("rxHAMChannel")) { + settings.m_rxHAMChannel = (LimeRFESettings::HAMChannel) response.getLimeRfeSettings()->getRxHamChannel(); + } + if (featureSettingsKeys.contains("rxCellularChannel")) { + settings.m_rxCellularChannel = (LimeRFESettings::CellularChannel) response.getLimeRfeSettings()->getRxCellularChannel(); + } + if (featureSettingsKeys.contains("rxPort")) { + settings.m_rxPort = (LimeRFESettings::RxPort) response.getLimeRfeSettings()->getRxPort(); + } + if (featureSettingsKeys.contains("rxOn")) { + settings.m_rxOn = response.getLimeRfeSettings()->getRxOn() != 0; + } + if (featureSettingsKeys.contains("amfmNotch")) { + settings.m_amfmNotch = response.getLimeRfeSettings()->getAmfmNotch() != 0; + } + if (featureSettingsKeys.contains("attenuationFactor")) { + settings.m_attenuationFactor = response.getLimeRfeSettings()->getAttenuationFactor(); + } + if (featureSettingsKeys.contains("txChannels")) { + settings.m_txChannels = (LimeRFESettings::ChannelGroups) response.getLimeRfeSettings()->getTxChannels(); + } + if (featureSettingsKeys.contains("txWidebandChannel")) { + settings.m_txWidebandChannel = (LimeRFESettings::WidebandChannel) response.getLimeRfeSettings()->getTxWidebandChannel(); + } + if (featureSettingsKeys.contains("txHAMChannel")) { + settings.m_txHAMChannel = (LimeRFESettings::HAMChannel) response.getLimeRfeSettings()->getTxHamChannel(); + } + if (featureSettingsKeys.contains("txCellularChannel")) { + settings.m_txCellularChannel = (LimeRFESettings::CellularChannel) response.getLimeRfeSettings()->getTxCellularChannel(); + } + if (featureSettingsKeys.contains("txPort")) { + settings.m_txPort = (LimeRFESettings::TxPort) response.getLimeRfeSettings()->getTxPort(); + } + if (featureSettingsKeys.contains("txOn")) { + settings.m_txOn = response.getLimeRfeSettings()->getTxOn() != 0; + } + if (featureSettingsKeys.contains("swrEnable")) { + settings.m_swrEnable = response.getLimeRfeSettings()->getSwrEnable() != 0; + } + if (featureSettingsKeys.contains("swrSource")) { + settings.m_swrSource = (LimeRFESettings::SWRSource) response.getLimeRfeSettings()->getSwrSource(); + } + if (featureSettingsKeys.contains("txRxDriven")) { + settings.m_txRxDriven = response.getLimeRfeSettings()->getTxRxDriven() != 0; + } + if (featureSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getLimeRfeSettings()->getUseReverseApi() != 0; + } + if (featureSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getLimeRfeSettings()->getReverseApiAddress(); + } + if (featureSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getLimeRfeSettings()->getReverseApiPort(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) { + settings.m_reverseAPIFeatureSetIndex = response.getLimeRfeSettings()->getReverseApiFeatureSetIndex(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) { + settings.m_reverseAPIFeatureIndex = response.getLimeRfeSettings()->getReverseApiFeatureIndex(); + } + if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(featureSettingsKeys, response.getLimeRfeSettings()->getRollupState()); + } +} + +int LimeRFE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response, QString& errorMessage) +{ + int fwdPower; + int rc = getFwdPower(fwdPower); + + if (rc != 0) + { + errorMessage = QString("Error getting forward power from LimeRFE device %1: %2") + .arg(m_settings.m_devicePath).arg(getError(rc).c_str()); + return 500; + } + + int refPower; + rc = getRefPower(refPower); + + if (rc != 0) + { + errorMessage = QString("Error getting reflected power from LimeRFE device %1: %2") + .arg(m_settings.m_devicePath).arg(getError(rc).c_str()); + return 500; + } + + response.getLimeRfeReport()->setForwardPower(fwdPower); + response.getLimeRfeReport()->setReflectedPower(refPower); + return 200; +} diff --git a/plugins/feature/limerfe/limerfe.h b/plugins/feature/limerfe/limerfe.h index e55c57956..e224fd87a 100644 --- a/plugins/feature/limerfe/limerfe.h +++ b/plugins/feature/limerfe/limerfe.h @@ -59,6 +59,44 @@ public: { } }; + class MsgReportSetRx : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool isOn() const { return m_on; } + + static MsgReportSetRx* create(bool on) { + return new MsgReportSetRx(on); + } + + private: + bool m_on; + + MsgReportSetRx(bool on) : + Message(), + m_on(on) + { } + }; + + class MsgReportSetTx : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool isOn() const { return m_on; } + + static MsgReportSetTx* create(bool on) { + return new MsgReportSetTx(on); + } + + private: + bool m_on; + + MsgReportSetTx(bool on) : + Message(), + m_on(on) + { } + }; + LimeRFE(WebAPIAdapterInterface *webAPIAdapterInterface); virtual ~LimeRFE(); virtual void destroy() { delete this; } @@ -71,6 +109,34 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage); + + virtual int webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage); + + static void webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const LimeRFESettings& settings); + + static void webapiUpdateFeatureSettings( + LimeRFESettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response); + LimeRFEUSBCalib *getCalib() { return &m_calib; } int openDevice(const std::string& serialDeviceName); @@ -108,6 +174,8 @@ private: void start(); void stop(); void listComPorts(); + void applySettings(const LimeRFESettings& settings, bool force = false); + int webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response, QString& errorMessage); private slots: void networkManagerFinished(QNetworkReply *reply); diff --git a/plugins/feature/limerfe/limerfegui.cpp b/plugins/feature/limerfe/limerfegui.cpp index 496fa88df..05c5fabe5 100644 --- a/plugins/feature/limerfe/limerfegui.cpp +++ b/plugins/feature/limerfe/limerfegui.cpp @@ -980,6 +980,33 @@ void LimeRFEGUI::tick() refreshPower(); } +bool LimeRFEGUI::handleMessage(const Message& message) +{ + if (LimeRFE::MsgConfigureLimeRFE::match(message)) + { + qDebug("LimeRFEGUI::handleMessage: LimeRFE::MsgConfigureLimeRFE"); + const LimeRFE::MsgConfigureLimeRFE& cfg = (LimeRFE::MsgConfigureLimeRFE&) message; + m_settings = cfg.getSettings(); + displaySettings(); + highlightApplyButton(cfg.getForce()); + return true; + } + + return false; +} + +void LimeRFEGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop())) + { + if (handleMessage(*message)) { + delete message; + } + } +} + void LimeRFEGUI::makeUIConnections() { QObject::connect(ui->openDevice, &QPushButton::clicked, this, &LimeRFEGUI::on_openDevice_clicked); diff --git a/plugins/feature/limerfe/limerfegui.h b/plugins/feature/limerfe/limerfegui.h index 6646ce788..1d6688aea 100644 --- a/plugins/feature/limerfe/limerfegui.h +++ b/plugins/feature/limerfe/limerfegui.h @@ -100,11 +100,13 @@ private: void stopStartTx(bool start); void syncRxTx(); void highlightApplyButton(bool highlight); + bool handleMessage(const Message& message); void makeUIConnections(); private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); + void handleInputMessages(); void on_openDevice_clicked(); void on_closeDevice_clicked(); void on_deviceToGUI_clicked(); diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index b9bec83ab..7e220ddf6 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -5273,6 +5273,9 @@ margin-bottom: 20px; "GS232ControllerActions" : { "$ref" : "#/definitions/GS232ControllerActions" }, + "LimeRFEActions" : { + "$ref" : "#/definitions/LimeRFEActions" + }, "MapActions" : { "$ref" : "#/definitions/MapActions" }, @@ -5409,6 +5412,9 @@ margin-bottom: 20px; "GS232ControllerReport" : { "$ref" : "#/definitions/GS232ControllerReport" }, + "LimeRFEReport" : { + "$ref" : "#/definitions/LimeRFEReport" + }, "MapReport" : { "$ref" : "#/definitions/MapReport" }, @@ -5504,6 +5510,9 @@ margin-bottom: 20px; "GS232ControllerSettings" : { "$ref" : "#/definitions/GS232ControllerSettings" }, + "LimeRFESettings" : { + "$ref" : "#/definitions/LimeRFESettings" + }, "MapSettings" : { "$ref" : "#/definitions/MapSettings" }, @@ -7069,6 +7078,30 @@ margin-bottom: 20px; } }, "description" : "KiwiSDR" +}; + defs.LimeRFEActions = { + "properties" : { + "selectChannel" : { + "type" : "integer", + "description" : "Select channel\n * 0 - Rx\n * 1 - Tx\n" + }, + "deviceSetIndex" : { + "type" : "integer" + }, + "switchChannel" : { + "type" : "integer", + "description" : "Switch on or off\n * 0 - Off\n * 1 - On\n" + }, + "fromToSettings" : { + "type" : "integer", + "description" : "Move from/to settings to/from device\n * 0 - From device to settings. The toGUI button in GUI mode\n * 1 - From settings to device. The Apply button in GUI mode\n" + }, + "openCloseDevice" : { + "type" : "integer", + "description" : "Open or close device\n * 0 - Close device\n * 1 - Open device\n" + } + }, + "description" : "LimeRFE" }; defs.LimeRFEDevice = { "properties" : { @@ -7107,10 +7140,29 @@ margin-bottom: 20px; "description" : "relative reflected power in centi-Bels" } }, - "description" : "report of forward and reflected power measurements" + "description" : "report of forward and reflected power measurements TO BE DECOMMISSIONED" +}; + defs.LimeRFEReport = { + "properties" : { + "forwardPower" : { + "type" : "integer", + "description" : "relative forward power in centi-Bels" + }, + "reflectedPower" : { + "type" : "integer", + "description" : "relative reflected power in centi-Bels" + } + }, + "description" : "LimeRFE" }; defs.LimeRFESettings = { "properties" : { + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, "devicePath" : { "type" : "string", "description" : "Path to the device serial interface (ex /dev/ttyUSB2)" @@ -7170,6 +7222,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "SWR measurement source (LimeRFEController::SWRSource)\n * 0 - External\n * 1 - Cellular\n" }, + "txRxDriven" : { + "type" : "integer", + "description" : "Boolean 1 if Tx is copy of Rx else 0" + }, "rxOn" : { "type" : "integer", "description" : "Boolean 1 if Rx is active else 0" @@ -7177,6 +7233,25 @@ margin-bottom: 20px; "txOn" : { "type" : "integer", "description" : "Boolean 1 if Tx is active else 0" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" } }, "description" : "LimeRFE" @@ -59698,7 +59773,7 @@ except ApiException as e:
- Generated 2022-05-19T00:27:23.053+02:00 + Generated 2022-05-21T22:11:42.796+02:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml index f1ee424cd..1df31dfb0 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml @@ -17,6 +17,8 @@ FeatureActions: $ref: "/doc/swagger/include/AFC.yaml#/AFCActions" GS232ControllerActions: $ref: "/doc/swagger/include/GS232Controller.yaml#/GS232ControllerActions" + LimeRFEActions: + $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFEActions" MapActions: $ref: "/doc/swagger/include/Map.yaml#/MapActions" PERTesterActions: diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml index 109b16b1f..ae6cd71ae 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml @@ -11,6 +11,8 @@ FeatureReport: $ref: "/doc/swagger/include/AFC.yaml#/AFCReport" GS232ControllerReport: $ref: "/doc/swagger/include/GS232Controller.yaml#/GS232ControllerReport" + LimeRFEReport: + $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFEReport" MapReport: $ref: "/doc/swagger/include/Map.yaml#/MapReport" PERTesterReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml index b30b75ca8..21245d2e5 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml @@ -27,6 +27,8 @@ FeatureSettings: $ref: "/doc/swagger/include/JogdialController.yaml#/JogdialControllerSettings" GS232ControllerSettings: $ref: "/doc/swagger/include/GS232Controller.yaml#/GS232ControllerSettings" + LimeRFESettings: + $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFESettings" MapSettings: $ref: "/doc/swagger/include/Map.yaml#/MapSettings" PERTesterSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml index 095a88c00..81c17aa87 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml @@ -1,6 +1,10 @@ LimeRFESettings: description: LimeRFE properties: + title: + type: string + rgbColor: + type: integer devicePath: description: Path to the device serial interface (ex /dev/ttyUSB2) type: string @@ -102,19 +106,76 @@ LimeRFESettings: SWR measurement source (LimeRFEController::SWRSource) * 0 - External * 1 - Cellular + txRxDriven: + description: Boolean 1 if Tx is copy of Rx else 0 + type: integer rxOn: description: Boolean 1 if Rx is active else 0 type: integer txOn: description: Boolean 1 if Tx is active else 0 type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +LimeRFEReport: + description: LimeRFE + properties: + forwardPower: + description: relative forward power in centi-Bels + type: integer + reflectedPower: + description: relative reflected power in centi-Bels + type: integer + +LimeRFEActions: + description: LimeRFE + properties: + selectChannel: + type: integer + description: > + Select channel + * 0 - Rx + * 1 - Tx + deviceSetIndex: + type: integer + dexcription: Index of device set to synchronize switch with + switchChannel: + type: integer + description: > + Switch on or off + * 0 - Off + * 1 - On + fromToSettings: + type: integer + description: > + Move from/to settings to/from device + * 0 - From device to settings. The toGUI button in GUI mode + * 1 - From settings to device. The Apply button in GUI mode + openCloseDevice: + type: integer + description: > + Open or close device + * 0 - Close device + * 1 - Open device LimeRFEPower: - description: report of forward and reflected power measurements + description: report of forward and reflected power measurements TO BE DECOMMISSIONED properties: forward: description: relative forward power in centi-Bels type: integer reflected: description: relative reflected power in centi-Bels - type: integer \ No newline at end of file + type: integer diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 449870e42..5df2a6e71 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -5602,6 +5602,11 @@ bool WebAPIRequestMapper::getFeatureActions( featureActions->setGs232ControllerActions(new SWGSDRangel::SWGGS232ControllerActions()); featureActions->getGs232ControllerActions()->fromJsonObject(actionsJsonObject); } + else if (featureActionsKey == "LimeRFEActions") + { + featureActions->setLimeRfeActions(new SWGSDRangel::SWGLimeRFEActions()); + featureActions->getLimeRfeActions()->fromJsonObject(actionsJsonObject); + } else if (featureActionsKey == "MapActions") { featureActions->setMapActions(new SWGSDRangel::SWGMapActions()); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 21398951c..1ffbb1209 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -275,6 +275,7 @@ const QMap WebAPIUtils::m_featureTypeToSettingsKey = { {"DemodAnalyzer", "DemodAnalyzerSettings"}, {"JogdialController", "JogdialControllerSettings"}, {"GS232Controller", "GS232ControllerSettings"}, // a.k.a Rotator Controller + {"LimeRFE", "LimeRFESettings"}, {"Map", "MapSettings"}, {"PERTester", "PERTesterSettings"}, {"Radiosonde", "RadiosondeSettings"}, @@ -288,6 +289,7 @@ const QMap WebAPIUtils::m_featureTypeToSettingsKey = { const QMap WebAPIUtils::m_featureTypeToActionsKey = { {"AFC", "AFCActions"}, {"GS232Controller", "GS232ControllerActions"}, + {"LimeRFE", "LimeRFEActions"}, {"Map", "MapActions"}, {"PERTester", "PERTesterActions"}, {"RigCtlServer", "RigCtlServerActions"}, @@ -305,6 +307,7 @@ const QMap WebAPIUtils::m_featureURIToSettingsKey = { {"sdrangel.feature.demodanalyzer", "DemodAnalyzerSettings"}, {"sdrangel.feature.jogdialcontroller", "JogdialControllerSettings"}, {"sdrangel.feature.gs232controller", "GS232ControllerSettings"}, + {"sdrangel.feature.limerfe", "LimeRFESettings"}, {"sdrangel.feature.map", "MapSettings"}, {"sdrangel.feature.pertester", "PERTesterSettings"}, {"sdrangel.feature.radiosonde", "RadiosondeSettings"}, diff --git a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml index 153399011..3a47ffe96 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml @@ -17,6 +17,8 @@ FeatureActions: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCActions" GS232ControllerActions: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerActions" + LimeRFEActions: + $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFEActions" MapActions: $ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapActions" PERTesterActions: diff --git a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml index 468022ad5..1264a7387 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml @@ -11,6 +11,8 @@ FeatureReport: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCReport" GS232ControllerReport: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerReport" + LimeRFEReport: + $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFEReport" MapReport: $ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapReport" PERTesterReport: diff --git a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml index da6a8cef2..0f0f0bf30 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml @@ -27,6 +27,8 @@ FeatureSettings: $ref: "http://swgserver:8081/api/swagger/include/JogdialController.yaml#/JogdialControllerSettings" GS232ControllerSettings: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerSettings" + LimeRFESettings: + $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFESettings" MapSettings: $ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapSettings" PERTesterSettings: diff --git a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml index 095a88c00..06fc4e70f 100644 --- a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml +++ b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml @@ -1,6 +1,10 @@ LimeRFESettings: description: LimeRFE properties: + title: + type: string + rgbColor: + type: integer devicePath: description: Path to the device serial interface (ex /dev/ttyUSB2) type: string @@ -102,19 +106,76 @@ LimeRFESettings: SWR measurement source (LimeRFEController::SWRSource) * 0 - External * 1 - Cellular + txRxDriven: + description: Boolean 1 if Tx is copy of Rx else 0 + type: integer rxOn: description: Boolean 1 if Rx is active else 0 type: integer txOn: description: Boolean 1 if Tx is active else 0 type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +LimeRFEReport: + description: LimeRFE + properties: + forwardPower: + description: relative forward power in centi-Bels + type: integer + reflectedPower: + description: relative reflected power in centi-Bels + type: integer + +LimeRFEActions: + description: LimeRFE + properties: + selectChannel: + type: integer + description: > + Select channel + * 0 - Rx + * 1 - Tx + deviceSetIndex: + type: integer + dexcription: Index of device set to synchronize switch with + switchChannel: + type: integer + description: > + Switch on or off + * 0 - Off + * 1 - On + fromToSettings: + type: integer + description: > + Move from/to settings to/from device + * 0 - From device to settings. The toGUI button in GUI mode + * 1 - From settings to device. The Apply button in GUI mode + openCloseDevice: + type: integer + description: > + Open or close device + * 0 - Close device + * 1 - Open device LimeRFEPower: - description: report of forward and reflected power measurements + description: report of forward and reflected power measurements TO BE DECOMMISSIONED properties: forward: description: relative forward power in centi-Bels type: integer reflected: description: relative reflected power in centi-Bels - type: integer \ No newline at end of file + type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index b9bec83ab..7e220ddf6 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -5273,6 +5273,9 @@ margin-bottom: 20px; "GS232ControllerActions" : { "$ref" : "#/definitions/GS232ControllerActions" }, + "LimeRFEActions" : { + "$ref" : "#/definitions/LimeRFEActions" + }, "MapActions" : { "$ref" : "#/definitions/MapActions" }, @@ -5409,6 +5412,9 @@ margin-bottom: 20px; "GS232ControllerReport" : { "$ref" : "#/definitions/GS232ControllerReport" }, + "LimeRFEReport" : { + "$ref" : "#/definitions/LimeRFEReport" + }, "MapReport" : { "$ref" : "#/definitions/MapReport" }, @@ -5504,6 +5510,9 @@ margin-bottom: 20px; "GS232ControllerSettings" : { "$ref" : "#/definitions/GS232ControllerSettings" }, + "LimeRFESettings" : { + "$ref" : "#/definitions/LimeRFESettings" + }, "MapSettings" : { "$ref" : "#/definitions/MapSettings" }, @@ -7069,6 +7078,30 @@ margin-bottom: 20px; } }, "description" : "KiwiSDR" +}; + defs.LimeRFEActions = { + "properties" : { + "selectChannel" : { + "type" : "integer", + "description" : "Select channel\n * 0 - Rx\n * 1 - Tx\n" + }, + "deviceSetIndex" : { + "type" : "integer" + }, + "switchChannel" : { + "type" : "integer", + "description" : "Switch on or off\n * 0 - Off\n * 1 - On\n" + }, + "fromToSettings" : { + "type" : "integer", + "description" : "Move from/to settings to/from device\n * 0 - From device to settings. The toGUI button in GUI mode\n * 1 - From settings to device. The Apply button in GUI mode\n" + }, + "openCloseDevice" : { + "type" : "integer", + "description" : "Open or close device\n * 0 - Close device\n * 1 - Open device\n" + } + }, + "description" : "LimeRFE" }; defs.LimeRFEDevice = { "properties" : { @@ -7107,10 +7140,29 @@ margin-bottom: 20px; "description" : "relative reflected power in centi-Bels" } }, - "description" : "report of forward and reflected power measurements" + "description" : "report of forward and reflected power measurements TO BE DECOMMISSIONED" +}; + defs.LimeRFEReport = { + "properties" : { + "forwardPower" : { + "type" : "integer", + "description" : "relative forward power in centi-Bels" + }, + "reflectedPower" : { + "type" : "integer", + "description" : "relative reflected power in centi-Bels" + } + }, + "description" : "LimeRFE" }; defs.LimeRFESettings = { "properties" : { + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, "devicePath" : { "type" : "string", "description" : "Path to the device serial interface (ex /dev/ttyUSB2)" @@ -7170,6 +7222,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "SWR measurement source (LimeRFEController::SWRSource)\n * 0 - External\n * 1 - Cellular\n" }, + "txRxDriven" : { + "type" : "integer", + "description" : "Boolean 1 if Tx is copy of Rx else 0" + }, "rxOn" : { "type" : "integer", "description" : "Boolean 1 if Rx is active else 0" @@ -7177,6 +7233,25 @@ margin-bottom: 20px; "txOn" : { "type" : "integer", "description" : "Boolean 1 if Tx is active else 0" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" } }, "description" : "LimeRFE" @@ -59698,7 +59773,7 @@ except ApiException as e:
- Generated 2022-05-19T00:27:23.053+02:00 + Generated 2022-05-21T22:11:42.796+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp index 7b2a0f520..ed766164f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp @@ -38,6 +38,8 @@ SWGFeatureActions::SWGFeatureActions() { m_afc_actions_isSet = false; gs232_controller_actions = nullptr; m_gs232_controller_actions_isSet = false; + lime_rfe_actions = nullptr; + m_lime_rfe_actions_isSet = false; map_actions = nullptr; m_map_actions_isSet = false; per_tester_actions = nullptr; @@ -70,6 +72,8 @@ SWGFeatureActions::init() { m_afc_actions_isSet = false; gs232_controller_actions = new SWGGS232ControllerActions(); m_gs232_controller_actions_isSet = false; + lime_rfe_actions = new SWGLimeRFEActions(); + m_lime_rfe_actions_isSet = false; map_actions = new SWGMapActions(); m_map_actions_isSet = false; per_tester_actions = new SWGPERTesterActions(); @@ -99,6 +103,9 @@ SWGFeatureActions::cleanup() { if(gs232_controller_actions != nullptr) { delete gs232_controller_actions; } + if(lime_rfe_actions != nullptr) { + delete lime_rfe_actions; + } if(map_actions != nullptr) { delete map_actions; } @@ -143,6 +150,8 @@ SWGFeatureActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gs232_controller_actions, pJson["GS232ControllerActions"], "SWGGS232ControllerActions", "SWGGS232ControllerActions"); + ::SWGSDRangel::setValue(&lime_rfe_actions, pJson["LimeRFEActions"], "SWGLimeRFEActions", "SWGLimeRFEActions"); + ::SWGSDRangel::setValue(&map_actions, pJson["MapActions"], "SWGMapActions", "SWGMapActions"); ::SWGSDRangel::setValue(&per_tester_actions, pJson["PERTesterActions"], "SWGPERTesterActions", "SWGPERTesterActions"); @@ -188,6 +197,9 @@ SWGFeatureActions::asJsonObject() { if((gs232_controller_actions != nullptr) && (gs232_controller_actions->isSet())){ toJsonValue(QString("GS232ControllerActions"), gs232_controller_actions, obj, QString("SWGGS232ControllerActions")); } + if((lime_rfe_actions != nullptr) && (lime_rfe_actions->isSet())){ + toJsonValue(QString("LimeRFEActions"), lime_rfe_actions, obj, QString("SWGLimeRFEActions")); + } if((map_actions != nullptr) && (map_actions->isSet())){ toJsonValue(QString("MapActions"), map_actions, obj, QString("SWGMapActions")); } @@ -263,6 +275,16 @@ SWGFeatureActions::setGs232ControllerActions(SWGGS232ControllerActions* gs232_co this->m_gs232_controller_actions_isSet = true; } +SWGLimeRFEActions* +SWGFeatureActions::getLimeRfeActions() { + return lime_rfe_actions; +} +void +SWGFeatureActions::setLimeRfeActions(SWGLimeRFEActions* lime_rfe_actions) { + this->lime_rfe_actions = lime_rfe_actions; + this->m_lime_rfe_actions_isSet = true; +} + SWGMapActions* SWGFeatureActions::getMapActions() { return map_actions; @@ -353,6 +375,9 @@ SWGFeatureActions::isSet(){ if(gs232_controller_actions && gs232_controller_actions->isSet()){ isObjectUpdated = true; break; } + if(lime_rfe_actions && lime_rfe_actions->isSet()){ + isObjectUpdated = true; break; + } if(map_actions && map_actions->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h index 16c3dc97b..46fa415e3 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h @@ -24,6 +24,7 @@ #include "SWGAFCActions.h" #include "SWGGS232ControllerActions.h" +#include "SWGLimeRFEActions.h" #include "SWGMapActions.h" #include "SWGPERTesterActions.h" #include "SWGRigCtlServerActions.h" @@ -66,6 +67,9 @@ public: SWGGS232ControllerActions* getGs232ControllerActions(); void setGs232ControllerActions(SWGGS232ControllerActions* gs232_controller_actions); + SWGLimeRFEActions* getLimeRfeActions(); + void setLimeRfeActions(SWGLimeRFEActions* lime_rfe_actions); + SWGMapActions* getMapActions(); void setMapActions(SWGMapActions* map_actions); @@ -106,6 +110,9 @@ private: SWGGS232ControllerActions* gs232_controller_actions; bool m_gs232_controller_actions_isSet; + SWGLimeRFEActions* lime_rfe_actions; + bool m_lime_rfe_actions_isSet; + SWGMapActions* map_actions; bool m_map_actions_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp index 20adc1677..1b79ae52a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp @@ -34,6 +34,8 @@ SWGFeatureReport::SWGFeatureReport() { m_afc_report_isSet = false; gs232_controller_report = nullptr; m_gs232_controller_report_isSet = false; + lime_rfe_report = nullptr; + m_lime_rfe_report_isSet = false; map_report = nullptr; m_map_report_isSet = false; per_tester_report = nullptr; @@ -62,6 +64,8 @@ SWGFeatureReport::init() { m_afc_report_isSet = false; gs232_controller_report = new SWGGS232ControllerReport(); m_gs232_controller_report_isSet = false; + lime_rfe_report = new SWGLimeRFEReport(); + m_lime_rfe_report_isSet = false; map_report = new SWGMapReport(); m_map_report_isSet = false; per_tester_report = new SWGPERTesterReport(); @@ -89,6 +93,9 @@ SWGFeatureReport::cleanup() { if(gs232_controller_report != nullptr) { delete gs232_controller_report; } + if(lime_rfe_report != nullptr) { + delete lime_rfe_report; + } if(map_report != nullptr) { delete map_report; } @@ -129,6 +136,8 @@ SWGFeatureReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gs232_controller_report, pJson["GS232ControllerReport"], "SWGGS232ControllerReport", "SWGGS232ControllerReport"); + ::SWGSDRangel::setValue(&lime_rfe_report, pJson["LimeRFEReport"], "SWGLimeRFEReport", "SWGLimeRFEReport"); + ::SWGSDRangel::setValue(&map_report, pJson["MapReport"], "SWGMapReport", "SWGMapReport"); ::SWGSDRangel::setValue(&per_tester_report, pJson["PERTesterReport"], "SWGPERTesterReport", "SWGPERTesterReport"); @@ -168,6 +177,9 @@ SWGFeatureReport::asJsonObject() { if((gs232_controller_report != nullptr) && (gs232_controller_report->isSet())){ toJsonValue(QString("GS232ControllerReport"), gs232_controller_report, obj, QString("SWGGS232ControllerReport")); } + if((lime_rfe_report != nullptr) && (lime_rfe_report->isSet())){ + toJsonValue(QString("LimeRFEReport"), lime_rfe_report, obj, QString("SWGLimeRFEReport")); + } if((map_report != nullptr) && (map_report->isSet())){ toJsonValue(QString("MapReport"), map_report, obj, QString("SWGMapReport")); } @@ -223,6 +235,16 @@ SWGFeatureReport::setGs232ControllerReport(SWGGS232ControllerReport* gs232_contr this->m_gs232_controller_report_isSet = true; } +SWGLimeRFEReport* +SWGFeatureReport::getLimeRfeReport() { + return lime_rfe_report; +} +void +SWGFeatureReport::setLimeRfeReport(SWGLimeRFEReport* lime_rfe_report) { + this->lime_rfe_report = lime_rfe_report; + this->m_lime_rfe_report_isSet = true; +} + SWGMapReport* SWGFeatureReport::getMapReport() { return map_report; @@ -307,6 +329,9 @@ SWGFeatureReport::isSet(){ if(gs232_controller_report && gs232_controller_report->isSet()){ isObjectUpdated = true; break; } + if(lime_rfe_report && lime_rfe_report->isSet()){ + isObjectUpdated = true; break; + } if(map_report && map_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h index 00267e15c..25c6c51d1 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h @@ -24,6 +24,7 @@ #include "SWGAFCReport.h" #include "SWGGS232ControllerReport.h" +#include "SWGLimeRFEReport.h" #include "SWGMapReport.h" #include "SWGPERTesterReport.h" #include "SWGRigCtlServerReport.h" @@ -60,6 +61,9 @@ public: SWGGS232ControllerReport* getGs232ControllerReport(); void setGs232ControllerReport(SWGGS232ControllerReport* gs232_controller_report); + SWGLimeRFEReport* getLimeRfeReport(); + void setLimeRfeReport(SWGLimeRFEReport* lime_rfe_report); + SWGMapReport* getMapReport(); void setMapReport(SWGMapReport* map_report); @@ -94,6 +98,9 @@ private: SWGGS232ControllerReport* gs232_controller_report; bool m_gs232_controller_report_isSet; + SWGLimeRFEReport* lime_rfe_report; + bool m_lime_rfe_report_isSet; + SWGMapReport* map_report; bool m_map_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp index c1a5dca05..61595592e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp @@ -48,6 +48,8 @@ SWGFeatureSettings::SWGFeatureSettings() { m_jogdial_controller_settings_isSet = false; gs232_controller_settings = nullptr; m_gs232_controller_settings_isSet = false; + lime_rfe_settings = nullptr; + m_lime_rfe_settings_isSet = false; map_settings = nullptr; m_map_settings_isSet = false; per_tester_settings = nullptr; @@ -92,6 +94,8 @@ SWGFeatureSettings::init() { m_jogdial_controller_settings_isSet = false; gs232_controller_settings = new SWGGS232ControllerSettings(); m_gs232_controller_settings_isSet = false; + lime_rfe_settings = new SWGLimeRFESettings(); + m_lime_rfe_settings_isSet = false; map_settings = new SWGMapSettings(); m_map_settings_isSet = false; per_tester_settings = new SWGPERTesterSettings(); @@ -138,6 +142,9 @@ SWGFeatureSettings::cleanup() { if(gs232_controller_settings != nullptr) { delete gs232_controller_settings; } + if(lime_rfe_settings != nullptr) { + delete lime_rfe_settings; + } if(map_settings != nullptr) { delete map_settings; } @@ -195,6 +202,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gs232_controller_settings, pJson["GS232ControllerSettings"], "SWGGS232ControllerSettings", "SWGGS232ControllerSettings"); + ::SWGSDRangel::setValue(&lime_rfe_settings, pJson["LimeRFESettings"], "SWGLimeRFESettings", "SWGLimeRFESettings"); + ::SWGSDRangel::setValue(&map_settings, pJson["MapSettings"], "SWGMapSettings", "SWGMapSettings"); ::SWGSDRangel::setValue(&per_tester_settings, pJson["PERTesterSettings"], "SWGPERTesterSettings", "SWGPERTesterSettings"); @@ -257,6 +266,9 @@ SWGFeatureSettings::asJsonObject() { if((gs232_controller_settings != nullptr) && (gs232_controller_settings->isSet())){ toJsonValue(QString("GS232ControllerSettings"), gs232_controller_settings, obj, QString("SWGGS232ControllerSettings")); } + if((lime_rfe_settings != nullptr) && (lime_rfe_settings->isSet())){ + toJsonValue(QString("LimeRFESettings"), lime_rfe_settings, obj, QString("SWGLimeRFESettings")); + } if((map_settings != nullptr) && (map_settings->isSet())){ toJsonValue(QString("MapSettings"), map_settings, obj, QString("SWGMapSettings")); } @@ -385,6 +397,16 @@ SWGFeatureSettings::setGs232ControllerSettings(SWGGS232ControllerSettings* gs232 this->m_gs232_controller_settings_isSet = true; } +SWGLimeRFESettings* +SWGFeatureSettings::getLimeRfeSettings() { + return lime_rfe_settings; +} +void +SWGFeatureSettings::setLimeRfeSettings(SWGLimeRFESettings* lime_rfe_settings) { + this->lime_rfe_settings = lime_rfe_settings; + this->m_lime_rfe_settings_isSet = true; +} + SWGMapSettings* SWGFeatureSettings::getMapSettings() { return map_settings; @@ -500,6 +522,9 @@ SWGFeatureSettings::isSet(){ if(gs232_controller_settings && gs232_controller_settings->isSet()){ isObjectUpdated = true; break; } + if(lime_rfe_settings && lime_rfe_settings->isSet()){ + isObjectUpdated = true; break; + } if(map_settings && map_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h index 64993003d..3271cb913 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h @@ -29,6 +29,7 @@ #include "SWGDemodAnalyzerSettings.h" #include "SWGGS232ControllerSettings.h" #include "SWGJogdialControllerSettings.h" +#include "SWGLimeRFESettings.h" #include "SWGMapSettings.h" #include "SWGPERTesterSettings.h" #include "SWGRadiosondeSettings.h" @@ -87,6 +88,9 @@ public: SWGGS232ControllerSettings* getGs232ControllerSettings(); void setGs232ControllerSettings(SWGGS232ControllerSettings* gs232_controller_settings); + SWGLimeRFESettings* getLimeRfeSettings(); + void setLimeRfeSettings(SWGLimeRFESettings* lime_rfe_settings); + SWGMapSettings* getMapSettings(); void setMapSettings(SWGMapSettings* map_settings); @@ -145,6 +149,9 @@ private: SWGGS232ControllerSettings* gs232_controller_settings; bool m_gs232_controller_settings_isSet; + SWGLimeRFESettings* lime_rfe_settings; + bool m_lime_rfe_settings_isSet; + SWGMapSettings* map_settings; bool m_map_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp new file mode 100644 index 000000000..9484cf862 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp @@ -0,0 +1,200 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGLimeRFEActions.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGLimeRFEActions::SWGLimeRFEActions(QString* json) { + init(); + this->fromJson(*json); +} + +SWGLimeRFEActions::SWGLimeRFEActions() { + select_channel = 0; + m_select_channel_isSet = false; + device_set_index = 0; + m_device_set_index_isSet = false; + switch_channel = 0; + m_switch_channel_isSet = false; + from_to_settings = 0; + m_from_to_settings_isSet = false; + open_close_device = 0; + m_open_close_device_isSet = false; +} + +SWGLimeRFEActions::~SWGLimeRFEActions() { + this->cleanup(); +} + +void +SWGLimeRFEActions::init() { + select_channel = 0; + m_select_channel_isSet = false; + device_set_index = 0; + m_device_set_index_isSet = false; + switch_channel = 0; + m_switch_channel_isSet = false; + from_to_settings = 0; + m_from_to_settings_isSet = false; + open_close_device = 0; + m_open_close_device_isSet = false; +} + +void +SWGLimeRFEActions::cleanup() { + + + + + +} + +SWGLimeRFEActions* +SWGLimeRFEActions::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGLimeRFEActions::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&select_channel, pJson["selectChannel"], "qint32", ""); + + ::SWGSDRangel::setValue(&device_set_index, pJson["deviceSetIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&switch_channel, pJson["switchChannel"], "qint32", ""); + + ::SWGSDRangel::setValue(&from_to_settings, pJson["fromToSettings"], "qint32", ""); + + ::SWGSDRangel::setValue(&open_close_device, pJson["openCloseDevice"], "qint32", ""); + +} + +QString +SWGLimeRFEActions::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGLimeRFEActions::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_select_channel_isSet){ + obj->insert("selectChannel", QJsonValue(select_channel)); + } + if(m_device_set_index_isSet){ + obj->insert("deviceSetIndex", QJsonValue(device_set_index)); + } + if(m_switch_channel_isSet){ + obj->insert("switchChannel", QJsonValue(switch_channel)); + } + if(m_from_to_settings_isSet){ + obj->insert("fromToSettings", QJsonValue(from_to_settings)); + } + if(m_open_close_device_isSet){ + obj->insert("openCloseDevice", QJsonValue(open_close_device)); + } + + return obj; +} + +qint32 +SWGLimeRFEActions::getSelectChannel() { + return select_channel; +} +void +SWGLimeRFEActions::setSelectChannel(qint32 select_channel) { + this->select_channel = select_channel; + this->m_select_channel_isSet = true; +} + +qint32 +SWGLimeRFEActions::getDeviceSetIndex() { + return device_set_index; +} +void +SWGLimeRFEActions::setDeviceSetIndex(qint32 device_set_index) { + this->device_set_index = device_set_index; + this->m_device_set_index_isSet = true; +} + +qint32 +SWGLimeRFEActions::getSwitchChannel() { + return switch_channel; +} +void +SWGLimeRFEActions::setSwitchChannel(qint32 switch_channel) { + this->switch_channel = switch_channel; + this->m_switch_channel_isSet = true; +} + +qint32 +SWGLimeRFEActions::getFromToSettings() { + return from_to_settings; +} +void +SWGLimeRFEActions::setFromToSettings(qint32 from_to_settings) { + this->from_to_settings = from_to_settings; + this->m_from_to_settings_isSet = true; +} + +qint32 +SWGLimeRFEActions::getOpenCloseDevice() { + return open_close_device; +} +void +SWGLimeRFEActions::setOpenCloseDevice(qint32 open_close_device) { + this->open_close_device = open_close_device; + this->m_open_close_device_isSet = true; +} + + +bool +SWGLimeRFEActions::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_select_channel_isSet){ + isObjectUpdated = true; break; + } + if(m_device_set_index_isSet){ + isObjectUpdated = true; break; + } + if(m_switch_channel_isSet){ + isObjectUpdated = true; break; + } + if(m_from_to_settings_isSet){ + isObjectUpdated = true; break; + } + if(m_open_close_device_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h new file mode 100644 index 000000000..29fa4c518 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h @@ -0,0 +1,82 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGLimeRFEActions.h + * + * LimeRFE + */ + +#ifndef SWGLimeRFEActions_H_ +#define SWGLimeRFEActions_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGLimeRFEActions: public SWGObject { +public: + SWGLimeRFEActions(); + SWGLimeRFEActions(QString* json); + virtual ~SWGLimeRFEActions(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGLimeRFEActions* fromJson(QString &jsonString) override; + + qint32 getSelectChannel(); + void setSelectChannel(qint32 select_channel); + + qint32 getDeviceSetIndex(); + void setDeviceSetIndex(qint32 device_set_index); + + qint32 getSwitchChannel(); + void setSwitchChannel(qint32 switch_channel); + + qint32 getFromToSettings(); + void setFromToSettings(qint32 from_to_settings); + + qint32 getOpenCloseDevice(); + void setOpenCloseDevice(qint32 open_close_device); + + + virtual bool isSet() override; + +private: + qint32 select_channel; + bool m_select_channel_isSet; + + qint32 device_set_index; + bool m_device_set_index_isSet; + + qint32 switch_channel; + bool m_switch_channel_isSet; + + qint32 from_to_settings; + bool m_from_to_settings_isSet; + + qint32 open_close_device; + bool m_open_close_device_isSet; + +}; + +} + +#endif /* SWGLimeRFEActions_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h index fce159f3b..917a6d3af 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h @@ -13,7 +13,7 @@ /* * SWGLimeRFEPower.h * - * report of forward and reflected power measurements + * report of forward and reflected power measurements TO BE DECOMMISSIONED */ #ifndef SWGLimeRFEPower_H_ diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp new file mode 100644 index 000000000..9df562a65 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.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 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: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGLimeRFEReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGLimeRFEReport::SWGLimeRFEReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGLimeRFEReport::SWGLimeRFEReport() { + forward_power = 0; + m_forward_power_isSet = false; + reflected_power = 0; + m_reflected_power_isSet = false; +} + +SWGLimeRFEReport::~SWGLimeRFEReport() { + this->cleanup(); +} + +void +SWGLimeRFEReport::init() { + forward_power = 0; + m_forward_power_isSet = false; + reflected_power = 0; + m_reflected_power_isSet = false; +} + +void +SWGLimeRFEReport::cleanup() { + + +} + +SWGLimeRFEReport* +SWGLimeRFEReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGLimeRFEReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&forward_power, pJson["forwardPower"], "qint32", ""); + + ::SWGSDRangel::setValue(&reflected_power, pJson["reflectedPower"], "qint32", ""); + +} + +QString +SWGLimeRFEReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGLimeRFEReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_forward_power_isSet){ + obj->insert("forwardPower", QJsonValue(forward_power)); + } + if(m_reflected_power_isSet){ + obj->insert("reflectedPower", QJsonValue(reflected_power)); + } + + return obj; +} + +qint32 +SWGLimeRFEReport::getForwardPower() { + return forward_power; +} +void +SWGLimeRFEReport::setForwardPower(qint32 forward_power) { + this->forward_power = forward_power; + this->m_forward_power_isSet = true; +} + +qint32 +SWGLimeRFEReport::getReflectedPower() { + return reflected_power; +} +void +SWGLimeRFEReport::setReflectedPower(qint32 reflected_power) { + this->reflected_power = reflected_power; + this->m_reflected_power_isSet = true; +} + + +bool +SWGLimeRFEReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_forward_power_isSet){ + isObjectUpdated = true; break; + } + if(m_reflected_power_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h new file mode 100644 index 000000000..6929fdf4f --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.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 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: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGLimeRFEReport.h + * + * LimeRFE + */ + +#ifndef SWGLimeRFEReport_H_ +#define SWGLimeRFEReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGLimeRFEReport: public SWGObject { +public: + SWGLimeRFEReport(); + SWGLimeRFEReport(QString* json); + virtual ~SWGLimeRFEReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGLimeRFEReport* fromJson(QString &jsonString) override; + + qint32 getForwardPower(); + void setForwardPower(qint32 forward_power); + + qint32 getReflectedPower(); + void setReflectedPower(qint32 reflected_power); + + + virtual bool isSet() override; + +private: + qint32 forward_power; + bool m_forward_power_isSet; + + qint32 reflected_power; + bool m_reflected_power_isSet; + +}; + +} + +#endif /* SWGLimeRFEReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp index fba79d1d8..c0e0e1098 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp @@ -28,6 +28,10 @@ SWGLimeRFESettings::SWGLimeRFESettings(QString* json) { } SWGLimeRFESettings::SWGLimeRFESettings() { + title = nullptr; + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; device_path = nullptr; m_device_path_isSet = false; rx_channels = 0; @@ -58,10 +62,24 @@ SWGLimeRFESettings::SWGLimeRFESettings() { m_swr_enable_isSet = false; swr_source = 0; m_swr_source_isSet = false; + tx_rx_driven = 0; + m_tx_rx_driven_isSet = false; rx_on = 0; m_rx_on_isSet = false; tx_on = 0; m_tx_on_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_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; } SWGLimeRFESettings::~SWGLimeRFESettings() { @@ -70,6 +88,10 @@ SWGLimeRFESettings::~SWGLimeRFESettings() { void SWGLimeRFESettings::init() { + title = new QString(""); + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; device_path = new QString(""); m_device_path_isSet = false; rx_channels = 0; @@ -100,14 +122,32 @@ SWGLimeRFESettings::init() { m_swr_enable_isSet = false; swr_source = 0; m_swr_source_isSet = false; + tx_rx_driven = 0; + m_tx_rx_driven_isSet = false; rx_on = 0; m_rx_on_isSet = false; tx_on = 0; m_tx_on_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_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; } void SWGLimeRFESettings::cleanup() { + if(title != nullptr) { + delete title; + } + if(device_path != nullptr) { delete device_path; } @@ -127,6 +167,17 @@ SWGLimeRFESettings::cleanup() { + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(rollup_state != nullptr) { + delete rollup_state; + } } SWGLimeRFESettings* @@ -140,6 +191,10 @@ SWGLimeRFESettings::fromJson(QString &json) { void SWGLimeRFESettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + ::SWGSDRangel::setValue(&device_path, pJson["devicePath"], "QString", "QString"); ::SWGSDRangel::setValue(&rx_channels, pJson["rxChannels"], "qint32", ""); @@ -170,10 +225,24 @@ SWGLimeRFESettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&swr_source, pJson["swrSource"], "qint32", ""); + ::SWGSDRangel::setValue(&tx_rx_driven, pJson["txRxDriven"], "qint32", ""); + ::SWGSDRangel::setValue(&rx_on, pJson["rxOn"], "qint32", ""); ::SWGSDRangel::setValue(&tx_on, pJson["txOn"], "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_feature_set_index, pJson["reverseAPIFeatureSetIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_feature_index, pJson["reverseAPIFeatureIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + } QString @@ -190,6 +259,12 @@ SWGLimeRFESettings::asJson () QJsonObject* SWGLimeRFESettings::asJsonObject() { QJsonObject* obj = new QJsonObject(); + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } if(device_path != nullptr && *device_path != QString("")){ toJsonValue(QString("devicePath"), device_path, obj, QString("QString")); } @@ -235,16 +310,57 @@ SWGLimeRFESettings::asJsonObject() { if(m_swr_source_isSet){ obj->insert("swrSource", QJsonValue(swr_source)); } + if(m_tx_rx_driven_isSet){ + obj->insert("txRxDriven", QJsonValue(tx_rx_driven)); + } if(m_rx_on_isSet){ obj->insert("rxOn", QJsonValue(rx_on)); } if(m_tx_on_isSet){ obj->insert("txOn", QJsonValue(tx_on)); } + 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_feature_set_index_isSet){ + obj->insert("reverseAPIFeatureSetIndex", QJsonValue(reverse_api_feature_set_index)); + } + if(m_reverse_api_feature_index_isSet){ + obj->insert("reverseAPIFeatureIndex", QJsonValue(reverse_api_feature_index)); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } return obj; } +QString* +SWGLimeRFESettings::getTitle() { + return title; +} +void +SWGLimeRFESettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGLimeRFESettings::getRgbColor() { + return rgb_color; +} +void +SWGLimeRFESettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + QString* SWGLimeRFESettings::getDevicePath() { return device_path; @@ -395,6 +511,16 @@ SWGLimeRFESettings::setSwrSource(qint32 swr_source) { this->m_swr_source_isSet = true; } +qint32 +SWGLimeRFESettings::getTxRxDriven() { + return tx_rx_driven; +} +void +SWGLimeRFESettings::setTxRxDriven(qint32 tx_rx_driven) { + this->tx_rx_driven = tx_rx_driven; + this->m_tx_rx_driven_isSet = true; +} + qint32 SWGLimeRFESettings::getRxOn() { return rx_on; @@ -415,11 +541,77 @@ SWGLimeRFESettings::setTxOn(qint32 tx_on) { this->m_tx_on_isSet = true; } +qint32 +SWGLimeRFESettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGLimeRFESettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGLimeRFESettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGLimeRFESettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGLimeRFESettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGLimeRFESettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGLimeRFESettings::getReverseApiFeatureSetIndex() { + return reverse_api_feature_set_index; +} +void +SWGLimeRFESettings::setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index) { + this->reverse_api_feature_set_index = reverse_api_feature_set_index; + this->m_reverse_api_feature_set_index_isSet = true; +} + +qint32 +SWGLimeRFESettings::getReverseApiFeatureIndex() { + return reverse_api_feature_index; +} +void +SWGLimeRFESettings::setReverseApiFeatureIndex(qint32 reverse_api_feature_index) { + this->reverse_api_feature_index = reverse_api_feature_index; + this->m_reverse_api_feature_index_isSet = true; +} + +SWGRollupState* +SWGLimeRFESettings::getRollupState() { + return rollup_state; +} +void +SWGLimeRFESettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + bool SWGLimeRFESettings::isSet(){ bool isObjectUpdated = false; do{ + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } if(device_path && *device_path != QString("")){ isObjectUpdated = true; break; } @@ -465,12 +657,33 @@ SWGLimeRFESettings::isSet(){ if(m_swr_source_isSet){ isObjectUpdated = true; break; } + if(m_tx_rx_driven_isSet){ + isObjectUpdated = true; break; + } if(m_rx_on_isSet){ isObjectUpdated = true; break; } if(m_tx_on_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_feature_set_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_feature_index_isSet){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h index 90517b823..e947fead1 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h @@ -22,6 +22,7 @@ #include +#include "SWGRollupState.h" #include #include "SWGObject.h" @@ -42,6 +43,12 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGLimeRFESettings* fromJson(QString &jsonString) override; + QString* getTitle(); + void setTitle(QString* title); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + QString* getDevicePath(); void setDevicePath(QString* device_path); @@ -87,16 +94,43 @@ public: qint32 getSwrSource(); void setSwrSource(qint32 swr_source); + qint32 getTxRxDriven(); + void setTxRxDriven(qint32 tx_rx_driven); + qint32 getRxOn(); void setRxOn(qint32 rx_on); qint32 getTxOn(); void setTxOn(qint32 tx_on); + 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 getReverseApiFeatureSetIndex(); + void setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index); + + qint32 getReverseApiFeatureIndex(); + void setReverseApiFeatureIndex(qint32 reverse_api_feature_index); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + virtual bool isSet() override; private: + QString* title; + bool m_title_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + QString* device_path; bool m_device_path_isSet; @@ -142,12 +176,33 @@ private: qint32 swr_source; bool m_swr_source_isSet; + qint32 tx_rx_driven; + bool m_tx_rx_driven_isSet; + qint32 rx_on; bool m_rx_on_isSet; qint32 tx_on; bool m_tx_on_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_feature_set_index; + bool m_reverse_api_feature_set_index_isSet; + + qint32 reverse_api_feature_index; + bool m_reverse_api_feature_index_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 8c8c47b41..120ac5f7f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -158,9 +158,11 @@ #include "SWGJogdialControllerSettings.h" #include "SWGKiwiSDRReport.h" #include "SWGKiwiSDRSettings.h" +#include "SWGLimeRFEActions.h" #include "SWGLimeRFEDevice.h" #include "SWGLimeRFEDevices.h" #include "SWGLimeRFEPower.h" +#include "SWGLimeRFEReport.h" #include "SWGLimeRFESettings.h" #include "SWGLimeSdrInputReport.h" #include "SWGLimeSdrInputSettings.h" @@ -1048,6 +1050,11 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGLimeRFEActions").compare(type) == 0) { + SWGLimeRFEActions *obj = new SWGLimeRFEActions(); + obj->init(); + return obj; + } if(QString("SWGLimeRFEDevice").compare(type) == 0) { SWGLimeRFEDevice *obj = new SWGLimeRFEDevice(); obj->init(); @@ -1063,6 +1070,11 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGLimeRFEReport").compare(type) == 0) { + SWGLimeRFEReport *obj = new SWGLimeRFEReport(); + obj->init(); + return obj; + } if(QString("SWGLimeRFESettings").compare(type) == 0) { SWGLimeRFESettings *obj = new SWGLimeRFESettings(); obj->init(); From ac8a22a94beb159444ec74f14a0bd69d7b735364 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 22 May 2022 19:53:57 +0200 Subject: [PATCH 04/29] LimeRFE feature: removed rxOn and txOn from settings --- plugins/feature/limerfe/limerfe.cpp | 98 +++++++++---------- plugins/feature/limerfe/limerfe.h | 8 +- plugins/feature/limerfe/limerfegui.cpp | 74 +++++++++----- plugins/feature/limerfe/limerfegui.h | 2 + plugins/feature/limerfe/limerfesettings.cpp | 6 -- plugins/feature/limerfe/limerfesettings.h | 2 - sdrbase/resources/webapi/doc/html2/index.html | 22 +++-- .../webapi/doc/swagger/include/LimeRFE.yaml | 15 +-- sdrbase/webapi/webapiadapter.cpp | 7 -- sdrbase/webapi/webapirequestmapper.cpp | 10 -- .../sdrangel/api/swagger/include/LimeRFE.yaml | 15 +-- swagger/sdrangel/code/html2/index.html | 22 +++-- .../code/qt5/client/SWGLimeRFEActions.cpp | 23 +++++ .../code/qt5/client/SWGLimeRFEActions.h | 6 ++ .../code/qt5/client/SWGLimeRFEReport.cpp | 46 +++++++++ .../code/qt5/client/SWGLimeRFEReport.h | 12 +++ .../code/qt5/client/SWGLimeRFESettings.cpp | 46 --------- .../code/qt5/client/SWGLimeRFESettings.h | 12 --- 18 files changed, 234 insertions(+), 192 deletions(-) diff --git a/plugins/feature/limerfe/limerfe.cpp b/plugins/feature/limerfe/limerfe.cpp index 4a9c9127a..a8d21dc7e 100644 --- a/plugins/feature/limerfe/limerfe.cpp +++ b/plugins/feature/limerfe/limerfe.cpp @@ -257,6 +257,27 @@ int LimeRFE::getState() qInfo("LimeRFE::getState: %s", getError(rc).c_str()); } + if (m_rfeBoardState.mode == RFE_MODE_RX) + { + m_rxOn = true; + m_txOn = false; + } + else if (m_rfeBoardState.mode == RFE_MODE_TX) + { + m_rxOn = false; + m_txOn = true; + } + else if (m_rfeBoardState.mode == RFE_MODE_NONE) + { + m_rxOn = false; + m_txOn = false; + } + else if (m_rfeBoardState.mode == RFE_MODE_TXRX) + { + m_rxOn = true; + m_txOn = true; + } + return rc; } @@ -271,7 +292,7 @@ std::string LimeRFE::getError(int errorCode) } } -int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) +int LimeRFE::setRx(bool rxOn) { if (!m_rfeDevice) { return -1; @@ -281,7 +302,7 @@ int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) if (rxOn) { - if (settings.m_txOn) { + if (m_txOn) { mode = RFE_MODE_TXRX; } else { mode = RFE_MODE_RX; @@ -289,7 +310,7 @@ int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) } else { - if (settings.m_txOn) { + if (m_txOn) { mode = RFE_MODE_TX; } } @@ -298,7 +319,7 @@ int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) int rc = RFE_Mode(m_rfeDevice, mode); if (rc == 0) { - settings.m_rxOn = rxOn; + m_rxOn = rxOn; } else { qInfo("LimeRFE::setRx: %s", getError(rc).c_str()); } @@ -306,7 +327,7 @@ int LimeRFE::setRx(LimeRFESettings& settings, bool rxOn) return rc; } -int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) +int LimeRFE::setTx(bool txOn) { if (!m_rfeDevice) { return -1; @@ -316,7 +337,7 @@ int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) if (txOn) { - if (settings.m_rxOn) { + if (m_rxOn) { mode = RFE_MODE_TXRX; } else { mode = RFE_MODE_TX; @@ -324,7 +345,7 @@ int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) } else { - if (settings.m_rxOn) { + if (m_rxOn) { mode = RFE_MODE_RX; } } @@ -333,7 +354,7 @@ int LimeRFE::setTx(LimeRFESettings& settings, bool txOn) int rc = RFE_Mode(m_rfeDevice, mode); if (rc == 0) { - settings.m_txOn = txOn; + m_txOn = txOn; } else { qInfo("LimeRFE::setTx: %s", getError(rc).c_str()); } @@ -433,14 +454,6 @@ void LimeRFE::settingsToState(const LimeRFESettings& settings) } else { - m_rfeBoardState.mode = settings.m_rxOn && settings.m_txOn ? - RFE_MODE_TXRX : - settings.m_rxOn ? - RFE_MODE_RX : - settings.m_txOn ? - RFE_MODE_TX : - RFE_MODE_NONE; - if (settings.m_rxChannels == LimeRFESettings::ChannelGroups::ChannelsWideband) { if (settings.m_rxWidebandChannel == LimeRFESettings::WidebandChannel::WidebandLow) { @@ -716,28 +729,6 @@ void LimeRFE::stateToSettings(LimeRFESettings& settings) settings.m_attenuationFactor = m_rfeBoardState.attValue; settings.m_amfmNotch = m_rfeBoardState.notchOnOff == RFE_NOTCH_ON; - - if (m_rfeBoardState.mode == RFE_MODE_RX) - { - settings.m_rxOn = true; - settings.m_txOn = false; - } - else if (m_rfeBoardState.mode == RFE_MODE_TX) - { - settings.m_rxOn = false; - settings.m_txOn = true; - } - else if (m_rfeBoardState.mode == RFE_MODE_NONE) - { - settings.m_rxOn = false; - settings.m_txOn = false; - } - else if (m_rfeBoardState.mode == RFE_MODE_TXRX) - { - settings.m_rxOn = true; - settings.m_txOn = true; - } - settings.m_swrEnable = m_rfeBoardState.enableSWR == RFE_SWR_ENABLE; settings.m_swrSource = m_rfeBoardState.sourceSWR == RFE_SWR_SRC_CELL ? LimeRFESettings::SWRSource::SWRCellular : @@ -847,6 +838,18 @@ int LimeRFE::webapiActionsPost( } } + if (featureActionsKeys.contains("getState") && (swgLimeRFEActions->getGetState() != 0)) + { + int rc = getState(); + unknownAction = false; + + if (rc != 0) + { + errorMessage = QString("Get state %1: %2").arg(m_settings.m_devicePath).arg(getError(rc).c_str()); + return 500; + } + } + if (featureActionsKeys.contains("fromToSettings") && (swgLimeRFEActions->getFromToSettings() != 0)) { settingsToState(m_settings); @@ -858,7 +861,7 @@ int LimeRFE::webapiActionsPost( if (channel == 0) { bool on = swgLimeRFEActions->getSwitchChannel() != 0; - int rc = setRx(m_settings, on); + int rc = setRx(on); if (rc != 0) { @@ -868,14 +871,14 @@ int LimeRFE::webapiActionsPost( if (getMessageQueueToGUI()) { - MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, false); + MsgReportSetRx *msg = MsgReportSetRx::create(on); getMessageQueueToGUI()->push(msg); } } else { bool on = swgLimeRFEActions->getSwitchChannel() != 0; - int rc = setTx(m_settings, swgLimeRFEActions->getSwitchChannel() != 0); + int rc = setTx(on); if (rc != 0) { @@ -885,7 +888,7 @@ int LimeRFE::webapiActionsPost( if (getMessageQueueToGUI()) { - MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, false); + MsgReportSetTx *msg = MsgReportSetTx::create(on); getMessageQueueToGUI()->push(msg); } } @@ -949,7 +952,6 @@ void LimeRFE::webapiFormatFeatureSettings( response.getLimeRfeSettings()->setRxHamChannel((int) settings.m_rxHAMChannel); response.getLimeRfeSettings()->setRxCellularChannel((int) settings.m_rxCellularChannel); response.getLimeRfeSettings()->setRxPort((int) settings.m_rxPort); - response.getLimeRfeSettings()->setRxOn(settings.m_rxOn ? 1 : 0); response.getLimeRfeSettings()->setAmfmNotch(settings.m_amfmNotch ? 1 : 0); response.getLimeRfeSettings()->setAttenuationFactor(settings.m_attenuationFactor); response.getLimeRfeSettings()->setTxChannels((int) settings.m_txChannels); @@ -957,7 +959,6 @@ void LimeRFE::webapiFormatFeatureSettings( response.getLimeRfeSettings()->setTxHamChannel((int) settings.m_txHAMChannel); response.getLimeRfeSettings()->setTxCellularChannel((int) settings.m_txCellularChannel); response.getLimeRfeSettings()->setTxPort((int) settings.m_txPort); - response.getLimeRfeSettings()->setTxOn(settings.m_txOn ? 1 : 0); response.getLimeRfeSettings()->setSwrEnable(settings.m_swrEnable ? 1 : 0); response.getLimeRfeSettings()->setSwrSource((int) settings.m_swrSource); response.getLimeRfeSettings()->setTxRxDriven(settings.m_txRxDriven ? 1 : 0); @@ -1018,9 +1019,6 @@ void LimeRFE::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("rxPort")) { settings.m_rxPort = (LimeRFESettings::RxPort) response.getLimeRfeSettings()->getRxPort(); } - if (featureSettingsKeys.contains("rxOn")) { - settings.m_rxOn = response.getLimeRfeSettings()->getRxOn() != 0; - } if (featureSettingsKeys.contains("amfmNotch")) { settings.m_amfmNotch = response.getLimeRfeSettings()->getAmfmNotch() != 0; } @@ -1042,9 +1040,6 @@ void LimeRFE::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("txPort")) { settings.m_txPort = (LimeRFESettings::TxPort) response.getLimeRfeSettings()->getTxPort(); } - if (featureSettingsKeys.contains("txOn")) { - settings.m_txOn = response.getLimeRfeSettings()->getTxOn() != 0; - } if (featureSettingsKeys.contains("swrEnable")) { settings.m_swrEnable = response.getLimeRfeSettings()->getSwrEnable() != 0; } @@ -1076,6 +1071,9 @@ void LimeRFE::webapiUpdateFeatureSettings( int LimeRFE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response, QString& errorMessage) { + response.getLimeRfeReport()->setRxOn(m_rxOn ? 1 : 0); + response.getLimeRfeReport()->setTxOn(m_txOn ? 1 : 0); + int fwdPower; int rc = getFwdPower(fwdPower); diff --git a/plugins/feature/limerfe/limerfe.h b/plugins/feature/limerfe/limerfe.h index e224fd87a..9f24490bc 100644 --- a/plugins/feature/limerfe/limerfe.h +++ b/plugins/feature/limerfe/limerfe.h @@ -146,8 +146,10 @@ public: int configure(); int getState(); static std::string getError(int errorCode); - int setRx(LimeRFESettings& settings, bool rxOn); - int setTx(LimeRFESettings& settings, bool txOn); + int setRx(bool rxOn); + int setTx(bool txOn); + bool getRx() const { return m_rxOn; }; + bool getTx() const { return m_txOn; }; bool turnDevice(int deviceSetIndex, bool on); int getFwdPower(int& powerDB); int getRefPower(int& powerDB); @@ -161,6 +163,8 @@ public: private: LimeRFESettings m_settings; LimeRFEUSBCalib m_calib; + bool m_rxOn; + bool m_txOn; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; diff --git a/plugins/feature/limerfe/limerfegui.cpp b/plugins/feature/limerfe/limerfegui.cpp index 05c5fabe5..32c136820 100644 --- a/plugins/feature/limerfe/limerfegui.cpp +++ b/plugins/feature/limerfe/limerfegui.cpp @@ -128,6 +128,8 @@ LimeRFEGUI::LimeRFEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature ui(new Ui::LimeRFEGUI), m_pluginAPI(pluginAPI), m_featureUISet(featureUISet), + m_rxOn(false), + m_txOn(false), m_doApplySettings(true), m_rxTxToggle(false), m_currentPowerCorrection(0.0), @@ -200,9 +202,9 @@ void LimeRFEGUI::displayMode() { QString s; - if (m_settings.m_rxOn) + if (m_rxOn) { - if (m_settings.m_txOn) { + if (m_txOn) { s = "Rx/Tx"; } else { s = "Rx"; @@ -210,7 +212,7 @@ void LimeRFEGUI::displayMode() } else { - if (m_settings.m_txOn) { + if (m_txOn) { s = "Tx"; } else { s = "None"; @@ -222,20 +224,20 @@ void LimeRFEGUI::displayMode() ui->modeRx->blockSignals(true); ui->modeTx->blockSignals(true); - if (m_settings.m_rxOn) { + if (m_rxOn) { ui->modeRx->setStyleSheet("QToolButton { background-color : green; }"); } else { ui->modeRx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); } - if (m_settings.m_txOn) { + if (m_txOn) { ui->modeTx->setStyleSheet("QToolButton { background-color : red; }"); } else { ui->modeTx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); } - ui->modeRx->setChecked(m_settings.m_rxOn); - ui->modeTx->setChecked(m_settings.m_txOn); + ui->modeRx->setChecked(m_rxOn); + ui->modeTx->setChecked(m_txOn); ui->modeRx->blockSignals(false); ui->modeTx->blockSignals(false); @@ -674,6 +676,8 @@ void LimeRFEGUI::on_deviceToGUI_clicked() } m_limeRFE->stateToSettings(m_settings); + m_rxOn = m_limeRFE->getRx(); + m_txOn = m_limeRFE->getTx(); displaySettings(); highlightApplyButton(false); } @@ -830,14 +834,14 @@ void LimeRFEGUI::on_deviceSetSync_clicked() void LimeRFEGUI::syncRxTx() { - if (!m_settings.m_txOn) { - stopStartTx(m_settings.m_txOn); + if (!m_txOn) { + stopStartTx(m_txOn); } - stopStartRx(m_settings.m_rxOn); + stopStartRx(m_rxOn); - if (m_settings.m_txOn) { - stopStartTx(m_settings.m_txOn); + if (m_txOn) { + stopStartTx(m_txOn); } } @@ -865,30 +869,30 @@ void LimeRFEGUI::on_modeRx_toggled(bool checked) { int rc; ui->statusText->clear(); - m_settings.m_rxOn = checked; + m_rxOn = checked; if (m_rxTxToggle) { - m_settings.m_txOn = !checked; + m_txOn = !checked; if (checked) // Rx on { - rc = m_limeRFE->setTx(m_settings, false); // stop Tx first + rc = m_limeRFE->setTx(false); // stop Tx first ui->statusText->append(QString("Stop TX: %1").arg(m_limeRFE->getError(rc).c_str())); } - rc = m_limeRFE->setRx(m_settings, m_settings.m_rxOn); // Rx on or off + rc = m_limeRFE->setRx(m_rxOn); // Rx on or off ui->statusText->append(QString("RX: %1").arg(m_limeRFE->getError(rc).c_str())); if (!checked) // Rx off { - rc = m_limeRFE->setTx(m_settings, true); // start Tx next + rc = m_limeRFE->setTx(true); // start Tx next ui->statusText->append(QString("Start TX: %1").arg(m_limeRFE->getError(rc).c_str())); } } else { - rc = m_limeRFE->setRx(m_settings, m_settings.m_rxOn); + rc = m_limeRFE->setRx(m_rxOn); ui->statusText->setText(m_limeRFE->getError(rc).c_str()); } @@ -903,30 +907,30 @@ void LimeRFEGUI::on_modeTx_toggled(bool checked) { int rc; ui->statusText->clear(); - m_settings.m_txOn = checked; + m_txOn = checked; if (m_rxTxToggle) { - m_settings.m_rxOn = !checked; + m_rxOn = !checked; if (checked) // Tx on { - rc = m_limeRFE->setRx(m_settings, false); // stop Rx first + rc = m_limeRFE->setRx(false); // stop Rx first ui->statusText->append(QString("Stop RX: %1").arg(m_limeRFE->getError(rc).c_str())); } - rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); // Tx on or off + rc = m_limeRFE->setTx(m_txOn); // Tx on or off ui->statusText->append(QString("TX: %1").arg(m_limeRFE->getError(rc).c_str())); if (!checked) // Tx off { - rc = m_limeRFE->setRx(m_settings, true); // start Rx next + rc = m_limeRFE->setRx(true); // start Rx next ui->statusText->append(QString("Start RX: %1").arg(m_limeRFE->getError(rc).c_str())); } } else { - rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); + rc = m_limeRFE->setTx(m_txOn); ui->statusText->setText(m_limeRFE->getError(rc).c_str()); } @@ -941,10 +945,10 @@ void LimeRFEGUI::on_rxTxToggle_clicked() { m_rxTxToggle = ui->rxTxToggle->isChecked(); - if (m_rxTxToggle && m_settings.m_rxOn && m_settings.m_txOn) + if (m_rxTxToggle && m_rxOn && m_txOn) { - m_settings.m_txOn = false; - int rc = m_limeRFE->setTx(m_settings, m_settings.m_txOn); + m_txOn = false; + int rc = m_limeRFE->setTx(m_txOn); ui->statusText->setText(m_limeRFE->getError(rc).c_str()); displayMode(); @@ -991,6 +995,22 @@ bool LimeRFEGUI::handleMessage(const Message& message) highlightApplyButton(cfg.getForce()); return true; } + else if (LimeRFE::MsgReportSetRx::match(message)) + { + bool on = ((LimeRFE::MsgReportSetRx&) message).isOn(); + qDebug("LimeRFEGUI::handleMessage: LimeRFE::MsgReportSetRx: %s", on ? "on" : "off"); + m_rxOn = on; + displaySettings(); + return true; + } + else if (LimeRFE::MsgReportSetTx::match(message)) + { + bool on = ((LimeRFE::MsgReportSetTx&) message).isOn(); + qDebug("LimeRFEGUI::handleMessage: LimeRFE::MsgReportSetTx: %s", on ? "on" : "off"); + m_txOn = on; + displaySettings(); + return true; + } return false; } diff --git a/plugins/feature/limerfe/limerfegui.h b/plugins/feature/limerfe/limerfegui.h index 1d6688aea..f3563b8ea 100644 --- a/plugins/feature/limerfe/limerfegui.h +++ b/plugins/feature/limerfe/limerfegui.h @@ -65,6 +65,8 @@ private: LimeRFESettings m_settings; LimeRFEUSBCalib* m_limeRFEUSBCalib; RollupState m_rollupState; + bool m_rxOn; + bool m_txOn; bool m_doApplySettings; bool m_rxTxToggle; QTimer m_timer; diff --git a/plugins/feature/limerfe/limerfesettings.cpp b/plugins/feature/limerfe/limerfesettings.cpp index e164cc96d..12a87b6fa 100644 --- a/plugins/feature/limerfe/limerfesettings.cpp +++ b/plugins/feature/limerfe/limerfesettings.cpp @@ -48,8 +48,6 @@ void LimeRFESettings::resetToDefaults() m_swrEnable = false; m_swrSource = SWRExternal; m_txRxDriven = false; - m_rxOn = false; - m_txOn = false; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -79,8 +77,6 @@ QByteArray LimeRFESettings::serialize() const s.writeS32(16, (int) m_swrSource); s.writeBool(20, m_txRxDriven); - s.writeBool(21, m_rxOn); - s.writeBool(22, m_txOn); s.writeString(30, m_title); s.writeU32(31, m_rgbColor); @@ -145,8 +141,6 @@ bool LimeRFESettings::deserialize(const QByteArray& data) m_swrSource = (SWRSource) tmp; d.readBool(20, &m_txRxDriven, false); - d.readBool(21, &m_rxOn, false); - d.readBool(22, &m_txOn, false); d.readString(30, &m_title, "Lime RFE"); d.readU32(31, &m_rgbColor, QColor(50, 205, 50).rgb()); diff --git a/plugins/feature/limerfe/limerfesettings.h b/plugins/feature/limerfe/limerfesettings.h index 9e3a98180..2e29381b5 100644 --- a/plugins/feature/limerfe/limerfesettings.h +++ b/plugins/feature/limerfe/limerfesettings.h @@ -97,8 +97,6 @@ struct LimeRFESettings SWRSource m_swrSource; // Rx/Tx bool m_txRxDriven; //!< Tx settings set according to Rx settings - bool m_rxOn; - bool m_txOn; // Common QString m_devicePath; QString m_title; diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 7e220ddf6..423a2de1f 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -7092,6 +7092,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "Switch on or off\n * 0 - Off\n * 1 - On\n" }, + "getState" : { + "type" : "integer", + "description" : "Set to non zero value to get the board state" + }, "fromToSettings" : { "type" : "integer", "description" : "Move from/to settings to/from device\n * 0 - From device to settings. The toGUI button in GUI mode\n * 1 - From settings to device. The Apply button in GUI mode\n" @@ -7144,6 +7148,14 @@ margin-bottom: 20px; }; defs.LimeRFEReport = { "properties" : { + "rxOn" : { + "type" : "integer", + "description" : "Boolean 1 if Rx is active else 0" + }, + "txOn" : { + "type" : "integer", + "description" : "Boolean 1 if Tx is active else 0" + }, "forwardPower" : { "type" : "integer", "description" : "relative forward power in centi-Bels" @@ -7226,14 +7238,6 @@ margin-bottom: 20px; "type" : "integer", "description" : "Boolean 1 if Tx is copy of Rx else 0" }, - "rxOn" : { - "type" : "integer", - "description" : "Boolean 1 if Rx is active else 0" - }, - "txOn" : { - "type" : "integer", - "description" : "Boolean 1 if Tx is active else 0" - }, "useReverseAPI" : { "type" : "integer", "description" : "Synchronize with reverse API (1 for yes, 0 for no)" @@ -59773,7 +59777,7 @@ except ApiException as e:
- Generated 2022-05-21T22:11:42.796+02:00 + Generated 2022-05-22T12:29:41.738+02:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml index 81c17aa87..bee219f91 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml @@ -109,12 +109,6 @@ LimeRFESettings: txRxDriven: description: Boolean 1 if Tx is copy of Rx else 0 type: integer - rxOn: - description: Boolean 1 if Rx is active else 0 - type: integer - txOn: - description: Boolean 1 if Tx is active else 0 - type: integer useReverseAPI: description: Synchronize with reverse API (1 for yes, 0 for no) type: integer @@ -132,6 +126,12 @@ LimeRFESettings: LimeRFEReport: description: LimeRFE properties: + rxOn: + description: Boolean 1 if Rx is active else 0 + type: integer + txOn: + description: Boolean 1 if Tx is active else 0 + type: integer forwardPower: description: relative forward power in centi-Bels type: integer @@ -157,6 +157,9 @@ LimeRFEActions: Switch on or off * 0 - Off * 1 - On + getState: + type: integer + description: Set to non zero value to get the board state fromToSettings: type: integer description: > diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index ea5885018..ff92d1761 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -995,7 +995,6 @@ int WebAPIAdapter::instanceLimeRFEConfigGet( response.setRxHamChannel((int) settings.m_rxHAMChannel); response.setRxCellularChannel((int) settings.m_rxCellularChannel); response.setRxPort((int) settings.m_rxPort); - response.setRxOn(settings.m_rxOn ? 1 : 0); response.setAmfmNotch(settings.m_amfmNotch ? 1 : 0); response.setAttenuationFactor(settings.m_attenuationFactor); response.setTxChannels((int) settings.m_txChannels); @@ -1003,7 +1002,6 @@ int WebAPIAdapter::instanceLimeRFEConfigGet( response.setTxHamChannel((int) settings.m_txHAMChannel); response.setTxCellularChannel((int) settings.m_txCellularChannel); response.setTxPort((int) settings.m_txPort); - response.setTxOn(settings.m_txOn ? 1 : 0); response.setSwrEnable(settings.m_swrEnable ? 1 : 0); response.setSwrSource((int) settings.m_swrSource); @@ -1032,7 +1030,6 @@ int WebAPIAdapter::instanceLimeRFEConfigPut( settings.m_rxHAMChannel = (LimeRFEController::HAMChannel) query.getRxHamChannel(); settings.m_rxCellularChannel = (LimeRFEController::CellularChannel) query.getRxCellularChannel(); settings.m_rxPort = (LimeRFEController::RxPort) query.getRxPort(); - settings.m_rxOn = query.getRxOn() != 0; settings.m_amfmNotch = query.getAmfmNotch() != 0; settings.m_attenuationFactor = query.getAttenuationFactor(); settings.m_txChannels = (LimeRFEController::ChannelGroups) query.getTxChannels(); @@ -1040,7 +1037,6 @@ int WebAPIAdapter::instanceLimeRFEConfigPut( settings.m_txHAMChannel = (LimeRFEController::HAMChannel) query.getTxHamChannel(); settings.m_txCellularChannel = (LimeRFEController::CellularChannel) query.getTxCellularChannel(); settings.m_txPort = (LimeRFEController::TxPort) query.getTxPort(); - settings.m_txOn = query.getTxOn() != 0; settings.m_swrEnable = query.getSwrEnable() != 0; settings.m_swrSource = (LimeRFEController::SWRSource) query.getSwrSource(); @@ -1078,9 +1074,6 @@ int WebAPIAdapter::instanceLimeRFERunPut( } LimeRFEController::LimeRFESettings settings; - settings.m_rxOn = query.getRxOn() != 0; - settings.m_txOn = query.getTxOn() != 0; - rc = controller.setRx(settings, settings.m_rxOn); if (rc != 0) diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 5df2a6e71..edfb00858 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4371,16 +4371,6 @@ bool WebAPIRequestMapper::validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& limeRFESettings.setTxPort(jsonObject["txPort"].toInt()); limeRFESettingsKeys.append("txPort"); } - if (jsonObject.contains("rxOn")) - { - limeRFESettings.setRxOn(jsonObject["rxOn"].toInt()); - limeRFESettingsKeys.append("rxOn"); - } - if (jsonObject.contains("txOn")) - { - limeRFESettings.setTxOn(jsonObject["txOn"].toInt()); - limeRFESettingsKeys.append("txOn"); - } if (jsonObject.contains("swrEnable")) { limeRFESettings.setSwrEnable(jsonObject["swrEnable"].toInt()); diff --git a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml index 06fc4e70f..f8c0bebf0 100644 --- a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml +++ b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml @@ -109,12 +109,6 @@ LimeRFESettings: txRxDriven: description: Boolean 1 if Tx is copy of Rx else 0 type: integer - rxOn: - description: Boolean 1 if Rx is active else 0 - type: integer - txOn: - description: Boolean 1 if Tx is active else 0 - type: integer useReverseAPI: description: Synchronize with reverse API (1 for yes, 0 for no) type: integer @@ -132,6 +126,12 @@ LimeRFESettings: LimeRFEReport: description: LimeRFE properties: + rxOn: + description: Boolean 1 if Rx is active else 0 + type: integer + txOn: + description: Boolean 1 if Tx is active else 0 + type: integer forwardPower: description: relative forward power in centi-Bels type: integer @@ -157,6 +157,9 @@ LimeRFEActions: Switch on or off * 0 - Off * 1 - On + getState: + type: integer + description: Set to non zero value to get the board state fromToSettings: type: integer description: > diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 7e220ddf6..423a2de1f 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -7092,6 +7092,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "Switch on or off\n * 0 - Off\n * 1 - On\n" }, + "getState" : { + "type" : "integer", + "description" : "Set to non zero value to get the board state" + }, "fromToSettings" : { "type" : "integer", "description" : "Move from/to settings to/from device\n * 0 - From device to settings. The toGUI button in GUI mode\n * 1 - From settings to device. The Apply button in GUI mode\n" @@ -7144,6 +7148,14 @@ margin-bottom: 20px; }; defs.LimeRFEReport = { "properties" : { + "rxOn" : { + "type" : "integer", + "description" : "Boolean 1 if Rx is active else 0" + }, + "txOn" : { + "type" : "integer", + "description" : "Boolean 1 if Tx is active else 0" + }, "forwardPower" : { "type" : "integer", "description" : "relative forward power in centi-Bels" @@ -7226,14 +7238,6 @@ margin-bottom: 20px; "type" : "integer", "description" : "Boolean 1 if Tx is copy of Rx else 0" }, - "rxOn" : { - "type" : "integer", - "description" : "Boolean 1 if Rx is active else 0" - }, - "txOn" : { - "type" : "integer", - "description" : "Boolean 1 if Tx is active else 0" - }, "useReverseAPI" : { "type" : "integer", "description" : "Synchronize with reverse API (1 for yes, 0 for no)" @@ -59773,7 +59777,7 @@ except ApiException as e:
- Generated 2022-05-21T22:11:42.796+02:00 + Generated 2022-05-22T12:29:41.738+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp index 9484cf862..0ca4f36b8 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.cpp @@ -34,6 +34,8 @@ SWGLimeRFEActions::SWGLimeRFEActions() { m_device_set_index_isSet = false; switch_channel = 0; m_switch_channel_isSet = false; + get_state = 0; + m_get_state_isSet = false; from_to_settings = 0; m_from_to_settings_isSet = false; open_close_device = 0; @@ -52,6 +54,8 @@ SWGLimeRFEActions::init() { m_device_set_index_isSet = false; switch_channel = 0; m_switch_channel_isSet = false; + get_state = 0; + m_get_state_isSet = false; from_to_settings = 0; m_from_to_settings_isSet = false; open_close_device = 0; @@ -65,6 +69,7 @@ SWGLimeRFEActions::cleanup() { + } SWGLimeRFEActions* @@ -84,6 +89,8 @@ SWGLimeRFEActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&switch_channel, pJson["switchChannel"], "qint32", ""); + ::SWGSDRangel::setValue(&get_state, pJson["getState"], "qint32", ""); + ::SWGSDRangel::setValue(&from_to_settings, pJson["fromToSettings"], "qint32", ""); ::SWGSDRangel::setValue(&open_close_device, pJson["openCloseDevice"], "qint32", ""); @@ -113,6 +120,9 @@ SWGLimeRFEActions::asJsonObject() { if(m_switch_channel_isSet){ obj->insert("switchChannel", QJsonValue(switch_channel)); } + if(m_get_state_isSet){ + obj->insert("getState", QJsonValue(get_state)); + } if(m_from_to_settings_isSet){ obj->insert("fromToSettings", QJsonValue(from_to_settings)); } @@ -153,6 +163,16 @@ SWGLimeRFEActions::setSwitchChannel(qint32 switch_channel) { this->m_switch_channel_isSet = true; } +qint32 +SWGLimeRFEActions::getGetState() { + return get_state; +} +void +SWGLimeRFEActions::setGetState(qint32 get_state) { + this->get_state = get_state; + this->m_get_state_isSet = true; +} + qint32 SWGLimeRFEActions::getFromToSettings() { return from_to_settings; @@ -187,6 +207,9 @@ SWGLimeRFEActions::isSet(){ if(m_switch_channel_isSet){ isObjectUpdated = true; break; } + if(m_get_state_isSet){ + isObjectUpdated = true; break; + } if(m_from_to_settings_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h index 29fa4c518..74e1dc096 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEActions.h @@ -50,6 +50,9 @@ public: qint32 getSwitchChannel(); void setSwitchChannel(qint32 switch_channel); + qint32 getGetState(); + void setGetState(qint32 get_state); + qint32 getFromToSettings(); void setFromToSettings(qint32 from_to_settings); @@ -69,6 +72,9 @@ private: qint32 switch_channel; bool m_switch_channel_isSet; + qint32 get_state; + bool m_get_state_isSet; + qint32 from_to_settings; bool m_from_to_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp index 9df562a65..1b2078baa 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.cpp @@ -28,6 +28,10 @@ SWGLimeRFEReport::SWGLimeRFEReport(QString* json) { } SWGLimeRFEReport::SWGLimeRFEReport() { + rx_on = 0; + m_rx_on_isSet = false; + tx_on = 0; + m_tx_on_isSet = false; forward_power = 0; m_forward_power_isSet = false; reflected_power = 0; @@ -40,6 +44,10 @@ SWGLimeRFEReport::~SWGLimeRFEReport() { void SWGLimeRFEReport::init() { + rx_on = 0; + m_rx_on_isSet = false; + tx_on = 0; + m_tx_on_isSet = false; forward_power = 0; m_forward_power_isSet = false; reflected_power = 0; @@ -50,6 +58,8 @@ void SWGLimeRFEReport::cleanup() { + + } SWGLimeRFEReport* @@ -63,6 +73,10 @@ SWGLimeRFEReport::fromJson(QString &json) { void SWGLimeRFEReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&rx_on, pJson["rxOn"], "qint32", ""); + + ::SWGSDRangel::setValue(&tx_on, pJson["txOn"], "qint32", ""); + ::SWGSDRangel::setValue(&forward_power, pJson["forwardPower"], "qint32", ""); ::SWGSDRangel::setValue(&reflected_power, pJson["reflectedPower"], "qint32", ""); @@ -83,6 +97,12 @@ SWGLimeRFEReport::asJson () QJsonObject* SWGLimeRFEReport::asJsonObject() { QJsonObject* obj = new QJsonObject(); + if(m_rx_on_isSet){ + obj->insert("rxOn", QJsonValue(rx_on)); + } + if(m_tx_on_isSet){ + obj->insert("txOn", QJsonValue(tx_on)); + } if(m_forward_power_isSet){ obj->insert("forwardPower", QJsonValue(forward_power)); } @@ -93,6 +113,26 @@ SWGLimeRFEReport::asJsonObject() { return obj; } +qint32 +SWGLimeRFEReport::getRxOn() { + return rx_on; +} +void +SWGLimeRFEReport::setRxOn(qint32 rx_on) { + this->rx_on = rx_on; + this->m_rx_on_isSet = true; +} + +qint32 +SWGLimeRFEReport::getTxOn() { + return tx_on; +} +void +SWGLimeRFEReport::setTxOn(qint32 tx_on) { + this->tx_on = tx_on; + this->m_tx_on_isSet = true; +} + qint32 SWGLimeRFEReport::getForwardPower() { return forward_power; @@ -118,6 +158,12 @@ bool SWGLimeRFEReport::isSet(){ bool isObjectUpdated = false; do{ + if(m_rx_on_isSet){ + isObjectUpdated = true; break; + } + if(m_tx_on_isSet){ + isObjectUpdated = true; break; + } if(m_forward_power_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h index 6929fdf4f..49006caba 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFEReport.h @@ -41,6 +41,12 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGLimeRFEReport* fromJson(QString &jsonString) override; + qint32 getRxOn(); + void setRxOn(qint32 rx_on); + + qint32 getTxOn(); + void setTxOn(qint32 tx_on); + qint32 getForwardPower(); void setForwardPower(qint32 forward_power); @@ -51,6 +57,12 @@ public: virtual bool isSet() override; private: + qint32 rx_on; + bool m_rx_on_isSet; + + qint32 tx_on; + bool m_tx_on_isSet; + qint32 forward_power; bool m_forward_power_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp index c0e0e1098..cce21df5c 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.cpp @@ -64,10 +64,6 @@ SWGLimeRFESettings::SWGLimeRFESettings() { m_swr_source_isSet = false; tx_rx_driven = 0; m_tx_rx_driven_isSet = false; - rx_on = 0; - m_rx_on_isSet = false; - tx_on = 0; - m_tx_on_isSet = false; use_reverse_api = 0; m_use_reverse_api_isSet = false; reverse_api_address = nullptr; @@ -124,10 +120,6 @@ SWGLimeRFESettings::init() { m_swr_source_isSet = false; tx_rx_driven = 0; m_tx_rx_driven_isSet = false; - rx_on = 0; - m_rx_on_isSet = false; - tx_on = 0; - m_tx_on_isSet = false; use_reverse_api = 0; m_use_reverse_api_isSet = false; reverse_api_address = new QString(""); @@ -167,8 +159,6 @@ SWGLimeRFESettings::cleanup() { - - if(reverse_api_address != nullptr) { delete reverse_api_address; } @@ -227,10 +217,6 @@ SWGLimeRFESettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&tx_rx_driven, pJson["txRxDriven"], "qint32", ""); - ::SWGSDRangel::setValue(&rx_on, pJson["rxOn"], "qint32", ""); - - ::SWGSDRangel::setValue(&tx_on, pJson["txOn"], "qint32", ""); - ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); @@ -313,12 +299,6 @@ SWGLimeRFESettings::asJsonObject() { if(m_tx_rx_driven_isSet){ obj->insert("txRxDriven", QJsonValue(tx_rx_driven)); } - if(m_rx_on_isSet){ - obj->insert("rxOn", QJsonValue(rx_on)); - } - if(m_tx_on_isSet){ - obj->insert("txOn", QJsonValue(tx_on)); - } if(m_use_reverse_api_isSet){ obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); } @@ -521,26 +501,6 @@ SWGLimeRFESettings::setTxRxDriven(qint32 tx_rx_driven) { this->m_tx_rx_driven_isSet = true; } -qint32 -SWGLimeRFESettings::getRxOn() { - return rx_on; -} -void -SWGLimeRFESettings::setRxOn(qint32 rx_on) { - this->rx_on = rx_on; - this->m_rx_on_isSet = true; -} - -qint32 -SWGLimeRFESettings::getTxOn() { - return tx_on; -} -void -SWGLimeRFESettings::setTxOn(qint32 tx_on) { - this->tx_on = tx_on; - this->m_tx_on_isSet = true; -} - qint32 SWGLimeRFESettings::getUseReverseApi() { return use_reverse_api; @@ -660,12 +620,6 @@ SWGLimeRFESettings::isSet(){ if(m_tx_rx_driven_isSet){ isObjectUpdated = true; break; } - if(m_rx_on_isSet){ - isObjectUpdated = true; break; - } - if(m_tx_on_isSet){ - isObjectUpdated = true; break; - } if(m_use_reverse_api_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h index e947fead1..a36df8076 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeRFESettings.h @@ -97,12 +97,6 @@ public: qint32 getTxRxDriven(); void setTxRxDriven(qint32 tx_rx_driven); - qint32 getRxOn(); - void setRxOn(qint32 rx_on); - - qint32 getTxOn(); - void setTxOn(qint32 tx_on); - qint32 getUseReverseApi(); void setUseReverseApi(qint32 use_reverse_api); @@ -179,12 +173,6 @@ private: qint32 tx_rx_driven; bool m_tx_rx_driven_isSet; - qint32 rx_on; - bool m_rx_on_isSet; - - qint32 tx_on; - bool m_tx_on_isSet; - qint32 use_reverse_api; bool m_use_reverse_api_isSet; From c057c86ef93afd8af853994647d3992e4616dbca Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 22 May 2022 20:59:34 +0200 Subject: [PATCH 05/29] Removed instance LimeRFE API --- sdrbase/resources/webapi/doc/html2/index.html | 2018 +---------------- .../webapi/doc/swagger/include/LimeRFE.yaml | 10 - .../resources/webapi/doc/swagger/swagger.yaml | 122 - sdrbase/webapi/webapiadapter.cpp | 207 -- sdrbase/webapi/webapiadapter.h | 26 - sdrbase/webapi/webapiadapterinterface.cpp | 4 - sdrbase/webapi/webapiadapterinterface.h | 20 - sdrbase/webapi/webapirequestmapper.cpp | 287 --- sdrbase/webapi/webapirequestmapper.h | 5 - .../sdrangel/api/swagger/include/LimeRFE.yaml | 10 - swagger/sdrangel/api/swagger/swagger.yaml | 122 - swagger/sdrangel/code/html2/index.html | 2018 +---------------- .../code/qt5/client/SWGInstanceApi.cpp | 282 --- .../sdrangel/code/qt5/client/SWGInstanceApi.h | 29 - .../code/qt5/client/SWGLimeRFEPower.cpp | 131 -- .../code/qt5/client/SWGLimeRFEPower.h | 64 - .../code/qt5/client/SWGModelFactory.h | 6 - 17 files changed, 2 insertions(+), 5359 deletions(-) delete mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 423a2de1f..474dfdf12 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -7132,19 +7132,6 @@ margin-bottom: 20px; } }, "description" : "List of LimeRFE devices (serial or server address)" -}; - defs.LimeRFEPower = { - "properties" : { - "forward" : { - "type" : "integer", - "description" : "relative forward power in centi-Bels" - }, - "reflected" : { - "type" : "integer", - "description" : "relative reflected power in centi-Bels" - } - }, - "description" : "report of forward and reflected power measurements TO BE DECOMMISSIONED" }; defs.LimeRFEReport = { "properties" : { @@ -14814,21 +14801,6 @@ margin-bottom: 20px;
  • instanceFeatures
  • -
  • - instanceLimeRFEConfigGet -
  • -
  • - instanceLimeRFEConfigPut -
  • -
  • - instanceLimeRFEPowerGet -
  • -
  • - instanceLimeRFERunPut -
  • -
  • - instanceLimeRFESerialGet -
  • instanceLocationGet
  • @@ -51155,1994 +51127,6 @@ except ApiException as e:
    -
    -
    -
    -

    instanceLimeRFEConfigGet

    -

    -
    -
    -
    -

    -

    get LimeRFE configuration

    -

    -
    -
    /sdrangel/limerfe/config
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/config?serial="
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    String *serial = serial_example; // device serial path
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEConfigGetWith:serial
    -              completionHandler: ^(LimeRFESettings output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var serial = serial_example; // {String} device serial path
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEConfigGet(serial, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEConfigGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var serial = serial_example;  // String | device serial path
    -
    -            try
    -            {
    -                LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEConfigGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$serial = serial_example; // String | device serial path
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEConfigGet($serial);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEConfigGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $serial = serial_example; # String | device serial path
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEConfigGet(serial => $serial);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEConfigGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -serial = serial_example # String | device serial path
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_config_get(serial)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEConfigGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - -
    Query parameters
    - - - - - - - - - -
    NameDescription
    serial* - - -
    -
    -
    - - String - - -
    - device serial path -
    -
    -
    - Required -
    -
    -
    -
    - -

    Responses

    -

    Status: 200 - On success return configuration information for the given device in input

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFEConfigPut

    -

    -
    -
    -
    -

    -

    replace LimeRFE configuration

    -

    -
    -
    /sdrangel/limerfe/config
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/limerfe/config"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    LimeRFESettings *body = ; // Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEConfigPutWith:body
    -              completionHandler: ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {LimeRFESettings} Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEConfigPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEConfigPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new LimeRFESettings(); // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEConfigPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEConfigPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEConfigPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::LimeRFESettings->new(); # LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEConfigPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEConfigPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_config_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEConfigPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - Success

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFEPowerGet

    -

    -
    -
    -
    -

    -

    get forward and reflected relative powers in centi-Bels

    -

    -
    -
    /sdrangel/limerfe/power
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/power?serial="
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEPowerGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEPowerGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    String *serial = serial_example; // device serial path
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEPowerGetWith:serial
    -              completionHandler: ^(LimeRFEPower output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var serial = serial_example; // {String} device serial path
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEPowerGet(serial, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEPowerGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var serial = serial_example;  // String | device serial path
    -
    -            try
    -            {
    -                LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEPowerGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$serial = serial_example; // String | device serial path
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEPowerGet($serial);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEPowerGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $serial = serial_example; # String | device serial path
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEPowerGet(serial => $serial);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEPowerGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -serial = serial_example # String | device serial path
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_power_get(serial)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEPowerGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - -
    Query parameters
    - - - - - - - - - -
    NameDescription
    serial* - - -
    -
    -
    - - String - - -
    - device serial path -
    -
    -
    - Required -
    -
    -
    -
    - -

    Responses

    -

    Status: 200 - On success return forward and reflected powers in centi-Bels

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFERunPut

    -

    -
    -
    -
    -

    -

    set Rx and Tx on or off

    -

    -
    -
    /sdrangel/limerfe/run
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/limerfe/run"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFERunPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFERunPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    LimeRFESettings *body = ; // Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFERunPutWith:body
    -              completionHandler: ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {LimeRFESettings} Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFERunPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFERunPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new LimeRFESettings(); // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFERunPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -try {
    -    $result = $api_instance->instanceLimeRFERunPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFERunPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::LimeRFESettings->new(); # LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFERunPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFERunPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_run_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFERunPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - Success

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFESerialGet

    -

    -
    -
    -
    -

    -

    get a list of available serial interfaces to LimeRFE device

    -

    -
    -
    /sdrangel/limerfe/serial
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/serial"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFESerialGetWithCompletionHandler: 
    -              ^(LimeRFEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFESerialGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFESerialGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFESerialGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceLimeRFESerialGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFESerialGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFESerialGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFESerialGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_serial_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFESerialGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of device paths possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -59777,7 +57761,7 @@ except ApiException as e:
    - Generated 2022-05-22T12:29:41.738+02:00 + Generated 2022-05-22T20:12:13.506+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml index bee219f91..8757eb8ee 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/LimeRFE.yaml @@ -172,13 +172,3 @@ LimeRFEActions: Open or close device * 0 - Close device * 1 - Open device - -LimeRFEPower: - description: report of forward and reflected power measurements TO BE DECOMMISSIONED - properties: - forward: - description: relative forward power in centi-Bels - type: integer - reflected: - description: relative reflected power in centi-Bels - type: integer diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index a81ae6e1a..5c3799cfe 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -548,128 +548,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/limerfe/serial: - x-swagger-router-controller: instance - get: - description: get a list of available serial interfaces to LimeRFE device - operationId: instanceLimeRFESerialGet - tags: - - Instance - responses: - "200": - description: On success return list of device paths possibly empty - schema: - $ref: "#/definitions/LimeRFEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/config: - x-swagger-router-controller: instance - get: - description: get LimeRFE configuration - operationId: instanceLimeRFEConfigGet - tags: - - Instance - parameters: - - name: serial - in: query - description: device serial path - required: true - type: string - responses: - "200": - description: On success return configuration information for the given device in input - schema: - $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFESettings" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - put: - description: replace LimeRFE configuration - operationId: instanceLimeRFEConfigPut - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API. - required: true - schema: - $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFESettings" - responses: - "200": - description: Success - schema: - $ref: "#/definitions/SuccessResponse" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/run: - x-swagger-router-controller: instance - put: - description: set Rx and Tx on or off - operationId: instanceLimeRFERunPut - tags: - - Instance - parameters: - - name: body - in: body - description: Give device serial path in devicePath field and run status in rxOn and txOn - required: true - schema: - $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFESettings" - responses: - "200": - description: Success - schema: - $ref: "#/definitions/SuccessResponse" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/power: - x-swagger-router-controller: instance - get: - description: get forward and reflected relative powers in centi-Bels - operationId: instanceLimeRFEPowerGet - tags: - - Instance - parameters: - - name: serial - in: query - description: device serial path - required: true - type: string - responses: - "200": - description: On success return forward and reflected powers in centi-Bels - schema: - $ref: "/doc/swagger/include/LimeRFE.yaml#/LimeRFEPower" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/presets: x-swagger-router-controller: instance get: diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index ff92d1761..b7c30ad7f 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -79,7 +79,6 @@ #include "SWGDeviceState.h" #include "SWGLimeRFEDevices.h" #include "SWGLimeRFESettings.h" -#include "SWGLimeRFEPower.h" #include "SWGFeaturePresets.h" #include "SWGFeaturePresetGroup.h" #include "SWGFeaturePresetItem.h" @@ -88,10 +87,6 @@ #include "SWGFeatureReport.h" #include "SWGFeatureActions.h" -#ifdef HAS_LIMERFEUSB -#include "limerfe/limerfecontroller.h" -#endif - #include "webapiadapter.h" WebAPIAdapter::WebAPIAdapter() @@ -934,208 +929,6 @@ int WebAPIAdapter::instanceAMBEDevicesPatch( return 200; } -#ifdef HAS_LIMERFEUSB -int WebAPIAdapter::instanceLimeRFESerialGet( - SWGSDRangel::SWGLimeRFEDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - response.init(); - std::vector comPorts; - SerialUtil::getComPorts(comPorts, "ttyUSB[0-9]+"); // regex is for Linux only - response.setNbDevices((int) comPorts.size()); - QList *deviceNamesList = response.getLimeRfeDevices(); - std::vector::iterator it = comPorts.begin(); - - while (it != comPorts.end()) - { - deviceNamesList->append(new SWGSDRangel::SWGLimeRFEDevice); - deviceNamesList->back()->init(); - *deviceNamesList->back()->getDeviceRef() = QString::fromStdString(*it); - ++it; - } - - return 200; -} - -int WebAPIAdapter::instanceLimeRFEConfigGet( - const QString& serial, - SWGSDRangel::SWGLimeRFESettings& response, - SWGSDRangel::SWGErrorResponse& error) -{ - LimeRFEController controller; - int rc = controller.openDevice(serial.toStdString()); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error opening LimeRFE device %1: %2") - .arg(serial).arg(controller.getError(rc).c_str()); - return 400; - } - - rc = controller.getState(); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error getting config from LimeRFE device %1: %2") - .arg(serial).arg(controller.getError(rc).c_str()); - return 500; - } - - controller.closeDevice(); - - LimeRFEController::LimeRFESettings settings; - controller.stateToSettings(settings); - response.init(); - response.setDevicePath(new QString(serial)); - response.setRxChannels((int) settings.m_rxChannels); - response.setRxWidebandChannel((int) settings.m_rxWidebandChannel); - response.setRxHamChannel((int) settings.m_rxHAMChannel); - response.setRxCellularChannel((int) settings.m_rxCellularChannel); - response.setRxPort((int) settings.m_rxPort); - response.setAmfmNotch(settings.m_amfmNotch ? 1 : 0); - response.setAttenuationFactor(settings.m_attenuationFactor); - response.setTxChannels((int) settings.m_txChannels); - response.setTxWidebandChannel((int) settings.m_txWidebandChannel); - response.setTxHamChannel((int) settings.m_txHAMChannel); - response.setTxCellularChannel((int) settings.m_txCellularChannel); - response.setTxPort((int) settings.m_txPort); - response.setSwrEnable(settings.m_swrEnable ? 1 : 0); - response.setSwrSource((int) settings.m_swrSource); - - return 200; -} - -int WebAPIAdapter::instanceLimeRFEConfigPut( - SWGSDRangel::SWGLimeRFESettings& query, - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) -{ - LimeRFEController controller; - int rc = controller.openDevice(query.getDevicePath()->toStdString()); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error opening LimeRFE device %1: %2") - .arg(*query.getDevicePath()).arg(controller.getError(rc).c_str()); - return 400; - } - - LimeRFEController::LimeRFESettings settings; - settings.m_rxChannels = (LimeRFEController::ChannelGroups) query.getRxChannels(); - settings.m_rxWidebandChannel = (LimeRFEController::WidebandChannel) query.getRxWidebandChannel(); - settings.m_rxHAMChannel = (LimeRFEController::HAMChannel) query.getRxHamChannel(); - settings.m_rxCellularChannel = (LimeRFEController::CellularChannel) query.getRxCellularChannel(); - settings.m_rxPort = (LimeRFEController::RxPort) query.getRxPort(); - settings.m_amfmNotch = query.getAmfmNotch() != 0; - settings.m_attenuationFactor = query.getAttenuationFactor(); - settings.m_txChannels = (LimeRFEController::ChannelGroups) query.getTxChannels(); - settings.m_txWidebandChannel = (LimeRFEController::WidebandChannel) query.getTxWidebandChannel(); - settings.m_txHAMChannel = (LimeRFEController::HAMChannel) query.getTxHamChannel(); - settings.m_txCellularChannel = (LimeRFEController::CellularChannel) query.getTxCellularChannel(); - settings.m_txPort = (LimeRFEController::TxPort) query.getTxPort(); - settings.m_swrEnable = query.getSwrEnable() != 0; - settings.m_swrSource = (LimeRFEController::SWRSource) query.getSwrSource(); - - controller.settingsToState(settings); - - rc = controller.configure(); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error configuring LimeRFE device %1: %2") - .arg(*query.getDevicePath()).arg(controller.getError(rc).c_str()); - return 500; - } - - response.init(); - *response.getMessage() = QString("LimeRFE device at %1 configuration updated successfully").arg(*query.getDevicePath()); - return 200; -} - -int WebAPIAdapter::instanceLimeRFERunPut( - SWGSDRangel::SWGLimeRFESettings& query, - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) -{ - LimeRFEController controller; - int rc = controller.openDevice(query.getDevicePath()->toStdString()); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error opening LimeRFE device %1: %2") - .arg(*query.getDevicePath()).arg(controller.getError(rc).c_str()); - return 400; - } - - LimeRFEController::LimeRFESettings settings; - rc = controller.setRx(settings, settings.m_rxOn); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error setting Rx/Tx LimeRFE device %1: %2") - .arg(*query.getDevicePath()).arg(controller.getError(rc).c_str()); - return 400; - } - - response.init(); - *response.getMessage() = QString("LimeRFE device at %1 mode updated successfully").arg(*query.getDevicePath()); - return 200; -} - -int WebAPIAdapter::instanceLimeRFEPowerGet( - const QString& serial, - SWGSDRangel::SWGLimeRFEPower& response, - SWGSDRangel::SWGErrorResponse& error) -{ - LimeRFEController controller; - int rc = controller.openDevice(serial.toStdString()); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error opening LimeRFE device %1: %2") - .arg(serial).arg(controller.getError(rc).c_str()); - return 400; - } - - int fwdPower; - rc = controller.getFwdPower(fwdPower); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error getting forward power from LimeRFE device %1: %2") - .arg(serial).arg(controller.getError(rc).c_str()); - return 500; - } - - int refPower; - rc = controller.getRefPower(refPower); - - if (rc != 0) - { - error.init(); - *error.getMessage() = QString("Error getting reflected power from LimeRFE device %1: %2") - .arg(serial).arg(controller.getError(rc).c_str()); - return 500; - } - - controller.closeDevice(); - - response.init(); - response.setForward(fwdPower); - response.setReflected(refPower); - return 200; -} -#endif - int WebAPIAdapter::instancePresetsGet( SWGSDRangel::SWGPresets& response, SWGSDRangel::SWGErrorResponse& error) diff --git a/sdrbase/webapi/webapiadapter.h b/sdrbase/webapi/webapiadapter.h index 0608f0060..22e6aaddb 100644 --- a/sdrbase/webapi/webapiadapter.h +++ b/sdrbase/webapi/webapiadapter.h @@ -146,32 +146,6 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); -#ifdef HAS_LIMERFEUSB - virtual int instanceLimeRFESerialGet( - SWGSDRangel::SWGLimeRFEDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceLimeRFEConfigGet( - const QString& serial, - SWGSDRangel::SWGLimeRFESettings& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceLimeRFEConfigPut( - SWGSDRangel::SWGLimeRFESettings& query, - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceLimeRFERunPut( - SWGSDRangel::SWGLimeRFESettings& query, - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceLimeRFEPowerGet( - const QString& serial, - SWGSDRangel::SWGLimeRFEPower& response, - SWGSDRangel::SWGErrorResponse& error); -#endif - virtual int instancePresetsGet( SWGSDRangel::SWGPresets& response, SWGSDRangel::SWGErrorResponse& error); diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index 3c10c31a0..9c3e15048 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -33,10 +33,6 @@ QString WebAPIAdapterInterface::instanceAudioOutputCleanupURL = "/sdrangel/audio QString WebAPIAdapterInterface::instanceLocationURL = "/sdrangel/location"; QString WebAPIAdapterInterface::instanceAMBESerialURL = "/sdrangel/ambe/serial"; QString WebAPIAdapterInterface::instanceAMBEDevicesURL = "/sdrangel/ambe/devices"; -QString WebAPIAdapterInterface::instanceLimeRFESerialURL = "/sdrangel/limerfe/serial"; -QString WebAPIAdapterInterface::instanceLimeRFEConfigURL = "/sdrangel/limerfe/config"; -QString WebAPIAdapterInterface::instanceLimeRFERunURL = "/sdrangel/limerfe/run"; -QString WebAPIAdapterInterface::instanceLimeRFEPowerURL = "/sdrangel/limerfe/power"; QString WebAPIAdapterInterface::instancePresetsURL = "/sdrangel/presets"; QString WebAPIAdapterInterface::instancePresetURL = "/sdrangel/preset"; QString WebAPIAdapterInterface::instancePresetFileURL = "/sdrangel/preset/file"; diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index 41a9e2c3b..302dfb3c0 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -537,22 +537,6 @@ public: return 501; } - /** - * Handler of /sdrangel/limerfe/power (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceLimeRFEPowerGet( - const QString& serial, - SWGSDRangel::SWGLimeRFEPower& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) serial; - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - /** * Handler of /sdrangel/presets (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) @@ -1788,10 +1772,6 @@ public: static QString instanceLocationURL; static QString instanceAMBESerialURL; static QString instanceAMBEDevicesURL; - static QString instanceLimeRFESerialURL; - static QString instanceLimeRFEConfigURL; - static QString instanceLimeRFERunURL; - static QString instanceLimeRFEPowerURL; static QString instancePresetsURL; static QString instancePresetURL; static QString instancePresetFileURL; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index edfb00858..a17083c6f 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -37,7 +37,6 @@ #include "SWGAMBEDevices.h" #include "SWGLimeRFEDevices.h" #include "SWGLimeRFESettings.h" -#include "SWGLimeRFEPower.h" #include "SWGPresets.h" #include "SWGPresetTransfer.h" #include "SWGPresetIdentifier.h" @@ -143,14 +142,6 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http instanceAMBESerialService(request, response); } else if (path == WebAPIAdapterInterface::instanceAMBEDevicesURL) { instanceAMBEDevicesService(request, response); - } else if (path == WebAPIAdapterInterface::instanceLimeRFESerialURL) { - instanceLimeRFESerialService(request, response); - } else if (path == WebAPIAdapterInterface::instanceLimeRFEConfigURL) { - instanceLimeRFEConfigService(request, response); - } else if (path == WebAPIAdapterInterface::instanceLimeRFERunURL) { - instanceLimeRFERunService(request, response); - } else if (path == WebAPIAdapterInterface::instanceLimeRFEPowerURL) { - instanceLimeRFEPowerService(request, response); } else if (path == WebAPIAdapterInterface::instancePresetsURL) { instancePresetsService(request, response); } else if (path == WebAPIAdapterInterface::instancePresetURL) { @@ -920,203 +911,6 @@ void WebAPIRequestMapper::instanceAMBEDevicesService(qtwebapp::HttpRequest& requ } } -void WebAPIRequestMapper::instanceLimeRFESerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - SWGSDRangel::SWGLimeRFEDevices normalResponse; - - int status = m_adapter->instanceLimeRFESerialGet(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - -void WebAPIRequestMapper::instanceLimeRFEConfigService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - QByteArray serialStr = request.getParameter("serial"); - SWGSDRangel::SWGLimeRFESettings normalResponse; - - int status = m_adapter->instanceLimeRFEConfigGet(serialStr, normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else if (request.getMethod() == "PUT") - { - SWGSDRangel::SWGLimeRFESettings query; - SWGSDRangel::SWGSuccessResponse normalResponse; - QString jsonStr = request.getBody(); - QJsonObject jsonObject; - - if (parseJsonBody(jsonStr, jsonObject, response)) - { - QStringList limeRFESettingsKeys; - - if (validateLimeRFEConfig(query, jsonObject, limeRFESettingsKeys)) - { - if (limeRFESettingsKeys.contains("devicePath")) - { - int status = m_adapter->instanceLimeRFEConfigPut(query, normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"LimeRFE device path expected in JSON body"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid request"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON format"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON format"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - -void WebAPIRequestMapper::instanceLimeRFERunService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "PUT") - { - SWGSDRangel::SWGLimeRFESettings query; - QString jsonStr = request.getBody(); - QJsonObject jsonObject; - - if (parseJsonBody(jsonStr, jsonObject, response)) - { - QStringList limeRFESettingsKeys; - - if (validateLimeRFEConfig(query, jsonObject, limeRFESettingsKeys)) - { - if (limeRFESettingsKeys.contains("devicePath")) - { - SWGSDRangel::SWGSuccessResponse normalResponse; - int status = m_adapter->instanceLimeRFERunPut(query, normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"LimeRFE device path expected in JSON body"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON format"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON format"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - -void WebAPIRequestMapper::instanceLimeRFEPowerService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - QByteArray serialStr = request.getParameter("serial"); - SWGSDRangel::SWGLimeRFEPower normalResponse; - - int status = m_adapter->instanceLimeRFEPowerGet(serialStr, normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - void WebAPIRequestMapper::instancePresetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -4304,87 +4098,6 @@ bool WebAPIRequestMapper::validateAMBEDevices(SWGSDRangel::SWGAMBEDevices& ambeD return false; } -bool WebAPIRequestMapper::validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& limeRFESettings, QJsonObject& jsonObject, QStringList& limeRFESettingsKeys) -{ - if (jsonObject.contains("devicePath")) - { - limeRFESettings.setDevicePath(new QString(jsonObject["devicePath"].toString())); - limeRFESettingsKeys.append("devicePath"); - } - if (jsonObject.contains("rxChannels")) - { - limeRFESettings.setRxChannels(jsonObject["rxChannels"].toInt()); - limeRFESettingsKeys.append("rxChannels"); - } - if (jsonObject.contains("rxWidebandChannel")) - { - limeRFESettings.setRxWidebandChannel(jsonObject["rxWidebandChannel"].toInt()); - limeRFESettingsKeys.append("rxWidebandChannel"); - } - if (jsonObject.contains("rxHAMChannel")) - { - limeRFESettings.setRxHamChannel(jsonObject["rxHAMChannel"].toInt()); - limeRFESettingsKeys.append("rxHAMChannel"); - } - if (jsonObject.contains("rxCellularChannel")) - { - limeRFESettings.setRxCellularChannel(jsonObject["rxCellularChannel"].toInt()); - limeRFESettingsKeys.append("rxCellularChannel"); - } - if (jsonObject.contains("rxPort")) - { - limeRFESettings.setRxPort(jsonObject["rxPort"].toInt()); - limeRFESettingsKeys.append("rxPort"); - } - if (jsonObject.contains("attenuationFactor")) - { - limeRFESettings.setAttenuationFactor(jsonObject["attenuationFactor"].toInt()); - limeRFESettingsKeys.append("attenuationFactor"); - } - if (jsonObject.contains("amfmNotch")) - { - limeRFESettings.setAmfmNotch(jsonObject["amfmNotch"].toInt()); - limeRFESettingsKeys.append("amfmNotch"); - } - if (jsonObject.contains("txChannels")) - { - limeRFESettings.setTxChannels(jsonObject["txChannels"].toInt()); - limeRFESettingsKeys.append("txChannels"); - } - if (jsonObject.contains("txWidebandChannel")) - { - limeRFESettings.setTxWidebandChannel(jsonObject["txWidebandChannel"].toInt()); - limeRFESettingsKeys.append("txWidebandChannel"); - } - if (jsonObject.contains("txHAMChannel")) - { - limeRFESettings.setTxHamChannel(jsonObject["txHAMChannel"].toInt()); - limeRFESettingsKeys.append("txHAMChannel"); - } - if (jsonObject.contains("txCellularChannel")) - { - limeRFESettings.setTxCellularChannel(jsonObject["txCellularChannel"].toInt()); - limeRFESettingsKeys.append("txCellularChannel"); - } - if (jsonObject.contains("txPort")) - { - limeRFESettings.setTxPort(jsonObject["txPort"].toInt()); - limeRFESettingsKeys.append("txPort"); - } - if (jsonObject.contains("swrEnable")) - { - limeRFESettings.setSwrEnable(jsonObject["swrEnable"].toInt()); - limeRFESettingsKeys.append("swrEnable"); - } - if (jsonObject.contains("swrSource")) - { - limeRFESettings.setSwrSource(jsonObject["swrSource"].toInt()); - limeRFESettingsKeys.append("swrSource"); - } - - return true; -} - bool WebAPIRequestMapper::validateSpectrumSettings(SWGSDRangel::SWGGLSpectrum& spectrumSettings, QJsonObject& jsonObject, QStringList& spectrumSettingsKeys) { extractKeys(jsonObject, spectrumSettingsKeys); diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index 048d4a02b..2bf22e846 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -69,10 +69,6 @@ private: void instanceDVSerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceAMBESerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceAMBEDevicesService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceLimeRFESerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceLimeRFEConfigService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceLimeRFERunService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceLimeRFEPowerService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetFileService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); @@ -131,7 +127,6 @@ private: bool validateAudioInputDevice(SWGSDRangel::SWGAudioInputDevice& audioInputDevice, QJsonObject& jsonObject, QStringList& audioInputDeviceKeys); bool validateAudioOutputDevice(SWGSDRangel::SWGAudioOutputDevice& audioOutputDevice, QJsonObject& jsonObject, QStringList& audioOutputDeviceKeys); bool validateAMBEDevices(SWGSDRangel::SWGAMBEDevices& ambeDevices, QJsonObject& jsonObject); - bool validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& limeRFESettings, QJsonObject& jsonObject, QStringList& limeRFESettingsKeys); bool validateConfig(SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, WebAPIAdapterInterface::ConfigKeys& configKeys); bool validateWorkspaceInfo(SWGSDRangel::SWGWorkspaceInfo& workspaceInfo, QJsonObject& jsonObject); bool validateConfigurationIdentifier(SWGSDRangel::SWGConfigurationIdentifier& configurationIdentifier); diff --git a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml index f8c0bebf0..b1704019a 100644 --- a/swagger/sdrangel/api/swagger/include/LimeRFE.yaml +++ b/swagger/sdrangel/api/swagger/include/LimeRFE.yaml @@ -172,13 +172,3 @@ LimeRFEActions: Open or close device * 0 - Close device * 1 - Open device - -LimeRFEPower: - description: report of forward and reflected power measurements TO BE DECOMMISSIONED - properties: - forward: - description: relative forward power in centi-Bels - type: integer - reflected: - description: relative reflected power in centi-Bels - type: integer diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index bc41da17a..70b12dfad 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -548,128 +548,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/limerfe/serial: - x-swagger-router-controller: instance - get: - description: get a list of available serial interfaces to LimeRFE device - operationId: instanceLimeRFESerialGet - tags: - - Instance - responses: - "200": - description: On success return list of device paths possibly empty - schema: - $ref: "#/definitions/LimeRFEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/config: - x-swagger-router-controller: instance - get: - description: get LimeRFE configuration - operationId: instanceLimeRFEConfigGet - tags: - - Instance - parameters: - - name: serial - in: query - description: device serial path - required: true - type: string - responses: - "200": - description: On success return configuration information for the given device in input - schema: - $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFESettings" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - put: - description: replace LimeRFE configuration - operationId: instanceLimeRFEConfigPut - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API. - required: true - schema: - $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFESettings" - responses: - "200": - description: Success - schema: - $ref: "#/definitions/SuccessResponse" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/run: - x-swagger-router-controller: instance - put: - description: set Rx and Tx on or off - operationId: instanceLimeRFERunPut - tags: - - Instance - parameters: - - name: body - in: body - description: Give device serial path in devicePath field and run status in rxOn and txOn - required: true - schema: - $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFESettings" - responses: - "200": - description: Success - schema: - $ref: "#/definitions/SuccessResponse" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/limerfe/power: - x-swagger-router-controller: instance - get: - description: get forward and reflected relative powers in centi-Bels - operationId: instanceLimeRFEPowerGet - tags: - - Instance - parameters: - - name: serial - in: query - description: device serial path - required: true - type: string - responses: - "200": - description: On success return forward and reflected powers in centi-Bels - schema: - $ref: "http://swgserver:8081/api/swagger/include/LimeRFE.yaml#/LimeRFEPower" - "400": - description: Error - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/presets: x-swagger-router-controller: instance get: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 423a2de1f..474dfdf12 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -7132,19 +7132,6 @@ margin-bottom: 20px; } }, "description" : "List of LimeRFE devices (serial or server address)" -}; - defs.LimeRFEPower = { - "properties" : { - "forward" : { - "type" : "integer", - "description" : "relative forward power in centi-Bels" - }, - "reflected" : { - "type" : "integer", - "description" : "relative reflected power in centi-Bels" - } - }, - "description" : "report of forward and reflected power measurements TO BE DECOMMISSIONED" }; defs.LimeRFEReport = { "properties" : { @@ -14814,21 +14801,6 @@ margin-bottom: 20px;
  • instanceFeatures
  • -
  • - instanceLimeRFEConfigGet -
  • -
  • - instanceLimeRFEConfigPut -
  • -
  • - instanceLimeRFEPowerGet -
  • -
  • - instanceLimeRFERunPut -
  • -
  • - instanceLimeRFESerialGet -
  • instanceLocationGet
  • @@ -51155,1994 +51127,6 @@ except ApiException as e:
    -
    -
    -
    -

    instanceLimeRFEConfigGet

    -

    -
    -
    -
    -

    -

    get LimeRFE configuration

    -

    -
    -
    /sdrangel/limerfe/config
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/config?serial="
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    String *serial = serial_example; // device serial path
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEConfigGetWith:serial
    -              completionHandler: ^(LimeRFESettings output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var serial = serial_example; // {String} device serial path
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEConfigGet(serial, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEConfigGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var serial = serial_example;  // String | device serial path
    -
    -            try
    -            {
    -                LimeRFESettings result = apiInstance.instanceLimeRFEConfigGet(serial);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEConfigGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$serial = serial_example; // String | device serial path
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEConfigGet($serial);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEConfigGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $serial = serial_example; # String | device serial path
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEConfigGet(serial => $serial);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEConfigGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -serial = serial_example # String | device serial path
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_config_get(serial)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEConfigGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - -
    Query parameters
    - - - - - - - - - -
    NameDescription
    serial* - - -
    -
    -
    - - String - - -
    - device serial path -
    -
    -
    - Required -
    -
    -
    -
    - -

    Responses

    -

    Status: 200 - On success return configuration information for the given device in input

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFEConfigPut

    -

    -
    -
    -
    -

    -

    replace LimeRFE configuration

    -

    -
    -
    /sdrangel/limerfe/config
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/limerfe/config"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEConfigPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    LimeRFESettings *body = ; // Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEConfigPutWith:body
    -              completionHandler: ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {LimeRFESettings} Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEConfigPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEConfigPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new LimeRFESettings(); // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceLimeRFEConfigPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEConfigPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEConfigPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEConfigPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::LimeRFESettings->new(); # LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEConfigPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEConfigPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # LimeRFESettings | Give device serial path in devicePath field. To switch Rx and/or Tx on or off use the run API.
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_config_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEConfigPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - Success

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFEPowerGet

    -

    -
    -
    -
    -

    -

    get forward and reflected relative powers in centi-Bels

    -

    -
    -
    /sdrangel/limerfe/power
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/power?serial="
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEPowerGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        String serial = serial_example; // String | device serial path
    -        try {
    -            LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFEPowerGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    String *serial = serial_example; // device serial path
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFEPowerGetWith:serial
    -              completionHandler: ^(LimeRFEPower output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var serial = serial_example; // {String} device serial path
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFEPowerGet(serial, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFEPowerGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var serial = serial_example;  // String | device serial path
    -
    -            try
    -            {
    -                LimeRFEPower result = apiInstance.instanceLimeRFEPowerGet(serial);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFEPowerGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$serial = serial_example; // String | device serial path
    -
    -try {
    -    $result = $api_instance->instanceLimeRFEPowerGet($serial);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFEPowerGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $serial = serial_example; # String | device serial path
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFEPowerGet(serial => $serial);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFEPowerGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -serial = serial_example # String | device serial path
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_power_get(serial)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFEPowerGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - -
    Query parameters
    - - - - - - - - - -
    NameDescription
    serial* - - -
    -
    -
    - - String - - -
    - device serial path -
    -
    -
    - Required -
    -
    -
    -
    - -

    Responses

    -

    Status: 200 - On success return forward and reflected powers in centi-Bels

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFERunPut

    -

    -
    -
    -
    -

    -

    set Rx and Tx on or off

    -

    -
    -
    /sdrangel/limerfe/run
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/limerfe/run"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFERunPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        LimeRFESettings body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -        try {
    -            SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFERunPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    LimeRFESettings *body = ; // Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFERunPutWith:body
    -              completionHandler: ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {LimeRFESettings} Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFERunPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFERunPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new LimeRFESettings(); // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceLimeRFERunPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFERunPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -try {
    -    $result = $api_instance->instanceLimeRFERunPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFERunPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::LimeRFESettings->new(); # LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFERunPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFERunPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # LimeRFESettings | Give device serial path in devicePath field and run status in rxOn and txOn
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_run_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFERunPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - Success

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 400 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceLimeRFESerialGet

    -

    -
    -
    -
    -

    -

    get a list of available serial interfaces to LimeRFE device

    -

    -
    -
    /sdrangel/limerfe/serial
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/limerfe/serial"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceLimeRFESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceLimeRFESerialGetWithCompletionHandler: 
    -              ^(LimeRFEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceLimeRFESerialGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceLimeRFESerialGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                LimeRFEDevices result = apiInstance.instanceLimeRFESerialGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceLimeRFESerialGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceLimeRFESerialGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceLimeRFESerialGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceLimeRFESerialGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceLimeRFESerialGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_lime_rfe_serial_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceLimeRFESerialGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of device paths possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -59777,7 +57761,7 @@ except ApiException as e:
    - Generated 2022-05-22T12:29:41.738+02:00 + Generated 2022-05-22T20:12:13.506+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp index 288dd007b..ab13b9f45 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp @@ -1707,288 +1707,6 @@ SWGInstanceApi::instanceFeaturesCallback(SWGHttpRequestWorker * worker) { } } -void -SWGInstanceApi::instanceLimeRFEConfigGet(QString* serial) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/limerfe/config"); - - - if (fullPath.indexOf("?") > 0) - fullPath.append("&"); - else - fullPath.append("?"); - fullPath.append(QUrl::toPercentEncoding("serial")) - .append("=") - .append(QUrl::toPercentEncoding(stringValue(serial))); - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceLimeRFEConfigGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceLimeRFEConfigGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGLimeRFESettings* output = static_cast(create(json, QString("SWGLimeRFESettings"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceLimeRFEConfigGetSignal(output); - } else { - emit instanceLimeRFEConfigGetSignalE(output, error_type, error_str); - emit instanceLimeRFEConfigGetSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceLimeRFEConfigPut(SWGLimeRFESettings& body) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/limerfe/config"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "PUT"); - - - - QString output = body.asJson(); - input.request_body.append(output.toUtf8()); - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceLimeRFEConfigPutCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceLimeRFEConfigPutCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceLimeRFEConfigPutSignal(output); - } else { - emit instanceLimeRFEConfigPutSignalE(output, error_type, error_str); - emit instanceLimeRFEConfigPutSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceLimeRFEPowerGet(QString* serial) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/limerfe/power"); - - - if (fullPath.indexOf("?") > 0) - fullPath.append("&"); - else - fullPath.append("?"); - fullPath.append(QUrl::toPercentEncoding("serial")) - .append("=") - .append(QUrl::toPercentEncoding(stringValue(serial))); - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceLimeRFEPowerGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceLimeRFEPowerGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGLimeRFEPower* output = static_cast(create(json, QString("SWGLimeRFEPower"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceLimeRFEPowerGetSignal(output); - } else { - emit instanceLimeRFEPowerGetSignalE(output, error_type, error_str); - emit instanceLimeRFEPowerGetSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceLimeRFERunPut(SWGLimeRFESettings& body) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/limerfe/run"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "PUT"); - - - - QString output = body.asJson(); - input.request_body.append(output.toUtf8()); - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceLimeRFERunPutCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceLimeRFERunPutCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceLimeRFERunPutSignal(output); - } else { - emit instanceLimeRFERunPutSignalE(output, error_type, error_str); - emit instanceLimeRFERunPutSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceLimeRFESerialGet() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/limerfe/serial"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceLimeRFESerialGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceLimeRFESerialGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGLimeRFEDevices* output = static_cast(create(json, QString("SWGLimeRFEDevices"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceLimeRFESerialGetSignal(output); - } else { - emit instanceLimeRFESerialGetSignalE(output, error_type, error_str); - emit instanceLimeRFESerialGetSignalEFull(worker, error_type, error_str); - } -} - void SWGInstanceApi::instanceLocationGet() { QString fullPath; diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h index e5b916a85..61a178a99 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h @@ -15,7 +15,6 @@ #include "SWGHttpRequest.h" -#include #include "SWGAMBEDevices.h" #include "SWGAudioDevices.h" #include "SWGAudioInputDevice.h" @@ -35,9 +34,6 @@ #include "SWGInstanceDevicesResponse.h" #include "SWGInstanceFeaturesResponse.h" #include "SWGInstanceSummaryResponse.h" -#include "SWGLimeRFEDevices.h" -#include "SWGLimeRFEPower.h" -#include "SWGLimeRFESettings.h" #include "SWGLocationInformation.h" #include "SWGLoggingInfo.h" #include "SWGPresetExport.h" @@ -93,11 +89,6 @@ public: void instanceFeaturePresetDelete(SWGFeaturePresetIdentifier& body); void instanceFeaturePresetGet(); void instanceFeatures(); - void instanceLimeRFEConfigGet(QString* serial); - void instanceLimeRFEConfigPut(SWGLimeRFESettings& body); - void instanceLimeRFEPowerGet(QString* serial); - void instanceLimeRFERunPut(SWGLimeRFESettings& body); - void instanceLimeRFESerialGet(); void instanceLocationGet(); void instanceLocationPut(SWGLocationInformation& body); void instanceLoggingGet(); @@ -145,11 +136,6 @@ private: void instanceFeaturePresetDeleteCallback (SWGHttpRequestWorker * worker); void instanceFeaturePresetGetCallback (SWGHttpRequestWorker * worker); void instanceFeaturesCallback (SWGHttpRequestWorker * worker); - void instanceLimeRFEConfigGetCallback (SWGHttpRequestWorker * worker); - void instanceLimeRFEConfigPutCallback (SWGHttpRequestWorker * worker); - void instanceLimeRFEPowerGetCallback (SWGHttpRequestWorker * worker); - void instanceLimeRFERunPutCallback (SWGHttpRequestWorker * worker); - void instanceLimeRFESerialGetCallback (SWGHttpRequestWorker * worker); void instanceLocationGetCallback (SWGHttpRequestWorker * worker); void instanceLocationPutCallback (SWGHttpRequestWorker * worker); void instanceLoggingGetCallback (SWGHttpRequestWorker * worker); @@ -197,11 +183,6 @@ signals: void instanceFeaturePresetDeleteSignal(SWGFeaturePresetIdentifier* summary); void instanceFeaturePresetGetSignal(SWGFeaturePresets* summary); void instanceFeaturesSignal(SWGInstanceFeaturesResponse* summary); - void instanceLimeRFEConfigGetSignal(SWGLimeRFESettings* summary); - void instanceLimeRFEConfigPutSignal(SWGSuccessResponse* summary); - void instanceLimeRFEPowerGetSignal(SWGLimeRFEPower* summary); - void instanceLimeRFERunPutSignal(SWGSuccessResponse* summary); - void instanceLimeRFESerialGetSignal(SWGLimeRFEDevices* summary); void instanceLocationGetSignal(SWGLocationInformation* summary); void instanceLocationPutSignal(SWGLocationInformation* summary); void instanceLoggingGetSignal(SWGLoggingInfo* summary); @@ -248,11 +229,6 @@ signals: void instanceFeaturePresetDeleteSignalE(SWGFeaturePresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetGetSignalE(SWGFeaturePresets* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturesSignalE(SWGInstanceFeaturesResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEConfigGetSignalE(SWGLimeRFESettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEConfigPutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEPowerGetSignalE(SWGLimeRFEPower* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFERunPutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFESerialGetSignalE(SWGLimeRFEDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLocationGetSignalE(SWGLocationInformation* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLocationPutSignalE(SWGLocationInformation* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLoggingGetSignalE(SWGLoggingInfo* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -299,11 +275,6 @@ signals: void instanceFeaturePresetDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturesSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEConfigGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEConfigPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFEPowerGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFERunPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceLimeRFESerialGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLocationGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLocationPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLoggingGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.cpp deleted file mode 100644 index 9f936e750..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGLimeRFEPower.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGLimeRFEPower::SWGLimeRFEPower(QString* json) { - init(); - this->fromJson(*json); -} - -SWGLimeRFEPower::SWGLimeRFEPower() { - forward = 0; - m_forward_isSet = false; - reflected = 0; - m_reflected_isSet = false; -} - -SWGLimeRFEPower::~SWGLimeRFEPower() { - this->cleanup(); -} - -void -SWGLimeRFEPower::init() { - forward = 0; - m_forward_isSet = false; - reflected = 0; - m_reflected_isSet = false; -} - -void -SWGLimeRFEPower::cleanup() { - - -} - -SWGLimeRFEPower* -SWGLimeRFEPower::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGLimeRFEPower::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&forward, pJson["forward"], "qint32", ""); - - ::SWGSDRangel::setValue(&reflected, pJson["reflected"], "qint32", ""); - -} - -QString -SWGLimeRFEPower::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGLimeRFEPower::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_forward_isSet){ - obj->insert("forward", QJsonValue(forward)); - } - if(m_reflected_isSet){ - obj->insert("reflected", QJsonValue(reflected)); - } - - return obj; -} - -qint32 -SWGLimeRFEPower::getForward() { - return forward; -} -void -SWGLimeRFEPower::setForward(qint32 forward) { - this->forward = forward; - this->m_forward_isSet = true; -} - -qint32 -SWGLimeRFEPower::getReflected() { - return reflected; -} -void -SWGLimeRFEPower::setReflected(qint32 reflected) { - this->reflected = reflected; - this->m_reflected_isSet = true; -} - - -bool -SWGLimeRFEPower::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_forward_isSet){ - isObjectUpdated = true; break; - } - if(m_reflected_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h b/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h deleted file mode 100644 index 917a6d3af..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGLimeRFEPower.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGLimeRFEPower.h - * - * report of forward and reflected power measurements TO BE DECOMMISSIONED - */ - -#ifndef SWGLimeRFEPower_H_ -#define SWGLimeRFEPower_H_ - -#include - - - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGLimeRFEPower: public SWGObject { -public: - SWGLimeRFEPower(); - SWGLimeRFEPower(QString* json); - virtual ~SWGLimeRFEPower(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGLimeRFEPower* fromJson(QString &jsonString) override; - - qint32 getForward(); - void setForward(qint32 forward); - - qint32 getReflected(); - void setReflected(qint32 reflected); - - - virtual bool isSet() override; - -private: - qint32 forward; - bool m_forward_isSet; - - qint32 reflected; - bool m_reflected_isSet; - -}; - -} - -#endif /* SWGLimeRFEPower_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 120ac5f7f..2cb26fd51 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -161,7 +161,6 @@ #include "SWGLimeRFEActions.h" #include "SWGLimeRFEDevice.h" #include "SWGLimeRFEDevices.h" -#include "SWGLimeRFEPower.h" #include "SWGLimeRFEReport.h" #include "SWGLimeRFESettings.h" #include "SWGLimeSdrInputReport.h" @@ -1065,11 +1064,6 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGLimeRFEPower").compare(type) == 0) { - SWGLimeRFEPower *obj = new SWGLimeRFEPower(); - obj->init(); - return obj; - } if(QString("SWGLimeRFEReport").compare(type) == 0) { SWGLimeRFEReport *obj = new SWGLimeRFEReport(); obj->init(); From d0c2b24694a9688d16a99a6f91f6a9999ebe2613 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 22 May 2022 22:10:36 +0200 Subject: [PATCH 06/29] Removed main LimeRFE support --- sdrbase/CMakeLists.txt | 17 - sdrbase/limerfe/limerfecontroller.cpp | 567 ------------- sdrbase/limerfe/limerfecontroller.h | 130 --- sdrbase/limerfe/limerfeusbcalib.cpp | 71 -- sdrbase/limerfe/limerfeusbcalib.h | 59 -- sdrbase/settings/mainsettings.cpp | 2 - sdrbase/settings/mainsettings.h | 3 - sdrgui/CMakeLists.txt | 17 - sdrgui/limerfegui/limerfeusbdialog.cpp | 881 --------------------- sdrgui/limerfegui/limerfeusbdialog.h | 110 --- sdrgui/limerfegui/limerfeusbdialog.ui | 1012 ------------------------ sdrgui/mainwindow.cpp | 20 - sdrgui/mainwindow.h | 1 - 13 files changed, 2890 deletions(-) delete mode 100644 sdrbase/limerfe/limerfecontroller.cpp delete mode 100644 sdrbase/limerfe/limerfecontroller.h delete mode 100644 sdrbase/limerfe/limerfeusbcalib.cpp delete mode 100644 sdrbase/limerfe/limerfeusbcalib.h delete mode 100644 sdrgui/limerfegui/limerfeusbdialog.cpp delete mode 100644 sdrgui/limerfegui/limerfeusbdialog.h delete mode 100644 sdrgui/limerfegui/limerfeusbdialog.ui diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index ceee21356..733e65c1d 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -31,19 +31,6 @@ else(FFTW3F_FOUND) add_definitions(-DUSE_KISSFFT) endif(FFTW3F_FOUND) -if (LIMESUITE_FOUND) - set(sdrbase_SOURCES - ${sdrbase_SOURCES} - limerfe/limerfecontroller.cpp - ) - set(sdrbase_HEADERS - ${sdrbase_HEADERS} - limerfe/limerfecontroller.h - ) - include_directories(${LIMESUITE_INCLUDE_DIR}) - set(sdrbase_LIMERFE_LIB ${LIMESUITE_LIBRARY}) -endif (LIMESUITE_FOUND) - if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(sdrbase_SOURCES ${sdrbase_SOURCES} @@ -165,8 +152,6 @@ set(sdrbase_SOURCES feature/featureutils.cpp feature/featurewebapiutils.cpp - limerfe/limerfeusbcalib.cpp - pipes/datafifostore.cpp pipes/datapipes.cpp pipes/datapipesgcworker.cpp @@ -373,8 +358,6 @@ set(sdrbase_HEADERS feature/featureutils.h feature/featurewebapiutils.h - limerfe/limerfeusbcalib.h - pipes/datafifostore.h pipes/datapipes.h pipes/datapipesgcworker.h diff --git a/sdrbase/limerfe/limerfecontroller.cpp b/sdrbase/limerfe/limerfecontroller.cpp deleted file mode 100644 index 35a718cea..000000000 --- a/sdrbase/limerfe/limerfecontroller.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include - -#include "limerfecontroller.h" - -const std::map LimeRFEController::m_errorCodesMap = { - { 0, "OK"}, - {-4, "Error synchronizing communication"}, - {-3, "Non-configurable GPIO pin specified. Only pins 4 and 5 are configurable."}, - {-2, "Problem with .ini configuration file"}, - {-1, "Communication error"}, - { 1, "Wrong TX connector - not possible to route TX of the selecrted channel to the specified port"}, - { 2, "Wrong RX connector - not possible to route RX of the selecrted channel to the specified port"}, - { 3, "Mode TXRX not allowed - when the same port is selected for RX and TX, it is not allowed to use mode RX & TX"}, - { 4, "Wrong mode for cellular channel - Cellular FDD bands (1, 2, 3, and 7) are only allowed mode RX & TX, while TDD band 38 is allowed only RX or TX mode"}, - { 5, "Cellular channels must be the same both for RX and TX"}, - { 6, "Requested channel code is wrong"} -}; - -LimeRFEController::LimeRFESettings::LimeRFESettings() -{ - m_rxChannels = ChannelsWideband; - m_rxWidebandChannel = WidebandLow; - m_rxHAMChannel = HAM_144_146MHz; - m_rxCellularChannel = CellularBand38; - m_rxPort = RxPortJ3; - m_amfmNotch = false; - m_attenuationFactor = 0; - m_txChannels = ChannelsWideband; - m_txWidebandChannel = WidebandLow; - m_txHAMChannel = HAM_144_146MHz; - m_txCellularChannel = CellularBand38; - m_txPort = TxPortJ3; - m_swrEnable = false; - m_swrSource = SWRExternal; - m_txRxDriven = false; - m_rxOn = false; - m_txOn = false; -} - -LimeRFEController::LimeRFEController() : - m_rfeDevice(nullptr) -{} - -LimeRFEController::~LimeRFEController() -{ - closeDevice(); -} - -int LimeRFEController::openDevice(const std::string& serialDeviceName) -{ - closeDevice(); - - rfe_dev_t *rfeDevice = RFE_Open(serialDeviceName.c_str(), nullptr); - - if (rfeDevice != (void *) -1) { - m_rfeDevice = rfeDevice; - return 0; - } - else - { - return -1; - } -} - -void LimeRFEController::closeDevice() -{ - if (m_rfeDevice) - { - RFE_Close(m_rfeDevice); - m_rfeDevice = nullptr; - } -} - -int LimeRFEController::configure() -{ - if (!m_rfeDevice) { - return -1; - } - - qDebug() << "LimeRFEController::configure: " - << "attValue: " << (int) m_rfeBoardState.attValue - << "channelIDRX: " << (int) m_rfeBoardState.channelIDRX - << "channelIDTX: " << (int) m_rfeBoardState.channelIDTX - << "mode: " << (int) m_rfeBoardState.mode - << "notchOnOff: " << (int) m_rfeBoardState.notchOnOff - << "selPortRX: " << (int) m_rfeBoardState.selPortRX - << "selPortTX: " << (int) m_rfeBoardState.selPortTX - << "enableSWR: " << (int) m_rfeBoardState.enableSWR - << "sourceSWR: " << (int) m_rfeBoardState.sourceSWR; - - int rc = RFE_ConfigureState(m_rfeDevice, m_rfeBoardState); - - if (rc != 0) { - qInfo("LimeRFEController::configure: %s", getError(rc).c_str()); - } else { - qDebug() << "LimeRFEController::configure: done"; - } - - return rc; -} - -int LimeRFEController::getState() -{ - if (!m_rfeDevice) { - return -1; - } - - int rc = RFE_GetState(m_rfeDevice, &m_rfeBoardState); - - qDebug() << "LimeRFEController::getState: " - << "attValue: " << (int) m_rfeBoardState.attValue - << "channelIDRX: " << (int) m_rfeBoardState.channelIDRX - << "channelIDTX: " << (int) m_rfeBoardState.channelIDTX - << "mode: " << (int) m_rfeBoardState.mode - << "notchOnOff: " << (int) m_rfeBoardState.notchOnOff - << "selPortRX: " << (int) m_rfeBoardState.selPortRX - << "selPortTX: " << (int) m_rfeBoardState.selPortTX - << "enableSWR: " << (int) m_rfeBoardState.enableSWR - << "sourceSWR: " << (int) m_rfeBoardState.sourceSWR; - - if (rc != 0) { - qInfo("LimeRFEController::getState: %s", getError(rc).c_str()); - } - - return rc; -} - -std::string LimeRFEController::getError(int errorCode) -{ - std::map::const_iterator it = m_errorCodesMap.find(errorCode); - - if (it == m_errorCodesMap.end()) { - return "Unknown error"; - } else { - return it->second; - } -} - -int LimeRFEController::setRx(LimeRFESettings& settings, bool rxOn) -{ - if (!m_rfeDevice) { - return -1; - } - - int mode = rxOn && settings.m_txOn ? - RFE_MODE_TXRX : rxOn ? - RFE_MODE_RX : settings.m_txOn ? - RFE_MODE_TX : RFE_MODE_NONE; - - int rc = RFE_Mode(m_rfeDevice, mode); - - if (rc == 0) { - settings.m_rxOn = rxOn; - } - - return rc; -} - -int LimeRFEController::setTx(LimeRFESettings& settings, bool txOn) -{ - if (!m_rfeDevice) { - return -1; - } - - int mode = txOn && settings.m_rxOn ? - RFE_MODE_TXRX : txOn ? - RFE_MODE_TX : settings.m_rxOn ? - RFE_MODE_RX : RFE_MODE_NONE; - - int rc = RFE_Mode(m_rfeDevice, mode); - - if (rc == 0) { - settings.m_txOn = txOn; - } - - return rc; -} - -int LimeRFEController::getFwdPower(int& powerDB) -{ - if (!m_rfeDevice) { - return -1; - } - - int power; - int rc = RFE_ReadADC(m_rfeDevice, RFE_ADC1, &power); - - if (rc == 0) { - powerDB = power; - } - - return rc; -} - -int LimeRFEController::getRefPower(int& powerDB) -{ - if (!m_rfeDevice) { - return -1; - } - - int power; - int rc = RFE_ReadADC(m_rfeDevice, RFE_ADC2, &power); - - if (rc == 0) { - powerDB = power; - } - - return rc; -} - -void LimeRFEController::settingsToState(const LimeRFESettings& settings) -{ - if (settings.m_rxChannels == ChannelsCellular) - { - if (settings.m_rxCellularChannel == CellularBand1) - { - m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND01; - m_rfeBoardState.mode = RFE_MODE_TXRX; - } - else if (settings.m_rxCellularChannel == CellularBand2) - { - m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND02; - m_rfeBoardState.mode = RFE_MODE_TXRX; - } - else if (settings.m_rxCellularChannel == CellularBand3) - { - m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND03; - m_rfeBoardState.mode = RFE_MODE_TXRX; - } - else if (settings.m_rxCellularChannel == CellularBand38) - { - m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND38; - } - else if (settings.m_rxCellularChannel == CellularBand7) - { - m_rfeBoardState.channelIDRX = RFE_CID_CELL_BAND07; - m_rfeBoardState.mode = RFE_MODE_TXRX; - } - - m_rfeBoardState.selPortRX = RFE_PORT_1; - m_rfeBoardState.selPortTX = RFE_PORT_1; - m_rfeBoardState.channelIDTX = m_rfeBoardState.channelIDRX; - } - else - { - m_rfeBoardState.mode = settings.m_rxOn && settings.m_txOn ? - RFE_MODE_TXRX : settings.m_rxOn ? - RFE_MODE_RX : settings.m_txOn ? - RFE_MODE_TX : RFE_MODE_NONE; - - if (settings.m_rxChannels == ChannelsWideband) - { - if (settings.m_rxWidebandChannel == WidebandLow) { - m_rfeBoardState.channelIDRX = RFE_CID_WB_1000; - } else if (settings.m_rxWidebandChannel == WidebandHigh) { - m_rfeBoardState.channelIDRX = RFE_CID_WB_4000; - } - } - else if (settings.m_rxChannels == ChannelsHAM) - { - if (settings.m_rxHAMChannel == HAM_30M) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0030; - } else if (settings.m_rxHAMChannel == HAM_50_70MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0070; - } else if (settings.m_rxHAMChannel == HAM_144_146MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0145; - } else if (settings.m_rxHAMChannel == HAM_220_225MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0220; - } else if (settings.m_rxHAMChannel == HAM_430_440MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0435; - } else if (settings.m_rxHAMChannel == HAM_902_928MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_0920; - } else if (settings.m_rxHAMChannel == HAM_1240_1325MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_1280; - } else if (settings.m_rxHAMChannel == HAM_2300_2450MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_2400; - } else if (settings.m_rxHAMChannel == HAM_3300_3500MHz) { - m_rfeBoardState.channelIDRX = RFE_CID_HAM_3500; - } - } - - if (settings.m_rxPort == RxPortJ3) { - m_rfeBoardState.selPortRX = RFE_PORT_1; - } else if (settings.m_rxPort == RxPortJ5) { - m_rfeBoardState.selPortRX = RFE_PORT_3; - } - - if (settings.m_txRxDriven) - { - m_rfeBoardState.channelIDTX = m_rfeBoardState.channelIDRX; - } - else - { - if (settings.m_txChannels == ChannelsWideband) - { - if (settings.m_txWidebandChannel == WidebandLow) { - m_rfeBoardState.channelIDTX = RFE_CID_WB_1000; - } else if (settings.m_txWidebandChannel == WidebandHigh) { - m_rfeBoardState.channelIDTX = RFE_CID_WB_4000; - } - } - else if (settings.m_txChannels == ChannelsHAM) - { - if (settings.m_txHAMChannel == HAM_30M) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0030; - } else if (settings.m_txHAMChannel == HAM_50_70MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0070; - } else if (settings.m_txHAMChannel == HAM_144_146MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0145; - } else if (settings.m_txHAMChannel == HAM_220_225MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0220; - } else if (settings.m_txHAMChannel == HAM_430_440MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0435; - } else if (settings.m_txHAMChannel == HAM_902_928MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_0920; - } else if (settings.m_txHAMChannel == HAM_1240_1325MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_1280; - } else if (settings.m_txHAMChannel == HAM_2300_2450MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_2400; - } else if (settings.m_txHAMChannel == HAM_3300_3500MHz) { - m_rfeBoardState.channelIDTX = RFE_CID_HAM_3500; - } - } - } - - if (settings.m_txPort == TxPortJ3) { - m_rfeBoardState.selPortTX = RFE_PORT_1; - } else if (settings.m_txPort == TxPortJ4) { - m_rfeBoardState.selPortTX = RFE_PORT_2; - } else if (settings.m_txPort == TxPortJ5) { - m_rfeBoardState.selPortTX = RFE_PORT_3; - } - } - - m_rfeBoardState.attValue = settings.m_attenuationFactor > 7 ? 7 : settings.m_attenuationFactor; - m_rfeBoardState.notchOnOff = settings.m_amfmNotch; - m_rfeBoardState.enableSWR = settings.m_swrEnable ? RFE_SWR_ENABLE : RFE_SWR_DISABLE; - - if (settings.m_swrSource == SWRExternal) { - m_rfeBoardState.sourceSWR = RFE_SWR_SRC_EXT; - } else if (settings.m_swrSource == SWRCellular) { - m_rfeBoardState.sourceSWR = RFE_SWR_SRC_CELL; - } -} - -void LimeRFEController::stateToSettings(LimeRFESettings& settings) -{ - if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND01) - { - settings.m_rxChannels = ChannelsCellular; - settings.m_rxCellularChannel = CellularBand1; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND02) - { - settings.m_rxChannels = ChannelsCellular; - settings.m_rxCellularChannel = CellularBand2; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND03) - { - settings.m_rxChannels = ChannelsCellular; - settings.m_rxCellularChannel = CellularBand3; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND07) - { - settings.m_rxChannels = ChannelsCellular; - settings.m_rxCellularChannel = CellularBand7; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_CELL_BAND38) - { - settings.m_rxChannels = ChannelsCellular; - settings.m_rxCellularChannel = CellularBand38; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_WB_1000) - { - settings.m_rxChannels = ChannelsWideband; - settings.m_rxWidebandChannel = WidebandLow; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_WB_4000) - { - settings.m_rxChannels = ChannelsWideband; - settings.m_rxWidebandChannel = WidebandHigh; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0030) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_30M; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0070) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_50_70MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0145) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_144_146MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0220) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_220_225MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0435) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_430_440MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_0920) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_902_928MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_1280) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_1240_1325MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_2400) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_2300_2450MHz; - } - else if (m_rfeBoardState.channelIDRX == RFE_CID_HAM_3500) - { - settings.m_rxChannels = ChannelsHAM; - settings.m_rxHAMChannel = HAM_3300_3500MHz; - } - - if (m_rfeBoardState.selPortRX == RFE_PORT_1) { - settings.m_rxPort = RxPortJ3; - } else if (m_rfeBoardState.selPortRX == RFE_PORT_3) { - settings.m_rxPort = RxPortJ5; - } - - if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND01) - { - settings.m_txChannels = ChannelsCellular; - settings.m_txCellularChannel = CellularBand1; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND02) - { - settings.m_txChannels = ChannelsCellular; - settings.m_txCellularChannel = CellularBand2; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND03) - { - settings.m_txChannels = ChannelsCellular; - settings.m_txCellularChannel = CellularBand3; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND07) - { - settings.m_txChannels = ChannelsCellular; - settings.m_txCellularChannel = CellularBand7; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_CELL_BAND38) - { - settings.m_txChannels = ChannelsCellular; - settings.m_txCellularChannel = CellularBand38; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_WB_1000) - { - settings.m_txChannels = ChannelsWideband; - settings.m_txWidebandChannel = WidebandLow; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_WB_4000) - { - settings.m_txChannels = ChannelsWideband; - settings.m_txWidebandChannel = WidebandHigh; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0030) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_30M; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0070) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_50_70MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0145) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_144_146MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0220) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_220_225MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0435) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_430_440MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_0920) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_902_928MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_1280) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_1240_1325MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_2400) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_2300_2450MHz; - } - else if (m_rfeBoardState.channelIDTX == RFE_CID_HAM_3500) - { - settings.m_txChannels = ChannelsHAM; - settings.m_txHAMChannel = HAM_3300_3500MHz; - } - - if (m_rfeBoardState.selPortTX == RFE_PORT_1) { - settings.m_txPort = TxPortJ3; - } else if (m_rfeBoardState.selPortTX == RFE_PORT_2) { - settings.m_txPort = TxPortJ4; - } else if (m_rfeBoardState.selPortTX == RFE_PORT_3) { - settings.m_txPort = TxPortJ5; - } - - settings.m_attenuationFactor = m_rfeBoardState.attValue; - settings.m_amfmNotch = m_rfeBoardState.notchOnOff == RFE_NOTCH_ON; - - if (m_rfeBoardState.mode == RFE_MODE_RX) - { - settings.m_rxOn = true; - settings.m_txOn = false; - } - else if (m_rfeBoardState.mode == RFE_MODE_TX) - { - settings.m_rxOn = false; - settings.m_txOn = true; - } - else if (m_rfeBoardState.mode == RFE_MODE_NONE) - { - settings.m_rxOn = false; - settings.m_txOn = false; - } - else if (m_rfeBoardState.mode == RFE_MODE_TXRX) - { - settings.m_rxOn = true; - settings.m_txOn = true; - } - - settings.m_swrEnable = m_rfeBoardState.enableSWR == RFE_SWR_ENABLE; - settings.m_swrSource = m_rfeBoardState.sourceSWR == RFE_SWR_SRC_CELL ? SWRCellular : SWRExternal; -} diff --git a/sdrbase/limerfe/limerfecontroller.h b/sdrbase/limerfe/limerfecontroller.h deleted file mode 100644 index 000ed15ca..000000000 --- a/sdrbase/limerfe/limerfecontroller.h +++ /dev/null @@ -1,130 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRBASE_LIMERFE_LIMERFECONTROLLER_H_ -#define SDRBASE_LIMERFE_LIMERFECONTROLLER_H_ - -#include -#include -#include "lime/limeRFE.h" -#include "export.h" - -class SDRBASE_API LimeRFEController -{ -public: - enum ChannelGroups - { - ChannelsWideband, - ChannelsHAM, - ChannelsCellular - }; - - enum WidebandChannel - { - WidebandLow, //!< 1 - 1000 MHz - WidebandHigh //!< 1000 - 4000 MHz - }; - - enum HAMChannel - { - HAM_30M, - HAM_50_70MHz, - HAM_144_146MHz, - HAM_220_225MHz, - HAM_430_440MHz, - HAM_902_928MHz, - HAM_1240_1325MHz, - HAM_2300_2450MHz, - HAM_3300_3500MHz - }; - - enum CellularChannel - { - CellularBand1, - CellularBand2, - CellularBand3, - CellularBand7, - CellularBand38 - }; - - enum RxPort - { - RxPortJ3, //!< Rx/Tx - RxPortJ5 //!< Rx/Tx HF - }; - - enum TxPort - { - TxPortJ3, //!< Rx/Tx - TxPortJ4, //!< Tx - TxPortJ5 //!< Rx/Tx HF - }; - - enum SWRSource - { - SWRExternal, - SWRCellular - }; - - struct SDRBASE_API LimeRFESettings - { - LimeRFESettings(); - // Rx - LimeRFEController::ChannelGroups m_rxChannels; - LimeRFEController::WidebandChannel m_rxWidebandChannel; - LimeRFEController::HAMChannel m_rxHAMChannel; - LimeRFEController::CellularChannel m_rxCellularChannel; - LimeRFEController::RxPort m_rxPort; - unsigned int m_attenuationFactor; //!< Attenuation is 2 times this factor in dB (0..7 => 0..14dB) - bool m_amfmNotch; - // Tx - LimeRFEController::ChannelGroups m_txChannels; - LimeRFEController::WidebandChannel m_txWidebandChannel; - LimeRFEController::HAMChannel m_txHAMChannel; - LimeRFEController::CellularChannel m_txCellularChannel; - LimeRFEController::TxPort m_txPort; - bool m_swrEnable; - LimeRFEController::SWRSource m_swrSource; - // Rx/Tx - bool m_txRxDriven; //!< Tx settings set according to Rx settings - bool m_rxOn; - bool m_txOn; - }; - - LimeRFEController(); - ~LimeRFEController(); - - int openDevice(const std::string& serialDeviceName); - void closeDevice(); - int configure(); - int getState(); - static std::string getError(int errorCode); - int setRx(LimeRFESettings& settings, bool rxOn); - int setTx(LimeRFESettings& settings, bool txOn); - int getFwdPower(int& powerDB); - int getRefPower(int& powerDB); - - void settingsToState(const LimeRFESettings& settings); - void stateToSettings(LimeRFESettings& settings); - -private: - rfe_dev_t *m_rfeDevice; - rfe_boardState m_rfeBoardState; - static const std::map m_errorCodesMap; -}; - -#endif // SDRBASE_LIMERFE_LIMERFECONTROLLER_H_ diff --git a/sdrbase/limerfe/limerfeusbcalib.cpp b/sdrbase/limerfe/limerfeusbcalib.cpp deleted file mode 100644 index 1502fe246..000000000 --- a/sdrbase/limerfe/limerfeusbcalib.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include - -#include "util/simpleserializer.h" - -#include "limerfeusbcalib.h" - -QByteArray LimeRFEUSBCalib::serialize() const -{ - SimpleSerializer s(1); - QByteArray data; - - serializeCalibMap(data); - s.writeBlob(1, data); - - return s.final(); -} - -bool LimeRFEUSBCalib::deserialize(const QByteArray& data) -{ - SimpleDeserializer d(data); - - if (!d.isValid()) { - return false; - } - - if (d.getVersion() == 1) - { - QByteArray data; - - d.readBlob(1, &data); - deserializeCalibMap(data); - - return true; - } - else - { - return false; - } -} - -void LimeRFEUSBCalib::serializeCalibMap(QByteArray& data) const -{ - QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); - *stream << m_calibrations; - delete stream; -} - -void LimeRFEUSBCalib::deserializeCalibMap(QByteArray& data) -{ - QDataStream readStream(&data, QIODevice::ReadOnly); - readStream >> m_calibrations; -} diff --git a/sdrbase/limerfe/limerfeusbcalib.h b/sdrbase/limerfe/limerfeusbcalib.h deleted file mode 100644 index 80f419575..000000000 --- a/sdrbase/limerfe/limerfeusbcalib.h +++ /dev/null @@ -1,59 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ -#define SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ - -#include -#include "export.h" - -class QByteArray; - -class SDRBASE_API LimeRFEUSBCalib -{ -public: - QByteArray serialize() const; - bool deserialize(const QByteArray& data); - - enum ChannelRange - { - WidebandLow, //!< 1 - 1000 MHz - WidebandHigh, //!< 1000 - 4000 MHz - HAM_30MHz, //!< Up to 30 MHz - HAM_50_70MHz, - HAM_144_146MHz, - HAM_220_225MHz, - HAM_430_440MHz, - HAM_902_928MHz, - HAM_1240_1325MHz, - HAM_2300_2450MHz, - HAM_3300_3500MHz, - CellularBand1, - CellularBand2, - CellularBand3, - CellularBand7, - CellularBand38 - }; - - QMap m_calibrations; //!< Channel range to calibration value in floating point decibels - -private: - void serializeCalibMap(QByteArray& data) const; - void deserializeCalibMap(QByteArray& data); -}; - -#endif // SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index e9529066f..a074a05d0 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -135,7 +135,6 @@ void MainSettings::load() } m_hardwareDeviceUserArgs.deserialize(qUncompress(QByteArray::fromBase64(s.value("hwDeviceUserArgs").toByteArray()))); - m_limeRFEUSBCalib.deserialize(qUncompress(QByteArray::fromBase64(s.value("limeRFEUSBCalib").toByteArray()))); } void MainSettings::save() const @@ -196,7 +195,6 @@ void MainSettings::save() const } s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.serialize()).toBase64()); - s.setValue("limeRFEUSBCalib", qCompress(m_limeRFEUSBCalib.serialize()).toBase64()); } void MainSettings::initialize() diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index 5e55e2f92..a475afbe6 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -4,7 +4,6 @@ #include #include #include "device/deviceuserargs.h" -#include "limerfe/limerfeusbcalib.h" #include "preferences.h" #include "preset.h" #include "featuresetpreset.h" @@ -182,7 +181,6 @@ public: } DeviceUserArgs& getDeviceUserArgs() { return m_hardwareDeviceUserArgs; } - LimeRFEUSBCalib& getLimeRFEUSBCalib() { return m_limeRFEUSBCalib; } const AudioDeviceManager *getAudioDeviceManager() const { return m_audioDeviceManager; } void setAudioDeviceManager(AudioDeviceManager *audioDeviceManager) { m_audioDeviceManager = audioDeviceManager; } void setAMBEEngine(AMBEEngine *ambeEngine) { m_ambeEngine = ambeEngine; } @@ -205,7 +203,6 @@ protected: typedef QList Configurations; Configurations m_configurations; DeviceUserArgs m_hardwareDeviceUserArgs; - LimeRFEUSBCalib m_limeRFEUSBCalib; AMBEEngine *m_ambeEngine; }; diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 086def148..4780935ee 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -246,23 +246,6 @@ set(sdrgui_FORMS soapygui/arginfogui.ui ) -if (LIMESUITE_FOUND) - set(sdrgui_SOURCES - ${sdrgui_SOURCES} - limerfegui/limerfeusbdialog.cpp - ) - set(sdrgui_HEADERS - ${sdrgui_HEADERS} - limerfegui/limerfeusbdialog.h - ) - set(sdrgui_FORMS - ${sdrgui_FORMS} - limerfegui/limerfeusbdialog.ui - ) - include_directories(${LIMESUITE_INCLUDE_DIR}) - set(sdrgui_LIMERFE_LIB ${LIMESUITE_LIBRARY}) -endif (LIMESUITE_FOUND) - qt5_wrap_ui(sdrgui_FORMS_HEADERS ${sdrgui_FORMS}) include_directories( diff --git a/sdrgui/limerfegui/limerfeusbdialog.cpp b/sdrgui/limerfegui/limerfeusbdialog.cpp deleted file mode 100644 index 8ba429a22..000000000 --- a/sdrgui/limerfegui/limerfeusbdialog.cpp +++ /dev/null @@ -1,881 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// 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 "util/serialutil.h" -#include "util/db.h" -#include "dsp/dspengine.h" -#include "dsp/dspdevicesourceengine.h" -#include "dsp/dspdevicesinkengine.h" -#include "mainwindow.h" -#include "device/deviceuiset.h" -#include "gui/doublevalidator.h" - -#include "limerfeusbdialog.h" -#include "ui_limerfeusbdialog.h" - -LimeRFEUSBDialog::LimeRFEUSBDialog(LimeRFEUSBCalib& limeRFEUSBCalib, MainWindow* mainWindow) : - QDialog(mainWindow), - ui(new Ui::LimeRFEUSBDialog), - m_limeRFEUSBCalib(limeRFEUSBCalib), - m_rxTxToggle(false), - m_currentPowerCorrection(0.0), - m_avgPower(false), - m_deviceSetSync(false) -{ - ui->setupUi(this); - m_mainWindow = mainWindow; - ui->powerCorrValue->setValidator(new DoubleValidator(-99.9, 99.9, 1, ui->powerCorrValue)); - std::vector comPorts; - SerialUtil::getComPorts(comPorts, "ttyUSB[0-9]+"); // regex is for Linux only - - for (std::vector::const_iterator it = comPorts.begin(); it != comPorts.end(); ++it) { - ui->device->addItem(QString(it->c_str())); - } - - updateDeviceSetList(); - displaySettings(); // default values - highlightApplyButton(false); - m_timer.setInterval(500); -} - -LimeRFEUSBDialog::~LimeRFEUSBDialog() -{ - delete ui; -} - -void LimeRFEUSBDialog::displaySettings() -{ - setRxChannels(); - ui->rxPort->setCurrentIndex(m_settings.m_rxPort); - ui->attenuation->setCurrentIndex(m_settings.m_attenuationFactor); - ui->amFmNotchFilter->setChecked(m_settings.m_amfmNotch); - setTxChannels(); - ui->txPort->setCurrentIndex(m_settings.m_txPort); - ui->txFollowsRx->setChecked(m_settings.m_txRxDriven); - ui->rxTxToggle->setChecked(m_rxTxToggle); - displayMode(); - displayPower(); -} - -void LimeRFEUSBDialog::displayMode() -{ - QString s; - - if (m_settings.m_rxOn) - { - if (m_settings.m_txOn) { - s = "Rx/Tx"; - } else { - s = "Rx"; - } - } - else - { - if (m_settings.m_txOn) { - s = "Tx"; - } else { - s = "None"; - } - } - - ui->modeText->setText(s); - - ui->modeRx->blockSignals(true); - ui->modeTx->blockSignals(true); - - if (m_settings.m_rxOn) { - ui->modeRx->setStyleSheet("QToolButton { background-color : green; }"); - } else { - ui->modeRx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); - } - - if (m_settings.m_txOn) { - ui->modeTx->setStyleSheet("QToolButton { background-color : red; }"); - } else { - ui->modeTx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); - } - - ui->modeRx->setChecked(m_settings.m_rxOn); - ui->modeTx->setChecked(m_settings.m_txOn); - - ui->modeRx->blockSignals(false); - ui->modeTx->blockSignals(false); -} - -void LimeRFEUSBDialog::displayPower() -{ - ui->powerEnable->blockSignals(true); - ui->powerSource->blockSignals(true); - - ui->powerEnable->setChecked(m_settings.m_swrEnable); - ui->powerSource->setCurrentIndex((int) m_settings.m_swrSource); - - ui->powerEnable->blockSignals(false); - ui->powerSource->blockSignals(false); -} - -void LimeRFEUSBDialog::refreshPower() -{ - int fwdPower, refPower; - int rc = m_controller.getFwdPower(fwdPower); - - if (rc != 0) - { - ui->statusText->setText(m_controller.getError(rc).c_str()); - return; - } - - rc = m_controller.getRefPower(refPower); - - if (rc != 0) - { - ui->statusText->setText(m_controller.getError(rc).c_str()); - return; - } - - double fwdPowerDB = fwdPower / 10.0; - double refPowerDB = refPower / 10.0; - double retLossDB = fwdPowerDB - refPowerDB; - - ui->powerFwdText->setText(QString::number(fwdPowerDB, 'f', 1)); - ui->powerRefText->setText(QString::number(refPowerDB, 'f', 1)); - ui->returnLossText->setText(QString::number(retLossDB, 'f', 1)); - - double denom = CalcDb::powerFromdB(retLossDB/2.0) - 1.0; - - if (denom == 0.0) - { - ui->swrText->setText("---"); - } - else - { - double vswr = (CalcDb::powerFromdB(retLossDB/2.0) + 1.0) / denom; - vswr = vswr < 0.0 ? 0.0 : vswr > 99.999 ? 99.999 : vswr; - ui->swrText->setText(QString::number(vswr, 'f', 3)); - } - - updateAbsPower(m_currentPowerCorrection); -} - -void LimeRFEUSBDialog::setRxChannels() -{ - ui->rxChannel->blockSignals(true); - ui->rxPort->blockSignals(true); - ui->rxChannel->clear(); - ui->rxPort->clear(); - - if (m_settings.m_rxChannels == LimeRFEController::ChannelsWideband) - { - ui->rxChannel->addItem("1-1000MHz"); - ui->rxChannel->addItem("1-4GHz"); - ui->rxChannel->setCurrentIndex((int) m_settings.m_rxWidebandChannel); - ui->rxPort->addItem("TX/RX (J3)"); - ui->rxPort->addItem("TX/RX 30M (J5)"); - ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); - ui->txFollowsRx->setEnabled(true); - ui->rxPort->setEnabled(true); - } - else if (m_settings.m_rxChannels == LimeRFEController::ChannelsHAM) - { - ui->rxChannel->addItem("<30MHz"); - ui->rxChannel->addItem("50-70MHz"); - ui->rxChannel->addItem("144-146MHz"); - ui->rxChannel->addItem("220-225MHz"); - ui->rxChannel->addItem("430-440MHz"); - ui->rxChannel->addItem("902-928MHz"); - ui->rxChannel->addItem("1240-1325MHz"); - ui->rxChannel->addItem("2300-2450MHz"); - ui->rxChannel->addItem("3300-3500MHz"); - ui->rxChannel->setCurrentIndex((int) m_settings.m_rxHAMChannel); - ui->txFollowsRx->setEnabled(true); - - switch(m_settings.m_rxHAMChannel) - { - case LimeRFEController::HAM_30M: - case LimeRFEController::HAM_50_70MHz: - case LimeRFEController::HAM_144_146MHz: - case LimeRFEController::HAM_220_225MHz: - case LimeRFEController::HAM_430_440MHz: - ui->rxPort->addItem("TX/RX (J3)"); - ui->rxPort->addItem("TX/RX 30M (J5)"); - ui->rxPort->setEnabled(true); - ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); - break; - case LimeRFEController::HAM_902_928MHz: - case LimeRFEController::HAM_1240_1325MHz: - case LimeRFEController::HAM_2300_2450MHz: - case LimeRFEController::HAM_3300_3500MHz: - ui->rxPort->addItem("TX/RX (J3)"); - ui->rxPort->setEnabled(false); - m_settings.m_rxPort = LimeRFEController::RxPortJ3; - ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); - break; - default: - break; - } - } - else if (m_settings.m_rxChannels == LimeRFEController::ChannelsCellular) - { - ui->rxChannel->addItem("Band1"); - ui->rxChannel->addItem("Band2"); - ui->rxChannel->addItem("Band3"); - ui->rxChannel->addItem("Band7"); - ui->rxChannel->addItem("Band38"); - ui->rxChannel->setCurrentIndex((int) m_settings.m_rxCellularChannel); - ui->rxPort->addItem("TX/RX (J3)"); - ui->rxPort->setEnabled(false); - m_settings.m_rxPort = LimeRFEController::RxPortJ3; - ui->rxPort->setCurrentIndex((int) m_settings.m_rxPort); - m_settings.m_txRxDriven = true; - ui->txFollowsRx->setEnabled(false); - ui->txFollowsRx->setChecked(m_settings.m_txRxDriven); - } - - ui->rxChannelGroup->setCurrentIndex((int) m_settings.m_rxChannels); - ui->rxPort->blockSignals(false); - ui->rxChannel->blockSignals(false); -} - -void LimeRFEUSBDialog::setTxChannels() -{ - ui->txChannel->blockSignals(true); - ui->txPort->blockSignals(true); - ui->powerCorrValue->blockSignals(true); - - ui->txChannel->clear(); - ui->txPort->clear(); - - if (m_settings.m_txChannels == LimeRFEController::ChannelsWideband) - { - ui->txChannel->addItem("1-1000MHz"); - ui->txChannel->addItem("1-4GHz"); - ui->txChannel->setCurrentIndex((int) m_settings.m_txWidebandChannel); - ui->txPort->addItem("TX/RX (J3)"); - ui->txPort->addItem("TX (J4)"); - ui->txPort->setCurrentIndex((int) m_settings.m_txPort); - ui->txPort->setEnabled(true); - } - else if (m_settings.m_txChannels == LimeRFEController::ChannelsHAM) - { - ui->txChannel->addItem("<30MHz"); - ui->txChannel->addItem("50-70MHz"); - ui->txChannel->addItem("144-146MHz"); - ui->txChannel->addItem("220-225MHz"); - ui->txChannel->addItem("430-440MHz"); - ui->txChannel->addItem("902-928MHz"); - ui->txChannel->addItem("1240-1325MHz"); - ui->txChannel->addItem("2300-2450MHz"); - ui->txChannel->addItem("3300-3500MHz"); - ui->txChannel->setCurrentIndex((int) m_settings.m_txHAMChannel); - - switch(m_settings.m_txHAMChannel) - { - case LimeRFEController::HAM_30M: - case LimeRFEController::HAM_50_70MHz: - ui->txPort->addItem("TX/RX (J3)"); - ui->txPort->addItem("TX (J4)"); - ui->txPort->addItem("TX/RX 30M (J5)"); - ui->txPort->setEnabled(false); - m_settings.m_txPort = LimeRFEController::TxPortJ5; - ui->txPort->setCurrentIndex((int) m_settings.m_txPort); - break; - case LimeRFEController::HAM_144_146MHz: - case LimeRFEController::HAM_220_225MHz: - case LimeRFEController::HAM_430_440MHz: - case LimeRFEController::HAM_902_928MHz: - case LimeRFEController::HAM_1240_1325MHz: - case LimeRFEController::HAM_2300_2450MHz: - case LimeRFEController::HAM_3300_3500MHz: - ui->txPort->addItem("TX/RX (J3)"); - ui->txPort->addItem("TX (J4)"); - ui->txPort->setCurrentIndex(m_settings.m_txPort < 2 ? m_settings.m_txPort : 1); - ui->txPort->setEnabled(true); - break; - default: - break; - } - } - else if (m_settings.m_txChannels == LimeRFEController::ChannelsCellular) - { - ui->txChannel->addItem("Band1"); - ui->txChannel->addItem("Band2"); - ui->txChannel->addItem("Band3"); - ui->txChannel->addItem("Band7"); - ui->txChannel->addItem("Band38"); - ui->txChannel->setCurrentIndex((int) m_settings.m_txCellularChannel); - ui->txPort->addItem("TX/RX (J3)"); - m_settings.m_txPort = LimeRFEController::TxPortJ3; - ui->txPort->setEnabled(false); - ui->txPort->setCurrentIndex((int) m_settings.m_txPort); - } - - ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); - m_currentPowerCorrection = getPowerCorrection(); - ui->powerCorrValue->setText(QString::number(m_currentPowerCorrection, 'f', 1)); - updateAbsPower(m_currentPowerCorrection); - - ui->powerCorrValue->blockSignals(false); - ui->txPort->blockSignals(false); - ui->txChannel->blockSignals(false); -} - -int LimeRFEUSBDialog::getPowerCorectionIndex() -{ - LimeRFEUSBCalib::ChannelRange range; - - switch (m_settings.m_txChannels) - { - case LimeRFEController::ChannelsWideband: - { - switch (m_settings.m_txWidebandChannel) - { - case LimeRFEController::WidebandLow: - range = LimeRFEUSBCalib::WidebandLow; - break; - case LimeRFEController::WidebandHigh: - range = LimeRFEUSBCalib::WidebandHigh; - break; - default: - return -1; - break; - } - break; - } - case LimeRFEController::ChannelsHAM: - { - switch (m_settings.m_txHAMChannel) - { - case LimeRFEController::HAM_30M: - range = LimeRFEUSBCalib::HAM_30MHz; - break; - case LimeRFEController::HAM_50_70MHz: - range = LimeRFEUSBCalib::HAM_50_70MHz; - break; - case LimeRFEController::HAM_144_146MHz: - range = LimeRFEUSBCalib::HAM_144_146MHz; - break; - case LimeRFEController::HAM_220_225MHz: - range = LimeRFEUSBCalib::HAM_220_225MHz; - break; - case LimeRFEController::HAM_430_440MHz: - range = LimeRFEUSBCalib::HAM_430_440MHz; - break; - case LimeRFEController::HAM_902_928MHz: - range = LimeRFEUSBCalib::HAM_902_928MHz; - break; - case LimeRFEController::HAM_1240_1325MHz: - range = LimeRFEUSBCalib::HAM_1240_1325MHz; - break; - case LimeRFEController::HAM_2300_2450MHz: - range = LimeRFEUSBCalib::HAM_2300_2450MHz; - break; - case LimeRFEController::HAM_3300_3500MHz: - range = LimeRFEUSBCalib::HAM_3300_3500MHz; - break; - default: - return -1; - break; - } - break; - } - case LimeRFEController::ChannelsCellular: - { - switch (m_settings.m_txCellularChannel) - { - case LimeRFEController::CellularBand1: - range = LimeRFEUSBCalib::CellularBand1; - break; - case LimeRFEController::CellularBand2: - range = LimeRFEUSBCalib::CellularBand2; - break; - case LimeRFEController::CellularBand3: - range = LimeRFEUSBCalib::CellularBand3; - break; - case LimeRFEController::CellularBand7: - range = LimeRFEUSBCalib::CellularBand7; - break; - case LimeRFEController::CellularBand38: - range = LimeRFEUSBCalib::CellularBand38; - break; - default: - return -1; - break; - } - break; - } - default: - return -1; - break; - } - - return (int) range; -} - -double LimeRFEUSBDialog::getPowerCorrection() -{ - int index = getPowerCorectionIndex(); - - QMap::const_iterator it = m_limeRFEUSBCalib.m_calibrations.find(index); - - if (it != m_limeRFEUSBCalib.m_calibrations.end()) { - return it.value(); - } else { - return 0.0; - } -} - -void LimeRFEUSBDialog::setPowerCorrection(double dbValue) -{ - int index = getPowerCorectionIndex(); - - if (index < 0) { - return; - } - - m_limeRFEUSBCalib.m_calibrations[index] = dbValue; -} - -void LimeRFEUSBDialog::on_openDevice_clicked() -{ - int rc = m_controller.openDevice(ui->device->currentText().toStdString()); - ui->statusText->append(QString("Open %1: %2").arg(ui->device->currentText()).arg(m_controller.getError(rc).c_str())); - - if (rc != 0) { - return; - } - - rc = m_controller.getState(); - ui->statusText->append(QString("Get state: %1").arg(m_controller.getError(rc).c_str())); -} - -void LimeRFEUSBDialog::on_closeDevice_clicked() -{ - ui->statusText->clear(); - m_controller.closeDevice(); - ui->statusText->setText("Closed"); -} - -void LimeRFEUSBDialog::on_deviceToGUI_clicked() -{ - int rc = m_controller.getState(); - - if (rc != 0) - { - ui->statusText->setText(m_controller.getError(rc).c_str()); - return; - } - - m_controller.stateToSettings(m_settings); - displaySettings(); - highlightApplyButton(false); -} - -void LimeRFEUSBDialog::on_rxChannelGroup_currentIndexChanged(int index) -{ - m_settings.m_rxChannels = (LimeRFEController::ChannelGroups) index; - setRxChannels(); - - if (m_settings.m_txRxDriven) - { - m_settings.m_txChannels = m_settings.m_rxChannels; - ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); - } - - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_rxChannel_currentIndexChanged(int index) -{ - if (m_settings.m_rxChannels == LimeRFEController::ChannelsWideband) { - m_settings.m_rxWidebandChannel = (LimeRFEController::WidebandChannel) index; - } else if (m_settings.m_rxChannels == LimeRFEController::ChannelsHAM) { - m_settings.m_rxHAMChannel = (LimeRFEController::HAMChannel) index; - } else if (m_settings.m_rxChannels == LimeRFEController::ChannelsCellular) { - m_settings.m_rxCellularChannel = (LimeRFEController::CellularChannel) index; - } - - setRxChannels(); - - if (m_settings.m_txRxDriven) - { - m_settings.m_txWidebandChannel = m_settings.m_rxWidebandChannel; - m_settings.m_txHAMChannel = m_settings.m_rxHAMChannel; - m_settings.m_txCellularChannel = m_settings.m_rxCellularChannel; - setTxChannels(); - } - - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_rxPort_currentIndexChanged(int index) -{ - m_settings.m_rxPort = (LimeRFEController::RxPort) index; - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_txFollowsRx_clicked() -{ - bool checked = ui->txFollowsRx->isChecked(); - m_settings.m_txRxDriven = checked; - ui->txChannelGroup->setEnabled(!checked); - ui->txChannel->setEnabled(!checked); - m_settings.m_txChannels = m_settings.m_rxChannels; - m_settings.m_txWidebandChannel = m_settings.m_rxWidebandChannel; - m_settings.m_txHAMChannel = m_settings.m_rxHAMChannel; - m_settings.m_txCellularChannel = m_settings.m_rxCellularChannel; - ui->txChannelGroup->setCurrentIndex((int) m_settings.m_txChannels); - - if (checked) { - highlightApplyButton(true); - } -} - -void LimeRFEUSBDialog::on_txChannelGroup_currentIndexChanged(int index) -{ - m_settings.m_txChannels = (LimeRFEController::ChannelGroups) index; - setTxChannels(); - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_txChannel_currentIndexChanged(int index) -{ - if (m_settings.m_txChannels == LimeRFEController::ChannelsWideband) { - m_settings.m_txWidebandChannel = (LimeRFEController::WidebandChannel) index; - } else if (m_settings.m_txChannels == LimeRFEController::ChannelsHAM) { - m_settings.m_txHAMChannel = (LimeRFEController::HAMChannel) index; - } else if (m_settings.m_txChannels == LimeRFEController::ChannelsCellular) { - m_settings.m_txCellularChannel = (LimeRFEController::CellularChannel) index; - } - - setTxChannels(); - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_txPort_currentIndexChanged(int index) -{ - m_settings.m_txPort = (LimeRFEController::TxPort) index; - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_powerEnable_clicked() -{ - m_settings.m_swrEnable = ui->powerEnable->isChecked(); - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_powerSource_currentIndexChanged(int index) -{ - m_settings.m_swrSource = (LimeRFEController::SWRSource) index; - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_powerRefresh_clicked() -{ - refreshPower(); -} - -void LimeRFEUSBDialog::on_powerAutoRefresh_toggled(bool checked) -{ - if (checked) - { - connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); - m_timer.start(); - } - else - { - m_timer.stop(); - disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); - } -} - -void LimeRFEUSBDialog::on_powerAbsAvg_clicked() -{ - m_avgPower = ui->powerAbsAvg->isChecked(); -} - -void LimeRFEUSBDialog::on_powerCorrValue_textEdited(const QString &text) -{ - bool ok; - double powerCorrection = text.toDouble(&ok); - - if (ok) - { - setPowerCorrection(powerCorrection); - m_currentPowerCorrection = powerCorrection; - updateAbsPower(powerCorrection); - } -} - -void LimeRFEUSBDialog::on_deviceSetRefresh_clicked() -{ - updateDeviceSetList(); -} - -void LimeRFEUSBDialog::on_deviceSetSync_clicked() -{ - m_deviceSetSync = ui->deviceSetSync->isChecked(); - - if (m_deviceSetSync) { - syncRxTx(); - } -} - -void LimeRFEUSBDialog::syncRxTx() -{ - if (!m_settings.m_txOn) { - stopStartTx(m_settings.m_txOn); - } - - stopStartRx(m_settings.m_rxOn); - - if (m_settings.m_txOn) { - stopStartTx(m_settings.m_txOn); - } -} - -void LimeRFEUSBDialog::stopStartRx(bool start) -{ - unsigned int rxDeviceSetSequence = ui->deviceSetRx->currentIndex(); - - if (rxDeviceSetSequence >= m_sourceEngines.size()) { - return; - } - - DSPDeviceSourceEngine *deviceSourceEngine = m_sourceEngines[rxDeviceSetSequence]; - - if (start) - { - if (deviceSourceEngine->initAcquisition()) { - deviceSourceEngine->startAcquisition(); - } - } - else - { - deviceSourceEngine->stopAcquistion(); - } -} - -void LimeRFEUSBDialog::stopStartTx(bool start) -{ - unsigned int txDeviceSetSequence = ui->deviceSetTx->currentIndex(); - - if (txDeviceSetSequence >= m_sinkEngines.size()) { - return; - } - - DSPDeviceSinkEngine *deviceSinkEngine = m_sinkEngines[txDeviceSetSequence]; - - if (start) - { - if (deviceSinkEngine->initGeneration()) { - deviceSinkEngine->startGeneration(); - } - } - else - { - deviceSinkEngine->stopGeneration(); - } -} - -void LimeRFEUSBDialog::updateAbsPower(double powerCorrDB) -{ - bool ok; - double power = ui->powerFwdText->text().toDouble(&ok); - - if (ok) - { - double powerCorrected = power + powerCorrDB; - double powerDisplayed = powerCorrected; - - if (m_avgPower) - { - m_powerMovingAverage(powerCorrected); - powerDisplayed = m_powerMovingAverage.asDouble(); - } - - ui->powerAbsDbText->setText(tr("%1 dBm").arg(QString::number(powerDisplayed, 'f', 1))); - double powerWatts = CalcDb::powerFromdB(powerDisplayed - 30.0); - powerWatts = powerWatts > 8.0 ? 8.0 : powerWatts; - ui->powerAbsWText->setText(tr("%1 W").arg(QString::number(powerWatts, 'f', 3))); - } -} - -void LimeRFEUSBDialog::updateDeviceSetList() -{ - std::vector& deviceUISets = m_mainWindow->getDeviceUISets(); - std::vector::const_iterator it = deviceUISets.begin(); - m_sourceEngines.clear(); - m_rxDeviceSetIndex.clear(); - m_sinkEngines.clear(); - m_txDeviceSetIndex.clear(); - ui->deviceSetRx->clear(); - ui->deviceSetTx->clear(); - unsigned int deviceIndex = 0; - unsigned int rxIndex = 0; - unsigned int txIndex = 0; - - for (; it != deviceUISets.end(); ++it, deviceIndex++) - { - DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; - DSPDeviceSinkEngine *deviceSinkEngine = (*it)->m_deviceSinkEngine; - - if (deviceSourceEngine) - { - m_sourceEngines.push_back(deviceSourceEngine); - m_rxDeviceSetIndex.push_back(deviceIndex); - ui->deviceSetRx->addItem(QString("%1").arg(deviceIndex)); - rxIndex++; - } - else if (deviceSinkEngine) - { - m_sinkEngines.push_back(deviceSinkEngine); - m_txDeviceSetIndex.push_back(deviceIndex); - ui->deviceSetTx->addItem(QString("%1").arg(deviceIndex)); - txIndex++; - } - } -} - -void LimeRFEUSBDialog::highlightApplyButton(bool highlight) -{ - if (highlight) { - ui->apply->setStyleSheet("QPushButton { background-color : green; }"); - } else { - ui->apply->setStyleSheet("QPushButton { background:rgb(79,79,79); }"); - } -} - -void LimeRFEUSBDialog::on_modeRx_toggled(bool checked) -{ - int rc; - ui->statusText->clear(); - m_settings.m_rxOn = checked; - - if (m_rxTxToggle) - { - m_settings.m_txOn = !checked; - - if (checked) // Rx on - { - rc = m_controller.setTx(m_settings, false); // stop Tx first - ui->statusText->append(QString("Stop TX: %1").arg(m_controller.getError(rc).c_str())); - } - - rc = m_controller.setRx(m_settings, m_settings.m_rxOn); // Rx on or off - ui->statusText->append(QString("RX: %1").arg(m_controller.getError(rc).c_str())); - - if (!checked) // Rx off - { - rc = m_controller.setTx(m_settings, true); // start Tx next - ui->statusText->append(QString("Start TX: %1").arg(m_controller.getError(rc).c_str())); - } - } - else - { - rc = m_controller.setRx(m_settings, m_settings.m_rxOn); - ui->statusText->setText(m_controller.getError(rc).c_str()); - } - - if (m_deviceSetSync) { - syncRxTx(); - } - - displayMode(); -} - -void LimeRFEUSBDialog::on_modeTx_toggled(bool checked) -{ - int rc; - ui->statusText->clear(); - m_settings.m_txOn = checked; - - if (m_rxTxToggle) - { - m_settings.m_rxOn = !checked; - - if (checked) // Tx on - { - rc = m_controller.setRx(m_settings, false); // stop Rx first - ui->statusText->append(QString("Stop RX: %1").arg(m_controller.getError(rc).c_str())); - } - - rc = m_controller.setTx(m_settings, m_settings.m_txOn); // Tx on or off - ui->statusText->append(QString("TX: %1").arg(m_controller.getError(rc).c_str())); - - if (!checked) // Tx off - { - rc = m_controller.setRx(m_settings, true); // start Rx next - ui->statusText->append(QString("Start RX: %1").arg(m_controller.getError(rc).c_str())); - } - } - else - { - rc = m_controller.setTx(m_settings, m_settings.m_txOn); - ui->statusText->setText(m_controller.getError(rc).c_str()); - } - - if (m_deviceSetSync) { - syncRxTx(); - } - - displayMode(); -} - -void LimeRFEUSBDialog::on_rxTxToggle_clicked() -{ - m_rxTxToggle = ui->rxTxToggle->isChecked(); - - if (m_rxTxToggle && m_settings.m_rxOn && m_settings.m_txOn) - { - m_settings.m_txOn = false; - int rc = m_controller.setTx(m_settings, m_settings.m_txOn); - ui->statusText->setText(m_controller.getError(rc).c_str()); - displayMode(); - - if (m_deviceSetSync) { - syncRxTx(); - } - } -} - -void LimeRFEUSBDialog::on_attenuation_currentIndexChanged(int index) -{ - m_settings.m_attenuationFactor = index; - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_amFmNotchFilter_clicked() -{ - m_settings.m_amfmNotch = ui->amFmNotchFilter->isChecked(); - highlightApplyButton(true); -} - -void LimeRFEUSBDialog::on_apply_clicked() -{ - ui->statusText->clear(); - m_controller.settingsToState(m_settings); - int rc = m_controller.configure(); - ui->statusText->setText(m_controller.getError(rc).c_str()); - highlightApplyButton(false); -} - -void LimeRFEUSBDialog::tick() -{ - refreshPower(); -} diff --git a/sdrgui/limerfegui/limerfeusbdialog.h b/sdrgui/limerfegui/limerfeusbdialog.h deleted file mode 100644 index 7fc32c8d0..000000000 --- a/sdrgui/limerfegui/limerfeusbdialog.h +++ /dev/null @@ -1,110 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// 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 SDRGUI_LIMERFEGUI_LIMERFEUSBDIALOG_H_ -#define SDRGUI_LIMERFEGUI_LIMERFEUSBDIALOG_H_ - -#include - -#include -#include - -#include "util/movingaverage.h" -#include "limerfe/limerfecontroller.h" -#include "limerfe/limerfeusbcalib.h" -#include "export.h" - -class DSPDeviceSourceEngine; -class DSPDeviceSinkEngine; -class MainWindow; - -namespace Ui { - class LimeRFEUSBDialog; -} - -class SDRGUI_API LimeRFEUSBDialog : public QDialog { - Q_OBJECT - -public: - explicit LimeRFEUSBDialog(LimeRFEUSBCalib& limeRFEUSBCalib, MainWindow* mainWindow); - ~LimeRFEUSBDialog(); - -private: - void displaySettings(); - void displayMode(); - void displayPower(); - void refreshPower(); - void setRxChannels(); - void setTxChannels(); - int getPowerCorectionIndex(); - double getPowerCorrection(); - void setPowerCorrection(double dbValue); - void updateAbsPower(double powerCorrDB); - void updateDeviceSetList(); - void stopStartRx(bool start); - void stopStartTx(bool start); - void syncRxTx(); - void highlightApplyButton(bool highlight); - - Ui::LimeRFEUSBDialog* ui; - MainWindow *m_mainWindow; - LimeRFEController m_controller; - LimeRFEController::LimeRFESettings m_settings; - LimeRFEUSBCalib& m_limeRFEUSBCalib; - bool m_rxTxToggle; - QTimer m_timer; - double m_currentPowerCorrection; - bool m_avgPower; - MovingAverageUtil m_powerMovingAverage; - bool m_deviceSetSync; - int m_rxDeviceSetSequence; - int m_txDeviceSetSequence; - std::vector m_sourceEngines; - std::vector m_rxDeviceSetIndex; - std::vector m_sinkEngines; - std::vector m_txDeviceSetIndex; - -private slots: - void on_openDevice_clicked(); - void on_closeDevice_clicked(); - void on_deviceToGUI_clicked(); - void on_rxChannelGroup_currentIndexChanged(int index); - void on_rxChannel_currentIndexChanged(int index); - void on_rxPort_currentIndexChanged(int index); - void on_attenuation_currentIndexChanged(int index); - void on_amFmNotchFilter_clicked(); - void on_txFollowsRx_clicked(); - void on_txChannelGroup_currentIndexChanged(int index); - void on_txChannel_currentIndexChanged(int index); - void on_txPort_currentIndexChanged(int index); - void on_powerEnable_clicked(); - void on_powerSource_currentIndexChanged(int index); - void on_powerRefresh_clicked(); - void on_powerAutoRefresh_toggled(bool checked); - void on_powerAbsAvg_clicked(); - void on_powerCorrValue_textEdited(const QString &text); - void on_modeRx_toggled(bool checked); - void on_modeTx_toggled(bool checked); - void on_rxTxToggle_clicked(); - void on_deviceSetRefresh_clicked(); - void on_deviceSetSync_clicked(); - void on_apply_clicked(); - void tick(); -}; - -#endif // SDRGUI_LIMERFEGUI_LIMERFEUSBDIALOG_H_ diff --git a/sdrgui/limerfegui/limerfeusbdialog.ui b/sdrgui/limerfegui/limerfeusbdialog.ui deleted file mode 100644 index a8b75a21f..000000000 --- a/sdrgui/limerfegui/limerfeusbdialog.ui +++ /dev/null @@ -1,1012 +0,0 @@ - - - LimeRFEUSBDialog - - - - 0 - 0 - 411 - 499 - - - - LimeRFE USB control - - - - - 310 - 460 - 81 - 32 - - - - Dismiss dialog - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 10 - 10 - 31 - 31 - - - - Dev - - - - - - 210 - 10 - 51 - 27 - - - - Open device - - - Open - - - - - - 270 - 10 - 51 - 27 - - - - Close Device - - - Close - - - - - - 50 - 10 - 151 - 27 - - - - Device - - - - - - 330 - 10 - 51 - 27 - - - - Transfer data to GUI - - - to GUI - - - - - - 0 - 40 - 401 - 16 - - - - Qt::Horizontal - - - - - - 10 - 50 - 91 - 27 - - - - Rx channel - - - - - - 10 - 80 - 151 - 27 - - - - Rx channel group - - - - Wideband - - - - - HAM - - - - - Cellular - - - - - - - 180 - 80 - 221 - 27 - - - - Rx channel range - - - - - - 10 - 110 - 61 - 27 - - - - Rx port - - - - - - 70 - 110 - 131 - 27 - - - - Rx port - - - - Tx/Rx (J3) - - - - - Tx/Rx HF (J5) - - - - - - - 210 - 110 - 31 - 27 - - - - Att - - - - - - 240 - 110 - 51 - 27 - - - - Rx attenuation - - - - 0 - - - - - 2 - - - - - 4 - - - - - 6 - - - - - 8 - - - - - 10 - - - - - 12 - - - - - 14 - - - - - - - 300 - 110 - 21 - 27 - - - - dB - - - - - - 330 - 110 - 71 - 27 - - - - AM/FM notch filter - - - Notch - - - - - - 0 - 140 - 401 - 16 - - - - Qt::Horizontal - - - - - - 10 - 150 - 91 - 27 - - - - Tx channel - - - - - - 110 - 150 - 111 - 27 - - - - Channel same as Rx - - - Same as Rx - - - - - - 10 - 180 - 151 - 27 - - - - Tx channel group - - - - Wideband - - - - - HAM - - - - - Cellular - - - - - - - 180 - 180 - 221 - 27 - - - - Tx channel range - - - - - - 10 - 210 - 61 - 27 - - - - Tx port - - - - - - 70 - 210 - 131 - 27 - - - - Tx port - - - - Tx/Rx (J3) - - - - - Tx (J4) - - - - - - - 0 - 240 - 401 - 16 - - - - Qt::Horizontal - - - - - - 10 - 350 - 50 - 25 - - - - Switch Rx - - - RX - - - true - - - - - - 70 - 350 - 50 - 25 - - - - Switch Tx - - - TX - - - true - - - - - - 70 - 320 - 111 - 27 - - - - Rx/Tx state - - - None - - - - - - 10 - 320 - 50 - 27 - - - - Mode - - - - - - 10 - 460 - 71 - 27 - - - - Apply changes - - - Apply - - - - - - 10 - 390 - 391 - 61 - - - - Messages - - - - - - 130 - 350 - 70 - 25 - - - - Rx/Tx toggle - - - Toggle - - - - - - 180 - 250 - 31 - 17 - - - - Fwd - - - - - - 230 - 250 - 31 - 17 - - - - Ref - - - - - - 230 - 270 - 35 - 17 - - - - Relative reflected power in dB - - - 00.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 180 - 270 - 35 - 17 - - - - Relative forward power in dB - - - 00.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 320 - 270 - 25 - 17 - - - - dB - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 280 - 250 - 31 - 17 - - - - RL - - - - - - 280 - 270 - 35 - 17 - - - - Return loss in dB - - - 00.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 10 - 250 - 50 - 27 - - - - Enable power measurements - - - Pwr - - - - - - 80 - 250 - 24 - 24 - - - - Refresh power - - - - - - - :/recycle.png:/recycle.png - - - - - - 360 - 250 - 45 - 17 - - - - VSWR - - - - - - 0 - 310 - 401 - 16 - - - - Qt::Horizontal - - - - - - 80 - 280 - 24 - 24 - - - - Auto refresh power - - - - - - - :/play.png:/play.png - - - true - - - - - - 350 - 270 - 50 - 17 - - - - Voltage Standing Wave Ratio - - - 1.000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 10 - 280 - 60 - 25 - - - - Power measurement source (EXTernal, CELlular) - - - - EXT - - - - - CEL - - - - - - - 120 - 260 - 31 - 17 - - - - Corr - - - - - - 170 - 290 - 75 - 17 - - - - Corrected forward power in dBm - - - -00.0 dBm - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 250 - 290 - 65 - 17 - - - - Corrected forward power in Watts - - - 0.000 W - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 110 - 280 - 55 - 27 - - - - Qt::ClickFocus - - - Power correction in dBm - - - -00.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 320 - 290 - 50 - 17 - - - - Corrected power averaging - - - Avg - - - - - - 230 - 350 - 50 - 25 - - - - Index of Rx DeviceSet - - - - - - 310 - 350 - 50 - 25 - - - - Index of Tx DeviceSet - - - - - - 210 - 350 - 25 - 25 - - - - Rx - - - - - - 290 - 350 - 25 - 25 - - - - Tx - - - - - - 210 - 320 - 100 - 25 - - - - DeviceSet synchronization - - - Rx/Tx Sync - - - - - - 370 - 350 - 24 - 24 - - - - Refresh DeviceSet indexes - - - - - - - :/recycle.png:/recycle.png - - - - - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    -
    - - - - - - buttonBox - accepted() - LimeRFEUSBDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - LimeRFEUSBDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
    diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index efd65e253..88c365528 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -97,10 +97,6 @@ #include #include -#if defined(HAS_LIMERFEUSB) -#include "limerfegui/limerfeusbdialog.h" -#endif - MainWindow *MainWindow::m_instance = 0; MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parser, QWidget* parent) : @@ -1474,11 +1470,6 @@ void MainWindow::createMenuBar() QAction *ambeAction = preferencesMenu->addAction("A&MBE..."); ambeAction->setToolTip("AMBE options"); QObject::connect(ambeAction, &QAction::triggered, this, &MainWindow::on_action_AMBE_triggered); -#endif -#if defined(HAS_LIMERFEUSB) - QAction *limeRFEAction = preferencesMenu->addAction("Lime &RFE..."); - limeRFEAction->setToolTip("Lime RFE options"); - QObject::connect(limeRFEAction, &QAction::triggered, this, &MainWindow::on_action_LimeRFE_triggered); #endif QMenu *devicesMenu = preferencesMenu->addMenu("&Devices"); QAction *userArgumentsAction = devicesMenu->addAction("&User arguments..."); @@ -2136,17 +2127,6 @@ void MainWindow::on_action_AMBE_triggered() #endif } -void MainWindow::on_action_LimeRFE_triggered() -{ - qDebug("MainWindow::on_action_LimeRFE_triggered"); -#if defined(HAS_LIMERFEUSB) - qDebug("MainWindow::on_action_LimeRFE_triggered: activated"); - LimeRFEUSBDialog *limeRFEUSBDialog = new LimeRFEUSBDialog(m_mainCore->m_settings.getLimeRFEUSBCalib(), this); - limeRFEUSBDialog->setModal(false); - limeRFEUSBDialog->show(); -#endif -} - void MainWindow::samplingDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex) { int deviceType = (int) deviceGUI->getDeviceType(); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 21488f49a..81b9e2fbe 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -182,7 +182,6 @@ private slots: void on_action_Logging_triggered(); void on_action_FFT_triggered(); void on_action_AMBE_triggered(); - void on_action_LimeRFE_triggered(); void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); void on_action_commands_triggered(); From 41901fed2505217e725a10b6169f6a5e05ecd399 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 22 May 2022 23:13:04 +0200 Subject: [PATCH 07/29] LimeRFE feature: move calibration to settings --- plugins/feature/limerfe/limerfe.cpp | 17 ++--------------- plugins/feature/limerfe/limerfe.h | 4 ---- plugins/feature/limerfe/limerfegui.cpp | 7 +++---- plugins/feature/limerfe/limerfegui.h | 2 -- plugins/feature/limerfe/limerfesettings.cpp | 3 +++ plugins/feature/limerfe/limerfesettings.h | 3 +++ 6 files changed, 11 insertions(+), 25 deletions(-) diff --git a/plugins/feature/limerfe/limerfe.cpp b/plugins/feature/limerfe/limerfe.cpp index a8d21dc7e..bf49c64bd 100644 --- a/plugins/feature/limerfe/limerfe.cpp +++ b/plugins/feature/limerfe/limerfe.cpp @@ -128,10 +128,7 @@ bool LimeRFE::handleMessage(const Message& cmd) QByteArray LimeRFE::serialize() const { SimpleSerializer s(1); - s.writeBlob(1, m_settings.serialize()); - s.writeBlob(2, m_calib.serialize()); - return s.final(); } @@ -148,31 +145,21 @@ bool LimeRFE::deserialize(const QByteArray& data) if (d.getVersion() == 1) { QByteArray bytetmp; - bool ret; - d.readBlob(1, &bytetmp); if (m_settings.deserialize(bytetmp)) { MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, true); m_inputMessageQueue.push(msg); - ret = true; + return true; } else { m_settings.resetToDefaults(); MsgConfigureLimeRFE *msg = MsgConfigureLimeRFE::create(m_settings, true); m_inputMessageQueue.push(msg); - ret = false; + return false; } - - d.readBlob(2, &bytetmp); - - if (!m_calib.deserialize(bytetmp)) { - ret = false; - } - - return ret; } else { diff --git a/plugins/feature/limerfe/limerfe.h b/plugins/feature/limerfe/limerfe.h index 9f24490bc..4f8e168e6 100644 --- a/plugins/feature/limerfe/limerfe.h +++ b/plugins/feature/limerfe/limerfe.h @@ -28,7 +28,6 @@ #include "util/message.h" #include "limerfesettings.h" -#include "limerfeusbcalib.h" class QNetworkReply; class QNetworkAccessManager; @@ -137,8 +136,6 @@ public: const QStringList& featureSettingsKeys, SWGSDRangel::SWGFeatureSettings& response); - LimeRFEUSBCalib *getCalib() { return &m_calib; } - int openDevice(const std::string& serialDeviceName); void closeDevice(); @@ -162,7 +159,6 @@ public: private: LimeRFESettings m_settings; - LimeRFEUSBCalib m_calib; bool m_rxOn; bool m_txOn; diff --git a/plugins/feature/limerfe/limerfegui.cpp b/plugins/feature/limerfe/limerfegui.cpp index 32c136820..ef3454a05 100644 --- a/plugins/feature/limerfe/limerfegui.cpp +++ b/plugins/feature/limerfe/limerfegui.cpp @@ -147,7 +147,6 @@ LimeRFEGUI::LimeRFEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature m_limeRFE = reinterpret_cast(feature); m_limeRFE->setMessageQueueToGUI(&m_inputMessageQueue); - m_limeRFEUSBCalib = m_limeRFE->getCalib(); for (const auto& comPortName : m_limeRFE->getComPorts()) { ui->device->addItem(comPortName); @@ -556,9 +555,9 @@ double LimeRFEGUI::getPowerCorrection() { int index = getPowerCorectionIndex(); - QMap::const_iterator it = m_limeRFEUSBCalib->m_calibrations.find(index); + QMap::const_iterator it = m_settings.m_calib.m_calibrations.find(index); - if (it != m_limeRFEUSBCalib->m_calibrations.end()) { + if (it != m_settings.m_calib.m_calibrations.end()) { return it.value(); } else { return 0.0; @@ -573,7 +572,7 @@ void LimeRFEGUI::setPowerCorrection(double dbValue) return; } - m_limeRFEUSBCalib->m_calibrations[index] = dbValue; + m_settings.m_calib.m_calibrations[index] = dbValue; } void LimeRFEGUI::updateAbsPower(double powerCorrDB) diff --git a/plugins/feature/limerfe/limerfegui.h b/plugins/feature/limerfe/limerfegui.h index f3563b8ea..17448886f 100644 --- a/plugins/feature/limerfe/limerfegui.h +++ b/plugins/feature/limerfe/limerfegui.h @@ -31,7 +31,6 @@ class PluginAPI; class FeatureUISet; class Feature; class LimeRFE; -class LimeRFEUSBCalib; class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; @@ -63,7 +62,6 @@ private: PluginAPI* m_pluginAPI; FeatureUISet* m_featureUISet; LimeRFESettings m_settings; - LimeRFEUSBCalib* m_limeRFEUSBCalib; RollupState m_rollupState; bool m_rxOn; bool m_txOn; diff --git a/plugins/feature/limerfe/limerfesettings.cpp b/plugins/feature/limerfe/limerfesettings.cpp index 12a87b6fa..98d94ffb0 100644 --- a/plugins/feature/limerfe/limerfesettings.cpp +++ b/plugins/feature/limerfe/limerfesettings.cpp @@ -93,6 +93,7 @@ QByteArray LimeRFESettings::serialize() const s.writeS32(38, m_workspaceIndex); s.writeBlob(39, m_geometryBytes); s.writeString(40, m_devicePath); + s.writeBlob(41, m_calib.serialize()); return s.final(); } @@ -168,6 +169,8 @@ bool LimeRFESettings::deserialize(const QByteArray& data) d.readS32(38, &m_workspaceIndex, 0); d.readBlob(39, &m_geometryBytes); d.readString(40, &m_devicePath, ""); + d.readBlob(41, &bytetmp); + m_calib.deserialize(bytetmp); return true; } diff --git a/plugins/feature/limerfe/limerfesettings.h b/plugins/feature/limerfe/limerfesettings.h index 2e29381b5..8dca60c6b 100644 --- a/plugins/feature/limerfe/limerfesettings.h +++ b/plugins/feature/limerfe/limerfesettings.h @@ -21,6 +21,8 @@ #include #include +#include "limerfeusbcalib.h" + class Serializable; struct LimeRFESettings @@ -109,6 +111,7 @@ struct LimeRFESettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + LimeRFEUSBCalib m_calib; LimeRFESettings(); void resetToDefaults(); From 928a4c76e03c7af2387558a03b79e2ecfde562aa Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 23 May 2022 00:16:50 +0200 Subject: [PATCH 08/29] LimeRFE feature documentation --- doc/img/LimeRFEUSB_dialog.png | Bin 49006 -> 0 bytes doc/img/LimeRFEUSB_dialog.xcf | Bin 167081 -> 0 bytes doc/img/LimeRFEUSB_dialog_power.png | Bin 13842 -> 0 bytes doc/img/LimeRFEUSB_dialog_power.xcf | Bin 55816 -> 0 bytes doc/img/LimeRFE_plugin.png | Bin 0 -> 48577 bytes doc/img/LimeRFE_plugin.xcf | Bin 0 -> 145207 bytes ...og_mode.png => LimeRFE_plugin_control.png} | Bin ...og_mode.xcf => LimeRFE_plugin_control.xcf} | Bin doc/img/LimeRFE_plugin_power.png | Bin 0 -> 14589 bytes doc/img/LimeRFE_plugin_power.xcf | Bin 0 -> 48794 bytes ...SB_dialog_rx.png => LimeRFE_plugin_rx.png} | Bin ...SB_dialog_rx.xcf => LimeRFE_plugin_rx.xcf} | Bin ...SB_dialog_tx.png => LimeRFE_plugin_tx.png} | Bin ...SB_dialog_tx.xcf => LimeRFE_plugin_tx.xcf} | Bin .../feature/limerfe/readme.md | 97 +++++++++--------- sdrgui/readme.md | 1 - 16 files changed, 46 insertions(+), 52 deletions(-) delete mode 100644 doc/img/LimeRFEUSB_dialog.png delete mode 100644 doc/img/LimeRFEUSB_dialog.xcf delete mode 100644 doc/img/LimeRFEUSB_dialog_power.png delete mode 100644 doc/img/LimeRFEUSB_dialog_power.xcf create mode 100644 doc/img/LimeRFE_plugin.png create mode 100644 doc/img/LimeRFE_plugin.xcf rename doc/img/{LimeRFEUSB_dialog_mode.png => LimeRFE_plugin_control.png} (100%) rename doc/img/{LimeRFEUSB_dialog_mode.xcf => LimeRFE_plugin_control.xcf} (100%) create mode 100644 doc/img/LimeRFE_plugin_power.png create mode 100644 doc/img/LimeRFE_plugin_power.xcf rename doc/img/{LimeRFEUSB_dialog_rx.png => LimeRFE_plugin_rx.png} (100%) rename doc/img/{LimeRFEUSB_dialog_rx.xcf => LimeRFE_plugin_rx.xcf} (100%) rename doc/img/{LimeRFEUSB_dialog_tx.png => LimeRFE_plugin_tx.png} (100%) rename doc/img/{LimeRFEUSB_dialog_tx.xcf => LimeRFE_plugin_tx.xcf} (100%) rename sdrgui/limerfeusbgui.md => plugins/feature/limerfe/readme.md (66%) diff --git a/doc/img/LimeRFEUSB_dialog.png b/doc/img/LimeRFEUSB_dialog.png deleted file mode 100644 index 64cd98f3cb6553c207747d1cc1fe28ca070c34a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49006 zcmcfpcQ}@R{67v~Mj5446jD?~rN|1|k)kpqvNF>mBYPzk4a$n_kwo^$rpTtUQbJ}{ z_Fnhn?EU%t?(g?^+<)I4$6LK|UDtWN&e!w#e5}{yqjE`sj+TX%L?Y3hKPRU~B9W8f zKYQxU#CIa@7vrzZMoJ2D_#1zg&J?=iFB+?J+BPK8mR-buWTdcgCVZ31_WVV8s(!Lv zjEuX7Way8PNbIEZaxxkYKgNDJy3lEDlub1?D9$&0Nd@7abPeGqq+S8_0%&f5xXnY>; zM=nF7_5^K$B-8ee?d}(je3-KfJ1FBpN!IJ#^g1X;$>vWd>q!2`ap#`dlE5eaPwrz8 zJa%3**nVQ|!~lCma{Sja=V#IG3U3#72j+P`UP+%|xL~j?#$Hj|^HoQ=eYv6PwtEep zIfoKnV%2E9Vx=ePzGrUF9eZTgEyH~1$ety8o@e`d<9quZif4HV~He?3)01 zLS%n$DXnKO>x>T^v)^^>&&-yTeVbHctXYX|!5!qbad0Tx#@$q(^&r^>K9|{! z{`?d|!M{;DCw-)2g00E>wfXNJqqjUw>`KH=>fo$0U*F5D#-HYS``TRI#>&Rr&oAyr zZ*{8E&fk8&5Z$=KmlWJGVr40cPaho?KEy9|^Q^~)uIPTEbOKq-?(y|1r`tRnZeZ^aM zsF}-QApbNW5|^f*aOO#j&+EV0DF))F8Mn?!z2M)OCY}F|Z`qPh@Zm(*$F-ZB!pYs+ zy`)CpiH?rCbs$W)63UW1(e0Q3=Sn;6&+%`O0~r) z@YUG!h%E=MeW~2iWxLt<%m>Fk^Q@29G|MkC^YIdI>G8`WBPZ*xyja??!FW$>;SM=b z^sx_S#(7NH(I;Y~1!p{tMC~41{;xUsk}3Pu`=L;#wB9%g{1HqrZ08~hvbiOIpWa&4 z->`^#%(AK8k6W~TkMs*8dRg(j0dI#9e=^hmX)TXGsv8whEYFW0b5^H5P@$CJvSdv| z?i*%W7ks{fL6%ze?eg3x*KsoW7qRD>9{#_ZkQ~i+@@$>=W2!{2()phzRVMJkaa7p+ zL}&CZ<}**X&Z#nZ_Xpa>?oaH|DBqg)hAuw0h-0+vz46BS8p*-gS-sS4O*dp_W~Og- zX=bE*d3iaFCqjEG9bHCN)~8ROyo7K6FuZ>K)x>ni(&C~@vgZ3C{dvbatHyBA2M->Q zCR?=(u31{9HE!9mr7x{?{h+vb_~N3&g$ozbb8-^Y5)`ve56Oa_Zf?QChk} z`JUm?PG=_G_gm#%N{{-gam=gg=&-za@#0o@NkpOjMDO_cn(mTIo4B~8+l-Fziqm~B zuTUxXlE%!8jEsuUpLepbC_ZBm!WA`~n%KTyyr|xUUQir#AFT-6F<+ z=21*8kERX1PP?B{Wxd}~Sir2FWVpIGrN*?AlEH;OnwwU2I~%!5W8=1GXFtVvwTjsP zroHxi;UwReGs&%L8m`0j+>^bsjtnP1D z@m_gVK0dx^o1sRHcN#wN@f`SGT}PI=U9HHXt8s&J#A>P;q~{9j<4^po;s?PS|5D;s)*eP!`ZWETavZ>w(S%9BiQ`#;ls?E z?I&c8#(&ryPSWcZf^&|K8lZ=eJsl(a1xu3s&-JzhM@O+&P{}iA z&OCkg?5>qn^o`bc;iv7ldGz=9C+L<0%Bp@I# zm+HFk_t%4m4`04~c|Re66VH`>v;9(H-L>-jn{K!=ow~z%HSZ3mpdbT&xyx~A-;Zny zud{L22J1sR3+$BEx{4fUT1wp)e^Y6uT!~3F@2&8;^&{ISJ)Mt5@VYdfI_lU(<-cjM zKls&Hn}#=pnV1C3o-S{&xvyrlL=7p)E0v`dD|GC?^=NY<>�ArQH2nP3zqY$w>VC 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

    )ppeq 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 Y=eBP7tjO{yiU9~bUHx3vIVCg!0I0zU%K!iX diff --git a/doc/img/LimeRFEUSB_dialog.xcf b/doc/img/LimeRFEUSB_dialog.xcf deleted file mode 100644 index c6e8763bb9c2037de12bfac58374f89390f4c421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167081 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 Nt=|^<{KKvD{{enXZE^qr diff --git a/doc/img/LimeRFEUSB_dialog_power.png b/doc/img/LimeRFEUSB_dialog_power.png deleted file mode 100644 index e59a302263491c793ce3bf9ebce85a4a97c526ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13842 zcmch;byU=E^fd}7Aq`3+U=Y&MAT2E=9nuOC64H&NfM6gX4bmVX-6bU zJ@fs&cimX;pLgBm!hy->bDned*?XU7!qrvf32>=#(a_Kc6cuDN(9qD4@Ou^x7W^MU zY1IQaSmsaUWzeor|I!aF@E?`s)!gIEd;2n{?5&~_XRHzzXYEQ{)!(ES2?mytleU(YmN}y(h}Yxz z_lAhaG4!<%Wa{b&N6c7Xy%l6v`(GTK9yHz`7q!K!fu5N$-{6Ye-ibVEjdesqH~KNQ|sY@wxuy+IN?zQ)F*sc|CwDk%N8`a{ee zwJ0s2$SwtQ;dXR*8-mY`VWwkO@Xq=rR3B@IE-}262WQ8Z6c5Kdg?v`7^(cG3&T7*a zl_TxFLm0rY$Vo|&kY8&1K*k|(Nv|?@d+c`SCC)FkfE~(-i^d5>)?~)FXU3Utmx-|@ ziJb&_5Q(iVR;Dey^J|3h#c!OOZJr01NU+rH{7ozM!##JuH{$v+Nd~q28AopImNkvMt);hEOyV+a%3gJKuXRVPwWco>AyANek$3FT=2q>`9|ig zbyux2?A4sjj_s&SBY`(z-MbLYRs2%Z%%&ip>}RG@c?{1_C(l^arKa!fE+8HMR!ls; zw^>rGfvo1DyIwnK9;0-hi(Vik@M?N~1p8ZEz?ta;F~$-9e7_l%US2f%U=!Cj-)Ym5 zUcrr#k2^?*#Y()zTD;IVwPeZ&w4%DUhE&IYc@tTm!bQ*@TfN+QJFWui=d1i(%X9%>1eTcSUVm zb9%k~$;N>j@urB}QqNp;vc>S#g`a-h`!|A`>H4W>&YEB0eVM^HbqldF4zs(#NZjsB zaj;Xge1l##?<(yNTU&81pJQ;ey{Q???AO@a?=B?t#BOZgr};Lc&FYFTgVb%!cr>y> z5HuI$9N)Tn!^1fbkpveLD8ARGTw0Pl+tj3f;IV{tY*sUo|4F~zs8L@(FKIb9s)_l& zL%(1E2L2f?>D@avK~g4R^;AUdlFwU8>PMW2nFU(?>wcV%zHY{afCld3JZD>w!FYEf zGs7o=Aii61n>t|5c3w+i9QHy*Y)X2$vbF|H*Bo2}J-!jTP0w!9} zxF0?h{5fqv(`MK?^tVLbkm7K#-NI^V_LdCd@RPqE)*Ru~8Ln=)hzD+iTx3*mkHz-= zni#^?E6K$dhwKsd{idgsj>RRZ`Kmj$v0CQ^_WhzGv-Rlx>d5Bs^|6@r=1~sjvADs; zn|)W+zZc#&FQ<~Bs+T1MD|+`*ud-XBJH_S8Ugbm{PyP;0$wAlv6S)|J>@!kyb z3b565eWG-ZdHA8dWL#3UctU~LiK0LYQLp<`Be5;hXD7yI)W9et1~| zy}Q3(He2IkY;1Onb7fUkXM6i~XXkx?e@%|+e=~1$3BXnH-b({9~ z?G9&(>r$wQlg27#A5M;q>D)Udpb^M2`w=Si=#hn$mAsA)c|51lVoxmFvj&gv-R>cK zB-GRqDMr7^b3^bb%AEfEDU#!HT9jEIE$qro^E1d_&U>1Pg^7uYcI(zH$F(8Gv2Xf6 zs$NYiUHHw^xt?s-@03`PLwx{eD42gQq66 zD!R7IbjfP_nZ)SZk3J(_s#b{^gVS^5mr`F`SYi*Nz2VU?X( z=Q}E&s|)1P^0M_2tSl9afV$N8%uU2?vwPa1xj)IFNzu-Z9XBL!w$Yo{VOBJh@V1hN zhtQ8OqOP8vh|NtqG#?)y1r?QS=ha`-PVTO**_Qo@GP1J2bJge<7Z)kTPG{>7mA2!S z@Iiga!ZGf%p1B4_*dY%oIC*(v-o1M#KgGk#Te$UjvOt@y&UK@+<;^wgv2RqsR+d&* zW0I1d`1y(BhF~x=Gh^XXwk-XOadLBu6V%7lZ1(eQYilblEk#R*&z6yw|Fg1!m1l9g z?Z=Prm6aX+{Zah3W5kq{-EARw`TFISv1$JBRtpOY&uSc{oPR|}NB5;Xisvw_BEEI2 zoyMWzjdVuKj~_wS!#Pjktpyxrm4mPd4pu~N=@MpmT%N4b@9pjV9V<#2y9@|GbK9Jd z^70aaS1;`?_qKO-+R%o5_&^NN=@~856?WUC6c!ft@IcV~+5Ge8M;wRY?!SM(r`!+z zJ{=m0*y&MLR9qO4xXOYg8Qm74tgI>2V%5;lptCOIoVChW^}8CYdS%GY&d$xxFPpDL zdi!?&N`I1mv!7U!z$^2s%Zuk$R#0Ga(^api1np_zI!AgQ4|0qb8~uTrfa<$8zr5V> z?@t#$S0j|G)83Ne(D3ld@t^lMZrm_y2|)KbS&lzGInh2jNlHlgU2dh8DDbN1yG0k; z@luRhjqjO25H^uz_lG+!e+S>R24Oq@9lX%6*mFs$}xejcr*Ba-50e}8s*dcfPaICU;- z7y;$wygE8M=cgMb9$sEH2Dj>6)}%c=Ji=Ess_l&+2iq5ZgcZ)Xa-UkB4262~JIp38 zEj?EmR&!mDdRSFjI*8+HrXs?_^Qp9yyI-7!h6XV+?I{keA1T%Ic5zjhvK_?_imncAvtP0OPVh+ z7=(orc6WDQ6FK|PuJk25_Va66Nez#Tw3w=}P8RbfmzS4^tfv%mWB^Qoy|@WkmM#@U z_oynJwdam#G5{ZsWgjIq-^&)r2G8xbuQr~_vE-=4l)>fE|VEl+Q>>v zV{D9z?gUfh zadC09;y#p5o;<1FM%pZ%0P7 zY#4bxiKcW-tX_I!STwNVSj_^qf2{kZw+OahRRwbc)q`+$Xo;o(CP2L}g;gUYjK z$vFf6F5jMsxUOrR)N<*UU8kp~SCLpd+!#lvgil6CN56jk`XH*A5Z*2rAD?f1jgwve zqL=*q;=;+vDFeVOI5_xawCgs4ih_cof3>eq8M>F8iwhqhI_5m&N3w`(1jJ)FUz7Rk z*RN=AqoX@}d-2rN2EufzIE@>+0X{qd<_ohhpR_lU)?Q&GB_{Hix8nfzPL{pswHQ~` zEi#ZrZQ68oiO%qV|J9j^+sM4fA7R@VK~!;Rd32B}`_`K%Ji&!2ArWOi0q4+Fe2K%aoxm5Zj$_hgcfqVB-s zYDoWILApso@+Lg|hC}m(fd9o|d>j%{>uhz2Y`*$k_Tq`Mva&kMEe}u6-w>64F+yT; z@=mBy1yxlhMn+lZ9YKd#S}v{<4f%L>!zz4eFpbU4G6?#cP~FhOI?4yc4cl$!>RfGW z6GzB!F+Dv!W#r_B0GRvw`_TbImd;Q1YwGGS<`)*EWMr<_*49EPSy)*7*dLG>fRaP?ZXk439m61^X`<*lnyTEa5FuOy-!iM3xpm6xlJ}WUY ztEFZs#xQ07dYh1t@N+2pNdliW>Cw^AXwT*7*8wp>!Fa;kBJ(RNopb(IQ5yN|)#HTg z3VlyA<-SAtLEA!6AE0hCuCkj93=6{s1OY(j2FeiSPD4tnk)67Ik?ICLlQkAM zMEXs3_KSpHy;D=vnwpv_`w1S~%b8G>L}Hh{T+O@i^HQ8%%m+G`S5xgdDoGKWM7X$jSy=L>CYuc2oNsb;--f41Twe-`v^~e!YYvA5FUut808mNazbBuJie^lgidVC_`=g zc66#@n2&SnU+=c{cO?HN8mg^~6&Z>k z5pZ3-2W!J8D@hI$)eiK4Y{#dkJQkh!HY0hh@Ra2>vg=ZL+l#63@m)j`=Pg!gzF2ul zjxJ(8ThK)NQpJ*hu9ex(h(Ie5a^I$X9EuO|&~td@47&rdknfG-_+C~vgpcUB>p)AFTiHUSARtNzR1()&2+KHaN{zK2b=j0p)S&WO1+&1Uo;}exMfL14n z`-%WbLQw}mjq;bj?*d|-?EWD7+1=d&SN-J+r7D2&T$3-{kq#+oaFO$$*uqlQ(z*qO zS7A3<_RM#SF++MY(1F zU1-(NvMBKI@W?rzbwd)PI)^%Itmu9pZ^Py3#(ieyVc=R*)ecvKaq$ITH4E!Kw04yO zmKW3;Za=i5qkHF4{ginqS@huaq`KY*=Zx*)=%SYvS*RoZXXGWZ)A(a8)>zA-#IB5r zbY}Gd7B>kH9Ht!+WQhYn-8ls$vkE%In3=GOzcXRYwoMR+ z;`pbNAn_X3{bl{>ljr5KBNP*3uyq?uMmJWQpYpx%@}&83n8%90Anx()%;E(m7T)`! zFC-jt9NSDxOd|!_8y}lBjT99Xm(OR2B_)7fPQXrh zA8qEB>ZjSyHK~9gg|uOFlEMK{R+*CCM$~5V-2UU^1tz)_%AUDu{8l6XKFFhc^3j9oQrDmlM+F64v$nR*%F1%QICU*HZZz0*cU`Vc z6ZcK5sNg$A?hJtVtBe*q9MPuRGBDE6c$hI;4y0d@(`HVB>*4B!0a#(<< zj9%sAB_`uR*9 z#gk$hijk_peVD2^KNmr30%YUs3?w@;nv zwMH&72Hw`_J_eE&9v)s(Q=<+HJ7o!wUQt>)(6Vl3j}-Jz$|HA5F)uzKzar4*AoY0x z2)<@!qPt4Yz{(~YJbBZN)fX?rjX|2T5+o(gOI#Bn2$+gm7{h!!i!!W z9;9#0`OX!vwrA@3-Ms_D{ZU%O_%{cvPzk7CvDxH%25@5%-c@4K{A(m%Gg8d$iswVR z1aI1nK-1t6dko$q>6sPe5x*S)I{jl+Cc7l&MPuWU$9kMJ$0xd#xt{GUsK8pqbR1|)rEUt zP|yhkcXxmP9+#R??Lc=o13UXWOgwU}p1nMvb$?>i#4`YRAWbvdT?h#YeSiU0fIfy6 zB>niYeuKwDp#CD2{+9Rp2gFQ=VlxZfAGR=+H_Ux+qwUE7s2XnqEO!O(ZckDr@9a0j;ucieZ zuso`3!pOz-J})mXjBsBCIM&^}fk30QYi7#I%M-ZGFbJsmOhHmI=9HI~rdti9Hb8bS zt*n5#(Su^?I5-U4-0D_GT!LF?1y|d~1}}{3en)uEYfBJ@p3uzniVDwtdd}6`MXn97 zIAIdrTGBaNkmOWIM_Z85%xgnM3cIxo^v{6nd90Fb43o-Ba1nCV(#Dk1K7S65jC}Gj ze)7L0uoMQ5DM8qo36<>3s>v;&bHMHS-p)^zoSX~_-!Q+hVl9wH&>jttBPcA#`St6s z(0|x;i*J38@euikiW@W*IbRYx;^PUajp{2eC1~RFmADZFw?3+M<)&P!`*w>|!FMx04&Hx#nN=MvXS_GaMXqxllPd z_&ck4xRQ-nV>J(uJ5gNc?h`Ix^RizD+SwmTFjD@&z`)>BY^?T@-a;=I8(ZOs4_%y2 zkJ*=HM@L5%p0JLfULl9s2L0hxdtkAkX!L7eGv1SXCz-FMbF%+AAt5|F+pRY{Dk`dz zv#!j)_UUDk)%0vB3!3TyHiL5&;cVX5(a2QFr|;l|nsCsArx zZLM%ee_rgzk5XW3B!o^aJzrJW@3>|+T@?kS1n7M$>>R2JfZaeiNl8iDpa*+^k`?wl z7y1!OaPQHhBb#-M0 zRYA{Rys$oW(iH%d=mPf&7#}bl6eQeaR$UralR(*V?&mfc)aN_%^|#xOueGcD+$KPa?=jt-tIF&*76(9M;ph44SG$F6DAy<7D31K{?t z`Mk*n()JMaBIr48LBa8FBSNMvXuy9vpnFVKy`n+GBWIrvA`+i}FL8;AEiLUgVI8`& zjn7}^?(7pDc+(fBywR+09l;kl4BP%Q+JCgl=0Eb`X{5JVbL4m2M|rv;&Mq!KSxs3H zrM(xcW@ctj&9fJsN)-_AXNobstgra;_?&d}wb+g7K7)Zv?(GA*Y7~dve_Ibp*ic5x zbY*;SeCl5K{Oz+!oIKv((Ux{jAg)+S6S&zG-#=_jPfFmrZ;@WxvWhMnOg7~4uQHzR-%2SqG0H-}a3^4!pPT?#k9Q?h+j3`Ll zBh2Ls-MFKr2E)$$8VV~4US8gY#zvIK`uq3q`%FyY8fXz9zq%;L}h}F zq-hb3sG%VN4*z3ptm-Z0OpQ|z_((BeDh@+7S`MTZDQXt#aFDa>wE)Y_(=DNc4u?MS zbO!hyHAw?_h&P#(xx1C#hlF4n8ymZ9BN8}`>Kx}=&{5Wfl9EyaxgnVR!99772w4-W zfSQe4GG2}TjE!`I|7O28Qu%H6@A5wTQGX(JrC-;t)^3DMJ!|mX7jnrvUbq2%ggImm z%E3FotQR?TY74#*91>z3>P1G_1x?)ony7NB=w!Mm@)Pimj**eipws^B>P3s8amSj3aK88H^i^bou55>hO$1PX`)&mhrkvPtpuJIxFF?Mpt1CQc^G8T z6}@(zOxQ6k<(L_kQ~Sc9hXZ?<^}Bvq6sDV48GT|-?M#W#%;?NWw!4E!j9a;4{@UwJ z>Q5GPrOKWw6Z}hMx@%>}7Jh_MW~clEZM#f&-n}Hn4G6Opp(|eVIt?~L4~&#>xqXcS zxxKC;>V83v4C5b1@k5Xk$->S;pge(-eg&_kFHNGkeMifNRkaQcjm!ph79e}PpwRMU z@km%mXt3VhK~z+9qS;>p$n*kWALEo(+CxD>tCPKDAOlN9f{VtIk1tpWwdU27 zvd2^Cg&M;~M_#dpuX?IpYSn2@E-TLMRw!MQCO9>+^9F7~c$HPXiX0?k@1mZ){u1&S z17=LS*n3~!ky9g^MCr3y~Xcyr;U>#@f z)oUB)%S!z&ygJ|0zmJNlb4vvu2f{{gAZIj@UE#C0zptpKHtw+vA|GqHc6wss zmi}EHHnt}gt6eIFYm2Eb<)-Y}?)ey$_deK0j>U45b3XolkLca26nrTpOZ)YX;Y@j= zepYGdspdf8z}~MI^79Ad5H+GnOB(r&;4|V7 z5?byp{d|$QLvOm+9*(kp(eSBwg251k;SUa^1T+$$!(Skas8WoSdb`@%0>P>#CL_}} zuTFgbo(N^%*w9s1S2M%7;Ocx;!oYg9FX4yxPNQyqRU%kF;Ec2l4{KhRY@aD@K%U9w z-8a+B*W!CQ7ytv2y?(*D4=E}AFu|Ctak_^x8euZ^K6&ns{gXI$v!9@5li&9c3ULpPljAjIfBfZ!1emwpd))KYK>o)YJqn4Jk2k3y_*G z{I+qwCt-i%xJ*=SY}Ow$`$vR7<-+#y7yM9NE$F_DXzl8fFO*)~+A_+Yk$@x>y*l6b zIoqnnrxjBF{Iq>$H@HzfK>T?A8klw(&us_O(NQBwDJdyb>h7n&cn9W2RKl;HXNR9< z+SQe5o7uL)L<1~!b6Dv)R0!=Ox4!wXR#|y@l&f|8?+>)05TcxapQYzY~WbEY-BeC{jFDSN$}vo1L$6Rf8I+BL07#Sd90_W zHwb1))77Q-YiDOg6_pp@Xn|bHo$#4KAW$y>FO>aR&4)yYf(IeY6E-KlU$e5ZLR%jz zdIEmz#Tn9Sp)KS(Cgwx%tieA131aC6KK@eeYAP>I{pYxW)ii(Ob%O<^>`yT6vbj+U z*#aZc*6C@(VI}pNTl<;c4)l!c+;>zV@Gxro^OHF(OQ*;nzzb&=2DlLwtjdXeH$tRa zfu^7$0;6P9d^hKs)4)dzFIz_+X$&sa1%}I~qY%2R0$pMDg=A7+_KTj|vj42`LBZlRPlE?1hYz_4B)hryZb6AMwhjr5QwghP3bzcQ~hfD+u)Of zhqeg482lEnz#jqa9dS|A*VkXz*yu&MX1oKUk9TGpA@A;kC;S4qQ(~M7>TH%wA7yVI zUOgmAng5j^%n#^nC(`aroWSMFo(kfj#DAqYHYR2)rA)p9_^%W<(E5JL697X?F*js1oD5quZvAR7 z!OlfV4iPbswlFsj%+6*+{k{g)=l6<=ktr(+3(fnt({E7LJFm_fjKCsCij55Sg<H_}UR+%K9A>W#r|X4@@qT8rAw`nl_avT;jf{*iY-)`8DE$FBasU4PK^T{Sr5zp- zfx&<>mQDN;09Q~m4lpP(mFnGL<}L|!SGh7+3KKyP7_gURa6Z86aJ>UO-nE(fYD+g& zZ*OnTXEo{C0#g1TUY^)Qh$AoX-m99Mf7KcGieXyMH{>#I$crs5ovcOquf8y59kKb! zd7nc?Onma!FV*rAU^I-WkByD#ARaKDM>jDs$t-)KzyF{RxxKa)9v>eMMo4&g>*#1~ zZmL%VP_&n>L|XS(R#s3T>2vJpb?`T162(OW(66CQe7C>`$~jVGNWsO$r6Q;V<{LO7 zi4ClEWOBq#CAUb1tGAw(R#e=-ckddkJ>{Ya4yk|$m2IHUcs&7<0#Y&ywpq`>fXA%u zIt=^Yz(5N~BuxGxTu)hJf&a&V8Z$RDlWO!jqedatjw~n2G2so!LnL9x`&`LM5^@ zI4CGcPOG=>bFg&ApvT?1f>&0SmLSH|SN;tJ+TZx!DL4Ls*1iN@Vc%0eRME8tV*2d1 zVxRx(qGyR!0=tY~rHF{f`^e7nSMpD=24MdA9F}kJr{1vIzKA5VEuaG0cLcbVVRB)`ke_E=7}H5I&3pvC%i zF4o_XZ-E`GI2#xwGW{*$1256|t9+I9FcS=ov=-fAB3E=+k8*8Kef}-V7MYoy+Qo-0 zJ0I^Z$ghV2wMo}4F_t|l(fj^&WWl9w#jICTEm`PkA?8vmF6d%T0M6g-8n+13cy;8J z+Kt}l;gAe>jfHoSjJ6%Tr`|H8qjT?3&VEAinbT;=n(X>WWXmmFYgq|zqD-TDH+H?! zfZv(&&RbLbaDGU^$EShGR0A^D@lA;x$*`5^vtjiCJtL~-%&7eQ2Qb{1GyF{&+|z?TUzz>y^A1|1M9)De(njPM#fZK>R|IMZ)-xF!nlvTQ8$L}RhQ4vEOa z*&v>$Urf!4izTV2Bc2DUQ)~Z@Wzf=eQghzRyWXtj-&?%%dGwEC%}wmC#ibiCAtH9t z<$AA&APpZ)06Ur(k!Ry?LY!q@5`*jg!nJ4(gGj&E2+lJJng5!&>D+0IZ+2g=ZV7!- z&VVz2B3@40tq9KO#Xi|Irk3qhl0n|PXqIE&?%)4z$EkT8FHoJJedZ_Uaufk>~Z$>kYg ztMP5D(KAm;ccR*+)AoLg1cYd!He<8Vs-o?BMRmS$tgl$U(E915Kn*g0!FPK!;r}`^ z{PS^<`=TFTKADYv+(+hPq*62FFs%0^TMf)23bNtJFv!7i%&q96@bf|_Z z_sen5YrdSr-du0ndM>nEc)FY#!+Au4I>J2VB${PFR&CGmMV$IPEtH91UR5MnA>gE0 z^5y3@xM$4R>0`bzw@0mUeP=^-jLhr>y9bJN#wW%mljWMK_%>~xaOm2W*)Ngq4b_qk z3wr)aCBLvW->{RimqQ=_$|F>Jj@2`b*=d~~YCr#wSQJarSxuq#x@K~3T2+LTE!MTP zH|PTaxsRUJVg0&@$Z;+WF&k+6Fgw4EA)w>9&0~ij;vgs~M;`dL9({LOetk>IOEvIm zq}klSXy+|A0pd#1#78sJRu-598$$pCSxSF-O`s85kTgS;m+~Cb z;~Lo!q-)(m&P9*Sgzl$>Hqh2kW6u!h4S5T3!YNvs;touQ*tlq$)1U0 zN;#XxF+Y1$m{j4V{!KHS+ltB~I7NZ=G=d^W8a@!DO+@6$rhScWy$|DX{pB(LpEaPl` zmoP0hosDMkFuJbWhvnA>YTDX6E<3o|Nn5_A!(O?+k1(J}JSby!8|AoHu7=)cR|pYP zeuFzj_`q^sWoC-^mysfi#e(^r(oz(h_A&a|9%hD4CHrku77S>m)6d$MivT+Lr ziSUp5?jGJY>$Sheu4T^mCEmWmC6IDyx56hX0>r~DtCvR}5CoxV5} z-w?56*>)a+tY&o;Ivq1W7hVzJcV20S!D|srjQ0|Qj^3NE4qQrc+$V5S=<(3Vl z=^Oj|ZMn0=wdmWkXfMc zHhQrYlp|F&vfS=0sBmtqNn|B_sawfbcQsu4I?gMuFu;8jU5K!XyMW&3;SJi`>Er$c zrKaNp1=rTMyQa_+T?4=IDKxSp#X9h6@@p|}YDVDrWaLpqG?*sq>9>T|4jwB06()&J zB}sa_YX7#vn|!@GxHNX%G{L5`mn-io`W(mHe3bvd_QCF`jjO-6n#0Ry0}8m)Rt0q- zD@)Nfo<+UVM;wP5<^0qM6vCDpS+nu3ylVI_vPSZ6c>7)7sj^99X40{{yJ(*$%8QPR zJHN9$T6SD{TS?r~T*HgHv0Vmz=XxRYx9{K32?_4Z-#k1<2KpR3ry`JyAEXr#oq6J@c@<>we96`F#*L_Tu275!HNGs5d{h)$|Cigi!@bleU=oH06{?C>GF zMq6EL?ybc;*HV9s*1pXvqFnqwAvQ30PNPFX?yVKgvGYeSH>J6M>yn13uScw17I^7t z@e^cSJNDve7?E#0{*l2i``-UZ>^aqx<8GkOmn-%IZ=yuc0tsfh)~b{Bh-dum$c@es z>s&GEbVta~WA-BzjRD6`zW36O41%@)}Xv?ctTU|K4s#RX;JOuQ-^6;W7DEK{iTz91aNd#gKk99u>q25 z;Zf&yP|{h`r>3Q6PtJo8+-c1~3GZ&3| z;-8e-K|Rc9;?U)2)hE!1pD@-O=j(Pe7BiQzn;vGY^Kr&vD;Vqj3og)K7`t;MV?%Fc zECrY6t{sfs(}A(EIFHA9;(o@ay~Wtfo{Y_LGxkUfV~@Ya*zSc&VLv{g7TOwTy0moV zf}=WjiNcwg=^u^|fUds;gsEo7h0 z!OW=6-LlUy#LL7DV?s>nF8h78$|J z?Gi*uWELhA3!+&Rgp;`L7Ac5{cF8P>6e5aJkR*sF;G_Uop(uLf0FzkZEkETIB|#1f zVp=gM_ zBKsQMkA}!AD%vKOM&W3PHFo<>Y6v+uG{n{-`*vyw*^h?UUWEM65V9W)v7OSRA?(2{ z#O^6#tjHd07nLFdh_p(h3G7z$N!D5t3-em$c?u;d4|ldF&v*`E?i(MnONXGZv(flZ zj+K1*-kzO4(V7U;5kKd;+Q6zG`0s893ygAYp+^x#k?ASj%9 zM@Uas6%LXzDhbg}md}3Beu~51m!>j&#Ov7v4 zgaxeMK>H8cuSQSuRJ6voeoc7!D}ZT0Z;SMM-Tj(mdwjQ;hb0S@#7QmHH} zeh+FC^$VHRh)Z35NPE^yy3r37sV9cF>cc-jE3TQ9O0lC911xrwa1k)()yyuJLT!VR z#6EdV@@`~ep40?2Q%`h573MX9T)DM+{Gn$>6Mqe5wUyRl>>8!dbWesJutwC-4 z??JjB!Aoltp&Oai01QPQAeqJ_92n2ojk1fe|HGLWEgc#&Qz>`nC=%AtAa9CCS?8kK z7+6{LsCMTohiYpRILyUjyL9Jqopthksi}6uQFP&OXcrWNYQxEjNRu_hybYZa)T*Bi zWJ^XcGcEV#+w#FwR0jy1jp(J#Y!mZACp4EL_vYm<&o|`^(`O+U7*@@8L{k1T60ElgprI$n$z!sA-suz zpN9HCE1(0=8yEtN1||V>fM0nq>l`T?m_QjKzB z;uc13Om320EJCKThPIA=u-L$kstNzR6o!WqB$Qyl#V~GTJ`KU{&<3aj2xa}ejzoa# zqa5|5@ZdU-|B*bnFpo6w*GyJd)}5|jNFR2St%(8EZ-0wlK9r|5sHswd1fPzJVcEuf z$gP#oo_KF#)}DB;ero#%^WMfx(?C~AQ;^iP!f03J9;$qw{VDlxrzjeuYHARt=N{+c zzr^FJ$A3FrN&L5K=Pt3g#Df3Eb-AS*tl*vg(z#prxNco9HUDi9fd96r#D81J{I_2s zIFk%mR}&Hw5))yMbK#VbI1nt@k_c|lHPMm?CYFGckZ?O#14&(d5)y|bBoZYaoM1|D zrw&a>94d!}F+rAlksZi}cQEp#G@9I$C??=J27-s+(MNVQ%L0-xT1 z%99rji76LwM>B?cQw37@}WU0$SJ++kYvRV5bY`iPojz1$L7L!sFU`o+K}1I>0rbaYG%noWp_jxoiEQ4~9d zI$(??207h;lMvmSGKMz@A&%Ba(6SAsA|aXpLT_};4QMVioPHv-N_5Qi)BwJS?cqg9 zZ46Qrj?54>+8~5CKvcP;K-pF<-*LU4o~TQ7Oi+v%O`R-8yjHD|CWK1S<`}V^Tz##1 z&{#1SHl;zgOaHp!`0@y(S4fL*PJ_mrEmP8=urh~1 zgG`rZQzvXvexPdDDi{vY4(p_=aZ<9?(|wL7KL~q4>QW_4_`;36yz`}ARf4>~k<9yB z#r=kwj%(dY?dItl37nlgl@(;fr=CbQ=0J;IU11UfTkORyGUIbPG9|Bt#Yg52j z^m{-d@D;EhC`P`>Hl`5}1#|-X0ErM*rBjGig<@SwS!KHRd-D#Nz?vn(U~hmW5d`@n zV9cvoqSaCe3XW0=P>KPT0+ezQFy_^)KrbZ*r5Ip|K`9pjV_wZ-v{L@m4<6*}(Fgst zxd9h<#Vvk$QNC8o9(m#wQvjcii@V|$Js&Mky_6V8bX?pOx9It3d1|FZ)0`=SMbi{d zKehgYr+C^Ul?bM3qN}DUDo^z+Kc0UasyvqbIi`LQ7+xViWQgfDMt~vD}vb%Ck!mM-_N&`OAn8K8vR% zN)%JRND>bu&w|fUDi@xmEX`YHGugx?sz_Xld`c8C^HJjGBy3H5V` z#P-C!lo|Z+itb-r+EOVz{}IKLRKH);M-IBg1IaI>9#WMfm1{pzNfoL@s1UK%#v}@o zTb7_)7nW^#+@j=^1*l)Kh$Tvt0oEWXYr7~fccJJx=@3z_N7V_>Vqm5hWQ~!J7x{=@ zs-s>j)KQ9{g+W)T`_|4utm2xbhusIBsgGPVzAs#JgzCNnPjGaZ<0WZL`>=NJ zNmC3$?^oV>)COS{|1wIy_K?&QR^bgpHMCsI|1Q1PE;QyHNosA73T@7yJv=8rmTuOi zXzl1#zd8$V+9qAA3$fy=QAMf6;P=jz-Z2#Gu&nJ>4-c4ySc4ENo6tm4^va>j_1|>% z^hbTsOp0P!OZcCu#jw8T>T|B|>2wR-BsDbvQdx8a8UR}O$r z1ZDv9fR}*RfVEWCWhLc-NO8Z=qT$Co0fn7gfnFsuLvTlxFp3MMd@kRU+=7#rU*UfY#>=Si7%R zK&uyOX{Slk`S|wLPygsR`f-1ql49pZgMHM5W0AW?mL2y>JG+ENJScr@5PHW;e}z86 z@t+Jr$F+_f9YdPUl%CLqXwvSV<6?VH=Ap^dJR!XI5D7i2uhNCE=ns)T?Hb;EhOo&X z^!^j8q|ipI<&&n9w@{J3h1CxRTK@tv2tx*rItHn5-ASrauAV6-8Z8$6=q%YXQm?o_ zV!OK}*PcU%!Rd~F8k{DXgZ_c+G74**60 zS-?zSKCl>g1NaEo&RCDVjP=64pkA1W^oCiZH_RQqI|A`Q0x$-c0$73PfPCOxD(i}4 z-j-YzB6aseRep16`I3Km)}TT#G-zRm76x3*s`(2^Yb+}SE9;?!%usU~C~Ht&ccS1y zXc_gS+yo}bl^gGT?CHu5FU+Hw_}5UbwsJN4_|1PJFDX|J)R+9{TbL6C9YLRf7IuW_ zK+22QNpb$P#p|R>Nl<@$~u>MJrZHnV*YVg4)d?MeFgC!&~?KR0npPDnAhXCkpPD(i~pWH z{^oToGsw4v*ud5RID={VKDee07U1QuTPsdeKH0Ca*>b781A&~dslS9mXjcIu+GGPa zoMZ&2xU?NIAI5UU4J&&-gHab^Sm&3>&JgB>B_H(0VuKTAa@h$^tJ=wpHgc$D*qQ6^ zllKjKNKsg=O(Gegp{NIw6le!0W+LUX6WWzx%0Us}6ePu9O^TA$o5au#Qxnn-PNb`l zozTvKbhMKIPO`(3(>TTWP2!CuE^}$i=P{_L5n4s^4_yLHNWqV5x z7`?B1d@E?6bJL1jj*YY=(*#KNk(FXLf zUS22Sl=YyKdZK%!FfSZ(<*qp&6Zvz6D>d`4vD^%Do2bk&7C!<$T>8EdxUU(@B)2u_ zPMJ~1VtDrf+FW*SPh}X(B)6swCwdvmII(K|)aAc~RafrIp-iipURAB)3d_FZ(vNfh z4XishihcrW=r_`|3uWC71p196>R2t-9s9q?x(BsGbJ3wPY@!^4dMP4JHfS*U{aJSi zs{oV6pKLj{!Fc_$EyyM+>qml_6G8dUF4ottc)*SqF}$J02Fo}xWxU1&PLs`?4?1l_ zsd5Nq7z80s$w|~YA5)f zAV70<9ShAHE|}h9ebQA=?se{Y#J6r82Jh?kQLSg`j6MkO1(NK&!dg6-t;;YS_&@1L zBh+3Y4p?vgI99UL3w|Ui57C_OO1&OE?zE3VduuZ9M(K6R+G@zAe2w&LLW9O>6|Tmp zmmYJmlmoB#CFypfbc`mw%^)prboOLI{j1U{oQ-rTW?5sYb*Aej(e|5d<>lK_bbYEj zC5WY4qK1Y$DoQ>A4R-QyibJFPs3c23cnZ!bAUY{6{lgRT5dJ)11KtA)fvK)t2i(bc(%(YtZE~!WtH%l!C1{mdggM6f<$p&df{{Tt6wVE9!>SR5CBne#R$mNRS6y~u z5YYl@!F{q!Hq0vQP<`tp8C*#tp;UQ2rBa%a*!h-*K=047&H_eYEW$82y`N(R2=s^4 z;B-QYg&IYxNq;3l5EgzYFJ**6g+)6GcVKOZ@(M*LyIox}QkMzxOCl|HgAfUJ746!x zb*Gj98pv;8ET9S?d*|m=nq4gp6;M~ST)?Ui7@r%_)j0-DHDXv*;%y{7Z{VGkyv|0_ z{^QQw4~JFGL)d|q(X(|lTpi!=>ePKORck_+wQrWT=-xU^DEYVUs}zU@A@k;Rtk*1M zpEOgC1NE0eePL%EgUH!5*IrWo<%2$bs!KyTb>%C8hF(RAD96Z$l(UhBj{G}b)QiFS z9t>aiz{*UA7Cs8Y-YDF!quK&-zyM$bkOj;H<^zj?H-L|T?Z6L=jm`#gfhT|^z?;AZ zU?;E#I1ad|q{~YG0?|vNAVvg=b1^cum9@iuU?VJ26vm3Hfto!Vb!aD9!xw-SK|4FN zGr(epR`A#K*{DN1yR03xJ2V7ely}QDBZ@2ULo>P*zI&Z&e)2AT{$GM z=2jAn@uIothyc`;1ou)C(xJ-kjUtb~O%rCU#q{(aln9T5JTMe0?oj4lp1(}>z=XKe zChwG^XtF_(X^CJ-%eO7oz2H4dU}`k@X=fhxd};AP4wl(M$!bdGO!)rp^MPDw(E>Zc zq5`ZlfZk=a?+wb~fe!7JQ{1S7SNA~cv!VdQr5r5;(L7A^p$=Zrp?gt>(=cKh{3a15 zIW0DHc1WRQ9VH_sd=)+))YhT}b{dK7Z!SQ<6omiS`5WvoN z)zQpu{*o2zm{EdNwRfIs;U|K@vcWGSStXh9%3E~NjA_kGjHHTAMYR=D3Q3f|1dASw zNq*Z~UH;dNdT|YnP*jwB1R9|lXn~BS2jG9{;D6&W{*MR$8()e4Wgx!{+?E+_fjD3Q zFapQ|W&-np#lRcDN5FQ*vT&8MvVmOS31A8ECa?k63G4xm11{3*zk>gtE8|8MvU^L+IW)J9qhTEibb{~h(;!2h~N(cn>2qw4{bbuJ@deKqWN8v(ms zIsC8YjDXnz_}_#|{BJ@On$*t-D6ICzoMAn93Ii@_yunoP#)mI z_*J1g!FrhwPc^kj;&vXayEG3;F=wF6SA||Q4#r!oK~xe@o2xuv!n21EsACk)OMCR@kJL> zQQ61#I}L(P^T4M+Lc z{{c7(IFTRpnHB-G1>%4Kzz843AVKz^;XT&xnh82yD#fS}>(N@HmtJTb&W- z-;;xf-RIczW6bAT*xyisY=1jI_P3UKQAEaCqDJaTp<$7bD>re?K32|SN_mBOq=CO? zvbstO6|!)N8*nk}GmZI>+Zw(O*8nBh;nQ*M_B~g-`{c%a$Ss#3UxPvxMufN<3nMnH zpMwA3m7U#~rh%@KrXZ+;K9~rysJ(FkPVGa9(7(|4 zkwkJUA4r7$`o|HWzy4uF=&yei5&G*NM1;oY+-_xL&A(}OJjVzBs($7#z{mZ-0ga#a zgV*>$KU|thpYrqnh##ux_xV0l)9=H5sH)#5`#?mR+dp*3_^> zWzi96SPD~u=>N>qid&>=d$P;;Oi zfcGRkbO&Rz+afLMItN4JoERV$=no7B#sLoij{%E-*MW7wHqs>^wXD0Ih1%l$P`#Cc z7G|ZqB;;j4^{Akd7x+zZUL290VC1Nt6gp;xyk_Z`S!uqyd91lD#(?TBs>D(~%1c6C zI;zXsl)NH{ibT`DB@F?t=I@eK?j7OiZ8eEY@i4!06b;KE0MS?pz<4jaO*o@AvKuFS0NCdy>PzwI zAXA3su4izQaaNNzgq!m11w;aYez!9_@+;jRvWtPk57$o{-a4`0Acv91Qs7JhB-ZX{OIO3)PhGY@>Lx%&Onk zR(#q=waEvS@Fnc(pXsyL`b)QT=kKz#{>JzFwC(r!_=x(6YfTd9D}9$U3~i`>WkR#g zR+Utj>f*JnH8*5XS#$&%GO*8HJ097ixQtZy5f@{R{|$HsSmGc4XeMKGGZ>qf4on9g z1z>HR_bRXk_?)rFK45JAKY;gvPl2z21Hf72X9BJQt_L~;w*pB(8gM_AWvzEHyWHz2 z>FwAhZ-CE!bw1k{($miybimJf&F^gwN6~#)TPebT-$N}Ul=OC_*Kxio8^e)au0Fn& zK$P@qCO_KNPi-zUCB6FM$wg&yQi*JI!ccl)4O;1q2let8(w@UEU0H7IdkySXq@`xP zJTF8z_X`P7Y?}+LuCc$7+gk5sN_#=oHTE@fQ(CHSn@c>+yP9d;Pw$Uf@1(wJt+)8H z_nsQ1sYHpZMO950k69l8V=b$nm^M8pGb3tnT5jev_|MB(Dbc0itYp|B1o{D@ok!~N z#3;p~D-G`Z1neohrvO;`wR^sQ_!BD_dvPUWD`8`K=QYL(@HW?NWsH4;G-xo>JTBhU zzD|4bCfUDmu&b>SDj?LvPfsnj+BwfJjpH-EALM@S^kCkUm$#3+ zdC9$zk(84u5-J37CH9$VCkHrt@Nd5z%~vh!FD;4X%_wr)ou9UrGgc%2N(B%6@=d-s zqtC(Tp2w-5l+3T@j`op}_kEX3*_xGXe=T;DmK}b*H(&A6v=7F7etSt=KdG-8v(<*L zzk;%AOj00-m)^JSb!*)5+>Km`hZ>tAJxSjoM~hEU#Orx#?_j>;%SZjrdy2Au@kp&Pp4bbCr;`z$vZLq7I?iK)3Zi) z8!2R^!z0ApcUSCP;(dy9MC^#F&I!X~hl>;B2-&HzsbaS3G^YwBqF@$I9ZOyR-SE`Z zaixzhrx)!C!l~32TD0*g<`ki1eo9bE#mN1yrlkDqBNDuLIHgsJI9|>uYnXYMQ1aNY z;9;&QWz%0iIFkG?9v;?en3&}kA!L~6<-;Y@hFRp`M#ID3JVS;wn|j<$w)9KPSX~V*XIY)mkdFBtxq9o3kammRGOLk->hbC9pT+*uKak|(hc~+2ku~Yg`6K5tR zy(kKACWR(d*xb^}q)dwKh{;S$ob8bQm6jNs=)C(g$(@&ofJWT)z}L?5Zy)3h5G5la zA^ls)wKyR-!8yN7+CM2FBZfERPHXwR{_zi$&g1pbvKe4gt{0tB#Y;Ml(^z@;op(j&5mLzA5@*F|!wLx5L8`(5cjDg(Oe<6)ANJli5 zyRN@QY;J-lv!FzUSSk#6 zc8Fz&kT`5eUAgC04r?4hnl`wx+<%R6* z&?%!Ln?&BI_FJo|kk^j4wc~B=cw0N(){eKnU*OvDRwmT7{I)W`Ex1sAdrv_vzpdrB zmB^sZf@}G$KTp1#<1L=#N>&pd=M>{%f%r6!lOJUDlK-1|`CwO1K2aLYZ*%j%$TACe z?91f;_~v;&<4cI}_B)GIK6%w5zWagC`J0P;v8L|0^(IdcdWVlJ8?&EeSA#7p=6GoHWy`rbDm-m&Qi-i4R-+-2i$9UaPN zIqv7(xFh#xzIr|9vrcf%9TU#*)vMsyOP+`J^3`v^^KV2e@Abti2H*Zwy)>Vd{gb=m zUOjgD)7yD>?t1J$1A44o4_C#Pk8nQJ-IE&QbQ+!k3a*jlc8(->NgCW0kn>pAT`||h z2yNT*=BUY|oTs>YMzo9&u5ZsHc*P_82KL&xhDWxF6x)(NHvjnPf?H*O48N9NOLN@c zK7~iR``b_dXHsNPr2D>~&-`luJ({CLVUZQr9?ETT=h=8X{HusRJGjLIhg#8dKqXK} z{RI#GgjLT%Bs{yC+y_$)!MUH1Xx>^1{$MV-H$P+uesmp)W^bn8jkC$UZk{1{)@BmT z|B!;;pGWRb<)@Wu*~y1V^xo45u6X+G$jBDQl6{F!wcJ1Znxv8*k@xnA>{&AOn!J52 zwZQPmGkuHB{yQTw^6Q5qBeV9N-Epf{-3w?cUzE0JpH`7VRC}CT$`{9K+7H7-Z5OZY z;`$9z+r?`~@!C=R&o_$8WCgbJn z;2W!L?e(zf0%*z8YP0Gv{d+h3co{2;@tX;8X;Bc$@vP3P)<2~u!-|j>4@ow z`^10L7I?&P!B=!?h5{oCCv4(_-fz z?T&`1(3}zctV)1137!qWxpXa&o+H_aE9mYB?oc_BE<$X$H!h^u+n=J?x;sLpQ6ASw zU54mzZ+V)cZ+?-Y>uyAM64UaOr0Ft5hx_9fDf$jMcijzLR5GTEKwRN|WgRHmi7cw5 z=0qh`o~B96`~;lC$}-}fI~d1vM(EB6zDH$n{((GqU&)tuasI=zTy-|*Csc0dABO9( zS?8A)?z+A3^zoH(=&j}GrB8`2SILf4-a_Y{7H(Sa>Cu1hV_J+KIKOqxZp6^}sEJqf z^dxYe_a*v_L+#Q}{eVU(nN-3DdgpC9`rHd!4$a2%8y|5+iw$>CyQQ5)sv~MQM=IWT z!#ljcG@sw+{TsjkFeQ@Pofs~nk13)5BVA~}!&={eN+>NAe@ew-usiq=JY*W?Mklx)GhZHr}o2hu0c|Fo-9xz^*SI?MNb!Qqxp8EDc9~0q%rf-Z(i_A zb6UM1-RIuM*B0~~bRdr^q~0`CO6+*&B>xoZg^8E$=6yGwKDIQLQmA(kT>|INv;a=m z%WD1#RIzq|(MO=#0p=;p-D(Gz+5v{w4lp1=+W1_{VDS4?%V2654D6#<9)ppmNlh`y z@POf9g2XqiKbWFf}=k&*%#}!ZYB{WP;e$}QXn)Q4PM^fxG`D_K} z@BjU598Yug@};agL2+Y(qMO^Hpq#pFJ`DQDQ~#63U)f5}iWa-{a!lBd|r={rM|=_hVpV z#qrkiS-hVx^wWp=isjRe@Ef>e>X8xU@tjW_fs9KwzsmWy^GXNsiQm5dHbOmdN4LL2 z8Na%BWNh4ov(RhtkN@VpXW8DVJeqqKOQR545x00>Y5DGl;Pn1gT0y6sV60a2PG0`i z-}olM;nJU7Zc2sO)(Afl_Z zT-tQ2FQRO|S6MM4KK?BtmhDz@j9+x1C(V15EM=R$Tj9Wn`OtpEbRSo>^!60*(W90d zNUw#T8SG8txgI6U9?zBEuEsO*vx9N*R0}+)(SCFF6vDmBOR{-;MHtLI-H*)u*ynZ5+}z_t zf4=N9)WA{jkGPdb_s+rdk``L@Uw2O7uPwkboj$y`W5lfE{o>w}hHAk<{6x~xI6nMf zEdSwQ{?HHnpYL!!+!!vbaFv=$&j{K4+0S_4GkoK6bZB1$u<;(JlTq}UCkyx;&Vl9q z`TMJ}D99K?nEfL<=$Qc({rHXzTR8vQ;alV0_KWbvLNr&g{RM87ayjqu!SUU*eBn(n z$K!Zr6HiK0D>de>G`x}E+Mz69~79#uVN?tK)tMl0LH%W37ax-KY`m(l8HZShYi zcVh*#zPbmtYs2Cl_zY`v5EUz-byGN~FxEk9>uW-I8LfiW*3g8YOe}&{7s^=8sl2x8 z|BF}M8dOLx)up$!Mger-t&{SXYspP77NZyA;?Un*NiQy|+cz!!zN~3eW=y8o=Y7{1 z#kmykI-`8(2=uNqixNctzwfWOb(Qh1v+u_-_G2<|4=}+${QGYZj`z)fU&7cAcxT!V zje*uc7oabY42%V)0gnI+fEB<8z-PcWz(Jq{d7FX8Kx?22(3i@h_w*Sc8Qdhl^nU-> zF<$!o&sU$y<{e82~DF0lpAFiWB-RUeXtpa-d`FH%bb{* zo|8Rga@3Ht>Gx3>x{!QmucEfXM|TFv>5f2!^f<<@j?WqBanNY^D2RMeiCR)c!hXb? z_H+%9^z$)Nv;630&U_G!AC`ZZ_ii2cfgOpPr!aEOYN1^bE=|#VO5zi9)*V^-MJ9D@q(_%{{xGd{rLa@ diff --git a/doc/img/LimeRFE_plugin.png b/doc/img/LimeRFE_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..5d91e6dee51faa530ac7d692309c611b42e04be3 GIT binary patch literal 48577 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 literal 0 HcmV?d00001 diff --git a/doc/img/LimeRFE_plugin.xcf b/doc/img/LimeRFE_plugin.xcf new file mode 100644 index 0000000000000000000000000000000000000000..0224841bf1ca6681d4a21829fb74a46fa1d4000c GIT binary patch literal 145207 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

    h;hrOtDfl#m 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 literal 0 HcmV?d00001 diff --git a/doc/img/LimeRFE_plugin_power.xcf b/doc/img/LimeRFE_plugin_power.xcf new file mode 100644 index 0000000000000000000000000000000000000000..303b9fd3331ce950866ea9289037f16fd41cfffc GIT binary patch literal 48794 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 literal 0 HcmV?d00001 diff --git a/doc/img/LimeRFEUSB_dialog_rx.png b/doc/img/LimeRFE_plugin_rx.png similarity index 100% rename from doc/img/LimeRFEUSB_dialog_rx.png rename to doc/img/LimeRFE_plugin_rx.png diff --git a/doc/img/LimeRFEUSB_dialog_rx.xcf b/doc/img/LimeRFE_plugin_rx.xcf similarity index 100% rename from doc/img/LimeRFEUSB_dialog_rx.xcf rename to doc/img/LimeRFE_plugin_rx.xcf diff --git a/doc/img/LimeRFEUSB_dialog_tx.png b/doc/img/LimeRFE_plugin_tx.png similarity index 100% rename from doc/img/LimeRFEUSB_dialog_tx.png rename to doc/img/LimeRFE_plugin_tx.png diff --git a/doc/img/LimeRFEUSB_dialog_tx.xcf b/doc/img/LimeRFE_plugin_tx.xcf similarity index 100% rename from doc/img/LimeRFEUSB_dialog_tx.xcf rename to doc/img/LimeRFE_plugin_tx.xcf diff --git a/sdrgui/limerfeusbgui.md b/plugins/feature/limerfe/readme.md similarity index 66% rename from sdrgui/limerfeusbgui.md rename to plugins/feature/limerfe/readme.md index 9ced50bc1..9aa7fc73c 100644 --- a/sdrgui/limerfeusbgui.md +++ b/plugins/feature/limerfe/readme.md @@ -1,36 +1,46 @@ -

    LimeRFE USB control

    +

    LimeRFE USB controller

    -The LimeRFE or Lime RF Front End is a power amplifier and LNA board designed to augment the capabilities of the LimeSDR in order to build an operational radio solution. The usage is not limited to LimeSDR any Rx or Tx device can be connected to it. The LimeRFE can be controlled directly via its USB port independently of a LimeSDR device. This interface allows exactly that from the SDRangel GUI. +

    Introduction

    -To open the LimeRFE USB dialog open the Preferences sub-menu from the top bar and click on the `LimeRFE` item. This item is available only when the code is compiled with the `LimeRFE` branch of LimeSuite. +This plugin supports the [LimeRFE](https://github.com/myriadrf/LimeRFE) board. This board hosts a hardware power amplifier (PA) module with appropriate filtering and support circuitry to augment primarily but not only the LimeSDR, LimeSDR Mini, and LimeNET Micro platforms, providing a complete solution that addresses real life applications ranging from HAM radio to standards-compliant cellular network implementations. -The dialog is non-modal so that it can be left open and keep the control on LimeRFE while other functions can be used. +As mentioned above it can be connected to a wide variety of SDR receivers and transmitters and this feature supports the synchronization with any receiver or trasmiiter device sets. -Whenever a change requires the LimeRFE configuration to be changed to become effective the "Apply" button (6) becomes green to suggest it should be activated. +This plugin depends on [LimeSuite](https://github.com/myriadrf/LimeSuite) that should be available in your system in order to be compiled. -⚠ Disclaimer: please use this interface and the LimeRFE sensibly by making sure you are licensed to operate it on the selected frequencies. If you are a licensed amateur radio you should make sure you operate on the bands allocated in your region as some bands are exclusive to a specific region or country. +

    Interface

    -![LimeRFE USB dialog](../doc/img/LimeRFEUSB_dialog.png) +![LimeRFE controller GUI](../../../doc/img/LimeRFE_plugin.png) -

    1. USB serial devices list

    +When starting you need first to open the LimeRFE device with button (2). You havr to select the appropriate serial device from (1). Note that all serial USB based devices are listed. You need to identify which one corresponds to the LimeRFE board you want to target. + +Once opened successfully (check status message in 6) you will apply the settings using the Apply button (5). Whenever the settings are changed this button lits in green showing that you may press it to update the board. Conversely the "to GUI" button (4) reads the settings from the board and updates the GUI. + +

    1. USB serial devices list

    This combo lists all USB serial devices list available in the system regardless if they are LimeRFE or other devices. You must specify the device corresponding to a LimeRFE device to be able to open it successfully with (2) -

    2. Open device

    +

    2. Open device

    Click on this button to open the serial device selected by (1). You need to open the device successfully prior to any operation. The open status is displayed in the status window (5). -

    3. Close device

    +

    3. Close device

    If you have more than one LimeRFE connected to your system you have to close one before opening another by using this button. -

    4. Pull device configuration to GUI

    +

    4. Pull device configuration to GUI

    Use this button to retrieve the LimeRFE device current configuration and populate the GUI with its data. +

    5. Apply changes

    + +Use this button to apply configuration changes. You must press this button to make any of your changes active. Whenever a change requires the LimeRFE configuration to be changed to become effective this button becomes green to suggest it should be activated. + +

    6. Status window

    A. Rx channel control -![LimeRFE USB Rx dialog](../doc/img/LimeRFEUSB_dialog_rx.png) +![LimeRFE Rx section](../../../doc/img/LimeRFE_plugin_rx.png)

    A.1. Rx channel group

    @@ -85,7 +95,7 @@ Toggle AM/FM broadcast bands notch filter.

    B. Tx channel control

    -![LimeRFE USB Tx dialog](../doc/img/LimeRFEUSB_dialog_tx.png) +![LimeRFE Tx section](../../../doc/img/LimeRFE_plugin_tx.png)

    B.1 Copy Rx band settings

    @@ -106,9 +116,12 @@ Select which port to connect the Rx to: - **Tx/Rx (J3)**: this is the J3 port combining Rx and Tx. When cellular bands are selected this is connected to a duplexer internally - **Tx (J4)**: Tx connected port only. Can be used to split Rx and Tx to drive a higher power P.A. for example + +This is where status messages are displayed. +

    C. Power and SWR

    -![LimeRFE USB power dialog](../doc/img/LimeRFEUSB_dialog_power.png) +![LimeRFE power section](../../../doc/img/LimeRFE_plugin_power.png)

    C.1 Activate power measurement

    @@ -118,52 +131,46 @@ Check this box to enable power measurements. Use this button to refresh the power measurements. -

    C.3 Forward power (relative)

    - -This is the relative forward direction power in dB. - -

    C.4 Reflected power (relative)

    - -This is the relative reverse direction power in dB. - -

    C.5 Return loss

    - -This is the return loss in dB and is exactly (C.3) minus (C.4). - -

    C.6 Voltage Standing Wave Ratio

    - -This is the VSWR computed from the return loss in (C.5) - -

    C.7 Power measurement source

    +

    C.3 Power measurement source

    Use this combo to select the power measurement source: - **EXT**: External: select this when a dual directional coupler is connected to `Ref` (J17) and `Fwd` (J18) ports - **CEL**: Cellular: select this to use the internal coupler when cellular bands are engaged -

    C.8 Monitor power continuously

    +

    C.4 Monitor power continuously

    Use this switch to activate the continuous monitoring. A measurement will be taken every 500ms. -

    C.9 Power correction

    +

    C.5 Power correction

    Use a power meter and apply this correction to obtain the real delivered power in dBm. There is one correction factor by band. The values are saved in the persistent settings. -

    C.10 Corrected power

    +

    C.6 Coupler relative power measurements

    -This is the corrected power in dBm and is exactly (C.3) plus (C.9). + - **Fwd**: This is the relative forward direction power in dB. + - **Ref**: This is the relative reverse direction power in dB. + - **RL**: This is the return loss in dB and is exactly Fwd minus Ref -

    C.11 Corrected power in Watts

    +

    C.7 Voltage Standing Wave Ratio

    + +This is the VSWR computed from the return loss RL in (C.6) + +

    C.8 Corrected power in dBm

    + +This is the corrected power in dBm and is exactly Fwd in (C.6) plus correction in (C.8). + +

    C.9 Corrected power in Watts

    This is the corrected power in Watts. -

    C.12 Corrected power averaging

    +

    C.10 Corrected power averaging

    Use this switch to activate the averaging of corrected power. This is a moving average over 10 measurements thus over a 5s period. -

    D. Rx/Tx mode selection

    +

    D. Control

    -![LimeRFE USB mode dialog](../doc/img/LimeRFEUSB_dialog_mode.png) +![LimeRFE control section](../../../doc/img/LimeRFE_plugin_control.png)

    D.1 Rx mode

    @@ -192,15 +199,3 @@ Select the Tx device set index with which you want to synchronize the Tx switch

    D.7 Refresh device sets indexes

    When the configuration of device sets changes you can use this button to refresh the device set indexes in (D.5) and (D.6). - -

    5. Status window

    - -This is where status messages are displayed. - -

    6. Apply changes

    - -Use this button to apply configuration changes. You must press this button to make any of your changes active. Whenever a change requires the LimeRFE configuration to be changed to become effective this button becomes green to suggest it should be activated. - -

    7. Close dialog

    - -This dismisses the dialog. diff --git a/sdrgui/readme.md b/sdrgui/readme.md index 53bcf9a2a..ad1cfd0b8 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -22,7 +22,6 @@ The menu items from left to right are: - _Logging_: opens a dialog to choose logging options. See "Logging" paragraph next for details - _FFT_: opens a dialog to run the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel. See "FFT" paragraph next for details. - _AMBE_: Opens a dialog to select AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. If none is selected AMBE frames decoding will be done with mbelib if available else no audio will be produced for AMBE digital voice. See "AMBE"paragraph next for details. - - _Lime RFE_: Presemt only if LimeSuite library is available. This opens a dialog to control a LimeRFE device via USB. The details are provided [here](limerfeusbgui.md). - _My Position_: opens a dialog to enter your station ("My Position") coordinates in decimal degrees with north latitudes positive and east longitudes positive. This is used whenever positional data is to be displayed (APRS, DPRS, ...). For it now only works with D-Star $$CRC frames. See [DSD demod plugin](../plugins/channelrx/demoddsd/readme.md) for details on how to decode Digital Voice modes. - _Devices_: section to deal with devices settings - _User arguments_: opens a dialog to let the user give arguments specific to a device and its instance (sequence) in the system From c915f4103188cbc67cfacd4571d270115eb5bf0c Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 23 May 2022 00:25:45 +0200 Subject: [PATCH 09/29] Updated version and changelogs --- CHANGELOG | 10 +++++++++- CMakeLists.txt | 4 ++-- debian/changelog | 8 ++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 22f61a20c..dee706bb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +sdrangel (7.1.0-1) unstable; urgency=medium + + * Support LimeRFE wtih a new feature plugin. Implements #1251 + * Fixed auto stack workspaces status save/restore + * Fix typing errors in readme's. PR #1253 + + -- Edouard Griffiths, F4EXB Sun, 22 May 2022 22:24:21 +0200 + sdrangel (7.0.1-1) unstable; urgency=medium * Added ability to build a package for MacOS. PR #1249 @@ -9,7 +17,7 @@ sdrangel (7.0.1-1) unstable; urgency=medium * Save/restore auto-stack function in workspaces. Part of #1250 * Fixed ButtonSwitch background color with stylesheets - -- Edouard Griffiths, F4EXB Thu, 19 May 2022 26:02:31 +0200 + -- Edouard Griffiths, F4EXB Thu, 19 May 2022 16:02:31 +0200 sdrangel (7.0.0-1) unstable; urgency=medium diff --git a/CMakeLists.txt b/CMakeLists.txt index f1bb54042..2c1003a4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # configure version set(sdrangel_VERSION_MAJOR "7") -set(sdrangel_VERSION_MINOR "0") -set(sdrangel_VERSION_PATCH "1") +set(sdrangel_VERSION_MINOR "1") +set(sdrangel_VERSION_PATCH "0") set(sdrangel_VERSION_SUFFIX "") # SDRAngel cmake options diff --git a/debian/changelog b/debian/changelog index a1ba031f5..6472a96a0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +sdrangel (7.1.0-1) unstable; urgency=medium + + * Support LimeRFE wtih a new feature plugin. Implements #1251 + * Fixed auto stack workspaces status save/restore + * Fix typing errors in readme's. PR #1253 + + -- Edouard Griffiths, F4EXB Sun, 22 May 2022 22:24:21 +0200 + sdrangel (7.0.1-1) unstable; urgency=medium * Added ability to build a package for MacOS. PR #1249 From ee651860574d6127c4353afc81676328fb0584e6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 23 May 2022 06:25:23 +0200 Subject: [PATCH 10/29] LimeRFE feature: corrected export for MSVC and added web API adapter --- plugins/feature/limerfe/CMakeLists.txt | 8 +-- plugins/feature/limerfe/limerfeusbcalib.h | 3 +- .../feature/limerfe/limerfewebapiadapter.cpp | 51 +++++++++++++++++++ .../feature/limerfe/limerfewebapiadapter.h | 49 ++++++++++++++++++ 4 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 plugins/feature/limerfe/limerfewebapiadapter.cpp create mode 100644 plugins/feature/limerfe/limerfewebapiadapter.h diff --git a/plugins/feature/limerfe/CMakeLists.txt b/plugins/feature/limerfe/CMakeLists.txt index bb1555046..6ed968ef8 100644 --- a/plugins/feature/limerfe/CMakeLists.txt +++ b/plugins/feature/limerfe/CMakeLists.txt @@ -5,9 +5,7 @@ set(limerfe_SOURCES limerfesettings.cpp limerfeusbcalib.cpp limerfeplugin.cpp - # limerfeworker.cpp - # limerfereport.cpp - # limerfewebapiadapter.cpp + limerfewebapiadapter.cpp ) set(limerfe_HEADERS @@ -15,9 +13,7 @@ set(limerfe_HEADERS limerfesettings.h limerfeusbcalib.h limerfeplugin.h - # limerfeworker.h - # limerfereport.h - # limerfewebapiadapter.h + limerfewebapiadapter.h ) include_directories( diff --git a/plugins/feature/limerfe/limerfeusbcalib.h b/plugins/feature/limerfe/limerfeusbcalib.h index 80f419575..4973e75b6 100644 --- a/plugins/feature/limerfe/limerfeusbcalib.h +++ b/plugins/feature/limerfe/limerfeusbcalib.h @@ -19,11 +19,10 @@ #define SDRBASE_LIMERFE_LIMERFEUSBCALIB_H_ #include -#include "export.h" class QByteArray; -class SDRBASE_API LimeRFEUSBCalib +class LimeRFEUSBCalib { public: QByteArray serialize() const; diff --git a/plugins/feature/limerfe/limerfewebapiadapter.cpp b/plugins/feature/limerfe/limerfewebapiadapter.cpp new file mode 100644 index 000000000..df8e98bb2 --- /dev/null +++ b/plugins/feature/limerfe/limerfewebapiadapter.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGFeatureSettings.h" +#include "limerfe.h" +#include "limerfewebapiadapter.h" + +LimeRFEWebAPIAdapter::LimeRFEWebAPIAdapter() +{} + +LimeRFEWebAPIAdapter::~LimeRFEWebAPIAdapter() +{} + +int LimeRFEWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setDemodAnalyzerSettings(new SWGSDRangel::SWGDemodAnalyzerSettings()); + response.getDemodAnalyzerSettings()->init(); + LimeRFE::webapiFormatFeatureSettings(response, m_settings); + + return 200; +} + +int LimeRFEWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) force; // no action + (void) errorMessage; + LimeRFE::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response); + + return 200; +} diff --git a/plugins/feature/limerfe/limerfewebapiadapter.h b/plugins/feature/limerfe/limerfewebapiadapter.h new file mode 100644 index 000000000..7e9a39f8a --- /dev/null +++ b/plugins/feature/limerfe/limerfewebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_LIMERFE_WEBAPIADAPTER_H +#define INCLUDE_LIMERFE_WEBAPIADAPTER_H + +#include "feature/featurewebapiadapter.h" +#include "limerfesettings.h" + +/** + * Standalone API adapter only for the settings + */ +class LimeRFEWebAPIAdapter : public FeatureWebAPIAdapter { +public: + LimeRFEWebAPIAdapter(); + virtual ~LimeRFEWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + +private: + LimeRFESettings m_settings; +}; + +#endif // INCLUDE_DEMODANALYZER_WEBAPIADAPTER_H From 1d72798d42e2506fba62fa5e8dba3a3d1ffb44fa Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 24 May 2022 15:18:55 +0200 Subject: [PATCH 11/29] AMBE feature: creation and changes to DSD demod --- plugins/channelrx/demoddsd/dsddemod.cpp | 111 +++++ plugins/channelrx/demoddsd/dsddemod.h | 37 ++ plugins/channelrx/demoddsd/dsddemodbaseband.h | 1 + plugins/channelrx/demoddsd/dsddemodgui.cpp | 59 +++ plugins/channelrx/demoddsd/dsddemodgui.h | 4 + plugins/channelrx/demoddsd/dsddemodgui.ui | 23 + plugins/channelrx/demoddsd/dsddemodplugin.cpp | 2 +- .../channelrx/demoddsd/dsddemodsettings.cpp | 6 + plugins/channelrx/demoddsd/dsddemodsettings.h | 13 + plugins/channelrx/demoddsd/dsddemodsink.cpp | 38 +- plugins/channelrx/demoddsd/dsddemodsink.h | 2 + plugins/feature/CMakeLists.txt | 4 + plugins/feature/ambe/CMakeLists.txt | 65 +++ plugins/feature/ambe/ambe.cpp | 170 ++++++++ plugins/feature/ambe/ambe.h | 92 ++++ plugins/feature/ambe/ambeengine.cpp | 402 ++++++++++++++++++ plugins/feature/ambe/ambeengine.h | 93 ++++ plugins/feature/ambe/ambegui.cpp | 343 +++++++++++++++ plugins/feature/ambe/ambegui.h | 92 ++++ plugins/feature/ambe/ambegui.ui | 285 +++++++++++++ plugins/feature/ambe/ambeplugin.cpp | 80 ++++ plugins/feature/ambe/ambeplugin.h | 48 +++ plugins/feature/ambe/ambesettings.cpp | 114 +++++ plugins/feature/ambe/ambesettings.h | 46 ++ plugins/feature/ambe/ambeworker.cpp | 232 ++++++++++ plugins/feature/ambe/ambeworker.h | 156 +++++++ sdrbase/dsp/dspcommands.cpp | 1 + sdrbase/dsp/dspcommands.h | 41 ++ 28 files changed, 2553 insertions(+), 7 deletions(-) create mode 100644 plugins/feature/ambe/CMakeLists.txt create mode 100644 plugins/feature/ambe/ambe.cpp create mode 100644 plugins/feature/ambe/ambe.h create mode 100644 plugins/feature/ambe/ambeengine.cpp create mode 100644 plugins/feature/ambe/ambeengine.h create mode 100644 plugins/feature/ambe/ambegui.cpp create mode 100644 plugins/feature/ambe/ambegui.h create mode 100644 plugins/feature/ambe/ambegui.ui create mode 100644 plugins/feature/ambe/ambeplugin.cpp create mode 100644 plugins/feature/ambe/ambeplugin.h create mode 100644 plugins/feature/ambe/ambesettings.cpp create mode 100644 plugins/feature/ambe/ambesettings.h create mode 100644 plugins/feature/ambe/ambeworker.cpp create mode 100644 plugins/feature/ambe/ambeworker.h diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 00307028b..980786713 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -39,6 +39,7 @@ #include "dsp/dspcommands.h" #include "device/deviceapi.h" #include "feature/feature.h" +#include "feature/featureset.h" #include "settings/serializable.h" #include "util/db.h" #include "maincore.h" @@ -46,6 +47,8 @@ #include "dsddemod.h" MESSAGE_CLASS_DEFINITION(DSDDemod::MsgConfigureDSDDemod, Message) +MESSAGE_CLASS_DEFINITION(DSDDemod::MsgQueryAvailableAMBEFeatures, Message) +MESSAGE_CLASS_DEFINITION(DSDDemod::MsgReportAvailableAMBEFeatures, Message) const char* const DSDDemod::m_channelIdURI = "sdrangel.channel.dsddemod"; const char* const DSDDemod::m_channelId = "DSDDemod"; @@ -82,6 +85,20 @@ DSDDemod::DSDDemod(DeviceAPI *deviceAPI) : this, &DSDDemod::handleIndexInDeviceSetChanged ); + QObject::connect( + MainCore::instance(), + &MainCore::featureAdded, + this, + &DSDDemod::handleFeatureAdded + ); + QObject::connect( + MainCore::instance(), + &MainCore::featureRemoved, + this, + &DSDDemod::handleFeatureRemoved + ); + + scanAvailableAMBEFeatures(); } DSDDemod::~DSDDemod() @@ -176,6 +193,11 @@ bool DSDDemod::handleMessage(const Message& cmd) return true; } + else if (MsgQueryAvailableAMBEFeatures::match(cmd)) + { + notifyUpdateAMBEFeatures(); + return true; + } else { return false; @@ -219,6 +241,8 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) << " m_traceStroke: " << settings.m_traceStroke << " m_traceDecay: " << settings.m_traceDecay << " m_streamIndex: " << settings.m_streamIndex + << " m_ambeFeatureIndex: " << settings.m_ambeFeatureIndex + << " m_connectAMBE: " << settings.m_connectAMBE << " force: " << force; QList reverseAPIKeys; @@ -280,6 +304,30 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { reverseAPIKeys.append("audioDeviceName"); } + if ((settings.m_ambeFeatureIndex != m_settings.m_ambeFeatureIndex) || force) { + reverseAPIKeys.append("ambeFeatureIndex"); + } + if ((settings.m_connectAMBE != m_settings.m_connectAMBE) || force) { + reverseAPIKeys.append("connectAMBE"); + } + + if ((m_settings.m_connectAMBE != settings.m_connectAMBE) + || (m_settings.m_ambeFeatureIndex != settings.m_ambeFeatureIndex) || force) + { + if (settings.m_connectAMBE) + { + for (const auto& feature : m_availableAMBEFeatures) + { + if (feature.m_featureIndex == settings.m_ambeFeatureIndex) { + m_basebandSink->setAMBEFeatureMessageQueue(feature.m_feature->getInputMessageQueue()); + } + } + } + else + { + m_basebandSink->setAMBEFeatureMessageQueue(nullptr); + } + } if (m_settings.m_streamIndex != settings.m_streamIndex) { @@ -339,6 +387,32 @@ bool DSDDemod::deserialize(const QByteArray& data) } } +void DSDDemod::scanAvailableAMBEFeatures() +{ + MainCore *mainCore = MainCore::instance(); + int nbFeatures = mainCore->getFeatureeSets()[0]->getNumberOfFeatures(); + m_availableAMBEFeatures.clear(); + + for (int i = 0; i < nbFeatures; i++) + { + Feature *feature = mainCore->getFeatureeSets()[0]->getFeatureAt(i); + + if (feature->getURI() == "sdrangel.feature.ambe") { + m_availableAMBEFeatures[feature] = DSDDemodSettings::AvailableAMBEFeature{i, feature}; + } + } +} + +void DSDDemod::notifyUpdateAMBEFeatures() +{ + if (getMessageQueueToGUI()) + { + MsgReportAvailableAMBEFeatures *msg = MsgReportAvailableAMBEFeatures::create(); + msg->getFeatures() = m_availableAMBEFeatures.values(); + getMessageQueueToGUI()->push(msg); + } +} + void DSDDemod::sendSampleRateToDemodAnalyzer() { QList pipes; @@ -794,3 +868,40 @@ void DSDDemod::handleIndexInDeviceSetChanged(int index) m_basebandSink->setFifoLabel(fifoLabel); m_basebandSink->setAudioFifoLabel(fifoLabel); } + +void DSDDemod::handleFeatureAdded(int featureSetIndex, Feature *feature) +{ + if (featureSetIndex != 0) { + return; + } + + if ((feature->getURI() == "sdrangel.feature.ambe") && !m_availableAMBEFeatures.contains(feature)) + { + m_availableAMBEFeatures[feature] = DSDDemodSettings::AvailableAMBEFeature{feature->getIndexInFeatureSet(), feature}; + + if (m_settings.m_connectAMBE && (m_settings.m_ambeFeatureIndex == feature->getIndexInFeatureSet())) { + m_basebandSink->setAMBEFeatureMessageQueue(feature->getInputMessageQueue()); + } + + notifyUpdateAMBEFeatures(); + } +} + +void DSDDemod::handleFeatureRemoved(int featureSetIndex, Feature *feature) +{ + if (featureSetIndex != 0) { + return; + } + + if (m_availableAMBEFeatures.contains(feature)) + { + if (m_settings.m_ambeFeatureIndex == m_availableAMBEFeatures[feature].m_featureIndex) + { + m_settings.m_connectAMBE = false; + m_basebandSink->setAMBEFeatureMessageQueue(nullptr); + } + + m_availableAMBEFeatures.remove(feature); + notifyUpdateAMBEFeatures(); + } +} diff --git a/plugins/channelrx/demoddsd/dsddemod.h b/plugins/channelrx/demoddsd/dsddemod.h index dd5584d1d..147d10143 100644 --- a/plugins/channelrx/demoddsd/dsddemod.h +++ b/plugins/channelrx/demoddsd/dsddemod.h @@ -62,6 +62,38 @@ public: { } }; + class MsgQueryAvailableAMBEFeatures : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgQueryAvailableAMBEFeatures* create() { + return new MsgQueryAvailableAMBEFeatures(); + } + + protected: + MsgQueryAvailableAMBEFeatures() : + Message() + { } + }; + + class MsgReportAvailableAMBEFeatures : public Message { + MESSAGE_CLASS_DECLARATION + + public: + QList& getFeatures() { return m_availableFeatures; } + + static MsgReportAvailableAMBEFeatures* create() { + return new MsgReportAvailableAMBEFeatures(); + } + + private: + QList m_availableFeatures; + + MsgReportAvailableAMBEFeatures() : + Message() + {} + }; + DSDDemod(DeviceAPI *deviceAPI); virtual ~DSDDemod(); virtual void destroy() { delete this; } @@ -140,6 +172,7 @@ private: DSDDemodBaseband *m_basebandSink; DSDDemodSettings m_settings; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink + QHash m_availableAMBEFeatures; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; @@ -148,6 +181,8 @@ private: virtual bool handleMessage(const Message& cmd); void applySettings(const DSDDemodSettings& settings, bool force = false); + void scanAvailableAMBEFeatures(); + void notifyUpdateAMBEFeatures(); void sendSampleRateToDemodAnalyzer(); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const DSDDemodSettings& settings, bool force); @@ -167,6 +202,8 @@ private: private slots: void networkManagerFinished(QNetworkReply *reply); void handleIndexInDeviceSetChanged(int index); + void handleFeatureAdded(int featureSetIndex, Feature *feature); + void handleFeatureRemoved(int featureSetIndex, Feature *feature); }; #endif // INCLUDE_DSDDEMOD_H diff --git a/plugins/channelrx/demoddsd/dsddemodbaseband.h b/plugins/channelrx/demoddsd/dsddemodbaseband.h index 13f54448c..e96df9e97 100644 --- a/plugins/channelrx/demoddsd/dsddemodbaseband.h +++ b/plugins/channelrx/demoddsd/dsddemodbaseband.h @@ -75,6 +75,7 @@ public: void setChannel(ChannelAPI *channel); void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); } + void setAMBEFeatureMessageQueue(MessageQueue *ambeFeatureMessageQueue) { m_sink.setAmbeFeatureMessageQueue(ambeFeatureMessageQueue); } private: SampleSinkFifo m_sampleFifo; diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index fc256d618..9dfa7490d 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -113,6 +113,13 @@ bool DSDDemodGUI::handleMessage(const Message& message) updateAbsoluteCenterFrequency(); return true; } + else if (DSDDemod::MsgReportAvailableAMBEFeatures::match(message)) + { + DSDDemod::MsgReportAvailableAMBEFeatures& report = (DSDDemod::MsgReportAvailableAMBEFeatures&) message; + m_availableAMBEFeatures = report.getFeatures(); + updateAMBEFeaturesList(); + return true; + } else { return false; @@ -263,6 +270,19 @@ void DSDDemodGUI::on_symbolPLLLock_toggled(bool checked) applySettings(); } +void DSDDemodGUI::on_ambeSupport_clicked(bool checked) +{ + m_settings.m_connectAMBE = checked; + m_settings.m_ambeFeatureIndex = m_availableAMBEFeatures[ui->ambeFeatures->currentIndex()].m_featureIndex; + applySettings(); +} + +void DSDDemodGUI::on_ambeFeatures_currentIndexChanged(int index) +{ + m_settings.m_ambeFeatureIndex = m_availableAMBEFeatures[index].m_featureIndex; + applySettings(); +} + void DSDDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -490,6 +510,17 @@ void DSDDemodGUI::displaySettings() ui->traceDecayText->setText(QString("%1").arg(m_settings.m_traceDecay)); m_scopeVisXY->setDecay(m_settings.m_traceDecay); + ui->ambeSupport->setChecked(m_settings.m_connectAMBE); + + for (int i = 0; i < ui->ambeFeatures->count(); i++) + { + if (ui->ambeFeatures->itemData(i).toInt() == m_settings.m_ambeFeatureIndex) + { + ui->ambeFeatures->setCurrentIndex(i); + break; + } + } + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); @@ -497,6 +528,32 @@ void DSDDemodGUI::displaySettings() blockApplySettings(false); } +void DSDDemodGUI::updateAMBEFeaturesList() +{ + ui->ambeFeatures->blockSignals(true); + ui->ambeSupport->blockSignals(true); + ui->ambeFeatures->clear(); + bool unsetAMBE = true; + + for (int i = 0; i < m_availableAMBEFeatures.count(); i++) + { + ui->ambeFeatures->addItem(tr("F:%1").arg(m_availableAMBEFeatures[i].m_featureIndex), m_availableAMBEFeatures[i].m_featureIndex); + + if (m_settings.m_ambeFeatureIndex == m_availableAMBEFeatures[i].m_featureIndex) + { + unsetAMBE = false; + ui->ambeFeatures->setCurrentIndex(i); + } + } + + if (unsetAMBE) { + ui->ambeSupport->setChecked(false); + } + + ui->ambeSupport->blockSignals(false); + ui->ambeFeatures->blockSignals(false); +} + void DSDDemodGUI::applySettings(bool force) { if (m_doApplySettings) @@ -658,6 +715,8 @@ void DSDDemodGUI::makeUIConnections() QObject::connect(ui->audioMute, &QToolButton::toggled, this, &DSDDemodGUI::on_audioMute_toggled); QObject::connect(ui->symbolPLLLock, &QToolButton::toggled, this, &DSDDemodGUI::on_symbolPLLLock_toggled); QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &DSDDemodGUI::on_viewStatusLog_clicked); + QObject::connect(ui->ambeSupport, &QCheckBox::clicked, this, &DSDDemodGUI::on_ambeSupport_clicked); + QObject::connect(ui->ambeFeatures, QOverload::of(&QComboBox::currentIndexChanged), this, &DSDDemodGUI::on_ambeFeatures_currentIndexChanged); } void DSDDemodGUI::updateAbsoluteCenterFrequency() diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index e5de1b302..c841fd342 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -90,6 +90,7 @@ private: qint64 m_deviceCenterFrequency; int m_basebandSampleRate; bool m_doApplySettings; + QList m_availableAMBEFeatures; ScopeVisXY* m_scopeVisXY; @@ -117,6 +118,7 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void updateAMBEFeaturesList(); void updateMyPosition(); bool handleMessage(const Message& message); void makeUIConnections(); @@ -145,6 +147,8 @@ private slots: void on_highPassFilter_toggled(bool checked); void on_audioMute_toggled(bool checked); void on_symbolPLLLock_toggled(bool checked); + void on_ambeSupport_clicked(bool checked); + void on_ambeFeatures_currentIndexChanged(int index); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void on_viewStatusLog_clicked(); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.ui b/plugins/channelrx/demoddsd/dsddemodgui.ui index 781d39d68..0403c7743 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.ui +++ b/plugins/channelrx/demoddsd/dsddemodgui.ui @@ -1231,6 +1231,29 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + 10 + 170 + 71 + 23 + + + + AMBE + + + + + + 80 + 170 + 50 + 20 + + + diff --git a/plugins/channelrx/demoddsd/dsddemodplugin.cpp b/plugins/channelrx/demoddsd/dsddemodplugin.cpp index 17f2485f9..903d7348a 100644 --- a/plugins/channelrx/demoddsd/dsddemodplugin.cpp +++ b/plugins/channelrx/demoddsd/dsddemodplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = { DSDDemod::m_channelId, QStringLiteral("DSD Demodulator"), - QStringLiteral("7.0.0"), + QStringLiteral("7.2.0"), QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.cpp b/plugins/channelrx/demoddsd/dsddemodsettings.cpp index ed7a6394f..73aaadda1 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsettings.cpp @@ -61,6 +61,8 @@ void DSDDemodSettings::resetToDefaults() m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; m_hidden = false; + m_ambeFeatureIndex = -1; + m_connectAMBE = false; } QByteArray DSDDemodSettings::serialize() const @@ -107,6 +109,8 @@ QByteArray DSDDemodSettings::serialize() const s.writeS32(32, m_workspaceIndex); s.writeBlob(33, m_geometryBytes); s.writeBool(34, m_hidden); + s.writeS32(35, m_ambeFeatureIndex); + s.writeBool(36, m_connectAMBE); return s.final(); } @@ -190,6 +194,8 @@ bool DSDDemodSettings::deserialize(const QByteArray& data) d.readS32(32, &m_workspaceIndex, 0); d.readBlob(33, &m_geometryBytes); d.readBool(34, &m_hidden, false); + d.readS32(35, &m_ambeFeatureIndex, -1); + d.readBool(36, &m_connectAMBE, false); return true; } diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.h b/plugins/channelrx/demoddsd/dsddemodsettings.h index 3339be650..e4d22c30e 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.h +++ b/plugins/channelrx/demoddsd/dsddemodsettings.h @@ -21,9 +21,20 @@ #include class Serializable; +class Feature; struct DSDDemodSettings { + struct AvailableAMBEFeature + { + int m_featureIndex; + Feature *m_feature; + + AvailableAMBEFeature() = default; + AvailableAMBEFeature(const AvailableAMBEFeature&) = default; + AvailableAMBEFeature& operator=(const AvailableAMBEFeature&) = default; + }; + qint64 m_inputFrequencyOffset; Real m_rfBandwidth; Real m_fmDeviation; @@ -55,6 +66,8 @@ struct DSDDemodSettings int m_workspaceIndex; QByteArray m_geometryBytes; bool m_hidden; + int m_ambeFeatureIndex; + bool m_connectAMBE; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/demoddsd/dsddemodsink.cpp b/plugins/channelrx/demoddsd/dsddemodsink.cpp index b1d20df72..e91af3932 100644 --- a/plugins/channelrx/demoddsd/dsddemodsink.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsink.cpp @@ -34,6 +34,7 @@ #include "dsp/dspengine.h" #include "dsp/basebandsamplesink.h" #include "dsp/datafifo.h" +#include "dsp/dspcommands.h" #include "audio/audiooutputdevice.h" #include "util/db.h" #include "util/messagequeue.h" @@ -44,6 +45,7 @@ DSDDemodSink::DSDDemodSink() : m_channelSampleRate(48000), m_channelFrequencyOffset(0), + m_ambeFeatureMessageQueue(nullptr), m_audioSampleRate(48000), m_interpolatorDistance(0.0f), m_interpolatorDistanceRemain(0.0f), @@ -229,20 +231,32 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV m_scopeSampleBuffer.push_back(s); } - if (DSPEngine::instance()->hasDVSerialSupport()) + // if (DSPEngine::instance()->hasDVSerialSupport()) + if (m_ambeFeatureMessageQueue) { if ((m_settings.m_slot1On) && m_dsdDecoder.mbeDVReady1()) { if (!m_settings.m_audioMute) { - DSPEngine::instance()->pushMbeFrame( + m_ambeFeatureMessageQueue->push( + new DSPPushMbeFrame( m_dsdDecoder.getMbeDVFrame1(), m_dsdDecoder.getMbeRateIndex(), m_settings.m_volume * 10.0, m_settings.m_tdmaStereo ? 1 : 3, // left or both channels m_settings.m_highPassFilter, m_audioSampleRate/8000, // upsample from native 8k - &m_audioFifo1); + &m_audioFifo1 + ) + ); + // DSPEngine::instance()->pushMbeFrame( + // m_dsdDecoder.getMbeDVFrame1(), + // m_dsdDecoder.getMbeRateIndex(), + // m_settings.m_volume * 10.0, + // m_settings.m_tdmaStereo ? 1 : 3, // left or both channels + // m_settings.m_highPassFilter, + // m_audioSampleRate/8000, // upsample from native 8k + // &m_audioFifo1); } m_dsdDecoder.resetMbeDV1(); @@ -252,14 +266,25 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV { if (!m_settings.m_audioMute) { - DSPEngine::instance()->pushMbeFrame( + m_ambeFeatureMessageQueue->push( + new DSPPushMbeFrame( m_dsdDecoder.getMbeDVFrame2(), m_dsdDecoder.getMbeRateIndex(), m_settings.m_volume * 10.0, m_settings.m_tdmaStereo ? 2 : 3, // right or both channels m_settings.m_highPassFilter, m_audioSampleRate/8000, // upsample from native 8k - &m_audioFifo2); + &m_audioFifo2 + ) + ); + // DSPEngine::instance()->pushMbeFrame( + // m_dsdDecoder.getMbeDVFrame2(), + // m_dsdDecoder.getMbeRateIndex(), + // m_settings.m_volume * 10.0, + // m_settings.m_tdmaStereo ? 2 : 3, // right or both channels + // m_settings.m_highPassFilter, + // m_audioSampleRate/8000, // upsample from native 8k + // &m_audioFifo2); } m_dsdDecoder.resetMbeDV2(); @@ -270,7 +295,8 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV } } - if (!DSPEngine::instance()->hasDVSerialSupport()) + if (!m_ambeFeatureMessageQueue) + // if (!DSPEngine::instance()->hasDVSerialSupport()) { if (m_settings.m_slot1On) { diff --git a/plugins/channelrx/demoddsd/dsddemodsink.h b/plugins/channelrx/demoddsd/dsddemodsink.h index 3279d553d..dd4dbdfc5 100644 --- a/plugins/channelrx/demoddsd/dsddemodsink.h +++ b/plugins/channelrx/demoddsd/dsddemodsink.h @@ -83,6 +83,7 @@ public: } const char *updateAndGetStatusText(); + void setAmbeFeatureMessageQueue(MessageQueue *queue) { m_ambeFeatureMessageQueue = queue; } private: struct MagSqLevelsStore @@ -114,6 +115,7 @@ private: int m_channelFrequencyOffset; DSDDemodSettings m_settings; ChannelAPI *m_channel; + MessageQueue *m_ambeFeatureMessageQueue; int m_audioSampleRate; QVector m_demodBuffer; int m_demodBufferFill; diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index ac1b031d5..4a79484d5 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -39,3 +39,7 @@ endif() if (ENABLE_LIMESUITE AND LIMESUITE_FOUND) add_subdirectory(limerfe) endif() + +if (LIBSERIALDV_FOUND) + add_subdirectory(ambe) +endif() diff --git a/plugins/feature/ambe/CMakeLists.txt b/plugins/feature/ambe/CMakeLists.txt new file mode 100644 index 000000000..d243b2633 --- /dev/null +++ b/plugins/feature/ambe/CMakeLists.txt @@ -0,0 +1,65 @@ +project(ambe) + +set(ambe_SOURCES + ambe.cpp + ambesettings.cpp + ambeplugin.cpp + ambeengine.cpp + ambeworker.cpp + # ambewebapiadapter.cpp +) + +set(ambe_HEADERS + ambe.h + ambesettings.h + ambeplugin.h + ambeengine.h + ambeworker.h + # ambewebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${LIBSERIALDV_INCLUDE_DIR} +) + +if(NOT SERVER_MODE) + set(ambe_SOURCES + ${ambe_SOURCES} + ambegui.cpp + ambegui.ui + ) + set(ambe_HEADERS + ${ambe_HEADERS} + ambegui.h + ) + + set(TARGET_NAME featureambe) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME featureambesrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${ambe_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + ${LIBSERIALDV_LIBRARY} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/feature/ambe/ambe.cpp b/plugins/feature/ambe/ambe.cpp new file mode 100644 index 000000000..5a6bb90b4 --- /dev/null +++ b/plugins/feature/ambe/ambe.cpp @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "SWGFeatureSettings.h" +#include "SWGFeatureReport.h" +#include "SWGFeatureActions.h" + +#include "settings/serializable.h" +#include "util/simpleserializer.h" +#include "dsp/dspcommands.h" + +#include "ambe.h" + +MESSAGE_CLASS_DEFINITION(AMBE::MsgConfigureAMBE, Message) + +const char* const AMBE::m_featureIdURI = "sdrangel.feature.ambe"; +const char* const AMBE::m_featureId = "AMBE"; + +AMBE::AMBE(WebAPIAdapterInterface *webAPIAdapterInterface) : + Feature(m_featureIdURI, webAPIAdapterInterface) +{ + setObjectName(m_featureId); + m_state = StIdle; + m_errorMessage = "AMBE error"; + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &AMBE::networkManagerFinished + ); +} + +AMBE::~AMBE() +{ + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &AMBE::networkManagerFinished + ); + delete m_networkManager; +} + +void AMBE::start() +{ + qDebug("AMBE::start"); + m_state = StRunning; +} + +void AMBE::stop() +{ + qDebug("AMBE::stop"); + m_state = StIdle; +} + +void AMBE::applySettings(const AMBESettings& settings, bool force) +{ + (void) force; + m_settings = settings; +} + +bool AMBE::handleMessage(const Message& cmd) +{ + if (MsgConfigureAMBE::match(cmd)) + { + MsgConfigureAMBE& cfg = (MsgConfigureAMBE&) cmd; + qDebug() << "AMBE::handleMessage: MsgConfigureAMBE"; + applySettings(cfg.getSettings(), cfg.getForce()); + return true; + } + else if (DSPPushMbeFrame::match(cmd)) + { + DSPPushMbeFrame& cfg = (DSPPushMbeFrame&) cmd; + m_ambeEngine.pushMbeFrame( + cfg.getMbeFrame(), + cfg.getMbeRateIndex(), + cfg.getMbeVolumeIndex(), + cfg.getChannels(), + cfg.getUseHP(), + cfg.getUpsampling(), + cfg.getAudioFifo() + ); + return true; + } + + return false; +} + +QByteArray AMBE::serialize() const +{ + SimpleSerializer s(1); + s.writeBlob(1, m_settings.serialize()); + return s.final(); +} + +bool AMBE::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + m_settings.resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + QByteArray bytetmp; + d.readBlob(1, &bytetmp); + + if (m_settings.deserialize(bytetmp)) + { + MsgConfigureAMBE *msg = MsgConfigureAMBE::create(m_settings, true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureAMBE *msg = MsgConfigureAMBE::create(m_settings, true); + m_inputMessageQueue.push(msg); + return false; + } + } + else + { + return false; + } +} + +void AMBE::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AMBE::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AMBE::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/feature/ambe/ambe.h b/plugins/feature/ambe/ambe.h new file mode 100644 index 000000000..a456e4a34 --- /dev/null +++ b/plugins/feature/ambe/ambe.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AMBE_H_ +#define INCLUDE_FEATURE_AMBE_H_ + +#include +#include + +#include "feature/feature.h" +#include "util/message.h" + +#include "ambeengine.h" +#include "ambesettings.h" + +class WebAPIAdapterInterface; +class QNetworkAccessManager; +class QNetworkReply; + +class AMBE : public Feature +{ + Q_OBJECT +public: + class MsgConfigureAMBE : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const AMBESettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureAMBE* create(const AMBESettings& settings, bool force) { + return new MsgConfigureAMBE(settings, force); + } + + private: + AMBESettings m_settings; + bool m_force; + + MsgConfigureAMBE(const AMBESettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + AMBE(WebAPIAdapterInterface *webAPIAdapterInterface); + virtual ~AMBE(); + virtual void destroy() { delete this; } + virtual bool handleMessage(const Message& cmd); + + virtual void getIdentifier(QString& id) const { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual void getTitle(QString& title) const { title = m_settings.m_title; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + AMBEEngine *getAMBEEngine() { return &m_ambeEngine; } + + static const char* const m_featureIdURI; + static const char* const m_featureId; + +private: + AMBESettings m_settings; + AMBEEngine m_ambeEngine; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void start(); + void stop(); + void applySettings(const AMBESettings& settings, bool force = false); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif diff --git a/plugins/feature/ambe/ambeengine.cpp b/plugins/feature/ambe/ambeengine.cpp new file mode 100644 index 000000000..bc16a6c07 --- /dev/null +++ b/plugins/feature/ambe/ambeengine.cpp @@ -0,0 +1,402 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 __APPLE__ +#include +#include +#endif + +#ifndef _MSC_VER +#include +#include +#include +#endif + +#if !defined(_WIN32) && !defined(__APPLE__) +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include + +#include "ambeworker.h" +#include "ambeengine.h" + +AMBEEngine::AMBEEngine() +{} + +AMBEEngine::~AMBEEngine() +{ + qDebug("AMBEEngine::~AMBEEngine: %lu controllers", m_controllers.size()); +} + +#if defined(_WIN32) +void AMBEEngine::getComList() +{ + m_comList.clear(); + m_comList8250.clear(); + char comCStr[16]; + + // Arbitrarily set the list to the 20 first COM ports + for (int i = 1; i <= 20; i++) + { + sprintf(comCStr, "COM%d", i); + m_comList.push_back(std::string(comCStr)); + } +} + +// Do not activate serial support at all for windows +void AMBEEngine::scan(std::vector& ambeDevices) +{ + (void) ambeDevices; +} +#elif defined(__APPLE__) +void AMBEEngine::getComList() +{ +} +void AMBEEngine::scan(std::vector& ambeDevices) +{ + (void) ambeDevices; +} +#else +void AMBEEngine::getComList() +{ + int n; + struct dirent **namelist; + m_comList.clear(); + m_comList8250.clear(); + const char* sysdir = "/sys/class/tty/"; + + // Scan through /sys/class/tty - it contains all tty-devices in the system + n = scandir(sysdir, &namelist, NULL, alphasort); + if (n < 0) + { + perror("scandir"); + } + else + { + while (n--) + { + if (strcmp(namelist[n]->d_name, "..") && strcmp(namelist[n]->d_name, ".")) + { + // Construct full absolute file path + std::string devicedir = sysdir; + devicedir += namelist[n]->d_name; + // Register the device + register_comport(m_comList, m_comList8250, devicedir); + } + + free(namelist[n]); + } + + free(namelist); + } + + // Only non-serial8250 has been added to comList without any further testing + // serial8250-devices must be probe to check for validity + probe_serial8250_comports(m_comList, m_comList8250); +} +#endif // not Windows nor Apple + +#if !defined(_WIN32) && !defined(__APPLE__) +void AMBEEngine::register_comport( + std::vector& comList, + std::vector& comList8250, + const std::string& dir) +{ + // Get the driver the device is using + std::string driver = get_driver(dir); + + // Skip devices without a driver + if (driver.size() > 0) + { + //std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl; + std::string devfile = std::string("/dev/") + basename((char *) dir.c_str()); + + // Put serial8250-devices in a seperate list + if (driver == "serial8250") { + comList8250.push_back(devfile); + } else { + comList.push_back(devfile); + } + } +} + +void AMBEEngine::probe_serial8250_comports( + std::vector& comList, + std::vector comList8250) +{ + struct serial_struct serinfo; + std::vector::iterator it = comList8250.begin(); + + // Iterate over all serial8250-devices + while (it != comList8250.end()) + { + + // Try to open the device + int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY); + + if (fd >= 0) + { + // Get serial_info + if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) + { + // If device type is no PORT_UNKNOWN we accept the port + if (serinfo.type != PORT_UNKNOWN) { + comList.push_back(*it); + } + } + + close(fd); + } + it++; + } +} + +std::string AMBEEngine::get_driver(const std::string& tty) +{ + struct stat st; + std::string devicedir = tty; + // Append '/device' to the tty-path + devicedir += "/device"; + + // Stat the devicedir and handle it if it is a symlink + if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode)) + { + char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + // Append '/driver' and return basename of the target + devicedir += "/driver"; + + if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) { + return basename(buffer); + } + } + + return ""; +} + +void AMBEEngine::scan(std::vector& ambeDevices) +{ + getComList(); + std::vector::const_iterator it = m_comList.begin(); + ambeDevices.clear(); + + while (it != m_comList.end()) + { + AMBEWorker *worker = new AMBEWorker(); + qDebug("AMBEEngine::scan: com: %s", it->c_str()); + + if (worker->open(*it)) + { + ambeDevices.push_back(QString(it->c_str())); + worker->close(); + } + + delete worker; + ++it; + } +} +#endif // not Windows nor Apple + +bool AMBEEngine::registerController(const std::string& deviceRef) +{ + AMBEWorker *worker = new AMBEWorker(); + + if (worker->open(deviceRef)) + { + qDebug("AMBEEngine::registerController: device: %s", deviceRef.c_str()); + m_controllers.push_back(AMBEController()); + m_controllers.back().worker = worker; + m_controllers.back().thread = new QThread(); + m_controllers.back().device = deviceRef; + + m_controllers.back().worker->moveToThread(m_controllers.back().thread); + connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().thread, SLOT(quit())); + connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().worker, SLOT(deleteLater())); + connect(m_controllers.back().thread, SIGNAL(finished()), m_controllers.back().thread, SLOT(deleteLater())); + connect(&m_controllers.back().worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), m_controllers.back().worker, SLOT(handleInputMessages())); + std::this_thread::sleep_for(std::chrono::seconds(1)); + m_controllers.back().thread->start(); + + return true; + } + + qWarning("AMBEEngine::registerController: failed to register device: %s", deviceRef.c_str()); + return false; +} + +void AMBEEngine::releaseController(const std::string& deviceRef) +{ + std::vector::iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + if (it->device == deviceRef) + { + disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages())); + it->worker->stop(); + it->thread->wait(100); + it->worker->m_inputMessageQueue.clear(); + it->worker->close(); + qDebug() << "AMBEEngine::releaseController: closed device at: " << it->device.c_str(); + m_controllers.erase(it); + break; + } + + ++it; + } +} + +void AMBEEngine::releaseAll() +{ + std::vector::iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages())); + it->worker->stop(); + it->thread->wait(100); + it->worker->m_inputMessageQueue.clear(); + it->worker->close(); + qDebug() << "AMBEEngine::release: closed device at: " << it->device.c_str(); + ++it; + } + + m_controllers.clear(); +} + +void AMBEEngine::getDeviceRefs(std::vector& deviceNames) +{ + std::vector::const_iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + deviceNames.push_back(QString(it->device.c_str())); + ++it; + } +} + +void AMBEEngine::pushMbeFrame( + const unsigned char *mbeFrame, + int mbeRateIndex, + int mbeVolumeIndex, + unsigned char channels, + bool useLP, + int upsampling, + AudioFifo *audioFifo) +{ + std::vector::iterator it = m_controllers.begin(); + std::vector::iterator itAvail = m_controllers.end(); + bool done = false; + QMutexLocker locker(&m_mutex); + + while (it != m_controllers.end()) + { + if (it->worker->hasFifo(audioFifo)) + { + it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo); + done = true; + } + else if (it->worker->isAvailable()) + { + itAvail = it; + } + + ++it; + } + + if (!done) + { + if (itAvail != m_controllers.end()) + { + int wNum = itAvail - m_controllers.begin(); + + qDebug("AMBEEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum); + itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo); + } + else + { + qDebug("AMBEEngine::pushMbeFrame: no DV device available. MBE frame dropped"); + } + } +} + +QByteArray AMBEEngine::serialize() const +{ + qDebug("AMBEEngine::serialize"); + QStringList qDeviceList; + std::vector::const_iterator it = m_controllers.begin(); + + while (it != m_controllers.end()) + { + qDebug("AMBEEngine::serialize: %s", it->device.c_str()); + qDeviceList << QString(it->device.c_str()); + ++it; + } + + QByteArray data; + QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); + (*stream) << qDeviceList; + delete stream; + + return data; +} + +bool AMBEEngine::deserialize(const QByteArray& data) +{ + if (data.size() <= 0) + { + qDebug("AMBEEngine::deserialize: invalid or no data"); + return false; + } + + QStringList qDeviceList; + QDataStream *stream = new QDataStream(data); + (*stream) >> qDeviceList; + delete stream; + + releaseAll(); + + for (int i = 0; i < qDeviceList.size(); ++i) + { + qDebug(" AMBEEngine::deserialize: %s", qDeviceList.at(i).toStdString().c_str()); + registerController(qDeviceList.at(i).toStdString()); + } + + return true; +} + +void AMBEEngine::formatTo(SWGSDRangel::SWGObject *swgObject) const +{ + (void) swgObject; + // TODO +} + +void AMBEEngine::updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject) +{ + (void) keys; + (void) swgObject; + // TODO +} diff --git a/plugins/feature/ambe/ambeengine.h b/plugins/feature/ambe/ambeengine.h new file mode 100644 index 000000000..897cefa71 --- /dev/null +++ b/plugins/feature/ambe/ambeengine.h @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 SDRBASE_AMBE_AMBEENGINE_H_ +#define SDRBASE_AMBE_AMBEENGINE_H_ + +#include +#include + +#include +#include +#include +#include + +#include "settings/serializable.h" + +class QThread; +class AMBEWorker; +class AudioFifo; + +class AMBEEngine : public QObject, public Serializable +{ + Q_OBJECT +public: + AMBEEngine(); + ~AMBEEngine(); + + void scan(std::vector& ambeDevices); + void releaseAll(); + + int getNbDevices() const { return m_controllers.size(); } //!< number of devices used + void getDeviceRefs(std::vector& devicesRefs); //!< reference of the devices used (device path or url) + bool registerController(const std::string& deviceRef); //!< create a new controller for the device in reference + void releaseController(const std::string& deviceRef); //!< release controller resources for the device in reference + + void pushMbeFrame( + const unsigned char *mbeFrame, + int mbeRateIndex, + int mbeVolumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + virtual void formatTo(SWGSDRangel::SWGObject *swgObject) const; //!< Serialize to API + virtual void updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject); //!< Deserialize from API + +private: + struct AMBEController + { + AMBEController() : + thread(nullptr), + worker(nullptr) + {} + + QThread *thread; + AMBEWorker *worker; + std::string device; + }; + +#ifndef _WIN32 + static std::string get_driver(const std::string& tty); + static void register_comport(std::vector& comList, std::vector& comList8250, const std::string& dir); + static void probe_serial8250_comports(std::vector& comList, std::vector comList8250); +#endif + void getComList(); + + std::vector m_controllers; + std::vector m_comList; + std::vector m_comList8250; + QMutex m_mutex; +}; + + + +#endif /* SDRBASE_AMBE_AMBEENGINE_H_ */ diff --git a/plugins/feature/ambe/ambegui.cpp b/plugins/feature/ambe/ambegui.cpp new file mode 100644 index 000000000..9c7eb425a --- /dev/null +++ b/plugins/feature/ambe/ambegui.cpp @@ -0,0 +1,343 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "feature/featureuiset.h" +#include "gui/basicfeaturesettingsdialog.h" +#include "gui/crightclickenabler.h" + +#include "ui_ambegui.h" +#include "ambegui.h" +#include "ambe.h" + +AMBEGUI* AMBEGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature) +{ + AMBEGUI* gui = new AMBEGUI(pluginAPI, featureUISet, feature); + return gui; +} + +void AMBEGUI::destroy() +{ + delete this; +} + +AMBEGUI::AMBEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) : + FeatureGUI(parent), + ui(new Ui::AMBEGUI), + m_pluginAPI(pluginAPI), + m_featureUISet(featureUISet), + m_doApplySettings(true) +{ + m_feature = feature; + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/feature/ambe/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + + m_ambe = reinterpret_cast(feature); + m_ambe->setMessageQueueToGUI(&m_inputMessageQueue); + + m_settings.setRollupState(&m_rollupState); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + populateSerialList(); + refreshInUseList(); + displaySettings(); + makeUIConnections(); +} + +AMBEGUI::~AMBEGUI() +{ + delete ui; +} + +void AMBEGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray AMBEGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool AMBEGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); + displaySettings(); + refreshInUseList(); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +void AMBEGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + +void AMBEGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + +void AMBEGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + getRollupContents()->saveState(m_rollupState); + applySettings(); +} + +void AMBEGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicFeatureSettingsDialog dialog(this); + dialog.setTitle(m_settings.m_title); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); + dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); + + dialog.move(p); + dialog.exec(); + + m_settings.m_title = dialog.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); + m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); + + setTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); + } + + resetContextMenuType(); +} + +void AMBEGUI::displaySettings() +{ + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); +} + +void AMBEGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + AMBE::MsgConfigureAMBE* message = AMBE::MsgConfigureAMBE::create( m_settings, force); + m_ambe->getInputMessageQueue()->push(message); + } +} + +bool AMBEGUI::handleMessage(const Message& message) +{ + if (AMBE::MsgConfigureAMBE::match(message)) + { + qDebug("AMBEGUI::handleMessage: AMBE::MsgConfigureAMBE"); + const AMBE::MsgConfigureAMBE& cfg = (AMBE::MsgConfigureAMBE&) message; + m_settings = cfg.getSettings(); + displaySettings(); + return true; + } + + return false; +} + +void AMBEGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop())) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +void AMBEGUI::populateSerialList() +{ + std::vector ambeSerialDevices; + m_ambe->getAMBEEngine()->scan(ambeSerialDevices); + ui->ambeSerialDevices->clear(); + std::vector::const_iterator it = ambeSerialDevices.begin(); + + for (; it != ambeSerialDevices.end(); ++it) { + ui->ambeSerialDevices->addItem(QString(*it)); + } +} + +void AMBEGUI::refreshInUseList() +{ + std::vector inUseDevices; + m_ambe->getAMBEEngine()->getDeviceRefs(inUseDevices); + ui->ambeDeviceRefs->clear(); + std::vector::const_iterator it = inUseDevices.begin(); + + for (; it != inUseDevices.end(); ++it) + { + qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(*it)); + ui->ambeDeviceRefs->addItem(*it); + } +} +void AMBEGUI::on_importSerial_clicked() +{ + QListWidgetItem *serialItem = ui->ambeSerialDevices->currentItem(); + + if (!serialItem) + { + ui->statusText->setText("No selection"); + return; + } + + QString serialName = serialItem->text(); + QList foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive); + + if (foundItems.size() == 0) + { + if (m_ambe->getAMBEEngine()->registerController(serialName.toStdString())) + { + ui->ambeDeviceRefs->addItem(serialName); + ui->statusText->setText(tr("%1 added").arg(serialName)); + } + else + { + ui->statusText->setText(tr("Cannot open %1").arg(serialName)); + } + } + else + { + ui->statusText->setText("Device already in use"); + } +} + +void AMBEGUI::on_importAllSerial_clicked() +{ + int count = 0; + + for (int i = 0; i < ui->ambeSerialDevices->count(); i++) + { + const QListWidgetItem *serialItem = ui->ambeSerialDevices->item(i); + QString serialName = serialItem->text(); + QList foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive); + + if (foundItems.size() == 0) + { + if (m_ambe->getAMBEEngine()->registerController(serialName.toStdString())) + { + ui->ambeDeviceRefs->addItem(serialName); + count++; + } + } + } + + ui->statusText->setText(tr("%1 devices added").arg(count)); +} + +void AMBEGUI::on_removeAmbeDevice_clicked() +{ + QListWidgetItem *deviceItem = ui->ambeDeviceRefs->currentItem(); + + if (!deviceItem) + { + ui->statusText->setText("No selection"); + return; + } + + QString deviceName = deviceItem->text(); + m_ambe->getAMBEEngine()->releaseController(deviceName.toStdString()); + ui->statusText->setText(tr("%1 removed").arg(deviceName)); + refreshInUseList(); +} + +void AMBEGUI::on_refreshAmbeList_clicked() +{ + refreshInUseList(); + ui->statusText->setText("In use refreshed"); +} + +void AMBEGUI::on_clearAmbeList_clicked() +{ + if (ui->ambeDeviceRefs->count() == 0) + { + ui->statusText->setText("No active items"); + return; + } + + m_ambe->getAMBEEngine()->releaseAll(); + ui->ambeDeviceRefs->clear(); + ui->statusText->setText("All items released"); +} + +void AMBEGUI::on_importAddress_clicked() +{ + QString addressAndPort = ui->ambeAddressText->text(); + + QList foundItems = ui->ambeDeviceRefs->findItems(addressAndPort, Qt::MatchFixedString|Qt::MatchCaseSensitive); + + if (foundItems.size() == 0) + { + if (m_ambe->getAMBEEngine()->registerController(addressAndPort.toStdString())) + { + ui->ambeDeviceRefs->addItem(addressAndPort); + ui->statusText->setText(tr("%1 added").arg(addressAndPort)); + } + else + { + ui->statusText->setText(tr("Cannot open %1").arg(addressAndPort)); + } + } + else + { + ui->statusText->setText("Address already in use"); + } +} + +void AMBEGUI::makeUIConnections() +{ + QObject::connect(ui->importSerial, &QPushButton::clicked, this, &AMBEGUI::on_importSerial_clicked); + QObject::connect(ui->importAllSerial, &QPushButton::clicked, this, &AMBEGUI::on_importAllSerial_clicked); + QObject::connect(ui->removeAmbeDevice, &QPushButton::clicked, this, &AMBEGUI::on_removeAmbeDevice_clicked); + QObject::connect(ui->refreshAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_refreshAmbeList_clicked); + QObject::connect(ui->clearAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_clearAmbeList_clicked); + QObject::connect(ui->importAddress, &QPushButton::clicked, this, &AMBEGUI::on_importAddress_clicked); +} diff --git a/plugins/feature/ambe/ambegui.h b/plugins/feature/ambe/ambegui.h new file mode 100644 index 000000000..f93dc16f2 --- /dev/null +++ b/plugins/feature/ambe/ambegui.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AMBEGUI_H_ +#define INCLUDE_FEATURE_AMBEGUI_H_ + +#include "feature/featuregui.h" +#include "util/messagequeue.h" +#include "util/movingaverage.h" +#include "settings/rollupstate.h" + +#include "ambesettings.h" + +class PluginAPI; +class FeatureUISet; +class Feature; +class AMBE; +class DSPDeviceSourceEngine; +class DSPDeviceSinkEngine; + +namespace Ui { + class AMBEGUI; +} + +class AMBEGUI : public FeatureGUI +{ + Q_OBJECT +public: + static AMBEGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index); + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + +protected: + void resizeEvent(QResizeEvent* size); + +private: + Ui::AMBEGUI* ui; + AMBE *m_ambe; + PluginAPI* m_pluginAPI; + FeatureUISet* m_featureUISet; + AMBESettings m_settings; + RollupState m_rollupState; + bool m_doApplySettings; + MessageQueue m_inputMessageQueue; + + explicit AMBEGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); + virtual ~AMBEGUI(); + + void populateSerialList(); + void refreshInUseList(); + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void applySettings(bool force = false); + void displaySettings(); + bool handleMessage(const Message& message); + void makeUIConnections(); + +private slots: + void onMenuDialogCalled(const QPoint &p); + void onWidgetRolled(QWidget* widget, bool rollDown); + void handleInputMessages(); + void on_importSerial_clicked(); + void on_importAllSerial_clicked(); + void on_removeAmbeDevice_clicked(); + void on_refreshAmbeList_clicked(); + void on_clearAmbeList_clicked(); + void on_importAddress_clicked(); +}; + +#endif diff --git a/plugins/feature/ambe/ambegui.ui b/plugins/feature/ambe/ambegui.ui new file mode 100644 index 000000000..1db075df0 --- /dev/null +++ b/plugins/feature/ambe/ambegui.ui @@ -0,0 +1,285 @@ + + + AMBEGUI + + + + 0 + 0 + 360 + 452 + + + + + 0 + 0 + + + + + 360 + 452 + + + + + 560 + 452 + + + + + Liberation Sans + 9 + + + + AMBE Decoder Controller + + + + + 0 + 0 + 358 + 450 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + AMBE server IP and port or direct input + + + + + + + + + + + AMBE server address as ip:port or direct input + + + + + + + + + + + + + + In use + + + + + + + Use server + + + + + + + :/arrow_down.png:/arrow_down.png + + + + + + + Release all devices and servers + + + + + + + :/bin.png:/bin.png + + + + + + + Refresh list of devices and servers in use + + + + + + + :/recycle.png:/recycle.png + + + + + + + Remove all devices + + + + + + + :/sweep.png:/sweep.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 16777215 + 140 + + + + List of devices/servers in use + + + + + + + + + + + Serial devices + + + + + + + Use serial device + + + + + + + :/arrow_up.png:/arrow_up.png + + + + + + + Use all serial devices + + + + + + + :/double_arrow_up.png:/double_arrow_up.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 16777215 + 140 + + + + List of available AMBE serial devices available + + + + + + + + + + + ... + + + + + + + + + + + RollupContents + QWidget +
    gui/rollupcontents.h
    + 1 +
    +
    + + + + +
    diff --git a/plugins/feature/ambe/ambeplugin.cpp b/plugins/feature/ambe/ambeplugin.cpp new file mode 100644 index 000000000..7157cb2cd --- /dev/null +++ b/plugins/feature/ambe/ambeplugin.cpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "ambegui.h" +#endif +#include "ambe.h" +#include "ambeplugin.h" +// #include "simplepttwebapiadapter.h" + +const PluginDescriptor AMBEPlugin::m_pluginDescriptor = { + AMBE::m_featureId, + QStringLiteral("AMBE Controller"), + QStringLiteral("7.2.0"), + QStringLiteral("(c) Edouard Griffiths, F4EXB"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +AMBEPlugin::AMBEPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(nullptr) +{ +} + +const PluginDescriptor& AMBEPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void AMBEPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register Simple PTT feature + m_pluginAPI->registerFeature(AMBE::m_featureIdURI, AMBE::m_featureId, this); +} + +#ifdef SERVER_MODE +FeatureGUI* AMBEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + (void) featureUISet; + (void) feature; + return nullptr; +} +#else +FeatureGUI* AMBEPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + return AMBEGUI::create(m_pluginAPI, featureUISet, feature); +} +#endif + +Feature* AMBEPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const +{ + return new AMBE(webAPIAdapterInterface); +} + +FeatureWebAPIAdapter* AMBEPlugin::createFeatureWebAPIAdapter() const +{ + return nullptr; // TODO new SimplePTTWebAPIAdapter(); +} diff --git a/plugins/feature/ambe/ambeplugin.h b/plugins/feature/ambe/ambeplugin.h new file mode 100644 index 000000000..2d95587b7 --- /dev/null +++ b/plugins/feature/ambe/ambeplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AMBEPLUGIN_H +#define INCLUDE_FEATURE_AMBEPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class FeatureGUI; +class WebAPIAdapterInterface; + +class AMBEPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.feature.ambe") + +public: + explicit AMBEPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const; + virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const; + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_FEATURE_AMBEPLUGIN_H diff --git a/plugins/feature/ambe/ambesettings.cpp b/plugins/feature/ambe/ambesettings.cpp new file mode 100644 index 000000000..13610c10e --- /dev/null +++ b/plugins/feature/ambe/ambesettings.cpp @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" + +#include "ambesettings.h" + +AMBESettings::AMBESettings() : + m_rollupState(nullptr) +{ + resetToDefaults(); +} + +void AMBESettings::resetToDefaults() +{ + m_title = "AMBE Controller"; + m_rgbColor = QColor(255, 0, 0).rgb(); + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIFeatureSetIndex = 0; + m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; +} + +QByteArray AMBESettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeString(1, m_title); + s.writeU32(2, m_rgbColor); + s.writeBool(7, m_useReverseAPI); + s.writeString(8, m_reverseAPIAddress); + s.writeU32(9, m_reverseAPIPort); + s.writeU32(10, m_reverseAPIFeatureSetIndex); + s.writeU32(11, m_reverseAPIFeatureIndex); + + if (m_rollupState) { + s.writeBlob(12, m_rollupState->serialize()); + } + + s.writeS32(18, m_workspaceIndex); + s.writeBlob(19, m_geometryBytes); + + return s.final(); +} + +bool AMBESettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + uint32_t utmp; + QString strtmp; + + d.readString(1, &m_title, "Simple PTT"); + d.readU32(2, &m_rgbColor, QColor(255, 0, 0).rgb()); + d.readBool(7, &m_useReverseAPI, false); + d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(9, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(10, &utmp, 0); + m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp; + d.readU32(11, &utmp, 0); + m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; + + if (m_rollupState) + { + d.readBlob(12, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + + d.readS32(18, &m_workspaceIndex, 0); + d.readBlob(19, &m_geometryBytes); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/feature/ambe/ambesettings.h b/plugins/feature/ambe/ambesettings.h new file mode 100644 index 000000000..5201a2c8f --- /dev/null +++ b/plugins/feature/ambe/ambesettings.h @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AMBESETTINGS_H_ +#define INCLUDE_FEATURE_AMBESETTINGS_H_ + +#include +#include + +class Serializable; + +struct AMBESettings +{ + QString m_title; + quint32 m_rgbColor; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIFeatureSetIndex; + uint16_t m_reverseAPIFeatureIndex; + Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + + AMBESettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } +}; + +#endif // INCLUDE_FEATURE_AMBESETTINGS_H_ diff --git a/plugins/feature/ambe/ambeworker.cpp b/plugins/feature/ambe/ambeworker.cpp new file mode 100644 index 000000000..37713daba --- /dev/null +++ b/plugins/feature/ambe/ambeworker.cpp @@ -0,0 +1,232 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 "audio/audiofifo.h" +#include "ambeworker.h" + +MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgMbeDecode, Message) +MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgTest, Message) + +AMBEWorker::AMBEWorker() : + m_running(false), + m_currentGainIn(0), + m_currentGainOut(0), + m_upsamplerLastValue(0.0f), + m_phase(0), + m_upsampling(1), + m_volume(1.0f) +{ + m_audioBuffer.resize(48000); + m_audioBufferFill = 0; + m_audioFifo = 0; + std::fill(m_dvAudioSamples, m_dvAudioSamples+SerialDV::MBE_AUDIO_BLOCK_SIZE, 0); + setVolumeFactors(); +} + +AMBEWorker::~AMBEWorker() +{} + +bool AMBEWorker::open(const std::string& deviceRef) +{ + return m_dvController.open(deviceRef); +} + +void AMBEWorker::close() +{ + m_dvController.close(); +} + +void AMBEWorker::process() +{ + m_running = true; + qDebug("AMBEWorker::process: started"); + + while (m_running) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + qDebug("AMBEWorker::process: stopped"); + emit finished(); +} + +void AMBEWorker::stop() +{ + m_running = false; +} + +void AMBEWorker::handleInputMessages() +{ + Message* message; + m_audioBufferFill = 0; + AudioFifo *audioFifo = 0; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (MsgMbeDecode::match(*message)) + { + MsgMbeDecode *decodeMsg = (MsgMbeDecode *) message; + int dBVolume = (decodeMsg->getVolumeIndex() - 30) / 4; + float volume = pow(10.0, dBVolume / 10.0f); + int upsampling = decodeMsg->getUpsampling(); + upsampling = upsampling > 6 ? 6 : upsampling < 1 ? 1 : upsampling; + + if ((volume != m_volume) || (upsampling != m_upsampling)) + { + m_volume = volume; + m_upsampling = upsampling; + setVolumeFactors(); + } + + m_upsampleFilter.useHP(decodeMsg->getUseHP()); + + if (m_dvController.decode(m_dvAudioSamples, decodeMsg->getMbeFrame(), decodeMsg->getMbeRate())) + { + if (upsampling > 1) { + upsample(upsampling, m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels()); + } else { + noUpsample(m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels()); + } + + audioFifo = decodeMsg->getAudioFifo(); + + if (audioFifo && (m_audioBufferFill >= m_audioBuffer.size() - 960)) + { + uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + } + else + { + qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: decode failed"); + } + } + + delete message; + + if (m_inputMessageQueue.size() > 100) + { + qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: too many messages in queue. Flushing..."); + m_inputMessageQueue.clear(); + break; + } + } + + if (audioFifo) + { + uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + + m_timestamp = QDateTime::currentDateTime(); +} + +void AMBEWorker::pushMbeFrame(const unsigned char *mbeFrame, + int mbeRateIndex, + int mbeVolumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo) +{ + m_audioFifo = audioFifo; + m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo)); +} + +bool AMBEWorker::isAvailable() +{ + if (m_audioFifo == 0) { + return true; + } + + return m_timestamp.time().msecsTo(QDateTime::currentDateTime().time()) > 1000; // 1 second inactivity timeout +} + +bool AMBEWorker::hasFifo(AudioFifo *audioFifo) +{ + return m_audioFifo == audioFifo; +} + +void AMBEWorker::upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels) +{ + for (int i = 0; i < nbSamplesIn; i++) + { + //float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) m_compressor.compress(in[i])) : (float) m_compressor.compress(in[i]); + float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i]; + float prev = m_upsamplerLastValue; + qint16 upsample; + + for (int j = 1; j <= upsampling; j++) + { + upsample = (qint16) m_upsampleFilter.runLP(cur*m_upsamplingFactors[j] + prev*m_upsamplingFactors[upsampling-j]); + m_audioBuffer[m_audioBufferFill].l = channels & 1 ? m_compressor.compress(upsample) : 0; + m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? m_compressor.compress(upsample) : 0; + + if (m_audioBufferFill < m_audioBuffer.size() - 1) { + ++m_audioBufferFill; + } + } + + m_upsamplerLastValue = cur; + } + + if (m_audioBufferFill >= m_audioBuffer.size() - 1) { + qDebug("AMBEWorker::upsample(%d): audio buffer is full check its size", upsampling); + } +} + +void AMBEWorker::noUpsample(short *in, int nbSamplesIn, unsigned char channels) +{ + for (int i = 0; i < nbSamplesIn; i++) + { + float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i]; + m_audioBuffer[m_audioBufferFill].l = channels & 1 ? cur*m_upsamplingFactors[0] : 0; + m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? cur*m_upsamplingFactors[0] : 0; + + if (m_audioBufferFill < m_audioBuffer.size() - 1) { + ++m_audioBufferFill; + } + } + + if (m_audioBufferFill >= m_audioBuffer.size() - 1) { + qDebug("AMBEWorker::noUpsample: audio buffer is full check its size"); + } +} + +void AMBEWorker::setVolumeFactors() +{ + m_upsamplingFactors[0] = m_volume; + + for (int i = 1; i <= m_upsampling; i++) { + m_upsamplingFactors[i] = (i*m_volume) / (float) m_upsampling; + } +} diff --git a/plugins/feature/ambe/ambeworker.h b/plugins/feature/ambe/ambeworker.h new file mode 100644 index 000000000..e6136c139 --- /dev/null +++ b/plugins/feature/ambe/ambeworker.h @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 SDRBASE_AMBE_AMBEWORKER_H_ +#define SDRBASE_AMBE_AMBEWORKER_H_ + +#include +#include +#include + +#include "export.h" +#include "dvcontroller.h" + +#include "util/messagequeue.h" +#include "util/message.h" +#include "dsp/filtermbe.h" +#include "dsp/dsptypes.h" +#include "audio/audiocompressor.h" + +class AudioFifo; + +class SDRBASE_API AMBEWorker : public QObject { + Q_OBJECT +public: + class MsgTest : public Message + { + MESSAGE_CLASS_DECLARATION + public: + static MsgTest* create() { return new MsgTest(); } + private: + MsgTest() {} + }; + + class MsgMbeDecode : public Message + { + MESSAGE_CLASS_DECLARATION + public: + const unsigned char *getMbeFrame() const { return m_mbeFrame; } + SerialDV::DVRate getMbeRate() const { return m_mbeRate; } + int getVolumeIndex() const { return m_volumeIndex; } + unsigned char getChannels() const { return m_channels % 4; } + bool getUseHP() const { return m_useHP; } + int getUpsampling() const { return m_upsampling; } + AudioFifo *getAudioFifo() { return m_audioFifo; } + + static MsgMbeDecode* create( + const unsigned char *mbeFrame, + int mbeRateIndex, + int volumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo) + { + return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, useHP, upsampling, audioFifo); + } + + private: + unsigned char m_mbeFrame[SerialDV::MBE_FRAME_MAX_LENGTH_BYTES]; + SerialDV::DVRate m_mbeRate; + int m_volumeIndex; + unsigned char m_channels; + bool m_useHP; + int m_upsampling; + AudioFifo *m_audioFifo; + + MsgMbeDecode(const unsigned char *mbeFrame, + SerialDV::DVRate mbeRate, + int volumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo) : + Message(), + m_mbeRate(mbeRate), + m_volumeIndex(volumeIndex), + m_channels(channels), + m_useHP(useHP), + m_upsampling(upsampling), + m_audioFifo(audioFifo) + { + memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::DVController::getNbMbeBytes(m_mbeRate)); + } + }; + + AMBEWorker(); + ~AMBEWorker(); + + void pushMbeFrame(const unsigned char *mbeFrame, + int mbeRateIndex, + int mbeVolumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo); + + bool open(const std::string& deviceRef); //!< Either serial device or ip:port + void close(); + void process(); + void stop(); + bool isAvailable(); + bool hasFifo(AudioFifo *audioFifo); + + void postTest() + { + //emit inputMessageReady(); + m_inputMessageQueue.push(MsgTest::create()); + } + + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + +signals: + void finished(); + +public slots: + void handleInputMessages(); + +private: + void upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels); + void noUpsample(short *in, int nbSamplesIn, unsigned char channels); + void setVolumeFactors(); + + SerialDV::DVController m_dvController; + AudioFifo *m_audioFifo; + QDateTime m_timestamp; + volatile bool m_running; + int m_currentGainIn; + int m_currentGainOut; + short m_dvAudioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE]; + AudioVector m_audioBuffer; + uint m_audioBufferFill; + float m_upsamplerLastValue; + float m_phase; + MBEAudioInterpolatorFilter m_upsampleFilter; + int m_upsampling; + float m_volume; + float m_upsamplingFactors[7]; + AudioCompressor m_compressor; +}; + +#endif // SDRBASE_AMBE_AMBEWORKER_H_ diff --git a/sdrbase/dsp/dspcommands.cpp b/sdrbase/dsp/dspcommands.cpp index 1ef8bbe9a..ba65aeef0 100644 --- a/sdrbase/dsp/dspcommands.cpp +++ b/sdrbase/dsp/dspcommands.cpp @@ -44,3 +44,4 @@ MESSAGE_CLASS_DEFINITION(DSPSignalNotification, Message) MESSAGE_CLASS_DEFINITION(DSPMIMOSignalNotification, Message) MESSAGE_CLASS_DEFINITION(DSPConfigureChannelizer, Message) MESSAGE_CLASS_DEFINITION(DSPConfigureAudio, Message) +MESSAGE_CLASS_DEFINITION(DSPPushMbeFrame, Message) diff --git a/sdrbase/dsp/dspcommands.h b/sdrbase/dsp/dspcommands.h index eda9ccc0e..9553ce2e6 100644 --- a/sdrbase/dsp/dspcommands.h +++ b/sdrbase/dsp/dspcommands.h @@ -345,4 +345,45 @@ private: AudioType m_autioType; }; +class SDRBASE_API DSPPushMbeFrame : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPPushMbeFrame( + const unsigned char *mbeFrame, + int mbeRateIndex, + int mbeVolumeIndex, + unsigned char channels, + bool useHP, + int upsampling, + AudioFifo *audioFifo + ) : + Message(), + m_mbeFrame(mbeFrame), + m_mbeRateIndex(mbeRateIndex), + m_mbeVolumeIndex(mbeVolumeIndex), + m_channels(channels), + m_useHP(useHP), + m_upsampling(upsampling), + m_audioFifo(audioFifo) + { } + + const unsigned char * getMbeFrame() const { return m_mbeFrame; } + int getMbeRateIndex() const { return m_mbeRateIndex; } + int getMbeVolumeIndex() const { return m_mbeVolumeIndex; } + unsigned char getChannels() const { return m_channels; } + bool getUseHP() const { return m_useHP; } + int getUpsampling() const { return m_upsampling; } + AudioFifo *getAudioFifo() const { return m_audioFifo; } + +private: + const unsigned char *m_mbeFrame; + int m_mbeRateIndex; + int m_mbeVolumeIndex; + unsigned char m_channels; + bool m_useHP; + int m_upsampling; + AudioFifo *m_audioFifo; +}; + #endif // INCLUDE_DSPCOMMANDS_H From 713e0299ab9465413e628ffcfd8e971292eefc70 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 24 May 2022 15:30:53 +0200 Subject: [PATCH 12/29] DSD demod: direct call to AMBE feature handle message method --- plugins/channelrx/demoddsd/dsddemod.cpp | 8 +-- plugins/channelrx/demoddsd/dsddemodbaseband.h | 3 +- plugins/channelrx/demoddsd/dsddemodsink.cpp | 63 +++++++------------ plugins/channelrx/demoddsd/dsddemodsink.h | 5 +- 4 files changed, 33 insertions(+), 46 deletions(-) diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 980786713..e2fc815db 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -319,13 +319,13 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) for (const auto& feature : m_availableAMBEFeatures) { if (feature.m_featureIndex == settings.m_ambeFeatureIndex) { - m_basebandSink->setAMBEFeatureMessageQueue(feature.m_feature->getInputMessageQueue()); + m_basebandSink->setAMBEFeature(feature.m_feature); } } } else { - m_basebandSink->setAMBEFeatureMessageQueue(nullptr); + m_basebandSink->setAMBEFeature(nullptr); } } @@ -880,7 +880,7 @@ void DSDDemod::handleFeatureAdded(int featureSetIndex, Feature *feature) m_availableAMBEFeatures[feature] = DSDDemodSettings::AvailableAMBEFeature{feature->getIndexInFeatureSet(), feature}; if (m_settings.m_connectAMBE && (m_settings.m_ambeFeatureIndex == feature->getIndexInFeatureSet())) { - m_basebandSink->setAMBEFeatureMessageQueue(feature->getInputMessageQueue()); + m_basebandSink->setAMBEFeature(feature); } notifyUpdateAMBEFeatures(); @@ -898,7 +898,7 @@ void DSDDemod::handleFeatureRemoved(int featureSetIndex, Feature *feature) if (m_settings.m_ambeFeatureIndex == m_availableAMBEFeatures[feature].m_featureIndex) { m_settings.m_connectAMBE = false; - m_basebandSink->setAMBEFeatureMessageQueue(nullptr); + m_basebandSink->setAMBEFeature(nullptr); } m_availableAMBEFeatures.remove(feature); diff --git a/plugins/channelrx/demoddsd/dsddemodbaseband.h b/plugins/channelrx/demoddsd/dsddemodbaseband.h index e96df9e97..7f11382c6 100644 --- a/plugins/channelrx/demoddsd/dsddemodbaseband.h +++ b/plugins/channelrx/demoddsd/dsddemodbaseband.h @@ -29,6 +29,7 @@ class DownChannelizer; class ChannelAPI; +class Feature; class DSDDemodBaseband : public QObject { @@ -75,7 +76,7 @@ public: void setChannel(ChannelAPI *channel); void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); } - void setAMBEFeatureMessageQueue(MessageQueue *ambeFeatureMessageQueue) { m_sink.setAmbeFeatureMessageQueue(ambeFeatureMessageQueue); } + void setAMBEFeature(Feature *ambeFeature) { m_sink.setAmbeFeature(ambeFeature); } private: SampleSinkFifo m_sampleFifo; diff --git a/plugins/channelrx/demoddsd/dsddemodsink.cpp b/plugins/channelrx/demoddsd/dsddemodsink.cpp index e91af3932..29a0a7ba9 100644 --- a/plugins/channelrx/demoddsd/dsddemodsink.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsink.cpp @@ -35,6 +35,7 @@ #include "dsp/basebandsamplesink.h" #include "dsp/datafifo.h" #include "dsp/dspcommands.h" +#include "feature/feature.h" #include "audio/audiooutputdevice.h" #include "util/db.h" #include "util/messagequeue.h" @@ -45,7 +46,7 @@ DSDDemodSink::DSDDemodSink() : m_channelSampleRate(48000), m_channelFrequencyOffset(0), - m_ambeFeatureMessageQueue(nullptr), + m_ambeFeature(nullptr), m_audioSampleRate(48000), m_interpolatorDistance(0.0f), m_interpolatorDistanceRemain(0.0f), @@ -232,31 +233,23 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV } // if (DSPEngine::instance()->hasDVSerialSupport()) - if (m_ambeFeatureMessageQueue) + if (m_ambeFeature) { if ((m_settings.m_slot1On) && m_dsdDecoder.mbeDVReady1()) { if (!m_settings.m_audioMute) { - m_ambeFeatureMessageQueue->push( - new DSPPushMbeFrame( - m_dsdDecoder.getMbeDVFrame1(), - m_dsdDecoder.getMbeRateIndex(), - m_settings.m_volume * 10.0, - m_settings.m_tdmaStereo ? 1 : 3, // left or both channels - m_settings.m_highPassFilter, - m_audioSampleRate/8000, // upsample from native 8k - &m_audioFifo1 - ) + DSPPushMbeFrame *msg = new DSPPushMbeFrame( + m_dsdDecoder.getMbeDVFrame1(), + m_dsdDecoder.getMbeRateIndex(), + m_settings.m_volume * 10.0, + m_settings.m_tdmaStereo ? 1 : 3, // left or both channels + m_settings.m_highPassFilter, + m_audioSampleRate/8000, // upsample from native 8k + &m_audioFifo1 ); - // DSPEngine::instance()->pushMbeFrame( - // m_dsdDecoder.getMbeDVFrame1(), - // m_dsdDecoder.getMbeRateIndex(), - // m_settings.m_volume * 10.0, - // m_settings.m_tdmaStereo ? 1 : 3, // left or both channels - // m_settings.m_highPassFilter, - // m_audioSampleRate/8000, // upsample from native 8k - // &m_audioFifo1); + m_ambeFeature->handleMessage(*msg); + delete msg; } m_dsdDecoder.resetMbeDV1(); @@ -266,25 +259,17 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV { if (!m_settings.m_audioMute) { - m_ambeFeatureMessageQueue->push( - new DSPPushMbeFrame( - m_dsdDecoder.getMbeDVFrame2(), - m_dsdDecoder.getMbeRateIndex(), - m_settings.m_volume * 10.0, - m_settings.m_tdmaStereo ? 2 : 3, // right or both channels - m_settings.m_highPassFilter, - m_audioSampleRate/8000, // upsample from native 8k - &m_audioFifo2 - ) + DSPPushMbeFrame *msg = new DSPPushMbeFrame( + m_dsdDecoder.getMbeDVFrame2(), + m_dsdDecoder.getMbeRateIndex(), + m_settings.m_volume * 10.0, + m_settings.m_tdmaStereo ? 2 : 3, // right or both channels + m_settings.m_highPassFilter, + m_audioSampleRate/8000, // upsample from native 8k + &m_audioFifo2 ); - // DSPEngine::instance()->pushMbeFrame( - // m_dsdDecoder.getMbeDVFrame2(), - // m_dsdDecoder.getMbeRateIndex(), - // m_settings.m_volume * 10.0, - // m_settings.m_tdmaStereo ? 2 : 3, // right or both channels - // m_settings.m_highPassFilter, - // m_audioSampleRate/8000, // upsample from native 8k - // &m_audioFifo2); + m_ambeFeature->handleMessage(*msg); + delete msg; } m_dsdDecoder.resetMbeDV2(); @@ -295,7 +280,7 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV } } - if (!m_ambeFeatureMessageQueue) + if (!m_ambeFeature) // if (!DSPEngine::instance()->hasDVSerialSupport()) { if (m_settings.m_slot1On) diff --git a/plugins/channelrx/demoddsd/dsddemodsink.h b/plugins/channelrx/demoddsd/dsddemodsink.h index dd4dbdfc5..5eaa7ec6a 100644 --- a/plugins/channelrx/demoddsd/dsddemodsink.h +++ b/plugins/channelrx/demoddsd/dsddemodsink.h @@ -36,6 +36,7 @@ class BasebandSampleSink; class ChannelAPI; +class Feature; class DSDDemodSink : public ChannelSampleSink { public: @@ -83,7 +84,7 @@ public: } const char *updateAndGetStatusText(); - void setAmbeFeatureMessageQueue(MessageQueue *queue) { m_ambeFeatureMessageQueue = queue; } + void setAmbeFeature(Feature *feature) { m_ambeFeature = feature; } private: struct MagSqLevelsStore @@ -115,7 +116,7 @@ private: int m_channelFrequencyOffset; DSDDemodSettings m_settings; ChannelAPI *m_channel; - MessageQueue *m_ambeFeatureMessageQueue; + Feature *m_ambeFeature; int m_audioSampleRate; QVector m_demodBuffer; int m_demodBufferFill; From 6d18d6358a94ebd43e3198992274bb7d8b2e4713 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 24 May 2022 17:14:22 +0200 Subject: [PATCH 13/29] AMBE feature: changes to DSD demod API --- plugins/channelrx/demoddsd/dsddemod.cpp | 14 ++++++ sdrbase/resources/webapi/doc/html2/index.html | 10 +++- .../webapi/doc/swagger/include/DSDDemod.yaml | 10 +++- .../api/swagger/include/DSDDemod.yaml | 10 +++- swagger/sdrangel/code/html2/index.html | 10 +++- .../code/qt5/client/SWGDSDDemodSettings.cpp | 46 +++++++++++++++++++ .../code/qt5/client/SWGDSDDemodSettings.h | 12 +++++ 7 files changed, 108 insertions(+), 4 deletions(-) diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index e2fc815db..45c93b3b7 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -566,6 +566,12 @@ void DSDDemod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { settings.m_reverseAPIChannelIndex = response.getDsdDemodSettings()->getReverseApiChannelIndex(); } + if (channelSettingsKeys.contains("ambeFeatureIndex")) { + settings.m_ambeFeatureIndex = response.getDsdDemodSettings()->getAmbeFeatureIndex(); + } + if (channelSettingsKeys.contains("connectAMBE")) { + settings.m_connectAMBE = response.getDsdDemodSettings()->getConnectAmbe() != 0; + } if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getDsdDemodSettings()->getChannelMarker()); } @@ -632,6 +638,8 @@ void DSDDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp response.getDsdDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); response.getDsdDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); response.getDsdDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + response.getDsdDemodSettings()->setAmbeFeatureIndex(settings.m_ambeFeatureIndex); + response.getDsdDemodSettings()->setConnectAmbe(settings.m_connectAMBE ? 1 : 0); if (settings.m_channelMarker) { @@ -818,6 +826,12 @@ void DSDDemod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("streamIndex") || force) { swgDSDDemodSettings->setStreamIndex(settings.m_streamIndex); } + if (channelSettingsKeys.contains("ambeFeatureIndex") || force) { + swgDSDDemodSettings->setAmbeFeatureIndex(settings.m_ambeFeatureIndex); + } + if (channelSettingsKeys.contains("connectAMBE") || force) { + swgDSDDemodSettings->setConnectAmbe(settings.m_connectAMBE ? 1 : 0); + } if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) { diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 474dfdf12..a74ba0c83 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -4605,6 +4605,14 @@ margin-bottom: 20px; }, "rollupState" : { "$ref" : "#/definitions/RollupState" + }, + "ambeFeatureIndex" : { + "type" : "integer", + "description" : "index of AMBE controller feature to be used for AMBE frames decoding" + }, + "connectAMBE" : { + "type" : "integer", + "description" : "Decode frames with AMBE feature\n * 0 - Do not decode frames with AMBE feature\n * 1 - Decode frames with AMBE feature\n" } }, "description" : "DSDDemod" @@ -57761,7 +57769,7 @@ except ApiException as e:
    - Generated 2022-05-22T20:12:13.506+02:00 + Generated 2022-05-24T15:35:23.001+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml index ec315e4f8..d61367309 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml @@ -72,7 +72,15 @@ DSDDemodSettings: $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" rollupState: $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" - + ambeFeatureIndex: + type: integer + description: index of AMBE controller feature to be used for AMBE frames decoding + connectAMBE: + type: integer + description: > + Decode frames with AMBE feature + * 0 - Do not decode frames with AMBE feature + * 1 - Decode frames with AMBE feature DSDDemodReport: description: DSDDemod properties: diff --git a/swagger/sdrangel/api/swagger/include/DSDDemod.yaml b/swagger/sdrangel/api/swagger/include/DSDDemod.yaml index 5e8e184aa..d0c87f99c 100644 --- a/swagger/sdrangel/api/swagger/include/DSDDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/DSDDemod.yaml @@ -72,7 +72,15 @@ DSDDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker" rollupState: $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" - + ambeFeatureIndex: + type: integer + description: index of AMBE controller feature to be used for AMBE frames decoding + connectAMBE: + type: integer + description: > + Decode frames with AMBE feature + * 0 - Do not decode frames with AMBE feature + * 1 - Decode frames with AMBE feature DSDDemodReport: description: DSDDemod properties: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 474dfdf12..a74ba0c83 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -4605,6 +4605,14 @@ margin-bottom: 20px; }, "rollupState" : { "$ref" : "#/definitions/RollupState" + }, + "ambeFeatureIndex" : { + "type" : "integer", + "description" : "index of AMBE controller feature to be used for AMBE frames decoding" + }, + "connectAMBE" : { + "type" : "integer", + "description" : "Decode frames with AMBE feature\n * 0 - Do not decode frames with AMBE feature\n * 1 - Decode frames with AMBE feature\n" } }, "description" : "DSDDemod" @@ -57761,7 +57769,7 @@ except ApiException as e:
    - Generated 2022-05-22T20:12:13.506+02:00 + Generated 2022-05-24T15:35:23.001+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp index 87ff0024f..53d735f50 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp @@ -88,6 +88,10 @@ SWGDSDDemodSettings::SWGDSDDemodSettings() { m_channel_marker_isSet = false; rollup_state = nullptr; m_rollup_state_isSet = false; + ambe_feature_index = 0; + m_ambe_feature_index_isSet = false; + connect_ambe = 0; + m_connect_ambe_isSet = false; } SWGDSDDemodSettings::~SWGDSDDemodSettings() { @@ -156,6 +160,10 @@ SWGDSDDemodSettings::init() { m_channel_marker_isSet = false; rollup_state = new SWGRollupState(); m_rollup_state_isSet = false; + ambe_feature_index = 0; + m_ambe_feature_index_isSet = false; + connect_ambe = 0; + m_connect_ambe_isSet = false; } void @@ -200,6 +208,8 @@ SWGDSDDemodSettings::cleanup() { if(rollup_state != nullptr) { delete rollup_state; } + + } SWGDSDDemodSettings* @@ -273,6 +283,10 @@ SWGDSDDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + ::SWGSDRangel::setValue(&ambe_feature_index, pJson["ambeFeatureIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&connect_ambe, pJson["connectAMBE"], "qint32", ""); + } QString @@ -379,6 +393,12 @@ SWGDSDDemodSettings::asJsonObject() { if((rollup_state != nullptr) && (rollup_state->isSet())){ toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); } + if(m_ambe_feature_index_isSet){ + obj->insert("ambeFeatureIndex", QJsonValue(ambe_feature_index)); + } + if(m_connect_ambe_isSet){ + obj->insert("connectAMBE", QJsonValue(connect_ambe)); + } return obj; } @@ -683,6 +703,26 @@ SWGDSDDemodSettings::setRollupState(SWGRollupState* rollup_state) { this->m_rollup_state_isSet = true; } +qint32 +SWGDSDDemodSettings::getAmbeFeatureIndex() { + return ambe_feature_index; +} +void +SWGDSDDemodSettings::setAmbeFeatureIndex(qint32 ambe_feature_index) { + this->ambe_feature_index = ambe_feature_index; + this->m_ambe_feature_index_isSet = true; +} + +qint32 +SWGDSDDemodSettings::getConnectAmbe() { + return connect_ambe; +} +void +SWGDSDDemodSettings::setConnectAmbe(qint32 connect_ambe) { + this->connect_ambe = connect_ambe; + this->m_connect_ambe_isSet = true; +} + bool SWGDSDDemodSettings::isSet(){ @@ -778,6 +818,12 @@ SWGDSDDemodSettings::isSet(){ if(rollup_state && rollup_state->isSet()){ isObjectUpdated = true; break; } + if(m_ambe_feature_index_isSet){ + isObjectUpdated = true; break; + } + if(m_connect_ambe_isSet){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h index 187574bbe..5e5a9b628 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h @@ -134,6 +134,12 @@ public: SWGRollupState* getRollupState(); void setRollupState(SWGRollupState* rollup_state); + qint32 getAmbeFeatureIndex(); + void setAmbeFeatureIndex(qint32 ambe_feature_index); + + qint32 getConnectAmbe(); + void setConnectAmbe(qint32 connect_ambe); + virtual bool isSet() override; @@ -228,6 +234,12 @@ private: SWGRollupState* rollup_state; bool m_rollup_state_isSet; + qint32 ambe_feature_index; + bool m_ambe_feature_index_isSet; + + qint32 connect_ambe; + bool m_connect_ambe_isSet; + }; } From 8885864e7965008740cd9d54d9bd6569d6045230 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 24 May 2022 22:51:29 +0200 Subject: [PATCH 14/29] AMBE feature: implemented API --- plugins/feature/ambe/ambe.cpp | 264 +++++++++++++++++ plugins/feature/ambe/ambe.h | 50 ++++ plugins/feature/ambe/ambeengine.cpp | 68 +---- plugins/feature/ambe/ambeengine.h | 14 +- plugins/feature/ambe/ambegui.cpp | 42 ++- plugins/feature/ambe/ambegui.h | 1 + plugins/feature/ambe/ambegui.ui | 46 ++- plugins/feature/ambe/ambeplugin.cpp | 4 +- plugins/feature/ambe/ambewebapiadapter.cpp | 51 ++++ plugins/feature/ambe/ambewebapiadapter.h | 49 ++++ sdrbase/resources/webapi.qrc | 1 + sdrbase/resources/webapi/doc/html2/index.html | 123 +++++++- .../webapi/doc/swagger/include/AMBE.yaml | 88 ++++++ .../doc/swagger/include/FeatureActions.yaml | 2 + .../doc/swagger/include/FeatureReport.yaml | 2 + .../doc/swagger/include/FeatureSettings.yaml | 2 + sdrbase/webapi/webapirequestmapper.cpp | 5 + sdrbase/webapi/webapiutils.cpp | 3 + .../sdrangel/api/swagger/include/AMBE.yaml | 88 ++++++ .../api/swagger/include/FeatureActions.yaml | 2 + .../api/swagger/include/FeatureReport.yaml | 2 + .../api/swagger/include/FeatureSettings.yaml | 2 + swagger/sdrangel/code/html2/index.html | 123 +++++++- .../code/qt5/client/SWGAMBEActions.cpp | 133 +++++++++ .../sdrangel/code/qt5/client/SWGAMBEActions.h | 65 +++++ .../code/qt5/client/SWGAMBEDevice_2.cpp | 133 +++++++++ .../code/qt5/client/SWGAMBEDevice_2.h | 65 +++++ .../code/qt5/client/SWGAMBEDevices_2.cpp | 137 +++++++++ .../code/qt5/client/SWGAMBEDevices_2.h | 66 +++++ .../code/qt5/client/SWGAMBEReport.cpp | 135 +++++++++ .../sdrangel/code/qt5/client/SWGAMBEReport.h | 66 +++++ .../code/qt5/client/SWGAMBESettings.cpp | 275 ++++++++++++++++++ .../code/qt5/client/SWGAMBESettings.h | 102 +++++++ .../code/qt5/client/SWGDVSerialDevice_2.cpp | 110 +++++++ .../code/qt5/client/SWGDVSerialDevice_2.h | 59 ++++ .../code/qt5/client/SWGDVSerialDevices_2.cpp | 137 +++++++++ .../code/qt5/client/SWGDVSerialDevices_2.h | 66 +++++ .../code/qt5/client/SWGFeatureActions.cpp | 25 ++ .../code/qt5/client/SWGFeatureActions.h | 7 + .../code/qt5/client/SWGFeatureReport.cpp | 25 ++ .../code/qt5/client/SWGFeatureReport.h | 7 + .../code/qt5/client/SWGFeatureSettings.cpp | 25 ++ .../code/qt5/client/SWGFeatureSettings.h | 7 + .../code/qt5/client/SWGModelFactory.h | 42 +++ 44 files changed, 2619 insertions(+), 100 deletions(-) create mode 100644 plugins/feature/ambe/ambewebapiadapter.cpp create mode 100644 plugins/feature/ambe/ambewebapiadapter.h create mode 100644 sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml create mode 100644 swagger/sdrangel/api/swagger/include/AMBE.yaml create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEActions.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEReport.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBESettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBESettings.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h diff --git a/plugins/feature/ambe/ambe.cpp b/plugins/feature/ambe/ambe.cpp index 5a6bb90b4..f62e825f8 100644 --- a/plugins/feature/ambe/ambe.cpp +++ b/plugins/feature/ambe/ambe.cpp @@ -23,6 +23,9 @@ #include "SWGFeatureSettings.h" #include "SWGFeatureReport.h" #include "SWGFeatureActions.h" +#include "SWGDVSerialDevices.h" +#include "SWGDVSerialDevice.h" +#include "SWGAMBEDevices.h" #include "settings/serializable.h" #include "util/simpleserializer.h" @@ -31,6 +34,7 @@ #include "ambe.h" MESSAGE_CLASS_DEFINITION(AMBE::MsgConfigureAMBE, Message) +MESSAGE_CLASS_DEFINITION(AMBE::MsgReportDevices, Message) const char* const AMBE::m_featureIdURI = "sdrangel.feature.ambe"; const char* const AMBE::m_featureId = "AMBE"; @@ -168,3 +172,263 @@ void AMBE::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } + +int AMBE::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAmbeSettings(new SWGSDRangel::SWGAMBESettings()); + response.getAmbeSettings()->init(); + webapiFormatFeatureSettings(response, m_settings); + return 200; +} + +int AMBE::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + AMBESettings settings = m_settings; + webapiUpdateFeatureSettings(settings, featureSettingsKeys, response); + + MsgConfigureAMBE *msg = MsgConfigureAMBE::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureAMBE *msgToGUI = MsgConfigureAMBE::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatFeatureSettings(response, settings); + + return 200; +} + +int AMBE::webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAmbeReport(new SWGSDRangel::SWGAMBEReport()); + response.getAmbeReport()->init(); + webapiFormatFeatureReport(response); + return 200; +} + +void AMBE::webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const AMBESettings& settings) +{ + if (response.getAmbeSettings()->getTitle()) { + *response.getAmbeSettings()->getTitle() = settings.m_title; + } else { + response.getAmbeSettings()->setTitle(new QString(settings.m_title)); + } + + response.getAmbeSettings()->setRgbColor(settings.m_rgbColor); + response.getAmbeSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAmbeSettings()->getReverseApiAddress()) { + *response.getAmbeSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAmbeSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAmbeSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAmbeSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex); + response.getAmbeSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex); + + if (settings.m_rollupState) + { + if (response.getAmbeSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.getAmbeSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.getAmbeSettings()->setRollupState(swgRollupState); + } + } +} + +void AMBE::webapiUpdateFeatureSettings( + AMBESettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response) +{ + if (featureSettingsKeys.contains("title")) { + settings.m_title = *response.getAmbeSettings()->getTitle(); + } + if (featureSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getAmbeSettings()->getRgbColor(); + } + if (featureSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAmbeSettings()->getUseReverseApi() != 0; + } + if (featureSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAmbeSettings()->getReverseApiAddress(); + } + if (featureSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAmbeSettings()->getReverseApiPort(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) { + settings.m_reverseAPIFeatureSetIndex = response.getAmbeSettings()->getReverseApiFeatureSetIndex(); + } + if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) { + settings.m_reverseAPIFeatureIndex = response.getAmbeSettings()->getReverseApiFeatureIndex(); + } + if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(featureSettingsKeys, response.getAmbeSettings()->getRollupState()); + } +} + +void AMBE::webapiReverseSendSettings(QList& featureSettingsKeys, const AMBESettings& settings, bool force) +{ + SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings(); + // swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet()); + // swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex()); + swgFeatureSettings->setFeatureType(new QString("AMBE")); + swgFeatureSettings->setAmbeSettings(new SWGSDRangel::SWGAMBESettings()); + SWGSDRangel::SWGAMBESettings *swgAMBESettings = swgFeatureSettings->getAmbeSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (featureSettingsKeys.contains("title") || force) { + swgAMBESettings->setTitle(new QString(settings.m_title)); + } + if (featureSettingsKeys.contains("rgbColor") || force) { + swgAMBESettings->setRgbColor(settings.m_rgbColor); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIFeatureSetIndex) + .arg(settings.m_reverseAPIFeatureIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgFeatureSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgFeatureSettings; +} + +void AMBE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) +{ + // serial + + SWGSDRangel::SWGDVSerialDevices_2 *swgDVSerialDevices = response.getAmbeReport()->getSerial(); + swgDVSerialDevices->init(); + + QList qDeviceNames; + getAMBEEngine()->scan(qDeviceNames); + swgDVSerialDevices->setNbDevices((int) qDeviceNames.size()); + QList *serialDeviceNamesList = swgDVSerialDevices->getDvSerialDevices(); + + for (const auto& deviceName : qDeviceNames) + { + serialDeviceNamesList->append(new SWGSDRangel::SWGDVSerialDevice); + serialDeviceNamesList->back()->init(); + *serialDeviceNamesList->back()->getDeviceName() = deviceName; + } + + // devices + + SWGSDRangel::SWGAMBEDevices_2 *swgAMBEDevices = response.getAmbeReport()->getDevices(); + swgAMBEDevices->init(); + + qDeviceNames.clear(); + getAMBEEngine()->getDeviceRefs(qDeviceNames); + swgAMBEDevices->setNbDevices((int) qDeviceNames.size()); + QList *ambeDeviceNamesList = swgAMBEDevices->getAmbeDevices(); + + for (const auto& deviceName : qDeviceNames) + { + ambeDeviceNamesList->append(new SWGSDRangel::SWGAMBEDevice); + ambeDeviceNamesList->back()->init(); + *ambeDeviceNamesList->back()->getDeviceRef() = deviceName; + ambeDeviceNamesList->back()->setDelete(0); + } +} + +int AMBE::webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage) +{ + SWGSDRangel::SWGAMBEActions *swgAMBEActions = query.getAmbeActions(); + + if (swgAMBEActions) + { + bool unknownAction = true; + + if (featureActionsKeys.contains("removeAll") && (swgAMBEActions->getRemoveAll() != 0)) + { + unknownAction = false; + getAMBEEngine()->releaseAll(); + + if (getMessageQueueToGUI()) + { + MsgReportDevices *msg = MsgReportDevices::create(); + getAMBEEngine()->scan(msg->getAvailableDevices()); + getAMBEEngine()->getDeviceRefs(msg->getUsedDevices()); + getMessageQueueToGUI()->push(msg); + } + } + + if (featureActionsKeys.contains("updateDevices")) + { + unknownAction = false; + bool updated = false; + SWGSDRangel::SWGAMBEDevices_2 *swgAMBEDevices = swgAMBEActions->getUpdateDevices(); + QList *ambeList = swgAMBEDevices->getAmbeDevices(); + + for (QList::const_iterator it = ambeList->begin(); it != ambeList->end(); ++it) + { + updated = true; + + if ((*it)->getDelete() != 0) { + getAMBEEngine()->releaseController((*it)->getDeviceRef()->toStdString()); + } else { + getAMBEEngine()->registerController((*it)->getDeviceRef()->toStdString()); + } + } + + if (updated && getMessageQueueToGUI()) + { + MsgReportDevices *msg = MsgReportDevices::create(); + getAMBEEngine()->scan(msg->getAvailableDevices()); + getAMBEEngine()->getDeviceRefs(msg->getUsedDevices()); + getMessageQueueToGUI()->push(msg); + } + } + + if (unknownAction) + { + errorMessage = "Unknown action"; + return 400; + } + else + { + return 202; + } + } + else + { + errorMessage = "Missing AMBEActions in query"; + return 400; + } +} diff --git a/plugins/feature/ambe/ambe.h b/plugins/feature/ambe/ambe.h index a456e4a34..6e049101c 100644 --- a/plugins/feature/ambe/ambe.h +++ b/plugins/feature/ambe/ambe.h @@ -57,6 +57,26 @@ public: { } }; + class MsgReportDevices : public Message { + MESSAGE_CLASS_DECLARATION + + public: + QList& getAvailableDevices() { return m_availableDevices; } + QList& getUsedDevices() { return m_usedDevices; } + + static MsgReportDevices* create() { + return new MsgReportDevices(); + } + + private: + QList m_availableDevices; + QList m_usedDevices; + + MsgReportDevices() : + Message() + {} + }; + AMBE(WebAPIAdapterInterface *webAPIAdapterInterface); virtual ~AMBE(); virtual void destroy() { delete this; } @@ -71,6 +91,34 @@ public: AMBEEngine *getAMBEEngine() { return &m_ambeEngine; } + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage); + + virtual int webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage); + + static void webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const AMBESettings& settings); + + static void webapiUpdateFeatureSettings( + AMBESettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response); + static const char* const m_featureIdURI; static const char* const m_featureId; @@ -84,6 +132,8 @@ private: void start(); void stop(); void applySettings(const AMBESettings& settings, bool force = false); + void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response); + void webapiReverseSendSettings(QList& featureSettingsKeys, const AMBESettings& settings, bool force); private slots: void networkManagerFinished(QNetworkReply *reply); diff --git a/plugins/feature/ambe/ambeengine.cpp b/plugins/feature/ambe/ambeengine.cpp index bc16a6c07..c77eade8a 100644 --- a/plugins/feature/ambe/ambeengine.cpp +++ b/plugins/feature/ambe/ambeengine.cpp @@ -54,6 +54,7 @@ AMBEEngine::~AMBEEngine() #if defined(_WIN32) void AMBEEngine::getComList() { + qDebug("AMBEEngine::getComList: Win"); m_comList.clear(); m_comList8250.clear(); char comCStr[16]; @@ -74,6 +75,7 @@ void AMBEEngine::scan(std::vector& ambeDevices) #elif defined(__APPLE__) void AMBEEngine::getComList() { + qDebug("AMBEEngine::getComList: Apple"); } void AMBEEngine::scan(std::vector& ambeDevices) { @@ -82,6 +84,7 @@ void AMBEEngine::scan(std::vector& ambeDevices) #else void AMBEEngine::getComList() { + qDebug("AMBEEngine::getComList: Linux"); int n; struct dirent **namelist; m_comList.clear(); @@ -197,9 +200,10 @@ std::string AMBEEngine::get_driver(const std::string& tty) return ""; } -void AMBEEngine::scan(std::vector& ambeDevices) +void AMBEEngine::scan(QList& ambeDevices) { - getComList(); + qDebug("AMBEEngine::scan"); + AMBEEngine::getComList(); std::vector::const_iterator it = m_comList.begin(); ambeDevices.clear(); @@ -287,7 +291,7 @@ void AMBEEngine::releaseAll() m_controllers.clear(); } -void AMBEEngine::getDeviceRefs(std::vector& deviceNames) +void AMBEEngine::getDeviceRefs(QList& deviceNames) { std::vector::const_iterator it = m_controllers.begin(); @@ -342,61 +346,3 @@ void AMBEEngine::pushMbeFrame( } } } - -QByteArray AMBEEngine::serialize() const -{ - qDebug("AMBEEngine::serialize"); - QStringList qDeviceList; - std::vector::const_iterator it = m_controllers.begin(); - - while (it != m_controllers.end()) - { - qDebug("AMBEEngine::serialize: %s", it->device.c_str()); - qDeviceList << QString(it->device.c_str()); - ++it; - } - - QByteArray data; - QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); - (*stream) << qDeviceList; - delete stream; - - return data; -} - -bool AMBEEngine::deserialize(const QByteArray& data) -{ - if (data.size() <= 0) - { - qDebug("AMBEEngine::deserialize: invalid or no data"); - return false; - } - - QStringList qDeviceList; - QDataStream *stream = new QDataStream(data); - (*stream) >> qDeviceList; - delete stream; - - releaseAll(); - - for (int i = 0; i < qDeviceList.size(); ++i) - { - qDebug(" AMBEEngine::deserialize: %s", qDeviceList.at(i).toStdString().c_str()); - registerController(qDeviceList.at(i).toStdString()); - } - - return true; -} - -void AMBEEngine::formatTo(SWGSDRangel::SWGObject *swgObject) const -{ - (void) swgObject; - // TODO -} - -void AMBEEngine::updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject) -{ - (void) keys; - (void) swgObject; - // TODO -} diff --git a/plugins/feature/ambe/ambeengine.h b/plugins/feature/ambe/ambeengine.h index 897cefa71..9e9fd805b 100644 --- a/plugins/feature/ambe/ambeengine.h +++ b/plugins/feature/ambe/ambeengine.h @@ -25,26 +25,25 @@ #include #include #include +#include #include -#include "settings/serializable.h" - class QThread; class AMBEWorker; class AudioFifo; -class AMBEEngine : public QObject, public Serializable +class AMBEEngine : public QObject { Q_OBJECT public: AMBEEngine(); ~AMBEEngine(); - void scan(std::vector& ambeDevices); + void scan(QList& ambeDevices); void releaseAll(); int getNbDevices() const { return m_controllers.size(); } //!< number of devices used - void getDeviceRefs(std::vector& devicesRefs); //!< reference of the devices used (device path or url) + void getDeviceRefs(QList& devicesRefs); //!< reference of the devices used (device path or url) bool registerController(const std::string& deviceRef); //!< create a new controller for the device in reference void releaseController(const std::string& deviceRef); //!< release controller resources for the device in reference @@ -57,11 +56,6 @@ public: int upsampling, AudioFifo *audioFifo); - virtual QByteArray serialize() const; - virtual bool deserialize(const QByteArray& data); - virtual void formatTo(SWGSDRangel::SWGObject *swgObject) const; //!< Serialize to API - virtual void updateFrom(const QStringList& keys, const SWGSDRangel::SWGObject *swgObject); //!< Deserialize from API - private: struct AMBEController { diff --git a/plugins/feature/ambe/ambegui.cpp b/plugins/feature/ambe/ambegui.cpp index 9c7eb425a..2db66558d 100644 --- a/plugins/feature/ambe/ambegui.cpp +++ b/plugins/feature/ambe/ambegui.cpp @@ -178,6 +178,25 @@ bool AMBEGUI::handleMessage(const Message& message) displaySettings(); return true; } + else if (AMBE::MsgReportDevices::match(message)) + { + qDebug("AMBEGUI::handleMessage: AMBE::MsgReportDevices"); + AMBE::MsgReportDevices& cfg = (AMBE::MsgReportDevices&) message; + ui->ambeSerialDevices->clear(); + ui->statusText->setText("Updated all devices lists"); + + for (const auto& ambeDevice : cfg.getAvailableDevices()) { + ui->ambeSerialDevices->addItem(ambeDevice); + } + + ui->ambeDeviceRefs->clear(); + + for (const auto& inUseDevice : cfg.getUsedDevices()) { + ui->ambeDeviceRefs->addItem(inUseDevice); + } + + return true; + } return false; } @@ -196,27 +215,25 @@ void AMBEGUI::handleInputMessages() void AMBEGUI::populateSerialList() { - std::vector ambeSerialDevices; + QList ambeSerialDevices; m_ambe->getAMBEEngine()->scan(ambeSerialDevices); ui->ambeSerialDevices->clear(); - std::vector::const_iterator it = ambeSerialDevices.begin(); - for (; it != ambeSerialDevices.end(); ++it) { - ui->ambeSerialDevices->addItem(QString(*it)); + for (const auto& ambeDevice : ambeSerialDevices) { + ui->ambeSerialDevices->addItem(ambeDevice); } } void AMBEGUI::refreshInUseList() { - std::vector inUseDevices; + QList inUseDevices; m_ambe->getAMBEEngine()->getDeviceRefs(inUseDevices); ui->ambeDeviceRefs->clear(); - std::vector::const_iterator it = inUseDevices.begin(); - for (; it != inUseDevices.end(); ++it) + for (const auto& inUseDevice : inUseDevices) { - qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(*it)); - ui->ambeDeviceRefs->addItem(*it); + qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(inUseDevice)); + ui->ambeDeviceRefs->addItem(inUseDevice); } } void AMBEGUI::on_importSerial_clicked() @@ -295,6 +312,12 @@ void AMBEGUI::on_refreshAmbeList_clicked() ui->statusText->setText("In use refreshed"); } +void AMBEGUI::on_refreshSerial_clicked() +{ + populateSerialList(); + ui->statusText->setText("Serial refreshed"); +} + void AMBEGUI::on_clearAmbeList_clicked() { if (ui->ambeDeviceRefs->count() == 0) @@ -338,6 +361,7 @@ void AMBEGUI::makeUIConnections() QObject::connect(ui->importAllSerial, &QPushButton::clicked, this, &AMBEGUI::on_importAllSerial_clicked); QObject::connect(ui->removeAmbeDevice, &QPushButton::clicked, this, &AMBEGUI::on_removeAmbeDevice_clicked); QObject::connect(ui->refreshAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_refreshAmbeList_clicked); + QObject::connect(ui->refreshSerial, &QPushButton::clicked, this, &AMBEGUI::on_refreshSerial_clicked); QObject::connect(ui->clearAmbeList, &QPushButton::clicked, this, &AMBEGUI::on_clearAmbeList_clicked); QObject::connect(ui->importAddress, &QPushButton::clicked, this, &AMBEGUI::on_importAddress_clicked); } diff --git a/plugins/feature/ambe/ambegui.h b/plugins/feature/ambe/ambegui.h index f93dc16f2..838a887e9 100644 --- a/plugins/feature/ambe/ambegui.h +++ b/plugins/feature/ambe/ambegui.h @@ -85,6 +85,7 @@ private slots: void on_importAllSerial_clicked(); void on_removeAmbeDevice_clicked(); void on_refreshAmbeList_clicked(); + void on_refreshSerial_clicked(); void on_clearAmbeList_clicked(); void on_importAddress_clicked(); }; diff --git a/plugins/feature/ambe/ambegui.ui b/plugins/feature/ambe/ambegui.ui index 1db075df0..c225f95f2 100644 --- a/plugins/feature/ambe/ambegui.ui +++ b/plugins/feature/ambe/ambegui.ui @@ -116,7 +116,7 @@ - Release all devices and servers + Remove selected device or server @@ -127,20 +127,6 @@ - - - - Refresh list of devices and servers in use - - - - - - - :/recycle.png:/recycle.png - - - @@ -155,6 +141,20 @@ + + + + Refresh list of devices and servers in use + + + + + + + :/recycle.png:/recycle.png + + + @@ -224,6 +224,20 @@ + + + + Refresh AMBE serial devices list + + + + + + + :/recycle.png:/recycle.png + + + @@ -250,7 +264,7 @@ - List of available AMBE serial devices available + List of AMBE serial devices in the system diff --git a/plugins/feature/ambe/ambeplugin.cpp b/plugins/feature/ambe/ambeplugin.cpp index 7157cb2cd..7a2c46a05 100644 --- a/plugins/feature/ambe/ambeplugin.cpp +++ b/plugins/feature/ambe/ambeplugin.cpp @@ -24,7 +24,7 @@ #endif #include "ambe.h" #include "ambeplugin.h" -// #include "simplepttwebapiadapter.h" +#include "ambewebapiadapter.h" const PluginDescriptor AMBEPlugin::m_pluginDescriptor = { AMBE::m_featureId, @@ -76,5 +76,5 @@ Feature* AMBEPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterfac FeatureWebAPIAdapter* AMBEPlugin::createFeatureWebAPIAdapter() const { - return nullptr; // TODO new SimplePTTWebAPIAdapter(); + return new AMBEWebAPIAdapter(); } diff --git a/plugins/feature/ambe/ambewebapiadapter.cpp b/plugins/feature/ambe/ambewebapiadapter.cpp new file mode 100644 index 000000000..3d8c86ab1 --- /dev/null +++ b/plugins/feature/ambe/ambewebapiadapter.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGFeatureSettings.h" +#include "ambe.h" +#include "ambewebapiadapter.h" + +AMBEWebAPIAdapter::AMBEWebAPIAdapter() +{} + +AMBEWebAPIAdapter::~AMBEWebAPIAdapter() +{} + +int AMBEWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAmbeSettings(new SWGSDRangel::SWGAMBESettings()); + response.getAmbeSettings()->init(); + AMBE::webapiFormatFeatureSettings(response, m_settings); + + return 200; +} + +int AMBEWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) force; // no action + (void) errorMessage; + AMBE::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response); + + return 200; +} diff --git a/plugins/feature/ambe/ambewebapiadapter.h b/plugins/feature/ambe/ambewebapiadapter.h new file mode 100644 index 000000000..d053cb9b0 --- /dev/null +++ b/plugins/feature/ambe/ambewebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AMBE_WEBAPIADAPTER_H +#define INCLUDE_AMBE_WEBAPIADAPTER_H + +#include "feature/featurewebapiadapter.h" +#include "ambesettings.h" + +/** + * Standalone API adapter only for the settings + */ +class AMBEWebAPIAdapter : public FeatureWebAPIAdapter { +public: + AMBEWebAPIAdapter(); + virtual ~AMBEWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + +private: + AMBESettings m_settings; +}; + +#endif // INCLUDE_AIS_WEBAPIADAPTER_H diff --git a/sdrbase/resources/webapi.qrc b/sdrbase/resources/webapi.qrc index 219ac5fc7..5c38b5c68 100644 --- a/sdrbase/resources/webapi.qrc +++ b/sdrbase/resources/webapi.qrc @@ -9,6 +9,7 @@ webapi/doc/swagger/include/AIS.yaml webapi/doc/swagger/include/AISDemod.yaml webapi/doc/swagger/include/AISMod.yaml + webapi/doc/swagger/include/AMBE.yaml webapi/doc/swagger/include/AMDemod.yaml webapi/doc/swagger/include/AMMod.yaml webapi/doc/swagger/include/AntennaTools.yaml diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index a74ba0c83..ea79bf8c3 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -1248,6 +1248,19 @@ margin-bottom: 20px; } }, "description" : "AIS settings" +}; + defs.AMBEActions = { + "properties" : { + "updateDevices" : { + "description" : "Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing", + "$ref" : "#/definitions/AMBEDevices_2" + }, + "removeAll" : { + "type" : "integer", + "description" : "Set to a non zero value to remove all AMBE devices from the list of used AMBE devices" + } + }, + "description" : "AMBE" }; defs.AMBEDevice = { "properties" : { @@ -1261,6 +1274,19 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" +}; + defs.AMBEDevice_2 = { + "properties" : { + "deviceRef" : { + "type" : "string", + "description" : "Serial device name or server address" + }, + "delete" : { + "type" : "integer", + "description" : "1 if device is to be removed from active list" + } + }, + "description" : "AMBE devices active in the system" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1278,6 +1304,66 @@ margin-bottom: 20px; } }, "description" : "List of AMBE devices (serial or server address)" +}; + defs.AMBEDevices_2 = { + "required" : [ "nbDevices" ], + "properties" : { + "nbDevices" : { + "type" : "integer", + "description" : "Number of DV serial devices" + }, + "ambeDevices" : { + "type" : "array", + "description" : "List of AMBE devices", + "items" : { + "$ref" : "#/definitions/AMBEDevice" + } + } + }, + "description" : "List of AMBE devices (serial or server address)" +}; + defs.AMBEReport = { + "properties" : { + "serial" : { + "description" : "List of available DV serial devices", + "$ref" : "#/definitions/DVSerialDevices_2" + }, + "devices" : { + "description" : "List of AMBE devices (serial or address) used for AMBE frames processing", + "$ref" : "#/definitions/AMBEDevices_2" + } + }, + "description" : "AMBE" +}; + defs.AMBESettings = { + "properties" : { + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "AMBE" }; defs.AMDemodReport = { "properties" : { @@ -4625,6 +4711,15 @@ margin-bottom: 20px; } }, "description" : "DV serial device details" +}; + defs.DVSerialDevice_2 = { + "properties" : { + "deviceName" : { + "type" : "string", + "description" : "Name of the serial device in the system" + } + }, + "description" : "DV serial device details" }; defs.DVSerialDevices = { "required" : [ "nbDevices" ], @@ -4642,6 +4737,23 @@ margin-bottom: 20px; } }, "description" : "List of DV serial devices available in the system" +}; + defs.DVSerialDevices_2 = { + "required" : [ "nbDevices" ], + "properties" : { + "nbDevices" : { + "type" : "integer", + "description" : "Number of DV serial devices" + }, + "dvSerialDevices" : { + "type" : "array", + "description" : "Device names of DV serial devices", + "items" : { + "$ref" : "#/definitions/DVSerialDevice" + } + } + }, + "description" : "List of DV serial devices available in the system" }; defs.DemodAnalyzerSettings = { "properties" : { @@ -5278,6 +5390,9 @@ margin-bottom: 20px; "AFCActions" : { "$ref" : "#/definitions/AFCActions" }, + "AMBEActions" : { + "$ref" : "#/definitions/AMBEActions" + }, "GS232ControllerActions" : { "$ref" : "#/definitions/GS232ControllerActions" }, @@ -5417,6 +5532,9 @@ margin-bottom: 20px; "AFCReport" : { "$ref" : "#/definitions/AFCReport" }, + "AMBEReport" : { + "$ref" : "#/definitions/AMBEReport" + }, "GS232ControllerReport" : { "$ref" : "#/definitions/GS232ControllerReport" }, @@ -5503,6 +5621,9 @@ margin-bottom: 20px; "AISSettings" : { "$ref" : "#/definitions/AISSettings" }, + "AMBESettings" : { + "$ref" : "#/definitions/AMBESettings" + }, "AntennaToolsSettings" : { "$ref" : "#/definitions/AntennaToolsSettings" }, @@ -57769,7 +57890,7 @@ except ApiException as e:
    - Generated 2022-05-24T15:35:23.001+02:00 + Generated 2022-05-24T21:52:35.030+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml new file mode 100644 index 000000000..9b4d070ef --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml @@ -0,0 +1,88 @@ +AMBESettings: + description: AMBE + properties: + title: + type: string + rgbColor: + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +AMBEReport: + description: AMBE + properties: + serial: + description: List of available DV serial devices + $ref: "/doc/swagger/include/AMBE.yaml#/definitions/DVSerialDevices" + devices: + description: List of AMBE devices (serial or address) used for AMBE frames processing + $ref: "/doc/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + +AMBEActions: + description: AMBE + properties: + updateDevices: + description: Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing + $ref: "/doc/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + removeAll: + type: integer + description: Set to a non zero value to remove all AMBE devices from the list of used AMBE devices + + +definitions: + + DVSerialDevices: + description: "List of DV serial devices available in the system" + required: + - nbDevices + properties: + nbDevices: + description: "Number of DV serial devices" + type: integer + dvSerialDevices: + description: "Device names of DV serial devices" + type: array + items: + $ref: "#/definitions/DVSerialDevice" + + DVSerialDevice: + description: "DV serial device details" + properties: + deviceName: + description: "Name of the serial device in the system" + type: string + + AMBEDevices: + description: "List of AMBE devices (serial or server address)" + required: + - nbDevices + properties: + nbDevices: + description: "Number of DV serial devices" + type: integer + ambeDevices: + description: "List of AMBE devices" + type: array + items: + $ref: "#/definitions/AMBEDevice" + + AMBEDevice: + description: "AMBE devices active in the system" + properties: + deviceRef: + description: "Serial device name or server address" + type: string + delete: + description: "1 if device is to be removed from active list" + type: integer diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml index 1df31dfb0..bee05f5cf 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureActions.yaml @@ -15,6 +15,8 @@ FeatureActions: type: integer AFCActions: $ref: "/doc/swagger/include/AFC.yaml#/AFCActions" + AMBEActions: + $ref: "/doc/swagger/include/AMBE.yaml#/AMBEActions" GS232ControllerActions: $ref: "/doc/swagger/include/GS232Controller.yaml#/GS232ControllerActions" LimeRFEActions: diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml index ae6cd71ae..c4c63649e 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureReport.yaml @@ -9,6 +9,8 @@ FeatureReport: type: string AFCReport: $ref: "/doc/swagger/include/AFC.yaml#/AFCReport" + AMBEReport: + $ref: "/doc/swagger/include/AMBE.yaml#/AMBEReport" GS232ControllerReport: $ref: "/doc/swagger/include/GS232Controller.yaml#/GS232ControllerReport" LimeRFEReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml index 21245d2e5..2bef41e84 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FeatureSettings.yaml @@ -17,6 +17,8 @@ FeatureSettings: $ref: "/doc/swagger/include/AFC.yaml#/AFCSettings" AISSettings: $ref: "/doc/swagger/include/AIS.yaml#/AISSettings" + AMBESettings: + $ref: "/doc/swagger/include/AMBE.yaml#/AMBESettings" AntennaToolsSettings: $ref: "/doc/swagger/include/AntennaTools.yaml#/AntennaToolsSettings" APRSSettings: diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index a17083c6f..77ed69f22 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -5300,6 +5300,11 @@ bool WebAPIRequestMapper::getFeatureActions( featureActions->setAfcActions(new SWGSDRangel::SWGAFCActions()); featureActions->getAfcActions()->fromJsonObject(actionsJsonObject); } + else if (featureActionsKey == "AMBEActions") + { + featureActions->setAmbeActions(new SWGSDRangel::SWGAMBEActions()); + featureActions->getAmbeActions()->fromJsonObject(actionsJsonObject); + } else if (featureActionsKey == "GS232ControllerActions") { featureActions->setGs232ControllerActions(new SWGSDRangel::SWGGS232ControllerActions()); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 1ffbb1209..8708cf012 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -270,6 +270,7 @@ const QMap WebAPIUtils::m_mimoDeviceHwIdToActionsKey = { const QMap WebAPIUtils::m_featureTypeToSettingsKey = { {"AFC", "AFCSettings"}, {"AIS", "AISSettings"}, + {"AMBE", "AMBESettings"}, {"AntennaTools", "AntennaToolsSettings"}, {"APRS", "APRSSettings"}, {"DemodAnalyzer", "DemodAnalyzerSettings"}, @@ -288,6 +289,7 @@ const QMap WebAPIUtils::m_featureTypeToSettingsKey = { const QMap WebAPIUtils::m_featureTypeToActionsKey = { {"AFC", "AFCActions"}, + {"AMBE", "AMBEActions"}, {"GS232Controller", "GS232ControllerActions"}, {"LimeRFE", "LimeRFEActions"}, {"Map", "MapActions"}, @@ -302,6 +304,7 @@ const QMap WebAPIUtils::m_featureTypeToActionsKey = { const QMap WebAPIUtils::m_featureURIToSettingsKey = { {"sdrangel.feature.afc", "AFCSettings"}, {"sdrangel.feature.ais", "AISSSettings"}, + {"sdrangel.feature.ambe", "AMBESSettings"}, {"sdrangel.feature.antennatools", "AntennaToolsSettings"}, {"sdrangel.feature.aprs", "APRSSettings"}, {"sdrangel.feature.demodanalyzer", "DemodAnalyzerSettings"}, diff --git a/swagger/sdrangel/api/swagger/include/AMBE.yaml b/swagger/sdrangel/api/swagger/include/AMBE.yaml new file mode 100644 index 000000000..f4e9cef2c --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/AMBE.yaml @@ -0,0 +1,88 @@ +AMBESettings: + description: AMBE + properties: + title: + type: string + rgbColor: + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIFeatureSetIndex: + type: integer + reverseAPIFeatureIndex: + type: integer + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +AMBEReport: + description: AMBE + properties: + serial: + description: List of AMBE serial devices in the system + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/DVSerialDevices" + devices: + description: List of AMBE devices or servers in use + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + +AMBEActions: + description: AMBE + properties: + updateDevices: + description: Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + removeAll: + type: integer + description: Set to a non zero value to remove all AMBE devices from the list of used AMBE devices + + +definitions: + + DVSerialDevices: + description: "List of DV serial devices available in the system" + required: + - nbDevices + properties: + nbDevices: + description: "Number of DV serial devices" + type: integer + dvSerialDevices: + description: "Device names of DV serial devices" + type: array + items: + $ref: "#/definitions/DVSerialDevice" + + DVSerialDevice: + description: "DV serial device details" + properties: + deviceName: + description: "Name of the serial device in the system" + type: string + + AMBEDevices: + description: "List of AMBE devices (serial or server address)" + required: + - nbDevices + properties: + nbDevices: + description: "Number of DV serial devices" + type: integer + ambeDevices: + description: "List of AMBE devices" + type: array + items: + $ref: "#/definitions/AMBEDevice" + + AMBEDevice: + description: "AMBE devices active in the system" + properties: + deviceRef: + description: "Serial device name or server address" + type: string + delete: + description: "1 if device is to be removed from active list" + type: integer diff --git a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml index 3a47ffe96..4ba69ceaa 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml @@ -15,6 +15,8 @@ FeatureActions: type: integer AFCActions: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCActions" + AMBEActions: + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/AMBEActions" GS232ControllerActions: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerActions" LimeRFEActions: diff --git a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml index 1264a7387..95fdcea9e 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml @@ -9,6 +9,8 @@ FeatureReport: type: string AFCReport: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCReport" + AMBEReport: + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/AMBEReport" GS232ControllerReport: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerReport" LimeRFEReport: diff --git a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml index 0f0f0bf30..6832f8ab4 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml @@ -17,6 +17,8 @@ FeatureSettings: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCSettings" AISSettings: $ref: "http://swgserver:8081/api/swagger/include/AIS.yaml#/AISSettings" + AMBESettings: + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/AMBESettings" AntennaToolsSettings: $ref: "http://swgserver:8081/api/swagger/include/AntennaTools.yaml#/AntennaToolsSettings" APRSSettings: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index a74ba0c83..ea79bf8c3 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -1248,6 +1248,19 @@ margin-bottom: 20px; } }, "description" : "AIS settings" +}; + defs.AMBEActions = { + "properties" : { + "updateDevices" : { + "description" : "Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing", + "$ref" : "#/definitions/AMBEDevices_2" + }, + "removeAll" : { + "type" : "integer", + "description" : "Set to a non zero value to remove all AMBE devices from the list of used AMBE devices" + } + }, + "description" : "AMBE" }; defs.AMBEDevice = { "properties" : { @@ -1261,6 +1274,19 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" +}; + defs.AMBEDevice_2 = { + "properties" : { + "deviceRef" : { + "type" : "string", + "description" : "Serial device name or server address" + }, + "delete" : { + "type" : "integer", + "description" : "1 if device is to be removed from active list" + } + }, + "description" : "AMBE devices active in the system" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1278,6 +1304,66 @@ margin-bottom: 20px; } }, "description" : "List of AMBE devices (serial or server address)" +}; + defs.AMBEDevices_2 = { + "required" : [ "nbDevices" ], + "properties" : { + "nbDevices" : { + "type" : "integer", + "description" : "Number of DV serial devices" + }, + "ambeDevices" : { + "type" : "array", + "description" : "List of AMBE devices", + "items" : { + "$ref" : "#/definitions/AMBEDevice" + } + } + }, + "description" : "List of AMBE devices (serial or server address)" +}; + defs.AMBEReport = { + "properties" : { + "serial" : { + "description" : "List of available DV serial devices", + "$ref" : "#/definitions/DVSerialDevices_2" + }, + "devices" : { + "description" : "List of AMBE devices (serial or address) used for AMBE frames processing", + "$ref" : "#/definitions/AMBEDevices_2" + } + }, + "description" : "AMBE" +}; + defs.AMBESettings = { + "properties" : { + "title" : { + "type" : "string" + }, + "rgbColor" : { + "type" : "integer" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIFeatureSetIndex" : { + "type" : "integer" + }, + "reverseAPIFeatureIndex" : { + "type" : "integer" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "AMBE" }; defs.AMDemodReport = { "properties" : { @@ -4625,6 +4711,15 @@ margin-bottom: 20px; } }, "description" : "DV serial device details" +}; + defs.DVSerialDevice_2 = { + "properties" : { + "deviceName" : { + "type" : "string", + "description" : "Name of the serial device in the system" + } + }, + "description" : "DV serial device details" }; defs.DVSerialDevices = { "required" : [ "nbDevices" ], @@ -4642,6 +4737,23 @@ margin-bottom: 20px; } }, "description" : "List of DV serial devices available in the system" +}; + defs.DVSerialDevices_2 = { + "required" : [ "nbDevices" ], + "properties" : { + "nbDevices" : { + "type" : "integer", + "description" : "Number of DV serial devices" + }, + "dvSerialDevices" : { + "type" : "array", + "description" : "Device names of DV serial devices", + "items" : { + "$ref" : "#/definitions/DVSerialDevice" + } + } + }, + "description" : "List of DV serial devices available in the system" }; defs.DemodAnalyzerSettings = { "properties" : { @@ -5278,6 +5390,9 @@ margin-bottom: 20px; "AFCActions" : { "$ref" : "#/definitions/AFCActions" }, + "AMBEActions" : { + "$ref" : "#/definitions/AMBEActions" + }, "GS232ControllerActions" : { "$ref" : "#/definitions/GS232ControllerActions" }, @@ -5417,6 +5532,9 @@ margin-bottom: 20px; "AFCReport" : { "$ref" : "#/definitions/AFCReport" }, + "AMBEReport" : { + "$ref" : "#/definitions/AMBEReport" + }, "GS232ControllerReport" : { "$ref" : "#/definitions/GS232ControllerReport" }, @@ -5503,6 +5621,9 @@ margin-bottom: 20px; "AISSettings" : { "$ref" : "#/definitions/AISSettings" }, + "AMBESettings" : { + "$ref" : "#/definitions/AMBESettings" + }, "AntennaToolsSettings" : { "$ref" : "#/definitions/AntennaToolsSettings" }, @@ -57769,7 +57890,7 @@ except ApiException as e:
    - Generated 2022-05-24T15:35:23.001+02:00 + Generated 2022-05-24T21:52:35.030+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp new file mode 100644 index 000000000..4a6079e5f --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp @@ -0,0 +1,133 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBEActions.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBEActions::SWGAMBEActions(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBEActions::SWGAMBEActions() { + update_devices = nullptr; + m_update_devices_isSet = false; + remove_all = 0; + m_remove_all_isSet = false; +} + +SWGAMBEActions::~SWGAMBEActions() { + this->cleanup(); +} + +void +SWGAMBEActions::init() { + update_devices = new SWGAMBEDevices_2(); + m_update_devices_isSet = false; + remove_all = 0; + m_remove_all_isSet = false; +} + +void +SWGAMBEActions::cleanup() { + if(update_devices != nullptr) { + delete update_devices; + } + +} + +SWGAMBEActions* +SWGAMBEActions::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBEActions::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&update_devices, pJson["updateDevices"], "SWGAMBEDevices_2", "SWGAMBEDevices_2"); + + ::SWGSDRangel::setValue(&remove_all, pJson["removeAll"], "qint32", ""); + +} + +QString +SWGAMBEActions::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBEActions::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if((update_devices != nullptr) && (update_devices->isSet())){ + toJsonValue(QString("updateDevices"), update_devices, obj, QString("SWGAMBEDevices_2")); + } + if(m_remove_all_isSet){ + obj->insert("removeAll", QJsonValue(remove_all)); + } + + return obj; +} + +SWGAMBEDevices_2* +SWGAMBEActions::getUpdateDevices() { + return update_devices; +} +void +SWGAMBEActions::setUpdateDevices(SWGAMBEDevices_2* update_devices) { + this->update_devices = update_devices; + this->m_update_devices_isSet = true; +} + +qint32 +SWGAMBEActions::getRemoveAll() { + return remove_all; +} +void +SWGAMBEActions::setRemoveAll(qint32 remove_all) { + this->remove_all = remove_all; + this->m_remove_all_isSet = true; +} + + +bool +SWGAMBEActions::isSet(){ + bool isObjectUpdated = false; + do{ + if(update_devices && update_devices->isSet()){ + isObjectUpdated = true; break; + } + if(m_remove_all_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h new file mode 100644 index 000000000..a742b6071 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h @@ -0,0 +1,65 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBEActions.h + * + * AMBE + */ + +#ifndef SWGAMBEActions_H_ +#define SWGAMBEActions_H_ + +#include + + +#include "SWGAMBEDevices_2.h" + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBEActions: public SWGObject { +public: + SWGAMBEActions(); + SWGAMBEActions(QString* json); + virtual ~SWGAMBEActions(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBEActions* fromJson(QString &jsonString) override; + + SWGAMBEDevices_2* getUpdateDevices(); + void setUpdateDevices(SWGAMBEDevices_2* update_devices); + + qint32 getRemoveAll(); + void setRemoveAll(qint32 remove_all); + + + virtual bool isSet() override; + +private: + SWGAMBEDevices_2* update_devices; + bool m_update_devices_isSet; + + qint32 remove_all; + bool m_remove_all_isSet; + +}; + +} + +#endif /* SWGAMBEActions_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp new file mode 100644 index 000000000..9cfff94c3 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp @@ -0,0 +1,133 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBEDevice_2.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBEDevice_2::SWGAMBEDevice_2(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBEDevice_2::SWGAMBEDevice_2() { + device_ref = nullptr; + m_device_ref_isSet = false; + _delete = 0; + m__delete_isSet = false; +} + +SWGAMBEDevice_2::~SWGAMBEDevice_2() { + this->cleanup(); +} + +void +SWGAMBEDevice_2::init() { + device_ref = new QString(""); + m_device_ref_isSet = false; + _delete = 0; + m__delete_isSet = false; +} + +void +SWGAMBEDevice_2::cleanup() { + if(device_ref != nullptr) { + delete device_ref; + } + +} + +SWGAMBEDevice_2* +SWGAMBEDevice_2::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBEDevice_2::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&device_ref, pJson["deviceRef"], "QString", "QString"); + + ::SWGSDRangel::setValue(&_delete, pJson["delete"], "qint32", ""); + +} + +QString +SWGAMBEDevice_2::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBEDevice_2::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(device_ref != nullptr && *device_ref != QString("")){ + toJsonValue(QString("deviceRef"), device_ref, obj, QString("QString")); + } + if(m__delete_isSet){ + obj->insert("delete", QJsonValue(_delete)); + } + + return obj; +} + +QString* +SWGAMBEDevice_2::getDeviceRef() { + return device_ref; +} +void +SWGAMBEDevice_2::setDeviceRef(QString* device_ref) { + this->device_ref = device_ref; + this->m_device_ref_isSet = true; +} + +qint32 +SWGAMBEDevice_2::getDelete() { + return _delete; +} +void +SWGAMBEDevice_2::setDelete(qint32 _delete) { + this->_delete = _delete; + this->m__delete_isSet = true; +} + + +bool +SWGAMBEDevice_2::isSet(){ + bool isObjectUpdated = false; + do{ + if(device_ref && *device_ref != QString("")){ + isObjectUpdated = true; break; + } + if(m__delete_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h new file mode 100644 index 000000000..7decb9e28 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h @@ -0,0 +1,65 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBEDevice_2.h + * + * AMBE devices active in the system + */ + +#ifndef SWGAMBEDevice_2_H_ +#define SWGAMBEDevice_2_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBEDevice_2: public SWGObject { +public: + SWGAMBEDevice_2(); + SWGAMBEDevice_2(QString* json); + virtual ~SWGAMBEDevice_2(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBEDevice_2* fromJson(QString &jsonString) override; + + QString* getDeviceRef(); + void setDeviceRef(QString* device_ref); + + qint32 getDelete(); + void setDelete(qint32 _delete); + + + virtual bool isSet() override; + +private: + QString* device_ref; + bool m_device_ref_isSet; + + qint32 _delete; + bool m__delete_isSet; + +}; + +} + +#endif /* SWGAMBEDevice_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp new file mode 100644 index 000000000..e3b3935ab --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp @@ -0,0 +1,137 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBEDevices_2.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBEDevices_2::SWGAMBEDevices_2(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBEDevices_2::SWGAMBEDevices_2() { + nb_devices = 0; + m_nb_devices_isSet = false; + ambe_devices = nullptr; + m_ambe_devices_isSet = false; +} + +SWGAMBEDevices_2::~SWGAMBEDevices_2() { + this->cleanup(); +} + +void +SWGAMBEDevices_2::init() { + nb_devices = 0; + m_nb_devices_isSet = false; + ambe_devices = new QList(); + m_ambe_devices_isSet = false; +} + +void +SWGAMBEDevices_2::cleanup() { + + if(ambe_devices != nullptr) { + auto arr = ambe_devices; + for(auto o: *arr) { + delete o; + } + delete ambe_devices; + } +} + +SWGAMBEDevices_2* +SWGAMBEDevices_2::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBEDevices_2::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&nb_devices, pJson["nbDevices"], "qint32", ""); + + + ::SWGSDRangel::setValue(&ambe_devices, pJson["ambeDevices"], "QList", "SWGAMBEDevice"); +} + +QString +SWGAMBEDevices_2::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBEDevices_2::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_nb_devices_isSet){ + obj->insert("nbDevices", QJsonValue(nb_devices)); + } + if(ambe_devices && ambe_devices->size() > 0){ + toJsonArray((QList*)ambe_devices, obj, "ambeDevices", "SWGAMBEDevice"); + } + + return obj; +} + +qint32 +SWGAMBEDevices_2::getNbDevices() { + return nb_devices; +} +void +SWGAMBEDevices_2::setNbDevices(qint32 nb_devices) { + this->nb_devices = nb_devices; + this->m_nb_devices_isSet = true; +} + +QList* +SWGAMBEDevices_2::getAmbeDevices() { + return ambe_devices; +} +void +SWGAMBEDevices_2::setAmbeDevices(QList* ambe_devices) { + this->ambe_devices = ambe_devices; + this->m_ambe_devices_isSet = true; +} + + +bool +SWGAMBEDevices_2::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_nb_devices_isSet){ + isObjectUpdated = true; break; + } + if(ambe_devices && (ambe_devices->size() > 0)){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h new file mode 100644 index 000000000..c60b61f99 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h @@ -0,0 +1,66 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBEDevices_2.h + * + * List of AMBE devices (serial or server address) + */ + +#ifndef SWGAMBEDevices_2_H_ +#define SWGAMBEDevices_2_H_ + +#include + + +#include "SWGAMBEDevice.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBEDevices_2: public SWGObject { +public: + SWGAMBEDevices_2(); + SWGAMBEDevices_2(QString* json); + virtual ~SWGAMBEDevices_2(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBEDevices_2* fromJson(QString &jsonString) override; + + qint32 getNbDevices(); + void setNbDevices(qint32 nb_devices); + + QList* getAmbeDevices(); + void setAmbeDevices(QList* ambe_devices); + + + virtual bool isSet() override; + +private: + qint32 nb_devices; + bool m_nb_devices_isSet; + + QList* ambe_devices; + bool m_ambe_devices_isSet; + +}; + +} + +#endif /* SWGAMBEDevices_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp new file mode 100644 index 000000000..9f22650d4 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp @@ -0,0 +1,135 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBEReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBEReport::SWGAMBEReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBEReport::SWGAMBEReport() { + serial = nullptr; + m_serial_isSet = false; + devices = nullptr; + m_devices_isSet = false; +} + +SWGAMBEReport::~SWGAMBEReport() { + this->cleanup(); +} + +void +SWGAMBEReport::init() { + serial = new SWGDVSerialDevices_2(); + m_serial_isSet = false; + devices = new SWGAMBEDevices_2(); + m_devices_isSet = false; +} + +void +SWGAMBEReport::cleanup() { + if(serial != nullptr) { + delete serial; + } + if(devices != nullptr) { + delete devices; + } +} + +SWGAMBEReport* +SWGAMBEReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBEReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&serial, pJson["serial"], "SWGDVSerialDevices_2", "SWGDVSerialDevices_2"); + + ::SWGSDRangel::setValue(&devices, pJson["devices"], "SWGAMBEDevices_2", "SWGAMBEDevices_2"); + +} + +QString +SWGAMBEReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBEReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if((serial != nullptr) && (serial->isSet())){ + toJsonValue(QString("serial"), serial, obj, QString("SWGDVSerialDevices_2")); + } + if((devices != nullptr) && (devices->isSet())){ + toJsonValue(QString("devices"), devices, obj, QString("SWGAMBEDevices_2")); + } + + return obj; +} + +SWGDVSerialDevices_2* +SWGAMBEReport::getSerial() { + return serial; +} +void +SWGAMBEReport::setSerial(SWGDVSerialDevices_2* serial) { + this->serial = serial; + this->m_serial_isSet = true; +} + +SWGAMBEDevices_2* +SWGAMBEReport::getDevices() { + return devices; +} +void +SWGAMBEReport::setDevices(SWGAMBEDevices_2* devices) { + this->devices = devices; + this->m_devices_isSet = true; +} + + +bool +SWGAMBEReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(serial && serial->isSet()){ + isObjectUpdated = true; break; + } + if(devices && devices->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h new file mode 100644 index 000000000..45f3f1d32 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h @@ -0,0 +1,66 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBEReport.h + * + * AMBE + */ + +#ifndef SWGAMBEReport_H_ +#define SWGAMBEReport_H_ + +#include + + +#include "SWGAMBEDevices_2.h" +#include "SWGDVSerialDevices_2.h" + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBEReport: public SWGObject { +public: + SWGAMBEReport(); + SWGAMBEReport(QString* json); + virtual ~SWGAMBEReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBEReport* fromJson(QString &jsonString) override; + + SWGDVSerialDevices_2* getSerial(); + void setSerial(SWGDVSerialDevices_2* serial); + + SWGAMBEDevices_2* getDevices(); + void setDevices(SWGAMBEDevices_2* devices); + + + virtual bool isSet() override; + +private: + SWGDVSerialDevices_2* serial; + bool m_serial_isSet; + + SWGAMBEDevices_2* devices; + bool m_devices_isSet; + +}; + +} + +#endif /* SWGAMBEReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBESettings.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBESettings.cpp new file mode 100644 index 000000000..7f70bf550 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBESettings.cpp @@ -0,0 +1,275 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBESettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBESettings::SWGAMBESettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBESettings::SWGAMBESettings() { + title = nullptr; + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGAMBESettings::~SWGAMBESettings() { + this->cleanup(); +} + +void +SWGAMBESettings::init() { + title = new QString(""); + m_title_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_feature_set_index = 0; + m_reverse_api_feature_set_index_isSet = false; + reverse_api_feature_index = 0; + m_reverse_api_feature_index_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGAMBESettings::cleanup() { + if(title != nullptr) { + delete title; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGAMBESettings* +SWGAMBESettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBESettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_feature_set_index, pJson["reverseAPIFeatureSetIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_feature_index, pJson["reverseAPIFeatureIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGAMBESettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBESettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_feature_set_index_isSet){ + obj->insert("reverseAPIFeatureSetIndex", QJsonValue(reverse_api_feature_set_index)); + } + if(m_reverse_api_feature_index_isSet){ + obj->insert("reverseAPIFeatureIndex", QJsonValue(reverse_api_feature_index)); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +QString* +SWGAMBESettings::getTitle() { + return title; +} +void +SWGAMBESettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGAMBESettings::getRgbColor() { + return rgb_color; +} +void +SWGAMBESettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +qint32 +SWGAMBESettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGAMBESettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGAMBESettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGAMBESettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGAMBESettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGAMBESettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGAMBESettings::getReverseApiFeatureSetIndex() { + return reverse_api_feature_set_index; +} +void +SWGAMBESettings::setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index) { + this->reverse_api_feature_set_index = reverse_api_feature_set_index; + this->m_reverse_api_feature_set_index_isSet = true; +} + +qint32 +SWGAMBESettings::getReverseApiFeatureIndex() { + return reverse_api_feature_index; +} +void +SWGAMBESettings::setReverseApiFeatureIndex(qint32 reverse_api_feature_index) { + this->reverse_api_feature_index = reverse_api_feature_index; + this->m_reverse_api_feature_index_isSet = true; +} + +SWGRollupState* +SWGAMBESettings::getRollupState() { + return rollup_state; +} +void +SWGAMBESettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGAMBESettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_feature_set_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_feature_index_isSet){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBESettings.h b/swagger/sdrangel/code/qt5/client/SWGAMBESettings.h new file mode 100644 index 000000000..36961d06a --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBESettings.h @@ -0,0 +1,102 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBESettings.h + * + * AMBE + */ + +#ifndef SWGAMBESettings_H_ +#define SWGAMBESettings_H_ + +#include + + +#include "SWGRollupState.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBESettings: public SWGObject { +public: + SWGAMBESettings(); + SWGAMBESettings(QString* json); + virtual ~SWGAMBESettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBESettings* fromJson(QString &jsonString) override; + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiFeatureSetIndex(); + void setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index); + + qint32 getReverseApiFeatureIndex(); + void setReverseApiFeatureIndex(qint32 reverse_api_feature_index); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + QString* title; + bool m_title_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_feature_set_index; + bool m_reverse_api_feature_set_index_isSet; + + qint32 reverse_api_feature_index; + bool m_reverse_api_feature_index_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGAMBESettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp new file mode 100644 index 000000000..06bba95f3 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp @@ -0,0 +1,110 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGDVSerialDevice_2.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGDVSerialDevice_2::SWGDVSerialDevice_2(QString* json) { + init(); + this->fromJson(*json); +} + +SWGDVSerialDevice_2::SWGDVSerialDevice_2() { + device_name = nullptr; + m_device_name_isSet = false; +} + +SWGDVSerialDevice_2::~SWGDVSerialDevice_2() { + this->cleanup(); +} + +void +SWGDVSerialDevice_2::init() { + device_name = new QString(""); + m_device_name_isSet = false; +} + +void +SWGDVSerialDevice_2::cleanup() { + if(device_name != nullptr) { + delete device_name; + } +} + +SWGDVSerialDevice_2* +SWGDVSerialDevice_2::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGDVSerialDevice_2::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&device_name, pJson["deviceName"], "QString", "QString"); + +} + +QString +SWGDVSerialDevice_2::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGDVSerialDevice_2::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(device_name != nullptr && *device_name != QString("")){ + toJsonValue(QString("deviceName"), device_name, obj, QString("QString")); + } + + return obj; +} + +QString* +SWGDVSerialDevice_2::getDeviceName() { + return device_name; +} +void +SWGDVSerialDevice_2::setDeviceName(QString* device_name) { + this->device_name = device_name; + this->m_device_name_isSet = true; +} + + +bool +SWGDVSerialDevice_2::isSet(){ + bool isObjectUpdated = false; + do{ + if(device_name && *device_name != QString("")){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h new file mode 100644 index 000000000..92a21c6be --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h @@ -0,0 +1,59 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGDVSerialDevice_2.h + * + * DV serial device details + */ + +#ifndef SWGDVSerialDevice_2_H_ +#define SWGDVSerialDevice_2_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGDVSerialDevice_2: public SWGObject { +public: + SWGDVSerialDevice_2(); + SWGDVSerialDevice_2(QString* json); + virtual ~SWGDVSerialDevice_2(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGDVSerialDevice_2* fromJson(QString &jsonString) override; + + QString* getDeviceName(); + void setDeviceName(QString* device_name); + + + virtual bool isSet() override; + +private: + QString* device_name; + bool m_device_name_isSet; + +}; + +} + +#endif /* SWGDVSerialDevice_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp new file mode 100644 index 000000000..18304ef01 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp @@ -0,0 +1,137 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGDVSerialDevices_2.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGDVSerialDevices_2::SWGDVSerialDevices_2(QString* json) { + init(); + this->fromJson(*json); +} + +SWGDVSerialDevices_2::SWGDVSerialDevices_2() { + nb_devices = 0; + m_nb_devices_isSet = false; + dv_serial_devices = nullptr; + m_dv_serial_devices_isSet = false; +} + +SWGDVSerialDevices_2::~SWGDVSerialDevices_2() { + this->cleanup(); +} + +void +SWGDVSerialDevices_2::init() { + nb_devices = 0; + m_nb_devices_isSet = false; + dv_serial_devices = new QList(); + m_dv_serial_devices_isSet = false; +} + +void +SWGDVSerialDevices_2::cleanup() { + + if(dv_serial_devices != nullptr) { + auto arr = dv_serial_devices; + for(auto o: *arr) { + delete o; + } + delete dv_serial_devices; + } +} + +SWGDVSerialDevices_2* +SWGDVSerialDevices_2::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGDVSerialDevices_2::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&nb_devices, pJson["nbDevices"], "qint32", ""); + + + ::SWGSDRangel::setValue(&dv_serial_devices, pJson["dvSerialDevices"], "QList", "SWGDVSerialDevice"); +} + +QString +SWGDVSerialDevices_2::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGDVSerialDevices_2::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_nb_devices_isSet){ + obj->insert("nbDevices", QJsonValue(nb_devices)); + } + if(dv_serial_devices && dv_serial_devices->size() > 0){ + toJsonArray((QList*)dv_serial_devices, obj, "dvSerialDevices", "SWGDVSerialDevice"); + } + + return obj; +} + +qint32 +SWGDVSerialDevices_2::getNbDevices() { + return nb_devices; +} +void +SWGDVSerialDevices_2::setNbDevices(qint32 nb_devices) { + this->nb_devices = nb_devices; + this->m_nb_devices_isSet = true; +} + +QList* +SWGDVSerialDevices_2::getDvSerialDevices() { + return dv_serial_devices; +} +void +SWGDVSerialDevices_2::setDvSerialDevices(QList* dv_serial_devices) { + this->dv_serial_devices = dv_serial_devices; + this->m_dv_serial_devices_isSet = true; +} + + +bool +SWGDVSerialDevices_2::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_nb_devices_isSet){ + isObjectUpdated = true; break; + } + if(dv_serial_devices && (dv_serial_devices->size() > 0)){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h new file mode 100644 index 000000000..d0065a428 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h @@ -0,0 +1,66 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGDVSerialDevices_2.h + * + * List of DV serial devices available in the system + */ + +#ifndef SWGDVSerialDevices_2_H_ +#define SWGDVSerialDevices_2_H_ + +#include + + +#include "SWGDVSerialDevice.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGDVSerialDevices_2: public SWGObject { +public: + SWGDVSerialDevices_2(); + SWGDVSerialDevices_2(QString* json); + virtual ~SWGDVSerialDevices_2(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGDVSerialDevices_2* fromJson(QString &jsonString) override; + + qint32 getNbDevices(); + void setNbDevices(qint32 nb_devices); + + QList* getDvSerialDevices(); + void setDvSerialDevices(QList* dv_serial_devices); + + + virtual bool isSet() override; + +private: + qint32 nb_devices; + bool m_nb_devices_isSet; + + QList* dv_serial_devices; + bool m_dv_serial_devices_isSet; + +}; + +} + +#endif /* SWGDVSerialDevices_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp index ed766164f..c6567fdd6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.cpp @@ -36,6 +36,8 @@ SWGFeatureActions::SWGFeatureActions() { m_originator_feature_index_isSet = false; afc_actions = nullptr; m_afc_actions_isSet = false; + ambe_actions = nullptr; + m_ambe_actions_isSet = false; gs232_controller_actions = nullptr; m_gs232_controller_actions_isSet = false; lime_rfe_actions = nullptr; @@ -70,6 +72,8 @@ SWGFeatureActions::init() { m_originator_feature_index_isSet = false; afc_actions = new SWGAFCActions(); m_afc_actions_isSet = false; + ambe_actions = new SWGAMBEActions(); + m_ambe_actions_isSet = false; gs232_controller_actions = new SWGGS232ControllerActions(); m_gs232_controller_actions_isSet = false; lime_rfe_actions = new SWGLimeRFEActions(); @@ -100,6 +104,9 @@ SWGFeatureActions::cleanup() { if(afc_actions != nullptr) { delete afc_actions; } + if(ambe_actions != nullptr) { + delete ambe_actions; + } if(gs232_controller_actions != nullptr) { delete gs232_controller_actions; } @@ -148,6 +155,8 @@ SWGFeatureActions::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&afc_actions, pJson["AFCActions"], "SWGAFCActions", "SWGAFCActions"); + ::SWGSDRangel::setValue(&ambe_actions, pJson["AMBEActions"], "SWGAMBEActions", "SWGAMBEActions"); + ::SWGSDRangel::setValue(&gs232_controller_actions, pJson["GS232ControllerActions"], "SWGGS232ControllerActions", "SWGGS232ControllerActions"); ::SWGSDRangel::setValue(&lime_rfe_actions, pJson["LimeRFEActions"], "SWGLimeRFEActions", "SWGLimeRFEActions"); @@ -194,6 +203,9 @@ SWGFeatureActions::asJsonObject() { if((afc_actions != nullptr) && (afc_actions->isSet())){ toJsonValue(QString("AFCActions"), afc_actions, obj, QString("SWGAFCActions")); } + if((ambe_actions != nullptr) && (ambe_actions->isSet())){ + toJsonValue(QString("AMBEActions"), ambe_actions, obj, QString("SWGAMBEActions")); + } if((gs232_controller_actions != nullptr) && (gs232_controller_actions->isSet())){ toJsonValue(QString("GS232ControllerActions"), gs232_controller_actions, obj, QString("SWGGS232ControllerActions")); } @@ -265,6 +277,16 @@ SWGFeatureActions::setAfcActions(SWGAFCActions* afc_actions) { this->m_afc_actions_isSet = true; } +SWGAMBEActions* +SWGFeatureActions::getAmbeActions() { + return ambe_actions; +} +void +SWGFeatureActions::setAmbeActions(SWGAMBEActions* ambe_actions) { + this->ambe_actions = ambe_actions; + this->m_ambe_actions_isSet = true; +} + SWGGS232ControllerActions* SWGFeatureActions::getGs232ControllerActions() { return gs232_controller_actions; @@ -372,6 +394,9 @@ SWGFeatureActions::isSet(){ if(afc_actions && afc_actions->isSet()){ isObjectUpdated = true; break; } + if(ambe_actions && ambe_actions->isSet()){ + isObjectUpdated = true; break; + } if(gs232_controller_actions && gs232_controller_actions->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h index 46fa415e3..50f7f4b2a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureActions.h @@ -23,6 +23,7 @@ #include "SWGAFCActions.h" +#include "SWGAMBEActions.h" #include "SWGGS232ControllerActions.h" #include "SWGLimeRFEActions.h" #include "SWGMapActions.h" @@ -64,6 +65,9 @@ public: SWGAFCActions* getAfcActions(); void setAfcActions(SWGAFCActions* afc_actions); + SWGAMBEActions* getAmbeActions(); + void setAmbeActions(SWGAMBEActions* ambe_actions); + SWGGS232ControllerActions* getGs232ControllerActions(); void setGs232ControllerActions(SWGGS232ControllerActions* gs232_controller_actions); @@ -107,6 +111,9 @@ private: SWGAFCActions* afc_actions; bool m_afc_actions_isSet; + SWGAMBEActions* ambe_actions; + bool m_ambe_actions_isSet; + SWGGS232ControllerActions* gs232_controller_actions; bool m_gs232_controller_actions_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp index 1b79ae52a..087a514f0 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp @@ -32,6 +32,8 @@ SWGFeatureReport::SWGFeatureReport() { m_feature_type_isSet = false; afc_report = nullptr; m_afc_report_isSet = false; + ambe_report = nullptr; + m_ambe_report_isSet = false; gs232_controller_report = nullptr; m_gs232_controller_report_isSet = false; lime_rfe_report = nullptr; @@ -62,6 +64,8 @@ SWGFeatureReport::init() { m_feature_type_isSet = false; afc_report = new SWGAFCReport(); m_afc_report_isSet = false; + ambe_report = new SWGAMBEReport(); + m_ambe_report_isSet = false; gs232_controller_report = new SWGGS232ControllerReport(); m_gs232_controller_report_isSet = false; lime_rfe_report = new SWGLimeRFEReport(); @@ -90,6 +94,9 @@ SWGFeatureReport::cleanup() { if(afc_report != nullptr) { delete afc_report; } + if(ambe_report != nullptr) { + delete ambe_report; + } if(gs232_controller_report != nullptr) { delete gs232_controller_report; } @@ -134,6 +141,8 @@ SWGFeatureReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&afc_report, pJson["AFCReport"], "SWGAFCReport", "SWGAFCReport"); + ::SWGSDRangel::setValue(&ambe_report, pJson["AMBEReport"], "SWGAMBEReport", "SWGAMBEReport"); + ::SWGSDRangel::setValue(&gs232_controller_report, pJson["GS232ControllerReport"], "SWGGS232ControllerReport", "SWGGS232ControllerReport"); ::SWGSDRangel::setValue(&lime_rfe_report, pJson["LimeRFEReport"], "SWGLimeRFEReport", "SWGLimeRFEReport"); @@ -174,6 +183,9 @@ SWGFeatureReport::asJsonObject() { if((afc_report != nullptr) && (afc_report->isSet())){ toJsonValue(QString("AFCReport"), afc_report, obj, QString("SWGAFCReport")); } + if((ambe_report != nullptr) && (ambe_report->isSet())){ + toJsonValue(QString("AMBEReport"), ambe_report, obj, QString("SWGAMBEReport")); + } if((gs232_controller_report != nullptr) && (gs232_controller_report->isSet())){ toJsonValue(QString("GS232ControllerReport"), gs232_controller_report, obj, QString("SWGGS232ControllerReport")); } @@ -225,6 +237,16 @@ SWGFeatureReport::setAfcReport(SWGAFCReport* afc_report) { this->m_afc_report_isSet = true; } +SWGAMBEReport* +SWGFeatureReport::getAmbeReport() { + return ambe_report; +} +void +SWGFeatureReport::setAmbeReport(SWGAMBEReport* ambe_report) { + this->ambe_report = ambe_report; + this->m_ambe_report_isSet = true; +} + SWGGS232ControllerReport* SWGFeatureReport::getGs232ControllerReport() { return gs232_controller_report; @@ -326,6 +348,9 @@ SWGFeatureReport::isSet(){ if(afc_report && afc_report->isSet()){ isObjectUpdated = true; break; } + if(ambe_report && ambe_report->isSet()){ + isObjectUpdated = true; break; + } if(gs232_controller_report && gs232_controller_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h index 25c6c51d1..563e26163 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h @@ -23,6 +23,7 @@ #include "SWGAFCReport.h" +#include "SWGAMBEReport.h" #include "SWGGS232ControllerReport.h" #include "SWGLimeRFEReport.h" #include "SWGMapReport.h" @@ -58,6 +59,9 @@ public: SWGAFCReport* getAfcReport(); void setAfcReport(SWGAFCReport* afc_report); + SWGAMBEReport* getAmbeReport(); + void setAmbeReport(SWGAMBEReport* ambe_report); + SWGGS232ControllerReport* getGs232ControllerReport(); void setGs232ControllerReport(SWGGS232ControllerReport* gs232_controller_report); @@ -95,6 +99,9 @@ private: SWGAFCReport* afc_report; bool m_afc_report_isSet; + SWGAMBEReport* ambe_report; + bool m_ambe_report_isSet; + SWGGS232ControllerReport* gs232_controller_report; bool m_gs232_controller_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp index 61595592e..992647cd0 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.cpp @@ -38,6 +38,8 @@ SWGFeatureSettings::SWGFeatureSettings() { m_afc_settings_isSet = false; ais_settings = nullptr; m_ais_settings_isSet = false; + ambe_settings = nullptr; + m_ambe_settings_isSet = false; antenna_tools_settings = nullptr; m_antenna_tools_settings_isSet = false; aprs_settings = nullptr; @@ -84,6 +86,8 @@ SWGFeatureSettings::init() { m_afc_settings_isSet = false; ais_settings = new SWGAISSettings(); m_ais_settings_isSet = false; + ambe_settings = new SWGAMBESettings(); + m_ambe_settings_isSet = false; antenna_tools_settings = new SWGAntennaToolsSettings(); m_antenna_tools_settings_isSet = false; aprs_settings = new SWGAPRSSettings(); @@ -127,6 +131,9 @@ SWGFeatureSettings::cleanup() { if(ais_settings != nullptr) { delete ais_settings; } + if(ambe_settings != nullptr) { + delete ambe_settings; + } if(antenna_tools_settings != nullptr) { delete antenna_tools_settings; } @@ -192,6 +199,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&ais_settings, pJson["AISSettings"], "SWGAISSettings", "SWGAISSettings"); + ::SWGSDRangel::setValue(&ambe_settings, pJson["AMBESettings"], "SWGAMBESettings", "SWGAMBESettings"); + ::SWGSDRangel::setValue(&antenna_tools_settings, pJson["AntennaToolsSettings"], "SWGAntennaToolsSettings", "SWGAntennaToolsSettings"); ::SWGSDRangel::setValue(&aprs_settings, pJson["APRSSettings"], "SWGAPRSSettings", "SWGAPRSSettings"); @@ -251,6 +260,9 @@ SWGFeatureSettings::asJsonObject() { if((ais_settings != nullptr) && (ais_settings->isSet())){ toJsonValue(QString("AISSettings"), ais_settings, obj, QString("SWGAISSettings")); } + if((ambe_settings != nullptr) && (ambe_settings->isSet())){ + toJsonValue(QString("AMBESettings"), ambe_settings, obj, QString("SWGAMBESettings")); + } if((antenna_tools_settings != nullptr) && (antenna_tools_settings->isSet())){ toJsonValue(QString("AntennaToolsSettings"), antenna_tools_settings, obj, QString("SWGAntennaToolsSettings")); } @@ -347,6 +359,16 @@ SWGFeatureSettings::setAisSettings(SWGAISSettings* ais_settings) { this->m_ais_settings_isSet = true; } +SWGAMBESettings* +SWGFeatureSettings::getAmbeSettings() { + return ambe_settings; +} +void +SWGFeatureSettings::setAmbeSettings(SWGAMBESettings* ambe_settings) { + this->ambe_settings = ambe_settings; + this->m_ambe_settings_isSet = true; +} + SWGAntennaToolsSettings* SWGFeatureSettings::getAntennaToolsSettings() { return antenna_tools_settings; @@ -507,6 +529,9 @@ SWGFeatureSettings::isSet(){ if(ais_settings && ais_settings->isSet()){ isObjectUpdated = true; break; } + if(ambe_settings && ambe_settings->isSet()){ + isObjectUpdated = true; break; + } if(antenna_tools_settings && antenna_tools_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h index 3271cb913..27b5d305a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSettings.h @@ -24,6 +24,7 @@ #include "SWGAFCSettings.h" #include "SWGAISSettings.h" +#include "SWGAMBESettings.h" #include "SWGAPRSSettings.h" #include "SWGAntennaToolsSettings.h" #include "SWGDemodAnalyzerSettings.h" @@ -73,6 +74,9 @@ public: SWGAISSettings* getAisSettings(); void setAisSettings(SWGAISSettings* ais_settings); + SWGAMBESettings* getAmbeSettings(); + void setAmbeSettings(SWGAMBESettings* ambe_settings); + SWGAntennaToolsSettings* getAntennaToolsSettings(); void setAntennaToolsSettings(SWGAntennaToolsSettings* antenna_tools_settings); @@ -134,6 +138,9 @@ private: SWGAISSettings* ais_settings; bool m_ais_settings_isSet; + SWGAMBESettings* ambe_settings; + bool m_ambe_settings_isSet; + SWGAntennaToolsSettings* antenna_tools_settings; bool m_antenna_tools_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 2cb26fd51..3d7f6bfc6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -25,8 +25,13 @@ #include "SWGAISModReport.h" #include "SWGAISModSettings.h" #include "SWGAISSettings.h" +#include "SWGAMBEActions.h" #include "SWGAMBEDevice.h" +#include "SWGAMBEDevice_2.h" #include "SWGAMBEDevices.h" +#include "SWGAMBEDevices_2.h" +#include "SWGAMBEReport.h" +#include "SWGAMBESettings.h" #include "SWGAMDemodReport.h" #include "SWGAMDemodSettings.h" #include "SWGAMModReport.h" @@ -94,7 +99,9 @@ #include "SWGDSDDemodReport.h" #include "SWGDSDDemodSettings.h" #include "SWGDVSerialDevice.h" +#include "SWGDVSerialDevice_2.h" #include "SWGDVSerialDevices.h" +#include "SWGDVSerialDevices_2.h" #include "SWGDemodAnalyzerSettings.h" #include "SWGDeviceActions.h" #include "SWGDeviceConfig.h" @@ -384,16 +391,41 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGAMBEActions").compare(type) == 0) { + SWGAMBEActions *obj = new SWGAMBEActions(); + obj->init(); + return obj; + } if(QString("SWGAMBEDevice").compare(type) == 0) { SWGAMBEDevice *obj = new SWGAMBEDevice(); obj->init(); return obj; } + if(QString("SWGAMBEDevice_2").compare(type) == 0) { + SWGAMBEDevice_2 *obj = new SWGAMBEDevice_2(); + obj->init(); + return obj; + } if(QString("SWGAMBEDevices").compare(type) == 0) { SWGAMBEDevices *obj = new SWGAMBEDevices(); obj->init(); return obj; } + if(QString("SWGAMBEDevices_2").compare(type) == 0) { + SWGAMBEDevices_2 *obj = new SWGAMBEDevices_2(); + obj->init(); + return obj; + } + if(QString("SWGAMBEReport").compare(type) == 0) { + SWGAMBEReport *obj = new SWGAMBEReport(); + obj->init(); + return obj; + } + if(QString("SWGAMBESettings").compare(type) == 0) { + SWGAMBESettings *obj = new SWGAMBESettings(); + obj->init(); + return obj; + } if(QString("SWGAMDemodReport").compare(type) == 0) { SWGAMDemodReport *obj = new SWGAMDemodReport(); obj->init(); @@ -729,11 +761,21 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGDVSerialDevice_2").compare(type) == 0) { + SWGDVSerialDevice_2 *obj = new SWGDVSerialDevice_2(); + obj->init(); + return obj; + } if(QString("SWGDVSerialDevices").compare(type) == 0) { SWGDVSerialDevices *obj = new SWGDVSerialDevices(); obj->init(); return obj; } + if(QString("SWGDVSerialDevices_2").compare(type) == 0) { + SWGDVSerialDevices_2 *obj = new SWGDVSerialDevices_2(); + obj->init(); + return obj; + } if(QString("SWGDemodAnalyzerSettings").compare(type) == 0) { SWGDemodAnalyzerSettings *obj = new SWGDemodAnalyzerSettings(); obj->init(); From bd4c633e9d39d4c4df753709a8330c4069bcff5f Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 24 May 2022 23:36:04 +0200 Subject: [PATCH 15/29] AMBE feature: cleanup of AMBE API support in Instance --- plugins/feature/ambe/ambe.cpp | 6 +- sdrbase/resources/webapi/doc/html2/index.html | 1862 +---------------- .../webapi/doc/swagger/include/AMBE.yaml | 4 +- .../resources/webapi/doc/swagger/swagger.yaml | 139 -- sdrbase/webapi/webapiadapter.cpp | 179 -- sdrbase/webapi/webapiadapter.h | 31 - sdrbase/webapi/webapiadapterinterface.cpp | 2 - sdrbase/webapi/webapiadapterinterface.h | 78 - sdrbase/webapi/webapirequestmapper.cpp | 158 -- sdrbase/webapi/webapirequestmapper.h | 4 - swagger/sdrangel/api/swagger/swagger.yaml | 139 -- swagger/sdrangel/code/html2/index.html | 1862 +---------------- .../code/qt5/client/SWGAMBEActions.cpp | 10 +- .../sdrangel/code/qt5/client/SWGAMBEActions.h | 8 +- .../code/qt5/client/SWGAMBEDevice_2.cpp | 133 -- .../code/qt5/client/SWGAMBEDevice_2.h | 65 - .../code/qt5/client/SWGAMBEDevices_2.cpp | 137 -- .../code/qt5/client/SWGAMBEDevices_2.h | 66 - .../code/qt5/client/SWGAMBEReport.cpp | 20 +- .../sdrangel/code/qt5/client/SWGAMBEReport.h | 16 +- .../code/qt5/client/SWGDVSerialDevice_2.cpp | 110 - .../code/qt5/client/SWGDVSerialDevice_2.h | 59 - .../code/qt5/client/SWGDVSerialDevices_2.cpp | 137 -- .../code/qt5/client/SWGDVSerialDevices_2.h | 66 - .../code/qt5/client/SWGInstanceApi.cpp | 266 --- .../sdrangel/code/qt5/client/SWGInstanceApi.h | 27 - .../code/qt5/client/SWGModelFactory.h | 24 - 27 files changed, 44 insertions(+), 5564 deletions(-) delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h diff --git a/plugins/feature/ambe/ambe.cpp b/plugins/feature/ambe/ambe.cpp index f62e825f8..7457f06da 100644 --- a/plugins/feature/ambe/ambe.cpp +++ b/plugins/feature/ambe/ambe.cpp @@ -330,7 +330,7 @@ void AMBE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) { // serial - SWGSDRangel::SWGDVSerialDevices_2 *swgDVSerialDevices = response.getAmbeReport()->getSerial(); + SWGSDRangel::SWGDVSerialDevices *swgDVSerialDevices = response.getAmbeReport()->getSerial(); swgDVSerialDevices->init(); QList qDeviceNames; @@ -347,7 +347,7 @@ void AMBE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) // devices - SWGSDRangel::SWGAMBEDevices_2 *swgAMBEDevices = response.getAmbeReport()->getDevices(); + SWGSDRangel::SWGAMBEDevices *swgAMBEDevices = response.getAmbeReport()->getDevices(); swgAMBEDevices->init(); qDeviceNames.clear(); @@ -393,7 +393,7 @@ int AMBE::webapiActionsPost( { unknownAction = false; bool updated = false; - SWGSDRangel::SWGAMBEDevices_2 *swgAMBEDevices = swgAMBEActions->getUpdateDevices(); + SWGSDRangel::SWGAMBEDevices *swgAMBEDevices = swgAMBEActions->getUpdateDevices(); QList *ambeList = swgAMBEDevices->getAmbeDevices(); for (QList::const_iterator it = ambeList->begin(); it != ambeList->end(); ++it) diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index ea79bf8c3..136e97d20 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -1253,7 +1253,7 @@ margin-bottom: 20px; "properties" : { "updateDevices" : { "description" : "Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing", - "$ref" : "#/definitions/AMBEDevices_2" + "$ref" : "#/definitions/AMBEDevices" }, "removeAll" : { "type" : "integer", @@ -1274,19 +1274,6 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" -}; - defs.AMBEDevice_2 = { - "properties" : { - "deviceRef" : { - "type" : "string", - "description" : "Serial device name or server address" - }, - "delete" : { - "type" : "integer", - "description" : "1 if device is to be removed from active list" - } - }, - "description" : "AMBE devices active in the system" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1304,33 +1291,16 @@ margin-bottom: 20px; } }, "description" : "List of AMBE devices (serial or server address)" -}; - defs.AMBEDevices_2 = { - "required" : [ "nbDevices" ], - "properties" : { - "nbDevices" : { - "type" : "integer", - "description" : "Number of DV serial devices" - }, - "ambeDevices" : { - "type" : "array", - "description" : "List of AMBE devices", - "items" : { - "$ref" : "#/definitions/AMBEDevice" - } - } - }, - "description" : "List of AMBE devices (serial or server address)" }; defs.AMBEReport = { "properties" : { "serial" : { - "description" : "List of available DV serial devices", - "$ref" : "#/definitions/DVSerialDevices_2" + "description" : "List of AMBE serial devices in the system", + "$ref" : "#/definitions/DVSerialDevices" }, "devices" : { - "description" : "List of AMBE devices (serial or address) used for AMBE frames processing", - "$ref" : "#/definitions/AMBEDevices_2" + "description" : "List of AMBE devices or servers in use", + "$ref" : "#/definitions/AMBEDevices" } }, "description" : "AMBE" @@ -4711,15 +4681,6 @@ margin-bottom: 20px; } }, "description" : "DV serial device details" -}; - defs.DVSerialDevice_2 = { - "properties" : { - "deviceName" : { - "type" : "string", - "description" : "Name of the serial device in the system" - } - }, - "description" : "DV serial device details" }; defs.DVSerialDevices = { "required" : [ "nbDevices" ], @@ -4737,23 +4698,6 @@ margin-bottom: 20px; } }, "description" : "List of DV serial devices available in the system" -}; - defs.DVSerialDevices_2 = { - "required" : [ "nbDevices" ], - "properties" : { - "nbDevices" : { - "type" : "integer", - "description" : "Number of DV serial devices" - }, - "dvSerialDevices" : { - "type" : "array", - "description" : "Device names of DV serial devices", - "items" : { - "$ref" : "#/definitions/DVSerialDevice" - } - } - }, - "description" : "List of DV serial devices available in the system" }; defs.DemodAnalyzerSettings = { "properties" : { @@ -14837,21 +14781,6 @@ margin-bottom: 20px;
    featuresetPresetPut -
  • - instanceAMBEDevicesDelete -
  • -
  • - instanceAMBEDevicesGet -
  • -
  • - instanceAMBEDevicesPatch -
  • -
  • - instanceAMBEDevicesPut -
  • -
  • - instanceAMBESerialGet -
  • instanceAudioGet
  • @@ -38965,1785 +38894,6 @@ $(document).ready(function() {

    Instance

    -
    -
    -
    -

    instanceAMBEDevicesDelete

    -

    -
    -
    -
    -

    -

    Emtpy the active devices thus effectively closing down AMBE devices support

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X DELETE "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesDeleteWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesDelete(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesDeleteExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesDelete: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesDelete();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesDelete: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesDelete();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesDelete: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_delete()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesDelete: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - Success.

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesGet

    -

    -
    -
    -
    -

    -

    get the list of AMBE devices (serial or address) used for AMBE frames decoding in digital voice modes

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesGetWithCompletionHandler: 
    -              ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of devices possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesPatch

    -

    -
    -
    -
    -

    -

    Add and/or delete devices to/from the active list

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PATCH "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPatch");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPatch");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    AMBEDevices *body = ; // List of AMBE devices (serial or address)
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesPatchWith:body
    -              completionHandler: ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {AMBEDevices} List of AMBE devices (serial or address)
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesPatch(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesPatchExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new AMBEDevices(); // AMBEDevices | List of AMBE devices (serial or address)
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesPatch: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesPatch($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesPatch: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::AMBEDevices->new(); # AMBEDevices | List of AMBE devices (serial or address)
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesPatch(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesPatch: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # AMBEDevices | List of AMBE devices (serial or address)
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_patch(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesPatch: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - On success return list of devices

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesPut

    -

    -
    -
    -
    -

    -

    Replace the list of active devices

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    AMBEDevices *body = ; // List of AMBE devices (serial or address)
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesPutWith:body
    -              completionHandler: ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {AMBEDevices} List of AMBE devices (serial or address)
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new AMBEDevices(); // AMBEDevices | List of AMBE devices (serial or address)
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::AMBEDevices->new(); # AMBEDevices | List of AMBE devices (serial or address)
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # AMBEDevices | List of AMBE devices (serial or address)
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - On success return list of devices

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBESerialGet

    -

    -
    -
    -
    -

    -

    get a list of available DV serial devices

    -

    -
    -
    /sdrangel/ambe/serial
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/ambe/serial"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBESerialGetWithCompletionHandler: 
    -              ^(DVSerialDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBESerialGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBESerialGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBESerialGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBESerialGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBESerialGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBESerialGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBESerialGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_serial_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBESerialGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of device paths possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -57890,7 +56040,7 @@ except ApiException as e:
    - Generated 2022-05-24T21:52:35.030+02:00 + Generated 2022-05-24T23:13:14.725+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml index 9b4d070ef..275857baf 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml @@ -23,10 +23,10 @@ AMBEReport: description: AMBE properties: serial: - description: List of available DV serial devices + description: List of AMBE serial devices in the system $ref: "/doc/swagger/include/AMBE.yaml#/definitions/DVSerialDevices" devices: - description: List of AMBE devices (serial or address) used for AMBE frames processing + description: List of AMBE devices or servers in use $ref: "/doc/swagger/include/AMBE.yaml#/definitions/AMBEDevices" AMBEActions: diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index 5c3799cfe..fed3b778b 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -454,100 +454,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/ambe/serial: - x-swagger-router-controller: instance - get: - description: get a list of available DV serial devices - operationId: instanceAMBESerialGet - tags: - - Instance - responses: - "200": - description: On success return list of device paths possibly empty - schema: - $ref: "#/definitions/DVSerialDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/ambe/devices: - x-swagger-router-controller: instance - get: - description: get the list of AMBE devices (serial or address) used for AMBE frames decoding in digital voice modes - operationId: InstanceAMBEDevicesGet - tags: - - Instance - responses: - "200": - description: On success return list of devices possibly empty - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - put: - description: Replace the list of active devices - operationId: InstanceAMBEDevicesPut - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: List of AMBE devices (serial or address) - required: true - schema: - $ref: "#/definitions/AMBEDevices" - responses: - "200": - description: On success return list of devices - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - delete: - description: Emtpy the active devices thus effectively closing down AMBE devices support - operationId: InstanceAMBEDevicesDelete - tags: - - Instance - responses: - "200": - description: Success. - schema: - $ref: "#/definitions/SuccessResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - patch: - description: Add and/or delete devices to/from the active list - operationId: InstanceAMBEDevicesPatch - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: List of AMBE devices (serial or address) - required: true - schema: - $ref: "#/definitions/AMBEDevices" - responses: - "200": - description: On success return list of devices - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/presets: x-swagger-router-controller: instance get: @@ -3311,51 +3217,6 @@ definitions: type: number format: float - DVSerialDevices: - description: "List of DV serial devices available in the system" - required: - - nbDevices - properties: - nbDevices: - description: "Number of DV serial devices" - type: integer - dvSerialDevices: - description: "Device names of DV serial devices" - type: array - items: - $ref: "#/definitions/DVSerialDevice" - - DVSerialDevice: - description: "DV serial device details" - properties: - deviceName: - description: "Name of the serial device in the system" - type: string - - AMBEDevices: - description: "List of AMBE devices (serial or server address)" - required: - - nbDevices - properties: - nbDevices: - description: "Number of DV serial devices" - type: integer - ambeDevices: - description: "List of AMBE devices" - type: array - items: - $ref: "#/definitions/AMBEDevice" - - AMBEDevice: - description: "AMBE devices active in the system" - properties: - deviceRef: - description: "Serial device name or server address" - type: string - delete: - description: "1 if device is to be removed from active list" - type: integer - LimeRFEDevices: description: "List of LimeRFE devices (serial or server address)" required: diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index b7c30ad7f..1a6e57976 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -51,9 +51,6 @@ #include "SWGDeviceListItem.h" #include "SWGAudioDevices.h" #include "SWGLocationInformation.h" -#include "SWGDVSerialDevices.h" -#include "SWGDVSerialDevice.h" -#include "SWGAMBEDevices.h" #include "SWGPresets.h" #include "SWGPresetGroup.h" #include "SWGPresetItem.h" @@ -753,182 +750,6 @@ int WebAPIAdapter::instanceLocationPut( return 200; } -int WebAPIAdapter::instanceDVSerialGet( - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - DSPEngine *dspEngine = DSPEngine::instance(); - response.init(); - - std::vector deviceNames; - dspEngine->getDVSerialNames(deviceNames); - response.setNbDevices((int) deviceNames.size()); - QList *deviceNamesList = response.getDvSerialDevices(); - - std::vector::iterator it = deviceNames.begin(); - - while (it != deviceNames.end()) - { - deviceNamesList->append(new SWGSDRangel::SWGDVSerialDevice); - deviceNamesList->back()->init(); - *deviceNamesList->back()->getDeviceName() = QString::fromStdString(*it); - ++it; - } - - return 200; -} - -int WebAPIAdapter::instanceDVSerialPatch( - bool dvserial, - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - DSPEngine *dspEngine = DSPEngine::instance(); - dspEngine->setDVSerialSupport(dvserial); - - MainCore::MsgDVSerial *msg = MainCore::MsgDVSerial::create(dvserial); - m_mainCore->m_mainMessageQueue->push(msg); - response.init(); - - if (dvserial) - { - std::vector deviceNames; - dspEngine->getDVSerialNames(deviceNames); - response.setNbDevices((int) deviceNames.size()); - QList *deviceNamesList = response.getDvSerialDevices(); - - std::vector::iterator it = deviceNames.begin(); - - while (it != deviceNames.end()) - { - deviceNamesList->append(new SWGSDRangel::SWGDVSerialDevice); - deviceNamesList->back()->init(); - *deviceNamesList->back()->getDeviceName() = QString::fromStdString(*it); - ++it; - } - } - else - { - response.setNbDevices(0); - } - - return 200; -} - -int WebAPIAdapter::instanceAMBESerialGet( - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - DSPEngine *dspEngine = DSPEngine::instance(); - response.init(); - - std::vector deviceNames; - std::vector qDeviceNames; - dspEngine->getAMBEEngine()->scan(qDeviceNames); - - for (std::vector::const_iterator it = qDeviceNames.begin(); it != qDeviceNames.end(); ++it) { - deviceNames.push_back(it->toStdString()); - } - - response.setNbDevices((int) deviceNames.size()); - QList *deviceNamesList = response.getDvSerialDevices(); - - std::vector::iterator it = deviceNames.begin(); - - while (it != deviceNames.end()) - { - deviceNamesList->append(new SWGSDRangel::SWGDVSerialDevice); - deviceNamesList->back()->init(); - *deviceNamesList->back()->getDeviceName() = QString::fromStdString(*it); - ++it; - } - - return 200; -} - -int WebAPIAdapter::instanceAMBEDevicesGet( - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - DSPEngine *dspEngine = DSPEngine::instance(); - response.init(); - - std::vector deviceNames; - dspEngine->getDVSerialNames(deviceNames); - response.setNbDevices((int) deviceNames.size()); - QList *deviceNamesList = response.getAmbeDevices(); - - std::vector::iterator it = deviceNames.begin(); - - while (it != deviceNames.end()) - { - deviceNamesList->append(new SWGSDRangel::SWGAMBEDevice); - deviceNamesList->back()->init(); - *deviceNamesList->back()->getDeviceRef() = QString::fromStdString(*it); - deviceNamesList->back()->setDelete(0); - ++it; - } - - return 200; -} - -int WebAPIAdapter::instanceAMBEDevicesDelete( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - DSPEngine *dspEngine = DSPEngine::instance(); - dspEngine->getAMBEEngine()->releaseAll(); - - response.init(); - *response.getMessage() = QString("All AMBE devices released"); - - return 200; -} - -int WebAPIAdapter::instanceAMBEDevicesPut( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - DSPEngine *dspEngine = DSPEngine::instance(); - dspEngine->getAMBEEngine()->releaseAll(); - - QList *ambeList = query.getAmbeDevices(); - - for (QList::const_iterator it = ambeList->begin(); it != ambeList->end(); ++it) { - dspEngine->getAMBEEngine()->registerController((*it)->getDeviceRef()->toStdString()); - } - - instanceAMBEDevicesGet(response, error); - return 200; -} - -int WebAPIAdapter::instanceAMBEDevicesPatch( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) -{ - DSPEngine *dspEngine = DSPEngine::instance(); - QList *ambeList = query.getAmbeDevices(); - - for (QList::const_iterator it = ambeList->begin(); it != ambeList->end(); ++it) - { - if ((*it)->getDelete()) { - dspEngine->getAMBEEngine()->releaseController((*it)->getDeviceRef()->toStdString()); - } else { - dspEngine->getAMBEEngine()->registerController((*it)->getDeviceRef()->toStdString()); - } - } - - instanceAMBEDevicesGet(response, error); - return 200; -} - int WebAPIAdapter::instancePresetsGet( SWGSDRangel::SWGPresets& response, SWGSDRangel::SWGErrorResponse& error) diff --git a/sdrbase/webapi/webapiadapter.h b/sdrbase/webapi/webapiadapter.h index 22e6aaddb..efcc1a4d4 100644 --- a/sdrbase/webapi/webapiadapter.h +++ b/sdrbase/webapi/webapiadapter.h @@ -115,37 +115,6 @@ public: SWGSDRangel::SWGLocationInformation& response, SWGSDRangel::SWGErrorResponse& error); - virtual int instanceDVSerialGet( - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceDVSerialPatch( - bool dvserial, - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceAMBESerialGet( - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceAMBEDevicesGet( - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceAMBEDevicesPut( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceAMBEDevicesPatch( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceAMBEDevicesDelete( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error); - virtual int instancePresetsGet( SWGSDRangel::SWGPresets& response, SWGSDRangel::SWGErrorResponse& error); diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index 9c3e15048..125cc3e3e 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -31,8 +31,6 @@ QString WebAPIAdapterInterface::instanceAudioOutputParametersURL = "/sdrangel/au QString WebAPIAdapterInterface::instanceAudioInputCleanupURL = "/sdrangel/audio/input/cleanup"; QString WebAPIAdapterInterface::instanceAudioOutputCleanupURL = "/sdrangel/audio/output/cleanup"; QString WebAPIAdapterInterface::instanceLocationURL = "/sdrangel/location"; -QString WebAPIAdapterInterface::instanceAMBESerialURL = "/sdrangel/ambe/serial"; -QString WebAPIAdapterInterface::instanceAMBEDevicesURL = "/sdrangel/ambe/devices"; QString WebAPIAdapterInterface::instancePresetsURL = "/sdrangel/presets"; QString WebAPIAdapterInterface::instancePresetURL = "/sdrangel/preset"; QString WebAPIAdapterInterface::instancePresetFileURL = "/sdrangel/preset/file"; diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index 302dfb3c0..cd1862c6b 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -41,8 +41,6 @@ namespace SWGSDRangel class SWGAudioInputDevice; class SWGAudioOutputDevice; class SWGLocationInformation; - class SWGDVSerialDevices; - class SWGAMBEDevices; class SWGLimeRFEDevices; class SWGLimeRFESettings; class SWGLimeRFEPower; @@ -401,80 +399,6 @@ public: return 501; } - /** - * Handler of /sdrangel/ambe/serial (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceAMBESerialGet( - SWGSDRangel::SWGDVSerialDevices& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - - /** - * Handler of /sdrangel/ambe/devices (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceAMBEDevicesGet( - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - - /** - * Handler of /sdrangel/ambe/devices (PUT) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceAMBEDevicesPut( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) query; - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - - /** - * Handler of /sdrangel/ambe/devices (PATCH) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceAMBEDevicesPatch( - SWGSDRangel::SWGAMBEDevices& query, - SWGSDRangel::SWGAMBEDevices& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) query; - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - - /** - * Handler of /sdrangel/ambe/devices (DELETE) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels - * returns the Http status code (default 501: not implemented) - */ - virtual int instanceAMBEDevicesDelete( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) - { - (void) response; - error.init(); - *error.getMessage() = QString("Function not implemented"); - return 501; - } - /** * Handler of /sdrangel/limerfe/serial (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) @@ -1770,8 +1694,6 @@ public: static QString instanceAudioInputCleanupURL; static QString instanceAudioOutputCleanupURL; static QString instanceLocationURL; - static QString instanceAMBESerialURL; - static QString instanceAMBEDevicesURL; static QString instancePresetsURL; static QString instancePresetURL; static QString instancePresetFileURL; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 77ed69f22..291ccb590 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -33,7 +33,6 @@ #include "SWGInstanceFeaturesResponse.h" #include "SWGAudioDevices.h" #include "SWGLocationInformation.h" -#include "SWGDVSerialDevices.h" #include "SWGAMBEDevices.h" #include "SWGLimeRFEDevices.h" #include "SWGLimeRFESettings.h" @@ -138,10 +137,6 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http instanceAudioOutputCleanupService(request, response); } else if (path == WebAPIAdapterInterface::instanceLocationURL) { instanceLocationService(request, response); - } else if (path == WebAPIAdapterInterface::instanceAMBESerialURL) { - instanceAMBESerialService(request, response); - } else if (path == WebAPIAdapterInterface::instanceAMBEDevicesURL) { - instanceAMBEDevicesService(request, response); } else if (path == WebAPIAdapterInterface::instancePresetsURL) { instancePresetsService(request, response); } else if (path == WebAPIAdapterInterface::instancePresetURL) { @@ -799,118 +794,6 @@ void WebAPIRequestMapper::instanceLocationService(qtwebapp::HttpRequest& request } } -void WebAPIRequestMapper::instanceAMBESerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - SWGSDRangel::SWGDVSerialDevices normalResponse; - - int status = m_adapter->instanceAMBESerialGet(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - -void WebAPIRequestMapper::instanceAMBEDevicesService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - SWGSDRangel::SWGAMBEDevices normalResponse; - - int status = m_adapter->instanceAMBEDevicesGet(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else if ((request.getMethod() == "PATCH") || (request.getMethod() == "PUT")) - { - SWGSDRangel::SWGAMBEDevices query; - SWGSDRangel::SWGAMBEDevices normalResponse; - QString jsonStr = request.getBody(); - QJsonObject jsonObject; - - if (parseJsonBody(jsonStr, jsonObject, response)) - { - if (validateAMBEDevices(query, jsonObject)) - { - int status; - - if (request.getMethod() == "PATCH") { - status = m_adapter->instanceAMBEDevicesPatch(query, normalResponse, errorResponse); - } else { - status = m_adapter->instanceAMBEDevicesPut(query, normalResponse, errorResponse); - } - - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON request"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON request"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(400,"Invalid JSON format"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid JSON format"; - response.write(errorResponse.asJson().toUtf8()); - } - } - else if (request.getMethod() == "DELETE") - { - SWGSDRangel::SWGSuccessResponse normalResponse; - - int status = m_adapter->instanceAMBEDevicesDelete(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - void WebAPIRequestMapper::instancePresetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -4057,47 +3940,6 @@ bool WebAPIRequestMapper::validateAudioOutputDevice( return true; } -bool WebAPIRequestMapper::validateAMBEDevices(SWGSDRangel::SWGAMBEDevices& ambeDevices, QJsonObject& jsonObject) -{ - if (jsonObject.contains("nbDevices")) - { - int nbDevices = jsonObject["nbDevices"].toInt(); - - if (jsonObject.contains("ambeDevices")) - { - QJsonArray ambeDevicesJson = jsonObject["ambeDevices"].toArray(); - - if (nbDevices != ambeDevicesJson.size()) { - return false; - } - - ambeDevices.init(); - ambeDevices.setNbDevices(nbDevices); - QList *ambeList = ambeDevices.getAmbeDevices(); - - for (int i = 0; i < nbDevices; i++) - { - QJsonObject ambeDeviceJson = ambeDevicesJson.at(i).toObject(); - if (ambeDeviceJson.contains("deviceRef") && ambeDeviceJson.contains("delete")) - { - ambeList->push_back(new SWGSDRangel::SWGAMBEDevice()); - ambeList->back()->init(); - ambeList->back()->setDeviceRef(new QString(ambeDeviceJson["deviceRef"].toString())); - ambeList->back()->setDelete(ambeDeviceJson["delete"].toInt()); - } - else - { - return false; - } - } - - return true; - } - } - - return false; -} - bool WebAPIRequestMapper::validateSpectrumSettings(SWGSDRangel::SWGGLSpectrum& spectrumSettings, QJsonObject& jsonObject, QStringList& spectrumSettingsKeys) { extractKeys(jsonObject, spectrumSettingsKeys); diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index 2bf22e846..5c7f0dec8 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -66,9 +66,6 @@ private: void instanceAudioInputCleanupService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceAudioOutputCleanupService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceLocationService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceDVSerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceAMBESerialService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceAMBEDevicesService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instancePresetFileService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); @@ -126,7 +123,6 @@ private: bool validateFeatureActions(SWGSDRangel::SWGFeatureActions& featureActions, QJsonObject& jsonObject, QStringList& featureActionsKeys); bool validateAudioInputDevice(SWGSDRangel::SWGAudioInputDevice& audioInputDevice, QJsonObject& jsonObject, QStringList& audioInputDeviceKeys); bool validateAudioOutputDevice(SWGSDRangel::SWGAudioOutputDevice& audioOutputDevice, QJsonObject& jsonObject, QStringList& audioOutputDeviceKeys); - bool validateAMBEDevices(SWGSDRangel::SWGAMBEDevices& ambeDevices, QJsonObject& jsonObject); bool validateConfig(SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, WebAPIAdapterInterface::ConfigKeys& configKeys); bool validateWorkspaceInfo(SWGSDRangel::SWGWorkspaceInfo& workspaceInfo, QJsonObject& jsonObject); bool validateConfigurationIdentifier(SWGSDRangel::SWGConfigurationIdentifier& configurationIdentifier); diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index 70b12dfad..321c79e11 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -454,100 +454,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/ambe/serial: - x-swagger-router-controller: instance - get: - description: get a list of available DV serial devices - operationId: instanceAMBESerialGet - tags: - - Instance - responses: - "200": - description: On success return list of device paths possibly empty - schema: - $ref: "#/definitions/DVSerialDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - - /sdrangel/ambe/devices: - x-swagger-router-controller: instance - get: - description: get the list of AMBE devices (serial or address) used for AMBE frames decoding in digital voice modes - operationId: InstanceAMBEDevicesGet - tags: - - Instance - responses: - "200": - description: On success return list of devices possibly empty - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - put: - description: Replace the list of active devices - operationId: InstanceAMBEDevicesPut - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: List of AMBE devices (serial or address) - required: true - schema: - $ref: "#/definitions/AMBEDevices" - responses: - "200": - description: On success return list of devices - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - delete: - description: Emtpy the active devices thus effectively closing down AMBE devices support - operationId: InstanceAMBEDevicesDelete - tags: - - Instance - responses: - "200": - description: Success. - schema: - $ref: "#/definitions/SuccessResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - patch: - description: Add and/or delete devices to/from the active list - operationId: InstanceAMBEDevicesPatch - tags: - - Instance - consumes: - - application/json - parameters: - - name: body - in: body - description: List of AMBE devices (serial or address) - required: true - schema: - $ref: "#/definitions/AMBEDevices" - responses: - "200": - description: On success return list of devices - schema: - $ref: "#/definitions/AMBEDevices" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/presets: x-swagger-router-controller: instance get: @@ -3311,51 +3217,6 @@ definitions: type: number format: float - DVSerialDevices: - description: "List of DV serial devices available in the system" - required: - - nbDevices - properties: - nbDevices: - description: "Number of DV serial devices" - type: integer - dvSerialDevices: - description: "Device names of DV serial devices" - type: array - items: - $ref: "#/definitions/DVSerialDevice" - - DVSerialDevice: - description: "DV serial device details" - properties: - deviceName: - description: "Name of the serial device in the system" - type: string - - AMBEDevices: - description: "List of AMBE devices (serial or server address)" - required: - - nbDevices - properties: - nbDevices: - description: "Number of DV serial devices" - type: integer - ambeDevices: - description: "List of AMBE devices" - type: array - items: - $ref: "#/definitions/AMBEDevice" - - AMBEDevice: - description: "AMBE devices active in the system" - properties: - deviceRef: - description: "Serial device name or server address" - type: string - delete: - description: "1 if device is to be removed from active list" - type: integer - LimeRFEDevices: description: "List of LimeRFE devices (serial or server address)" required: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index ea79bf8c3..136e97d20 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -1253,7 +1253,7 @@ margin-bottom: 20px; "properties" : { "updateDevices" : { "description" : "Add or remove AMBE devices (serial or address) int the list to be used for AMBE frames processing", - "$ref" : "#/definitions/AMBEDevices_2" + "$ref" : "#/definitions/AMBEDevices" }, "removeAll" : { "type" : "integer", @@ -1274,19 +1274,6 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" -}; - defs.AMBEDevice_2 = { - "properties" : { - "deviceRef" : { - "type" : "string", - "description" : "Serial device name or server address" - }, - "delete" : { - "type" : "integer", - "description" : "1 if device is to be removed from active list" - } - }, - "description" : "AMBE devices active in the system" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1304,33 +1291,16 @@ margin-bottom: 20px; } }, "description" : "List of AMBE devices (serial or server address)" -}; - defs.AMBEDevices_2 = { - "required" : [ "nbDevices" ], - "properties" : { - "nbDevices" : { - "type" : "integer", - "description" : "Number of DV serial devices" - }, - "ambeDevices" : { - "type" : "array", - "description" : "List of AMBE devices", - "items" : { - "$ref" : "#/definitions/AMBEDevice" - } - } - }, - "description" : "List of AMBE devices (serial or server address)" }; defs.AMBEReport = { "properties" : { "serial" : { - "description" : "List of available DV serial devices", - "$ref" : "#/definitions/DVSerialDevices_2" + "description" : "List of AMBE serial devices in the system", + "$ref" : "#/definitions/DVSerialDevices" }, "devices" : { - "description" : "List of AMBE devices (serial or address) used for AMBE frames processing", - "$ref" : "#/definitions/AMBEDevices_2" + "description" : "List of AMBE devices or servers in use", + "$ref" : "#/definitions/AMBEDevices" } }, "description" : "AMBE" @@ -4711,15 +4681,6 @@ margin-bottom: 20px; } }, "description" : "DV serial device details" -}; - defs.DVSerialDevice_2 = { - "properties" : { - "deviceName" : { - "type" : "string", - "description" : "Name of the serial device in the system" - } - }, - "description" : "DV serial device details" }; defs.DVSerialDevices = { "required" : [ "nbDevices" ], @@ -4737,23 +4698,6 @@ margin-bottom: 20px; } }, "description" : "List of DV serial devices available in the system" -}; - defs.DVSerialDevices_2 = { - "required" : [ "nbDevices" ], - "properties" : { - "nbDevices" : { - "type" : "integer", - "description" : "Number of DV serial devices" - }, - "dvSerialDevices" : { - "type" : "array", - "description" : "Device names of DV serial devices", - "items" : { - "$ref" : "#/definitions/DVSerialDevice" - } - } - }, - "description" : "List of DV serial devices available in the system" }; defs.DemodAnalyzerSettings = { "properties" : { @@ -14837,21 +14781,6 @@ margin-bottom: 20px; featuresetPresetPut -
  • - instanceAMBEDevicesDelete -
  • -
  • - instanceAMBEDevicesGet -
  • -
  • - instanceAMBEDevicesPatch -
  • -
  • - instanceAMBEDevicesPut -
  • -
  • - instanceAMBESerialGet -
  • instanceAudioGet
  • @@ -38965,1785 +38894,6 @@ $(document).ready(function() {

    Instance

    -
    -
    -
    -

    instanceAMBEDevicesDelete

    -

    -
    -
    -
    -

    -

    Emtpy the active devices thus effectively closing down AMBE devices support

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X DELETE "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesDeleteWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesDelete(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesDeleteExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceAMBEDevicesDelete();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesDelete: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesDelete();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesDelete: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesDelete();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesDelete: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_delete()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesDelete: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - Success.

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesGet

    -

    -
    -
    -
    -

    -

    get the list of AMBE devices (serial or address) used for AMBE frames decoding in digital voice modes

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesGetWithCompletionHandler: 
    -              ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of devices possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesPatch

    -

    -
    -
    -
    -

    -

    Add and/or delete devices to/from the active list

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PATCH "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPatch");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPatch");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    AMBEDevices *body = ; // List of AMBE devices (serial or address)
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesPatchWith:body
    -              completionHandler: ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {AMBEDevices} List of AMBE devices (serial or address)
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesPatch(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesPatchExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new AMBEDevices(); // AMBEDevices | List of AMBE devices (serial or address)
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesPatch(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesPatch: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesPatch($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesPatch: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::AMBEDevices->new(); # AMBEDevices | List of AMBE devices (serial or address)
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesPatch(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesPatch: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # AMBEDevices | List of AMBE devices (serial or address)
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_patch(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesPatch: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - On success return list of devices

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBEDevicesPut

    -

    -
    -
    -
    -

    -

    Replace the list of active devices

    -

    -
    -
    /sdrangel/ambe/devices
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X PUT "http://localhost/sdrangel/ambe/devices"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        AMBEDevices body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -        try {
    -            AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBEDevicesPut");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    AMBEDevices *body = ; // List of AMBE devices (serial or address)
    -
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBEDevicesPutWith:body
    -              completionHandler: ^(AMBEDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var body = ; // {AMBEDevices} List of AMBE devices (serial or address)
    -
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBEDevicesPut(body, callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBEDevicesPutExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -            var body = new AMBEDevices(); // AMBEDevices | List of AMBE devices (serial or address)
    -
    -            try
    -            {
    -                AMBEDevices result = apiInstance.instanceAMBEDevicesPut(body);
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBEDevicesPut: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -$body = ; // AMBEDevices | List of AMBE devices (serial or address)
    -
    -try {
    -    $result = $api_instance->instanceAMBEDevicesPut($body);
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBEDevicesPut: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -my $body = SWGSDRangel::Object::AMBEDevices->new(); # AMBEDevices | List of AMBE devices (serial or address)
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBEDevicesPut(body => $body);
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBEDevicesPut: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -body =  # AMBEDevices | List of AMBE devices (serial or address)
    -
    -try: 
    -    api_response = api_instance.instance_ambe_devices_put(body)
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBEDevicesPut: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - -
    Body parameters
    - - - - - - - - - -
    NameDescription
    body * - - - -
    -
    - - - -

    Responses

    -

    Status: 200 - On success return list of devices

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceAMBESerialGet

    -

    -
    -
    -
    -

    -

    get a list of available DV serial devices

    -

    -
    -
    /sdrangel/ambe/serial
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/ambe/serial"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceAMBESerialGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceAMBESerialGetWithCompletionHandler: 
    -              ^(DVSerialDevices output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceAMBESerialGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceAMBESerialGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                DVSerialDevices result = apiInstance.instanceAMBESerialGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceAMBESerialGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceAMBESerialGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceAMBESerialGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceAMBESerialGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceAMBESerialGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_ambe_serial_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceAMBESerialGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return list of device paths possibly empty

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -57890,7 +56040,7 @@ except ApiException as e:
    - Generated 2022-05-24T21:52:35.030+02:00 + Generated 2022-05-24T23:13:14.725+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp index 4a6079e5f..3b1891dd0 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.cpp @@ -40,7 +40,7 @@ SWGAMBEActions::~SWGAMBEActions() { void SWGAMBEActions::init() { - update_devices = new SWGAMBEDevices_2(); + update_devices = new SWGAMBEDevices(); m_update_devices_isSet = false; remove_all = 0; m_remove_all_isSet = false; @@ -65,7 +65,7 @@ SWGAMBEActions::fromJson(QString &json) { void SWGAMBEActions::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&update_devices, pJson["updateDevices"], "SWGAMBEDevices_2", "SWGAMBEDevices_2"); + ::SWGSDRangel::setValue(&update_devices, pJson["updateDevices"], "SWGAMBEDevices", "SWGAMBEDevices"); ::SWGSDRangel::setValue(&remove_all, pJson["removeAll"], "qint32", ""); @@ -86,7 +86,7 @@ QJsonObject* SWGAMBEActions::asJsonObject() { QJsonObject* obj = new QJsonObject(); if((update_devices != nullptr) && (update_devices->isSet())){ - toJsonValue(QString("updateDevices"), update_devices, obj, QString("SWGAMBEDevices_2")); + toJsonValue(QString("updateDevices"), update_devices, obj, QString("SWGAMBEDevices")); } if(m_remove_all_isSet){ obj->insert("removeAll", QJsonValue(remove_all)); @@ -95,12 +95,12 @@ SWGAMBEActions::asJsonObject() { return obj; } -SWGAMBEDevices_2* +SWGAMBEDevices* SWGAMBEActions::getUpdateDevices() { return update_devices; } void -SWGAMBEActions::setUpdateDevices(SWGAMBEDevices_2* update_devices) { +SWGAMBEActions::setUpdateDevices(SWGAMBEDevices* update_devices) { this->update_devices = update_devices; this->m_update_devices_isSet = true; } diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h index a742b6071..6ef64370d 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEActions.h @@ -22,7 +22,7 @@ #include -#include "SWGAMBEDevices_2.h" +#include "SWGAMBEDevices.h" #include "SWGObject.h" #include "export.h" @@ -42,8 +42,8 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGAMBEActions* fromJson(QString &jsonString) override; - SWGAMBEDevices_2* getUpdateDevices(); - void setUpdateDevices(SWGAMBEDevices_2* update_devices); + SWGAMBEDevices* getUpdateDevices(); + void setUpdateDevices(SWGAMBEDevices* update_devices); qint32 getRemoveAll(); void setRemoveAll(qint32 remove_all); @@ -52,7 +52,7 @@ public: virtual bool isSet() override; private: - SWGAMBEDevices_2* update_devices; + SWGAMBEDevices* update_devices; bool m_update_devices_isSet; qint32 remove_all; diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp deleted file mode 100644 index 9cfff94c3..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGAMBEDevice_2.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGAMBEDevice_2::SWGAMBEDevice_2(QString* json) { - init(); - this->fromJson(*json); -} - -SWGAMBEDevice_2::SWGAMBEDevice_2() { - device_ref = nullptr; - m_device_ref_isSet = false; - _delete = 0; - m__delete_isSet = false; -} - -SWGAMBEDevice_2::~SWGAMBEDevice_2() { - this->cleanup(); -} - -void -SWGAMBEDevice_2::init() { - device_ref = new QString(""); - m_device_ref_isSet = false; - _delete = 0; - m__delete_isSet = false; -} - -void -SWGAMBEDevice_2::cleanup() { - if(device_ref != nullptr) { - delete device_ref; - } - -} - -SWGAMBEDevice_2* -SWGAMBEDevice_2::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGAMBEDevice_2::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&device_ref, pJson["deviceRef"], "QString", "QString"); - - ::SWGSDRangel::setValue(&_delete, pJson["delete"], "qint32", ""); - -} - -QString -SWGAMBEDevice_2::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGAMBEDevice_2::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(device_ref != nullptr && *device_ref != QString("")){ - toJsonValue(QString("deviceRef"), device_ref, obj, QString("QString")); - } - if(m__delete_isSet){ - obj->insert("delete", QJsonValue(_delete)); - } - - return obj; -} - -QString* -SWGAMBEDevice_2::getDeviceRef() { - return device_ref; -} -void -SWGAMBEDevice_2::setDeviceRef(QString* device_ref) { - this->device_ref = device_ref; - this->m_device_ref_isSet = true; -} - -qint32 -SWGAMBEDevice_2::getDelete() { - return _delete; -} -void -SWGAMBEDevice_2::setDelete(qint32 _delete) { - this->_delete = _delete; - this->m__delete_isSet = true; -} - - -bool -SWGAMBEDevice_2::isSet(){ - bool isObjectUpdated = false; - do{ - if(device_ref && *device_ref != QString("")){ - isObjectUpdated = true; break; - } - if(m__delete_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h b/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h deleted file mode 100644 index 7decb9e28..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEDevice_2.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGAMBEDevice_2.h - * - * AMBE devices active in the system - */ - -#ifndef SWGAMBEDevice_2_H_ -#define SWGAMBEDevice_2_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGAMBEDevice_2: public SWGObject { -public: - SWGAMBEDevice_2(); - SWGAMBEDevice_2(QString* json); - virtual ~SWGAMBEDevice_2(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGAMBEDevice_2* fromJson(QString &jsonString) override; - - QString* getDeviceRef(); - void setDeviceRef(QString* device_ref); - - qint32 getDelete(); - void setDelete(qint32 _delete); - - - virtual bool isSet() override; - -private: - QString* device_ref; - bool m_device_ref_isSet; - - qint32 _delete; - bool m__delete_isSet; - -}; - -} - -#endif /* SWGAMBEDevice_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp deleted file mode 100644 index e3b3935ab..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGAMBEDevices_2.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGAMBEDevices_2::SWGAMBEDevices_2(QString* json) { - init(); - this->fromJson(*json); -} - -SWGAMBEDevices_2::SWGAMBEDevices_2() { - nb_devices = 0; - m_nb_devices_isSet = false; - ambe_devices = nullptr; - m_ambe_devices_isSet = false; -} - -SWGAMBEDevices_2::~SWGAMBEDevices_2() { - this->cleanup(); -} - -void -SWGAMBEDevices_2::init() { - nb_devices = 0; - m_nb_devices_isSet = false; - ambe_devices = new QList(); - m_ambe_devices_isSet = false; -} - -void -SWGAMBEDevices_2::cleanup() { - - if(ambe_devices != nullptr) { - auto arr = ambe_devices; - for(auto o: *arr) { - delete o; - } - delete ambe_devices; - } -} - -SWGAMBEDevices_2* -SWGAMBEDevices_2::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGAMBEDevices_2::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&nb_devices, pJson["nbDevices"], "qint32", ""); - - - ::SWGSDRangel::setValue(&ambe_devices, pJson["ambeDevices"], "QList", "SWGAMBEDevice"); -} - -QString -SWGAMBEDevices_2::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGAMBEDevices_2::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_nb_devices_isSet){ - obj->insert("nbDevices", QJsonValue(nb_devices)); - } - if(ambe_devices && ambe_devices->size() > 0){ - toJsonArray((QList*)ambe_devices, obj, "ambeDevices", "SWGAMBEDevice"); - } - - return obj; -} - -qint32 -SWGAMBEDevices_2::getNbDevices() { - return nb_devices; -} -void -SWGAMBEDevices_2::setNbDevices(qint32 nb_devices) { - this->nb_devices = nb_devices; - this->m_nb_devices_isSet = true; -} - -QList* -SWGAMBEDevices_2::getAmbeDevices() { - return ambe_devices; -} -void -SWGAMBEDevices_2::setAmbeDevices(QList* ambe_devices) { - this->ambe_devices = ambe_devices; - this->m_ambe_devices_isSet = true; -} - - -bool -SWGAMBEDevices_2::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_nb_devices_isSet){ - isObjectUpdated = true; break; - } - if(ambe_devices && (ambe_devices->size() > 0)){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h b/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h deleted file mode 100644 index c60b61f99..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEDevices_2.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGAMBEDevices_2.h - * - * List of AMBE devices (serial or server address) - */ - -#ifndef SWGAMBEDevices_2_H_ -#define SWGAMBEDevices_2_H_ - -#include - - -#include "SWGAMBEDevice.h" -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGAMBEDevices_2: public SWGObject { -public: - SWGAMBEDevices_2(); - SWGAMBEDevices_2(QString* json); - virtual ~SWGAMBEDevices_2(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGAMBEDevices_2* fromJson(QString &jsonString) override; - - qint32 getNbDevices(); - void setNbDevices(qint32 nb_devices); - - QList* getAmbeDevices(); - void setAmbeDevices(QList* ambe_devices); - - - virtual bool isSet() override; - -private: - qint32 nb_devices; - bool m_nb_devices_isSet; - - QList* ambe_devices; - bool m_ambe_devices_isSet; - -}; - -} - -#endif /* SWGAMBEDevices_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp index 9f22650d4..a3b8c8fd7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp @@ -40,9 +40,9 @@ SWGAMBEReport::~SWGAMBEReport() { void SWGAMBEReport::init() { - serial = new SWGDVSerialDevices_2(); + serial = new SWGDVSerialDevices(); m_serial_isSet = false; - devices = new SWGAMBEDevices_2(); + devices = new SWGAMBEDevices(); m_devices_isSet = false; } @@ -67,9 +67,9 @@ SWGAMBEReport::fromJson(QString &json) { void SWGAMBEReport::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&serial, pJson["serial"], "SWGDVSerialDevices_2", "SWGDVSerialDevices_2"); + ::SWGSDRangel::setValue(&serial, pJson["serial"], "SWGDVSerialDevices", "SWGDVSerialDevices"); - ::SWGSDRangel::setValue(&devices, pJson["devices"], "SWGAMBEDevices_2", "SWGAMBEDevices_2"); + ::SWGSDRangel::setValue(&devices, pJson["devices"], "SWGAMBEDevices", "SWGAMBEDevices"); } @@ -88,31 +88,31 @@ QJsonObject* SWGAMBEReport::asJsonObject() { QJsonObject* obj = new QJsonObject(); if((serial != nullptr) && (serial->isSet())){ - toJsonValue(QString("serial"), serial, obj, QString("SWGDVSerialDevices_2")); + toJsonValue(QString("serial"), serial, obj, QString("SWGDVSerialDevices")); } if((devices != nullptr) && (devices->isSet())){ - toJsonValue(QString("devices"), devices, obj, QString("SWGAMBEDevices_2")); + toJsonValue(QString("devices"), devices, obj, QString("SWGAMBEDevices")); } return obj; } -SWGDVSerialDevices_2* +SWGDVSerialDevices* SWGAMBEReport::getSerial() { return serial; } void -SWGAMBEReport::setSerial(SWGDVSerialDevices_2* serial) { +SWGAMBEReport::setSerial(SWGDVSerialDevices* serial) { this->serial = serial; this->m_serial_isSet = true; } -SWGAMBEDevices_2* +SWGAMBEDevices* SWGAMBEReport::getDevices() { return devices; } void -SWGAMBEReport::setDevices(SWGAMBEDevices_2* devices) { +SWGAMBEReport::setDevices(SWGAMBEDevices* devices) { this->devices = devices; this->m_devices_isSet = true; } diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h index 45f3f1d32..feef5985b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h @@ -22,8 +22,8 @@ #include -#include "SWGAMBEDevices_2.h" -#include "SWGDVSerialDevices_2.h" +#include "SWGAMBEDevices.h" +#include "SWGDVSerialDevices.h" #include "SWGObject.h" #include "export.h" @@ -43,20 +43,20 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGAMBEReport* fromJson(QString &jsonString) override; - SWGDVSerialDevices_2* getSerial(); - void setSerial(SWGDVSerialDevices_2* serial); + SWGDVSerialDevices* getSerial(); + void setSerial(SWGDVSerialDevices* serial); - SWGAMBEDevices_2* getDevices(); - void setDevices(SWGAMBEDevices_2* devices); + SWGAMBEDevices* getDevices(); + void setDevices(SWGAMBEDevices* devices); virtual bool isSet() override; private: - SWGDVSerialDevices_2* serial; + SWGDVSerialDevices* serial; bool m_serial_isSet; - SWGAMBEDevices_2* devices; + SWGAMBEDevices* devices; bool m_devices_isSet; }; diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp deleted file mode 100644 index 06bba95f3..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGDVSerialDevice_2.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGDVSerialDevice_2::SWGDVSerialDevice_2(QString* json) { - init(); - this->fromJson(*json); -} - -SWGDVSerialDevice_2::SWGDVSerialDevice_2() { - device_name = nullptr; - m_device_name_isSet = false; -} - -SWGDVSerialDevice_2::~SWGDVSerialDevice_2() { - this->cleanup(); -} - -void -SWGDVSerialDevice_2::init() { - device_name = new QString(""); - m_device_name_isSet = false; -} - -void -SWGDVSerialDevice_2::cleanup() { - if(device_name != nullptr) { - delete device_name; - } -} - -SWGDVSerialDevice_2* -SWGDVSerialDevice_2::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGDVSerialDevice_2::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&device_name, pJson["deviceName"], "QString", "QString"); - -} - -QString -SWGDVSerialDevice_2::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGDVSerialDevice_2::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(device_name != nullptr && *device_name != QString("")){ - toJsonValue(QString("deviceName"), device_name, obj, QString("QString")); - } - - return obj; -} - -QString* -SWGDVSerialDevice_2::getDeviceName() { - return device_name; -} -void -SWGDVSerialDevice_2::setDeviceName(QString* device_name) { - this->device_name = device_name; - this->m_device_name_isSet = true; -} - - -bool -SWGDVSerialDevice_2::isSet(){ - bool isObjectUpdated = false; - do{ - if(device_name && *device_name != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h deleted file mode 100644 index 92a21c6be..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevice_2.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGDVSerialDevice_2.h - * - * DV serial device details - */ - -#ifndef SWGDVSerialDevice_2_H_ -#define SWGDVSerialDevice_2_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGDVSerialDevice_2: public SWGObject { -public: - SWGDVSerialDevice_2(); - SWGDVSerialDevice_2(QString* json); - virtual ~SWGDVSerialDevice_2(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGDVSerialDevice_2* fromJson(QString &jsonString) override; - - QString* getDeviceName(); - void setDeviceName(QString* device_name); - - - virtual bool isSet() override; - -private: - QString* device_name; - bool m_device_name_isSet; - -}; - -} - -#endif /* SWGDVSerialDevice_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp deleted file mode 100644 index 18304ef01..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGDVSerialDevices_2.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGDVSerialDevices_2::SWGDVSerialDevices_2(QString* json) { - init(); - this->fromJson(*json); -} - -SWGDVSerialDevices_2::SWGDVSerialDevices_2() { - nb_devices = 0; - m_nb_devices_isSet = false; - dv_serial_devices = nullptr; - m_dv_serial_devices_isSet = false; -} - -SWGDVSerialDevices_2::~SWGDVSerialDevices_2() { - this->cleanup(); -} - -void -SWGDVSerialDevices_2::init() { - nb_devices = 0; - m_nb_devices_isSet = false; - dv_serial_devices = new QList(); - m_dv_serial_devices_isSet = false; -} - -void -SWGDVSerialDevices_2::cleanup() { - - if(dv_serial_devices != nullptr) { - auto arr = dv_serial_devices; - for(auto o: *arr) { - delete o; - } - delete dv_serial_devices; - } -} - -SWGDVSerialDevices_2* -SWGDVSerialDevices_2::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGDVSerialDevices_2::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&nb_devices, pJson["nbDevices"], "qint32", ""); - - - ::SWGSDRangel::setValue(&dv_serial_devices, pJson["dvSerialDevices"], "QList", "SWGDVSerialDevice"); -} - -QString -SWGDVSerialDevices_2::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGDVSerialDevices_2::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_nb_devices_isSet){ - obj->insert("nbDevices", QJsonValue(nb_devices)); - } - if(dv_serial_devices && dv_serial_devices->size() > 0){ - toJsonArray((QList*)dv_serial_devices, obj, "dvSerialDevices", "SWGDVSerialDevice"); - } - - return obj; -} - -qint32 -SWGDVSerialDevices_2::getNbDevices() { - return nb_devices; -} -void -SWGDVSerialDevices_2::setNbDevices(qint32 nb_devices) { - this->nb_devices = nb_devices; - this->m_nb_devices_isSet = true; -} - -QList* -SWGDVSerialDevices_2::getDvSerialDevices() { - return dv_serial_devices; -} -void -SWGDVSerialDevices_2::setDvSerialDevices(QList* dv_serial_devices) { - this->dv_serial_devices = dv_serial_devices; - this->m_dv_serial_devices_isSet = true; -} - - -bool -SWGDVSerialDevices_2::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_nb_devices_isSet){ - isObjectUpdated = true; break; - } - if(dv_serial_devices && (dv_serial_devices->size() > 0)){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h b/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h deleted file mode 100644 index d0065a428..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGDVSerialDevices_2.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 7.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGDVSerialDevices_2.h - * - * List of DV serial devices available in the system - */ - -#ifndef SWGDVSerialDevices_2_H_ -#define SWGDVSerialDevices_2_H_ - -#include - - -#include "SWGDVSerialDevice.h" -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGDVSerialDevices_2: public SWGObject { -public: - SWGDVSerialDevices_2(); - SWGDVSerialDevices_2(QString* json); - virtual ~SWGDVSerialDevices_2(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGDVSerialDevices_2* fromJson(QString &jsonString) override; - - qint32 getNbDevices(); - void setNbDevices(qint32 nb_devices); - - QList* getDvSerialDevices(); - void setDvSerialDevices(QList* dv_serial_devices); - - - virtual bool isSet() override; - -private: - qint32 nb_devices; - bool m_nb_devices_isSet; - - QList* dv_serial_devices; - bool m_dv_serial_devices_isSet; - -}; - -} - -#endif /* SWGDVSerialDevices_2_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp index ab13b9f45..08f2ff927 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp @@ -28,272 +28,6 @@ SWGInstanceApi::SWGInstanceApi(QString host, QString basePath) { this->basePath = basePath; } -void -SWGInstanceApi::instanceAMBEDevicesDelete() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/ambe/devices"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "DELETE"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceAMBEDevicesDeleteCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceAMBEDevicesDeleteCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceAMBEDevicesDeleteSignal(output); - } else { - emit instanceAMBEDevicesDeleteSignalE(output, error_type, error_str); - emit instanceAMBEDevicesDeleteSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceAMBEDevicesGet() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/ambe/devices"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceAMBEDevicesGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceAMBEDevicesGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGAMBEDevices* output = static_cast(create(json, QString("SWGAMBEDevices"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceAMBEDevicesGetSignal(output); - } else { - emit instanceAMBEDevicesGetSignalE(output, error_type, error_str); - emit instanceAMBEDevicesGetSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceAMBEDevicesPatch(SWGAMBEDevices& body) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/ambe/devices"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "PATCH"); - - - - QString output = body.asJson(); - input.request_body.append(output.toUtf8()); - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceAMBEDevicesPatchCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceAMBEDevicesPatchCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGAMBEDevices* output = static_cast(create(json, QString("SWGAMBEDevices"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceAMBEDevicesPatchSignal(output); - } else { - emit instanceAMBEDevicesPatchSignalE(output, error_type, error_str); - emit instanceAMBEDevicesPatchSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceAMBEDevicesPut(SWGAMBEDevices& body) { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/ambe/devices"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "PUT"); - - - - QString output = body.asJson(); - input.request_body.append(output.toUtf8()); - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceAMBEDevicesPutCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceAMBEDevicesPutCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGAMBEDevices* output = static_cast(create(json, QString("SWGAMBEDevices"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceAMBEDevicesPutSignal(output); - } else { - emit instanceAMBEDevicesPutSignalE(output, error_type, error_str); - emit instanceAMBEDevicesPutSignalEFull(worker, error_type, error_str); - } -} - -void -SWGInstanceApi::instanceAMBESerialGet() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/ambe/serial"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceAMBESerialGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceAMBESerialGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGDVSerialDevices* output = static_cast(create(json, QString("SWGDVSerialDevices"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceAMBESerialGetSignal(output); - } else { - emit instanceAMBESerialGetSignalE(output, error_type, error_str); - emit instanceAMBESerialGetSignalEFull(worker, error_type, error_str); - } -} - void SWGInstanceApi::instanceAudioGet() { QString fullPath; diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h index 61a178a99..3ea0cb05d 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h @@ -15,7 +15,6 @@ #include "SWGHttpRequest.h" -#include "SWGAMBEDevices.h" #include "SWGAudioDevices.h" #include "SWGAudioInputDevice.h" #include "SWGAudioOutputDevice.h" @@ -23,7 +22,6 @@ #include "SWGConfigurationIdentifier.h" #include "SWGConfigurationImportExport.h" #include "SWGConfigurations.h" -#include "SWGDVSerialDevices.h" #include "SWGDeviceSetList.h" #include "SWGErrorResponse.h" #include "SWGFeaturePresetIdentifier.h" @@ -58,11 +56,6 @@ public: QString basePath; QMap defaultHeaders; - void instanceAMBEDevicesDelete(); - void instanceAMBEDevicesGet(); - void instanceAMBEDevicesPatch(SWGAMBEDevices& body); - void instanceAMBEDevicesPut(SWGAMBEDevices& body); - void instanceAMBESerialGet(); void instanceAudioGet(); void instanceAudioInputCleanupPatch(); void instanceAudioInputDelete(SWGAudioInputDevice& body); @@ -105,11 +98,6 @@ public: void instanceSummary(); private: - void instanceAMBEDevicesDeleteCallback (SWGHttpRequestWorker * worker); - void instanceAMBEDevicesGetCallback (SWGHttpRequestWorker * worker); - void instanceAMBEDevicesPatchCallback (SWGHttpRequestWorker * worker); - void instanceAMBEDevicesPutCallback (SWGHttpRequestWorker * worker); - void instanceAMBESerialGetCallback (SWGHttpRequestWorker * worker); void instanceAudioGetCallback (SWGHttpRequestWorker * worker); void instanceAudioInputCleanupPatchCallback (SWGHttpRequestWorker * worker); void instanceAudioInputDeleteCallback (SWGHttpRequestWorker * worker); @@ -152,11 +140,6 @@ private: void instanceSummaryCallback (SWGHttpRequestWorker * worker); signals: - void instanceAMBEDevicesDeleteSignal(SWGSuccessResponse* summary); - void instanceAMBEDevicesGetSignal(SWGAMBEDevices* summary); - void instanceAMBEDevicesPatchSignal(SWGAMBEDevices* summary); - void instanceAMBEDevicesPutSignal(SWGAMBEDevices* summary); - void instanceAMBESerialGetSignal(SWGDVSerialDevices* summary); void instanceAudioGetSignal(SWGAudioDevices* summary); void instanceAudioInputCleanupPatchSignal(SWGSuccessResponse* summary); void instanceAudioInputDeleteSignal(SWGAudioInputDevice* summary); @@ -198,11 +181,6 @@ signals: void instancePresetPutSignal(SWGPresetIdentifier* summary); void instanceSummarySignal(SWGInstanceSummaryResponse* summary); - void instanceAMBEDevicesDeleteSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesGetSignalE(SWGAMBEDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesPatchSignalE(SWGAMBEDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesPutSignalE(SWGAMBEDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBESerialGetSignalE(SWGDVSerialDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioGetSignalE(SWGAudioDevices* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioInputCleanupPatchSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioInputDeleteSignalE(SWGAudioInputDevice* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -244,11 +222,6 @@ signals: void instancePresetPutSignalE(SWGPresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceSummarySignalE(SWGInstanceSummaryResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBEDevicesPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceAMBESerialGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioInputCleanupPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceAudioInputDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 3d7f6bfc6..5d387a7fa 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -27,9 +27,7 @@ #include "SWGAISSettings.h" #include "SWGAMBEActions.h" #include "SWGAMBEDevice.h" -#include "SWGAMBEDevice_2.h" #include "SWGAMBEDevices.h" -#include "SWGAMBEDevices_2.h" #include "SWGAMBEReport.h" #include "SWGAMBESettings.h" #include "SWGAMDemodReport.h" @@ -99,9 +97,7 @@ #include "SWGDSDDemodReport.h" #include "SWGDSDDemodSettings.h" #include "SWGDVSerialDevice.h" -#include "SWGDVSerialDevice_2.h" #include "SWGDVSerialDevices.h" -#include "SWGDVSerialDevices_2.h" #include "SWGDemodAnalyzerSettings.h" #include "SWGDeviceActions.h" #include "SWGDeviceConfig.h" @@ -401,21 +397,11 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGAMBEDevice_2").compare(type) == 0) { - SWGAMBEDevice_2 *obj = new SWGAMBEDevice_2(); - obj->init(); - return obj; - } if(QString("SWGAMBEDevices").compare(type) == 0) { SWGAMBEDevices *obj = new SWGAMBEDevices(); obj->init(); return obj; } - if(QString("SWGAMBEDevices_2").compare(type) == 0) { - SWGAMBEDevices_2 *obj = new SWGAMBEDevices_2(); - obj->init(); - return obj; - } if(QString("SWGAMBEReport").compare(type) == 0) { SWGAMBEReport *obj = new SWGAMBEReport(); obj->init(); @@ -761,21 +747,11 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGDVSerialDevice_2").compare(type) == 0) { - SWGDVSerialDevice_2 *obj = new SWGDVSerialDevice_2(); - obj->init(); - return obj; - } if(QString("SWGDVSerialDevices").compare(type) == 0) { SWGDVSerialDevices *obj = new SWGDVSerialDevices(); obj->init(); return obj; } - if(QString("SWGDVSerialDevices_2").compare(type) == 0) { - SWGDVSerialDevices_2 *obj = new SWGDVSerialDevices_2(); - obj->init(); - return obj; - } if(QString("SWGDemodAnalyzerSettings").compare(type) == 0) { SWGDemodAnalyzerSettings *obj = new SWGDemodAnalyzerSettings(); obj->init(); From 3880a0f98f102c1247a792b700137c601a5c8fcd Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 00:13:40 +0200 Subject: [PATCH 16/29] AMBE feature: cleanup of main application AMBE support --- plugins/channelrx/demoddsd/dsddemodsink.cpp | 3 +- sdrbase/CMakeLists.txt | 4 --- sdrbase/dsp/dspengine.cpp | 31 --------------------- sdrbase/dsp/dspengine.h | 17 ----------- sdrbase/settings/mainsettings.cpp | 11 +------- sdrbase/settings/mainsettings.h | 3 -- sdrbench/mainbench.cpp | 15 ---------- sdrbench/mainbench.h | 1 - sdrbench/parserbench.cpp | 2 -- sdrbench/parserbench.h | 1 - sdrgui/CMakeLists.txt | 2 -- sdrgui/mainwindow.cpp | 15 ---------- sdrgui/mainwindow.h | 1 - sdrsrv/mainserver.cpp | 1 - 14 files changed, 2 insertions(+), 105 deletions(-) diff --git a/plugins/channelrx/demoddsd/dsddemodsink.cpp b/plugins/channelrx/demoddsd/dsddemodsink.cpp index 29a0a7ba9..5b2f9e164 100644 --- a/plugins/channelrx/demoddsd/dsddemodsink.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsink.cpp @@ -92,7 +92,7 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV m_scopeSampleBuffer.clear(); - m_dsdDecoder.enableMbelib(!DSPEngine::instance()->hasDVSerialSupport()); // disable mbelib if DV serial support is present and activated else enable it + m_dsdDecoder.enableMbelib(!m_ambeFeature); // disable mbelib if DV serial support is present and activated else enable it for (SampleVector::const_iterator it = begin; it != end; ++it) { @@ -281,7 +281,6 @@ void DSDDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV } if (!m_ambeFeature) - // if (!DSPEngine::instance()->hasDVSerialSupport()) { if (m_settings.m_slot1On) { diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 733e65c1d..094466cef 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -52,8 +52,6 @@ set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY}) set(sdrbase_SOURCES ${sdrbase_SOURCES} - ambe/ambeengine.cpp - ambe/ambeworker.cpp audio/audiocompressor.cpp audio/audiocompressorsnd.cpp @@ -227,8 +225,6 @@ set(sdrbase_SOURCES set(sdrbase_HEADERS ${sdrbase_HEADERS} - ambe/ambeengine.h - ambe/ambeworker.h audio/audiocompressor.h audio/audiocompressorsnd.h diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index de1561cd3..76bace2c3 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -168,37 +168,6 @@ void DSPEngine::removeDeviceEngineAt(int deviceIndex) m_deviceEngineReferences.removeAt(deviceIndex); } -bool DSPEngine::hasDVSerialSupport() -{ - return m_ambeEngine.getNbDevices() > 0; -} - -void DSPEngine::setDVSerialSupport(bool support) -{ (void) support; } - -void DSPEngine::getDVSerialNames(std::vector& deviceNames) -{ - std::vector qDeviceRefs; - m_ambeEngine.getDeviceRefs(qDeviceRefs); - deviceNames.clear(); - - for (std::vector::const_iterator it = qDeviceRefs.begin(); it != qDeviceRefs.end(); ++it) { - deviceNames.push_back(it->toStdString()); - } -} - -void DSPEngine::pushMbeFrame( - const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo) -{ - m_ambeEngine.pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo); -} - void DSPEngine::createFFTFactory(const QString& fftWisdomFileName) { m_fftFactory = new FFTFactory(fftWisdomFileName); diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index 1f1bafbd1..0b332e4e6 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -26,7 +26,6 @@ #include "audio/audiodevicemanager.h" #include "audio/audiooutputdevice.h" #include "export.h" -#include "ambe/ambeengine.h" class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; @@ -55,7 +54,6 @@ public: void removeDeviceEngineAt(int deviceIndex); AudioDeviceManager *getAudioDeviceManager() { return &m_audioDeviceManager; } - AMBEEngine *getAMBEEngine() { return &m_ambeEngine; } uint32_t getDeviceSourceEnginesNumber() const { return m_deviceSourceEngines.size(); } DSPDeviceSourceEngine *getDeviceSourceEngineByIndex(unsigned int deviceIndex) { return m_deviceSourceEngines[deviceIndex]; } @@ -66,20 +64,6 @@ public: uint32_t getDeviceMIMOEnginesNumber() const { return m_deviceMIMOEngines.size(); } DSPDeviceMIMOEngine *getDeviceMIMOEngineByIndex(unsigned int deviceIndex) { return m_deviceMIMOEngines[deviceIndex]; } - // Serial DV methods: - - bool hasDVSerialSupport(); - void setDVSerialSupport(bool support); - void getDVSerialNames(std::vector& deviceNames); - void pushMbeFrame( - const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo); - const QTimer& getMasterTimer() const { return m_masterTimer; } void setMIMOSupport(bool mimoSupport) { m_mimoSupport = mimoSupport; } bool getMIMOSupport() const { return m_mimoSupport; } @@ -109,7 +93,6 @@ private: QTimer m_masterTimer; bool m_dvSerialSupport; bool m_mimoSupport; - AMBEEngine m_ambeEngine; FFTFactory *m_fftFactory; }; diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index a074a05d0..a1cd3b40f 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -10,8 +10,7 @@ #include "ambe/ambeengine.h" MainSettings::MainSettings() : - m_audioDeviceManager(nullptr), - m_ambeEngine(nullptr) + m_audioDeviceManager(nullptr) { resetToDefaults(); qInfo("MainSettings::MainSettings: settings file: format: %d location: %s", getFileFormat(), qPrintable(getFileLocation())); @@ -60,10 +59,6 @@ void MainSettings::load() m_audioDeviceManager->deserialize(qUncompress(QByteArray::fromBase64(s.value("audio").toByteArray()))); } - if (m_ambeEngine) { - m_ambeEngine->deserialize(qUncompress(QByteArray::fromBase64(s.value("ambe").toByteArray()))); - } - QStringList groups = s.childGroups(); for (int i = 0; i < groups.size(); ++i) @@ -148,10 +143,6 @@ void MainSettings::save() const s.setValue("audio", qCompress(m_audioDeviceManager->serialize()).toBase64()); } - if (m_ambeEngine) { - s.setValue("ambe", qCompress(m_ambeEngine->serialize()).toBase64()); - } - QStringList groups = s.childGroups(); for(int i = 0; i < groups.size(); ++i) diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index a475afbe6..d341dee3d 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -13,7 +13,6 @@ class Command; class AudioDeviceManager; -class AMBEEngine; class SDRBASE_API MainSettings : public QObject { @@ -183,7 +182,6 @@ public: DeviceUserArgs& getDeviceUserArgs() { return m_hardwareDeviceUserArgs; } const AudioDeviceManager *getAudioDeviceManager() const { return m_audioDeviceManager; } void setAudioDeviceManager(AudioDeviceManager *audioDeviceManager) { m_audioDeviceManager = audioDeviceManager; } - void setAMBEEngine(AMBEEngine *ambeEngine) { m_ambeEngine = ambeEngine; } signals: void preferenceChanged(int); @@ -203,7 +201,6 @@ protected: typedef QList Configurations; Configurations m_configurations; DeviceUserArgs m_hardwareDeviceUserArgs; - AMBEEngine *m_ambeEngine; }; #endif // INCLUDE_SETTINGS_H diff --git a/sdrbench/mainbench.cpp b/sdrbench/mainbench.cpp index cb31eb8a3..5d1ec92bc 100644 --- a/sdrbench/mainbench.cpp +++ b/sdrbench/mainbench.cpp @@ -62,8 +62,6 @@ void MainBench::run() testDecimateFI(); } else if (m_parser.getTestType() == ParserBench::TestDecimatorsFF) { testDecimateFF(); - } else if (m_parser.getTestType() == ParserBench::TestAMBE) { - testAMBE(); } else if (m_parser.getTestType() == ParserBench::TestGolay2312) { testGolay2312(); } else { @@ -197,19 +195,6 @@ void MainBench::testDecimateFF() delete[] buf; } -void MainBench::testAMBE() -{ - qDebug() << "MainBench::testAMBE"; - AMBEEngine ambeEngine; - std::vector ambeDevices; - ambeEngine.scan(ambeDevices); - - for (std::vector::const_iterator it = ambeDevices.begin(); it != ambeDevices.end(); ++it) { - qDebug("MainBench::testAMBE: detected AMBE device %s", qPrintable(*it)); - } -} - - void MainBench::decimateII(const qint16* buf, int len) { SampleVector::iterator it = m_convertBuffer.begin(); diff --git a/sdrbench/mainbench.h b/sdrbench/mainbench.h index 397ee6319..8992ecc2c 100644 --- a/sdrbench/mainbench.h +++ b/sdrbench/mainbench.h @@ -53,7 +53,6 @@ private: void testDecimateIF(); void testDecimateFI(); void testDecimateFF(); - void testAMBE(); void testGolay2312(); void decimateII(const qint16 *buf, int len); void decimateInfII(const qint16 *buf, int len); diff --git a/sdrbench/parserbench.cpp b/sdrbench/parserbench.cpp index 7602a6904..a03fa749e 100644 --- a/sdrbench/parserbench.cpp +++ b/sdrbench/parserbench.cpp @@ -125,8 +125,6 @@ ParserBench::TestType ParserBench::getTestType() const return TestDecimatorsInfII; } else if (m_testStr == "decimatesupii") { return TestDecimatorsSupII; - } else if (m_testStr == "ambe") { - return TestAMBE; } else if (m_testStr == "golay2312") { return TestGolay2312; } else { diff --git a/sdrbench/parserbench.h b/sdrbench/parserbench.h index 9dce1cfdd..50b455c27 100644 --- a/sdrbench/parserbench.h +++ b/sdrbench/parserbench.h @@ -35,7 +35,6 @@ public: TestDecimatorsFF, TestDecimatorsInfII, TestDecimatorsSupII, - TestAMBE, TestGolay2312 } TestType; diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 4780935ee..21397b50d 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -8,7 +8,6 @@ set(sdrgui_SOURCES mainwindow.cpp gui/aboutdialog.cpp gui/addpresetdialog.cpp - gui/ambedevicesdialog.cpp gui/audiodialog.cpp gui/audioselectdialog.cpp gui/basicchannelsettingsdialog.cpp @@ -108,7 +107,6 @@ set(sdrgui_HEADERS mainwindow.h gui/aboutdialog.h gui/addpresetdialog.h - gui/ambedevicesdialog.h gui/audiodialog.h gui/audioselectdialog.h gui/basicchannelsettingsdialog.h diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 88c365528..b426ecd73 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -116,7 +116,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_mainCore->m_masterTabIndex = 0; m_mainCore->m_mainMessageQueue = &m_inputMessageQueue; m_mainCore->m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager()); - m_mainCore->m_settings.setAMBEEngine(m_dspEngine->getAMBEEngine()); QFontDatabase::addApplicationFont(":/LiberationSans-Regular.ttf"); QFontDatabase::addApplicationFont(":/LiberationMono-Regular.ttf"); @@ -1466,11 +1465,6 @@ void MainWindow::createMenuBar() QAction *fftAction = preferencesMenu->addAction("&FFT..."); fftAction->setToolTip("Set FFT cache"); QObject::connect(fftAction, &QAction::triggered, this, &MainWindow::on_action_FFT_triggered); -#ifndef __APPLE__ - QAction *ambeAction = preferencesMenu->addAction("A&MBE..."); - ambeAction->setToolTip("AMBE options"); - QObject::connect(ambeAction, &QAction::triggered, this, &MainWindow::on_action_AMBE_triggered); -#endif QMenu *devicesMenu = preferencesMenu->addMenu("&Devices"); QAction *userArgumentsAction = devicesMenu->addAction("&User arguments..."); userArgumentsAction->setToolTip("Device custom user arguments"); @@ -2118,15 +2112,6 @@ void MainWindow::fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exi m_fftWisdomProcess = nullptr; } -void MainWindow::on_action_AMBE_triggered() -{ - qDebug("MainWindow::on_action_AMBE_triggered"); -#ifndef __APPLE__ - AMBEDevicesDialog ambeDevicesDialog(m_dspEngine->getAMBEEngine(), this); - ambeDevicesDialog.exec(); -#endif -} - void MainWindow::samplingDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex) { int deviceType = (int) deviceGUI->getDeviceType(); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 81b9e2fbe..5e8261906 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -181,7 +181,6 @@ private slots: void on_action_Audio_triggered(); void on_action_Logging_triggered(); void on_action_FFT_triggered(); - void on_action_AMBE_triggered(); void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); void on_action_commands_triggered(); diff --git a/sdrsrv/mainserver.cpp b/sdrsrv/mainserver.cpp index 2d084e654..949865178 100644 --- a/sdrsrv/mainserver.cpp +++ b/sdrsrv/mainserver.cpp @@ -51,7 +51,6 @@ MainServer::MainServer(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_mainCore->m_logger = logger; m_mainCore->m_mainMessageQueue = &m_inputMessageQueue; m_mainCore->m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager()); - m_mainCore->m_settings.setAMBEEngine(m_dspEngine->getAMBEEngine()); m_mainCore->m_masterTabIndex = -1; qDebug() << "MainServer::MainServer: create FFT factory..."; From 558955f6f91d25f0ea79f99779d7fbcfeed68740 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 00:53:57 +0200 Subject: [PATCH 17/29] AMBE feature: removed AMBE devices dialog --- sdrgui/CMakeLists.txt | 1 - sdrgui/gui/ambedevicesdialog.cpp | 178 ------------------- sdrgui/gui/ambedevicesdialog.h | 56 ------ sdrgui/gui/ambedevicesdialog.ui | 284 ------------------------------- sdrgui/mainwindow.cpp | 1 - 5 files changed, 520 deletions(-) delete mode 100644 sdrgui/gui/ambedevicesdialog.cpp delete mode 100644 sdrgui/gui/ambedevicesdialog.h delete mode 100644 sdrgui/gui/ambedevicesdialog.ui diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 21397b50d..5f6d97ffa 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -205,7 +205,6 @@ set(sdrgui_FORMS # mainwindow.ui gui/aboutdialog.ui gui/addpresetdialog.ui - gui/ambedevicesdialog.ui gui/basicchannelsettingsdialog.ui gui/basicdevicesettingsdialog.ui gui/basicfeaturesettingsdialog.ui diff --git a/sdrgui/gui/ambedevicesdialog.cpp b/sdrgui/gui/ambedevicesdialog.cpp deleted file mode 100644 index 3c8195a61..000000000 --- a/sdrgui/gui/ambedevicesdialog.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 "ambedevicesdialog.h" -#include "ui_ambedevicesdialog.h" - -AMBEDevicesDialog::AMBEDevicesDialog(AMBEEngine* ambeEngine, QWidget* parent) : - QDialog(parent), - ui(new Ui::AMBEDevicesDialog), - m_ambeEngine(ambeEngine) -{ - ui->setupUi(this); - populateSerialList(); - refreshInUseList(); -} - -AMBEDevicesDialog::~AMBEDevicesDialog() -{ - delete ui; -} - -void AMBEDevicesDialog::populateSerialList() -{ - std::vector ambeSerialDevices; - m_ambeEngine->scan(ambeSerialDevices); - ui->ambeSerialDevices->clear(); - std::vector::const_iterator it = ambeSerialDevices.begin(); - - for (; it != ambeSerialDevices.end(); ++it) - { - ui->ambeSerialDevices->addItem(QString(*it)); - } -} - -void AMBEDevicesDialog::refreshInUseList() -{ - std::vector inUseDevices; - m_ambeEngine->getDeviceRefs(inUseDevices); - ui->ambeDeviceRefs->clear(); - std::vector::const_iterator it = inUseDevices.begin(); - - for (; it != inUseDevices.end(); ++it) - { - qDebug("AMBEDevicesDialog::refreshInUseList: %s", qPrintable(*it)); - ui->ambeDeviceRefs->addItem(*it); - } -} - -void AMBEDevicesDialog::on_importSerial_clicked() -{ - QListWidgetItem *serialItem = ui->ambeSerialDevices->currentItem(); - - if (!serialItem) - { - ui->statusText->setText("No selection"); - return; - } - - QString serialName = serialItem->text(); - QList foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive); - - if (foundItems.size() == 0) - { - if (m_ambeEngine->registerController(serialName.toStdString())) - { - ui->ambeDeviceRefs->addItem(serialName); - ui->statusText->setText(tr("%1 added").arg(serialName)); - } - else - { - ui->statusText->setText(tr("Cannot open %1").arg(serialName)); - } - } - else - { - ui->statusText->setText("Device already in use"); - } -} - -void AMBEDevicesDialog::on_importAllSerial_clicked() -{ - int count = 0; - - for (int i = 0; i < ui->ambeSerialDevices->count(); i++) - { - const QListWidgetItem *serialItem = ui->ambeSerialDevices->item(i); - QString serialName = serialItem->text(); - QList foundItems = ui->ambeDeviceRefs->findItems(serialName, Qt::MatchFixedString|Qt::MatchCaseSensitive); - - if (foundItems.size() == 0) - { - if (m_ambeEngine->registerController(serialName.toStdString())) - { - ui->ambeDeviceRefs->addItem(serialName); - count++; - } - } - } - - ui->statusText->setText(tr("%1 devices added").arg(count)); -} - -void AMBEDevicesDialog::on_removeAmbeDevice_clicked() -{ - QListWidgetItem *deviceItem = ui->ambeDeviceRefs->currentItem(); - - if (!deviceItem) - { - ui->statusText->setText("No selection"); - return; - } - - QString deviceName = deviceItem->text(); - m_ambeEngine->releaseController(deviceName.toStdString()); - ui->statusText->setText(tr("%1 removed").arg(deviceName)); - refreshInUseList(); -} - -void AMBEDevicesDialog::on_refreshAmbeList_clicked() -{ - refreshInUseList(); - ui->statusText->setText("In use refreshed"); -} - -void AMBEDevicesDialog::on_clearAmbeList_clicked() -{ - if (ui->ambeDeviceRefs->count() == 0) - { - ui->statusText->setText("No active items"); - return; - } - - m_ambeEngine->releaseAll(); - ui->ambeDeviceRefs->clear(); - ui->statusText->setText("All items released"); -} - -void AMBEDevicesDialog::on_importAddress_clicked() -{ - QString addressAndPort = ui->ambeAddressText->text(); - - QList foundItems = ui->ambeDeviceRefs->findItems(addressAndPort, Qt::MatchFixedString|Qt::MatchCaseSensitive); - - if (foundItems.size() == 0) - { - if (m_ambeEngine->registerController(addressAndPort.toStdString())) - { - ui->ambeDeviceRefs->addItem(addressAndPort); - ui->statusText->setText(tr("%1 added").arg(addressAndPort)); - } - else - { - ui->statusText->setText(tr("Cannot open %1").arg(addressAndPort)); - } - } - else - { - ui->statusText->setText("Address already in use"); - } -} diff --git a/sdrgui/gui/ambedevicesdialog.h b/sdrgui/gui/ambedevicesdialog.h deleted file mode 100644 index d32009e5a..000000000 --- a/sdrgui/gui/ambedevicesdialog.h +++ /dev/null @@ -1,56 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 SDRGUI_GUI_AMBEDEVICESDIALOG_H_ -#define SDRGUI_GUI_AMBEDEVICESDIALOG_H_ - -#include - -#include "export.h" -#include "ambe/ambeengine.h" - -class QListWidgetItem; - -namespace Ui { - class AMBEDevicesDialog; -} - -class SDRGUI_API AMBEDevicesDialog : public QDialog { - Q_OBJECT - -public: - explicit AMBEDevicesDialog(AMBEEngine* ambeEngine, QWidget* parent = nullptr); - ~AMBEDevicesDialog(); - -private: - void populateSerialList(); - void refreshInUseList(); - - Ui::AMBEDevicesDialog* ui; - AMBEEngine* m_ambeEngine; - -private slots: - void on_importSerial_clicked(); - void on_importAllSerial_clicked(); - void on_removeAmbeDevice_clicked(); - void on_refreshAmbeList_clicked(); - void on_clearAmbeList_clicked(); - void on_importAddress_clicked(); -}; - -#endif // SDRGUI_GUI_AMBEDEVICESDIALOG_H_ diff --git a/sdrgui/gui/ambedevicesdialog.ui b/sdrgui/gui/ambedevicesdialog.ui deleted file mode 100644 index f1e34d493..000000000 --- a/sdrgui/gui/ambedevicesdialog.ui +++ /dev/null @@ -1,284 +0,0 @@ - - - AMBEDevicesDialog - - - - 0 - 0 - 401 - 450 - - - - AMBE devices control - - - - - 310 - 410 - 81 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 10 - 280 - 181 - 121 - - - - List of available AMBE serial devices available - - - - - - 10 - 90 - 381 - 151 - - - - List of devices/servers in use - - - - - - 10 - 260 - 101 - 17 - - - - Serial devices - - - - - - 10 - 10 - 381 - 17 - - - - AMBE server IP and port or direct input - - - - - - 10 - 30 - 381 - 25 - - - - AMBE server address as ip:port or direct input - - - - - - - - - 310 - 60 - 28 - 24 - - - - Refresh list of devices and servers in use - - - - - - - :/recycle.png:/recycle.png - - - - - - 280 - 60 - 24 - 23 - - - - Release all devices and servers - - - - - - - :/bin.png:/bin.png - - - - - - 10 - 70 - 91 - 17 - - - - In use - - - - - - 120 - 250 - 24 - 23 - - - - Use serial device - - - - - - - :/arrow_up.png:/arrow_up.png - - - - - - 120 - 60 - 24 - 23 - - - - Use server - - - - - - - :/arrow_down.png:/arrow_down.png - - - - - - 360 - 60 - 28 - 24 - - - - Remove all devices - - - - - - - :/sweep.png:/sweep.png - - - - - - 10 - 420 - 291 - 19 - - - - ... - - - - - - 150 - 250 - 24 - 23 - - - - Use all serial devices - - - - - - - :/double_arrow_up.png:/double_arrow_up.png - - - - - - - - - buttonBox - accepted() - AMBEDevicesDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - AMBEDevicesDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index b426ecd73..1a186e990 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -61,7 +61,6 @@ #include "gui/sdrangelsplash.h" #include "gui/mypositiondialog.h" #include "gui/fftwisdomdialog.h" -#include "gui/ambedevicesdialog.h" #include "gui/workspace.h" #include "gui/featurepresetsdialog.h" #include "gui/devicesetpresetsdialog.h" From 0eb487781bf3e31b582eda85aba47685326dc303 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 01:00:33 +0200 Subject: [PATCH 18/29] AMBE feature: removed AMBE support in main application --- sdrbase/ambe/ambeengine.cpp | 386 ------------------------------ sdrbase/ambe/ambeengine.h | 91 ------- sdrbase/ambe/ambeworker.cpp | 232 ------------------ sdrbase/ambe/ambeworker.h | 156 ------------ sdrbase/settings/mainsettings.cpp | 1 - sdrbench/mainbench.cpp | 2 - 6 files changed, 868 deletions(-) delete mode 100644 sdrbase/ambe/ambeengine.cpp delete mode 100644 sdrbase/ambe/ambeengine.h delete mode 100644 sdrbase/ambe/ambeworker.cpp delete mode 100644 sdrbase/ambe/ambeworker.h diff --git a/sdrbase/ambe/ambeengine.cpp b/sdrbase/ambe/ambeengine.cpp deleted file mode 100644 index 8c56c4ec3..000000000 --- a/sdrbase/ambe/ambeengine.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 __APPLE__ -#include -#include -#endif - -#ifndef _MSC_VER -#include -#include -#include -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) -#include -#include -#include -#endif - -#include -#include - -#include -#include -#include - -#include "ambeworker.h" -#include "ambeengine.h" - -AMBEEngine::AMBEEngine() -{} - -AMBEEngine::~AMBEEngine() -{ - qDebug("AMBEEngine::~AMBEEngine: %lu controllers", m_controllers.size()); -} - -#if defined(_WIN32) -void AMBEEngine::getComList() -{ - m_comList.clear(); - m_comList8250.clear(); - char comCStr[16]; - - // Arbitrarily set the list to the 20 first COM ports - for (int i = 1; i <= 20; i++) - { - sprintf(comCStr, "COM%d", i); - m_comList.push_back(std::string(comCStr)); - } -} - -// Do not activate serial support at all for windows -void AMBEEngine::scan(std::vector& ambeDevices) -{ - (void) ambeDevices; -} -#elif defined(__APPLE__) -void AMBEEngine::getComList() -{ -} -void AMBEEngine::scan(std::vector& ambeDevices) -{ - (void) ambeDevices; -} -#else -void AMBEEngine::getComList() -{ - int n; - struct dirent **namelist; - m_comList.clear(); - m_comList8250.clear(); - const char* sysdir = "/sys/class/tty/"; - - // Scan through /sys/class/tty - it contains all tty-devices in the system - n = scandir(sysdir, &namelist, NULL, alphasort); - if (n < 0) - { - perror("scandir"); - } - else - { - while (n--) - { - if (strcmp(namelist[n]->d_name, "..") && strcmp(namelist[n]->d_name, ".")) - { - // Construct full absolute file path - std::string devicedir = sysdir; - devicedir += namelist[n]->d_name; - // Register the device - register_comport(m_comList, m_comList8250, devicedir); - } - - free(namelist[n]); - } - - free(namelist); - } - - // Only non-serial8250 has been added to comList without any further testing - // serial8250-devices must be probe to check for validity - probe_serial8250_comports(m_comList, m_comList8250); -} -#endif // not Windows nor Apple - -#if !defined(_WIN32) && !defined(__APPLE__) -void AMBEEngine::register_comport( - std::vector& comList, - std::vector& comList8250, - const std::string& dir) -{ - // Get the driver the device is using - std::string driver = get_driver(dir); - - // Skip devices without a driver - if (driver.size() > 0) - { - //std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl; - std::string devfile = std::string("/dev/") + basename((char *) dir.c_str()); - - // Put serial8250-devices in a seperate list - if (driver == "serial8250") { - comList8250.push_back(devfile); - } else { - comList.push_back(devfile); - } - } -} - -void AMBEEngine::probe_serial8250_comports( - std::vector& comList, - std::vector comList8250) -{ - struct serial_struct serinfo; - std::vector::iterator it = comList8250.begin(); - - // Iterate over all serial8250-devices - while (it != comList8250.end()) - { - - // Try to open the device - int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY); - - if (fd >= 0) - { - // Get serial_info - if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) - { - // If device type is no PORT_UNKNOWN we accept the port - if (serinfo.type != PORT_UNKNOWN) { - comList.push_back(*it); - } - } - - close(fd); - } - it++; - } -} - -std::string AMBEEngine::get_driver(const std::string& tty) -{ - struct stat st; - std::string devicedir = tty; - // Append '/device' to the tty-path - devicedir += "/device"; - - // Stat the devicedir and handle it if it is a symlink - if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode)) - { - char buffer[1024]; - memset(buffer, 0, sizeof(buffer)); - // Append '/driver' and return basename of the target - devicedir += "/driver"; - - if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) { - return basename(buffer); - } - } - - return ""; -} - -void AMBEEngine::scan(std::vector& ambeDevices) -{ - getComList(); - std::vector::const_iterator it = m_comList.begin(); - ambeDevices.clear(); - - while (it != m_comList.end()) - { - AMBEWorker *worker = new AMBEWorker(); - qDebug("AMBEEngine::scan: com: %s", it->c_str()); - - if (worker->open(*it)) - { - ambeDevices.push_back(QString(it->c_str())); - worker->close(); - } - - delete worker; - ++it; - } -} -#endif // not Windows nor Apple - -bool AMBEEngine::registerController(const std::string& deviceRef) -{ - AMBEWorker *worker = new AMBEWorker(); - - if (worker->open(deviceRef)) - { - m_controllers.push_back(AMBEController()); - m_controllers.back().worker = worker; - m_controllers.back().thread = new QThread(); - m_controllers.back().device = deviceRef; - - m_controllers.back().worker->moveToThread(m_controllers.back().thread); - connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().thread, SLOT(quit())); - connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().worker, SLOT(deleteLater())); - connect(m_controllers.back().thread, SIGNAL(finished()), m_controllers.back().thread, SLOT(deleteLater())); - connect(&m_controllers.back().worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), m_controllers.back().worker, SLOT(handleInputMessages())); - std::this_thread::sleep_for(std::chrono::seconds(1)); - m_controllers.back().thread->start(); - - return true; - } - - return false; -} - -void AMBEEngine::releaseController(const std::string& deviceRef) -{ - std::vector::iterator it = m_controllers.begin(); - - while (it != m_controllers.end()) - { - if (it->device == deviceRef) - { - disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages())); - it->worker->stop(); - it->thread->wait(100); - it->worker->m_inputMessageQueue.clear(); - it->worker->close(); - qDebug() << "AMBEEngine::releaseController: closed device at: " << it->device.c_str(); - m_controllers.erase(it); - break; - } - - ++it; - } -} - -void AMBEEngine::releaseAll() -{ - std::vector::iterator it = m_controllers.begin(); - - while (it != m_controllers.end()) - { - disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages())); - it->worker->stop(); - it->thread->wait(100); - it->worker->m_inputMessageQueue.clear(); - it->worker->close(); - qDebug() << "AMBEEngine::release: closed device at: " << it->device.c_str(); - ++it; - } - - m_controllers.clear(); -} - -void AMBEEngine::getDeviceRefs(std::vector& deviceNames) -{ - std::vector::const_iterator it = m_controllers.begin(); - - while (it != m_controllers.end()) - { - deviceNames.push_back(QString(it->device.c_str())); - ++it; - } -} - -void AMBEEngine::pushMbeFrame( - const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useLP, - int upsampling, - AudioFifo *audioFifo) -{ - std::vector::iterator it = m_controllers.begin(); - std::vector::iterator itAvail = m_controllers.end(); - bool done = false; - QMutexLocker locker(&m_mutex); - - while (it != m_controllers.end()) - { - if (it->worker->hasFifo(audioFifo)) - { - it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo); - done = true; - } - else if (it->worker->isAvailable()) - { - itAvail = it; - } - - ++it; - } - - if (!done) - { - if (itAvail != m_controllers.end()) - { - int wNum = itAvail - m_controllers.begin(); - - qDebug("AMBEEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum); - itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo); - } - else - { - qDebug("AMBEEngine::pushMbeFrame: no DV device available. MBE frame dropped"); - } - } -} - -QByteArray AMBEEngine::serialize() const -{ - QStringList qDeviceList; - std::vector::const_iterator it = m_controllers.begin(); - - while (it != m_controllers.end()) - { - qDebug("AMBEEngine::serialize: %s", it->device.c_str()); - qDeviceList << QString(it->device.c_str()); - ++it; - } - - QByteArray data; - QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); - (*stream) << qDeviceList; - delete stream; - - return data; -} - -bool AMBEEngine::deserialize(const QByteArray& data) -{ - if (data.size() <= 0) - { - qDebug("AMBEEngine::deserialize: invalid or no data"); - return false; - } - - QStringList qDeviceList; - QDataStream *stream = new QDataStream(data); - (*stream) >> qDeviceList; - delete stream; - - releaseAll(); - - for (int i = 0; i < qDeviceList.size(); ++i) - { - qDebug(" AMBEEngine::deserialize: %s", qDeviceList.at(i).toStdString().c_str()); - registerController(qDeviceList.at(i).toStdString()); - } - - return true; -} diff --git a/sdrbase/ambe/ambeengine.h b/sdrbase/ambe/ambeengine.h deleted file mode 100644 index f083d17b7..000000000 --- a/sdrbase/ambe/ambeengine.h +++ /dev/null @@ -1,91 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 SDRBASE_AMBE_AMBEENGINE_H_ -#define SDRBASE_AMBE_AMBEENGINE_H_ - -#include -#include - -#include -#include -#include -#include - -#include "export.h" - -class QThread; -class AMBEWorker; -class AudioFifo; - -class SDRBASE_API AMBEEngine : public QObject -{ - Q_OBJECT -public: - AMBEEngine(); - ~AMBEEngine(); - - void scan(std::vector& ambeDevices); - void releaseAll(); - - int getNbDevices() const { return m_controllers.size(); } //!< number of devices used - void getDeviceRefs(std::vector& devicesRefs); //!< reference of the devices used (device path or url) - bool registerController(const std::string& deviceRef); //!< create a new controller for the device in reference - void releaseController(const std::string& deviceRef); //!< release controller resources for the device in reference - - void pushMbeFrame( - const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo); - - QByteArray serialize() const; - bool deserialize(const QByteArray& data); - -private: - struct AMBEController - { - AMBEController() : - thread(nullptr), - worker(nullptr) - {} - - QThread *thread; - AMBEWorker *worker; - std::string device; - }; - -#ifndef _WIN32 - static std::string get_driver(const std::string& tty); - static void register_comport(std::vector& comList, std::vector& comList8250, const std::string& dir); - static void probe_serial8250_comports(std::vector& comList, std::vector comList8250); -#endif - void getComList(); - - std::vector m_controllers; - std::vector m_comList; - std::vector m_comList8250; - QMutex m_mutex; -}; - - - -#endif /* SDRBASE_AMBE_AMBEENGINE_H_ */ diff --git a/sdrbase/ambe/ambeworker.cpp b/sdrbase/ambe/ambeworker.cpp deleted file mode 100644 index 37713daba..000000000 --- a/sdrbase/ambe/ambeworker.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 "audio/audiofifo.h" -#include "ambeworker.h" - -MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgMbeDecode, Message) -MESSAGE_CLASS_DEFINITION(AMBEWorker::MsgTest, Message) - -AMBEWorker::AMBEWorker() : - m_running(false), - m_currentGainIn(0), - m_currentGainOut(0), - m_upsamplerLastValue(0.0f), - m_phase(0), - m_upsampling(1), - m_volume(1.0f) -{ - m_audioBuffer.resize(48000); - m_audioBufferFill = 0; - m_audioFifo = 0; - std::fill(m_dvAudioSamples, m_dvAudioSamples+SerialDV::MBE_AUDIO_BLOCK_SIZE, 0); - setVolumeFactors(); -} - -AMBEWorker::~AMBEWorker() -{} - -bool AMBEWorker::open(const std::string& deviceRef) -{ - return m_dvController.open(deviceRef); -} - -void AMBEWorker::close() -{ - m_dvController.close(); -} - -void AMBEWorker::process() -{ - m_running = true; - qDebug("AMBEWorker::process: started"); - - while (m_running) - { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - qDebug("AMBEWorker::process: stopped"); - emit finished(); -} - -void AMBEWorker::stop() -{ - m_running = false; -} - -void AMBEWorker::handleInputMessages() -{ - Message* message; - m_audioBufferFill = 0; - AudioFifo *audioFifo = 0; - - while ((message = m_inputMessageQueue.pop()) != 0) - { - if (MsgMbeDecode::match(*message)) - { - MsgMbeDecode *decodeMsg = (MsgMbeDecode *) message; - int dBVolume = (decodeMsg->getVolumeIndex() - 30) / 4; - float volume = pow(10.0, dBVolume / 10.0f); - int upsampling = decodeMsg->getUpsampling(); - upsampling = upsampling > 6 ? 6 : upsampling < 1 ? 1 : upsampling; - - if ((volume != m_volume) || (upsampling != m_upsampling)) - { - m_volume = volume; - m_upsampling = upsampling; - setVolumeFactors(); - } - - m_upsampleFilter.useHP(decodeMsg->getUseHP()); - - if (m_dvController.decode(m_dvAudioSamples, decodeMsg->getMbeFrame(), decodeMsg->getMbeRate())) - { - if (upsampling > 1) { - upsample(upsampling, m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels()); - } else { - noUpsample(m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels()); - } - - audioFifo = decodeMsg->getAudioFifo(); - - if (audioFifo && (m_audioBufferFill >= m_audioBuffer.size() - 960)) - { - uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) { - qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - } - else - { - qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: decode failed"); - } - } - - delete message; - - if (m_inputMessageQueue.size() > 100) - { - qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: too many messages in queue. Flushing..."); - m_inputMessageQueue.clear(); - break; - } - } - - if (audioFifo) - { - uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) { - qDebug("AMBEWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - m_timestamp = QDateTime::currentDateTime(); -} - -void AMBEWorker::pushMbeFrame(const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo) -{ - m_audioFifo = audioFifo; - m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo)); -} - -bool AMBEWorker::isAvailable() -{ - if (m_audioFifo == 0) { - return true; - } - - return m_timestamp.time().msecsTo(QDateTime::currentDateTime().time()) > 1000; // 1 second inactivity timeout -} - -bool AMBEWorker::hasFifo(AudioFifo *audioFifo) -{ - return m_audioFifo == audioFifo; -} - -void AMBEWorker::upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels) -{ - for (int i = 0; i < nbSamplesIn; i++) - { - //float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) m_compressor.compress(in[i])) : (float) m_compressor.compress(in[i]); - float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i]; - float prev = m_upsamplerLastValue; - qint16 upsample; - - for (int j = 1; j <= upsampling; j++) - { - upsample = (qint16) m_upsampleFilter.runLP(cur*m_upsamplingFactors[j] + prev*m_upsamplingFactors[upsampling-j]); - m_audioBuffer[m_audioBufferFill].l = channels & 1 ? m_compressor.compress(upsample) : 0; - m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? m_compressor.compress(upsample) : 0; - - if (m_audioBufferFill < m_audioBuffer.size() - 1) { - ++m_audioBufferFill; - } - } - - m_upsamplerLastValue = cur; - } - - if (m_audioBufferFill >= m_audioBuffer.size() - 1) { - qDebug("AMBEWorker::upsample(%d): audio buffer is full check its size", upsampling); - } -} - -void AMBEWorker::noUpsample(short *in, int nbSamplesIn, unsigned char channels) -{ - for (int i = 0; i < nbSamplesIn; i++) - { - float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i]; - m_audioBuffer[m_audioBufferFill].l = channels & 1 ? cur*m_upsamplingFactors[0] : 0; - m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? cur*m_upsamplingFactors[0] : 0; - - if (m_audioBufferFill < m_audioBuffer.size() - 1) { - ++m_audioBufferFill; - } - } - - if (m_audioBufferFill >= m_audioBuffer.size() - 1) { - qDebug("AMBEWorker::noUpsample: audio buffer is full check its size"); - } -} - -void AMBEWorker::setVolumeFactors() -{ - m_upsamplingFactors[0] = m_volume; - - for (int i = 1; i <= m_upsampling; i++) { - m_upsamplingFactors[i] = (i*m_volume) / (float) m_upsampling; - } -} diff --git a/sdrbase/ambe/ambeworker.h b/sdrbase/ambe/ambeworker.h deleted file mode 100644 index e6136c139..000000000 --- a/sdrbase/ambe/ambeworker.h +++ /dev/null @@ -1,156 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2019 F4EXB // -// written by Edouard Griffiths // -// // -// 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 SDRBASE_AMBE_AMBEWORKER_H_ -#define SDRBASE_AMBE_AMBEWORKER_H_ - -#include -#include -#include - -#include "export.h" -#include "dvcontroller.h" - -#include "util/messagequeue.h" -#include "util/message.h" -#include "dsp/filtermbe.h" -#include "dsp/dsptypes.h" -#include "audio/audiocompressor.h" - -class AudioFifo; - -class SDRBASE_API AMBEWorker : public QObject { - Q_OBJECT -public: - class MsgTest : public Message - { - MESSAGE_CLASS_DECLARATION - public: - static MsgTest* create() { return new MsgTest(); } - private: - MsgTest() {} - }; - - class MsgMbeDecode : public Message - { - MESSAGE_CLASS_DECLARATION - public: - const unsigned char *getMbeFrame() const { return m_mbeFrame; } - SerialDV::DVRate getMbeRate() const { return m_mbeRate; } - int getVolumeIndex() const { return m_volumeIndex; } - unsigned char getChannels() const { return m_channels % 4; } - bool getUseHP() const { return m_useHP; } - int getUpsampling() const { return m_upsampling; } - AudioFifo *getAudioFifo() { return m_audioFifo; } - - static MsgMbeDecode* create( - const unsigned char *mbeFrame, - int mbeRateIndex, - int volumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo) - { - return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, useHP, upsampling, audioFifo); - } - - private: - unsigned char m_mbeFrame[SerialDV::MBE_FRAME_MAX_LENGTH_BYTES]; - SerialDV::DVRate m_mbeRate; - int m_volumeIndex; - unsigned char m_channels; - bool m_useHP; - int m_upsampling; - AudioFifo *m_audioFifo; - - MsgMbeDecode(const unsigned char *mbeFrame, - SerialDV::DVRate mbeRate, - int volumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo) : - Message(), - m_mbeRate(mbeRate), - m_volumeIndex(volumeIndex), - m_channels(channels), - m_useHP(useHP), - m_upsampling(upsampling), - m_audioFifo(audioFifo) - { - memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::DVController::getNbMbeBytes(m_mbeRate)); - } - }; - - AMBEWorker(); - ~AMBEWorker(); - - void pushMbeFrame(const unsigned char *mbeFrame, - int mbeRateIndex, - int mbeVolumeIndex, - unsigned char channels, - bool useHP, - int upsampling, - AudioFifo *audioFifo); - - bool open(const std::string& deviceRef); //!< Either serial device or ip:port - void close(); - void process(); - void stop(); - bool isAvailable(); - bool hasFifo(AudioFifo *audioFifo); - - void postTest() - { - //emit inputMessageReady(); - m_inputMessageQueue.push(MsgTest::create()); - } - - MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication - -signals: - void finished(); - -public slots: - void handleInputMessages(); - -private: - void upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels); - void noUpsample(short *in, int nbSamplesIn, unsigned char channels); - void setVolumeFactors(); - - SerialDV::DVController m_dvController; - AudioFifo *m_audioFifo; - QDateTime m_timestamp; - volatile bool m_running; - int m_currentGainIn; - int m_currentGainOut; - short m_dvAudioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE]; - AudioVector m_audioBuffer; - uint m_audioBufferFill; - float m_upsamplerLastValue; - float m_phase; - MBEAudioInterpolatorFilter m_upsampleFilter; - int m_upsampling; - float m_volume; - float m_upsamplingFactors[7]; - AudioCompressor m_compressor; -}; - -#endif // SDRBASE_AMBE_AMBEWORKER_H_ diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index a1cd3b40f..f4d988d59 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -7,7 +7,6 @@ #include "settings/mainsettings.h" #include "commands/command.h" #include "audio/audiodevicemanager.h" -#include "ambe/ambeengine.h" MainSettings::MainSettings() : m_audioDeviceManager(nullptr) diff --git a/sdrbench/mainbench.cpp b/sdrbench/mainbench.cpp index 5d1ec92bc..624d3fcba 100644 --- a/sdrbench/mainbench.cpp +++ b/sdrbench/mainbench.cpp @@ -20,8 +20,6 @@ #include #include -#include "ambe/ambeengine.h" - #include "mainbench.h" MainBench *MainBench::m_instance = 0; From 100ca3510c76a7541e6a11cf2c3bc1bc6f718cf0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 01:31:56 +0200 Subject: [PATCH 19/29] AMBE feature: removed required dependency to SerialDV library --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c1003a4a..7c94d32c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -393,7 +393,7 @@ else() find_package(Codec2) find_package(CM256cc) find_package(LibMbe) - find_package(SerialDV REQUIRED) + find_package(SerialDV) find_package(LibDSDcc) find_package(Sgp4) find_package(AptDec) From c4cb135177b278d72761f806fd85b303bf7e71e2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 14:20:48 +0200 Subject: [PATCH 20/29] AMBE feature: report frames decoding successes and failures --- plugins/feature/ambe/ambe.cpp | 20 +-- plugins/feature/ambe/ambe.h | 4 +- plugins/feature/ambe/ambeengine.cpp | 14 +- plugins/feature/ambe/ambeengine.h | 12 +- plugins/feature/ambe/ambegui.cpp | 22 ++- plugins/feature/ambe/ambeworker.cpp | 7 +- plugins/feature/ambe/ambeworker.h | 4 + sdrbase/resources/webapi/doc/html2/index.html | 24 ++- .../webapi/doc/swagger/include/AMBE.yaml | 17 +- .../sdrangel/api/swagger/include/AMBE.yaml | 17 +- swagger/sdrangel/code/html2/index.html | 24 ++- .../code/qt5/client/SWGAMBEDeviceReport.cpp | 156 ++++++++++++++++++ .../code/qt5/client/SWGAMBEDeviceReport.h | 71 ++++++++ .../code/qt5/client/SWGAMBEReport.cpp | 18 +- .../sdrangel/code/qt5/client/SWGAMBEReport.h | 9 +- .../code/qt5/client/SWGModelFactory.h | 6 + 16 files changed, 384 insertions(+), 41 deletions(-) create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.h diff --git a/plugins/feature/ambe/ambe.cpp b/plugins/feature/ambe/ambe.cpp index 7457f06da..b75ba8179 100644 --- a/plugins/feature/ambe/ambe.cpp +++ b/plugins/feature/ambe/ambe.cpp @@ -347,20 +347,16 @@ void AMBE::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) // devices - SWGSDRangel::SWGAMBEDevices *swgAMBEDevices = response.getAmbeReport()->getDevices(); - swgAMBEDevices->init(); + response.getAmbeReport()->setDevices(new QList); + QList deviceRefs; + getAMBEEngine()->getDeviceRefs(deviceRefs); - qDeviceNames.clear(); - getAMBEEngine()->getDeviceRefs(qDeviceNames); - swgAMBEDevices->setNbDevices((int) qDeviceNames.size()); - QList *ambeDeviceNamesList = swgAMBEDevices->getAmbeDevices(); - - for (const auto& deviceName : qDeviceNames) + for (auto& deviceRef : deviceRefs) { - ambeDeviceNamesList->append(new SWGSDRangel::SWGAMBEDevice); - ambeDeviceNamesList->back()->init(); - *ambeDeviceNamesList->back()->getDeviceRef() = deviceName; - ambeDeviceNamesList->back()->setDelete(0); + response.getAmbeReport()->getDevices()->append(new SWGSDRangel::SWGAMBEDeviceReport); + response.getAmbeReport()->getDevices()->back()->setDevicePath(new QString(deviceRef.m_devicePath)); + response.getAmbeReport()->getDevices()->back()->setSuccessCount(deviceRef.m_successCount); + response.getAmbeReport()->getDevices()->back()->setFailureCount(deviceRef.m_failureCount); } } diff --git a/plugins/feature/ambe/ambe.h b/plugins/feature/ambe/ambe.h index 6e049101c..e4e317669 100644 --- a/plugins/feature/ambe/ambe.h +++ b/plugins/feature/ambe/ambe.h @@ -62,7 +62,7 @@ public: public: QList& getAvailableDevices() { return m_availableDevices; } - QList& getUsedDevices() { return m_usedDevices; } + QList& getUsedDevices() { return m_usedDevices; } static MsgReportDevices* create() { return new MsgReportDevices(); @@ -70,7 +70,7 @@ public: private: QList m_availableDevices; - QList m_usedDevices; + QList m_usedDevices; MsgReportDevices() : Message() diff --git a/plugins/feature/ambe/ambeengine.cpp b/plugins/feature/ambe/ambeengine.cpp index c77eade8a..faa91610a 100644 --- a/plugins/feature/ambe/ambeengine.cpp +++ b/plugins/feature/ambe/ambeengine.cpp @@ -43,6 +43,16 @@ #include "ambeworker.h" #include "ambeengine.h" +uint32_t AMBEEngine::AMBEController::getSuccessCount() const +{ + return worker ? worker->getSuccessCount() : 0; +} + +uint32_t AMBEEngine::AMBEController::getFailureCount() const +{ + return worker ? worker->getFailureCount() : 0; +} + AMBEEngine::AMBEEngine() {} @@ -291,13 +301,13 @@ void AMBEEngine::releaseAll() m_controllers.clear(); } -void AMBEEngine::getDeviceRefs(QList& deviceNames) +void AMBEEngine::getDeviceRefs(QList& deviceRefs) { std::vector::const_iterator it = m_controllers.begin(); while (it != m_controllers.end()) { - deviceNames.push_back(QString(it->device.c_str())); + deviceRefs.push_back(DeviceRef{QString(it->device.c_str()), it->getSuccessCount(), it->getFailureCount()}); ++it; } } diff --git a/plugins/feature/ambe/ambeengine.h b/plugins/feature/ambe/ambeengine.h index 9e9fd805b..21bbb421a 100644 --- a/plugins/feature/ambe/ambeengine.h +++ b/plugins/feature/ambe/ambeengine.h @@ -36,6 +36,13 @@ class AMBEEngine : public QObject { Q_OBJECT public: + struct DeviceRef + { + QString m_devicePath; //!< device path or url + uint32_t m_successCount; //!< number of frames successfully decoded + uint32_t m_failureCount; //!< number of frames failing decoding + }; + AMBEEngine(); ~AMBEEngine(); @@ -43,7 +50,7 @@ public: void releaseAll(); int getNbDevices() const { return m_controllers.size(); } //!< number of devices used - void getDeviceRefs(QList& devicesRefs); //!< reference of the devices used (device path or url) + void getDeviceRefs(QList& devicesRefs); //!< reference of the devices used bool registerController(const std::string& deviceRef); //!< create a new controller for the device in reference void releaseController(const std::string& deviceRef); //!< release controller resources for the device in reference @@ -67,6 +74,9 @@ private: QThread *thread; AMBEWorker *worker; std::string device; + + uint32_t getSuccessCount() const; + uint32_t getFailureCount() const; }; #ifndef _WIN32 diff --git a/plugins/feature/ambe/ambegui.cpp b/plugins/feature/ambe/ambegui.cpp index 2db66558d..e99026cc2 100644 --- a/plugins/feature/ambe/ambegui.cpp +++ b/plugins/feature/ambe/ambegui.cpp @@ -192,7 +192,12 @@ bool AMBEGUI::handleMessage(const Message& message) ui->ambeDeviceRefs->clear(); for (const auto& inUseDevice : cfg.getUsedDevices()) { - ui->ambeDeviceRefs->addItem(inUseDevice); + ui->ambeDeviceRefs->addItem( + tr("%1 - %2|%3") + .arg(inUseDevice.m_devicePath) + .arg(inUseDevice.m_successCount) + .arg(inUseDevice.m_failureCount) + ); } return true; @@ -226,14 +231,19 @@ void AMBEGUI::populateSerialList() void AMBEGUI::refreshInUseList() { - QList inUseDevices; + QList inUseDevices; m_ambe->getAMBEEngine()->getDeviceRefs(inUseDevices); ui->ambeDeviceRefs->clear(); for (const auto& inUseDevice : inUseDevices) { - qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(inUseDevice)); - ui->ambeDeviceRefs->addItem(inUseDevice); + qDebug("AMBEGUI::refreshInUseList: %s", qPrintable(inUseDevice.m_devicePath)); + ui->ambeDeviceRefs->addItem( + tr("%1 - %2|%3") + .arg(inUseDevice.m_devicePath) + .arg(inUseDevice.m_successCount) + .arg(inUseDevice.m_failureCount) + ); } } void AMBEGUI::on_importSerial_clicked() @@ -253,7 +263,7 @@ void AMBEGUI::on_importSerial_clicked() { if (m_ambe->getAMBEEngine()->registerController(serialName.toStdString())) { - ui->ambeDeviceRefs->addItem(serialName); + ui->ambeDeviceRefs->addItem(tr("%1 - 0|0").arg(serialName)); ui->statusText->setText(tr("%1 added").arg(serialName)); } else @@ -300,7 +310,7 @@ void AMBEGUI::on_removeAmbeDevice_clicked() return; } - QString deviceName = deviceItem->text(); + QString deviceName = deviceItem->text().split(" ").at(0); m_ambe->getAMBEEngine()->releaseController(deviceName.toStdString()); ui->statusText->setText(tr("%1 removed").arg(deviceName)); refreshInUseList(); diff --git a/plugins/feature/ambe/ambeworker.cpp b/plugins/feature/ambe/ambeworker.cpp index 37713daba..11c6f2ae3 100644 --- a/plugins/feature/ambe/ambeworker.cpp +++ b/plugins/feature/ambe/ambeworker.cpp @@ -33,7 +33,9 @@ AMBEWorker::AMBEWorker() : m_upsamplerLastValue(0.0f), m_phase(0), m_upsampling(1), - m_volume(1.0f) + m_volume(1.0f), + m_successCount(0), + m_failureCount(0) { m_audioBuffer.resize(48000); m_audioBufferFill = 0; @@ -119,10 +121,13 @@ void AMBEWorker::handleInputMessages() m_audioBufferFill = 0; } + + m_successCount++; } else { qDebug("AMBEWorker::handleInputMessages: MsgMbeDecode: decode failed"); + m_failureCount++; } } diff --git a/plugins/feature/ambe/ambeworker.h b/plugins/feature/ambe/ambeworker.h index e6136c139..f094f83c0 100644 --- a/plugins/feature/ambe/ambeworker.h +++ b/plugins/feature/ambe/ambeworker.h @@ -115,6 +115,8 @@ public: void stop(); bool isAvailable(); bool hasFifo(AudioFifo *audioFifo); + uint32_t getSuccessCount() const { return m_successCount; } + uint32_t getFailureCount() const { return m_failureCount; } void postTest() { @@ -151,6 +153,8 @@ private: float m_volume; float m_upsamplingFactors[7]; AudioCompressor m_compressor; + uint32_t m_successCount; + uint32_t m_failureCount; }; #endif // SDRBASE_AMBE_AMBEWORKER_H_ diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 136e97d20..120b8e2ef 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -1274,6 +1274,23 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" +}; + defs.AMBEDeviceReport = { + "properties" : { + "devicePath" : { + "type" : "string", + "description" : "AMBE device full path or AMBE server URL" + }, + "successCount" : { + "type" : "integer", + "description" : "number of frames decoded successfully" + }, + "failureCount" : { + "type" : "integer", + "description" : "number of frames that failed to decode" + } + }, + "description" : "Report of AMBE device in use" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1299,8 +1316,11 @@ margin-bottom: 20px; "$ref" : "#/definitions/DVSerialDevices" }, "devices" : { + "type" : "array", "description" : "List of AMBE devices or servers in use", - "$ref" : "#/definitions/AMBEDevices" + "items" : { + "$ref" : "#/definitions/AMBEDeviceReport" + } } }, "description" : "AMBE" @@ -56040,7 +56060,7 @@ except ApiException as e:
    - Generated 2022-05-24T23:13:14.725+02:00 + Generated 2022-05-25T12:47:57.273+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml index 275857baf..1720cecfb 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/AMBE.yaml @@ -27,7 +27,9 @@ AMBEReport: $ref: "/doc/swagger/include/AMBE.yaml#/definitions/DVSerialDevices" devices: description: List of AMBE devices or servers in use - $ref: "/doc/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + type: array + items: + $ref: "/doc/swagger/include/AMBE.yaml#/definitions/AMBEDeviceReport" AMBEActions: description: AMBE @@ -86,3 +88,16 @@ definitions: delete: description: "1 if device is to be removed from active list" type: integer + + AMBEDeviceReport: + description: "Report of AMBE device in use" + properties: + devicePath: + type: string + description: AMBE device full path or AMBE server URL + successCount: + type: integer + description: number of frames decoded successfully + failureCount: + type: integer + description: number of frames that failed to decode diff --git a/swagger/sdrangel/api/swagger/include/AMBE.yaml b/swagger/sdrangel/api/swagger/include/AMBE.yaml index f4e9cef2c..d696083a0 100644 --- a/swagger/sdrangel/api/swagger/include/AMBE.yaml +++ b/swagger/sdrangel/api/swagger/include/AMBE.yaml @@ -27,7 +27,9 @@ AMBEReport: $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/DVSerialDevices" devices: description: List of AMBE devices or servers in use - $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/AMBEDevices" + type: array + items: + $ref: "http://swgserver:8081/api/swagger/include/AMBE.yaml#/definitions/AMBEDeviceReport" AMBEActions: description: AMBE @@ -86,3 +88,16 @@ definitions: delete: description: "1 if device is to be removed from active list" type: integer + + AMBEDeviceReport: + description: "Report of AMBE device in use" + properties: + devicePath: + type: string + description: AMBE device full path or AMBE server URL + successCount: + type: integer + description: number of frames decoded successfully + failureCount: + type: integer + description: number of frames that failed to decode diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 136e97d20..120b8e2ef 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -1274,6 +1274,23 @@ margin-bottom: 20px; } }, "description" : "AMBE devices active in the system" +}; + defs.AMBEDeviceReport = { + "properties" : { + "devicePath" : { + "type" : "string", + "description" : "AMBE device full path or AMBE server URL" + }, + "successCount" : { + "type" : "integer", + "description" : "number of frames decoded successfully" + }, + "failureCount" : { + "type" : "integer", + "description" : "number of frames that failed to decode" + } + }, + "description" : "Report of AMBE device in use" }; defs.AMBEDevices = { "required" : [ "nbDevices" ], @@ -1299,8 +1316,11 @@ margin-bottom: 20px; "$ref" : "#/definitions/DVSerialDevices" }, "devices" : { + "type" : "array", "description" : "List of AMBE devices or servers in use", - "$ref" : "#/definitions/AMBEDevices" + "items" : { + "$ref" : "#/definitions/AMBEDeviceReport" + } } }, "description" : "AMBE" @@ -56040,7 +56060,7 @@ except ApiException as e:
    - Generated 2022-05-24T23:13:14.725+02:00 + Generated 2022-05-25T12:47:57.273+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.cpp new file mode 100644 index 000000000..5b750967e --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.cpp @@ -0,0 +1,156 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGAMBEDeviceReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGAMBEDeviceReport::SWGAMBEDeviceReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGAMBEDeviceReport::SWGAMBEDeviceReport() { + device_path = nullptr; + m_device_path_isSet = false; + success_count = 0; + m_success_count_isSet = false; + failure_count = 0; + m_failure_count_isSet = false; +} + +SWGAMBEDeviceReport::~SWGAMBEDeviceReport() { + this->cleanup(); +} + +void +SWGAMBEDeviceReport::init() { + device_path = new QString(""); + m_device_path_isSet = false; + success_count = 0; + m_success_count_isSet = false; + failure_count = 0; + m_failure_count_isSet = false; +} + +void +SWGAMBEDeviceReport::cleanup() { + if(device_path != nullptr) { + delete device_path; + } + + +} + +SWGAMBEDeviceReport* +SWGAMBEDeviceReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGAMBEDeviceReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&device_path, pJson["devicePath"], "QString", "QString"); + + ::SWGSDRangel::setValue(&success_count, pJson["successCount"], "qint32", ""); + + ::SWGSDRangel::setValue(&failure_count, pJson["failureCount"], "qint32", ""); + +} + +QString +SWGAMBEDeviceReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGAMBEDeviceReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(device_path != nullptr && *device_path != QString("")){ + toJsonValue(QString("devicePath"), device_path, obj, QString("QString")); + } + if(m_success_count_isSet){ + obj->insert("successCount", QJsonValue(success_count)); + } + if(m_failure_count_isSet){ + obj->insert("failureCount", QJsonValue(failure_count)); + } + + return obj; +} + +QString* +SWGAMBEDeviceReport::getDevicePath() { + return device_path; +} +void +SWGAMBEDeviceReport::setDevicePath(QString* device_path) { + this->device_path = device_path; + this->m_device_path_isSet = true; +} + +qint32 +SWGAMBEDeviceReport::getSuccessCount() { + return success_count; +} +void +SWGAMBEDeviceReport::setSuccessCount(qint32 success_count) { + this->success_count = success_count; + this->m_success_count_isSet = true; +} + +qint32 +SWGAMBEDeviceReport::getFailureCount() { + return failure_count; +} +void +SWGAMBEDeviceReport::setFailureCount(qint32 failure_count) { + this->failure_count = failure_count; + this->m_failure_count_isSet = true; +} + + +bool +SWGAMBEDeviceReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(device_path && *device_path != QString("")){ + isObjectUpdated = true; break; + } + if(m_success_count_isSet){ + isObjectUpdated = true; break; + } + if(m_failure_count_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.h b/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.h new file mode 100644 index 000000000..ea3614186 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEDeviceReport.h @@ -0,0 +1,71 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGAMBEDeviceReport.h + * + * Report of AMBE device in use + */ + +#ifndef SWGAMBEDeviceReport_H_ +#define SWGAMBEDeviceReport_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGAMBEDeviceReport: public SWGObject { +public: + SWGAMBEDeviceReport(); + SWGAMBEDeviceReport(QString* json); + virtual ~SWGAMBEDeviceReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGAMBEDeviceReport* fromJson(QString &jsonString) override; + + QString* getDevicePath(); + void setDevicePath(QString* device_path); + + qint32 getSuccessCount(); + void setSuccessCount(qint32 success_count); + + qint32 getFailureCount(); + void setFailureCount(qint32 failure_count); + + + virtual bool isSet() override; + +private: + QString* device_path; + bool m_device_path_isSet; + + qint32 success_count; + bool m_success_count_isSet; + + qint32 failure_count; + bool m_failure_count_isSet; + +}; + +} + +#endif /* SWGAMBEDeviceReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp index a3b8c8fd7..ae3c88090 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.cpp @@ -42,7 +42,7 @@ void SWGAMBEReport::init() { serial = new SWGDVSerialDevices(); m_serial_isSet = false; - devices = new SWGAMBEDevices(); + devices = new QList(); m_devices_isSet = false; } @@ -52,6 +52,10 @@ SWGAMBEReport::cleanup() { delete serial; } if(devices != nullptr) { + auto arr = devices; + for(auto o: *arr) { + delete o; + } delete devices; } } @@ -69,8 +73,8 @@ void SWGAMBEReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&serial, pJson["serial"], "SWGDVSerialDevices", "SWGDVSerialDevices"); - ::SWGSDRangel::setValue(&devices, pJson["devices"], "SWGAMBEDevices", "SWGAMBEDevices"); + ::SWGSDRangel::setValue(&devices, pJson["devices"], "QList", "SWGAMBEDeviceReport"); } QString @@ -90,8 +94,8 @@ SWGAMBEReport::asJsonObject() { if((serial != nullptr) && (serial->isSet())){ toJsonValue(QString("serial"), serial, obj, QString("SWGDVSerialDevices")); } - if((devices != nullptr) && (devices->isSet())){ - toJsonValue(QString("devices"), devices, obj, QString("SWGAMBEDevices")); + if(devices && devices->size() > 0){ + toJsonArray((QList*)devices, obj, "devices", "SWGAMBEDeviceReport"); } return obj; @@ -107,12 +111,12 @@ SWGAMBEReport::setSerial(SWGDVSerialDevices* serial) { this->m_serial_isSet = true; } -SWGAMBEDevices* +QList* SWGAMBEReport::getDevices() { return devices; } void -SWGAMBEReport::setDevices(SWGAMBEDevices* devices) { +SWGAMBEReport::setDevices(QList* devices) { this->devices = devices; this->m_devices_isSet = true; } @@ -125,7 +129,7 @@ SWGAMBEReport::isSet(){ if(serial && serial->isSet()){ isObjectUpdated = true; break; } - if(devices && devices->isSet()){ + if(devices && (devices->size() > 0)){ isObjectUpdated = true; break; } }while(false); diff --git a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h index feef5985b..2471dfddd 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMBEReport.h @@ -22,8 +22,9 @@ #include -#include "SWGAMBEDevices.h" +#include "SWGAMBEDeviceReport.h" #include "SWGDVSerialDevices.h" +#include #include "SWGObject.h" #include "export.h" @@ -46,8 +47,8 @@ public: SWGDVSerialDevices* getSerial(); void setSerial(SWGDVSerialDevices* serial); - SWGAMBEDevices* getDevices(); - void setDevices(SWGAMBEDevices* devices); + QList* getDevices(); + void setDevices(QList* devices); virtual bool isSet() override; @@ -56,7 +57,7 @@ private: SWGDVSerialDevices* serial; bool m_serial_isSet; - SWGAMBEDevices* devices; + QList* devices; bool m_devices_isSet; }; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 5d387a7fa..cf694db10 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -27,6 +27,7 @@ #include "SWGAISSettings.h" #include "SWGAMBEActions.h" #include "SWGAMBEDevice.h" +#include "SWGAMBEDeviceReport.h" #include "SWGAMBEDevices.h" #include "SWGAMBEReport.h" #include "SWGAMBESettings.h" @@ -397,6 +398,11 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGAMBEDeviceReport").compare(type) == 0) { + SWGAMBEDeviceReport *obj = new SWGAMBEDeviceReport(); + obj->init(); + return obj; + } if(QString("SWGAMBEDevices").compare(type) == 0) { SWGAMBEDevices *obj = new SWGAMBEDevices(); obj->init(); From 7e0ac4d2a8cd8ad21d027c4ac3f0f455bf60d219 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 18:19:13 +0200 Subject: [PATCH 21/29] AMBE feature: updated documentation --- doc/img/AMBE_plugin.png | Bin 0 -> 26869 bytes doc/img/AMBE_plugin.xcf | Bin 0 -> 85972 bytes doc/img/DSDdemod_plugin.png | Bin 130529 -> 133793 bytes doc/img/DSDdemod_plugin.xcf | Bin 334032 -> 333279 bytes plugins/channelrx/demoddsd/readme.md | 8 ++++ plugins/feature/ambe/readme.md | 58 +++++++++++++++++++++++++++ 6 files changed, 66 insertions(+) create mode 100644 doc/img/AMBE_plugin.png create mode 100644 doc/img/AMBE_plugin.xcf create mode 100644 plugins/feature/ambe/readme.md diff --git a/doc/img/AMBE_plugin.png b/doc/img/AMBE_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..d92d13e54a520574cf5e714b93b9ede3b2ac589b GIT binary patch literal 26869 zcmce;c{o-78#cO|lv0W!5~)zAWF|u?WDaG{*fwXDISo|GkW85?Q<8ZWQYteUB14pU zNM<3!x!3P^-t+$Pp7UPk{Byq7)i-T>uf5i1eV*sJpZmU_<#$C{cK1%kog@-zx7_7R zY9!L8I{dk}g95JvT^k+1KYT4NUc4fA@!}CX2U}AMYch#+BG@Zf{Bq^_gAe)&!XIC~ z^!8}~)0d*!dk@&RaqfR9_4clcxjkS$+5r|M|`RC%GAWGVUE&+R{Hd zm^gZr?RLa|olmY+_S92eQ*}RF?!4DGebae%&+901B8@$3$n^tKnLCV0Yy2K;rsIyU zp8Jd}l{^fZayc6}bp7whsD=ak7SGKy+^xHxxZZblZ*38oqLtNBReRLrS4CBFWad~0 zIigRPo@sHe{!y@U1Fh^MU#UZNH|rlikQb9?x^^{Sc8ww8K%;KVH-|8RsTzZWjTT&W zdMN__Tj{d5?%RChs;U$NopGL3;`7VRZatNfgGR<~a*1X2PkLm0 zj9th_`y)%n-7Lq>o70=OS^xX@z2FeX`JW6$EjsJ+ho3JWFKcgkC40U}(9Zrx%b@sA zhwTSFTi7l-B$7^fOHh}_Hw%&Pmg~N-YM9iWV@jbuALSlax@pgzyUm-)nI5I@a?5Nt z+WG(e-6C_%A?%1*WG3zlg|UL{CH#&5mM^~Z#$S|nm$e;9q|?WUKbu^WByZux?M`w^ z(%Xl(G3-9YGeRpSkCzynq_vzb+TOfLws9g|bRZi!k#8Jvv2Zd!A}gnKMf2_+dJ^df zN$%2xtFB$Yf4F!C{N31?snQndWRQxhe< zwg(KQcCh;FEmo^z-;1A)bNk{iN(Hy0vYD&sqgEb)z{8$55=Z_A9)xlR^Utw)(HF$qJR>Etgy7`FD-lMY8ISh;Mrpk}8 zva+hQvL8P_{llg2q-4Yi)y=lHw(W1VE58IBnps#Nx4l-?($N{VxPAECt*h$>={f>$ zG}3pTPgP5r{`lu2*QkO69U=JA!ZjDrAmy(hyau|9%8&_B;L`g~M zIo~Yn^!s~olf*y&od-C1JeQ`c!gb$jopt~FCw;XiS?LfPH!esa;`H^)N=jh~$~#!~ zdAnYf81tDmCffWc8C_V>OOaDjA`jO`YWJpHO{MW(TcEG0sUdy(^ohsl<5qq~r7sk% zmScVIC6%K1FQl~Yqoe!c&%k$CUcSGu?{FwPC8MS9{)j$AAB>MUq(hxyAC^o6j#U+$WA&>^wnd^2MK3 zTs$^aJ;h;T-3zPK@X>b{PLDj+l2jipbVj$+wtSg=)22=ShtGxM#|IbgQyAIUDErZ{ z9pp2mQcqTVshO$YQ)GX?u-AQ8NR5wjiV5zetA|Hxn&z4ErT$%v0+#dDXSV()vREt@ ze|MVai)hB>>zv9pYie9v?Yk$(O9WzNqiAI>d@#PG=9e{gkqj%59#@@!9;N5srPRmihvM`d&NO6@;-Q`O*u^b@+7W?2?8W$xhMjqo!DaaK-_+Al zH7&cKVEDuR?Va}o<~AxH>`u@vv{_vm2s#)bqmiLkldKdyc$p4)rjDb~x|6rWWh!n+ zJfXQdhg_GVWMylsGTD{iS>{nV+xsm|Q#WmrZdzJwd6l3K&Y z&Tjhd!BST6G1=K)4_Svyz z4}YMaDVx%IQnLH|_dzS|rWlcnzB>=R)+^>E-Rv&7_OZ35#t&<6Xl0eQ)onQ7@_*C9 zRbn6FXxVvr$0a1<5W<7P*R8F&kIDGIuALTyFIbqG zepy+Vu=t+EN+KeFTpRiekuj^u;j>pQm%Arws4h`8L8jI*0$YMxC-2dg?R%=5Ienf+ z4LQCFyT05{xnsvuc1y55F3!3mVQO9HhtAkNh zUR9)$X^HyuzpX1iLr>m%Z>(GQmPX=Enz?P1{q3#n>tj(-X|jIbnro)Ay0&KT=(y!4 zlCH_$KR3whTz_{f8|mcs#+pkMzlGXp-C5gSvC`7gonaMP?=9KomxyEH|Y@AbCXxhMddu>7Uh{=l)xe)f|cP_TK|4j;!8Br?sut?|~ zFC=Se9lTGmyH?mPPX`rhX*k+;387YOKk!AN%;xjs0}05A);&ctk>TfDrwwUnXm&cq z-R#QyH9J%%a;tn}u1V62p*fjnexf6YOEbd`DNm$wCuLoP^uU*ZV%M3Yh`K}+u>D*b z{zXN?LvwQ@P3rG%-K-5irEufMaZ*M`Mq+aEv#6-32~TWnB}2m_2RN^~z6t4i$a4FU zPOR#Ozo=z%!wn=?clQ?D4JXmV8>@5Fttl!awP9SLoYx+s9%LDQ*z7((`XNr-mttZnS7x#HZgGXxJ_OCTjjuy*eWMowT{OtXuC0CND%b)t~ES`6iQ;zZTha#7? zXK>kY-C4+I9Dz(Ps*i|BSj@hdq8!$cE|U$&;bL z7P`8+^$iU;(jK#>xFqaml#-M8<8-yOvTk+@mN<=le9C(9^_7>KTa%ScX8J#`uKn$E z2tOb#6>R%p^6SejNa1W)denj&R*ig&0l$08q6=wWHJ%$k~-4|X5=(qZ7Ogt%HB2$EbvQE9f!PfSXB z`OPvrE9-^hNTc=lth??D<3<)1ly~$qQ;aGfAOg7z=4w=~tQQU=aN;J{u)0472QLA+ zEE8xT_3HeeAG(xXLVcbgckYl7*md|Z!9T~?*0$;SS(E3&_D9P+{tkc7HYqA98q_NI zV0b=V!^Fa(2Cp!Px$-!Vx4k6Sg=^{R@>#ST4?m?VgEK!UV7Xu1eg1`hnL7z*IMc3c z+l(5H7=144%=ve!F9pHXd2cqou(v-%MZyS0AEyz$Z5Q1%me0h@?CbMjK99P;JNhcVNZ$kld~pYNLLU^?u)-`vWzN6afxTop7lR?_8#^IEhA$s(sAxK zva*3|@9lp#%a?}_J)mNcQdIPBAt!z?d|}a=OzajcPPS1cMfu8jn(Vp~$#Y}vFUkLq zkn%mX%Ub2!M3f@^Q&LfFHJoTq&$H?{-OaMPIN3cjJ1dp0khDxh*>1=b$@@qd0)9WcK--6r9X>gTxjTiY|4gT zl@G(Zbk3hA0Ts5rxu#$t)tzfb%^u=+NXW)+Vf-4Skj>k+MBARC3$CuN<-;LU?3C%@ zSYowuPq#p3@u*2h0|Nt@07hwdcTtM_cTu|ZJthT6itMz_&CM|)judyMr|pn3*%ZPD z8-)6fb89U+0D-X|8BwbakD6^Pr1&TN6k)NLtYB0ZL+hy$s zs76npJ)8aRjO2In-`^=s(Q)cMd)P%qMOC`bW=@2(dLo;DFLmSd^Yc6Zg?tA)nOL6o zw+yIjBV^C545BPsDHEx^?>Jhb^Q%Ov2|obI2#J zy$OvG@a3rB6VtEp*-Do|OD-aD#Yy9Rp8Jc^2ojmYHBh{+#jV?Lx&Cl zE?2#|mX^a^SXNdxJ6G9pA6s;Kdb*;iDQLJMI@fVT`HpAPhr5oc7e{GXrDYep4sVk7a6Y(w_S&dpViV&D_+v5pJzUSH;@^Hd1q zlty~UUF*HSojQj>eG6``5`uz)R0ny^rv`61&c~}#bLI)F$G+A^7HetPCn#dowMn5XU?3FSYI08Zm^6Mb$$gn z(`hr`S?+ais&sA-jwydc?9cImi3x>`^|jQiscL~j>SD^MLAj<4YEdi6{{?+Tjw5@# zH`as*C_OXy&ra!ht{^Y2kHEw@>T?lC_VeW-0tXm)CwhKGky*WR4B&2x8+RNVxi4@c z$ZB~7t=g%87>*1yc2AZ6JKYhY25<@7JDl4jF@TGaZ(w^rck(ij@lR|H>{BKtCiC7> zx9}=!2gl)h1|l&mjDNf9U$uAr(AAAFx3bPJUmgJ7Ij%0*ql3)K&VGs1BCk}4Gi5@4 zta2k#bjL*Vm{X_3IlG^vKY#v&p1#g_2cJfW6+($oiJCQSMl>`uI zDF7~^T$+2r__b^3_>|p^`lnSP#dyJ{Q<|?jkl%B_G&qdG#;iqt@j9l@Nm!Jk3gph z&5zKZ$9pX@#NS>yDU%S~=e;4W`$Q@k2!{5+fzQ~6(~FD4ZK>+DW&p^>AMR~ajF&i^ zucIU-wHf&6Q&pAZt#cAJD>u-Y{g1}syv*FBisahXikc7(?L6~+2m+aO4ab>&>Dx=c zwzJ)MhysEtw1khuyRtBgIz2)sq?Tdq^cm6kxzcKtBc*XNO`&58@H(_ z?&m1KbD1&)XyLwI!e7TmV%0b0thl;g<5Okj#l>N+WQ5{>d-(}jiQ!|jwkS)r$JnLB z0yw;ke@Y+w@q_YzK=@Cs!KPaHhi~$0q{i{x63H*56qohlq^39QGnrzg73Z2~r7!PL z^{JdyeYyDRK^FL>nfl6Ew{{t{yk`$m$xn&)4>>s4e;GKGot>R?*2ne3n3E_Nh~UY@ zSJe;5U>EG|?Rgci?6do~c$0Bm+qKD;`Hyq_DTA03dCTw5r4{_XG$WstpD&%HHk7)B zhO*A{;0$Y?#a3GlUtYGK@t3kJba>_j4^m-y&Bi9wN@IBc{%V$(*(Ncy;{E-^+!RlW z^4%uy)@ef#iqBjF@!spI?rb4h+(*LXE8jNz3VB<&ACP)vmiao)BfeX+w{8EUVrq|| z4?YI0r}g*n;>y)>>yP$JgnGW-7z$>oY22$KlW_N0Y+mSF@&h+H0mib&^T&@-22QT`5# z;3RWKiMF*NN}!I82ZyZ!=0@DaBE|86i9*Ax63I&@Jr}ZNre+pI(Zd8e#9aYM}#TZ zZct6;yDwPYyEnU@Wiiqi8^-#Ix3jA&$6-i5CMG6S(`F(p;wFJb<0ZUu^fJGWIjsUG zyuJR8j-H;LpnGuCw^Y9Qt~18%8P^p87zLJA=b9quAU3VAl5Mr~p zG~HY!` z2*T$$tZ0L-4RM1#VFqwO0z9@G{mM!n^aoy6L$+Y5yR59741N%h|D4Lilr z0^yS5|72O1&$QmKF-D{ZkWg2vJym@>DnTHNWaBI8C+L&z(NqIb4gw{*by1)JKYH}& zJwQFPlL?44RB_+VTr=8(2dlxI5Ezow*w~20Cr7TM(Ja0#S52KByYt-&qrO$NYJ&eR zuIKOOC<>Tl6+QXqR^Ny5Jp|>E8D3Fw{`chMSbO>*#()Rtn_7zOO~_6wis-ITmF*En zp48@6WU{YDTGFk_uF}7~9(guDq5wU3ZvEC`^T(A4d#&(e&sJhPqhSzVKv`+uU8diJ zvv6FU&9d#2K&$Eh?%g@0Q*P~?tzq)MV2n_EwJGu&ZyrMD+*)XBP+C@&8)jr?_7HF6 z(s=vg_S(uS_Ap4E9ox5W=RS1`+*u>9mzr9O`og+uMj^w(M%K$lJ*zjn3qF0kPUx2oVavV#wWO%9+`E5&tT|yjiaXos)5?)wdV87C z6{xAJGYH!qp}5a#Kt6u_xFOF$*K_u0mVRp5Z&PE)2Bnv7)Vr&Id)NCd2zdtLVUQp zxmp2ig>SD4-@SYHxgtAy+%M?IEWWH5{GD#NC;J>@Jlw%b$cSe{_7HuVOiGaP&L{N9V_yR~%J|IMYxHkmSTb8`zj z4DQ&?$d6u|VB+K>P7{|+LS`a&WLy`K&`?Fy5|jLs1s)h1{6Tv9srL4EG!J*c@f^DI z_3IV1r}t$NHoZ`a7VLTF5`;99r*n$v$+fbpG=rGq+ED_c1g!dphaW^o9|9r5arEeB zR6`&!2IP5AqaQwg+>9NsY2YPoXU7BLvDUe@URKwkn*!7jvfT+X8nNK0s9NxYb>Q;8 z7u-CQS+*GE(2<#v!T~btXLax~tl6{ha2Wtt)wFN?=3np7ozlsII+D_xGFHfH^ZxE! zA6sy^;m3lOkdCib=LBDNS=r9Ik8xTvGQTaQ$gdy0{YJ)D6#uFp)leU@K6+_ivez{Y zelamU%20NtsGpOQ4cNj2UxPC?LQerACuSG@>91HdfU~UJ+yvB1f`jNZi$6J6;-{GT(DSj; zawre}{BR$88x-T{k9R_;si|7p+A|Vm#t%=3zpdXGcz;V z$zGv=i@eU`SFuVDn#~76olr47pRp?|E*5!0f96YTYY5mcS)2Q^*tEmsV)18%Pva1^kc`c_W52tQCRLVGyIOkhSyD z?^LJ$GmNTM616vk=SwDWP12X?U< zY4a#vXfpe(d|HCdLvsh~rzb4ozV?nmd!-)HM<}O+WO*|1vrKL}k7^8_nn0Jq33yxF zCM&CnlyNu3kFz3Kd3l$T)KIXZO-6?M$svkslGZ!PjY;xhn|Yb%PSh{&Qf;#e)kzoB z6$+P@ekozL%uUAsUUhYMY!siln38iX-fG!iy|$J?`jMu;nAyoo5^F#6`-{URO=YQ* zS|5+F&GFvFh0Yo%?8~-Lzw*{fxm@!)_7_5g6Kqx|M zTX6R9u4mW;mia5bT;BD8%uRW#Zm~HojlFi4@0)3q7Z)!(W;R1on9tfTLJ<}j$#Ct> zRU$6K-u!%j1v?R2e8;X`A+wIt+;u8)HNDzWMh{Z>7j|OFj-z=6Z_#Yd56!CZYi)LZ zK7_)^wWi`#tyghY%OY;!L-7z`B7LLU41G^n8jZCuicy>&ayL}p$**&{l)59ZGP!2& ztrwR&xKD9&qftEB<^YE1JoLJ_5>IrcHP6`4)q)Uq{QKuR`RBNq>UshK;@9BdC*%{q zM~|x2mn)9d6@meV3I#aDboj8nlT#w9{P(V|XV0HkH$)5hL2LsUc&?aSmEmn9h*N*; zu5~MRYuHBX=b)P>Uw1Obj8|+-Y}KSbFcz@!ns)NZ)$*hyHjq&taL~LwenIQ+=dG=O zI~sT^C@6^D`h7V~BmLc#@vlSYMzAC&}PmPWDFok zMWDEWk&!B#_mJXIA0INSoWh;Ds-H2EKHD$q@$>)QskCxONzVKn&F#}AMi#u0adKM2 z8ebA>HB+w=-Mov73z4JclRoyW_FZ_2zAq4Q4xq>+YhJ&fj7+35Yt62vAjx1>#=Nz6~*VJv)lPcvk5@|U0&m-dR zy+}6x$?doHNSA9Yzj}~_U4(Tf*oC0od{{Fy^2!j@?mkQcN{RQJr6dvAZhd|Itxo<< zobtcL$uSVwNcwN}%c4LtAJAHcdhtAx>>m&r6(tWas+pn3Dj;wmR#_H=zT3aaLR8r* z5*UES%pllk<>l^Ni1O&nFqNg|`Hy557(7{p~e{vXHBs2hjIJn2bVf z&~a-$9q=oz;9Rq?kXLGGY2ob{Blh~voks-(@+&$zfXe`;$dq=})YL7lt;J4bH2V7b z*y(?RFDxIgudhF$@%A~51Neai*&v7MpBIM2aj9=KGaJ9YyhzaH5U=yBJD;JEqB9q! zh;#f^_j!*)I1M6L@^|fpAAwH~Za-%#-!RLHW`hDP69+5nrdZ{Okv^|yQDGkpkBEyO zMzw@AfDnM)fadUFCBy^t{%dg|R5(p>@v@3qDE=Mx&dy0H@e=6V=wg+t4%wD&BDi^U zP$a!l*L^H3ET=(=yqK^Bvhbtj_&TwGTTMl=1zo6lzRr-s1<)2%frm{{5vUG}?#D$y zM*9d!Z3i`VL{t(mgfCNQLLs?lFn3sYhor`fTo!S6esEm~F}0ic~{uJ|5AYk z^z{Zn_#Jy1N45Te@v8jz@!rPYjdd1)Iw}?kCN3^6poE9iEW=PqAlnd#SB_j$FLUV%oBU5=WF5QrjpRGS~7fW?ZaSI}hmEn2oFG%tN$9?V|_ zEyU}&WCvigG?o&-gOU<^8%LL9ufUmbx)jvaarO&b*a zgH#j|P5>H5mVwi0n`Zj>`5;jWJ2iA>_`+wk80qlRRUq+|}41A_`Ah>#ZdM>M| zn4xZxE=fzm40U&_vnoW)t|k{E}eHyqa&pwG?{9}`ww@wcEol{wiD|8?D6Fu zDnxoyGwPgIKUlPb+uHE6)_3iqj*~1IyU7w1PYqzeA^aQui~^6bs(enPS0ECC2PdRh z7>-V6LKHorl%qnUG13%Y)zJ|#KR?fXJtrwKG0St=K{-ZP^|euC#GU%O(pmM}fWF{; zKftU2oDF%j5*mZ7ni>tr^P~LyS0f)Hn2{3^0VU^aHxM>Fk&3UsSv|^V=5NL*sF=m_#_sB#WbY_hLx+E5U=C*BNa4ZSgrB}% z3rbz?^5rA&LV(SR!>1TQ!H@uRp%1yeA}>u%VQ-u)+jT8f%@RRZGu6-@@l~$^|(Qwau5(nZZ7{d z73{kI;YuQP{XkU^ZZ#7LtxhF~5(`MB*hNdr%bV=hR-8d6Tsu-mNq#T{;cXL|*#|j& zj_SFlN`xv%I(qEbE^85o0aft>NF;tW?*Hc*8yEsBn0|V+582BHMknY}NY_d39-qihxH)Q#ADSDT-z$CKr)D7_?!F>+8k?q+TP6Q$}b?Cy1|qD6Cw zz3h+n6AknrQ2U3LmS;$*>S+8?DMEGZY#RDiXu<_G#!81g?+)9JT=^H^7SLBf7lB-_ zmg^}{TK(h4g)jM?iHXONwE~q>h=Rr}?r!*B?F<`P6y(Ek4i0)tt$BKbXWt8Qj+PiR0sE_KqirozzjJ(469kC{;gVj1?nDdnZwsK-B?ps zSD%@iJ9hlI59qrS0^=ZXkiGl@1KBw_$NfC^(%@80^RFt?ZIDe(WdJe?3JQEh>0ZUg zUJvY8m{nhIrP9;eJ4F9wWsBQsNm0?L(b3TqPD#hDv@oiY8&_)c!(w2Wvw=8G)PuJ4 zt`K=e#X+cAO=YXRO&)8GMXJNv;TzKx&4e>HSeA~C4mX0!00!r-mVVY3DSv(Z6|PbD|wC25<*#Rcy_byt3;RgdVbQ|oiZ z{S$nu6TA1QWLpj;Otg<=)i+7^-?~Ok9mFk3KYX2V(>nZ}Hdg>Cc8X@=>hWl!BtNAc zGrU~gy)#MI7qKGyYrKv&WZ`|E(zJ59clI^zK$9WBi$ZAT;SpC!i_@jrXCqyx98Zsh}i1Ia!@eA!NG`DGtataO9si?V$i=K z!asmc097=>KVKVY3VDZ;nwkSfv#cy0aF5bxO4iqs6Tk6-hbLH4C=tsLrtBOX++QtZ zXk{NI*mUIQ7-9Q+U?N|3Y@GW8j^_g#l=PK z0zz%pE_FQ%5$ve2@N-I9_7@ro1CIzXpdug07x-#_*DBvdA>?mZDoz-|F(?Px{mEu7 zv(c|FA^txCFB$~TCA=3~GW5$H2L%Q3a#Gw$OXE^jR=%349}9^i+wPatQ2#%8V2Gj* znZBPe8i3PffpX>-ZUzevu?Aq8W*g5rS*m$kL|FLBt4oxP zapDTNV9+hUu>axQ3?n>kkp%?(4ks&Q7Z?lgK6$c>MBqPM(T2_W{3uX5C-Ps`^Ta{J z1kuc`Tj2k<&pRHjL*kgo{T0v=am*S>@RDnvd?;yWO97QnGl{#0f`H%hG&J-RPH_xL zEpUJn>S?y+H%@>J&|r+D4fa^}s zU8`n<2f!jhHO5W*2=WqEJCMZoPW4?r8SqqeWE=(s#DN=zgU)u_zHd%apeI&ie~Fnx zgW9!1JE2gM|sKR#_ea`O$ z^wSQgDbV%9{(@D9rK7&3V-jUFRVV+=>VaLX`s-0cPX>RnYax2uGYx{8&5w(SFn|>& zm*DoYK#zcsLsfya46pSo7_JBpB(|tpfweXX6hbw!7;XyZ5&QF} zVXyaw7h7V6!FwW3=Soi#9%DF`Qea;|Y7YVN2&e5%pxO{}3EmEOEYX>Q6Es|z9|Kcj z77a__8Vm&@eefgF+V(+Rvd6XD`qNVbi2)Ra35w0G1y9 zuE#}KI#n*Gv^Kw1JqBHW`;8uM0uOjH)6yoa_BA%{QBhGrz6y&72-uCr1Z4dlSh%|i zZBeZW^O}76c&tlr&D_FQ0O1)}c{igS-p=+mK;{@{7-x3?S7fCa0~N(k8N5W$0F>Zm zgtKn|(l&ZX8A^vc@a_>!p}zN;GrVv2AjQKHt1i)D&aW6J7Ks&YNmb`SuNrbwJ2oap z*kkeStou^bXeC}E?7!wMiIi}Rsal4+&0u%T5q8lUj(9tG46Nfhjvv1<(e$mhma~Jk z2rkxYUvjiemiMMlD;K*|AN|>HoH!Ze;Ae6rrGFy|M&E-N~b?P3;{Ct z!@|PU>guR+P)Af>N^LtbpwkUsW!Qut!u%t+J|LkJn=GxY6re`tv^@r)9ePd$zySQm z70XA6Ok&@|M&&R+s>=2nN*qDqBg0hH)KKE;3EK~p4{Yk=XU_(MY6BPDga#EZauTo2 zow1Hg7R&r7Xc9w3yEzeu5fKqIJS<-G`vL<436(W5F>$bD5=IE#9S+J{Sn=;^sNKBg zUyqPIu;E2IhFrC9W`tRgu=xqu_SQR>w6$F&1{pwXfeH~86~)_Ma5s!d?NGf@Ir~oK zLnGrWW5e2`uje++&p(KL$a=PT`XkX6UCQhrEI42TD?p{Grt(sxp#?RCY`wI!w0G~` z3b^FI=UdU^F31Z5oZg4RMDP_y5^TFb{)L2xCx;UG7HU0VYlQZP-Ek;J)cG-z!IqzJ zPhtCpn)WCnn13QO5?mNohR3R%oABm?Rmo}lf;!)cC8R&C_Y-9S8hQ*cAek}`ei9mH z=0?Jn|L)eQ+qZAm4zPl%N=BgTl{m?{G4sKK+_&W@2M0$htjdHpaI7^Y?96`*y(>Ns zALimriG1fkY}+7a@e|rP$Dv4RhrcA;UbV|;prEe+;Sy60@D4n`*-goM$>_vgx(Ljjw@Gk6GQym-i2)X6Zwv?<%JZMBA1dq3Y6v0!SG}o_n}4 zL3w(V>G$#hnmwjHKOL<&xc`-p+RUT&IiTMGn-M8w8^`1ebILsrC=V0P3>&x1DFk9z z-h7F;6L<5&%j2$lPP-rSf?R~jkBfojRgVG!!q{PbY2$8PpQ7}s9Wq+CG#A<1dw4UK z%bgFTh6$;66geTlfTbS=>WciM6*_5m#4Bz5T}zjJ;UHp{w&~Qk*(x{j`)2}mPdb<- z1LWAw$pQ9pF@<~E+;8JI|3&fsHYKwyd44$8n5NDCuG5w!xpXzLmneYy_U?VLYHefF zh)pl-JiZ?U&95fOjd};At6#qDmnwZ~R6MuiZTD_E_oaESX@jlZ!%{A1zM30Wy}aj@ zn20hA<@)3Rx2c1pqX{|;B59zrB9A3EeE4t|wafvG5TMOx7j z+~k3K4{$S=D)ZX^D?`cV?haU=8`~%Q5PYHKR%l_AV2*1|G0WSiD~f{FBFMd|WaG@h za9ngDCVd8+=LA>FnY)6xiyR1S0Y)Teo8oX$I3&n(*y?5D(0BEQzieGv*elhFOS9Fa!6dn_>%4WH9@2&c{>6Vnwgn_Il2pSI^v9hDaa&| z@EPKwX_?TnywxkFLDUR|b-NFd4*`@kgJ0toKL~fz`s$ntj3Z9~`V2g0wgDr{WOgL9 zj%fiD6YUv{vqTmqDy>kj>p<$?{QLK}Am~j{zO{S%ARB;S7y+3F?k*b@gIuq~psBuN zM(QEkjh-I$jKiahH%e>s@2jxWTQj6-W)5TW!Bx=F#QV>uVLv^+EJX6V{_DDES9jgfNc&t_ zc^}I?EF2G=!z@*s++xlTFm)wak%0g@!0?fRHyP1U)ykCuhk&Vm)ocz;9sWMaDqgrR z04W#Mw?72!46yJ4W?bBfT*&oF14|GGDE2VD)=Tl|5j@CmzIWfgeb%7C6d2%;8U#`# z%nI;-z>qDE4N0=Qb&DNbLf!nfeWE}r7c)C@F~y~qf;lN6%>EptdceIlY5L4wz;|z^ zwbn2@56@mCKm_6gnFPYf$#2thuuMOmEDG)(VM+jCbZoVn3^;r6^OhYM4Fn+wk2s7% zJ3*^m`*;F2Yr;H+3fJ@FhohJu{S7-GAD{n?XQbB7G^mRL4G7S&<{1Cl`@6sY{2{EA zuMOTyNN%k15rbSGKCo7OMKk1nV^qvWPw9V}G?l9e~0k{CJ7-Iu#4<8$_+=<57 zk(f$ZS)5dW!)nK#J;~SyXoiI9D!!sag!K^9C$&f=xd9_f!U#8z)*u7r|LXQ;m5(ufv4-Wom!2Ir7%#%G1DFg70qqnfu-H179UcBZctgOs z401U!3uPq3HUJCoTZ8v;xP$oeOK@%MprT4dZ{#%Db*-4f4TFleSH?J?<7D(GvJqG5 zFsCTW#)Da`L%(1*s1=aEL(C|FEj%(n@d8;W7vM;5iq4Da{)Y;%Y z^}3B5hC?STsvY}=@W){n1Lv>$)p`mG9~lVL#ZCa2b_0MsshPn9)HZ}hk74`A|679y zF8*pQj9|N9GSw?{4?{mhj*NgJc!Sp-JUg5$QA2$6^bxYXzoJDlOl>?pJ*Q`8RBvaf zr|bb?mVh>dgn83_K=6d6A9n#A`XE|*(7T)imIapI#)HXXVj080f7ykFW@y&xW6xrFd?rL-HY-p9yqen%%g83QYXHi>qJl&l5Mxh0Rtw|zT|2MV~Ciy*ntd-G0Kvdae` zG1tlbj?j0v;ps`mD2;3&6Q&ZbFnoe5qZK_V(*H|;YMq1VNOnUYCE>z|d zlvqC{;LGH>!9FLCI&ee1+A;obr}X` zuYr02TdS6#CxVi{^yl4_u-(r)Y9ScD5+&41bULP(+64)+;CrqB>ofDV+BF zj#O=8z#aD8_~8WcBJT|eVuJDEeNQQ!!(?8-xr7O z@ATC%dSGYdga2pk?H^+A_@6`e{bQH;-k!MP=cQryK1wzt-b#(;UzqgNZtgJ%|73;cKC8RsJo`4JT#_G_at-wqRw%Xg5lm4_^N$p0|Bu|DR23-Ub`S z%8C0XY(E{`E;pj27aMVp^|OzB`m@;oeASt_dG<=V+>?IahK~8_xBqzIq)2f>RY7(p znUP@^8J6T8g&Dq>Wck?u7&Uv=B zQF(d3x5QX@|F)2xB1!gqin`o)@p5kD63dg_Ju!plC^D9kXMb9Yt~|4p7iv}OQd0TW zbS;KC`ouXI;-LomdreJO2YU7@%}S2$srO))4u6}p<>xaM8p;$!QevN=!-6#@T_B@1 zr+K!!dXgu*&t5myhOAd#6N`h6{>VAl{_Db1hmB6AuPZ&&WAzkTxyBCPcM-hsuA|v9 zT%`IfUh&$Ia%W{oF&?^xeNs;{j%yXaar4SzXdlXdPPNxt-+gAE)L;;QNy!}%?o?C z#^1Sj;+|gOR-VhW7Jk7;uNktI-9H)nRLb3<@t;Zk-OHQzZT>XGv4b!IIBIL%Y z-{O28jfO7f%i?K5?$<@*HkrmKL{p9rI8?4Gc<@%2yzY>_)Rq%^^ho*zTb@We@zpu; z^uOcGyS7Uzu&&#&M{1tZ%K7l@CfkCV;7CV`y@Pv+KGQzcI;R{$nEdhmnZ$3FgsqxLz9JhJ-blhh z!NHMosJ5qJ`l@CoF6YnF&&J66dviPM!8QKT5ocy*CIqj@s!|VaZ>F<<^ViYl^t4`4 z-=%TM=%jC3j+4DTF^}t#9|y>cf;t;7|zL1iXqza<9Z06Gb@L3h7Tq^~8t?^9o97Lt z^`?BJ&dyFPZaT=DJkbp>(Z-F$c?7}&!3+AR5w(2OV29<^dgF>#dP;7VCr{LAYAoWI z#D?n3>g4yuz7FY=wux9|UhXz>J9FEna2>z~G+@G^WMYPHw(g9HVDyyy8bDfQbMwC3 zPDL3RigUMq8v?Nr;|w5O2~3Q;3G8<@O(OvP!@$tcLgDzC%U1$fobFU@3Hmus6BK)0 z@DE%6YlF!n>g&#%UFg$#KONm!cW8NOiI~+P#A0g6)$``&Cm`FGq?=s8onjXgW0IAv z(hXljDSEA$*?X!kvcdFYW#yFlqkMpUK2SJEmsdSpUB6;r7npcT&I=Vc18lgITr~(V?JAf*0vA6cZ=_pFzpPX0y%TrW+9Jz_@n4 zSJ(BA-$deQy~XN1`uGQzU7}_hI|77n_p%toOCFqcDwQDauMi_1G6AA}{&!~xwAOG< zeK%h~?Ab1>%(Y49>m^QpxCJ4>!K#VO1H_E5H=ZtVv;&iR4W-i`cTpV@^aq!Np*(L* z-Eag!8y!(bKj#dzuWcFe`luq(%0M$>iq@?}G?ubA&7`~}2w zN=!}J(WQFMe5Tjp4)^TX{S~tmczi`*Z0yXBlb~`jM>c$ju5gvY)JztXx_N8zZW4w` ze!|5;JpW>w&ibuDhLVyJHBiq)^h7F(&3vLoih2rfkDXoCWYlM*0Zh3ypv1X z7)JjCqvFd@AV9t`W0F=8&=i3C7nXh7n+**`t?Xys3i)}O-1?`3_QqSbaKt~-3GV>b z<8z-kFS#`-51@%zG=zG8zkjo~zUPcIfZ=B>7v!FEFfsTui!G!i9ODJ{3Ir&{qEntKe_BamK0$QSa`koIm-(v-<==~JKGKN zEw5o`*kOVnJG@{F(hI~r@s7whF0Tk_Zn4u0ml_3-+Lz-At(-$S=E$*Qgy)_a8lRNM zp7o+pzu1$}*kv$KczzHrc9X`@xu8Wse`CFXjF1w&GV zX0i>>!$_WVZC6(j|BN0C;jqaN+yu189UygdJ3yui?Rgg-7A8$RvS5xF64ud)npCV` z1;Ebr`giL;4|FN_EWQaQb)mYpmS}&8=j&vF1|{r9mo9CE#t1(b#T{5Sa^RNarc=mR z5VC6bM{kH|zW{-sunB;_DIY7z&B=Kb6hzF`h|Pb!2x`_3ZWP!HZPN}FAlVQ%RfwtE z+1~yo#$}KJrZSu=Auvg5{(Tb?LaQP+Lwp+=>#jI1X!zi^?VO!Yg|7VQ>9KQh;eZN@ zJ3}{`os^v1Rlt#A_KK&(h~Ao8+C7=;?g8c5UlpggH-4H)FqZm0F3*nfOTUp<5$p6U z=uwH$+>%{)o<&IdJ3_Nb_TE@0SS#F|U*HMQ!H@lTS9miLEtT%B8PkrB`Or#1qJMA9 zaB^_SaW)yQg6`14wtHr#L%9fNQibLT^k!$Wh^Q#~K|1)>AewH6p=1E@k^-3--YL$e z1r9d0&UF=KVj_L6YY3SKBEn{&=3vs>bt1hGQr{8O zc^qXQm?2~1{_Dj~COGzQs6rTWJNO~na1)10+;Pa5GXquKCEw_|wJPuc3utjA7UJ8| zH!xXIV3I6ikYj^x@$;<{a9*rGAlnFWyD+)@Tr18WZDkLaznv}Im9p>fNL2?HCr?qU zc0&37SkjqSS*3x{q#$Y*xQ6zccn zNo8GK-QxR*2zm$(g?cV{_69MUYUng`dv&IYFaW~=Kqq|JL9fW}E+hj(-pJ}&-jY>r zx?O}eFDO`WrJOX>xox?M1&w8JWF%z;#KoHn*UQ|$Ewc3^?$Fqkd>s$+^UDsf*^h^B z;H%)ct#NTf&q+L_#m?>sA72PKGWzlSwD-*RKRb>cxU;js;(!--=!_p5UtQ$I5hsiM z>d6!B27G9)<>2SmlnK}K`U=e_QU{DIlbAvi(%@hl2sJb7&S zG(AQ$@tx1{$Pd5;0z3WwZH>B)2ZDgcBTAaCA+CiG%}Bhwyu_mqfB@=T4q+dk2ihVA zh$AB-32hCDJtsGJ?t0;8V#-VNLGlxs0pS7<;^s3R?~X0$ zNlvZgEU#TZxL|+{3s+Pm$S@fB3AqhG9BggR*9QFrncb+y^6=tUS63f_IE@&j(-C?b z!cguyNyQ}?G1QR|80t{il3#Nms%`af+P6>C(RHJ=DuaTNcZ}^nZLH)LO zyj^K+ZHK1@nxAhD}(^Pzh#?`EUpEKmo|{H#0)AcP5Y{qsYd?v+iPRH>k>c z>9chvAC+8<5_lg@9@C&NyDPmv{HP4QgQB8h+#Ckt{*1>;^!I1*n0!&h=1 zEG(RKh{IGSJmKdtMt`%=S{j{>bPNnX*o$}yNY6LAC_JHUa^>s$m6W!4$L0FiHiM^| z{{81|{+-r@#7)$$ZH(=cx8P{C!^RgI`BAsxHRZVGE5H_M;)Xo7&geDbmL)Vpjba{n+E@F$$OeaQq*wU1>O$Y2PPXA<{AtsVI@VA?409&ljZzJ=FFmUUGGM*nPAS)wE_YK94n7)78COd z=iDY)?r}m(qvzWWmtfwHE2YK6#y96tGIN6Kl3o;A$tK${ z=iA$1#O88pRPSkPy`o4^K+;*C>Nw}x29MO;e7|-CZQ|P>wqLT>p*r^2I#YY^iC)Eo zz&`oSj+Vm%V~=&lH!4k*>OA138g{njt}5hT{h`c>%X3Frify8Uq0%b0J?gBF_L*65 z$+O=IoS`3l3bEjkLYdm zhbyZR$0|DQu6p?e%I*`V$eg*t-`KI2!sD=@8KT5`lNsnPdgGTBR*hxy9c8~OX5K2A z+{BXuopnvC8;)5*vkn_9(HwV>p+OmGQrfJu( zgX_-e&X{H$d&HmU38Pmz28e5x)s^gFcMw}@|*SLv+T}DYrhU3n_TXv?Q z?Fp4VoMNT{Q(BoI)izw+89XvyKDNp`e`QXbHT%ih?dEU4Ia-TrZwP4a6JRhm@e5lN zr1mFx@?BQn$v&x?viUraUy+sAhK=?l?s+_U#6 z;Jhs-+F@o*typiIR4DV~{JGBh#sih>gGHY59i;6aS}P_tL!oGS1SdQ^IH|4aW&h#5 z>#5rEXJ;+csLsJX@3U;xJ)D1-wP-F09hH{Jw4$q`>V%-`AN}6V zaZiMH=7+4&JhC{=bw5n8Z)b~RT9uP+&a~?NB;B{Mu3F|&-r2wW-d(*y%5PKZfc|LJ zcsC=v!AD_vA8<-%UEdZgza*i~B6G)2(v~);7WxGw&0sq-C}T&3GzC z$_)7ojYvyr(lWQlL^!Y}{w zYv1t3?Sfs-xf{|O28&M1XNPz64F+adSTne;f8Q5FPkha*^v8&!bxo<2$nzY7t{Fk^ zvC>s{EIr;XqZ*t4#-GIySs0g*9L$J2sL>OjLy`Xwej}`D#Ym0+Y{~*{+MYlC%OO35 z3lnVYrOPv~2i5!SKg#dztJCFly60#z&xgTUf6BnJu}|SK8!xsg=#9!5g*~b|GhZ;t zI?1ijSGvGXY2%NL2uk2mH61phO$|KZpvPzI>561$u*W7u?2l}zoE(qW%-UEQU9`|H z$#`L<%r8FMKcY|*`2L3)Hg!AOoL|mGWcK+aoz$t|kI!^rR~4L^G^DZ*)n@LHJghNn zZxFBTubZCuT6(nWYwVq&50!R1om*Jh)Gw=Tx3Cf9O;~Pdxav&q**yFA7IuDZh4(nh zynL_UZfNkh`dM^vEOvXjR+Q*4EpMan`Zg=`0tt}$~HpZvJmG?n$x?N`@mnzG+~I8BZD#P9h( zO@7w?IM!lRSNwCC((v5ecW?4G-0|<}?XfoaHlI7(Zdke@v!UCxGi|;$B&27s#7{$g z!S?Cbs+|RoGFz9~T5e8U=Haq}rqE_#AR2f@Epw5XaK6rWOEh!ULTh-Gjq+WK=SLk5rG8ym z!VFlqnDn-yE}yTuZ3QEtra1jvQN{dVVlOX)x8>M2i~VYXzfT&)G_aXhN9r);kDO36 ziU{8!Y*Jn_d{(MiQvZt@BY%bD5NDg?D=uxJdK(w{LYubscP7l3T@KcvA!kPNM)q2^ z{WLmA$-Ep?EYup?t~Ao>kgD^Y=taJW_*ykrx?)|T#PvB5^?T}Z; z<+r2eMM_VkS3~xio0i?Rk=^$LK8Dduw1(#&i1SUGKWi0?W*La7bk*!G-r!eU*d`%4c<~)@sY{l>)UirM+SJ#+WIWj z{1zqShC-f~t*_F5%Vj3}73#SRJ2d6cl#KJ3&$Cq4+FTEK*V}b$^@-WMv-#4HVk7XY zL$(-rAi7^h#HW!jU)}+*K>wbIkrP3u^uDQq`u^LrPXL#o3U8Bb18xZkWoGg0`SUL)YE(!NBdm?` zUc+bL2YXz6P$MNZm%`felKXgL#RKUZaVsC&XU#0<72?^qX!ODpgKOx?*kRLBms6i@ zY;5EJh!`8(M&cmAh2pv|LJCfTD}@z$kk&ykJmTtx2#nlKb)VyUUp;_}80IIMtRi=z z#U?x;eD|zCH@KiXCGk`k-lO3Dgv=yIKP05}Y1W0*Ah(rN(|wnQTqsP-<F|CbDh%Mw9_$IgGH=5E>nE@wPiul1;AN#k1l~k4B~ndN z)DA5@ z)18ox0i;Vqb3+A_SQUZe?V^UbarI(W&IZ~Bi3ka%MRsr(iMhp7AsJ9C08St`p}`!= zyoOmkR7Ep3RlHI$VWm|Mbt0cP1XvZzsbSL>5kQ790S>g z)iVRcC4Y)Xh1(o#eQLcEoMW&WS6Dtnw5z6_U26InOf-hD$pH^yF~{Tu7s`h$i$hSu z9Yv!psb3@bXauJFZ4}CN3{V8wg>4FstI~n`t!eNZ@_{?ZG~WF*3A+UoZx5RF{wtzm2>aYpPp)B8N<>8n6?gMIbbYT zUse2{AWPr|$o6fkE6g>Zh#>9?zzA8_Spc2M3@09`S`K9bD+$zg$nATguc0xVD6CK# zq4ih^k0z zEArpufWF{hnE*gGj!!3I>q}<@=yv8^8s#+<+ne$JcSWAi`na2PK< ztX~ju>99{M^z?54W9+QI`d%vnhJ zHGuOV$+`eS?kYIni(e&hQ|wg!#u9!g4{f8@Q+LU`#bq3-TMFC$v=Owf`~zx$Z?4eq1p3*dgD_lJ5 zRcKK!-}%K_NsyJ+ekFRgTnZnq>r^h+rOLaLV~ugyJAR(buN{-G*PD$3-eWS%wn7ky z3~U-uU*rX!NJ_&>jRHIYDbKLC2@Wen6CLr53R!DB?ZX{fQYGrk3#4>$$~A)cfAiL@?9e+RyahPk^XaVuQjMZ_k85RTXWJp}6sNu|FhPo0$$gkrz70(g zwfB+M3ujv(;iz_FlyBJuCY5MRo^h5a5Hais5eUiHM5*M+MEGE^Fp@>ynQN|`4w zTu>$2Z^9se{RGuW&>w<;5f2j-l^NWhyP7Z*~ zajFNI4m@nmMXP2!-$5&;XzDzUcVQKkBI69KYWYv@V`F=R3~})Ai-f%neCy%d zN=L8IWaI%LmuR%eKbVDg!PQP;Ltxtc6X?$`+u0@pG?e}N_w|9C+W)wMJ!^lj-rxyj zntFbh4l7*yZZX|7`xiy31lC8}(qiO-XZbug!>0P~0Uezi>8R`?EO<4qvBlsxXCVay zd_B7LV*y4lp>+NgYzhmS2{56jfU1w!HOD%S#5vU>*b5}x9+1os&91dmIgInInihSo z(`3s`&i$gxTYoEayp*p$ElNVmN#+n}!&sv&|Xrpe8|4h1AB9TpEsVyRjR4gTS?agZqt}a@wwpyvG zq7=21prTZ1E?sQ3heQa82wCqo-!o^Pb91v;5{)6T&9$47tV=z_0*xA9?i(njkffqCIsSZX3rY@KoXlLpCMma-3`5eqKH-#94|OJ|QS9q*d6I@UhH~ z#opX(YUHel5Z9@bBErWuuh1+sEO=7Hq$#e=B($0mHYUi`UHWU=9D>Ib6WrES`eVT} zr%nzE4xbq2+SVOhvkJJ-(Q4Yn@JQG2iNWJSrZS01p`lYlB3-5UR(5VJ7@3vX>Z9n% z9S*E&;8W!q=S#0~Hi+SLYrxs2A!mpCoZZ!&y=rlKikv>toWoz?96gsa{2=Ef1?S8g zobPyWMq@nf0q$OTi}TYA&b7Im-(vndmUH*PW)kgQo50=pZRhE1jW33!=3&FdoiYg`yyi}`Xe~ltlXc$vpu(KF@fv#1?sHJa5 z@CVyLRg6md=5GC_m^Eu-zzZ$R*9i<`S=sFchZqGIHRNG!w=Q{oxRQnxd@RNunwc65 zlnSV(DlmoZP;$wS`Im(qN=u>SkiQ+Zwp*7IN=^%x+|YcEjwY!EPF`xNQc{`(_d+7~ z(<{#|>~IM@(Za98d@0FMkiOVem9-1n%n>!EIt7wnYf-<#*9?!Z{5#0{r;6UwL#Du! zJ&Jd2<*7@PpO?~hX{-yh`Z(gx4D+&70Y0>jUOZwI{0XUY9NlL)*|$Xo>=oF^fUy;_ z8xqs0Faq5ZkZ&WGBfmgyK^{P!Kwd_sAx)eWD1hmB??9yEp|w13&W5)F|QvjkU4m|G%&Q7ajwQZ81uYEs%QDfnB; z#me^x|B|*d>o&^lAP==rpcJemk2btkHI<^6Y=M%p(mvYontxhZ>DgAoePQRMMmtk;+|e!%zS&R))HBJuJdI&MEfaFUly(eL2V_g9mbsS`wX7|uWnJ70 zemcOk11Mepv|3ix0JSB5R8~*+sOZ@X`rtovQ@*UpZ#LKTpjzgtsQLU0kT%?1i^`W% zH!nNTvC)3IfCWf;o=PxHrE{UMFJ@h{_S`_5L%r;#HTca2jSylUrfTr~fVQ*pG@sBz z$YR>n%CFdnrec#UF7upvdS1+htVCAIq6RMNqO-N9QqYH<%Dug&7Q6hy?Z-;*DX1=% zyRKG#j!WCsvToMuQ{(k~heh?n&ZGv;I5vX)RzkWUTOzw6J(0ta5y-hnEpjz-Gjcyt$60$OG6s1Jd5^OW&gj-5 z=uSl%IqRNAqPt#W?yl#FlqS;!p&pjQ72U22c(^Opuj@K=??fI!o<=4iGdUZmkkyfm zksXk|k%N$9k&&D*M09?}MiFOI7p?1{QkGDGc92ZLf%2o22}eQ0=at9`Pbgc(*g);`N~-AVntbzj8#v}b9o^JaDdo|9*2 zN;rMPlyZ4>UFpgucFNVFIvc)T08?6(LIl~uvp3KgJ#vZwlX|IDR7s+lfXy!m-&lI*GeYe;6ylH96*RPFGl-bmY zyQvCR#wW1?!qiTm0~vRJ>{;Axdf42Cd%5d~0)kH)eA|oJE%PB@Z{Cdg0J|V@-lRFE(|jEI&qPKeS0Ohb_aKiU|3=xgNP2c@%jTnT*Wl zY;BLMfpkN5LiRxhB14hWk&BQYBiAE$Bab4_B9oEXoX>cC!p1@h7?)U^XE{FlH{`EA zF1XTqlCh&M(96{W^L&w)U!(k3(6Fn~!PS7V)4^3dhvem#u7C5lIzmDM7wnLZB9opsld6dNxd)tvvdGbfr#8_j2 zPutm?(zhJO{8z~SHGM#_uo%#bd=)+>Zyz5Rsr$fsYd?D*>I)-yuq%A@e*FeQ5OaS!U}AVsqm@&@|&c*7qns`W)w7i#*{j0GQyfw+5P_r>nr8w+t` z;j(ty$I`t}0I(}!O?!6lSAdPXl!7@HgY^4iWA|*@&BNfD_Nijcdk?aRpv}h8om?bm zZ*1(|qX*%eyX_!pxFYz50!x1A-oxFB*u1?5S@a#;O%KS0fnvt)-3JAgifMfTa7nQ> z-N46Upg4_GZnQC6kQ-c3V|zE+6!HgqP7^n`#*G`exi#T#QxmtQP#y@8Vm4H`8D4JO zl!rnpjn!__jdBBf6R@|!R2sXvHFJ3x(&BDAH`<7$0=Y`*n3}qQ8`5^;?(C)z-<8F$ z%{^#y2T2s}f72#PH`;`i)cOkGn%oM{5#-g-{+)euUmu7InyC4OtDrQFfdVi+b~HWS z&Y|XPdWySES1Fe;xL)?LHuf*gpi7t=GVO9$K~1SqDs+xc0;ge9RgCRL54Cry zHO+K@yG`HIx^Oj{Xk92gWM+z!+~3hQ+@1Xa#CL@F*%i2lxqd1XZ5B^FulC1(=iEOs zl$9E;(8|<=4%?!c6U#}r2z}Yb?-)*S^?3Ow;=V7m2L#83~ije`Y z&gkET&D6oC3!BAb#B{nyvjgDnhyFv6;mBFYCCE>a8xCm z_CpRuh9hSommojod?kpp8>Z6@)9Hrkbi;JIVLII~oo<*;H%zD7Moecf@;LH5G6k8( z`Kl9AL^elyAp0SQBEylhkV}xCA~z!UB99}_bHV`pX@`&{l~=$p*V>d+{%PY|=gsU- zn_fxfOPaqrMX2EeOFCfed|+9#j1131ql5uC%P|&m1Y;KqIhK*(d029EgB-!wxj~L) zWLWw&+$=dti+GS@87_X~LZMWvae{+SXE0@V5ACG%SsohY9=MN?V=TDJ=>G9`!O~~O zlA{~A%IN;_cEQqT#*!liR~gPkKkp??|8en4+TT1by^@9(&R>8Y<{6r& zT4e3aekIG3ioYDn69=ZWLqWDzps9g-byf z*i(9o>wS{_D`tmknn1g8oB8y4UJz3^Ivs864brRVP*2r!x3Xe@Z8{ym3uLsrl%(WV zQ(wA(m(H}++>qIze(D$x@DiG$=ul=WT*ilLiRG?qkQ-iUyN2p=0-U|sZW{a)))ISr z^MC=UNqRB`8a|$tIoY>G2JC(6bN@cLf22=$bYnVwMxlEWQiFUCxdyovc@X(K@+vYN zNjdvgLDomML3T%aBS#@8AvMVNkZX`zkq0^Zv)NJj^zVlBLXJRAK)#J!j{E|-1$h8@ z0(lvkhBR>wsEn+OY=!KG^g@n6PC&kmT#oz#xdnLuc>;MEnZ_xUe=Vt`qmswPFFa~_ zT>O7Jyfpc%j|uhRm<(p@V(Nf-{>aO(QLX~*1+E5m*f3*9;cA{k^78)+ujXl6dudwt z)8=U{U*7T;o+F*86Z=glPdZU8D9=hTr5*UN1I@{mbIY zJAvgFRONIXaJO&ap?$#@PaOpFU-7UVR@&AJD{d$4rB_Ya`<}*wCr{ver$*T7`#Tg4 zoC*`p0b~tmY1>>tU*_l_dDVomZm7KekcAcM+qZ`HaDp+XSPuD{N8ZME&gWmCep;}OG zUNyeVX8MFi!n)i_+8f=Y(A|r>vz;?iy(?D@%FS_R9&lA-U|DV|OU*&hdmqipIy#7D zRQRS?DGmi!<;q>RBRBTac01MN1UP+{<-0Aq6FynKsBa8sPZfNIH$t}IWZxDUu=}Il z;*WZZKk6<1n2!JNT+aV0G95`d2US7VN47zBM|vYiAtxa<$oDt{PjL>ejO>g2FLEE} zP|7*13bH=34YE7Z8#xL&38_K8hg^f)iadz?9eEX*j-;G#R6*89wn27BdLu_6Cm}V+ z_mFFlTagEmzay_A(~*>Ocoj~YzNJxhEX5X9SXn94b%4jG&o?f17o_r+cq=WW|3BXB z$Tv4a`PpdsxxWW+4Qy+1g*b`qu1G#Q^wG)J`H*?q6b>(Iw{_mkUe@^)PQSe6FGM{* ztEL^SvgD6E3!vg2BPYoLKsgMoBNxm5 zg_i_n&0nTGBOp`um$$4IT)?aZ<3Xlz$_twLGYE2gE?-y)EjTj(s{QYL^QX1V1Zq2< z0LZt&*;+c-C<>!E9+Mu0+$CM!^qcNa1xx@iH!NtvzEWm`@=471fwQyXbthrtsX_{X z0wdKr^9d1np`aH;UifyKN-qt%N@s9kx>`-m$18A0ZuMEZ z`&qxwmU1av-B!wLL7l(cbtG~#Ep0~vzcSXJAWgo@qQB#_610Pdg@2Cx7K#4MWsw1U z@O&N~{2}sdB*qUujP6s&MC1d`u}b8N$VN!iSwrSf?gkZTszC(Z1lHTVM-Z!ZA z64e5)F@C2tm55N1yOuwL!S6VeXLa+mWe`ho4eahSqp37j0QnN#QFa}y6)?l(*)=?E z8KgE+l6zgk`&{DmA6FJz{snbl;W^U5I4|#Jn ze{*Vwa-xI5MpTbZ?wvd5Z#rcgQ9HJWkJUz0=I7zw)}vF~{8OOI5N|O~g7IX0JX;5i2dfUPj)DPBL+0MF=J!|pZ*U+Fi^CQ+ zjRHEDhvB5`QVY4q;$Z}dfw0q17tanh=7K7L>YUgjDj51Jx4}EJ7+Rdox|HzZYkbXU z@Ua*tfi6-+LkXgt1f@hM^OESr(UKj?47Md!4gnoZ!mvbkL7AhXL4(CWP(3)&QyMsWl zsU%;h>SQYL$o7KqX7Y7(!2j(@ONK+^c@UI284l)_M4*&+oRXouVCYS5gExPru)?%1 z6_hi%p!yD_d+82+9|{WZvlyc|8 z`&OUH>U@Vqqr{$IGQQ_N^AL|S^90hef3TnyI)!Z}fX@^W*&K=KOzDU2p~!ILEaVd8 zr^t<*ktiRD@{uSXiSm&sABpmjC?ARP(=mKHhEK=v>2mm9p3n5-$n(e)WFF@XC!~mM zj`TqGLk>lTBWEF(AU{QJMD9f%N1jKfAoDn9Iw3`5bEF5dA95%%961ZQ1oO!h5b}tIJiypRLmvM)F^^=(1B_iVJfUo)q4rhXwfTLpfD&T0P{g7=elz5*y*1T^IBm#~cIJW7)!ToS1 z5_3bg`}yfipq=Uzu@Dt}q0DgF5)+0p$I{)*56XOWA3wPX$}9^<$%i!Mz(GrUcki(r z+T?&qLGA6{r-bZ(WXXZ@W@U;V!DM!8s&a#faL|zhoW%tBf-R@9TN5`|I6g@_j|mn+ z@nj|^!O*@r`hx1*6wX9y%4C^c;Rfd^N%Bx`mu3p-e6h!<6-*14eUdwU&O!?67!yIh9!;6-8n$vqAdNvszVr zq0?r1kh^INWS-HMJ91x|`CCTr`IW9L;U`v`%U$0pKb56bn=8o)aB8~dy9=w$n#nvY zdI)kX68)LWA_MmS66OQ+L5@aZ{QpLw`+Z~#avL%Z`3L7bbkE1|`LY|s=O^%Z^Y3vk z;1Cu#bIU^HBAKq-e~~+qAbpUdk&}^8$oG*k$Zg0t#$aISu0*CWmyHG45+9U=IDjxG9@wB>+2)IM)@C|}|l1$n9E zp?ryJ738JX$K-33;`&+ZV@6XcgvpnpHPx0{AI){Rk>X{uENVmu`~-fN~gEm*W4SfPRPl6P5z!9f1zBMj2!NY7=NLaQ+fq zV;@7;f+yH>zKU71=8LalV%R}La0(L~oFtvl1gc>SmHa9Q#!Yb65(|bUOt7wDsW=LB z6F-Z|K`?%~CT1-xzQ))KiWs_vowfuUY}drZtc4A>a(-)gep23&9keK`LKZ!c>Cx{s z%kMK+fOqjAWzYhZl<8*jK}-(dW(OR>=4A+D`klO^T7K`Dv#>5#aQPNAQs;3$-Math z>`E+gsJd046rQTca)i2D{4uFj+4BZ{lewYp22(BC2GFt026d-Ad|7&x80t>B?w&g@ zqtR)5YI}Aj6I9+y$8NUdB{ep>c)bZPtg++mi}m>SWV#KGgxbJ!%(oXVx0Nzg@CD*> z*OAE0x3nDz{(l)cndNb^&8iAi+;4d%=gK3=NZN=O%EOJrB1CvrG40y!6{MXp9}M(#)Ike86DNF(RUN=O%EOJrB1CvrG40y!6{ zMXp9}M(*c?zVC5kqJKJE5m#`GQC#|!J`-GH;6Ijtv5SHKSW;$GfgT5WJ(K_S@{IE; zu3n$U?+NZ3yzE-0cPqa&@YcbWAK44G&*ne#;D6NlALaRvKc`P{KmYMOt=pe1|APIX z@Eqw{(t$qZP1j;?FO_zP#Pq6C=~`{eiLUhwk8@fj4;>g~eM-eWSGiaP%3GsVnceFY)sGGrKNm~lgz?{|ALz};ozeauz3O*=RVk@#*DKF1>~Kj6faS%1!~!QR9f94S ztcalh<=YEao7LdIpw3k8Is~~HmbOE1loR0mEHmG2(SNYbf8LCTtv!uQK&EoCZ;K4r zSLbs7FEEcU*jaS&`2y4Vq6@lTM-D-bL(b&*!NC8P_o zC9*5h6FD3ift-ufB3C0fBljb9oaI1gvX2xh()N-m*_s!um^@;JpQovTcCBGK=pY9$ zCEo-Gxjba!98XiqfsF;9!F3RRoR2CF$#RwabQB<1i7AG} zHWS*3VsqL_oPNH8Ld=GU^>aN$ar!SWiuXOCwEKT(QfI*(mmVn~!$QSP+g;mTp7f&l zfQ7pMaOa-RQ~cbd%*zU8p-p$B-gvim6Vd##v6baZBYYv+YR^`C|EspAQM$7uN8bX& zj-f54=PP$BXH1id0aMzZk4-X53V1EJi0;z1A^ zc|{@S9G)XiY9)#*wn1pmdJ93w{8z9_)c+7Iih;Mmv#q;lMnzFC30#HH9bZB*>;4sK zFWz0aM-*KQbN;&iRb%n3lTBT|+-MKID#XrWv72Q~cJaIWjY2fm+x(*oMsG?tH)?+L zGqKKY{r!t=rReq|ovaki2jRjqeSc5c{QXMkS&yx+>ma5{85p6b9eqh$v%Nv>*_Z07 zSq|{kE=iKH7xjOIi7n2~cM{F4@b|Amy#-wm69EmVha7uGs#3hq3f1tMZ=J|n4aM#6 zt3-4A36KVpN}sQ5+T@py#eLtph;QDlUw3MAxKPgw1zhOV=1S2n@4m^9_mvoyn0ca? z*o^8GdFRfGBQr$tP@ch*cV4Ws@@~qyMq;~N30I-tZcKZNOY-zIFRv|FHFiBk+pV?T zTH90m+_IQLgLD(cw)Rbx`*(XG%i@=jSProT;LjQ8qq53YuZVjtbzD-+aWo-|fn zzRpq9^PX5Crg>b_w-f7Ko8jW~r)#~WS+)CRbXSVGe9*djrAka?-39N0MX}CrWAfUG zBhNwS6p#pg(1v%F;ypHg6~*t4oxZudv*;4iR4JxH|0{~~z84|#vRxY!PYw}h{RGh$ z?ox^m*nn0P=PYq+yzXpmwRng1zGAoQ%~|Bn?t9k`&T^l>8zRp=2*X}BsDrnIw4 z%wWB)=yGDT$f9cFpyxh4ZstLVx_Bpaf{9sdU^}1%^v<*`{02a$3qd{ZDH07dIcWS} z5dBP;-wp8Frd5mStPlsbHFW((C%ObRg$~zIOowC}q=ktiAGC2vm|Ms9{(mrDo84)5 zcDG|4r_{O9voOwvxMHs}8D|1Tv1fe7g|QkzFWT;g?QYnfjfXrN$|-C(&W5;b^^$$$ zA!#u~^R_I!JgwNWa*qI0+M&7C`6KMocCvrwBY)(2z@hn0Jg4HI_2h&c2V{1h=Yp{l z_vMEZ>huT_XXl@q<6XrNWEL65$FJwc` z@-!}IbNI}Zc3iGt7nJ{rW5=!H$5IJO?hzLx(eAZ=NPY&2-D}*Rds6L5ZbBvf2y`q^V3Z2>_sWu&XMa7cFz3tdb?UfxPLES05vu`eh=lUv$`Q8i#`Pb+Nn=7mg*#S?x&lL5HuB^t*~%_cMoD$y`zpBOUqPDFw+rA5DgBPr3Q7 z1EE<8O0!hN>^C1uIflM|AxcFui%2cJ3Rj;Rtt6&Nxnt~!Av%j@MXN}XU)HLA#Ao+8 z^7$Wq$+RtGeAa6u{&RL+8p!S*9XZq#OqS+GkN$HY8F+7u12Nr#|Cm8;g#418b$5q9 zRAf@p>2c)s%MT`Ma}T3f1|3O0Ou4e=)MD~Qa&qEIM`DW1i)>0LRG>Lwb{CS@=iV$b zG1uRN7-nBfNqGBp@@g|mUY)72`<0l!e(;upm_DEjl_cYHI-MLc>GRTe_ah|3tTfRZ zb&0Xptz`#Ul$75g%C%I8vXPL%7r%ulvl^1rcYf}j>O;r}aT&u18GrF23s(^RH-}lc z6G@Y9E+GpOZk!)O0t_qDxBQ|eiMknN(<+eq>HBqr?D+T+PjzU*d?w{cQt{4?e=vf~ zzT4Y}e%WJ$UPZ1COX)#ICH4Thkkp=iGklqxJ~N9R8US(zrJjDKBt~|F;nIJIg# z=}m_davpBkw|Yh9P19Q-OD@iw{|mu9`+uJJnpDzU=^m4VnTlJv8H>EB7a`VT_ji2yP%`*Xe~_~$=084Fkz2oi0y5;_W+KU$^!ltnBjC!+Dia;7B=@BI zOUUAcq=ZET#58u%#N4TUm)4MJCpV9pN8lgyyAOIXFIJ7b_eH5pBw?Z6}t% z@H>FM*Oh(uvHsQwain|e?lm=PLG0dIW4o`m`)Ye$&p&_9>z{l3q2 z8h3d2u0m3OyS%LznRqTS0p^Kre0F;w%pMI_lC0$k*Uv{v^9axxo>?UIxggCZ76SP^ z6Co>ZrIEggi#+eoA^z#pNW$^HFoUEbS(EM$Ayc!x*=&N6Bw()#!pwu zvit2M=!mv$C0DgX5;kwy30-o62Bs50*=5~s+D%q$X?g)?oVM+N*@v%xe)lwpu8kvC z-$v1&)`93>@57V=h_Ze+^Bfn2DF!z2WXv3Ta6O3ruxri@5dC2bxjGj`zuN_(*WZR- z8AMtCn|Ygy!c+sBdopj?tsr;3Qsxi1KZ^uryd_D(v=aQ5OuQdRW<2ncME}BGSSm1x z;{4O3`@?%&yuGz2nQ->n)#XGIW|K=wlB2zP?d(K0$Dk%Rd1oxk;v^J#FZHg*E~?mm z*mhrS_to~iK0nXvVPYN^2A#>S^)T{{@~%tF+usFe|9;@SHnKL+|Khh2X6}_FY1ev^ z^wHtdX9CEc?MME)FrF+sMMyvD_Y+NCAQHn5Kf^q|irj!`2}gU9pT8ly(g%{IH+o<+ zLLoIn-vO3bR~Heoc0bv*ix4jwm>&y@AScB7!kC}PE|_)hMaSevfrMTkk)II8iiIgy z8nEOvtX@z*p51pxV3M5|N=_?1fP8YDOugHideBho8R{$<4z|g&V!U%hdKEh2Bp!= zH_y1D+c00TdmWPsb@0oYlWs6mv+==vKR=$=*3EpBElc%ZbNT+3*WS}B zZkg}GP~O}+#KAYq@6#Mg%~`Abe0>W_=~E(M>GB7d6&iYZ#q0!FQf?jM=#%IBxt{93 zgny4xFm~#x6~rs$hhFDGNU!)6{nKZXzB|TAc{uqP2K`9)3{e)AcuF?8c36doqc!6}(NOKNz9%W_KjG+CYXe)x4|_RLv7^Wrx6FRR;h&djwV;{W9F@ z4pm`=?g)9MEvyi>m1fxre8V1*6^mg5TVP@pF}HDgz0j9Mai)W4_BO))lBTqqSa-$M zG$slW&%7o_4B9JR>?XEKv5vS@=gw9kVN=H?_gl4F>`OYiI8hWM&R_krr+7RHcBR}J zCa#;YonD)G!+0rXA8hp4@`)vv9k5-G3!j;~LHuH0od&-oie4FA#ZkBGP1lK{e~OEk z+);$`_}>;i4DO=qgPs=Qu-uitTsU)gsCeN`*s+}`uHCE z*g`SnjA#)Sy}r4^gkhg!y?af?JHteAc%ryHLy?$xcY?Sls%}~@anUCh>B*PH9U9Tg zR0p>8wW9w~Nu>tafMcR~X`CodNfcMbsKw+^-H@}Qcs#%&*f?v@gVth4`ier#9inek zFQO^GVukqkszsvsb*xL{-x5X7JDtUjJKaRrdq2z*n`YD#MZKr!^`O2eZn(A|ve|XS zqn_tQsI1?&^V)`OhZlQ`Vw*#^PivCk!p~nlKM^*XZ!o%xVX!ALYh(8OMrSj8L_Zn^ z;!Rd1WOTN#px~XXDobFmhx(YYZRB!ikXUX0~G!9PN=E@yKTTNN|Xly-{ zt%s5tA#Y{29?I53*?K737|J$=vW=l^W2h$^d9k%pRcmvnE}(98=~TmFtJ+#Q!59(z zst2qFGwrW>0hjXx)euwYm`?EGCuoEEy;6Gwm(@O8>kmNogMjk2|NN=KX;VmHEuRL4n-a>9<1Pd}a9G3Tia$9_g?4GlxojNqCn8b8@EC0-S>GT61pP^?dy*TFa0uDY|QS`!*r9L0fYj66f zz0G5#B8|28&Kl1$_;13|)k)LrE&h()*_yjI!bj+@4fXa`4>Si$(Sz*=X1$rP>Cl~J ziNCG85ovGncN(bwZ_e4B7sn|Es=dr(q{w6Jy|PB1yK^t^M$Y{^2@&=d{|a8ld6}l~ zoh%*>UUY%|9=hDY;#1Mf{NK$gFSVyRNJ=Zn(UbbC2RT~Y)jU-LOv3^@z>@$9bqyIlM?IGUSc3+NqS8 z=3HGEcz^Nz3!<@h&xEmX9)o9wz4gIo_dV0$JReWEo{S8A`e-;BZT6Mcl|@5qgW=uU zQgn5JgV65u%oE+;&27(8uhaA5WH|Ka%ag5Dcs80n=%I(cl*CkGcA<086r$nY2#`~V zc`Npbb++tsRpUu%dCjKR5yfD3$b~0Vo=G|=?MD7VX`<`w`)#CZ=yG9&IOAyD7sR{< zY4cnf@7g7afD$jAo|c?{dYYKAub*_2a&N4xE1oeZ^Jwde;=KXK)6;gj%O~;_K2)tE zJ5Q~f3$}H^wl3J#1=~i!wo$Nc6l@y>|9>6@*CNHvLDVEg zCm(7O8mY2OG^mGUjg1&OW!bm8zI%WAPzUA@5%#Jq6AkLYhGqM|yJ`Ss+LZKNs67jS z7#fw;)Wd_Vc}7Z&diXM&o+?c})Fjr^1otn%q{8uUzgqtbECHVhRk2A3>lmtldvDw} zn{!qK1_X>+l1+^p1Coj9mPr7mpD~6S);jtbKgppxmCPT~wkZ5cU*kt+(?&;M zV~n1zQ!;-DwrKZCAH&>idNRn~$MD9_R3D{eA%!B0Jw^4OK!oT_nlMq0U{T*y-ujPn zXzt2U3h%t(OR{L*8XqMKghVVlyJ}#@pna6)#DDYY*GID{{dJg%g+UaH8m{(wFlrwJ z8Q3k!I}##=SVsUA-|vG{*|310^Y^O3l8O((7A@V;GdF0~vd@;!3Gs4Zp3sD>GThv*_oNrmhjrGZo!2syMC9)?Q)WFU;0nTaU4|S6lxI&q7GK>0e_X+rN&C zEoc3!1Ce{$lG{|{Nu##XtA_uYZ;kDmo{M|Q-j#Z?rMKrN<|}PAiiIQ(5B#YpZMbwG z<{Q)D2%27WNM-iMyNbvM{!}F99)9q?qg8PKez3gA&ilDxIq!!oj6ZY%Hg1y0i#L;( z!6vThN|MrJ)9p*k=@9+`DESNacJ)Y|&eE2T{JDkuu%dEiv2$^HXU--OCOUvJoV4E*8BgZ^=cgBM7I2YzTlfUDwwS(<`o>}gqlTOzEKT<(<)kzuVpn6 z#yzYizL>|GwXEj3m`8Xmt2byQj|u^%;1y#k6!K;pIEL(^dxtt%X0X(QQps!E({FF? ziND(Xygs%QASUjO7gpd8HJ;_h-Wh_;8##l%$b$_P z*{cTSzZIkQ&mrvSw*h_G&$E8_I(CE`90Ej$M(_m`f9C8`98u|pVjT-D!K319#*m~icMjwPZ zSti8PK6l8P)BXCM{s@10@u;?kMu)oDmXQiu)%pGmegq#Y$yg_EgU``Zzm0aX%$2FV zSO*)(if>?*-#XRm=OZ(wngriF1M1VDY8c3Z$yl8?gLs*b1bY)vs`o zwg2Aamq9_(QCV_nRm6hsX$@7|;gFfBYY%)#mhySVkK3M4CF z^7X&N0|JKaHP6eB{jjyF3M6yzdZXd9fI$;-=pS!bzw=t`!WG0H^QW2WhWW>s>7BQ& zUwJLOidBI;cn78&v!;$Z0}DGF2V1Xr6tqg)KLx{p_Wzf2dtd)UZk+m}LE4)^wwG1e1k5X&fqf}rS+4d;$WoO%?6b_Gu z?N_PbTNsacI*PuL@rcM}`>RyUs^TMlm5Qz~{*Ql^90d9IsRTAS%H+9NhkEjIHfpO; z^<_u2*skljr~!VzddUZMP-)G^>Kpz`#;k>~v20)cDm3>&wY*JtDE-)hnhz_LG&fy1 zM-8(YRcAsFvCT<2;?{w***Th0%R1sbj~i0DN_y#rXignauhYW%lz!c!QTsn>*X5L& zYX3+>J{c@Dh^eb=8a@Cf;@+5Q$z{+qio$0;526zWn6gQI`it}5bbHObRb41OSj$LV z5-61CU`={al}vLg|7j84=s4{VE25HS`fj4lzo9gyJ`EeLph*q(ra4@vVN>XzEhq}p zZj0%6ZKy?<{xY@+6IRd%`l{))#*dWls!s!}DC#G^xR!b^H&p+FE}CkQ{?d&OdYAs9 zFu^vt6ybtODmBRN??!3UZz)|}pH2x^)8tJads|W3^?*h2LB*dc-=Yb^c?HegtIpJa zeUD$Ug8n^a5v8G?#s{y|r$=jCp$YyOG^c97GMerTvzzLp^q0!Hl#XuDAF}c5aY_Gc zGpMYNgL!Q$XnM2XDZTkxP4{=}!G*VI+~O+*)dwk)=>~5F%^Fomxn(}<#ahB{&s4=$skR0wPx&mT5Ji&H&Lisv8 zfi>o$?Xj(%BNW4bn~vUSA1_1}@wRcbH2^TM)u&7JG@tE$xwt@)zMRBZzt@fqiT5VueZTJ7EwaFvuhl*NlgK-wYh4$kdG}<}B z423o~RJmx$&;|U8723bM_t$E>v{<^(wW@YPrPea>KdL)wc4o8RUBEBHPxDCSBS16U4@(0w4~*0&XT=}))-pRJ(@d; zwmMsXY3nclU-XxP)L)jxcab+TY|oVKnff4cFSti5dN=o?nR(J&;SahG^n$&$T2cxxJ>)Xqymwo& z|B5(aSWB8^-}hHYv`Q6qwQVYD`l!jRmh|q8w;ivuc&5LaeF>YHB0Ak_Lk(4Tf)^Zc zGHU=ez2TSBn%>L#=!Kh7CYPJfqMvj(w4oW!1Adc==va4}fwoa89q27MZ;uu|oubai zH}33h(Nat+aQwuy^R4TBx3!g+W_`NEF*wViqu%vpk;3T_S@P)-V(I`mVj;KZ%VLJr zF%hDe-4nd;dA=;B7B~&!j;AQr&g;?2a@0dM9QE+a6jAJ&TmNM-t-v`BlP|b8*s%f1 zR^X_H+AEUpY;US@OcU{jXwxb;pQeozR4Ru$L0aw7?p2(c%xtMmTior%rIEry?Gk_G zeC_LXqLy@O+)8^ldge0bsf`w(yY{P~K=^y(1h6ivz`e^VwY_EtPh5uc= z=rj{$|If#d7CgKAA?JhZ*=~_hRBUlqT|0MJytE@xxxSOi7zDjkR!t-QU629$v^v8MEx~(aV6RfV!OMxyZf+%{B6%J zKd1XyKD+SzvfTuBTIruQRw3r8x8SgU_>WzZwH~=D^`!V87rg1hgnO|)v3n&0e@!{} z`7MaUyG;I17QE@E9s}=Bll##vzxL?+JB!2nM*c4qyy;LvPHE*1lfAUR7~&{y-Gx4o z|NjIi0fB$Z!#c(s@Jydh$P9==eH_RG_)iHKEOmkU>+=X%%JPu9HT>rY7=vJ6x8rEf zey3O*-hc6bhaktnPR%d=?!}cU8oR5+Rx5wJOKi1LlC}c>_y$|eKW2OK@gA)8D@T;z z-Hg)37X)DknD@Z2&rmRZC7U3$1UBM!yLdscn+J9~Fco_S1&+$>dX#C2`2wz*JrYoL#_fA53Z`uI?e&9$XrfO9?l)I48(sul6$?r z0QnIT(|LU-x{n}FBa@Jsoc&bD>d3~(4#?ifLCCSlNaO8yjU0p=i;P4rKz@W= zhun!gf;^2(k_cZK=K_Y7xmyt@X!1?j=U4dp^7Cfpu;pL4e#+Wi-s)e7g2`Enf%O23 zo%#Rc?w17VkM-3zz#PKoa0+lV+C6~o+-~`as^T9K5fU63K53$>SJ2e)EL6p#SbnNB z;O0M7JfNgMRjwc=r}+Zf+0Ps_2>B<(4Xo_f%KB3s988Qw&HvbWo&+!Xmx71cc`2j_ zcHY~N`;o`21Z5czANUs!A9xmd0eJ;^9hr>0jl7GzkIY8uk!DVBxjgLG9`f^Mzt)WG z@)@fkYanYQ>mk{%K8wfdg|P*)HL@MD6Y@XESCKuDeUSZ;1Cf5nK;$swNaPq~DDq9@ zMC26YbmVO0JIHy+MaZSdcabZQA0t0Qeu-R*T#wv@{9Yn_!LJGHVT(7s_bon*$&`n1 Q-UF~+pon?tiJsyA10j8Z(f|Me literal 0 HcmV?d00001 diff --git a/doc/img/DSDdemod_plugin.png b/doc/img/DSDdemod_plugin.png index 1e6d12cb229e81ab2148a87aadb6495f6b4c1919..7f59b3942ce17174c4479f036c435c8b3efb59a6 100644 GIT binary patch literal 133793 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 z

    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 w{r2%+|MmaHoBjXwwEx%d@&6#Gddf=*6?xd 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 literal 334032 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.18: Gain after discriminator This is the gain applied to the output of the discriminator before the decoder. Normally this would be set at unit gain 1.0 while the FM deviation is adjusted. However this can be used to extend the range of FM adjustment. + +

    B.19: Activate AMBE hardware feature

    + +Connects to an [AMBE Feature](../../feature/ambe/readme.md) to process AMBE frames in hardware + +

    B.20: AMBE feature index for hardware decoding

    + +Select the AMBE feature index used to process AMBE frames in hardware. If no AMBE feature is present then the list is empty. The list is automatically updated when an AMBE feature is added or removed. diff --git a/plugins/feature/ambe/readme.md b/plugins/feature/ambe/readme.md new file mode 100644 index 000000000..ca3f72eca --- /dev/null +++ b/plugins/feature/ambe/readme.md @@ -0,0 +1,58 @@ +

    AMBE controller

    + +

    Introduction

    + +Control AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. + +

    Interface

    + +![AMBE controller GUI](../../../doc/img/AMBE_plugin.png) + + +

    1: AMBE server address and port or direct input

    + +Use this freeflow text input box to specify either the address and port of an AMBE server in the form: <IPv4 address>:<port> or any directly attached physical device address like a COM port on Windows. + +

    2: Import above address or device

    + +Import the address or device specified in (1) into the list of used devices. The system will try to open the device or contact the server and will add it to the list only if successful. + +

    3: Remove in use device or address

    + +When a device or address is selected in the in use list (6) push this button to remove it from the list. The corresponding resources will be released. + +

    4: Refresh in use list

    + +Checks the list of devices or addresses currently in use and update the in use list (6). + +

    5: Empty in use list

    + +Removes all devices or addresses in use. The in use list (6) is cleared consequently. This removes all AMBE devices related resources attached to the current instance of the SDRangel program. Therefore consecutive AMBE frames decoding will be handled by the mbelib library if available or no audio will be output. + +

    6: In use list

    + +List of devices or addresses currently in use for AMBE frames decoding by this feature. + +Format is the device path or URL followed by a dash then the number of frames successfully decoded then a vertical bar then the number of frames that failed decoding. i.e: + +` - |` + +

    7: Import serial device

    + +Imports a serial device scanned in the list of available AMBE 3000 serial devices (10) in the in use list. If this device is already in the in use list then nothing happens and this is reported in the status text (11) + +

    8: Import all serial devices

    + +Imports all serial devices scanned in the list of available AMBE 3000 serial devices (9) in the in use list. If any device is already in the in use list then it is not added twice. + +

    9: Refresh list of AMBE serial devices

    + +Scans available AMBE 3000 serial devices and updates the list. + +

    10: List of AMBE 3000 serial devices

    + +List of AMBE 3000 connected to the system. Use button (9) to update the list. + +

    11: Status text

    + +A brief text reports the result of the current action. From 84d7aa6a8e7a1a268d3e3a5cb4f6bd47d6589d37 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 18:22:06 +0200 Subject: [PATCH 22/29] LimeRFE plugin: fixed missing call to web API adapter --- plugins/feature/limerfe/limerfeplugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/feature/limerfe/limerfeplugin.cpp b/plugins/feature/limerfe/limerfeplugin.cpp index 491011da3..09f911209 100644 --- a/plugins/feature/limerfe/limerfeplugin.cpp +++ b/plugins/feature/limerfe/limerfeplugin.cpp @@ -24,7 +24,7 @@ #endif #include "limerfe.h" #include "limerfeplugin.h" -// #include "simplepttwebapiadapter.h" +#include "limerfewebapiadapter.h" const PluginDescriptor LimeRFEPlugin::m_pluginDescriptor = { LimeRFE::m_featureId, @@ -76,5 +76,5 @@ Feature* LimeRFEPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInter FeatureWebAPIAdapter* LimeRFEPlugin::createFeatureWebAPIAdapter() const { - return nullptr; // TODO new SimplePTTWebAPIAdapter(); + return new LimeRFEWebAPIAdapter(); } From d6d4619296048d8f2786df1a06301ae8ce285531 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 18:36:24 +0200 Subject: [PATCH 23/29] Updated versions and changelogs --- CHANGELOG | 6 ++++++ CMakeLists.txt | 2 +- debian/changelog | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index dee706bb9..9c5b83baa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +sdrangel (7.2.0-1) unstable; urgency=medium + + * Support hardware AMBE decoding wih a new feature plugin. Implements #1254 + + -- Edouard Griffiths, F4EXB Wed, 25 May 2022 16:35:36 +0200 + sdrangel (7.1.0-1) unstable; urgency=medium * Support LimeRFE wtih a new feature plugin. Implements #1251 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c94d32c0..949568520 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # configure version set(sdrangel_VERSION_MAJOR "7") -set(sdrangel_VERSION_MINOR "1") +set(sdrangel_VERSION_MINOR "2") set(sdrangel_VERSION_PATCH "0") set(sdrangel_VERSION_SUFFIX "") diff --git a/debian/changelog b/debian/changelog index 6472a96a0..4b3b1e10a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sdrangel (7.2.0-1) unstable; urgency=medium + + * Support hardware AMBE decoding wih a new feature plugin. Implements #1254 + + -- Edouard Griffiths, F4EXB Wed, 25 May 2022 16:35:36 +0200 + sdrangel (7.1.0-1) unstable; urgency=medium * Support LimeRFE wtih a new feature plugin. Implements #1251 From 9af5179e1dd96556d69b24545db1b25b1406f365 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 19:45:07 +0200 Subject: [PATCH 24/29] AMBE feature: corrected export for MSVC --- plugins/feature/ambe/ambeworker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feature/ambe/ambeworker.h b/plugins/feature/ambe/ambeworker.h index f094f83c0..b3879d7a4 100644 --- a/plugins/feature/ambe/ambeworker.h +++ b/plugins/feature/ambe/ambeworker.h @@ -34,7 +34,7 @@ class AudioFifo; -class SDRBASE_API AMBEWorker : public QObject { +class AMBEWorker : public QObject { Q_OBJECT public: class MsgTest : public Message From 2971dfe3af0b618aaa40653f3ca63d25f42f5eb5 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 19:58:18 +0200 Subject: [PATCH 25/29] AMBE feature: more updates to documentation --- plugins/channelrx/demoddsd/readme.md | 21 +---- plugins/feature/ambe/readme.md | 19 +++- sdrgui/readme.md | 129 ++++++++------------------- 3 files changed, 59 insertions(+), 110 deletions(-) diff --git a/plugins/channelrx/demoddsd/readme.md b/plugins/channelrx/demoddsd/readme.md index fbeee2038..b2ce48abc 100644 --- a/plugins/channelrx/demoddsd/readme.md +++ b/plugins/channelrx/demoddsd/readme.md @@ -18,26 +18,9 @@ To enable this plugin at compile time you will need to have DSDcc installed in y

    DV serial device support

    -You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. You may also connect to an AMBE server instance over the network. +You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. You may also connect to an AMBE server instance over the network. This is supported via the [AMBE feature](../../feature/ambe/readme.md). -DV serial devices are supported using the [SerialDV](https://github.com/f4exb/serialDV) library that is a mandatory requirement. Therefore you have to compile and install it in your system. Please refer to this project Readme.md to compile and install SerialDV. f you install it in a custom location say `/opt/install/serialdv` you will need to add this define to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv` - -To effectively use serial DV devices for AMBE decoding you will have to add at least one device to the list of AMBE devices in use using the `AMBE devices control` dialog opened with the `AMBE` option in the `Preferences` menu. The list of devices is saved in the program preferences so that they are persistent across program stop/start. However if the device name or server address changes in between the corresponding reference will be lost. - -Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices connected to your system as the number of conversations you would like to be handled in parallel. - -Note also that hardware serial devices are not supported in Windows because of trouble with COM port support (contributors welcome!). - -If no AMBE devices or servers are activated with the `AMBE devices control` AMBE decoding will take place with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better. - ---- -⚠ With kernel 4.4.52 and maybe other 4.4 versions the default for FTDI devices (that is in the ftdi_sio kernel module) is not to set it as low latency. This results in the ThumbDV dongle not working anymore because its response is too slow to sustain the normal AMBE packets flow. The solution is to force low latency by changing the variable for your device (ex: /dev/ttyUSB0) as follows: - -`echo 1 | sudo tee /sys/bus/usb-serial/devices/ttyUSB0/latency_timer` or `sudo setserial /dev/ttyUSB0 low_latency` - -Newer kernels do not seem to have this issue. - ---- +If no AMBE features are active or the AMBE support is not engaged (B.19) AMBE decoding will take place with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better.

    Mbelib support

    diff --git a/plugins/feature/ambe/readme.md b/plugins/feature/ambe/readme.md index ca3f72eca..be836e436 100644 --- a/plugins/feature/ambe/readme.md +++ b/plugins/feature/ambe/readme.md @@ -2,7 +2,24 @@

    Introduction

    -Control AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. +Control AMBE3000 serial a.k.a. DV serial devices or AMBE server addresses to use for AMBE digital voice processing. + +DV serial devices are supported using the [SerialDV](https://github.com/f4exb/serialDV) library that is a mandatory requirement for this feature to be compiled. Therefore you have to compile and install SerialDV in your system. Please refer to this project Readme.md to compile and install SerialDV. f you install it in a custom location say `/opt/install/serialdv` you will need to add this define to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv` + +To effectively use serial DV devices for AMBE decoding you will have to add at least one device to the list of AMBE devices (6) in use. + +Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices listed in (6) as the number of conversations you would like to be handled in parallel. + +Note also that hardware serial devices are not supported in Windows because of trouble with COM port support (contributors welcome!). + +--- +⚠ With kernel 4.4.52 and maybe other 4.4 versions the default for FTDI devices (that is in the ftdi_sio kernel module) is not to set it as low latency. This results in the ThumbDV dongle not working anymore because its response is too slow to sustain the normal AMBE packets flow. The solution is to force low latency by changing the variable for your device (ex: /dev/ttyUSB0) as follows: + +`echo 1 | sudo tee /sys/bus/usb-serial/devices/ttyUSB0/latency_timer` or `sudo setserial /dev/ttyUSB0 low_latency` + +Newer kernels do not seem to have this issue. + +---

    Interface

    diff --git a/sdrgui/readme.md b/sdrgui/readme.md index ad1cfd0b8..cefd1f8af 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -21,7 +21,6 @@ The menu items from left to right are: - _Audio_: opens a dialog to choose the audio output device. See the audio management documentation [here](audio.md) - _Logging_: opens a dialog to choose logging options. See "Logging" paragraph next for details - _FFT_: opens a dialog to run the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel. See "FFT" paragraph next for details. - - _AMBE_: Opens a dialog to select AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. If none is selected AMBE frames decoding will be done with mbelib if available else no audio will be produced for AMBE digital voice. See "AMBE"paragraph next for details. - _My Position_: opens a dialog to enter your station ("My Position") coordinates in decimal degrees with north latitudes positive and east longitudes positive. This is used whenever positional data is to be displayed (APRS, DPRS, ...). For it now only works with D-Star $$CRC frames. See [DSD demod plugin](../plugins/channelrx/demoddsd/readme.md) for details on how to decode Digital Voice modes. - _Devices_: section to deal with devices settings - _User arguments_: opens a dialog to let the user give arguments specific to a device and its instance (sequence) in the system @@ -201,7 +200,7 @@ Use the "Cancel" button to dismiss all changes When clicking on the FFT submenu a dialog opens for running the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel. -![Main Window AMBE](../doc/img/MainWindow_fft.png) +![Main Window FFT](../doc/img/MainWindow_fft.png)

    2.2.1: FFTW Wisdom program

    @@ -231,57 +230,7 @@ When clicking the "OK" button the `fftwf-wisdom` program is launched in the back When clicking the "Cancel" button the dialog is dismissed without execution of the `fftwf-wisdom` program. -

    2.3: AMBE

    - -When clicking on the AMBE submenu a dialog opens to let you specify physical AMBE devices to decode AMBE frames produced by digital voice signals (using DSD decoder plugin). - -![Main Window AMBE](../doc/img/MainWindow_ambe.png) - -

    2.3.1: AMBE server address and port or direct input

    - -Use this freeflow text input box to specify either the address and port of an AMBE server in the form: <IPv4 address>:<port> or any directly attached physical device address like a COM port on Windows. - -

    2.3.2: Import above address or device

    - -Import the address or device specified in (1) into the list of used devices. The system will try to open the device or contact the server and will add it to the list only if successful. - -

    2.3.3: Remove in use device or address

    - -When a device or address is selected in the in use list (6) push this button to remove it from the list. The corresponding resources will be released. - -

    2.3.4: Refresh in use list

    - -Checks the list of devices or addresses currently in use and update the in use list (6). - -

    2.3.5: Empty in use list

    - -Removes all devices or addresses in use. The in use list (6) is cleared consequently. This removes all AMBE devices related resources attached to the current instance of the SDRangel program. Therefore consecutive AMBE frames decoding will be handled by the mbelib library if available or no audio will be output. - -

    2.3.6: In use list

    - -List of devices or addresses currently in use for AMBE frames decoding by this instance of the SDRangel program. - -

    2.3.7: Import serial device

    - -Imports a serial device scanned in the list of available AMBE 3000 serial devices (9) in the in use list. If this device is already in the in use list then nothing happens and this is reported in the status text (10) - -

    2.3.8: Import all serial devices

    - -Imports all serial devices scanned in the list of available AMBE 3000 serial devices (9) in the in use list. If any device is already in the in use list then it is not added twice. - -

    2.3.9: List of available AMBE 3000 serial devices

    - -This is the list of AMBE 3000 currently attached to the system directly. This list gets updated at every opening of the dialog. - -

    2.3.10: Status text

    - -A brief text reports the result of the current action - -

    2.3.11: Close button

    - -Use this button to dismiss the dialog - -

    2.4: Commands

    +

    2.3: Commands

    This is a tree view of the saved commands. Commands describe the path to an executable file, its arguments a possible link to a keystroke event that triggers the execution. Similarly to presets commands can be arranged into groups and have a description short text. @@ -291,43 +240,43 @@ Of course any binary that resides in your system can be used that way like `/bin ![Main Window presets view](../doc/img/MainWindow_commands_view.png) -

    2.4.1: Command selection

    +

    2.3.1: Command selection

    You select a command or a command group by clicking on its line in the tree view. All actions (6) will be done relative to this command or command group. -

    2.4.2: Group

    +

    2.3.2: Group

    You can organize your commands into groups. Groups can be collapsed or expanded by using the caret icon on the left. -

    2.4.3: Description

    +

    2.3.3: Description

    Short description of a command. -

    2.4.4: Key binding indicator

    +

    2.3.4: Key binding indicator

    - `-`: no key binding - `P`: key press binding - `R`: key release binding -

    2.4.5: Key binding sequence

    +

    2.3.5: Key binding sequence

    Thisis a descriptive text of the key sequence that is used for the key binding. -

    2.4.6: Command control or actions

    +

    2.3.6: Command control or actions

    The controls are located as icons at the bottom of the window: ![Main Window commands](../doc/img/MainWindow_commands.png) -
    2.4.6.1: Create new command
    +
    2.3.6.1: Create new command
    Click on this icon to create a new command. This opens an edit dialog see the edit section (5B.6.3) for the details of the edit dialog. -
    2.4.6.2: Duplicate command
    +
    2.3.6.2: Duplicate command
    Click on this icon to duplicate the currently selected command (inactive on groups). Later you can edit the details of the copy with the edit dialog (see 5B.6.3 next) -
    2.4.6.3: Edit command or command group
    +
    2.3.6.3: Edit command or command group
    Command groups @@ -341,15 +290,15 @@ You can edit the details of the command with this dialog. ![Main Window command group edit](../doc/img/MainWindow_command_edit.png) -
    2.4.6.3.1: Edit group
    +
    2.3.6.3.1: Edit group
    You can select an existing group with the combo or create a new one for this command using the text edit box -
    2.4.6.3.2: Edit description
    +
    2.3.6.3.2: Edit description
    You can edit the description using this text box. The description will appear in the tree view. -
    2.4.6.3.3: Executable file selection
    +
    2.3.6.3.3: Executable file selection
    Clicking on this button will open a file dialog to select the executable file that will be run with this command. The file selection dialog has predefined file pattern selections: @@ -358,11 +307,11 @@ Clicking on this button will open a file dialog to select the executable file th - `*.sh` or `*.bat` for shell or batch files - `*.bin` or `*.exe` for binary files -
    2.4.6.3.4: Executable file path
    +
    2.3.6.3.4: Executable file path
    This is the full path of the selected executable file. -
    2.4.6.3.5: Command line arguments
    +
    2.3.6.3.5: Command line arguments
    Use the text box to edit the arguments given to the executable file as in `program arguments`. @@ -372,41 +321,41 @@ You can use special codes to insert information specific to the application cont - `%2`: the port of the web REST API - `%3`: the currently selected device set index -
    2.4.6.3.6: Key binding
    +
    2.3.6.3.6: Key binding
    Use this checkbox to enable or disable the command execution binding to a key or combination of keys press or release event -
    2.4.6.3.7: Key binding capture
    +
    2.3.6.3.7: Key binding capture
    Use this button to capture the key or key combination that will be used for the key binding. After pushing this button just type in the key or key combination. -
    2.4.6.3.8: Key binding display
    +
    2.3.6.3.8: Key binding display
    This shows the key or combination of keys used for the key binding. -
    2.4.6.3.9: Release key binding
    +
    2.3.6.3.9: Release key binding
    Use this checkbox to bind the key or combination of keys to the key release event. If unchecked the binding will be associated to the key press event. -
    2.4.6.3.10: Confirm changes
    +
    2.3.6.3.10: Confirm changes
    Use the "OK" button to confirm the changes. -
    2.4.6.3.11: Cancel changes
    +
    2.3.6.3.11: Cancel changes
    Use the "Cancel" button to cancel the changes. -
    2.4.6.4: Run command or groups of commands
    +
    2.3.6.4: Run command or groups of commands
    This will run the currently selected command. If the selection is a group it will run all commands of the group starting them in the displayed order. Please note that commands are run in independent processes and therefore all launched commands in the group will run concurrently. -
    2.4.6.5: View last command run details
    +
    2.3.6.5: View last command run details
    This dialog will show the results of the last run including the output (merged stdout and stderr). ![Main Window command output](../doc/img/MainWindow_command_output.png) -
    2.4.6.5.1: Process status
    +
    2.3.6.5.1: Process status
    When the process is not running the stop icon (■) is displayed. The background color indicate different states: @@ -416,31 +365,31 @@ When the process is not running the stop icon (■) is displayed. The backgr When the process is running the play icon (▶) is displayed with an orange background. -
    2.4.6.5.2: Refresh data
    +
    2.3.6.5.2: Refresh data
    Pushing this button will update the data displayed with the latest status. Please note that the log is displayed only when the process is terminated. -
    2.4.6.5.3: Start time
    +
    2.3.6.5.3: Start time
    This is the timestamp of process start. It is filled with dots `...` if the process has never started during this session. -
    2.4.6.5.4: End time
    +
    2.3.6.5.4: End time
    This is the timestamp of process end. It is filled with dots `...` if the process has never terminated during this session. -
    2.4.6.5.3: PID
    +
    2.3.6.5.3: PID
    This is the process PID. It is 0 if the process has never run during this session. -
    2.4.6.5.6: Process kill
    +
    2.3.6.5.6: Process kill
    Use this button to kill (send SIGKILL) the running process. It has no effect if the process is not running. -
    2.4.6.5.7: Command line
    +
    2.3.6.5.7: Command line
    This shows the actual command line that was used to start the process -
    2.4.6.5.8: Error status
    +
    2.3.6.5.8: Error status
    This is the translation of `QProcess::ProcessError`. Possible values are: @@ -452,31 +401,31 @@ This is the translation of `QProcess::ProcessError`. Possible values are: - `Read error`: an error occurred when attempting to read from the process. For example, the process may not be running. - `Unknown error`: an unknown error occurred. -
    2.4.6.5.9: Exit code
    +
    2.3.6.5.9: Exit code
    This is the program exit code. When the process crashes this is the signal by which the process end was caused. For example if you kill the process with button (6) it sends the process a SIGKILL (code 9) and therefore the value is 9. -
    2.4.6.5.10: Exit status
    +
    2.3.6.5.10: Exit status
    There are only two possibilities: either the program exits normally but possibly with a non zero exit code or it ends with a crash. -
    2.4.6.5.11: Process log
    +
    2.3.6.5.11: Process log
    This is the log of the process (merged stdout and stderr). Please note that it is updated only on program termination. -
    2.4.6.5.12: Exit
    +
    2.3.6.5.12: Exit
    By pushing the "Close" button the process output window is closed. -
    2.4.6.6: Save commands
    +
    2.3.6.6: Save commands
    This will save the commands immediately. The commands will be automatically saved when the application exits normally. -
    2.4.6.7: Delete commands or group of commands
    +
    2.3.6.7: Delete commands or group of commands
    This will delete the currently selected command or if selection is a group this will delete all commands in the group. -
    2.4.6.8: Activate keyboard bindings
    +
    2.3.6.8: Activate keyboard bindings
    Use this button to activate the keyboard bindings. Note that you need to have this button selected (its background should be lit in beige/orange) for the key bindings to be effective. From 236c0e8d218a8a74469a0b840c7bac3fa42f1e70 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 21:25:31 +0200 Subject: [PATCH 26/29] AMBE feature support in Linux only --- plugins/channelrx/demoddsd/readme.md | 2 ++ plugins/feature/CMakeLists.txt | 2 +- plugins/feature/ambe/readme.md | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/channelrx/demoddsd/readme.md b/plugins/channelrx/demoddsd/readme.md index b2ce48abc..2e0b07e40 100644 --- a/plugins/channelrx/demoddsd/readme.md +++ b/plugins/channelrx/demoddsd/readme.md @@ -18,6 +18,8 @@ To enable this plugin at compile time you will need to have DSDcc installed in y

    DV serial device support

    +⚠ This is supported in Linux only since the AMBE feature plugin is supported in Linux only. + You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. You may also connect to an AMBE server instance over the network. This is supported via the [AMBE feature](../../feature/ambe/readme.md). If no AMBE features are active or the AMBE support is not engaged (B.19) AMBE decoding will take place with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better. diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index 4a79484d5..3367631f2 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -40,6 +40,6 @@ if (ENABLE_LIMESUITE AND LIMESUITE_FOUND) add_subdirectory(limerfe) endif() -if (LIBSERIALDV_FOUND) +if (LINUX AND LIBSERIALDV_FOUND) add_subdirectory(ambe) endif() diff --git a/plugins/feature/ambe/readme.md b/plugins/feature/ambe/readme.md index be836e436..6fa077727 100644 --- a/plugins/feature/ambe/readme.md +++ b/plugins/feature/ambe/readme.md @@ -4,14 +4,14 @@ Control AMBE3000 serial a.k.a. DV serial devices or AMBE server addresses to use for AMBE digital voice processing. +⚠ Note: this plugin is supported in Linux only. + DV serial devices are supported using the [SerialDV](https://github.com/f4exb/serialDV) library that is a mandatory requirement for this feature to be compiled. Therefore you have to compile and install SerialDV in your system. Please refer to this project Readme.md to compile and install SerialDV. f you install it in a custom location say `/opt/install/serialdv` you will need to add this define to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv` To effectively use serial DV devices for AMBE decoding you will have to add at least one device to the list of AMBE devices (6) in use. Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices listed in (6) as the number of conversations you would like to be handled in parallel. -Note also that hardware serial devices are not supported in Windows because of trouble with COM port support (contributors welcome!). - --- ⚠ With kernel 4.4.52 and maybe other 4.4 versions the default for FTDI devices (that is in the ftdi_sio kernel module) is not to set it as low latency. This results in the ThumbDV dongle not working anymore because its response is too slow to sustain the normal AMBE packets flow. The solution is to force low latency by changing the variable for your device (ex: /dev/ttyUSB0) as follows: @@ -28,7 +28,7 @@ Newer kernels do not seem to have this issue.

    1: AMBE server address and port or direct input

    -Use this freeflow text input box to specify either the address and port of an AMBE server in the form: <IPv4 address>:<port> or any directly attached physical device address like a COM port on Windows. +Use this freeflow text input box to specify either the address and port of an AMBE server in the form: <IPv4 address>:<port> or any directly attached physical device address.

    2: Import above address or device

    From 162ef6c39337ec60d6c51b320e8b143b37ce4261 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 22:26:43 +0200 Subject: [PATCH 27/29] AMBE feature: restore Windows and Mac support --- plugins/channelrx/demoddsd/readme.md | 2 -- plugins/feature/CMakeLists.txt | 2 +- plugins/feature/ambe/ambeengine.cpp | 4 ++-- plugins/feature/ambe/readme.md | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/channelrx/demoddsd/readme.md b/plugins/channelrx/demoddsd/readme.md index 2e0b07e40..b2ce48abc 100644 --- a/plugins/channelrx/demoddsd/readme.md +++ b/plugins/channelrx/demoddsd/readme.md @@ -18,8 +18,6 @@ To enable this plugin at compile time you will need to have DSDcc installed in y

    DV serial device support

    -⚠ This is supported in Linux only since the AMBE feature plugin is supported in Linux only. - You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. You may also connect to an AMBE server instance over the network. This is supported via the [AMBE feature](../../feature/ambe/readme.md). If no AMBE features are active or the AMBE support is not engaged (B.19) AMBE decoding will take place with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better. diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index 3367631f2..4a79484d5 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -40,6 +40,6 @@ if (ENABLE_LIMESUITE AND LIMESUITE_FOUND) add_subdirectory(limerfe) endif() -if (LINUX AND LIBSERIALDV_FOUND) +if (LIBSERIALDV_FOUND) add_subdirectory(ambe) endif() diff --git a/plugins/feature/ambe/ambeengine.cpp b/plugins/feature/ambe/ambeengine.cpp index faa91610a..03571a8da 100644 --- a/plugins/feature/ambe/ambeengine.cpp +++ b/plugins/feature/ambe/ambeengine.cpp @@ -78,7 +78,7 @@ void AMBEEngine::getComList() } // Do not activate serial support at all for windows -void AMBEEngine::scan(std::vector& ambeDevices) +void AMBEEngine::scan(QList& ambeDevices) { (void) ambeDevices; } @@ -87,7 +87,7 @@ void AMBEEngine::getComList() { qDebug("AMBEEngine::getComList: Apple"); } -void AMBEEngine::scan(std::vector& ambeDevices) +void AMBEEngine::scan(QList& ambeDevices) { (void) ambeDevices; } diff --git a/plugins/feature/ambe/readme.md b/plugins/feature/ambe/readme.md index 6fa077727..9b6bdf98e 100644 --- a/plugins/feature/ambe/readme.md +++ b/plugins/feature/ambe/readme.md @@ -4,8 +4,6 @@ Control AMBE3000 serial a.k.a. DV serial devices or AMBE server addresses to use for AMBE digital voice processing. -⚠ Note: this plugin is supported in Linux only. - DV serial devices are supported using the [SerialDV](https://github.com/f4exb/serialDV) library that is a mandatory requirement for this feature to be compiled. Therefore you have to compile and install SerialDV in your system. Please refer to this project Readme.md to compile and install SerialDV. f you install it in a custom location say `/opt/install/serialdv` you will need to add this define to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv` To effectively use serial DV devices for AMBE decoding you will have to add at least one device to the list of AMBE devices (6) in use. From 2035e6ea624ea5794c79f49d5a564c6efc875e98 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 25 May 2022 23:13:14 +0200 Subject: [PATCH 28/29] AMBE feature: added missing ambewebapiadapter sources to cmake file --- plugins/feature/ambe/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/feature/ambe/CMakeLists.txt b/plugins/feature/ambe/CMakeLists.txt index d243b2633..e2fb11f72 100644 --- a/plugins/feature/ambe/CMakeLists.txt +++ b/plugins/feature/ambe/CMakeLists.txt @@ -6,7 +6,7 @@ set(ambe_SOURCES ambeplugin.cpp ambeengine.cpp ambeworker.cpp - # ambewebapiadapter.cpp + ambewebapiadapter.cpp ) set(ambe_HEADERS @@ -15,7 +15,7 @@ set(ambe_HEADERS ambeplugin.h ambeengine.h ambeworker.h - # ambewebapiadapter.h + ambewebapiadapter.h ) include_directories( From 22e11f8cb4621a9e2a4bf0be273baf78c0f0f749 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 26 May 2022 05:13:46 +0200 Subject: [PATCH 29/29] Updated versions and changelogs --- CHANGELOG | 6 ++++++ CMakeLists.txt | 2 +- debian/changelog | 6 ++++++ plugins/feature/ambe/ambeplugin.cpp | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9c5b83baa..808e0c97c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +sdrangel (7.2.1-1) unstable; urgency=medium + + * AMBE feature: fixed many errors preventing build on Windows and MacOS. Issue #1254 + + -- Edouard Griffiths, F4EXB Thu, 26 May 2022 03:12:00 +0200 + sdrangel (7.2.0-1) unstable; urgency=medium * Support hardware AMBE decoding wih a new feature plugin. Implements #1254 diff --git a/CMakeLists.txt b/CMakeLists.txt index 949568520..32ee35ff3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # configure version set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MINOR "2") -set(sdrangel_VERSION_PATCH "0") +set(sdrangel_VERSION_PATCH "1") set(sdrangel_VERSION_SUFFIX "") # SDRAngel cmake options diff --git a/debian/changelog b/debian/changelog index 4b3b1e10a..dd9b651e2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sdrangel (7.2.1-1) unstable; urgency=medium + + * AMBE feature: fixed many errors preventing build on Windows and MacOS. Issue #1254 + + -- Edouard Griffiths, F4EXB Thu, 26 May 2022 03:12:00 +0200 + sdrangel (7.2.0-1) unstable; urgency=medium * Support hardware AMBE decoding wih a new feature plugin. Implements #1254 diff --git a/plugins/feature/ambe/ambeplugin.cpp b/plugins/feature/ambe/ambeplugin.cpp index 7a2c46a05..495e8881a 100644 --- a/plugins/feature/ambe/ambeplugin.cpp +++ b/plugins/feature/ambe/ambeplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor AMBEPlugin::m_pluginDescriptor = { AMBE::m_featureId, QStringLiteral("AMBE Controller"), - QStringLiteral("7.2.0"), + QStringLiteral("7.2.1"), QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("https://github.com/f4exb/sdrangel"), true,