diff --git a/.gitignore b/.gitignore index e8a7ef5ed..48b21d85d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ debian/files debian/sdrangel.debhelper.log debian/debhelper-build-stamp obj-x86_64-linux-gnu/* +**/venv/ +*.pyc diff --git a/CMakeLists.txt b/CMakeLists.txt index 03b3e3a10..65dd6c33a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ option(SANITIZE_ADDRESS "Activate memory address sanitization" OFF) option(HOST_RPI "Compiling on RPi" OFF) option(RX_SAMPLE_24BIT "Internal 24 bit Rx DSP" OFF) option(NO_DSP_SIMD "Do not use SIMD instructions for DSP even if available" OFF) +option(BUILD_SERVER "Build Server" ON) +option(BUILD_GUI "Build GUI" ON) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) @@ -211,7 +213,11 @@ else() message(STATUS "Compiling with SIMD instructions for DSP if available") endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wvla -Woverloaded-virtual -ffast-math -ftree-vectorize ${EXTRA_FLAGS}") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wvla -Woverloaded-virtual -ffast-math -ftree-vectorize ${EXTRA_FLAGS}") +elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W3 -MP ${EXTRA_FLAGS}") +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (BUILD_DEBIAN) @@ -224,8 +230,12 @@ endif() ############################################################################## # base libraries add_subdirectory(sdrbase) +if (BUILD_GUI) add_subdirectory(sdrgui) +endif() +if (BUILD_SERVER) add_subdirectory(sdrsrv) +endif() add_subdirectory(sdrbench) add_subdirectory(httpserver) add_subdirectory(logging) @@ -250,6 +260,7 @@ qt5_add_external_resources(sdrbase.rcc sdrbase/resources/res.qrc) ############################################################################## # main GUI application +if (BUILD_GUI) set(sdrangel_SOURCES app/main.cpp @@ -281,9 +292,11 @@ if(WIN32) endif(WIN32) target_link_libraries(sdrangel Qt5::Widgets Qt5::Multimedia) +endif() ############################################################################## # main server application +if (BUILD_SERVER) set(sdrangelsrv_SOURCES appsrv/main.cpp @@ -305,6 +318,7 @@ target_link_libraries(sdrangelsrv ) target_link_libraries(sdrangelsrv Qt5::Multimedia) +endif() ############################################################################## # main benchmark application @@ -350,8 +364,12 @@ if (BUILD_DEBIAN) endif (BUILD_DEBIAN) add_subdirectory(devices) +if (BUILD_GUI) add_subdirectory(plugins) +endif() +if (BUILD_SERVER) add_subdirectory(pluginssrv) +endif() if(LIBUSB_FOUND AND UNIX) add_subdirectory(fcdhid) @@ -361,15 +379,21 @@ endif(LIBUSB_FOUND AND UNIX) ############################################################################## #install targets +if (BUILD_GUI) install(TARGETS sdrangel DESTINATION bin) +endif() +if (BUILD_SERVER) install(TARGETS sdrangelsrv DESTINATION bin) +endif() install(TARGETS sdrangelbench DESTINATION bin) #install(TARGETS sdrbase DESTINATION lib) #install files and directories install(DIRECTORY udev-rules DESTINATION share/sdrangel) -install(FILES udev-rules/install.sh DESTINATION share/sdrangel/udev-rules PERMISSIONS WORLD_EXECUTE) +install(FILES udev-rules/install.sh DESTINATION share/sdrangel/udev-rules PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sdrbase.rcc DESTINATION bin) +install(FILES desktop/sdrangel.desktop DESTINATION share/applications) +install(FILES desktop/sdrangel_icon.png DESTINATION share/pixmaps) ############################################################################## diff --git a/app/main.cpp b/app/main.cpp index 101f06656..511586000 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -35,7 +35,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo */ QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangel"); - QCoreApplication::setApplicationVersion("4.3.1"); + QCoreApplication::setApplicationVersion("4.3.2"); #if QT_VERSION >= 0x050600 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // DPI support QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); //HiDPI pixmaps diff --git a/appbench/main.cpp b/appbench/main.cpp index 49161a862..4523e8be1 100644 --- a/appbench/main.cpp +++ b/appbench/main.cpp @@ -57,7 +57,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangelBench"); - QCoreApplication::setApplicationVersion("4.3.1"); + QCoreApplication::setApplicationVersion("4.3.2"); int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP}; std::vector vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int)); diff --git a/appsrv/main.cpp b/appsrv/main.cpp index af308e7a3..b6e093761 100644 --- a/appsrv/main.cpp +++ b/appsrv/main.cpp @@ -56,7 +56,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo QCoreApplication::setOrganizationName("f4exb"); QCoreApplication::setApplicationName("SDRangelSrv"); - QCoreApplication::setApplicationVersion("4.3.1"); + QCoreApplication::setApplicationVersion("4.3.2"); int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP}; std::vector vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int)); diff --git a/debian/changelog b/debian/changelog index 7bee4db1d..e04312234 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +sdrangel (4.3.2-1) unstable; urgency=medium + + * Reverse API to forward device and channel changes to external application + * Funcube dongle: fixed segfault when stopping device + * Channel analzyer: fixed rational downsampler range + * SoapySDR support: fixed memory leaks + + -- Edouard Griffiths, F4EXB Sun, 09 Dec 2018 21:14:18 +0100 + sdrangel (4.3.1-1) unstable; urgency=medium * RTL-SDR: offset tuning support diff --git a/desktop/sdrangel.desktop b/desktop/sdrangel.desktop index 6669aefa9..47b0edd79 100644 --- a/desktop/sdrangel.desktop +++ b/desktop/sdrangel.desktop @@ -3,7 +3,7 @@ Name=SDRangel GenericName=SDR/Analyzer frontend Comment=SDR/Analyzer frontend for Airspy, BladeRF, HackRF, RTL-SDR and FunCube Exec=sdrangel -Icon=sdrangel_icon.xpm +Icon=sdrangel_icon.png StartupNotify=true Terminal=false Type=Application diff --git a/desktop/sdrangel_icon.png b/desktop/sdrangel_icon.png new file mode 100644 index 000000000..eb5f8cbe5 Binary files /dev/null and b/desktop/sdrangel_icon.png differ diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt index 65439fb0d..422cc588b 100644 --- a/devices/CMakeLists.txt +++ b/devices/CMakeLists.txt @@ -28,19 +28,19 @@ else(BUILD_DEBIAN) if(LIBUSB_FOUND AND LIMESUITE_FOUND) add_subdirectory(limesdr) endif(LIBUSB_FOUND AND LIMESUITE_FOUND) - + find_package(LibIIO) if(LIBUSB_FOUND AND LIBIIO_FOUND) add_subdirectory(plutosdr) endif(LIBUSB_FOUND AND LIBIIO_FOUND) - + find_package(LibPerseus) if(LIBUSB_FOUND AND LIBPERSEUS_FOUND) add_subdirectory(perseus) endif() - + find_package(SoapySDR) - if(LIBUSB_FOUND AND SOAPYSDR_FOUND) + if(SOAPYSDR_FOUND) add_subdirectory(soapysdr) endif() endif (BUILD_DEBIAN) diff --git a/devices/bladerf1/CMakeLists.txt b/devices/bladerf1/CMakeLists.txt index ddb327d5f..526530a93 100644 --- a/devices/bladerf1/CMakeLists.txt +++ b/devices/bladerf1/CMakeLists.txt @@ -35,6 +35,8 @@ add_library(bladerf1device SHARED ${bladerf1device_SOURCES} ) +set_target_properties(bladerf1device PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(bladerf1device bladerf diff --git a/devices/bladerf2/CMakeLists.txt b/devices/bladerf2/CMakeLists.txt index 2b0c96490..428421025 100644 --- a/devices/bladerf2/CMakeLists.txt +++ b/devices/bladerf2/CMakeLists.txt @@ -32,6 +32,8 @@ add_library(bladerf2device SHARED ${bladerf2device_SOURCES} ) +set_target_properties(bladerf2device PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(bladerf2device bladerf diff --git a/devices/hackrf/CMakeLists.txt b/devices/hackrf/CMakeLists.txt index bef8053ef..073ac89af 100644 --- a/devices/hackrf/CMakeLists.txt +++ b/devices/hackrf/CMakeLists.txt @@ -34,6 +34,8 @@ add_library(hackrfdevice SHARED ${hackrfdevice_SOURCES} ) +set_target_properties(hackrfdevice PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(hackrfdevice hackrf diff --git a/devices/limesdr/CMakeLists.txt b/devices/limesdr/CMakeLists.txt index f887dadc9..491b98899 100644 --- a/devices/limesdr/CMakeLists.txt +++ b/devices/limesdr/CMakeLists.txt @@ -41,6 +41,8 @@ add_library(limesdrdevice SHARED ${limesdrdevice_SOURCES} ) +set_target_properties(limesdrdevice PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(limesdrdevice limesuite diff --git a/devices/perseus/CMakeLists.txt b/devices/perseus/CMakeLists.txt index 659af597a..e4615944c 100644 --- a/devices/perseus/CMakeLists.txt +++ b/devices/perseus/CMakeLists.txt @@ -33,6 +33,8 @@ add_library(perseusdevice SHARED ${perseusdevice_SOURCES} ) +set_target_properties(perseusdevice PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(perseusdevice perseus diff --git a/devices/plutosdr/CMakeLists.txt b/devices/plutosdr/CMakeLists.txt index 6f0a169c1..74f4e5f41 100644 --- a/devices/plutosdr/CMakeLists.txt +++ b/devices/plutosdr/CMakeLists.txt @@ -39,6 +39,8 @@ add_library(plutosdrdevice SHARED ${plutosdrdevice_SOURCES} ) +set_target_properties(plutosdrdevice PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(plutosdrdevice iio diff --git a/devices/soapysdr/CMakeLists.txt b/devices/soapysdr/CMakeLists.txt index c7902bdde..3fb9f1e84 100644 --- a/devices/soapysdr/CMakeLists.txt +++ b/devices/soapysdr/CMakeLists.txt @@ -38,6 +38,8 @@ add_library(soapysdrdevice SHARED ${soapysdrdevice_SOURCES} ) +set_target_properties(soapysdrdevice PROPERTIES DEFINE_SYMBOL "devices_EXPORTS") + if (BUILD_DEBIAN) target_link_libraries(soapysdrdevice SoapySDR diff --git a/doc/img/BasicChannelSettings.png b/doc/img/BasicChannelSettings.png index 14d38bdeb..ec2065606 100644 Binary files a/doc/img/BasicChannelSettings.png and b/doc/img/BasicChannelSettings.png differ diff --git a/doc/img/BasicChannelSettings.xcf b/doc/img/BasicChannelSettings.xcf index 9a5e73aa9..5fafecc35 100644 Binary files a/doc/img/BasicChannelSettings.xcf and b/doc/img/BasicChannelSettings.xcf differ diff --git a/doc/img/BasicDeviceSettings.png b/doc/img/BasicDeviceSettings.png new file mode 100644 index 000000000..51e44aa7c Binary files /dev/null and b/doc/img/BasicDeviceSettings.png differ diff --git a/doc/img/BasicDeviceSettings.xcf b/doc/img/BasicDeviceSettings.xcf new file mode 100644 index 000000000..f34690921 Binary files /dev/null and b/doc/img/BasicDeviceSettings.xcf differ diff --git a/fcdlib/fcdtraits.cpp b/fcdlib/fcdtraits.cpp index ac008da78..7317531e7 100644 --- a/fcdlib/fcdtraits.cpp +++ b/fcdlib/fcdtraits.cpp @@ -34,8 +34,8 @@ const char *fcd_traits::displayedName = "FunCube Dongle Pro+"; const char *fcd_traits::pluginDisplayedName = "FunCube Pro Input"; const char *fcd_traits::pluginDisplayedName = "FunCube Pro+ Input"; -const char *fcd_traits::pluginVersion = "4.3.0"; -const char *fcd_traits::pluginVersion = "4.3.0"; +const char *fcd_traits::pluginVersion = "4.3.2"; +const char *fcd_traits::pluginVersion = "4.3.2"; const int64_t fcd_traits::loLowLimitFreq = 64000000L; const int64_t fcd_traits::loLowLimitFreq = 150000L; diff --git a/httpserver/CMakeLists.txt b/httpserver/CMakeLists.txt index fc1f8e605..2e6365750 100644 --- a/httpserver/CMakeLists.txt +++ b/httpserver/CMakeLists.txt @@ -48,6 +48,8 @@ add_library(httpserver SHARED ${httpserver_HEADERS_MOC} ) +set_target_properties(httpserver PROPERTIES DEFINE_SYMBOL "httpserver_EXPORTS") + target_link_libraries(httpserver ${QT_LIBRARIES} ) diff --git a/logging/CMakeLists.txt b/logging/CMakeLists.txt index 6b0fbf998..f08965e0f 100644 --- a/logging/CMakeLists.txt +++ b/logging/CMakeLists.txt @@ -34,6 +34,8 @@ add_library(logging SHARED ${logging_HEADERS_MOC} ) +set_target_properties(logging PROPERTIES DEFINE_SYMBOL "logging_EXPORTS") + target_link_libraries(logging ${QT_LIBRARIES} ) diff --git a/plugins/channelrx/chanalyzer/CMakeLists.txt b/plugins/channelrx/chanalyzer/CMakeLists.txt index e286273dd..f89fdca83 100644 --- a/plugins/channelrx/chanalyzer/CMakeLists.txt +++ b/plugins/channelrx/chanalyzer/CMakeLists.txt @@ -23,6 +23,7 @@ set(chanalyzer_FORMS include_directories( . ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} ) #include(${QT_USE_FILE}) diff --git a/plugins/channelrx/chanalyzer/chanalyzer.cpp b/plugins/channelrx/chanalyzer/chanalyzer.cpp index 2b6038084..e46489bcd 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzer.cpp @@ -25,7 +25,6 @@ #include "chanalyzer.h" MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzer, Message) -MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzerOld, Message) MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelizer, Message) MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgReportChannelSampleRateChanged, Message) @@ -75,20 +74,6 @@ ChannelAnalyzer::~ChannelAnalyzer() delete RRCFilter; } -void ChannelAnalyzer::configure(MessageQueue* messageQueue, - int channelSampleRate, - Real Bandwidth, - Real LowCutoff, - int spanLog2, - bool ssb, - bool pll, - bool fll, - unsigned int pllPskOrder) -{ - Message* cmd = MsgConfigureChannelAnalyzerOld::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb, pll, fll, pllPskOrder); - messageQueue->push(cmd); -} - void ChannelAnalyzer::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) { (void) positiveOnly; @@ -234,14 +219,16 @@ bool ChannelAnalyzer::handleMessage(const Message& cmd) } else { - if (m_sampleSink != 0) - { - return m_sampleSink->handleMessage(cmd); - } - else - { - return false; - } + // Processed through GUI +// if (m_sampleSink != 0) +// { +// return m_sampleSink->handleMessage(cmd); +// } +// else +// { +// return false; +// } + return false; } } diff --git a/plugins/channelrx/chanalyzer/chanalyzer.h b/plugins/channelrx/chanalyzer/chanalyzer.h index 54be55fd2..4e66bd055 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.h +++ b/plugins/channelrx/chanalyzer/chanalyzer.h @@ -65,71 +65,6 @@ public: { } }; - class MsgConfigureChannelAnalyzerOld : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getChannelSampleRate() const { return m_channelSampleRate; } - Real getBandwidth() const { return m_Bandwidth; } - Real getLoCutoff() const { return m_LowCutoff; } - int getSpanLog2() const { return m_spanLog2; } - bool getSSB() const { return m_ssb; } - bool getPLL() const { return m_pll; } - bool getFLL() const { return m_fll; } - unsigned int getPLLPSKOrder() const { return m_pllPskOrder; } - - static MsgConfigureChannelAnalyzerOld* create( - int channelSampleRate, - Real Bandwidth, - Real LowCutoff, - int spanLog2, - bool ssb, - bool pll, - bool fll, - unsigned int pllPskOrder) - { - return new MsgConfigureChannelAnalyzerOld( - channelSampleRate, - Bandwidth, - LowCutoff, - spanLog2, - ssb, - pll, - fll, - pllPskOrder); - } - - private: - int m_channelSampleRate; - Real m_Bandwidth; - Real m_LowCutoff; - int m_spanLog2; - bool m_ssb; - bool m_pll; - bool m_fll; - unsigned int m_pllPskOrder; - - MsgConfigureChannelAnalyzerOld( - int channelSampleRate, - Real Bandwidth, - Real LowCutoff, - int spanLog2, - bool ssb, - bool pll, - bool fll, - unsigned int pllPskOrder) : - Message(), - m_channelSampleRate(channelSampleRate), - m_Bandwidth(Bandwidth), - m_LowCutoff(LowCutoff), - m_spanLog2(spanLog2), - m_ssb(ssb), - m_pll(pll), - m_fll(fll), - m_pllPskOrder(pllPskOrder) - { } - }; - class MsgConfigureChannelizer : public Message { MESSAGE_CLASS_DECLARATION @@ -175,15 +110,15 @@ public: virtual void destroy() { delete this; } void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } - void configure(MessageQueue* messageQueue, - int channelSampleRate, - Real Bandwidth, - Real LowCutoff, - int spanLog2, - bool ssb, - bool pll, - bool fll, - unsigned int pllPskOrder); +// void configure(MessageQueue* messageQueue, +// int channelSampleRate, +// Real Bandwidth, +// Real LowCutoff, +// int spanLog2, +// bool ssb, +// bool pll, +// bool fll, +// unsigned int pllPskOrder); DownChannelizer *getChannelizer() { return m_channelizer; } int getInputSampleRate() const { return m_inputSampleRate; } diff --git a/plugins/channelrx/chanalyzer/chanalyzer.pro b/plugins/channelrx/chanalyzer/chanalyzer.pro index 38e1f1522..2ed6190dd 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.pro +++ b/plugins/channelrx/chanalyzer/chanalyzer.pro @@ -24,7 +24,6 @@ INCLUDEPATH += ../../../sdrgui CONFIG(ANDROID):INCLUDEPATH += /opt/softs/boost_1_60_0 CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../../../boost_1_69_0" diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index d394b076e..ea6488ef4 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -14,25 +14,25 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include "device/deviceuiset.h" -#include #include #include +#include "device/devicesourceapi.h" +#include "device/deviceuiset.h" +#include "dsp/downchannelizer.h" #include "dsp/threadedbasebandsamplesink.h" -#include "ui_chanalyzergui.h" #include "dsp/spectrumscopecombovis.h" #include "dsp/spectrumvis.h" +#include "dsp/dspengine.h" #include "gui/glspectrum.h" #include "gui/glscope.h" #include "gui/basicchannelsettingsdialog.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" -#include "dsp/dspengine.h" #include "mainwindow.h" +#include "ui_chanalyzergui.h" #include "chanalyzer.h" #include "chanalyzergui.h" @@ -101,10 +101,12 @@ void ChannelAnalyzerGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + ui->channelSampleRate->setValueRange(7, 0.501*m_channelAnalyzer->getInputSampleRate(), m_channelAnalyzer->getInputSampleRate()); + ui->channelSampleRate->setValue(m_settings.m_downSampleRate); + blockApplySettings(true); ui->useRationalDownsampler->setChecked(m_settings.m_downSample); - ui->channelSampleRate->setValue(m_settings.m_downSampleRate); setNewFinalRate(); if (m_settings.m_ssb) { ui->BWLabel->setText("LP"); @@ -186,9 +188,10 @@ bool ChannelAnalyzerGUI::handleMessage(const Message& message) { if (ChannelAnalyzer::MsgReportChannelSampleRateChanged::match(message)) { - qDebug() << "ChannelAnalyzerGUI::handleMessage: MsgReportChannelSampleRateChanged"; - ui->channelSampleRate->setValueRange(7, 2000U, m_channelAnalyzer->getInputSampleRate()); + qDebug() << "ChannelAnalyzerGUI::handleMessage: MsgReportChannelSampleRateChanged:" << m_channelAnalyzer->getInputSampleRate(); + ui->channelSampleRate->setValueRange(7, 0.501*m_channelAnalyzer->getInputSampleRate(), m_channelAnalyzer->getInputSampleRate()); ui->channelSampleRate->setValue(m_settings.m_downSampleRate); + m_settings.m_downSampleRate = ui->channelSampleRate->getValueNew(); setNewFinalRate(); return true; @@ -401,7 +404,7 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); - ui->channelSampleRate->setValueRange(7, 2000U, 9999999U); + ui->channelSampleRate->setValueRange(7, 0.501*m_rate, m_rate); ui->glSpectrum->setCenterFrequency(m_rate/2); ui->glSpectrum->setSampleRate(m_rate); @@ -544,10 +547,6 @@ void ChannelAnalyzerGUI::applySettings(bool force) ChannelAnalyzer::MsgConfigureChannelizer::create(sampleRate, m_channelMarker.getCenterFrequency()); m_channelAnalyzer->getInputMessageQueue()->push(msgChannelizer); - ChannelAnalyzer::MsgConfigureChannelizer *msg = - ChannelAnalyzer::MsgConfigureChannelizer::create(sampleRate, m_channelMarker.getCenterFrequency()); - m_channelAnalyzer->getInputMessageQueue()->push(msg); - ChannelAnalyzer::MsgConfigureChannelAnalyzer* message = ChannelAnalyzer::MsgConfigureChannelAnalyzer::create( m_settings, force); m_channelAnalyzer->getInputMessageQueue()->push(message); diff --git a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp index 60aa4b978..3004c4139 100644 --- a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor ChannelAnalyzerPlugin::m_pluginDescriptor = { QString("Channel Analyzer"), - QString("4.3.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/daemonsink/daemonsink.cpp b/plugins/channelrx/daemonsink/daemonsink.cpp index 797e5b429..94f7cb867 100644 --- a/plugins/channelrx/daemonsink/daemonsink.cpp +++ b/plugins/channelrx/daemonsink/daemonsink.cpp @@ -25,6 +25,10 @@ #include #include +#include +#include +#include + #include "SWGChannelSettings.h" #include "util/simpleserializer.h" @@ -63,14 +67,21 @@ DaemonSink::DaemonSink(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } DaemonSink::~DaemonSink() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_dataBlockMutex.lock(); + if (m_dataBlock && !m_dataBlock->m_txControlBlock.m_complete) { delete m_dataBlock; } + m_dataBlockMutex.unlock(); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSink(m_threadedChannelizer); @@ -325,23 +336,43 @@ void DaemonSink::applySettings(const DaemonSinkSettings& settings, bool force) << " m_dataPort: " << settings.m_dataPort << " force: " << force; - if ((m_settings.m_nbFECBlocks != settings.m_nbFECBlocks) || force) { + QList reverseAPIKeys; + + if ((m_settings.m_nbFECBlocks != settings.m_nbFECBlocks) || force) + { + reverseAPIKeys.append("nbFECBlocks"); setNbBlocksFEC(settings.m_nbFECBlocks); setTxDelay(settings.m_txDelay, settings.m_nbFECBlocks); } - if ((m_settings.m_txDelay != settings.m_txDelay) || force) { + if ((m_settings.m_txDelay != settings.m_txDelay) || force) + { + reverseAPIKeys.append("txDelay"); setTxDelay(settings.m_txDelay, settings.m_nbFECBlocks); } - if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) { + if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) + { + reverseAPIKeys.append("dataAddress"); m_dataAddress = settings.m_dataAddress; } - if ((m_settings.m_dataPort != settings.m_dataPort) || force) { + if ((m_settings.m_dataPort != settings.m_dataPort) || force) + { + reverseAPIKeys.append("dataPort"); m_dataPort = settings.m_dataPort; } + if ((settings.m_useReverseAPI) && (reverseAPIKeys.size() != 0)) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -408,7 +439,21 @@ int DaemonSink::webapiSettingsPutPatch( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getDaemonSinkSettings()->getTitle(); } - + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getDaemonSinkSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getDaemonSinkSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getDaemonSinkSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getDaemonSinkSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getDaemonSinkSettings()->getReverseApiChannelIndex(); + } MsgConfigureDaemonSink *msg = MsgConfigureDaemonSink::create(settings, force); m_inputMessageQueue.push(msg); @@ -445,4 +490,82 @@ void DaemonSink::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& re response.getDaemonSinkSettings()->setTitle(new QString(settings.m_title)); } + response.getDaemonSinkSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getDaemonSinkSettings()->getReverseApiAddress()) { + *response.getDaemonSinkSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getDaemonSinkSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getDaemonSinkSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getDaemonSinkSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getDaemonSinkSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); +} + +void DaemonSink::webapiReverseSendSettings(QList& channelSettingsKeys, const DaemonSinkSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("DaemonSink")); + swgChannelSettings->setDaemonSinkSettings(new SWGSDRangel::SWGDaemonSinkSettings()); + SWGSDRangel::SWGDaemonSinkSettings *swgDaemonSinkSettings = swgChannelSettings->getDaemonSinkSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("nbFECBlocks") || force) { + swgDaemonSinkSettings->setNbFecBlocks(settings.m_nbFECBlocks); + } + if (channelSettingsKeys.contains("txDelay") || force) + { + swgDaemonSinkSettings->setTxDelay(settings.m_txDelay); + } + if (channelSettingsKeys.contains("dataAddress") || force) { + swgDaemonSinkSettings->setDataAddress(new QString(settings.m_dataAddress)); + } + if (channelSettingsKeys.contains("dataPort") || force) { + swgDaemonSinkSettings->setDataPort(settings.m_dataPort); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgDaemonSinkSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgDaemonSinkSettings->setTitle(new QString(settings.m_title)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void DaemonSink::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "DaemonSink::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("DaemonSink::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); } diff --git a/plugins/channelrx/daemonsink/daemonsink.h b/plugins/channelrx/daemonsink/daemonsink.h index b50986c48..82aa71230 100644 --- a/plugins/channelrx/daemonsink/daemonsink.h +++ b/plugins/channelrx/daemonsink/daemonsink.h @@ -25,12 +25,15 @@ #include #include +#include #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "channel/sdrdaemondatablock.h" #include "daemonsinksettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -148,9 +151,15 @@ private: int m_txDelay; QString m_dataAddress; uint16_t m_dataPort; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; void applySettings(const DaemonSinkSettings& settings, bool force = false); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DaemonSinkSettings& settings); + void webapiReverseSendSettings(QList& channelSettingsKeys, const DaemonSinkSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* INCLUDE_DAEMONSINK_H_ */ diff --git a/plugins/channelrx/daemonsink/daemonsinkgui.cpp b/plugins/channelrx/daemonsink/daemonsinkgui.cpp index a77bc9a64..69d2866e0 100644 --- a/plugins/channelrx/daemonsink/daemonsinkgui.cpp +++ b/plugins/channelrx/daemonsink/daemonsinkgui.cpp @@ -113,6 +113,7 @@ DaemonSinkGUI::DaemonSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_daemonSink = (DaemonSink*) channelrx; m_daemonSink->setMessageQueueToGUI(getInputMessageQueue()); @@ -217,11 +218,22 @@ void DaemonSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) void DaemonSinkGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/channelrx/daemonsink/daemonsinkgui.h b/plugins/channelrx/daemonsink/daemonsinkgui.h index 139f3281c..477d6991b 100644 --- a/plugins/channelrx/daemonsink/daemonsinkgui.h +++ b/plugins/channelrx/daemonsink/daemonsinkgui.h @@ -17,6 +17,8 @@ #ifndef PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKGUI_H_ #define PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKGUI_H_ +#include + #include #include diff --git a/plugins/channelrx/daemonsink/daemonsinkplugin.cpp b/plugins/channelrx/daemonsink/daemonsinkplugin.cpp index e65dfae0d..aea67d0cf 100644 --- a/plugins/channelrx/daemonsink/daemonsinkplugin.cpp +++ b/plugins/channelrx/daemonsink/daemonsinkplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor DaemonSinkPlugin::m_pluginDescriptor = { QString("Daemon channel Sink"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/daemonsink/daemonsinksettings.cpp b/plugins/channelrx/daemonsink/daemonsinksettings.cpp index 1579bc80f..54fb2208c 100644 --- a/plugins/channelrx/daemonsink/daemonsinksettings.cpp +++ b/plugins/channelrx/daemonsink/daemonsinksettings.cpp @@ -40,6 +40,11 @@ void DaemonSinkSettings::resetToDefaults() m_rgbColor = QColor(140, 4, 4).rgb(); m_title = "Daemon sink"; m_channelMarker = nullptr; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray DaemonSinkSettings::serialize() const @@ -51,6 +56,11 @@ QByteArray DaemonSinkSettings::serialize() const s.writeU32(4, m_dataPort); s.writeU32(5, m_rgbColor); s.writeString(6, m_title); + s.writeBool(7, m_useReverseAPI); + s.writeString(8, m_reverseAPIAddress); + s.writeU32(9, m_reverseAPIPort); + s.writeU32(10, m_reverseAPIDeviceIndex); + s.writeU32(11, m_reverseAPIChannelIndex); return s.final(); } @@ -90,6 +100,20 @@ bool DaemonSinkSettings::deserialize(const QByteArray& data) d.readU32(5, &m_rgbColor, QColor(0, 255, 255).rgb()); d.readString(6, &m_title, "Daemon sink"); + d.readBool(7, &m_useReverseAPI, false); + d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(9, &tmp, 0); + + if ((tmp > 1023) && (tmp < 65535)) { + m_reverseAPIPort = tmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(10, &tmp, 0); + m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp; + d.readU32(11, &tmp, 0); + m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp; return true; } diff --git a/plugins/channelrx/daemonsink/daemonsinksettings.h b/plugins/channelrx/daemonsink/daemonsinksettings.h index 49584c57c..304446384 100644 --- a/plugins/channelrx/daemonsink/daemonsinksettings.h +++ b/plugins/channelrx/daemonsink/daemonsinksettings.h @@ -35,6 +35,11 @@ struct DaemonSinkSettings uint16_t m_dataPort; quint32 m_rgbColor; QString m_title; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index f0f48d562..c3c733777 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2015 Edouard Griffiths, F4EXB. // +// Copyright (C) 2015-2018 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 // @@ -18,6 +18,9 @@ #include #include +#include +#include +#include #include #include @@ -85,10 +88,15 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) : m_pllFilt.create(101, m_audioSampleRate, 200.0); m_pll.computeCoefficients(0.05, 0.707, 1000); m_syncAMBuffIndex = 0; + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } AMDemod::~AMDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSink(m_threadedChannelizer); @@ -427,8 +435,15 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) << " m_audioDeviceName: " << settings.m_audioDeviceName << " m_pll: " << settings.m_pll << " m_syncAMOperation: " << (int) settings.m_syncAMOperation + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIAddress: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex << " force: " << force; + QList reverseAPIKeys; + if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || (m_settings.m_bandpassEnable != settings.m_bandpassEnable) || force) { @@ -439,11 +454,19 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_rfBandwidth / 2.0f); DSBFilter->create_dsb_filter((2.0f * settings.m_rfBandwidth) / (float) m_audioSampleRate); m_settingsMutex.unlock(); + + if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if ((m_settings.m_bandpassEnable != settings.m_bandpassEnable) || force) { + reverseAPIKeys.append("bandpassEnable"); + } } if ((m_settings.m_squelch != settings.m_squelch) || force) { m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch); + reverseAPIKeys.append("squelch"); } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) @@ -457,6 +480,8 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) if (m_audioSampleRate != audioSampleRate) { applyAudioSampleRate(audioSampleRate); } + + reverseAPIKeys.append("audioDeviceName"); } if ((m_settings.m_pll != settings.m_pll) || force) @@ -470,10 +495,38 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force) { m_volumeAGC.resizeNew(m_audioSampleRate/10, 0.003); } + + reverseAPIKeys.append("pll"); + reverseAPIKeys.append("syncAMOperation"); } - if ((m_settings.m_syncAMOperation != settings.m_syncAMOperation) || force) { + if ((m_settings.m_syncAMOperation != settings.m_syncAMOperation) || force) + { m_syncAMBuffIndex = 0; + reverseAPIKeys.append("pll"); + reverseAPIKeys.append("syncAMOperation"); + } + + if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + + if ((m_settings.m_audioMute != settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + + if ((m_settings.m_volume != settings.m_volume) || force) { + reverseAPIKeys.append("volume"); + } + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); } m_settings = settings; @@ -560,7 +613,23 @@ int AMDemod::webapiSettingsPutPatch( qint32 syncAMOperationCode = response.getAmDemodSettings()->getSyncAmOperation(); settings.m_syncAMOperation = syncAMOperationCode < 0 ? AMDemodSettings::SyncAMDSB : syncAMOperationCode > 2 ? - AMDemodSettings::SyncAMDSB : (AMDemodSettings::SyncAMOperation) syncAMOperationCode; + AMDemodSettings::SyncAMLSB : (AMDemodSettings::SyncAMOperation) syncAMOperationCode; + } + + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAmDemodSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAmDemodSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAmDemodSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getAmDemodSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getAmDemodSettings()->getReverseApiChannelIndex(); } if (frequencyOffsetChanged) @@ -620,6 +689,17 @@ void AMDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respo response.getAmDemodSettings()->setPll(settings.m_pll ? 1 : 0); response.getAmDemodSettings()->setSyncAmOperation((int) m_settings.m_syncAMOperation); + response.getAmDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAmDemodSettings()->getReverseApiAddress()) { + *response.getAmDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAmDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAmDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAmDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getAmDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void AMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -634,3 +714,83 @@ void AMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getAmDemodReport()->setChannelSampleRate(m_inputSampleRate); } +void AMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const AMDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("AMDemod")); + swgChannelSettings->setAmDemodSettings(new SWGSDRangel::SWGAMDemodSettings()); + SWGSDRangel::SWGAMDemodSettings *swgAMDemodSettings = swgChannelSettings->getAmDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("audioMute") || force) { + swgAMDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgAMDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgAMDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgAMDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgAMDemodSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("title") || force) { + swgAMDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("volume") || force) { + swgAMDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("bandpassEnable") || force) { + swgAMDemodSettings->setBandpassEnable(settings.m_bandpassEnable ? 1 : 0); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgAMDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("pll") || force) { + swgAMDemodSettings->setPll(settings.m_pll); + } + if (channelSettingsKeys.contains("syncAMOperation") || force) { + swgAMDemodSettings->setSyncAmOperation((int) settings.m_syncAMOperation); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void AMDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AMDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AMDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodam/amdemod.h b/plugins/channelrx/demodam/amdemod.h index f08ed6451..d32536b13 100644 --- a/plugins/channelrx/demodam/amdemod.h +++ b/plugins/channelrx/demodam/amdemod.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2015 Edouard Griffiths, F4EXB. // +// Copyright (C) 2015-2018 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 // @@ -17,9 +17,11 @@ #ifndef INCLUDE_AMDEMOD_H #define INCLUDE_AMDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/nco.h" @@ -35,6 +37,8 @@ #include "amdemodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class DownChannelizer; class ThreadedBasebandSampleSink; @@ -204,6 +208,9 @@ private: static const int m_udpBlockSize; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + QMutex m_settingsMutex; void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); @@ -211,8 +218,13 @@ private: void applyAudioSampleRate(int sampleRate); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const AMDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const AMDemodSettings& settings, bool force); void processOneSample(Complex &ci); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + }; #endif // INCLUDE_AMDEMOD_H diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 2bc57c488..6374a859c 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "amdemodgui.h" #include "amdemodssbdialog.h" @@ -94,7 +95,6 @@ bool AMDemodGUI::deserialize(const QByteArray& data) bool AMDemodGUI::handleMessage(const Message& message) { - (void) message; if (AMDemod::MsgConfigureAMDemod::match(message)) { qDebug("AMDemodGUI::handleMessage: AMDemod::MsgConfigureAMDemod"); @@ -206,12 +206,22 @@ void AMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) void AMDemodGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -241,10 +251,10 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); CRightClickEnabler *samSidebandRightClickEnabler = new CRightClickEnabler(ui->ssb); - connect(samSidebandRightClickEnabler, SIGNAL(rightClick()), this, SLOT(samSSBSelect())); + connect(samSidebandRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(samSSBSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -335,17 +345,32 @@ void AMDemodGUI::displaySettings() ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable); ui->pll->setChecked(m_settings.m_pll); - if (m_settings.m_pll) { - if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMLSB) { + qDebug() << "AMDemodGUI::displaySettings:" + << " m_pll: " << m_settings.m_pll + << " m_syncAMOperation: " << m_settings.m_syncAMOperation; + + if (m_settings.m_pll) + { + if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMLSB) + { m_samUSB = false; + ui->ssb->setChecked(true); ui->ssb->setIcon(m_iconDSBLSB); - } else { + } + else if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMUSB) + { m_samUSB = true; + ui->ssb->setChecked(true); ui->ssb->setIcon(m_iconDSBUSB); } + else + { + ui->ssb->setChecked(false); + } } else { + ui->ssb->setChecked(false); ui->ssb->setIcon(m_iconDSBUSB); } diff --git a/plugins/channelrx/demodam/amdemodplugin.cpp b/plugins/channelrx/demodam/amdemodplugin.cpp index 28a4b640c..fb36f8fc9 100644 --- a/plugins/channelrx/demodam/amdemodplugin.cpp +++ b/plugins/channelrx/demodam/amdemodplugin.cpp @@ -9,7 +9,7 @@ const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = { QString("AM Demodulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodam/amdemodsettings.cpp b/plugins/channelrx/demodam/amdemodsettings.cpp index cf3c36ae5..14d9eeb1f 100644 --- a/plugins/channelrx/demodam/amdemodsettings.cpp +++ b/plugins/channelrx/demodam/amdemodsettings.cpp @@ -40,6 +40,11 @@ void AMDemodSettings::resetToDefaults() m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; m_pll = false; m_syncAMOperation = SyncAMDSB; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray AMDemodSettings::serialize() const @@ -60,6 +65,11 @@ QByteArray AMDemodSettings::serialize() const s.writeString(11, m_audioDeviceName); s.writeBool(12, m_pll); s.writeS32(13, (int) m_syncAMOperation); + s.writeBool(14, m_useReverseAPI); + s.writeString(15, m_reverseAPIAddress); + s.writeU32(16, m_reverseAPIPort); + s.writeU32(17, m_reverseAPIDeviceIndex); + s.writeU32(18, m_reverseAPIChannelIndex); return s.final(); } @@ -78,6 +88,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; QString strtmp; d.readS32(1, &m_inputFrequencyOffset, 0); @@ -100,6 +111,20 @@ bool AMDemodSettings::deserialize(const QByteArray& data) d.readBool(12, &m_pll, false); d.readS32(13, &tmp, 0); m_syncAMOperation = tmp < 0 ? SyncAMDSB : tmp > 2 ? SyncAMDSB : (SyncAMOperation) tmp; + d.readBool(14, &m_useReverseAPI, false); + d.readString(15, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(16, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(17, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(18, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/channelrx/demodam/amdemodsettings.h b/plugins/channelrx/demodam/amdemodsettings.h index 3aaebd230..1e551f4d6 100644 --- a/plugins/channelrx/demodam/amdemodsettings.h +++ b/plugins/channelrx/demodam/amdemodsettings.h @@ -42,6 +42,11 @@ struct AMDemodSettings QString m_audioDeviceName; bool m_pll; SyncAMOperation m_syncAMOperation; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; AMDemodSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodatv/CMakeLists.txt b/plugins/channelrx/demodatv/CMakeLists.txt index 30e835539..5e03a69f4 100644 --- a/plugins/channelrx/demodatv/CMakeLists.txt +++ b/plugins/channelrx/demodatv/CMakeLists.txt @@ -23,6 +23,7 @@ set(atv_FORMS include_directories( . ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} ) #include(${QT_USE_FILE}) diff --git a/plugins/channelrx/demodatv/demodatv.pro b/plugins/channelrx/demodatv/demodatv.pro index 8ac0bc4ad..a7aebe2ae 100644 --- a/plugins/channelrx/demodatv/demodatv.pro +++ b/plugins/channelrx/demodatv/demodatv.pro @@ -26,7 +26,6 @@ CONFIG(Release):build_subdir = release CONFIG(Debug):build_subdir = debug CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../../../boost_1_69_0" diff --git a/plugins/channelrx/demodbfm/CMakeLists.txt b/plugins/channelrx/demodbfm/CMakeLists.txt index 71721706c..eb6a15bff 100644 --- a/plugins/channelrx/demodbfm/CMakeLists.txt +++ b/plugins/channelrx/demodbfm/CMakeLists.txt @@ -36,6 +36,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${Boost_INCLUDE_DIRS} ) #include(${QT_USE_FILE}) diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 1ac07377e..3f79ff6a8 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -15,12 +15,16 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include "boost/format.hpp" #include #include +#include +#include +#include +#include +#include + #include "SWGChannelSettings.h" #include "SWGBFMDemodSettings.h" #include "SWGChannelReport.h" @@ -100,10 +104,16 @@ BFMDemod::BFMDemod(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } BFMDemod::~BFMDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); @@ -449,15 +459,38 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) << " m_showPilot: " << settings.m_showPilot << " m_rdsActive: " << settings.m_rdsActive << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_useReverseAPI: " << settings.m_useReverseAPI << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_volume != m_settings.m_volume) || force) { + reverseAPIKeys.append("volume"); + } + if ((settings.m_audioStereo != m_settings.m_audioStereo) || force) { + reverseAPIKeys.append("audioStereo"); + } + if ((settings.m_lsbStereo != m_settings.m_lsbStereo) || force) { + reverseAPIKeys.append("lsbStereo"); + } + if ((settings.m_showPilot != m_settings.m_showPilot) || force) { + reverseAPIKeys.append("showPilot"); + } + if ((settings.m_rdsActive != m_settings.m_rdsActive) || force) { + reverseAPIKeys.append("rdsActive"); + } + if ((settings.m_audioStereo && (settings.m_audioStereo != m_settings.m_audioStereo)) || force) { m_pilotPLL.configure(19000.0/m_inputSampleRate, 50.0/m_inputSampleRate, 0.01); } - if((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) + if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_interpolator.create(16, m_inputSampleRate, settings.m_afBandwidth); @@ -472,11 +505,14 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) m_interpolatorRDSDistanceRemain = (Real) m_inputSampleRate / 250000.0; m_interpolatorRDSDistance = (Real) m_inputSampleRate / 250000.0; + m_lowpass.create(21, m_audioSampleRate, settings.m_afBandwidth); + m_settingsMutex.unlock(); } - if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_inputSampleRate; Real hiCut = (settings.m_rfBandwidth / 2.0) / m_inputSampleRate; @@ -485,22 +521,15 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) m_settingsMutex.unlock(); } - if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) - { - m_settingsMutex.lock(); - qDebug() << "BFMDemod::handleMessage: m_lowpass.create"; - m_lowpass.create(21, m_audioSampleRate, settings.m_afBandwidth); - m_settingsMutex.unlock(); - } - if ((settings.m_squelch != m_settings.m_squelch) || force) { - qDebug() << "BFMDemod::handleMessage: set m_squelchLevel"; + reverseAPIKeys.append("squelch"); m_squelchLevel = std::pow(10.0, settings.m_squelch / 10.0); } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); @@ -512,6 +541,16 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -596,6 +635,21 @@ int BFMDemod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("audioDeviceName")) { settings.m_audioDeviceName = *response.getBfmDemodSettings()->getAudioDeviceName(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getBfmDemodSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getBfmDemodSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getBfmDemodSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getBfmDemodSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getBfmDemodSettings()->getReverseApiChannelIndex(); + } if (frequencyOffsetChanged) { @@ -654,6 +708,18 @@ void BFMDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp } else { response.getBfmDemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); } + + response.getBfmDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getBfmDemodSettings()->getReverseApiAddress()) { + *response.getBfmDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getBfmDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getBfmDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getBfmDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getBfmDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void BFMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -707,3 +773,87 @@ void BFMDemod::webapiFormatRDSReport(SWGSDRangel::SWGRDSReport *report) } } } + +void BFMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const BFMDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("BFMDemod")); + swgChannelSettings->setBfmDemodSettings(new SWGSDRangel::SWGBFMDemodSettings()); + SWGSDRangel::SWGBFMDemodSettings *swgBFMDemodSettings = swgChannelSettings->getBfmDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgBFMDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgBFMDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgBFMDemodSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("volume") || force) { + swgBFMDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgBFMDemodSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("audioStereo") || force) { + swgBFMDemodSettings->setAudioStereo(settings.m_audioStereo ? 1 : 0); + } + if (channelSettingsKeys.contains("lsbStereo") || force) { + swgBFMDemodSettings->setLsbStereo(settings.m_lsbStereo ? 1 : 0); + } + if (channelSettingsKeys.contains("showPilot") || force) { + swgBFMDemodSettings->setShowPilot(settings.m_showPilot ? 1 : 0); + } + if (channelSettingsKeys.contains("rdsActive") || force) { + swgBFMDemodSettings->setRdsActive(settings.m_rdsActive ? 1 : 0); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgBFMDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgBFMDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgBFMDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void BFMDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "BFMDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("BFMDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodbfm/bfmdemod.h b/plugins/channelrx/demodbfm/bfmdemod.h index 700deb6cf..61868beed 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.h +++ b/plugins/channelrx/demodbfm/bfmdemod.h @@ -18,9 +18,11 @@ #ifndef INCLUDE_BFMDEMOD_H #define INCLUDE_BFMDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/nco.h" @@ -39,6 +41,8 @@ #include "rdsdemod.h" #include "bfmdemodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -48,6 +52,7 @@ namespace SWGSDRangel { } class BFMDemod : public BasebandSampleSink, public ChannelSinkAPI { + Q_OBJECT public: class MsgConfigureBFMDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -117,7 +122,7 @@ public: BFMDemod(DeviceSourceAPI *deviceAPI); virtual ~BFMDemod(); - virtual void destroy() { delete this; } + virtual void destroy() { delete this; } void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } int getSampleRate() const { return m_inputSampleRate; } @@ -269,6 +274,9 @@ private: static const int m_udpBlockSize; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + void applyAudioSampleRate(int sampleRate); void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const BFMDemodSettings& settings, bool force = false); @@ -276,6 +284,10 @@ private: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const BFMDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiFormatRDSReport(SWGSDRangel::SWGRDSReport *report); + void webapiReverseSendSettings(QList& channelSettingsKeys, const BFMDemodSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_BFMDEMOD_H diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index a2fded696..0e8aa2012 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -319,12 +319,23 @@ void BFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -348,7 +359,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioStereo); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp index 9bb54d736..96fa37cb0 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp @@ -48,6 +48,11 @@ void BFMDemodSettings::resetToDefaults() m_rgbColor = QColor(80, 120, 228).rgb(); m_title = "Broadcast FM Demod"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray BFMDemodSettings::serialize() const @@ -73,6 +78,11 @@ QByteArray BFMDemodSettings::serialize() const s.writeString(12, m_title); s.writeString(13, m_audioDeviceName); + s.writeBool(14, m_useReverseAPI); + s.writeString(15, m_reverseAPIAddress); + s.writeU32(16, m_reverseAPIPort); + s.writeU32(17, m_reverseAPIDeviceIndex); + s.writeU32(18, m_reverseAPIChannelIndex); return s.final(); } @@ -91,6 +101,7 @@ bool BFMDemodSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; QString strtmp; d.readS32(1, &tmp, 0); @@ -122,6 +133,20 @@ bool BFMDemodSettings::deserialize(const QByteArray& data) d.readString(12, &m_title, "Broadcast FM Demod"); d.readString(13, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(14, &m_useReverseAPI, false); + d.readString(15, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(16, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(17, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(18, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.h b/plugins/channelrx/demodbfm/bfmdemodsettings.h index 5400baff3..3dbbc6f44 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.h +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.h @@ -35,6 +35,11 @@ struct BFMDemodSettings quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodbfm/bfmplugin.cpp b/plugins/channelrx/demodbfm/bfmplugin.cpp index b6af176e5..b74bab9e0 100644 --- a/plugins/channelrx/demodbfm/bfmplugin.cpp +++ b/plugins/channelrx/demodbfm/bfmplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor BFMPlugin::m_pluginDescriptor = { QString("Broadcast FM Demodulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodbfm/demodbfm.pro b/plugins/channelrx/demodbfm/demodbfm.pro index 2821bf0be..24d7bee46 100644 --- a/plugins/channelrx/demodbfm/demodbfm.pro +++ b/plugins/channelrx/demodbfm/demodbfm.pro @@ -25,7 +25,6 @@ INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client CONFIG(ANDROID):INCLUDEPATH += /opt/softs/boost_1_60_0 CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../../../boost_1_69_0" diff --git a/plugins/channelrx/demoddsd/demoddsd.pro b/plugins/channelrx/demoddsd/demoddsd.pro index b78c79546..4060495a9 100644 --- a/plugins/channelrx/demoddsd/demoddsd.pro +++ b/plugins/channelrx/demoddsd/demoddsd.pro @@ -18,17 +18,14 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBDSDCCSRC = "C:\softs\dsdcc" -CONFIG(MINGW64):LIBDSDCCSRC = "C:\softs\dsdcc" CONFIG(MSVC):LIBDSDCCSRC = "C:\softs\dsdcc" CONFIG(macx):LIBDSDCCSRC = "../../../../deps/dsdcc" CONFIG(MINGW32):LIBMBELIBSRC = "C:\softs\mbelib" -CONFIG(MINGW64):LIBMBELIBSRC = "C:\softs\mbelib" CONFIG(MSVC):LIBMBELIBSRC = "C:\softs\mbelib" CONFIG(macx):LIBMBELIBSRC = "../../../../deps/mbelib" CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../../../boost_1_69_0" diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 0b63c3c35..86c3f0132 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -16,12 +16,16 @@ /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include #include #include +#include +#include +#include +#include +#include + #include "SWGChannelSettings.h" #include "SWGDSDDemodSettings.h" #include "SWGChannelReport.h" @@ -92,10 +96,15 @@ DSDDemod::DSDDemod(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } DSDDemod::~DSDDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; delete[] m_sampleBuffer; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo1); DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo2); @@ -479,10 +488,41 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) << " m_pllLock: " << settings.m_pllLock << " m_highPassFilter: "<< settings.m_highPassFilter << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_traceLengthMutliplier: " << settings.m_traceLengthMutliplier + << " m_traceStroke: " << settings.m_traceStroke + << " m_traceDecay: " << settings.m_traceDecay << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_demodGain != m_settings.m_demodGain) || force) { + reverseAPIKeys.append("demodGain"); + } + if ((settings.m_audioMute != m_settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + if ((settings.m_syncOrConstellation != m_settings.m_syncOrConstellation) || force) { + reverseAPIKeys.append("syncOrConstellation"); + } + if ((settings.m_slot1On != m_settings.m_slot1On) || force) { + reverseAPIKeys.append("slot1On"); + } + if ((settings.m_slot2On != m_settings.m_slot2On) || force) { + reverseAPIKeys.append("slot2On"); + } + if ((settings.m_demodGain != m_settings.m_demodGain) || force) { + reverseAPIKeys.append("demodGain"); + } + if ((settings.m_traceLengthMutliplier != m_settings.m_traceLengthMutliplier) || force) { + reverseAPIKeys.append("traceLengthMutliplier"); + } + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); m_interpolator.create(16, m_inputSampleRate, (settings.m_rfBandwidth) / 2.2); m_interpolatorDistanceRemain = 0; @@ -493,53 +533,63 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); m_phaseDiscri.setFMScaling(48000.0f / (2.0f*settings.m_fmDeviation)); } if ((settings.m_squelchGate != m_settings.m_squelchGate) || force) { + reverseAPIKeys.append("squelchGate"); m_squelchGate = 480 * settings.m_squelchGate; // gate is given in 10s of ms at 48000 Hz audio sample rate m_squelchCount = 0; // reset squelch open counter } if ((settings.m_squelch != m_settings.m_squelch) || force) { + reverseAPIKeys.append("squelch"); // input is a value in dB m_squelchLevel = std::pow(10.0, settings.m_squelch / 10.0); } if ((settings.m_volume != m_settings.m_volume) || force) { + reverseAPIKeys.append("volume"); m_dsdDecoder.setAudioGain(settings.m_volume); } if ((settings.m_baudRate != m_settings.m_baudRate) || force) { + reverseAPIKeys.append("baudRate"); m_dsdDecoder.setBaudRate(settings.m_baudRate); } if ((settings.m_enableCosineFiltering != m_settings.m_enableCosineFiltering) || force) { + reverseAPIKeys.append("enableCosineFiltering"); m_dsdDecoder.enableCosineFiltering(settings.m_enableCosineFiltering); } if ((settings.m_tdmaStereo != m_settings.m_tdmaStereo) || force) { + reverseAPIKeys.append("tdmaStereo"); m_dsdDecoder.setTDMAStereo(settings.m_tdmaStereo); } if ((settings.m_pllLock != m_settings.m_pllLock) || force) { + reverseAPIKeys.append("pllLock"); m_dsdDecoder.setSymbolPLLLock(settings.m_pllLock); } if ((settings.m_highPassFilter != m_settings.m_highPassFilter) || force) { + reverseAPIKeys.append("highPassFilter"); m_dsdDecoder.useHPMbelib(settings.m_highPassFilter); } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); @@ -552,6 +602,16 @@ void DSDDemod::applySettings(const DSDDemodSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -855,6 +915,21 @@ int DSDDemod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("traceDecay")) { settings.m_traceDecay = response.getDsdDemodSettings()->getTraceDecay(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getDsdDemodSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getDsdDemodSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getDsdDemodSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getDsdDemodSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getDsdDemodSettings()->getReverseApiChannelIndex(); + } if (frequencyOffsetChanged) { @@ -924,6 +999,18 @@ void DSDDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp response.getDsdDemodSettings()->setTraceLengthMutliplier(settings.m_traceLengthMutliplier); response.getDsdDemodSettings()->setTraceStroke(settings.m_traceStroke); response.getDsdDemodSettings()->setTraceDecay(settings.m_traceDecay); + response.getDsdDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getDsdDemodSettings()->getReverseApiAddress()) { + *response.getDsdDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getDsdDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getDsdDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getDsdDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getDsdDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + } void DSDDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -946,3 +1033,117 @@ void DSDDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response response.getDsdDemodReport()->setSyncRate(getDecoder().getSymbolSyncQuality()); response.getDsdDemodReport()->setStatusText(new QString(updateAndGetStatusText())); } + +void DSDDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const DSDDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("DSDDemod")); + swgChannelSettings->setDsdDemodSettings(new SWGSDRangel::SWGDSDDemodSettings()); + SWGSDRangel::SWGDSDDemodSettings *swgDSDDemodSettings = swgChannelSettings->getDsdDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgDSDDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgDSDDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgDSDDemodSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("demodGain") || force) { + swgDSDDemodSettings->setDemodGain(settings.m_demodGain); + } + if (channelSettingsKeys.contains("volume") || force) { + swgDSDDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("baudRate") || force) { + swgDSDDemodSettings->setBaudRate(settings.m_baudRate); + } + if (channelSettingsKeys.contains("squelchGate") || force) { + swgDSDDemodSettings->setSquelchGate(settings.m_squelchGate); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgDSDDemodSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgDSDDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("enableCosineFiltering") || force) { + swgDSDDemodSettings->setEnableCosineFiltering(settings.m_enableCosineFiltering ? 1 : 0); + } + if (channelSettingsKeys.contains("syncOrConstellation") || force) { + swgDSDDemodSettings->setSyncOrConstellation(settings.m_syncOrConstellation ? 1 : 0); + } + if (channelSettingsKeys.contains("slot1On") || force) { + swgDSDDemodSettings->setSlot1On(settings.m_slot1On ? 1 : 0); + } + if (channelSettingsKeys.contains("slot2On") || force) { + swgDSDDemodSettings->setSlot2On(settings.m_slot2On ? 1 : 0); + } + if (channelSettingsKeys.contains("tdmaStereo") || force) { + swgDSDDemodSettings->setTdmaStereo(settings.m_tdmaStereo ? 1 : 0); + } + if (channelSettingsKeys.contains("pllLock") || force) { + swgDSDDemodSettings->setPllLock(settings.m_pllLock ? 1 : 0); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgDSDDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgDSDDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgDSDDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("highPassFilter") || force) { + swgDSDDemodSettings->setHighPassFilter(settings.m_highPassFilter ? 1 : 0); + } + if (channelSettingsKeys.contains("traceLengthMutliplier") || force) { + swgDSDDemodSettings->setTraceLengthMutliplier(settings.m_traceLengthMutliplier); + } + if (channelSettingsKeys.contains("traceStroke") || force) { + swgDSDDemodSettings->setTraceStroke(settings.m_traceStroke); + } + if (channelSettingsKeys.contains("traceDecay") || force) { + swgDSDDemodSettings->setTraceDecay(settings.m_traceDecay); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void DSDDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "DSDDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("DSDDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demoddsd/dsddemod.h b/plugins/channelrx/demoddsd/dsddemod.h index 207a05f60..d0a4f9285 100644 --- a/plugins/channelrx/demoddsd/dsddemod.h +++ b/plugins/channelrx/demoddsd/dsddemod.h @@ -18,9 +18,11 @@ #ifndef INCLUDE_DSDDEMOD_H #define INCLUDE_DSDDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/phasediscri.h" @@ -38,11 +40,14 @@ #include "dsddemodsettings.h" #include "dsddecoder.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; class DSDDemod : public BasebandSampleSink, public ChannelSinkAPI { + Q_OBJECT public: class MsgConfigureDSDDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -244,6 +249,9 @@ private: SignalFormat m_signalFormat; //!< Used to keep formatting during successive calls for the same standard type PhaseDiscriminators m_phaseDiscri; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + QMutex m_settingsMutex; static const int m_udpBlockSize; @@ -255,6 +263,11 @@ private: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DSDDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const DSDDemodSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + }; #endif // INCLUDE_DSDDEMOD_H diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index b8dbf0429..82073d507 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -274,12 +274,23 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) { //qDebug("DSDDemodGUI::onMenuDialogCalled: x: %d y: %d", p.x(), p.y()); BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -319,7 +330,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); m_scopeVisXY = new ScopeVisXY(ui->screenTV); m_scopeVisXY->setScale(2.0); diff --git a/plugins/channelrx/demoddsd/dsddemodplugin.cpp b/plugins/channelrx/demoddsd/dsddemodplugin.cpp index 4f8835841..f1e97b50f 100644 --- a/plugins/channelrx/demoddsd/dsddemodplugin.cpp +++ b/plugins/channelrx/demoddsd/dsddemodplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = { QString("DSD Demodulator"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.cpp b/plugins/channelrx/demoddsd/dsddemodsettings.cpp index d73e029dd..cc957dd6f 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsettings.cpp @@ -52,6 +52,11 @@ void DSDDemodSettings::resetToDefaults() m_traceStroke = 100; m_traceDecay = 200; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray DSDDemodSettings::serialize() const @@ -87,6 +92,11 @@ QByteArray DSDDemodSettings::serialize() const s.writeS32(21, m_traceLengthMutliplier); s.writeS32(22, m_traceStroke); s.writeS32(23, m_traceDecay); + s.writeBool(24, m_useReverseAPI); + s.writeString(25, m_reverseAPIAddress); + s.writeU32(26, m_reverseAPIPort); + s.writeU32(27, m_reverseAPIDeviceIndex); + s.writeU32(28, m_reverseAPIChannelIndex); return s.final(); } @@ -106,6 +116,7 @@ bool DSDDemodSettings::deserialize(const QByteArray& data) QByteArray bytetmp; QString strtmp; qint32 tmp; + uint32_t utmp; if (m_channelMarker) { d.readBlob(17, &bytetmp); @@ -147,6 +158,20 @@ bool DSDDemodSettings::deserialize(const QByteArray& data) m_traceStroke = tmp < 0 ? 0 : tmp > 255 ? 255 : tmp; d.readS32(23, &tmp, 200); m_traceDecay = tmp < 0 ? 0 : tmp > 255 ? 255 : tmp; + d.readBool(24, &m_useReverseAPI, false); + d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(26, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(27, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(28, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.h b/plugins/channelrx/demoddsd/dsddemodsettings.h index e22bfa3f0..49b3bbd4d 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.h +++ b/plugins/channelrx/demoddsd/dsddemodsettings.h @@ -45,6 +45,11 @@ struct DSDDemodSettings int m_traceStroke; // [0..255] int m_traceDecay; // [0..255] QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_scopeGUI; diff --git a/plugins/channelrx/demodnfm/nfmdemod.cpp b/plugins/channelrx/demodnfm/nfmdemod.cpp index 69d75bba4..fd195356c 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.cpp +++ b/plugins/channelrx/demodnfm/nfmdemod.cpp @@ -15,11 +15,15 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include #include +#include +#include +#include +#include +#include + #include "SWGChannelSettings.h" #include "SWGNFMDemodSettings.h" #include "SWGChannelReport.h" @@ -94,10 +98,15 @@ NFMDemod::NFMDemod(DeviceSourceAPI *devieAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } NFMDemod::~NFMDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSink(m_threadedChannelizer); @@ -491,10 +500,33 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) << " m_ctcssOn: " << settings.m_ctcssOn << " m_audioMute: " << settings.m_audioMute << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_useReverseAPI: " << settings.m_useReverseAPI << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_volume != m_settings.m_volume) || force) { + reverseAPIKeys.append("volume"); + } + if ((settings.m_ctcssOn != m_settings.m_ctcssOn) || force) { + reverseAPIKeys.append("ctcssOn"); + } + if ((settings.m_audioMute != m_settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) { + reverseAPIKeys.append("rgbColor"); + } + if ((settings.m_title != m_settings.m_title) || force) { + reverseAPIKeys.append("title"); + } + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); m_interpolator.create(16, m_inputSampleRate, settings.m_rfBandwidth / 2.2); m_interpolatorDistanceRemain = 0; @@ -504,11 +536,13 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); m_phaseDiscri.setFMScaling((8.0f*m_audioSampleRate) / static_cast(settings.m_fmDeviation)); // integrate 4x factor } if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_afBandwidth); m_settingsMutex.unlock(); @@ -516,10 +550,18 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) if ((settings.m_squelchGate != m_settings.m_squelchGate) || force) { + reverseAPIKeys.append("squelchGate"); m_squelchGate = (m_audioSampleRate / 100) * settings.m_squelchGate; // gate is given in 10s of ms at 48000 Hz audio sample rate m_squelchCount = 0; // reset squelch open counter } + if ((settings.m_squelch != m_settings.m_squelch) || force) { + reverseAPIKeys.append("squelch"); + } + if ((settings.m_deltaSquelch != m_settings.m_deltaSquelch) || force) { + reverseAPIKeys.append("deltaSquelch"); + } + if ((settings.m_squelch != m_settings.m_squelch) || (settings.m_deltaSquelch != m_settings.m_deltaSquelch) || force) { @@ -540,11 +582,13 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) if ((settings.m_ctcssIndex != m_settings.m_ctcssIndex) || force) { + reverseAPIKeys.append("ctcssIndex"); setSelectedCtcssIndex(settings.m_ctcssIndex); } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); @@ -556,6 +600,16 @@ void NFMDemod::applySettings(const NFMDemodSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -722,3 +776,93 @@ void NFMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response response.getNfmDemodReport()->setAudioSampleRate(m_audioSampleRate); response.getNfmDemodReport()->setChannelSampleRate(m_inputSampleRate); } + +void NFMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const NFMDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("NFMDemod")); + swgChannelSettings->setNfmDemodSettings(new SWGSDRangel::SWGNFMDemodSettings()); + SWGSDRangel::SWGNFMDemodSettings *swgNFMDemodSettings = swgChannelSettings->getNfmDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgNFMDemodSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgNFMDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("ctcssIndex") || force) { + swgNFMDemodSettings->setCtcssIndex(settings.m_ctcssIndex); + } + if (channelSettingsKeys.contains("ctcssOn") || force) { + swgNFMDemodSettings->setCtcssOn(settings.m_ctcssOn ? 1 : 0); + } + if (channelSettingsKeys.contains("deltaSquelch") || force) { + swgNFMDemodSettings->setDeltaSquelch(settings.m_deltaSquelch ? 1 : 0); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgNFMDemodSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgNFMDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgNFMDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgNFMDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgNFMDemodSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("squelchGate") || force) { + swgNFMDemodSettings->setSquelchGate(settings.m_squelchGate); + } + if (channelSettingsKeys.contains("title") || force) { + swgNFMDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("volume") || force) { + swgNFMDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgNFMDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void NFMDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "NFMDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("NFMDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodnfm/nfmdemod.h b/plugins/channelrx/demodnfm/nfmdemod.h index ae1ec8c9b..cd24a8e07 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.h +++ b/plugins/channelrx/demodnfm/nfmdemod.h @@ -18,9 +18,11 @@ #ifndef INCLUDE_NFMDEMOD_H #define INCLUDE_NFMDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/phasediscri.h" @@ -38,11 +40,14 @@ #include "nfmdemodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; class NFMDemod : public BasebandSampleSink, public ChannelSinkAPI { + Q_OBJECT public: class MsgConfigureNFMDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -235,6 +240,9 @@ private: PhaseDiscriminators m_phaseDiscri; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_udpBlockSize; // void apply(bool force = false); @@ -243,6 +251,10 @@ private: void applyAudioSampleRate(int sampleRate); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const NFMDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const NFMDemodSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_NFMDEMOD_H diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index e3ac5afc5..2b739cc99 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -227,12 +227,22 @@ void NFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -263,7 +273,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); blockApplySettings(true); diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp index b23684402..a7f515598 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp @@ -52,6 +52,11 @@ void NFMDemodSettings::resetToDefaults() m_rgbColor = QColor(255, 0, 0).rgb(); m_title = "NFM Demodulator"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray NFMDemodSettings::serialize() const @@ -75,6 +80,11 @@ QByteArray NFMDemodSettings::serialize() const s.writeString(14, m_title); s.writeString(15, m_audioDeviceName); + s.writeBool(16, m_useReverseAPI); + s.writeString(17, m_reverseAPIAddress); + s.writeU32(18, m_reverseAPIPort); + s.writeU32(19, m_reverseAPIDeviceIndex); + s.writeU32(20, m_reverseAPIChannelIndex); return s.final(); } @@ -93,6 +103,7 @@ bool NFMDemodSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; if (m_channelMarker) { @@ -119,6 +130,20 @@ bool NFMDemodSettings::deserialize(const QByteArray& data) d.readBool(12, &m_deltaSquelch, false); d.readString(14, &m_title, "NFM Demodulator"); d.readString(15, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(16, &m_useReverseAPI, false); + d.readString(17, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(18, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(19, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(20, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.h b/plugins/channelrx/demodnfm/nfmdemodsettings.h index 285735674..3f4098a43 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.h +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.h @@ -41,6 +41,11 @@ struct NFMDemodSettings quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; diff --git a/plugins/channelrx/demodnfm/nfmplugin.cpp b/plugins/channelrx/demodnfm/nfmplugin.cpp index 53b5f8357..c2ac63ef0 100644 --- a/plugins/channelrx/demodnfm/nfmplugin.cpp +++ b/plugins/channelrx/demodnfm/nfmplugin.cpp @@ -9,7 +9,7 @@ const PluginDescriptor NFMPlugin::m_pluginDescriptor = { QString("NFM Demodulator"), - QString("4.2.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index 311cca888..6d14d3843 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -17,9 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////// +#include + #include #include -#include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGSSBDemodSettings.h" @@ -98,10 +102,15 @@ SSBDemod::SSBDemod(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SSBDemod::~SSBDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); @@ -455,6 +464,18 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) << " m_audioDeviceName: " << settings.m_audioDeviceName << " force: " << force; + QList reverseAPIKeys; + + if((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if((m_settings.m_lowCutoff != settings.m_lowCutoff) || force) { + reverseAPIKeys.append("lowCutoff"); + } + if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || (m_settings.m_lowCutoff != settings.m_lowCutoff) || force) { @@ -491,10 +512,24 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) if ((m_settings.m_volume != settings.m_volume) || force) { + reverseAPIKeys.append("volume"); m_volume = settings.m_volume; m_volume /= 4.0; // for 3276.8 } + if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || force) { + reverseAPIKeys.append("agcTimeLog2"); + } + if ((m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || force) { + reverseAPIKeys.append("agcPowerThreshold"); + } + if ((m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || force) { + reverseAPIKeys.append("agcThresholdGate"); + } + if ((m_settings.m_agcClamping != settings.m_agcClamping) || force) { + reverseAPIKeys.append("agcClamping"); + } + if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || (m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || (m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || @@ -542,6 +577,7 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); @@ -552,6 +588,25 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) } } + if ((m_settings.m_spanLog2 != settings.m_spanLog2) || force) { + reverseAPIKeys.append("spanLog2"); + } + if ((m_settings.m_audioBinaural != settings.m_audioBinaural) || force) { + reverseAPIKeys.append("audioBinaural"); + } + if ((m_settings.m_audioFlipChannels != settings.m_audioFlipChannels) || force) { + reverseAPIKeys.append("audioFlipChannels"); + } + if ((m_settings.m_dsb != settings.m_dsb) || force) { + reverseAPIKeys.append("dsb"); + } + if ((m_settings.m_audioMute != settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + if ((m_settings.m_agc != settings.m_agc) || force) { + reverseAPIKeys.append("agc"); + } + m_spanLog2 = settings.m_spanLog2; m_audioBinaual = settings.m_audioBinaural; m_audioFlipChannels = settings.m_audioFlipChannels; @@ -559,6 +614,16 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) m_audioMute = settings.m_audioMute; m_agcActive = settings.m_agc; + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -736,3 +801,101 @@ void SSBDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response response.getSsbDemodReport()->setChannelSampleRate(m_inputSampleRate); } +void SSBDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const SSBDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("SSBDemod")); + swgChannelSettings->setSsbDemodSettings(new SWGSDRangel::SWGSSBDemodSettings()); + SWGSDRangel::SWGSSBDemodSettings *swgSSBDemodSettings = swgChannelSettings->getSsbDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgSSBDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgSSBDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("lowCutoff") || force) { + swgSSBDemodSettings->setLowCutoff(settings.m_lowCutoff); + } + if (channelSettingsKeys.contains("volume") || force) { + swgSSBDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("spanLog2") || force) { + swgSSBDemodSettings->setSpanLog2(settings.m_spanLog2); + } + if (channelSettingsKeys.contains("audioBinaural") || force) { + swgSSBDemodSettings->setAudioBinaural(settings.m_audioBinaural ? 1 : 0); + } + if (channelSettingsKeys.contains("audioFlipChannels") || force) { + swgSSBDemodSettings->setAudioFlipChannels(settings.m_audioFlipChannels ? 1 : 0); + } + if (channelSettingsKeys.contains("dsb") || force) { + swgSSBDemodSettings->setDsb(settings.m_dsb ? 1 : 0); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgSSBDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("agc") || force) { + swgSSBDemodSettings->setAgc(settings.m_agc ? 1 : 0); + } + if (channelSettingsKeys.contains("agcClamping") || force) { + swgSSBDemodSettings->setAgcClamping(settings.m_agcClamping ? 1 : 0); + } + if (channelSettingsKeys.contains("agcTimeLog2") || force) { + swgSSBDemodSettings->setAgcTimeLog2(settings.m_agcTimeLog2); + } + if (channelSettingsKeys.contains("agcPowerThreshold") || force) { + swgSSBDemodSettings->setAgcPowerThreshold(settings.m_agcPowerThreshold); + } + if (channelSettingsKeys.contains("agcThresholdGate") || force) { + swgSSBDemodSettings->setAgcThresholdGate(settings.m_agcThresholdGate); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgSSBDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgSSBDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgSSBDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void SSBDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SSBDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SSBDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodssb/ssbdemod.h b/plugins/channelrx/demodssb/ssbdemod.h index 27521aa01..da30d0470 100644 --- a/plugins/channelrx/demodssb/ssbdemod.h +++ b/plugins/channelrx/demodssb/ssbdemod.h @@ -18,9 +18,11 @@ #ifndef INCLUDE_SSBDEMOD_H #define INCLUDE_SSBDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/ncof.h" @@ -36,11 +38,14 @@ #define ssbFftLen 1024 #define agcTarget 3276.8 // -10 dB amplitude => -20 dB power: center of normal signal +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; class SSBDemod : public BasebandSampleSink, public ChannelSinkAPI { + Q_OBJECT public: class MsgConfigureSSBDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -310,6 +315,9 @@ private: AudioFifo m_audioFifo; quint32 m_audioSampleRate; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + QMutex m_settingsMutex; void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); @@ -317,6 +325,10 @@ private: void applyAudioSampleRate(int sampleRate); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const SSBDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const SSBDemodSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_SSBDEMOD_H diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index d6b3e2e85..ca4702b0b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -238,12 +238,23 @@ void SSBDemodGUI::on_flipSidebands_clicked(bool checked) void SSBDemodGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -281,7 +292,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_ssbDemod->setSampleSink(m_spectrumVis); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.cpp b/plugins/channelrx/demodssb/ssbdemodsettings.cpp index fbc4b7689..1b7fe8149 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.cpp +++ b/plugins/channelrx/demodssb/ssbdemodsettings.cpp @@ -55,6 +55,11 @@ void SSBDemodSettings::resetToDefaults() m_rgbColor = QColor(0, 255, 0).rgb(); m_title = "SSB Demodulator"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray SSBDemodSettings::serialize() const @@ -81,6 +86,11 @@ QByteArray SSBDemodSettings::serialize() const s.writeBool(15, m_agcClamping); s.writeString(16, m_title); s.writeString(17, m_audioDeviceName); + s.writeBool(18, m_useReverseAPI); + s.writeString(19, m_reverseAPIAddress); + s.writeU32(20, m_reverseAPIPort); + s.writeU32(21, m_reverseAPIDeviceIndex); + s.writeU32(22, m_reverseAPIChannelIndex); return s.final(); } @@ -99,6 +109,7 @@ bool SSBDemodSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; QString strtmp; d.readS32(1, &m_inputFrequencyOffset, 0); @@ -126,6 +137,20 @@ bool SSBDemodSettings::deserialize(const QByteArray& data) d.readBool(15, &m_agcClamping, false); d.readString(16, &m_title, "SSB Demodulator"); d.readString(17, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(18, &m_useReverseAPI, false); + d.readString(19, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(20, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(21, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(22, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.h b/plugins/channelrx/demodssb/ssbdemodsettings.h index 7b6ac2035..33632fbc5 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.h +++ b/plugins/channelrx/demodssb/ssbdemodsettings.h @@ -40,6 +40,11 @@ struct SSBDemodSettings quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index 9a9ab8e9c..8a121fa1b 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -10,7 +10,7 @@ const PluginDescriptor SSBPlugin::m_pluginDescriptor = { QString("SSB Demodulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodwfm/wfmdemod.cpp b/plugins/channelrx/demodwfm/wfmdemod.cpp index b9dbd0547..8db9a5889 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.cpp +++ b/plugins/channelrx/demodwfm/wfmdemod.cpp @@ -16,11 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include #include +#include +#include +#include +#include +#include + #include "SWGChannelSettings.h" #include "SWGWFMDemodSettings.h" #include "SWGChannelReport.h" @@ -74,10 +78,15 @@ WFMDemod::WFMDemod(DeviceSourceAPI* deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } WFMDemod::~WFMDemod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); @@ -317,8 +326,39 @@ void WFMDemod::applySettings(const WFMDemodSettings& settings, bool force) << " m_volume: " << settings.m_volume << " m_squelch: " << settings.m_squelch << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_audioMute: " << settings.m_audioMute << " force: " << force; + QList reverseAPIKeys; + + if((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); + } + if((settings.m_volume != m_settings.m_volume) || force) { + reverseAPIKeys.append("volume"); + } + if((settings.m_squelch != m_settings.m_squelch) || force) { + reverseAPIKeys.append("squelch"); + } + if((settings.m_audioMute != m_settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + if((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); + } + if((settings.m_title != m_settings.m_title) || force) { + reverseAPIKeys.append("title"); + } + if((settings.m_rgbColor != m_settings.m_rgbColor) || force) { + reverseAPIKeys.append("rgbColor"); + } + if((settings.m_afBandwidth != m_settings.m_afBandwidth) || (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { @@ -356,6 +396,16 @@ void WFMDemod::applySettings(const WFMDemodSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -500,3 +550,77 @@ void WFMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response response.getWfmDemodReport()->setChannelSampleRate(m_inputSampleRate); } +void WFMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const WFMDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("WFMDemod")); + swgChannelSettings->setWfmDemodSettings(new SWGSDRangel::SWGWFMDemodSettings()); + SWGSDRangel::SWGWFMDemodSettings *swgWFMDemodSettings = swgChannelSettings->getWfmDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgWFMDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgWFMDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgWFMDemodSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("volume") || force) { + swgWFMDemodSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgWFMDemodSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgWFMDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgWFMDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgWFMDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgWFMDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void WFMDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "WFMDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("WFMDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodwfm/wfmdemod.h b/plugins/channelrx/demodwfm/wfmdemod.h index fa707a600..d1edd3674 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.h +++ b/plugins/channelrx/demodwfm/wfmdemod.h @@ -18,9 +18,11 @@ #ifndef INCLUDE_WFMDEMOD_H #define INCLUDE_WFMDEMOD_H -#include #include +#include +#include + #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" #include "dsp/nco.h" @@ -36,11 +38,14 @@ #define rfFilterFftLength 1024 +class QNetworkAccessManager; +class QNetworkReply; class ThreadedBasebandSampleSink; class DownChannelizer; class DeviceSourceAPI; class WFMDemod : public BasebandSampleSink, public ChannelSinkAPI { + Q_OBJECT public: class MsgConfigureWFMDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -203,6 +208,9 @@ private: PhaseDiscriminators m_phaseDiscri; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_udpBlockSize; void applyAudioSampleRate(int sampleRate); @@ -211,6 +219,10 @@ private: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const WFMDemodSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const WFMDemodSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_WFMDEMOD_H diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index b6b6cfc85..32437cc51 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -169,12 +169,23 @@ void WFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -202,7 +213,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp index ed50294d9..0b0625877 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp @@ -45,6 +45,11 @@ void WFMDemodSettings::resetToDefaults() m_rgbColor = QColor(0, 0, 255).rgb(); m_title = "WFM Demodulator"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray WFMDemodSettings::serialize() const @@ -63,6 +68,11 @@ QByteArray WFMDemodSettings::serialize() const s.writeBlob(11, m_channelMarker->serialize()); } + s.writeBool(12, m_useReverseAPI); + s.writeString(13, m_reverseAPIAddress); + s.writeU32(14, m_reverseAPIPort); + s.writeU32(15, m_reverseAPIDeviceIndex); + s.writeU32(16, m_reverseAPIChannelIndex); return s.final(); } @@ -81,6 +91,7 @@ bool WFMDemodSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; QString strtmp; d.readS32(1, &tmp, 0); @@ -103,6 +114,21 @@ bool WFMDemodSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readBool(12, &m_useReverseAPI, false); + d.readString(13, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(14, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(15, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(16, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.h b/plugins/channelrx/demodwfm/wfmdemodsettings.h index 96e62c944..1afa1ee67 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.h +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.h @@ -33,6 +33,11 @@ struct WFMDemodSettings quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; diff --git a/plugins/channelrx/demodwfm/wfmplugin.cpp b/plugins/channelrx/demodwfm/wfmplugin.cpp index 55c366b16..ac3f2febf 100644 --- a/plugins/channelrx/demodwfm/wfmplugin.cpp +++ b/plugins/channelrx/demodwfm/wfmplugin.cpp @@ -10,7 +10,7 @@ const PluginDescriptor WFMPlugin::m_pluginDescriptor = { QString("WFM Demodulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/udpsink/udpsink.cpp b/plugins/channelrx/udpsink/udpsink.cpp index ba828f590..240584867 100644 --- a/plugins/channelrx/udpsink/udpsink.cpp +++ b/plugins/channelrx/udpsink/udpsink.cpp @@ -17,6 +17,9 @@ #include #include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGUDPSinkSettings.h" @@ -110,10 +113,15 @@ UDPSink::UDPSink(DeviceSourceAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); m_deviceAPI->addThreadedSink(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } UDPSink::~UDPSink() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; delete m_audioSocket; delete m_udpBuffer24; delete m_udpBuffer16; @@ -505,6 +513,57 @@ void UDPSink::applySettings(const UDPSinkSettings& settings, bool force) << " m_audioPort: " << settings.m_audioPort << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_audioActive != m_settings.m_audioActive) || force) { + reverseAPIKeys.append("audioActive"); + } + if ((settings.m_audioStereo != m_settings.m_audioStereo) || force) { + reverseAPIKeys.append("audioStereo"); + } + if ((settings.m_gain != m_settings.m_gain) || force) { + reverseAPIKeys.append("gain"); + } + if ((settings.m_volume != m_settings.m_volume) || force) { + reverseAPIKeys.append("volume"); + } + if ((settings.m_squelchEnabled != m_settings.m_squelchEnabled) || force) { + reverseAPIKeys.append("squelchEnabled"); + } + if ((settings.m_squelchdB != m_settings.m_squelchdB) || force) { + reverseAPIKeys.append("squelchDB"); + } + if ((settings.m_squelchGate != m_settings.m_squelchGate) || force) { + reverseAPIKeys.append("squelchGate"); + } + if ((settings.m_agc != m_settings.m_agc) || force) { + reverseAPIKeys.append("agc"); + } + if ((settings.m_sampleFormat != m_settings.m_sampleFormat) || force) { + reverseAPIKeys.append("sampleFormat"); + } + if ((settings.m_outputSampleRate != m_settings.m_outputSampleRate) || force) { + reverseAPIKeys.append("outputSampleRate"); + } + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } + if ((settings.m_audioPort != m_settings.m_audioPort) || force) { + reverseAPIKeys.append("audioPort"); + } + m_settingsMutex.lock(); if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || @@ -617,6 +676,16 @@ void UDPSink::applySettings(const UDPSinkSettings& settings, bool force) m_settingsMutex.unlock(); + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -798,3 +867,108 @@ void UDPSink::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getUdpSinkReport()->setSquelch(m_squelchOpen ? 1 : 0); response.getUdpSinkReport()->setInputSampleRate(m_inputSampleRate); } + +void UDPSink::webapiReverseSendSettings(QList& channelSettingsKeys, const UDPSinkSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(0); + swgChannelSettings->setChannelType(new QString("UDPSink")); + swgChannelSettings->setUdpSinkSettings(new SWGSDRangel::SWGUDPSinkSettings()); + SWGSDRangel::SWGUDPSinkSettings *swgUDPSinkSettings = swgChannelSettings->getUdpSinkSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("outputSampleRate") || force) { + swgUDPSinkSettings->setOutputSampleRate(settings.m_outputSampleRate); + } + if (channelSettingsKeys.contains("sampleFormat") || force) { + swgUDPSinkSettings->setSampleFormat((int) settings.m_sampleFormat); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgUDPSinkSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgUDPSinkSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgUDPSinkSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("channelMute") || force) { + swgUDPSinkSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("gain") || force) { + swgUDPSinkSettings->setGain(settings.m_gain); + } + if (channelSettingsKeys.contains("squelchDB") || force) { + swgUDPSinkSettings->setSquelchDb(settings.m_squelchdB); + } + if (channelSettingsKeys.contains("squelchGate") || force) { + swgUDPSinkSettings->setSquelchGate(settings.m_squelchGate); + } + if (channelSettingsKeys.contains("squelchEnabled") || force) { + swgUDPSinkSettings->setSquelchEnabled(settings.m_squelchEnabled ? 1 : 0); + } + if (channelSettingsKeys.contains("agc") || force) { + swgUDPSinkSettings->setAgc(settings.m_agc ? 1 : 0); + } + if (channelSettingsKeys.contains("audioActive") || force) { + swgUDPSinkSettings->setAudioActive(settings.m_audioActive ? 1 : 0); + } + if (channelSettingsKeys.contains("audioStereo") || force) { + swgUDPSinkSettings->setAudioStereo(settings.m_audioStereo ? 1 : 0); + } + if (channelSettingsKeys.contains("volume") || force) { + swgUDPSinkSettings->setVolume(settings.m_volume); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgUDPSinkSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgUDPSinkSettings->setUdpPort(settings.m_udpPort); + } + if (channelSettingsKeys.contains("audioPort") || force) { + swgUDPSinkSettings->setAudioPort(settings.m_audioPort); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgUDPSinkSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgUDPSinkSettings->setTitle(new QString(settings.m_title)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void UDPSink::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "UDPSink::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("UDPSink::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channelrx/udpsink/udpsink.h b/plugins/channelrx/udpsink/udpsink.h index 5923226b0..9f2e7a919 100644 --- a/plugins/channelrx/udpsink/udpsink.h +++ b/plugins/channelrx/udpsink/udpsink.h @@ -20,6 +20,7 @@ #include #include +#include #include "dsp/basebandsamplesink.h" #include "channel/channelsinkapi.h" @@ -36,6 +37,8 @@ #include "udpsinksettings.h" +class QNetworkAccessManager; +class QNetworkReply; class QUdpSocket; class DeviceSourceAPI; class ThreadedBasebandSampleSink; @@ -135,6 +138,9 @@ public: public slots: void audioReadyRead(); +private slots: + void networkManagerFinished(QNetworkReply *reply); + protected: class MsgUDPSinkSpectrum : public Message { MESSAGE_CLASS_DECLARATION @@ -227,6 +233,9 @@ protected: MagAGC m_agc; Bandpass m_bandpass; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + QMutex m_settingsMutex; void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = true); @@ -234,6 +243,7 @@ protected: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const UDPSinkSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const UDPSinkSettings& settings, bool force); inline void calculateSquelch(double value) { diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index c4c8a06bb..ab50c9bd2 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -615,13 +615,23 @@ void UDPSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/channelrx/udpsink/udpsinkplugin.cpp b/plugins/channelrx/udpsink/udpsinkplugin.cpp index b95e8f087..d425b5db2 100644 --- a/plugins/channelrx/udpsink/udpsinkplugin.cpp +++ b/plugins/channelrx/udpsink/udpsinkplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor UDPSinkPlugin::m_pluginDescriptor = { QString("UDP Channel Sink"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/udpsink/udpsinksettings.cpp b/plugins/channelrx/udpsink/udpsinksettings.cpp index fdf24a6d8..d67c1b98b 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.cpp +++ b/plugins/channelrx/udpsink/udpsinksettings.cpp @@ -49,6 +49,11 @@ void UDPSinkSettings::resetToDefaults() m_audioPort = 9997; m_rgbColor = QColor(225, 25, 99).rgb(); m_title = "UDP Sample Sink"; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray UDPSinkSettings::serialize() const @@ -80,6 +85,11 @@ QByteArray UDPSinkSettings::serialize() const s.writeString(20, m_udpAddress); s.writeU32(21, m_udpPort); s.writeU32(22, m_audioPort); + s.writeBool(23, m_useReverseAPI); + s.writeString(24, m_reverseAPIAddress); + s.writeU32(25, m_reverseAPIPort); + s.writeU32(26, m_reverseAPIDeviceIndex); + s.writeU32(27, m_reverseAPIChannelIndex); return s.final(); @@ -155,6 +165,21 @@ bool UDPSinkSettings::deserialize(const QByteArray& data) m_audioPort = 9997; } + d.readBool(23, &m_useReverseAPI, false); + d.readString(24, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(25, &u32tmp, 0); + + if ((u32tmp > 1023) && (u32tmp < 65535)) { + m_reverseAPIPort = u32tmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(26, &u32tmp, 0); + m_reverseAPIDeviceIndex = u32tmp > 99 ? 99 : u32tmp; + d.readU32(27, &u32tmp, 0); + m_reverseAPIChannelIndex = u32tmp > 99 ? 99 : u32tmp; + return true; } else diff --git a/plugins/channelrx/udpsink/udpsinksettings.h b/plugins/channelrx/udpsink/udpsinksettings.h index c825ead71..3007ec4c0 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.h +++ b/plugins/channelrx/udpsink/udpsinksettings.h @@ -62,6 +62,12 @@ struct UDPSinkSettings QString m_title; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/daemonsource/daemonsource.cpp b/plugins/channeltx/daemonsource/daemonsource.cpp index bf57d0283..dd8af8951 100644 --- a/plugins/channeltx/daemonsource/daemonsource.cpp +++ b/plugins/channeltx/daemonsource/daemonsource.cpp @@ -20,6 +20,9 @@ #include #include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGChannelReport.h" @@ -59,10 +62,15 @@ DaemonSource::DaemonSource(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } DaemonSource::~DaemonSource() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); delete m_threadedChannelizer; @@ -201,19 +209,36 @@ void DaemonSource::applySettings(const DaemonSourceSettings& settings, bool forc << " force: " << force; bool change = false; + QList reverseAPIKeys; - if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) { + if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) + { + reverseAPIKeys.append("dataAddress"); change = true; } - if ((m_settings.m_dataPort != settings.m_dataPort) || force) { + if ((m_settings.m_dataPort != settings.m_dataPort) || force) + { + reverseAPIKeys.append("dataPort"); change = true; } - if (change && m_sourceThread) { + if (change && m_sourceThread) + { + reverseAPIKeys.append("sourceThread"); m_sourceThread->dataBind(settings.m_dataAddress, settings.m_dataPort); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -392,6 +417,21 @@ int DaemonSource::webapiSettingsPutPatch( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getDaemonSourceSettings()->getTitle(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getDaemonSourceSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getDaemonSourceSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getDaemonSourceSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getDaemonSourceSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getDaemonSourceSettings()->getReverseApiChannelIndex(); + } MsgConfigureDaemonSource *msg = MsgConfigureDaemonSource::create(settings, force); m_inputMessageQueue.push(msg); @@ -435,6 +475,18 @@ void DaemonSource::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& } else { response.getDaemonSourceSettings()->setTitle(new QString(settings.m_title)); } + + response.getDaemonSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getDaemonSourceSettings()->getReverseApiAddress()) { + *response.getDaemonSourceSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getDaemonSourceSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getDaemonSourceSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getDaemonSourceSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getDaemonSourceSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void DaemonSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -457,3 +509,62 @@ void DaemonSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& resp response.getDaemonSourceReport()->setDeviceSampleRate(m_deviceAPI->getSampleSink()->getSampleRate()); } +void DaemonSource::webapiReverseSendSettings(QList& channelSettingsKeys, const DaemonSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("DaemonSource")); + swgChannelSettings->setDaemonSourceSettings(new SWGSDRangel::SWGDaemonSourceSettings()); + SWGSDRangel::SWGDaemonSourceSettings *swgDaemonSourceSettings = swgChannelSettings->getDaemonSourceSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("dataAddress") || force) { + swgDaemonSourceSettings->setDataAddress(new QString(settings.m_dataAddress)); + } + if (channelSettingsKeys.contains("dataPort") || force) { + swgDaemonSourceSettings->setDataPort(settings.m_dataPort); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgDaemonSourceSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgDaemonSourceSettings->setTitle(new QString(settings.m_title)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void DaemonSource::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "DaemonSource::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("DaemonSource::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/daemonsource/daemonsource.h b/plugins/channeltx/daemonsource/daemonsource.h index 25237fa25..f753dbc90 100644 --- a/plugins/channeltx/daemonsource/daemonsource.h +++ b/plugins/channeltx/daemonsource/daemonsource.h @@ -18,6 +18,7 @@ #define PLUGINS_CHANNELTX_DAEMONSRC_DAEMONSRC_H_ #include +#include #include "cm256.h" @@ -35,6 +36,8 @@ class UpChannelizer; class DeviceSinkAPI; class DaemonSourceThread; class SDRDaemonDataBlock; +class QNetworkAccessManager; +class QNetworkReply; class DaemonSource : public BasebandSampleSource, public ChannelSourceAPI { Q_OBJECT @@ -233,14 +236,19 @@ private: uint32_t m_nbCorrectableErrors; //!< count of correctable errors in number of blocks uint32_t m_nbUncorrectableErrors; //!< count of uncorrectable errors in number of blocks + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + void applySettings(const DaemonSourceSettings& settings, bool force = false); void handleDataBlock(SDRDaemonDataBlock *dataBlock); void printMeta(const QString& header, SDRDaemonMetaDataFEC *metaData); uint32_t calculateDataReadQueueSize(int sampleRate); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DaemonSourceSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const DaemonSourceSettings& settings, bool force); private slots: + void networkManagerFinished(QNetworkReply *reply); void handleData(); }; diff --git a/plugins/channeltx/daemonsource/daemonsourcegui.cpp b/plugins/channeltx/daemonsource/daemonsourcegui.cpp index 5a95a4059..658bfb06c 100644 --- a/plugins/channeltx/daemonsource/daemonsourcegui.cpp +++ b/plugins/channeltx/daemonsource/daemonsourcegui.cpp @@ -273,11 +273,22 @@ void DaemonSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) void DaemonSourceGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/channeltx/daemonsource/daemonsourceplugin.cpp b/plugins/channeltx/daemonsource/daemonsourceplugin.cpp index 805ad8e89..6c73f6225 100644 --- a/plugins/channeltx/daemonsource/daemonsourceplugin.cpp +++ b/plugins/channeltx/daemonsource/daemonsourceplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor DaemonSourcePlugin::m_pluginDescriptor = { QString("Daemon channel source"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/daemonsource/daemonsourcesettings.cpp b/plugins/channeltx/daemonsource/daemonsourcesettings.cpp index ca0dafbe5..bf30eb8f8 100644 --- a/plugins/channeltx/daemonsource/daemonsourcesettings.cpp +++ b/plugins/channeltx/daemonsource/daemonsourcesettings.cpp @@ -32,6 +32,11 @@ void DaemonSourceSettings::resetToDefaults() m_rgbColor = QColor(140, 4, 4).rgb(); m_title = "Daemon source"; m_channelMarker = nullptr; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray DaemonSourceSettings::serialize() const @@ -41,6 +46,11 @@ QByteArray DaemonSourceSettings::serialize() const s.writeU32(2, m_dataPort); s.writeU32(3, m_rgbColor); s.writeString(4, m_title); + s.writeBool(5, m_useReverseAPI); + s.writeString(6, m_reverseAPIAddress); + s.writeU32(7, m_reverseAPIPort); + s.writeU32(8, m_reverseAPIDeviceIndex); + s.writeU32(9, m_reverseAPIChannelIndex); return s.final(); } @@ -71,7 +81,20 @@ bool DaemonSourceSettings::deserialize(const QByteArray& data) d.readU32(3, &m_rgbColor, QColor(0, 255, 255).rgb()); d.readString(4, &m_title, "Daemon source"); + d.readBool(5, &m_useReverseAPI, false); + d.readString(6, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(7, &tmp, 0); + if ((tmp > 1023) && (tmp < 65535)) { + m_reverseAPIPort = tmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(8, &tmp, 0); + m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp; + d.readU32(9, &tmp, 0); + m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp; return true; } else diff --git a/plugins/channeltx/daemonsource/daemonsourcesettings.h b/plugins/channeltx/daemonsource/daemonsourcesettings.h index df1ab6736..832ecc747 100644 --- a/plugins/channeltx/daemonsource/daemonsourcesettings.h +++ b/plugins/channeltx/daemonsource/daemonsourcesettings.h @@ -28,6 +28,11 @@ struct DaemonSourceSettings uint16_t m_dataPort; //!< Listening data port quint32 m_rgbColor; QString m_title; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index 8604b001b..39f9680ee 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -14,19 +14,22 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "ammod.h" +#include +#include #include #include #include - -#include -#include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGChannelReport.h" #include "SWGAMModReport.h" +#include "ammod.h" + #include "dsp/upchannelizer.h" #include "dsp/dspengine.h" #include "dsp/threadedbasebandsamplesource.h" @@ -84,10 +87,15 @@ AMMod::AMMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } AMMod::~AMMod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); delete m_threadedChannelizer; @@ -332,6 +340,16 @@ bool AMMod::handleMessage(const Message& cmd) return true; } + else if (CWKeyer::MsgConfigureCWKeyer::match(cmd)) + { + const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) cmd; + + if (m_settings.m_useReverseAPI) { + webapiReverseSendCWSettings(cfg.getSettings()); + } + + return true; + } else if (DSPConfigureAudio::match(cmd)) { DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; @@ -451,14 +469,46 @@ void AMMod::applySettings(const AMModSettings& settings, bool force) << " m_modFactor: " << settings.m_modFactor << " m_toneFrequency: " << settings.m_toneFrequency << " m_volumeFactor: " << settings.m_volumeFactor - << " m_audioMute: " << settings.m_channelMute + << " m_channelMute: " << settings.m_channelMute << " m_playLoop: " << settings.m_playLoop << " m_modAFInput " << settings.m_modAFInput << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIAddress: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex << " force: " << force; - if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + + if ((settings.m_modFactor != m_settings.m_modFactor) || force) { + reverseAPIKeys.append("modFactor"); + } + + if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) { + reverseAPIKeys.append("volumeFactor"); + } + + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + + if ((settings.m_playLoop != m_settings.m_playLoop) || force) { + reverseAPIKeys.append("playLoop"); + } + + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) { + reverseAPIKeys.append("modAFInput"); + } + + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -469,6 +519,7 @@ void AMMod::applySettings(const AMModSettings& settings, bool force) if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); m_settingsMutex.lock(); m_toneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate); m_settingsMutex.unlock(); @@ -476,16 +527,28 @@ void AMMod::applySettings(const AMModSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (m_audioSampleRate != audioSampleRate) { + reverseAPIKeys.append("audioSampleRate"); applyAudioSampleRate(audioSampleRate); } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -567,6 +630,21 @@ int AMMod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("modFactor")) { settings.m_modFactor = response.getAmModSettings()->getModFactor(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAmModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAmModSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAmModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getAmModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getAmModSettings()->getReverseApiChannelIndex(); + } if (channelSettingsKeys.contains("cwKeyer")) { @@ -676,6 +754,18 @@ void AMMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respons } else { response.getAmModSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); } + + response.getAmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAmModSettings()->getReverseApiAddress()) { + *response.getAmModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAmModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAmModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getAmModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void AMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -684,3 +774,131 @@ void AMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getAmModReport()->setAudioSampleRate(m_audioSampleRate); response.getAmModReport()->setChannelSampleRate(m_outputSampleRate); } + +void AMMod::webapiReverseSendSettings(QList& channelSettingsKeys, const AMModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("AMMod")); + swgChannelSettings->setAmModSettings(new SWGSDRangel::SWGAMModSettings()); + SWGSDRangel::SWGAMModSettings *swgAMModSettings = swgChannelSettings->getAmModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("channelMute") || force) { + swgAMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgAMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgAMModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgAMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgAMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgAMModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgAMModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgAMModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgAMModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgAMModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("modFactor") || force) { + swgAMModSettings->setModFactor(settings.m_modFactor); + } + + if (force) + { + const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings(); + swgAMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgAMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void AMMod::webapiReverseSendCWSettings(const CWKeyerSettings& cwKeyerSettings) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("AMMod")); + swgChannelSettings->setAmModSettings(new SWGSDRangel::SWGAMModSettings()); + SWGSDRangel::SWGAMModSettings *swgAMModSettings = swgChannelSettings->getAmModSettings(); + + swgAMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgAMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex) + .arg(m_settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void AMMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AMMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h index 4e41f52bf..95b11ed72 100644 --- a/plugins/channeltx/modam/ammod.h +++ b/plugins/channeltx/modam/ammod.h @@ -17,11 +17,13 @@ #ifndef PLUGINS_CHANNELTX_MODAM_AMMOD_H_ #define PLUGINS_CHANNELTX_MODAM_AMMOD_H_ -#include #include #include #include +#include +#include + #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" #include "dsp/nco.h" @@ -35,6 +37,8 @@ #include "ammodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class ThreadedBasebandSampleSource; class UpChannelizer; class DeviceSinkAPI; @@ -291,6 +295,9 @@ private: static const int m_levelNbSamples; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + void applyAudioSampleRate(int sampleRate); void applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const AMModSettings& settings, bool force = false); @@ -301,6 +308,11 @@ private: void seekFileStream(int seekPercentage); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const AMModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const AMModSettings& settings, bool force); + void webapiReverseSendCWSettings(const CWKeyerSettings& settings); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index e3decf2bc..28aa53342 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -279,12 +279,22 @@ void AMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void AMModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -316,7 +326,7 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channeltx/modam/ammodplugin.cpp b/plugins/channeltx/modam/ammodplugin.cpp index 23d880d4e..e41cfab52 100644 --- a/plugins/channeltx/modam/ammodplugin.cpp +++ b/plugins/channeltx/modam/ammodplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor AMModPlugin::m_pluginDescriptor = { QString("AM Modulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modam/ammodsettings.cpp b/plugins/channeltx/modam/ammodsettings.cpp index da1d78a2e..60b2a6aa7 100644 --- a/plugins/channeltx/modam/ammodsettings.cpp +++ b/plugins/channeltx/modam/ammodsettings.cpp @@ -41,6 +41,11 @@ void AMModSettings::resetToDefaults() m_title = "AM Modulator"; m_modAFInput = AMModInputAF::AMModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray AMModSettings::serialize() const @@ -65,6 +70,11 @@ QByteArray AMModSettings::serialize() const s.writeString(9, m_title); s.writeString(10, m_audioDeviceName); s.writeS32(11, (int) m_modAFInput); + s.writeBool(12, m_useReverseAPI); + s.writeString(13, m_reverseAPIAddress); + s.writeU32(14, m_reverseAPIPort); + s.writeU32(15, m_reverseAPIDeviceIndex); + s.writeU32(16, m_reverseAPIChannelIndex); return s.final(); } @@ -83,6 +93,7 @@ bool AMModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -112,6 +123,21 @@ bool AMModSettings::deserialize(const QByteArray& data) m_modAFInput = (AMModInputAF) tmp; } + d.readBool(12, &m_useReverseAPI, false); + d.readString(13, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(14, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(15, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(16, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/channeltx/modam/ammodsettings.h b/plugins/channeltx/modam/ammodsettings.h index 0ed6b435e..ae8102cca 100644 --- a/plugins/channeltx/modam/ammodsettings.h +++ b/plugins/channeltx/modam/ammodsettings.h @@ -43,6 +43,11 @@ struct AMModSettings QString m_title; AMModInputAF m_modAFInput; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index bce87b3f6..7af3c7307 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -14,9 +14,13 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include #include +#include +#include +#include +#include + #include "SWGChannelSettings.h" #include "SWGChannelReport.h" #include "SWGATVModReport.h" @@ -100,12 +104,21 @@ ATVMod::ATVMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } ATVMod::~ATVMod() { - if (m_video.isOpened()) m_video.release(); - releaseCameras(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_video.isOpened()) { + m_video.release(); + } + + releaseCameras(); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); delete m_threadedChannelizer; @@ -1090,8 +1103,72 @@ void ATVMod::applySettings(const ATVModSettings& settings, bool force) << " m_fmExcursion: " << settings.m_fmExcursion << " m_forceDecimator: " << settings.m_forceDecimator << " m_showOverlayText: " << settings.m_showOverlayText + << " m_overlayText: " << settings.m_overlayText << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if ((settings.m_rfOppBandwidth != m_settings.m_rfOppBandwidth) || force) { + reverseAPIKeys.append("rfOppBandwidth"); + } + if ((settings.m_atvStd != m_settings.m_atvStd) || force) { + reverseAPIKeys.append("atvStd"); + } + if ((settings.m_nbLines != m_settings.m_nbLines) || force) { + reverseAPIKeys.append("nbLines"); + } + if ((settings.m_fps != m_settings.m_fps) || force) { + reverseAPIKeys.append("fps"); + } + if ((settings.m_atvModInput != m_settings.m_atvModInput) || force) { + reverseAPIKeys.append("atvModInput"); + } + if ((settings.m_uniformLevel != m_settings.m_uniformLevel) || force) { + reverseAPIKeys.append("uniformLevel"); + } + if ((settings.m_uniformLevel != m_settings.m_uniformLevel) || force) { + reverseAPIKeys.append("uniformLevel"); + } + if ((settings.m_atvModulation != m_settings.m_atvModulation) || force) { + reverseAPIKeys.append("atvModulation"); + } + if ((settings.m_videoPlayLoop != m_settings.m_videoPlayLoop) || force) { + reverseAPIKeys.append("videoPlayLoop"); + } + if ((settings.m_videoPlay != m_settings.m_videoPlay) || force) { + reverseAPIKeys.append("videoPlay"); + } + if ((settings.m_cameraPlay != m_settings.m_cameraPlay) || force) { + reverseAPIKeys.append("cameraPlay"); + } + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + if ((settings.m_invertedVideo != m_settings.m_invertedVideo) || force) { + reverseAPIKeys.append("invertedVideo"); + } + if ((settings.m_rfScalingFactor != m_settings.m_rfScalingFactor) || force) { + reverseAPIKeys.append("rfScalingFactor"); + } + if ((settings.m_fmExcursion != m_settings.m_fmExcursion) || force) { + reverseAPIKeys.append("fmExcursion"); + } + if ((settings.m_forceDecimator != m_settings.m_forceDecimator) || force) { + reverseAPIKeys.append("forceDecimator"); + } + if ((settings.m_showOverlayText != m_settings.m_showOverlayText) || force) { + reverseAPIKeys.append("showOverlayText"); + } + if ((settings.m_overlayText != m_settings.m_overlayText) || force) { + reverseAPIKeys.append("overlayText"); + } + if ((settings.m_atvStd != m_settings.m_atvStd) || (settings.m_nbLines != m_settings.m_nbLines) || (settings.m_fps != m_settings.m_fps) @@ -1159,6 +1236,16 @@ void ATVMod::applySettings(const ATVModSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -1214,7 +1301,7 @@ int ATVMod::webapiSettingsPutPatch( settings.m_rfBandwidth = response.getAtvModSettings()->getRfBandwidth(); } if (channelSettingsKeys.contains("rfOppBandwidth")) { - settings.m_rfOppBandwidth = response.getAtvModSettings()->getRfBandwidth(); + settings.m_rfOppBandwidth = response.getAtvModSettings()->getRfOppBandwidth(); } if (channelSettingsKeys.contains("atvStd")) { settings.m_atvStd = (ATVModSettings::ATVStd) response.getAtvModSettings()->getAtvStd(); @@ -1270,7 +1357,21 @@ int ATVMod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getAtvModSettings()->getTitle(); } - + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAtvModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAtvModSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAtvModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getAtvModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getAtvModSettings()->getReverseApiChannelIndex(); + } if (frequencyOffsetChanged) { ATVMod::MsgConfigureChannelizer *msgChan = ATVMod::MsgConfigureChannelizer::create( @@ -1377,6 +1478,18 @@ void ATVMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon } else { response.getAtvModSettings()->setVideoFileName(new QString(m_videoFileName)); } + + response.getAtvModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAtvModSettings()->getReverseApiAddress()) { + *response.getAtvModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAtvModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAtvModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAtvModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getAtvModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void ATVMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -1384,3 +1497,114 @@ void ATVMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getAtvModReport()->setChannelPowerDb(CalcDb::dbPower(getMagSq())); response.getAtvModReport()->setChannelSampleRate(m_outputSampleRate); } + +void ATVMod::webapiReverseSendSettings(QList& channelSettingsKeys, const ATVModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("ATVMod")); + swgChannelSettings->setAtvModSettings(new SWGSDRangel::SWGATVModSettings()); + SWGSDRangel::SWGATVModSettings *swgATVModSettings = swgChannelSettings->getAtvModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgATVModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgATVModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rfOppBandwidth") || force) { + swgATVModSettings->setRfOppBandwidth(settings.m_rfOppBandwidth); + } + if (channelSettingsKeys.contains("atvStd") || force) { + swgATVModSettings->setAtvStd((int) settings.m_atvStd); + } + if (channelSettingsKeys.contains("nbLines") || force) { + swgATVModSettings->setNbLines(settings.m_nbLines); + } + if (channelSettingsKeys.contains("fps") || force) { + swgATVModSettings->setFps(settings.m_fps); + } + if (channelSettingsKeys.contains("atvModInput") || force) { + swgATVModSettings->setAtvModInput((int) settings.m_atvModInput); + } + if (channelSettingsKeys.contains("uniformLevel") || force) { + swgATVModSettings->setUniformLevel(settings.m_uniformLevel); + } + if (channelSettingsKeys.contains("atvModulation") || force) { + swgATVModSettings->setAtvModulation((int) settings.m_atvModulation); + } + if (channelSettingsKeys.contains("videoPlayLoop") || force) { + swgATVModSettings->setVideoPlayLoop(settings.m_videoPlayLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("videoPlay") || force) { + swgATVModSettings->setVideoPlay(settings.m_videoPlay ? 1 : 0); + } + if (channelSettingsKeys.contains("cameraPlay") || force) { + swgATVModSettings->setCameraPlay(settings.m_cameraPlay ? 1 : 0); + } + if (channelSettingsKeys.contains("channelMute") || force) { + swgATVModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("invertedVideo") || force) { + swgATVModSettings->setInvertedVideo(settings.m_invertedVideo ? 1 : 0); + } + if (channelSettingsKeys.contains("rfScalingFactor") || force) { + swgATVModSettings->setRfScalingFactor(settings.m_rfScalingFactor); + } + if (channelSettingsKeys.contains("fmExcursion") || force) { + swgATVModSettings->setFmExcursion(settings.m_fmExcursion); + } + if (channelSettingsKeys.contains("forceDecimator") || force) { + swgATVModSettings->setForceDecimator(settings.m_forceDecimator ? 1 : 0); + } + if (channelSettingsKeys.contains("showOverlayText") || force) { + swgATVModSettings->setShowOverlayText(settings.m_showOverlayText ? 1 : 0); + } + if (channelSettingsKeys.contains("overlayText") || force) { + swgATVModSettings->setOverlayText(new QString(settings.m_overlayText)); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgATVModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgATVModSettings->setTitle(new QString(settings.m_title)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void ATVMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "ATVMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("ATVMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h index 1e8913dc9..8b845f826 100644 --- a/plugins/channeltx/modatv/atvmod.h +++ b/plugins/channeltx/modatv/atvmod.h @@ -17,10 +17,11 @@ #ifndef PLUGINS_CHANNELTX_MODATV_ATVMOD_H_ #define PLUGINS_CHANNELTX_MODATV_ATVMOD_H_ +#include + #include #include - -#include +#include #include #include @@ -37,6 +38,8 @@ #include "atvmodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; @@ -400,6 +403,9 @@ signals: */ void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); +private slots: + void networkManagerFinished(QNetworkReply *reply); + private: struct ATVCamera { @@ -524,8 +530,10 @@ private: Complex* m_DSBFilterBuffer; int m_DSBFilterBufferIndex; - static const int m_ssbFftLen; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_ssbFftLen; static const float m_blackLevel; static const float m_spanLevel; static const int m_levelNbSamples; @@ -556,6 +564,7 @@ private: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const ATVModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const ATVModSettings& settings, bool force); inline void pullImageLine(Real& sample, bool noHSync = false) { diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index be40013e2..bf151740f 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -207,8 +207,8 @@ void ATVModGUI::setRFFiltersSlidersRange(int sampleRate) ui->rfOppBW->setMaximum((sampleRate) / m_rfSliderDivisor); } - ui->rfBWText->setText(QString("%1k").arg((ui->rfBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); - ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); + ui->rfBWText->setText(QString("%1k").arg((ui->rfBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); + ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); } int ATVModGUI::getNbLines() @@ -372,6 +372,7 @@ void ATVModGUI::handleSourceMessages() void ATVModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = value; applySettings(); } @@ -399,7 +400,7 @@ void ATVModGUI::on_fmExcursion_valueChanged(int value) void ATVModGUI::on_rfBW_valueChanged(int value) { - ui->rfBWText->setText(QString("%1k").arg((value*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); + ui->rfBWText->setText(QString("%1k").arg((value*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); m_settings.m_rfBandwidth = value * m_rfSliderDivisor * 1.0f; setChannelMarkerBandwidth(); applySettings(); @@ -407,7 +408,7 @@ void ATVModGUI::on_rfBW_valueChanged(int value) void ATVModGUI::on_rfOppBW_valueChanged(int value) { - ui->rfOppBWText->setText(QString("%1k").arg((value*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); + ui->rfOppBWText->setText(QString("%1k").arg((value*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); m_settings.m_rfOppBandwidth = value * m_rfSliderDivisor * 1.0f; setChannelMarkerBandwidth(); applySettings(); @@ -614,12 +615,23 @@ void ATVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void ATVModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -737,11 +749,10 @@ void ATVModGUI::displaySettings() setRFFiltersSlidersRange(m_atvMod->getEffectiveSampleRate()); ui->rfBW->setValue(roundf(m_settings.m_rfBandwidth / m_rfSliderDivisor)); - ui->rfBWText->setText(QString("%1k").arg((ui->rfBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); + ui->rfBWText->setText(QString("%1k").arg((ui->rfBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); ui->rfOppBW->setValue(roundf(m_settings.m_rfOppBandwidth / m_rfSliderDivisor)); - ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); - + ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value()*m_rfSliderDivisor) / 1000.0, 0, 'f', 1)); ui->forceDecimator->setChecked(m_settings.m_forceDecimator); ui->channelMute->setChecked(m_settings.m_channelMute); diff --git a/plugins/channeltx/modatv/atvmodplugin.cpp b/plugins/channeltx/modatv/atvmodplugin.cpp index 4aa4b1919..0cba9c5b8 100644 --- a/plugins/channeltx/modatv/atvmodplugin.cpp +++ b/plugins/channeltx/modatv/atvmodplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor ATVModPlugin::m_pluginDescriptor = { QString("ATV Modulator"), - QString("4.0.7"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modatv/atvmodsettings.cpp b/plugins/channeltx/modatv/atvmodsettings.cpp index e74212334..37b34bcbf 100644 --- a/plugins/channeltx/modatv/atvmodsettings.cpp +++ b/plugins/channeltx/modatv/atvmodsettings.cpp @@ -50,6 +50,11 @@ void ATVModSettings::resetToDefaults() m_overlayText = "ATV"; m_rgbColor = QColor(255, 255, 255).rgb(); m_title = "ATV Modulator"; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray ATVModSettings::serialize() const @@ -76,6 +81,11 @@ QByteArray ATVModSettings::serialize() const } s.writeString(16, m_title); + s.writeBool(17, m_useReverseAPI); + s.writeString(18, m_reverseAPIAddress); + s.writeU32(19, m_reverseAPIPort); + s.writeU32(20, m_reverseAPIDeviceIndex); + s.writeU32(21, m_reverseAPIChannelIndex); return s.final(); } @@ -94,6 +104,7 @@ bool ATVModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -124,7 +135,20 @@ bool ATVModSettings::deserialize(const QByteArray& data) } d.readString(16, &m_title, "ATV Modulator"); + d.readBool(17, &m_useReverseAPI, false); + d.readString(18, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(19, &utmp, 0); + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(20, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(21, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; return true; } else diff --git a/plugins/channeltx/modatv/atvmodsettings.h b/plugins/channeltx/modatv/atvmodsettings.h index 633c179fd..7b62427e6 100644 --- a/plugins/channeltx/modatv/atvmodsettings.h +++ b/plugins/channeltx/modatv/atvmodsettings.h @@ -79,6 +79,11 @@ struct ATVModSettings QString m_overlayText; quint32 m_rgbColor; QString m_title; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; diff --git a/plugins/channeltx/modatv/modatv.pro b/plugins/channeltx/modatv/modatv.pro index 99d76db6f..98b262545 100644 --- a/plugins/channeltx/modatv/modatv.pro +++ b/plugins/channeltx/modatv/modatv.pro @@ -27,7 +27,6 @@ CONFIG(Release):build_subdir = release CONFIG(Debug):build_subdir = debug CONFIG(MINGW32):INCLUDEPATH += "D:\softs\opencv\build\include" -CONFIG(MINGW64):INCLUDEPATH += "D:\softs\opencv\build\include" CONFIG(macx):INCLUDEPATH += "/opt/local/include" SOURCES += atvmod.cpp\ @@ -51,7 +50,6 @@ macx { } CONFIG(MINGW32):LIBS += -LD:\softs\opencv\build\mw32\install\x86\mingw\bin -llibopencv_core2413 -llibopencv_highgui2413 -llibopencv_imgproc2413 -CONFIG(MINGW64):LIBS += -LD:\softs\opencv\build\mw64\install\x64\mingw\bin -llibopencv_core2413 -llibopencv_highgui2413 -llibopencv_imgproc2413 CONFIG(macx):LIBS += -L/opt/local/lib -lopencv_core -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_video -lopencv_videoio RESOURCES = ../../../sdrgui/resources/res.qrc diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index f51e91276..0ef3d7c3d 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGCWKeyerSettings.h" @@ -90,10 +93,15 @@ NFMMod::NFMMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } NFMMod::~NFMMod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSource(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); @@ -348,6 +356,16 @@ bool NFMMod::handleMessage(const Message& cmd) return true; } + else if (CWKeyer::MsgConfigureCWKeyer::match(cmd)) + { + const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) cmd; + + if (m_settings.m_useReverseAPI) { + webapiReverseSendCWSettings(cfg.getSettings()); + } + + return true; + } else if (DSPConfigureAudio::match(cmd)) { DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; @@ -475,12 +493,48 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) << " m_ctcssOn: " << settings.m_ctcssOn << " m_channelMute: " << settings.m_channelMute << " m_playLoop: " << settings.m_playLoop - << " m_modAFInout " << settings.m_modAFInput + << " m_modAFInput " << settings.m_modAFInput << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIAddress: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + + if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) { + reverseAPIKeys.append("volumeFactor"); + } + + if ((settings.m_ctcssOn != m_settings.m_ctcssOn) || force) { + reverseAPIKeys.append("ctcssOn"); + } + + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + + if ((settings.m_playLoop != m_settings.m_playLoop) || force) { + reverseAPIKeys.append("playLoop"); + } + + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) { + reverseAPIKeys.append("modAFInput"); + } + if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -491,6 +545,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_lowpass.create(301, m_audioSampleRate, 250.0); m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_afBandwidth); @@ -499,6 +554,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); m_settingsMutex.lock(); m_toneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate); m_settingsMutex.unlock(); @@ -506,6 +562,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_ctcssIndex != m_settings.m_ctcssIndex) || force) { + reverseAPIKeys.append("ctcssIndex"); m_settingsMutex.lock(); m_ctcssNco.setFreq(NFMModSettings::getCTCSSFreq(settings.m_ctcssIndex), m_audioSampleRate); m_settingsMutex.unlock(); @@ -513,16 +570,28 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (m_audioSampleRate != audioSampleRate) { + reverseAPIKeys.append("audioSampleRate"); applyAudioSampleRate(audioSampleRate); } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -617,6 +686,21 @@ int NFMMod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("volumeFactor")) { settings.m_volumeFactor = response.getNfmModSettings()->getVolumeFactor(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getNfmModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getNfmModSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getNfmModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getNfmModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getNfmModSettings()->getReverseApiChannelIndex(); + } if (channelSettingsKeys.contains("cwKeyer")) { @@ -729,6 +813,18 @@ void NFMMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon } apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + response.getNfmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getNfmModSettings()->getReverseApiAddress()) { + *response.getNfmModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getNfmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getNfmModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getNfmModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getNfmModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void NFMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -737,3 +833,141 @@ void NFMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getNfmModReport()->setAudioSampleRate(m_audioSampleRate); response.getNfmModReport()->setChannelSampleRate(m_outputSampleRate); } + +void NFMMod::webapiReverseSendSettings(QList& channelSettingsKeys, const NFMModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("NFMMod")); + swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings()); + SWGSDRangel::SWGNFMModSettings *swgNFMModSettings = swgChannelSettings->getNfmModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("channelMute") || force) { + swgNFMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgNFMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgNFMModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgNFMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgNFMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgNFMModSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgNFMModSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgNFMModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgNFMModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgNFMModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgNFMModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgNFMModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("ctcssOn") || force) { + swgNFMModSettings->setCtcssOn(settings.m_ctcssOn ? 1 : 0); + } + if (channelSettingsKeys.contains("ctcssIndex") || force) { + swgNFMModSettings->setCtcssIndex(settings.m_ctcssIndex); + } + + if (force) + { + const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings(); + swgNFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void NFMMod::webapiReverseSendCWSettings(const CWKeyerSettings& cwKeyerSettings) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("AMMod")); + swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings()); + SWGSDRangel::SWGNFMModSettings *swgNFModSettings = swgChannelSettings->getNfmModSettings(); + + swgNFModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex) + .arg(m_settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void NFMMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "NFMMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("NFMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} + diff --git a/plugins/channeltx/modnfm/nfmmod.h b/plugins/channeltx/modnfm/nfmmod.h index 8a42d7a8b..7a757b4b2 100644 --- a/plugins/channeltx/modnfm/nfmmod.h +++ b/plugins/channeltx/modnfm/nfmmod.h @@ -17,11 +17,13 @@ #ifndef PLUGINS_CHANNELTX_MODNFM_NFMMOD_H_ #define PLUGINS_CHANNELTX_MODNFM_NFMMOD_H_ -#include #include #include #include +#include +#include + #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" #include "dsp/nco.h" @@ -40,6 +42,8 @@ class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; +class QNetworkAccessManager; +class QNetworkReply; class NFMMod : public BasebandSampleSource, public ChannelSourceAPI { Q_OBJECT @@ -295,6 +299,10 @@ private: Real m_peakLevel; Real m_levelSum; CWKeyer m_cwKeyer; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_levelNbSamples; void applyAudioSampleRate(int sampleRate); @@ -307,6 +315,11 @@ private: void seekFileStream(int seekPercentage); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const NFMModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const NFMModSettings& settings, bool force); + void webapiReverseSendCWSettings(const CWKeyerSettings& settings); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 9d7ec0262..506f3834b 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -296,12 +296,22 @@ void NFMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void NFMModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -344,7 +354,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channeltx/modnfm/nfmmodplugin.cpp b/plugins/channeltx/modnfm/nfmmodplugin.cpp index 8f231d52c..2f13cc2df 100644 --- a/plugins/channeltx/modnfm/nfmmodplugin.cpp +++ b/plugins/channeltx/modnfm/nfmmodplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor NFMModPlugin::m_pluginDescriptor = { QString("NFM Modulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modnfm/nfmmodsettings.cpp b/plugins/channeltx/modnfm/nfmmodsettings.cpp index 874c11a6e..7b1ae270e 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.cpp +++ b/plugins/channeltx/modnfm/nfmmodsettings.cpp @@ -59,6 +59,11 @@ void NFMModSettings::resetToDefaults() m_title = "NFM Modulator"; m_modAFInput = NFMModInputAF::NFMModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray NFMModSettings::serialize() const @@ -86,6 +91,11 @@ QByteArray NFMModSettings::serialize() const s.writeString(12, m_title); s.writeS32(13, (int) m_modAFInput); s.writeString(14, m_audioDeviceName); + s.writeBool(15, m_useReverseAPI); + s.writeString(16, m_reverseAPIAddress); + s.writeU32(17, m_reverseAPIPort); + s.writeU32(18, m_reverseAPIDeviceIndex); + s.writeU32(19, m_reverseAPIChannelIndex); return s.final(); } @@ -104,6 +114,7 @@ bool NFMModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -138,6 +149,21 @@ bool NFMModSettings::deserialize(const QByteArray& data) d.readString(14, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(15, &m_useReverseAPI, false); + d.readString(16, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(17, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(18, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(19, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/channeltx/modnfm/nfmmodsettings.h b/plugins/channeltx/modnfm/nfmmodsettings.h index 83f264be8..8826641d2 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.h +++ b/plugins/channeltx/modnfm/nfmmodsettings.h @@ -51,6 +51,11 @@ struct NFMModSettings QString m_title; NFMModInputAF m_modAFInput; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index b8fcbb357..3a962bae6 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include @@ -114,10 +117,16 @@ SSBMod::SSBMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SSBMod::~SSBMod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSource(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); @@ -583,6 +592,16 @@ bool SSBMod::handleMessage(const Message& cmd) return true; } + else if (CWKeyer::MsgConfigureCWKeyer::match(cmd)) + { + const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) cmd; + + if (m_settings.m_useReverseAPI) { + webapiReverseSendCWSettings(cfg.getSettings()); + } + + return true; + } else if (DSPConfigureAudio::match(cmd)) { DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; @@ -744,6 +763,77 @@ void SSBMod::applySettings(const SSBModSettings& settings, bool force) float band = settings.m_bandwidth; float lowCutoff = settings.m_lowCutoff; bool usb = settings.m_usb; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_bandwidth != m_settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); + } + if ((settings.m_lowCutoff != m_settings.m_lowCutoff) || force) { + reverseAPIKeys.append("lowCutoff"); + } + if ((settings.m_usb != m_settings.m_usb) || force) { + reverseAPIKeys.append("usb"); + } + if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); + } + if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) { + reverseAPIKeys.append("volumeFactor"); + } + if ((settings.m_spanLog2 != m_settings.m_spanLog2) || force) { + reverseAPIKeys.append("spanLog2"); + } + if ((settings.m_audioBinaural != m_settings.m_audioBinaural) || force) { + reverseAPIKeys.append("audioBinaural"); + } + if ((settings.m_audioFlipChannels != m_settings.m_audioFlipChannels) || force) { + reverseAPIKeys.append("audioFlipChannels"); + } + if ((settings.m_dsb != m_settings.m_dsb) || force) { + reverseAPIKeys.append("dsb"); + } + if ((settings.m_audioMute != m_settings.m_audioMute) || force) { + reverseAPIKeys.append("audioMute"); + } + if ((settings.m_playLoop != m_settings.m_playLoop) || force) { + reverseAPIKeys.append("playLoop"); + } + if ((settings.m_agc != m_settings.m_agc) || force) { + reverseAPIKeys.append("agc"); + } + if ((settings.m_agcOrder != m_settings.m_agcOrder) || force) { + reverseAPIKeys.append("agcOrder"); + } + if ((settings.m_agcTime != m_settings.m_agcTime) || force) { + reverseAPIKeys.append("agcTime"); + } + if ((settings.m_agcThresholdEnable != m_settings.m_agcThresholdEnable) || force) { + reverseAPIKeys.append("agcThresholdEnable"); + } + if ((settings.m_agcThreshold != m_settings.m_agcThreshold) || force) { + reverseAPIKeys.append("agcThreshold"); + } + if ((settings.m_agcThresholdGate != m_settings.m_agcThresholdGate) || force) { + reverseAPIKeys.append("agcThresholdGate"); + } + if ((settings.m_agcThresholdDelay != m_settings.m_agcThresholdDelay) || force) { + reverseAPIKeys.append("agcThresholdDelay"); + } + if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) { + reverseAPIKeys.append("rgbColor"); + } + if ((settings.m_title != m_settings.m_title) || force) { + reverseAPIKeys.append("title"); + } + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) { + reverseAPIKeys.append("modAFInput"); + } + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); + } if ((settings.m_bandwidth != m_settings.m_bandwidth) || (settings.m_lowCutoff != m_settings.m_lowCutoff) || force) @@ -842,6 +932,16 @@ void SSBMod::applySettings(const SSBModSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; m_settings.m_bandwidth = band; m_settings.m_lowCutoff = lowCutoff; @@ -962,6 +1062,21 @@ int SSBMod::webapiSettingsPutPatch( if (channelSettingsKeys.contains("audioDeviceName")) { settings.m_audioDeviceName = *response.getSsbModSettings()->getAudioDeviceName(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getSsbModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getSsbModSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getSsbModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getSsbModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getSsbModSettings()->getReverseApiChannelIndex(); + } if (channelSettingsKeys.contains("cwKeyer")) { @@ -1083,6 +1198,18 @@ void SSBMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon } apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + response.getAmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAmModSettings()->getReverseApiAddress()) { + *response.getAmModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAmModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAmModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getAmModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void SSBMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -1092,3 +1219,166 @@ void SSBMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getSsbModReport()->setChannelSampleRate(m_outputSampleRate); } +void SSBMod::webapiReverseSendSettings(QList& channelSettingsKeys, const SSBModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("SSBMod")); + swgChannelSettings->setSsbModSettings(new SWGSDRangel::SWGSSBModSettings()); + SWGSDRangel::SWGSSBModSettings *swgSSBModSettings = swgChannelSettings->getSsbModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgSSBModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("bandwidth") || force) { + swgSSBModSettings->setBandwidth(settings.m_bandwidth); + } + if (channelSettingsKeys.contains("lowCutoff") || force) { + swgSSBModSettings->setLowCutoff(settings.m_lowCutoff); + } + if (channelSettingsKeys.contains("usb") || force) { + swgSSBModSettings->setUsb(settings.m_usb ? 1 : 0); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgSSBModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgSSBModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("spanLog2") || force) { + swgSSBModSettings->setSpanLog2(settings.m_spanLog2); + } + if (channelSettingsKeys.contains("audioBinaural") || force) { + swgSSBModSettings->setAudioBinaural(settings.m_audioBinaural ? 1 : 0); + } + if (channelSettingsKeys.contains("audioFlipChannels") || force) { + swgSSBModSettings->setAudioFlipChannels(settings.m_audioFlipChannels ? 1 : 0); + } + if (channelSettingsKeys.contains("dsb") || force) { + swgSSBModSettings->setDsb(settings.m_dsb ? 1 : 0); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgSSBModSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgSSBModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("agc") || force) { + swgSSBModSettings->setAgc(settings.m_agc ? 1 : 0); + } + if (channelSettingsKeys.contains("agcOrder") || force) { + swgSSBModSettings->setAgcOrder(settings.m_agcOrder); + } + if (channelSettingsKeys.contains("agcTime") || force) { + swgSSBModSettings->setAgcTime(settings.m_agcTime); + } + if (channelSettingsKeys.contains("agcThresholdEnable") || force) { + swgSSBModSettings->setAgcThresholdEnable(settings.m_agcThresholdEnable ? 1 : 0); + } + if (channelSettingsKeys.contains("agcThreshold") || force) { + swgSSBModSettings->setAgcThreshold(settings.m_agcThreshold); + } + if (channelSettingsKeys.contains("agcThresholdGate") || force) { + swgSSBModSettings->setAgcThresholdGate(settings.m_agcThresholdGate); + } + if (channelSettingsKeys.contains("agcThresholdDelay") || force) { + swgSSBModSettings->setAgcThresholdDelay(settings.m_agcThresholdDelay); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgSSBModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgSSBModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgSSBModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgSSBModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + if (force) + { + const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings(); + swgSSBModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgSSBModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void SSBMod::webapiReverseSendCWSettings(const CWKeyerSettings& cwKeyerSettings) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("SSBMod")); + swgChannelSettings->setSsbModSettings(new SWGSDRangel::SWGSSBModSettings()); + SWGSDRangel::SWGSSBModSettings *swgSSBModSettings = swgChannelSettings->getSsbModSettings(); + + swgSSBModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgSSBModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex) + .arg(m_settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void SSBMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SSBMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SSBMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/modssb/ssbmod.h b/plugins/channeltx/modssb/ssbmod.h index 6dbd4760c..be2b9a273 100644 --- a/plugins/channeltx/modssb/ssbmod.h +++ b/plugins/channeltx/modssb/ssbmod.h @@ -17,11 +17,13 @@ #ifndef PLUGINS_CHANNELTX_MODSSB_SSBMOD_H_ #define PLUGINS_CHANNELTX_MODSSB_SSBMOD_H_ -#include #include #include #include +#include +#include + #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" #include "dsp/basebandsamplesink.h" @@ -36,6 +38,8 @@ #include "ssbmodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; @@ -309,6 +313,9 @@ private: MagAGC m_inAGC; int m_agcStepLength; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_levelNbSamples; void applyAudioSampleRate(int sampleRate); @@ -321,6 +328,11 @@ private: void seekFileStream(int seekPercentage); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const SSBModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const SSBModSettings& settings, bool force); + void webapiReverseSendCWSettings(const CWKeyerSettings& settings); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index 85d13da29..b7d41b0c6 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -372,12 +372,23 @@ void SSBModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void SSBModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -421,7 +432,7 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channeltx/modssb/ssbmodplugin.cpp b/plugins/channeltx/modssb/ssbmodplugin.cpp index cfd279871..50df84aa4 100644 --- a/plugins/channeltx/modssb/ssbmodplugin.cpp +++ b/plugins/channeltx/modssb/ssbmodplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor SSBModPlugin::m_pluginDescriptor = { QString("SSB Modulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modssb/ssbmodsettings.cpp b/plugins/channeltx/modssb/ssbmodsettings.cpp index 34b76d2fc..7a5178c2b 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.cpp +++ b/plugins/channeltx/modssb/ssbmodsettings.cpp @@ -68,6 +68,11 @@ void SSBModSettings::resetToDefaults() m_title = "SSB Modulator"; m_modAFInput = SSBModInputAF::SSBModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray SSBModSettings::serialize() const @@ -107,6 +112,11 @@ QByteArray SSBModSettings::serialize() const s.writeString(19, m_title); s.writeString(20, m_audioDeviceName); s.writeS32(21, (int) m_modAFInput); + s.writeBool(22, m_useReverseAPI); + s.writeString(23, m_reverseAPIAddress); + s.writeU32(24, m_reverseAPIPort); + s.writeU32(25, m_reverseAPIDeviceIndex); + s.writeU32(26, m_reverseAPIChannelIndex); return s.final(); } @@ -125,6 +135,7 @@ bool SSBModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -181,6 +192,21 @@ bool SSBModSettings::deserialize(const QByteArray& data) m_modAFInput = (SSBModInputAF) tmp; } + d.readBool(22, &m_useReverseAPI, false); + d.readString(23, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(24, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(25, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(26, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/channeltx/modssb/ssbmodsettings.h b/plugins/channeltx/modssb/ssbmodsettings.h index 0163f1ef0..b8604571f 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.h +++ b/plugins/channeltx/modssb/ssbmodsettings.h @@ -62,6 +62,12 @@ struct SSBModSettings SSBModInputAF m_modAFInput; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + Serializable *m_channelMarker; Serializable *m_spectrumGUI; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modwfm/wfmmod.cpp b/plugins/channeltx/modwfm/wfmmod.cpp index dcf34a040..cadf72934 100644 --- a/plugins/channeltx/modwfm/wfmmod.cpp +++ b/plugins/channeltx/modwfm/wfmmod.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include @@ -95,10 +98,15 @@ WFMMod::WFMMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } WFMMod::~WFMMod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSource(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); @@ -358,6 +366,16 @@ bool WFMMod::handleMessage(const Message& cmd) return true; } + else if (CWKeyer::MsgConfigureCWKeyer::match(cmd)) + { + const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) cmd; + + if (m_settings.m_useReverseAPI) { + webapiReverseSendCWSettings(cfg.getSettings()); + } + + return true; + } else if (DSPConfigureAudio::match(cmd)) { DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; @@ -478,11 +496,34 @@ void WFMMod::applySettings(const WFMModSettings& settings, bool force) << " m_toneFrequency: " << settings.m_toneFrequency << " m_channelMute: " << settings.m_channelMute << " m_playLoop: " << settings.m_playLoop + << " m_modAFInput: " << settings.m_modAFInput << " m_audioDeviceName: " << settings.m_audioDeviceName << " force: " << force; - if((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) { + reverseAPIKeys.append("volumeFactor"); + } + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + if ((settings.m_playLoop != m_settings.m_playLoop) || force) { + reverseAPIKeys.append("playLoop"); + } + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) { + reverseAPIKeys.append("modAFInput"); + } + + if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -493,6 +534,7 @@ void WFMMod::applySettings(const WFMModSettings& settings, bool force) if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settingsMutex.lock(); Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_outputSampleRate; Real hiCut = (settings.m_rfBandwidth / 2.0) / m_outputSampleRate; @@ -502,6 +544,7 @@ void WFMMod::applySettings(const WFMModSettings& settings, bool force) if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); m_settingsMutex.lock(); m_toneNcoRF.setFreq(settings.m_toneFrequency, m_outputSampleRate); m_settingsMutex.unlock(); @@ -509,6 +552,7 @@ void WFMMod::applySettings(const WFMModSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); @@ -519,6 +563,16 @@ void WFMMod::applySettings(const WFMModSettings& settings, bool force) } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -720,3 +774,133 @@ void WFMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getWfmModReport()->setChannelSampleRate(m_outputSampleRate); } +void WFMMod::webapiReverseSendSettings(QList& channelSettingsKeys, const WFMModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("WFMMod")); + swgChannelSettings->setWfmModSettings(new SWGSDRangel::SWGWFMModSettings()); + SWGSDRangel::SWGWFMModSettings *swgWFMModSettings = swgChannelSettings->getWfmModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("channelMute") || force) { + swgWFMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgWFMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgWFMModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgWFMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgWFMModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgWFMModSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgWFMModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgWFMModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgWFMModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgWFMModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("fmDeviation")) { + swgWFMModSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgWFMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + if (force) + { + const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings(); + swgWFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgWFMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void WFMMod::webapiReverseSendCWSettings(const CWKeyerSettings& cwKeyerSettings) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("WFMMod")); + swgChannelSettings->setWfmModSettings(new SWGSDRangel::SWGWFMModSettings()); + SWGSDRangel::SWGWFMModSettings *swgWFMModSettings = swgChannelSettings->getWfmModSettings(); + + swgWFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgWFMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex) + .arg(m_settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void WFMMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "WFMMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("WFMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/modwfm/wfmmod.h b/plugins/channeltx/modwfm/wfmmod.h index f742e7b26..310727dc7 100644 --- a/plugins/channeltx/modwfm/wfmmod.h +++ b/plugins/channeltx/modwfm/wfmmod.h @@ -17,11 +17,13 @@ #ifndef PLUGINS_CHANNELTX_MODWFM_WFMMOD_H_ #define PLUGINS_CHANNELTX_MODWFM_WFMMOD_H_ -#include #include #include #include +#include +#include + #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" #include "dsp/nco.h" @@ -36,6 +38,8 @@ #include "wfmmodsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; @@ -295,6 +299,10 @@ private: Real m_peakLevel; Real m_levelSum; CWKeyer m_cwKeyer; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_levelNbSamples; void applyAudioSampleRate(int sampleRate); @@ -306,6 +314,11 @@ private: void seekFileStream(int seekPercentage); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const WFMModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const WFMModSettings& settings, bool force); + void webapiReverseSendCWSettings(const CWKeyerSettings& settings); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index eeef938ef..03ef7be51 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -285,12 +285,23 @@ void WFMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void WFMModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); @@ -333,7 +344,7 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); - connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/channeltx/modwfm/wfmmodplugin.cpp b/plugins/channeltx/modwfm/wfmmodplugin.cpp index b526dc703..212258756 100644 --- a/plugins/channeltx/modwfm/wfmmodplugin.cpp +++ b/plugins/channeltx/modwfm/wfmmodplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor WFMModPlugin::m_pluginDescriptor = { QString("WFM Modulator"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modwfm/wfmmodsettings.cpp b/plugins/channeltx/modwfm/wfmmodsettings.cpp index 6f9f06873..ed8295773 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.cpp +++ b/plugins/channeltx/modwfm/wfmmodsettings.cpp @@ -49,6 +49,11 @@ void WFMModSettings::resetToDefaults() m_title = "WFM Modulator"; m_modAFInput = WFMModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray WFMModSettings::serialize() const @@ -74,6 +79,11 @@ QByteArray WFMModSettings::serialize() const s.writeString(10, m_title); s.writeString(11, m_audioDeviceName); s.writeS32(12, (int) m_modAFInput); + s.writeBool(13, m_useReverseAPI); + s.writeString(14, m_reverseAPIAddress); + s.writeU32(15, m_reverseAPIPort); + s.writeU32(16, m_reverseAPIDeviceIndex); + s.writeU32(17, m_reverseAPIChannelIndex); return s.final(); } @@ -92,6 +102,7 @@ bool WFMModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -122,6 +133,21 @@ bool WFMModSettings::deserialize(const QByteArray& data) m_modAFInput = (WFMModInputAF) tmp; } + d.readBool(13, &m_useReverseAPI, false); + d.readString(14, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(15, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(16, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(17, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/channeltx/modwfm/wfmmodsettings.h b/plugins/channeltx/modwfm/wfmmodsettings.h index d511e69db..6d0b6c66c 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.h +++ b/plugins/channeltx/modwfm/wfmmodsettings.h @@ -47,6 +47,11 @@ struct WFMModSettings QString m_title; WFMModInputAF m_modAFInput; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/udpsource/udpsource.cpp b/plugins/channeltx/udpsource/udpsource.cpp index 809919927..d051f2b3a 100644 --- a/plugins/channeltx/udpsource/udpsource.cpp +++ b/plugins/channeltx/udpsource/udpsource.cpp @@ -15,6 +15,9 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGChannelReport.h" @@ -78,10 +81,15 @@ UDPSource::UDPSource(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } UDPSource::~UDPSource() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); delete m_threadedChannelizer; @@ -489,6 +497,7 @@ void UDPSource::applySettings(const UDPSourceSettings& settings, bool force) << " m_rfBandwidth: " << settings.m_rfBandwidth << " m_lowCutoff: " << settings.m_lowCutoff << " m_fmDeviation: " << settings.m_fmDeviation + << " m_amModFactor: " << settings.m_amModFactor << " m_udpAddressStr: " << settings.m_udpAddress << " m_udpPort: " << settings.m_udpPort << " m_channelMute: " << settings.m_channelMute @@ -501,6 +510,60 @@ void UDPSource::applySettings(const UDPSourceSettings& settings, bool force) << " m_stereoInput: " << settings.m_stereoInput << " force: " << force; + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_sampleFormat != m_settings.m_sampleFormat) || force) { + reverseAPIKeys.append("sampleFormat"); + } + if ((settings.m_inputSampleRate != m_settings.m_inputSampleRate) || force) { + reverseAPIKeys.append("inputSampleRate"); + } + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if ((settings.m_lowCutoff != m_settings.m_lowCutoff) || force) { + reverseAPIKeys.append("lowCutoff"); + } + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + if ((settings.m_amModFactor != m_settings.m_amModFactor) || force) { + reverseAPIKeys.append("amModFactor"); + } + if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) { + reverseAPIKeys.append("udpAddress"); + } + if ((settings.m_udpPort != m_settings.m_udpPort) || force) { + reverseAPIKeys.append("udpPort"); + } + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + if ((settings.m_gainIn != m_settings.m_gainIn) || force) { + reverseAPIKeys.append("gainIn"); + } + if ((settings.m_gainOut != m_settings.m_gainOut) || force) { + reverseAPIKeys.append("gainOut"); + } + if ((settings.m_squelchGate != m_settings.m_squelchGate) || force) { + reverseAPIKeys.append("squelchGate"); + } + if ((settings.m_squelch != m_settings.m_squelch) || force) { + reverseAPIKeys.append("squelch"); + } + if ((settings.m_squelchEnabled != m_settings.m_squelchEnabled) || force) { + reverseAPIKeys.append("squelchEnabled"); + } + if ((settings.m_autoRWBalance != m_settings.m_autoRWBalance) || force) { + reverseAPIKeys.append("autoRWBalance"); + } + if ((settings.m_stereoInput != m_settings.m_stereoInput) || force) { + reverseAPIKeys.append("stereoInput"); + } + if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || (settings.m_lowCutoff != m_settings.m_lowCutoff) || (settings.m_inputSampleRate != m_settings.m_inputSampleRate) || force) @@ -572,6 +635,16 @@ void UDPSource::applySettings(const UDPSourceSettings& settings, bool force) m_settingsMutex.unlock(); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -677,6 +750,21 @@ int UDPSource::webapiSettingsPutPatch( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getUdpSourceSettings()->getTitle(); } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getUdpSourceSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getUdpSourceSettings()->getReverseApiAddress() != 0; + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getUdpSourceSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getUdpSourceSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getUdpSourceSettings()->getReverseApiChannelIndex(); + } if (frequencyOffsetChanged) { @@ -743,6 +831,18 @@ void UDPSource::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res } else { response.getUdpSourceSettings()->setTitle(new QString(settings.m_title)); } + + response.getUdpSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getUdpSourceSettings()->getReverseApiAddress()) { + *response.getUdpSourceSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getUdpSourceSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getUdpSourceSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getUdpSourceSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getUdpSourceSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); } void UDPSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -753,3 +853,108 @@ void UDPSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respons response.getUdpSourceReport()->setBufferGauge(getBufferGauge()); response.getUdpSourceReport()->setChannelSampleRate(m_outputSampleRate); } + +void UDPSource::webapiReverseSendSettings(QList& channelSettingsKeys, const UDPSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("UDPSource")); + swgChannelSettings->setUdpSourceSettings(new SWGSDRangel::SWGUDPSourceSettings()); + SWGSDRangel::SWGUDPSourceSettings *swgUDPSourceSettings = swgChannelSettings->getUdpSourceSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("sampleFormat") || force) { + swgUDPSourceSettings->setSampleFormat((int) settings.m_sampleFormat); + } + if (channelSettingsKeys.contains("inputSampleRate") || force) { + swgUDPSourceSettings->setInputSampleRate(settings.m_inputSampleRate); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgUDPSourceSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgUDPSourceSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("lowCutoff") || force) { + swgUDPSourceSettings->setLowCutoff(settings.m_lowCutoff); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgUDPSourceSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("amModFactor") || force) { + swgUDPSourceSettings->setAmModFactor(settings.m_amModFactor); + } + if (channelSettingsKeys.contains("channelMute") || force) { + swgUDPSourceSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("gainIn") || force) { + swgUDPSourceSettings->setGainIn(settings.m_gainIn); + } + if (channelSettingsKeys.contains("gainOut") || force) { + swgUDPSourceSettings->setGainOut(settings.m_gainOut); + } + if (channelSettingsKeys.contains("squelch") || force) { + swgUDPSourceSettings->setSquelch(settings.m_squelch); + } + if (channelSettingsKeys.contains("squelchGate") || force) { + swgUDPSourceSettings->setSquelchGate(settings.m_squelchGate); + } + if (channelSettingsKeys.contains("squelchEnabled") || force) { + swgUDPSourceSettings->setSquelchEnabled(settings.m_squelchEnabled ? 1 : 0); + } + if (channelSettingsKeys.contains("autoRWBalance") || force) { + swgUDPSourceSettings->setAutoRwBalance(settings.m_autoRWBalance ? 1 : 0); + } + if (channelSettingsKeys.contains("stereoInput") || force) { + swgUDPSourceSettings->setStereoInput(settings.m_stereoInput ? 1 : 0); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgUDPSourceSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgUDPSourceSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgUDPSourceSettings->setUdpPort(settings.m_udpPort); + } + if (channelSettingsKeys.contains("title") || force) { + swgUDPSourceSettings->setTitle(new QString(settings.m_title)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void UDPSource::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "UDPSource::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("UDPSource::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/udpsource/udpsource.h b/plugins/channeltx/udpsource/udpsource.h index 809834f82..ea2e3d443 100644 --- a/plugins/channeltx/udpsource/udpsource.h +++ b/plugins/channeltx/udpsource/udpsource.h @@ -18,6 +18,7 @@ #define PLUGINS_CHANNELTX_UDPSINK_UDPSOURCE_H_ #include +#include #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" @@ -31,6 +32,8 @@ #include "udpsourcesettings.h" #include "udpsourceudphandler.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; @@ -138,6 +141,9 @@ signals: */ void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); +private slots: + void networkManagerFinished(QNetworkReply *reply); + private: class MsgUDPSourceSpectrum : public Message { MESSAGE_CLASS_DECLARATION @@ -226,6 +232,9 @@ private: Complex* m_SSBFilterBuffer; int m_SSBFilterBufferIndex; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + QMutex m_settingsMutex; static const int m_sampleRateAverageItems = 17; @@ -239,6 +248,7 @@ private: void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const UDPSourceSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const UDPSourceSettings& settings, bool force); inline void calculateSquelch(double value) { diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 08ad2942a..78db91231 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -472,11 +472,22 @@ void UDPSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/channeltx/udpsource/udpsourceplugin.cpp b/plugins/channeltx/udpsource/udpsourceplugin.cpp index 2727ae797..98f44ae95 100644 --- a/plugins/channeltx/udpsource/udpsourceplugin.cpp +++ b/plugins/channeltx/udpsource/udpsourceplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor UDPSourcePlugin::m_pluginDescriptor = { QString("UDP Channel Source"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/udpsource/udpsourcesettings.cpp b/plugins/channeltx/udpsource/udpsourcesettings.cpp index c7acbd0a5..9853ddcab 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.cpp +++ b/plugins/channeltx/udpsource/udpsourcesettings.cpp @@ -50,6 +50,11 @@ void UDPSourceSettings::resetToDefaults() m_udpPort = 9998; m_rgbColor = QColor(225, 25, 99).rgb(); m_title = "UDP Sample Source"; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray UDPSourceSettings::serialize() const @@ -79,6 +84,11 @@ QByteArray UDPSourceSettings::serialize() const s.writeString(18, m_udpAddress); s.writeU32(19, m_udpPort); s.writeString(20, m_title); + s.writeBool(21, m_useReverseAPI); + s.writeString(22, m_reverseAPIAddress); + s.writeU32(23, m_reverseAPIPort); + s.writeU32(24, m_reverseAPIDeviceIndex); + s.writeU32(25, m_reverseAPIChannelIndex); return s.final(); } @@ -156,6 +166,21 @@ bool UDPSourceSettings::deserialize(const QByteArray& data) d.readString(20, &m_title, "UDP Sample Sink"); + d.readBool(21, &m_useReverseAPI, false); + d.readString(22, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(23, &u32tmp, 0); + + if ((u32tmp > 1023) && (u32tmp < 65535)) { + m_reverseAPIPort = u32tmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(24, &u32tmp, 0); + m_reverseAPIDeviceIndex = u32tmp > 99 ? 99 : u32tmp; + d.readU32(25, &u32tmp, 0); + m_reverseAPIChannelIndex = u32tmp > 99 ? 99 : u32tmp; + return true; } else diff --git a/plugins/channeltx/udpsource/udpsourcesettings.h b/plugins/channeltx/udpsource/udpsourcesettings.h index 4a7be431f..14d2c57f6 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.h +++ b/plugins/channeltx/udpsource/udpsourcesettings.h @@ -58,6 +58,12 @@ struct UDPSourceSettings QString m_title; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/samplesink/CMakeLists.txt b/plugins/samplesink/CMakeLists.txt index a57d121a4..ecedfd0d6 100644 --- a/plugins/samplesink/CMakeLists.txt +++ b/plugins/samplesink/CMakeLists.txt @@ -29,7 +29,7 @@ if(CM256CC_FOUND) endif(CM256CC_FOUND) find_package(SoapySDR) -if(LIBUSB_FOUND AND SOAPYSDR_FOUND) +if(SOAPYSDR_FOUND) add_subdirectory(soapysdroutput) endif() diff --git a/plugins/samplesink/bladerf1output/bladerf1output.cpp b/plugins/samplesink/bladerf1output/bladerf1output.cpp index 9255cf3c3..0c4eb5693 100644 --- a/plugins/samplesink/bladerf1output/bladerf1output.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1output.cpp @@ -14,11 +14,12 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "bladerf1output.h" - #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -29,6 +30,7 @@ #include "device/devicesourceapi.h" #include "bladerf1/devicebladerf1shared.h" #include "bladerf1outputthread.h" +#include "bladerf1output.h" MESSAGE_CLASS_DEFINITION(Bladerf1Output::MsgConfigureBladerf1, Message) MESSAGE_CLASS_DEFINITION(Bladerf1Output::MsgStartStop, Message) @@ -45,11 +47,19 @@ Bladerf1Output::Bladerf1Output(DeviceSinkAPI *deviceAPI) : m_sampleSourceFifo.resize(16*BLADERFOUTPUT_BLOCKSIZE); openDevice(); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } Bladerf1Output::~Bladerf1Output() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + closeDevice(); m_deviceAPI->setBuddySharedPtr(0); } @@ -276,6 +286,10 @@ bool Bladerf1Output::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -289,10 +303,21 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool bool forwardChange = false; bool suspendOwnThread = false; bool threadWasRunning = false; + QList reverseAPIKeys; // QMutexLocker mutexLocker(&m_mutex); qDebug() << "BladerfOutput::applySettings: m_dev: " << m_dev; + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); + } + if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); + } + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2Interp != settings.m_log2Interp) || force) { @@ -337,12 +362,9 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool { unsigned int actualSamplerate; - if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_TX, settings.m_devSampleRate, &actualSamplerate) < 0) - { + if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_TX, settings.m_devSampleRate, &actualSamplerate) < 0) { qCritical("BladerfOutput::applySettings: could not set sample rate: %d", settings.m_devSampleRate); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: bladerf_set_sample_rate(BLADERF_MODULE_TX) actual sample rate is " << actualSamplerate; } } @@ -361,14 +383,13 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_vga1 != settings.m_vga1) || force) { + reverseAPIKeys.append("vga1"); + if (m_dev != 0) { - if(bladerf_set_txvga1(m_dev, settings.m_vga1) != 0) - { + if (bladerf_set_txvga1(m_dev, settings.m_vga1) != 0) { qDebug("BladerfOutput::applySettings: bladerf_set_txvga1() failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: VGA1 gain set to " << settings.m_vga1; } } @@ -376,14 +397,13 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_vga2 != settings.m_vga2) || force) { + reverseAPIKeys.append("vga2"); + if(m_dev != 0) { - if(bladerf_set_txvga2(m_dev, settings.m_vga2) != 0) - { + if (bladerf_set_txvga2(m_dev, settings.m_vga2) != 0) { qDebug("BladerfOutput::applySettings:bladerf_set_rxvga2() failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: VGA2 gain set to " << settings.m_vga2; } } @@ -391,6 +411,8 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_xb200 != settings.m_xb200) || force) { + reverseAPIKeys.append("xb200"); + if (m_dev != 0) { bool changeSettings; @@ -399,12 +421,9 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool { DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[0]; - if (buddy->getDeviceSourceEngine()->state() == DSPDeviceSourceEngine::StRunning) // Tx side running - { + if (buddy->getDeviceSourceEngine()->state() == DSPDeviceSourceEngine::StRunning) { // Tx side running changeSettings = false; - } - else - { + } else { changeSettings = true; } } @@ -417,23 +436,17 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool { if (settings.m_xb200) { - if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0) - { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0) { qDebug("BladerfOutput::applySettings: bladerf_expansion_attach(xb200) failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: Attach XB200"; } } else { - if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0) - { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0) { qDebug("BladerfOutput::applySettings: bladerf_expansion_attach(none) failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: Detach XB200"; } } @@ -445,14 +458,13 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_xb200Path != settings.m_xb200Path) || force) { + reverseAPIKeys.append("xb200Path"); + if (m_dev != 0) { - if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_TX, settings.m_xb200Path) != 0) - { + if (bladerf_xb200_set_path(m_dev, BLADERF_MODULE_TX, settings.m_xb200Path) != 0) { qDebug("BladerfOutput::applySettings: bladerf_xb200_set_path(BLADERF_MODULE_TX) failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: set xb200 path to " << settings.m_xb200Path; } } @@ -460,14 +472,13 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_xb200Filter != settings.m_xb200Filter) || force) { + reverseAPIKeys.append("xb200Filter"); + if (m_dev != 0) { - if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_TX, settings.m_xb200Filter) != 0) - { + if (bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_TX, settings.m_xb200Filter) != 0) { qDebug("BladerfOutput::applySettings: bladerf_xb200_set_filterbank(BLADERF_MODULE_TX) failed"); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: set xb200 filter to " << settings.m_xb200Filter; } } @@ -475,16 +486,15 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { - if(m_dev != 0) + reverseAPIKeys.append("bandwidth"); + + if (m_dev != 0) { unsigned int actualBandwidth; - if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_TX, settings.m_bandwidth, &actualBandwidth) < 0) - { + if (bladerf_set_bandwidth(m_dev, BLADERF_MODULE_TX, settings.m_bandwidth, &actualBandwidth) < 0) { qCritical("BladerfOutput::applySettings: could not set bandwidth: %d", settings.m_bandwidth); - } - else - { + } else { qDebug() << "BladerfOutput::applySettings: bladerf_set_bandwidth(BLADERF_MODULE_TX) actual bandwidth is " << actualBandwidth; } } @@ -495,7 +505,7 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool forwardChange = true; } - if (m_dev != NULL) + if (m_dev != 0) { if (bladerf_set_frequency( m_dev, BLADERF_MODULE_TX, settings.m_centerFrequency ) != 0) { @@ -508,15 +518,16 @@ bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool m_bladerfThread->startWork(); } - m_settings.m_centerFrequency = settings.m_centerFrequency; - m_settings.m_bandwidth = settings.m_bandwidth; - m_settings.m_xb200Filter = settings.m_xb200Filter; - m_settings.m_xb200 = settings.m_xb200; - m_settings.m_xb200Path = settings.m_xb200Path; - m_settings.m_vga2 = settings.m_vga2; - m_settings.m_vga1 = settings.m_vga1; - m_settings.m_devSampleRate = settings.m_devSampleRate; - m_settings.m_log2Interp = settings.m_log2Interp; + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; if (forwardChange) { @@ -635,3 +646,91 @@ int Bladerf1Output::webapiRun( return 200; } +void Bladerf1Output::webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF1OutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("BladeRF1")); + swgDeviceSettings->setBladeRf1OutputSettings(new SWGSDRangel::SWGBladeRF1OutputSettings()); + SWGSDRangel::SWGBladeRF1OutputSettings *swgBladeRF1OutputSettings = swgDeviceSettings->getBladeRf1OutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgBladeRF1OutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgBladeRF1OutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("vga1") || force) { + swgBladeRF1OutputSettings->setVga1(settings.m_vga1); + } + if (deviceSettingsKeys.contains("vga2") || force) { + swgBladeRF1OutputSettings->setVga2(settings.m_vga2); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgBladeRF1OutputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("log2Interp") || force) { + swgBladeRF1OutputSettings->setLog2Interp(settings.m_log2Interp); + } + if (deviceSettingsKeys.contains("xb200") || force) { + swgBladeRF1OutputSettings->setXb200(settings.m_xb200 ? 1 : 0); + } + if (deviceSettingsKeys.contains("xb200Path") || force) { + swgBladeRF1OutputSettings->setXb200Path((int) settings.m_xb200Path); + } + if (deviceSettingsKeys.contains("xb200Filter") || force) { + swgBladeRF1OutputSettings->setXb200Filter((int) settings.m_xb200Filter); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void Bladerf1Output::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void Bladerf1Output::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "Bladerf1Output::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("Bladerf1Output::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/bladerf1output/bladerf1output.h b/plugins/samplesink/bladerf1output/bladerf1output.h index 2a71e825d..834030e3e 100644 --- a/plugins/samplesink/bladerf1output/bladerf1output.h +++ b/plugins/samplesink/bladerf1output/bladerf1output.h @@ -17,18 +17,22 @@ #ifndef INCLUDE_BLADERFOUTPUT_H #define INCLUDE_BLADERFOUTPUT_H -#include -#include #include +#include -#include "../../../devices/bladerf1/devicebladerf1.h" -#include "../../../devices/bladerf1/devicebladerf1param.h" +#include "libbladeRF.h" +#include "dsp/devicesamplesink.h" +#include "bladerf1/devicebladerf1.h" +#include "bladerf1/devicebladerf1param.h" #include "bladerf1outputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class Bladerf1OutputThread; class Bladerf1Output : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigureBladerf1 : public Message { MESSAGE_CLASS_DECLARATION @@ -128,11 +132,6 @@ public: QString& errorMessage); private: - bool openDevice(); - void closeDevice(); - bool applySettings(const BladeRF1OutputSettings& settings, bool force); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1OutputSettings& settings); - DeviceSinkAPI *m_deviceAPI; QMutex m_mutex; BladeRF1OutputSettings m_settings; @@ -141,6 +140,18 @@ private: QString m_deviceDescription; DeviceBladeRF1Params m_sharedParams; bool m_running; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const BladeRF1OutputSettings& settings, bool force); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1OutputSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF1OutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_BLADERFOUTPUT_H diff --git a/plugins/samplesink/bladerf1output/bladerf1output.pro b/plugins/samplesink/bladerf1output/bladerf1output.pro index a777d371e..edda3b0bb 100644 --- a/plugins/samplesink/bladerf1output/bladerf1output.pro +++ b/plugins/samplesink/bladerf1output/bladerf1output.pro @@ -18,7 +18,6 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBBLADERF = "C:\Programs\bladeRF" -CONFIG(MINGW64):LIBBLADERF = "C:\Programs\bladeRF" INCLUDEPATH += $$PWD INCLUDEPATH += ../../../exports INCLUDEPATH += ../../../sdrbase @@ -26,7 +25,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBBLADERF = "C:\Programs\bladeRF" INCLUDEPATH += $$LIBBLADERF/include } @@ -57,7 +56,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L$$LIBBLADERF/lib -lbladeRF } diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index d94afe77d..538fe8529 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -22,6 +22,8 @@ #include "ui_bladerf1outputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" @@ -59,6 +61,9 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -406,3 +411,22 @@ unsigned int Bladerf1OutputGui::getXb200Index(bool xb_200, bladerf_xb200_path xb return 0; } } + +void Bladerf1OutputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.h b/plugins/samplesink/bladerf1output/bladerf1outputgui.h index a3dc5988a..a8ced210c 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.h @@ -84,6 +84,7 @@ private slots: void on_startStop_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_BLADERFOUTPUTGUI_H diff --git a/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp b/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp index cfcbc4a92..b21619603 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor Bladerf1OutputPlugin::m_pluginDescriptor = { QString("BladeRF1 Output"), - QString("4.2.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp index 1ff12929c..fcf544597 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp @@ -36,6 +36,10 @@ void BladeRF1OutputSettings::resetToDefaults() m_xb200 = false; m_xb200Path = BLADERF_XB200_MIX; m_xb200Filter = BLADERF_XB200_AUTO_1DB; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray BladeRF1OutputSettings::serialize() const @@ -50,6 +54,10 @@ QByteArray BladeRF1OutputSettings::serialize() const s.writeBool(6, m_xb200); s.writeS32(7, (int) m_xb200Path); s.writeS32(8, (int) m_xb200Filter); + s.writeBool(9, m_useReverseAPI); + s.writeString(10, m_reverseAPIAddress); + s.writeU32(11, m_reverseAPIPort); + s.writeU32(12, m_reverseAPIDeviceIndex); return s.final(); } @@ -67,6 +75,7 @@ bool BladeRF1OutputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_devSampleRate); d.readS32(2, &m_vga1); @@ -78,6 +87,18 @@ bool BladeRF1OutputSettings::deserialize(const QByteArray& data) m_xb200Path = (bladerf_xb200_path) intval; d.readS32(8, &intval); m_xb200Filter = (bladerf_xb200_filter) intval; + d.readBool(9, &m_useReverseAPI, false); + d.readString(10, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(11, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(12, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h index 07b1115ed..30ad7f5d0 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h @@ -18,6 +18,8 @@ #define _BLADERF_BLADERFOUTPUTSETTINGS_H_ #include +#include + #include struct BladeRF1OutputSettings { @@ -30,6 +32,10 @@ struct BladeRF1OutputSettings { bool m_xb200; bladerf_xb200_path m_xb200Path; bladerf_xb200_filter m_xb200Filter; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; BladeRF1OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/bladerf2output/bladerf2output.cpp b/plugins/samplesink/bladerf2output/bladerf2output.cpp index caea1d0a5..abc8cdcb0 100644 --- a/plugins/samplesink/bladerf2output/bladerf2output.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2output.cpp @@ -17,7 +17,10 @@ #include #include + #include +#include +#include #include "SWGDeviceState.h" #include "SWGDeviceSettings.h" @@ -48,10 +51,15 @@ BladeRF2Output::BladeRF2Output(DeviceSinkAPI *deviceAPI) : m_running(false) { openDevice(); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } BladeRF2Output::~BladeRF2Output() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -675,6 +683,10 @@ bool BladeRF2Output::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -688,6 +700,7 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool bool forwardChangeOwnDSP = false; bool forwardChangeRxBuddies = false; bool forwardChangeTxBuddies = false; + QList reverseAPIKeys; struct bladerf *dev = m_deviceShared.m_dev->getDev(); int requestedChannel = m_deviceAPI->getItemIndex(); @@ -698,6 +711,7 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("devSampleRate"); BladeRF2OutputThread *bladeRF2OutputThread = findThread(); SampleSourceFifo *fifo = 0; @@ -755,6 +769,7 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); forwardChangeTxBuddies = true; if (dev != 0) @@ -776,6 +791,7 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); forwardChangeOwnDSP = true; BladeRF2OutputThread *outputThread = findThread(); @@ -786,6 +802,19 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) @@ -812,12 +841,14 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool if ((m_settings.m_biasTee != settings.m_biasTee) || force) { + reverseAPIKeys.append("biasTee"); forwardChangeTxBuddies = true; m_deviceShared.m_dev->setBiasTeeTx(settings.m_biasTee); } if ((m_settings.m_globalGain != settings.m_globalGain) || force) { + reverseAPIKeys.append("globalGain"); forwardChangeTxBuddies = true; if (dev) @@ -877,6 +908,15 @@ bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; qDebug() << "BladeRF2Output::applySettings: " @@ -1056,3 +1096,91 @@ int BladeRF2Output::webapiRun( return 200; } +void BladeRF2Output::webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2OutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("BladeRF2")); + swgDeviceSettings->setBladeRf2OutputSettings(new SWGSDRangel::SWGBladeRF2OutputSettings()); + SWGSDRangel::SWGBladeRF2OutputSettings *swgBladeRF2OutputSettings = swgDeviceSettings->getBladeRf2OutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgBladeRF2OutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgBladeRF2OutputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgBladeRF2OutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgBladeRF2OutputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("log2Interp") || force) { + swgBladeRF2OutputSettings->setLog2Interp(settings.m_log2Interp); + } + if (deviceSettingsKeys.contains("biasTee") || force) { + swgBladeRF2OutputSettings->setBiasTee(settings.m_biasTee ? 1 : 0); + } + if (deviceSettingsKeys.contains("globalGain") || force) { + swgBladeRF2OutputSettings->setGlobalGain(settings.m_globalGain); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgBladeRF2OutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgBladeRF2OutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void BladeRF2Output::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void BladeRF2Output::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "BladeRF2Output::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("BladeRF2Output::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/bladerf2output/bladerf2output.h b/plugins/samplesink/bladerf2output/bladerf2output.h index 9b501aeaa..7d5f4c7ce 100644 --- a/plugins/samplesink/bladerf2output/bladerf2output.h +++ b/plugins/samplesink/bladerf2output/bladerf2output.h @@ -18,17 +18,22 @@ #define PLUGINS_SAMPLESINK_BLADERF2OUTPUT_BLADERF2OUTPUT_H_ #include +#include + #include #include "dsp/devicesamplesink.h" #include "bladerf2/devicebladerf2shared.h" #include "bladerf2outputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class BladeRF2OutputThread; struct bladerf_gain_modes; class BladeRF2Output : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigureBladeRF2 : public Message { MESSAGE_CLASS_DECLARATION @@ -147,6 +152,17 @@ public: QString& errorMessage); private: + DeviceSinkAPI *m_deviceAPI; + QMutex m_mutex; + BladeRF2OutputSettings m_settings; + struct bladerf* m_dev; + BladeRF2OutputThread* m_thread; + QString m_deviceDescription; + DeviceBladeRF2Shared m_deviceShared; + bool m_running; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + bool openDevice(); void closeDevice(); BladeRF2OutputThread *findThread(); @@ -156,15 +172,11 @@ private: bool setDeviceCenterFrequency(struct bladerf *dev, int requestedChannel, quint64 freq_hz, int loPpmTenths); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF2OutputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2OutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); - DeviceSinkAPI *m_deviceAPI; - QMutex m_mutex; - BladeRF2OutputSettings m_settings; - struct bladerf* m_dev; - BladeRF2OutputThread* m_thread; - QString m_deviceDescription; - DeviceBladeRF2Shared m_deviceShared; - bool m_running; +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* PLUGINS_SAMPLESINK_BLADERF2OUTPUT_BLADERF2OUTPUT_H_ */ diff --git a/plugins/samplesink/bladerf2output/bladerf2output.pro b/plugins/samplesink/bladerf2output/bladerf2output.pro index 74f0859a2..8fc7ce06e 100644 --- a/plugins/samplesink/bladerf2output/bladerf2output.pro +++ b/plugins/samplesink/bladerf2output/bladerf2output.pro @@ -24,7 +24,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBBLADERF = "C:\Programs\bladeRF" INCLUDEPATH += $$LIBBLADERF/include } @@ -55,7 +55,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L$$LIBBLADERF/lib -lbladeRF } diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index 283a68cb0..06a77316d 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -22,6 +22,8 @@ #include "ui_bladerf2outputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" @@ -71,6 +73,9 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -368,3 +373,21 @@ void BladeRF2OutputGui::updateStatus() } } +void BladeRF2OutputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.h b/plugins/samplesink/bladerf2output/bladerf2outputgui.h index 7c3b9be78..1655194a0 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.h @@ -86,6 +86,7 @@ private slots: void on_transverter_clicked(); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp b/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp index 32c222514..8307b331f 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor BladeRF2OutputPlugin::m_pluginDescriptor = { QString("BladeRF2 Output"), - QString("4.3.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp index 2ea428ef5..27f8cd29f 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp @@ -36,6 +36,10 @@ void BladeRF2OutputSettings::resetToDefaults() m_log2Interp = 0; m_transverterMode = false; m_transverterDeltaFrequency = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray BladeRF2OutputSettings::serialize() const @@ -50,6 +54,10 @@ QByteArray BladeRF2OutputSettings::serialize() const s.writeU32(6, m_log2Interp); s.writeBool(7, m_transverterMode); s.writeS64(8, m_transverterDeltaFrequency); + s.writeBool(9, m_useReverseAPI); + s.writeString(10, m_reverseAPIAddress); + s.writeU32(11, m_reverseAPIPort); + s.writeU32(12, m_reverseAPIDeviceIndex); return s.final(); } @@ -66,6 +74,8 @@ bool BladeRF2OutputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { + uint32_t uintval; + d.readS32(1, &m_devSampleRate); d.readS32(2, &m_bandwidth); d.readS32(3, &m_LOppmTenths); @@ -74,6 +84,18 @@ bool BladeRF2OutputSettings::deserialize(const QByteArray& data) d.readU32(6, &m_log2Interp); d.readBool(7, &m_transverterMode, false); d.readS64(8, &m_transverterDeltaFrequency, 0); + d.readBool(9, &m_useReverseAPI, false); + d.readString(10, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(11, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(12, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h index b83cc380e..a2bca08dd 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h @@ -18,6 +18,7 @@ #define PLUGINS_SAMPLESINK_BLADERF2OUTPUT_BLADERF2OUTPUTSETTINGS_H_ #include +#include struct BladeRF2OutputSettings { quint64 m_centerFrequency; @@ -29,6 +30,10 @@ struct BladeRF2OutputSettings { quint32 m_log2Interp; bool m_transverterMode; qint64 m_transverterDeltaFrequency; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; BladeRF2OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutput.cpp b/plugins/samplesink/hackrfoutput/hackrfoutput.cpp index 00f0137cd..238f57213 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutput.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutput.cpp @@ -14,11 +14,12 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "hackrfoutput.h" - #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -29,8 +30,8 @@ #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" #include "hackrf/devicehackrfshared.h" - #include "hackrfoutputthread.h" +#include "hackrfoutput.h" MESSAGE_CLASS_DEFINITION(HackRFOutput::MsgConfigureHackRF, Message) MESSAGE_CLASS_DEFINITION(HackRFOutput::MsgStartStop, Message) @@ -46,11 +47,19 @@ HackRFOutput::HackRFOutput(DeviceSinkAPI *deviceAPI) : { openDevice(); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } HackRFOutput::~HackRFOutput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + closeDevice(); m_deviceAPI->setBuddySharedPtr(0); } @@ -264,6 +273,10 @@ bool HackRFOutput::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -297,8 +310,26 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc bool suspendThread = false; bool threadWasRunning = false; hackrf_error rc; + QList reverseAPIKeys; - qDebug() << "HackRFOutput::applySettings"; + qDebug() << "HackRFOutput::applySettings" + << " m_centerFrequency: " << m_settings.m_centerFrequency + << " m_LOppmTenths: " << m_settings.m_LOppmTenths + << " m_bandwidth: " << m_settings.m_bandwidth + << " m_devSampleRate: " << m_settings.m_devSampleRate + << " m_log2Interp: " << m_settings.m_log2Interp + << " m_biasT: " << m_settings.m_biasT + << " m_lnaExt: " << m_settings.m_lnaExt + << " m_vgaGain: " << m_settings.m_vgaGain + << " m_useReverseAPI: " << m_settings.m_useReverseAPI + << " m_reverseAPIAddress: " << m_settings.m_reverseAPIAddress + << " m_reverseAPIPort: " << m_settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << m_settings.m_reverseAPIDeviceIndex + << " force: " << force; + + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); + } if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2Interp != settings.m_log2Interp) || force) @@ -349,6 +380,8 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); + if (m_hackRFThread != 0) { m_hackRFThread->setLog2Interpolation(settings.m_log2Interp); @@ -356,6 +389,13 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_LOppmTenths != settings.m_LOppmTenths)) { @@ -370,16 +410,15 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc if ((m_settings.m_vgaGain != settings.m_vgaGain) || force) { + reverseAPIKeys.append("vgaGain"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_txvga_gain(m_dev, settings.m_vgaGain); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFOutput::applySettings: hackrf_set_txvga_gain failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFOutput:applySettings: TxVGA gain set to " << settings.m_vgaGain; } } @@ -387,17 +426,16 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); + if (m_dev != 0) { uint32_t bw_index = hackrf_compute_baseband_filter_bw_round_down_lt(settings.m_bandwidth + 1); // +1 so the round down to lower than yields desired bandwidth rc = (hackrf_error) hackrf_set_baseband_filter_bandwidth(m_dev, bw_index); - if (rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_baseband_filter_bandwidth failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: Baseband BW filter set to " << settings.m_bandwidth << " Hz"; } } @@ -405,16 +443,15 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc if ((m_settings.m_biasT != settings.m_biasT) || force) { + reverseAPIKeys.append("biasT"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_antenna_enable(m_dev, (settings.m_biasT ? 1 : 0)); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_antenna_enable failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: bias tee set to " << settings.m_biasT; } } @@ -422,16 +459,15 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc if ((m_settings.m_lnaExt != settings.m_lnaExt) || force) { + reverseAPIKeys.append("lnaExt"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_amp_enable(m_dev, (settings.m_lnaExt ? 1 : 0)); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_amp_enable failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: extra LNA set to " << settings.m_lnaExt; } } @@ -442,14 +478,16 @@ bool HackRFOutput::applySettings(const HackRFOutputSettings& settings, bool forc m_hackRFThread->startWork(); } - m_settings.m_devSampleRate = settings.m_devSampleRate; - m_settings.m_log2Interp = settings.m_log2Interp; - m_settings.m_centerFrequency = settings.m_centerFrequency; - m_settings.m_LOppmTenths = settings.m_LOppmTenths; - m_settings.m_vgaGain = settings.m_vgaGain; - m_settings.m_bandwidth = settings.m_bandwidth; - m_settings.m_biasT = settings.m_biasT; - m_settings.m_lnaExt = settings.m_lnaExt; + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; if (forwardChange) { @@ -559,3 +597,88 @@ int HackRFOutput::webapiRun( return 200; } +void HackRFOutput::webapiReverseSendSettings(QList& deviceSettingsKeys, const HackRFOutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("HackRF")); + swgDeviceSettings->setHackRfOutputSettings(new SWGSDRangel::SWGHackRFOutputSettings()); + SWGSDRangel::SWGHackRFOutputSettings *swgHackRFOutputSettings = swgDeviceSettings->getHackRfOutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgHackRFOutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgHackRFOutputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgHackRFOutputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("vgaGain") || force) { + swgHackRFOutputSettings->setVgaGain(settings.m_vgaGain); + } + if (deviceSettingsKeys.contains("log2Interp") || force) { + swgHackRFOutputSettings->setLog2Interp(settings.m_log2Interp); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgHackRFOutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("biasT") || force) { + swgHackRFOutputSettings->setBiasT(settings.m_biasT ? 1 : 0); + } + if (deviceSettingsKeys.contains("lnaExt") || force) { + swgHackRFOutputSettings->setLnaExt(settings.m_lnaExt ? 1 : 0); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void HackRFOutput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void HackRFOutput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "HackRFOutput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("HackRFOutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/hackrfoutput/hackrfoutput.h b/plugins/samplesink/hackrfoutput/hackrfoutput.h index 73c6a430b..b234a08fd 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutput.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutput.h @@ -17,18 +17,22 @@ #ifndef INCLUDE_HACKRFOUTPUT_H #define INCLUDE_HACKRFOUTPUT_H +#include +#include + #include "dsp/devicesamplesink.h" #include "libhackrf/hackrf.h" -#include - #include "hackrf/devicehackrf.h" #include "hackrf/devicehackrfparam.h" #include "hackrfoutputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class HackRFOutputThread; class HackRFOutput : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigureHackRF : public Message { @@ -129,13 +133,6 @@ public: QString& errorMessage); private: - bool openDevice(); - void closeDevice(); - bool applySettings(const HackRFOutputSettings& settings, bool force); -// hackrf_device *open_hackrf_from_sequence(int sequence); - void setDeviceCenterFrequency(quint64 freq_hz, qint32 LOppmTenths); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const HackRFOutputSettings& settings); - DeviceSinkAPI *m_deviceAPI; QMutex m_mutex; HackRFOutputSettings m_settings; @@ -143,7 +140,21 @@ private: HackRFOutputThread* m_hackRFThread; QString m_deviceDescription; DeviceHackRFParams m_sharedParams; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + bool m_running; + bool openDevice(); + void closeDevice(); + bool applySettings(const HackRFOutputSettings& settings, bool force); +// hackrf_device *open_hackrf_from_sequence(int sequence); + void setDeviceCenterFrequency(quint64 freq_hz, qint32 LOppmTenths); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const HackRFOutputSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const HackRFOutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_HACKRFINPUT_H diff --git a/plugins/samplesink/hackrfoutput/hackrfoutput.pro b/plugins/samplesink/hackrfoutput/hackrfoutput.pro index 910fbb579..c19eacd30 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutput.pro +++ b/plugins/samplesink/hackrfoutput/hackrfoutput.pro @@ -18,7 +18,6 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBHACKRFSRC = "C:\softs\hackrf\host" -CONFIG(MINGW64):LIBHACKRFSRC = "C:\softs\hackrf\host" CONFIG(MSVC):LIBHACKRFSRC = "C:\softs\hackrf\host" INCLUDEPATH += $$PWD diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index 153df00d9..28189e187 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -14,8 +14,6 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "hackrfoutputgui.h" - #include #include @@ -23,6 +21,8 @@ #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" @@ -31,6 +31,7 @@ #include "hackrf/devicehackrfvalues.h" #include "hackrf/devicehackrfshared.h" +#include "hackrfoutputgui.h" #include "ui_hackrfoutputgui.h" HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : @@ -56,6 +57,9 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); displayBandwidths(); sendSettings(); @@ -329,7 +333,7 @@ void HackRFOutputGui::updateHardware() qDebug() << "HackRFOutputGui::updateHardware"; HackRFOutput::MsgConfigureHackRF* message = HackRFOutput::MsgConfigureHackRF::create(m_settings, m_forceSettings); m_deviceSampleSink->getInputMessageQueue()->push(message); - m_forceSettings = true; + m_forceSettings = false; m_updateTimer.stop(); } } @@ -363,3 +367,22 @@ void HackRFOutputGui::updateStatus() m_lastEngineState = state; } } + +void HackRFOutputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index fdcba768e..921efc1c2 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -17,10 +17,10 @@ #ifndef INCLUDE_HACKRFOUTPUTGUI_H #define INCLUDE_HACKRFOUTPUTGUI_H -#include #include #include +#include "plugin/plugininstancegui.h" #include "util/messagequeue.h" #include "hackrfoutput.h" @@ -95,6 +95,7 @@ private slots: void on_startStop_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_HACKRFOUTPUTGUI_H diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp index d15ae3580..427b1075e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp @@ -35,6 +35,10 @@ void HackRFOutputSettings::resetToDefaults() m_vgaGain = 22; m_bandwidth = 1750000; m_devSampleRate = 2400000; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray HackRFOutputSettings::serialize() const @@ -48,6 +52,10 @@ QByteArray HackRFOutputSettings::serialize() const s.writeU32(6, m_vgaGain); s.writeU32(7, m_bandwidth); s.writeU64(8, m_devSampleRate); + s.writeBool(9, m_useReverseAPI); + s.writeString(10, m_reverseAPIAddress); + s.writeU32(11, m_reverseAPIPort); + s.writeU32(12, m_reverseAPIDeviceIndex); return s.final(); } @@ -64,6 +72,8 @@ bool HackRFOutputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { + uint32_t uintval; + d.readS32(1, &m_LOppmTenths, 0); d.readBool(3, &m_biasT, false); d.readU32(4, &m_log2Interp, 0); @@ -71,6 +81,18 @@ bool HackRFOutputSettings::deserialize(const QByteArray& data) d.readU32(6, &m_vgaGain, 30); d.readU32(7, &m_bandwidth, 1750000); d.readU64(8, &m_devSampleRate, 2400000); + d.readBool(9, &m_useReverseAPI, false); + d.readString(10, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(11, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(12, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h index 610e9f4e4..29d32368b 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h @@ -18,6 +18,7 @@ #define _HACKRF_HACKRFOUTPUTSETTINGS_H_ #include +#include struct HackRFOutputSettings { quint64 m_centerFrequency; @@ -28,6 +29,10 @@ struct HackRFOutputSettings { quint64 m_devSampleRate; bool m_biasT; bool m_lnaExt; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; HackRFOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/limesdroutput/limesdroutput.cpp b/plugins/samplesink/limesdroutput/limesdroutput.cpp index d0b82ee5d..655893a8c 100644 --- a/plugins/samplesink/limesdroutput/limesdroutput.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutput.cpp @@ -14,10 +14,14 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include #include + +#include +#include +#include +#include + #include "lime/LimeSuite.h" #include "SWGDeviceSettings.h" @@ -57,11 +61,19 @@ LimeSDROutput::LimeSDROutput(DeviceSinkAPI *deviceAPI) : openDevice(); resumeTxBuddies(); resumeRxBuddies(); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } LimeSDROutput::~LimeSDROutput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + suspendRxBuddies(); suspendTxBuddies(); closeDevice(); @@ -524,6 +536,10 @@ bool LimeSDROutput::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (DeviceLimeSDRShared::MsgReportBuddyChange::match(message)) @@ -721,6 +737,7 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo bool doCalibration = false; bool doLPCalibration = false; double clockGenFreq = 0.0; + QList reverseAPIKeys; // QMutexLocker mutexLocker(&m_mutex); qint64 deviceCenterFrequency = settings.m_centerFrequency; @@ -740,6 +757,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_gain != settings.m_gain) || force) { + reverseAPIKeys.append("gain"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (LMS_SetGaindB(m_deviceShared.m_deviceParams->getDevice(), @@ -760,6 +779,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force) { + reverseAPIKeys.append("devSampleRate"); + reverseAPIKeys.append("log2HardInterp"); forwardChangeAllDSP = true; //m_settings.m_devSampleRate != settings.m_devSampleRate; if (m_deviceShared.m_deviceParams->getDevice() != 0) @@ -789,6 +810,9 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force) { + reverseAPIKeys.append("devSampleRate"); + reverseAPIKeys.append("log2SoftInterp"); + int fifoSize = (std::max)( (int) ((settings.m_devSampleRate/(1<getDevice() != 0 && m_channelAcquired) { doLPCalibration = true; @@ -807,6 +833,9 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) || (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force) { + reverseAPIKeys.append("lpfFIRBW"); + reverseAPIKeys.append("lpfFIREnable"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (LMS_SetGFIRLPF(m_deviceShared.m_deviceParams->getDevice(), @@ -832,6 +861,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) || (m_settings.m_ncoEnable != settings.m_ncoEnable) || force || forceNCOFrequency) { + reverseAPIKeys.append("ncoFrequency"); + reverseAPIKeys.append("ncoEnable"); forwardChangeOwnDSP = true; if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -859,6 +890,7 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force) { + reverseAPIKeys.append("log2SoftInterp"); forwardChangeOwnDSP = true; m_deviceShared.m_log2Soft = settings.m_log2SoftInterp; // for buddies @@ -871,6 +903,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_antennaPath != settings.m_antennaPath) || force) { + reverseAPIKeys.append("antennaPath"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (DeviceLimeSDR::setTxAntennaPath(m_deviceShared.m_deviceParams->getDevice(), @@ -894,6 +928,9 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + reverseAPIKeys.append("transverterMode"); + reverseAPIKeys.append("transverterDeltaFrequency"); forwardChangeTxDSP = true; if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -914,6 +951,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_extClock != settings.m_extClock) || (settings.m_extClock && (m_settings.m_extClockFreq != settings.m_extClockFreq)) || force) { + reverseAPIKeys.append("extClock"); + reverseAPIKeys.append("extClockFreq"); if (DeviceLimeSDR::setClockSource(m_deviceShared.m_deviceParams->getDevice(), settings.m_extClock, @@ -938,6 +977,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo { if ((m_settings.m_gpioDir != settings.m_gpioDir) || force) { + reverseAPIKeys.append("gpioDir"); + if (LMS_GPIODirWrite(m_deviceShared.m_deviceParams->getDevice(), &settings.m_gpioDir, 1) != 0) { qCritical("LimeSDROutput::applySettings: could not set GPIO directions to %u", settings.m_gpioDir); @@ -951,6 +992,8 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo if ((m_settings.m_gpioPins != settings.m_gpioPins) || force) { + reverseAPIKeys.append("gpioPins"); + if (LMS_GPIOWrite(m_deviceShared.m_deviceParams->getDevice(), &settings.m_gpioPins, 1) != 0) { qCritical("LimeSDROutput::applySettings: could not set GPIO pins to %u", settings.m_gpioPins); @@ -963,6 +1006,15 @@ bool LimeSDROutput::applySettings(const LimeSDROutputSettings& settings, bool fo } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; double clockGenFreqAfter; @@ -1244,6 +1296,18 @@ int LimeSDROutput::webapiSettingsPutPatch( if (deviceSettingsKeys.contains("gpioPins")) { settings.m_gpioPins = response.getLimeSdrOutputSettings()->getGpioPins() & 0xFF; } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getLimeSdrOutputSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getLimeSdrOutputSettings()->getReverseApiAddress() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getLimeSdrOutputSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getLimeSdrOutputSettings()->getReverseApiDeviceIndex(); + } MsgConfigureLimeSDR *msg = MsgConfigureLimeSDR::create(settings, force); m_inputMessageQueue.push(msg); @@ -1287,6 +1351,16 @@ void LimeSDROutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& r response.getLimeSdrOutputSettings()->setTransverterMode(settings.m_transverterMode ? 1 : 0); response.getLimeSdrOutputSettings()->setGpioDir(settings.m_gpioDir); response.getLimeSdrOutputSettings()->setGpioPins(settings.m_gpioPins); + response.getLimeSdrOutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getLimeSdrOutputSettings()->getReverseApiAddress()) { + *response.getLimeSdrOutputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getLimeSdrOutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getLimeSdrOutputSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getLimeSdrOutputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); } int LimeSDROutput::webapiRunGet( @@ -1356,3 +1430,116 @@ void LimeSDROutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respo response.getLimeSdrOutputReport()->setGpioDir(gpioDir); response.getLimeSdrOutputReport()->setGpioPins(gpioPins); } + +void LimeSDROutput::webapiReverseSendSettings(QList& deviceSettingsKeys, const LimeSDROutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("LimeSDR")); + swgDeviceSettings->setLimeSdrOutputSettings(new SWGSDRangel::SWGLimeSdrOutputSettings()); + SWGSDRangel::SWGLimeSdrOutputSettings *swgLimeSdrOutputSettings = swgDeviceSettings->getLimeSdrOutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("antennaPath") || force) { + swgLimeSdrOutputSettings->setAntennaPath((int) settings.m_antennaPath); + } + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgLimeSdrOutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgLimeSdrOutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("extClock") || force) { + swgLimeSdrOutputSettings->setExtClock(settings.m_extClock ? 1 : 0); + } + if (deviceSettingsKeys.contains("extClockFreq") || force) { + swgLimeSdrOutputSettings->setExtClockFreq(settings.m_extClockFreq); + } + if (deviceSettingsKeys.contains("gain") || force) { + swgLimeSdrOutputSettings->setGain(settings.m_gain); + } + if (deviceSettingsKeys.contains("log2HardInterp") || force) { + swgLimeSdrOutputSettings->setLog2HardInterp(settings.m_log2HardInterp); + } + if (deviceSettingsKeys.contains("log2SoftInterp") || force) { + swgLimeSdrOutputSettings->setLog2SoftInterp(settings.m_log2SoftInterp); + } + if (deviceSettingsKeys.contains("lpfBW") || force) { + swgLimeSdrOutputSettings->setLpfBw(settings.m_lpfBW); + } + if (deviceSettingsKeys.contains("lpfFIREnable") || force) { + swgLimeSdrOutputSettings->setLpfFirEnable(settings.m_lpfFIREnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("lpfFIRBW") || force) { + swgLimeSdrOutputSettings->setLpfFirbw(settings.m_lpfFIRBW); + } + if (deviceSettingsKeys.contains("ncoEnable") || force) { + swgLimeSdrOutputSettings->setNcoEnable(settings.m_ncoEnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("ncoFrequency") || force) { + swgLimeSdrOutputSettings->setNcoFrequency(settings.m_ncoFrequency); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgLimeSdrOutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgLimeSdrOutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("gpioDir") || force) { + swgLimeSdrOutputSettings->setGpioDir(settings.m_gpioDir & 0xFF); + } + if (deviceSettingsKeys.contains("gpioPins") || force) { + swgLimeSdrOutputSettings->setGpioPins(settings.m_gpioPins & 0xFF); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void LimeSDROutput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void LimeSDROutput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "LimeSDROutput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("LimeSDROutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/limesdroutput/limesdroutput.h b/plugins/samplesink/limesdroutput/limesdroutput.h index e4979c5ca..f94b7f6a5 100644 --- a/plugins/samplesink/limesdroutput/limesdroutput.h +++ b/plugins/samplesink/limesdroutput/limesdroutput.h @@ -17,19 +17,24 @@ #ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_ #define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_ -#include #include +#include +#include + #include "dsp/devicesamplesink.h" #include "limesdr/devicelimesdrshared.h" #include "limesdroutputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class LimeSDROutputThread; struct DeviceLimeSDRParams; class LimeSDROutput : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigureLimeSDR : public Message { MESSAGE_CLASS_DECLARATION @@ -235,8 +240,9 @@ private: bool m_running; DeviceLimeSDRShared m_deviceShared; bool m_channelAcquired; - lms_stream_t m_streamId; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -249,6 +255,11 @@ private: bool applySettings(const LimeSDROutputSettings& settings, bool force = false, bool forceNCOFrequency = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const LimeSDROutputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const LimeSDROutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUT_H_ */ diff --git a/plugins/samplesink/limesdroutput/limesdroutput.pro b/plugins/samplesink/limesdroutput/limesdroutput.pro index 8f4f60b51..638a66363 100644 --- a/plugins/samplesink/limesdroutput/limesdroutput.pro +++ b/plugins/samplesink/limesdroutput/limesdroutput.pro @@ -18,7 +18,6 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBLIMESUITESRC = "C:\softs\LimeSuite" -CONFIG(MINGW64):LIBLIMESUITESRC = "C:\softs\LimeSuite" INCLUDEPATH += $$PWD INCLUDEPATH += ../../../exports @@ -27,7 +26,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 || macx { +MINGW32 || macx { INCLUDEPATH += ../../../liblimesuite/srcmw INCLUDEPATH += $$LIBLIMESUITESRC/src INCLUDEPATH += $$LIBLIMESUITESRC/src/ADF4002 @@ -67,7 +66,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 || macx { +MINGW32 || macx { LIBS += -L../../../liblimesuite/$${build_subdir} -lliblimesuite } MSVC { diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index f5aa0b9c6..f74f0f40b 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -20,6 +20,8 @@ #include "ui_limesdroutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" @@ -77,6 +79,9 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + sendSettings(); } @@ -561,4 +566,21 @@ void LimeSDROutputGUI::on_transverter_clicked() sendSettings(); } +void LimeSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index bfa1147b6..9f341454f 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -94,6 +94,7 @@ private slots: void on_antenna_currentIndexChanged(int index); void on_extClock_clicked(); void on_transverter_clicked(); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index 4764834d6..80dc81946 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -34,7 +34,7 @@ const PluginDescriptor LimeSDROutputPlugin::m_pluginDescriptor = { QString("LimeSDR Output"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp index 3983126af..8286ae8e5 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp @@ -42,6 +42,10 @@ void LimeSDROutputSettings::resetToDefaults() m_transverterDeltaFrequency = 0; m_gpioDir = 0; m_gpioPins = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray LimeSDROutputSettings::serialize() const @@ -64,6 +68,10 @@ QByteArray LimeSDROutputSettings::serialize() const s.writeS64(17, m_transverterDeltaFrequency); s.writeU32(18, m_gpioDir); s.writeU32(19, m_gpioPins); + s.writeBool(20, m_useReverseAPI); + s.writeString(21, m_reverseAPIAddress); + s.writeU32(22, m_reverseAPIPort); + s.writeU32(23, m_reverseAPIDeviceIndex); return s.final(); } @@ -102,6 +110,18 @@ bool LimeSDROutputSettings::deserialize(const QByteArray& data) m_gpioDir = uintval & 0xFF; d.readU32(19, &uintval, 0); m_gpioPins = uintval & 0xFF; + d.readBool(20, &m_useReverseAPI, false); + d.readString(21, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(22, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(23, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.h b/plugins/samplesink/limesdroutput/limesdroutputsettings.h index f29ac63ba..501adc1da 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.h +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.h @@ -17,9 +17,11 @@ #ifndef PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTSETTINGS_H_ #define PLUGINS_SAMPLESOURCE_LIMESDROUTPUT_LIMESDROUTPUTSETTINGS_H_ -#include #include +#include +#include + /** * These are the settings individual to each hardware channel or software Tx chain * Plus the settings to be saved in the presets @@ -58,6 +60,10 @@ struct LimeSDROutputSettings qint64 m_transverterDeltaFrequency; uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output uint8_t m_gpioPins; //!< GPIO pins to write; LSB first + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; LimeSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.cpp b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp index 54157a930..ec727f0a3 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutput.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutput.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -54,10 +56,16 @@ PlutoSDROutput::PlutoSDROutput(DeviceSinkAPI *deviceAPI) : suspendBuddies(); openDevice(); resumeBuddies(); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } PlutoSDROutput::~PlutoSDROutput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + suspendBuddies(); closeDevice(); resumeBuddies(); @@ -210,6 +218,10 @@ bool PlutoSDROutput::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -315,6 +327,7 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool bool suspendAllOtherThreads = false; // All others means Rx in fact DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox(); QLocale loc; + QList reverseAPIKeys; qDebug().noquote() << "PlutoSDROutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" << " m_devSampleRate: " << loc.toString(m_settings.m_devSampleRate) << "S/s" @@ -331,6 +344,25 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool << " m_transverterDeltaFrequency: " << m_settings.m_transverterDeltaFrequency << " force: " << force; + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); + } + if ((m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force) { + reverseAPIKeys.append("lpfFIREnable"); + } + if ((m_settings.m_lpfFIRlog2Interp != settings.m_lpfFIRlog2Interp) || force) { + reverseAPIKeys.append("lpfFIRlog2Interp"); + } + if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) || force) { + reverseAPIKeys.append("lpfFIRBW"); + } + if ((m_settings.m_lpfFIRGain != settings.m_lpfFIRGain) || force) { + reverseAPIKeys.append("lpfFIRGain"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + // determine if buddies threads or own thread need to be suspended // changes affecting all buddies can occur if @@ -401,6 +433,7 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); m_sampleSourceFifo.resize((32*PLUTOSDR_BLOCKSIZE_SAMPLES)/(1< params; bool paramsToSet = false; + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) @@ -442,12 +485,14 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool if ((m_settings.m_lpfBW != settings.m_lpfBW) || force) { + reverseAPIKeys.append("lpfBW"); params.push_back(QString(tr("out_voltage_rf_bandwidth=%1").arg(settings.m_lpfBW)).toStdString()); paramsToSet = true; } if ((m_settings.m_antennaPath != settings.m_antennaPath) || force) { + reverseAPIKeys.append("antennaPath"); QString rfPortStr; PlutoSDROutputSettings::translateRFPath(settings.m_antennaPath, rfPortStr); params.push_back(QString(tr("out_voltage0_rf_port_select=%1").arg(rfPortStr)).toStdString()); @@ -456,6 +501,7 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool if ((m_settings.m_att != settings.m_att) || force) { + reverseAPIKeys.append("att"); float attF = settings.m_att * 0.25f; params.push_back(QString(tr("out_voltage0_hardwaregain=%1").arg(attF)).toStdString()); paramsToSet = true; @@ -466,6 +512,15 @@ bool PlutoSDROutput::applySettings(const PlutoSDROutputSettings& settings, bool plutoBox->set_params(DevicePlutoSDRBox::DEVICE_PHY, params); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; if (suspendAllOtherThreads) @@ -687,3 +742,104 @@ void PlutoSDROutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& resp fetchTemperature(); response.getPlutoSdrOutputReport()->setTemperature(getTemperature()); } + +void PlutoSDROutput::webapiReverseSendSettings(QList& deviceSettingsKeys, const PlutoSDROutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("PlutoSDR")); + swgDeviceSettings->setPlutoSdrOutputSettings(new SWGSDRangel::SWGPlutoSdrOutputSettings()); + SWGSDRangel::SWGPlutoSdrOutputSettings *swgPlutoSdrOutputSettings = swgDeviceSettings->getPlutoSdrOutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgPlutoSdrOutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgPlutoSdrOutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgPlutoSdrOutputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("lpfFIREnable") || force) { + swgPlutoSdrOutputSettings->setLpfFirEnable(settings.m_lpfFIREnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("lpfFIRBW") || force) { + swgPlutoSdrOutputSettings->setLpfFirbw(settings.m_lpfFIRBW); + } + if (deviceSettingsKeys.contains("lpfFIRlog2Interp") || force) { + swgPlutoSdrOutputSettings->setLpfFiRlog2Interp(settings.m_lpfFIRlog2Interp); + } + if (deviceSettingsKeys.contains("lpfFIRGain") || force) { + swgPlutoSdrOutputSettings->setLpfFirGain(settings.m_lpfFIRGain); + } + if (deviceSettingsKeys.contains("log2Interp") || force) { + swgPlutoSdrOutputSettings->setLog2Interp(settings.m_log2Interp); + } + if (deviceSettingsKeys.contains("lpfBW") || force) { + swgPlutoSdrOutputSettings->setLpfBw(settings.m_lpfBW); + } + if (deviceSettingsKeys.contains("att") || force) { + swgPlutoSdrOutputSettings->setAtt(settings.m_att); + } + if (deviceSettingsKeys.contains("antennaPath") || force) { + swgPlutoSdrOutputSettings->setAntennaPath((int) settings.m_antennaPath); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgPlutoSdrOutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgPlutoSdrOutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void PlutoSDROutput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void PlutoSDROutput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "PlutoSDROutput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("PlutoSDROutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.h b/plugins/samplesink/plutosdroutput/plutosdroutput.h index b5512edc8..abc20902d 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutput.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutput.h @@ -18,18 +18,22 @@ #define PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDRINPUT_H_ #include +#include #include "iio.h" -#include +#include "dsp/devicesamplesink.h" #include "util/message.h" #include "plutosdr/deviceplutosdrshared.h" #include "plutosdr/deviceplutosdrbox.h" #include "plutosdroutputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class PlutoSDROutputThread; class PlutoSDROutput : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigurePlutoSDR : public Message { MESSAGE_CLASS_DECLARATION @@ -131,6 +135,8 @@ public: PlutoSDROutputThread *m_plutoSDROutputThread; DevicePlutoSDRBox::SampleRates m_deviceSampleRates; QMutex m_mutex; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -139,6 +145,11 @@ public: bool applySettings(const PlutoSDROutputSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const PlutoSDROutputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const PlutoSDROutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutput.pro b/plugins/samplesink/plutosdroutput/plutosdroutput.pro index 1cee810e4..dd71cdce8 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutput.pro +++ b/plugins/samplesink/plutosdroutput/plutosdroutput.pro @@ -24,7 +24,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBIIOSRC = "C:\softs\libiio" INCLUDEPATH += ../../../libiio/includemw INCLUDEPATH += $$LIBIIOSRC @@ -56,7 +56,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L../../../libiio/$${build_subdir} -llibiio } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index 4d96665e6..68fd31011 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -21,6 +21,8 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "device/devicesinkapi.h" #include "device/deviceuiset.h" #include "plutosdr/deviceplutosdr.h" @@ -59,6 +61,9 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) ui->swInterpLabel->setText(QString::fromUtf8("S\u2191")); ui->lpFIRInterpolationLabel->setText(QString::fromUtf8("\u2191")); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + blockApplySettings(true); displaySettings(); blockApplySettings(false); @@ -436,3 +441,22 @@ void PlutoSDROutputGUI::updateSampleRateAndFrequency() m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } + +void PlutoSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index 6ca6f2d20..cd037466b 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -92,6 +92,7 @@ private slots: void updateHardware(); void updateStatus(); void handleInputMessages(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif /* PLUGINS_SAMPLESOURCE_PLUTOSDROUTPUT_PLUTOSDROUTPUTGUI_H_ */ diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 99d3b4259..e1fc9d661 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -30,7 +30,7 @@ class DeviceSourceAPI; const PluginDescriptor PlutoSDROutputPlugin::m_pluginDescriptor = { QString("PlutoSDR Output"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp index 90e1652e8..158a8b713 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp @@ -39,6 +39,10 @@ void PlutoSDROutputSettings::resetToDefaults() m_antennaPath = RFPATH_A; m_transverterMode = false; m_transverterDeltaFrequency = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray PlutoSDROutputSettings::serialize() const @@ -57,6 +61,10 @@ QByteArray PlutoSDROutputSettings::serialize() const s.writeS32(14, (int) m_antennaPath); s.writeBool(15, m_transverterMode); s.writeS64(16, m_transverterDeltaFrequency); + s.writeBool(17, m_useReverseAPI); + s.writeString(18, m_reverseAPIAddress); + s.writeU32(19, m_reverseAPIPort); + s.writeU32(20, m_reverseAPIDeviceIndex); return s.final(); } @@ -98,6 +106,18 @@ bool PlutoSDROutputSettings::deserialize(const QByteArray& data) } d.readBool(15, &m_transverterMode, false); d.readS64(16, &m_transverterDeltaFrequency, 0); + d.readBool(17, &m_useReverseAPI, false); + d.readString(18, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(19, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(20, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h index 672dbd4fd..39125bde3 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h @@ -17,9 +17,11 @@ #ifndef _PLUTOSDR_PLUTOSDROUTPUTSETTINGS_H_ #define _PLUTOSDR_PLUTOSDROUTPUTSETTINGS_H_ -#include #include +#include +#include + struct PlutoSDROutputSettings { enum RFPath { @@ -44,7 +46,10 @@ struct PlutoSDROutputSettings { RFPath m_antennaPath; bool m_transverterMode; qint64 m_transverterDeltaFrequency; - + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; PlutoSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsink.pro b/plugins/samplesink/sdrdaemonsink/sdrdaemonsink.pro index edec1918d..d259e75ac 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsink.pro +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsink.pro @@ -12,7 +12,6 @@ QT += core gui widgets multimedia network opengl TARGET = outputsdrdaemonsink CONFIG(MINGW32):LIBCM256CCSRC = "C:\softs\cm256cc" -CONFIG(MINGW64):LIBCM256CCSRC = "C:\softs\cm256cc" CONFIG(MSVC):LIBCM256CCSRC = "C:\softs\cm256cc" CONFIG(macx):LIBCM256CCSRC = "../../../../deps/cm256cc" @@ -36,7 +35,6 @@ CONFIG(Release):build_subdir = release CONFIG(Debug):build_subdir = debug CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../boost_1_69_0" diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp index b7eb7e9ff..a37743009 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp @@ -15,7 +15,6 @@ /////////////////////////////////////////////////////////////////////////////////// #include - #include #include #include @@ -31,6 +30,8 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -95,6 +96,9 @@ SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : displayEventCounts(); displayEventTimer(); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); sendSettings(); } @@ -624,3 +628,22 @@ void SDRdaemonSinkGui::analyzeApiReply(const QJsonObject& jsonObject) ui->infoText->setText(infoLine); } } + +void SDRdaemonSinkGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h index 4204dfc16..772f137fe 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h @@ -154,6 +154,7 @@ private slots: void updateStatus(); void tick(); void networkManagerFinished(QNetworkReply *reply); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_FILESINKGUI_H diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp index 7f9c9ab62..7ff09249d 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -217,6 +218,10 @@ bool SDRdaemonSinkOutput::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgConfigureSDRdaemonSinkChunkCorrection::match(message)) @@ -241,6 +246,20 @@ void SDRdaemonSinkOutput::applySettings(const SDRdaemonSinkSettings& settings, b QMutexLocker mutexLocker(&m_mutex); bool forwardChange = false; bool changeTxDelay = false; + QList reverseAPIKeys; + + if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) { + reverseAPIKeys.append("dataAddress"); + } + if ((m_settings.m_dataPort != settings.m_dataPort) || force) { + reverseAPIKeys.append("dataPort"); + } + if ((m_settings.m_apiAddress != settings.m_apiAddress) || force) { + reverseAPIKeys.append("apiAddress"); + } + if ((m_settings.m_apiPort != settings.m_apiPort) || force) { + reverseAPIKeys.append("apiPort"); + } if (force || (m_settings.m_dataAddress != settings.m_dataAddress) || (m_settings.m_dataPort != settings.m_dataPort)) { @@ -251,6 +270,8 @@ void SDRdaemonSinkOutput::applySettings(const SDRdaemonSinkSettings& settings, b if (force || (m_settings.m_sampleRate != settings.m_sampleRate)) { + reverseAPIKeys.append("sampleRate"); + if (m_sdrDaemonSinkThread != 0) { m_sdrDaemonSinkThread->setSamplerate(settings.m_sampleRate); } @@ -264,6 +285,8 @@ void SDRdaemonSinkOutput::applySettings(const SDRdaemonSinkSettings& settings, b if (force || (m_settings.m_nbFECBlocks != settings.m_nbFECBlocks)) { + reverseAPIKeys.append("nbFECBlocks"); + if (m_sdrDaemonSinkThread != 0) { m_sdrDaemonSinkThread->setNbBlocksFEC(settings.m_nbFECBlocks); } @@ -273,6 +296,7 @@ void SDRdaemonSinkOutput::applySettings(const SDRdaemonSinkSettings& settings, b if (force || (m_settings.m_txDelay != settings.m_txDelay)) { + reverseAPIKeys.append("txDelay"); changeTxDelay = true; } @@ -300,6 +324,15 @@ void SDRdaemonSinkOutput::applySettings(const SDRdaemonSinkSettings& settings, b m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -461,7 +494,7 @@ void SDRdaemonSinkOutput::networkManagerFinished(QNetworkReply *reply) if (error.error == QJsonParseError::NoError) { - analyzeApiReply(doc.object()); + analyzeApiReply(doc.object(), answer); } else { @@ -476,7 +509,7 @@ void SDRdaemonSinkOutput::networkManagerFinished(QNetworkReply *reply) } } -void SDRdaemonSinkOutput::analyzeApiReply(const QJsonObject& jsonObject) +void SDRdaemonSinkOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& answer) { if (jsonObject.contains("DaemonSourceReport")) { @@ -546,6 +579,10 @@ void SDRdaemonSinkOutput::analyzeApiReply(const QJsonObject& jsonObject) m_lastSampleCount = sampleCount; m_lastQueueLength = queueLength; } + else if (jsonObject.contains("sdrDaemonSinkSettings")) + { + qDebug("SDRdaemonSinkOutput::analyzeApiReply: reply:\n%s", answer.toStdString().c_str()); + } } void SDRdaemonSinkOutput::sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount) @@ -559,3 +596,74 @@ void SDRdaemonSinkOutput::sampleRateCorrection(double remoteTimeDeltaUs, double MsgConfigureSDRdaemonSinkChunkCorrection* message = MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection); getInputMessageQueue()->push(message); } + +void SDRdaemonSinkOutput::webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRdaemonSinkSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("SDRdaemonSink")); + swgDeviceSettings->setSdrDaemonSinkSettings(new SWGSDRangel::SWGSDRdaemonSinkSettings()); + SWGSDRangel::SWGSDRdaemonSinkSettings *swgSdrDaemonSinkSettings = swgDeviceSettings->getSdrDaemonSinkSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("sampleRate") || force) { + swgSdrDaemonSinkSettings->setSampleRate(settings.m_sampleRate); + } + if (deviceSettingsKeys.contains("txDelay") || force) { + swgSdrDaemonSinkSettings->setTxDelay(settings.m_txDelay); + } + if (deviceSettingsKeys.contains("nbFECBlocks") || force) { + swgSdrDaemonSinkSettings->setNbFecBlocks(settings.m_nbFECBlocks); + } + if (deviceSettingsKeys.contains("apiAddress") || force) { + swgSdrDaemonSinkSettings->setApiAddress(new QString(settings.m_apiAddress)); + } + if (deviceSettingsKeys.contains("apiPort") || force) { + swgSdrDaemonSinkSettings->setApiPort(settings.m_apiPort); + } + if (deviceSettingsKeys.contains("dataAddress") || force) { + swgSdrDaemonSinkSettings->setDataAddress(new QString(settings.m_dataAddress)); + } + if (deviceSettingsKeys.contains("dataPort") || force) { + swgSdrDaemonSinkSettings->setDataPort(settings.m_dataPort); + } + if (deviceSettingsKeys.contains("deviceIndex") || force) { + swgSdrDaemonSinkSettings->setDeviceIndex(settings.m_deviceIndex); + } + if (deviceSettingsKeys.contains("channelIndex") || force) { + swgSdrDaemonSinkSettings->setChannelIndex(settings.m_channelIndex); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void SDRdaemonSinkOutput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h index 93ce45d5e..f0940324a 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h @@ -193,8 +193,10 @@ private: void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRdaemonSinkSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); - void analyzeApiReply(const QJsonObject& jsonObject); + void analyzeApiReply(const QJsonObject& jsonObject, const QString& answer); void sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRdaemonSinkSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); private slots: void tick(); diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index 33c7f05c8..05e294324 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor SDRdaemonSinkPlugin::m_pluginDescriptor = { QString("SDRdaemon sink output"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp index 8467b259d..f81813dd6 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.cpp @@ -34,6 +34,10 @@ void SDRdaemonSinkSettings::resetToDefaults() m_dataPort = 9090; m_deviceIndex = 0; m_channelIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray SDRdaemonSinkSettings::serialize() const @@ -50,6 +54,10 @@ QByteArray SDRdaemonSinkSettings::serialize() const s.writeU32(8, m_dataPort); s.writeU32(10, m_deviceIndex); s.writeU32(11, m_channelIndex); + s.writeBool(12, m_useReverseAPI); + s.writeString(13, m_reverseAPIAddress); + s.writeU32(14, m_reverseAPIPort); + s.writeU32(15, m_reverseAPIDeviceIndex); return s.final(); } @@ -80,6 +88,18 @@ bool SDRdaemonSinkSettings::deserialize(const QByteArray& data) m_dataPort = uintval % (1<<16); d.readU32(10, &m_deviceIndex, 0); d.readU32(11, &m_channelIndex, 0); + d.readBool(12, &m_useReverseAPI, false); + d.readString(13, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(14, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(15, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h index 88176fdec..8ba59ddeb 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinksettings.h @@ -30,6 +30,10 @@ struct SDRdaemonSinkSettings { quint16 m_dataPort; quint32 m_deviceIndex; quint32 m_channelIndex; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; SDRdaemonSinkSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp index d6effe288..675d70176 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGSoapySDROutputSettings.h" @@ -47,10 +49,16 @@ SoapySDROutput::SoapySDROutput(DeviceSinkAPI *deviceAPI) : initTunableElementsSettings(m_settings); initStreamArgSettings(m_settings); initDeviceArgSettings(m_settings); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SoapySDROutput::~SoapySDROutput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -482,6 +490,9 @@ bool SoapySDROutput::start() ((DeviceSoapySDRShared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0); } + delete[] log2Interps; + delete[] fifos; + needsStart = true; } else @@ -609,6 +620,9 @@ void SoapySDROutput::stop() qDebug("SoapySDROutput::stop: restarting the thread"); soapySDROutputThread->startWork(); } + + delete[] log2Interps; + delete[] fifos; } else // remove channel from existing thread { @@ -754,6 +768,10 @@ bool SoapySDROutput::handleMessage(const Message& message) m_deviceAPI->stopGeneration(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (DeviceSoapySDRShared::MsgReportBuddyChange::match(message)) @@ -825,6 +843,7 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool bool globalGainChanged = false; bool individualGainsChanged = false; bool deviceArgsChanged = false; + QList reverseAPIKeys; SoapySDR::Device *dev = m_deviceShared.m_device; SoapySDROutputThread *outputThread = findThread(); @@ -867,6 +886,7 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); forwardChangeOwnDSP = true; forwardChangeToBuddies = true; @@ -898,6 +918,7 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); forwardChangeOwnDSP = true; if (outputThread != 0) @@ -907,6 +928,19 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) @@ -924,6 +958,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_antenna != settings.m_antenna) || force) { + reverseAPIKeys.append("antenna"); + if (dev != 0) { try @@ -941,6 +977,7 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); forwardChangeToBuddies = true; if (dev != 0) @@ -985,6 +1022,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_globalGain != settings.m_globalGain) || force) { + reverseAPIKeys.append("globalGain"); + if (dev != 0) { try @@ -1029,6 +1068,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_autoGain != settings.m_autoGain) || force) { + reverseAPIKeys.append("autoGain"); + if (dev != 0) { try @@ -1045,6 +1086,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_autoDCCorrection != settings.m_autoDCCorrection) || force) { + reverseAPIKeys.append("autoDCCorrection"); + if ((dev != 0) && hasDCAutoCorrection()) { try @@ -1061,6 +1104,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_dcCorrection != settings.m_dcCorrection) || force) { + reverseAPIKeys.append("dcCorrection"); + if ((dev != 0) && hasDCCorrectionValue()) { try @@ -1077,6 +1122,8 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + if ((dev != 0) && hasIQCorrectionValue()) { try @@ -1199,6 +1246,20 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + + if (fullUpdate || force) { + webapiReverseSendSettings(reverseAPIKeys, settings, true); + } else if (reverseAPIKeys.size() != 0) { + webapiReverseSendSettings(reverseAPIKeys, settings, false); + } + } + m_settings = settings; if (globalGainChanged || individualGainsChanged) @@ -1718,3 +1779,114 @@ void SoapySDROutput::webapiFormatArgInfo(const SoapySDR::ArgInfo& arg, SWGSDRang argInfo->getOptionNames()->append(new QString(itOpt.c_str())); } } + +void SoapySDROutput::webapiReverseSendSettings(QList& deviceSettingsKeys, const SoapySDROutputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(1); + swgDeviceSettings->setDeviceHwType(new QString("SoapySDR")); + swgDeviceSettings->setSoapySdrOutputSettings(new SWGSDRangel::SWGSoapySDROutputSettings()); + swgDeviceSettings->getSoapySdrOutputSettings()->init(); + SWGSDRangel::SWGSoapySDROutputSettings *swgSoapySDROutputSettings = swgDeviceSettings->getSoapySdrOutputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgSoapySDROutputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgSoapySDROutputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgSoapySDROutputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgSoapySDROutputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("log2Interp") || force) { + swgSoapySDROutputSettings->setLog2Interp(settings.m_log2Interp); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgSoapySDROutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgSoapySDROutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("antenna") || force) { + swgSoapySDROutputSettings->setAntenna(new QString(settings.m_antenna)); + } + if (deviceSettingsKeys.contains("globalGain") || force) { + swgSoapySDROutputSettings->setGlobalGain(settings.m_globalGain); + } + if (deviceSettingsKeys.contains("autoGain") || force) { + swgSoapySDROutputSettings->setAutoGain(settings.m_autoGain ? 1 : 0); + } + if (deviceSettingsKeys.contains("autoDCCorrection") || force) { + swgSoapySDROutputSettings->setAutoDcCorrection(settings.m_autoDCCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("autoIQCorrection") || force) { + swgSoapySDROutputSettings->setAutoIqCorrection(settings.m_autoIQCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("dcCorrection") || force) + { + swgSoapySDROutputSettings->setDcCorrection(new SWGSDRangel::SWGComplex()); + swgSoapySDROutputSettings->getDcCorrection()->setReal(settings.m_dcCorrection.real()); + swgSoapySDROutputSettings->getDcCorrection()->setImag(settings.m_dcCorrection.imag()); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) + { + swgSoapySDROutputSettings->setIqCorrection(new SWGSDRangel::SWGComplex()); + swgSoapySDROutputSettings->getIqCorrection()->setReal(settings.m_iqCorrection.real()); + swgSoapySDROutputSettings->getIqCorrection()->setImag(settings.m_iqCorrection.imag()); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void SoapySDROutput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void SoapySDROutput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SoapySDROutput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SoapySDROutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.h b/plugins/samplesink/soapysdroutput/soapysdroutput.h index 7be726329..4572af548 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.h @@ -17,15 +17,19 @@ #ifndef PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUT_H_ #define PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUT_H_ +#include + #include #include -#include +#include #include "dsp/devicesamplesink.h" #include "soapysdr/devicesoapysdrshared.h" #include "soapysdroutputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSinkAPI; class SoapySDROutputThread; @@ -42,6 +46,7 @@ namespace SWGSDRangel } class SoapySDROutput : public DeviceSampleSink { + Q_OBJECT public: class MsgConfigureSoapySDROutput : public Message { MESSAGE_CLASS_DECLARATION @@ -182,6 +187,8 @@ private: bool m_running; SoapySDROutputThread *m_thread; DeviceSoapySDRShared m_deviceShared; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -196,6 +203,11 @@ private: QVariant webapiVariantFromArgValue(SWGSDRangel::SWGArgValue *argValue); void webapiFormatArgValue(const QVariant& v, SWGSDRangel::SWGArgValue *argValue); void webapiFormatArgInfo(const SoapySDR::ArgInfo& arg, SWGSDRangel::SWGArgInfo *argInfo); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const SoapySDROutputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index 07d67a66a..b88dee9b2 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -24,6 +24,8 @@ #include "util/simpleserializer.h" #include "ui_soapysdroutputgui.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "soapygui/discreterangegui.h" #include "soapygui/intervalrangegui.h" #include "soapygui/stringrangegui.h" @@ -87,6 +89,9 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -876,3 +881,21 @@ void SoapySDROutputGui::updateStatus() } } +void SoapySDROutputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index 655c25df2..b3695f2f5 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -138,6 +138,7 @@ private slots: void on_startStop_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif /* PLUGINS_SAMPLESINK_SOAPYSDROUTPUT_SOAPYSDROUTPUTGUI_H_ */ diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp index 87a641ed3..3cdd98365 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputplugin.cpp @@ -31,7 +31,7 @@ const PluginDescriptor SoapySDROutputPlugin::m_pluginDescriptor = { QString("SoapySDR Output"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp index 5e95ddb8b..b8956ae5f 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp @@ -42,6 +42,10 @@ void SoapySDROutputSettings::resetToDefaults() m_autoIQCorrection = false; m_dcCorrection = std::complex{0,0}; m_iqCorrection = std::complex{0,0}; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray SoapySDROutputSettings::serialize() const @@ -67,6 +71,10 @@ QByteArray SoapySDROutputSettings::serialize() const s.writeDouble(20, m_iqCorrection.imag()); s.writeBlob(21, serializeArgumentMap(m_streamArgSettings)); s.writeBlob(22, serializeArgumentMap(m_deviceArgSettings)); + s.writeBool(23, m_useReverseAPI); + s.writeString(24, m_reverseAPIAddress); + s.writeU32(25, m_reverseAPIPort); + s.writeU32(26, m_reverseAPIDeviceIndex); return s.final(); } @@ -85,6 +93,7 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) { QByteArray blob; double realval, imagval; + uint32_t uintval; d.readS32(1, &m_devSampleRate, 1024000); d.readS32(2, &m_LOppmTenths, 0); @@ -111,6 +120,18 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) deserializeArgumentMap(blob, m_streamArgSettings); d.readBlob(22, &blob); deserializeArgumentMap(blob, m_deviceArgSettings); + d.readBool(23, &m_useReverseAPI, false); + d.readString(24, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(25, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(26, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h index 110b38910..b1fcceb0d 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h @@ -40,6 +40,10 @@ struct SoapySDROutputSettings { std::complex m_iqCorrection; QMap m_streamArgSettings; QMap m_deviceArgSettings; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; SoapySDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 9e442fa11..1fbdd8aa9 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -78,7 +78,7 @@ else(LIBUSB_FOUND AND LIBMIRISDR_FOUND) endif(LIBUSB_FOUND AND LIBMIRISDR_FOUND) find_package(SoapySDR) -if(LIBUSB_FOUND AND SOAPYSDR_FOUND) +if(SOAPYSDR_FOUND) add_subdirectory(soapysdrinput) message(STATUS "SoapySDR found") else() diff --git a/plugins/samplesource/airspy/airspy.pro b/plugins/samplesource/airspy/airspy.pro index 600b51672..711d6624b 100644 --- a/plugins/samplesource/airspy/airspy.pro +++ b/plugins/samplesource/airspy/airspy.pro @@ -12,7 +12,6 @@ QT += core gui widgets multimedia opengl TARGET = inputairspy CONFIG(MINGW32):LIBAIRSPYSRC = "C:\softs\libairspy" -CONFIG(MINGW64):LIBAIRSPYSRC = "C:\softs\libairspy" CONFIG(MSVC):LIBAIRSPYSRC = "C:\softs\libairspy" INCLUDEPATH += $$PWD diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index ae59a6ca7..5b8ce1313 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -28,6 +28,8 @@ #include "ui_airspygui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -51,6 +53,9 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); m_rates = ((AirspyInput*) m_sampleSource)->getSampleRates(); @@ -454,3 +459,22 @@ int AirspyGui::getDevSampleRateIndex(uint32_t sampeRate) return -1; } + +void AirspyGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index aca2de1e0..af27c46c0 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -95,6 +95,7 @@ private slots: void updateHardware(); void updateStatus(); void handleInputMessages(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_AIRSPYGUI_H diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index 111ea4dcc..96bb04e4b 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -16,8 +16,11 @@ #include #include + #include #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -27,8 +30,8 @@ #include "airspyinput.h" #include "airspyplugin.h" -#include -#include +#include "device/devicesourceapi.h" +#include "dsp/filerecord.h" #include "dsp/dspcommands.h" #include "dsp/dspengine.h" #include "airspysettings.h" @@ -52,11 +55,20 @@ AirspyInput::AirspyInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } AirspyInput::~AirspyInput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -298,6 +310,10 @@ bool AirspyInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgFileRecord::match(message)) @@ -351,48 +367,47 @@ bool AirspyInput::applySettings(const AirspySettings& settings, bool force) bool forwardChange = false; airspy_error rc = AIRSPY_ERROR_OTHER; + QList reverseAPIKeys; qDebug() << "AirspyInput::applySettings"; + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); + } + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + } + if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { - m_settings.m_dcBlock = settings.m_dcBlock; - m_settings.m_iqCorrection = settings.m_iqCorrection; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force) { + reverseAPIKeys.append("devSampleRateIndex"); forwardChange = true; - if (settings.m_devSampleRateIndex < m_sampleRates.size()) - { - m_settings.m_devSampleRateIndex = settings.m_devSampleRateIndex; - } - else - { - m_settings.m_devSampleRateIndex = m_sampleRates.size() - 1; - } - if (m_dev != 0) { - rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(m_settings.m_devSampleRateIndex)); + rc = (airspy_error) airspy_set_samplerate(m_dev, static_cast(settings.m_devSampleRateIndex)); if (rc != AIRSPY_SUCCESS) { - qCritical("AirspyInput::applySettings: could not set sample rate index %u (%d S/s): %s", m_settings.m_devSampleRateIndex, m_sampleRates[m_settings.m_devSampleRateIndex], airspy_error_name(rc)); + qCritical("AirspyInput::applySettings: could not set sample rate index %u (%d S/s): %s", settings.m_devSampleRateIndex, m_sampleRates[settings.m_devSampleRateIndex], airspy_error_name(rc)); } else if (m_airspyThread != 0) { - qDebug("AirspyInput::applySettings: sample rate set to index: %u (%d S/s)", m_settings.m_devSampleRateIndex, m_sampleRates[m_settings.m_devSampleRateIndex]); - m_airspyThread->setSamplerate(m_sampleRates[m_settings.m_devSampleRateIndex]); + qDebug("AirspyInput::applySettings: sample rate set to index: %u (%d S/s)", settings.m_devSampleRateIndex, m_sampleRates[settings.m_devSampleRateIndex]); + m_airspyThread->setSamplerate(m_sampleRates[settings.m_devSampleRateIndex]); } } } if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChange = true; if (m_airspyThread != 0) @@ -402,6 +417,22 @@ bool AirspyInput::applySettings(const AirspySettings& settings, bool force) } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_fcPos != settings.m_fcPos) || force) { + reverseAPIKeys.append("fcPos"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_LOppmTenths != settings.m_LOppmTenths) || (m_settings.m_fcPos != settings.m_fcPos) @@ -414,15 +445,9 @@ bool AirspyInput::applySettings(const AirspySettings& settings, bool force) settings.m_transverterDeltaFrequency, settings.m_log2Decim, (DeviceSampleSource::fcPos_t) settings.m_fcPos, - m_sampleRates[m_settings.m_devSampleRateIndex], + m_sampleRates[settings.m_devSampleRateIndex], settings.m_transverterMode); - m_settings.m_centerFrequency = settings.m_centerFrequency; - m_settings.m_log2Decim = settings.m_log2Decim; - m_settings.m_transverterMode = settings.m_transverterMode; - m_settings.m_transverterDeltaFrequency = settings.m_transverterDeltaFrequency; - m_settings.m_LOppmTenths = settings.m_LOppmTenths; - if (m_dev != 0) { setDeviceCenterFrequency(deviceCenterFrequency); } @@ -432,129 +457,118 @@ bool AirspyInput::applySettings(const AirspySettings& settings, bool force) if ((m_settings.m_fcPos != settings.m_fcPos) || force) { - m_settings.m_fcPos = settings.m_fcPos; - if (m_airspyThread != 0) { - m_airspyThread->setFcPos((int) m_settings.m_fcPos); - qDebug() << "AirspyInput: set fc pos (enum) to " << (int) m_settings.m_fcPos; + m_airspyThread->setFcPos((int) settings.m_fcPos); + qDebug() << "AirspyInput: set fc pos (enum) to " << (int) settings.m_fcPos; } } if ((m_settings.m_lnaGain != settings.m_lnaGain) || force) { - m_settings.m_lnaGain = settings.m_lnaGain; + reverseAPIKeys.append("lnaGain"); if (m_dev != 0) { - rc = (airspy_error) airspy_set_lna_gain(m_dev, m_settings.m_lnaGain); + rc = (airspy_error) airspy_set_lna_gain(m_dev, settings.m_lnaGain); - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_lna_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: LNA gain set to " << m_settings.m_lnaGain; + } else { + qDebug() << "AirspyInput:applySettings: LNA gain set to " << settings.m_lnaGain; } } } if ((m_settings.m_lnaAGC != settings.m_lnaAGC) || force) { - m_settings.m_lnaAGC = settings.m_lnaAGC; + reverseAPIKeys.append("lnaAGC"); - if (m_dev != 0) - { - rc = (airspy_error) airspy_set_lna_agc(m_dev, (m_settings.m_lnaAGC ? 1 : 0)); + if (m_dev != 0) { + rc = (airspy_error) airspy_set_lna_agc(m_dev, (settings.m_lnaAGC ? 1 : 0)); } - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_lna_agc failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: LNA AGC set to " << m_settings.m_lnaAGC; + } else { + qDebug() << "AirspyInput:applySettings: LNA AGC set to " << settings.m_lnaAGC; } } if ((m_settings.m_mixerGain != settings.m_mixerGain) || force) { - m_settings.m_mixerGain = settings.m_mixerGain; + reverseAPIKeys.append("mixerGain"); if (m_dev != 0) { - rc = (airspy_error) airspy_set_mixer_gain(m_dev, m_settings.m_mixerGain); + rc = (airspy_error) airspy_set_mixer_gain(m_dev, settings.m_mixerGain); - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_mixer_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: mixer gain set to " << m_settings.m_mixerGain; + } else { + qDebug() << "AirspyInput:applySettings: mixer gain set to " << settings.m_mixerGain; } } } if ((m_settings.m_mixerAGC != settings.m_mixerAGC) || force) { - m_settings.m_mixerAGC = settings.m_mixerAGC; + reverseAPIKeys.append("mixerAGC"); - if (m_dev != 0) - { - rc = (airspy_error) airspy_set_mixer_agc(m_dev, (m_settings.m_mixerAGC ? 1 : 0)); + if (m_dev != 0) { + rc = (airspy_error) airspy_set_mixer_agc(m_dev, (settings.m_mixerAGC ? 1 : 0)); } - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_mixer_agc failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: Mixer AGC set to " << m_settings.m_mixerAGC; + } else { + qDebug() << "AirspyInput:applySettings: Mixer AGC set to " << settings.m_mixerAGC; } } if ((m_settings.m_vgaGain != settings.m_vgaGain) || force) { - m_settings.m_vgaGain = settings.m_vgaGain; + reverseAPIKeys.append("vgaGain"); if (m_dev != 0) { - rc = (airspy_error) airspy_set_vga_gain(m_dev, m_settings.m_vgaGain); + rc = (airspy_error) airspy_set_vga_gain(m_dev, settings.m_vgaGain); - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_vga_gain failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: VGA gain set to " << m_settings.m_vgaGain; + } else { + qDebug() << "AirspyInput:applySettings: VGA gain set to " << settings.m_vgaGain; } } } if ((m_settings.m_biasT != settings.m_biasT) || force) { - m_settings.m_biasT = settings.m_biasT; + reverseAPIKeys.append("biasT"); if (m_dev != 0) { - rc = (airspy_error) airspy_set_rf_bias(m_dev, (m_settings.m_biasT ? 1 : 0)); + rc = (airspy_error) airspy_set_rf_bias(m_dev, (settings.m_biasT ? 1 : 0)); - if(rc != AIRSPY_SUCCESS) - { + if (rc != AIRSPY_SUCCESS) { qDebug("AirspyInput::applySettings: airspy_set_rf_bias failed: %s", airspy_error_name(rc)); - } - else - { - qDebug() << "AirspyInput:applySettings: bias tee set to " << m_settings.m_biasT; + } else { + qDebug() << "AirspyInput:applySettings: bias tee set to " << settings.m_biasT; } } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + if (forwardChange) { int sampleRate = m_sampleRates[m_settings.m_devSampleRateIndex]/(1<getSampleRates()->back()->setRate(*it); } } + +void AirspyInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const AirspySettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("Airspy")); + swgDeviceSettings->setAirspySettings(new SWGSDRangel::SWGAirspySettings()); + SWGSDRangel::SWGAirspySettings *swgAirspySettings = swgDeviceSettings->getAirspySettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgAirspySettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgAirspySettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("devSampleRateIndex") || force) { + swgAirspySettings->setDevSampleRateIndex(settings.m_devSampleRateIndex); + } + if (deviceSettingsKeys.contains("lnaGain") || force) { + swgAirspySettings->setLnaGain(settings.m_lnaGain); + } + if (deviceSettingsKeys.contains("mixerGain") || force) { + swgAirspySettings->setMixerGain(settings.m_mixerGain); + } + if (deviceSettingsKeys.contains("vgaGain") || force) { + swgAirspySettings->setVgaGain(settings.m_vgaGain); + } + if (deviceSettingsKeys.contains("lnaAGC") || force) { + swgAirspySettings->setLnaAgc(settings.m_lnaAGC ? 1 : 0); + } + if (deviceSettingsKeys.contains("mixerAGC") || force) { + swgAirspySettings->setMixerAgc(settings.m_mixerAGC ? 1 : 0); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgAirspySettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgAirspySettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("biasT") || force) { + swgAirspySettings->setBiasT(settings.m_biasT ? 1 : 0); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgAirspySettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgAirspySettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgAirspySettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgAirspySettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgAirspySettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void AirspyInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void AirspyInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AirspyInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AirspyInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/airspy/airspyinput.h b/plugins/samplesource/airspy/airspyinput.h index f6d4788f2..e05cc58b2 100644 --- a/plugins/samplesource/airspy/airspyinput.h +++ b/plugins/samplesource/airspy/airspyinput.h @@ -19,16 +19,20 @@ #include #include +#include #include #include #include "airspysettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class AirspyThread; class FileRecord; class AirspyInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureAirspy : public Message { MESSAGE_CLASS_DECLARATION @@ -138,14 +142,6 @@ public: static const qint64 loHighLimitFreq; private: - bool openDevice(); - void closeDevice(); - bool applySettings(const AirspySettings& settings, bool force); - struct airspy_device *open_airspy_from_sequence(int sequence); - void setDeviceCenterFrequency(quint64 freq); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AirspySettings& settings); - void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); - DeviceSourceAPI *m_deviceAPI; QMutex m_mutex; AirspySettings m_settings; @@ -155,6 +151,21 @@ private: std::vector m_sampleRates; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const AirspySettings& settings, bool force); + struct airspy_device *open_airspy_from_sequence(int sequence); + void setDeviceCenterFrequency(quint64 freq); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AirspySettings& settings); + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const AirspySettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_AIRSPYINPUT_H diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index a3a65cf75..cc21a8fd3 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -32,7 +32,7 @@ const int AirspyPlugin::m_maxDevices = 32; const PluginDescriptor AirspyPlugin::m_pluginDescriptor = { QString("Airspy Input"), - QString("4.0.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/airspy/airspysettings.cpp b/plugins/samplesource/airspy/airspysettings.cpp index d1c7d786b..2de6b46cf 100644 --- a/plugins/samplesource/airspy/airspysettings.cpp +++ b/plugins/samplesource/airspy/airspysettings.cpp @@ -41,6 +41,10 @@ void AirspySettings::resetToDefaults() m_transverterMode = false; m_transverterDeltaFrequency = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray AirspySettings::serialize() const @@ -61,6 +65,10 @@ QByteArray AirspySettings::serialize() const s.writeBool(12, m_mixerAGC); s.writeBool(13, m_transverterMode); s.writeS64(14, m_transverterDeltaFrequency); + s.writeBool(15, m_useReverseAPI); + s.writeString(16, m_reverseAPIAddress); + s.writeU32(17, m_reverseAPIPort); + s.writeU32(18, m_reverseAPIDeviceIndex); return s.final(); } @@ -78,6 +86,7 @@ bool AirspySettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_LOppmTenths, 0); d.readU32(2, &m_devSampleRateIndex, 0); @@ -94,6 +103,18 @@ bool AirspySettings::deserialize(const QByteArray& data) d.readBool(12, &m_mixerAGC, false); d.readBool(13, &m_transverterMode, false); d.readS64(14, &m_transverterDeltaFrequency, 0); + d.readBool(15, &m_useReverseAPI, false); + d.readString(16, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(17, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(18, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/airspy/airspysettings.h b/plugins/samplesource/airspy/airspysettings.h index b8daf0ea0..b5c619ce4 100644 --- a/plugins/samplesource/airspy/airspysettings.h +++ b/plugins/samplesource/airspy/airspysettings.h @@ -42,6 +42,10 @@ struct AirspySettings { bool m_transverterMode; qint64 m_transverterDeltaFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; AirspySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/airspyhf/airspyhf.pro b/plugins/samplesource/airspyhf/airspyhf.pro index 9c611a347..114d49f38 100644 --- a/plugins/samplesource/airspyhf/airspyhf.pro +++ b/plugins/samplesource/airspyhf/airspyhf.pro @@ -12,7 +12,6 @@ QT += core gui widgets multimedia opengl TARGET = inputairspyhf CONFIG(MINGW32):LIBAIRSPYHFSRC = "C:\softs\airspyhf" -CONFIG(MINGW64):LIBAIRSPYHFSRC = "C:\softs\airspyhf" CONFIG(MSVC):LIBAIRSPYHFSRC = "C:\softs\airspyhf" INCLUDEPATH += $$PWD diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index 3cfb3f103..aef2be568 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -26,6 +26,8 @@ #include "ui_airspyhfgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "airspyhfgui.h" @@ -50,6 +52,9 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); m_rates = ((AirspyHFInput*) m_sampleSource)->getSampleRates(); @@ -397,3 +402,22 @@ int AirspyHFGui::getDevSampleRateIndex(uint32_t sampeRate) return -1; } + +void AirspyHFGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/airspyhf/airspyhfgui.h b/plugins/samplesource/airspyhf/airspyhfgui.h index d5b43503e..6dea4bc27 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.h +++ b/plugins/samplesource/airspyhf/airspyhfgui.h @@ -89,6 +89,7 @@ private slots: void updateHardware(); void updateStatus(); void handleInputMessages(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_AIRSPYHFGUI_H diff --git a/plugins/samplesource/airspyhf/airspyhfinput.cpp b/plugins/samplesource/airspyhf/airspyhfinput.cpp index 25b16b437..9d3556479 100644 --- a/plugins/samplesource/airspyhf/airspyhfinput.cpp +++ b/plugins/samplesource/airspyhf/airspyhfinput.cpp @@ -16,7 +16,10 @@ #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -54,11 +57,19 @@ AirspyHFInput::AirspyHFInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } AirspyHFInput::~AirspyHFInput() { - if (m_running) { stop(); } + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -309,6 +320,10 @@ bool AirspyHFInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgFileRecord::match(message)) @@ -363,16 +378,23 @@ void AirspyHFInput::setDeviceCenterFrequency(quint64 freq_hz, const AirspyHFSett bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { - QMutexLocker mutexLocker(&m_mutex); + qDebug() << "AirspyHFInput::applySettings"; + + QMutexLocker mutexLocker(&m_mutex); bool forwardChange = false; airspyhf_error rc; - int sampleRateIndex = settings.m_devSampleRateIndex; + QList reverseAPIKeys; - qDebug() << "AirspyHFInput::applySettings"; + int sampleRateIndex = settings.m_devSampleRateIndex; + + if ((m_settings.m_bandIndex != settings.m_bandIndex) || force) { + reverseAPIKeys.append("bandIndex"); + } if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force) { + reverseAPIKeys.append("devSampleRateIndex"); forwardChange = true; if (settings.m_devSampleRateIndex >= m_sampleRates.size()) { @@ -397,6 +419,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChange = true; if (m_airspyHFThread != 0) @@ -408,6 +431,8 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + if (m_dev != 0) { rc = (airspyhf_error) airspyhf_set_calibration(m_dev, settings.m_LOppmTenths * 100); @@ -423,6 +448,16 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) } } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) { + reverseAPIKeys.append("centerFrequency"); + } + if (force || (m_settings.m_transverterMode != settings.m_transverterMode)) { + reverseAPIKeys.append("transverterMode"); + } + if (force || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) @@ -455,6 +490,15 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; return true; } @@ -523,7 +567,7 @@ int AirspyHFInput::webapiSettingsPutPatch( settings.m_transverterMode = response.getAirspyHfSettings()->getTransverterMode() != 0; } if (deviceSettingsKeys.contains("bandIndex")) { - settings.m_bandIndex = response.getAirspyHfSettings()->getBandIndex() != 0; + settings.m_bandIndex = response.getAirspyHfSettings()->getBandIndex(); } if (deviceSettingsKeys.contains("fileRecordName")) { settings.m_fileRecordName = *response.getAirspyHfSettings()->getFileRecordName(); @@ -609,3 +653,88 @@ int AirspyHFInput::webapiRun( return 200; } +void AirspyHFInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const AirspyHFSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("AirspyHF")); + swgDeviceSettings->setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings()); + SWGSDRangel::SWGAirspyHFSettings *swgAirspyHFSettings = swgDeviceSettings->getAirspyHfSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgAirspyHFSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRateIndex") || force) { + swgAirspyHFSettings->setDevSampleRateIndex(settings.m_devSampleRateIndex); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgAirspyHFSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgAirspyHFSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgAirspyHFSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgAirspyHFSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("bandIndex") || force) { + swgAirspyHFSettings->setBandIndex(settings.m_bandIndex); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgAirspyHFSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void AirspyHFInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void AirspyHFInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AirspyHFInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AirspyHFInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/airspyhf/airspyhfinput.h b/plugins/samplesource/airspyhf/airspyhfinput.h index bb5e5e213..fe7db2ba2 100644 --- a/plugins/samplesource/airspyhf/airspyhfinput.h +++ b/plugins/samplesource/airspyhf/airspyhfinput.h @@ -19,17 +19,21 @@ #include #include +#include #include #include #include "airspyhfsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class AirspyHFThread; class FileRecord; class AirspyHFInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureAirspyHF : public Message { MESSAGE_CLASS_DECLARATION @@ -141,14 +145,6 @@ public: static const qint64 loHighLimitFreqVHF; private: - bool openDevice(); - void closeDevice(); - bool applySettings(const AirspyHFSettings& settings, bool force); - airspyhf_device_t *open_airspyhf_from_serial(const QString& serialStr); - void setDeviceCenterFrequency(quint64 freq, const AirspyHFSettings& settings); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AirspyHFSettings& settings); - void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); - DeviceSourceAPI *m_deviceAPI; QMutex m_mutex; AirspyHFSettings m_settings; @@ -158,6 +154,21 @@ private: std::vector m_sampleRates; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const AirspyHFSettings& settings, bool force); + airspyhf_device_t *open_airspyhf_from_serial(const QString& serialStr); + void setDeviceCenterFrequency(quint64 freq, const AirspyHFSettings& settings); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const AirspyHFSettings& settings); + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const AirspyHFSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_AIRSPYHFINPUT_H diff --git a/plugins/samplesource/airspyhf/airspyhfplugin.cpp b/plugins/samplesource/airspyhf/airspyhfplugin.cpp index b593150df..9337646d6 100644 --- a/plugins/samplesource/airspyhf/airspyhfplugin.cpp +++ b/plugins/samplesource/airspyhf/airspyhfplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor AirspyHFPlugin::m_pluginDescriptor = { QString("AirspyHF Input"), - QString("4.0.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.cpp b/plugins/samplesource/airspyhf/airspyhfsettings.cpp index a0971776c..77f96f383 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.cpp +++ b/plugins/samplesource/airspyhf/airspyhfsettings.cpp @@ -34,6 +34,10 @@ void AirspyHFSettings::resetToDefaults() m_transverterDeltaFrequency = 0; m_bandIndex = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray AirspyHFSettings::serialize() const @@ -46,6 +50,10 @@ QByteArray AirspyHFSettings::serialize() const s.writeBool(7, m_transverterMode); s.writeS64(8, m_transverterDeltaFrequency); s.writeU32(9, m_bandIndex); + s.writeBool(10, m_useReverseAPI); + s.writeString(11, m_reverseAPIAddress); + s.writeU32(12, m_reverseAPIPort); + s.writeU32(13, m_reverseAPIDeviceIndex); return s.final(); } @@ -73,7 +81,18 @@ bool AirspyHFSettings::deserialize(const QByteArray& data) d.readS64(8, &m_transverterDeltaFrequency, 0); d.readU32(9, &uintval, 0); m_bandIndex = uintval > 1 ? 1 : uintval; + d.readBool(10, &m_useReverseAPI, false); + d.readString(11, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(12, &uintval, 0); + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(13, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } else diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.h b/plugins/samplesource/airspyhf/airspyhfsettings.h index 937355d96..a29e083af 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.h +++ b/plugins/samplesource/airspyhf/airspyhfsettings.h @@ -29,6 +29,10 @@ struct AirspyHFSettings qint64 m_transverterDeltaFrequency; quint32 m_bandIndex; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; AirspyHFSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf1input/bladerf1input.cpp b/plugins/samplesource/bladerf1input/bladerf1input.cpp index f21349624..11bd0392b 100644 --- a/plugins/samplesource/bladerf1input/bladerf1input.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1input.cpp @@ -14,11 +14,12 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "bladerf1input.h" - #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -30,6 +31,7 @@ #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" +#include "bladerf1input.h" #include "bladerf1inputthread.h" MESSAGE_CLASS_DEFINITION(Bladerf1Input::MsgConfigureBladerf1, Message) @@ -49,11 +51,20 @@ Bladerf1Input::Bladerf1Input(DeviceSourceAPI *deviceAPI) : m_deviceAPI->addSink(m_fileSink); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } Bladerf1Input::~Bladerf1Input() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -311,6 +322,10 @@ bool Bladerf1Input::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -322,30 +337,33 @@ bool Bladerf1Input::handleMessage(const Message& message) bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool force) { bool forwardChange = false; + QList reverseAPIKeys; // QMutexLocker mutexLocker(&m_mutex); qDebug() << "BladerfInput::applySettings: m_dev: " << m_dev; + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); + } + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + } + if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { -// m_settings.m_dcBlock = settings.m_dcBlock; -// m_settings.m_iqCorrection = settings.m_iqCorrection; m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_lnaGain != settings.m_lnaGain) || force) { -// m_settings.m_lnaGain = settings.m_lnaGain; + reverseAPIKeys.append("lnaGain"); if (m_dev != 0) { - if(bladerf_set_lna_gain(m_dev, getLnaGain(settings.m_lnaGain)) != 0) - { + if(bladerf_set_lna_gain(m_dev, getLnaGain(settings.m_lnaGain)) != 0) { qDebug("BladerfInput::applySettings: bladerf_set_lna_gain() failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: LNA gain set to " << getLnaGain(settings.m_lnaGain); } } @@ -353,16 +371,13 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_vga1 != settings.m_vga1) || force) { -// m_settings.m_vga1 = settings.m_vga1; + reverseAPIKeys.append("vga1"); if (m_dev != 0) { - if(bladerf_set_rxvga1(m_dev, settings.m_vga1) != 0) - { + if(bladerf_set_rxvga1(m_dev, settings.m_vga1) != 0) { qDebug("BladerfInput::applySettings: bladerf_set_rxvga1() failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: VGA1 gain set to " << settings.m_vga1; } } @@ -370,16 +385,13 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_vga2 != settings.m_vga2) || force) { -// m_settings.m_vga2 = settings.m_vga2; + reverseAPIKeys.append("vga2"); if(m_dev != 0) { - if(bladerf_set_rxvga2(m_dev, settings.m_vga2) != 0) - { + if(bladerf_set_rxvga2(m_dev, settings.m_vga2) != 0) { qDebug("BladerfInput::applySettings: bladerf_set_rxvga2() failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: VGA2 gain set to " << settings.m_vga2; } } @@ -387,7 +399,7 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_xb200 != settings.m_xb200) || force) { -// m_settings.m_xb200 = settings.m_xb200; + reverseAPIKeys.append("xb200"); if (m_dev != 0) { @@ -397,12 +409,9 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo { DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[0]; - if (buddy->getDeviceSinkEngine()->state() == DSPDeviceSinkEngine::StRunning) // Tx side running - { + if (buddy->getDeviceSinkEngine()->state() == DSPDeviceSinkEngine::StRunning) { // Tx side running changeSettings = false; - } - else - { + } else { changeSettings = true; } } @@ -415,23 +424,17 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo { if (settings.m_xb200) { - if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0) - { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0) { qDebug("BladerfInput::applySettings: bladerf_expansion_attach(xb200) failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: Attach XB200"; } } else { - if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0) - { + if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0) { qDebug("BladerfInput::applySettings: bladerf_expansion_attach(none) failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: Detach XB200"; } } @@ -443,16 +446,13 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_xb200Path != settings.m_xb200Path) || force) { -// m_settings.m_xb200Path = settings.m_xb200Path; + reverseAPIKeys.append("xb200Path"); if (m_dev != 0) { - if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_RX, settings.m_xb200Path) != 0) - { + if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_RX, settings.m_xb200Path) != 0) { qDebug("BladerfInput::applySettings: bladerf_xb200_set_path(BLADERF_MODULE_RX) failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: set xb200 path to " << settings.m_xb200Path; } } @@ -460,16 +460,13 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_xb200Filter != settings.m_xb200Filter) || force) { -// m_settings.m_xb200Filter = settings.m_xb200Filter; + reverseAPIKeys.append("xb200Filter"); if (m_dev != 0) { - if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_RX, settings.m_xb200Filter) != 0) - { + if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_RX, settings.m_xb200Filter) != 0) { qDebug("BladerfInput::applySettings: bladerf_xb200_set_filterbank(BLADERF_MODULE_RX) failed"); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: set xb200 filter to " << settings.m_xb200Filter; } } @@ -477,19 +474,16 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { -// m_settings.m_devSampleRate = settings.m_devSampleRate; + reverseAPIKeys.append("devSampleRate"); forwardChange = true; if (m_dev != 0) { unsigned int actualSamplerate; - if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_RX, settings.m_devSampleRate, &actualSamplerate) < 0) - { + if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_RX, settings.m_devSampleRate, &actualSamplerate) < 0) { qCritical("BladerfInput::applySettings: could not set sample rate: %d", settings.m_devSampleRate); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: bladerf_set_sample_rate(BLADERF_MODULE_RX) actual sample rate is " << actualSamplerate; } } @@ -497,18 +491,15 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { -// m_settings.m_bandwidth = settings.m_bandwidth; + reverseAPIKeys.append("bandwidth"); if(m_dev != 0) { unsigned int actualBandwidth; - if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_RX, settings.m_bandwidth, &actualBandwidth) < 0) - { + if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_RX, settings.m_bandwidth, &actualBandwidth) < 0) { qCritical("BladerfInput::applySettings: could not set bandwidth: %d", settings.m_bandwidth); - } - else - { + } else { qDebug() << "BladerfInput::applySettings: bladerf_set_bandwidth(BLADERF_MODULE_RX) actual bandwidth is " << actualBandwidth; } } @@ -516,6 +507,8 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_fcPos != settings.m_fcPos) || force) { + reverseAPIKeys.append("fcPos"); + if (m_bladerfThread != 0) { m_bladerfThread->setFcPos((int) settings.m_fcPos); @@ -525,7 +518,7 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { -// m_settings.m_log2Decim = settings.m_log2Decim; + reverseAPIKeys.append("log2Decim"); forwardChange = true; if (m_bladerfThread != 0) @@ -535,6 +528,10 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_fcPos != settings.m_fcPos) @@ -547,10 +544,6 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo (DeviceSampleSource::fcPos_t) settings.m_fcPos, settings.m_devSampleRate); -// m_settings.m_centerFrequency = settings.m_centerFrequency; -// m_settings.m_log2Decim = settings.m_log2Decim; -// m_settings.m_fcPos = settings.m_fcPos; - forwardChange = true; if (m_dev != 0) @@ -571,6 +564,15 @@ bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool fo m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; qDebug() << "BladerfInput::applySettings: " @@ -733,3 +735,107 @@ int Bladerf1Input::webapiRun( return 200; } + +void Bladerf1Input::webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF1InputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("BladeRF1")); + swgDeviceSettings->setBladeRf1InputSettings(new SWGSDRangel::SWGBladeRF1InputSettings()); + SWGSDRangel::SWGBladeRF1InputSettings *swgBladeRF1Settings = swgDeviceSettings->getBladeRf1InputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgBladeRF1Settings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgBladeRF1Settings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("lnaGain") || force) { + swgBladeRF1Settings->setLnaGain(settings.m_lnaGain); + } + if (deviceSettingsKeys.contains("vga1") || force) { + swgBladeRF1Settings->setVga1(settings.m_vga1); + } + if (deviceSettingsKeys.contains("vga2") || force) { + swgBladeRF1Settings->setVga1(settings.m_vga2); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgBladeRF1Settings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgBladeRF1Settings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgBladeRF1Settings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("xb200") || force) { + swgBladeRF1Settings->setXb200(settings.m_xb200 ? 1 : 0); + } + if (deviceSettingsKeys.contains("xb200Path") || force) { + swgBladeRF1Settings->setXb200Path((int) settings.m_xb200Path); + } + if (deviceSettingsKeys.contains("xb200Filter") || force) { + swgBladeRF1Settings->setXb200Filter((int) settings.m_xb200Filter); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgBladeRF1Settings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgBladeRF1Settings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgBladeRF1Settings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void Bladerf1Input::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void Bladerf1Input::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "Bladerf1Input::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("Bladerf1Input::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/bladerf1input/bladerf1input.h b/plugins/samplesource/bladerf1input/bladerf1input.h index 19b8feefb..ec3265c4f 100644 --- a/plugins/samplesource/bladerf1input/bladerf1input.h +++ b/plugins/samplesource/bladerf1input/bladerf1input.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -27,11 +28,14 @@ #include "../../../devices/bladerf1/devicebladerf1param.h" #include "bladerf1inputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class Bladerf1InputThread; class FileRecord; class Bladerf1Input : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureBladerf1 : public Message { MESSAGE_CLASS_DECLARATION @@ -133,12 +137,6 @@ public: QString& errorMessage); private: - bool openDevice(); - void closeDevice(); - bool applySettings(const BladeRF1InputSettings& settings, bool force); - bladerf_lna_gain getLnaGain(int lnaGain); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1InputSettings& settings); - DeviceSourceAPI *m_deviceAPI; QMutex m_mutex; BladeRF1InputSettings m_settings; @@ -148,6 +146,19 @@ private: DeviceBladeRF1Params m_sharedParams; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const BladeRF1InputSettings& settings, bool force); + bladerf_lna_gain getLnaGain(int lnaGain); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1InputSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF1InputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_BLADERFINPUT_H diff --git a/plugins/samplesource/bladerf1input/bladerf1input.pro b/plugins/samplesource/bladerf1input/bladerf1input.pro index 8bfda4fb1..304174684 100644 --- a/plugins/samplesource/bladerf1input/bladerf1input.pro +++ b/plugins/samplesource/bladerf1input/bladerf1input.pro @@ -24,7 +24,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBBLADERF = "C:\Programs\bladeRF" INCLUDEPATH += $$LIBBLADERF/include } @@ -55,7 +55,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L$$LIBBLADERF/lib -lbladeRF } diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 3978c3f6e..a627961ec 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -22,6 +22,8 @@ #include "ui_bladerf1inputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" @@ -60,6 +62,9 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -475,3 +480,21 @@ unsigned int Bladerf1InputGui::getXb200Index(bool xb_200, bladerf_xb200_path xb2 } } +void Bladerf1InputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.h b/plugins/samplesource/bladerf1input/bladerf1inputgui.h index c27a8f8cf..156aed389 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.h @@ -89,6 +89,7 @@ private slots: void on_record_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_BLADERFINPUTGUI_H diff --git a/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp b/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp index 6a826eedc..e208439ab 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor Blderf1InputPlugin::m_pluginDescriptor = { QString("BladeRF1 Input"), - QString("4.2.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp index 922b93c81..336501a93 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp @@ -41,6 +41,10 @@ void BladeRF1InputSettings::resetToDefaults() m_dcBlock = false; m_iqCorrection = false; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray BladeRF1InputSettings::serialize() const @@ -59,6 +63,10 @@ QByteArray BladeRF1InputSettings::serialize() const s.writeS32(10, (int) m_xb200Filter); s.writeBool(11, m_dcBlock); s.writeBool(12, m_iqCorrection); + s.writeBool(13, m_useReverseAPI); + s.writeString(14, m_reverseAPIAddress); + s.writeU32(15, m_reverseAPIPort); + s.writeU32(16, m_reverseAPIDeviceIndex); return s.final(); } @@ -76,6 +84,7 @@ bool BladeRF1InputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_devSampleRate, 3072000); d.readS32(2, &m_lnaGain); @@ -92,6 +101,18 @@ bool BladeRF1InputSettings::deserialize(const QByteArray& data) m_xb200Filter = (bladerf_xb200_filter) intval; d.readBool(11, &m_dcBlock); d.readBool(12, &m_iqCorrection); + d.readBool(13, &m_useReverseAPI, false); + d.readString(14, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(15, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(16, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h index ddaf11b6f..850ab4261 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h @@ -42,6 +42,10 @@ struct BladeRF1InputSettings { bool m_dcBlock; bool m_iqCorrection; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; BladeRF1InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf2input/bladerf2input.cpp b/plugins/samplesource/bladerf2input/bladerf2input.cpp index 2f3faa600..175afea03 100644 --- a/plugins/samplesource/bladerf2input/bladerf2input.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2input.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "libbladeRF.h" @@ -65,10 +67,15 @@ BladeRF2Input::BladeRF2Input(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } BladeRF2Input::~BladeRF2Input() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -743,6 +750,10 @@ bool BladeRF2Input::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -762,6 +773,14 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo qint64 xlatedDeviceCenterFrequency = settings.m_centerFrequency; xlatedDeviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; xlatedDeviceCenterFrequency = xlatedDeviceCenterFrequency < 0 ? 0 : xlatedDeviceCenterFrequency; + QList reverseAPIKeys; + + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); + } + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + } if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqCorrection != settings.m_iqCorrection) || force) @@ -771,6 +790,7 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); forwardChangeOwnDSP = true; forwardChangeRxBuddies = true; forwardChangeTxBuddies = true; @@ -794,6 +814,7 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); forwardChangeRxBuddies = true; if (dev != 0) @@ -815,6 +836,7 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo if ((m_settings.m_fcPos != settings.m_fcPos) || force) { + reverseAPIKeys.append("fcPos"); BladeRF2InputThread *inputThread = findThread(); if (inputThread != 0) @@ -826,6 +848,7 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChangeOwnDSP = true; BladeRF2InputThread *inputThread = findThread(); @@ -836,6 +859,22 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) @@ -871,12 +910,14 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo if ((m_settings.m_biasTee != settings.m_biasTee) || force) { + reverseAPIKeys.append("biasTee"); forwardChangeRxBuddies = true; m_deviceShared.m_dev->setBiasTeeRx(settings.m_biasTee); } if ((m_settings.m_gainMode != settings.m_gainMode) || force) { + reverseAPIKeys.append("gainMode"); forwardChangeRxBuddies = true; if (dev) @@ -892,6 +933,13 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo } } + if ((m_settings.m_globalGain != settings.m_globalGain) || force) { + reverseAPIKeys.append("globalGain"); + } + if ((m_settings.m_gainMode != settings.m_gainMode) || force) { + reverseAPIKeys.append("gainMode"); + } + if ((m_settings.m_globalGain != settings.m_globalGain) || ((m_settings.m_gainMode != settings.m_gainMode) && (settings.m_gainMode == BLADERF_GAIN_MANUAL)) || force) { @@ -955,6 +1003,15 @@ bool BladeRF2Input::applySettings(const BladeRF2InputSettings& settings, bool fo } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; qDebug() << "BladeRF2Input::applySettings: " @@ -1161,3 +1218,107 @@ int BladeRF2Input::webapiRun( return 200; } + +void BladeRF2Input::webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2InputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("BladeRF2")); + swgDeviceSettings->setBladeRf2InputSettings(new SWGSDRangel::SWGBladeRF2InputSettings()); + SWGSDRangel::SWGBladeRF2InputSettings *swgBladeRF2Settings = swgDeviceSettings->getBladeRf2InputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgBladeRF2Settings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgBladeRF2Settings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgBladeRF2Settings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgBladeRF2Settings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgBladeRF2Settings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgBladeRF2Settings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgBladeRF2Settings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgBladeRF2Settings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgBladeRF2Settings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + if (deviceSettingsKeys.contains("devSampleRate")) { + swgBladeRF2Settings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("bandwidth")) { + swgBladeRF2Settings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("biasTee")) { + swgBladeRF2Settings->setBiasTee(settings.m_biasTee); + } + if (deviceSettingsKeys.contains("gainMode")) { + swgBladeRF2Settings->setGainMode(settings.m_gainMode); + } + if (deviceSettingsKeys.contains("globalGain")) { + swgBladeRF2Settings->setGlobalGain(settings.m_globalGain); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void BladeRF2Input::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void BladeRF2Input::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "BladeRF2Input::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("BladeRF2Input::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/bladerf2input/bladerf2input.h b/plugins/samplesource/bladerf2input/bladerf2input.h index 4b9a79561..ea4498c5f 100644 --- a/plugins/samplesource/bladerf2input/bladerf2input.h +++ b/plugins/samplesource/bladerf2input/bladerf2input.h @@ -17,22 +17,26 @@ #ifndef PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ #define PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ +#include + #include #include -#include +#include #include "dsp/devicesamplesource.h" #include "bladerf2/devicebladerf2shared.h" #include "bladerf2inputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class BladeRF2InputThread; class FileRecord; struct bladerf_gain_modes; struct bladerf; -class BladeRF2Input : public DeviceSampleSource -{ +class BladeRF2Input : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureBladeRF2 : public Message { MESSAGE_CLASS_DECLARATION @@ -186,6 +190,8 @@ private: BladeRF2InputThread *m_thread; FileRecord *m_fileSink; //!< File sink to record device I/Q output std::vector m_gainModes; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -195,6 +201,11 @@ private: bool setDeviceCenterFrequency(struct bladerf *dev, int requestedChannel, quint64 freq_hz, int loPpmTenths); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF2InputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2InputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ */ diff --git a/plugins/samplesource/bladerf2input/bladerf2input.pro b/plugins/samplesource/bladerf2input/bladerf2input.pro index fe47be190..de7b3dcd7 100644 --- a/plugins/samplesource/bladerf2input/bladerf2input.pro +++ b/plugins/samplesource/bladerf2input/bladerf2input.pro @@ -24,7 +24,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBBLADERF = "C:\Programs\bladeRF" INCLUDEPATH += $$LIBBLADERF/include } @@ -55,7 +55,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L$$LIBBLADERF/lib -lbladeRF } diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index 02e78775a..e00db93d2 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -22,6 +22,8 @@ #include "ui_bladerf2inputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" @@ -81,6 +83,9 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -457,3 +462,22 @@ void BladeRF2InputGui::updateStatus() m_lastEngineState = state; } } + +void BladeRF2InputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.h b/plugins/samplesource/bladerf2input/bladerf2inputgui.h index 665670f3d..0b2483793 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.h @@ -91,6 +91,7 @@ private slots: void on_record_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; diff --git a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp index 2008e9bd7..af26bfb3c 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor Blderf2InputPlugin::m_pluginDescriptor = { QString("BladeRF2 Input"), - QString("4.3.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp index 21e939ff2..597891783 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp @@ -39,6 +39,10 @@ void BladeRF2InputSettings::resetToDefaults() m_transverterMode = false; m_transverterDeltaFrequency = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray BladeRF2InputSettings::serialize() const @@ -57,6 +61,10 @@ QByteArray BladeRF2InputSettings::serialize() const s.writeS32(10, m_LOppmTenths); s.writeBool(11, m_transverterMode); s.writeS64(12, m_transverterDeltaFrequency); + s.writeBool(13, m_useReverseAPI); + s.writeString(14, m_reverseAPIAddress); + s.writeU32(15, m_reverseAPIPort); + s.writeU32(16, m_reverseAPIDeviceIndex); return s.final(); } @@ -74,6 +82,7 @@ bool BladeRF2InputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_devSampleRate, 3072000); d.readS32(2, &m_bandwidth); @@ -88,6 +97,18 @@ bool BladeRF2InputSettings::deserialize(const QByteArray& data) d.readS32(10, &m_LOppmTenths); d.readBool(11, &m_transverterMode, false); d.readS64(12, &m_transverterDeltaFrequency, 0); + d.readBool(13, &m_useReverseAPI, false); + d.readString(14, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(15, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(16, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h index 722be9e6e..26ad4ca56 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h @@ -41,6 +41,10 @@ struct BladeRF2InputSettings { bool m_transverterMode; qint64 m_transverterDeltaFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; BladeRF2InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 6928742b1..ffb6ae1af 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -19,6 +19,8 @@ #include "ui_fcdprogui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "fcdprogui.h" @@ -143,6 +145,9 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -524,3 +529,21 @@ void FCDProGui::updateHardware() m_updateTimer.stop(); } +void FCDProGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index de38ae1de..4b807f1ab 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -102,6 +102,7 @@ private slots: void on_transverter_clicked(); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_FCDPROGUI_H diff --git a/plugins/samplesource/fcdpro/fcdproinput.cpp b/plugins/samplesource/fcdpro/fcdproinput.cpp index 2ba8b52c0..ac6e8cef1 100644 --- a/plugins/samplesource/fcdpro/fcdproinput.cpp +++ b/plugins/samplesource/fcdpro/fcdproinput.cpp @@ -14,10 +14,13 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include #include #include +#include +#include +#include + #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -47,10 +50,15 @@ FCDProInput::FCDProInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } FCDProInput::~FCDProInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -82,6 +90,16 @@ bool FCDProInput::openDevice() return false; } + if (!openFCDAudio(fcd_traits::qtDeviceName)) + { + qCritical("FCDProInput::start: could not open FCD audio source"); + return false; + } + else + { + qDebug("FCDProInput::start: FCD audio source opened"); + } + return true; } @@ -115,16 +133,6 @@ bool FCDProInput::start() return false; } - if (!openFCDAudio(fcd_traits::qtDeviceName)) - { - qCritical("FCDProInput::start: could not open FCD audio source"); - return false; - } - else - { - qDebug("FCDProInput::start: FCD audio source opened"); - } - m_FCDThread = new FCDProThread(&m_sampleFifo, &m_fcdFIFO); m_FCDThread->startWork(); @@ -145,6 +153,8 @@ void FCDProInput::closeDevice() fcdClose(m_dev); m_dev = 0; + + closeFCDAudio(); } bool FCDProInput::openFCDAudio(const char* cardname) @@ -186,7 +196,6 @@ void FCDProInput::stop() // wait for thread to quit ? delete m_FCDThread; m_FCDThread = nullptr; - closeFCDAudio(); } m_running = false; @@ -275,6 +284,10 @@ bool FCDProInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgFileRecord::match(message)) @@ -308,6 +321,17 @@ bool FCDProInput::handleMessage(const Message& message) void FCDProInput::applySettings(const FCDProSettings& settings, bool force) { bool forwardChange = false; + QList reverseAPIKeys; + + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) { + reverseAPIKeys.append("centerFrequency"); + } + if (force || (m_settings.m_transverterMode != settings.m_transverterMode)) { + reverseAPIKeys.append("transverterMode"); + } + if (force || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) @@ -332,186 +356,181 @@ void FCDProInput::applySettings(const FCDProSettings& settings, bool force) if ((m_settings.m_lnaGainIndex != settings.m_lnaGainIndex) || force) { - m_settings.m_lnaGainIndex = settings.m_lnaGainIndex; + reverseAPIKeys.append("lnaGainIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_lnaGain(settings.m_lnaGainIndex); } } if ((m_settings.m_rfFilterIndex != settings.m_rfFilterIndex) || force) { - m_settings.m_rfFilterIndex = settings.m_rfFilterIndex; + reverseAPIKeys.append("rfFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_rfFilter(settings.m_rfFilterIndex); } } if ((m_settings.m_lnaEnhanceIndex != settings.m_lnaEnhanceIndex) || force) { - m_settings.m_lnaEnhanceIndex = settings.m_lnaEnhanceIndex; + reverseAPIKeys.append("lnaEnhanceIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_lnaEnhance(settings.m_lnaEnhanceIndex); } } if ((m_settings.m_bandIndex != settings.m_bandIndex) || force) { - m_settings.m_bandIndex = settings.m_bandIndex; + reverseAPIKeys.append("bandIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_band(settings.m_bandIndex); } } if ((m_settings.m_mixerGainIndex != settings.m_mixerGainIndex) || force) { - m_settings.m_mixerGainIndex = settings.m_mixerGainIndex; + reverseAPIKeys.append("mixerGainIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_mixerGain(settings.m_mixerGainIndex); } } if ((m_settings.m_mixerFilterIndex != settings.m_mixerFilterIndex) || force) { - m_settings.m_mixerFilterIndex = settings.m_mixerFilterIndex; + reverseAPIKeys.append("mixerFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_mixerFilter(settings.m_mixerFilterIndex); } } if ((m_settings.m_biasCurrentIndex != settings.m_biasCurrentIndex) || force) { - m_settings.m_biasCurrentIndex = settings.m_biasCurrentIndex; + reverseAPIKeys.append("biasCurrentIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_biasCurrent(settings.m_biasCurrentIndex); } } if ((m_settings.m_modeIndex != settings.m_modeIndex) || force) { - m_settings.m_modeIndex = settings.m_modeIndex; + reverseAPIKeys.append("modeIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_mode(settings.m_modeIndex); } } if ((m_settings.m_gain1Index != settings.m_gain1Index) || force) { - m_settings.m_gain1Index = settings.m_gain1Index; + reverseAPIKeys.append("gain1Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain1(settings.m_gain1Index); } } if ((m_settings.m_rcFilterIndex != settings.m_rcFilterIndex) || force) { - m_settings.m_rcFilterIndex = settings.m_rcFilterIndex; + reverseAPIKeys.append("rcFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_rcFilter(settings.m_rcFilterIndex); } } if ((m_settings.m_gain2Index != settings.m_gain2Index) || force) { - m_settings.m_gain2Index = settings.m_gain2Index; + reverseAPIKeys.append("gain2Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain2(settings.m_gain2Index); } } if ((m_settings.m_gain3Index != settings.m_gain3Index) || force) { - m_settings.m_gain3Index = settings.m_gain3Index; + reverseAPIKeys.append("gain3Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain3(settings.m_gain3Index); } } if ((m_settings.m_gain4Index != settings.m_gain4Index) || force) { - m_settings.m_gain4Index = settings.m_gain4Index; + reverseAPIKeys.append("gain4Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain4(settings.m_gain4Index); } } if ((m_settings.m_ifFilterIndex != settings.m_ifFilterIndex) || force) { - m_settings.m_ifFilterIndex = settings.m_ifFilterIndex; + reverseAPIKeys.append("ifFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_ifFilter(settings.m_ifFilterIndex); } } if ((m_settings.m_gain5Index != settings.m_gain5Index) || force) { - m_settings.m_gain5Index = settings.m_gain5Index; + reverseAPIKeys.append("gain5Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain5(settings.m_gain5Index); } } if ((m_settings.m_gain6Index != settings.m_gain6Index) || force) { - m_settings.m_gain6Index = settings.m_gain6Index; + reverseAPIKeys.append("gain6Index"); - if (m_dev != 0) - { + if (m_dev != 0) { set_gain6(settings.m_gain6Index); } } if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { - m_settings.m_LOppmTenths = settings.m_LOppmTenths; + reverseAPIKeys.append("LOppmTenths"); + m_settings.m_LOppmTenths = settings.m_LOppmTenths; - if (m_dev != 0) - { + if (m_dev != 0) { set_lo_ppm(); } } if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { - m_settings.m_dcBlock = settings.m_dcBlock; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection); + reverseAPIKeys.append("dcBlock"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { - m_settings.m_iqCorrection = settings.m_iqCorrection; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection); + reverseAPIKeys.append("iqCorrection"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + if (forwardChange) { DSPSignalNotification *notif = new DSPSignalNotification(fcd_traits::sampleRate, m_settings.m_centerFrequency); @@ -948,3 +967,134 @@ void FCDProInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& res response.getFcdProSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); } } + +void FCDProInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const FCDProSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("FCDPro")); + swgDeviceSettings->setFcdProSettings(new SWGSDRangel::SWGFCDProSettings()); + SWGSDRangel::SWGFCDProSettings *swgFCDProSettings = swgDeviceSettings->getFcdProSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgFCDProSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgFCDProSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("lnaGainIndex") || force) { + swgFCDProSettings->setLnaGainIndex(settings.m_lnaGainIndex); + } + if (deviceSettingsKeys.contains("rfFilterIndex") || force) { + swgFCDProSettings->setRfFilterIndex(settings.m_rfFilterIndex); + } + if (deviceSettingsKeys.contains("lnaEnhanceIndex") || force) { + swgFCDProSettings->setLnaEnhanceIndex(settings.m_lnaEnhanceIndex); + } + if (deviceSettingsKeys.contains("bandIndex") || force) { + swgFCDProSettings->setBandIndex(settings.m_bandIndex); + } + if (deviceSettingsKeys.contains("mixerGainIndex") || force) { + swgFCDProSettings->setMixerGainIndex(settings.m_mixerGainIndex); + } + if (deviceSettingsKeys.contains("mixerFilterIndex") || force) { + swgFCDProSettings->setMixerFilterIndex(settings.m_mixerFilterIndex); + } + if (deviceSettingsKeys.contains("biasCurrentIndex") || force) { + swgFCDProSettings->setBiasCurrentIndex(settings.m_biasCurrentIndex); + } + if (deviceSettingsKeys.contains("modeIndex") || force) { + swgFCDProSettings->setModeIndex(settings.m_modeIndex); + } + if (deviceSettingsKeys.contains("gain1Index") || force) { + swgFCDProSettings->setGain1Index(settings.m_gain1Index); + } + if (deviceSettingsKeys.contains("gain2Index") || force) { + swgFCDProSettings->setGain2Index(settings.m_gain2Index); + } + if (deviceSettingsKeys.contains("gain3Index") || force) { + swgFCDProSettings->setGain3Index(settings.m_gain3Index); + } + if (deviceSettingsKeys.contains("gain4Index") || force) { + swgFCDProSettings->setGain4Index(settings.m_gain4Index); + } + if (deviceSettingsKeys.contains("gain5Index") || force) { + swgFCDProSettings->setGain5Index(settings.m_gain5Index); + } + if (deviceSettingsKeys.contains("gain6Index") || force) { + swgFCDProSettings->setGain6Index(settings.m_gain6Index); + } + if (deviceSettingsKeys.contains("rcFilterIndex") || force) { + swgFCDProSettings->setRcFilterIndex(settings.m_rcFilterIndex); + } + if (deviceSettingsKeys.contains("ifFilterIndex") || force) { + swgFCDProSettings->setIfFilterIndex(settings.m_ifFilterIndex); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgFCDProSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgFCDProSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgFCDProSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgFCDProSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgFCDProSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void FCDProInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void FCDProInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "FCDProInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("FCDProInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/fcdpro/fcdproinput.h b/plugins/samplesource/fcdpro/fcdproinput.h index aa0c7240f..06df9e25e 100644 --- a/plugins/samplesource/fcdpro/fcdproinput.h +++ b/plugins/samplesource/fcdpro/fcdproinput.h @@ -17,9 +17,11 @@ #ifndef INCLUDE_FCDPROINPUT_H #define INCLUDE_FCDPROINPUT_H +#include + #include #include -#include +#include #include "dsp/devicesamplesource.h" #include "audio/audioinput.h" @@ -33,11 +35,14 @@ struct fcd_buffer { std::size_t length; }; +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class FCDProThread; class FileRecord; class FCDProInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureFCDPro : public Message { MESSAGE_CLASS_DECLARATION @@ -158,15 +163,6 @@ public: void set_gain6(int index); private: - bool openDevice(); - void closeDevice(); - bool openFCDAudio(const char *filename); - void closeFCDAudio(); - void applySettings(const FCDProSettings& settings, bool force); - void set_lo_ppm(); - - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const FCDProSettings& settings); - DeviceSourceAPI *m_deviceAPI; hid_device *m_dev; AudioInput m_fcdAudioInput; @@ -177,6 +173,22 @@ private: QString m_deviceDescription; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool openFCDAudio(const char *filename); + void closeFCDAudio(); + void applySettings(const FCDProSettings& settings, bool force); + void set_lo_ppm(); + + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const FCDProSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const FCDProSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_FCDPROINPUT_H diff --git a/plugins/samplesource/fcdpro/fcdprosettings.cpp b/plugins/samplesource/fcdpro/fcdprosettings.cpp index 65c92133a..18e809f4b 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.cpp +++ b/plugins/samplesource/fcdpro/fcdprosettings.cpp @@ -48,6 +48,10 @@ void FCDProSettings::resetToDefaults() m_transverterMode = false; m_transverterDeltaFrequency = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray FCDProSettings::serialize() const @@ -75,6 +79,10 @@ QByteArray FCDProSettings::serialize() const s.writeS32(19, m_gain6Index); s.writeBool(20, m_transverterMode); s.writeS64(21, m_transverterDeltaFrequency); + s.writeBool(22, m_useReverseAPI); + s.writeString(23, m_reverseAPIAddress); + s.writeU32(24, m_reverseAPIPort); + s.writeU32(25, m_reverseAPIDeviceIndex); return s.final(); } @@ -91,6 +99,8 @@ bool FCDProSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { + uint32_t uintval; + d.readBool(1, &m_dcBlock, false); d.readBool(2, &m_iqCorrection, false); d.readS32(3, &m_LOppmTenths, 0); @@ -112,6 +122,18 @@ bool FCDProSettings::deserialize(const QByteArray& data) d.readS32(19, &m_gain6Index, 0); d.readBool(20, &m_transverterMode, false); d.readS64(21, &m_transverterDeltaFrequency, 0); + d.readBool(22, &m_useReverseAPI, false); + d.readString(23, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(24, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(25, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/fcdpro/fcdprosettings.h b/plugins/samplesource/fcdpro/fcdprosettings.h index f4972717d..4ef9d1d8e 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.h +++ b/plugins/samplesource/fcdpro/fcdprosettings.h @@ -43,6 +43,10 @@ struct FCDProSettings { bool m_transverterMode; qint64 m_transverterDeltaFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; FCDProSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 7b261a9ca..5f2c92d77 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -20,6 +20,8 @@ #include "ui_fcdproplusgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "fcdproplusgui.h" @@ -61,6 +63,9 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -349,3 +354,22 @@ void FCDProPlusGui::on_transverter_clicked() m_settings.m_centerFrequency = ui->centerFrequency->getValueNew()*1000; sendSettings(); } + +void FCDProPlusGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 56be9dacd..81649fb11 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -89,6 +89,7 @@ private slots: void on_transverter_clicked(); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_FCDGUI_H diff --git a/plugins/samplesource/fcdproplus/fcdproplusinput.cpp b/plugins/samplesource/fcdproplus/fcdproplusinput.cpp index 4c9776e13..809d603ea 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusinput.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusinput.cpp @@ -14,10 +14,13 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include #include #include +#include +#include +#include + #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -47,10 +50,15 @@ FCDProPlusInput::FCDProPlusInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } FCDProPlusInput::~FCDProPlusInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -82,6 +90,16 @@ bool FCDProPlusInput::openDevice() return false; } + if (!openFCDAudio(fcd_traits::qtDeviceName)) + { + qCritical("FCDProPlusInput::start: could not open FCD audio source"); + return false; + } + else + { + qDebug("FCDProPlusInput::start: FCD audio source opened"); + } + return true; } @@ -117,16 +135,6 @@ bool FCDProPlusInput::start() return false; } - if (!openFCDAudio(fcd_traits::qtDeviceName)) - { - qCritical("FCDProPlusInput::start: could not open FCD audio source"); - return false; - } - else - { - qDebug("FCDProPlusInput::start: FCD audio source opened"); - } - m_FCDThread = new FCDProPlusThread(&m_sampleFifo, &m_fcdFIFO); m_FCDThread->startWork(); @@ -147,6 +155,8 @@ void FCDProPlusInput::closeDevice() fcdClose(m_dev); m_dev = 0; + + closeFCDAudio(); } bool FCDProPlusInput::openFCDAudio(const char* cardname) @@ -188,7 +198,6 @@ void FCDProPlusInput::stop() // wait for thread to quit ? delete m_FCDThread; m_FCDThread = nullptr; - closeFCDAudio(); } m_running = false; @@ -277,6 +286,10 @@ bool FCDProPlusInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgFileRecord::match(message)) @@ -310,6 +323,17 @@ bool FCDProPlusInput::handleMessage(const Message& message) void FCDProPlusInput::applySettings(const FCDProPlusSettings& settings, bool force) { bool forwardChange = false; + QList reverseAPIKeys; + + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) { + reverseAPIKeys.append("centerFrequency"); + } + if (force || (m_settings.m_transverterMode != settings.m_transverterMode)) { + reverseAPIKeys.append("transverterMode"); + } + if (force || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) @@ -319,8 +343,7 @@ void FCDProPlusInput::applySettings(const FCDProPlusSettings& settings, bool for deviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; deviceCenterFrequency = deviceCenterFrequency < 0 ? 0 : deviceCenterFrequency; - if (m_dev != 0) - { + if (m_dev != 0) { set_center_freq((double) deviceCenterFrequency); } @@ -334,86 +357,91 @@ void FCDProPlusInput::applySettings(const FCDProPlusSettings& settings, bool for if ((m_settings.m_lnaGain != settings.m_lnaGain) || force) { - m_settings.m_lnaGain = settings.m_lnaGain; + reverseAPIKeys.append("lnaGain"); - if (m_dev != 0) - { + if (m_dev != 0) { set_lna_gain(settings.m_lnaGain); } } if ((m_settings.m_biasT != settings.m_biasT) || force) { - m_settings.m_biasT = settings.m_biasT; + reverseAPIKeys.append("biasT"); - if (m_dev != 0) - { + if (m_dev != 0) { set_bias_t(settings.m_biasT); } } if ((m_settings.m_mixGain != settings.m_mixGain) || force) { - m_settings.m_mixGain = settings.m_mixGain; + reverseAPIKeys.append("mixGain"); - if (m_dev != 0) - { + if (m_dev != 0) { set_mixer_gain(settings.m_mixGain); } } if ((m_settings.m_ifGain != settings.m_ifGain) || force) { - m_settings.m_ifGain = settings.m_ifGain; + reverseAPIKeys.append("ifGain"); - if (m_dev != 0) - { + if (m_dev != 0) { set_if_gain(settings.m_ifGain); } } if ((m_settings.m_ifFilterIndex != settings.m_ifFilterIndex) || force) { - m_settings.m_ifFilterIndex = settings.m_ifFilterIndex; + reverseAPIKeys.append("ifFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_if_filter(settings.m_ifFilterIndex); } } if ((m_settings.m_rfFilterIndex != settings.m_rfFilterIndex) || force) { - m_settings.m_rfFilterIndex = settings.m_rfFilterIndex; + reverseAPIKeys.append("rfFilterIndex"); - if (m_dev != 0) - { + if (m_dev != 0) { set_rf_filter(settings.m_rfFilterIndex); } } if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); m_settings.m_LOppmTenths = settings.m_LOppmTenths; - if (m_dev != 0) - { + if (m_dev != 0) { set_lo_ppm(); } } if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { - m_settings.m_dcBlock = settings.m_dcBlock; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqImbalance); + reverseAPIKeys.append("dcBlock"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqImbalance); } if ((m_settings.m_iqImbalance != settings.m_iqImbalance) || force) { - m_settings.m_iqImbalance = settings.m_iqImbalance; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqImbalance); + reverseAPIKeys.append("iqImbalance"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqImbalance); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + if (forwardChange) { DSPSignalNotification *notif = new DSPSignalNotification(fcd_traits::sampleRate, m_settings.m_centerFrequency); @@ -631,4 +659,106 @@ void FCDProPlusInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& } } +void FCDProPlusInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const FCDProPlusSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("FCDPro+")); + swgDeviceSettings->setFcdProPlusSettings(new SWGSDRangel::SWGFCDProPlusSettings()); + SWGSDRangel::SWGFCDProPlusSettings *swgFCDProPlusSettings = swgDeviceSettings->getFcdProPlusSettings(); + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgFCDProPlusSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("rangeLow") || force) { + swgFCDProPlusSettings->setRangeLow(settings.m_rangeLow ? 1 : 0); + } + if (deviceSettingsKeys.contains("lnaGain") || force) { + swgFCDProPlusSettings->setLnaGain(settings.m_lnaGain ? 1 : 0); + } + if (deviceSettingsKeys.contains("mixGain") || force) { + swgFCDProPlusSettings->setMixGain(settings.m_mixGain ? 1 : 0); + } + if (deviceSettingsKeys.contains("biasT") || force) { + swgFCDProPlusSettings->setBiasT(settings.m_biasT ? 1 : 0); + } + if (deviceSettingsKeys.contains("ifGain") || force) { + swgFCDProPlusSettings->setIfGain(settings.m_ifGain); + } + if (deviceSettingsKeys.contains("ifFilterIndex") || force) { + swgFCDProPlusSettings->setIfFilterIndex(settings.m_ifFilterIndex); + } + if (deviceSettingsKeys.contains("rfFilterIndex") || force) { + swgFCDProPlusSettings->setRfFilterIndex(settings.m_rfFilterIndex); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgFCDProPlusSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgFCDProPlusSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqImbalance") || force) { + swgFCDProPlusSettings->setIqImbalance(settings.m_iqImbalance ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgFCDProPlusSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgFCDProPlusSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgFCDProPlusSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void FCDProPlusInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void FCDProPlusInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "FCDProPlusInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("FCDProPlusInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/fcdproplus/fcdproplusinput.h b/plugins/samplesource/fcdproplus/fcdproplusinput.h index 94206cd61..be5af4a71 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusinput.h +++ b/plugins/samplesource/fcdproplus/fcdproplusinput.h @@ -17,9 +17,11 @@ #ifndef INCLUDE_FCDINPUT_H #define INCLUDE_FCDINPUT_H +#include + #include #include -#include +#include #include "dsp/devicesamplesource.h" #include "audio/audioinput.h" @@ -33,11 +35,14 @@ struct fcd_buffer { std::size_t length; }; +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class FCDProPlusThread; class FileRecord; class FCDProPlusInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureFCDProPlus : public Message { MESSAGE_CLASS_DECLARATION @@ -148,13 +153,6 @@ public: void set_lo_ppm(); private: - bool openDevice(); - void closeDevice(); - bool openFCDAudio(const char *filename); - void closeFCDAudio(); - void applySettings(const FCDProPlusSettings& settings, bool force); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const FCDProPlusSettings& settings); - DeviceSourceAPI *m_deviceAPI; hid_device *m_dev; AudioInput m_fcdAudioInput; @@ -165,6 +163,20 @@ private: QString m_deviceDescription; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool openFCDAudio(const char *filename); + void closeFCDAudio(); + void applySettings(const FCDProPlusSettings& settings, bool force); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const FCDProPlusSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const FCDProPlusSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_FCD_H diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp index 59d10c2d1..ba6d46367 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp @@ -39,6 +39,10 @@ void FCDProPlusSettings::resetToDefaults() m_transverterMode = false; m_transverterDeltaFrequency = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray FCDProPlusSettings::serialize() const @@ -56,6 +60,10 @@ QByteArray FCDProPlusSettings::serialize() const s.writeU32(9, m_ifGain); s.writeBool(10, m_transverterMode); s.writeS64(11, m_transverterDeltaFrequency); + s.writeBool(12, m_useReverseAPI); + s.writeString(13, m_reverseAPIAddress); + s.writeU32(14, m_reverseAPIPort); + s.writeU32(15, m_reverseAPIDeviceIndex); return s.final(); } @@ -72,6 +80,8 @@ bool FCDProPlusSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { + uint32_t uintval; + d.readBool(1, &m_biasT, false); d.readBool(2, &m_rangeLow, false); d.readBool(3, &m_mixGain, true); @@ -83,7 +93,18 @@ bool FCDProPlusSettings::deserialize(const QByteArray& data) d.readU32(9, &m_ifGain, 0); d.readBool(10, &m_transverterMode, false); d.readS64(11, &m_transverterDeltaFrequency, 0); + d.readBool(12, &m_useReverseAPI, false); + d.readString(13, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(14, &uintval, 0); + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(15, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } else diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.h b/plugins/samplesource/fcdproplus/fcdproplussettings.h index f4fe21c3d..7d505dcb4 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.h +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.h @@ -34,6 +34,10 @@ struct FCDProPlusSettings { bool m_transverterMode; qint64 m_transverterDeltaFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; FCDProPlusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/filesource/filesourcegui.cpp b/plugins/samplesource/filesource/filesourcegui.cpp index 700065a27..6134cb6a9 100644 --- a/plugins/samplesource/filesource/filesourcegui.cpp +++ b/plugins/samplesource/filesource/filesourcegui.cpp @@ -26,6 +26,8 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -63,6 +65,9 @@ FileSourceGui::FileSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + setAccelerationCombo(); displaySettings(); @@ -438,3 +443,22 @@ void FileSourceGui::setNumberStr(int n, QString& s) s = tr("%1G").arg(n/1000000000); } } + +void FileSourceGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/filesource/filesourcegui.h b/plugins/samplesource/filesource/filesourcegui.h index 2e43c5b42..bffe54515 100644 --- a/plugins/samplesource/filesource/filesourcegui.h +++ b/plugins/samplesource/filesource/filesourcegui.h @@ -97,6 +97,7 @@ private slots: void on_acceleration_currentIndexChanged(int index); void updateStatus(); void tick(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_FILESOURCEGUI_H diff --git a/plugins/samplesource/filesource/filesourceinput.cpp b/plugins/samplesource/filesource/filesourceinput.cpp index c68d3bbd1..ab710c391 100644 --- a/plugins/samplesource/filesource/filesourceinput.cpp +++ b/plugins/samplesource/filesource/filesourceinput.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGFileSourceSettings.h" @@ -62,10 +64,15 @@ FileSourceInput::FileSourceInput(DeviceSourceAPI *deviceAPI) : qDebug("FileSourceInput::FileSourceInput: device source engine: %p", m_deviceAPI->getDeviceSourceEngine()); qDebug("FileSourceInput::FileSourceInput: device source engine message queue: %p", m_deviceAPI->getDeviceEngineInputMessageQueue()); qDebug("FileSourceInput::FileSourceInput: device source: %p", m_deviceAPI->getDeviceSourceEngine()->getSource()); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } FileSourceInput::~FileSourceInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + stop(); } @@ -349,6 +356,10 @@ bool FileSourceInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (FileSourceThread::MsgReportEOF::match(message)) @@ -386,12 +397,16 @@ bool FileSourceInput::handleMessage(const Message& message) bool FileSourceInput::applySettings(const FileSourceSettings& settings, bool force) { + QList reverseAPIKeys; + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { m_centerFrequency = settings.m_centerFrequency; } if ((m_settings.m_accelerationFactor != settings.m_accelerationFactor) || force) { + reverseAPIKeys.append("accelerationFactor"); + if (m_fileSourceThread) { QMutexLocker mutexLocker(&m_mutex); @@ -403,6 +418,22 @@ bool FileSourceInput::applySettings(const FileSourceSettings& settings, bool for } } + if ((m_settings.m_loop != settings.m_loop)) { + reverseAPIKeys.append("loop"); + } + if ((m_settings.m_fileName != settings.m_fileName)) { + reverseAPIKeys.append("fileName"); + } + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; return true; } @@ -533,4 +564,73 @@ void FileSourceInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& res response.getFileSourceReport()->setSampleSize(m_sampleSize); } +void FileSourceInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const FileSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("FileSource")); + swgDeviceSettings->setFileSourceSettings(new SWGSDRangel::SWGFileSourceSettings()); + SWGSDRangel::SWGFileSourceSettings *swgFileSourceSettings = swgDeviceSettings->getFileSourceSettings(); + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("accelerationFactor") || force) { + swgFileSourceSettings->setAccelerationFactor(settings.m_accelerationFactor); + } + if (deviceSettingsKeys.contains("loop") || force) { + swgFileSourceSettings->setLoop(settings.m_loop); + } + if (deviceSettingsKeys.contains("fileName") || force) { + swgFileSourceSettings->setFileName(new QString(settings.m_fileName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void FileSourceInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void FileSourceInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "FileSourceInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("FileSourceInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/filesource/filesourceinput.h b/plugins/samplesource/filesource/filesourceinput.h index adef3b065..381c4d47a 100644 --- a/plugins/samplesource/filesource/filesourceinput.h +++ b/plugins/samplesource/filesource/filesourceinput.h @@ -17,20 +17,25 @@ #ifndef INCLUDE_FILESOURCEINPUT_H #define INCLUDE_FILESOURCEINPUT_H -#include -#include -#include #include #include #include -#include +#include +#include +#include +#include + +#include "dsp/devicesamplesource.h" #include "filesourcesettings.h" +class QNetworkAccessManager; +class QNetworkReply; class FileSourceThread; class DeviceSourceAPI; class FileSourceInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureFileSource : public Message { MESSAGE_CLASS_DECLARATION @@ -326,12 +331,19 @@ public: quint64 m_recordLength; //!< record length in seconds computed from file size quint64 m_startingTimeStamp; const QTimer& m_masterTimer; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; void openFileStream(); void seekFileStream(int seekMillis); bool applySettings(const FileSourceSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const FileSourceSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const FileSourceSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_FILESOURCEINPUT_H diff --git a/plugins/samplesource/filesource/filesourcesettings.cpp b/plugins/samplesource/filesource/filesourcesettings.cpp index f8a6b370a..3b105f4ad 100644 --- a/plugins/samplesource/filesource/filesourcesettings.cpp +++ b/plugins/samplesource/filesource/filesourcesettings.cpp @@ -32,6 +32,10 @@ void FileSourceSettings::resetToDefaults() m_fileName = "./test.sdriq"; m_accelerationFactor = 1; m_loop = true; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray FileSourceSettings::serialize() const @@ -40,6 +44,11 @@ QByteArray FileSourceSettings::serialize() const s.writeString(1, m_fileName); s.writeU32(2, m_accelerationFactor); s.writeBool(3, m_loop); + s.writeBool(4, m_useReverseAPI); + s.writeString(5, m_reverseAPIAddress); + s.writeU32(6, m_reverseAPIPort); + s.writeU32(7, m_reverseAPIDeviceIndex); + return s.final(); } @@ -52,12 +61,30 @@ bool FileSourceSettings::deserialize(const QByteArray& data) return false; } - if(d.getVersion() == 1) { + if (d.getVersion() == 1) + { + uint32_t uintval; + d.readString(1, &m_fileName, "./test.sdriq"); d.readU32(2, &m_accelerationFactor, 1); d.readBool(3, &m_loop, true); + d.readBool(4, &m_useReverseAPI, false); + d.readString(5, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(6, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(7, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + return true; - } else { + } + else + { resetToDefaults(); return false; } diff --git a/plugins/samplesource/filesource/filesourcesettings.h b/plugins/samplesource/filesource/filesourcesettings.h index d355e842c..51b94b80a 100644 --- a/plugins/samplesource/filesource/filesourcesettings.h +++ b/plugins/samplesource/filesource/filesourcesettings.h @@ -26,6 +26,11 @@ struct FileSourceSettings { QString m_fileName; quint32 m_accelerationFactor; bool m_loop; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 FileSourceSettings(); diff --git a/plugins/samplesource/hackrfinput/hackrfinput.cpp b/plugins/samplesource/hackrfinput/hackrfinput.cpp index 2e0dc8dc9..614e86dd3 100644 --- a/plugins/samplesource/hackrfinput/hackrfinput.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinput.cpp @@ -14,11 +14,12 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "hackrfinput.h" - #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -32,6 +33,7 @@ #include "hackrf/devicehackrfvalues.h" #include "hackrf/devicehackrfshared.h" +#include "hackrfinput.h" #include "hackrfinputthread.h" MESSAGE_CLASS_DEFINITION(HackRFInput::MsgConfigureHackRF, Message) @@ -53,11 +55,20 @@ HackRFInput::HackRFInput(DeviceSourceAPI *deviceAPI) : m_deviceAPI->addSink(m_fileSink); m_deviceAPI->setBuddySharedPtr(&m_sharedParams); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } HackRFInput::~HackRFInput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -292,6 +303,10 @@ bool HackRFInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -323,9 +338,20 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) bool forwardChange = false; hackrf_error rc; + QList reverseAPIKeys; qDebug() << "HackRFInput::applySettings"; + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); + } + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + } + if ((m_settings.m_linkTxFrequency != settings.m_linkTxFrequency) || force) { + reverseAPIKeys.append("linkTxFrequency"); + } + if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { @@ -334,6 +360,7 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); forwardChange = true; if (m_dev != 0) @@ -357,6 +384,7 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChange = true; if (m_hackRFThread != 0) @@ -368,7 +396,9 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) // forward delta to buddy if necessary { - if (m_settings.m_linkTxFrequency && (m_deviceAPI->getSinkBuddies().size() > 0)) + reverseAPIKeys.append("centerFrequency"); + + if (m_settings.m_linkTxFrequency && (m_deviceAPI->getSinkBuddies().size() > 0)) { DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[0]; DeviceHackRFShared::MsgConfigureFrequencyDelta *deltaMsg = DeviceHackRFShared::MsgConfigureFrequencyDelta::create( @@ -384,6 +414,13 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) } } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_fcPos != settings.m_fcPos) || force) { + reverseAPIKeys.append("fcPos"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_LOppmTenths != settings.m_LOppmTenths) || @@ -415,16 +452,15 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_lnaGain != settings.m_lnaGain) || force) { + reverseAPIKeys.append("lnaGain"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_lna_gain(m_dev, settings.m_lnaGain); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: airspy_set_lna_gain failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: LNA gain set to " << settings.m_lnaGain; } } @@ -432,16 +468,15 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_vgaGain != settings.m_vgaGain) || force) { + reverseAPIKeys.append("vgaGain"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_vga_gain(m_dev, settings.m_vgaGain); - if (rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_vga_gain failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: VGA gain set to " << settings.m_vgaGain; } } @@ -449,17 +484,16 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); + if (m_dev != 0) { uint32_t bw_index = hackrf_compute_baseband_filter_bw_round_down_lt(settings.m_bandwidth + 1); // +1 so the round down to lower than yields desired bandwidth rc = (hackrf_error) hackrf_set_baseband_filter_bandwidth(m_dev, bw_index); - if (rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_baseband_filter_bandwidth failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: Baseband BW filter set to " << settings.m_bandwidth << " Hz"; } } @@ -467,16 +501,15 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_biasT != settings.m_biasT) || force) { + reverseAPIKeys.append("biasT"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_antenna_enable(m_dev, (settings.m_biasT ? 1 : 0)); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_antenna_enable failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: bias tee set to " << settings.m_biasT; } } @@ -484,16 +517,15 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) if ((m_settings.m_lnaExt != settings.m_lnaExt) || force) { + reverseAPIKeys.append("lnaExt"); + if (m_dev != 0) { rc = (hackrf_error) hackrf_set_amp_enable(m_dev, (settings.m_lnaExt ? 1 : 0)); - if(rc != HACKRF_SUCCESS) - { + if (rc != HACKRF_SUCCESS) { qDebug("HackRFInput::applySettings: hackrf_set_amp_enable failed: %s", hackrf_error_name(rc)); - } - else - { + } else { qDebug() << "HackRFInput:applySettings: extra LNA set to " << settings.m_lnaExt; } } @@ -507,6 +539,15 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force) m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; qDebug() << "HackRFInput::applySettings: " @@ -656,20 +697,106 @@ int HackRFInput::webapiRun( return 200; } -//hackrf_device *HackRFInput::open_hackrf_from_sequence(int sequence) -//{ -// hackrf_device_list_t *hackrf_devices = hackrf_device_list(); -// hackrf_device *hackrf_ptr; -// hackrf_error rc; -// -// rc = (hackrf_error) hackrf_device_list_open(hackrf_devices, sequence, &hackrf_ptr); -// -// if (rc == HACKRF_SUCCESS) -// { -// return hackrf_ptr; -// } -// else -// { -// return 0; -// } -//} +void HackRFInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const HackRFInputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("HackRF")); + swgDeviceSettings->setHackRfInputSettings(new SWGSDRangel::SWGHackRFInputSettings()); + SWGSDRangel::SWGHackRFInputSettings *swgHackRFInputSettings = swgDeviceSettings->getHackRfInputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgHackRFInputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgHackRFInputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgHackRFInputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("lnaGain") || force) { + swgHackRFInputSettings->setLnaGain(settings.m_lnaGain); + } + if (deviceSettingsKeys.contains("vgaGain") || force) { + swgHackRFInputSettings->setVgaGain(settings.m_vgaGain); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgHackRFInputSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgHackRFInputSettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgHackRFInputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("biasT") || force) { + swgHackRFInputSettings->setBiasT(settings.m_biasT ? 1 : 0); + } + if (deviceSettingsKeys.contains("lnaExt") || force) { + swgHackRFInputSettings->setLnaExt(settings.m_lnaExt ? 1 : 0); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgHackRFInputSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgHackRFInputSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("linkTxFrequency") || force) { + swgHackRFInputSettings->setLinkTxFrequency(settings.m_linkTxFrequency ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgHackRFInputSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void HackRFInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void HackRFInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "HackRFInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("HackRFInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/hackrfinput/hackrfinput.h b/plugins/samplesource/hackrfinput/hackrfinput.h index 890e6d616..4204d4d9a 100644 --- a/plugins/samplesource/hackrfinput/hackrfinput.h +++ b/plugins/samplesource/hackrfinput/hackrfinput.h @@ -19,18 +19,22 @@ #include #include +#include #include "libhackrf/hackrf.h" -#include +#include "dsp/devicesamplesource.h" #include "hackrf/devicehackrf.h" #include "hackrf/devicehackrfparam.h" #include "hackrfinputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class HackRFInputThread; class FileRecord; class HackRFInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureHackRF : public Message { @@ -151,13 +155,6 @@ public: private: - bool openDevice(); - void closeDevice(); - bool applySettings(const HackRFInputSettings& settings, bool force); -// hackrf_device *open_hackrf_from_sequence(int sequence); - void setDeviceCenterFrequency(quint64 freq); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const HackRFInputSettings& settings); - DeviceSourceAPI *m_deviceAPI; QMutex m_mutex; HackRFInputSettings m_settings; @@ -167,6 +164,19 @@ private: DeviceHackRFParams m_sharedParams; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const HackRFInputSettings& settings, bool force); + void setDeviceCenterFrequency(quint64 freq); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const HackRFInputSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const HackRFInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_HACKRFINPUT_H diff --git a/plugins/samplesource/hackrfinput/hackrfinput.pro b/plugins/samplesource/hackrfinput/hackrfinput.pro index afb2cf9d7..da1cf188d 100644 --- a/plugins/samplesource/hackrfinput/hackrfinput.pro +++ b/plugins/samplesource/hackrfinput/hackrfinput.pro @@ -18,7 +18,6 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBHACKRFSRC = "C:\softs\hackrf\host" -CONFIG(MINGW64):LIBHACKRFSRC = "C:\softs\hackrf\host" CONFIG(MSVC):LIBHACKRFSRC = "C:\softs\hackrf\host" INCLUDEPATH += $$PWD diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 6de6ca1b0..6ef9b54ed 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -23,6 +23,8 @@ #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" @@ -55,6 +57,9 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); displayBandwidths(); @@ -417,3 +422,22 @@ void HackRFInputGui::updateStatus() m_lastEngineState = state; } } + +void HackRFInputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 0f714af56..5e28a879d 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -100,6 +100,7 @@ private slots: void on_record_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_HACKRFINPUTGUI_H diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 3ac127c62..d7da12789 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -32,7 +32,7 @@ const PluginDescriptor HackRFInputPlugin::m_pluginDescriptor = { QString("HackRF Input"), - QString("3.14.6"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp index d2831bcc1..0809b776d 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp @@ -41,6 +41,10 @@ void HackRFInputSettings::resetToDefaults() m_devSampleRate = 2400000; m_linkTxFrequency = false; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray HackRFInputSettings::serialize() const @@ -59,6 +63,10 @@ QByteArray HackRFInputSettings::serialize() const s.writeBool(11, m_iqCorrection); s.writeU64(12, m_devSampleRate); s.writeBool(13, m_linkTxFrequency); + s.writeBool(14, m_useReverseAPI); + s.writeString(15, m_reverseAPIAddress); + s.writeU32(16, m_reverseAPIPort); + s.writeU32(17, m_reverseAPIDeviceIndex); return s.final(); } @@ -76,6 +84,7 @@ bool HackRFInputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_LOppmTenths, 0); d.readBool(3, &m_biasT, false); @@ -89,7 +98,19 @@ bool HackRFInputSettings::deserialize(const QByteArray& data) d.readBool(10, &m_dcBlock, false); d.readBool(11, &m_iqCorrection, false); d.readU64(12, &m_devSampleRate, 2400000U); - d.readBool(11, &m_linkTxFrequency, false); + d.readBool(13, &m_linkTxFrequency, false); + d.readBool(14, &m_useReverseAPI, false); + d.readString(15, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(16, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(17, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.h b/plugins/samplesource/hackrfinput/hackrfinputsettings.h index b2f0b3056..b1042ce07 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.h +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.h @@ -41,6 +41,10 @@ struct HackRFInputSettings { bool m_iqCorrection; bool m_linkTxFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; HackRFInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/limesdrinput/limesdrinput.cpp b/plugins/samplesource/limesdrinput/limesdrinput.cpp index ab08e7c3e..5b605891e 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinput.cpp @@ -14,10 +14,14 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include -#include #include #include + +#include +#include +#include +#include + #include "lime/LimeSuite.h" #include "SWGDeviceSettings.h" @@ -61,11 +65,20 @@ LimeSDRInput::LimeSDRInput(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } LimeSDRInput::~LimeSDRInput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; suspendRxBuddies(); @@ -759,6 +772,10 @@ bool LimeSDRInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -779,6 +796,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc bool doLPCalibration = false; bool setAntennaAuto = false; double clockGenFreq = 0.0; + QList reverseAPIKeys; + // QMutexLocker mutexLocker(&m_mutex); qint64 deviceCenterFrequency = settings.m_centerFrequency; @@ -798,16 +817,20 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_gainMode != settings.m_gainMode) || force) { + reverseAPIKeys.append("gainMode"); + if (settings.m_gainMode == LimeSDRInputSettings::GAIN_AUTO) { if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -871,6 +894,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_gainMode == LimeSDRInputSettings::GAIN_AUTO) && (m_settings.m_gain != settings.m_gain)) { + reverseAPIKeys.append("gain"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (LMS_SetGaindB(m_deviceShared.m_deviceParams->getDevice(), @@ -890,6 +915,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_gainMode == LimeSDRInputSettings::GAIN_MANUAL) && (m_settings.m_lnaGain != settings.m_lnaGain)) { + reverseAPIKeys.append("lnaGain"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (DeviceLimeSDR::SetRFELNA_dB(m_deviceShared.m_deviceParams->getDevice(), @@ -908,6 +935,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_gainMode == LimeSDRInputSettings::GAIN_MANUAL) && (m_settings.m_tiaGain != settings.m_tiaGain)) { + reverseAPIKeys.append("tiaGain"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (DeviceLimeSDR::SetRFETIA_dB(m_deviceShared.m_deviceParams->getDevice(), @@ -926,6 +955,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_gainMode == LimeSDRInputSettings::GAIN_MANUAL) && (m_settings.m_pgaGain != settings.m_pgaGain)) { + reverseAPIKeys.append("pgaGain"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (DeviceLimeSDR::SetRBBPGA_dB(m_deviceShared.m_deviceParams->getDevice(), @@ -945,6 +976,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force) { + reverseAPIKeys.append("devSampleRate"); + reverseAPIKeys.append("log2HardDecim"); forwardChangeAllDSP = true; //m_settings.m_devSampleRate != settings.m_devSampleRate; if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -973,6 +1006,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_lpfBW != settings.m_lpfBW) || force) { + reverseAPIKeys.append("lpfBW"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { doLPCalibration = true; @@ -982,6 +1017,9 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) || (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force) { + reverseAPIKeys.append("lpfFIRBW"); + reverseAPIKeys.append("lpfFIREnable"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (LMS_SetGFIRLPF(m_deviceShared.m_deviceParams->getDevice(), @@ -1007,6 +1045,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) || (m_settings.m_ncoEnable != settings.m_ncoEnable) || force || forceNCOFrequency) { + reverseAPIKeys.append("ncoFrequency"); + reverseAPIKeys.append("ncoEnable"); forwardChangeOwnDSP = true; if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -1034,6 +1074,7 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_log2SoftDecim != settings.m_log2SoftDecim) || force) { + reverseAPIKeys.append("log2SoftDecim"); forwardChangeOwnDSP = true; m_deviceShared.m_log2Soft = settings.m_log2SoftDecim; // for buddies @@ -1046,6 +1087,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_antennaPath != settings.m_antennaPath) || force) { + reverseAPIKeys.append("antennaPath"); + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) { if (DeviceLimeSDR::setRxAntennaPath(m_deviceShared.m_deviceParams->getDevice(), @@ -1071,6 +1114,9 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || setAntennaAuto || force) { + reverseAPIKeys.append("centerFrequency"); + reverseAPIKeys.append("transverterMode"); + reverseAPIKeys.append("transverterDeltaFrequency"); forwardChangeRxDSP = true; if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) @@ -1091,6 +1137,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_extClock != settings.m_extClock) || (settings.m_extClock && (m_settings.m_extClockFreq != settings.m_extClockFreq)) || force) { + reverseAPIKeys.append("extClock"); + reverseAPIKeys.append("extClockFreq"); if (DeviceLimeSDR::setClockSource(m_deviceShared.m_deviceParams->getDevice(), settings.m_extClock, @@ -1115,6 +1163,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc { if ((m_settings.m_gpioDir != settings.m_gpioDir) || force) { + reverseAPIKeys.append("gpioDir"); + if (LMS_GPIODirWrite(m_deviceShared.m_deviceParams->getDevice(), &settings.m_gpioDir, 1) != 0) { qCritical("LimeSDROutput::applySettings: could not set GPIO directions to %u", settings.m_gpioDir); @@ -1128,6 +1178,8 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc if ((m_settings.m_gpioPins != settings.m_gpioPins) || force) { + reverseAPIKeys.append("gpioPins"); + if (LMS_GPIOWrite(m_deviceShared.m_deviceParams->getDevice(), &settings.m_gpioPins, 1) != 0) { qCritical("LimeSDROutput::applySettings: could not set GPIO pins to %u", settings.m_gpioPins); @@ -1140,6 +1192,15 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; double clockGenFreqAfter; @@ -1444,6 +1505,18 @@ int LimeSDRInput::webapiSettingsPutPatch( if (deviceSettingsKeys.contains("gpioPins")) { settings.m_gpioPins = response.getLimeSdrInputSettings()->getGpioPins() & 0xFF; } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getLimeSdrInputSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getLimeSdrInputSettings()->getReverseApiAddress() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getLimeSdrInputSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getLimeSdrInputSettings()->getReverseApiDeviceIndex(); + } MsgConfigureLimeSDR *msg = MsgConfigureLimeSDR::create(settings, force); m_inputMessageQueue.push(msg); @@ -1490,6 +1563,16 @@ void LimeSDRInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& re response.getLimeSdrInputSettings()->setGpioDir(settings.m_gpioDir); response.getLimeSdrInputSettings()->setGpioPins(settings.m_gpioPins); + response.getLimeSdrInputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getLimeSdrInputSettings()->getReverseApiAddress()) { + *response.getLimeSdrInputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getLimeSdrInputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getLimeSdrInputSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getLimeSdrInputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); } int LimeSDRInput::webapiReportGet( @@ -1570,3 +1653,137 @@ void LimeSDRInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respon response.getLimeSdrInputReport()->setGpioDir(gpioDir); response.getLimeSdrInputReport()->setGpioPins(gpioPins); } + +void LimeSDRInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const LimeSDRInputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("LimeSDR")); + swgDeviceSettings->setLimeSdrInputSettings(new SWGSDRangel::SWGLimeSdrInputSettings()); + SWGSDRangel::SWGLimeSdrInputSettings *swgLimeSdrInputSettings = swgDeviceSettings->getLimeSdrInputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("antennaPath") || force) { + swgLimeSdrInputSettings->setAntennaPath((int) settings.m_antennaPath); + } + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgLimeSdrInputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgLimeSdrInputSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgLimeSdrInputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("extClock") || force) { + swgLimeSdrInputSettings->setExtClock(settings.m_extClock ? 1 : 0); + } + if (deviceSettingsKeys.contains("extClockFreq") || force) { + swgLimeSdrInputSettings->setExtClockFreq(settings.m_extClockFreq); + } + if (deviceSettingsKeys.contains("gain") || force) { + swgLimeSdrInputSettings->setGain(settings.m_gain); + } + if (deviceSettingsKeys.contains("gainMode") || force) { + swgLimeSdrInputSettings->setGainMode((int) settings.m_gainMode); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgLimeSdrInputSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("lnaGain") || force) { + swgLimeSdrInputSettings->setLnaGain(settings.m_lnaGain); + } + if (deviceSettingsKeys.contains("log2HardDecim") || force) { + swgLimeSdrInputSettings->setLog2HardDecim(settings.m_log2HardDecim); + } + if (deviceSettingsKeys.contains("log2SoftDecim") || force) { + swgLimeSdrInputSettings->setLog2SoftDecim(settings.m_log2SoftDecim); + } + if (deviceSettingsKeys.contains("lpfBW") || force) { + swgLimeSdrInputSettings->setLpfBw(settings.m_lpfBW); + } + if (deviceSettingsKeys.contains("lpfFIREnable") || force) { + swgLimeSdrInputSettings->setLpfFirEnable(settings.m_lpfFIREnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("lpfFIRBW") || force) { + swgLimeSdrInputSettings->setLpfFirbw(settings.m_lpfFIRBW); + } + if (deviceSettingsKeys.contains("ncoEnable") || force) { + swgLimeSdrInputSettings->setNcoEnable(settings.m_ncoEnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("ncoFrequency") || force) { + swgLimeSdrInputSettings->setNcoFrequency(settings.m_ncoFrequency); + } + if (deviceSettingsKeys.contains("pgaGain") || force) { + swgLimeSdrInputSettings->setPgaGain(settings.m_pgaGain); + } + if (deviceSettingsKeys.contains("tiaGain") || force) { + swgLimeSdrInputSettings->setTiaGain(settings.m_tiaGain); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgLimeSdrInputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgLimeSdrInputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgLimeSdrInputSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + if (deviceSettingsKeys.contains("gpioDir") || force) { + swgLimeSdrInputSettings->setGpioDir(settings.m_gpioDir & 0xFF); + } + if (deviceSettingsKeys.contains("gpioPins") || force) { + swgLimeSdrInputSettings->setGpioPins(settings.m_gpioPins & 0xFF); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void LimeSDRInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void LimeSDRInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "LimeSDRInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("LimeSDRInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/limesdrinput/limesdrinput.h b/plugins/samplesource/limesdrinput/limesdrinput.h index 532f2579a..036a60c28 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.h +++ b/plugins/samplesource/limesdrinput/limesdrinput.h @@ -17,9 +17,11 @@ #ifndef PLUGINS_SAMPLESOURCE_LIMESDRINPUT_LIMESDRINPUT_H_ #define PLUGINS_SAMPLESOURCE_LIMESDRINPUT_LIMESDRINPUT_H_ +#include + #include #include -#include +#include #include "dsp/devicesamplesource.h" #include "limesdr/devicelimesdrshared.h" @@ -28,9 +30,12 @@ class DeviceSourceAPI; class LimeSDRInputThread; class FileRecord; +class QNetworkAccessManager; +class QNetworkReply; class LimeSDRInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureLimeSDR : public Message { MESSAGE_CLASS_DECLARATION @@ -257,6 +262,8 @@ private: bool m_channelAcquired; lms_stream_t m_streamId; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -269,6 +276,11 @@ private: bool applySettings(const LimeSDRInputSettings& settings, bool force = false, bool forceNCOFrequency = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const LimeSDRInputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const LimeSDRInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* PLUGINS_SAMPLESOURCE_LIMESDRINPUT_LIMESDRINPUT_H_ */ diff --git a/plugins/samplesource/limesdrinput/limesdrinput.pro b/plugins/samplesource/limesdrinput/limesdrinput.pro index 8c6ec1fe0..e3aec508c 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.pro +++ b/plugins/samplesource/limesdrinput/limesdrinput.pro @@ -20,7 +20,6 @@ QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBLIMESUITESRC = "C:\softs\LimeSuite" -CONFIG(MINGW64):LIBLIMESUITESRC = "C:\softs\LimeSuite" INCLUDEPATH += $$PWD INCLUDEPATH += ../../../exports @@ -29,7 +28,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 || macx { +MINGW32 || macx { INCLUDEPATH += ../../../liblimesuite/srcmw INCLUDEPATH += $$LIBLIMESUITESRC/src INCLUDEPATH += $$LIBLIMESUITESRC/src/ADF4002 @@ -68,7 +67,7 @@ LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 || macx { +MINGW32 || macx { LIBS += -L../../../liblimesuite/$${build_subdir} -lliblimesuite } MSVC { diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index ff0f60ea9..13ccb5c4e 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -24,6 +24,8 @@ #include "ui_limesdrinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" @@ -77,6 +79,9 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_limeSDRInput->setMessageQueueToGUI(&m_inputMessageQueue); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } LimeSDRInputGUI::~LimeSDRInputGUI() @@ -651,4 +656,21 @@ void LimeSDRInputGUI::on_transverter_clicked() sendSettings(); } +void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index 3ded84e9f..3da1bc347 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -100,6 +100,7 @@ private slots: void on_antenna_currentIndexChanged(int index); void on_extClock_clicked(); void on_transverter_clicked(); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index b1abed6d4..fd8cfe3d6 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -33,7 +33,7 @@ const PluginDescriptor LimeSDRInputPlugin::m_pluginDescriptor = { QString("LimeSDR Input"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp index 736069ea0..ae86774da 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp @@ -48,6 +48,10 @@ void LimeSDRInputSettings::resetToDefaults() m_fileRecordName = ""; m_gpioDir = 0; m_gpioPins = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray LimeSDRInputSettings::serialize() const @@ -76,7 +80,10 @@ QByteArray LimeSDRInputSettings::serialize() const s.writeS64(21, m_transverterDeltaFrequency); s.writeU32(22, m_gpioDir); s.writeU32(23, m_gpioPins); - + s.writeBool(24, m_useReverseAPI); + s.writeString(25, m_reverseAPIAddress); + s.writeU32(26, m_reverseAPIPort); + s.writeU32(27, m_reverseAPIDeviceIndex); return s.final(); } @@ -121,6 +128,18 @@ bool LimeSDRInputSettings::deserialize(const QByteArray& data) m_gpioDir = uintval & 0xFF; d.readU32(23, &uintval, 0); m_gpioPins = uintval & 0xFF; + d.readBool(24, &m_useReverseAPI, false); + d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(26, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(27, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.h b/plugins/samplesource/limesdrinput/limesdrinputsettings.h index cade58966..87ddbe785 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.h +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.h @@ -68,6 +68,10 @@ struct LimeSDRInputSettings QString m_fileRecordName; uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output uint8_t m_gpioPins; //!< GPIO pins to write; LSB first + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; LimeSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 7ca36aa33..53180d55a 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -24,6 +24,8 @@ #include "ui_perseusgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "perseusgui.h" @@ -48,6 +50,9 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); m_rates = m_sampleSource->getSampleRates(); @@ -398,6 +403,21 @@ int PerseusGui::getDevSampleRateIndex(uint32_t sampeRate) return -1; } +void PerseusGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.move(p); + dialog.exec(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + sendSettings(); +} diff --git a/plugins/samplesource/perseus/perseusgui.h b/plugins/samplesource/perseus/perseusgui.h index cf7fe0497..8ca4fb63b 100644 --- a/plugins/samplesource/perseus/perseusgui.h +++ b/plugins/samplesource/perseus/perseusgui.h @@ -91,6 +91,7 @@ private slots: void updateHardware(); void updateStatus(); void handleInputMessages(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif /* PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSGUI_H_ */ diff --git a/plugins/samplesource/perseus/perseusinput.cpp b/plugins/samplesource/perseus/perseusinput.cpp index 67632131e..33d48e51e 100644 --- a/plugins/samplesource/perseus/perseusinput.cpp +++ b/plugins/samplesource/perseus/perseusinput.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -45,10 +47,15 @@ PerseusInput::PerseusInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } PerseusInput::~PerseusInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -187,6 +194,10 @@ bool PerseusInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgFileRecord::match(message)) @@ -287,9 +298,11 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) { bool forwardChange = false; int sampleRateIndex = settings.m_devSampleRateIndex; + QList reverseAPIKeys; if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force) { + reverseAPIKeys.append("devSampleRateIndex"); forwardChange = true; if (settings.m_devSampleRateIndex >= m_sampleRates.size()) { @@ -321,6 +334,7 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChange = true; if (m_perseusThread != 0) @@ -330,6 +344,22 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) } } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency)) { + reverseAPIKeys.append("centerFrequency"); + } + if (force || (m_settings.m_LOppmTenths != settings.m_LOppmTenths)) { + reverseAPIKeys.append("LOppmTenths"); + } + if (force || (m_settings.m_wideBand != settings.m_wideBand)) { + reverseAPIKeys.append("wideBand"); + } + if (force || (m_settings.m_transverterMode != settings.m_transverterMode)) { + reverseAPIKeys.append("transverterMode"); + } + if (force || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_LOppmTenths != settings.m_LOppmTenths) || (m_settings.m_wideBand != settings.m_wideBand) @@ -352,6 +382,7 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) if ((m_settings.m_attenuator != settings.m_attenuator) || force) { + reverseAPIKeys.append("attenuator"); int rc = perseus_set_attenuator_n(m_perseusDescriptor, (int) settings.m_attenuator); if (rc < 0) { @@ -361,6 +392,13 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) } } + if (force || (m_settings.m_adcDither != settings.m_adcDither)) { + reverseAPIKeys.append("adcDither"); + } + if (force || (m_settings.m_adcPreamp != settings.m_adcPreamp)) { + reverseAPIKeys.append("adcPreamp"); + } + if ((m_settings.m_adcDither != settings.m_adcDither) || (m_settings.m_adcPreamp != settings.m_adcPreamp) || force) { @@ -383,6 +421,15 @@ bool PerseusInput::applySettings(const PerseusSettings& settings, bool force) m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; m_settings.m_devSampleRateIndex = sampleRateIndex; @@ -539,3 +586,97 @@ void PerseusInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respon } } +void PerseusInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const PerseusSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("Perseus")); + swgDeviceSettings->setPerseusSettings(new SWGSDRangel::SWGPerseusSettings()); + SWGSDRangel::SWGPerseusSettings *swgPerseusSettings = swgDeviceSettings->getPerseusSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgPerseusSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgPerseusSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("devSampleRateIndex") || force) { + swgPerseusSettings->setDevSampleRateIndex(settings.m_devSampleRateIndex); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgPerseusSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("adcDither") || force) { + swgPerseusSettings->setAdcDither(settings.m_adcDither ? 1 : 0); + } + if (deviceSettingsKeys.contains("adcPreamp") || force) { + swgPerseusSettings->setAdcPreamp(settings.m_adcPreamp ? 1 : 0); + } + if (deviceSettingsKeys.contains("wideBand") || force) { + swgPerseusSettings->setWideBand(settings.m_wideBand ? 1 : 0); + } + if (deviceSettingsKeys.contains("attenuator") || force) { + swgPerseusSettings->setAttenuator((int) settings.m_attenuator); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgPerseusSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgPerseusSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgPerseusSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void PerseusInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void PerseusInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "PerseusInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("PerseusInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/perseus/perseusinput.h b/plugins/samplesource/perseus/perseusinput.h index f30ed9877..9c5f05886 100644 --- a/plugins/samplesource/perseus/perseusinput.h +++ b/plugins/samplesource/perseus/perseusinput.h @@ -18,17 +18,22 @@ #define PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSINPUT_H_ #include -#include "perseus-sdr.h" +#include + +#include "perseus-sdr.h" #include "dsp/devicesamplesource.h" #include "util/message.h" #include "perseussettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class FileRecord; class PerseusThread; class PerseusInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigurePerseus : public Message { MESSAGE_CLASS_DECLARATION @@ -144,6 +149,8 @@ private: PerseusThread *m_perseusThread; perseus_descr *m_perseusDescriptor; std::vector m_sampleRates; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -151,6 +158,11 @@ private: bool applySettings(const PerseusSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const PerseusSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const PerseusSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif /* PLUGINS_SAMPLESOURCE_PERSEUS_PERSEUSINPUT_H_ */ diff --git a/plugins/samplesource/perseus/perseusplugin.cpp b/plugins/samplesource/perseus/perseusplugin.cpp index f4e5e6276..64a30ace5 100644 --- a/plugins/samplesource/perseus/perseusplugin.cpp +++ b/plugins/samplesource/perseus/perseusplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor PerseusPlugin::m_pluginDescriptor = { QString("Perseus Input"), - QString("4.0.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/perseus/perseussettings.cpp b/plugins/samplesource/perseus/perseussettings.cpp index 5ae31d97d..0d6535e4b 100644 --- a/plugins/samplesource/perseus/perseussettings.cpp +++ b/plugins/samplesource/perseus/perseussettings.cpp @@ -36,6 +36,10 @@ void PerseusSettings::resetToDefaults() m_wideBand = false; m_attenuator = Attenuator_None; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray PerseusSettings::serialize() const @@ -51,6 +55,10 @@ QByteArray PerseusSettings::serialize() const s.writeBool(7, m_adcPreamp); s.writeBool(8, m_wideBand); s.writeS32(9, (int) m_attenuator); + s.writeBool(10, m_useReverseAPI); + s.writeString(11, m_reverseAPIAddress); + s.writeU32(12, m_reverseAPIPort); + s.writeU32(13, m_reverseAPIDeviceIndex); return s.final(); } @@ -68,6 +76,7 @@ bool PerseusSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readU32(1, &m_devSampleRateIndex, 0); d.readS32(2, &m_LOppmTenths, 0); @@ -85,6 +94,19 @@ bool PerseusSettings::deserialize(const QByteArray& data) m_attenuator = Attenuator_None; } + d.readBool(10, &m_useReverseAPI, false); + d.readString(11, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(12, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(13, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + return true; } else diff --git a/plugins/samplesource/perseus/perseussettings.h b/plugins/samplesource/perseus/perseussettings.h index 1330e2747..f6bf92174 100644 --- a/plugins/samplesource/perseus/perseussettings.h +++ b/plugins/samplesource/perseus/perseussettings.h @@ -42,6 +42,10 @@ struct PerseusSettings bool m_wideBand; Attenuator m_attenuator; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; PerseusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp index c56c35a36..85e495d6f 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -59,10 +61,15 @@ PlutoSDRInput::PlutoSDRInput(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } PlutoSDRInput::~PlutoSDRInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; suspendBuddies(); @@ -226,6 +233,10 @@ bool PlutoSDRInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (DevicePlutoSDRShared::MsgCrossReportToBuddy::match(message)) // message from buddy @@ -352,15 +363,19 @@ bool PlutoSDRInput::applySettings(const PlutoSDRInputSettings& settings, bool fo bool suspendAllOtherThreads = false; // All others means Tx in fact DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox(); QLocale loc; + QList reverseAPIKeys; qDebug().noquote() << "PlutoSDRInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" << " m_devSampleRate: " << loc.toString(m_settings.m_devSampleRate) << "S/s" << " m_LOppmTenths: " << m_settings.m_LOppmTenths + << " m_dcBlock: " << m_settings.m_dcBlock + << " m_iqCorrection: " << m_settings.m_iqCorrection << " m_lpfFIREnable: " << m_settings.m_lpfFIREnable << " m_lpfFIRBW: " << loc.toString(m_settings.m_lpfFIRBW) << " m_lpfFIRlog2Decim: " << m_settings.m_lpfFIRlog2Decim << " m_lpfFIRGain: " << m_settings.m_lpfFIRGain << " m_log2Decim: " << loc.toString(1<set_params(DevicePlutoSDRBox::DEVICE_PHY, params); } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; if (suspendAllOtherThreads) @@ -789,3 +868,119 @@ void PlutoSDRInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respo fetchTemperature(); response.getPlutoSdrInputReport()->setTemperature(getTemperature()); } + +void PlutoSDRInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const PlutoSDRInputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("PlutoSDR")); + swgDeviceSettings->setPlutoSdrInputSettings(new SWGSDRangel::SWGPlutoSdrInputSettings()); + SWGSDRangel::SWGPlutoSdrInputSettings *swgPlutoSdrInputSettings = swgDeviceSettings->getPlutoSdrInputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgPlutoSdrInputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgPlutoSdrInputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgPlutoSdrInputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("lpfFIREnable") || force) { + swgPlutoSdrInputSettings->setLpfFirEnable(settings.m_lpfFIREnable ? 1 : 0); + } + if (deviceSettingsKeys.contains("lpfFIRBW") || force) { + swgPlutoSdrInputSettings->setLpfFirbw(settings.m_lpfFIRBW); + } + if (deviceSettingsKeys.contains("lpfFIRlog2Decim") || force) { + swgPlutoSdrInputSettings->setLpfFiRlog2Decim(settings.m_lpfFIRlog2Decim); + } + if (deviceSettingsKeys.contains("lpfFIRGain") || force) { + swgPlutoSdrInputSettings->setLpfFirGain(settings.m_lpfFIRGain); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgPlutoSdrInputSettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgPlutoSdrInputSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgPlutoSdrInputSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgPlutoSdrInputSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("lpfBW") || force) { + swgPlutoSdrInputSettings->setLpfBw(settings.m_lpfBW); + } + if (deviceSettingsKeys.contains("gain") || force) { + swgPlutoSdrInputSettings->setGain(settings.m_gain); + } + if (deviceSettingsKeys.contains("antennaPath") || force) { + swgPlutoSdrInputSettings->setAntennaPath((int) settings.m_antennaPath); + } + if (deviceSettingsKeys.contains("gainMode") || force) { + swgPlutoSdrInputSettings->setGainMode((int) settings.m_gainMode); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgPlutoSdrInputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgPlutoSdrInputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgPlutoSdrInputSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void PlutoSDRInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void PlutoSDRInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "PlutoSDRInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("PlutoSDRInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.h b/plugins/samplesource/plutosdrinput/plutosdrinput.h index a2191f755..5cc459425 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.h @@ -19,19 +19,23 @@ #include #include +#include #include "iio.h" -#include +#include "dsp/devicesamplesource.h" #include "util/message.h" #include "plutosdr/deviceplutosdrshared.h" #include "plutosdr/deviceplutosdrbox.h" #include "plutosdrinputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class FileRecord; class PlutoSDRInputThread; class PlutoSDRInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigurePlutoSDR : public Message { MESSAGE_CLASS_DECLARATION @@ -154,6 +158,8 @@ public: PlutoSDRInputThread *m_plutoSDRInputThread; DevicePlutoSDRBox::SampleRates m_deviceSampleRates; QMutex m_mutex; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -162,6 +168,11 @@ public: bool applySettings(const PlutoSDRInputSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const PlutoSDRInputSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const PlutoSDRInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.pro b/plugins/samplesource/plutosdrinput/plutosdrinput.pro index 74999834f..148725397 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.pro +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.pro @@ -24,7 +24,7 @@ INCLUDEPATH += ../../../sdrgui INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client INCLUDEPATH += ../../../devices -MINGW32 || MINGW64 { +MINGW32 { LIBIIOSRC = "C:\softs\libiio" INCLUDEPATH += ../../../libiio/includemw INCLUDEPATH += $$LIBIIOSRC @@ -56,7 +56,7 @@ LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui LIBS += -L../../../swagger/$${build_subdir} -lswagger LIBS += -L../../../devices/$${build_subdir} -ldevices -MINGW32 || MINGW64 { +MINGW32 { LIBS += -L../../../libiio/$${build_subdir} -llibiio } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 9121a91a0..01ad4867a 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -21,6 +21,8 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "device/devicesourceapi.h" #include "device/deviceuiset.h" #include "plutosdr/deviceplutosdr.h" @@ -59,6 +61,9 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->swDecimLabel->setText(QString::fromUtf8("S\u2193")); ui->lpFIRDecimationLabel->setText(QString::fromUtf8("\u2193")); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + blockApplySettings(true); displaySettings(); blockApplySettings(false); @@ -481,3 +486,22 @@ void PlutoSDRInputGui::updateSampleRateAndFrequency() m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } + +void PlutoSDRInputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 0c5cba146..23a237b0e 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -97,6 +97,7 @@ private slots: void updateHardware(); void updateStatus(); void handleInputMessages(); + void openDeviceSettingsDialog(const QPoint& p); }; #endif /* PLUGINS_SAMPLESOURCE_PLUTOSDRINPUT_PLUTOSDRINPUTGUI_H_ */ diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index e840a6db1..bbfae06d6 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -30,7 +30,7 @@ class DeviceSourceAPI; const PluginDescriptor PlutoSDRInputPlugin::m_pluginDescriptor = { QString("PlutoSDR Input"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp index 556977d75..3db0d2750 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp @@ -45,6 +45,10 @@ void PlutoSDRInputSettings::resetToDefaults() m_transverterMode = false; m_transverterDeltaFrequency = 0; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray PlutoSDRInputSettings::serialize() const @@ -67,6 +71,10 @@ QByteArray PlutoSDRInputSettings::serialize() const s.writeS32(15, (int) m_gainMode); s.writeBool(16, m_transverterMode); s.writeS64(17, m_transverterDeltaFrequency); + s.writeBool(18, m_useReverseAPI); + s.writeString(19, m_reverseAPIAddress); + s.writeU32(20, m_reverseAPIPort); + s.writeU32(21, m_reverseAPIDeviceIndex); return s.final(); } @@ -122,6 +130,18 @@ bool PlutoSDRInputSettings::deserialize(const QByteArray& data) } d.readBool(16, &m_transverterMode, false); d.readS64(17, &m_transverterDeltaFrequency, 0); + d.readBool(18, &m_useReverseAPI, false); + d.readString(19, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(20, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(21, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h index 691161181..96861b263 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h @@ -75,6 +75,10 @@ struct PlutoSDRInputSettings { bool m_transverterMode; qint64 m_transverterDeltaFrequency; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; PlutoSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/rtlsdr/rtlsdr.pro b/plugins/samplesource/rtlsdr/rtlsdr.pro index cff062277..61e3b610a 100644 --- a/plugins/samplesource/rtlsdr/rtlsdr.pro +++ b/plugins/samplesource/rtlsdr/rtlsdr.pro @@ -18,7 +18,6 @@ QMAKE_CXXFLAGS += -msse4.1 QMAKE_CXXFLAGS += -std=c++11 CONFIG(MINGW32):LIBRTLSDRSRC = "C:\softs\librtlsdr" -CONFIG(MINGW64):LIBRTLSDRSRC = "C:\softs\librtlsdr" CONFIG(MSVC):LIBRTLSDRSRC = "C:\softs\librtlsdr" INCLUDEPATH += $$PWD diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 92cd317f7..a241e55c9 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -26,6 +26,8 @@ #include "ui_rtlsdrgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -63,6 +65,9 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } RTLSDRGui::~RTLSDRGui() @@ -468,3 +473,22 @@ void RTLSDRGui::on_lowSampleRate_toggled(bool checked) qDebug("RTLSDRGui::on_lowSampleRate_toggled: %d S/s", m_settings.m_devSampleRate); sendSettings(); } + +void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index 69e010f68..75c5360c8 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -92,6 +92,7 @@ private slots: void on_startStop_toggled(bool checked); void on_record_toggled(bool checked); void on_transverter_clicked(); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); }; diff --git a/plugins/samplesource/rtlsdr/rtlsdrinput.cpp b/plugins/samplesource/rtlsdr/rtlsdrinput.cpp index 01d3265b9..24b945dac 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrinput.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrinput.cpp @@ -15,10 +15,13 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include #include #include +#include +#include +#include + #include "SWGDeviceSettings.h" #include "SWGRtlSdrSettings.h" #include "SWGDeviceState.h" @@ -57,13 +60,23 @@ RTLSDRInput::RTLSDRInput(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } RTLSDRInput::~RTLSDRInput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; + closeDevice(); } @@ -330,8 +343,7 @@ bool RTLSDRInput::handleMessage(const Message& message) if (cmd.getStartStop()) { - if (m_deviceAPI->initAcquisition()) - { + if (m_deviceAPI->initAcquisition()) { m_deviceAPI->startAcquisition(); } } @@ -340,6 +352,10 @@ bool RTLSDRInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -351,58 +367,52 @@ bool RTLSDRInput::handleMessage(const Message& message) bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) { bool forwardChange = false; + QList reverseAPIKeys; if ((m_settings.m_agc != settings.m_agc) || force) { - if (rtlsdr_set_agc_mode(m_dev, settings.m_agc ? 1 : 0) < 0) - { + reverseAPIKeys.append("agc"); + + if (rtlsdr_set_agc_mode(m_dev, settings.m_agc ? 1 : 0) < 0) { qCritical("RTLSDRInput::applySettings: could not set AGC mode %s", settings.m_agc ? "on" : "off"); - } - else - { + } else { qDebug("RTLSDRInput::applySettings: AGC mode %s", settings.m_agc ? "on" : "off"); - m_settings.m_agc = settings.m_agc; } } if ((m_settings.m_gain != settings.m_gain) || force) { - m_settings.m_gain = settings.m_gain; + reverseAPIKeys.append("gain"); if(m_dev != 0) { - if (rtlsdr_set_tuner_gain(m_dev, m_settings.m_gain) != 0) - { + if (rtlsdr_set_tuner_gain(m_dev, settings.m_gain) != 0) { qCritical("RTLSDRInput::applySettings: rtlsdr_set_tuner_gain() failed"); - } - else - { - qDebug("RTLSDRInput::applySettings: rtlsdr_set_tuner_gain() to %d", m_settings.m_gain); + } else { + qDebug("RTLSDRInput::applySettings: rtlsdr_set_tuner_gain() to %d", settings.m_gain); } } } if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqImbalance != settings.m_iqImbalance) || force) { - m_settings.m_dcBlock = settings.m_dcBlock; - m_settings.m_iqImbalance = settings.m_iqImbalance; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqImbalance); + reverseAPIKeys.append("dcBlock"); + reverseAPIKeys.append("iqImbalance"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqImbalance); qDebug("RTLSDRInput::applySettings: corrections: DC block: %s IQ imbalance: %s", - m_settings.m_dcBlock ? "true" : "false", - m_settings.m_iqImbalance ? "true" : "false"); + settings.m_dcBlock ? "true" : "false", + settings.m_iqImbalance ? "true" : "false"); } if ((m_settings.m_loPpmCorrection != settings.m_loPpmCorrection) || force) { + reverseAPIKeys.append("loPpmCorrection"); + if (m_dev != 0) { - if (rtlsdr_set_freq_correction(m_dev, settings.m_loPpmCorrection) < 0) - { + if (rtlsdr_set_freq_correction(m_dev, settings.m_loPpmCorrection) < 0) { qCritical("RTLSDRInput::applySettings: could not set LO ppm correction: %d", settings.m_loPpmCorrection); - } - else - { - m_settings.m_loPpmCorrection = settings.m_loPpmCorrection; + } else { qDebug("RTLSDRInput::applySettings: LO ppm correction set to: %d", settings.m_loPpmCorrection); } } @@ -410,7 +420,7 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { - m_settings.m_devSampleRate = settings.m_devSampleRate; + reverseAPIKeys.append("devSampleRate"); forwardChange = true; if(m_dev != 0) @@ -421,22 +431,25 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) } else { - if (m_rtlSDRThread) m_rtlSDRThread->setSamplerate(settings.m_devSampleRate); - qDebug("RTLSDRInput::applySettings: sample rate set to %d", m_settings.m_devSampleRate); + if (m_rtlSDRThread) { + m_rtlSDRThread->setSamplerate(settings.m_devSampleRate); + } + + qDebug("RTLSDRInput::applySettings: sample rate set to %d", settings.m_devSampleRate); } } } if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChange = true; - if (m_rtlSDRThread != 0) - { + if (m_rtlSDRThread != 0) { m_rtlSDRThread->setLog2Decimation(settings.m_log2Decim); } - qDebug("RTLSDRInput::applySettings: log2decim set to %d", m_settings.m_log2Decim); + qDebug("RTLSDRInput::applySettings: log2decim set to %d", settings.m_log2Decim); } if ((m_settings.m_centerFrequency != settings.m_centerFrequency) @@ -446,6 +459,13 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + reverseAPIKeys.append("fcPos"); + reverseAPIKeys.append("log2Decim"); + reverseAPIKeys.append("devSampleRate"); + reverseAPIKeys.append("transverterMode"); + reverseAPIKeys.append("transverterDeltaFrequency"); + qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency( settings.m_centerFrequency, settings.m_transverterDeltaFrequency, @@ -454,28 +474,20 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) settings.m_devSampleRate, settings.m_transverterMode); - m_settings.m_centerFrequency = settings.m_centerFrequency; - m_settings.m_log2Decim = settings.m_log2Decim; - m_settings.m_devSampleRate = settings.m_devSampleRate; - m_settings.m_transverterMode = settings.m_transverterMode; - m_settings.m_transverterDeltaFrequency = settings.m_transverterDeltaFrequency; - forwardChange = true; if ((m_settings.m_fcPos != settings.m_fcPos) || force) { - m_settings.m_fcPos = settings.m_fcPos; - if (m_rtlSDRThread != 0) { - m_rtlSDRThread->setFcPos((int) m_settings.m_fcPos); + m_rtlSDRThread->setFcPos((int) settings.m_fcPos); } - qDebug() << "RTLSDRInput::applySettings: set fc pos (enum) to " << (int) m_settings.m_fcPos; + qDebug() << "RTLSDRInput::applySettings: set fc pos (enum) to " << (int) settings.m_fcPos; } if (m_dev != 0) { - if (rtlsdr_set_center_freq( m_dev, deviceCenterFrequency ) != 0) { + if (rtlsdr_set_center_freq(m_dev, deviceCenterFrequency) != 0) { qWarning("RTLSDRInput::applySettings: rtlsdr_set_center_freq(%lld) failed", deviceCenterFrequency); } else { qDebug("RTLSDRInput::applySettings: rtlsdr_set_center_freq(%lld)", deviceCenterFrequency); @@ -485,24 +497,24 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) if ((m_settings.m_noModMode != settings.m_noModMode) || force) { - m_settings.m_noModMode = settings.m_noModMode; - qDebug() << "RTLSDRInput::applySettings: set noModMode to " << m_settings.m_noModMode; + reverseAPIKeys.append("noModMode"); + qDebug() << "RTLSDRInput::applySettings: set noModMode to " << settings.m_noModMode; // Direct Modes: 0: off, 1: I, 2: Q, 3: NoMod. - if (m_settings.m_noModMode) { + if (settings.m_noModMode) { set_ds_mode(3); } else { set_ds_mode(0); } } - if ((m_settings.m_lowSampleRate != settings.m_lowSampleRate) || force) - { - m_settings.m_lowSampleRate = settings.m_lowSampleRate; + if ((m_settings.m_lowSampleRate != settings.m_lowSampleRate) || force) { + reverseAPIKeys.append("lowSampleRate"); } if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); m_settings.m_rfBandwidth = settings.m_rfBandwidth; if (m_dev != 0) @@ -517,18 +529,30 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, bool force) if ((m_settings.m_offsetTuning != settings.m_offsetTuning) || force) { - m_settings.m_offsetTuning = settings.m_offsetTuning; + reverseAPIKeys.append("offsetTuning"); if (m_dev != 0) { if (rtlsdr_set_offset_tuning(m_dev, m_settings.m_offsetTuning ? 0 : 1) != 0) { - qCritical("RTLSDRInput::applySettings: could not set offset tuning to %s", m_settings.m_offsetTuning ? "on" : "off"); + qCritical("RTLSDRInput::applySettings: could not set offset tuning to %s", settings.m_offsetTuning ? "on" : "off"); } else { - qDebug("RTLSDRInput::applySettings: offset tuning set to %s", m_settings.m_offsetTuning ? "on" : "off"); + qDebug("RTLSDRInput::applySettings: offset tuning set to %s", settings.m_offsetTuning ? "on" : "off"); } } } + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + if (forwardChange) { int sampleRate = m_settings.m_devSampleRate/(1<getFileRecordName(); } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getRtlSdrSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getRtlSdrSettings()->getReverseApiAddress() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getRtlSdrSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getRtlSdrSettings()->getReverseApiDeviceIndex(); + } MsgConfigureRTLSDR *msg = MsgConfigureRTLSDR::create(settings, force); m_inputMessageQueue.push(msg); @@ -651,6 +687,17 @@ void RTLSDRInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& res } else { response.getRtlSdrSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); } + + response.getRtlSdrSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getRtlSdrSettings()->getReverseApiAddress()) { + *response.getRtlSdrSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getRtlSdrSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getRtlSdrSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getRtlSdrSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); } int RTLSDRInput::webapiRunGet( @@ -703,4 +750,112 @@ void RTLSDRInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respons } } +void RTLSDRInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const RTLSDRSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("RTLSDR")); + swgDeviceSettings->setRtlSdrSettings(new SWGSDRangel::SWGRtlSdrSettings()); + SWGSDRangel::SWGRtlSdrSettings *swgRtlSdrSettings = swgDeviceSettings->getRtlSdrSettings(); + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("agc") || force) { + swgRtlSdrSettings->setAgc(settings.m_agc ? 1 : 0); + } + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgRtlSdrSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgRtlSdrSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgRtlSdrSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgRtlSdrSettings->setFcPos(settings.m_fcPos); + } + if (deviceSettingsKeys.contains("gain") || force) { + swgRtlSdrSettings->setGain(settings.m_gain); + } + if (deviceSettingsKeys.contains("iqImbalance") || force) { + swgRtlSdrSettings->setIqImbalance(settings.m_iqImbalance ? 1 : 0); + } + if (deviceSettingsKeys.contains("loPpmCorrection") || force) { + swgRtlSdrSettings->setLoPpmCorrection(settings.m_loPpmCorrection); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgRtlSdrSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("lowSampleRate") || force) { + swgRtlSdrSettings->setLowSampleRate(settings.m_lowSampleRate); + } + if (deviceSettingsKeys.contains("noModMode") || force) { + swgRtlSdrSettings->setNoModMode(settings.m_noModMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("offsetTuning") || force) { + swgRtlSdrSettings->setOffsetTuning(settings.m_offsetTuning ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgRtlSdrSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgRtlSdrSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("rfBandwidth") || force) { + swgRtlSdrSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgRtlSdrSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void RTLSDRInput::webapiReverseSendStartStop(bool start) +{ + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void RTLSDRInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "TestSourceInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("RTLSDRInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/rtlsdr/rtlsdrinput.h b/plugins/samplesource/rtlsdr/rtlsdrinput.h index c67b6815d..5c3085b3c 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrinput.h +++ b/plugins/samplesource/rtlsdr/rtlsdrinput.h @@ -20,6 +20,7 @@ #include #include +#include #include #include "rtlsdrsettings.h" @@ -28,8 +29,11 @@ class DeviceSourceAPI; class RTLSDRThread; class FileRecord; +class QNetworkAccessManager; +class QNetworkReply; class RTLSDRInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureRTLSDR : public Message { MESSAGE_CLASS_DECLARATION @@ -156,12 +160,19 @@ private: QString m_deviceDescription; std::vector m_gains; bool m_running; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); bool applySettings(const RTLSDRSettings& settings, bool force); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const RTLSDRSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const RTLSDRSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_RTLSDRINPUT_H diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index ebde7cd55..71d94126b 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -14,7 +14,7 @@ const PluginDescriptor RTLSDRPlugin::m_pluginDescriptor = { QString("RTL-SDR Input"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp index 95494b820..0b233ec8b 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp @@ -41,6 +41,10 @@ void RTLSDRSettings::resetToDefaults() m_rfBandwidth = 2500 * 1000; // Hz m_fileRecordName = ""; m_offsetTuning = false; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray RTLSDRSettings::serialize() const @@ -61,6 +65,10 @@ QByteArray RTLSDRSettings::serialize() const s.writeS64(13, m_transverterDeltaFrequency); s.writeU32(14, m_rfBandwidth); s.writeBool(15, m_offsetTuning); + s.writeBool(16, m_useReverseAPI); + s.writeString(17, m_reverseAPIAddress); + s.writeU32(18, m_reverseAPIPort); + s.writeU32(19, m_reverseAPIDeviceIndex); return s.final(); } @@ -78,6 +86,7 @@ bool RTLSDRSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t utmp; d.readS32(2, &m_gain, 0); d.readS32(3, &m_loPpmCorrection, 0); @@ -94,6 +103,18 @@ bool RTLSDRSettings::deserialize(const QByteArray& data) d.readS64(13, &m_transverterDeltaFrequency, 0); d.readU32(4, &m_rfBandwidth, 2500 * 1000); d.readBool(15, &m_offsetTuning, false); + d.readBool(16, &m_useReverseAPI, false); + d.readString(17, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(18, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(19, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.h b/plugins/samplesource/rtlsdr/rtlsdrsettings.h index e42b92616..7b1725ab1 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.h +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.h @@ -42,6 +42,10 @@ struct RTLSDRSettings { quint32 m_rfBandwidth; //!< RF filter bandwidth in Hz QString m_fileRecordName; bool m_offsetTuning; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; RTLSDRSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsource.pro b/plugins/samplesource/sdrdaemonsource/sdrdaemonsource.pro index 6f2a61d21..363200437 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsource.pro +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsource.pro @@ -12,7 +12,6 @@ QT += core gui widgets multimedia network opengl TARGET = inputsdrdaemonsource CONFIG(MINGW32):LIBCM256CCSRC = "C:\softs\cm256cc" -CONFIG(MINGW64):LIBCM256CCSRC = "C:\softs\cm256cc" CONFIG(MSVC):LIBCM256CCSRC = "C:\softs\cm256cc" CONFIG(macx):LIBCM256CCSRC = "../../../../deps/cm256cc" @@ -36,7 +35,6 @@ CONFIG(Release):build_subdir = release CONFIG(Debug):build_subdir = debug CONFIG(MINGW32):INCLUDEPATH += "C:\softs\boost_1_66_0" -CONFIG(MINGW64):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(MSVC):INCLUDEPATH += "C:\softs\boost_1_66_0" CONFIG(macx):INCLUDEPATH += "../../../boost_1_69_0" diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp index 4b05bbc0c..b785bd62c 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp @@ -32,6 +32,8 @@ #include "ui_sdrdaemonsourcegui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "mainwindow.h" @@ -77,6 +79,9 @@ SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceUISet *deviceUISet, QWidget* parent ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999U); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); @@ -631,3 +636,22 @@ void SDRdaemonSourceGui::analyzeApiReply(const QJsonObject& jsonObject) ui->infoText->setText(infoLine); } } + +void SDRdaemonSourceGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h index 92d6a8147..ace89a564 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h @@ -133,6 +133,7 @@ private slots: void updateHardware(); void updateStatus(); void networkManagerFinished(QNetworkReply *reply); + void openDeviceSettingsDialog(const QPoint& p); }; #endif // INCLUDE_SDRDAEMONSOURCEGUI_H diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.cpp index 566130e8b..f0afea029 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.cpp @@ -16,7 +16,10 @@ #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -26,8 +29,8 @@ #include "util/simpleserializer.h" #include "dsp/dspcommands.h" #include "dsp/dspengine.h" -#include -#include +#include "device/devicesourceapi.h" +#include "dsp/filerecord.h" #include "sdrdaemonsourceinput.h" #include "sdrdaemonsourceudphandler.h" @@ -52,10 +55,15 @@ SDRdaemonSourceInput::SDRdaemonSourceInput(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SDRdaemonSourceInput::~SDRdaemonSourceInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; stop(); m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; @@ -194,6 +202,10 @@ bool SDRdaemonSourceInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (MsgConfigureSDRdaemonSource::match(message)) @@ -215,6 +227,29 @@ void SDRdaemonSourceInput::applySettings(const SDRdaemonSourceSettings& settings std::ostringstream os; QString remoteAddress; m_SDRdaemonUDPHandler->getRemoteAddress(remoteAddress); + QList reverseAPIKeys; + + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { + reverseAPIKeys.append("dcBlock"); + } + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + } + if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) { + reverseAPIKeys.append("dataAddress"); + } + if ((m_settings.m_dataPort != settings.m_dataPort) || force) { + reverseAPIKeys.append("dataPort"); + } + if ((m_settings.m_apiAddress != settings.m_apiAddress) || force) { + reverseAPIKeys.append("apiAddress"); + } + if ((m_settings.m_apiPort != settings.m_apiPort) || force) { + reverseAPIKeys.append("apiPort"); + } + if ((m_settings.m_fileRecordName != settings.m_fileRecordName) || force) { + reverseAPIKeys.append("fileRecordName"); + } if ((m_settings.m_dcBlock != settings.m_dcBlock) || (m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { @@ -228,6 +263,16 @@ void SDRdaemonSourceInput::applySettings(const SDRdaemonSourceSettings& settings m_SDRdaemonUDPHandler->getRemoteAddress(remoteAddress); mutexLocker.unlock(); + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; m_remoteAddress = remoteAddress; @@ -361,3 +406,86 @@ void SDRdaemonSourceInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport response.getSdrDaemonSourceReport()->setMinNbBlocks(m_SDRdaemonUDPHandler->getMinNbBlocks()); response.getSdrDaemonSourceReport()->setMaxNbRecovery(m_SDRdaemonUDPHandler->getMaxNbRecovery()); } + +void SDRdaemonSourceInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRdaemonSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("SDRdaemonSource")); + swgDeviceSettings->setSdrDaemonSourceSettings(new SWGSDRangel::SWGSDRdaemonSourceSettings()); + SWGSDRangel::SWGSDRdaemonSourceSettings *swgSDRDaemonSourceSettings = swgDeviceSettings->getSdrDaemonSourceSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("apiAddress") || force) { + swgSDRDaemonSourceSettings->setApiAddress(new QString(settings.m_apiAddress)); + } + if (deviceSettingsKeys.contains("apiPort") || force) { + swgSDRDaemonSourceSettings->setApiPort(settings.m_apiPort); + } + if (deviceSettingsKeys.contains("dataAddress") || force) { + swgSDRDaemonSourceSettings->setDataAddress(new QString(settings.m_dataAddress)); + } + if (deviceSettingsKeys.contains("dataPort") || force) { + swgSDRDaemonSourceSettings->setDataPort(settings.m_dataPort); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgSDRDaemonSourceSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgSDRDaemonSourceSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgSDRDaemonSourceSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void SDRdaemonSourceInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void SDRdaemonSourceInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SDRdaemonSourceInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SDRdaemonSourceInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.h index 3f5c09a29..c0a1d7530 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceinput.h @@ -17,21 +17,26 @@ #ifndef INCLUDE_SDRDAEMONSOURCEINPUT_H #define INCLUDE_SDRDAEMONSOURCEINPUT_H -#include -#include -#include #include #include #include +#include +#include +#include +#include + #include #include "sdrdaemonsourcesettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class SDRdaemonSourceUDPHandler; class FileRecord; class SDRdaemonSourceInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureSDRdaemonSource : public Message { MESSAGE_CLASS_DECLARATION @@ -314,10 +319,17 @@ private: QString m_deviceDescription; std::time_t m_startingTimeStamp; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; void applySettings(const SDRdaemonSourceSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRdaemonSourceSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRdaemonSourceSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_SDRDAEMONSOURCEINPUT_H diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index 2a11bdcb9..4f62e99d1 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor SDRdaemonSourcePlugin::m_pluginDescriptor = { QString("SDRdaemon source input"), - QString("4.1.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.cpp index de6bfc2fd..2b6b939f4 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.cpp @@ -31,6 +31,10 @@ void SDRdaemonSourceSettings::resetToDefaults() m_dcBlock = false; m_iqCorrection = false; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray SDRdaemonSourceSettings::serialize() const @@ -43,6 +47,10 @@ QByteArray SDRdaemonSourceSettings::serialize() const s.writeString(8, m_dataAddress); s.writeBool(9, m_dcBlock); s.writeBool(10, m_iqCorrection); + s.writeBool(11, m_useReverseAPI); + s.writeString(12, m_reverseAPIAddress); + s.writeU32(13, m_reverseAPIPort); + s.writeU32(14, m_reverseAPIDeviceIndex); return s.final(); } @@ -60,6 +68,7 @@ bool SDRdaemonSourceSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { quint32 uintval; + d.readString(5, &m_apiAddress, "127.0.0.1"); d.readU32(6, &uintval, 9090); m_apiPort = uintval % (1<<16); @@ -68,6 +77,18 @@ bool SDRdaemonSourceSettings::deserialize(const QByteArray& data) d.readString(8, &m_dataAddress, "127.0.0.1"); d.readBool(9, &m_dcBlock, false); d.readBool(10, &m_iqCorrection, false); + d.readBool(11, &m_useReverseAPI, false); + d.readString(12, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(13, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(14, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } else diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.h index e1b010585..1dd3d9469 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcesettings.h @@ -28,6 +28,10 @@ struct SDRdaemonSourceSettings { bool m_dcBlock; bool m_iqCorrection; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; SDRdaemonSourceSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index 34e4a3d13..c43286baa 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -25,6 +25,8 @@ #include "ui_sdrplaygui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -70,6 +72,9 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -472,3 +477,21 @@ void SDRPlayGui::on_record_toggled(bool checked) m_sampleSource->getInputMessageQueue()->push(message); } +void SDRPlayGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 12616b2c1..93b6bd0be 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -95,6 +95,7 @@ private slots: void on_gainBaseband_valueChanged(int value); void on_startStop_toggled(bool checked); void on_record_toggled(bool checked); + void openDeviceSettingsDialog(const QPoint& p); }; #endif /* PLUGINS_SAMPLESOURCE_SDRPLAY_SDRPLAYGUI_H_ */ diff --git a/plugins/samplesource/sdrplay/sdrplayinput.cpp b/plugins/samplesource/sdrplay/sdrplayinput.cpp index 80ff8815a..27e598deb 100644 --- a/plugins/samplesource/sdrplay/sdrplayinput.cpp +++ b/plugins/samplesource/sdrplay/sdrplayinput.cpp @@ -16,7 +16,10 @@ #include #include + #include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -51,11 +54,20 @@ SDRPlayInput::SDRPlayInput(DeviceSourceAPI *deviceAPI) : openDevice(); m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SDRPlayInput::~SDRPlayInput() { - if (m_running) stop(); + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; closeDevice(); @@ -334,6 +346,10 @@ bool SDRPlayInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -345,38 +361,43 @@ bool SDRPlayInput::handleMessage(const Message& message) bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardChange, bool force) { bool forceGainSetting = false; - //settings.debug("SDRPlayInput::applySettings"); + QList reverseAPIKeys; QMutexLocker mutexLocker(&m_mutex); if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) { - m_settings.m_dcBlock = settings.m_dcBlock; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection); + reverseAPIKeys.append("dcBlock"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { - m_settings.m_iqCorrection = settings.m_iqCorrection; - m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection); + reverseAPIKeys.append("iqCorrection"); + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); } // gains processing if ((m_settings.m_tunerGainMode != settings.m_tunerGainMode) || force) { - m_settings.m_tunerGainMode = settings.m_tunerGainMode; + reverseAPIKeys.append("tunerGainMode"); forceGainSetting = true; } - if (m_settings.m_tunerGainMode) // auto + if ((m_settings.m_tunerGain != settings.m_tunerGain) || force) { + reverseAPIKeys.append("tunerGain"); + } + if ((m_settings.m_lnaOn != settings.m_lnaOn) || force) { + reverseAPIKeys.append("lnaOn"); + } + + if (settings.m_tunerGainMode) // auto { if ((m_settings.m_tunerGain != settings.m_tunerGain) || forceGainSetting) { - m_settings.m_tunerGain = settings.m_tunerGain; - if(m_dev != 0) { - int r = mirisdr_set_tuner_gain(m_dev, m_settings.m_tunerGain); + int r = mirisdr_set_tuner_gain(m_dev, settings.m_tunerGain); if (r < 0) { @@ -386,7 +407,7 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh { int lnaGain; - if (m_settings.m_frequencyBandIndex < 3) // bands using AM mode + if (settings.m_frequencyBandIndex < 3) // bands using AM mode { lnaGain = mirisdr_get_mixbuffer_gain(m_dev); } @@ -415,19 +436,15 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh if ((m_settings.m_lnaOn != settings.m_lnaOn) || forceGainSetting) { - if(m_dev != 0) + if (m_dev != 0) { - if (m_settings.m_frequencyBandIndex < 3) // bands using AM mode + if (settings.m_frequencyBandIndex < 3) // bands using AM mode { int r = mirisdr_set_mixbuffer_gain(m_dev, settings.m_lnaOn ? 0 : 1); // mirisdr_set_mixbuffer_gain takes gain reduction - if (r != 0) - { + if (r != 0) { qDebug("SDRPlayInput::applySettings: could not set mixer buffer gain"); - } - else - { - m_settings.m_lnaOn = settings.m_lnaOn; + } else { anyChange = true; } } @@ -435,13 +452,9 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh { int r = mirisdr_set_lna_gain(m_dev, settings.m_lnaOn ? 0 : 1); // mirisdr_set_lna_gain takes gain reduction - if (r != 0) - { + if (r != 0) { qDebug("SDRPlayInput::applySettings: could not set LNA gain"); - } - else - { - m_settings.m_lnaOn = settings.m_lnaOn; + } else { anyChange = true; } } @@ -450,17 +463,15 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh if ((m_settings.m_mixerAmpOn != settings.m_mixerAmpOn) || forceGainSetting) { - if(m_dev != 0) + reverseAPIKeys.append("mixerAmpOn"); + + if (m_dev != 0) { int r = mirisdr_set_mixer_gain(m_dev, settings.m_mixerAmpOn ? 0 : 1); // mirisdr_set_lna_gain takes gain reduction - if (r != 0) - { + if (r != 0) { qDebug("SDRPlayInput::applySettings: could not set mixer gain"); - } - else - { - m_settings.m_mixerAmpOn = settings.m_mixerAmpOn; + } else { anyChange = true; } } @@ -468,17 +479,15 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh if ((m_settings.m_basebandGain != settings.m_basebandGain) || forceGainSetting) { - if(m_dev != 0) + reverseAPIKeys.append("basebandGain"); + + if (m_dev != 0) { int r = mirisdr_set_baseband_gain(m_dev, settings.m_basebandGain); - if (r != 0) - { + if (r != 0) { qDebug("SDRPlayInput::applySettings: could not set mixer gain"); - } - else - { - m_settings.m_basebandGain = settings.m_basebandGain; + } else { anyChange = true; } } @@ -488,12 +497,9 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh { int lnaGain; - if (m_settings.m_frequencyBandIndex < 3) // bands using AM mode - { + if (settings.m_frequencyBandIndex < 3) { // bands using AM mode lnaGain = mirisdr_get_mixbuffer_gain(m_dev); - } - else - { + } else { lnaGain = mirisdr_get_lna_gain(m_dev); } @@ -512,22 +518,33 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); + if (m_sdrPlayThread != 0) { - m_sdrPlayThread->setLog2Decimation(m_settings.m_log2Decim); + m_sdrPlayThread->setLog2Decimation(settings.m_log2Decim); qDebug() << "SDRPlayInput::applySettings: set decimation to " << (1<setFcPos((int) m_settings.m_fcPos); + m_sdrPlayThread->setFcPos((int) settings.m_fcPos); qDebug() << "SDRPlayInput: set fc pos (enum) to " << (int) settings.m_fcPos; } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_LOppmTenths != settings.m_LOppmTenths) || (m_settings.m_fcPos != settings.m_fcPos) @@ -538,61 +555,55 @@ bool SDRPlayInput::applySettings(const SDRPlaySettings& settings, bool forwardCh 0, settings.m_log2Decim, (DeviceSampleSource::fcPos_t) settings.m_fcPos, - SDRPlaySampleRates::getRate(m_settings.m_devSampleRateIndex)); - - m_settings.m_centerFrequency = settings.m_centerFrequency; - m_settings.m_LOppmTenths = settings.m_LOppmTenths; - m_settings.m_fcPos = settings.m_fcPos; - m_settings.m_log2Decim = settings.m_log2Decim; + SDRPlaySampleRates::getRate(settings.m_devSampleRateIndex)); forwardChange = true; if(m_dev != 0) { if (setDeviceCenterFrequency(deviceCenterFrequency)) { - qDebug() << "SDRPlayInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"; + qDebug() << "SDRPlayInput::applySettings: center freq: " << settings.m_centerFrequency << " Hz"; } } } - if ((m_settings.m_frequencyBandIndex != settings.m_frequencyBandIndex) || force) - { - m_settings.m_frequencyBandIndex = settings.m_frequencyBandIndex; - // change of frequency is done already - } - if ((m_settings.m_bandwidthIndex != settings.m_bandwidthIndex) || force) { + reverseAPIKeys.append("bandwidthIndex"); int bandwidth = SDRPlayBandwidths::getBandwidth(settings.m_bandwidthIndex); int r = mirisdr_set_bandwidth(m_dev, bandwidth); - if (r < 0) - { + if (r < 0) { qCritical("SDRPlayInput::applySettings: set bandwidth %d failed: rc: %d", bandwidth, r); - } - else - { + } else { qDebug("SDRPlayInput::applySettings: bandwidth set to %d", bandwidth); - m_settings.m_bandwidthIndex = settings.m_bandwidthIndex; } } if ((m_settings.m_ifFrequencyIndex != settings.m_ifFrequencyIndex) || force) { + reverseAPIKeys.append("ifFrequencyIndex"); int iFFrequency = SDRPlayIF::getIF(settings.m_ifFrequencyIndex); int r = mirisdr_set_if_freq(m_dev, iFFrequency); - if (r < 0) - { + if (r < 0) { qCritical("SDRPlayInput::applySettings: set IF frequency to %d failed: rc: %d", iFFrequency, r); - } - else - { + } else { qDebug("SDRPlayInput::applySettings: IF frequency set to %d", iFFrequency); - m_settings.m_ifFrequencyIndex = settings.m_ifFrequencyIndex; } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; + if (forwardChange) { int sampleRate = getSampleRate(); @@ -809,6 +820,116 @@ void SDRPlayInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& respon } } +void SDRPlayInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRPlaySettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("SDRplay1")); + swgDeviceSettings->setSdrPlaySettings(new SWGSDRangel::SWGSDRPlaySettings()); + SWGSDRangel::SWGSDRPlaySettings *swgSDRPlaySettings = swgDeviceSettings->getSdrPlaySettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgSDRPlaySettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("tunerGain") || force) { + swgSDRPlaySettings->setTunerGain(settings.m_tunerGain); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgSDRPlaySettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("frequencyBandIndex") || force) { + swgSDRPlaySettings->setFrequencyBandIndex(settings.m_frequencyBandIndex); + } + if (deviceSettingsKeys.contains("ifFrequencyIndex") || force) { + swgSDRPlaySettings->setIfFrequencyIndex(settings.m_ifFrequencyIndex); + } + if (deviceSettingsKeys.contains("bandwidthIndex") || force) { + swgSDRPlaySettings->setBandwidthIndex(settings.m_bandwidthIndex); + } + if (deviceSettingsKeys.contains("devSampleRateIndex") || force) { + swgSDRPlaySettings->setDevSampleRateIndex(settings.m_devSampleRateIndex); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgSDRPlaySettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgSDRPlaySettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgSDRPlaySettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgSDRPlaySettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("tunerGainMode") || force) { + swgSDRPlaySettings->setTunerGainMode(settings.m_tunerGainMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("lnaOn") || force) { + swgSDRPlaySettings->setLnaOn(settings.m_lnaOn ? 1 : 0); + } + if (deviceSettingsKeys.contains("mixerAmpOn") || force) { + swgSDRPlaySettings->setMixerAmpOn(settings.m_mixerAmpOn ? 1 : 0); + } + if (deviceSettingsKeys.contains("basebandGain") || force) { + swgSDRPlaySettings->setBasebandGain(settings.m_basebandGain); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgSDRPlaySettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void SDRPlayInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void SDRPlayInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SDRPlayInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SDRPlayInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} + // ==================================================================== unsigned int SDRPlaySampleRates::m_rates[m_nb_rates] = { diff --git a/plugins/samplesource/sdrplay/sdrplayinput.h b/plugins/samplesource/sdrplay/sdrplayinput.h index 3d5b52d6a..ed7b0823c 100644 --- a/plugins/samplesource/sdrplay/sdrplayinput.h +++ b/plugins/samplesource/sdrplay/sdrplayinput.h @@ -17,19 +17,24 @@ #ifndef PLUGINS_SAMPLESOURCE_SDRPLAY_SDRPLAYINPUT_H_ #define PLUGINS_SAMPLESOURCE_SDRPLAY_SDRPLAYINPUT_H_ -#include -#include #include +#include +#include +#include + #include -#include +#include "dsp/devicesamplesource.h" #include "sdrplaysettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class SDRPlayThread; class FileRecord; class SDRPlayInput : public DeviceSampleSource { + Q_OBJECT public: enum SDRPlayVariant { @@ -174,13 +179,6 @@ public: SDRPlayVariant getVariant() const { return m_variant; } private: - bool openDevice(); - void closeDevice(); - bool applySettings(const SDRPlaySettings& settings, bool forwardChange, bool force); - bool setDeviceCenterFrequency(quint64 freq); - void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRPlaySettings& settings); - void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); - DeviceSourceAPI *m_deviceAPI; QMutex m_mutex; SDRPlayVariant m_variant; @@ -191,6 +189,20 @@ private: int m_devNumber; bool m_running; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + bool openDevice(); + void closeDevice(); + bool applySettings(const SDRPlaySettings& settings, bool forwardChange, bool force); + bool setDeviceCenterFrequency(quint64 freq); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRPlaySettings& settings); + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const SDRPlaySettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; // ==================================================================== diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index ebb5698ce..099ac4e52 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -28,7 +28,7 @@ const PluginDescriptor SDRPlayPlugin::m_pluginDescriptor = { QString("SDRPlay RSP1 Input"), - QString("4.0.0"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.cpp b/plugins/samplesource/sdrplay/sdrplaysettings.cpp index 8585f54c5..923dac2a9 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.cpp +++ b/plugins/samplesource/sdrplay/sdrplaysettings.cpp @@ -42,6 +42,10 @@ void SDRPlaySettings::resetToDefaults() m_mixerAmpOn = false; m_basebandGain = 29; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray SDRPlaySettings::serialize() const @@ -62,6 +66,10 @@ QByteArray SDRPlaySettings::serialize() const s.writeBool(12, m_lnaOn); s.writeBool(13, m_mixerAmpOn); s.writeS32(14, m_basebandGain); + s.writeBool(15, m_useReverseAPI); + s.writeString(16, m_reverseAPIAddress); + s.writeU32(17, m_reverseAPIPort); + s.writeU32(18, m_reverseAPIDeviceIndex); return s.final(); } @@ -79,6 +87,7 @@ bool SDRPlaySettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; d.readS32(1, &m_LOppmTenths, 0); d.readU32(2, &m_frequencyBandIndex, 0); @@ -95,6 +104,18 @@ bool SDRPlaySettings::deserialize(const QByteArray& data) d.readBool(12, &m_lnaOn, false); d.readBool(13, &m_mixerAmpOn, false); d.readS32(14, &m_basebandGain, 29); + d.readBool(15, &m_useReverseAPI, false); + d.readString(16, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(17, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(18, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.h b/plugins/samplesource/sdrplay/sdrplaysettings.h index 36b048928..c9c9670ee 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.h +++ b/plugins/samplesource/sdrplay/sdrplaysettings.h @@ -45,6 +45,10 @@ struct SDRPlaySettings { bool m_mixerAmpOn; int m_basebandGain; QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; SDRPlaySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp index b516f9eb6..d00218924 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp @@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "util/simpleserializer.h" @@ -54,10 +56,16 @@ SoapySDRInput::SoapySDRInput(DeviceSourceAPI *deviceAPI) : m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); m_deviceAPI->addSink(m_fileSink); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } SoapySDRInput::~SoapySDRInput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + if (m_running) { stop(); } @@ -518,6 +526,10 @@ bool SoapySDRInput::start() ((DeviceSoapySDRShared*) (*it)->getBuddySharedPtr())->m_source->setThread(0); } + delete[] fcPoss; + delete[] log2Decims; + delete[] fifos; + needsStart = true; } else @@ -653,6 +665,10 @@ void SoapySDRInput::stop() qDebug("SoapySDRInput::stop: restarting the thread"); soapySDRInputThread->startWork(); } + + delete[] fcPoss; + delete[] log2Decims; + delete[] fifos; } else // remove channel from existing thread { @@ -803,6 +819,10 @@ bool SoapySDRInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else if (DeviceSoapySDRShared::MsgReportBuddyChange::match(message)) @@ -880,6 +900,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo bool globalGainChanged = false; bool individualGainsChanged = false; bool deviceArgsChanged = false; + QList reverseAPIKeys; SoapySDR::Device *dev = m_deviceShared.m_device; SoapySDRInputThread *inputThread = findThread(); @@ -888,6 +909,13 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo xlatedDeviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0; xlatedDeviceCenterFrequency = xlatedDeviceCenterFrequency < 0 ? 0 : xlatedDeviceCenterFrequency; + if ((m_settings.m_softDCCorrection != settings.m_softDCCorrection) || force) { + reverseAPIKeys.append("softDCCorrection"); + } + if ((m_settings.m_softIQCorrection != settings.m_softIQCorrection) || force) { + reverseAPIKeys.append("softIQCorrection"); + } + if ((m_settings.m_softDCCorrection != settings.m_softDCCorrection) || (m_settings.m_softIQCorrection != settings.m_softIQCorrection) || force) { @@ -896,6 +924,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) { + reverseAPIKeys.append("devSampleRate"); forwardChangeOwnDSP = true; forwardChangeToBuddies = true; @@ -927,6 +956,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_fcPos != settings.m_fcPos) || force) { + reverseAPIKeys.append("fcPos"); + if (inputThread != 0) { inputThread->setFcPos(requestedChannel, (int) settings.m_fcPos); @@ -936,6 +967,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); forwardChangeOwnDSP = true; SoapySDRInputThread *inputThread = findThread(); @@ -946,6 +978,19 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo } } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { + reverseAPIKeys.append("centerFrequency"); + } + if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) { + reverseAPIKeys.append("transverterMode"); + } + if ((m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) || force) { + reverseAPIKeys.append("transverterDeltaFrequency"); + } + if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) { + reverseAPIKeys.append("LOppmTenths"); + } + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || (m_settings.m_transverterMode != settings.m_transverterMode) || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency) @@ -971,6 +1016,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_antenna != settings.m_antenna) || force) { + reverseAPIKeys.append("antenna"); + if (dev != 0) { try @@ -988,6 +1035,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_bandwidth != settings.m_bandwidth) || force) { + reverseAPIKeys.append("bandwidth"); forwardChangeToBuddies = true; if (dev != 0) @@ -1032,6 +1080,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_globalGain != settings.m_globalGain) || force) { + reverseAPIKeys.append("globalGain"); + if (dev != 0) { try @@ -1076,6 +1126,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_autoGain != settings.m_autoGain) || force) { + reverseAPIKeys.append("autoGain"); + if (dev != 0) { try @@ -1092,6 +1144,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_autoDCCorrection != settings.m_autoDCCorrection) || force) { + reverseAPIKeys.append("autoDCCorrection"); + if ((dev != 0) && hasDCAutoCorrection()) { try @@ -1108,6 +1162,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_dcCorrection != settings.m_dcCorrection) || force) { + reverseAPIKeys.append("dcCorrection"); + if ((dev != 0) && hasDCCorrectionValue()) { try @@ -1124,6 +1180,8 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) { + reverseAPIKeys.append("iqCorrection"); + if ((dev != 0) && hasIQCorrectionValue()) { try @@ -1247,6 +1305,20 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + + if (fullUpdate || force) { + webapiReverseSendSettings(reverseAPIKeys, settings, true); + } else if (reverseAPIKeys.size() != 0) { + webapiReverseSendSettings(reverseAPIKeys, settings, false); + } + } + m_settings = settings; if (globalGainChanged || individualGainsChanged) @@ -1257,7 +1329,7 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo if (getMessageQueueToGUI()) { - MsgReportGainChange *report = MsgReportGainChange::create(m_settings, individualGainsChanged, globalGainChanged); + MsgReportGainChange *report = MsgReportGainChange::create(m_settings, globalGainChanged, individualGainsChanged); getMessageQueueToGUI()->push(report); } } @@ -1791,3 +1863,126 @@ void SoapySDRInput::webapiFormatArgInfo(const SoapySDR::ArgInfo& arg, SWGSDRange argInfo->getOptionNames()->append(new QString(itOpt.c_str())); } } + +void SoapySDRInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const SoapySDRInputSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("SoapySDR")); + swgDeviceSettings->setSoapySdrInputSettings(new SWGSDRangel::SWGSoapySDRInputSettings()); + swgDeviceSettings->getSoapySdrInputSettings()->init(); + SWGSDRangel::SWGSoapySDRInputSettings *swgSoapySDRInputSettings = swgDeviceSettings->getSoapySdrInputSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgSoapySDRInputSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("LOppmTenths") || force) { + swgSoapySDRInputSettings->setLOppmTenths(settings.m_LOppmTenths); + } + if (deviceSettingsKeys.contains("devSampleRate") || force) { + swgSoapySDRInputSettings->setDevSampleRate(settings.m_devSampleRate); + } + if (deviceSettingsKeys.contains("bandwidth") || force) { + swgSoapySDRInputSettings->setBandwidth(settings.m_bandwidth); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgSoapySDRInputSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgSoapySDRInputSettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("softDCCorrection") || force) { + swgSoapySDRInputSettings->setSoftDcCorrection(settings.m_softDCCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("softIQCorrection") || force) { + swgSoapySDRInputSettings->setSoftIqCorrection(settings.m_softIQCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) { + swgSoapySDRInputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency); + } + if (deviceSettingsKeys.contains("transverterMode") || force) { + swgSoapySDRInputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0); + } + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgSoapySDRInputSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + if (deviceSettingsKeys.contains("antenna") || force) { + swgSoapySDRInputSettings->setAntenna(new QString(settings.m_antenna)); + } + if (deviceSettingsKeys.contains("globalGain") || force) { + swgSoapySDRInputSettings->setGlobalGain(settings.m_globalGain); + } + if (deviceSettingsKeys.contains("autoGain") || force) { + swgSoapySDRInputSettings->setAutoGain(settings.m_autoGain ? 1 : 0); + } + if (deviceSettingsKeys.contains("autoDCCorrection") || force) { + swgSoapySDRInputSettings->setAutoDcCorrection(settings.m_autoDCCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("autoIQCorrection") || force) { + swgSoapySDRInputSettings->setAutoIqCorrection(settings.m_autoIQCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("dcCorrection") || force) + { + swgSoapySDRInputSettings->setDcCorrection(new SWGSDRangel::SWGComplex()); + swgSoapySDRInputSettings->getDcCorrection()->setReal(settings.m_dcCorrection.real()); + swgSoapySDRInputSettings->getDcCorrection()->setImag(settings.m_dcCorrection.imag()); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) + { + swgSoapySDRInputSettings->setIqCorrection(new SWGSDRangel::SWGComplex()); + swgSoapySDRInputSettings->getIqCorrection()->setReal(settings.m_iqCorrection.real()); + swgSoapySDRInputSettings->getIqCorrection()->setImag(settings.m_iqCorrection.imag()); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void SoapySDRInput::webapiReverseSendStartStop(bool start) +{ + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void SoapySDRInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "SoapySDRInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SoapySDRInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.h b/plugins/samplesource/soapysdrinput/soapysdrinput.h index d0cfb2739..61fa9d9e2 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.h @@ -17,15 +17,19 @@ #ifndef PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUT_H_ #define PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUT_H_ +#include + #include #include -#include +#include #include "soapysdr/devicesoapysdrshared.h" #include "dsp/devicesamplesource.h" #include "soapysdrinputsettings.h" +class QNetworkAccessManager; +class QNetworkReply; class DeviceSourceAPI; class SoapySDRInputThread; class FileRecord; @@ -44,6 +48,7 @@ namespace SWGSDRangel class SoapySDRInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureSoapySDRInput : public Message { MESSAGE_CLASS_DECLARATION @@ -205,6 +210,8 @@ private: SoapySDRInputThread *m_thread; DeviceSoapySDRShared m_deviceShared; FileRecord *m_fileSink; //!< File sink to record device I/Q output + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool openDevice(); void closeDevice(); @@ -219,6 +226,11 @@ private: QVariant webapiVariantFromArgValue(SWGSDRangel::SWGArgValue *argValue); void webapiFormatArgValue(const QVariant& v, SWGSDRangel::SWGArgValue *argValue); void webapiFormatArgInfo(const SoapySDR::ArgInfo& arg, SWGSDRangel::SWGArgInfo *argInfo); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const SoapySDRInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index baac02b5a..fd738cfbf 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -23,6 +23,8 @@ #include "device/deviceuiset.h" #include "util/simpleserializer.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "soapygui/discreterangegui.h" #include "soapygui/intervalrangegui.h" #include "soapygui/stringrangegui.h" @@ -88,6 +90,9 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + displaySettings(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -926,3 +931,21 @@ void SoapySDRInputGui::updateStatus() } } +void SoapySDRInputGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index e71340818..d541e3b26 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -139,6 +139,7 @@ private slots: void on_record_toggled(bool checked); void updateHardware(); void updateStatus(); + void openDeviceSettingsDialog(const QPoint& p); }; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp index d103760d2..d4f336117 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor SoapySDRInputPlugin::m_pluginDescriptor = { QString("SoapySDR Input"), - QString("4.3.1"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp index 72500dfec..9f25ed0d7 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp @@ -45,6 +45,10 @@ void SoapySDRInputSettings::resetToDefaults() m_autoIQCorrection = false; m_dcCorrection = std::complex{0,0}; m_iqCorrection = std::complex{0,0}; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray SoapySDRInputSettings::serialize() const @@ -73,6 +77,10 @@ QByteArray SoapySDRInputSettings::serialize() const s.writeDouble(20, m_iqCorrection.imag()); s.writeBlob(21, serializeArgumentMap(m_streamArgSettings)); s.writeBlob(22, serializeArgumentMap(m_deviceArgSettings)); + s.writeBool(23, m_useReverseAPI); + s.writeString(24, m_reverseAPIAddress); + s.writeU32(25, m_reverseAPIPort); + s.writeU32(26, m_reverseAPIDeviceIndex); return s.final(); } @@ -90,6 +98,7 @@ bool SoapySDRInputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t uintval; QByteArray blob; double realval, imagval; @@ -122,6 +131,18 @@ bool SoapySDRInputSettings::deserialize(const QByteArray& data) deserializeArgumentMap(blob, m_streamArgSettings); d.readBlob(22, &blob); deserializeArgumentMap(blob, m_deviceArgSettings); + d.readBool(23, &m_useReverseAPI, false); + d.readString(24, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(25, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(26, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h index 82bf66f25..1d04db403 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h @@ -51,6 +51,10 @@ struct SoapySDRInputSettings { std::complex m_iqCorrection; QMap m_streamArgSettings; QMap m_deviceArgSettings; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; SoapySDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index ccffc6a25..68cb2bcdb 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -25,6 +25,8 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/crightclickenabler.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "util/db.h" @@ -66,6 +68,9 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); + + CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } TestSourceGui::~TestSourceGui() @@ -512,4 +517,21 @@ void TestSourceGui::updateSampleRateAndFrequency() ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); } +void TestSourceGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} \ No newline at end of file diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h index 3c0432d69..7bc29d4a5 100644 --- a/plugins/samplesource/testsource/testsourcegui.h +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -97,6 +97,7 @@ private slots: void on_qBias_valueChanged(int value); void on_phaseImbalance_valueChanged(int value); void on_record_toggled(bool checked); + void openDeviceSettingsDialog(const QPoint& p); void updateStatus(); void updateHardware(); }; diff --git a/plugins/samplesource/testsource/testsourceinput.cpp b/plugins/samplesource/testsource/testsourceinput.cpp index 776a1c540..ce72750b9 100644 --- a/plugins/samplesource/testsource/testsourceinput.cpp +++ b/plugins/samplesource/testsource/testsourceinput.cpp @@ -14,10 +14,14 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include #include #include +#include +#include +#include +#include + #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -47,11 +51,20 @@ TestSourceInput::TestSourceInput(DeviceSourceAPI *deviceAPI) : if (!m_sampleFifo.setSize(96000 * 4)) { qCritical("TestSourceInput::TestSourceInput: Could not allocate SampleFifo"); } + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } TestSourceInput::~TestSourceInput() { - if (m_running) { stop(); } + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + + if (m_running) { + stop(); + } + m_deviceAPI->removeSink(m_fileSink); delete m_fileSink; } @@ -210,6 +223,10 @@ bool TestSourceInput::handleMessage(const Message& message) m_deviceAPI->stopAcquisition(); } + if (m_settings.m_useReverseAPI) { + webapiReverseSendStartStop(cmd.getStartStop()); + } + return true; } else @@ -220,8 +237,12 @@ bool TestSourceInput::handleMessage(const Message& message) bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool force) { + QList reverseAPIKeys; + if ((m_settings.m_autoCorrOptions != settings.m_autoCorrOptions) || force) { + reverseAPIKeys.append("autoCorrOptions"); + switch(settings.m_autoCorrOptions) { case TestSourceSettings::AutoCorrDC: @@ -239,6 +260,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) { + reverseAPIKeys.append("sampleRate"); + if (m_testSourceThread != 0) { m_testSourceThread->setSamplerate(settings.m_sampleRate); @@ -248,6 +271,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); + if (m_testSourceThread != 0) { m_testSourceThread->setLog2Decimation(settings.m_log2Decim); @@ -261,6 +286,10 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for || (m_settings.m_sampleRate != settings.m_sampleRate) || (m_settings.m_log2Decim != settings.m_log2Decim) || force) { + reverseAPIKeys.append("centerFrequency"); + reverseAPIKeys.append("fcPos"); + reverseAPIKeys.append("frequencyShift"); + qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency( settings.m_centerFrequency, 0, // no transverter mode @@ -294,6 +323,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_amplitudeBits != settings.m_amplitudeBits) || force) { + reverseAPIKeys.append("amplitudeBits"); + if (m_testSourceThread != 0) { m_testSourceThread->setAmplitudeBits(settings.m_amplitudeBits); } @@ -301,6 +332,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_dcFactor != settings.m_dcFactor) || force) { + reverseAPIKeys.append("dcFactor"); + if (m_testSourceThread != 0) { m_testSourceThread->setDCFactor(settings.m_dcFactor); } @@ -308,6 +341,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_iFactor != settings.m_iFactor) || force) { + reverseAPIKeys.append("iFactor"); + if (m_testSourceThread != 0) { m_testSourceThread->setIFactor(settings.m_iFactor); } @@ -315,6 +350,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_qFactor != settings.m_qFactor) || force) { + reverseAPIKeys.append("qFactor"); + if (m_testSourceThread != 0) { m_testSourceThread->setQFactor(settings.m_qFactor); } @@ -322,6 +359,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_phaseImbalance != settings.m_phaseImbalance) || force) { + reverseAPIKeys.append("phaseImbalance"); + if (m_testSourceThread != 0) { m_testSourceThread->setPhaseImbalance(settings.m_phaseImbalance); } @@ -329,6 +368,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_sampleSizeIndex != settings.m_sampleSizeIndex) || force) { + reverseAPIKeys.append("sampleSizeIndex"); + if (m_testSourceThread != 0) { m_testSourceThread->setBitSize(settings.m_sampleSizeIndex); } @@ -347,6 +388,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_modulationTone != settings.m_modulationTone) || force) { + reverseAPIKeys.append("modulationTone"); + if (m_testSourceThread != 0) { m_testSourceThread->setToneFrequency(settings.m_modulationTone * 10); } @@ -354,6 +397,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_modulation != settings.m_modulation) || force) { + reverseAPIKeys.append("modulation"); + if (m_testSourceThread != 0) { m_testSourceThread->setModulation(settings.m_modulation); @@ -370,6 +415,8 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_amModulation != settings.m_amModulation) || force) { + reverseAPIKeys.append("amModulation"); + if (m_testSourceThread != 0) { m_testSourceThread->setAMModulation(settings.m_amModulation / 100.0f); } @@ -377,11 +424,23 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + if (m_testSourceThread != 0) { m_testSourceThread->setFMDeviation(settings.m_fmDeviation * 100.0f); } } + if (settings.m_useReverseAPI) + { + qDebug("TestSourceInput::applySettings: call webapiReverseSendSettings"); + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; return true; } @@ -493,6 +552,18 @@ int TestSourceInput::webapiSettingsPutPatch( if (deviceSettingsKeys.contains("fileRecordName")) { settings.m_fileRecordName = *response.getTestSourceSettings()->getFileRecordName(); } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getTestSourceSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getTestSourceSettings()->getReverseApiAddress() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getTestSourceSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getTestSourceSettings()->getReverseApiDeviceIndex(); + } MsgConfigureTestSource *msg = MsgConfigureTestSource::create(settings, force); m_inputMessageQueue.push(msg); @@ -531,5 +602,130 @@ void TestSourceInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& } else { response.getTestSourceSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); } + + response.getTestSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getTestSourceSettings()->getReverseApiAddress()) { + *response.getTestSourceSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getTestSourceSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getTestSourceSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getTestSourceSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); } +void TestSourceInput::webapiReverseSendSettings(QList& deviceSettingsKeys, const TestSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setTx(0); + swgDeviceSettings->setDeviceHwType(new QString("TestSource")); + swgDeviceSettings->setTestSourceSettings(new SWGSDRangel::SWGTestSourceSettings()); + SWGSDRangel::SWGTestSourceSettings *swgTestSourceSettings = swgDeviceSettings->getTestSourceSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (deviceSettingsKeys.contains("centerFrequency") || force) { + swgTestSourceSettings->setCenterFrequency(settings.m_centerFrequency); + } + if (deviceSettingsKeys.contains("frequencyShift") || force) { + swgTestSourceSettings->setFrequencyShift(settings.m_frequencyShift); + } + if (deviceSettingsKeys.contains("sampleRate") || force) { + swgTestSourceSettings->setSampleRate(settings.m_sampleRate); + } + if (deviceSettingsKeys.contains("log2Decim") || force) { + swgTestSourceSettings->setLog2Decim(settings.m_log2Decim); + } + if (deviceSettingsKeys.contains("fcPos") || force) { + swgTestSourceSettings->setFcPos((int) settings.m_fcPos); + } + if (deviceSettingsKeys.contains("sampleSizeIndex") || force) { + swgTestSourceSettings->setSampleSizeIndex(settings.m_sampleSizeIndex); + } + if (deviceSettingsKeys.contains("amplitudeBits") || force) { + swgTestSourceSettings->setAmplitudeBits(settings.m_amplitudeBits); + } + if (deviceSettingsKeys.contains("autoCorrOptions") || force) { + swgTestSourceSettings->setAutoCorrOptions((int) settings.m_sampleSizeIndex); + } + if (deviceSettingsKeys.contains("modulation") || force) { + swgTestSourceSettings->setModulation((int) settings.m_modulation); + } + if (deviceSettingsKeys.contains("modulationTone")) { + swgTestSourceSettings->setModulationTone(settings.m_modulationTone); + } + if (deviceSettingsKeys.contains("amModulation") || force) { + swgTestSourceSettings->setAmModulation(settings.m_amModulation); + }; + if (deviceSettingsKeys.contains("fmDeviation") || force) { + swgTestSourceSettings->setFmDeviation(settings.m_fmDeviation); + }; + if (deviceSettingsKeys.contains("dcFactor") || force) { + swgTestSourceSettings->setDcFactor(settings.m_dcFactor); + }; + if (deviceSettingsKeys.contains("iFactor") || force) { + swgTestSourceSettings->setIFactor(settings.m_iFactor); + }; + if (deviceSettingsKeys.contains("qFactor") || force) { + swgTestSourceSettings->setQFactor(settings.m_qFactor); + }; + if (deviceSettingsKeys.contains("phaseImbalance") || force) { + swgTestSourceSettings->setPhaseImbalance(settings.m_phaseImbalance); + }; + if (deviceSettingsKeys.contains("fileRecordName") || force) { + swgTestSourceSettings->setFileRecordName(new QString(settings.m_fileRecordName)); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer=new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); +// qDebug("TestSourceInput::webapiReverseSendSettings: %s", channelSettingsURL.toStdString().c_str()); +// qDebug("TestSourceInput::webapiReverseSendSettings: query:\n%s", swgDeviceSettings->asJson().toStdString().c_str()); + + // Always use PATCH to avoid passing reverse API settings + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgDeviceSettings; +} + +void TestSourceInput::webapiReverseSendStartStop(bool start) +{ + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + + if (start) { + m_networkManager->sendCustomRequest(m_networkRequest, "POST"); + } else { + m_networkManager->sendCustomRequest(m_networkRequest, "DELETE"); + } +} + +void TestSourceInput::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "TestSourceInput::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("TestSourceInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/samplesource/testsource/testsourceinput.h b/plugins/samplesource/testsource/testsourceinput.h index 6938f7b52..bbaf386e6 100644 --- a/plugins/samplesource/testsource/testsourceinput.h +++ b/plugins/samplesource/testsource/testsourceinput.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "testsourcesettings.h" @@ -27,8 +28,11 @@ class DeviceSourceAPI; class TestSourceThread; class FileRecord; +class QNetworkAccessManager; +class QNetworkReply; class TestSourceInput : public DeviceSampleSource { + Q_OBJECT public: class MsgConfigureTestSource : public Message { MESSAGE_CLASS_DECLARATION @@ -138,9 +142,16 @@ private: QString m_deviceDescription; bool m_running; const QTimer& m_masterTimer; + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; bool applySettings(const TestSourceSettings& settings, bool force); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestSourceSettings& settings); + void webapiReverseSendSettings(QList& deviceSettingsKeys, const TestSourceSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; #endif // _TESTSOURCE_TESTSOURCEINPUT_H_ diff --git a/plugins/samplesource/testsource/testsourceplugin.cpp b/plugins/samplesource/testsource/testsourceplugin.cpp index ad19169ad..8535a150a 100644 --- a/plugins/samplesource/testsource/testsourceplugin.cpp +++ b/plugins/samplesource/testsource/testsourceplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor TestSourcePlugin::m_pluginDescriptor = { QString("Test Source input"), - QString("4.2.4"), + QString("4.3.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/testsource/testsourcesettings.cpp b/plugins/samplesource/testsource/testsourcesettings.cpp index 65e1b10b2..eb0cb8838 100644 --- a/plugins/samplesource/testsource/testsourcesettings.cpp +++ b/plugins/samplesource/testsource/testsourcesettings.cpp @@ -42,6 +42,10 @@ void TestSourceSettings::resetToDefaults() m_qFactor = 0.0f; m_phaseImbalance = 0.0f; m_fileRecordName = ""; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray TestSourceSettings::serialize() const @@ -63,7 +67,10 @@ QByteArray TestSourceSettings::serialize() const s.writeS32(15, m_modulationTone); s.writeS32(16, m_amModulation); s.writeS32(17, m_fmDeviation); - + s.writeBool(18, m_useReverseAPI); + s.writeString(19, m_reverseAPIAddress); + s.writeU32(20, m_reverseAPIPort); + s.writeU32(21, m_reverseAPIDeviceIndex); return s.final(); } @@ -80,6 +87,7 @@ bool TestSourceSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t utmp; d.readS32(2, &m_frequencyShift, 0); d.readU32(3, &m_sampleRate, 768*1000); @@ -112,6 +120,19 @@ bool TestSourceSettings::deserialize(const QByteArray& data) d.readS32(16, &m_amModulation, 50); d.readS32(17, &m_fmDeviation, 50); + d.readBool(18, &m_useReverseAPI, false); + d.readString(19, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(20, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(21, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + return true; } else diff --git a/plugins/samplesource/testsource/testsourcesettings.h b/plugins/samplesource/testsource/testsourcesettings.h index 60c7d055f..4075f0d0b 100644 --- a/plugins/samplesource/testsource/testsourcesettings.h +++ b/plugins/samplesource/testsource/testsourcesettings.h @@ -60,6 +60,10 @@ struct TestSourceSettings { float m_qFactor; //!< -1.0 < x < 1.0 float m_phaseImbalance; //!< -1.0 < x < 1.0 QString m_fileRecordName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; TestSourceSettings(); void resetToDefaults(); diff --git a/pluginssrv/samplesink/soapysdroutput/CMakeLists.txt b/pluginssrv/samplesink/soapysdroutput/CMakeLists.txt index b84de7a4a..28907918d 100644 --- a/pluginssrv/samplesink/soapysdroutput/CMakeLists.txt +++ b/pluginssrv/samplesink/soapysdroutput/CMakeLists.txt @@ -65,4 +65,4 @@ endif (BUILD_DEBIAN) target_link_libraries(outputsoapysdrsrv Qt5::Core) -install(TARGETS outputsoapysdrsrv DESTINATION lib/plugins/samplesink) +install(TARGETS outputsoapysdrsrv DESTINATION lib/pluginssrv/samplesink) diff --git a/pluginssrv/samplesource/soapysdrinput/CMakeLists.txt b/pluginssrv/samplesource/soapysdrinput/CMakeLists.txt index ceccc4ff2..b4f458a76 100644 --- a/pluginssrv/samplesource/soapysdrinput/CMakeLists.txt +++ b/pluginssrv/samplesource/soapysdrinput/CMakeLists.txt @@ -65,4 +65,4 @@ endif (BUILD_DEBIAN) target_link_libraries(inputsoapysdrsrv Qt5::Core) -install(TARGETS inputsoapysdrsrv DESTINATION lib/plugins/samplesource) +install(TARGETS inputsoapysdrsrv DESTINATION lib/pluginssrv/samplesource) diff --git a/qrtplib/CMakeLists.txt b/qrtplib/CMakeLists.txt index ef57c69c2..f194ca43b 100644 --- a/qrtplib/CMakeLists.txt +++ b/qrtplib/CMakeLists.txt @@ -88,6 +88,8 @@ add_library(qrtplib SHARED ${qrtplib_HEADERS_MOC} ) +set_target_properties(qrtplib PROPERTIES DEFINE_SYMBOL "qrtplib_EXPORTS") + target_link_libraries(qrtplib ${QT_LIBRARIES} ) diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 13ddb98f6..c8732e590 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -289,6 +289,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/httpserver ${CMAKE_SOURCE_DIR}/qrtplib ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${Boost_INCLUDE_DIRS} ) target_link_libraries(sdrbase diff --git a/sdrbase/dsp/downchannelizer.cpp b/sdrbase/dsp/downchannelizer.cpp index 4d53b3b45..b8fe5f3f3 100644 --- a/sdrbase/dsp/downchannelizer.cpp +++ b/sdrbase/dsp/downchannelizer.cpp @@ -295,7 +295,7 @@ void DownChannelizer::freeFilterChain() void DownChannelizer::debugFilterChain() { - qDebug("DownChannelizer::debugFilterChain: %u stages", m_filterStages.size()); + qDebug("DownChannelizer::debugFilterChain: %lu stages", m_filterStages.size()); for(FilterStages::iterator it = m_filterStages.begin(); it != m_filterStages.end(); ++it) { diff --git a/sdrbase/dsp/interpolator.cpp b/sdrbase/dsp/interpolator.cpp index a8ff5c88f..467ed0a48 100644 --- a/sdrbase/dsp/interpolator.cpp +++ b/sdrbase/dsp/interpolator.cpp @@ -28,31 +28,43 @@ void Interpolator::createPolyphaseLowPass( { int ntaps = (int)(nbTapsPerPhase * phaseSteps); qDebug("Interpolator::createPolyphaseLowPass: ntaps: %d", ntaps); - if((ntaps % 2) != 0) - ntaps++; + + if ((ntaps % 2) != 0) { + ntaps++; + } + ntaps *= phaseSteps; taps.resize(ntaps); std::vector window(ntaps); - for(int n = 0; n < ntaps; n++) - window[n] = 0.54 - 0.46 * cos ((2 * M_PI * n) / (ntaps - 1)); + for (int n = 0; n < ntaps; n++) { + window[n] = 0.54 - 0.46 * cos ((2 * M_PI * n) / (ntaps - 1)); + } int M = (ntaps - 1) / 2; double fwT0 = 2 * M_PI * cutoffFreqHz / sampleRateHz; - for(int n = -M; n <= M; n++) { - if(n == 0) taps[n + M] = fwT0 / M_PI * window[n + M]; - else taps[n + M] = sin (n * fwT0) / (n * M_PI) * window[n + M]; + + for (int n = -M; n <= M; n++) + { + if (n == 0) { + taps[n + M] = fwT0 / M_PI * window[n + M]; + } else { + taps[n + M] = sin (n * fwT0) / (n * M_PI) * window[n + M]; + } } double max = taps[0 + M]; - for(int n = 1; n <= M; n++) - max += 2.0 * taps[n + M]; + + for (int n = 1; n <= M; n++) { + max += 2.0 * taps[n + M]; + } gain /= max; - for(int i = 0; i < ntaps; i++) - taps[i] *= gain; + for (int i = 0; i < ntaps; i++) { + taps[i] *= gain; + } } Interpolator::Interpolator() : @@ -90,39 +102,60 @@ void Interpolator::create(int phaseSteps, double sampleRate, double cutoff, doub m_nTaps = taps.size() / phaseSteps; m_phaseSteps = phaseSteps; m_samples.resize(m_nTaps + 2); - for(int i = 0; i < m_nTaps + 2; i++) - m_samples[i] = 0; + + for (int i = 0; i < m_nTaps + 2; i++) { + m_samples[i] = 0; + } // reorder into polyphase std::vector polyphase(taps.size()); - for(int phase = 0; phase < phaseSteps; phase++) { - for(int i = 0; i < m_nTaps; i++) - polyphase[phase * m_nTaps + i] = taps[i * phaseSteps + phase]; + + for (int phase = 0; phase < phaseSteps; phase++) + { + for (int i = 0; i < m_nTaps; i++) { + polyphase[phase * m_nTaps + i] = taps[i * phaseSteps + phase]; + } } // normalize phase filters - for(int phase = 0; phase < phaseSteps; phase++) { + for (int phase = 0; phase < phaseSteps; phase++) + { Real sum = 0; - for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++) - sum += polyphase[i]; - for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++) - polyphase[i] /= sum; + + for (int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++) { + sum += polyphase[i]; + } + + for (int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++) { + polyphase[i] /= sum; + } } // move taps around to match sse storage requirements m_taps = new float[2 * taps.size() + 8]; - for(uint i = 0; i < 2 * taps.size() + 8; ++i) - m_taps[i] = 0; + + for (uint i = 0; i < 2 * taps.size() + 8; ++i) { + m_taps[i] = 0; + } + m_alignedTaps = (float*)((((quint64)m_taps) + 15) & ~15); - for(uint i = 0; i < taps.size(); ++i) { + + for (uint i = 0; i < taps.size(); ++i) + { m_alignedTaps[2 * i + 0] = polyphase[i]; m_alignedTaps[2 * i + 1] = polyphase[i]; } + m_taps2 = new float[2 * taps.size() + 8]; - for(uint i = 0; i < 2 * taps.size() + 8; ++i) - m_taps2[i] = 0; + + for (uint i = 0; i < 2 * taps.size() + 8; ++i) { + m_taps2[i] = 0; + } + m_alignedTaps2 = (float*)((((quint64)m_taps2) + 15) & ~15); - for(uint i = 1; i < taps.size(); ++i) { + + for (uint i = 1; i < taps.size(); ++i) + { m_alignedTaps2[2 * (i - 1) + 0] = polyphase[i]; m_alignedTaps2[2 * (i - 1) + 1] = polyphase[i]; } @@ -130,7 +163,8 @@ void Interpolator::create(int phaseSteps, double sampleRate, double cutoff, doub void Interpolator::free() { - if(m_taps != NULL) { + if (m_taps != NULL) + { delete[] m_taps; m_taps = NULL; m_alignedTaps = NULL; diff --git a/sdrbase/dsp/interpolator.h b/sdrbase/dsp/interpolator.h index 07d5171d0..e413d8907 100644 --- a/sdrbase/dsp/interpolator.h +++ b/sdrbase/dsp/interpolator.h @@ -1,3 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 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 // +// // +// 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_INTERPOLATOR_H #define INCLUDE_INTERPOLATOR_H @@ -17,13 +33,13 @@ public: void free(); // Original code allowed for upsampling, but was never used that way + // The decimation factor should always be lower than 2 for proper work bool decimate(Real *distance, const Complex& next, Complex* result) { advanceFilter(next); *distance -= 1.0; - if (*distance >= 1.0) - { + if (*distance >= 1.0) { return false; } @@ -53,9 +69,9 @@ public: // sampling frequency must be the highest of the two bool resample(Real* distance, const Complex& next, bool* consumed, Complex* result) { - while(*distance >= 1.0) + while (*distance >= 1.0) { - if(!(*consumed)) + if (!(*consumed)) { advanceFilter(next); *distance -= 1.0; @@ -104,24 +120,31 @@ private: void advanceFilter(const Complex& next) { m_ptr--; - if(m_ptr < 0) - m_ptr = m_nTaps - 1; + + if (m_ptr < 0) { + m_ptr = m_nTaps - 1; + } + m_samples[m_ptr] = next; } void advanceFilter() { m_ptr--; - if(m_ptr < 0) + + if (m_ptr < 0) { m_ptr = m_nTaps - 1; + } + m_samples[m_ptr].real(0.0); m_samples[m_ptr].imag(0.0); } void doInterpolate(int phase, Complex* result) { - if (phase < 0) - phase = 0; + if (phase < 0) { + phase = 0; + } #if USE_SSE2 // beware of the ringbuffer if(m_ptr == 0) { @@ -182,12 +205,13 @@ private: Real rAcc = 0; Real iAcc = 0; - for(int i = 0; i < m_nTaps; i++) { + for (int i = 0; i < m_nTaps; i++) { rAcc += *coeff * m_samples[sample].real(); iAcc += *coeff * m_samples[sample].imag(); sample = (sample + 1) % m_nTaps; coeff += 2; } + *result = Complex(rAcc, iAcc); #endif diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 54b9dc825..7e20021dd 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -760,6 +760,22 @@ margin-bottom: 20px; "syncAMOperation" : { "type" : "integer", "description" : "Synchronous AM sidebands mode (DSB, USB, LSB)" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "AMDemod" @@ -821,6 +837,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -916,6 +948,22 @@ margin-bottom: 20px; }, "videoFileName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "ATVMod" @@ -1261,6 +1309,22 @@ margin-bottom: 20px; }, "audioDeviceName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "BFMDemod" @@ -1834,6 +1898,22 @@ margin-bottom: 20px; "traceDecay" : { "type" : "integer", "description" : "0 to 255" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "DSDDemod" @@ -1887,6 +1967,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "Daemon channel sink settings" @@ -1963,6 +2059,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "Daemon channel source settings" @@ -2715,6 +2827,19 @@ margin-bottom: 20px; "gpioPins" : { "type" : "integer", "format" : "int8" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "LimeSDR" @@ -2824,6 +2949,19 @@ margin-bottom: 20px; "gpioPins" : { "type" : "integer", "format" : "int8" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "LimeSDR" @@ -3013,6 +3151,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -3521,6 +3675,19 @@ margin-bottom: 20px; }, "fileRecordName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "RTLSDR" @@ -3897,6 +4064,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -4292,6 +4475,19 @@ margin-bottom: 20px; }, "fileRecordName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "TestSource" @@ -4490,6 +4686,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "UDPSource" @@ -4612,6 +4824,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -23649,7 +23877,7 @@ except ApiException as e:
- Generated 2018-11-29T00:50:52.609+01:00 + Generated 2018-12-22T12:54:35.895+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml index 08c6f601d..22aaa5aa4 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/AMDemod.yaml @@ -33,6 +33,17 @@ AMDemodSettings: syncAMOperation: description: Synchronous AM sidebands mode (DSB, USB, LSB) type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer AMDemodReport: description: AMDemod diff --git a/sdrbase/resources/webapi/doc/swagger/include/AMMod.yaml b/sdrbase/resources/webapi/doc/swagger/include/AMMod.yaml index 5b96deaaf..98328661f 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/AMMod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/AMMod.yaml @@ -29,9 +29,20 @@ AMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "/doc/swagger/include/CWKeyer.yaml#/CWKeyerSettings" - + AMModReport: description: AMMod properties: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ATVMod.yaml b/sdrbase/resources/webapi/doc/swagger/include/ATVMod.yaml index 58acb2067..5c04ee404 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ATVMod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ATVMod.yaml @@ -53,7 +53,18 @@ ATVModSettings: type: string videoFileName: type: string - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + ATVModReport: description: ATVMod properties: diff --git a/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml index 65b979633..6b1fb54fc 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml @@ -30,6 +30,17 @@ BFMDemodSettings: type: string audioDeviceName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer BFMDemodReport: description: BFMDemod @@ -54,7 +65,7 @@ BFMDemodReport: format: float rdsReport: $ref: "#/RDSReport" - + RDSReport: description: RDS information properties: diff --git a/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml index 91895f382..024995803 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/DSDDemod.yaml @@ -54,6 +54,17 @@ DSDDemodSettings: traceDecay: description: 0 to 255 type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer DSDDemodReport: description: DSDDemod @@ -96,5 +107,4 @@ DSDDemodReport: statusText: description: mode dependent status messages (ref UI documentation) type: string - - \ No newline at end of file + diff --git a/sdrbase/resources/webapi/doc/swagger/include/DaemonSink.yaml b/sdrbase/resources/webapi/doc/swagger/include/DaemonSink.yaml index f381ced78..787afb7f1 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/DaemonSink.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/DaemonSink.yaml @@ -14,6 +14,17 @@ DaemonSinkSettings: description: "Minimum delay in ms between consecutive USB blocks transmissions" type: integer rgbColor: - type: integer + type: integer title: - type: string + type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer diff --git a/sdrbase/resources/webapi/doc/swagger/include/DaemonSource.yaml b/sdrbase/resources/webapi/doc/swagger/include/DaemonSource.yaml index 36f13f348..745b3df89 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/DaemonSource.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/DaemonSource.yaml @@ -11,7 +11,18 @@ DaemonSourceSettings: type: integer title: type: string - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + DaemonSourceReport: description: "Daemon channel source report" properties: diff --git a/sdrbase/resources/webapi/doc/swagger/include/LimeSdr.yaml b/sdrbase/resources/webapi/doc/swagger/include/LimeSdr.yaml index bc4b90408..eb7921cd0 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/LimeSdr.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/LimeSdr.yaml @@ -53,7 +53,16 @@ LimeSdrInputSettings: gpioPins: type: integer format: int8 - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + LimeSdrOutputSettings: description: LimeSDR properties: @@ -95,6 +104,15 @@ LimeSdrOutputSettings: gpioPins: type: integer format: int8 + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer LimeSdrInputReport: description: LimeSDR diff --git a/sdrbase/resources/webapi/doc/swagger/include/NFMMod.yaml b/sdrbase/resources/webapi/doc/swagger/include/NFMMod.yaml index e476cc261..b543a71e7 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/NFMMod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/NFMMod.yaml @@ -35,6 +35,17 @@ NFMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "/doc/swagger/include/CWKeyer.yaml#/CWKeyerSettings" diff --git a/sdrbase/resources/webapi/doc/swagger/include/RtlSdr.yaml b/sdrbase/resources/webapi/doc/swagger/include/RtlSdr.yaml index 8b80ce510..f68313c7b 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/RtlSdr.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/RtlSdr.yaml @@ -35,6 +35,15 @@ RtlSdrSettings: type: integer fileRecordName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer RtlSdrReport: description: RTLSDR diff --git a/sdrbase/resources/webapi/doc/swagger/include/SSBMod.yaml b/sdrbase/resources/webapi/doc/swagger/include/SSBMod.yaml index a460bff62..16b56d655 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/SSBMod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/SSBMod.yaml @@ -53,6 +53,17 @@ SSBModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "/doc/swagger/include/CWKeyer.yaml#/CWKeyerSettings" diff --git a/sdrbase/resources/webapi/doc/swagger/include/TestSource.yaml b/sdrbase/resources/webapi/doc/swagger/include/TestSource.yaml index d5c88645c..4c4d6ae96 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/TestSource.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/TestSource.yaml @@ -40,4 +40,13 @@ TestSourceSettings: format: float fileRecordName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer \ No newline at end of file diff --git a/sdrbase/resources/webapi/doc/swagger/include/UDPSource.yaml b/sdrbase/resources/webapi/doc/swagger/include/UDPSource.yaml index 46e8b680b..5f3193a13 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/UDPSource.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/UDPSource.yaml @@ -35,20 +35,31 @@ UDPSourceSettings: type: number format: float squelchEnabled: - type: integer + type: integer autoRWBalance: - type: integer + type: integer stereoInput: - type: integer + type: integer rgbColor: - type: integer + type: integer udpAddress: type: string udpPort: type: integer - format: uint16 + format: uint16 title: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer UDPSourceReport: description: UDPSource @@ -69,4 +80,3 @@ UDPSourceReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/sdrbase/resources/webapi/doc/swagger/include/WFMMod.yaml b/sdrbase/resources/webapi/doc/swagger/include/WFMMod.yaml index 8fb9b6fc7..9cdf733db 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/WFMMod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/WFMMod.yaml @@ -31,6 +31,17 @@ WFMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "/doc/swagger/include/CWKeyer.yaml#/CWKeyerSettings" @@ -45,4 +56,3 @@ WFMModReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index ecf762126..5c2c609b1 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -9,6 +9,7 @@ set(sdrgui_SOURCES gui/audiodialog.cpp gui/audioselectdialog.cpp gui/basicchannelsettingsdialog.cpp + gui/basicdevicesettingsdialog.cpp gui/buttonswitch.cpp gui/channelwindow.cpp gui/clickablelabel.cpp @@ -23,9 +24,7 @@ set(sdrgui_SOURCES gui/externalclockbutton.cpp gui/externalclockdialog.cpp gui/glscope.cpp - gui/glscopemulti.cpp gui/glscopegui.cpp - gui/glscopemultigui.cpp gui/glshadersimple.cpp gui/glshadertextured.cpp gui/glshadertvarray.cpp @@ -49,7 +48,6 @@ set(sdrgui_SOURCES gui/valuedialz.cpp dsp/scopevis.cpp - dsp/scopevismulti.cpp dsp/scopevisxy.cpp dsp/spectrumvis.cpp dsp/spectrumscopecombovis.cpp @@ -76,6 +74,7 @@ set(sdrgui_HEADERS gui/audiodialog.h gui/audioselectdialog.h gui/basicchannelsettingsdialog.h + gui/basicdevicesettingsdialog.h gui/buttonswitch.h gui/channelwindow.h gui/colormapper.h @@ -89,9 +88,7 @@ set(sdrgui_HEADERS gui/externalclockbutton.h gui/externalclockdialog.h gui/glscope.h - gui/glscopemulti.h gui/glscopegui.h - gui/glscopemultigui.h gui/glshadersimple.h gui/glshadertvarray.h gui/glshadertextured.h @@ -116,7 +113,6 @@ set(sdrgui_HEADERS gui/valuedialz.h dsp/scopevis.h - dsp/scopevismulti.h dsp/scopevisxy.h dsp/spectrumvis.h dsp/spectrumscopecombovis.h @@ -146,12 +142,12 @@ set(sdrgui_FORMS gui/aboutdialog.ui gui/addpresetdialog.ui gui/basicchannelsettingsdialog.ui + gui/basicdevicesettingsdialog.ui gui/commandoutputdialog.ui gui/cwkeyergui.ui gui/editcommanddialog.ui gui/externalclockdialog.ui gui/glscopegui.ui - gui/glscopemultigui.ui gui/glspectrumgui.ui gui/pluginsdialog.ui gui/audiodialog.ui @@ -198,6 +194,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client ${CMAKE_CURRENT_BINARY_DIR} ${OPENGL_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} ) target_link_libraries(sdrgui diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 0bb8d32dc..cd44a9262 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -78,7 +78,7 @@ private: ChannelInstanceRegistration() : m_channelName(), - m_gui(NULL) + m_gui(nullptr) { } ChannelInstanceRegistration(const QString& channelName, PluginInstanceGUI* pluginGUI) : diff --git a/sdrgui/dsp/scopevismulti.cpp b/sdrgui/dsp/scopevismulti.cpp deleted file mode 100644 index 7318d18cb..000000000 --- a/sdrgui/dsp/scopevismulti.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// 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 "scopevismulti.h" -#include "dsp/dspcommands.h" -#include "gui/glscopemulti.h" - -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgConfigureScopeVisNG, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGAddTrigger, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGChangeTrigger, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGRemoveTrigger, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMoveTrigger, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGFocusOnTrigger, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGAddTrace, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGChangeTrace, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGRemoveTrace, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMoveTrace, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGFocusOnTrace, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGOneShot, Message) -MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMemoryTrace, Message) - -ScopeVisMulti::ScopeVisMulti(GLScopeMulti* glScope) : - m_glScope(glScope), - m_preTriggerDelay(0), - m_currentTriggerIndex(0), - m_focusedTriggerIndex(0), - m_triggerState(TriggerUntriggered), - m_focusedTraceIndex(0), - m_traceSize(m_traceChunkSize), - m_nbSamples(0), - m_timeBase(1), - m_timeOfsProMill(0), - m_traceStart(true), - m_postTrigBuffering(false), - m_traceFill(0), - m_zTraceIndex(-1), - m_sampleRate(0), - m_freeRun(true), - m_maxTraceDelay(0), - m_triggerOneShot(false), - m_triggerWaitForReset(false), - m_currentTraceMemoryIndex(0), - m_nbSources(0) -{ - setObjectName("ScopeVisNG"); - m_glScope->setTraces(&m_traces.m_tracesData, &m_traces.m_traces[0]); - for (int i = 0; i < (int) nbProjectionTypes; i++) { - m_projectorCache[i] = 0.0; - } -} - -ScopeVisMulti::~ScopeVisMulti() -{ -} - -void ScopeVisMulti::setSampleRate(int sampleRate) -{ - if (sampleRate != m_sampleRate) - { - m_sampleRate = sampleRate; - if (m_glScope) m_glScope->setSampleRate(m_sampleRate); - } -} - -void ScopeVisMulti::configure( - uint32_t nbSources, - uint32_t traceSize, - uint32_t timeBase, - uint32_t timeOfsProMill, - uint32_t triggerPre, - bool freeRun) -{ - Message* cmd = MsgConfigureScopeVisNG::create(nbSources, traceSize, timeBase, timeOfsProMill, triggerPre, freeRun); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::addTrace(const TraceData& traceData) -{ - qDebug() << "ScopeVisMulti::addTrace:" - << " m_amp: " << traceData.m_amp - << " m_ofs: " << traceData.m_ofs - << " m_traceDelay: " << traceData.m_traceDelay; - Message* cmd = MsgScopeVisNGAddTrace::create(traceData); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::changeTrace(const TraceData& traceData, uint32_t traceIndex) -{ - qDebug() << "ScopeVisMulti::changeTrace:" - << " trace: " << traceIndex - << " m_amp: " << traceData.m_amp - << " m_ofs: " << traceData.m_ofs - << " m_traceDelay: " << traceData.m_traceDelay; - Message* cmd = MsgScopeVisNGChangeTrace::create(traceData, traceIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::removeTrace(uint32_t traceIndex) -{ - qDebug() << "ScopeVisMulti::removeTrace:" - << " trace: " << traceIndex; - Message* cmd = MsgScopeVisNGRemoveTrace::create(traceIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::moveTrace(uint32_t traceIndex, bool upElseDown) -{ - qDebug() << "ScopeVisMulti::moveTrace:" - << " trace: " << traceIndex - << " up: " << upElseDown; - Message* cmd = MsgScopeVisNGMoveTrace::create(traceIndex, upElseDown); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::focusOnTrace(uint32_t traceIndex) -{ - Message* cmd = MsgScopeVisNGFocusOnTrace::create(traceIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::addTrigger(const TriggerData& triggerData) -{ - Message* cmd = MsgScopeVisNGAddTrigger::create(triggerData); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::changeTrigger(const TriggerData& triggerData, uint32_t triggerIndex) -{ - Message* cmd = MsgScopeVisNGChangeTrigger::create(triggerData, triggerIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::removeTrigger(uint32_t triggerIndex) -{ - Message* cmd = MsgScopeVisNGRemoveTrigger::create(triggerIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::moveTrigger(uint32_t triggerIndex, bool upElseDown) -{ - Message* cmd = MsgScopeVisNGMoveTrigger::create(triggerIndex, upElseDown); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::focusOnTrigger(uint32_t triggerIndex) -{ - Message* cmd = MsgScopeVisNGFocusOnTrigger::create(triggerIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::setOneShot(bool oneShot) -{ - Message* cmd = MsgScopeVisNGOneShot::create(oneShot); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::setMemoryIndex(uint32_t memoryIndex) -{ - Message* cmd = MsgScopeVisNGMemoryTrace::create(memoryIndex); - m_inputMessageQueue.push(cmd); -} - -void ScopeVisMulti::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, uint32_t sourceIndex) -{ - if ((m_triggerWaitForReset) || (m_currentTraceMemoryIndex > 0)) { - return; - } - - if(!m_mutex.tryLock(2)) { // prevent conflicts with configuration process - return; - } - - m_traceDiscreteMemory.feed(cbegin, end, sourceIndex); // memory storage - - if (m_traceDiscreteMemory.minFill() >= m_traceSize) - { - if (m_triggerState == TriggerTriggered) - { - processSources(); - } - else - { - lookForTrigger(); - } - } -} - -void ScopeVisMulti::lookForTrigger() -{ - int count = 0; - - if ((m_freeRun) || (m_triggerConditions.size() == 0)) // immediate re-trigger - { - if (m_triggerState == TriggerUntriggered) - { - m_traceStart = true; // start trace processing - m_nbSamples = m_traceSize + m_maxTraceDelay; - m_triggerState = TriggerTriggered; - } - } - else if ((m_triggerState == TriggerUntriggered) || (m_triggerState == TriggerDelay)) - { - TriggerCondition& triggerCondition = m_triggerConditions[m_currentTriggerIndex]; // current trigger condition - uint32_t sourceIndex = triggerCondition.m_triggerData.m_inputIndex; - - SampleVector::iterator begin = m_traceDiscreteMemory.getBeginIterator(sourceIndex); - SampleVector::iterator end = begin + m_traceSize; - SampleVector::const_iterator cbegin = begin; - - while (begin < end) - { - if (m_triggerState == TriggerDelay) - { - if (triggerCondition.m_triggerDelayCount > 0) // skip samples during delay period - { - triggerCondition.m_triggerDelayCount--; - ++begin; - continue; - } - else // process trigger - { - if (nextTrigger()) // move to next trigger and keep going - { - m_triggerComparator.reset(); - m_triggerState = TriggerUntriggered; - ++begin; - continue; - } - else // this was the last trigger then start trace - { - m_traceStart = true; // start trace processing - m_nbSamples = m_traceSize + m_maxTraceDelay; - m_triggerComparator.reset(); - m_triggerState = TriggerTriggered; - break; - } - } - } - - // look for trigger - if (m_triggerComparator.triggered(*begin, triggerCondition)) - { - if (triggerCondition.m_triggerData.m_triggerDelay > 0) - { - triggerCondition.m_triggerDelayCount = triggerCondition.m_triggerData.m_triggerDelay; // initialize delayed samples counter - m_triggerState = TriggerDelay; - ++begin; - continue; - } - - if (nextTrigger()) // move to next trigger and keep going - { - m_triggerComparator.reset(); - m_triggerState = TriggerUntriggered; - } - else // this was the last trigger then start trace - { - m_traceStart = true; // start trace processing - m_nbSamples = m_traceSize + m_maxTraceDelay; - m_triggerComparator.reset(); - m_triggerState = TriggerTriggered; - break; - } - } - - ++begin; - } - - count = begin - cbegin; - } - - m_traceDiscreteMemory.consume(count); -} - -void ScopeVisMulti::processSources() -{ - if (m_glScope->getDataChanged()) // optimization: process trace only if required by glScope - { - m_triggerState = TriggerUntriggered; - return; - } - - uint32_t nbSources = m_traceDiscreteMemory.getNbSources(); - - for (unsigned int is = 0; is < nbSources; is++) - { - SampleVector::iterator begin = m_traceDiscreteMemory.getBeginIterator(is); // trigger position - SampleVector::iterator end = begin + m_traceSize; - - if (m_traceStart) - { - // trace back - if (m_maxTraceDelay > 0) - { - processTraces(begin - m_preTriggerDelay - m_maxTraceDelay, begin - m_preTriggerDelay, true, is); - } - - // pre-trigger - if (m_preTriggerDelay > 0) - { - processTraces(begin - m_preTriggerDelay, begin, false, is); - } - - m_traceStart = false; - } - - processTraces(begin, end, false, is); - m_traceDiscreteMemory.markEnd(is, end); - } // sources loop - - m_glScope->newTraces(&m_traces.m_traces[m_traces.currentBufferIndex()]); - m_traces.switchBuffer(); - m_traceDiscreteMemory.store(); -} - -void ScopeVisMulti::processMemorySources() -{ - if ((m_currentTraceMemoryIndex > 0) && (m_currentTraceMemoryIndex < m_nbTraceMemories)) - { - uint32_t nbSources = m_traceDiscreteMemory.getNbSources(); - - for (unsigned int is = 0; is < nbSources; is++) - { - SampleVector::const_iterator mend = m_traceDiscreteMemory.m_traceBackBuffers[is].at(m_currentTraceMemoryIndex).m_endPoint; - SampleVector::const_iterator mbegin = mend - m_traceSize; - SampleVector::const_iterator mbegin_tb = mbegin - m_maxTraceDelay; - processTraces(mbegin_tb, mbegin, true, is); // traceback - processTraces(mbegin, mend, false, is); - } - } -} - - -bool ScopeVisMulti::nextTrigger() -{ - TriggerCondition& triggerCondition = m_triggerConditions[m_currentTriggerIndex]; // current trigger condition - - if (triggerCondition.m_triggerData.m_triggerRepeat > 0) - { - if (triggerCondition.m_triggerCounter < triggerCondition.m_triggerData.m_triggerRepeat) - { - triggerCondition.m_triggerCounter++; - return true; // not final keep going - } - else - { - triggerCondition.m_triggerCounter = 0; // reset for next time - } - } - - if (m_triggerConditions.size() == 0) - { - m_currentTriggerIndex = 0; - return false; // final - } - else if (m_currentTriggerIndex < m_triggerConditions.size() - 1) // check if next trigger is available - { - m_currentTriggerIndex++; - return true; // not final keep going - } - else - { - // now this is really finished - m_currentTriggerIndex = 0; - return false; // final - } -} - -void ScopeVisMulti::processTraces(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool traceBack, uint32_t sourceIndex) -{ - SampleVector::const_iterator begin(cbegin); - uint32_t shift = (m_timeOfsProMill / 1000.0) * m_traceSize; - uint32_t length = m_traceSize / m_timeBase; - int nbSamples = end - begin; - - while (begin < end) - { - std::vector::iterator itCtl = m_traces.m_tracesControl.begin(); - std::vector::iterator itData = m_traces.m_tracesData.begin(); - std::vector::iterator itTrace = m_traces.m_traces[m_traces.currentBufferIndex()].begin(); - - for (; itCtl != m_traces.m_tracesControl.end(); ++itCtl, ++itData, ++itTrace) - { - if (itData->m_inputIndex != sourceIndex) { - continue; - } - - if (traceBack && ((end - begin) > itData->m_traceDelay)) { // before start of trace - continue; - } - - ProjectionType projectionType = itData->m_projectionType; - - if (itCtl->m_traceCount[m_traces.currentBufferIndex()] < m_traceSize) - { - uint32_t& traceCount = itCtl->m_traceCount[m_traces.currentBufferIndex()]; // reference for code clarity - float v; - - if (projectionType == ProjectionMagLin) - { - v = (itCtl->m_projector.run(*begin) - itData->m_ofs)*itData->m_amp - 1.0f; - } - else if (projectionType == ProjectionMagDB) - { - // there is no processing advantage in direct calculation without projector -// uint32_t magsq = begin->m_real*begin->m_real + begin->m_imag*begin->m_imag; -// v = ((log10f(magsq/1073741824.0f)*0.2f - 2.0f*itData->m_ofs) + 2.0f)*itData->m_amp - 1.0f; - float pdB = itCtl->m_projector.run(*begin); - float p = pdB - (100.0f * itData->m_ofs); - v = ((p/50.0f) + 2.0f)*itData->m_amp - 1.0f; - - if ((traceCount >= shift) && (traceCount < shift+length)) // power display overlay values construction - { - if (traceCount == shift) - { - itCtl->m_maxPow = -200.0f; - itCtl->m_sumPow = 0.0f; - itCtl->m_nbPow = 1; - } - - if (pdB > -200.0f) - { - if (pdB > itCtl->m_maxPow) - { - itCtl->m_maxPow = pdB; - } - - itCtl->m_sumPow += pdB; - itCtl->m_nbPow++; - } - } - - if ((nbSamples == 1) && (itCtl->m_nbPow > 0)) // on last sample create power display overlay - { - double avgPow = itCtl->m_sumPow / itCtl->m_nbPow; - double peakToAvgPow = itCtl->m_maxPow - avgPow; - itData->m_textOverlay = QString("%1 %2 %3").arg(itCtl->m_maxPow, 0, 'f', 1).arg(avgPow, 0, 'f', 1).arg(peakToAvgPow, 4, 'f', 1, ' '); - itCtl->m_nbPow = 0; - } - } - else - { - v = (itCtl->m_projector.run(*begin) - itData->m_ofs) * itData->m_amp; - } - - if(v > 1.0f) { - v = 1.0f; - } else if (v < -1.0f) { - v = -1.0f; - } - - (*itTrace)[2*traceCount] - = traceCount - shift; // display x - (*itTrace)[2*traceCount + 1] = v; // display y - traceCount++; - } - } - - ++begin; - nbSamples--; - } -} - -bool ScopeVisMulti::handleMessage(const Message& message) -{ - qDebug() << "ScopeVisNG::handleMessage" << message.getIdentifier(); - - if (DSPSignalNotification::match(message)) - { - DSPSignalNotification& notif = (DSPSignalNotification&) message; - setSampleRate(notif.getSampleRate()); - qDebug() << "ScopeVisNG::handleMessage: DSPSignalNotification: m_sampleRate: " << m_sampleRate; - return true; - } - else if (MsgConfigureScopeVisNG::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgConfigureScopeVisNG& conf = (MsgConfigureScopeVisNG&) message; - - uint32_t nbSources = conf.getNbSources(); - uint32_t traceSize = conf.getTraceSize(); - uint32_t timeBase = conf.getTimeBase(); - uint32_t timeOfsProMill = conf.getTimeOfsProMill(); - uint32_t triggerPre = conf.getTriggerPre(); - bool freeRun = conf.getFreeRun(); - - if (m_nbSources != nbSources) - { - if (nbSources == 0) { - m_nbSources = 1; - } else if (nbSources > m_maxNbTraceSources) { - m_nbSources = m_maxNbTraceSources; - } else { - m_nbSources = nbSources; - } - } - - if (m_traceSize != traceSize) - { - m_traceSize = traceSize; - m_traces.resize(m_traceSize); - m_traceDiscreteMemory.resizeBuffers(m_traceSize); - initTraceBuffers(); - - if (m_glScope) { - m_glScope->setTraceSize(m_traceSize); - } - } - - if (m_timeBase != timeBase) - { - m_timeBase = timeBase; - - if (m_glScope) { - m_glScope->setTimeBase(m_timeBase); - } - } - - if (m_timeOfsProMill != timeOfsProMill) - { - m_timeOfsProMill = timeOfsProMill; - - if (m_glScope) { - m_glScope->setTimeOfsProMill(m_timeOfsProMill); - } - } - - if (m_preTriggerDelay != triggerPre) - { - m_preTriggerDelay = triggerPre; - - if (m_glScope) { - m_glScope->setTriggerPre(m_preTriggerDelay); - } - } - - if (freeRun != m_freeRun) - { - m_freeRun = freeRun; - } - - qDebug() << "ScopeVisNG::handleMessage: MsgConfigureScopeVisNG:" - << " m_traceSize: " << m_traceSize - << " m_timeOfsProMill: " << m_timeOfsProMill - << " m_preTriggerDelay: " << m_preTriggerDelay - << " m_freeRun: " << m_freeRun; - - if ((m_glScope) && (m_currentTraceMemoryIndex > 0)) { - processMemorySources(); - } - - return true; - } - else if (MsgScopeVisNGAddTrigger::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGAddTrigger& conf = (MsgScopeVisNGAddTrigger&) message; - m_triggerConditions.push_back(TriggerCondition(conf.getTriggerData())); - m_triggerConditions.back().initProjector(); - return true; - } - else if (MsgScopeVisNGChangeTrigger::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGChangeTrigger& conf = (MsgScopeVisNGChangeTrigger&) message; - uint32_t triggerIndex = conf.getTriggerIndex(); - - if (triggerIndex < m_triggerConditions.size()) - { - m_triggerConditions[triggerIndex].setData(conf.getTriggerData()); - - if (triggerIndex == m_focusedTriggerIndex) - { - computeDisplayTriggerLevels(); - m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData); - updateGLScopeDisplay(); - } - } - - return true; - } - else if (MsgScopeVisNGRemoveTrigger::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGRemoveTrigger& conf = (MsgScopeVisNGRemoveTrigger&) message; - uint32_t triggerIndex = conf.getTriggerIndex(); - - if (triggerIndex < m_triggerConditions.size()) { - m_triggerConditions.erase(m_triggerConditions.begin() + triggerIndex); - } - - return true; - } - else if (MsgScopeVisNGMoveTrigger::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGMoveTrigger& conf = (MsgScopeVisNGMoveTrigger&) message; - int triggerIndex = conf.getTriggerIndex(); - - if (!conf.getMoveUp() && (triggerIndex == 0)) { - return true; - } - - int nextTriggerIndex = (triggerIndex + (conf.getMoveUp() ? 1 : -1)) % m_triggerConditions.size(); - - TriggerCondition nextTrigger = m_triggerConditions[nextTriggerIndex]; - m_triggerConditions[nextTriggerIndex] = m_triggerConditions[triggerIndex]; - m_triggerConditions[triggerIndex] = nextTrigger; - - computeDisplayTriggerLevels(); - m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData); - updateGLScopeDisplay(); - - return true; - } - else if (MsgScopeVisNGFocusOnTrigger::match(message)) - { - MsgScopeVisNGFocusOnTrigger& conf = (MsgScopeVisNGFocusOnTrigger&) message; - uint32_t triggerIndex = conf.getTriggerIndex(); - - if (triggerIndex < m_triggerConditions.size()) - { - m_focusedTriggerIndex = triggerIndex; - computeDisplayTriggerLevels(); - m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData); - updateGLScopeDisplay(); - } - - return true; - } - else if (MsgScopeVisNGAddTrace::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGAddTrace& conf = (MsgScopeVisNGAddTrace&) message; - m_traces.addTrace(conf.getTraceData(), m_traceSize); - initTraceBuffers(); - updateMaxTraceDelay(); - computeDisplayTriggerLevels(); - updateGLScopeDisplay(); - return true; - } - else if (MsgScopeVisNGChangeTrace::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGChangeTrace& conf = (MsgScopeVisNGChangeTrace&) message; - bool doComputeTriggerLevelsOnDisplay = m_traces.isVerticalDisplayChange(conf.getTraceData(), conf.getTraceIndex()); - m_traces.changeTrace(conf.getTraceData(), conf.getTraceIndex()); - updateMaxTraceDelay(); - if (doComputeTriggerLevelsOnDisplay) computeDisplayTriggerLevels(); - updateGLScopeDisplay(); - return true; - } - else if (MsgScopeVisNGRemoveTrace::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGRemoveTrace& conf = (MsgScopeVisNGRemoveTrace&) message; - m_traces.removeTrace(conf.getTraceIndex()); - updateMaxTraceDelay(); - computeDisplayTriggerLevels(); - updateGLScopeDisplay(); - return true; - } - else if (MsgScopeVisNGMoveTrace::match(message)) - { - QMutexLocker configLocker(&m_mutex); - MsgScopeVisNGMoveTrace& conf = (MsgScopeVisNGMoveTrace&) message; - m_traces.moveTrace(conf.getTraceIndex(), conf.getMoveUp()); - //updateMaxTraceDelay(); - computeDisplayTriggerLevels(); - updateGLScopeDisplay(); - return true; - } - else if (MsgScopeVisNGFocusOnTrace::match(message)) - { - MsgScopeVisNGFocusOnTrace& conf = (MsgScopeVisNGFocusOnTrace&) message; - uint32_t traceIndex = conf.getTraceIndex(); - - if (traceIndex < m_traces.m_tracesData.size()) - { - m_focusedTraceIndex = traceIndex; - computeDisplayTriggerLevels(); - m_glScope->setFocusedTraceIndex(m_focusedTraceIndex); - updateGLScopeDisplay(); - } - - return true; - } - else if (MsgScopeVisNGOneShot::match(message)) - { - MsgScopeVisNGOneShot& conf = (MsgScopeVisNGOneShot&) message; - bool oneShot = conf.getOneShot(); - m_triggerOneShot = oneShot; - if (m_triggerWaitForReset && !oneShot) m_triggerWaitForReset = false; - return true; - } - else if (MsgScopeVisNGMemoryTrace::match(message)) - { - MsgScopeVisNGMemoryTrace& conf = (MsgScopeVisNGMemoryTrace&) message; - uint32_t memoryIndex = conf.getMemoryIndex(); - - if (memoryIndex != m_currentTraceMemoryIndex) - { - m_currentTraceMemoryIndex = memoryIndex; - - if (m_currentTraceMemoryIndex > 0) { - processMemorySources(); - } - } - return true; - } - else - { - return false; - } -} - -void ScopeVisMulti::updateMaxTraceDelay() -{ - int maxTraceDelay = 0; - bool allocateCache = false; - uint32_t projectorCounts[(int) nbProjectionTypes]; - memset(projectorCounts, 0, ((int) nbProjectionTypes)*sizeof(uint32_t)); - std::vector::iterator itData = m_traces.m_tracesData.begin(); - std::vector::iterator itCtrl = m_traces.m_tracesControl.begin(); - - for (; itData != m_traces.m_tracesData.end(); ++itData, ++itCtrl) - { - if (itData->m_traceDelay > maxTraceDelay) - { - maxTraceDelay = itData->m_traceDelay; - } - - if (projectorCounts[(int) itData->m_projectionType] > 0) - { - allocateCache = true; - itCtrl->m_projector.setCacheMaster(false); - } - else - { - itCtrl->m_projector.setCacheMaster(true); - } - - projectorCounts[(int) itData->m_projectionType]++; - } - - itCtrl = m_traces.m_tracesControl.begin(); - - for (; itCtrl != m_traces.m_tracesControl.end(); ++itCtrl) - { - if (allocateCache) { - itCtrl->m_projector.setCache(m_projectorCache); - } else { - itCtrl->m_projector.setCache(0); - } - } - - m_maxTraceDelay = maxTraceDelay; -} - -void ScopeVisMulti::initTraceBuffers() -{ - int shift = (m_timeOfsProMill / 1000.0) * m_traceSize; - - std::vector::iterator it0 = m_traces.m_traces[0].begin(); - std::vector::iterator it1 = m_traces.m_traces[1].begin(); - - for (; it0 != m_traces.m_traces[0].end(); ++it0, ++it1) - { - for (unsigned int i = 0; i < m_traceSize; i++) - { - (*it0)[2*i] = (i - shift); // display x - (*it0)[2*i + 1] = 0.0f; // display y - (*it1)[2*i] = (i - shift); // display x - (*it1)[2*i + 1] = 0.0f; // display y - } - } -} - -void ScopeVisMulti::computeDisplayTriggerLevels() -{ - std::vector::iterator itData = m_traces.m_tracesData.begin(); - - for (; itData != m_traces.m_tracesData.end(); ++itData) - { - if ((m_focusedTriggerIndex < m_triggerConditions.size()) && (m_triggerConditions[m_focusedTriggerIndex].m_projector.getProjectionType() == itData->m_projectionType)) - { - float level = m_triggerConditions[m_focusedTriggerIndex].m_triggerData.m_triggerLevel; - float levelPowerLin = level + 1.0f; - float levelPowerdB = (100.0f * (level - 1.0f)); - float v; - - if (itData->m_projectionType == ProjectionMagLin) - { - v = (levelPowerLin - itData->m_ofs)*itData->m_amp - 1.0f; - } - else if (itData->m_projectionType == ProjectionMagDB) - { - float ofsdB = itData->m_ofs * 100.0f; - v = ((levelPowerdB + 100.0f - ofsdB)*itData->m_amp)/50.0f - 1.0f; - } - else - { - v = (level - itData->m_ofs) * itData->m_amp; - } - - if(v > 1.0f) { - v = 1.0f; - } else if (v < -1.0f) { - v = -1.0f; - } - - itData->m_triggerDisplayLevel = v; - } - else - { - itData->m_triggerDisplayLevel = 2.0f; - } - } -} - -void ScopeVisMulti::updateGLScopeDisplay() -{ - if (m_currentTraceMemoryIndex > 0) { - m_glScope->setConfigChanged(); - processMemorySources(); - } else { - m_glScope->updateDisplay(); - } -} - -void ScopeVisMulti::handleInputMessages() -{ - Message* message; - - while ((message = m_inputMessageQueue.pop()) != 0) - { - if (handleMessage(*message)) - { - delete message; - } - } -} diff --git a/sdrgui/dsp/scopevismulti.h b/sdrgui/dsp/scopevismulti.h deleted file mode 100644 index 8f8447a59..000000000 --- a/sdrgui/dsp/scopevismulti.h +++ /dev/null @@ -1,1172 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 F4EXB // -// written by Edouard Griffiths // -// // -// ScopeVis class specialized for multiple sources handling // -// // -// 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 // -// // -// 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_DSP_SCOPEVISMULTI_H_ -#define SDRBASE_DSP_SCOPEVISMULTI_H_ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "dsp/dsptypes.h" -#include "export.h" -#include "util/message.h" -#include "util/messagequeue.h" -#include "util/doublebuffer.h" - -class GLScopeMulti; - -class SDRGUI_API ScopeVisMulti : public QObject { - Q_OBJECT -public: - enum ProjectionType - { - ProjectionReal = 0, //!< Extract real part - ProjectionImag, //!< Extract imaginary part - ProjectionMagLin, //!< Calculate linear magnitude or modulus - ProjectionMagDB, //!< Calculate logarithmic (dB) of squared magnitude - ProjectionPhase, //!< Calculate phase - ProjectionDPhase, //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate - nbProjectionTypes //!< Gives the number of projections in the enum - }; - - struct TraceData - { - ProjectionType m_projectionType; //!< Complex to real projection type - uint32_t m_inputIndex; //!< Input or feed index this trace is associated with - float m_amp; //!< Amplification factor - uint32_t m_ampIndex; //!< Index in list of amplification factors - float m_ofs; //!< Offset factor - int m_ofsCoarse; //!< Coarse offset slider value - int m_ofsFine; //!< Fine offset slider value - int m_traceDelay; //!< Trace delay in number of samples - int m_traceDelayCoarse; //!< Coarse delay slider value - int m_traceDelayFine; //!< Fine delay slider value - float m_triggerDisplayLevel; //!< Displayable trigger display level in -1:+1 scale. Off scale if not displayable. - QColor m_traceColor; //!< Trace display color - float m_traceColorR; //!< Trace display color - red shortcut - float m_traceColorG; //!< Trace display color - green shortcut - float m_traceColorB; //!< Trace display color - blue shortcut - bool m_hasTextOverlay; //!< True if a text overlay has to be displayed - QString m_textOverlay; //!< Text overlay to display - bool m_viewTrace; //!< Trace visibility - - TraceData() : - m_projectionType(ProjectionReal), - m_inputIndex(0), - m_amp(1.0f), - m_ampIndex(0), - m_ofs(0.0f), - m_ofsCoarse(0), - m_ofsFine(0), - m_traceDelay(0), - m_traceDelayCoarse(0), - m_traceDelayFine(0), - m_triggerDisplayLevel(2.0), // OVer scale by default (2.0) - m_traceColor(255,255,64), - m_hasTextOverlay(false), - m_viewTrace(true) - { - setColor(m_traceColor); - } - - void setColor(QColor color) - { - m_traceColor = color; - qreal r,g,b,a; - m_traceColor.getRgbF(&r, &g, &b, &a); - m_traceColorR = r; - m_traceColorG = g; - m_traceColorB = b; - } - }; - - - struct TriggerData - { - ProjectionType m_projectionType; //!< Complex to real projection type - uint32_t m_inputIndex; //!< Input or feed index this trigger is associated with - Real m_triggerLevel; //!< Level in real units - int m_triggerLevelCoarse; - int m_triggerLevelFine; - bool m_triggerPositiveEdge; //!< Trigger on the positive edge (else negative) - bool m_triggerBothEdges; //!< Trigger on both edges (else only one) - uint32_t m_triggerDelay; //!< Delay before the trigger is kicked off in number of samples (trigger delay) - double m_triggerDelayMult; //!< Trigger delay as a multiplier of trace length - int m_triggerDelayCoarse; - int m_triggerDelayFine; - uint32_t m_triggerRepeat; //!< Number of trigger conditions before the final decisive trigger - QColor m_triggerColor; //!< Trigger line display color - float m_triggerColorR; //!< Trigger line display color - red shortcut - float m_triggerColorG; //!< Trigger line display color - green shortcut - float m_triggerColorB; //!< Trigger line display color - blue shortcut - - TriggerData() : - m_projectionType(ProjectionReal), - m_inputIndex(0), - m_triggerLevel(0.0f), - m_triggerLevelCoarse(0), - m_triggerLevelFine(0), - m_triggerPositiveEdge(true), - m_triggerBothEdges(false), - m_triggerDelay(0), - m_triggerDelayMult(0.0), - m_triggerDelayCoarse(0), - m_triggerDelayFine(0), - m_triggerRepeat(0), - m_triggerColor(0,255,0) - { - setColor(m_triggerColor); - } - - void setColor(QColor color) - { - m_triggerColor = color; - qreal r,g,b,a; - m_triggerColor.getRgbF(&r, &g, &b, &a); - m_triggerColorR = r; - m_triggerColorG = g; - m_triggerColorB = b; - } - }; - - static const uint32_t m_traceChunkSize = 4800; - static const uint32_t m_maxNbTriggers = 10; - static const uint32_t m_maxNbTraces = 10; - static const uint32_t m_nbTraceMemories = 16; - static const uint32_t m_maxNbTraceSources = 4; - - ScopeVisMulti(GLScopeMulti* glScope = 0); - ~ScopeVisMulti(); - - void setSampleRate(int sampleRate); - void configure(uint32_t nbSources, uint32_t traceSize, uint32_t timeBase, uint32_t timeOfsProMill, uint32_t triggerPre, bool freeRun); - void addTrace(const TraceData& traceData); - void changeTrace(const TraceData& traceData, uint32_t traceIndex); - void removeTrace(uint32_t traceIndex); - void moveTrace(uint32_t traceIndex, bool upElseDown); - void focusOnTrace(uint32_t traceIndex); - void addTrigger(const TriggerData& triggerData); - void changeTrigger(const TriggerData& triggerData, uint32_t triggerIndex); - void removeTrigger(uint32_t triggerIndex); - void moveTrigger(uint32_t triggerIndex, bool upElseDown); - void focusOnTrigger(uint32_t triggerIndex); - void setOneShot(bool oneShot); - void setMemoryIndex(uint32_t memoryIndex); - - void getTriggerData(TriggerData& triggerData, uint32_t triggerIndex) - { - if (triggerIndex < m_triggerConditions.size()) - { - triggerData = m_triggerConditions[triggerIndex].m_triggerData; - } - } - - void getTraceData(TraceData& traceData, uint32_t traceIndex) - { - if (traceIndex < m_traces.m_tracesData.size()) - { - traceData = m_traces.m_tracesData[traceIndex]; - } - } - - const TriggerData& getTriggerData(uint32_t triggerIndex) const { return m_triggerConditions[triggerIndex].m_triggerData; } - const std::vector& getTracesData() const { return m_traces.m_tracesData; } - uint32_t getNbTriggers() const { return m_triggerConditions.size(); } - - void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, uint32_t sourceIndex); - -protected: - MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication - MessageQueue m_outputMessageQueue; //!< Queue for asynchronous outbound communication - - bool handleMessage(const Message& message); - -protected slots: - void handleInputMessages(); - -private: - // === messages === - // --------------------------------------------- - class MsgConfigureScopeVisNG : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgConfigureScopeVisNG* create( - uint32_t nbSources, - uint32_t traceSize, - uint32_t timeBase, - uint32_t timeOfsProMill, - uint32_t triggerPre, - bool freeRun) - { - return new MsgConfigureScopeVisNG(nbSources, traceSize, timeBase, timeOfsProMill, triggerPre, freeRun); - } - - uint32_t getNbSources() const { return m_nbSources; } - uint32_t getTraceSize() const { return m_traceSize; } - uint32_t getTimeBase() const { return m_timeBase; } - uint32_t getTimeOfsProMill() const { return m_timeOfsProMill; } - uint32_t getTriggerPre() const { return m_triggerPre; } - bool getFreeRun() const { return m_freeRun; } - - private: - uint32_t m_nbSources; - uint32_t m_traceSize; - uint32_t m_timeBase; - uint32_t m_timeOfsProMill; - uint32_t m_triggerPre; - bool m_freeRun; - - MsgConfigureScopeVisNG( - uint32_t nbSources, - uint32_t traceSize, - uint32_t timeBase, - uint32_t timeOfsProMill, - uint32_t triggerPre, - bool freeRun) : - m_nbSources(nbSources), - m_traceSize(traceSize), - m_timeBase(timeBase), - m_timeOfsProMill(timeOfsProMill), - m_triggerPre(triggerPre), - m_freeRun(freeRun) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGAddTrigger : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGAddTrigger* create( - const TriggerData& triggerData) - { - return new MsgScopeVisNGAddTrigger(triggerData); - } - - const TriggerData& getTriggerData() const { return m_triggerData; } - - private: - TriggerData m_triggerData; - - MsgScopeVisNGAddTrigger(const TriggerData& triggerData) : - m_triggerData(triggerData) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGChangeTrigger : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGChangeTrigger* create( - const TriggerData& triggerData, uint32_t triggerIndex) - { - return new MsgScopeVisNGChangeTrigger(triggerData, triggerIndex); - } - - const TriggerData& getTriggerData() const { return m_triggerData; } - uint32_t getTriggerIndex() const { return m_triggerIndex; } - - private: - TriggerData m_triggerData; - uint32_t m_triggerIndex; - - MsgScopeVisNGChangeTrigger(const TriggerData& triggerData, uint32_t triggerIndex) : - m_triggerData(triggerData), - m_triggerIndex(triggerIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGRemoveTrigger : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGRemoveTrigger* create( - uint32_t triggerIndex) - { - return new MsgScopeVisNGRemoveTrigger(triggerIndex); - } - - uint32_t getTriggerIndex() const { return m_triggerIndex; } - - private: - uint32_t m_triggerIndex; - - MsgScopeVisNGRemoveTrigger(uint32_t triggerIndex) : - m_triggerIndex(triggerIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGMoveTrigger : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGMoveTrigger* create( - uint32_t triggerIndex, - bool moveUpElseDown) - { - return new MsgScopeVisNGMoveTrigger(triggerIndex, moveUpElseDown); - } - - uint32_t getTriggerIndex() const { return m_triggerIndex; } - bool getMoveUp() const { return m_moveUpElseDown; } - - private: - uint32_t m_triggerIndex; - bool m_moveUpElseDown; - - MsgScopeVisNGMoveTrigger(uint32_t triggerIndex, bool moveUpElseDown) : - m_triggerIndex(triggerIndex), - m_moveUpElseDown(moveUpElseDown) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGFocusOnTrigger : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGFocusOnTrigger* create( - uint32_t triggerIndex) - { - return new MsgScopeVisNGFocusOnTrigger(triggerIndex); - } - - uint32_t getTriggerIndex() const { return m_triggerIndex; } - - private: - uint32_t m_triggerIndex; - - MsgScopeVisNGFocusOnTrigger(uint32_t triggerIndex) : - m_triggerIndex(triggerIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGAddTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGAddTrace* create( - const TraceData& traceData) - { - return new MsgScopeVisNGAddTrace(traceData); - } - - const TraceData& getTraceData() const { return m_traceData; } - - private: - TraceData m_traceData; - - MsgScopeVisNGAddTrace(const TraceData& traceData) : - m_traceData(traceData) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGChangeTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGChangeTrace* create( - const TraceData& traceData, uint32_t traceIndex) - { - return new MsgScopeVisNGChangeTrace(traceData, traceIndex); - } - - const TraceData& getTraceData() const { return m_traceData; } - uint32_t getTraceIndex() const { return m_traceIndex; } - - private: - TraceData m_traceData; - uint32_t m_traceIndex; - - MsgScopeVisNGChangeTrace(TraceData traceData, uint32_t traceIndex) : - m_traceData(traceData), - m_traceIndex(traceIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGRemoveTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGRemoveTrace* create( - uint32_t traceIndex) - { - return new MsgScopeVisNGRemoveTrace(traceIndex); - } - - uint32_t getTraceIndex() const { return m_traceIndex; } - - private: - uint32_t m_traceIndex; - - MsgScopeVisNGRemoveTrace(uint32_t traceIndex) : - m_traceIndex(traceIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGMoveTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGMoveTrace* create( - uint32_t traceIndex, - bool moveUpElseDown) - { - return new MsgScopeVisNGMoveTrace(traceIndex, moveUpElseDown); - } - - uint32_t getTraceIndex() const { return m_traceIndex; } - bool getMoveUp() const { return m_moveUpElseDown; } - - private: - uint32_t m_traceIndex; - bool m_moveUpElseDown; - - MsgScopeVisNGMoveTrace(uint32_t traceIndex, bool moveUpElseDown) : - m_traceIndex(traceIndex), - m_moveUpElseDown(moveUpElseDown) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGFocusOnTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGFocusOnTrace* create( - uint32_t traceIndex) - { - return new MsgScopeVisNGFocusOnTrace(traceIndex); - } - - uint32_t getTraceIndex() const { return m_traceIndex; } - - private: - uint32_t m_traceIndex; - - MsgScopeVisNGFocusOnTrace(uint32_t traceIndex) : - m_traceIndex(traceIndex) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGOneShot : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGOneShot* create( - bool oneShot) - { - return new MsgScopeVisNGOneShot(oneShot); - } - - bool getOneShot() const { return m_oneShot; } - - private: - bool m_oneShot; - - MsgScopeVisNGOneShot(bool oneShot) : - m_oneShot(oneShot) - {} - }; - - // --------------------------------------------- - class MsgScopeVisNGMemoryTrace : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgScopeVisNGMemoryTrace* create( - uint32_t memoryIndex) - { - return new MsgScopeVisNGMemoryTrace(memoryIndex); - } - - uint32_t getMemoryIndex() const { return m_memoryIndex; } - - private: - uint32_t m_memoryIndex; - - MsgScopeVisNGMemoryTrace(uint32_t memoryIndex) : - m_memoryIndex(memoryIndex) - {} - }; - - // --------------------------------------------- - - /** - * Projection stuff - */ - class Projector - { - public: - Projector(ProjectionType projectionType) : - m_projectionType(projectionType), - m_prevArg(0.0f), - m_cache(0), - m_cacheMaster(true) - {} - - ~Projector() - {} - - ProjectionType getProjectionType() const { return m_projectionType; } - void settProjectionType(ProjectionType projectionType) { m_projectionType = projectionType; } - void setCache(Real *cache) { m_cache = cache; } - void setCacheMaster(bool cacheMaster) { m_cacheMaster = cacheMaster; } - - Real run(const Sample& s) - { - Real v; - - if ((m_cache) && !m_cacheMaster) { - return m_cache[(int) m_projectionType]; - } - else - { - switch (m_projectionType) - { - case ProjectionImag: - v = s.m_imag / SDR_RX_SCALEF; - break; - case ProjectionMagLin: - { - Real re = s.m_real / SDR_RX_SCALEF; - Real im = s.m_imag / SDR_RX_SCALEF; - Real magsq = re*re + im*im; - v = std::sqrt(magsq); - } - break; - case ProjectionMagDB: - { - Real re = s.m_real / SDR_RX_SCALEF; - Real im = s.m_imag / SDR_RX_SCALEF; - Real magsq = re*re + im*im; - v = log10f(magsq) * 10.0f; - } - break; - case ProjectionPhase: - v = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI; - break; - case ProjectionDPhase: - { - Real curArg = std::atan2((float) s.m_imag, (float) s.m_real); - Real dPhi = (curArg - m_prevArg) / M_PI; - m_prevArg = curArg; - - if (dPhi < -1.0f) { - dPhi += 2.0f; - } else if (dPhi > 1.0f) { - dPhi -= 2.0f; - } - - v = dPhi; - } - break; - case ProjectionReal: - default: - v = s.m_real / SDR_RX_SCALEF; - break; - } - - if (m_cache) { - m_cache[(int) m_projectionType] = v; - } - - return v; - } - } - - private: - ProjectionType m_projectionType; - Real m_prevArg; - Real *m_cache; - bool m_cacheMaster; - }; - - /** - * Trigger stuff - */ - enum TriggerState - { - TriggerUntriggered, //!< Trigger is not kicked off yet (or trigger list is empty) - TriggerTriggered, //!< Trigger has been kicked off - TriggerDelay, //!< Trigger conditions have been kicked off but it is waiting for delay before final kick off - TriggerNewConfig, //!< Special condition when a new configuration has been received - }; - - struct TriggerCondition - { - public: - Projector m_projector; - TriggerData m_triggerData; //!< Trigger data - bool m_prevCondition; //!< Condition (above threshold) at previous sample - uint32_t m_triggerDelayCount; //!< Counter of samples for delay - uint32_t m_triggerCounter; //!< Counter of trigger occurences - - TriggerCondition(const TriggerData& triggerData) : - m_projector(ProjectionReal), - m_triggerData(triggerData), - m_prevCondition(false), - m_triggerDelayCount(0), - m_triggerCounter(0) - { - } - - ~TriggerCondition() - { - } - - void initProjector() - { - m_projector.settProjectionType(m_triggerData.m_projectionType); - } - - void releaseProjector() - { - } - - void setData(const TriggerData& triggerData) - { - m_triggerData = triggerData; - - if (m_projector.getProjectionType() != m_triggerData.m_projectionType) - { - m_projector.settProjectionType(m_triggerData.m_projectionType); - } - - m_prevCondition = false; - m_triggerDelayCount = 0; - m_triggerCounter = 0; - } - - void operator=(const TriggerCondition& other) - { - setData(other.m_triggerData); - } - }; - - /** - * Complex trace stuff - */ - typedef DoubleBufferSimple TraceBuffer; - - struct TraceBackBuffer - { - TraceBuffer m_traceBuffer; - SampleVector::iterator m_endPoint; - - TraceBackBuffer() - { - m_endPoint = m_traceBuffer.getCurrent(); - } - - void resize(uint32_t size) - { - m_traceBuffer.resize(size); - } - - void reset() - { - m_traceBuffer.reset(); - } - - void write(const SampleVector::const_iterator begin, const SampleVector::const_iterator end) - { - m_traceBuffer.write(begin, end); - } - - unsigned int absoluteFill() const { - return m_traceBuffer.absoluteFill(); - } - - SampleVector::iterator getCurrent() { return m_traceBuffer.getCurrent(); } - }; - - typedef std::vector TBBVector; - - struct TraceBackDiscreteMemory - { - std::vector m_traceBackBuffers; - std::vector m_sourceFill; - uint32_t m_nbSources; - uint32_t m_memSize; - uint32_t m_currentMemIndex; - uint32_t m_traceSize; - int m_preTrigCount; - - /** - * Allocate with maximum number of traces - */ - TraceBackDiscreteMemory() : - m_nbSources(1), - m_memSize(m_nbTraceMemories), - m_currentMemIndex(0), - m_traceSize(m_traceChunkSize), - m_preTrigCount(0) - { - m_traceBackBuffers.resize(m_nbSources); - m_sourceFill.resize(m_nbSources); - resizeBuffers(m_traceSize); - } - - /** - * Resize all trace buffers in memory - */ - void resizeBuffers(uint32_t size) - { - m_traceSize = size; - std::vector::iterator itFill = m_sourceFill.begin(); - - for (std::vector::iterator itTBB = m_traceBackBuffers.begin(); itTBB != m_traceBackBuffers.end(); ++itTBB) - { - for (std::vector::iterator it = itTBB->begin(); it != itTBB->end(); ++it) - { - it->resize(4*m_traceSize); - } - - *itFill = 0; - ++itFill; - } - } - - /** - * Set the number of sources - */ - void setNbSources(uint32_t nbSources) - { - if ((nbSources < 1) || (nbSources > m_maxNbTraceSources)) { - return; - } - - if (nbSources != m_nbSources) - { - m_traceBackBuffers.resize(nbSources); - resizeBuffers(m_traceSize); - m_nbSources = nbSources; - } - } - - /** - * Feed current buffer at source index with sample - */ - void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, uint32_t sourceIndex) - { - m_traceBackBuffers[sourceIndex][m_currentMemIndex].write(begin, end); - m_sourceFill[sourceIndex] += end - begin; - } - - /** - * Return the minimum number of samples in buffer - */ - uint32_t minFill() const - { - return *std::min_element(m_sourceFill.begin(), m_sourceFill.end()); - } - - /** - * Consume a number of samples from the buffers (that is reduce the fill count) - */ - void consume(int nbSamples) - { - for (std::vector::iterator itFill = m_sourceFill.begin(); itFill != m_sourceFill.end(); ++itFill) - { - (*itFill) -= nbSamples; - } - } - - SampleVector::iterator getBeginIterator(int sourceIndex) - { - return m_traceBackBuffers[sourceIndex][m_currentMemIndex].getCurrent() - m_sourceFill[sourceIndex]; - } - - uint32_t getNbSources() const - { - return m_nbSources; - } - - void markEnd(int sourceIndex, SampleVector::iterator& end) - { - m_traceBackBuffers[sourceIndex][m_currentMemIndex].m_endPoint = end; - } - - void store() - { - uint32_t nextMemIndex = m_currentMemIndex < (m_memSize-1) ? m_currentMemIndex+1 : 0; - - for (std::vector::iterator itTBB = m_traceBackBuffers.begin(); itTBB != m_traceBackBuffers.end(); ++itTBB) - { - (*itTBB)[nextMemIndex].reset(); - (*itTBB)[nextMemIndex].write( - (*itTBB)[m_currentMemIndex].m_endPoint - m_traceSize, - (*itTBB)[m_currentMemIndex].m_endPoint); - } - - m_currentMemIndex = nextMemIndex; - } - }; - - /** - * Displayable trace stuff - */ - struct TraceControl - { - Projector m_projector; //!< Projector transform from complex trace to real (displayable) trace - uint32_t m_traceCount[2]; //!< Count of samples processed (double buffered) - Real m_maxPow; //!< Maximum power over the current trace for MagDB overlay display - Real m_sumPow; //!< Cumulative power over the current trace for MagDB overlay display - int m_nbPow; //!< Number of power samples over the current trace for MagDB overlay display - - TraceControl() : m_projector(ProjectionReal) - { - reset(); - } - - ~TraceControl() - { - } - - void initProjector(ProjectionType projectionType) - { - m_projector.settProjectionType(projectionType); - } - - void releaseProjector() - { - } - - void reset() - { - m_traceCount[0] = 0; - m_traceCount[1] = 0; - m_maxPow = 0.0f; - m_sumPow = 0.0f; - m_nbPow = 0; - } - }; - - struct Traces - { - std::vector m_tracesControl; //!< Corresponding traces control data - std::vector m_tracesData; //!< Corresponding traces data - std::vector m_traces[2]; //!< Double buffer of traces processed by glScope - int m_traceSize; //!< Current size of a trace in buffer - int m_maxTraceSize; //!< Maximum Size of a trace in buffer - bool evenOddIndex; //!< Even (true) or odd (false) index - - Traces() : - m_traceSize(0), - m_maxTraceSize(0), - evenOddIndex(true), - m_x0(0), - m_x1(0) - { - } - - ~Traces() - { - if (m_x0) delete[] m_x0; - if (m_x1) delete[] m_x1; - m_maxTraceSize = 0; - } - - bool isVerticalDisplayChange(const TraceData& traceData, uint32_t traceIndex) - { - return (m_tracesData[traceIndex].m_projectionType != traceData.m_projectionType) - || (m_tracesData[traceIndex].m_amp != traceData.m_amp) - || (m_tracesData[traceIndex].m_ofs != traceData.m_ofs - || (m_tracesData[traceIndex].m_traceColor != traceData.m_traceColor)); - } - - void addTrace(const TraceData& traceData, int traceSize) - { - if (m_traces[0].size() < m_maxNbTraces) - { - m_traces[0].push_back(0); - m_traces[1].push_back(0); - m_tracesData.push_back(traceData); - m_tracesControl.push_back(TraceControl()); - m_tracesControl.back().initProjector(traceData.m_projectionType); - - resize(traceSize); - } - } - - void changeTrace(const TraceData& traceData, uint32_t traceIndex) - { - if (traceIndex < m_tracesControl.size()) { - m_tracesControl[traceIndex].releaseProjector(); - m_tracesControl[traceIndex].initProjector(traceData.m_projectionType); - m_tracesData[traceIndex] = traceData; - } - } - - void removeTrace(uint32_t traceIndex) - { - if (traceIndex < m_tracesControl.size()) - { - m_traces[0].erase(m_traces[0].begin() + traceIndex); - m_traces[1].erase(m_traces[1].begin() + traceIndex); - m_tracesControl[traceIndex].releaseProjector(); - m_tracesControl.erase(m_tracesControl.begin() + traceIndex); - m_tracesData.erase(m_tracesData.begin() + traceIndex); - - resize(m_traceSize); // reallocate pointers - } - } - - void moveTrace(uint32_t traceIndex, bool upElseDown) - { - if ((!upElseDown) && (traceIndex == 0)) { - return; - } - - int nextControlIndex = (traceIndex + (upElseDown ? 1 : -1)) % (m_tracesControl.size()); - int nextDataIndex = (traceIndex + (upElseDown ? 1 : -1)) % (m_tracesData.size()); // should be the same - - m_tracesControl[traceIndex].releaseProjector(); - m_tracesControl[nextControlIndex].releaseProjector(); - - TraceControl nextControl = m_tracesControl[nextControlIndex]; - m_tracesControl[nextControlIndex] = m_tracesControl[traceIndex]; - m_tracesControl[traceIndex] = nextControl; - - TraceData nextData = m_tracesData[nextDataIndex]; - m_tracesData[nextDataIndex] = m_tracesData[traceIndex]; - m_tracesData[traceIndex] = nextData; - - m_tracesControl[traceIndex].initProjector(m_tracesData[traceIndex].m_projectionType); - m_tracesControl[nextControlIndex].initProjector(m_tracesData[nextDataIndex].m_projectionType); - } - - void resize(int traceSize) - { - m_traceSize = traceSize; - - if (m_traceSize > m_maxTraceSize) - { - delete[] m_x0; - delete[] m_x1; - m_x0 = new float[2*m_traceSize*m_maxNbTraces]; - m_x1 = new float[2*m_traceSize*m_maxNbTraces]; - - m_maxTraceSize = m_traceSize; - } - - std::fill_n(m_x0, 2*m_traceSize*m_traces[0].size(), 0.0f); - std::fill_n(m_x1, 2*m_traceSize*m_traces[0].size(), 0.0f); - - for (unsigned int i = 0; i < m_traces[0].size(); i++) - { - (m_traces[0])[i] = &m_x0[2*m_traceSize*i]; - (m_traces[1])[i] = &m_x1[2*m_traceSize*i]; - } - } - - uint32_t currentBufferIndex() const { return evenOddIndex? 0 : 1; } - uint32_t size() const { return m_tracesControl.size(); } - - void switchBuffer() - { - evenOddIndex = !evenOddIndex; - - for (std::vector::iterator it = m_tracesControl.begin(); it != m_tracesControl.end(); ++it) - { - it->m_traceCount[currentBufferIndex()] = 0; - } - } - - private: - float *m_x0; - float *m_x1; - }; - - class TriggerComparator - { - public: - TriggerComparator() : m_level(0), m_reset(true) - { - computeLevels(); - } - - bool triggered(const Sample& s, TriggerCondition& triggerCondition) - { - if (triggerCondition.m_triggerData.m_triggerLevel != m_level) - { - m_level = triggerCondition.m_triggerData.m_triggerLevel; - computeLevels(); - } - - bool condition, trigger; - - if (triggerCondition.m_projector.getProjectionType() == ProjectionMagDB) { - condition = triggerCondition.m_projector.run(s) > m_levelPowerDB; - } else if (triggerCondition.m_projector.getProjectionType() == ProjectionMagLin) { - condition = triggerCondition.m_projector.run(s) > m_levelPowerLin; - } else { - condition = triggerCondition.m_projector.run(s) > m_level; - } - - if (m_reset) - { - triggerCondition.m_prevCondition = condition; - m_reset = false; - return false; - } - - if (triggerCondition.m_triggerData.m_triggerBothEdges) { - trigger = triggerCondition.m_prevCondition ? !condition : condition; // This is a XOR between bools - } else if (triggerCondition.m_triggerData.m_triggerPositiveEdge) { - trigger = !triggerCondition.m_prevCondition && condition; - } else { - trigger = triggerCondition.m_prevCondition && !condition; - } - -// if (trigger) { -// qDebug("ScopeVisNG::triggered: %s/%s %f/%f", -// triggerCondition.m_prevCondition ? "T" : "F", -// condition ? "T" : "F", -// triggerCondition.m_projector->run(s), -// triggerCondition.m_triggerData.m_triggerLevel); -// } - - triggerCondition.m_prevCondition = condition; - return trigger; - } - - void reset() - { - m_reset = true; - } - - private: - void computeLevels() - { - m_levelPowerLin = m_level + 1.0f; - m_levelPowerDB = (100.0f * (m_level - 1.0f)); - } - - Real m_level; - Real m_levelPowerDB; - Real m_levelPowerLin; - bool m_reset; - }; - - typedef std::vector TBMemoriesBegins; - - GLScopeMulti* m_glScope; - uint32_t m_preTriggerDelay; //!< Pre-trigger delay in number of samples - std::vector m_triggerConditions; //!< Chain of triggers - uint32_t m_currentTriggerIndex; //!< Index of current index in the chain - uint32_t m_focusedTriggerIndex; //!< Index of the trigger that has focus - TriggerState m_triggerState; //!< Current trigger state - Traces m_traces; //!< Displayable traces - int m_focusedTraceIndex; //!< Index of the trace that has focus - uint32_t m_traceSize; //!< Size of traces in number of samples - int m_nbSamples; //!< Number of samples yet to process in one complex trace - uint32_t m_timeBase; //!< Trace display time divisor - uint32_t m_timeOfsProMill; //!< Start trace shift in 1/1000 trace size - bool m_traceStart; //!< Trace is at start point - bool m_postTrigBuffering; //!< Buffering after trigger match to get enough samples for the display traces - int m_traceFill; //!< Count of samples accumulated into trace - int m_zTraceIndex; //!< Index of the trace used for Z input (luminance or false colors) - SampleVector::const_iterator m_triggerPoint; //!< Trigger start location in the samples vector - int m_sampleRate; - TraceBackDiscreteMemory m_traceDiscreteMemory; //!< Complex trace memories for triggered states. One trace history per source - bool m_freeRun; //!< True if free running (trigger globally disabled) - int m_maxTraceDelay; //!< Maximum trace delay - TriggerComparator m_triggerComparator; //!< Compares sample level to trigger level - QMutex m_mutex; - Real m_projectorCache[(int) nbProjectionTypes]; - bool m_triggerOneShot; //!< True when one shot mode is active - bool m_triggerWaitForReset; //!< In one shot mode suspended until reset by UI - uint32_t m_currentTraceMemoryIndex; //!< The current index of trace in memory (0: current) - uint32_t m_nbSources; //!< Current number of sample sources - TBMemoriesBegins m_tbMemoriesBegins; //!< Current begin iterators - - /** - * Moves on to the next trigger if any or increments trigger count if in repeat mode - * - If not final it returns true - * - If final i.e. signal is actually triggerd it returns false - */ - bool nextTrigger(); //!< Returns true if not final - - /** - * Process all input sources when ready. Assumes all memoruy traces are fed with enough samples - */ - void processSources(); - - /** - * process a trace in memory at current trace index in memory - */ - void processMemorySources(); - - /** - * Process traces from complex trace memory buffer. - * - if finished it returns the number of unprocessed samples left in the buffer - * - if not finished it returns -1 - */ - void processTraces(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool traceBack, uint32_t sourceIndex); - - /** - * Get maximum trace delay - */ - void updateMaxTraceDelay(); - - /** - * Initialize trace buffers - */ - void initTraceBuffers(); - - /** - * Calculate trigger levels on display - * - every time a trigger condition focus changes TBD - * - every time the focused trigger condition changes its projection type or level - * - every time a trace data changes: projection type, amp, offset - * - every time a trace data is added or removed - */ - void computeDisplayTriggerLevels(); - - /** - * Update glScope display - * - Live trace: call glScipe update method - * - Trace in memory: call process memory trace - */ - void updateGLScopeDisplay(); - - void lookForTrigger(); -}; - - - - -#endif /* SDRBASE_DSP_SCOPEVISMULTI_H_ */ diff --git a/sdrgui/gui/basicchannelsettingsdialog.cpp b/sdrgui/gui/basicchannelsettingsdialog.cpp index 76b21c982..084c4ee62 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.cpp +++ b/sdrgui/gui/basicchannelsettingsdialog.cpp @@ -15,6 +15,11 @@ BasicChannelSettingsDialog::BasicChannelSettingsDialog(ChannelMarker* marker, QW ui->title->setText(m_channelMarker->getTitle()); m_color = m_channelMarker->getColor(); ui->fScaleDisplayType->setCurrentIndex((int) m_channelMarker->getFrequencyScaleDisplayType()); + setUseReverseAPI(false); + setReverseAPIAddress("127.0.0.1"); + setReverseAPIPort(8888); + setReverseAPIDeviceIndex(0); + setReverseAPIChannelIndex(0); paintColor(); } @@ -23,6 +28,41 @@ BasicChannelSettingsDialog::~BasicChannelSettingsDialog() delete ui; } +void BasicChannelSettingsDialog::setUseReverseAPI(bool useReverseAPI) +{ + m_useReverseAPI = useReverseAPI; + ui->reverseAPI->setChecked(m_useReverseAPI); +} + +void BasicChannelSettingsDialog::setReverseAPIAddress(const QString& address) +{ + m_reverseAPIAddress = address; + ui->reverseAPIAddress->setText(m_reverseAPIAddress); +} + +void BasicChannelSettingsDialog::setReverseAPIPort(uint16_t port) +{ + if (port < 1024) { + return; + } else { + m_reverseAPIPort = port; + } + + ui->reverseAPIPort->setText(tr("%1").arg(m_reverseAPIPort)); +} + +void BasicChannelSettingsDialog::setReverseAPIDeviceIndex(uint16_t deviceIndex) +{ + m_reverseAPIDeviceIndex = deviceIndex > 99 ? 99 : deviceIndex; + ui->reverseAPIDeviceIndex->setText(tr("%1").arg(m_reverseAPIDeviceIndex)); +} + +void BasicChannelSettingsDialog::setReverseAPIChannelIndex(uint16_t channelIndex) +{ + m_reverseAPIChannelIndex = channelIndex > 99 ? 99 : channelIndex; + ui->reverseAPIDeviceIndex->setText(tr("%1").arg(m_reverseAPIChannelIndex)); +} + void BasicChannelSettingsDialog::paintColor() { QPixmap pm(24, 24); @@ -38,12 +78,60 @@ void BasicChannelSettingsDialog::on_colorBtn_clicked() { QColor c = m_color; c = QColorDialog::getColor(c, this, tr("Select Color for Channel"), QColorDialog::DontUseNativeDialog); - if(c.isValid()) { + + if (c.isValid()) + { m_color = c; paintColor(); } } +void BasicChannelSettingsDialog::on_reverseAPI_toggled(bool checked) +{ + m_useReverseAPI = checked; +} + +void BasicChannelSettingsDialog::on_reverseAPIAddress_editingFinished() +{ + m_reverseAPIAddress = ui->reverseAPIAddress->text(); +} + +void BasicChannelSettingsDialog::on_reverseAPIPort_editingFinished() +{ + bool dataOk; + int reverseAPIPort = ui->reverseAPIPort->text().toInt(&dataOk); + + if((!dataOk) || (reverseAPIPort < 1024) || (reverseAPIPort > 65535)) { + return; + } else { + m_reverseAPIPort = reverseAPIPort; + } +} + +void BasicChannelSettingsDialog::on_reverseAPIDeviceIndex_editingFinished() +{ + bool dataOk; + int reverseAPIDeviceIndex = ui->reverseAPIDeviceIndex->text().toInt(&dataOk); + + if ((!dataOk) || (reverseAPIDeviceIndex < 0)) { + return; + } else { + m_reverseAPIDeviceIndex = reverseAPIDeviceIndex; + } +} + +void BasicChannelSettingsDialog::on_reverseAPIChannelIndex_editingFinished() +{ + bool dataOk; + int reverseAPIChannelIndex = ui->reverseAPIChannelIndex->text().toInt(&dataOk); + + if ((!dataOk) || (reverseAPIChannelIndex < 0)) { + return; + } else { + m_reverseAPIChannelIndex = reverseAPIChannelIndex; + } +} + void BasicChannelSettingsDialog::accept() { m_channelMarker->blockSignals(true); diff --git a/sdrgui/gui/basicchannelsettingsdialog.h b/sdrgui/gui/basicchannelsettingsdialog.h index 4276a4c23..ffe8c7d36 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.h +++ b/sdrgui/gui/basicchannelsettingsdialog.h @@ -3,7 +3,7 @@ #include -#include "export.h" +#include "../../exports/export.h" namespace Ui { class BasicChannelSettingsDialog; @@ -19,15 +19,35 @@ public: explicit BasicChannelSettingsDialog(ChannelMarker* marker, QWidget *parent = 0); ~BasicChannelSettingsDialog(); bool hasChanged() const { return m_hasChanged; } + bool useReverseAPI() const { return m_useReverseAPI; } + const QString& getReverseAPIAddress() const { return m_reverseAPIAddress; } + uint16_t getReverseAPIPort() const { return m_reverseAPIPort; } + uint16_t getReverseAPIDeviceIndex() const { return m_reverseAPIDeviceIndex; } + uint16_t getReverseAPIChannelIndex() const { return m_reverseAPIChannelIndex; } + void setUseReverseAPI(bool useReverseAPI); + void setReverseAPIAddress(const QString& address); + void setReverseAPIPort(uint16_t port); + void setReverseAPIDeviceIndex(uint16_t deviceIndex); + void setReverseAPIChannelIndex(uint16_t channelIndex); private slots: void on_colorBtn_clicked(); + void on_reverseAPI_toggled(bool checked); + void on_reverseAPIAddress_editingFinished(); + void on_reverseAPIPort_editingFinished(); + void on_reverseAPIDeviceIndex_editingFinished(); + void on_reverseAPIChannelIndex_editingFinished(); void accept(); private: Ui::BasicChannelSettingsDialog *ui; ChannelMarker* m_channelMarker; QColor m_color; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; bool m_hasChanged; void paintColor(); diff --git a/sdrgui/gui/basicchannelsettingsdialog.ui b/sdrgui/gui/basicchannelsettingsdialog.ui index 25af72d52..51d5e6116 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.ui +++ b/sdrgui/gui/basicchannelsettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 131 + 135 @@ -112,6 +112,154 @@ + + + + + + Sychronize with reverse API + + + reverse API + + + + + + + + 120 + 0 + + + + Reverse API address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + Reverse API port + + + 00000 + + + 8888 + + + + + + + D + + + + + + + + 22 + 0 + + + + + 22 + 16777215 + + + + Reverse API destination device index + + + 00 + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + C + + + + + + + + 22 + 0 + + + + + 22 + 16777215 + + + + Reverse API destination channel index + + + 00 + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/sdrgui/gui/basicdevicesettingsdialog.cpp b/sdrgui/gui/basicdevicesettingsdialog.cpp new file mode 100644 index 000000000..8b2fa6b52 --- /dev/null +++ b/sdrgui/gui/basicdevicesettingsdialog.cpp @@ -0,0 +1,88 @@ +#include "basicdevicesettingsdialog.h" +#include "ui_basicdevicesettingsdialog.h" + +BasicDeviceSettingsDialog::BasicDeviceSettingsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::BasicDeviceSettingsDialog), + m_hasChanged(false) +{ + ui->setupUi(this); + setUseReverseAPI(false); + setReverseAPIAddress("127.0.0.1"); + setReverseAPIPort(8888); + setReverseAPIDeviceIndex(0); +} + +BasicDeviceSettingsDialog::~BasicDeviceSettingsDialog() +{ + delete ui; +} + +void BasicDeviceSettingsDialog::setUseReverseAPI(bool useReverseAPI) +{ + m_useReverseAPI = useReverseAPI; + ui->reverseAPI->setChecked(m_useReverseAPI); +} + +void BasicDeviceSettingsDialog::setReverseAPIAddress(const QString& address) +{ + m_reverseAPIAddress = address; + ui->reverseAPIAddress->setText(m_reverseAPIAddress); +} + +void BasicDeviceSettingsDialog::setReverseAPIPort(uint16_t port) +{ + if (port < 1024) { + return; + } else { + m_reverseAPIPort = port; + } + + ui->reverseAPIPort->setText(tr("%1").arg(m_reverseAPIPort)); +} + +void BasicDeviceSettingsDialog::setReverseAPIDeviceIndex(uint16_t deviceIndex) +{ + m_reverseAPIDeviceIndex = deviceIndex > 99 ? 99 : deviceIndex; + ui->reverseAPIDeviceIndex->setText(tr("%1").arg(m_reverseAPIDeviceIndex)); +} + +void BasicDeviceSettingsDialog::on_reverseAPI_toggled(bool checked) +{ + m_useReverseAPI = checked; +} + +void BasicDeviceSettingsDialog::on_reverseAPIAddress_editingFinished() +{ + m_reverseAPIAddress = ui->reverseAPIAddress->text(); +} + +void BasicDeviceSettingsDialog::on_reverseAPIPort_editingFinished() +{ + bool dataOk; + int reverseAPIPort = ui->reverseAPIPort->text().toInt(&dataOk); + + if((!dataOk) || (reverseAPIPort < 1024) || (reverseAPIPort > 65535)) { + return; + } else { + m_reverseAPIPort = reverseAPIPort; + } +} + +void BasicDeviceSettingsDialog::on_reverseAPIDeviceIndex_editingFinished() +{ + bool dataOk; + int reverseAPIDeviceIndex = ui->reverseAPIDeviceIndex->text().toInt(&dataOk); + + if ((!dataOk) || (reverseAPIDeviceIndex < 0)) { + return; + } else { + m_reverseAPIDeviceIndex = reverseAPIDeviceIndex; + } +} + +void BasicDeviceSettingsDialog::accept() +{ + m_hasChanged = true; + QDialog::accept(); +} diff --git a/sdrgui/gui/basicdevicesettingsdialog.h b/sdrgui/gui/basicdevicesettingsdialog.h new file mode 100644 index 000000000..1bd885a83 --- /dev/null +++ b/sdrgui/gui/basicdevicesettingsdialog.h @@ -0,0 +1,45 @@ +#ifndef BASICDEVICESETTINGSDIALOG_H +#define BASICDEVICESETTINGSDIALOG_H + +#include + +#include "../../exports/export.h" + +namespace Ui { + class BasicDeviceSettingsDialog; +} + +class SDRGUI_API BasicDeviceSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit BasicDeviceSettingsDialog(QWidget *parent = 0); + ~BasicDeviceSettingsDialog(); + bool hasChanged() const { return m_hasChanged; } + bool useReverseAPI() const { return m_useReverseAPI; } + const QString& getReverseAPIAddress() const { return m_reverseAPIAddress; } + uint16_t getReverseAPIPort() const { return m_reverseAPIPort; } + uint16_t getReverseAPIDeviceIndex() const { return m_reverseAPIDeviceIndex; } + void setUseReverseAPI(bool useReverseAPI); + void setReverseAPIAddress(const QString& address); + void setReverseAPIPort(uint16_t port); + void setReverseAPIDeviceIndex(uint16_t deviceIndex); + +private slots: + void on_reverseAPI_toggled(bool checked); + void on_reverseAPIAddress_editingFinished(); + void on_reverseAPIPort_editingFinished(); + void on_reverseAPIDeviceIndex_editingFinished(); + void accept(); + +private: + Ui::BasicDeviceSettingsDialog *ui; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + bool m_hasChanged; +}; + +#endif // BASICDEVICESETTINGSDIALOG_H diff --git a/sdrgui/gui/basicdevicesettingsdialog.ui b/sdrgui/gui/basicdevicesettingsdialog.ui new file mode 100644 index 000000000..a4c5736bd --- /dev/null +++ b/sdrgui/gui/basicdevicesettingsdialog.ui @@ -0,0 +1,183 @@ + + + BasicDeviceSettingsDialog + + + + 0 + 0 + 394 + 77 + + + + + Liberation Sans + 9 + + + + Basic device settings + + + + + + + + Sychronize with reverse API + + + reverse API + + + + + + + + 120 + 0 + + + + Reverse API address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + Reverse API port + + + 00000 + + + 8888 + + + + + + + D + + + + + + + + 22 + 0 + + + + + 22 + 16777215 + + + + Reverse API destination device index + + + 00 + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + BasicDeviceSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + BasicDeviceSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sdrgui/gui/crightclickenabler.h b/sdrgui/gui/crightclickenabler.h index f7843b34c..85bd6f69c 100644 --- a/sdrgui/gui/crightclickenabler.h +++ b/sdrgui/gui/crightclickenabler.h @@ -29,20 +29,22 @@ public: CRightClickEnabler(QAbstractButton *button); signals: - void rightClick(); + void rightClick(const QPoint&); protected: inline bool eventFilter(QObject *watched, QEvent *event) override { (void) watched; + if (event->type() == QEvent::MouseButtonPress) { - auto mouseEvent = (QMouseEvent*)event; + auto mouseEvent = (QMouseEvent*) event; + if (mouseEvent->button() == Qt::RightButton) { - //_button->click(); - emit rightClick(); + emit rightClick(mouseEvent->globalPos()); } } + return false; } diff --git a/sdrgui/gui/cwkeyergui.cpp b/sdrgui/gui/cwkeyergui.cpp index 72abccc90..db5f7be0b 100644 --- a/sdrgui/gui/cwkeyergui.cpp +++ b/sdrgui/gui/cwkeyergui.cpp @@ -22,6 +22,7 @@ #include "ui_cwkeyergui.h" #include "dsp/cwkeyer.h" #include "util/simpleserializer.h" +#include "util/messagequeue.h" CWKeyerGUI::CWKeyerGUI(QWidget* parent) : QWidget(parent), @@ -44,6 +45,7 @@ void CWKeyerGUI::setBuddies(MessageQueue* messageQueue, CWKeyer* cwKeyer) m_messageQueue = messageQueue; m_cwKeyer = cwKeyer; applySettings(); + sendSettings(); } void CWKeyerGUI::resetToDefaults() @@ -83,6 +85,8 @@ bool CWKeyerGUI::deserialize(const QByteArray& data) ui->cwSpeed->setValue(aValue); applySettings(); + sendSettings(); + return true; } else @@ -102,13 +106,22 @@ void CWKeyerGUI::on_cwTextClear_clicked(bool checked) void CWKeyerGUI::on_cwTextEdit_editingFinished() { - if (m_doApplySettings) { m_cwKeyer->setText(ui->cwTextEdit->text()); } + if (m_doApplySettings) + { + m_cwKeyer->setText(ui->cwTextEdit->text()); + sendSettings(); + } } void CWKeyerGUI::on_cwSpeed_valueChanged(int value) { ui->cwSpeedText->setText(QString("%1").arg(value)); - if (m_doApplySettings) { m_cwKeyer->setWPM(value); } + + if (m_doApplySettings) + { + m_cwKeyer->setWPM(value); + sendSettings(); + } } void CWKeyerGUI::on_playDots_toggled(bool checked) @@ -117,7 +130,11 @@ void CWKeyerGUI::on_playDots_toggled(bool checked) ui->playDashes->setEnabled(!checked); ui->playText->setEnabled(!checked); - if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDots : CWKeyerSettings::CWNone); } + if (m_doApplySettings) + { + m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDots : CWKeyerSettings::CWNone); + sendSettings(); + } } void CWKeyerGUI::on_playDashes_toggled(bool checked) @@ -126,7 +143,11 @@ void CWKeyerGUI::on_playDashes_toggled(bool checked) //ui->playDashes->setEnabled(!checked); ui->playText->setEnabled(!checked); - if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDashes : CWKeyerSettings::CWNone); } + if (m_doApplySettings) + { + m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDashes : CWKeyerSettings::CWNone); + sendSettings(); + } } void CWKeyerGUI::on_playText_toggled(bool checked) @@ -135,7 +156,11 @@ void CWKeyerGUI::on_playText_toggled(bool checked) ui->playDashes->setEnabled(!checked); //ui->playText->setEnabled(!checked); - if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWText : CWKeyerSettings::CWNone); } + if (m_doApplySettings) + { + m_cwKeyer->setMode(checked ? CWKeyerSettings::CWText : CWKeyerSettings::CWNone); + sendSettings(); + } if (checked) { ui->playStop->setChecked(true); @@ -146,17 +171,18 @@ void CWKeyerGUI::on_playText_toggled(bool checked) void CWKeyerGUI::on_playLoopCW_toggled(bool checked) { - if (m_doApplySettings) { m_cwKeyer->setLoop(checked); } + if (m_doApplySettings) + { + m_cwKeyer->setLoop(checked); + sendSettings(); + } } void CWKeyerGUI::on_playStop_toggled(bool checked) { - if (checked) - { + if (checked) { m_cwKeyer->resetText(); - } - else - { + } else { m_cwKeyer->stopText(); } } @@ -200,3 +226,12 @@ void CWKeyerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; } + +void CWKeyerGUI::sendSettings() +{ + if (m_cwKeyer && m_messageQueue) + { + CWKeyer::MsgConfigureCWKeyer *msg = CWKeyer::MsgConfigureCWKeyer::create(m_cwKeyer->getSettings(), false); + m_messageQueue->push(msg); + } +} diff --git a/sdrgui/gui/cwkeyergui.h b/sdrgui/gui/cwkeyergui.h index a9d273c5b..f1f840159 100644 --- a/sdrgui/gui/cwkeyergui.h +++ b/sdrgui/gui/cwkeyergui.h @@ -54,6 +54,7 @@ private: bool m_doApplySettings; void applySettings(); + void sendSettings(); void blockApplySettings(bool block); private slots: diff --git a/sdrgui/gui/glscopemulti.cpp b/sdrgui/gui/glscopemulti.cpp deleted file mode 100644 index d3d9549b4..000000000 --- a/sdrgui/gui/glscopemulti.cpp +++ /dev/null @@ -1,1976 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glscopemulti.h" - -GLScopeMulti::GLScopeMulti(QWidget* parent) : - QGLWidget(parent), - m_tracesData(0), - m_traces(0), - m_bufferIndex(0), - m_displayMode(DisplayX), - m_dataChanged(false), - m_configChanged(false), - m_sampleRate(0), - m_timeOfsProMill(0), - m_triggerPre(0), - m_traceSize(0), - m_timeBase(1), - m_timeOffset(0), - m_focusedTraceIndex(0), - m_displayGridIntensity(10), - m_displayTraceIntensity(50) -{ - setAttribute(Qt::WA_OpaquePaintEvent); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); - m_timer.start(50); - - m_y1Scale.setFont(font()); - m_y1Scale.setOrientation(Qt::Vertical); - m_y2Scale.setFont(font()); - m_y2Scale.setOrientation(Qt::Vertical); - m_x1Scale.setFont(font()); - m_x1Scale.setOrientation(Qt::Horizontal); - m_x2Scale.setFont(font()); - m_x2Scale.setOrientation(Qt::Horizontal); - - m_channelOverlayFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); - m_channelOverlayFont.setBold(true); - m_channelOverlayFont.setPointSize(font().pointSize()+1); - - //m_traceCounter = 0; -} - -GLScopeMulti::~GLScopeMulti() -{ - cleanup(); -} - -void GLScopeMulti::setDisplayGridIntensity(int intensity) -{ - m_displayGridIntensity = intensity; - if (m_displayGridIntensity > 100) { - m_displayGridIntensity = 100; - } else if (m_displayGridIntensity < 0) { - m_displayGridIntensity = 0; - } - update(); -} - -void GLScopeMulti::setDisplayTraceIntensity(int intensity) -{ - m_displayTraceIntensity = intensity; - if (m_displayTraceIntensity > 100) { - m_displayTraceIntensity = 100; - } else if (m_displayTraceIntensity < 0) { - m_displayTraceIntensity = 0; - } - update(); -} - -void GLScopeMulti::setTraces(std::vector* tracesData, std::vector* traces) -{ - m_tracesData = tracesData; - m_traces = traces; -} - -void GLScopeMulti::newTraces(std::vector* traces) -{ - if (traces->size() > 0) - { - if(!m_mutex.tryLock(2)) - return; - - m_traces = traces; - m_dataChanged = true; - - m_mutex.unlock(); - } -} - -void GLScopeMulti::initializeGL() -{ - QOpenGLContext *glCurrentContext = QOpenGLContext::currentContext(); - - if (glCurrentContext) { - if (QOpenGLContext::currentContext()->isValid()) { - qDebug() << "GLScopeMulti::initializeGL: context:" - << " major: " << (QOpenGLContext::currentContext()->format()).majorVersion() - << " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion() - << " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no"); - } - else { - qDebug() << "GLScopeMulti::initializeGL: current context is invalid"; - } - } else { - qCritical() << "GLScopeMulti::initializeGL: no current context"; - return; - } - - QSurface *surface = glCurrentContext->surface(); - - if (surface == 0) - { - qCritical() << "GLScopeMulti::initializeGL: no surface attached"; - return; - } - else - { - if (surface->surfaceType() != QSurface::OpenGLSurface) - { - qCritical() << "GLScopeMulti::initializeGL: surface is not an OpenGLSurface: " << surface->surfaceType() - << " cannot use an OpenGL context"; - return; - } - else - { - qDebug() << "GLScopeMulti::initializeGL: OpenGL surface:" - << " class: " << (surface->surfaceClass() == QSurface::Window ? "Window" : "Offscreen"); - } - } - - connect(glCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this, &GLScopeMulti::cleanup); // TODO: when migrating to QOpenGLWidget - - QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions(); - glFunctions->initializeOpenGLFunctions(); - - //glDisable(GL_DEPTH_TEST); - m_glShaderSimple.initializeGL(); - m_glShaderLeft1Scale.initializeGL(); - m_glShaderBottom1Scale.initializeGL(); - m_glShaderLeft2Scale.initializeGL(); - m_glShaderBottom2Scale.initializeGL(); - m_glShaderPowerOverlay.initializeGL(); -} - -void GLScopeMulti::resizeGL(int width, int height) -{ - QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions(); - glFunctions->glViewport(0, 0, width, height); - m_configChanged = true; -} - -void GLScopeMulti::paintGL() -{ - if(!m_mutex.tryLock(2)) - return; - - if(m_configChanged) - applyConfig(); - -// qDebug("GLScopeMulti::paintGL: m_traceCounter: %d", m_traceCounter); -// m_traceCounter = 0; - - m_dataChanged = false; - - QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions(); - glFunctions->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glFunctions->glClear(GL_COLOR_BUFFER_BIT); - - if ((m_displayMode == DisplayX) || (m_displayMode == DisplayXYV) || (m_displayMode == DisplayXYH)) // display trace #0 - { - // draw rect around - { - GLfloat q3[] { - 1, 1, - 0, 1, - 0, 0, - 1, 0 - }; - - QVector4D color(1.0f, 1.0f, 1.0f, 0.5f); - m_glShaderSimple.drawContour(m_glScopeMatrix1, color, q3, 4); - } - - // paint grid - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - // Y1 (X trace or trace #0) - { - tickList = &m_y1Scale.getTickList(); - - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickY1.m_array; - int effectiveTicks = 0; - - for (int i= 0; i < tickList->count(); i++) - { - tick = &(*tickList)[i]; - - if (tick->major) - { - if (tick->textSize > 0) - { - float y = 1 - (tick->pos / m_y1Scale.getSize()); - q3[4*effectiveTicks] = 0; - q3[4*effectiveTicks+1] = y; - q3[4*effectiveTicks+2] = 1; - q3[4*effectiveTicks+3] = y; - effectiveTicks++; - } - } - } - - float blue = 1.0f; - QVector4D color(1.0f, 1.0f, blue, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks); - } - - // X1 (time) - { - tickList = &m_x1Scale.getTickList(); - - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickX1.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float x = tick->pos / m_x1Scale.getSize(); - q3[4*effectiveTicks] = x; - q3[4*effectiveTicks+1] = 0; - q3[4*effectiveTicks+2] = x; - q3[4*effectiveTicks+3] = 1; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks); - } - - // paint left #1 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - m_glShaderLeft1Scale.drawSurface(m_glLeft1ScaleMatrix, tex1, vtx1, 4); - } - - // paint bottom #1 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - m_glShaderBottom1Scale.drawSurface(m_glBot1ScaleMatrix, tex1, vtx1, 4); - } - - // paint trace #1 - if (m_traceSize > 0) - { - const float *trace = (*m_traces)[0]; - const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[0]; - - if (traceData.m_viewTrace) - { - int start = (m_timeOfsProMill/1000.0) * m_traceSize; - int end = std::min(start + m_traceSize/m_timeBase, m_traceSize); - - if(end - start < 2) - start--; - - float rectX = m_glScopeRect1.x(); - float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f; - float rectW = m_glScopeRect1.width() * (float)m_timeBase / (float)(m_traceSize - 1); - //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp; - float rectH = -m_glScopeRect1.height() / 2.0f; - - //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f); - QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start); - - // Paint trigger level if any - if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f)) - { - GLfloat q3[] { - 0, traceData.m_triggerDisplayLevel, - 1, traceData.m_triggerDisplayLevel - }; - - float rectX = m_glScopeRect1.x(); - float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f; - float rectW = m_glScopeRect1.width(); - float rectH = -m_glScopeRect1.height() / 2.0f; - - QVector4D color( - m_focusedTriggerData.m_triggerColorR, - m_focusedTriggerData.m_triggerColorG, - m_focusedTriggerData.m_triggerColorB, - 0.4f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawSegments(mat, color, q3, 2); - } // display trigger - - // Paint overlay if any - if ((m_focusedTraceIndex == 0) && (traceData.m_hasTextOverlay)) - { - drawChannelOverlay( - traceData.m_textOverlay, - traceData.m_traceColor, - m_channelOverlayPixmap1, - m_glScopeRect1); - } // display overlay - } // displayable trace - } // trace length > 0 - } // Display X - - if ((m_displayMode == DisplayY) || (m_displayMode == DisplayXYV) || (m_displayMode == DisplayXYH)) // display traces #1..n - { - // draw rect around - { - GLfloat q3[] { - 1, 1, - 0, 1, - 0, 0, - 1, 0 - }; - - QVector4D color(1.0f, 1.0f, 1.0f, 0.5f); - m_glShaderSimple.drawContour(m_glScopeMatrix2, color, q3, 4); - } - - // paint grid - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - // Y2 (Focused Y trace) - { - tickList = &m_y2Scale.getTickList(); - - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickY2.m_array; - int effectiveTicks = 0; - - for (int i= 0; i < tickList->count(); i++) - { - tick = &(*tickList)[i]; - - if (tick->major) - { - if (tick->textSize > 0) - { - float y = 1 - (tick->pos / m_y2Scale.getSize()); - q3[4*effectiveTicks] = 0; - q3[4*effectiveTicks+1] = y; - q3[4*effectiveTicks+2] = 1; - q3[4*effectiveTicks+3] = y; - effectiveTicks++; - } - } - } - - float blue = 1.0f; - QVector4D color(1.0f, 1.0f, blue, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks); - } - - // X2 (time) - { - tickList = &m_x2Scale.getTickList(); - - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickX2.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float x = tick->pos / m_x2Scale.getSize(); - q3[4*effectiveTicks] = x; - q3[4*effectiveTicks+1] = 0; - q3[4*effectiveTicks+2] = x; - q3[4*effectiveTicks+3] = 1; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks); - } - - // paint left #2 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - m_glShaderLeft2Scale.drawSurface(m_glLeft2ScaleMatrix, tex1, vtx1, 4); - } - - // paint bottom #2 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - m_glShaderBottom2Scale.drawSurface(m_glBot2ScaleMatrix, tex1, vtx1, 4); - } - - // paint traces #1..n - if (m_traceSize > 0) - { - int start = (m_timeOfsProMill/1000.0) * m_traceSize; - int end = std::min(start + m_traceSize/m_timeBase, m_traceSize); - - if(end - start < 2) - start--; - - for (unsigned int i = 1; i < m_traces->size(); i++) - { - const float *trace = (*m_traces)[i]; - const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i]; - - if (!traceData.m_viewTrace) { - continue; - } - - float rectX = m_glScopeRect2.x(); - float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f; - float rectW = m_glScopeRect2.width() * (float)m_timeBase / (float)(m_traceSize - 1); - //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp; - float rectH = -m_glScopeRect2.height() / 2.0f; - - //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f); - QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start); - - // Paint trigger level if any - if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f)) - { - GLfloat q3[] { - 0, traceData.m_triggerDisplayLevel, - 1, traceData.m_triggerDisplayLevel - }; - - float rectX = m_glScopeRect2.x(); - float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f; - float rectW = m_glScopeRect2.width(); - float rectH = -m_glScopeRect2.height() / 2.0f; - - QVector4D color( - m_focusedTriggerData.m_triggerColorR, - m_focusedTriggerData.m_triggerColorG, - m_focusedTriggerData.m_triggerColorB, - 0.4f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawSegments(mat, color, q3, 2); - } - - // Paint overlay if any - if ((i == m_focusedTraceIndex) && (traceData.m_hasTextOverlay)) - { - drawChannelOverlay( - traceData.m_textOverlay, - traceData.m_traceColor, - m_channelOverlayPixmap2, - m_glScopeRect2); - } - - } // one trace display - } // trace length > 0 - } // Display Y - - if (m_displayMode == DisplayPol) - { - // paint left display: mixed XY - - // draw rect around - { - GLfloat q3[] { - 1, 1, - 0, 1, - 0, 0, - 1, 0 - }; - - QVector4D color(1.0f, 1.0f, 1.0f, 0.5f); - m_glShaderSimple.drawContour(m_glScopeMatrix1, color, q3, 4); - - } - - // paint grid - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - // Horizontal Y1 - tickList = &m_y1Scale.getTickList(); - { - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickY1.m_array; - int effectiveTicks = 0; - - for (int i= 0; i < tickList->count(); i++) - { - tick = &(*tickList)[i]; - - if (tick->major) - { - if (tick->textSize > 0) - { - float y = 1 - (tick->pos / m_y1Scale.getSize()); - q3[4*effectiveTicks] = 0; - q3[4*effectiveTicks+1] = y; - q3[4*effectiveTicks+2] = 1; - q3[4*effectiveTicks+3] = y; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 0.25f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks); - } - - // Vertical X1 - tickList = &m_x1Scale.getTickList(); - { - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickX1.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float x = tick->pos / m_x1Scale.getSize(); - q3[4*effectiveTicks] = x; - q3[4*effectiveTicks+1] = 0; - q3[4*effectiveTicks+2] = x; - q3[4*effectiveTicks+3] = 1; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks); - } - - // paint left #1 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - m_glShaderLeft1Scale.drawSurface(m_glLeft1ScaleMatrix, tex1, vtx1, 4); - } - - // paint bottom #1 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - m_glShaderBottom1Scale.drawSurface(m_glBot1ScaleMatrix, tex1, vtx1, 4); - } - - // Paint secondary grid - - // Horizontal Y2 - tickList = &m_y2Scale.getTickList(); - { - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickY2.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float y = 1 - (tick->pos / m_y2Scale.getSize()); - q3[4*effectiveTicks] = 0; - q3[4*effectiveTicks+1] = y; - q3[4*effectiveTicks+2] = 1; - q3[4*effectiveTicks+3] = y; - effectiveTicks++; - } - } - } - - QVector4D color(0.25f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks); - } - - // Paint secondary scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - m_glShaderLeft2Scale.drawSurface(m_glRight1ScaleMatrix, tex1, vtx1, 4); - } - - // paint all traces - if (m_traceSize > 0) - { - int start = (m_timeOfsProMill/1000.0) * m_traceSize; - int end = std::min(start + m_traceSize/m_timeBase, m_traceSize); - - if(end - start < 2) - start--; - - for (unsigned int i = 0; i < m_traces->size(); i++) - { - const float *trace = (*m_traces)[i]; - const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i]; - - if (!traceData.m_viewTrace) { - continue; - } - - float rectX = m_glScopeRect1.x(); - float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f; - float rectW = m_glScopeRect1.width() * (float)m_timeBase / (float)(m_traceSize - 1); - //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp; - float rectH = -m_glScopeRect1.height() / 2.0f; - - //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f); - QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start); - - // Paint trigger level if any - if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f)) - { - GLfloat q3[] { - 0, traceData.m_triggerDisplayLevel, - 1, traceData.m_triggerDisplayLevel - }; - - float rectX = m_glScopeRect1.x(); - float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f; - float rectW = m_glScopeRect1.width(); - float rectH = -m_glScopeRect1.height() / 2.0f; - - QVector4D color( - m_focusedTriggerData.m_triggerColorR, - m_focusedTriggerData.m_triggerColorG, - m_focusedTriggerData.m_triggerColorB, - 0.4f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawSegments(mat, color, q3, 2); - } - - // Paint overlay if any - if ((i == m_focusedTraceIndex) && (traceData.m_hasTextOverlay)) - { - drawChannelOverlay( - traceData.m_textOverlay, - traceData.m_traceColor, - m_channelOverlayPixmap1, - m_glScopeRect1); - } - } // all traces display - } // trace length > 0 - - // paint right display: polar XY - - // draw rect around - { - GLfloat q3[] { - 1, 1, - 0, 1, - 0, 0, - 1, 0 - }; - - QVector4D color(1.0f, 1.0f, 1.0f, 0.5f); - m_glShaderSimple.drawContour(m_glScopeMatrix2, color, q3, 4); - } - - // paint grid - - // Horizontal Y2 - tickList = &m_y2Scale.getTickList(); - { - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickY2.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float y = 1 - (tick->pos / m_y2Scale.getSize()); - q3[4*effectiveTicks] = 0; - q3[4*effectiveTicks+1] = y; - q3[4*effectiveTicks+2] = 1; - q3[4*effectiveTicks+3] = y; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks); - } - - // Vertical X2 - tickList = &m_x2Scale.getTickList(); - { - //GLfloat q3[4*tickList->count()]; - GLfloat *q3 = m_q3TickX2.m_array; - int effectiveTicks = 0; - for(int i= 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - float x = tick->pos / m_x2Scale.getSize(); - q3[4*effectiveTicks] = x; - q3[4*effectiveTicks+1] = 0; - q3[4*effectiveTicks+2] = x; - q3[4*effectiveTicks+3] = 1; - effectiveTicks++; - } - } - } - - QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f); - m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks); - } - - // paint left #2 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - m_glShaderLeft2Scale.drawSurface(m_glLeft2ScaleMatrix, tex1, vtx1, 4); - } - - // paint bottom #2 scale - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - m_glShaderBottom2Scale.drawSurface(m_glBot2ScaleMatrix, tex1, vtx1, 4); - } - - // paint polar traces - - if (m_traceSize > 0) - { - int start = (m_timeOfsProMill/1000.0) * m_traceSize; - int end = std::min(start + m_traceSize/m_timeBase, m_traceSize); - - if(end - start < 2) - start--; - - //GLfloat q3[2*(end - start)]; - GLfloat *q3 = m_q3Polar.m_array; - const float *trace0 = (*m_traces)[0]; - memcpy(q3, &(trace0[2*start+1]), (2*(end - start) - 1)*sizeof(float)); // copy X values - - for (unsigned int i = 1; i < m_traces->size(); i++) - { - const float *trace = (*m_traces)[i]; - const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i]; - - if (!traceData.m_viewTrace) { - continue; - } - - for(int i = start; i < end; i++) - { - float y = trace[2*i+1]; - q3[2*(i-start)+1] = y; - } - - float rectX = m_glScopeRect2.x() + m_glScopeRect2.width() / 2.0f; - float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f; - float rectW = m_glScopeRect2.width() / 2.0f; - float rectH = -(m_glScopeRect2.height() / 2.0f); - - QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f); - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderSimple.drawPolyline(mat, color, q3, end -start); - } // XY polar display - } // trace length > 0 - } // XY mixed + polar display - - m_mutex.unlock(); -} - -void GLScopeMulti::setSampleRate(int sampleRate) -{ - m_sampleRate = sampleRate; - m_configChanged = true; - update(); - emit sampleRateChanged(m_sampleRate); -} - -void GLScopeMulti::setTimeBase(int timeBase) -{ - m_timeBase = timeBase; - m_configChanged = true; - update(); -} - -void GLScopeMulti::setTriggerPre(uint32_t triggerPre) -{ - m_triggerPre = triggerPre; - m_configChanged = true; - update(); -} - -void GLScopeMulti::setTimeOfsProMill(int timeOfsProMill) -{ - m_timeOfsProMill = timeOfsProMill; - m_configChanged = true; - update(); -} - -void GLScopeMulti::setFocusedTraceIndex(uint32_t traceIndex) -{ - m_focusedTraceIndex = traceIndex; - m_configChanged = true; - update(); -} - -void GLScopeMulti::setDisplayMode(DisplayMode displayMode) -{ - m_displayMode = displayMode; - m_configChanged = true; - update(); -} - -void GLScopeMulti::setTraceSize(int traceSize) -{ - m_traceSize = traceSize; - m_configChanged = true; - update(); -} - -void GLScopeMulti::updateDisplay() -{ - m_configChanged = true; - update(); -} - -void GLScopeMulti::applyConfig() -{ - m_configChanged = false; - - QFontMetrics fm(font()); - //float t_start = ((m_timeOfsProMill / 1000.0) * ((float) m_traceSize / m_sampleRate)) - ((float) m_triggerPre / m_sampleRate); - float t_start = (((m_timeOfsProMill / 1000.0f) * (float) m_traceSize) / m_sampleRate) - ((float) m_triggerPre / m_sampleRate); - float t_len = ((float) m_traceSize / m_sampleRate) / (float) m_timeBase; - - // scales - - m_x1Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale - - if (m_displayMode == DisplayPol) - { - setYScale(m_x2Scale, 0); // polar scale (X) - } - else - { - m_x2Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale - } - - if (m_traces->size() > 0) - { - setYScale(m_y1Scale, 0); // This is always the X trace (trace #0) - } - - if ((m_traces->size() > 1) && (m_focusedTraceIndex < m_traces->size())) - { - setYScale(m_y2Scale, m_focusedTraceIndex > 0 ? m_focusedTraceIndex : 1); // if Highlighted trace is #0 (X trace) set it to first Y trace (trace #1) - } - else - { - setYScale(m_y2Scale, 0); // Default to the X trace (trace #0) - If there is only one trace it should not get there (Y displays disabled in the UI) - } - - // display arrangements - - if ((m_displayMode == DisplayX) || (m_displayMode == DisplayY)) // unique displays - { - setUniqueDisplays(); - } - else if (m_displayMode == DisplayXYV) // both displays vertically arranged - { - setVerticalDisplays(); - } - else if (m_displayMode == DisplayXYH) // both displays horizontally arranged - { - setHorizontalDisplays(); - } - else if (m_displayMode == DisplayPol) // horizontal arrangement: XY stacked on left and polar on right - { - setPolarDisplays(); - } - - m_q3TickY1.allocate(4*m_y1Scale.getTickList().count()); - m_q3TickY2.allocate(4*m_y2Scale.getTickList().count()); - m_q3TickX1.allocate(4*m_x1Scale.getTickList().count()); - m_q3TickX2.allocate(4*m_x2Scale.getTickList().count()); - - int start = (m_timeOfsProMill/1000.0) * m_traceSize; - int end = std::min(start + m_traceSize/m_timeBase, m_traceSize); - - if(end - start < 2) - start--; - - m_q3Polar.allocate(2*(end - start)); -} - -void GLScopeMulti::setUniqueDisplays() -{ - QFontMetrics fm(font()); - int M = fm.width("-"); - int scopeHeight = height() - m_topMargin - m_botMargin; - int scopeWidth = width() - m_leftMargin - m_rightMargin; - - // X display - - m_glScopeRect1 = QRectF( - (float) m_leftMargin / (float) width(), - (float) m_topMargin / (float) height(), - (float) scopeWidth / (float) width(), - (float) scopeHeight / (float) height() - ); - - m_glScopeMatrix1.setToIdentity(); - m_glScopeMatrix1.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix1.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot1ScaleMatrix.setToIdentity(); - m_glBot1ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot1ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft1ScaleMatrix.setToIdentity(); - m_glLeft1ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft1ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - // Y displays - - m_glScopeRect2 = QRectF( - (float) m_leftMargin / (float) width(), - (float) m_topMargin / (float) height(), - (float) scopeWidth / (float) width(), - (float) scopeHeight / (float) height() - ); - - m_glScopeMatrix2.setToIdentity(); - m_glScopeMatrix2.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix2.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot2ScaleMatrix.setToIdentity(); - m_glBot2ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot2ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft2ScaleMatrix.setToIdentity(); - m_glLeft2ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft2ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - { // X horizontal scale (X1) - m_x1Scale.setSize(scopeWidth); - - m_bot1ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot1ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage()); - } // X horizontal scale - - { // Y horizontal scale (X2) - m_x2Scale.setSize(scopeWidth); - - m_bot2ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot2ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage()); - } // Y horizontal scale - - { // X vertical scale (Y1) - m_y1Scale.setSize(scopeHeight); - - m_left1ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left1ScalePixmap.fill(Qt::black); - QPainter painter(&m_left1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage()); - } // X vertical scale - - { // Y vertical scale (Y2) - m_y2Scale.setSize(scopeHeight); - - m_left2ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left2ScalePixmap.fill(Qt::black); - QPainter painter(&m_left2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage()); - } // Y vertical scale -} - -void GLScopeMulti::setVerticalDisplays() -{ - QFontMetrics fm(font()); - int M = fm.width("-"); - int scopeHeight = (height() - m_topMargin) / 2 - m_botMargin; - int scopeWidth = width() - m_leftMargin - m_rightMargin; - - // X display - - m_glScopeRect1 = QRectF( - (float) m_leftMargin / (float) width(), - (float) m_topMargin / (float) height(), - (float) scopeWidth / (float) width(), - (float) scopeHeight / (float) height() - ); - - m_glScopeMatrix1.setToIdentity(); - m_glScopeMatrix1.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix1.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot1ScaleMatrix.setToIdentity(); - m_glBot1ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot1ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft1ScaleMatrix.setToIdentity(); - m_glLeft1ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft1ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - // Y display - - m_glScopeRect2 = QRectF( - (float) m_leftMargin / (float)width(), - (float) (m_botMargin + m_topMargin + scopeHeight) / (float)height(), - (float) scopeWidth / (float)width(), - (float) scopeHeight / (float)height() - ); - - m_glScopeMatrix2.setToIdentity(); - m_glScopeMatrix2.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(m_botMargin + m_topMargin + scopeHeight) / (float) height()) - ); - m_glScopeMatrix2.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot2ScaleMatrix.setToIdentity(); - m_glBot2ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + scopeHeight + m_botMargin + 1) / (float) height()) - ); - m_glBot2ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft2ScaleMatrix.setToIdentity(); - m_glLeft2ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*(m_topMargin + scopeHeight + m_botMargin) / (float) height()) - ); - m_glLeft2ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - { // X horizontal scale (X1) - m_x1Scale.setSize(scopeWidth); - - m_bot1ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot1ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage()); - } // X horizontal scale (X1) - - { // Y horizontal scale (X2) - m_x2Scale.setSize(scopeWidth); - m_bot2ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot2ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage()); - } // Y horizontal scale (X2) - - { // X vertical scale (Y1) - m_y1Scale.setSize(scopeHeight); - - m_left1ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left1ScalePixmap.fill(Qt::black); - QPainter painter(&m_left1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage()); - } // X vertical scale (Y1) - - { // Y vertical scale (Y2) - m_y2Scale.setSize(scopeHeight); - - m_left2ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left2ScalePixmap.fill(Qt::black); - QPainter painter(&m_left2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage()); - } // Y vertical scale (Y2) -} - -void GLScopeMulti::setHorizontalDisplays() -{ - QFontMetrics fm(font()); - int M = fm.width("-"); - int scopeHeight = height() - m_topMargin - m_botMargin; - int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin; - - // X display - - m_glScopeRect1 = QRectF( - (float) m_leftMargin / (float) width(), - (float) m_topMargin / (float) height(), - (float) scopeWidth / (float) width(), - (float) scopeHeight / (float) height() - ); - m_glScopeMatrix1.setToIdentity(); - m_glScopeMatrix1.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix1.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot1ScaleMatrix.setToIdentity(); - m_glBot1ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot1ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft1ScaleMatrix.setToIdentity(); - m_glLeft1ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft1ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - // Y display - - m_glScopeRect2 = QRectF( - (float)(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float)width(), - (float)m_topMargin / (float)height(), - (float)((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float)width(), - (float)(height() - m_topMargin - m_botMargin) / (float)height() - ); - m_glScopeMatrix2.setToIdentity(); - m_glScopeMatrix2.translate ( - -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix2.scale ( - (float) 2*((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float) width(), - (float) -2*(height() - m_topMargin - m_botMargin) / (float) height() - ); - - m_glBot2ScaleMatrix.setToIdentity(); - m_glBot2ScaleMatrix.translate ( - -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot2ScaleMatrix.scale ( - (float) 2*scopeWidth / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft2ScaleMatrix.setToIdentity(); - m_glLeft2ScaleMatrix.translate ( - -1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft2ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - { // X horizontal scale (X1) - m_x1Scale.setSize(scopeWidth); - - m_bot1ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot1ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage()); - - } // X horizontal scale (X1) - - { // Y horizontal scale (X2) - m_x2Scale.setSize(scopeWidth); - m_bot2ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot2ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage()); - } // Y horizontal scale (X2) - - { // X vertical scale (Y1) - m_y1Scale.setSize(scopeHeight); - - m_left1ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left1ScalePixmap.fill(Qt::black); - QPainter painter(&m_left1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage()); - } // X vertical scale (Y1) - - { // Y vertical scale (Y2) - m_y2Scale.setSize(scopeHeight); - - m_left2ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left2ScalePixmap.fill(Qt::black); - QPainter painter(&m_left2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage()); - } // Y vertical scale (Y2) -} - -void GLScopeMulti::setPolarDisplays() -{ - QFontMetrics fm(font()); - int M = fm.width("-"); - int scopeHeight = height() - m_topMargin - m_botMargin; - int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin; - int scopeDim = std::min(scopeWidth, scopeHeight); - scopeWidth += scopeWidth - scopeDim; - - // Mixed XY display (left) - - m_glScopeRect1 = QRectF( - (float) m_leftMargin / (float) width(), - (float) m_topMargin / (float) height(), - (float) (scopeWidth-m_leftMargin) / (float) width(), - (float) scopeHeight / (float) height() - ); - m_glScopeMatrix1.setToIdentity(); - m_glScopeMatrix1.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix1.scale ( - (float) 2*(scopeWidth-m_leftMargin) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glBot1ScaleMatrix.setToIdentity(); - m_glBot1ScaleMatrix.translate ( - -1.0f + ((float) 2*m_leftMargin / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot1ScaleMatrix.scale ( - (float) 2*(scopeWidth-m_leftMargin) / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft1ScaleMatrix.setToIdentity(); - m_glLeft1ScaleMatrix.translate ( - -1.0f, - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft1ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - m_glRight1ScaleMatrix.setToIdentity(); - m_glRight1ScaleMatrix.translate ( - -1.0f + ((float) 2*scopeWidth / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glRight1ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - // Polar XY display (right) - - m_glScopeRect2 = QRectF( - (float)(m_leftMargin + scopeWidth + m_leftMargin) / (float)width(), - (float)m_topMargin / (float)height(), - (float) scopeDim / (float)width(), - (float)(height() - m_topMargin - m_botMargin) / (float)height() - ); - m_glScopeMatrix2.setToIdentity(); - m_glScopeMatrix2.translate ( - -1.0f + ((float) 2*(m_leftMargin + scopeWidth + m_leftMargin) / (float) width()), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glScopeMatrix2.scale ( - (float) 2*scopeDim / (float) width(), - (float) -2*(height() - m_topMargin - m_botMargin) / (float) height() - ); - - m_glBot2ScaleMatrix.setToIdentity(); - m_glBot2ScaleMatrix.translate ( - -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()), - 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height()) - ); - m_glBot2ScaleMatrix.scale ( - (float) 2*scopeDim / (float) width(), - (float) -2*(m_botMargin - 1) / (float) height() - ); - - m_glLeft2ScaleMatrix.setToIdentity(); - m_glLeft2ScaleMatrix.translate ( - -1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(), - 1.0f - ((float) 2*m_topMargin / (float) height()) - ); - m_glLeft2ScaleMatrix.scale ( - (float) 2*(m_leftMargin-1) / (float) width(), - (float) -2*scopeHeight / (float) height() - ); - - { // Mixed XY horizontal scale (X1) - m_x1Scale.setSize(scopeWidth); - - m_bot1ScalePixmap = QPixmap( - scopeWidth, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot1ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage()); - } // Mixed XY horizontal scale (X1) - - { // Polar XY horizontal scale (X2) - m_x2Scale.setSize(scopeDim); - m_bot2ScalePixmap = QPixmap( - scopeDim, - m_botMargin - 1 - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_bot2ScalePixmap.fill(Qt::black); - QPainter painter(&m_bot2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_x2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text); - } - } - } - - m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage()); - } // Polar XY horizontal scale (X2) - - { // Mixed XY vertical scale (Y1) - m_y1Scale.setSize(scopeHeight); - - m_left1ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left1ScalePixmap.fill(Qt::black); - QPainter painter(&m_left1ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y1Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage()); - - } // Mixed XY vertical scale (Y1) - - { // Polar XY vertical scale (Y2) - m_y2Scale.setSize(scopeHeight); - - m_left2ScalePixmap = QPixmap( - m_leftMargin - 1, - scopeHeight - ); - - const ScaleEngine::TickList* tickList; - const ScaleEngine::Tick* tick; - - m_left2ScalePixmap.fill(Qt::black); - QPainter painter(&m_left2ScalePixmap); - painter.setPen(QColor(0xf0, 0xf0, 0xff)); - painter.setFont(font()); - tickList = &m_y2Scale.getTickList(); - - for(int i = 0; i < tickList->count(); i++) { - tick = &(*tickList)[i]; - if(tick->major) { - if(tick->textSize > 0) { - painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text); - } - } - } - - m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage()); - } // Polar XY vertical scale (Y2) -} - -void GLScopeMulti::setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex) -{ - ScopeVisMulti::TraceData& traceData = (*m_tracesData)[highlightedTraceIndex]; - float amp_range = 2.0 / traceData.m_amp; - float amp_ofs = traceData.m_ofs; - float pow_floor = -100.0 + traceData.m_ofs * 100.0; - float pow_range = 100.0 / traceData.m_amp; - - switch (traceData.m_projectionType) - { - case ScopeVisMulti::ProjectionMagDB: // dB scale - scale.setRange(Unit::Decibel, pow_floor, pow_floor + pow_range); - break; - case ScopeVisMulti::ProjectionMagLin: - if (amp_range < 2.0) { - scale.setRange(Unit::None, amp_ofs * 1000.0, amp_range * 1000.0 + amp_ofs * 1000.0); - } else { - scale.setRange(Unit::None, amp_ofs, amp_range + amp_ofs); - } - break; - case ScopeVisMulti::ProjectionPhase: // Phase or frequency - case ScopeVisMulti::ProjectionDPhase: - scale.setRange(Unit::None, -1.0/traceData.m_amp + amp_ofs, 1.0/traceData.m_amp + amp_ofs); - break; - case ScopeVisMulti::ProjectionReal: // Linear generic - case ScopeVisMulti::ProjectionImag: - default: - if (amp_range < 2.0) { - scale.setRange(Unit::None, - amp_range * 500.0 + amp_ofs * 1000.0, amp_range * 500.0 + amp_ofs * 1000.0); - } else { - scale.setRange(Unit::None, - amp_range * 0.5 + amp_ofs, amp_range * 0.5 + amp_ofs); - } - break; - } -} - -void GLScopeMulti::drawChannelOverlay( - const QString& text, - const QColor& color, - QPixmap& channelOverlayPixmap, - const QRectF& glScopeRect) -{ - if (text.isEmpty()) { - return; - } - - QFontMetricsF metrics(m_channelOverlayFont); - QRectF rect = metrics.boundingRect(text); - channelOverlayPixmap = QPixmap(rect.width() + 4.0f, rect.height()); - channelOverlayPixmap.fill(Qt::transparent); - QPainter painter(&channelOverlayPixmap); - painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing, false); - painter.fillRect(rect, QColor(0, 0, 0, 0x80)); - QColor textColor(color); - textColor.setAlpha(0xC0); - painter.setPen(textColor); - painter.setFont(m_channelOverlayFont); - painter.drawText(QPointF(0, rect.height() - 2.0f), text); - painter.end(); - - m_glShaderPowerOverlay.initTexture(channelOverlayPixmap.toImage()); - - { - GLfloat vtx1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - GLfloat tex1[] = { - 0, 1, - 1, 1, - 1, 0, - 0, 0 - }; - - float shiftX = glScopeRect.width() - ((rect.width() + 4.0f) / width()); - float rectX = glScopeRect.x() + shiftX; - float rectY = glScopeRect.y(); - float rectW = rect.width() / (float) width(); - float rectH = rect.height() / (float) height(); - - QMatrix4x4 mat; - mat.setToIdentity(); - mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY); - mat.scale(2.0f * rectW, -2.0f * rectH); - m_glShaderPowerOverlay.drawSurface(mat, tex1, vtx1, 4); - } -} - -void GLScopeMulti::tick() -{ - if(m_dataChanged) { - update(); - } -} - -void GLScopeMulti::connectTimer(const QTimer& timer) -{ - qDebug() << "GLScopeMulti::connectTimer"; - disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); - connect(&timer, SIGNAL(timeout()), this, SLOT(tick())); - m_timer.stop(); -} - -void GLScopeMulti::cleanup() -{ - //makeCurrent(); - m_glShaderSimple.cleanup(); - m_glShaderBottom1Scale.cleanup(); - m_glShaderBottom2Scale.cleanup(); - m_glShaderLeft1Scale.cleanup(); - m_glShaderPowerOverlay.cleanup(); - //doneCurrent(); -} - diff --git a/sdrgui/gui/glscopemulti.h b/sdrgui/gui/glscopemulti.h deleted file mode 100644 index 4991b48eb..000000000 --- a/sdrgui/gui/glscopemulti.h +++ /dev/null @@ -1,169 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// 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_GUI_GLSCOPEMULTI_H_ -#define SDRBASE_GUI_GLSCOPEMULTI_H_ - -#include -#include -#include -#include -#include -#include -#include "dsp/dsptypes.h" -#include "dsp/scopevismulti.h" -#include "gui/scaleengine.h" -#include "gui/glshadersimple.h" -#include "gui/glshadertextured.h" -#include "export.h" -#include "util/bitfieldindex.h" -#include "util/incrementalarray.h" - -class QPainter; - -class SDRGUI_API GLScopeMulti: public QGLWidget { - Q_OBJECT - -public: - enum DisplayMode { - DisplayXYH, - DisplayXYV, - DisplayX, - DisplayY, - DisplayPol - }; - - GLScopeMulti(QWidget* parent = 0); - virtual ~GLScopeMulti(); - - void connectTimer(const QTimer& timer); - - void setTraces(std::vector* tracesData, std::vector* traces); - void newTraces(std::vector* traces); - - int getSampleRate() const { return m_sampleRate; } - int getTraceSize() const { return m_traceSize; } - - void setTriggerPre(uint32_t triggerPre); //!< number of samples - void setTimeOfsProMill(int timeOfsProMill); - void setSampleRate(int sampleRate); - void setTimeBase(int timeBase); - void setFocusedTraceIndex(uint32_t traceIndex); - void setDisplayMode(DisplayMode displayMode); - void setTraceSize(int trceSize); - void updateDisplay(); - void setDisplayGridIntensity(int intensity); - void setDisplayTraceIntensity(int intensity); - void setFocusedTriggerData(ScopeVisMulti::TriggerData& triggerData) { m_focusedTriggerData = triggerData; } - void setConfigChanged() { m_configChanged = true; } - //void incrementTraceCounter() { m_traceCounter++; } - - bool getDataChanged() const { return m_dataChanged; } - DisplayMode getDisplayMode() const { return m_displayMode; } - -signals: - void sampleRateChanged(int); - -private: - std::vector *m_tracesData; - std::vector *m_traces; - ScopeVisMulti::TriggerData m_focusedTriggerData; - //int m_traceCounter; - uint32_t m_bufferIndex; - DisplayMode m_displayMode; - QTimer m_timer; - QMutex m_mutex; - bool m_dataChanged; - bool m_configChanged; - int m_sampleRate; - int m_timeOfsProMill; - uint32_t m_triggerPre; - int m_traceSize; - int m_timeBase; - int m_timeOffset; - uint32_t m_focusedTraceIndex; - - // graphics stuff - QRectF m_glScopeRect1; - QRectF m_glScopeRect2; - QMatrix4x4 m_glScopeMatrix1; - QMatrix4x4 m_glScopeMatrix2; - QMatrix4x4 m_glLeft1ScaleMatrix; - QMatrix4x4 m_glRight1ScaleMatrix; - QMatrix4x4 m_glLeft2ScaleMatrix; - QMatrix4x4 m_glBot1ScaleMatrix; - QMatrix4x4 m_glBot2ScaleMatrix; - - QPixmap m_left1ScalePixmap; - QPixmap m_left2ScalePixmap; - QPixmap m_bot1ScalePixmap; - QPixmap m_bot2ScalePixmap; - QPixmap m_channelOverlayPixmap1; - QPixmap m_channelOverlayPixmap2; - - int m_displayGridIntensity; - int m_displayTraceIntensity; - - ScaleEngine m_x1Scale; //!< Display #1 X scale. Time scale - ScaleEngine m_x2Scale; //!< Display #2 X scale. Time scale - ScaleEngine m_y1Scale; //!< Display #1 Y scale. Always connected to trace #0 (X trace) - ScaleEngine m_y2Scale; //!< Display #2 Y scale. Connected to highlighted Y trace (#1..n) - - QFont m_channelOverlayFont; - - GLShaderSimple m_glShaderSimple; - GLShaderTextured m_glShaderLeft1Scale; - GLShaderTextured m_glShaderBottom1Scale; - GLShaderTextured m_glShaderLeft2Scale; - GLShaderTextured m_glShaderBottom2Scale; - GLShaderTextured m_glShaderPowerOverlay; - - IncrementalArray m_q3Polar; - IncrementalArray m_q3TickY1; - IncrementalArray m_q3TickY2; - IncrementalArray m_q3TickX1; - IncrementalArray m_q3TickX2; - - static const int m_topMargin = 5; - static const int m_botMargin = 20; - static const int m_leftMargin = 35; - static const int m_rightMargin = 5; - - void initializeGL(); - void resizeGL(int width, int height); - void paintGL(); - - void applyConfig(); - void setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex); - void setUniqueDisplays(); //!< Arrange displays when X and Y are unique on screen - void setVerticalDisplays(); //!< Arrange displays when X and Y are stacked vertically - void setHorizontalDisplays(); //!< Arrange displays when X and Y are stacked horizontally - void setPolarDisplays(); //!< Arrange displays when X and Y are stacked over on the left and polar display is on the right - - void drawChannelOverlay( //!< Draws a text overlay - const QString& text, - const QColor& color, - QPixmap& channelOverlayPixmap, - const QRectF& glScopeRect); - -protected slots: - void cleanup(); - void tick(); - -}; - -#endif /* SDRBASE_GUI_GLSCOPENG_H_ */ diff --git a/sdrgui/gui/glscopemultigui.cpp b/sdrgui/gui/glscopemultigui.cpp deleted file mode 100644 index e525e7cd5..000000000 --- a/sdrgui/gui/glscopemultigui.cpp +++ /dev/null @@ -1,1543 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// 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 "glscopemultigui.h" -#include "glscopemulti.h" -#include "ui_glscopemultigui.h" -#include "util/simpleserializer.h" - -const double GLScopeMultiGUI::amps[11] = { 0.2, 0.1, 0.05, 0.02, 0.01, 0.005, 0.002, 0.001, 0.0005, 0.0002, 0.0001 }; - -GLScopeMultiGUI::GLScopeMultiGUI(QWidget* parent) : - QWidget(parent), - ui(new Ui::GLScopeMultiGUI), - m_messageQueue(0), - m_scopeVis(0), - m_glScope(0), - m_sampleRate(0), - m_timeBase(1), - m_timeOffset(0), - m_traceLenMult(1) -{ - qDebug("GLScopeMultiGUI::GLScopeMultiGUI"); - setEnabled(false); - ui->setupUi(this); - ui->trigDelayFine->setMaximum(ScopeVisMulti::m_traceChunkSize / 10.0); - ui->traceColor->setStyleSheet("QLabel { background-color : rgb(255,255,64); }"); - m_focusedTraceColor.setRgb(255,255,64); - ui->trigColor->setStyleSheet("QLabel { background-color : rgb(0,255,0); }"); - m_focusedTriggerColor.setRgb(0,255,0); - ui->traceText->setText("X"); - ui->mem->setMaximum(ScopeVisMulti::m_nbTraceMemories - 1); -} - -GLScopeMultiGUI::~GLScopeMultiGUI() -{ - delete ui; -} - -void GLScopeMultiGUI::setBuddies(MessageQueue* messageQueue, ScopeVisMulti* scopeVis, GLScopeMulti* glScope) -{ - qDebug("GLScopeMultiGUI::setBuddies"); - - m_messageQueue = messageQueue; - m_scopeVis = scopeVis; - m_glScope = glScope; - - // initialize display combo - ui->onlyX->setChecked(true); - ui->onlyY->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->verticalXY->setChecked(false); - ui->polar->setChecked(false); - ui->onlyY->setEnabled(false); - ui->horizontalXY->setEnabled(false); - ui->verticalXY->setEnabled(false); - ui->polar->setEnabled(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayX); - - // initialize trigger combo - ui->trigPos->setChecked(true); - ui->trigNeg->setChecked(false); - ui->trigBoth->setChecked(false); - ui->trigOneShot->setChecked(false); - ui->trigOneShot->setEnabled(false); - ui->freerun->setChecked(true); - - // Add a trigger - ScopeVisMulti::TriggerData triggerData; - fillTriggerData(triggerData); - m_scopeVis->addTrigger(triggerData); - - // Add a trace - ScopeVisMulti::TraceData traceData; - fillTraceData(traceData); - m_scopeVis->addTrace(traceData); - - setEnabled(true); - connect(m_glScope, SIGNAL(sampleRateChanged(int)), this, SLOT(on_scope_sampleRateChanged(int))); - - ui->traceMode->clear(); - fillProjectionCombo(ui->traceMode); - - ui->trigMode->clear(); - fillProjectionCombo(ui->trigMode); - - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - 2*m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); - - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); - - setTraceLenDisplay(); - setTimeScaleDisplay(); - setTimeOfsDisplay(); - setAmpScaleDisplay(); - setAmpOfsDisplay(); - setTraceDelayDisplay(); -} - -void GLScopeMultiGUI::setSampleRate(int sampleRate) -{ - m_sampleRate = sampleRate; -} - -void GLScopeMultiGUI::on_scope_sampleRateChanged(int sampleRate) -{ - //m_sampleRate = m_glScope->getSampleRate(); - m_sampleRate = sampleRate; - ui->sampleRateText->setText(tr("%1\nkS/s").arg(m_sampleRate / 1000.0f, 0, 'f', 2)); - setTraceLenDisplay(); - setTimeScaleDisplay(); - setTimeOfsDisplay(); - setTraceDelayDisplay(); - setTrigPreDisplay(); - setTrigDelayDisplay(); -} - -void GLScopeMultiGUI::resetToDefaults() -{ -} - - -QByteArray GLScopeMultiGUI::serialize() const -{ - SimpleSerializer s(1); - - // first row - s.writeS32(1, (int) m_glScope->getDisplayMode()); - s.writeS32(2, ui->traceIntensity->value()); - s.writeS32(3, ui->gridIntensity->value()); - s.writeS32(4, ui->time->value()); - s.writeS32(5, ui->timeOfs->value()); - s.writeS32(6, ui->traceLen->value()); - - // second row - by trace - const std::vector& tracesData = m_scopeVis->getTracesData(); - std::vector::const_iterator traceDataIt = tracesData.begin(); - s.writeU32(10, (uint32_t) tracesData.size()); - int i = 0; - - for (; traceDataIt != tracesData.end(); ++traceDataIt, i++) - { - s.writeS32(20 + 16*i, (int) traceDataIt->m_projectionType); - s.writeU32(21 + 16*i, traceDataIt->m_ampIndex); - s.writeS32(22 + 16*i, traceDataIt->m_ofsCoarse); - s.writeS32(23 + 16*i, traceDataIt->m_ofsFine); - s.writeS32(24 + 16*i, traceDataIt->m_traceDelayCoarse); - s.writeS32(25 + 16*i, traceDataIt->m_traceDelayFine); - s.writeFloat(26 + 16*i, traceDataIt->m_traceColorR); - s.writeFloat(27 + 16*i, traceDataIt->m_traceColorG); - s.writeFloat(28 + 16*i, traceDataIt->m_traceColorB); - } - - // third row - by trigger - s.writeU32(200, (uint32_t) m_scopeVis->getNbTriggers()); - s.writeS32(201, ui->trigPre->value()); - - for (unsigned int i = 0; i < m_scopeVis->getNbTriggers(); i++) - { - const ScopeVisMulti::TriggerData& triggerData = m_scopeVis->getTriggerData(i); - s.writeS32(210 + 16*i, (int) triggerData.m_projectionType); - s.writeS32(211 + 16*i, triggerData.m_triggerRepeat); - s.writeBool(212 + 16*i, triggerData.m_triggerPositiveEdge); - s.writeBool(213 + 16*i, triggerData.m_triggerBothEdges); - s.writeS32(214 + 16*i, triggerData.m_triggerLevelCoarse); - s.writeS32(215 + 16*i, triggerData.m_triggerLevelFine); - s.writeS32(216 + 16*i, triggerData.m_triggerDelayCoarse); - s.writeS32(217 + 16*i, triggerData.m_triggerDelayFine); - s.writeFloat(218 + 16*i, triggerData.m_triggerColorR); - s.writeFloat(219 + 16*i, triggerData.m_triggerColorG); - s.writeFloat(220 + 16*i, triggerData.m_triggerColorB); - } - - return s.final(); -} - -bool GLScopeMultiGUI::deserialize(const QByteArray& data) -{ - qDebug("GLScopeMultiGUI::deserialize"); - SimpleDeserializer d(data); - - if(!d.isValid()) { - resetToDefaults(); - return false; - } - - if(d.getVersion() == 1) - { - TraceUIBlocker traceUIBlocker(ui); - TrigUIBlocker trigUIBlocker(ui); - int intValue; - uint32_t uintValue; - bool boolValue; - - ui->onlyX->setEnabled(false); - ui->onlyY->setEnabled(false); - ui->horizontalXY->setEnabled(false); - ui->verticalXY->setEnabled(false); - ui->polar->setEnabled(false); - - ui->traceMode->setCurrentIndex(0); - d.readS32(1, &intValue, (int) GLScopeMulti::DisplayX); - m_glScope->setDisplayMode((GLScopeMulti::DisplayMode) intValue); - - ui->onlyX->setChecked(false); - ui->onlyY->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->verticalXY->setChecked(false); - ui->polar->setChecked(false); - - switch (m_glScope->getDisplayMode()) - { - case GLScopeMulti::DisplayY: - ui->onlyY->setChecked(true); - break; - case GLScopeMulti::DisplayXYH: - ui->horizontalXY->setChecked(true); - break; - case GLScopeMulti::DisplayXYV: - ui->verticalXY->setChecked(true); - break; - case GLScopeMulti::DisplayPol: - ui->polar->setChecked(true); - break; - case GLScopeMulti::DisplayX: - default: - ui->onlyX->setChecked(true); - break; - } - - d.readS32(2, &intValue, 50); - ui->traceIntensity->setValue(intValue); - d.readS32(3, &intValue, 10); - ui->gridIntensity->setValue(intValue); - d.readS32(4, &intValue, 1); - ui->time->setValue(intValue); - d.readS32(5, &intValue, 0); - ui->timeOfs->setValue(intValue); - d.readS32(6, &intValue, 1); - ui->traceLen->setValue(intValue); - - // trace stuff - - uint32_t nbTracesSaved; - d.readU32(10, &nbTracesSaved, 1); - const std::vector& tracesData = m_scopeVis->getTracesData(); - uint32_t iTrace = tracesData.size(); - - qDebug("GLScopeMultiGUI::deserialize: nbTracesSaved: %u tracesData.size(): %lu", nbTracesSaved, tracesData.size()); - - while (iTrace > nbTracesSaved) // remove possible traces in excess - { - m_scopeVis->removeTrace(iTrace - 1); - iTrace--; - } - - for (iTrace = 0; iTrace < nbTracesSaved; iTrace++) - { - ScopeVisMulti::TraceData traceData; - float r, g, b; - - d.readS32(20 + 16*iTrace, &intValue, 0); - ui->traceMode->setCurrentIndex(intValue); - d.readU32(21 + 16*iTrace, &uintValue, 0); - ui->amp->setValue(uintValue); - d.readS32(22 + 16*iTrace, &intValue, 0); - ui->ofsCoarse->setValue(intValue); - d.readS32(23 + 16*iTrace, &intValue, 0); - ui->ofsFine->setValue(intValue); - d.readS32(24 + 16*iTrace, &intValue, 0); - ui->traceDelayCoarse->setValue(intValue); - d.readS32(25 + 16*iTrace, &intValue, 0); - ui->traceDelayFine->setValue(intValue); - d.readFloat(26 + 16*iTrace, &r, 1.0f); - d.readFloat(27 + 16*iTrace, &g, 1.0f); - d.readFloat(28 + 16*iTrace, &b, 1.0f); - m_focusedTraceColor.setRgbF(r, g, b); - - fillTraceData(traceData); - - if (iTrace < tracesData.size()) // change existing traces - { - m_scopeVis->changeTrace(traceData, iTrace); - } - else // add new traces - { - m_scopeVis->addTrace(traceData); - } - } - - - ui->trace->setMaximum(nbTracesSaved-1); - ui->trace->setValue(nbTracesSaved-1); - m_glScope->setFocusedTraceIndex(nbTracesSaved-1); - - int r,g,b,a; - m_focusedTraceColor.getRgb(&r, &g, &b, &a); - ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); - - setTraceIndexDisplay(); - setAmpScaleDisplay(); - setAmpOfsDisplay(); - setTraceDelayDisplay(); - - ui->onlyX->setEnabled(true); - ui->onlyY->setEnabled(nbTracesSaved > 1); - ui->horizontalXY->setEnabled(nbTracesSaved > 1); - ui->verticalXY->setEnabled(nbTracesSaved > 1); - ui->polar->setEnabled(nbTracesSaved > 1); - - // trigger stuff - - uint32_t nbTriggersSaved; - d.readU32(200, &nbTriggersSaved, 1); - uint32_t nbTriggers = m_scopeVis->getNbTriggers(); - uint32_t iTrigger = nbTriggers; - - d.readS32(201, &intValue, 0); - ui->trigPre->setValue(intValue); - - qDebug("GLScopeMultiGUI::deserialize: nbTriggersSaved: %u nbTriggers: %u", nbTriggersSaved, nbTriggers); - - while (iTrigger > nbTriggersSaved) // remove possible triggers in excess - { - m_scopeVis->removeTrigger(iTrigger - 1); - iTrigger--; - } - - for (iTrigger = 0; iTrigger < nbTriggersSaved; iTrigger++) - { - ScopeVisMulti::TriggerData triggerData = m_scopeVis->getTriggerData(iTrigger); - float r, g, b; - - d.readS32(210 + 16*iTrigger, &intValue, 0); - ui->trigMode->setCurrentIndex(intValue); - d.readS32(211 + 16*iTrigger, &intValue, 1); - ui->trigCount->setValue(intValue); - d.readBool(212 + 16*iTrigger, &boolValue, true); - ui->trigPos->setChecked(boolValue); - d.readBool(213 + 16*iTrigger, &boolValue, false); - ui->trigBoth->setChecked(boolValue); - d.readS32(214 + 16*iTrigger, &intValue, 1); - ui->trigLevelCoarse->setValue(intValue); - d.readS32(215 + 16*iTrigger, &intValue, 1); - ui->trigLevelFine->setValue(intValue); - d.readS32(216 + 16*iTrigger, &intValue, 1); - ui->trigDelayCoarse->setValue(intValue); - d.readS32(217 + 16*iTrigger, &intValue, 1); - ui->trigDelayFine->setValue(intValue); - d.readFloat(218 + 16*iTrigger, &r, 1.0f); - d.readFloat(219 + 16*iTrigger, &g, 1.0f); - d.readFloat(220 + 16*iTrigger, &b, 1.0f); - m_focusedTriggerColor.setRgbF(r, g, b); - - fillTriggerData(triggerData); - - if (iTrigger < nbTriggers) // change existing triggers - { - m_scopeVis->changeTrigger(triggerData, iTrigger); - } - else // add new trigers - { - m_scopeVis->addTrigger(triggerData); - } - - if (iTrigger == nbTriggersSaved-1) - { - m_glScope->setFocusedTriggerData(triggerData); - } - } - - ui->trig->setMaximum(nbTriggersSaved-1); - ui->trig->setValue(nbTriggersSaved-1); - - m_focusedTriggerColor.getRgb(&r, &g, &b, &a); - ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); - - setTrigCountDisplay(); - setTrigDelayDisplay(); - setTrigIndexDisplay(); - setTrigLevelDisplay(); - setTrigPreDisplay(); - - return true; - } - else - { - resetToDefaults(); - return false; - } -} - -void GLScopeMultiGUI::on_onlyX_toggled(bool checked) -{ - if (checked) - { - ui->onlyY->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->verticalXY->setChecked(false); - ui->polar->setChecked(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayX); - } -} - -void GLScopeMultiGUI::on_onlyY_toggled(bool checked) -{ - if (checked) - { - ui->onlyX->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->verticalXY->setChecked(false); - ui->polar->setChecked(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayY); - } -} - -void GLScopeMultiGUI::on_horizontalXY_toggled(bool checked) -{ - if (checked) - { - ui->onlyX->setChecked(false); - ui->onlyY->setChecked(false); - ui->verticalXY->setChecked(false); - ui->polar->setChecked(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayXYH); - } -} - -void GLScopeMultiGUI::on_verticalXY_toggled(bool checked) -{ - if (checked) - { - ui->onlyX->setChecked(false); - ui->onlyY->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->polar->setChecked(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayXYV); - } -} - -void GLScopeMultiGUI::on_polar_toggled(bool checked) -{ - if (checked) - { - ui->onlyX->setChecked(false); - ui->onlyY->setChecked(false); - ui->horizontalXY->setChecked(false); - ui->verticalXY->setChecked(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayPol); - } -} - -void GLScopeMultiGUI::on_traceIntensity_valueChanged(int value) -{ - ui->traceIntensity->setToolTip(QString("Trace intensity: %1").arg(value)); - m_glScope->setDisplayTraceIntensity(value); -} - -void GLScopeMultiGUI::on_gridIntensity_valueChanged(int value) -{ - ui->gridIntensity->setToolTip(QString("Grid intensity: %1").arg(value)); - m_glScope->setDisplayGridIntensity(value); -} - -void GLScopeMultiGUI::on_time_valueChanged(int value) -{ - m_timeBase = value; - setTimeScaleDisplay(); - setTraceDelayDisplay(); - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); -} - -void GLScopeMultiGUI::on_timeOfs_valueChanged(int value) -{ - if ((value < 0) || (value > 100)) { - return; - } - - m_timeOffset = value; - setTimeOfsDisplay(); - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); -} - -void GLScopeMultiGUI::on_traceLen_valueChanged(int value) -{ - if ((value < 1) || (value > 100)) { - return; - } - - m_traceLenMult = value; - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); - setTraceLenDisplay(); - setTimeScaleDisplay(); - setTimeOfsDisplay(); - setTrigDelayDisplay(); - setTrigPreDisplay(); -} - -void GLScopeMultiGUI::on_trace_valueChanged(int value) -{ - ui->traceText->setText(value == 0 ? "X" : QString("Y%1").arg(ui->trace->value())); - - ScopeVisMulti::TraceData traceData; - m_scopeVis->getTraceData(traceData, value); - - qDebug() << "GLScopeMultiGUI::on_trace_valueChanged:" - << " m_projectionType: " << (int) traceData.m_projectionType - << " m_amp" << traceData.m_amp - << " m_ofs" << traceData.m_ofs - << " m_traceDelay" << traceData.m_traceDelay; - - setTraceUI(traceData); - - m_scopeVis->focusOnTrace(value); -} - -void GLScopeMultiGUI::on_traceAdd_clicked(bool checked __attribute__((unused))) -{ - ScopeVisMulti::TraceData traceData; - fillTraceData(traceData); - addTrace(traceData); -} - -void GLScopeMultiGUI::on_traceDel_clicked(bool checked __attribute__((unused))) -{ - if (ui->trace->value() > 0) // not the X trace - { - ui->trace->setMaximum(ui->trace->maximum() - 1); - - if (ui->trace->value() == 0) - { - ui->onlyX->setChecked(true); - ui->onlyY->setEnabled(false); - ui->horizontalXY->setEnabled(false); - ui->verticalXY->setEnabled(false); - ui->polar->setEnabled(false); - m_glScope->setDisplayMode(GLScopeMulti::DisplayX); - } - - m_scopeVis->removeTrace(ui->trace->value()); - changeCurrentTrace(); - } -} - -void GLScopeMultiGUI::on_traceUp_clicked(bool checked __attribute__((unused))) -{ - if (ui->trace->maximum() > 0) // more than one trace - { - int newTraceIndex = (ui->trace->value() + 1) % (ui->trace->maximum()+1); - m_scopeVis->moveTrace(ui->trace->value(), true); - ui->trace->setValue(newTraceIndex); // follow trace - ScopeVisMulti::TraceData traceData; - m_scopeVis->getTraceData(traceData, ui->trace->value()); - setTraceUI(traceData); - m_scopeVis->focusOnTrace(ui->trace->value()); - } -} - -void GLScopeMultiGUI::on_traceDown_clicked(bool checked __attribute__((unused))) -{ - if (ui->trace->value() > 0) // not the X (lowest) trace - { - int newTraceIndex = (ui->trace->value() - 1) % (ui->trace->maximum()+1); - m_scopeVis->moveTrace(ui->trace->value(), false); - ui->trace->setValue(newTraceIndex); // follow trace - ScopeVisMulti::TraceData traceData; - m_scopeVis->getTraceData(traceData, ui->trace->value()); - setTraceUI(traceData); - m_scopeVis->focusOnTrace(ui->trace->value()); - } -} - -void GLScopeMultiGUI::on_trig_valueChanged(int value) -{ - ui->trigText->setText(tr("%1").arg(value)); - - ScopeVisMulti::TriggerData triggerData; - m_scopeVis->getTriggerData(triggerData, value); - - qDebug() << "GLScopeMultiGUI::on_trig_valueChanged:" - << " m_projectionType: " << (int) triggerData.m_projectionType - << " m_triggerRepeat" << triggerData.m_triggerRepeat - << " m_triggerPositiveEdge" << triggerData.m_triggerPositiveEdge - << " m_triggerBothEdges" << triggerData.m_triggerBothEdges - << " m_triggerLevel" << triggerData.m_triggerLevel; - - setTriggerUI(triggerData); - - m_scopeVis->focusOnTrigger(value); -} - -void GLScopeMultiGUI::on_trigAdd_clicked(bool checked __attribute__((unused))) -{ - ScopeVisMulti::TriggerData triggerData; - fillTriggerData(triggerData); - addTrigger(triggerData); -} - -void GLScopeMultiGUI::on_trigDel_clicked(bool checked __attribute__((unused))) -{ - if (ui->trig->value() > 0) - { - m_scopeVis->removeTrigger(ui->trig->value()); - ui->trig->setMaximum(ui->trig->maximum() - 1); - } -} - -void GLScopeMultiGUI::on_trigUp_clicked(bool checked __attribute__((unused))) -{ - if (ui->trig->maximum() > 0) // more than one trigger - { - int newTriggerIndex = (ui->trig->value() + 1) % (ui->trig->maximum()+1); - m_scopeVis->moveTrigger(ui->trace->value(), true); - ui->trig->setValue(newTriggerIndex); // follow trigger - ScopeVisMulti::TriggerData triggerData; - m_scopeVis->getTriggerData(triggerData, ui->trig->value()); - setTriggerUI(triggerData); - m_scopeVis->focusOnTrigger(ui->trig->value()); - } -} - -void GLScopeMultiGUI::on_trigDown_clicked(bool checked __attribute__((unused))) -{ - if (ui->trig->value() > 0) // not the 0 (lowest) trigger - { - int newTriggerIndex = (ui->trig->value() - 1) % (ui->trig->maximum()+1); - m_scopeVis->moveTrigger(ui->trace->value(), false); - ui->trig->setValue(newTriggerIndex); // follow trigger - ScopeVisMulti::TriggerData triggerData; - m_scopeVis->getTriggerData(triggerData, ui->trig->value()); - setTriggerUI(triggerData); - m_scopeVis->focusOnTrigger(ui->trig->value()); - } -} - -void GLScopeMultiGUI::on_traceMode_currentIndexChanged(int index __attribute__((unused))) -{ - setAmpScaleDisplay(); - setAmpOfsDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_amp_valueChanged(int value __attribute__((unused))) -{ - setAmpScaleDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_ofsCoarse_valueChanged(int value __attribute__((unused))) -{ - setAmpOfsDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_ofsFine_valueChanged(int value __attribute__((unused))) -{ - setAmpOfsDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_traceDelayCoarse_valueChanged(int value __attribute__((unused))) -{ - setTraceDelayDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_traceDelayFine_valueChanged(int value __attribute__((unused))) -{ - setTraceDelayDisplay(); - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_traceView_toggled(bool checked __attribute__((unused))) -{ - changeCurrentTrace(); -} - -void GLScopeMultiGUI::on_traceColor_clicked() -{ - QColor newColor = QColorDialog::getColor(m_focusedTraceColor, this, tr("Select Color for trace"), QColorDialog::DontUseNativeDialog); - - if (newColor.isValid()) // user clicked OK and selected a color - { - m_focusedTraceColor = newColor; - int r,g,b,a; - m_focusedTraceColor.getRgb(&r, &g, &b, &a); - ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); - changeCurrentTrace(); - } -} - -void GLScopeMultiGUI::on_mem_valueChanged(int value) -{ - QString text; - text.sprintf("%02d", value); - ui->memText->setText(text); - disableLiveMode(value > 0); // block trigger UI line if memory is active - m_scopeVis->setMemoryIndex(value); -} - -void GLScopeMultiGUI::on_trigMode_currentIndexChanged(int index __attribute__((unused))) -{ - setTrigLevelDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigCount_valueChanged(int value __attribute__((unused))) -{ - setTrigCountDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigPos_toggled(bool checked) -{ - if (checked) - { - ui->trigNeg->setChecked(false); - ui->trigBoth->setChecked(false); - } - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigNeg_toggled(bool checked) -{ - if (checked) - { - ui->trigPos->setChecked(false); - ui->trigBoth->setChecked(false); - } - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigBoth_toggled(bool checked) -{ - if (checked) - { - ui->trigNeg->setChecked(false); - ui->trigPos->setChecked(false); - } - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigLevelCoarse_valueChanged(int value __attribute__((unused))) -{ - setTrigLevelDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigLevelFine_valueChanged(int value __attribute__((unused))) -{ - setTrigLevelDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigDelayCoarse_valueChanged(int value __attribute__((unused))) -{ - setTrigDelayDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigDelayFine_valueChanged(int value __attribute__((unused))) -{ - setTrigDelayDisplay(); - changeCurrentTrigger(); -} - -void GLScopeMultiGUI::on_trigPre_valueChanged(int value __attribute__((unused))) -{ - setTrigPreDisplay(); - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); -} - -void GLScopeMultiGUI::on_trigColor_clicked() -{ - QColor newColor = QColorDialog::getColor(m_focusedTriggerColor, this, tr("Select Color for trigger line"), QColorDialog::DontUseNativeDialog); - - if (newColor.isValid()) // user clicked "OK" - { - m_focusedTriggerColor = newColor; - int r,g,b,a; - m_focusedTriggerColor.getRgb(&r, &g, &b, &a); - ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); - changeCurrentTrigger(); - } -} - -void GLScopeMultiGUI::on_trigOneShot_toggled(bool checked) -{ - m_scopeVis->setOneShot(checked); -} - -void GLScopeMultiGUI::on_freerun_toggled(bool checked) -{ - if (checked) - { - ui->trigOneShot->setChecked(false); - ui->trigOneShot->setEnabled(false); - } - else - { - ui->trigOneShot->setEnabled(true); - } - - m_scopeVis->configure( - ui->sourcePort->maximum() + 1, - m_traceLenMult*ScopeVisMulti::m_traceChunkSize, - m_timeBase, - m_timeOffset*10, - (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)), - ui->freerun->isChecked()); -} - -void GLScopeMultiGUI::setTraceIndexDisplay() -{ - ui->traceText->setText(ui->trace->value() == 0 ? "X" : QString("Y%1").arg(ui->trace->value())); -} - -void GLScopeMultiGUI::setTrigCountDisplay() -{ - QString text; - text.sprintf("%02d", ui->trigCount->value()); - ui->trigCountText->setText(text); -} - -void GLScopeMultiGUI::setTimeScaleDisplay() -{ - m_sampleRate = m_glScope->getSampleRate(); - unsigned int n_samples = (m_glScope->getTraceSize() * 1.0) / (double) m_timeBase; - double t = (m_glScope->getTraceSize() * 1.0 / m_sampleRate) / (double) m_timeBase; - - if (n_samples < 1000) { - ui->timeText->setToolTip(tr("%1 S").arg(n_samples)); - } else if (n_samples < 1000000) { - ui->timeText->setToolTip(tr("%1 kS").arg(n_samples/1000.0)); - } else { - ui->timeText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0)); - } - - if(t < 0.000001) - { - t = round(t * 100000000000.0) / 100.0; - ui->timeText->setText(tr("%1\nns").arg(t)); - } - else if(t < 0.001) - { - t = round(t * 100000000.0) / 100.0; - ui->timeText->setText(tr("%1\nµs").arg(t)); - } - else if(t < 1.0) - { - t = round(t * 100000.0) / 100.0; - ui->timeText->setText(tr("%1\nms").arg(t)); - } - else - { - t = round(t * 100.0) / 100.0; - ui->timeText->setText(tr("%1\ns").arg(t)); - } -} - -void GLScopeMultiGUI::setTraceLenDisplay() -{ - unsigned int n_samples = m_traceLenMult * ScopeVisMulti::m_traceChunkSize; - - if (n_samples < 1000) { - ui->traceLenText->setToolTip(tr("%1 S").arg(n_samples)); - } else if (n_samples < 1000000) { - ui->traceLenText->setToolTip(tr("%1 kS").arg(n_samples/1000.0)); - } else { - ui->traceLenText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0)); - } - - m_sampleRate = m_glScope->getSampleRate(); - double t = (m_glScope->getTraceSize() * 1.0 / m_sampleRate); - - if(t < 0.000001) - ui->traceLenText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2)); - else if(t < 0.001) - ui->traceLenText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2)); - else if(t < 1.0) - ui->traceLenText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2)); - else - ui->traceLenText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2)); -} - -void GLScopeMultiGUI::setTimeOfsDisplay() -{ - unsigned int n_samples = m_glScope->getTraceSize() * (m_timeOffset/100.0); - double dt = m_glScope->getTraceSize() * (m_timeOffset/100.0) / m_sampleRate; - - if (n_samples < 1000) { - ui->timeOfsText->setToolTip(tr("%1 S").arg(n_samples)); - } else if (n_samples < 1000000) { - ui->timeOfsText->setToolTip(tr("%1 kS").arg(n_samples/1000.0)); - } else { - ui->timeOfsText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0)); - } - - if(dt < 0.000001) - ui->timeOfsText->setText(tr("%1\nns").arg(dt * 1000000000.0, 0, 'f', 2)); - else if(dt < 0.001) - ui->timeOfsText->setText(tr("%1\nµs").arg(dt * 1000000.0, 0, 'f', 2)); - else if(dt < 1.0) - ui->timeOfsText->setText(tr("%1\nms").arg(dt * 1000.0, 0, 'f', 2)); - else - ui->timeOfsText->setText(tr("%1\ns").arg(dt * 1.0, 0, 'f', 2)); -} - -void GLScopeMultiGUI::setAmpScaleDisplay() -{ - ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex(); - double ampValue = amps[ui->amp->value()]; - - if (projectionType == ScopeVisMulti::ProjectionMagDB) - { - double displayValue = ampValue*500.0f; - - if (displayValue < 10.0f) { - ui->ampText->setText(tr("%1\ndB").arg(displayValue, 0, 'f', 2)); - } - else { - ui->ampText->setText(tr("%1\ndB").arg(displayValue, 0, 'f', 1)); - } - } - else - { - double a = ampValue*10.0f; - - if(a < 0.000001) - ui->ampText->setText(tr("%1\nn").arg(a * 1000000000.0)); - else if(a < 0.001) - ui->ampText->setText(tr("%1\nµ").arg(a * 1000000.0)); - else if(a < 1.0) - ui->ampText->setText(tr("%1\nm").arg(a * 1000.0)); - else - ui->ampText->setText(tr("%1").arg(a * 1.0)); - } -} - -void GLScopeMultiGUI::setAmpOfsDisplay() -{ - ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex(); - double o = (ui->ofsCoarse->value() * 10.0f) + (ui->ofsFine->value() / 20.0f); - - if (projectionType == ScopeVisMulti::ProjectionMagDB) - { - ui->ofsText->setText(tr("%1\ndB").arg(o/10.0f - 100.0f, 0, 'f', 1)); - } - else - { - double a; - - if (projectionType == ScopeVisMulti::ProjectionMagLin) - { - a = o/2000.0f; - } - else - { - a = o/1000.0f; - } - - if(fabs(a) < 0.000001f) - ui->ofsText->setText(tr("%1\nn").arg(a * 1000000000.0)); - else if(fabs(a) < 0.001f) - ui->ofsText->setText(tr("%1\nµ").arg(a * 1000000.0)); - else if(fabs(a) < 1.0f) - ui->ofsText->setText(tr("%1\nm").arg(a * 1000.0)); - else - ui->ofsText->setText(tr("%1").arg(a * 1.0)); - } -} - -void GLScopeMultiGUI::setTraceDelayDisplay() -{ - if (m_sampleRate > 0) - { - int n_samples = ui->traceDelayCoarse->value()*100 + ui->traceDelayFine->value(); - double t = ((double) n_samples) / m_sampleRate; - - if (n_samples < 1000) { - ui->traceDelayText->setToolTip(tr("%1 S").arg(n_samples)); - } else if (n_samples < 1000000) { - ui->traceDelayText->setToolTip(tr("%1 kS").arg(n_samples/1000.0)); - } else { - ui->traceDelayText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0)); - } - - if(t < 0.000001) - ui->traceDelayText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2)); - else if(t < 0.001) - ui->traceDelayText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2)); - else if(t < 1.0) - ui->traceDelayText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2)); - else - ui->traceDelayText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2)); - } -} - -void GLScopeMultiGUI::setTrigIndexDisplay() -{ - ui->trigText->setText(tr("%1").arg(ui->trig->value())); -} - -void GLScopeMultiGUI::setTrigLevelDisplay() -{ - double t = (ui->trigLevelCoarse->value() / 100.0f) + (ui->trigLevelFine->value() / 20000.0f); - ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->trigMode->currentIndex(); - - ui->trigLevelCoarse->setToolTip(QString("Trigger level coarse: %1 %").arg(ui->trigLevelCoarse->value() / 100.0f)); - ui->trigLevelFine->setToolTip(QString("Trigger level fine: %1 ppm").arg(ui->trigLevelFine->value() * 50)); - - if (projectionType == ScopeVisMulti::ProjectionMagDB) { - ui->trigLevelText->setText(tr("%1\ndB").arg(100.0 * (t - 1.0), 0, 'f', 1)); - } - else - { - double a; - - if (projectionType == ScopeVisMulti::ProjectionMagLin) { - a = 1.0 + t; - } else { - a = t; - } - - if(fabs(a) < 0.000001) - ui->trigLevelText->setText(tr("%1\nn").arg(a * 1000000000.0f, 0, 'f', 2)); - else if(fabs(a) < 0.001) - ui->trigLevelText->setText(tr("%1\nµ").arg(a * 1000000.0f, 0, 'f', 2)); - else if(fabs(a) < 1.0) - ui->trigLevelText->setText(tr("%1\nm").arg(a * 1000.0f, 0, 'f', 2)); - else - ui->trigLevelText->setText(tr("%1").arg(a * 1.0f, 0, 'f', 2)); - } -} - -void GLScopeMultiGUI::setTrigDelayDisplay() -{ - if (m_sampleRate > 0) - { - double delayMult = ui->trigDelayCoarse->value() + ui->trigDelayFine->value() / (ScopeVisMulti::m_traceChunkSize / 10.0); - unsigned int n_samples_delay = m_traceLenMult * ScopeVisMulti::m_traceChunkSize * delayMult; - - if (n_samples_delay < 1000) { - ui->trigDelayText->setToolTip(tr("%1 S").arg(n_samples_delay)); - } else if (n_samples_delay < 1000000) { - ui->trigDelayText->setToolTip(tr("%1 kS").arg(n_samples_delay/1000.0)); - } else if (n_samples_delay < 1000000000) { - ui->trigDelayText->setToolTip(tr("%1 MS").arg(n_samples_delay/1000000.0)); - } else { - ui->trigDelayText->setToolTip(tr("%1 GS").arg(n_samples_delay/1000000000.0)); - } - - m_sampleRate = m_glScope->getSampleRate(); - double t = (n_samples_delay * 1.0f / m_sampleRate); - - if(t < 0.000001) - ui->trigDelayText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2)); - else if(t < 0.001) - ui->trigDelayText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2)); - else if(t < 1.0) - ui->trigDelayText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2)); - else - ui->trigDelayText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2)); - } -} - -void GLScopeMultiGUI::setTrigPreDisplay() -{ - if (m_sampleRate > 0) - { - unsigned int n_samples_delay = m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f); - double dt = m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f) / m_sampleRate; - - if (n_samples_delay < 1000) { - ui->trigPreText->setToolTip(tr("%1 S").arg(n_samples_delay)); - } else if (n_samples_delay < 1000000) { - ui->trigPreText->setToolTip(tr("%1 kS").arg(n_samples_delay/1000.0)); - } else if (n_samples_delay < 1000000000) { - ui->trigPreText->setToolTip(tr("%1 MS").arg(n_samples_delay/1000000.0)); - } else { - ui->trigPreText->setToolTip(tr("%1 GS").arg(n_samples_delay/1000000000.0)); - } - - if(dt < 0.000001) - ui->trigPreText->setText(tr("%1\nns").arg(dt * 1000000000.0f, 0, 'f', 2)); - else if(dt < 0.001) - ui->trigPreText->setText(tr("%1\nµs").arg(dt * 1000000.0f, 0, 'f', 2)); - else if(dt < 1.0) - ui->trigPreText->setText(tr("%1\nms").arg(dt * 1000.0f, 0, 'f', 2)); - else - ui->trigPreText->setText(tr("%1\ns").arg(dt * 1.0f, 0, 'f', 2)); - } -} - -void GLScopeMultiGUI::changeCurrentTrace() -{ - ScopeVisMulti::TraceData traceData; - fillTraceData(traceData); - uint32_t currentTraceIndex = ui->trace->value(); - m_scopeVis->changeTrace(traceData, currentTraceIndex); -} - -void GLScopeMultiGUI::changeCurrentTrigger() -{ - ScopeVisMulti::TriggerData triggerData; - fillTriggerData(triggerData); - uint32_t currentTriggerIndex = ui->trig->value(); - m_scopeVis->changeTrigger(triggerData, currentTriggerIndex); -} - -void GLScopeMultiGUI::fillProjectionCombo(QComboBox* comboBox) -{ - comboBox->addItem("Real", ScopeVisMulti::ProjectionReal); - comboBox->addItem("Imag", ScopeVisMulti::ProjectionImag); - comboBox->addItem("Mag", ScopeVisMulti::ProjectionMagLin); - comboBox->addItem("MagdB", ScopeVisMulti::ProjectionMagDB); - comboBox->addItem("Phi", ScopeVisMulti::ProjectionPhase); - comboBox->addItem("dPhi", ScopeVisMulti::ProjectionDPhase); -} - -void GLScopeMultiGUI::disableLiveMode(bool disable) -{ - ui->traceLen->setEnabled(!disable); - ui->trig->setEnabled(!disable); - ui->trigAdd->setEnabled(!disable); - ui->trigDel->setEnabled(!disable); - ui->trigMode->setEnabled(!disable); - ui->trigCount->setEnabled(!disable); - ui->trigPos->setEnabled(!disable); - ui->trigNeg->setEnabled(!disable); - ui->trigBoth->setEnabled(!disable); - ui->trigLevelCoarse->setEnabled(!disable); - ui->trigLevelFine->setEnabled(!disable); - ui->trigDelayCoarse->setEnabled(!disable); - ui->trigDelayFine->setEnabled(!disable); - ui->trigPre->setEnabled(!disable); - ui->trigOneShot->setEnabled(!disable); - ui->freerun->setEnabled(!disable); -} - -void GLScopeMultiGUI::fillTraceData(ScopeVisMulti::TraceData& traceData) -{ - traceData.m_projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex(); - traceData.m_hasTextOverlay = (traceData.m_projectionType == ScopeVisMulti::ProjectionMagDB); - traceData.m_textOverlay.clear(); - traceData.m_inputIndex = 0; - traceData.m_amp = 0.2 / amps[ui->amp->value()]; - traceData.m_ampIndex = ui->amp->value(); - - traceData.m_ofsCoarse = ui->ofsCoarse->value(); - traceData.m_ofsFine = ui->ofsFine->value(); - - if (traceData.m_projectionType == ScopeVisMulti::ProjectionMagLin) { - traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 2000.0f; - } else { - traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 1000.0f; - } - - traceData.m_traceDelayCoarse = ui->traceDelayCoarse->value(); - traceData.m_traceDelayFine = ui->traceDelayFine->value(); - traceData.m_traceDelay = traceData.m_traceDelayCoarse * 100 + traceData.m_traceDelayFine; - traceData.setColor(m_focusedTraceColor); - traceData.m_viewTrace = ui->traceView->isChecked(); -} - -void GLScopeMultiGUI::fillTriggerData(ScopeVisMulti::TriggerData& triggerData) -{ - triggerData.m_projectionType = (ScopeVisMulti::ProjectionType) ui->trigMode->currentIndex(); - triggerData.m_inputIndex = 0; - triggerData.m_triggerLevel = (ui->trigLevelCoarse->value() / 100.0) + (ui->trigLevelFine->value() / 20000.0); - triggerData.m_triggerLevelCoarse = ui->trigLevelCoarse->value(); - triggerData.m_triggerLevelFine = ui->trigLevelFine->value(); - triggerData.m_triggerPositiveEdge = ui->trigPos->isChecked(); - triggerData.m_triggerBothEdges = ui->trigBoth->isChecked(); - triggerData.m_triggerRepeat = ui->trigCount->value(); - triggerData.m_triggerDelayMult = ui->trigDelayCoarse->value() + ui->trigDelayFine->value() / (ScopeVisMulti::m_traceChunkSize / 10.0); - triggerData.m_triggerDelay = (int) (m_traceLenMult * ScopeVisMulti::m_traceChunkSize * triggerData.m_triggerDelayMult); - triggerData.m_triggerDelayCoarse = ui->trigDelayCoarse->value(); - triggerData.m_triggerDelayFine = ui->trigDelayFine->value(); - triggerData.setColor(m_focusedTriggerColor); -} - -void GLScopeMultiGUI::setTraceUI(const ScopeVisMulti::TraceData& traceData) -{ - TraceUIBlocker traceUIBlocker(ui); - - ui->traceMode->setCurrentIndex((int) traceData.m_projectionType); - ui->amp->setValue(traceData.m_ampIndex); - setAmpScaleDisplay(); - - ui->ofsCoarse->setValue(traceData.m_ofsCoarse); - ui->ofsFine->setValue(traceData.m_ofsFine); - setAmpOfsDisplay(); - - ui->traceDelayCoarse->setValue(traceData.m_traceDelayCoarse); - ui->traceDelayFine->setValue(traceData.m_traceDelayFine); - setTraceDelayDisplay(); - - m_focusedTraceColor = traceData.m_traceColor; - int r, g, b, a; - m_focusedTraceColor.getRgb(&r, &g, &b, &a); - ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); - - ui->traceView->setChecked(traceData.m_viewTrace); -} - -void GLScopeMultiGUI::setTriggerUI(const ScopeVisMulti::TriggerData& triggerData) -{ - TrigUIBlocker trigUIBlocker(ui); - - ui->trigMode->setCurrentIndex((int) triggerData.m_projectionType); - ui->trigCount->setValue(triggerData.m_triggerRepeat); - setTrigCountDisplay(); - - ui->trigPos->setChecked(false); - ui->trigNeg->setChecked(false); - ui->trigPos->doToggle(false); - ui->trigNeg->doToggle(false); - - if (triggerData.m_triggerBothEdges) - { - ui->trigBoth->setChecked(true); - ui->trigBoth->doToggle(true); - } - else - { - ui->trigBoth->setChecked(false); - ui->trigBoth->doToggle(false); - - if (triggerData.m_triggerPositiveEdge) - { - ui->trigPos->setChecked(true); - ui->trigPos->doToggle(true); - } - else - { - ui->trigNeg->setChecked(true); - ui->trigNeg->doToggle(true); - } - } - - ui->trigLevelCoarse->setValue(triggerData.m_triggerLevelCoarse); - ui->trigLevelFine->setValue(triggerData.m_triggerLevelFine); - setTrigLevelDisplay(); - - ui->trigDelayCoarse->setValue(triggerData.m_triggerDelayCoarse); - ui->trigDelayFine->setValue(triggerData.m_triggerDelayFine); - setTrigDelayDisplay(); - - m_focusedTriggerColor = triggerData.m_triggerColor; - int r, g, b, a; - m_focusedTriggerColor.getRgb(&r, &g, &b, &a); - ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); -} - -void GLScopeMultiGUI::applySettings() -{ -} - -bool GLScopeMultiGUI::handleMessage(Message* message __attribute__((unused))) -{ - return false; -} - -GLScopeMultiGUI::TrigUIBlocker::TrigUIBlocker(Ui::GLScopeMultiGUI *ui) : - m_ui(ui) -{ - m_oldStateTrigMode = ui->trigMode->blockSignals(true); - m_oldStateTrigCount = ui->trigCount->blockSignals(true); - m_oldStateTrigPos = ui->trigPos->blockSignals(true); - m_oldStateTrigNeg = ui->trigNeg->blockSignals(true); - m_oldStateTrigBoth = ui->trigBoth->blockSignals(true); - m_oldStateTrigLevelCoarse = ui->trigLevelCoarse->blockSignals(true); - m_oldStateTrigLevelFine = ui->trigLevelFine->blockSignals(true); - m_oldStateTrigDelayCoarse = ui->trigDelayCoarse->blockSignals(true); - m_oldStateTrigDelayFine = ui->trigDelayFine->blockSignals(true); -} - -GLScopeMultiGUI::TrigUIBlocker::~TrigUIBlocker() -{ - unBlock(); -} - -void GLScopeMultiGUI::TrigUIBlocker::unBlock() -{ - m_ui->trigMode->blockSignals(m_oldStateTrigMode); - m_ui->trigCount->blockSignals(m_oldStateTrigCount); - m_ui->trigPos->blockSignals(m_oldStateTrigPos); - m_ui->trigNeg->blockSignals(m_oldStateTrigNeg); - m_ui->trigBoth->blockSignals(m_oldStateTrigBoth); - m_ui->trigLevelCoarse->blockSignals(m_oldStateTrigLevelCoarse); - m_ui->trigLevelFine->blockSignals(m_oldStateTrigLevelFine); - m_ui->trigDelayCoarse->blockSignals(m_oldStateTrigDelayCoarse); - m_ui->trigDelayFine->blockSignals(m_oldStateTrigDelayFine); -} - -GLScopeMultiGUI::TraceUIBlocker::TraceUIBlocker(Ui::GLScopeMultiGUI* ui) : - m_ui(ui) -{ - m_oldStateTrace = m_ui->trace->blockSignals(true); - m_oldStateTraceAdd = m_ui->traceAdd->blockSignals(true); - m_oldStateTraceDel = m_ui->traceDel->blockSignals(true); - m_oldStateTraceMode = m_ui->traceMode->blockSignals(true); - m_oldStateAmp = m_ui->amp->blockSignals(true); - m_oldStateOfsCoarse = m_ui->ofsCoarse->blockSignals(true); - m_oldStateOfsFine = m_ui->ofsFine->blockSignals(true); - m_oldStateTraceDelayCoarse = m_ui->traceDelayCoarse->blockSignals(true); - m_oldStateTraceDelayFine = m_ui->traceDelayFine->blockSignals(true); - m_oldStateTraceColor = m_ui->traceColor->blockSignals(true); -} - -GLScopeMultiGUI::TraceUIBlocker::~TraceUIBlocker() -{ - unBlock(); -} - -void GLScopeMultiGUI::TraceUIBlocker::unBlock() -{ - m_ui->trace->blockSignals(m_oldStateTrace); - m_ui->traceAdd->blockSignals(m_oldStateTraceAdd); - m_ui->traceDel->blockSignals(m_oldStateTraceDel); - m_ui->traceMode->blockSignals(m_oldStateTraceMode); - m_ui->amp->blockSignals(m_oldStateAmp); - m_ui->ofsCoarse->blockSignals(m_oldStateOfsCoarse); - m_ui->ofsFine->blockSignals(m_oldStateOfsFine); - m_ui->traceDelayCoarse->blockSignals(m_oldStateTraceDelayCoarse); - m_ui->traceDelayFine->blockSignals(m_oldStateTraceDelayFine); - m_ui->traceColor->blockSignals(m_oldStateTraceColor); -} - -GLScopeMultiGUI::MainUIBlocker::MainUIBlocker(Ui::GLScopeMultiGUI* ui) : - m_ui(ui) -{ - m_oldStateOnlyX = m_ui->onlyX->blockSignals(true); - m_oldStateOnlyY = m_ui->onlyY->blockSignals(true); - m_oldStateHorizontalXY = m_ui->horizontalXY->blockSignals(true); - m_oldStateVerticalXY = m_ui->verticalXY->blockSignals(true); - m_oldStatePolar = m_ui->polar->blockSignals(true); -// m_oldStateTime = m_ui->time->blockSignals(true); -// m_oldStateTimeOfs = m_ui->timeOfs->blockSignals(true); -// m_oldStateTraceLen = m_ui->traceLen->blockSignals(true); -} - -GLScopeMultiGUI::MainUIBlocker::~MainUIBlocker() -{ - unBlock(); -} - -void GLScopeMultiGUI::MainUIBlocker::unBlock() -{ - m_ui->onlyX->blockSignals(m_oldStateOnlyX); - m_ui->onlyY->blockSignals(m_oldStateOnlyY); - m_ui->horizontalXY->blockSignals(m_oldStateHorizontalXY); - m_ui->verticalXY->blockSignals(m_oldStateVerticalXY); - m_ui->polar->blockSignals(m_oldStatePolar); -// m_ui->time->blockSignals(m_oldStateTime); -// m_ui->timeOfs->blockSignals(m_oldStateTimeOfs); -// m_ui->traceLen->blockSignals(m_oldStateTraceLen); -} - -void GLScopeMultiGUI::setDisplayMode(DisplayMode displayMode) -{ - if (ui->trace->maximum() == 0) - { - ui->onlyX->setChecked(true); - } - else - { - switch (displayMode) - { - case DisplayX: - ui->onlyX->setChecked(true); - break; - case DisplayY: - ui->onlyY->setChecked(true); - break; - case DisplayXYH: - ui->horizontalXY->setChecked(true); - break; - case DisplayXYV: - ui->verticalXY->setChecked(true); - break; - case DisplayPol: - ui->polar->setChecked(true); - break; - default: - ui->onlyX->setChecked(true); - break; - } - } -} - -void GLScopeMultiGUI::setTraceIntensity(int value) -{ - if ((value < ui->traceIntensity->minimum()) || (value > ui->traceIntensity->maximum())) { - return; - } - - ui->traceIntensity->setValue(value); -} - -void GLScopeMultiGUI::setGridIntensity(int value) -{ - if ((value < ui->gridIntensity->minimum()) || (value > ui->gridIntensity->maximum())) { - return; - } - - ui->gridIntensity->setValue(value); -} - -void GLScopeMultiGUI::setTimeBase(int step) -{ - if ((step < ui->time->minimum()) || (step > ui->time->maximum())) { - return; - } - - ui->time->setValue(step); -} - -void GLScopeMultiGUI::setTimeOffset(int step) -{ - if ((step < ui->timeOfs->minimum()) || (step > ui->timeOfs->maximum())) { - return; - } - - ui->timeOfs->setValue(step); -} - -void GLScopeMultiGUI::setTraceLength(int step) -{ - if ((step < ui->traceLen->minimum()) || (step > ui->traceLen->maximum())) { - return; - } - - ui->traceLen->setValue(step); -} - -void GLScopeMultiGUI::setPreTrigger(int step) -{ - if ((step < ui->trigPre->minimum()) || (step > ui->trigPre->maximum())) { - return; - } - - ui->trigPre->setValue(step); -} - -void GLScopeMultiGUI::changeTrace(int traceIndex, const ScopeVisMulti::TraceData& traceData) -{ - m_scopeVis->changeTrace(traceData, traceIndex); -} - -void GLScopeMultiGUI::addTrace(const ScopeVisMulti::TraceData& traceData) -{ - if (ui->trace->maximum() < 3) - { - if (ui->trace->value() == 0) - { - ui->onlyY->setEnabled(true); - ui->horizontalXY->setEnabled(true); - ui->verticalXY->setEnabled(true); - ui->polar->setEnabled(true); - } - - m_scopeVis->addTrace(traceData); - ui->trace->setMaximum(ui->trace->maximum() + 1); - } -} - -void GLScopeMultiGUI::focusOnTrace(int traceIndex) -{ - on_trace_valueChanged(traceIndex); -} - -void GLScopeMultiGUI::changeTrigger(int triggerIndex, const ScopeVisMulti::TriggerData& triggerData) -{ - m_scopeVis->changeTrigger(triggerData, triggerIndex); -} - -void GLScopeMultiGUI::addTrigger(const ScopeVisMulti::TriggerData& triggerData) -{ - if (ui->trig->maximum() < 9) - { - m_scopeVis->addTrigger(triggerData); - ui->trig->setMaximum(ui->trig->maximum() + 1); - } -} - -void GLScopeMultiGUI::focusOnTrigger(int triggerIndex) -{ - on_trig_valueChanged(triggerIndex); -} - - diff --git a/sdrgui/gui/glscopemultigui.h b/sdrgui/gui/glscopemultigui.h deleted file mode 100644 index 291d1ca9b..000000000 --- a/sdrgui/gui/glscopemultigui.h +++ /dev/null @@ -1,235 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// 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_GUI_GLSCOPEMULTIGUI_H_ -#define SDRBASE_GUI_GLSCOPEMULTIGUI_H_ - -#include -#include - -#include "dsp/dsptypes.h" -#include "export.h" -#include "util/message.h" -#include "dsp/scopevismulti.h" - -namespace Ui { - class GLScopeMultiGUI; -} - -class MessageQueue; -class GLScopeMulti; - -class SDRGUI_API GLScopeMultiGUI : public QWidget { - Q_OBJECT - -public: - enum DisplayMode { - DisplayXYH, - DisplayXYV, - DisplayX, - DisplayY, - DisplayPol - }; - - explicit GLScopeMultiGUI(QWidget* parent = 0); - ~GLScopeMultiGUI(); - - void setBuddies(MessageQueue* messageQueue, ScopeVisMulti* scopeVis, GLScopeMulti* glScope); - - void setSampleRate(int sampleRate); - void resetToDefaults(); - QByteArray serialize() const; - bool deserialize(const QByteArray& data); - - bool handleMessage(Message* message); - - // preconfiguration methods - // global (first line): - void setDisplayMode(DisplayMode displayMode); - void setTraceIntensity(int value); - void setGridIntensity(int value); - void setTimeBase(int step); - void setTimeOffset(int step); - void setTraceLength(int step); - void setPreTrigger(int step); - // trace (second line): - void changeTrace(int traceIndex, const ScopeVisMulti::TraceData& traceData); - void addTrace(const ScopeVisMulti::TraceData& traceData); - void focusOnTrace(int traceIndex); - // trigger (third line): - void changeTrigger(int triggerIndex, const ScopeVisMulti::TriggerData& triggerData); - void addTrigger(const ScopeVisMulti::TriggerData& triggerData); - void focusOnTrigger(int triggerIndex); - -private: - class TrigUIBlocker - { - public: - TrigUIBlocker(Ui::GLScopeMultiGUI *ui); - ~TrigUIBlocker(); - - void unBlock(); - - private: - Ui::GLScopeMultiGUI *m_ui; - bool m_oldStateTrigMode; - bool m_oldStateTrigCount; - bool m_oldStateTrigPos; - bool m_oldStateTrigNeg; - bool m_oldStateTrigBoth; - bool m_oldStateTrigLevelCoarse; - bool m_oldStateTrigLevelFine; - bool m_oldStateTrigDelayCoarse; - bool m_oldStateTrigDelayFine; - }; - - class TraceUIBlocker - { - public: - TraceUIBlocker(Ui::GLScopeMultiGUI *ui); - ~TraceUIBlocker(); - - void unBlock(); - - private: - Ui::GLScopeMultiGUI *m_ui; - bool m_oldStateTrace; - bool m_oldStateTraceAdd; - bool m_oldStateTraceDel; - bool m_oldStateTraceMode; - bool m_oldStateAmp; - bool m_oldStateOfsCoarse; - bool m_oldStateOfsFine; - bool m_oldStateTraceDelayCoarse; - bool m_oldStateTraceDelayFine; - bool m_oldStateTraceColor; - }; - - class MainUIBlocker - { - public: - MainUIBlocker(Ui::GLScopeMultiGUI *ui); - ~MainUIBlocker(); - - void unBlock(); - - private: - Ui::GLScopeMultiGUI *m_ui; - bool m_oldStateOnlyX; - bool m_oldStateOnlyY; - bool m_oldStateHorizontalXY; - bool m_oldStateVerticalXY; - bool m_oldStatePolar; -// bool m_oldStateTime; -// bool m_oldStateTimeOfs; -// bool m_oldStateTraceLen; - }; - - Ui::GLScopeMultiGUI* ui; - - MessageQueue* m_messageQueue; - ScopeVisMulti* m_scopeVis; - GLScopeMulti* m_glScope; - - int m_sampleRate; - int m_timeBase; - int m_timeOffset; - int m_traceLenMult; - QColor m_focusedTraceColor; - QColor m_focusedTriggerColor; - - static const double amps[11]; - - void applySettings(); - // First row - void setTraceIndexDisplay(); - void setTimeScaleDisplay(); - void setTraceLenDisplay(); - void setTimeOfsDisplay(); - // Second row - void setAmpScaleDisplay(); - void setAmpOfsDisplay(); - void setTraceDelayDisplay(); - // Third row - void setTrigIndexDisplay(); - void setTrigCountDisplay(); - void setTrigLevelDisplay(); - void setTrigDelayDisplay(); - void setTrigPreDisplay(); - - void changeCurrentTrace(); - void changeCurrentTrigger(); - - void fillTraceData(ScopeVisMulti::TraceData& traceData); - void fillTriggerData(ScopeVisMulti::TriggerData& triggerData); - void setTriggerUI(const ScopeVisMulti::TriggerData& triggerData); - void setTraceUI(const ScopeVisMulti::TraceData& traceData); - - void fillProjectionCombo(QComboBox* comboBox); - void disableLiveMode(bool disable); - -private slots: - void on_scope_sampleRateChanged(int value); - // First row - void on_onlyX_toggled(bool checked); - void on_onlyY_toggled(bool checked); - void on_horizontalXY_toggled(bool checked); - void on_verticalXY_toggled(bool checked); - void on_polar_toggled(bool checked); - void on_traceIntensity_valueChanged(int value); - void on_gridIntensity_valueChanged(int value); - void on_time_valueChanged(int value); - void on_timeOfs_valueChanged(int value); - void on_traceLen_valueChanged(int value); - // Second row - void on_trace_valueChanged(int value); - void on_traceAdd_clicked(bool checked); - void on_traceDel_clicked(bool checked); - void on_traceUp_clicked(bool checked); - void on_traceDown_clicked(bool checked); - void on_traceMode_currentIndexChanged(int index); - void on_amp_valueChanged(int value); - void on_ofsCoarse_valueChanged(int value); - void on_ofsFine_valueChanged(int value); - void on_traceDelayCoarse_valueChanged(int value); - void on_traceDelayFine_valueChanged(int value); - void on_traceView_toggled(bool checked); - void on_traceColor_clicked(); - void on_mem_valueChanged(int value); - // Third row - void on_trig_valueChanged(int value); - void on_trigAdd_clicked(bool checked); - void on_trigDel_clicked(bool checked); - void on_trigUp_clicked(bool checked); - void on_trigDown_clicked(bool checked); - void on_trigMode_currentIndexChanged(int index); - void on_trigCount_valueChanged(int value); - void on_trigPos_toggled(bool checked); - void on_trigNeg_toggled(bool checked); - void on_trigBoth_toggled(bool checked); - void on_trigLevelCoarse_valueChanged(int value); - void on_trigLevelFine_valueChanged(int value); - void on_trigDelayCoarse_valueChanged(int value); - void on_trigDelayFine_valueChanged(int value); - void on_trigPre_valueChanged(int value); - void on_trigColor_clicked(); - void on_trigOneShot_toggled(bool checked); - void on_freerun_toggled(bool checked); -}; - - -#endif /* SDRBASE_GUI_GLSCOPEMULTIGUI_H_ */ diff --git a/sdrgui/gui/glscopemultigui.ui b/sdrgui/gui/glscopemultigui.ui deleted file mode 100644 index e9b51bb4a..000000000 --- a/sdrgui/gui/glscopemultigui.ui +++ /dev/null @@ -1,1908 +0,0 @@ - - - GLScopeMultiGUI - - - - 0 - 0 - 750 - 121 - - - - - 750 - 0 - - - - - Liberation Sans - 8 - - - - Oscilloscope - - - - 1 - - - 2 - - - 1 - - - 2 - - - 1 - - - - - 2 - - - 0 - - - 0 - - - - - - 24 - 24 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - Show only X trace (trace #0) - - - X - - - true - - - false - - - - - - - - 24 - 24 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - Show only Y traces - - - Y - - - - - - - - 24 - 24 - - - - Display X and Y traces side by side - - - ... - - - - :/horizontal_w.png:/horizontal_w.png - - - - - - - - 24 - 24 - - - - Display X and Y trances on top of each other - - - ... - - - - :/vertical_w.png:/vertical_w.png - - - - - - - - 24 - 24 - - - - Display XY traces and polar trace - - - ... - - - - :/constellation.png:/constellation.png - - - - - - - - 0 - 0 - - - - - 24 - 24 - - - - Trace intensity - - - 100 - - - 1 - - - 50 - - - - - - - - 24 - 24 - - - - Grid intensity - - - 100 - - - 1 - - - 10 - - - - - - - Qt::Vertical - - - - - - - Time range - - - T: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Time range - - - 1 - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - Time offset - - - O: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Time offset - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - Trace length - - - L: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Trace length - - - 1 - - - 20 - - - 1 - - - 1 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - - 52 - 0 - - - - Currently displayed trace sample rate (kS/s) - - - 00000.00 -kS/s - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - Qt::Horizontal - - - - - - - 2 - - - - - - 24 - 0 - - - - Tra - - - - - - - - 15 - 0 - - - - 0 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 24 - 24 - - - - Trace index (0 is X trace) - - - 0 - - - 1 - - - - - - - 0 - - - - - - 18 - 18 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - - 10 - - - - Add a new Y trace - - - + - - - - - - - - 18 - 18 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - - 10 - - - - Remove current Y trace - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 18 - 18 - - - - Move trace up in trace list (wraps around) - - - - - - - :/arrow_up.png:/arrow_up.png - - - - - - - - 18 - 18 - - - - Move trace down in trace list - - - - - - - :/arrow_down.png:/arrow_down.png - - - - - - - - - Qt::Vertical - - - - - - - - 65 - 16777215 - - - - Trace mode - - - - Real - - - - - Imag - - - - - Mag - - - - - MagdB - - - - - Phi - - - - - dPhi - - - - - - - - - 24 - 24 - - - - Source port selection - - - 0 - - - 1 - - - - - - - - 12 - 0 - - - - Source port number - - - 0 - - - - - - - Qt::Vertical - - - - - - - Vertical range - - - A: - - - - - - - - 36 - 0 - - - - Vertical range value - - - 000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Vertical range - - - 10 - - - 1 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - Vertical offset - - - O: - - - - - - - - 36 - 0 - - - - Vertical offset value - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - - - - 16777215 - 14 - - - - Vertical offset coarse - - - -100 - - - 1 - - - Qt::Horizontal - - - - - - - - 16777215 - 14 - - - - Vertical offset fine - - - 200 - - - 1 - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - - - - Trace delay - - - D: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Trace delay coarse - - - 48 - - - 1 - - - Qt::Horizontal - - - - - - - Trace delay fine - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - - - - View trace toggle - - - - - - true - - - - - - - - 16 - 16 - - - - - 16 - 16 - - - - Current trace color (click to change) - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - - - - M: - - - - - - - Trace memory index (greater is older) - - - 00 - - - - - - - - 24 - 24 - - - - Select trace memory (0 is live) - - - 15 - - - 1 - - - - - - - - - Qt::Horizontal - - - - - - - 2 - - - - - - 24 - 0 - - - - Trig - - - - - - - - 15 - 0 - - - - 0 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 24 - 24 - - - - Select trigger in trigger chain - - - 0 - - - 1 - - - - - - - 0 - - - - - - 18 - 18 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - - 10 - - - - Add a new trigger to the trigger chain - - - + - - - - - - - - 18 - 18 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - - 10 - - - - Remove current trigger from the chain - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 18 - 18 - - - - Move trigger up in trigger chain (wraps around) - - - - - - - :/arrow_up.png:/arrow_up.png - - - - - - - - 18 - 18 - - - - Move trigger down in trigger chain - - - - - - - :/arrow_down.png:/arrow_down.png - - - - - - - - - Qt::Vertical - - - - - - - - 65 - 16777215 - - - - Trigger mode - - - - Real - - - - - Imag - - - - - Mag - - - - - MagdB - - - - - Phi - - - - - dPhi - - - - - - - - - 24 - 24 - - - - 0 - - - 1 - - - - - - - - 12 - 0 - - - - 0 - - - - - - - Qt::Vertical - - - - - - - - 24 - 24 - - - - Number of trigger condition before trigger is actuated - - - 100 - - - 1 - - - - - - - 00 - - - - - - - - 24 - 24 - - - - Trigger on rising edge - - - ... - - - - :/slopep_icon.png:/slopep_icon.png - - - - - - - - 24 - 24 - - - - Trigger on falling edge - - - ... - - - - :/slopen_icon.png:/slopen_icon.png - - - - - - - - 24 - 24 - - - - Trigger on both edges - - - ... - - - - :/slopeb_icon.png:/slopeb_icon.png - - - - - - - Qt::Vertical - - - - - - - Trigger level - - - L: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - - - - 16777215 - 14 - - - - Trigger level coarse - - - -100 - - - 1 - - - Qt::Horizontal - - - - - - - - 16777215 - 14 - - - - Trigger level fine - - - 200 - - - 1 - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - - - - Trigger delay - - - D: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Trigger delay coarse (trace length multiplier) - - - 100 - - - 1 - - - Qt::Horizontal - - - - - - - Trigger delay fine (trace length divider) - - - 480 - - - 1 - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - - - - Pre-trigger delay - - - P: - - - - - - - - 40 - 0 - - - - 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Pre-trigger delay - - - 1 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 16 - 16 - - - - - 16 - 16 - - - - Current trigger color (click to change) - - - - - - - - - - - 24 - 24 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - One shot trigger - - - 1 - - - - - - - - 24 - 24 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - - - 190 - 190 - 190 - - - - - - - - Freerun (de-activate triggers) - - - F - - - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
- - ClickableLabel - QLabel -
gui/clickablelabel.h
-
-
- - - - -
diff --git a/sdrgui/readme.md b/sdrgui/readme.md index 10f0cc163..5b976e15e 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -147,11 +147,72 @@ This is where the plugin GUI specific to the device is displayed. Control of one

2.1. Start or stop acquisition

+
2.1.1 left click
+ +Left click to start or stop streaming + - When a play icon (▶) is displayed with a grey background the device is not operational - When a play icon (▶) is displayed with a blue background the device is ready to start - When a stop icon (■) is displayed with a green background the device is currently running - When a play icon (▶) is displayed with a red background there is an error and a popup displays the error message. An Error typically occurs when you try to start the same device in more than one tab. +
2.1.2 right click
+ +Right click to control device reverse API. This dialog opens: + +![Basic device settings](../doc/img/BasicDeviceSettings.png) + +
2.1.2.1: Toggle reverse API feature
+ +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the device settings are forwarded to an API endpoint given by address (2.1.2.2), port (2.1.2.3) and device index (2.1.2.4) in the same format as the SDRangel REST API device settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/device/settings` The JSON payload follows the same format as the SDRangel REST API device settings. For example with HachRF Rx this would be something like: + +``` +{ + "deviceHwType": "HackRF", + "hackRFInputSettings": { + "LOppmTenths": 0, + "bandwidth": 1750000, + "biasT": 0, + "centerFrequency": 435000000, + "dcBlock": 0, + "devSampleRate": 2400000, + "fcPos": 2, + "iqCorrection": 0, + "linkTxFrequency": 0, + "lnaExt": 0, + "lnaGain": 16, + "log2Decim": 0, + "vgaGain": 16 + }, + "tx": 0 +} +``` +Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. + +The start and stop actions are also forwarded with the `/sdrangel/deviceset/{deviceSetIndex}/device/run` API endpoint using POST (start) or DELETE (stop) methods. + +More details on this feature can be found on the corresponding Wiki page. + +
2.1.2.2: API address
+ +This is the IP address of the API endpoint + +
2.1.2.3: API port
+ +This is the IP port of the API endpoint + +
2.1.2.4: Device index
+ +This is the targeted device index + +
2.1.2.5: Cancel changes and exit dialog
+ +Do not make any changes and exit dialog + +
2.1.2.6: Validate and exit dialog
+ +Validates the data (saves it in the channel marker object) and exits the dialog +

2.2. Record I/Q

This is the I/Q from device record toggle. When a red background is displayed the recording is currently active. The name of the file created is `test_n.sdriq` where `n` is the slot number. @@ -612,7 +673,7 @@ This area shows the control GUIs of the channels currently active for the device Details about the GUIs can be found in the channel plugins documentation which consists of a readme.md file in each of the channel plugins folder (done partially). -With these channels: AM demod, BFM demod, DSD demod, NFM demod, UDP source, UDP sink some common basic settings can be set with a popup dialog. This dialog is opened by clicking on the small grey square on the top left of the channel window. The settings are as follows: +With most channel types some common basic settings can be set with a popup dialog. This dialog is opened by clicking on the small grey square on the top left of the channel window. The settings are as follows: ![Basic channel settings](../doc/img/BasicChannelSettings.png) @@ -633,14 +694,63 @@ When the mouse is over the channel window or over the central line in the spectr - AdSnd: UDP address and send port - AdRcv: UDP address and receive port -

6.4: Validate and exit dialog

+

6.4: Toggle reverse API feature

-Validates the data (saves it in the channel marker object) and exits the dialog +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (6.5), port (6.6), device index (6.7) and channel index (6.8) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: -

6.5: Cancel changes and exit dialog

+``` +{ + "SSBDemodSettings": { + "agc": 0, + "agcClamping": 0, + "agcPowerThreshold": -40, + "agcThresholdGate": 4, + "agcTimeLog2": 7, + "audioBinaural": 0, + "audioDeviceName": "System default device", + "audioFlipChannels": 0, + "audioMute": 0, + "dsb": 0, + "inputFrequencyOffset": 0, + "lowCutoff": 300, + "rfBandwidth": 3000, + "rgbColor": -16711936, + "spanLog2": 3, + "title": "SSB Demodulator", + "volume": 3 + }, + "channelType": "SSBDemod", + "tx": 0 +} +``` +Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. + +More details on this feature can be found on the corresponding Wiki page. + +

6.5: API address

+ +This is the IP address of the API endpoint + +

6.6: API port

+ +This is the IP port of the API endpoint + +

6.7: Device index

+ +This is the targeted device index + +

6.8: Channel index

+ +This is the targeted channel index + +

6.9: Cancel changes and exit dialog

Do not make any changes and exit dialog +

6.10: Validate and exit dialog

+ +Validates the data (saves it in the channel marker object) and exits the dialog +

7. Spectrum from device

This shows the spectrum in the passband returned from the sampling device possibly after decimation. The actual sample rate is shown in the device control at the left of the frequency display (2.3) diff --git a/sdrgui/sdrgui.pro b/sdrgui/sdrgui.pro index e863575b9..e011f1d46 100644 --- a/sdrgui/sdrgui.pro +++ b/sdrgui/sdrgui.pro @@ -49,10 +49,12 @@ SOURCES += mainwindow.cpp\ gui/aboutdialog.cpp\ gui/addpresetdialog.cpp\ gui/basicchannelsettingsdialog.cpp\ + gui/basicdevicesettingsdialog.cpp\ gui/buttonswitch.cpp\ gui/channelwindow.cpp\ gui/clickablelabel.cpp\ gui/colormapper.cpp\ + gui/comboboxnoarrow.cpp\ gui/commanditem.cpp\ gui/commandkeyreceiver.cpp\ gui/commandoutputdialog.cpp\ @@ -108,10 +110,12 @@ HEADERS += mainwindow.h\ gui/audiodialog.h\ gui/audioselectdialog.h\ gui/basicchannelsettingsdialog.h\ + gui/basicdevicesettingsdialog.h\ gui/buttonswitch.h\ gui/channelwindow.h\ gui/clickablelabel.h\ gui/colormapper.h\ + gui/comboboxnoarrow.h\ gui/commanditem.h\ gui/commandkeyreceiver.h\ gui/commandoutputdialog.h\ @@ -159,6 +163,7 @@ FORMS += mainwindow.ui\ gui/scopewindow.ui\ gui/addpresetdialog.ui\ gui/basicchannelsettingsdialog.ui\ + gui/basicdevicesettingsdialog.ui\ gui/commandoutputdialog.ui\ gui/cwkeyergui.ui\ gui/editcommanddialog.ui\ diff --git a/serialdv/CMakeLists.txt b/serialdv/CMakeLists.txt index f120477be..f02cb35e7 100644 --- a/serialdv/CMakeLists.txt +++ b/serialdv/CMakeLists.txt @@ -22,6 +22,8 @@ add_library(serialdv SHARED ${serialdv_SOURCES} ) +set_target_properties(serialdv PROPERTIES DEFINE_SYMBOL "serialdv_EXPORTS") + target_link_libraries(serialdv ${LIBUSB_LIBRARIES} ) diff --git a/swagger/CMakeLists.txt b/swagger/CMakeLists.txt index 92cd86f9b..f027ae662 100644 --- a/swagger/CMakeLists.txt +++ b/swagger/CMakeLists.txt @@ -25,13 +25,15 @@ include_directories( ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client ) -set_target_properties(swagger PROPERTIES COMPILE_FLAGS "-Wno-conversion-null -Wno-unused-variable -Wno-unused-parameter") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_target_properties(swagger PROPERTIES COMPILE_FLAGS "-Wno-conversion-null -Wno-unused-variable -Wno-unused-parameter") +endif() target_link_libraries(swagger ${QT_LIBRARIES} ) -set_target_properties(swagger PROPERTIES DEFINE_SYMBOL "sdrangel_EXPORTS") +set_target_properties(swagger PROPERTIES DEFINE_SYMBOL "swagger_EXPORTS") target_link_libraries(swagger Qt5::Core Qt5::Network) diff --git a/swagger/sdrangel/api/swagger/include/AMDemod.yaml b/swagger/sdrangel/api/swagger/include/AMDemod.yaml index 08c6f601d..22aaa5aa4 100644 --- a/swagger/sdrangel/api/swagger/include/AMDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/AMDemod.yaml @@ -33,6 +33,17 @@ AMDemodSettings: syncAMOperation: description: Synchronous AM sidebands mode (DSB, USB, LSB) type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer AMDemodReport: description: AMDemod diff --git a/swagger/sdrangel/api/swagger/include/AMMod.yaml b/swagger/sdrangel/api/swagger/include/AMMod.yaml index 7393f2d7e..9d8fc89d1 100644 --- a/swagger/sdrangel/api/swagger/include/AMMod.yaml +++ b/swagger/sdrangel/api/swagger/include/AMMod.yaml @@ -29,9 +29,20 @@ AMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "http://localhost:8081/api/swagger/include/CWKeyer.yaml#/CWKeyerSettings" - + AMModReport: description: AMMod properties: diff --git a/swagger/sdrangel/api/swagger/include/ATVMod.yaml b/swagger/sdrangel/api/swagger/include/ATVMod.yaml index 58acb2067..5c04ee404 100644 --- a/swagger/sdrangel/api/swagger/include/ATVMod.yaml +++ b/swagger/sdrangel/api/swagger/include/ATVMod.yaml @@ -53,7 +53,18 @@ ATVModSettings: type: string videoFileName: type: string - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + ATVModReport: description: ATVMod properties: diff --git a/swagger/sdrangel/api/swagger/include/BFMDemod.yaml b/swagger/sdrangel/api/swagger/include/BFMDemod.yaml index 65b979633..6b1fb54fc 100644 --- a/swagger/sdrangel/api/swagger/include/BFMDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/BFMDemod.yaml @@ -30,6 +30,17 @@ BFMDemodSettings: type: string audioDeviceName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer BFMDemodReport: description: BFMDemod @@ -54,7 +65,7 @@ BFMDemodReport: format: float rdsReport: $ref: "#/RDSReport" - + RDSReport: description: RDS information properties: diff --git a/swagger/sdrangel/api/swagger/include/DSDDemod.yaml b/swagger/sdrangel/api/swagger/include/DSDDemod.yaml index 91895f382..024995803 100644 --- a/swagger/sdrangel/api/swagger/include/DSDDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/DSDDemod.yaml @@ -54,6 +54,17 @@ DSDDemodSettings: traceDecay: description: 0 to 255 type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer DSDDemodReport: description: DSDDemod @@ -96,5 +107,4 @@ DSDDemodReport: statusText: description: mode dependent status messages (ref UI documentation) type: string - - \ No newline at end of file + diff --git a/swagger/sdrangel/api/swagger/include/DaemonSink.yaml b/swagger/sdrangel/api/swagger/include/DaemonSink.yaml index f381ced78..787afb7f1 100644 --- a/swagger/sdrangel/api/swagger/include/DaemonSink.yaml +++ b/swagger/sdrangel/api/swagger/include/DaemonSink.yaml @@ -14,6 +14,17 @@ DaemonSinkSettings: description: "Minimum delay in ms between consecutive USB blocks transmissions" type: integer rgbColor: - type: integer + type: integer title: - type: string + type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer diff --git a/swagger/sdrangel/api/swagger/include/DaemonSource.yaml b/swagger/sdrangel/api/swagger/include/DaemonSource.yaml index 36f13f348..745b3df89 100644 --- a/swagger/sdrangel/api/swagger/include/DaemonSource.yaml +++ b/swagger/sdrangel/api/swagger/include/DaemonSource.yaml @@ -11,7 +11,18 @@ DaemonSourceSettings: type: integer title: type: string - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + DaemonSourceReport: description: "Daemon channel source report" properties: diff --git a/swagger/sdrangel/api/swagger/include/LimeSdr.yaml b/swagger/sdrangel/api/swagger/include/LimeSdr.yaml index bc4b90408..eb7921cd0 100644 --- a/swagger/sdrangel/api/swagger/include/LimeSdr.yaml +++ b/swagger/sdrangel/api/swagger/include/LimeSdr.yaml @@ -53,7 +53,16 @@ LimeSdrInputSettings: gpioPins: type: integer format: int8 - + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + LimeSdrOutputSettings: description: LimeSDR properties: @@ -95,6 +104,15 @@ LimeSdrOutputSettings: gpioPins: type: integer format: int8 + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer LimeSdrInputReport: description: LimeSDR diff --git a/swagger/sdrangel/api/swagger/include/NFMMod.yaml b/swagger/sdrangel/api/swagger/include/NFMMod.yaml index 591eaa3b0..669a6af6b 100644 --- a/swagger/sdrangel/api/swagger/include/NFMMod.yaml +++ b/swagger/sdrangel/api/swagger/include/NFMMod.yaml @@ -35,6 +35,17 @@ NFMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "http://localhost:8081/api/swagger/include/CWKeyer.yaml#/CWKeyerSettings" diff --git a/swagger/sdrangel/api/swagger/include/RtlSdr.yaml b/swagger/sdrangel/api/swagger/include/RtlSdr.yaml index 1c4c0fde2..e2089605d 100644 --- a/swagger/sdrangel/api/swagger/include/RtlSdr.yaml +++ b/swagger/sdrangel/api/swagger/include/RtlSdr.yaml @@ -35,6 +35,15 @@ RtlSdrSettings: type: integer fileRecordName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer RtlSdrReport: description: RTLSDR diff --git a/swagger/sdrangel/api/swagger/include/SSBMod.yaml b/swagger/sdrangel/api/swagger/include/SSBMod.yaml index d66cbc458..dcdfb5439 100644 --- a/swagger/sdrangel/api/swagger/include/SSBMod.yaml +++ b/swagger/sdrangel/api/swagger/include/SSBMod.yaml @@ -53,6 +53,17 @@ SSBModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "http://localhost:8081/api/swagger/include/CWKeyer.yaml#/CWKeyerSettings" diff --git a/swagger/sdrangel/api/swagger/include/TestSource.yaml b/swagger/sdrangel/api/swagger/include/TestSource.yaml index d5c88645c..4c4d6ae96 100644 --- a/swagger/sdrangel/api/swagger/include/TestSource.yaml +++ b/swagger/sdrangel/api/swagger/include/TestSource.yaml @@ -40,4 +40,13 @@ TestSourceSettings: format: float fileRecordName: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer \ No newline at end of file diff --git a/swagger/sdrangel/api/swagger/include/UDPSource.yaml b/swagger/sdrangel/api/swagger/include/UDPSource.yaml index 46e8b680b..5f3193a13 100644 --- a/swagger/sdrangel/api/swagger/include/UDPSource.yaml +++ b/swagger/sdrangel/api/swagger/include/UDPSource.yaml @@ -35,20 +35,31 @@ UDPSourceSettings: type: number format: float squelchEnabled: - type: integer + type: integer autoRWBalance: - type: integer + type: integer stereoInput: - type: integer + type: integer rgbColor: - type: integer + type: integer udpAddress: type: string udpPort: type: integer - format: uint16 + format: uint16 title: type: string + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer UDPSourceReport: description: UDPSource @@ -69,4 +80,3 @@ UDPSourceReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/swagger/sdrangel/api/swagger/include/WFMMod.yaml b/swagger/sdrangel/api/swagger/include/WFMMod.yaml index 35d1e389d..6088a1426 100644 --- a/swagger/sdrangel/api/swagger/include/WFMMod.yaml +++ b/swagger/sdrangel/api/swagger/include/WFMMod.yaml @@ -31,6 +31,17 @@ WFMModSettings: type: string modAFInput: type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer cwKeyer: $ref: "http://localhost:8081/api/swagger/include/CWKeyer.yaml#/CWKeyerSettings" @@ -45,4 +56,3 @@ WFMModReport: type: integer channelSampleRate: type: integer - \ No newline at end of file diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 54b9dc825..7e20021dd 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -760,6 +760,22 @@ margin-bottom: 20px; "syncAMOperation" : { "type" : "integer", "description" : "Synchronous AM sidebands mode (DSB, USB, LSB)" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "AMDemod" @@ -821,6 +837,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -916,6 +948,22 @@ margin-bottom: 20px; }, "videoFileName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "ATVMod" @@ -1261,6 +1309,22 @@ margin-bottom: 20px; }, "audioDeviceName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "BFMDemod" @@ -1834,6 +1898,22 @@ margin-bottom: 20px; "traceDecay" : { "type" : "integer", "description" : "0 to 255" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "DSDDemod" @@ -1887,6 +1967,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "Daemon channel sink settings" @@ -1963,6 +2059,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "Daemon channel source settings" @@ -2715,6 +2827,19 @@ margin-bottom: 20px; "gpioPins" : { "type" : "integer", "format" : "int8" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "LimeSDR" @@ -2824,6 +2949,19 @@ margin-bottom: 20px; "gpioPins" : { "type" : "integer", "format" : "int8" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "LimeSDR" @@ -3013,6 +3151,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -3521,6 +3675,19 @@ margin-bottom: 20px; }, "fileRecordName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "RTLSDR" @@ -3897,6 +4064,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -4292,6 +4475,19 @@ margin-bottom: 20px; }, "fileRecordName" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" } }, "description" : "TestSource" @@ -4490,6 +4686,22 @@ margin-bottom: 20px; }, "title" : { "type" : "string" + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" } }, "description" : "UDPSource" @@ -4612,6 +4824,22 @@ margin-bottom: 20px; "modAFInput" : { "type" : "integer" }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, "cwKeyer" : { "$ref" : "#/definitions/CWKeyerSettings" } @@ -23649,7 +23877,7 @@ except ApiException as e:
- Generated 2018-11-29T00:50:52.609+01:00 + Generated 2018-12-22T12:54:35.895+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp index 6770781ed..8edcf14b2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp @@ -50,6 +50,16 @@ SWGAMDemodSettings::SWGAMDemodSettings() { m_pll_isSet = false; sync_am_operation = 0; m_sync_am_operation_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGAMDemodSettings::~SWGAMDemodSettings() { @@ -80,6 +90,16 @@ SWGAMDemodSettings::init() { m_pll_isSet = false; sync_am_operation = 0; m_sync_am_operation_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -99,6 +119,13 @@ SWGAMDemodSettings::cleanup() { } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGAMDemodSettings* @@ -134,6 +161,16 @@ SWGAMDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&sync_am_operation, pJson["syncAMOperation"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -183,6 +220,21 @@ SWGAMDemodSettings::asJsonObject() { if(m_sync_am_operation_isSet){ obj->insert("syncAMOperation", QJsonValue(sync_am_operation)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -297,6 +349,56 @@ SWGAMDemodSettings::setSyncAmOperation(qint32 sync_am_operation) { this->m_sync_am_operation_isSet = true; } +qint32 +SWGAMDemodSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGAMDemodSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGAMDemodSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGAMDemodSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGAMDemodSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGAMDemodSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGAMDemodSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGAMDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGAMDemodSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGAMDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGAMDemodSettings::isSet(){ @@ -313,6 +415,11 @@ SWGAMDemodSettings::isSet(){ if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_pll_isSet){ isObjectUpdated = true; break;} if(m_sync_am_operation_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h index 8959d26d3..5604cf757 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h @@ -75,6 +75,21 @@ public: qint32 getSyncAmOperation(); void setSyncAmOperation(qint32 sync_am_operation); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -112,6 +127,21 @@ private: qint32 sync_am_operation; bool m_sync_am_operation_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGAMModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGAMModSettings.cpp index 6877cc37b..4a8172493 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGAMModSettings.cpp @@ -50,6 +50,16 @@ SWGAMModSettings::SWGAMModSettings() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = nullptr; m_cw_keyer_isSet = false; } @@ -82,6 +92,16 @@ SWGAMModSettings::init() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = new SWGCWKeyerSettings(); m_cw_keyer_isSet = false; } @@ -103,6 +123,13 @@ SWGAMModSettings::cleanup() { delete audio_device_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + if(cw_keyer != nullptr) { delete cw_keyer; } @@ -141,6 +168,16 @@ SWGAMModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + ::SWGSDRangel::setValue(&cw_keyer, pJson["cwKeyer"], "SWGCWKeyerSettings", "SWGCWKeyerSettings"); } @@ -192,6 +229,21 @@ SWGAMModSettings::asJsonObject() { if(m_mod_af_input_isSet){ obj->insert("modAFInput", QJsonValue(mod_af_input)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } if((cw_keyer != nullptr) && (cw_keyer->isSet())){ toJsonValue(QString("cwKeyer"), cw_keyer, obj, QString("SWGCWKeyerSettings")); } @@ -309,6 +361,56 @@ SWGAMModSettings::setModAfInput(qint32 mod_af_input) { this->m_mod_af_input_isSet = true; } +qint32 +SWGAMModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGAMModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGAMModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGAMModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGAMModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGAMModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGAMModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGAMModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGAMModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGAMModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + SWGCWKeyerSettings* SWGAMModSettings::getCwKeyer() { return cw_keyer; @@ -335,6 +437,11 @@ SWGAMModSettings::isSet(){ if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_mod_af_input_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} if(cw_keyer != nullptr && cw_keyer->isSet()){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; diff --git a/swagger/sdrangel/code/qt5/client/SWGAMModSettings.h b/swagger/sdrangel/code/qt5/client/SWGAMModSettings.h index 53ebe1549..28d067989 100644 --- a/swagger/sdrangel/code/qt5/client/SWGAMModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGAMModSettings.h @@ -76,6 +76,21 @@ public: qint32 getModAfInput(); void setModAfInput(qint32 mod_af_input); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + SWGCWKeyerSettings* getCwKeyer(); void setCwKeyer(SWGCWKeyerSettings* cw_keyer); @@ -116,6 +131,21 @@ private: qint32 mod_af_input; bool m_mod_af_input_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + SWGCWKeyerSettings* cw_keyer; bool m_cw_keyer_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGATVModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGATVModSettings.cpp index 30559acf8..a6c765006 100644 --- a/swagger/sdrangel/code/qt5/client/SWGATVModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGATVModSettings.cpp @@ -74,6 +74,16 @@ SWGATVModSettings::SWGATVModSettings() { m_image_file_name_isSet = false; video_file_name = nullptr; m_video_file_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGATVModSettings::~SWGATVModSettings() { @@ -128,6 +138,16 @@ SWGATVModSettings::init() { m_image_file_name_isSet = false; video_file_name = new QString(""); m_video_file_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -163,6 +183,13 @@ SWGATVModSettings::cleanup() { if(video_file_name != nullptr) { delete video_file_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGATVModSettings* @@ -222,6 +249,16 @@ SWGATVModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&video_file_name, pJson["videoFileName"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -307,6 +344,21 @@ SWGATVModSettings::asJsonObject() { if(video_file_name != nullptr && *video_file_name != QString("")){ toJsonValue(QString("videoFileName"), video_file_name, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -541,6 +593,56 @@ SWGATVModSettings::setVideoFileName(QString* video_file_name) { this->m_video_file_name_isSet = true; } +qint32 +SWGATVModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGATVModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGATVModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGATVModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGATVModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGATVModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGATVModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGATVModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGATVModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGATVModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGATVModSettings::isSet(){ @@ -569,6 +671,11 @@ SWGATVModSettings::isSet(){ if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(image_file_name != nullptr && *image_file_name != QString("")){ isObjectUpdated = true; break;} if(video_file_name != nullptr && *video_file_name != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGATVModSettings.h b/swagger/sdrangel/code/qt5/client/SWGATVModSettings.h index 65836bf5f..6bfe9f3cd 100644 --- a/swagger/sdrangel/code/qt5/client/SWGATVModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGATVModSettings.h @@ -111,6 +111,21 @@ public: QString* getVideoFileName(); void setVideoFileName(QString* video_file_name); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -184,6 +199,21 @@ private: QString* video_file_name; bool m_video_file_name_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp index 07285f789..42edd857a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp @@ -52,6 +52,16 @@ SWGBFMDemodSettings::SWGBFMDemodSettings() { m_title_isSet = false; audio_device_name = nullptr; m_audio_device_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGBFMDemodSettings::~SWGBFMDemodSettings() { @@ -84,6 +94,16 @@ SWGBFMDemodSettings::init() { m_title_isSet = false; audio_device_name = new QString(""); m_audio_device_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -104,6 +124,13 @@ SWGBFMDemodSettings::cleanup() { if(audio_device_name != nullptr) { delete audio_device_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGBFMDemodSettings* @@ -141,6 +168,16 @@ SWGBFMDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -193,6 +230,21 @@ SWGBFMDemodSettings::asJsonObject() { if(audio_device_name != nullptr && *audio_device_name != QString("")){ toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -317,6 +369,56 @@ SWGBFMDemodSettings::setAudioDeviceName(QString* audio_device_name) { this->m_audio_device_name_isSet = true; } +qint32 +SWGBFMDemodSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGBFMDemodSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGBFMDemodSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGBFMDemodSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGBFMDemodSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGBFMDemodSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGBFMDemodSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGBFMDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGBFMDemodSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGBFMDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGBFMDemodSettings::isSet(){ @@ -334,6 +436,11 @@ SWGBFMDemodSettings::isSet(){ if(m_rgb_color_isSet){ isObjectUpdated = true; break;} if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h index da74f3352..4b15eb0ec 100644 --- a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h @@ -78,6 +78,21 @@ public: QString* getAudioDeviceName(); void setAudioDeviceName(QString* audio_device_name); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -118,6 +133,21 @@ private: QString* audio_device_name; bool m_audio_device_name_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp index fccdf2e25..dfb05305a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.cpp @@ -72,6 +72,16 @@ SWGDSDDemodSettings::SWGDSDDemodSettings() { m_trace_stroke_isSet = false; trace_decay = 0; m_trace_decay_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGDSDDemodSettings::~SWGDSDDemodSettings() { @@ -124,6 +134,16 @@ SWGDSDDemodSettings::init() { m_trace_stroke_isSet = false; trace_decay = 0; m_trace_decay_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -154,6 +174,13 @@ SWGDSDDemodSettings::cleanup() { + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGDSDDemodSettings* @@ -211,6 +238,16 @@ SWGDSDDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&trace_decay, pJson["traceDecay"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -293,6 +330,21 @@ SWGDSDDemodSettings::asJsonObject() { if(m_trace_decay_isSet){ obj->insert("traceDecay", QJsonValue(trace_decay)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -517,6 +569,56 @@ SWGDSDDemodSettings::setTraceDecay(qint32 trace_decay) { this->m_trace_decay_isSet = true; } +qint32 +SWGDSDDemodSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGDSDDemodSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGDSDDemodSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGDSDDemodSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGDSDDemodSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGDSDDemodSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGDSDDemodSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGDSDDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGDSDDemodSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGDSDDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGDSDDemodSettings::isSet(){ @@ -544,6 +646,11 @@ SWGDSDDemodSettings::isSet(){ if(m_trace_length_mutliplier_isSet){ isObjectUpdated = true; break;} if(m_trace_stroke_isSet){ isObjectUpdated = true; break;} if(m_trace_decay_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h index 91e44660e..678ab20ad 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDSDDemodSettings.h @@ -108,6 +108,21 @@ public: qint32 getTraceDecay(); void setTraceDecay(qint32 trace_decay); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -178,6 +193,21 @@ private: qint32 trace_decay; bool m_trace_decay_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.cpp index 586402538..c067be0e3 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.cpp @@ -40,6 +40,16 @@ SWGDaemonSinkSettings::SWGDaemonSinkSettings() { m_rgb_color_isSet = false; title = nullptr; m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGDaemonSinkSettings::~SWGDaemonSinkSettings() { @@ -60,6 +70,16 @@ SWGDaemonSinkSettings::init() { m_rgb_color_isSet = false; title = new QString(""); m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -74,6 +94,13 @@ SWGDaemonSinkSettings::cleanup() { if(title != nullptr) { delete title; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGDaemonSinkSettings* @@ -99,6 +126,16 @@ SWGDaemonSinkSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -133,6 +170,21 @@ SWGDaemonSinkSettings::asJsonObject() { if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -197,6 +249,56 @@ SWGDaemonSinkSettings::setTitle(QString* title) { this->m_title_isSet = true; } +qint32 +SWGDaemonSinkSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGDaemonSinkSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGDaemonSinkSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGDaemonSinkSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGDaemonSinkSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGDaemonSinkSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGDaemonSinkSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGDaemonSinkSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGDaemonSinkSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGDaemonSinkSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGDaemonSinkSettings::isSet(){ @@ -208,6 +310,11 @@ SWGDaemonSinkSettings::isSet(){ if(m_tx_delay_isSet){ isObjectUpdated = true; break;} if(m_rgb_color_isSet){ isObjectUpdated = true; break;} if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.h b/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.h index b0b285ff3..522766381 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDaemonSinkSettings.h @@ -60,6 +60,21 @@ public: QString* getTitle(); void setTitle(QString* title); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -82,6 +97,21 @@ private: QString* title; bool m_title_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.cpp index 287f4f324..222433d36 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.cpp @@ -36,6 +36,16 @@ SWGDaemonSourceSettings::SWGDaemonSourceSettings() { m_rgb_color_isSet = false; title = nullptr; m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGDaemonSourceSettings::~SWGDaemonSourceSettings() { @@ -52,6 +62,16 @@ SWGDaemonSourceSettings::init() { m_rgb_color_isSet = false; title = new QString(""); m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -64,6 +84,13 @@ SWGDaemonSourceSettings::cleanup() { if(title != nullptr) { delete title; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGDaemonSourceSettings* @@ -85,6 +112,16 @@ SWGDaemonSourceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -113,6 +150,21 @@ SWGDaemonSourceSettings::asJsonObject() { if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -157,6 +209,56 @@ SWGDaemonSourceSettings::setTitle(QString* title) { this->m_title_isSet = true; } +qint32 +SWGDaemonSourceSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGDaemonSourceSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGDaemonSourceSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGDaemonSourceSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGDaemonSourceSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGDaemonSourceSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGDaemonSourceSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGDaemonSourceSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGDaemonSourceSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGDaemonSourceSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGDaemonSourceSettings::isSet(){ @@ -166,6 +268,11 @@ SWGDaemonSourceSettings::isSet(){ if(m_data_port_isSet){ isObjectUpdated = true; break;} if(m_rgb_color_isSet){ isObjectUpdated = true; break;} if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.h b/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.h index 7fecec636..6bdab70dd 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDaemonSourceSettings.h @@ -54,6 +54,21 @@ public: QString* getTitle(); void setTitle(QString* title); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -70,6 +85,21 @@ private: QString* title; bool m_title_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.cpp index 2fe4db37c..ee85db37c 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.cpp @@ -76,6 +76,14 @@ SWGLimeSdrInputSettings::SWGLimeSdrInputSettings() { m_gpio_dir_isSet = false; gpio_pins = 0; m_gpio_pins_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } SWGLimeSdrInputSettings::~SWGLimeSdrInputSettings() { @@ -132,6 +140,14 @@ SWGLimeSdrInputSettings::init() { m_gpio_dir_isSet = false; gpio_pins = 0; m_gpio_pins_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } void @@ -162,6 +178,12 @@ SWGLimeSdrInputSettings::cleanup() { } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + } SWGLimeSdrInputSettings* @@ -223,6 +245,14 @@ SWGLimeSdrInputSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gpio_pins, pJson["gpioPins"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + } QString @@ -311,6 +341,18 @@ SWGLimeSdrInputSettings::asJsonObject() { if(m_gpio_pins_isSet){ obj->insert("gpioPins", QJsonValue(gpio_pins)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } return obj; } @@ -555,6 +597,46 @@ SWGLimeSdrInputSettings::setGpioPins(qint32 gpio_pins) { this->m_gpio_pins_isSet = true; } +qint32 +SWGLimeSdrInputSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGLimeSdrInputSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGLimeSdrInputSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGLimeSdrInputSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGLimeSdrInputSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGLimeSdrInputSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGLimeSdrInputSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGLimeSdrInputSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + bool SWGLimeSdrInputSettings::isSet(){ @@ -584,6 +666,10 @@ SWGLimeSdrInputSettings::isSet(){ if(file_record_name != nullptr && *file_record_name != QString("")){ isObjectUpdated = true; break;} if(m_gpio_dir_isSet){ isObjectUpdated = true; break;} if(m_gpio_pins_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.h b/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.h index 4672f90b2..448baf589 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeSdrInputSettings.h @@ -114,6 +114,18 @@ public: qint32 getGpioPins(); void setGpioPins(qint32 gpio_pins); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + virtual bool isSet() override; @@ -190,6 +202,18 @@ private: qint32 gpio_pins; bool m_gpio_pins_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.cpp index f808a8d9b..6fbd6cb50 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.cpp @@ -62,6 +62,14 @@ SWGLimeSdrOutputSettings::SWGLimeSdrOutputSettings() { m_gpio_dir_isSet = false; gpio_pins = 0; m_gpio_pins_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } SWGLimeSdrOutputSettings::~SWGLimeSdrOutputSettings() { @@ -104,6 +112,14 @@ SWGLimeSdrOutputSettings::init() { m_gpio_dir_isSet = false; gpio_pins = 0; m_gpio_pins_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } void @@ -125,6 +141,12 @@ SWGLimeSdrOutputSettings::cleanup() { + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + } SWGLimeSdrOutputSettings* @@ -172,6 +194,14 @@ SWGLimeSdrOutputSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gpio_pins, pJson["gpioPins"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + } QString @@ -239,6 +269,18 @@ SWGLimeSdrOutputSettings::asJsonObject() { if(m_gpio_pins_isSet){ obj->insert("gpioPins", QJsonValue(gpio_pins)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } return obj; } @@ -413,6 +455,46 @@ SWGLimeSdrOutputSettings::setGpioPins(qint32 gpio_pins) { this->m_gpio_pins_isSet = true; } +qint32 +SWGLimeSdrOutputSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGLimeSdrOutputSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGLimeSdrOutputSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGLimeSdrOutputSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGLimeSdrOutputSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGLimeSdrOutputSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGLimeSdrOutputSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGLimeSdrOutputSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + bool SWGLimeSdrOutputSettings::isSet(){ @@ -435,6 +517,10 @@ SWGLimeSdrOutputSettings::isSet(){ if(m_transverter_delta_frequency_isSet){ isObjectUpdated = true; break;} if(m_gpio_dir_isSet){ isObjectUpdated = true; break;} if(m_gpio_pins_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.h b/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.h index 5eeff69eb..ce64a8c25 100644 --- a/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGLimeSdrOutputSettings.h @@ -22,6 +22,7 @@ #include +#include #include "SWGObject.h" #include "export.h" @@ -92,6 +93,18 @@ public: qint32 getGpioPins(); void setGpioPins(qint32 gpio_pins); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + virtual bool isSet() override; @@ -147,6 +160,18 @@ private: qint32 gpio_pins; bool m_gpio_pins_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.cpp index 9bd1fe4de..f81d52d8a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.cpp @@ -56,6 +56,16 @@ SWGNFMModSettings::SWGNFMModSettings() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = nullptr; m_cw_keyer_isSet = false; } @@ -94,6 +104,16 @@ SWGNFMModSettings::init() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = new SWGCWKeyerSettings(); m_cw_keyer_isSet = false; } @@ -118,6 +138,13 @@ SWGNFMModSettings::cleanup() { delete audio_device_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + if(cw_keyer != nullptr) { delete cw_keyer; } @@ -162,6 +189,16 @@ SWGNFMModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + ::SWGSDRangel::setValue(&cw_keyer, pJson["cwKeyer"], "SWGCWKeyerSettings", "SWGCWKeyerSettings"); } @@ -222,6 +259,21 @@ SWGNFMModSettings::asJsonObject() { if(m_mod_af_input_isSet){ obj->insert("modAFInput", QJsonValue(mod_af_input)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } if((cw_keyer != nullptr) && (cw_keyer->isSet())){ toJsonValue(QString("cwKeyer"), cw_keyer, obj, QString("SWGCWKeyerSettings")); } @@ -369,6 +421,56 @@ SWGNFMModSettings::setModAfInput(qint32 mod_af_input) { this->m_mod_af_input_isSet = true; } +qint32 +SWGNFMModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGNFMModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGNFMModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGNFMModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGNFMModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGNFMModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGNFMModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGNFMModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGNFMModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGNFMModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + SWGCWKeyerSettings* SWGNFMModSettings::getCwKeyer() { return cw_keyer; @@ -398,6 +500,11 @@ SWGNFMModSettings::isSet(){ if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_mod_af_input_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} if(cw_keyer != nullptr && cw_keyer->isSet()){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; diff --git a/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.h b/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.h index 179c541b9..84f15fc3f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGNFMModSettings.h @@ -85,6 +85,21 @@ public: qint32 getModAfInput(); void setModAfInput(qint32 mod_af_input); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + SWGCWKeyerSettings* getCwKeyer(); void setCwKeyer(SWGCWKeyerSettings* cw_keyer); @@ -134,6 +149,21 @@ private: qint32 mod_af_input; bool m_mod_af_input_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + SWGCWKeyerSettings* cw_keyer; bool m_cw_keyer_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.cpp index 4300f89d3..545c208f9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.cpp @@ -60,6 +60,14 @@ SWGRtlSdrSettings::SWGRtlSdrSettings() { m_rf_bandwidth_isSet = false; file_record_name = nullptr; m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } SWGRtlSdrSettings::~SWGRtlSdrSettings() { @@ -100,6 +108,14 @@ SWGRtlSdrSettings::init() { m_rf_bandwidth_isSet = false; file_record_name = new QString(""); m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } void @@ -122,6 +138,12 @@ SWGRtlSdrSettings::cleanup() { if(file_record_name != nullptr) { delete file_record_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + } SWGRtlSdrSettings* @@ -167,6 +189,14 @@ SWGRtlSdrSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&file_record_name, pJson["fileRecordName"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + } QString @@ -231,6 +261,18 @@ SWGRtlSdrSettings::asJsonObject() { if(file_record_name != nullptr && *file_record_name != QString("")){ toJsonValue(QString("fileRecordName"), file_record_name, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } return obj; } @@ -395,6 +437,46 @@ SWGRtlSdrSettings::setFileRecordName(QString* file_record_name) { this->m_file_record_name_isSet = true; } +qint32 +SWGRtlSdrSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGRtlSdrSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGRtlSdrSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGRtlSdrSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGRtlSdrSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGRtlSdrSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGRtlSdrSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGRtlSdrSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + bool SWGRtlSdrSettings::isSet(){ @@ -416,6 +498,10 @@ SWGRtlSdrSettings::isSet(){ if(m_transverter_delta_frequency_isSet){ isObjectUpdated = true; break;} if(m_rf_bandwidth_isSet){ isObjectUpdated = true; break;} if(file_record_name != nullptr && *file_record_name != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.h b/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.h index 16c1dcdff..e1ac03a82 100644 --- a/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGRtlSdrSettings.h @@ -90,6 +90,18 @@ public: QString* getFileRecordName(); void setFileRecordName(QString* file_record_name); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + virtual bool isSet() override; @@ -142,6 +154,18 @@ private: QString* file_record_name; bool m_file_record_name_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.cpp index 599716f4b..fd42afd09 100644 --- a/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.cpp @@ -74,6 +74,16 @@ SWGSSBModSettings::SWGSSBModSettings() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = nullptr; m_cw_keyer_isSet = false; } @@ -130,6 +140,16 @@ SWGSSBModSettings::init() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = new SWGCWKeyerSettings(); m_cw_keyer_isSet = false; } @@ -163,6 +183,13 @@ SWGSSBModSettings::cleanup() { delete audio_device_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + if(cw_keyer != nullptr) { delete cw_keyer; } @@ -225,6 +252,16 @@ SWGSSBModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + ::SWGSDRangel::setValue(&cw_keyer, pJson["cwKeyer"], "SWGCWKeyerSettings", "SWGCWKeyerSettings"); } @@ -312,6 +349,21 @@ SWGSSBModSettings::asJsonObject() { if(m_mod_af_input_isSet){ obj->insert("modAFInput", QJsonValue(mod_af_input)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } if((cw_keyer != nullptr) && (cw_keyer->isSet())){ toJsonValue(QString("cwKeyer"), cw_keyer, obj, QString("SWGCWKeyerSettings")); } @@ -549,6 +601,56 @@ SWGSSBModSettings::setModAfInput(qint32 mod_af_input) { this->m_mod_af_input_isSet = true; } +qint32 +SWGSSBModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGSSBModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGSSBModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGSSBModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGSSBModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGSSBModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGSSBModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGSSBModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGSSBModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGSSBModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + SWGCWKeyerSettings* SWGSSBModSettings::getCwKeyer() { return cw_keyer; @@ -587,6 +689,11 @@ SWGSSBModSettings::isSet(){ if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_mod_af_input_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} if(cw_keyer != nullptr && cw_keyer->isSet()){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; diff --git a/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.h b/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.h index 62145591a..4d03d2809 100644 --- a/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGSSBModSettings.h @@ -112,6 +112,21 @@ public: qint32 getModAfInput(); void setModAfInput(qint32 mod_af_input); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + SWGCWKeyerSettings* getCwKeyer(); void setCwKeyer(SWGCWKeyerSettings* cw_keyer); @@ -188,6 +203,21 @@ private: qint32 mod_af_input; bool m_mod_af_input_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + SWGCWKeyerSettings* cw_keyer; bool m_cw_keyer_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.cpp index ac5689feb..5713e085e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.cpp @@ -62,6 +62,14 @@ SWGTestSourceSettings::SWGTestSourceSettings() { m_phase_imbalance_isSet = false; file_record_name = nullptr; m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } SWGTestSourceSettings::~SWGTestSourceSettings() { @@ -104,6 +112,14 @@ SWGTestSourceSettings::init() { m_phase_imbalance_isSet = false; file_record_name = new QString(""); m_file_record_name_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; } void @@ -127,6 +143,12 @@ SWGTestSourceSettings::cleanup() { if(file_record_name != nullptr) { delete file_record_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + } SWGTestSourceSettings* @@ -174,6 +196,14 @@ SWGTestSourceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&file_record_name, pJson["fileRecordName"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + } QString @@ -241,6 +271,18 @@ SWGTestSourceSettings::asJsonObject() { if(file_record_name != nullptr && *file_record_name != QString("")){ toJsonValue(QString("fileRecordName"), file_record_name, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } return obj; } @@ -415,6 +457,46 @@ SWGTestSourceSettings::setFileRecordName(QString* file_record_name) { this->m_file_record_name_isSet = true; } +qint32 +SWGTestSourceSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGTestSourceSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGTestSourceSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGTestSourceSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGTestSourceSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGTestSourceSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGTestSourceSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGTestSourceSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + bool SWGTestSourceSettings::isSet(){ @@ -437,6 +519,10 @@ SWGTestSourceSettings::isSet(){ if(m_q_factor_isSet){ isObjectUpdated = true; break;} if(m_phase_imbalance_isSet){ isObjectUpdated = true; break;} if(file_record_name != nullptr && *file_record_name != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.h b/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.h index ca65917ee..95e336f29 100644 --- a/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGTestSourceSettings.h @@ -93,6 +93,18 @@ public: QString* getFileRecordName(); void setFileRecordName(QString* file_record_name); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + virtual bool isSet() override; @@ -148,6 +160,18 @@ private: QString* file_record_name; bool m_file_record_name_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.cpp index 559453e37..f7ba156d2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.cpp @@ -66,6 +66,16 @@ SWGUDPSourceSettings::SWGUDPSourceSettings() { m_udp_port_isSet = false; title = nullptr; m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } SWGUDPSourceSettings::~SWGUDPSourceSettings() { @@ -112,6 +122,16 @@ SWGUDPSourceSettings::init() { m_udp_port_isSet = false; title = new QString(""); m_title_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; } void @@ -139,6 +159,13 @@ SWGUDPSourceSettings::cleanup() { if(title != nullptr) { delete title; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + } SWGUDPSourceSettings* @@ -190,6 +217,16 @@ SWGUDPSourceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + } QString @@ -263,6 +300,21 @@ SWGUDPSourceSettings::asJsonObject() { if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } return obj; } @@ -457,6 +509,56 @@ SWGUDPSourceSettings::setTitle(QString* title) { this->m_title_isSet = true; } +qint32 +SWGUDPSourceSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGUDPSourceSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGUDPSourceSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGUDPSourceSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGUDPSourceSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGUDPSourceSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGUDPSourceSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGUDPSourceSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGUDPSourceSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGUDPSourceSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + bool SWGUDPSourceSettings::isSet(){ @@ -481,6 +583,11 @@ SWGUDPSourceSettings::isSet(){ if(udp_address != nullptr && *udp_address != QString("")){ isObjectUpdated = true; break;} if(m_udp_port_isSet){ isObjectUpdated = true; break;} if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.h b/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.h index c13f47a3e..b8238bddf 100644 --- a/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGUDPSourceSettings.h @@ -99,6 +99,21 @@ public: QString* getTitle(); void setTitle(QString* title); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + virtual bool isSet() override; @@ -160,6 +175,21 @@ private: QString* title; bool m_title_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.cpp index 7a5089d6d..711edafb6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.cpp @@ -52,6 +52,16 @@ SWGWFMModSettings::SWGWFMModSettings() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = nullptr; m_cw_keyer_isSet = false; } @@ -86,6 +96,16 @@ SWGWFMModSettings::init() { m_audio_device_name_isSet = false; mod_af_input = 0; m_mod_af_input_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; cw_keyer = new SWGCWKeyerSettings(); m_cw_keyer_isSet = false; } @@ -108,6 +128,13 @@ SWGWFMModSettings::cleanup() { delete audio_device_name; } + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + if(cw_keyer != nullptr) { delete cw_keyer; } @@ -148,6 +175,16 @@ SWGWFMModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", ""); + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + ::SWGSDRangel::setValue(&cw_keyer, pJson["cwKeyer"], "SWGCWKeyerSettings", "SWGCWKeyerSettings"); } @@ -202,6 +239,21 @@ SWGWFMModSettings::asJsonObject() { if(m_mod_af_input_isSet){ obj->insert("modAFInput", QJsonValue(mod_af_input)); } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } if((cw_keyer != nullptr) && (cw_keyer->isSet())){ toJsonValue(QString("cwKeyer"), cw_keyer, obj, QString("SWGCWKeyerSettings")); } @@ -329,6 +381,56 @@ SWGWFMModSettings::setModAfInput(qint32 mod_af_input) { this->m_mod_af_input_isSet = true; } +qint32 +SWGWFMModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGWFMModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGWFMModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGWFMModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGWFMModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGWFMModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGWFMModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGWFMModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGWFMModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGWFMModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + SWGCWKeyerSettings* SWGWFMModSettings::getCwKeyer() { return cw_keyer; @@ -356,6 +458,11 @@ SWGWFMModSettings::isSet(){ if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} if(audio_device_name != nullptr && *audio_device_name != QString("")){ isObjectUpdated = true; break;} if(m_mod_af_input_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} if(cw_keyer != nullptr && cw_keyer->isSet()){ isObjectUpdated = true; break;} }while(false); return isObjectUpdated; diff --git a/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.h b/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.h index 49bc5b36e..33c0666a2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGWFMModSettings.h @@ -79,6 +79,21 @@ public: qint32 getModAfInput(); void setModAfInput(qint32 mod_af_input); + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + SWGCWKeyerSettings* getCwKeyer(); void setCwKeyer(SWGCWKeyerSettings* cw_keyer); @@ -122,6 +137,21 @@ private: qint32 mod_af_input; bool m_mod_af_input_isSet; + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + SWGCWKeyerSettings* cw_keyer; bool m_cw_keyer_isSet; diff --git a/swagger/sdrangel/examples/Readme.md b/swagger/sdrangel/examples/Readme.md index 4bf83a84d..f33fdc37b 100644 --- a/swagger/sdrangel/examples/Readme.md +++ b/swagger/sdrangel/examples/Readme.md @@ -102,6 +102,21 @@ It uses the following APIs: - URI: `/sdrangel/deviceset/{deviceSetIndex}/device/run` - HTTP method: `DELETE` +

reverseapi.py

+ +This script runs continuously to act as the server side of SDRangel reverse API feature. It will respond with a copy of the settings when forwarding channel or device settings which is a valid reply and will also send back a valid reply when forwarding the device start/stop actions. The reverse API feature was implemented in version 4.3.2 and you can learn more about it in the Wiki. + +It uses Python flask package for the server so you will need to either install it system wide or better create your own virtual environment and use the `pip install flask` command once your virtual environment is activated. If you know Python then you know what a virtual environment is about. + +Then in the virtual environment do: + +``` +export FLASK_APP=reverseapi.py +export FLASK_ENV=development +flask run +``` +By default the server will listen on port 5000. It was used to conveniently test the reverse API but actually does not do much. It can serve as a base to implement more complex scenarios to control other devices or software or other instances of SDRangel. +

rtlsdr_settings.py

Make sure a RTLSDR device is selected on device set #0. Get and change the settings of this RTLSDR device. diff --git a/swagger/sdrangel/examples/reverseapi.py b/swagger/sdrangel/examples/reverseapi.py new file mode 100644 index 000000000..6c61affee --- /dev/null +++ b/swagger/sdrangel/examples/reverseapi.py @@ -0,0 +1,57 @@ +''' Test reverse API +Run with: +export FLASK_APP=reverseapi.py +export FLASK_ENV=development # for debug mode and auto reload +flask run +''' +from flask import Flask +from flask import request, jsonify +app = Flask(__name__) + + +@app.route('/sdrangel') +def hello_sdrangel(): + return 'Hello, SDRangel!' + + +@app.route('/sdrangel/deviceset//device/run', methods=['GET', 'POST', 'DELETE']) +def device_run(deviceset_index): + ''' Reply with the expected reply of a working device ''' + if request.method == 'POST': + print("Start device %d" % deviceset_index) + reply = { "state": "idle" } + return jsonify(reply) + elif request.method == 'DELETE': + print("Stop device %d" % deviceset_index) + reply = { "state": "running" } + return jsonify(reply) + elif request.method == 'GET': + return "RUN device %d" % deviceset_index + + +@app.route('/sdrangel/deviceset//device/settings', methods=['GET', 'PATCH', 'PUT']) +def device_settings(deviceset_index): + ''' Reply with a copy of the device setting structure received ''' + content = request.get_json(silent=True) + if request.method == 'PATCH': + print("Partial update of device %d" % deviceset_index) + return jsonify(content) + if request.method == 'PUT': + print("Full update of device %d" % deviceset_index) + return jsonify(content) + if request.method == 'GET': + return 'GET settings for device %d' % deviceset_index + + +@app.route('/sdrangel/deviceset//channel//settings', methods=['GET', 'PATCH', 'PUT']) +def channel_settings(deviceset_index, channel_index): + ''' Reply with a copy of the channel setting structure received ''' + content = request.get_json(silent=True) + if request.method == 'PATCH': + print("Partial update of device %d channel %d" % (deviceset_index, channel_index)) + return jsonify(content) + if request.method == 'PUT': + print("Full update of device %d channel %d" % (deviceset_index, channel_index)) + return jsonify(content) + if request.method == 'GET': + return 'GET settings for device %d and channel %d' % (deviceset_index, channel_index)