From 6087160593e74aff9147153c69ea23849fc8b921 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 27 Jul 2022 12:05:05 +0200 Subject: Add PrecisionWave DEXTER support --- doc/example.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'doc') diff --git a/doc/example.ini b/doc/example.ini index aca7634..2105535 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -164,7 +164,7 @@ enabled=0 polycoeffile=polyCoefs [output] -; choose output: possible values: uhd, file, zmq, soapysdr, limesdr, bladerf +; choose output: possible values: uhd, file, zmq, dexter, soapysdr, limesdr, bladerf output=uhd [fileoutput] @@ -322,6 +322,11 @@ channel=13C ; Set to 0 to disable ;dpd_port=50055 +[dexteroutput] +txgain=32768 +;frequency=234208000 +channel=13C + [limeoutput] ; Lime output directly runs against the LMS device driver. It does not support SFN nor predistortion. device= -- cgit v1.2.3 From bf6e05a427e050ec54b9da91da8ac04f52fa006c Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 1 Feb 2023 13:53:55 +0100 Subject: Remove easydabv3 support --- .gitignore | 3 +++ Makefile.am | 9 ++------- configure.ac | 19 +++---------------- doc/easydabv3.ini | 37 ------------------------------------- src/DabModulator.cpp | 47 +++++++++++++++++++---------------------------- src/Utils.cpp | 16 ++-------------- 6 files changed, 29 insertions(+), 102 deletions(-) delete mode 100644 doc/easydabv3.ini (limited to 'doc') diff --git a/.gitignore b/.gitignore index 3ac59f6..2a6b136 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ config.h config.h.in config.status odr-dabmod +*~ + +*.iq __pycache__/ *.py[cod] diff --git a/Makefile.am b/Makefile.am index 46f2c21..64884e0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,7 @@ odr_dabmod_CFLAGS = -Wall -Isrc -Ilib \ $(GITVERSION_FLAGS) odr_dabmod_CXXFLAGS = -Wall -Isrc -Ilib -std=c++11 \ $(GITVERSION_FLAGS) $(BOOST_CPPFLAGS) -odr_dabmod_LDADD = $(BOOST_LDFLAGS) $(BOOST_THREAD_LIB) +odr_dabmod_LDADD = $(BOOST_LDFLAGS) $(BOOST_THREAD_LIB) $(UHD_LIBS) $(LIMESDR_LIBS) $(ADDITIONAL_UHD_LIBS) odr_dabmod_SOURCES = src/DabMod.cpp \ src/PcDebug.h \ src/DabModulator.cpp \ @@ -123,10 +123,7 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ lib/edi/ETIDecoder.hpp \ lib/edi/ETIDecoder.cpp \ lib/edi/PFT.hpp \ - lib/edi/PFT.cpp - -if !COMPILE_FOR_EASYDABV3 -odr_dabmod_SOURCES += \ + lib/edi/PFT.cpp \ src/FIRFilter.cpp \ src/FIRFilter.h \ src/MemlessPoly.cpp \ @@ -173,7 +170,5 @@ odr_dabmod_SOURCES += \ src/TII.cpp \ src/TII.h -odr_dabmod_LDADD += $(UHD_LIBS) $(LIMESDR_LIBS) $(ADDITIONAL_UHD_LIBS) -endif man_MANS = man/odr-dabmod.1 diff --git a/configure.ac b/configure.ac index 9b45a89..59ee4e5 100644 --- a/configure.ac +++ b/configure.ac @@ -52,9 +52,6 @@ AC_ARG_ENABLE([zeromq], AC_ARG_ENABLE([native], [AS_HELP_STRING([--disable-native], [Do not compile with -march=native])], [], [enable_native=yes]) -AC_ARG_ENABLE([easydabv3], - [AS_HELP_STRING([--enable-easydabv3], [Build for EasyDABv3 board])], - [], [enable_easydabv3=no]) AC_ARG_ENABLE([dexter], [AS_HELP_STRING([--enable-dexter], [Build for PrecisionWave Dexter board])], [], [enable_dexter=no]) @@ -80,9 +77,7 @@ AX_CHECK_COMPILE_FLAG([-Wdouble-promotion], [CXXFLAGS="$CXXFLAGS -Wdouble-promot AX_CHECK_COMPILE_FLAG(["-Wformat=2"], [CXXFLAGS="$CXXFLAGS -Wformat=2"], [], ["-Werror"]) AC_LANG_POP([C++]) - -AS_IF([test "x$enable_easydabv3" = "xno"], - [PKG_CHECK_MODULES([FFTW], [fftw3f], [], [AC_MSG_ERROR([FFTW is required])])]) +PKG_CHECK_MODULES([FFTW], [fftw3f], [], [AC_MSG_ERROR([FFTW is required])]) echo "Checking zeromq" @@ -104,13 +99,11 @@ AS_IF([test "x$enable_trace" != "xno"], # Define conditionals for Makefile.am AM_CONDITIONAL([IS_GIT_REPO], [test -d '.git']) -AM_CONDITIONAL([COMPILE_FOR_EASYDABV3], [test "x$enable_easydabv3" = "xyes"]) # Defines for config.h AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) -AS_IF([test "x$enable_easydabv3" = "xno"], - [PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], enable_soapysdr=yes, enable_soapysdr=no)]) +PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], enable_soapysdr=yes, enable_soapysdr=no) AS_IF([test "x$enable_limesdr" = "xyes"], [AC_CHECK_LIB([LimeSuite], [LMS_Init], [LIMESDR_LIBS="-lLimeSuite"], @@ -128,9 +121,6 @@ AC_SUBST([CFLAGS], ["$CFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAG AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $IIO_LIBS $BLADERF_LIBS"]) -AS_IF([test "x$enable_easydabv3" = "xyes" && test "x$enable_output_uhd" == "xyes"], - AC_MSG_ERROR([Cannot enable both EasyDABv3 and UHD output])) - # Checks for UHD. AS_IF([test "x$enable_output_uhd" = "xyes"], [ PKG_CHECK_MODULES([UHD], [uhd], [], [AC_MSG_ERROR([UHD is required])]) @@ -157,9 +147,6 @@ AS_IF([test "x$enable_dexter" = "xyes"], AS_IF([test "x$enable_bladerf" = "xyes"], [AC_DEFINE(HAVE_BLADERF, [1], [Define if BladeRF output is enabled]) ]) -AS_IF([test "x$enable_easydabv3" = "xyes"], - AC_DEFINE(BUILD_FOR_EASYDABV3, [1], [Define if we are building for EasyDABv3])) - # Checks for header files. AC_CHECK_HEADERS([fcntl.h limits.h memory.h netinet/in.h stdint.h stdlib.h string.h sys/time.h sys/timeb.h unistd.h]) @@ -227,7 +214,7 @@ echo "***********************************************" echo enabled="" disabled="" -for feat in prof trace output_uhd zeromq soapysdr easydabv3 limesdr bladerf dexter +for feat in prof trace output_uhd zeromq soapysdr limesdr bladerf dexter do eval var=\$enable_$feat AS_IF([test "x$var" = "xyes"], diff --git a/doc/easydabv3.ini b/doc/easydabv3.ini deleted file mode 100644 index 5f0103f..0000000 --- a/doc/easydabv3.ini +++ /dev/null @@ -1,37 +0,0 @@ -; This sample configuration is useful if ODR-DabMod is compiled -; with --enable-easydabv3 - -[remotecontrol] -zmqctrl=1 -zmqctrlendpoint=tcp://127.0.0.1:9400 -; There is no telnet RC available in this build - - -[log] -syslog=0 -filelog=0 -filename=odr-dabmod.log - -[input] -transport=zeromq -source=tcp://localhost:9100 -max_frames_queued=400 - -; There are no [modulator], [cfr], [firfilter], [poly] nor [tii] sections - -[output] -output=file - -[fileoutput] -; to be confirmed -format=complexf - -filename=/dev/csdiof1 - -show_metadata=0 -; TODO add option for writing out timestamps to csdiof1 - -[delaymanagement] -synchronous=0 -mutenotimestamps=0 -offset=1.002 diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index aa4f2a8..1f16d1d 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2019 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -31,32 +31,29 @@ #include "DabModulator.h" #include "PcDebug.h" -#if !defined(BUILD_FOR_EASYDABV3) -# include "QpskSymbolMapper.h" -# include "FrequencyInterleaver.h" -# include "PhaseReference.h" -# include "DifferentialModulator.h" -# include "NullSymbol.h" -# include "CicEqualizer.h" -# include "OfdmGenerator.h" -# include "GainControl.h" -# include "GuardIntervalInserter.h" -# include "Resampler.h" -# include "FIRFilter.h" -# include "MemlessPoly.h" -# include "TII.h" -#endif - -#include "FrameMultiplexer.h" -#include "PrbsGenerator.h" #include "BlockPartitioner.h" -#include "SignalMultiplexer.h" +#include "CicEqualizer.h" #include "ConvEncoder.h" +#include "DifferentialModulator.h" +#include "FIRFilter.h" +#include "FrameMultiplexer.h" +#include "FrequencyInterleaver.h" +#include "GainControl.h" +#include "GuardIntervalInserter.h" +#include "Log.h" +#include "MemlessPoly.h" +#include "NullSymbol.h" +#include "OfdmGenerator.h" +#include "PhaseReference.h" +#include "PrbsGenerator.h" #include "PuncturingEncoder.h" +#include "QpskSymbolMapper.h" +#include "RemoteControl.h" +#include "Resampler.h" +#include "SignalMultiplexer.h" +#include "TII.h" #include "TimeInterleaver.h" #include "TimestampDecoder.h" -#include "RemoteControl.h" -#include "Log.h" using namespace std; @@ -140,7 +137,6 @@ int DabModulator::process(Buffer* dataOut) auto cifMux = make_shared(myEtiSource); auto cifPart = make_shared(mode); -#if !defined(BUILD_FOR_EASYDABV3) auto cifMap = make_shared(myNbCarriers); auto cifRef = make_shared(mode); auto cifFreq = make_shared(mode); @@ -231,7 +227,6 @@ int DabModulator::process(Buffer* dataOut) m_settings.outputRate, mySpacing); } -#endif myOutput = make_shared(dataOut); @@ -340,9 +335,6 @@ int DabModulator::process(Buffer* dataOut) } myFlowgraph->connect(cifMux, cifPart); -#if defined(BUILD_FOR_EASYDABV3) - myFlowgraph->connect(cifPart, myOutput); -#else myFlowgraph->connect(cifPart, cifMap); myFlowgraph->connect(cifMap, cifFreq); myFlowgraph->connect(cifRef, cifDiff); @@ -372,7 +364,6 @@ int DabModulator::process(Buffer* dataOut) prev_plugin = p; } } -#endif } //////////////////////////////////////////////////////////////////// diff --git a/src/Utils.cpp b/src/Utils.cpp index f39c4c9..350838e 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2018 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -62,10 +62,6 @@ static void printHeader() "SSE " << #endif "\n"; - -#if defined(BUILD_FOR_EASYDABV3) - std::cerr << " This is a build for the EasyDABv3 board" << std::endl; -#endif } void printUsage(const char* progName) @@ -77,13 +73,8 @@ void printUsage(const char* progName) fprintf(out, "Usage with command line options:\n"); fprintf(out, "\t%s" " input" -#if defined(BUILD_FOR_EASYDABV3) - " -f filename -F format" -#else " (-f filename -F format | -u uhddevice -F frequency)" -#endif " [-o offset]" -#if !defined(BUILD_FOR_EASYDABV3) "\n\t" " [-G txgain]" " [-T filter_taps_file]" @@ -93,7 +84,6 @@ void printUsage(const char* progName) " [-g gainMode]" " [-m dabMode]" " [-r samplingRate]" -#endif " [-l]" " [-h]" "\n", progName); @@ -108,7 +98,6 @@ void printUsage(const char* progName) fprintf(out, " Specifying this option has two implications: It enables synchronous transmission,\n" " requiring an external REFCLK and PPS signal and frames that do not contain a valid timestamp\n" " get muted.\n\n"); -#if !defined(BUILD_FOR_EASYDABV3) fprintf(out, "-u device: Use UHD output with given device string. (use "" for default device)\n"); fprintf(out, "-F frequency: Set the transmit frequency when using UHD output. (mandatory option when using UHD)\n"); fprintf(out, "-G txgain: Set the transmit gain for the UHD driver (default: 0)\n"); @@ -119,7 +108,6 @@ void printUsage(const char* progName) fprintf(out, "-g gainmode: Set computation gain mode: fix, max or var\n"); fprintf(out, "-m mode: Set DAB mode: (0: auto, 1-4: force).\n"); fprintf(out, "-r rate: Set output sampling rate (default: 2048000).\n\n"); -#endif fprintf(out, "-l: Loop file when reach end of file.\n"); fprintf(out, "-h: Print this help.\n"); } @@ -132,7 +120,7 @@ void printVersion(void) " ODR-DabMod is copyright (C) Her Majesty the Queen in Right of Canada,\n" " 2005 -- 2012 Communications Research Centre (CRC),\n" " and\n" - " Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li\n" + " Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li\n" "\n" " http://opendigitalradio.org\n" "\n" -- cgit v1.2.3 From 0aec6da11b4add62ac473e3f4ea813bb4a8a556d Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 1 Feb 2023 14:03:03 +0100 Subject: Remove ZeroMQ input --- Makefile.am | 1 - README.md | 2 +- doc/example.ini | 7 - src/ConfigParser.cpp | 7 +- src/ConfigParser.h | 5 +- src/DabMod.cpp | 71 +--------- src/InputFileReader.cpp | 3 +- src/InputReader.h | 61 --------- src/InputZeroMQReader.cpp | 323 ---------------------------------------------- src/Utils.cpp | 1 - 10 files changed, 7 insertions(+), 474 deletions(-) delete mode 100644 src/InputZeroMQReader.cpp (limited to 'doc') diff --git a/Makefile.am b/Makefile.am index 64884e0..fe566bb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -75,7 +75,6 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ src/InputMemory.h \ src/InputReader.h \ src/InputTcpReader.cpp \ - src/InputZeroMQReader.cpp \ src/OutputFile.cpp \ src/OutputFile.h \ src/FrameMultiplexer.cpp \ diff --git a/README.md b/README.md index a23de3d..23e5c36 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Features - TII insertion - Logging: log to file, to syslog - EDI sources: TCP and UDP, both with and without Protection and Fragmentation Layer. -- ETI sources: ETI-over-TCP, file (Raw, Framed and Streamed) and ZeroMQ +- ETI sources: ETI-over-TCP, file (Raw, Framed and Streamed) - A Telnet and ZeroMQ remote-control that can be used to change some parameters during runtime and retrieve statistics. See `doc/README-RC.md` for more information diff --git a/doc/example.ini b/doc/example.ini index 2105535..cd48ef4 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -69,13 +69,6 @@ loop=0 ;transport=tcp ;source=localhost:9200 -; When recieving data using ZeroMQ, the source is the URI to be used -;transport=zeromq -;source=tcp://localhost:9100 -; The option max_frames_queued defines the maximum number of ETI frames -; (frame duration: 24ms) that can be in the input queue -;max_frames_queued=100 - [modulator] ; Mode 'fix' uses a fixed factor and is really not recommended. It is more ; useful on an academic perspective for people trying to understand the DAB diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 9190c60..3e223c3 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2018 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -113,8 +113,6 @@ static void parse_configfile( } mod_settings.inputTransport = pt.Get("input.transport", "file"); - mod_settings.inputMaxFramesQueued = pt.GetInteger("input.max_frames_queued", - ZMQ_INPUT_MAX_FRAME_QUEUE); mod_settings.edi_max_delay_ms = pt.GetReal("input.edi_max_delay", 0.0f); @@ -574,8 +572,7 @@ void parse_args(int argc, char **argv, mod_settings_t& mod_settings) if (mod_settings.inputName.substr(0, 4) == "zmq+" && mod_settings.inputName.find("://") != std::string::npos) { - // if the name starts with zmq+XYZ://somewhere:port - mod_settings.inputTransport = "zeromq"; + throw std::runtime_error("Support for ZeroMQ input transport has been removed."); } else if (mod_settings.inputName.substr(0, 6) == "tcp://") { mod_settings.inputTransport = "tcp"; diff --git a/src/ConfigParser.h b/src/ConfigParser.h index 8f2a1d2..8681175 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2017 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -40,8 +40,6 @@ #include "output/Lime.h" #include "output/BladeRF.h" -#define ZMQ_INPUT_MAX_FRAME_QUEUE 500 - struct mod_settings_t { std::string outputName; bool useZeroMQOutput = false; @@ -69,7 +67,6 @@ struct mod_settings_t { bool loop = false; std::string inputName = ""; std::string inputTransport = "file"; - unsigned inputMaxFramesQueued = ZMQ_INPUT_MAX_FRAME_QUEUE; float edi_max_delay_ms = 0.0f; tii_config_t tiiConfig; diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 45f4d0a..57e6e32 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -381,17 +381,6 @@ int launch_modulator(int argc, char* argv[]) inputReader = inputFileReader; } - else if (mod_settings.inputTransport == "zeromq") { -#if !defined(HAVE_ZEROMQ) - throw std::runtime_error("Unable to open input: " - "ZeroMQ input transport selected, but not compiled in!"); -#else - auto inputZeroMQReader = make_shared(); - inputZeroMQReader->Open(mod_settings.inputName, mod_settings.inputMaxFramesQueued); - rcs.enrol(inputZeroMQReader.get()); - inputReader = inputZeroMQReader; -#endif - } else if (mod_settings.inputTransport == "tcp") { auto inputTcpReader = make_shared(); inputTcpReader->Open(mod_settings.inputName); @@ -460,17 +449,6 @@ int launch_modulator(int argc, char* argv[]) run_again = true; } } -#if defined(HAVE_ZEROMQ) - else if (auto in_zmq = dynamic_pointer_cast(inputReader)) { - run_again = true; - // Create a new input reader - rcs.remove_controllable(in_zmq.get()); - auto inputZeroMQReader = make_shared(); - inputZeroMQReader->Open(mod_settings.inputName, mod_settings.inputMaxFramesQueued); - rcs.enrol(inputZeroMQReader.get()); - inputReader = inputZeroMQReader; - } -#endif else if (dynamic_pointer_cast(inputReader)) { // Keep the same inputReader, as there is no input buffer overflow run_again = true; @@ -500,14 +478,6 @@ int launch_modulator(int argc, char* argv[]) return ret; } -struct zmq_input_timeout : public std::exception -{ - const char* what() const throw() - { - return "InputZMQ timeout"; - } -}; - static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, modulator_data& m) { auto ret = run_modulator_state_t::failure; @@ -535,36 +505,9 @@ static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, m ret = run_modulator_state_t::normal_end; break; } -#if defined(HAVE_ZEROMQ) - else if (dynamic_pointer_cast(m.inputReader)) { - /* An empty frame marks a timeout. We ignore it, but we are - * now able to handle SIGINT properly. - * - * Also, we reconnect zmq every 10 seconds to avoid some - * issues, discussed in - * https://stackoverflow.com/questions/26112992/zeromq-pub-sub-on-unreliable-connection - * - * > It is possible that the PUB socket sees the error - * > while the SUB socket does not. - * > - * > The ZMTP RFC has a proposal for heartbeating that would - * > solve this problem. The current best solution is for - * > PUB sockets to send heartbeats (e.g. 1 per second) when - * > traffic is low, and for SUB sockets to disconnect / - * > reconnect if they stop getting these. - * - * We don't need a heartbeat, because our application is constant frame rate, - * the frames themselves can act as heartbeats. - */ - - const auto now = chrono::steady_clock::now(); - if (last_frame_received + chrono::seconds(10) < now) { - throw zmq_input_timeout(); - } - } -#endif // defined(HAVE_ZEROMQ) else if (dynamic_pointer_cast(m.inputReader)) { - /* Same as for ZeroMQ */ + /* An empty frame marks a timeout. We ignore it, but we are + * now able to handle SIGINT properly. */ } else { throw logic_error("Unhandled framesize==0!"); @@ -681,16 +624,6 @@ static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, m } } } - catch (const zmq_input_timeout&) { - // The ZeroMQ input timeout - etiLog.level(warn) << "Timeout"; - ret = run_modulator_state_t::again; - } - catch (const zmq_input_overflow& e) { - // The ZeroMQ input has overflowed its buffer - etiLog.level(warn) << e.what(); - ret = run_modulator_state_t::again; - } catch (const FrameMultiplexerError& e) { // The FrameMultiplexer saw an error or a change in the size of a // subchannel. This can be due to a multiplex reconfiguration. diff --git a/src/InputFileReader.cpp b/src/InputFileReader.cpp index 5a9780b..a6b482e 100644 --- a/src/InputFileReader.cpp +++ b/src/InputFileReader.cpp @@ -6,8 +6,7 @@ Copyrigth (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li - - Input module for reading the ETI data from file or pipe, or ZeroMQ. + Input module for reading the ETI data from file or pipe. Supported file formats: RAW, FRAMED, STREAMED Supports re-sync to RAW ETI file diff --git a/src/InputReader.h b/src/InputReader.h index ab45d4f..2484948 100644 --- a/src/InputReader.h +++ b/src/InputReader.h @@ -38,11 +38,6 @@ #include #include #include -#if defined(HAVE_ZEROMQ) -# include "zmq.hpp" -# include "ThreadsafeQueue.h" -# include "RemoteControl.h" -#endif #include "Log.h" #include "Socket.h" #define INVALID_SOCKET -1 @@ -148,60 +143,4 @@ class InputTcpReader : public InputReader std::string m_uri; }; -struct zmq_input_overflow : public std::exception -{ - const char* what () const throw () - { - return "InputZMQ buffer overflow"; - } -}; - -#if defined(HAVE_ZEROMQ) -/* A ZeroMQ input. See www.zeromq.org for more info */ - -class InputZeroMQReader : public InputReader, public RemoteControllable -{ - public: - InputZeroMQReader(); - InputZeroMQReader(const InputZeroMQReader& other) = delete; - InputZeroMQReader& operator=(const InputZeroMQReader& other) = delete; - ~InputZeroMQReader(); - - int Open(const std::string& uri, size_t max_queued_frames); - virtual int GetNextFrame(void* buffer) override; - virtual std::string GetPrintableInfo() const override; - - /* Base function to set parameters. */ - virtual void set_parameter( - const std::string& parameter, - const std::string& value) override; - - /* Getting a parameter always returns a string. */ - virtual const std::string get_parameter( - const std::string& parameter) const override; - - private: - std::atomic m_running = ATOMIC_VAR_INIT(false); - std::string m_uri; - size_t m_max_queued_frames = 0; - - // Either must contain a full ETI frame, or one flag must be set - struct message_t { - std::vector eti_frame; - bool overflow = false; - bool timeout = false; - bool fault = false; - }; - ThreadsafeQueue m_in_messages; - - mutable std::mutex m_last_in_messages_size_mutex; - size_t m_last_in_messages_size = 0; - - void RecvProcess(void); - - zmq::context_t m_zmqcontext; // is thread-safe - std::thread m_recv_thread; -}; - -#endif diff --git a/src/InputZeroMQReader.cpp b/src/InputZeroMQReader.cpp deleted file mode 100644 index 40a07d4..0000000 --- a/src/InputZeroMQReader.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Her Majesty the Queen in Right of Canada (Communications Research - Center Canada) - - Copyright (C) 2018 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://opendigitalradio.org - */ -/* - This file is part of ODR-DabMod. - - ODR-DabMod 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, either version 3 of the - License, or (at your option) any later version. - - ODR-DabMod 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 for more details. - - You should have received a copy of the GNU General Public License - along with ODR-DabMod. If not, see . - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if defined(HAVE_ZEROMQ) - -#include -#include -#include -#include -#include "zmq.hpp" -#include "InputReader.h" -#include "PcDebug.h" -#include "Utils.h" - -using namespace std; - -constexpr int ZMQ_TIMEOUT_MS = 100; - -#define NUM_FRAMES_PER_ZMQ_MESSAGE 4 -/* A concatenation of four ETI frames, - * whose maximal size is 6144. - * - * Four frames in one zmq message are sent, so that - * we do not risk breaking ETI vs. transmission frame - * phase. - * - * The header is followed by the four ETI frames. - */ -struct zmq_msg_header_t -{ - uint32_t version; - uint16_t buflen[NUM_FRAMES_PER_ZMQ_MESSAGE]; -}; - -#define ZMQ_DAB_MESSAGE_T_HEADERSIZE \ - (sizeof(uint32_t) + NUM_FRAMES_PER_ZMQ_MESSAGE*sizeof(uint16_t)) - -InputZeroMQReader::InputZeroMQReader() : - InputReader(), - RemoteControllable("inputzmq") -{ - RC_ADD_PARAMETER(buffer, "Size of input buffer [us] (read-only)"); -} - -InputZeroMQReader::~InputZeroMQReader() -{ - m_running = false; - // This avoids the ugly "context was terminated" error because it lets - // poll do its thing first - this_thread::sleep_for(chrono::milliseconds(2 * ZMQ_TIMEOUT_MS)); - m_zmqcontext.close(); - if (m_recv_thread.joinable()) { - m_recv_thread.join(); - } -} - -int InputZeroMQReader::Open(const string& uri, size_t max_queued_frames) -{ - // The URL might start with zmq+tcp:// - if (uri.substr(0, 4) == "zmq+") { - m_uri = uri.substr(4); - } - else { - m_uri = uri; - } - - m_max_queued_frames = max_queued_frames; - - m_running = true; - m_recv_thread = std::thread(&InputZeroMQReader::RecvProcess, this); - - return 0; -} - -int InputZeroMQReader::GetNextFrame(void* buffer) -{ - if (not m_running) { - throw runtime_error("ZMQ input is not ready yet"); - } - - message_t incoming; - - /* Do some prebuffering because reads will happen in bursts - * (4 ETI frames in TM1) and we should make sure that - * we can serve the data required for a full transmission frame. - */ - if (m_in_messages.size() < 4) { - const size_t prebuffering = 10; - etiLog.log(trace, "ZMQ,wait1"); - m_in_messages.wait_and_pop(incoming, prebuffering); - } - else { - etiLog.log(trace, "ZMQ,wait2"); - m_in_messages.wait_and_pop(incoming); - } - etiLog.log(trace, "ZMQ,pop"); - - constexpr size_t framesize = 6144; - - if (incoming.timeout) { - return 0; - } - else if (incoming.fault) { - throw runtime_error("ZMQ input has terminated"); - } - else if (incoming.overflow) { - throw zmq_input_overflow(); - } - else if (incoming.eti_frame.size() == framesize) { - unique_lock lock(m_last_in_messages_size_mutex); - m_last_in_messages_size--; - lock.unlock(); - - memcpy(buffer, &incoming.eti_frame.front(), framesize); - - return framesize; - } - else { - throw logic_error("ZMQ ETI not 6144"); - } -} - -std::string InputZeroMQReader::GetPrintableInfo() const -{ - return "Input ZeroMQ: Receiving from " + m_uri; -} - -void InputZeroMQReader::RecvProcess() -{ - set_thread_name("zmqinput"); - - size_t queue_size = 0; - - zmq::socket_t subscriber(m_zmqcontext, ZMQ_SUB); - // zmq sockets are not thread safe. That's why - // we create it here, and not at object creation. - - bool success = true; - - try { - subscriber.connect(m_uri.c_str()); - } - catch (const zmq::error_t& err) { - etiLog.level(error) << "Failed to connect ZeroMQ socket to '" << - m_uri << "': '" << err.what() << "'"; - success = false; - } - - if (success) try { - // subscribe to all messages - subscriber.setsockopt(ZMQ_SUBSCRIBE, NULL, 0); - } - catch (const zmq::error_t& err) { - etiLog.level(error) << "Failed to subscribe ZeroMQ socket to messages: '" << - err.what() << "'"; - success = false; - } - - if (success) try { - while (m_running) { - zmq::message_t incoming; - zmq::pollitem_t items[1]; - items[0].socket = subscriber; - items[0].events = ZMQ_POLLIN; - const int num_events = zmq::poll(items, 1, ZMQ_TIMEOUT_MS); - if (num_events == 0) { - message_t msg; - msg.timeout = true; - m_in_messages.push(move(msg)); - continue; - } - - subscriber.recv(incoming); - - if (queue_size < m_max_queued_frames) { - if (incoming.size() < ZMQ_DAB_MESSAGE_T_HEADERSIZE) { - throw runtime_error("ZeroMQ packet too small for header"); - } - else { - zmq_msg_header_t dab_msg; - memcpy(&dab_msg, incoming.data(), sizeof(zmq_msg_header_t)); - - if (dab_msg.version != 1) { - etiLog.level(error) << - "ZeroMQ wrong packet version " << - dab_msg.version; - } - - int offset = sizeof(dab_msg.version) + - NUM_FRAMES_PER_ZMQ_MESSAGE * sizeof(*dab_msg.buflen); - - for (int i = 0; i < NUM_FRAMES_PER_ZMQ_MESSAGE; i++) { - if (dab_msg.buflen[i] > 6144) { - stringstream ss; - ss << "ZeroMQ buffer " << i << - " has invalid buflen " << dab_msg.buflen[i]; - throw runtime_error(ss.str()); - } - else { - vector buf(6144, 0x55); - - const int framesize = dab_msg.buflen[i]; - - if ((ssize_t)incoming.size() < offset + framesize) { - throw runtime_error("ZeroMQ packet too small"); - } - - memcpy(&buf.front(), - ((uint8_t*)incoming.data()) + offset, - framesize); - - offset += framesize; - - message_t msg; - msg.eti_frame = move(buf); - queue_size = m_in_messages.push(move(msg)); - etiLog.log(trace, "ZMQ,push %zu", queue_size); - - unique_lock lock(m_last_in_messages_size_mutex); - m_last_in_messages_size++; - } - } - } - } - else { - message_t msg; - msg.overflow = true; - queue_size = m_in_messages.push(move(msg)); - etiLog.level(warn) << "ZeroMQ buffer overfull !"; - throw runtime_error("ZMQ input full"); - } - - if (queue_size < 5) { - etiLog.level(warn) << "ZeroMQ buffer low: " << queue_size << " elements !"; - } - } - } - catch (const zmq::error_t& err) { - etiLog.level(error) << "ZeroMQ error during receive: '" << err.what() << "'"; - } - catch (const std::exception& err) { - etiLog.level(error) << "Exception during receive: '" << err.what() << "'"; - } - - m_running = false; - - etiLog.level(info) << "ZeroMQ input worker terminated"; - - subscriber.close(); - - message_t msg; - msg.fault = true; - queue_size = m_in_messages.push(move(msg)); -} - -// ======================================= -// Remote Control -// ======================================= -void InputZeroMQReader::set_parameter(const string& parameter, const string& value) -{ - stringstream ss(value); - ss.exceptions ( stringstream::failbit | stringstream::badbit ); - - if (parameter == "buffer") { - throw ParameterError("Parameter " + parameter + " is read-only."); - } - else { - stringstream ss_err; - ss_err << "Parameter '" << parameter - << "' is not exported by controllable " << get_rc_name(); - throw ParameterError(ss_err.str()); - } -} - -const string InputZeroMQReader::get_parameter(const string& parameter) const -{ - stringstream ss; - ss << std::fixed; - if (parameter == "buffer") { - // Do not use size of the queue, as it will contain empty - // frames to signal timeouts - unique_lock lock(m_last_in_messages_size_mutex); - const long time_in_buffer_us = 24000 * m_last_in_messages_size; - ss << time_in_buffer_us; - } - else { - ss << "Parameter '" << parameter << - "' is not exported by controllable " << get_rc_name(); - throw ParameterError(ss.str()); - } - return ss.str(); -} - -#endif - diff --git a/src/Utils.cpp b/src/Utils.cpp index 350838e..3f378a7 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -90,7 +90,6 @@ void printUsage(const char* progName) fprintf(out, "Where:\n"); fprintf(out, "input: ETI input filename (default: stdin), or\n"); fprintf(out, " tcp://source:port for ETI-over-TCP input, or\n"); - fprintf(out, " zmq+tcp://source:port for ZMQ input.\n"); fprintf(out, " udp://:port for EDI input.\n"); fprintf(out, "-f name: Use file output with given filename. (use /dev/stdout for standard output)\n"); fprintf(out, "-F format: Set the output format (see doc/example.ini for formats) for the file output.\n"); -- cgit v1.2.3 From a65370dbbcafecedcedb4619a6a07a02ec2ff721 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 4 Apr 2023 14:43:41 +0200 Subject: Add json remote HTTP server --- doc/zmq-ctrl/json_remote_server.py | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100755 doc/zmq-ctrl/json_remote_server.py (limited to 'doc') diff --git a/doc/zmq-ctrl/json_remote_server.py b/doc/zmq-ctrl/json_remote_server.py new file mode 100755 index 0000000..728cf7c --- /dev/null +++ b/doc/zmq-ctrl/json_remote_server.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# +# This is an example program that illustrates +# how to interact with the zeromq remote control +# using JSON. +# +# LICENSE: see bottom of file + +import sys +import zmq +from pprint import pprint +import json +import re +from http.server import BaseHTTPRequestHandler, HTTPServer +import time + +re_url = re.compile(r"/([a-zA-Z0-9]+).json") + +ZMQ_REMOTE = "tcp://localhost:9400" +HTTP_HOSTNAME = "localhost" +HTTP_PORT = 8080 + +class DabMuxServer(BaseHTTPRequestHandler): + def err500(self, message): + self.send_response(500) + self.send_header("Content-type", "application/json") + self.end_headers() + self.wfile.write(json.dumps({"error": message}).encode()) + + def do_GET(self): + m = re_url.match(self.path) + if m: + sock = context.socket(zmq.REQ) + poller = zmq.Poller() + poller.register(sock, zmq.POLLIN) + sock.connect(ZMQ_REMOTE) + + sock.send(b"ping") + socks = dict(poller.poll(1000)) + if socks: + if socks.get(sock) == zmq.POLLIN: + data = sock.recv() + if data != b"ok": + print(f"Received {data} to ping!", file=sys.stderr) + self.err500("ping failure") + return + else: + print("ZMQ error: ping timeout", file=sys.stderr) + self.err500("ping timeout") + return + + sock.send(b"showjson", flags=zmq.SNDMORE) + sock.send(m.group(1).encode()) + + socks = dict(poller.poll(1000)) + if socks: + if socks.get(sock) == zmq.POLLIN: + data = sock.recv_multipart() + print("Received: {}".format(len(data)), file=sys.stderr) + parts = [] + for i, part_data in enumerate(data): + part = part_data.decode() + print(" RX {}: {}".format(i, part.replace('\n',' ')), file=sys.stderr) + + if i == 0 and part != "fail": + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + self.wfile.write(part_data) + return + parts.append(part) + self.err500("data error " + " ".join(parts)) + return + + else: + print("ZMQ error: timeout", file=sys.stderr) + self.err500("timeout") + return + else: + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write("""ODR-DabMod RC HTTP server\n""".encode()) + self.wfile.write("""\n""".encode()) + for mod in ("sdr", "tist", "modulator", "tii", "ofdm", "gain", "guardinterval"): + self.wfile.write(f"""

{mod}.json

\n""".encode()) + self.wfile.write("""\n""".encode()) + + +if __name__ == "__main__": + context = zmq.Context() + + webServer = HTTPServer((HTTP_HOSTNAME, HTTP_PORT), DabMuxServer) + print("Server started http://%s:%s" % (HTTP_HOSTNAME, HTTP_PORT)) + + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + + context.destroy(linger=5) + + +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + + -- cgit v1.2.3 From 150b75b244602c789934f1a5094f33aa7da3c09a Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 6 Jun 2023 12:10:39 +0200 Subject: DEXTER: Add pacontrol interface --- doc/example.ini | 6 +++++ src/MemlessPoly.cpp | 4 +-- src/output/Dexter.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++++- src/output/Dexter.h | 14 +++++++++- src/output/SDRDevice.h | 4 +++ 5 files changed, 95 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/doc/example.ini b/doc/example.ini index cd48ef4..4cc6d26 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -320,6 +320,12 @@ txgain=32768 ;frequency=234208000 channel=13C +pacontrol_config_endpoint=tcp://localhost:5558 + +; TargetPower in dBm +; Runtime-changes to this setting are directly sent to pacontrol +pacontrol_targetpower=33 + [limeoutput] ; Lime output directly runs against the LMS device driver. It does not support SFN nor predistortion. device= diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp index 4801ba0..a2b0082 100644 --- a/src/MemlessPoly.cpp +++ b/src/MemlessPoly.cpp @@ -314,7 +314,7 @@ void MemlessPoly::worker_thread(MemlessPoly::worker_t *workerdata) set_thread_name("MemlessPoly"); while (true) { - worker_t::input_data_t in_data; + worker_t::input_data_t in_data = {}; try { workerdata->in_queue.wait_and_pop(in_data); } @@ -386,7 +386,7 @@ int MemlessPoly::internal_process(Buffer* const dataIn, Buffer* dataOut) // Wait for completion of the tasks for (auto& worker : m_workers) { - int ret; + int ret = 0; worker.out_queue.wait_and_pop(ret); } } diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index c1d8d2f..dd51517 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -66,9 +66,35 @@ static void fill_time(struct timespec *t) } } +static void pacontrol_set_targetpower(zmq::socket_t& sock, int targetpower_dBm) +{ + stringstream message_builder; + message_builder << "{'PA0' : {'TargetPower': "; + message_builder << targetpower_dBm; + message_builder << "}}"; + const auto message = message_builder.str(); + + try { + const auto r = sock.send(zmq::const_buffer{message.data(), message.size()}); + if (r.has_value()) { + etiLog.level(debug) << "Sent TargetPower=" << targetpower_dBm << " to pacontrol"; + } + else { + // zmq_send returned EAGAIN + etiLog.level(info) << "Send TargetPower=" << targetpower_dBm << " failed"; + } + } + catch (const zmq::error_t& err) { + etiLog.level(warn) << "Failed to send TargetPower=" << targetpower_dBm << ": " << err.what(); + } +} + + Dexter::Dexter(SDRDeviceConfig& config) : SDRDevice(), - m_conf(config) + m_conf(config), + m_zmq_context(1), + m_zmq_sock(m_zmq_context, ZMQ_PUSH) { etiLog.level(info) << "Dexter:Creating the device"; @@ -166,6 +192,41 @@ Dexter::Dexter(SDRDeviceConfig& config) : m_running = true; m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); + + m_zmq_sock.setsockopt(ZMQ_SNDTIMEO, 0); + if (not m_conf.pacontrol_config_endpoint.empty()) { + etiLog.level(debug) << "Creating pacontrol connection to " << + m_conf.pacontrol_config_endpoint; + m_zmq_sock.connect(m_conf.pacontrol_config_endpoint.c_str()); + } + + if (m_conf.pacontrol_targetpower.has_value()) { + pacontrol_set_targetpower(m_zmq_sock, *m_conf.pacontrol_targetpower); + } + else { + etiLog.level(warn) << "Config does not defined PA target power"; + } +} + +static void pacontrol_set_mute(zmq::socket_t& sock, bool mute) +{ + string message = "{'PA0' : {'Mute': "; + message += (mute ? "True" : "False"); + message += "}}"; + + try { + const auto r = sock.send(zmq::const_buffer{message.data(), message.size()}); + if (r.has_value()) { + etiLog.level(debug) << "Sent mute=" << mute << " to pacontrol"; + } + else { + // zmq_send returned EAGAIN + etiLog.level(info) << "Send mute=" << mute << " failed"; + } + } + catch (const zmq::error_t& err) { + etiLog.level(warn) << "Failed to send mute=" << mute << ": " << err.what(); + } } void Dexter::channel_up() @@ -178,6 +239,10 @@ void Dexter::channel_up() m_channel_is_up = true; etiLog.level(debug) << "DEXTER CHANNEL_UP"; + + if (m_zmq_sock.connected()) { + pacontrol_set_mute(m_zmq_sock, false); + } } void Dexter::channel_down() @@ -194,6 +259,10 @@ void Dexter::channel_down() m_channel_is_up = false; etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; + + if (m_zmq_sock.connected()) { + pacontrol_set_mute(m_zmq_sock, true); + } } void Dexter::handle_hw_time() diff --git a/src/output/Dexter.h b/src/output/Dexter.h index d4f425f..57b9798 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -34,8 +34,14 @@ DESCRIPTION: # include #endif -#ifdef HAVE_DEXTER +#if defined(HAVE_DEXTER) + +#if !defined(HAVE_ZEROMQ) +#error "ZeroMQ is mandatory for DEXTER" +#endif + #include "iio.h" +#include "zmq.hpp" #include #include @@ -120,6 +126,12 @@ class Dexter : public Output::SDRDevice size_t num_buffers_pushed = 0; + /* Communication with pacontrol */ + zmq::context_t m_zmq_context; + zmq::socket_t m_zmq_sock; + std::string m_pacontrol_endpoint; + + /* Clock State */ DexterClockState m_clock_state = DexterClockState::Startup; // Only valid when m_clock_state is not Startup diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index 26272be..f84b340 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -94,6 +94,10 @@ struct SDRDeviceConfig { // TCP port on which to serve TX and RX samples for the // digital pre distortion learning tool uint16_t dpdFeedbackServerPort = 0; + + // DEXTER-specific + std::string pacontrol_config_endpoint; + std::optional pacontrol_targetpower; // dBm }; // Each frame contains one OFDM frame, and its -- cgit v1.2.3 From 343df6eb8792b3efd33f4426766865ae03ccf316 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 19 Jul 2023 22:12:18 +0200 Subject: Add events --- Makefile.am | 2 + doc/receive_events.py | 59 +++++++++++++++++++++++++++++ lib/Json.cpp | 2 +- lib/Json.h | 10 +---- lib/RemoteControl.cpp | 2 +- src/ConfigParser.cpp | 8 +++- src/DabMod.cpp | 3 ++ src/DabModulator.cpp | 4 +- src/Events.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++ src/Events.h | 76 +++++++++++++++++++++++++++++++++++++ src/FIRFilter.cpp | 4 +- src/GainControl.cpp | 10 ++--- src/GuardIntervalInserter.cpp | 2 +- src/MemlessPoly.cpp | 6 +-- src/TII.cpp | 8 ++-- src/TimestampDecoder.cpp | 10 ++--- src/output/Dexter.cpp | 16 ++++---- src/output/SDR.cpp | 16 ++++---- src/output/Soapy.cpp | 8 ++-- src/output/UHD.cpp | 16 ++++---- 20 files changed, 287 insertions(+), 62 deletions(-) create mode 100755 doc/receive_events.py create mode 100644 src/Events.cpp create mode 100644 src/Events.h (limited to 'doc') diff --git a/Makefile.am b/Makefile.am index 6e7c9ce..5c75c62 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,8 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ src/EtiReader.h \ src/Eti.cpp \ src/Eti.h \ + src/Events.cpp \ + src/Events.h \ src/FicSource.cpp \ src/FicSource.h \ src/PuncturingRule.cpp \ diff --git a/doc/receive_events.py b/doc/receive_events.py new file mode 100755 index 0000000..dca27cd --- /dev/null +++ b/doc/receive_events.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# This is an example program that shows +# how to receive runtime events from ODR-DabMod +# +# LICENSE: see bottom of file + +import sys +import zmq +import json +from pprint import pprint + +context = zmq.Context() +sock = context.socket(zmq.SUB) + +ep = "tcp://127.0.0.1:5557" +print(f"Receive from {ep}") +sock.connect(ep) + +# subscribe to all events +sock.setsockopt(zmq.SUBSCRIBE, bytes([])) + +while True: + parts = sock.recv_multipart() + if len(parts) == 2: + print("Received event '{}'".format(parts[0].decode())) + pprint(json.loads(parts[1].decode())) + + else: + print("Received strange event:") + pprint(parts) + + print() + + +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to diff --git a/lib/Json.cpp b/lib/Json.cpp index 9bda8c3..da5b078 100644 --- a/lib/Json.cpp +++ b/lib/Json.cpp @@ -67,7 +67,7 @@ namespace json { ss << "\"" << escape_json(element.first) << "\": "; - const auto& value = element.second.data; + const auto& value = element.second.v; if (std::holds_alternative(value)) { ss << "\"" << escape_json(std::get(value)) << "\""; } diff --git a/lib/Json.h b/lib/Json.h index 26da9a8..ee67f35 100644 --- a/lib/Json.h +++ b/lib/Json.h @@ -51,15 +51,7 @@ namespace json { size_t, ssize_t, bool, - std::nullopt_t> data; - - template - value_t operator=(const T& map) { - value_t v; - v.data = map; - return v; - } - + std::nullopt_t> v; }; std::string map_to_json(const map_t& values); diff --git a/lib/RemoteControl.cpp b/lib/RemoteControl.cpp index b544461..fbe0662 100644 --- a/lib/RemoteControl.cpp +++ b/lib/RemoteControl.cpp @@ -109,7 +109,7 @@ std::list< std::vector > RemoteControllers::get_param_list_values(c std::string RemoteControllers::get_showjson() { json::map_t root; for (auto &controllable : rcs.controllables) { - root[controllable->get_rc_name()].data = controllable->get_all_values(); + root[controllable->get_rc_name()].v = controllable->get_all_values(); } return json::map_to_json(root); diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index cb4dc24..68ee74b 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -37,6 +37,7 @@ #include "ConfigParser.h" #include "Utils.h" #include "Log.h" +#include "Events.h" #include "DabModulator.h" #include "output/SDR.h" @@ -114,11 +115,16 @@ static void parse_configfile( mod_settings.inputTransport = pt.Get("input.transport", "file"); - mod_settings.edi_max_delay_ms = pt.GetReal("input.edi_max_delay", 0.0f); + mod_settings.edi_max_delay_ms = pt.GetReal("input.edi_max_delay", 0.0); mod_settings.inputName = pt.Get("input.source", "/dev/stdin"); // log parameters: + const string events_endpoint = pt.Get("log.events_endpoint", ""); + if (not events_endpoint.empty()) { + events.bind(events_endpoint); + } + if (pt.GetInteger("log.syslog", 0) == 1) { etiLog.register_backend(make_shared()); } diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 805fab5..fdd9e93 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -47,6 +47,7 @@ # include #endif +#include "Events.h" #include "Utils.h" #include "Log.h" #include "DabModulator.h" @@ -324,6 +325,8 @@ int launch_modulator(int argc, char* argv[]) mod_settings_t mod_settings; parse_args(argc, argv, mod_settings); + etiLog.register_backend(make_shared()); + etiLog.level(info) << "Configuration parsed. Starting up version " << #if defined(GITVERSION) GITVERSION; diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 0fe9c6d..4a29132 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -438,7 +438,7 @@ const string DabModulator::get_parameter(const string& parameter) const const json::map_t DabModulator::get_all_values() const { json::map_t map; - map["rate"] = m_settings.outputRate; - map["num_clipped_samples"] = m_formatConverter ? m_formatConverter->get_num_clipped_samples() : 0; + map["rate"].v = m_settings.outputRate; + map["num_clipped_samples"].v = m_formatConverter ? m_formatConverter->get_num_clipped_samples() : 0; return map; } diff --git a/src/Events.cpp b/src/Events.cpp new file mode 100644 index 0000000..d65b73a --- /dev/null +++ b/src/Events.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 + Her Majesty the Queen in Right of Canada (Communications Research + Center Canada) + + Copyright (C) 2023 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://www.opendigitalradio.org + */ +/* + 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, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License 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 "Events.h" + +EventSender events; + +EventSender::EventSender() : + m_zmq_context(1), + m_socket(m_zmq_context, zmq::socket_type::pub) +{ + int linger = 2000; + m_socket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); +} + +EventSender::~EventSender() +{ } + +void EventSender::bind(const std::string& bind_endpoint) +{ + m_socket.bind(bind_endpoint); +} + +void EventSender::send(const std::string& event_name, const json::map_t& detail) +{ + zmq::message_t zmsg1(event_name.data(), event_name.size()); + const auto detail_json = json::map_to_json(detail); + zmq::message_t zmsg2(detail_json.data(), detail_json.size()); + + try { + m_socket.send(zmsg1, zmq::send_flags::sndmore); + m_socket.send(zmsg2, zmq::send_flags::none); + } + catch (const zmq::error_t& err) { + fprintf(stderr, "Cannot send event %s: %s", event_name.c_str(), err.what()); + } +} + + +void LogToEventSender::log(log_level_t level, const std::string& message) +{ + std::string event_name; + if (level == log_level_t::warn) { event_name = "warn"; } + else if (level == log_level_t::error) { event_name = "error"; } + else if (level == log_level_t::alert) { event_name = "alert"; } + else if (level == log_level_t::emerg) { event_name = "emerg"; } + + if (not event_name.empty()) { + json::map_t detail; + detail["message"].v = message; + events.send(event_name, detail); + } +} + +std::string LogToEventSender::get_name() const +{ + return "EventSender"; +} diff --git a/src/Events.h b/src/Events.h new file mode 100644 index 0000000..215c5a8 --- /dev/null +++ b/src/Events.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 + Her Majesty the Queen in Right of Canada (Communications Research + Center Canada) + + Copyright (C) 2023 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://www.opendigitalradio.org + + This module adds remote-control capability to some of the dabmux/dabmod modules. + */ +/* + 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, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(HAVE_ZEROMQ) +# include "zmq.hpp" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "Log.h" +#include "Json.h" + +class EventSender { + public: + EventSender(); + EventSender(const EventSender& other) = delete; + const EventSender& operator=(const EventSender& other) = delete; + EventSender(EventSender&& other) = delete; + EventSender& operator=(EventSender&& other) = delete; + ~EventSender(); + + void bind(const std::string& bind_endpoint); + + void send(const std::string& event_name, const json::map_t& detail); + private: + zmq::context_t m_zmq_context; + zmq::socket_t m_socket; +}; + +class LogToEventSender: public LogBackend { + public: + virtual ~LogToEventSender() {}; + virtual void log(log_level_t level, const std::string& message); + virtual std::string get_name() const; +}; + +/* events is a singleton used in all parts of the program to output log messages. + * It is constructed in Events.cpp */ +extern EventSender events; + diff --git a/src/FIRFilter.cpp b/src/FIRFilter.cpp index d2a6121..57e7127 100644 --- a/src/FIRFilter.cpp +++ b/src/FIRFilter.cpp @@ -350,7 +350,7 @@ const string FIRFilter::get_parameter(const string& parameter) const const json::map_t FIRFilter::get_all_values() const { json::map_t map; - map["ntaps"] = m_taps.size(); - map["tapsfile"] = m_taps_file; + map["ntaps"].v = m_taps.size(); + map["tapsfile"].v = m_taps_file; return map; } diff --git a/src/GainControl.cpp b/src/GainControl.cpp index beb93f6..84cf065 100644 --- a/src/GainControl.cpp +++ b/src/GainControl.cpp @@ -586,18 +586,18 @@ const string GainControl::get_parameter(const string& parameter) const const json::map_t GainControl::get_all_values() const { json::map_t map; - map["digital"] = m_digGain; + map["digital"].v = m_digGain; switch (m_gainmode) { case GainMode::GAIN_FIX: - map["mode"] = "fix"; + map["mode"].v = "fix"; break; case GainMode::GAIN_MAX: - map["mode"] = "max"; + map["mode"].v = "max"; break; case GainMode::GAIN_VAR: - map["mode"] = "var"; + map["mode"].v = "var"; break; } - map["var"] = m_var_variance_rc; + map["var"].v = m_var_variance_rc; return map; } diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index 80394b7..3c2db14 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -306,6 +306,6 @@ const std::string GuardIntervalInserter::get_parameter(const std::string& parame const json::map_t GuardIntervalInserter::get_all_values() const { json::map_t map; - map["windowlen"] = d_windowOverlap; + map["windowlen"].v = d_windowOverlap; return map; } diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp index 30d4ce9..184b5bd 100644 --- a/src/MemlessPoly.cpp +++ b/src/MemlessPoly.cpp @@ -470,8 +470,8 @@ const string MemlessPoly::get_parameter(const string& parameter) const const json::map_t MemlessPoly::get_all_values() const { json::map_t map; - map["ncoefs"] = m_coefs_am.size(); - map["coefs"] = serialise_coefficients(); - map["coeffile"] = m_coefs_file; + map["ncoefs"].v = m_coefs_am.size(); + map["coefs"].v = serialise_coefficients(); + map["coeffile"].v = m_coefs_file; return map; } diff --git a/src/TII.cpp b/src/TII.cpp index 9068630..2656cbf 100644 --- a/src/TII.cpp +++ b/src/TII.cpp @@ -388,9 +388,9 @@ const std::string TII::get_parameter(const std::string& parameter) const const json::map_t TII::get_all_values() const { json::map_t map; - map["enable"] = m_conf.enable; - map["pattern"] = m_conf.pattern; - map["comb"] = m_conf.comb; - map["old_variant"] = m_conf.old_variant; + map["enable"].v = m_conf.enable; + map["pattern"].v = m_conf.pattern; + map["comb"].v = m_conf.comb; + map["old_variant"].v = m_conf.old_variant; return map; } diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 4277e55..a7972c9 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -304,19 +304,19 @@ const std::string TimestampDecoder::get_parameter( const json::map_t TimestampDecoder::get_all_values() const { json::map_t map; - map["offset"] = timestamp_offset; + map["offset"].v = timestamp_offset; if (full_timestamp_received) { - map["timestamp"] = time_secs + ((double)time_pps / 16384000.0); + map["timestamp"].v = time_secs + ((double)time_pps / 16384000.0); } else { - map["timestamp"] = std::nullopt; + map["timestamp"].v = std::nullopt; } if (full_timestamp_received) { - map["timestamp0"] = time_secs_of_frame0 + ((double)time_pps_of_frame0 / 16384000.0); + map["timestamp0"].v = time_secs_of_frame0 + ((double)time_pps_of_frame0 / 16384000.0); } else { - map["timestamp0"] = std::nullopt; + map["timestamp0"].v = std::nullopt; } return map; } diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 132636c..e52f774 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -470,20 +470,20 @@ SDRDevice::run_statistics_t Dexter::get_run_statistics(void) const run_statistics_t rs; { std::unique_lock lock(m_attr_thread_mutex); - rs["underruns"] = underflows; + rs["underruns"].v = underflows; } - rs["latepackets"] = num_late; - rs["frames"] = num_frames_modulated; + rs["latepackets"].v = num_late; + rs["frames"].v = num_frames_modulated; - rs["in_holdover_since"] = 0; + rs["in_holdover_since"].v = 0; switch (m_clock_state) { case DexterClockState::Startup: - rs["clock_state"] = "startup"; break; + rs["clock_state"].v = "startup"; break; case DexterClockState::Normal: - rs["clock_state"] = "normal"; break; + rs["clock_state"].v = "normal"; break; case DexterClockState::Holdover: - rs["clock_state"] = "holdover"; - rs["in_holdover_since"] = m_holdover_since_t; + rs["clock_state"].v = "holdover"; + rs["in_holdover_since"].v = m_holdover_since_t; break; } diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 4fc3277..6c03b53 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -450,7 +450,7 @@ const string SDR::get_parameter(const string& parameter) const if (m_device) { const auto stat = m_device->get_run_statistics(); try { - const auto& value = stat.at(parameter).data; + const auto& value = stat.at(parameter).v; if (std::holds_alternative(value)) { ss << std::get(value); } @@ -489,19 +489,19 @@ const json::map_t SDR::get_all_values() const { json::map_t stat = m_device->get_run_statistics(); - stat["txgain"] = m_config.txgain; - stat["rxgain"] = m_config.rxgain; - stat["freq"] = m_config.frequency; - stat["muting"] = m_config.muting; - stat["temp"] = std::nullopt; + stat["txgain"].v = m_config.txgain; + stat["rxgain"].v = m_config.rxgain; + stat["freq"].v = m_config.frequency; + stat["muting"].v = m_config.muting; + stat["temp"].v = std::nullopt; if (m_device) { const std::optional temp = m_device->get_temperature(); if (temp) { - stat["temp"] = *temp; + stat["temp"].v = *temp; } } - stat["queued_frames_ms"] = m_queue.size() * + stat["queued_frames_ms"].v = m_queue.size() * (size_t)chrono::duration_cast(transmission_frame_duration(m_config.dabMode)) .count(); diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index 4d33e39..7931860 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -183,10 +183,10 @@ double Soapy::get_bandwidth(void) const SDRDevice::run_statistics_t Soapy::get_run_statistics(void) const { run_statistics_t rs; - rs["underruns"] = underflows; - rs["overruns"] = overflows; - rs["timeouts"] = timeouts; - rs["frames"] = num_frames_modulated; + rs["underruns"].v = underflows; + rs["overruns"].v = overflows; + rs["timeouts"].v = timeouts; + rs["frames"].v = num_frames_modulated; return rs; } diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 6638b6c..094e021 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -380,19 +380,19 @@ void UHD::transmit_frame(struct FrameData&& frame) SDRDevice::run_statistics_t UHD::get_run_statistics(void) const { run_statistics_t rs; - rs["underruns"] = num_underflows; - rs["overruns"] = num_overflows; - rs["late_packets"] = num_late_packets; - rs["frames"] = num_frames_modulated; + rs["underruns"].v = num_underflows; + rs["overruns"].v = num_overflows; + rs["late_packets"].v = num_late_packets; + rs["frames"].v = num_frames_modulated; if (m_device_time) { const auto gpsdo_stat = m_device_time->get_gnss_stats(); - rs["gpsdo_holdover"] = gpsdo_stat.holdover; - rs["gpsdo_num_sv"] = gpsdo_stat.num_sv; + rs["gpsdo_holdover"].v = gpsdo_stat.holdover; + rs["gpsdo_num_sv"].v = gpsdo_stat.num_sv; } else { - rs["gpsdo_holdover"] = true; - rs["gpsdo_num_sv"] = 0; + rs["gpsdo_holdover"].v = true; + rs["gpsdo_num_sv"].v = 0; } return rs; } -- cgit v1.2.3 From c8e4c6851b0975200b5a34b8003e7c08389e6b33 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 19 Jul 2023 22:20:29 +0200 Subject: Improve zmq_remote.py --- doc/zmq-ctrl/zmq_remote.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'doc') diff --git a/doc/zmq-ctrl/zmq_remote.py b/doc/zmq-ctrl/zmq_remote.py index 56465d3..7581575 100755 --- a/doc/zmq-ctrl/zmq_remote.py +++ b/doc/zmq-ctrl/zmq_remote.py @@ -16,7 +16,7 @@ poller = zmq.Poller() poller.register(sock, zmq.POLLIN) if len(sys.argv) < 2: - print("Usage: program url cmd [args...]") + print("Usage: program url cmd [args...]", file=sys.stderr) sys.exit(1) sock.connect(sys.argv[1]) @@ -25,7 +25,7 @@ message_parts = sys.argv[2:] # first do a ping test -print("ping") +print("ping", file=sys.stderr) sock.send(b"ping") socks = dict(poller.poll(1000)) @@ -33,9 +33,9 @@ if socks: if socks.get(sock) == zmq.POLLIN: data = sock.recv_multipart() - print("Received: {}".format(len(data))) + print("Received: {}".format(len(data)), file=sys.stderr) for i,part in enumerate(data): - print(" {}".format(part)) + print(" {}".format(part), file=sys.stderr) for i, part in enumerate(message_parts): if i == len(message_parts) - 1: @@ -43,18 +43,22 @@ if socks: else: f = zmq.SNDMORE - print("Send {}({}): '{}'".format(i, f, part)) + print("Send {}({}): '{}'".format(i, f, part), file=sys.stderr) sock.send(part.encode(), flags=f) data = sock.recv_multipart() - print("Received: {}".format(len(data))) - for i,part in enumerate(data): - print(" RX {}: {}".format(i, part.decode().replace('\n',' '))) + print("Received: {}".format(len(data)), file=sys.stderr) + for i, part in enumerate(data): + if message_parts[0] == 'showjson': + # This allows you to pipe the JSON into another tool + print(part.decode()) + else: + print(" RX {}: {}".format(i, part.decode().replace('\n',' ')), file=sys.stderr) else: - print("ZMQ error: timeout") + print("ZMQ error: timeout", file=sys.stderr) context.destroy(linger=5) # This is free and unencumbered software released into the public domain. -- cgit v1.2.3 From 2de7bab3cf1c7757078a113b35e927ecbccc5e3d Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 20 Jul 2023 09:23:17 +0200 Subject: Handle event socket bind failure --- doc/receive_events.py | 2 +- src/Events.cpp | 12 +++++++++++- src/Events.h | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'doc') diff --git a/doc/receive_events.py b/doc/receive_events.py index dca27cd..bfd6f86 100755 --- a/doc/receive_events.py +++ b/doc/receive_events.py @@ -13,7 +13,7 @@ from pprint import pprint context = zmq.Context() sock = context.socket(zmq.SUB) -ep = "tcp://127.0.0.1:5557" +ep = "tcp://127.0.0.1:5556" print(f"Receive from {ep}") sock.connect(ep) diff --git a/src/Events.cpp b/src/Events.cpp index d65b73a..3171cda 100644 --- a/src/Events.cpp +++ b/src/Events.cpp @@ -47,11 +47,21 @@ EventSender::~EventSender() void EventSender::bind(const std::string& bind_endpoint) { - m_socket.bind(bind_endpoint); + try { + m_socket.bind(bind_endpoint); + m_socket_valid = true; + } + catch (const zmq::error_t& err) { + fprintf(stderr, "Cannot bind event socket: %s", err.what()); + } } void EventSender::send(const std::string& event_name, const json::map_t& detail) { + if (not m_socket_valid) { + return; + } + zmq::message_t zmsg1(event_name.data(), event_name.size()); const auto detail_json = json::map_to_json(detail); zmq::message_t zmsg2(detail_json.data(), detail_json.size()); diff --git a/src/Events.h b/src/Events.h index 215c5a8..9f838e5 100644 --- a/src/Events.h +++ b/src/Events.h @@ -61,6 +61,7 @@ class EventSender { private: zmq::context_t m_zmq_context; zmq::socket_t m_socket; + bool m_socket_valid = false; }; class LogToEventSender: public LogBackend { -- cgit v1.2.3 From 738a1e859737458d3ee9c5a8e92655bdd107a07e Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 15 Aug 2023 10:05:09 +0200 Subject: Revert "DEXTER: Add pacontrol interface" This reverts commit 150b75b244602c789934f1a5094f33aa7da3c09a. --- doc/example.ini | 6 ----- src/MemlessPoly.cpp | 4 +-- src/output/Dexter.cpp | 71 +------------------------------------------------- src/output/Dexter.h | 14 +--------- src/output/SDRDevice.h | 4 --- 5 files changed, 4 insertions(+), 95 deletions(-) (limited to 'doc') diff --git a/doc/example.ini b/doc/example.ini index 4cc6d26..cd48ef4 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -320,12 +320,6 @@ txgain=32768 ;frequency=234208000 channel=13C -pacontrol_config_endpoint=tcp://localhost:5558 - -; TargetPower in dBm -; Runtime-changes to this setting are directly sent to pacontrol -pacontrol_targetpower=33 - [limeoutput] ; Lime output directly runs against the LMS device driver. It does not support SFN nor predistortion. device= diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp index 184b5bd..17a7f57 100644 --- a/src/MemlessPoly.cpp +++ b/src/MemlessPoly.cpp @@ -314,7 +314,7 @@ void MemlessPoly::worker_thread(MemlessPoly::worker_t *workerdata) set_thread_name("MemlessPoly"); while (true) { - worker_t::input_data_t in_data = {}; + worker_t::input_data_t in_data; try { workerdata->in_queue.wait_and_pop(in_data); } @@ -386,7 +386,7 @@ int MemlessPoly::internal_process(Buffer* const dataIn, Buffer* dataOut) // Wait for completion of the tasks for (auto& worker : m_workers) { - int ret = 0; + int ret; worker.out_queue.wait_and_pop(ret); } } diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index e52f774..14edace 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -66,35 +66,9 @@ static void fill_time(struct timespec *t) } } -static void pacontrol_set_targetpower(zmq::socket_t& sock, int targetpower_dBm) -{ - stringstream message_builder; - message_builder << "{'PA0' : {'TargetPower': "; - message_builder << targetpower_dBm; - message_builder << "}}"; - const auto message = message_builder.str(); - - try { - const auto r = sock.send(zmq::const_buffer{message.data(), message.size()}); - if (r.has_value()) { - etiLog.level(debug) << "Sent TargetPower=" << targetpower_dBm << " to pacontrol"; - } - else { - // zmq_send returned EAGAIN - etiLog.level(info) << "Send TargetPower=" << targetpower_dBm << " failed"; - } - } - catch (const zmq::error_t& err) { - etiLog.level(warn) << "Failed to send TargetPower=" << targetpower_dBm << ": " << err.what(); - } -} - - Dexter::Dexter(SDRDeviceConfig& config) : SDRDevice(), - m_conf(config), - m_zmq_context(1), - m_zmq_sock(m_zmq_context, ZMQ_PUSH) + m_conf(config) { etiLog.level(info) << "Dexter:Creating the device"; @@ -192,41 +166,6 @@ Dexter::Dexter(SDRDeviceConfig& config) : m_running = true; m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); - - m_zmq_sock.setsockopt(ZMQ_SNDTIMEO, 0); - if (not m_conf.pacontrol_config_endpoint.empty()) { - etiLog.level(debug) << "Creating pacontrol connection to " << - m_conf.pacontrol_config_endpoint; - m_zmq_sock.connect(m_conf.pacontrol_config_endpoint.c_str()); - } - - if (m_conf.pacontrol_targetpower.has_value()) { - pacontrol_set_targetpower(m_zmq_sock, *m_conf.pacontrol_targetpower); - } - else { - etiLog.level(warn) << "Config does not define PA target power"; - } -} - -static void pacontrol_set_mute(zmq::socket_t& sock, bool mute) -{ - string message = "{'PA0' : {'Mute': "; - message += (mute ? "True" : "False"); - message += "}}"; - - try { - const auto r = sock.send(zmq::const_buffer{message.data(), message.size()}); - if (r.has_value()) { - etiLog.level(debug) << "Sent mute=" << mute << " to pacontrol"; - } - else { - // zmq_send returned EAGAIN - etiLog.level(info) << "Send mute=" << mute << " failed"; - } - } - catch (const zmq::error_t& err) { - etiLog.level(warn) << "Failed to send mute=" << mute << ": " << err.what(); - } } void Dexter::channel_up() @@ -239,10 +178,6 @@ void Dexter::channel_up() m_channel_is_up = true; etiLog.level(debug) << "DEXTER CHANNEL_UP"; - - if (m_zmq_sock.connected()) { - pacontrol_set_mute(m_zmq_sock, false); - } } void Dexter::channel_down() @@ -259,10 +194,6 @@ void Dexter::channel_down() m_channel_is_up = false; etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; - - if (m_zmq_sock.connected()) { - pacontrol_set_mute(m_zmq_sock, true); - } } void Dexter::handle_hw_time() diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 57b9798..d4f425f 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -34,14 +34,8 @@ DESCRIPTION: # include #endif -#if defined(HAVE_DEXTER) - -#if !defined(HAVE_ZEROMQ) -#error "ZeroMQ is mandatory for DEXTER" -#endif - +#ifdef HAVE_DEXTER #include "iio.h" -#include "zmq.hpp" #include #include @@ -126,12 +120,6 @@ class Dexter : public Output::SDRDevice size_t num_buffers_pushed = 0; - /* Communication with pacontrol */ - zmq::context_t m_zmq_context; - zmq::socket_t m_zmq_sock; - std::string m_pacontrol_endpoint; - - /* Clock State */ DexterClockState m_clock_state = DexterClockState::Startup; // Only valid when m_clock_state is not Startup diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index f728d8b..378829c 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -94,10 +94,6 @@ struct SDRDeviceConfig { // TCP port on which to serve TX and RX samples for the // digital pre distortion learning tool uint16_t dpdFeedbackServerPort = 0; - - // DEXTER-specific - std::string pacontrol_config_endpoint; - std::optional pacontrol_targetpower; // dBm }; // Each frame contains one OFDM frame, and its -- cgit v1.2.3 From 6c0732dcfc2af963e2b8577e9fc38f71307a1033 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 15 Aug 2023 10:11:06 +0200 Subject: Dexter: remove old metrics from SDR.cpp --- doc/example.ini | 9 ++++++++- src/output/SDR.cpp | 21 --------------------- 2 files changed, 8 insertions(+), 22 deletions(-) (limited to 'doc') diff --git a/doc/example.ini b/doc/example.ini index cd48ef4..f009fbe 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -316,10 +316,17 @@ channel=13C ;dpd_port=50055 [dexteroutput] -txgain=32768 +txgain=65535 + +; channel/frequency is applied to ad9957.center_frequency ;frequency=234208000 channel=13C +; lo offset is applied to dexter_dsp_tx.frequency0 +lo_offset=0 + +max_gps_holdover_time=3600 + [limeoutput] ; Lime output directly runs against the LMS device driver. It does not support SFN nor predistortion. device= diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 6c03b53..d2956a2 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -99,27 +99,6 @@ SDR::SDR(SDRDeviceConfig& config, std::shared_ptr device) : RC_ADD_PARAMETER(fifo_fill, "A value representing the Lime FIFO fullness [percent]"); } #endif // HAVE_LIMESDR - -#ifdef HAVE_DEXTER - if (std::dynamic_pointer_cast(device)) { - RC_ADD_PARAMETER(clks, "DEXTER internal clk counter value"); - RC_ADD_PARAMETER(fifo_not_empty_clks, "DEXTER internal clk counter value when FIFO was last empty"); - RC_ADD_PARAMETER(gpsdo_locked, "1 if GPSDO is locked"); - RC_ADD_PARAMETER(pps_clk_error_hz, "Estimated error in Hz of clock"); - RC_ADD_PARAMETER(pps_cnt, "Number of 1PPS pulses seen from GPS"); - RC_ADD_PARAMETER(dsp_version, "Version of FPGA DSP"); - RC_ADD_PARAMETER(vcc3v3, "Voltage of VCC 3V3"); - RC_ADD_PARAMETER(vcc5v4, "Voltage of VCC 5V4"); - RC_ADD_PARAMETER(vfan, "Fan voltage"); - RC_ADD_PARAMETER(vcc_main_in, "Main input voltage"); - RC_ADD_PARAMETER(vcc3v3pll, "Voltage of VCC 3V3 PLL"); - RC_ADD_PARAMETER(vcc2v5io, "Voltage of VCC 2V5 IO"); - RC_ADD_PARAMETER(vccocxo, "OCXO voltage"); - RC_ADD_PARAMETER(tempfpga, "FPGA temperature [celsius]"); - RC_ADD_PARAMETER(voltage_alarm, "Voltage out of bounds"); - RC_ADD_PARAMETER(temp_alarm, "Temperature out of bounds"); - } -#endif // HAVE_DEXTER } SDR::~SDR() -- cgit v1.2.3