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(-) 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