From ae70341365e27d766c8530924209fc4826036aea Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 31 Mar 2023 14:31:32 +0200 Subject: Add JSON output to RC --- src/TII.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src/TII.h') diff --git a/src/TII.h b/src/TII.h index d8c785d..b0ba646 100644 --- a/src/TII.h +++ b/src/TII.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 @@ -83,15 +83,13 @@ class TII : public ModCodec, public RemoteControllable public: TII(unsigned int dabmode, tii_config_t& tii_config); - int process(Buffer* dataIn, Buffer* dataOut); - const char* name(); + int process(Buffer* dataIn, Buffer* dataOut) override; + const char* name() override; /******* REMOTE CONTROL ********/ - virtual void set_parameter(const std::string& parameter, - const std::string& value); - - virtual const std::string get_parameter( - const std::string& parameter) const; + virtual void set_parameter(const std::string& parameter, const std::string& value) override; + virtual const std::string get_parameter(const std::string& parameter) const override; + virtual const RemoteControllable::map_t get_all_values() const override; protected: // Fill m_Acp with the correct carriers for the pattern/comb -- cgit v1.2.3 From d521d4f0c5ad3b663a322453c5798626081cb1f3 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 19 Jul 2023 20:26:36 +0200 Subject: Change RC showjson command --- Makefile.am | 2 + lib/Json.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++ lib/Json.h | 66 +++++++++++++++++++++++++++ lib/RemoteControl.cpp | 94 ++++++++------------------------------ lib/RemoteControl.h | 8 ++-- src/DabModulator.cpp | 4 +- src/DabModulator.h | 2 +- src/FIRFilter.cpp | 4 +- src/FIRFilter.h | 2 +- src/GainControl.cpp | 4 +- src/GainControl.h | 2 +- src/GuardIntervalInserter.cpp | 4 +- src/GuardIntervalInserter.h | 2 +- src/MemlessPoly.cpp | 4 +- src/MemlessPoly.h | 2 +- src/OfdmGenerator.cpp | 4 +- src/OfdmGenerator.h | 2 +- src/TII.cpp | 4 +- src/TII.h | 2 +- src/TimestampDecoder.cpp | 4 +- src/TimestampDecoder.h | 2 +- src/output/SDR.cpp | 6 +-- src/output/SDR.h | 2 +- src/output/SDRDevice.h | 2 +- 24 files changed, 220 insertions(+), 110 deletions(-) create mode 100644 lib/Json.cpp create mode 100644 lib/Json.h (limited to 'src/TII.h') diff --git a/Makefile.am b/Makefile.am index 0e09236..6e7c9ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,6 +98,8 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ lib/RemoteControl.h \ lib/Log.cpp \ lib/Log.h \ + lib/Json.h \ + lib/Json.cpp \ lib/Globals.cpp \ lib/INIReader.h \ lib/crc.h \ diff --git a/lib/Json.cpp b/lib/Json.cpp new file mode 100644 index 0000000..9bda8c3 --- /dev/null +++ b/lib/Json.cpp @@ -0,0 +1,102 @@ +/* + 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 "Json.h" + +namespace json { + static std::string escape_json(const std::string &s) { + std::ostringstream o; + for (auto c = s.cbegin(); c != s.cend(); c++) { + switch (*c) { + case '"': o << "\\\""; break; + case '\\': o << "\\\\"; break; + case '\b': o << "\\b"; break; + case '\f': o << "\\f"; break; + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\t': o << "\\t"; break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + o << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); + } else { + o << *c; + } + } + } + return o.str(); + } + + std::string map_to_json(const map_t& values) { + std::ostringstream ss; + ss << "{ "; + size_t ix = 0; + for (const auto& element : values) { + if (ix > 0) { + ss << ","; + } + + ss << "\"" << escape_json(element.first) << "\": "; + + const auto& value = element.second.data; + if (std::holds_alternative(value)) { + ss << "\"" << escape_json(std::get(value)) << "\""; + } + else if (std::holds_alternative(value)) { + ss << std::defaultfloat << std::get(value); + } + else if (std::holds_alternative(value)) { + ss << std::get(value); + } + else if (std::holds_alternative(value)) { + ss << std::get(value); + } + else if (std::holds_alternative(value)) { + ss << (std::get(value) ? "true" : "false"); + } + else if (std::holds_alternative(value)) { + ss << "null"; + } + else if (std::holds_alternative(value)) { + ss << map_to_json(std::get(value)); + } + else { + throw std::logic_error("variant alternative not handled"); + } + + ix++; + } + ss << " }"; + + return ss.str(); + } +} diff --git a/lib/Json.h b/lib/Json.h new file mode 100644 index 0000000..26da9a8 --- /dev/null +++ b/lib/Json.h @@ -0,0 +1,66 @@ +/* + 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 + +#include +#include +#include +#include +#include +#include +#include + +namespace json { + + using map_t = std::unordered_map; + + struct value_t { + std::variant< + map_t, + std::string, + double, + 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::string map_to_json(const map_t& values); +} diff --git a/lib/RemoteControl.cpp b/lib/RemoteControl.cpp index fd0ea77..b544461 100644 --- a/lib/RemoteControl.cpp +++ b/lib/RemoteControl.cpp @@ -105,71 +105,14 @@ std::list< std::vector > RemoteControllers::get_param_list_values(c } -static std::string escape_json(const std::string &s) { - std::ostringstream o; - for (auto c = s.cbegin(); c != s.cend(); c++) { - switch (*c) { - case '"': o << "\\\""; break; - case '\\': o << "\\\\"; break; - case '\b': o << "\\b"; break; - case '\f': o << "\\f"; break; - case '\n': o << "\\n"; break; - case '\r': o << "\\r"; break; - case '\t': o << "\\t"; break; - default: - if ('\x00' <= *c && *c <= '\x1f') { - o << "\\u" - << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); - } else { - o << *c; - } - } - } - return o.str(); -} - -std::string RemoteControllers::get_params_json(const std::string& name) { - RemoteControllable* controllable = get_controllable_(name); - const auto& values = controllable->get_all_values(); - - std::ostringstream ss; - ss << "{ "; - size_t ix = 0; - for (const auto& element : values) { - if (ix > 0) { - ss << ","; - } - - ss << "\"" << escape_json(element.first) << "\": "; - - const auto& value = element.second; - if (std::holds_alternative(value)) { - ss << "\"" << escape_json(std::get(value)) << "\""; - } - else if (std::holds_alternative(value)) { - ss << std::defaultfloat << std::get(value); - } - else if (std::holds_alternative(value)) { - ss << std::get(value); - } - else if (std::holds_alternative(value)) { - ss << std::get(value); - } - else if (std::holds_alternative(value)) { - ss << (std::get(value) ? "true" : "false"); - } - else if (std::holds_alternative(value)) { - ss << "null"; - } - else { - throw std::logic_error("variant alternative not handled"); - } - ix++; +std::string RemoteControllers::get_showjson() { + json::map_t root; + for (auto &controllable : rcs.controllables) { + root[controllable->get_rc_name()].data = controllable->get_all_values(); } - ss << " }"; - return ss.str(); + return json::map_to_json(root); } std::string RemoteControllers::get_param(const std::string& name, const std::string& param) { @@ -590,6 +533,19 @@ void RemoteControllerZmq::process() repSocket.send(zmsg, (--cohort_size > 0) ? zmq::send_flags::sndmore : zmq::send_flags::none); } } + else if (msg.size() == 1 && command == "showjson") { + try { + std::string json = rcs.get_showjson(); + + zmq::message_t zmsg(json.size()); + memcpy(zmsg.data(), json.data(), json.size()); + + repSocket.send(zmsg, zmq::send_flags::none); + } + catch (const ParameterError &err) { + send_fail_reply(repSocket, err.what()); + } + } else if (msg.size() == 2 && command == "show") { const std::string module((char*) msg[1].data(), msg[1].size()); try { @@ -608,20 +564,6 @@ void RemoteControllerZmq::process() send_fail_reply(repSocket, err.what()); } } - else if (msg.size() == 2 && command == "showjson") { - const std::string module((char*) msg[1].data(), msg[1].size()); - try { - std::string json = rcs.get_params_json(module); - - zmq::message_t zmsg(json.size()); - memcpy(zmsg.data(), json.data(), json.size()); - - repSocket.send(zmsg, zmq::send_flags::none); - } - catch (const ParameterError &err) { - send_fail_reply(repSocket, err.what()); - } - } else if (msg.size() == 3 && command == "get") { const std::string module((char*) msg[1].data(), msg[1].size()); const std::string parameter((char*) msg[2].data(), msg[2].size()); diff --git a/lib/RemoteControl.h b/lib/RemoteControl.h index 4bc3b68..26f30d9 100644 --- a/lib/RemoteControl.h +++ b/lib/RemoteControl.h @@ -48,6 +48,7 @@ #include "Log.h" #include "Socket.h" +#include "Json.h" #define RC_ADD_PARAMETER(p, desc) { \ std::vector p; \ @@ -120,10 +121,7 @@ class RemoteControllable { /* Getting a parameter always returns a string. */ virtual const std::string get_parameter(const std::string& parameter) const = 0; - using value_t = std::variant; - using map_t = std::unordered_map; - - virtual const map_t get_all_values() const = 0; + virtual const json::map_t get_all_values() const = 0; protected: std::string m_rc_name; @@ -140,7 +138,7 @@ class RemoteControllers { void check_faults(); std::list< std::vector > get_param_list_values(const std::string& name); std::string get_param(const std::string& name, const std::string& param); - std::string get_params_json(const std::string& name); + std::string get_showjson(); void set_param( const std::string& name, diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 5213d8d..0fe9c6d 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -435,9 +435,9 @@ const string DabModulator::get_parameter(const string& parameter) const return ss.str(); } -const RemoteControllable::map_t DabModulator::get_all_values() const +const json::map_t DabModulator::get_all_values() const { - map_t map; + json::map_t map; map["rate"] = m_settings.outputRate; map["num_clipped_samples"] = m_formatConverter ? m_formatConverter->get_num_clipped_samples() : 0; return map; diff --git a/src/DabModulator.h b/src/DabModulator.h index 6381252..140f313 100644 --- a/src/DabModulator.h +++ b/src/DabModulator.h @@ -64,7 +64,7 @@ public: /******* REMOTE CONTROL ********/ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: void setMode(unsigned mode); diff --git a/src/FIRFilter.cpp b/src/FIRFilter.cpp index 523d405..d2a6121 100644 --- a/src/FIRFilter.cpp +++ b/src/FIRFilter.cpp @@ -347,9 +347,9 @@ const string FIRFilter::get_parameter(const string& parameter) const return ss.str(); } -const RemoteControllable::map_t FIRFilter::get_all_values() const +const json::map_t FIRFilter::get_all_values() const { - map_t map; + json::map_t map; map["ntaps"] = m_taps.size(); map["tapsfile"] = m_taps_file; return map; diff --git a/src/FIRFilter.h b/src/FIRFilter.h index 2a469aa..a4effa1 100644 --- a/src/FIRFilter.h +++ b/src/FIRFilter.h @@ -61,7 +61,7 @@ public: /******* REMOTE CONTROL ********/ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: virtual int internal_process(Buffer* const dataIn, Buffer* dataOut) override; diff --git a/src/GainControl.cpp b/src/GainControl.cpp index d90da45..beb93f6 100644 --- a/src/GainControl.cpp +++ b/src/GainControl.cpp @@ -583,9 +583,9 @@ const string GainControl::get_parameter(const string& parameter) const return ss.str(); } -const RemoteControllable::map_t GainControl::get_all_values() const +const json::map_t GainControl::get_all_values() const { - map_t map; + json::map_t map; map["digital"] = m_digGain; switch (m_gainmode) { case GainMode::GAIN_FIX: diff --git a/src/GainControl.h b/src/GainControl.h index f024fa2..04f6b58 100644 --- a/src/GainControl.h +++ b/src/GainControl.h @@ -66,7 +66,7 @@ class GainControl : public PipelinedModCodec, public RemoteControllable /* Functions for the remote control */ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: virtual int internal_process( diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index d5c71fb..80394b7 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -303,9 +303,9 @@ const std::string GuardIntervalInserter::get_parameter(const std::string& parame return ss.str(); } -const RemoteControllable::map_t GuardIntervalInserter::get_all_values() const +const json::map_t GuardIntervalInserter::get_all_values() const { - map_t map; + json::map_t map; map["windowlen"] = d_windowOverlap; return map; } diff --git a/src/GuardIntervalInserter.h b/src/GuardIntervalInserter.h index f88bdac..5aaad2b 100644 --- a/src/GuardIntervalInserter.h +++ b/src/GuardIntervalInserter.h @@ -58,7 +58,7 @@ class GuardIntervalInserter : public ModCodec, public RemoteControllable /******* REMOTE CONTROL ********/ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: void update_window(size_t new_window_overlap); diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp index a2b0082..30d4ce9 100644 --- a/src/MemlessPoly.cpp +++ b/src/MemlessPoly.cpp @@ -467,9 +467,9 @@ const string MemlessPoly::get_parameter(const string& parameter) const return ss.str(); } -const RemoteControllable::map_t MemlessPoly::get_all_values() const +const json::map_t MemlessPoly::get_all_values() const { - map_t map; + json::map_t map; map["ncoefs"] = m_coefs_am.size(); map["coefs"] = serialise_coefficients(); map["coeffile"] = m_coefs_file; diff --git a/src/MemlessPoly.h b/src/MemlessPoly.h index 09adc13..91e6860 100644 --- a/src/MemlessPoly.h +++ b/src/MemlessPoly.h @@ -68,7 +68,7 @@ public: /******* REMOTE CONTROL ********/ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; private: int internal_process(Buffer* const dataIn, Buffer* dataOut) override; diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index d161861..cb799d3 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -458,9 +458,9 @@ const std::string OfdmGenerator::get_parameter(const std::string& parameter) con return ss.str(); } -const RemoteControllable::map_t OfdmGenerator::get_all_values() const +const json::map_t OfdmGenerator::get_all_values() const { - map_t map; + json::map_t map; // TODO needs rework of the values return map; } diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h index 90e562a..dc1ad46 100644 --- a/src/OfdmGenerator.h +++ b/src/OfdmGenerator.h @@ -61,7 +61,7 @@ class OfdmGenerator : public ModCodec, public RemoteControllable /* Functions for the remote control */ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: struct cfr_iter_stat_t { diff --git a/src/TII.cpp b/src/TII.cpp index b329cdb..9068630 100644 --- a/src/TII.cpp +++ b/src/TII.cpp @@ -385,9 +385,9 @@ const std::string TII::get_parameter(const std::string& parameter) const return ss.str(); } -const RemoteControllable::map_t TII::get_all_values() const +const json::map_t TII::get_all_values() const { - map_t map; + json::map_t map; map["enable"] = m_conf.enable; map["pattern"] = m_conf.pattern; map["comb"] = m_conf.comb; diff --git a/src/TII.h b/src/TII.h index b0ba646..a8d0ca9 100644 --- a/src/TII.h +++ b/src/TII.h @@ -89,7 +89,7 @@ class TII : public ModCodec, public RemoteControllable /******* REMOTE CONTROL ********/ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; protected: // Fill m_Acp with the correct carriers for the pattern/comb diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 149cd50..4277e55 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -301,9 +301,9 @@ const std::string TimestampDecoder::get_parameter( return ss.str(); } -const RemoteControllable::map_t TimestampDecoder::get_all_values() const +const json::map_t TimestampDecoder::get_all_values() const { - map_t map; + json::map_t map; map["offset"] = timestamp_offset; if (full_timestamp_received) { map["timestamp"] = time_secs + ((double)time_pps / 16384000.0); diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index dc5aa78..b90c328 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -120,7 +120,7 @@ class TimestampDecoder : public RemoteControllable /* Base function to set parameters. */ virtual void set_parameter(const std::string& parameter, const std::string& value) override; virtual const std::string get_parameter(const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; const char* name() { return "TS"; } diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 11321f2..4fc3277 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); + const auto& value = stat.at(parameter).data; if (std::holds_alternative(value)) { ss << std::get(value); } @@ -485,9 +485,9 @@ const string SDR::get_parameter(const string& parameter) const return ss.str(); } -const RemoteControllable::map_t SDR::get_all_values() const +const json::map_t SDR::get_all_values() const { - map_t stat = m_device->get_run_statistics(); + json::map_t stat = m_device->get_run_statistics(); stat["txgain"] = m_config.txgain; stat["rxgain"] = m_config.rxgain; diff --git a/src/output/SDR.h b/src/output/SDR.h index 94c972b..960de0c 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -67,7 +67,7 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { virtual const std::string get_parameter( const std::string& parameter) const override; - virtual const RemoteControllable::map_t get_all_values() const override; + virtual const json::map_t get_all_values() const override; private: void process_thread_entry(void); diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index f84b340..f728d8b 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -116,7 +116,7 @@ struct FrameData { // All SDR Devices must implement the SDRDevice interface class SDRDevice { public: - using run_statistics_t = RemoteControllable::map_t; + using run_statistics_t = json::map_t; virtual void tune(double lo_offset, double frequency) = 0; virtual double get_tx_freq(void) const = 0; -- cgit v1.2.3 From 0887d7e859605fad9617681695e70e3ef738a19c Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 20 Jul 2023 18:14:51 +0200 Subject: Add main loop RC metrics --- lib/Socket.cpp | 2 +- src/DabMod.cpp | 165 ++++++++++++++++++-------------------------- src/DabModulator.h | 2 + src/EtiReader.cpp | 2 +- src/GuardIntervalInserter.h | 2 + src/InputTcpReader.cpp | 3 + src/TII.h | 1 + src/TimestampDecoder.h | 1 + src/Utils.cpp | 82 ++++++++++++++++++++++ src/Utils.h | 13 ++-- 10 files changed, 169 insertions(+), 104 deletions(-) (limited to 'src/TII.h') diff --git a/lib/Socket.cpp b/lib/Socket.cpp index 10ec1ca..b71c01e 100644 --- a/lib/Socket.cpp +++ b/lib/Socket.cpp @@ -893,7 +893,7 @@ ssize_t TCPClient::recv(void *buffer, size_t length, int flags, int timeout_ms) return 0; } - return 0; + throw std::logic_error("unreachable"); } void TCPClient::reconnect() diff --git a/src/DabMod.cpp b/src/DabMod.cpp index fdd9e93..7daa72a 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.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 @@ -96,18 +96,58 @@ void signalHandler(int signalNb) running = 0; } -struct modulator_data -{ - // For ETI - std::shared_ptr inputReader; - std::shared_ptr etiReader; +class ModulatorData : public RemoteControllable { + public: + // For ETI + std::shared_ptr inputReader; + std::shared_ptr etiReader; + + // For EDI + std::shared_ptr ediInput; + + // Common to both EDI and EDI + uint64_t framecount = 0; + Flowgraph *flowgraph = nullptr; + + + // RC-related + ModulatorData() : RemoteControllable("mainloop") { + RC_ADD_PARAMETER(num_modulator_restarts, "(Read-only) Number of mod restarts"); + RC_ADD_PARAMETER(most_recent_edi_decoded, "(Read-only) UNIX Timestamp of most recently decoded EDI frame"); + } + + virtual ~ModulatorData() {} + + virtual void set_parameter(const std::string& parameter, const std::string& value) { + throw ParameterError("Parameter " + parameter + " is read-only"); + } + + virtual const std::string get_parameter(const std::string& parameter) const { + stringstream ss; + if (parameter == "num_modulator_restarts") { + ss << num_modulator_restarts; + } + else if (parameter == "most_recent_edi_decoded") { + ss << most_recent_edi_decoded; + } + else { + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } + return ss.str(); + } - // For EDI - std::shared_ptr ediInput; + virtual const json::map_t get_all_values() const + { + json::map_t map; + map["num_modulator_restarts"].v = num_modulator_restarts; + map["most_recent_edi_decoded"].v = most_recent_edi_decoded; + return map; + } - // Common to both EDI and EDI - uint64_t framecount = 0; - Flowgraph *flowgraph = nullptr; + size_t num_modulator_restarts = 0; + time_t most_recent_edi_decoded = 0; }; enum class run_modulator_state_t { @@ -117,88 +157,8 @@ enum class run_modulator_state_t { reconfigure // Some sort of change of configuration we cannot handle happened }; -static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, modulator_data& m); - -static void printModSettings(const mod_settings_t& mod_settings) -{ - stringstream ss; - // Print settings - ss << "Input\n"; - ss << " Type: " << mod_settings.inputTransport << "\n"; - ss << " Source: " << mod_settings.inputName << "\n"; - - ss << "Output\n"; - - if (mod_settings.useFileOutput) { - ss << " Name: " << mod_settings.outputName << "\n"; - } -#if defined(HAVE_OUTPUT_UHD) - else if (mod_settings.useUHDOutput) { - ss << " UHD\n" << - " Device: " << mod_settings.sdr_device_config.device << "\n" << - " Subdevice: " << - mod_settings.sdr_device_config.subDevice << "\n" << - " master_clock_rate: " << - mod_settings.sdr_device_config.masterClockRate << "\n" << - " refclk: " << - mod_settings.sdr_device_config.refclk_src << "\n" << - " pps source: " << - mod_settings.sdr_device_config.pps_src << "\n"; - } -#endif -#if defined(HAVE_SOAPYSDR) - else if (mod_settings.useSoapyOutput) { - ss << " SoapySDR\n" - " Device: " << mod_settings.sdr_device_config.device << "\n" << - " master_clock_rate: " << - mod_settings.sdr_device_config.masterClockRate << "\n"; - } -#endif -#if defined(HAVE_DEXTER) - else if (mod_settings.useDexterOutput) { - ss << " PrecisionWave DEXTER\n"; - } -#endif -#if defined(HAVE_LIMESDR) - else if (mod_settings.useLimeOutput) { - ss << " LimeSDR\n" - " Device: " << mod_settings.sdr_device_config.device << "\n" << - " master_clock_rate: " << - mod_settings.sdr_device_config.masterClockRate << "\n"; - } -#endif -#if defined(HAVE_BLADERF) - else if (mod_settings.useBladeRFOutput) { - ss << " BladeRF\n" - " Device: " << mod_settings.sdr_device_config.device << "\n" << - " refclk: " << mod_settings.sdr_device_config.refclk_src << "\n"; - } -#endif - else if (mod_settings.useZeroMQOutput) { - ss << " ZeroMQ\n" << - " Listening on: " << mod_settings.outputName << "\n" << - " Socket type : " << mod_settings.zmqOutputSocketType << "\n"; - } +static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, ModulatorData& m); - ss << " Sampling rate: "; - if (mod_settings.outputRate > 1000) { - if (mod_settings.outputRate > 1000000) { - ss << std::fixed << std::setprecision(4) << - mod_settings.outputRate / 1000000.0 << - " MHz\n"; - } - else { - ss << std::fixed << std::setprecision(4) << - mod_settings.outputRate / 1000.0 << - " kHz\n"; - } - } - else { - ss << std::fixed << std::setprecision(4) << - mod_settings.outputRate << " Hz\n"; - } - fprintf(stderr, "%s", ss.str().c_str()); -} static shared_ptr prepare_output(mod_settings_t& s) { @@ -346,6 +306,9 @@ int launch_modulator(int argc, char* argv[]) printModSettings(mod_settings); + ModulatorData m; + rcs.enrol(&m); + { // This is mostly useful on ARM systems where FFTW planning takes some time. If we do it here // it will be done before the modulator starts up @@ -422,14 +385,15 @@ int launch_modulator(int argc, char* argv[]) "invalid input transport " + mod_settings.inputTransport + " selected!"); } + m.ediInput = ediInput; + m.inputReader = inputReader; + bool run_again = true; while (run_again) { Flowgraph flowgraph(mod_settings.showProcessTime); - modulator_data m; - m.ediInput = ediInput; - m.inputReader = inputReader; + m.framecount = 0; m.flowgraph = &flowgraph; shared_ptr modulator; @@ -493,13 +457,14 @@ int launch_modulator(int argc, char* argv[]) } etiLog.level(info) << m.framecount << " DAB frames, " << ((float)m.framecount * 0.024f) << " seconds encoded"; + m.num_modulator_restarts++; } etiLog.level(info) << "Terminating"; return ret; } -static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, modulator_data& m) +static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, ModulatorData& m) { auto ret = run_modulator_state_t::failure; try { @@ -579,6 +544,12 @@ static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, m break; } + struct timespec t; + if (clock_gettime(CLOCK_REALTIME, &t) != 0) { + throw std::runtime_error(std::string("Failed to retrieve CLOCK_REALTIME") + strerror(errno)); + } + + m.most_recent_edi_decoded = t.tv_sec; fct = m.ediInput->ediReader.getFct(); fp = m.ediInput->ediReader.getFp(); ts = m.ediInput->ediReader.getTimestamp(); @@ -611,7 +582,7 @@ static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, m last_eti_fct = fct; } else { - etiLog.level(info) << "ETI FCT discontinuity, expected " << + etiLog.level(warn) << "ETI FCT discontinuity, expected " << expected_fct << " received " << fct; if (m.ediInput) { m.ediInput->ediReader.clearFrame(); diff --git a/src/DabModulator.h b/src/DabModulator.h index 140f313..093a782 100644 --- a/src/DabModulator.h +++ b/src/DabModulator.h @@ -53,6 +53,8 @@ public: DabModulator(EtiSource& etiSource, mod_settings_t& settings, const std::string& format); // Allowed formats: s8, u8 and s16. Empty string means no conversion + virtual ~DabModulator() {} + int process(Buffer* dataOut) override; const char* name() override { return "DabModulator"; } diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index e992e62..580088b 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -646,7 +646,7 @@ bool EdiTransport::rxPacket() const int timeout_ms = 1000; try { ssize_t ret = m_tcpclient.recv(m_tcpbuffer.data(), m_tcpbuffer.size(), 0, timeout_ms); - if (ret == 0 or ret == -1) { + if (ret <= 0) { return false; } else if (ret > (ssize_t)m_tcpbuffer.size()) { diff --git a/src/GuardIntervalInserter.h b/src/GuardIntervalInserter.h index 5aaad2b..f78ac91 100644 --- a/src/GuardIntervalInserter.h +++ b/src/GuardIntervalInserter.h @@ -52,6 +52,8 @@ class GuardIntervalInserter : public ModCodec, public RemoteControllable size_t symSize, size_t& windowOverlap); + virtual ~GuardIntervalInserter() {} + int process(Buffer* const dataIn, Buffer* dataOut) override; const char* name() override { return "GuardIntervalInserter"; } diff --git a/src/InputTcpReader.cpp b/src/InputTcpReader.cpp index 21f8496..8ba4d74 100644 --- a/src/InputTcpReader.cpp +++ b/src/InputTcpReader.cpp @@ -79,6 +79,9 @@ int InputTcpReader::GetNextFrame(void* buffer) etiLog.level(debug) << "TCP input auto reconnect"; std::this_thread::sleep_for(std::chrono::seconds(1)); } + else if (ret == -2) { + etiLog.level(debug) << "TCP input timeout"; + } return ret; } diff --git a/src/TII.h b/src/TII.h index a8d0ca9..f6de70b 100644 --- a/src/TII.h +++ b/src/TII.h @@ -82,6 +82,7 @@ class TII : public ModCodec, public RemoteControllable { public: TII(unsigned int dabmode, tii_config_t& tii_config); + virtual ~TII() {} int process(Buffer* dataIn, Buffer* dataOut) override; const char* name() override; diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index b90c328..25796ca 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -98,6 +98,7 @@ class TimestampDecoder : public RemoteControllable * frame transmission */ TimestampDecoder(double& offset_s); + virtual ~TimestampDecoder() {} frame_timestamp getTimestamp(void); diff --git a/src/Utils.cpp b/src/Utils.cpp index 3f378a7..94f198c 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -25,6 +25,7 @@ along with ODR-DabMod. If not, see . */ +#include "sstream" #include "Utils.h" #include "GainControl.h" #if defined(HAVE_PRCTL) @@ -144,6 +145,87 @@ void printStartupInfo() printHeader(); } +void printModSettings(const mod_settings_t& mod_settings) +{ + std::stringstream ss; + // Print settings + ss << "Input\n"; + ss << " Type: " << mod_settings.inputTransport << "\n"; + ss << " Source: " << mod_settings.inputName << "\n"; + + ss << "Output\n"; + + if (mod_settings.useFileOutput) { + ss << " Name: " << mod_settings.outputName << "\n"; + } +#if defined(HAVE_OUTPUT_UHD) + else if (mod_settings.useUHDOutput) { + ss << " UHD\n" << + " Device: " << mod_settings.sdr_device_config.device << "\n" << + " Subdevice: " << + mod_settings.sdr_device_config.subDevice << "\n" << + " master_clock_rate: " << + mod_settings.sdr_device_config.masterClockRate << "\n" << + " refclk: " << + mod_settings.sdr_device_config.refclk_src << "\n" << + " pps source: " << + mod_settings.sdr_device_config.pps_src << "\n"; + } +#endif +#if defined(HAVE_SOAPYSDR) + else if (mod_settings.useSoapyOutput) { + ss << " SoapySDR\n" + " Device: " << mod_settings.sdr_device_config.device << "\n" << + " master_clock_rate: " << + mod_settings.sdr_device_config.masterClockRate << "\n"; + } +#endif +#if defined(HAVE_DEXTER) + else if (mod_settings.useDexterOutput) { + ss << " PrecisionWave DEXTER\n"; + } +#endif +#if defined(HAVE_LIMESDR) + else if (mod_settings.useLimeOutput) { + ss << " LimeSDR\n" + " Device: " << mod_settings.sdr_device_config.device << "\n" << + " master_clock_rate: " << + mod_settings.sdr_device_config.masterClockRate << "\n"; + } +#endif +#if defined(HAVE_BLADERF) + else if (mod_settings.useBladeRFOutput) { + ss << " BladeRF\n" + " Device: " << mod_settings.sdr_device_config.device << "\n" << + " refclk: " << mod_settings.sdr_device_config.refclk_src << "\n"; + } +#endif + else if (mod_settings.useZeroMQOutput) { + ss << " ZeroMQ\n" << + " Listening on: " << mod_settings.outputName << "\n" << + " Socket type : " << mod_settings.zmqOutputSocketType << "\n"; + } + + ss << " Sampling rate: "; + if (mod_settings.outputRate > 1000) { + if (mod_settings.outputRate > 1000000) { + ss << std::fixed << std::setprecision(4) << + mod_settings.outputRate / 1000000.0 << + " MHz\n"; + } + else { + ss << std::fixed << std::setprecision(4) << + mod_settings.outputRate / 1000.0 << + " kHz\n"; + } + } + else { + ss << std::fixed << std::setprecision(4) << + mod_settings.outputRate << " Hz\n"; + } + fprintf(stderr, "%s", ss.str().c_str()); +} + int set_realtime_prio(int prio) { // Set thread priority to realtime diff --git a/src/Utils.h b/src/Utils.h index 9e88488..82728e9 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -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 @@ -31,12 +31,13 @@ # include "config.h" #endif -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include "ConfigParser.h" void printUsage(const char* progName); @@ -44,6 +45,8 @@ void printVersion(void); void printStartupInfo(void); +void printModSettings(const mod_settings_t& mod_settings); + // Set SCHED_RR with priority prio (0=lowest) int set_realtime_prio(int prio); -- cgit v1.2.3