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 --- src/output/Dexter.h | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/output/Dexter.h (limited to 'src/output/Dexter.h') diff --git a/src/output/Dexter.h b/src/output/Dexter.h new file mode 100644 index 0000000..7a7f6c1 --- /dev/null +++ b/src/output/Dexter.h @@ -0,0 +1,101 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the + Queen in Right of Canada (Communications Research Center Canada) + + Copyright (C) 2022 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org + +DESCRIPTION: + It is an output driver using libiio targeting the PrecisionWave DEXTER board. +*/ + +/* + 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 . + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_DEXTER +#include "iio.h" + +#include +#include + +#include "output/SDR.h" +#include "ModPlugin.h" +#include "EtiReader.h" +#include "RemoteControl.h" + +namespace Output { + +class Dexter : public Output::SDRDevice +{ + public: + Dexter(SDRDeviceConfig& config); + Dexter(const Dexter& other) = delete; + Dexter& operator=(const Dexter& other) = delete; + ~Dexter(); + + virtual void tune(double lo_offset, double frequency) override; + virtual double get_tx_freq(void) const override; + virtual void set_txgain(double txgain) override; + virtual double get_txgain(void) const override; + virtual void set_bandwidth(double bandwidth) override; + virtual double get_bandwidth(void) const override; + virtual void transmit_frame(const struct FrameData& frame) override; + virtual RunStatistics get_run_statistics(void) const override; + virtual double get_real_secs(void) const override; + + virtual void set_rxgain(double rxgain) override; + virtual double get_rxgain(void) const override; + virtual size_t receive_frame( + complexf *buf, + size_t num_samples, + struct frame_timestamp& ts, + double timeout_secs) override; + + // Return true if GPS and reference clock inputs are ok + virtual bool is_clk_source_ok(void) const override; + virtual const char* device_name(void) const override; + + virtual double get_temperature(void) const override; + + private: + SDRDeviceConfig& m_conf; + + struct iio_context* m_ctx = nullptr; + struct iio_device* m_dexter_dsp_tx = nullptr; + + struct iio_device* m_ad9957_tx0 = nullptr; + struct iio_channel* m_tx_channel = nullptr; + struct iio_buffer *m_buffer = nullptr; + + size_t underflows = 0; + size_t overflows = 0; + size_t late_packets = 0; + size_t num_frames_modulated = 0; +}; + +} // namespace Output + +#endif //HAVE_DEXTER + -- cgit v1.2.3 From 0280bd327598342b5562ce11645fae8fcf649e2a Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 18 Aug 2022 13:15:57 +0200 Subject: Improve DEXTER SFN support --- src/ConfigParser.cpp | 3 +- src/DabMod.cpp | 45 ++++++++++--- src/EtiReader.cpp | 34 ++++------ src/EtiReader.h | 17 +++-- src/FicSource.cpp | 69 +++++++++----------- src/FicSource.h | 21 +++--- src/FormatConverter.cpp | 27 ++++++-- src/FormatConverter.h | 5 +- src/ModPlugin.h | 4 +- src/OutputFile.cpp | 31 ++++----- src/TimestampDecoder.cpp | 32 ++++++--- src/TimestampDecoder.h | 4 +- src/output/BladeRF.cpp | 2 +- src/output/BladeRF.h | 4 +- src/output/Dexter.cpp | 164 +++++++++++++++++++++++++++++++++++++++++------ src/output/Dexter.h | 10 ++- src/output/Feedback.cpp | 2 +- src/output/Feedback.h | 2 +- src/output/Lime.cpp | 2 +- src/output/Lime.h | 2 +- src/output/SDR.cpp | 15 +++-- src/output/SDR.h | 2 + src/output/SDRDevice.h | 5 +- src/output/Soapy.cpp | 2 +- src/output/Soapy.h | 2 +- src/output/UHD.cpp | 2 +- src/output/UHD.h | 2 +- src/output/USRPTime.cpp | 9 ++- 28 files changed, 351 insertions(+), 168 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 44d52e6..9190c60 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -409,7 +409,7 @@ static void parse_configfile( } -#if defined(HAVE_OUTPUT_UHD) +#if defined(HAVE_OUTPUT_UHD) || defined(HAVE_DEXTER) mod_settings.sdr_device_config.enableSync = (pt.GetInteger("delaymanagement.synchronous", 0) == 1); mod_settings.sdr_device_config.muteNoTimestamps = (pt.GetInteger("delaymanagement.mutenotimestamps", 0) == 1); if (mod_settings.sdr_device_config.enableSync) { @@ -430,7 +430,6 @@ static void parse_configfile( throw std::runtime_error("Configuration error"); } } - #endif diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 15cdbaa..278f8ce 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -115,7 +115,7 @@ enum class run_modulator_state_t { reconfigure // Some sort of change of configuration we cannot handle happened }; -static run_modulator_state_t run_modulator(modulator_data& m); +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) { @@ -426,6 +426,10 @@ int launch_modulator(int argc, char* argv[]) if (format_converter) { flowgraph.connect(modulator, format_converter); flowgraph.connect(format_converter, output); + + if (auto o = dynamic_pointer_cast(output)) { + o->set_sample_size(format_converter->get_format_size()); + } } else { flowgraph.connect(modulator, output); @@ -435,7 +439,7 @@ int launch_modulator(int argc, char* argv[]) etiLog.level(info) << inputReader->GetPrintableInfo(); } - run_modulator_state_t st = run_modulator(m); + run_modulator_state_t st = run_modulator(mod_settings, m); etiLog.log(trace, "DABMOD,run_modulator() = %d", st); switch (st) { @@ -505,12 +509,13 @@ struct zmq_input_timeout : public std::exception } }; -static run_modulator_state_t run_modulator(modulator_data& m) +static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, modulator_data& m) { auto ret = run_modulator_state_t::failure; try { int last_eti_fct = -1; auto last_frame_received = chrono::steady_clock::now(); + frame_timestamp ts; Buffer data; if (m.inputReader) { data.setLength(6144); @@ -584,6 +589,7 @@ static run_modulator_state_t run_modulator(modulator_data& m) fct = m.etiReader->getFct(); fp = m.etiReader->getFp(); + ts = m.ediInput->ediReader.getTimestamp(); } else if (m.ediInput) { while (running and not m.ediInput->ediReader.isFrameReady()) { @@ -612,9 +618,10 @@ static run_modulator_state_t run_modulator(modulator_data& m) fct = m.ediInput->ediReader.getFct(); fp = m.ediInput->ediReader.getFp(); + ts = m.ediInput->ediReader.getTimestamp(); } - const unsigned expected_fct = (last_eti_fct + 1) % 250; + bool fct_good = false; if (last_eti_fct == -1) { if (fp != 0) { // Do not start the flowgraph before we get to FP 0 @@ -625,19 +632,37 @@ static run_modulator_state_t run_modulator(modulator_data& m) continue; } else { - last_eti_fct = fct; - m.framecount++; - m.flowgraph->run(); + fct_good = true; + } + } + else { + const unsigned expected_fct = (last_eti_fct + 1) % 250; + if (fct == expected_fct) { + fct_good = true; + } + else { + etiLog.level(info) << "ETI FCT discontinuity, expected " << + expected_fct << " received " << fct; + if (m.ediInput) { + m.ediInput->ediReader.clearFrame(); + } + return run_modulator_state_t::again; } } - else if (fct == expected_fct) { + + // timestamp is good if we run unsynchronised, or if it's in the future + bool ts_good = not mod_settings.sdr_device_config.enableSync or + (ts.timestamp_valid and ts.offset_to_system_time() > 0); + + if (fct_good and ts_good) { last_eti_fct = fct; m.framecount++; m.flowgraph->run(); } else { - etiLog.level(info) << "ETI FCT discontinuity, expected " << - expected_fct << " received " << fct; + etiLog.level(warn) << "Skipping frame " << fct << " FCT " << + (fct_good ? "good" : "bad") << " TS " << + (ts_good ? "good" : "bad"); if (m.ediInput) { m.ediInput->ediReader.clearFrame(); } diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index d1c7622..e992e62 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -78,6 +78,11 @@ unsigned EtiReader::getFct() return eti_fc.FCT; } +frame_timestamp EtiReader::getTimestamp() +{ + return myTimestampDecoder.getTimestamp(); +} + const std::vector > EtiReader::getSubchannels() const { @@ -278,22 +283,15 @@ int EtiReader::loadEtiData(const Buffer& dataIn) return dataIn.getLength() - input_size; } -bool EtiReader::sourceContainsTimestamp() -{ - return (ntohl(eti_tist.TIST) & 0xFFFFFF) != 0xFFFFFF; - /* See ETS 300 799, Annex C.2.2 */ -} - uint32_t EtiReader::getPPSOffset() { - if (!sourceContainsTimestamp()) { - //fprintf(stderr, "****** SOURCE NO TS\n"); + const uint32_t timestamp = ntohl(eti_tist.TIST) & 0xFFFFFF; + + /* See ETS 300 799, Annex C.2.2 */ + if (timestamp == 0xFFFFFF) { return 0.0; } - uint32_t timestamp = ntohl(eti_tist.TIST) & 0xFFFFFF; - //fprintf(stderr, "****** TIST 0x%x\n", timestamp); - return timestamp; } @@ -329,6 +327,11 @@ unsigned EdiReader::getFct() return m_fc.fct(); } +frame_timestamp EdiReader::getTimestamp() +{ + return m_timestamp_decoder.getTimestamp(); +} + const std::vector > EdiReader::getSubchannels() const { std::vector > sources; @@ -346,15 +349,6 @@ const std::vector > EdiReader::getSubchannels( return sources; } -bool EdiReader::sourceContainsTimestamp() -{ - if (not (m_frameReady and m_fc_valid)) { - throw std::runtime_error("Trying to get timestamp before it is ready"); - } - - return m_fc.tsta != 0xFFFFFF; -} - bool EdiReader::isFrameReady() { return m_frameReady; diff --git a/src/EtiReader.h b/src/EtiReader.h index d97acf6..fb2c84c 100644 --- a/src/EtiReader.h +++ b/src/EtiReader.h @@ -59,8 +59,8 @@ public: /* Get the current Frame Count */ virtual unsigned getFct() = 0; - /* Returns true if we have valid time stamps in the ETI*/ - virtual bool sourceContainsTimestamp() = 0; + /* Returns current Timestamp */ + virtual frame_timestamp getTimestamp() = 0; /* Return the FIC source to be used for modulation */ virtual std::shared_ptr& getFic(void); @@ -97,18 +97,17 @@ class EtiReader : public EtiSource public: EtiReader(double& tist_offset_s); - virtual unsigned getMode(); - virtual unsigned getFp(); - virtual unsigned getFct(); + virtual unsigned getMode() override; + virtual unsigned getFp() override; + virtual unsigned getFct() override; + virtual frame_timestamp getTimestamp() override; /* Read ETI data from dataIn. Returns the number of bytes * read from the buffer. */ int loadEtiData(const Buffer& dataIn); - virtual bool sourceContainsTimestamp(); - - virtual const std::vector > getSubchannels() const; + virtual const std::vector > getSubchannels() const override; private: /* Transform the ETI TIST to a PPS offset in units of 1/16384000 s */ @@ -141,7 +140,7 @@ public: virtual unsigned getMode() override; virtual unsigned getFp() override; virtual unsigned getFct() override; - virtual bool sourceContainsTimestamp() override; + virtual frame_timestamp getTimestamp() override; virtual const std::vector > getSubchannels() const override; virtual bool isFrameReady(void); diff --git a/src/FicSource.cpp b/src/FicSource.cpp index 2b95085..d824058 100644 --- a/src/FicSource.cpp +++ b/src/FicSource.cpp @@ -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) 2022 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -27,7 +27,6 @@ #include "FicSource.h" #include "PcDebug.h" #include "Log.h" -#include "TimestampDecoder.h" #include #include @@ -36,46 +35,45 @@ #include -const std::vector& FicSource::get_rules() -{ - return d_puncturing_rules; -} - - FicSource::FicSource(unsigned ficf, unsigned mid) : ModInput() { // PDEBUG("FicSource::FicSource(...)\n"); // PDEBUG(" Start address: %i\n", d_start_address); -// PDEBUG(" Framesize: %i\n", d_framesize); +// PDEBUG(" Framesize: %i\n", m_framesize); // PDEBUG(" Protection: %i\n", d_protection); if (ficf == 0) { - d_framesize = 0; - d_buffer.setLength(0); + m_buffer.setLength(0); return; } if (mid == 3) { - d_framesize = 32 * 4; - d_puncturing_rules.emplace_back(29 * 16, 0xeeeeeeee); - d_puncturing_rules.emplace_back(3 * 16, 0xeeeeeeec); + m_framesize = 32 * 4; + m_puncturing_rules.emplace_back(29 * 16, 0xeeeeeeee); + m_puncturing_rules.emplace_back(3 * 16, 0xeeeeeeec); } else { - d_framesize = 24 * 4; - d_puncturing_rules.emplace_back(21 * 16, 0xeeeeeeee); - d_puncturing_rules.emplace_back(3 * 16, 0xeeeeeeec); + m_framesize = 24 * 4; + m_puncturing_rules.emplace_back(21 * 16, 0xeeeeeeee); + m_puncturing_rules.emplace_back(3 * 16, 0xeeeeeeec); } - d_buffer.setLength(d_framesize); + m_buffer.setLength(m_framesize); +} + +size_t FicSource::getFramesize() const +{ + return m_framesize; } -size_t FicSource::getFramesize() +const std::vector& FicSource::get_rules() const { - return d_framesize; + return m_puncturing_rules; } + void FicSource::loadFicData(const Buffer& fic) { - d_buffer = fic; + m_buffer = fic; } int FicSource::process(Buffer* outputData) @@ -83,34 +81,31 @@ int FicSource::process(Buffer* outputData) PDEBUG("FicSource::process (outputData: %p, outputSize: %zu)\n", outputData, outputData->getLength()); - if (d_buffer.getLength() != d_framesize) { + if (m_buffer.getLength() != m_framesize) { throw std::runtime_error( - "ERROR: FicSource::process.outputSize != d_framesize: " + - std::to_string(d_buffer.getLength()) + " != " + - std::to_string(d_framesize)); + "ERROR: FicSource::process.outputSize != m_framesize: " + + std::to_string(m_buffer.getLength()) + " != " + + std::to_string(m_framesize)); } - *outputData = d_buffer; + *outputData = m_buffer; return outputData->getLength(); } -void FicSource::loadTimestamp(const std::shared_ptr& ts) +void FicSource::loadTimestamp(const frame_timestamp& ts) { - d_ts = ts; + m_ts_valid = true; + m_ts = ts; } - meta_vec_t FicSource::process_metadata(const meta_vec_t& metadataIn) { - if (not d_ts) { - return {}; - } - - using namespace std; meta_vec_t md_vec; - flowgraph_metadata meta; - meta.ts = d_ts; - md_vec.push_back(meta); + if (m_ts_valid) { + flowgraph_metadata meta; + meta.ts = m_ts; + md_vec.push_back(meta); + } return md_vec; } diff --git a/src/FicSource.h b/src/FicSource.h index 93c1a7f..01dba2d 100644 --- a/src/FicSource.h +++ b/src/FicSource.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) 2016 + Copyright (C) 2022 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -33,6 +33,7 @@ #include "PuncturingRule.h" #include "Eti.h" #include "ModPlugin.h" +#include "TimestampDecoder.h" #include #include @@ -41,21 +42,21 @@ class FicSource : public ModInput, public ModMetadata public: FicSource(unsigned ficf, unsigned mid); - size_t getFramesize(); - const std::vector& get_rules(); + size_t getFramesize() const; + const std::vector& get_rules() const; void loadFicData(const Buffer& fic); int process(Buffer* outputData) override; const char* name() override { return "FicSource"; } - void loadTimestamp(const std::shared_ptr& ts); - virtual meta_vec_t process_metadata( - const meta_vec_t& metadataIn) override; + void loadTimestamp(const frame_timestamp& ts); + virtual meta_vec_t process_metadata(const meta_vec_t& metadataIn) override; private: - size_t d_framesize; - Buffer d_buffer; - std::shared_ptr d_ts; - std::vector d_puncturing_rules; + size_t m_framesize = 0; + Buffer m_buffer; + frame_timestamp m_ts; + bool m_ts_valid = false; + std::vector m_puncturing_rules; }; diff --git a/src/FormatConverter.cpp b/src/FormatConverter.cpp index 0f86d42..cda8a4d 100644 --- a/src/FormatConverter.cpp +++ b/src/FormatConverter.cpp @@ -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) 2017 + Copyright (C) 2022 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -34,10 +34,6 @@ #include #include -#ifdef __SSE__ -# include -#endif - FormatConverter::FormatConverter(const std::string& format) : ModCodec(), m_format(format) @@ -68,7 +64,7 @@ int FormatConverter::process(Buffer* const dataIn, Buffer* dataOut) out[i] = in[i] + 128; } } - else { + else if (m_format == "s8") { dataOut->setLength(sizeIn * sizeof(int8_t)); int8_t* out = reinterpret_cast(dataOut->getData()); @@ -76,6 +72,9 @@ int FormatConverter::process(Buffer* const dataIn, Buffer* dataOut) out[i] = in[i]; } } + else { + throw std::runtime_error("FormatConverter: Invalid format " + m_format); + } return dataOut->getLength(); } @@ -85,3 +84,19 @@ const char* FormatConverter::name() return "FormatConverter"; } +size_t FormatConverter::get_format_size() const +{ + // Returns 2*sizeof(SAMPLE_TYPE) because we have I + Q + if (m_format == "s16") { + return 4; + } + else if (m_format == "u8") { + return 2; + } + else if (m_format == "s8") { + return 2; + } + else { + throw std::runtime_error("FormatConverter: Invalid format " + m_format); + } +} diff --git a/src/FormatConverter.h b/src/FormatConverter.h index cc8a606..ceb2e17 100644 --- a/src/FormatConverter.h +++ b/src/FormatConverter.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) 2017 + Copyright (C) 2022 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -40,11 +40,14 @@ class FormatConverter : public ModCodec { public: + // Allowed formats: s8, u8 and s16 FormatConverter(const std::string& format); int process(Buffer* const dataIn, Buffer* dataOut); const char* name(); + size_t get_format_size() const; + private: std::string m_format; }; diff --git a/src/ModPlugin.h b/src/ModPlugin.h index 7f03618..470508f 100644 --- a/src/ModPlugin.h +++ b/src/ModPlugin.h @@ -32,6 +32,7 @@ #include "Buffer.h" #include "ThreadsafeQueue.h" +#include "TimestampDecoder.h" #include #include #include @@ -41,9 +42,8 @@ // All flowgraph elements derive from ModPlugin, or a variant of it. // Some ModPlugins also support handling metadata. -struct frame_timestamp; struct flowgraph_metadata { - std::shared_ptr ts; + frame_timestamp ts; }; using meta_vec_t = std::vector; diff --git a/src/OutputFile.cpp b/src/OutputFile.cpp index acaebad..2ee838c 100644 --- a/src/OutputFile.cpp +++ b/src/OutputFile.cpp @@ -74,28 +74,23 @@ meta_vec_t OutputFile::process_metadata(const meta_vec_t& metadataIn) frame_timestamp first_ts; for (const auto& md : metadataIn) { - if (md.ts) { - // The following code assumes TM I, where we get called every 96ms. - // Support for other transmission modes skipped because this is mostly - // debugging code. + // The following code assumes TM I, where we get called every 96ms. + // Support for other transmission modes skipped because this is mostly + // debugging code. - if (md.ts->fp == 0 or md.ts->fp == 4) { - first_ts = *md.ts; - } + if (md.ts.fp == 0 or md.ts.fp == 4) { + first_ts = md.ts; + } - ss << " FCT=" << md.ts->fct << - " FP=" << (int)md.ts->fp; - if (md.ts->timestamp_valid) { - ss << " TS=" << md.ts->timestamp_sec << " + " << - std::fixed - << (double)md.ts->timestamp_pps / 163840000.0 << ";"; - } - else { - ss << " TS invalid;"; - } + ss << " FCT=" << md.ts.fct << + " FP=" << (int)md.ts.fp; + if (md.ts.timestamp_valid) { + ss << " TS=" << md.ts.timestamp_sec << " + " << + std::fixed + << (double)md.ts.timestamp_pps / 163840000.0 << ";"; } else { - ss << " void, "; + ss << " TS invalid;"; } } diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp index 3cfa0cc..54a5817 100644 --- a/src/TimestampDecoder.cpp +++ b/src/TimestampDecoder.cpp @@ -36,6 +36,20 @@ //#define MDEBUG(fmt, args...) fprintf (LOG, "*****" fmt , ## args) #define MDEBUG(fmt, args...) PDEBUG(fmt, ## args) +double frame_timestamp::offset_to_system_time() const +{ + if (not timestamp_valid) { + throw new std::runtime_error("Cannot calculate offset for invalid timestamp"); + } + + struct timespec t; + if (clock_gettime(CLOCK_REALTIME, &t) != 0) { + throw std::runtime_error(std::string("Failed to retrieve CLOCK_REALTIME") + strerror(errno)); + } + + return get_real_secs() - (double)t.tv_sec - (t.tv_nsec / 1000000000.0); +} + frame_timestamp& frame_timestamp::operator+=(const double& diff) { double offset_pps, offset_secs; @@ -75,20 +89,20 @@ TimestampDecoder::TimestampDecoder(double& offset_s) : timestamp_offset << " offset"; } -std::shared_ptr TimestampDecoder::getTimestamp() +frame_timestamp TimestampDecoder::getTimestamp() { - auto ts = std::make_shared(); + frame_timestamp ts; - ts->timestamp_valid = full_timestamp_received; - ts->timestamp_sec = time_secs; - ts->timestamp_pps = time_pps; - ts->fct = latestFCT; - ts->fp = latestFP; + ts.timestamp_valid = full_timestamp_received; + ts.timestamp_sec = time_secs; + ts.timestamp_pps = time_pps; + ts.fct = latestFCT; + ts.fp = latestFP; - ts->timestamp_refresh = offset_changed; + ts.timestamp_refresh = offset_changed; offset_changed = false; - *ts += timestamp_offset; + ts += timestamp_offset; return ts; } diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h index d083061..3616bab 100644 --- a/src/TimestampDecoder.h +++ b/src/TimestampDecoder.h @@ -56,6 +56,8 @@ struct frame_timestamp return timestamp_pps / 16384000.0; } + double offset_to_system_time() const; + double get_real_secs() const { double t = timestamp_sec; t += pps_offset(); @@ -93,7 +95,7 @@ class TimestampDecoder : public RemoteControllable */ TimestampDecoder(double& offset_s); - std::shared_ptr getTimestamp(void); + frame_timestamp getTimestamp(void); /* Update timestamp data from ETI */ void updateTimestampEti( diff --git a/src/output/BladeRF.cpp b/src/output/BladeRF.cpp index a6ad0cc..dd48736 100755 --- a/src/output/BladeRF.cpp +++ b/src/output/BladeRF.cpp @@ -269,7 +269,7 @@ double BladeRF::get_rxgain(void) const size_t BladeRF::receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp &ts, + frame_timestamp &ts, double timeout_secs) { // TODO diff --git a/src/output/BladeRF.h b/src/output/BladeRF.h index bc6db38..e048daa 100755 --- a/src/output/BladeRF.h +++ b/src/output/BladeRF.h @@ -83,7 +83,7 @@ class BladeRF : public Output::SDRDevice virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) override; // Return true if GPS and reference clock inputs are ok @@ -109,4 +109,4 @@ class BladeRF : public Output::SDRDevice } // namespace Output -#endif // HAVE_BLADERF \ No newline at end of file +#endif // HAVE_BLADERF diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 605c61a..e4f672b 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -44,7 +44,11 @@ using namespace std; namespace Output { +static constexpr uint64_t DSP_CLOCK = 2048000uLL * 80; + static constexpr size_t TRANSMISSION_FRAME_LEN = (2656 + 76 * 2552) * 4; +static constexpr size_t IIO_BUFFERS = 4; +static constexpr size_t IIO_BUFFER_LEN = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; static string get_iio_error(int err) { @@ -53,6 +57,13 @@ static string get_iio_error(int err) return string(dst); } +static void fill_time(struct timespec *t) +{ + if (clock_gettime(CLOCK_REALTIME, t) != 0) { + throw std::runtime_error(string("Failed to retrieve CLOCK_REALTIME") + strerror(errno)); + } +} + Dexter::Dexter(SDRDeviceConfig& config) : SDRDevice(), m_conf(config) @@ -93,8 +104,6 @@ Dexter::Dexter(SDRDeviceConfig& config) : etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = 0: " << get_iio_error(r); } - //iio_device_attr_read_longlong(const struct iio_device *dev, const char *attr, long long *val); - if (m_conf.sampleRate != 2048000) { throw std::runtime_error("Dexter: Only 2048000 samplerate supported"); } @@ -109,7 +118,62 @@ Dexter::Dexter(SDRDeviceConfig& config) : // skip: antenna - // TODO: set H/W time + // get H/W time + /* Procedure: + * Wait 200ms after second change, fetch pps_clks attribute + * idem at the next second, and check that pps_clks incremented by DSP_CLOCK + * If ok, store the correspondence between current second change (measured in UTC clock time) + * and the counter value at pps rising edge. */ + + etiLog.level(info) << "Dexter: Waiting for second change..."; + + struct timespec time_at_startup; + fill_time(&time_at_startup); + time_at_startup.tv_nsec = 0; + + struct timespec time_now; + do { + fill_time(&time_now); + this_thread::sleep_for(chrono::milliseconds(1)); + } while (time_at_startup.tv_sec == time_now.tv_sec); + this_thread::sleep_for(chrono::milliseconds(200)); + + long long pps_clks = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + time_t tnow = time_now.tv_sec; + etiLog.level(info) << "Dexter: pps_clks " << pps_clks << " at UTC " << + put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); + + time_at_startup.tv_sec = time_now.tv_sec; + do { + fill_time(&time_now); + this_thread::sleep_for(chrono::milliseconds(1)); + } while (time_at_startup.tv_sec == time_now.tv_sec); + this_thread::sleep_for(chrono::milliseconds(200)); + + long long pps_clks2 = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks2)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + tnow = time_now.tv_sec; + etiLog.level(info) << "Dexter: pps_clks increased by " << pps_clks2 - pps_clks << " at UTC " << + put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); + + if ((uint64_t)pps_clks + DSP_CLOCK != (uint64_t)pps_clks2) { + throw std::runtime_error("Dexter: Wrong increase of pps_clks, expected " + to_string(DSP_CLOCK)); + } + m_utc_seconds_at_startup = time_now.tv_sec; + m_clock_count_at_startup = pps_clks2; + + // Reset start_clks + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", 0)) != 0) { + etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = " << 0 << " : " << get_iio_error(r); + } // Prepare streams constexpr int CHANNEL_INDEX = 0; @@ -120,7 +184,7 @@ Dexter::Dexter(SDRDeviceConfig& config) : iio_channel_enable(m_tx_channel); - m_buffer = iio_device_create_buffer(m_ad9957_tx0, TRANSMISSION_FRAME_LEN/sizeof(int16_t), 0); + m_buffer = iio_device_create_buffer(m_ad9957_tx0, IIO_BUFFER_LEN/sizeof(int16_t), 0); if (!m_buffer) { throw std::runtime_error("Dexter: Cannot create IIO buffer."); } @@ -208,8 +272,8 @@ SDRDevice::RunStatistics Dexter::get_run_statistics(void) const { RunStatistics rs; rs.num_underruns = underflows; - rs.num_overruns = overflows; - rs.num_late_packets = late_packets; + rs.num_overruns = 0; + rs.num_late_packets = num_late; rs.num_frames_modulated = num_frames_modulated; return rs; } @@ -217,8 +281,22 @@ SDRDevice::RunStatistics Dexter::get_run_statistics(void) const double Dexter::get_real_secs(void) const { - // TODO - return 0; + struct timespec time_now; + fill_time(&time_now); + return (double)time_now.tv_sec + time_now.tv_nsec / 1000000000.0; + + /* We don't use actual device time, because we only have clock counter on pps edge available, not + * current clock counter. */ +#if 0 + long long pps_clks = 0; + int r = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + return (double)m_utc_seconds_at_startup + (double)(pps_clks - m_clock_count_at_startup) / (double)DSP_CLOCK; +#endif } void Dexter::set_rxgain(double rxgain) @@ -235,7 +313,7 @@ double Dexter::get_rxgain(void) const size_t Dexter::receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) { // TODO @@ -263,34 +341,82 @@ double Dexter::get_temperature(void) const void Dexter::transmit_frame(const struct FrameData& frame) { - long long int timeNs = frame.ts.get_ns(); - const bool has_time_spec = (m_conf.enableSync and frame.ts.timestamp_valid); - if (frame.buf.size() != TRANSMISSION_FRAME_LEN) { etiLog.level(debug) << "Dexter::transmit_frame Expected " << TRANSMISSION_FRAME_LEN << " got " << frame.buf.size(); throw std::runtime_error("Dexter: invalid buffer size"); } + const bool has_time_spec = (m_conf.enableSync and frame.ts.timestamp_valid); + + if (has_time_spec) { + /* + uint64_t timeS = frame.ts.timestamp_sec; + etiLog.level(debug) << "Dexter: TS S " << timeS << " - " << m_utc_seconds_at_startup << " = " << + timeS - m_utc_seconds_at_startup; + */ + + // 10 because timestamp_pps is represented in 16.384 MHz clocks + constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; + uint64_t frame_ts_clocks = + // at second level + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + + // at subsecond level + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; + + long long pps_clks = 0; + int r; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + } + + etiLog.level(debug) << "Dexter: TS CLK " << + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << + m_clock_count_at_startup << " + " << + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << + frame_ts_clocks << " DELTA " << + frame_ts_clocks << " - " << pps_clks << " = " << + (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; + + // Ensure we hand the frame over to HW at least 0.1s before timestamp + if (((int64_t)frame_ts_clocks - pps_clks) < (int64_t)DSP_CLOCK / 10) { + etiLog.level(warn) << "Skip frame short margin"; + num_late++; + return; + } + + + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_ts_clocks)) != 0) { + etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_ts_clocks << " : " << get_iio_error(r); + num_late++; + return; + } + } + // DabMod::launch_modulator ensures we get int16_t IQ here //const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); //const int16_t *buf = reinterpret_cast(frame.buf.data()); - memcpy(iio_buffer_start(m_buffer), frame.buf.data(), frame.buf.size()); - ssize_t pushed = iio_buffer_push(m_buffer); - if (pushed < 0) { - etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed); + for (size_t i = 0; i < IIO_BUFFERS; i++) { + constexpr size_t buflen = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; + + memcpy(iio_buffer_start(m_buffer), frame.buf.data() + (i * buflen), buflen); + ssize_t pushed = iio_buffer_push(m_buffer); + if (pushed < 0) { + etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed); + } } num_frames_modulated++; - // TODO overflows, late_packets long long attr_value = 0; int r = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { - fprintf(stderr, "buffer_underflows0 %lld\n", attr_value); - underflows = attr_value; + if ((size_t)attr_value != underflows and underflows != 0) { + etiLog.level(warn) << "Dexter: underflow! " << underflows << " -> " << attr_value; + underflows = attr_value; + } } } diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 7a7f6c1..5418b73 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -39,6 +39,7 @@ DESCRIPTION: #include #include +#include #include "output/SDR.h" #include "ModPlugin.h" @@ -70,7 +71,7 @@ class Dexter : public Output::SDRDevice virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) override; // Return true if GPS and reference clock inputs are ok @@ -90,9 +91,12 @@ class Dexter : public Output::SDRDevice struct iio_buffer *m_buffer = nullptr; size_t underflows = 0; - size_t overflows = 0; - size_t late_packets = 0; + size_t num_late = 0; size_t num_frames_modulated = 0; + + uint64_t m_utc_seconds_at_startup; + uint64_t m_clock_count_at_startup = 0; + uint64_t m_clock_count_frame = 0; }; } // namespace Output diff --git a/src/output/Feedback.cpp b/src/output/Feedback.cpp index 88d8319..d112b5a 100644 --- a/src/output/Feedback.cpp +++ b/src/output/Feedback.cpp @@ -84,7 +84,7 @@ DPDFeedbackServer::~DPDFeedbackServer() void DPDFeedbackServer::set_tx_frame( const std::vector &buf, - const struct frame_timestamp &buf_ts) + const frame_timestamp &buf_ts) { if (not m_running) { throw runtime_error("DPDFeedbackServer not running"); diff --git a/src/output/Feedback.h b/src/output/Feedback.h index aef86b0..b31347f 100644 --- a/src/output/Feedback.h +++ b/src/output/Feedback.h @@ -94,7 +94,7 @@ class DPDFeedbackServer { ~DPDFeedbackServer(); void set_tx_frame(const std::vector &buf, - const struct frame_timestamp& ts); + const frame_timestamp& ts); private: // Thread that reacts to burstRequests and receives from the SDR device diff --git a/src/output/Lime.cpp b/src/output/Lime.cpp index 6f7eed5..d3e4640 100644 --- a/src/output/Lime.cpp +++ b/src/output/Lime.cpp @@ -353,7 +353,7 @@ double Lime::get_rxgain(void) const size_t Lime::receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp &ts, + frame_timestamp &ts, double timeout_secs) { // TODO diff --git a/src/output/Lime.h b/src/output/Lime.h index 72a018e..a4603c0 100644 --- a/src/output/Lime.h +++ b/src/output/Lime.h @@ -75,7 +75,7 @@ class Lime : public Output::SDRDevice virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp &ts, + frame_timestamp &ts, double timeout_secs) override; // Return true if GPS and reference clock inputs are ok diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 6078fc7..f1ed2b0 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -104,6 +104,12 @@ SDR::~SDR() } } +void SDR::set_sample_size(size_t size) +{ + etiLog.level(debug) << "Setting sample size to " << size; + m_size = size; +} + int SDR::process(Buffer *dataIn) { if (not m_running) { @@ -125,6 +131,7 @@ meta_vec_t SDR::process_metadata(const meta_vec_t& metadataIn) if (m_device and m_running) { FrameData frame; frame.buf = std::move(m_frame); + frame.sampleSize = m_size; if (metadataIn.empty()) { etiLog.level(info) << @@ -138,7 +145,7 @@ meta_vec_t SDR::process_metadata(const meta_vec_t& metadataIn) * This behaviour is different to earlier versions of ODR-DabMod, * which took the timestamp from the latest ETI frame. */ - frame.ts = *(metadataIn[0].ts); + frame.ts = metadataIn[0].ts; // TODO check device running @@ -261,8 +268,6 @@ void SDR::handle_frame(struct FrameData& frame) { // Assumes m_device is valid - constexpr double tx_timeout = 20.0; - if (not m_device->is_clk_source_ok()) { sleep_through_frame(); return; @@ -298,7 +303,7 @@ void SDR::handle_frame(struct FrameData& frame) } if (last_tx_time_initialised) { - const size_t sizeIn = frame.buf.size() / sizeof(complexf); + const size_t sizeIn = frame.buf.size() / frame.sampleSize; // Checking units for the increment calculation: // samps * ticks/s / (samps/s) @@ -337,7 +342,7 @@ void SDR::handle_frame(struct FrameData& frame) etiLog.log(trace, "SDR,tist %f", time_spec.get_real_secs()); - if (time_spec.get_real_secs() + tx_timeout < device_time) { + if (time_spec.get_real_secs() < device_time) { etiLog.level(warn) << "OutputSDR: Timestamp in the past at FCT=" << frame.ts.fct << " offset: " << std::fixed << diff --git a/src/output/SDR.h b/src/output/SDR.h index ee89243..4dfde73 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -51,6 +51,7 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { SDR operator=(const SDR& other) = delete; virtual ~SDR(); + virtual void set_sample_size(size_t size); virtual int process(Buffer *dataIn) override; virtual meta_vec_t process_metadata(const meta_vec_t& metadataIn) override; @@ -75,6 +76,7 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { std::atomic m_running = ATOMIC_VAR_INIT(false); std::thread m_device_thread; + size_t m_size = sizeof(complexf); std::vector m_frame; ThreadsafeQueue m_queue; diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index bb63f60..0bba74a 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -98,10 +98,11 @@ struct SDRDeviceConfig { struct FrameData { // Buffer holding frame data std::vector buf; + size_t sampleSize = sizeof(complexf); // A full timestamp contains a TIST according to standard // and time information within MNSC with tx_second. - struct frame_timestamp ts; + frame_timestamp ts; }; @@ -132,7 +133,7 @@ class SDRDevice { virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) = 0; // Returns device temperature in degrees C or NaN if not available diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index f138e9a..50f91a4 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -216,7 +216,7 @@ double Soapy::get_rxgain(void) const size_t Soapy::receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) { int flags = 0; diff --git a/src/output/Soapy.h b/src/output/Soapy.h index 4ee53ca..ca2618b 100644 --- a/src/output/Soapy.h +++ b/src/output/Soapy.h @@ -74,7 +74,7 @@ class Soapy : public Output::SDRDevice virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) override; // Return true if GPS and reference clock inputs are ok diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 3cf5aef..9b22dde 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -411,7 +411,7 @@ double UHD::get_rxgain() const size_t UHD::receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) { uhd::stream_cmd_t cmd( diff --git a/src/output/UHD.h b/src/output/UHD.h index 29867fb..4c1a4f0 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -88,7 +88,7 @@ class UHD : public Output::SDRDevice virtual size_t receive_frame( complexf *buf, size_t num_samples, - struct frame_timestamp& ts, + frame_timestamp& ts, double timeout_secs) override; // Return true if GPS and reference clock inputs are ok diff --git a/src/output/USRPTime.cpp b/src/output/USRPTime.cpp index d1197ec..5a11851 100644 --- a/src/output/USRPTime.cpp +++ b/src/output/USRPTime.cpp @@ -46,11 +46,14 @@ USRPTime::USRPTime( m_conf(conf), time_last_check(timepoint_t::clock::now()) { - if (m_conf.pps_src == "none") { + if (m_conf.refclk_src == "internal" and m_conf.pps_src != "none") { + etiLog.level(warn) << "OutputUHD: Unusal refclk and pps source settings. Setting time once, no monitoring."; + set_usrp_time_from_pps(); + } + else if (m_conf.pps_src == "none") { if (m_conf.enableSync) { etiLog.level(warn) << - "OutputUHD: WARNING:" - " you are using synchronous transmission without PPS input!"; + "OutputUHD: you are using synchronous transmission without PPS input!"; } set_usrp_time_from_localtime(); -- cgit v1.2.3 From a2be0c3ab77dab50ded4850f38d2b796b322d0c4 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 14 Oct 2022 20:00:26 +0200 Subject: Add timestamp refresh logic --- src/DabMod.cpp | 4 ++- src/output/Dexter.cpp | 67 +++++++++++++++++++++++++++++++++++++-------------- src/output/Dexter.h | 8 ++++++ src/output/SDR.cpp | 1 + 4 files changed, 61 insertions(+), 19 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 278f8ce..5a4da9a 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -662,7 +662,9 @@ static run_modulator_state_t run_modulator(const mod_settings_t& mod_settings, m else { etiLog.level(warn) << "Skipping frame " << fct << " FCT " << (fct_good ? "good" : "bad") << " TS " << - (ts_good ? "good" : "bad"); + (ts_good ? "good, " : "bad, ") << + (ts.timestamp_valid ? (ts.offset_to_system_time() > 0 ? "in the future" : "in the past") : "invalid"); + if (m.ediInput) { m.ediInput->ediReader.clearFrame(); } diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index b389b31..9437ae6 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -347,9 +347,13 @@ void Dexter::transmit_frame(const struct FrameData& frame) throw std::runtime_error("Dexter: invalid buffer size"); } - const bool has_time_spec = (m_conf.enableSync and frame.ts.timestamp_valid); + const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); - if (has_time_spec) { + if (not require_timestamped_tx) { + etiLog.level(debug) << "TIMESTAMP_STATE STREAMING 1"; + timestamp_state = timestamp_state_t::STREAMING; + } + else if (require_timestamped_tx and timestamp_state == timestamp_state_t::REQUIRES_SET) { /* uint64_t timeS = frame.ts.timestamp_sec; etiLog.level(debug) << "Dexter: TS S " << timeS << " - " << m_utc_seconds_at_startup << " = " << @@ -393,31 +397,58 @@ void Dexter::transmit_frame(const struct FrameData& frame) num_late++; return; } + timestamp_state = timestamp_state_t::STREAMING; + etiLog.level(debug) << "TIMESTAMP_STATE STREAMING 2"; + } + + if (frame.ts.timestamp_refresh) { + etiLog.level(debug) << "TIMESTAMP_STATE WAIT_FOR_UNDERRUN"; + timestamp_state = timestamp_state_t::WAIT_FOR_UNDERRUN; + long long attr_value = 0; + int r = 0; + + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { + underflows = attr_value; + etiLog.level(debug) << "UNDERFLOWS CAPTURE " << underflows; + } } // DabMod::launch_modulator ensures we get int16_t IQ here //const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); //const int16_t *buf = reinterpret_cast(frame.buf.data()); - for (size_t i = 0; i < IIO_BUFFERS; i++) { - constexpr size_t buflen = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; - - memcpy(iio_buffer_start(m_buffer), frame.buf.data() + (i * buflen), buflen); - ssize_t pushed = iio_buffer_push(m_buffer); - if (pushed < 0) { - etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed); + if (timestamp_state == timestamp_state_t::STREAMING) { + for (size_t i = 0; i < IIO_BUFFERS; i++) { + constexpr size_t buflen = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; + + memcpy(iio_buffer_start(m_buffer), frame.buf.data() + (i * buflen), buflen); + ssize_t pushed = iio_buffer_push(m_buffer); + if (pushed < 0) { + etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed); + etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; + timestamp_state = timestamp_state_t::REQUIRES_SET; + } } + num_frames_modulated++; } - num_frames_modulated++; - - long long attr_value = 0; - int r = 0; - - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { - if ((size_t)attr_value != underflows and underflows != 0) { - etiLog.level(warn) << "Dexter: underflow! " << underflows << " -> " << attr_value; - underflows = attr_value; +#warning "We should update underflows all the time" + if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { + long long attr_value = 0; + int r = 0; + + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { + size_t underflows_new = attr_value; + etiLog.level(debug) << "UNDERFLOWS COMPARE " << underflows_new; + + if (underflows_new != underflows and attr_value != 0) { + etiLog.level(warn) << "Dexter: underflow! " << underflows << " -> " << underflows_new; + underflows = underflows_new; + if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { + etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; + timestamp_state = timestamp_state_t::REQUIRES_SET; + } + } } } } diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 5418b73..3e9c34f 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -97,6 +97,14 @@ class Dexter : public Output::SDRDevice uint64_t m_utc_seconds_at_startup; uint64_t m_clock_count_at_startup = 0; uint64_t m_clock_count_frame = 0; + + enum class timestamp_state_t { + REQUIRES_SET, + STREAMING, + WAIT_FOR_UNDERRUN, + }; + + timestamp_state_t timestamp_state = timestamp_state_t::REQUIRES_SET; }; } // namespace Output diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index bd02cab..53f68c2 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -331,6 +331,7 @@ void SDR::handle_frame(struct FrameData& frame) "(" << tx_pps << ")"; frame.ts.timestamp_refresh = true; +#error "wrong, as the frame could be discarded" } } -- cgit v1.2.3 From d2c8a1f40be73417964523e5a942d7d4c558e967 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 26 Oct 2022 18:04:55 +0200 Subject: Add dexter underflow thread, still problematic --- src/DabMod.cpp | 3 +- src/output/Dexter.cpp | 76 +++++++++++++++++++++++++++++++++++---------------- src/output/Dexter.h | 29 ++++++++++++++------ src/output/SDR.cpp | 1 - 4 files changed, 73 insertions(+), 36 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 5a4da9a..45f4d0a 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -493,8 +493,7 @@ int launch_modulator(int argc, char* argv[]) break; } - etiLog.level(info) << m.framecount << " DAB frames encoded"; - etiLog.level(info) << ((float)m.framecount * 0.024f) << " seconds encoded"; + etiLog.level(info) << m.framecount << " DAB frames, " << ((float)m.framecount * 0.024f) << " seconds encoded"; } etiLog.level(info) << "Terminating"; diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index ad4711c..5904824 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -188,10 +188,21 @@ Dexter::Dexter(SDRDeviceConfig& config) : if (!m_buffer) { throw std::runtime_error("Dexter: Cannot create IIO buffer."); } + +#warning "TODO underflow thread" + /* Disabled because it still provokes failed to push buffer Unknown error -110 + m_running = true; + m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); + */ } Dexter::~Dexter() { + m_running = false; + if (m_underflow_read_thread.joinable()) { + m_underflow_read_thread.join(); + } + if (m_ctx) { if (m_dexter_dsp_tx) { iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0); @@ -271,7 +282,10 @@ double Dexter::get_bandwidth(void) const SDRDevice::RunStatistics Dexter::get_run_statistics(void) const { RunStatistics rs; - rs.num_underruns = underflows; + { + std::unique_lock lock(m_underflows_mutex); + rs.num_underruns = underflows; + } rs.num_overruns = 0; rs.num_late_packets = num_late; rs.num_frames_modulated = num_frames_modulated; @@ -374,19 +388,18 @@ void Dexter::transmit_frame(const struct FrameData& frame) etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); } - /* + const double margin = (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; + etiLog.level(debug) << "Dexter: TS CLK " << ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << m_clock_count_at_startup << " + " << (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << frame_ts_clocks << " DELTA " << - frame_ts_clocks << " - " << pps_clks << " = " << - (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; - */ + frame_ts_clocks << " - " << pps_clks << " = " << margin; - // Ensure we hand the frame over to HW at least 0.1s before timestamp - if (((int64_t)frame_ts_clocks - pps_clks) < (int64_t)DSP_CLOCK / 10) { - etiLog.level(warn) << "Skip frame short margin"; + // Ensure we hand the frame over to HW at least 0.2s before timestamp + if (margin < 0.2) { + etiLog.level(warn) << "Skip frame short margin " << margin; num_late++; return; } @@ -404,13 +417,6 @@ void Dexter::transmit_frame(const struct FrameData& frame) if (m_require_timestamp_refresh) { etiLog.level(debug) << "TIMESTAMP_STATE WAIT_FOR_UNDERRUN"; timestamp_state = timestamp_state_t::WAIT_FOR_UNDERRUN; - long long attr_value = 0; - int r = 0; - - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { - underflows = attr_value; - etiLog.level(debug) << "UNDERFLOWS CAPTURE " << underflows; - } } // DabMod::launch_modulator ensures we get int16_t IQ here @@ -424,33 +430,55 @@ void Dexter::transmit_frame(const struct FrameData& frame) memcpy(iio_buffer_start(m_buffer), frame.buf.data() + (i * buflen), buflen); ssize_t pushed = iio_buffer_push(m_buffer); if (pushed < 0) { - etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed); + etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed) << + " after " << num_buffers_pushed << " bufs"; + num_buffers_pushed = 0; etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; timestamp_state = timestamp_state_t::REQUIRES_SET; + break; } + num_buffers_pushed++; } num_frames_modulated++; } -#warning "We should update underflows all the time" - if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { + { + std::unique_lock lock(m_underflows_mutex); + size_t u = underflows; + lock.unlock(); + + if (u != 0 and u != prev_underflows) { + etiLog.level(warn) << "Dexter: underflow! " << prev_underflows << " -> " << u; + if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { + etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; + timestamp_state = timestamp_state_t::REQUIRES_SET; + } + } + + prev_underflows = u; + } +} + +void Dexter::underflow_read_process() +{ + set_thread_name("dexter_underflow"); + + while (m_running) { long long attr_value = 0; int r = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { size_t underflows_new = attr_value; - etiLog.level(debug) << "UNDERFLOWS COMPARE " << underflows_new; + std::unique_lock lock(m_underflows_mutex); + etiLog.level(debug) << "UNDERFLOWS INC BY " << attr_value - (ssize_t)underflows; if (underflows_new != underflows and attr_value != 0) { - etiLog.level(warn) << "Dexter: underflow! " << underflows << " -> " << underflows_new; underflows = underflows_new; - if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { - etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; - timestamp_state = timestamp_state_t::REQUIRES_SET; - } } } + this_thread::sleep_for(chrono::seconds(1)); } + m_running = false; } } // namespace Output diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 3e9c34f..a3c827b 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -40,6 +40,8 @@ DESCRIPTION: #include #include #include +#include +#include #include "output/SDR.h" #include "ModPlugin.h" @@ -54,20 +56,20 @@ class Dexter : public Output::SDRDevice Dexter(SDRDeviceConfig& config); Dexter(const Dexter& other) = delete; Dexter& operator=(const Dexter& other) = delete; - ~Dexter(); + virtual ~Dexter(); virtual void tune(double lo_offset, double frequency) override; virtual double get_tx_freq(void) const override; virtual void set_txgain(double txgain) override; - virtual double get_txgain(void) const override; + virtual double get_txgain() const override; virtual void set_bandwidth(double bandwidth) override; - virtual double get_bandwidth(void) const override; + virtual double get_bandwidth() const override; virtual void transmit_frame(const struct FrameData& frame) override; - virtual RunStatistics get_run_statistics(void) const override; - virtual double get_real_secs(void) const override; + virtual RunStatistics get_run_statistics() const override; + virtual double get_real_secs() const override; virtual void set_rxgain(double rxgain) override; - virtual double get_rxgain(void) const override; + virtual double get_rxgain() const override; virtual size_t receive_frame( complexf *buf, size_t num_samples, @@ -75,10 +77,10 @@ class Dexter : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const override; - virtual const char* device_name(void) const override; + virtual bool is_clk_source_ok() const override; + virtual const char* device_name() const override; - virtual double get_temperature(void) const override; + virtual double get_temperature() const override; private: SDRDeviceConfig& m_conf; @@ -90,10 +92,19 @@ class Dexter : public Output::SDRDevice struct iio_channel* m_tx_channel = nullptr; struct iio_buffer *m_buffer = nullptr; + /* Underflows are counted in a separate thread */ + std::atomic m_running = ATOMIC_VAR_INIT(false); + std::thread m_underflow_read_thread; + void underflow_read_process(); + mutable std::mutex m_underflows_mutex; size_t underflows = 0; + + size_t prev_underflows = 0; size_t num_late = 0; size_t num_frames_modulated = 0; + size_t num_buffers_pushed = 0; + uint64_t m_utc_seconds_at_startup; uint64_t m_clock_count_at_startup = 0; uint64_t m_clock_count_frame = 0; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index ae09acd..2b6700f 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -106,7 +106,6 @@ SDR::~SDR() void SDR::set_sample_size(size_t size) { - etiLog.level(debug) << "Setting sample size to " << size; m_size = size; } -- cgit v1.2.3 From 4f1f002ffad12144237352ad096353b8872171be Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 1 Feb 2023 13:48:11 +0100 Subject: Remove dexter timestamp_state_t --- src/output/Dexter.cpp | 131 ++++++++++++++++++++++++++------------------------ src/output/Dexter.h | 12 ++--- 2 files changed, 72 insertions(+), 71 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 5904824..cc10c57 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -175,7 +175,16 @@ Dexter::Dexter(SDRDeviceConfig& config) : etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = " << 0 << " : " << get_iio_error(r); } - // Prepare streams +#warning "TODO underflow thread" + /* Disabled because it still provokes failed to push buffer Unknown error -110 + m_running = true; + m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); + */ +} + +void Dexter::channel_up() +{ + etiLog.level(debug) << "DEXTER CHANNEL_UP"; constexpr int CHANNEL_INDEX = 0; m_tx_channel = iio_device_get_channel(m_ad9957_tx0, CHANNEL_INDEX); if (m_tx_channel == nullptr) { @@ -185,17 +194,23 @@ Dexter::Dexter(SDRDeviceConfig& config) : iio_channel_enable(m_tx_channel); m_buffer = iio_device_create_buffer(m_ad9957_tx0, IIO_BUFFER_LEN/sizeof(int16_t), 0); - if (!m_buffer) { + if (not m_buffer) { throw std::runtime_error("Dexter: Cannot create IIO buffer."); } +} -#warning "TODO underflow thread" - /* Disabled because it still provokes failed to push buffer Unknown error -110 - m_running = true; - m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); - */ +void Dexter::channel_down() +{ + iio_channel_disable(m_tx_channel); + + etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; + if (m_buffer) { + iio_buffer_destroy(m_buffer); + m_buffer = nullptr; + } } + Dexter::~Dexter() { m_running = false; @@ -208,10 +223,6 @@ Dexter::~Dexter() iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0); } - if (m_buffer) { - iio_buffer_destroy(m_buffer); - } - iio_context_destroy(m_ctx); m_ctx = nullptr; } @@ -363,67 +374,66 @@ void Dexter::transmit_frame(const struct FrameData& frame) const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); - if (not require_timestamped_tx) { - etiLog.level(debug) << "TIMESTAMP_STATE STREAMING 1"; - timestamp_state = timestamp_state_t::STREAMING; - } - else if (require_timestamped_tx and timestamp_state == timestamp_state_t::REQUIRES_SET) { - /* - uint64_t timeS = frame.ts.timestamp_sec; - etiLog.level(debug) << "Dexter: TS S " << timeS << " - " << m_utc_seconds_at_startup << " = " << - timeS - m_utc_seconds_at_startup; - */ - - // 10 because timestamp_pps is represented in 16.384 MHz clocks - constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; - uint64_t frame_ts_clocks = - // at second level - ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + - // at subsecond level - (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; - - long long pps_clks = 0; - int r; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); - } + if (m_buffer == nullptr) { + if (require_timestamped_tx) { + /* + uint64_t timeS = frame.ts.timestamp_sec; + etiLog.level(debug) << "Dexter: TS S " << timeS << " - " << m_utc_seconds_at_startup << " = " << + timeS - m_utc_seconds_at_startup; + */ + + // 10 because timestamp_pps is represented in 16.384 MHz clocks + constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; + uint64_t frame_ts_clocks = + // at second level + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + + // at subsecond level + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; + + long long pps_clks = 0; + int r; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + } - const double margin = (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; + const double margin = (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; - etiLog.level(debug) << "Dexter: TS CLK " << - ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << - m_clock_count_at_startup << " + " << - (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << - frame_ts_clocks << " DELTA " << - frame_ts_clocks << " - " << pps_clks << " = " << margin; + etiLog.level(debug) << "Dexter: TS CLK " << + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << + m_clock_count_at_startup << " + " << + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << + frame_ts_clocks << " DELTA " << + frame_ts_clocks << " - " << pps_clks << " = " << margin; - // Ensure we hand the frame over to HW at least 0.2s before timestamp - if (margin < 0.2) { - etiLog.level(warn) << "Skip frame short margin " << margin; - num_late++; - return; - } + // Ensure we hand the frame over to HW at least 0.2s before timestamp + if (margin < 0.2) { + etiLog.level(warn) << "Skip frame short margin " << margin; + num_late++; + return; + } - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_ts_clocks)) != 0) { - etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_ts_clocks << " : " << get_iio_error(r); - num_late++; - return; + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_ts_clocks)) != 0) { + etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_ts_clocks << " : " << get_iio_error(r); + num_late++; + return; + } } - timestamp_state = timestamp_state_t::STREAMING; - etiLog.level(debug) << "TIMESTAMP_STATE STREAMING 2"; + + channel_up(); } if (m_require_timestamp_refresh) { - etiLog.level(debug) << "TIMESTAMP_STATE WAIT_FOR_UNDERRUN"; - timestamp_state = timestamp_state_t::WAIT_FOR_UNDERRUN; + etiLog.level(debug) << "DEXTER REQUIRE REFRESH"; + channel_down(); + m_require_timestamp_refresh = false; } // DabMod::launch_modulator ensures we get int16_t IQ here //const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); //const int16_t *buf = reinterpret_cast(frame.buf.data()); - if (timestamp_state == timestamp_state_t::STREAMING) { + if (m_buffer) { for (size_t i = 0; i < IIO_BUFFERS; i++) { constexpr size_t buflen = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; @@ -433,8 +443,7 @@ void Dexter::transmit_frame(const struct FrameData& frame) etiLog.level(error) << "Dexter: failed to push buffer " << get_iio_error(pushed) << " after " << num_buffers_pushed << " bufs"; num_buffers_pushed = 0; - etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; - timestamp_state = timestamp_state_t::REQUIRES_SET; + channel_down(); break; } num_buffers_pushed++; @@ -449,10 +458,6 @@ void Dexter::transmit_frame(const struct FrameData& frame) if (u != 0 and u != prev_underflows) { etiLog.level(warn) << "Dexter: underflow! " << prev_underflows << " -> " << u; - if (timestamp_state == timestamp_state_t::WAIT_FOR_UNDERRUN) { - etiLog.level(debug) << "TIMESTAMP_STATE REQUIRES_SET"; - timestamp_state = timestamp_state_t::REQUIRES_SET; - } } prev_underflows = u; diff --git a/src/output/Dexter.h b/src/output/Dexter.h index a3c827b..36d0ef7 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -83,6 +83,10 @@ class Dexter : public Output::SDRDevice virtual double get_temperature() const override; private: + + void channel_up(); + void channel_down(); + SDRDeviceConfig& m_conf; struct iio_context* m_ctx = nullptr; @@ -108,14 +112,6 @@ class Dexter : public Output::SDRDevice uint64_t m_utc_seconds_at_startup; uint64_t m_clock_count_at_startup = 0; uint64_t m_clock_count_frame = 0; - - enum class timestamp_state_t { - REQUIRES_SET, - STREAMING, - WAIT_FOR_UNDERRUN, - }; - - timestamp_state_t timestamp_state = timestamp_state_t::REQUIRES_SET; }; } // namespace Output -- cgit v1.2.3 From 89e4bcbcba5883355a9b4777cea2bce0a1afd53d Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 3 Mar 2023 14:24:27 +0100 Subject: Dexter: calculate frame margin from system time --- src/output/BladeRF.cpp | 2 +- src/output/BladeRF.h | 2 +- src/output/Dexter.cpp | 24 ++++++++++-------------- src/output/Dexter.h | 4 ++-- src/output/Lime.cpp | 2 +- src/output/Lime.h | 2 +- src/output/SDR.cpp | 6 +++--- src/output/SDR.h | 4 ++-- src/output/SDRDevice.h | 2 +- src/output/Soapy.cpp | 2 +- src/output/Soapy.h | 2 +- src/output/UHD.cpp | 2 +- src/output/UHD.h | 2 +- 13 files changed, 26 insertions(+), 30 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/output/BladeRF.cpp b/src/output/BladeRF.cpp index dd48736..db29898 100755 --- a/src/output/BladeRF.cpp +++ b/src/output/BladeRF.cpp @@ -304,7 +304,7 @@ double BladeRF::get_temperature(void) const } -void BladeRF::transmit_frame(const struct FrameData &frame) // SC16 frames +void BladeRF::transmit_frame(struct FrameData&& frame) // SC16 frames { const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); diff --git a/src/output/BladeRF.h b/src/output/BladeRF.h index e048daa..1a63fbf 100755 --- a/src/output/BladeRF.h +++ b/src/output/BladeRF.h @@ -74,7 +74,7 @@ class BladeRF : public Output::SDRDevice virtual double get_txgain(void) const override; virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; - virtual void transmit_frame(const struct FrameData& frame) override; + virtual void transmit_frame(struct FrameData&& frame) override; virtual RunStatistics get_run_statistics(void) const override; virtual double get_real_secs(void) const override; diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 4e24cfb..b6e6700 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2022 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -369,7 +369,7 @@ double Dexter::get_temperature(void) const return std::numeric_limits::quiet_NaN(); } -void Dexter::transmit_frame(const struct FrameData& frame) +void Dexter::transmit_frame(struct FrameData&& frame) { if (frame.buf.size() != TRANSMISSION_FRAME_LEN) { etiLog.level(debug) << "Dexter::transmit_frame Expected " << @@ -379,6 +379,8 @@ void Dexter::transmit_frame(const struct FrameData& frame) const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); + const double margin_s = frame.ts.offset_to_system_time(); + if (m_buffer == nullptr) { if (require_timestamped_tx) { /* @@ -387,36 +389,28 @@ void Dexter::transmit_frame(const struct FrameData& frame) timeS - m_utc_seconds_at_startup; */ - // 10 because timestamp_pps is represented in 16.384 MHz clocks constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; + // TIMESTAMP_PPS_PER_DSP_CLOCKS=10 because timestamp_pps is represented in 16.384 MHz clocks uint64_t frame_ts_clocks = // at second level ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + // at subsecond level (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; - long long pps_clks = 0; - int r; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); - } - - const double margin_s = (double)((int64_t)frame_ts_clocks - pps_clks) / DSP_CLOCK; - etiLog.level(debug) << "DEXTER FCT " << frame.ts.fct << " TS CLK " << ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << m_clock_count_at_startup << " + " << (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << - frame_ts_clocks << " DELTA " << - frame_ts_clocks << " - " << pps_clks << " = " << margin_s; + frame_ts_clocks << " DELTA " << margin_s; // Ensure we hand the frame over to HW with a bit of margin - if (margin_s < 0.1) { + if (margin_s < 0.2) { etiLog.level(warn) << "Skip frame short margin " << margin_s; num_late++; return; } + int r; if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_ts_clocks)) != 0) { etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_ts_clocks << " : " << get_iio_error(r); num_late++; @@ -428,6 +422,8 @@ void Dexter::transmit_frame(const struct FrameData& frame) channel_up(); } + etiLog.level(debug) << "DEXTER TX " << frame.ts.fct << " TS margin " << margin_s; + if (m_require_timestamp_refresh) { etiLog.level(debug) << "DEXTER REQUIRE REFRESH"; channel_down(); diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 36d0ef7..2bd63b1 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2022 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -64,7 +64,7 @@ class Dexter : public Output::SDRDevice virtual double get_txgain() const override; virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth() const override; - virtual void transmit_frame(const struct FrameData& frame) override; + virtual void transmit_frame(struct FrameData&& frame) override; virtual RunStatistics get_run_statistics() const override; virtual double get_real_secs() const override; diff --git a/src/output/Lime.cpp b/src/output/Lime.cpp index d3e4640..bf466c3 100644 --- a/src/output/Lime.cpp +++ b/src/output/Lime.cpp @@ -389,7 +389,7 @@ float Lime::get_fifo_fill_percent(void) const return m_last_fifo_fill_percent * 100; } -void Lime::transmit_frame(const struct FrameData &frame) +void Lime::transmit_frame(struct FrameData&& frame) { if (not m_device) throw runtime_error("Lime device not set up"); diff --git a/src/output/Lime.h b/src/output/Lime.h index a4603c0..95c5c48 100644 --- a/src/output/Lime.h +++ b/src/output/Lime.h @@ -66,7 +66,7 @@ class Lime : public Output::SDRDevice virtual double get_txgain(void) const override; virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; - virtual void transmit_frame(const struct FrameData &frame) override; + virtual void transmit_frame(struct FrameData&& frame) override; virtual RunStatistics get_run_statistics(void) const override; virtual double get_real_secs(void) const override; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 0b3299a..e22617e 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -213,7 +213,7 @@ void SDR::process_thread_entry() } if (m_device) { - handle_frame(frame); + handle_frame(std::move(frame)); } } } @@ -260,7 +260,7 @@ void SDR::sleep_through_frame() t_last_frame += wait_time; } -void SDR::handle_frame(struct FrameData& frame) +void SDR::handle_frame(struct FrameData&& frame) { // Assumes m_device is valid @@ -386,7 +386,7 @@ void SDR::handle_frame(struct FrameData& frame) " TS " << frame.ts.to_string(); } - m_device->transmit_frame(frame); + m_device->transmit_frame(std::move(frame)); } // ======================================= diff --git a/src/output/SDR.h b/src/output/SDR.h index 5c3b599..eb0ed9d 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2022 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -69,7 +69,7 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { private: void process_thread_entry(void); - void handle_frame(struct FrameData &frame); + void handle_frame(struct FrameData&& frame); void sleep_through_frame(void); SDRDeviceConfig& m_config; diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index f4b6c34..ffa1a3b 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -123,7 +123,7 @@ class SDRDevice { virtual double get_tx_freq(void) const = 0; virtual void set_txgain(double txgain) = 0; virtual double get_txgain(void) const = 0; - virtual void transmit_frame(const struct FrameData& frame) = 0; + virtual void transmit_frame(struct FrameData&& frame) = 0; virtual RunStatistics get_run_statistics(void) const = 0; virtual double get_real_secs(void) const = 0; virtual void set_rxgain(double rxgain) = 0; diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index c2ae88a..8c52546 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -272,7 +272,7 @@ double Soapy::get_temperature(void) const return std::numeric_limits::quiet_NaN(); } -void Soapy::transmit_frame(const struct FrameData& frame) +void Soapy::transmit_frame(struct FrameData&& frame) { if (not m_device) throw runtime_error("Soapy device not set up"); diff --git a/src/output/Soapy.h b/src/output/Soapy.h index ca2618b..f3e1ee2 100644 --- a/src/output/Soapy.h +++ b/src/output/Soapy.h @@ -65,7 +65,7 @@ class Soapy : public Output::SDRDevice virtual double get_txgain(void) const override; virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; - virtual void transmit_frame(const struct FrameData& frame) override; + virtual void transmit_frame(struct FrameData&& frame) override; virtual RunStatistics get_run_statistics(void) const override; virtual double get_real_secs(void) const override; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 6810249..c325272 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -315,7 +315,7 @@ double UHD::get_bandwidth(void) const return m_usrp->get_tx_bandwidth(); } -void UHD::transmit_frame(const struct FrameData& frame) +void UHD::transmit_frame(struct FrameData&& frame) { const double tx_timeout = 20.0; const size_t sizeIn = frame.buf.size() / sizeof(complexf); diff --git a/src/output/UHD.h b/src/output/UHD.h index 4c1a4f0..164254c 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -79,7 +79,7 @@ class UHD : public Output::SDRDevice virtual double get_txgain(void) const override; virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; - virtual void transmit_frame(const struct FrameData& frame) override; + virtual void transmit_frame(struct FrameData&& frame) override; virtual RunStatistics get_run_statistics(void) const override; virtual double get_real_secs(void) const override; -- cgit v1.2.3 From ed8e8bed00d5ad47a9d4798059fd0e3a8c3f7b8c Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 24 Mar 2023 10:31:24 +0100 Subject: Fix startup hiccup due to iio buffer allocation taking several seconds --- src/output/Dexter.cpp | 105 ++++++++++++++++++++++++++++++++++---------------- src/output/Dexter.h | 3 +- src/output/SDR.cpp | 2 +- 3 files changed, 75 insertions(+), 35 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index aa0b7ad..93f1bb6 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -48,9 +48,9 @@ static constexpr uint64_t DSP_CLOCK = 2048000uLL * 80; static constexpr uint64_t IIO_TIMEOUT_MS = 1000; -static constexpr size_t TRANSMISSION_FRAME_LEN = (2656 + 76 * 2552) * 4; -static constexpr size_t IIO_BUFFERS = 4; -static constexpr size_t IIO_BUFFER_LEN = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; +static constexpr size_t TRANSMISSION_FRAME_LEN_SAMPS = (2656 + 76 * 2552) * /* I+Q */ 2; +static constexpr size_t IIO_BUFFERS = 2; +static constexpr size_t IIO_BUFFER_LEN_SAMPS = TRANSMISSION_FRAME_LEN_SAMPS / IIO_BUFFERS; static string get_iio_error(int err) { @@ -73,7 +73,7 @@ Dexter::Dexter(SDRDeviceConfig& config) : etiLog.level(info) << "Dexter:Creating the device"; m_ctx = iio_create_local_context(); - if (!m_ctx) { + if (not m_ctx) { throw std::runtime_error("Dexter: Unable to create iio scan context"); } @@ -83,12 +83,12 @@ Dexter::Dexter(SDRDeviceConfig& config) : } m_dexter_dsp_tx = iio_context_find_device(m_ctx, "dexter_dsp_tx"); - if (!m_dexter_dsp_tx) { + if (not m_dexter_dsp_tx) { throw std::runtime_error("Dexter: Unable to find dexter_dsp_tx iio device"); } m_ad9957_tx0 = iio_context_find_device(m_ctx, "ad9957_tx0"); - if (!m_ad9957_tx0) { + if (not m_ad9957_tx0) { throw std::runtime_error("Dexter: Unable to find ad9957_tx0 iio device"); } @@ -101,10 +101,6 @@ Dexter::Dexter(SDRDeviceConfig& config) : etiLog.level(warn) << "Failed to set dexter_dsp_tx.dc1 = false: " << get_iio_error(r); } - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", m_conf.txgain)) != 0) { - etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); - } - if (m_conf.sampleRate != 2048000) { throw std::runtime_error("Dexter: Only 2048000 samplerate supported"); } @@ -176,16 +172,11 @@ Dexter::Dexter(SDRDeviceConfig& config) : etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = " << 0 << " : " << get_iio_error(r); } -#warning "TODO underflow thread" - /* Disabled because it still provokes failed to push buffer Unknown error -110 - m_running = true; - m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); - */ -} + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0)) != 0) { + etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = 0" << + " : " << get_iio_error(r); + } -void Dexter::channel_up() -{ - etiLog.level(debug) << "DEXTER CHANNEL_UP"; constexpr int CHANNEL_INDEX = 0; m_tx_channel = iio_device_get_channel(m_ad9957_tx0, CHANNEL_INDEX); if (m_tx_channel == nullptr) { @@ -194,21 +185,62 @@ void Dexter::channel_up() iio_channel_enable(m_tx_channel); - m_buffer = iio_device_create_buffer(m_ad9957_tx0, IIO_BUFFER_LEN/sizeof(int16_t), 0); + m_buffer = iio_device_create_buffer(m_ad9957_tx0, IIO_BUFFER_LEN_SAMPS, 0); if (not m_buffer) { throw std::runtime_error("Dexter: Cannot create IIO buffer."); } + + // Flush the FPGA FIFO + { + constexpr size_t buflen_samps = TRANSMISSION_FRAME_LEN_SAMPS / IIO_BUFFERS; + constexpr size_t buflen = buflen_samps * sizeof(int16_t); + + memset(iio_buffer_start(m_buffer), 0, buflen); + ssize_t pushed = iio_buffer_push(m_buffer); + if (pushed < 0) { + etiLog.level(error) << "Dexter: init push buffer " << get_iio_error(pushed); + } + this_thread::sleep_for(chrono::milliseconds(200)); + } + + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", m_conf.txgain)) != 0) { + etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = " << m_conf.txgain << + " : " << get_iio_error(r); + } + +#warning "TODO underflow thread" + /* Disabled because it still provokes failed to push buffer Unknown error -110 + m_running = true; + m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); + */ +} + +void Dexter::channel_up() +{ + int r; + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", m_conf.txgain)) != 0) { + etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = " << m_conf.txgain << + " : " << get_iio_error(r); + } + + m_channel_is_up = true; + etiLog.level(debug) << "DEXTER CHANNEL_UP"; } void Dexter::channel_down() { - iio_channel_disable(m_tx_channel); + int r; + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0)) != 0) { + etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); + } - etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; - if (m_buffer) { - iio_buffer_destroy(m_buffer); - m_buffer = nullptr; + // This will flush out the FIFO + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", 0)) != 0) { + etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = 0 : " << get_iio_error(r); } + + m_channel_is_up = false; + etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; } @@ -224,6 +256,11 @@ Dexter::~Dexter() iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0); } + if (m_buffer) { + iio_buffer_destroy(m_buffer); + m_buffer = nullptr; + } + iio_context_destroy(m_ctx); m_ctx = nullptr; } @@ -259,12 +296,12 @@ void Dexter::set_txgain(double txgain) { int r = 0; if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", txgain)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); } long long txgain_readback = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "gain0", &txgain_readback)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); } else { m_conf.txgain = txgain_readback; @@ -276,7 +313,7 @@ double Dexter::get_txgain(void) const long long txgain_readback = 0; int r = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "gain0", &txgain_readback)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); } return txgain_readback; } @@ -367,9 +404,10 @@ double Dexter::get_temperature(void) const void Dexter::transmit_frame(struct FrameData&& frame) { - if (frame.buf.size() != TRANSMISSION_FRAME_LEN) { + constexpr size_t frame_len_bytes = TRANSMISSION_FRAME_LEN_SAMPS * sizeof(int16_t); + if (frame.buf.size() != frame_len_bytes) { etiLog.level(debug) << "Dexter::transmit_frame Expected " << - TRANSMISSION_FRAME_LEN << " got " << frame.buf.size(); + frame_len_bytes << " got " << frame.buf.size(); throw std::runtime_error("Dexter: invalid buffer size"); } @@ -377,7 +415,7 @@ void Dexter::transmit_frame(struct FrameData&& frame) const double margin_s = frame.ts.offset_to_system_time(); - if (m_buffer == nullptr) { + if (not m_channel_is_up) { if (require_timestamped_tx) { /* uint64_t timeS = frame.ts.timestamp_sec; @@ -430,9 +468,10 @@ void Dexter::transmit_frame(struct FrameData&& frame) //const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); //const int16_t *buf = reinterpret_cast(frame.buf.data()); - if (m_buffer) { + if (m_channel_is_up) { for (size_t i = 0; i < IIO_BUFFERS; i++) { - constexpr size_t buflen = TRANSMISSION_FRAME_LEN / IIO_BUFFERS; + constexpr size_t buflen_samps = TRANSMISSION_FRAME_LEN_SAMPS / IIO_BUFFERS; + constexpr size_t buflen = buflen_samps * sizeof(int16_t); memcpy(iio_buffer_start(m_buffer), frame.buf.data() + (i * buflen), buflen); ssize_t pushed = iio_buffer_push(m_buffer); diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 2bd63b1..2f0524e 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -83,10 +83,11 @@ class Dexter : public Output::SDRDevice virtual double get_temperature() const override; private: - void channel_up(); void channel_down(); + bool m_channel_is_up = false; + SDRDeviceConfig& m_conf; struct iio_context* m_ctx = nullptr; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index e22617e..f53197e 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -165,7 +165,7 @@ meta_vec_t SDR::process_metadata(const meta_vec_t& metadataIn) const auto max_size = m_config.enableSync ? - (frame.ts.timestamp_offset * 4.0) / frame_duration_s + (frame.ts.timestamp_offset * 400.0) / frame_duration_s : FRAMES_MAX_SIZE; auto r = m_queue.push_overflow(std::move(frame), max_size); -- cgit v1.2.3 From fd46d43f51796a6bbdd629e08535bacbc853f283 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 29 Mar 2023 16:30:06 +0200 Subject: Replace RunStatistics to permit representation of SDR device specific stats --- src/output/BladeRF.cpp | 22 +++---- src/output/BladeRF.h | 9 +-- src/output/Dexter.cpp | 153 ++++++++++++++++++++++++++++++------------------- src/output/Dexter.h | 7 ++- src/output/Lime.cpp | 31 +++++----- src/output/Lime.h | 8 +-- src/output/SDR.cpp | 77 +++++++++++-------------- src/output/SDRDevice.h | 22 +++---- src/output/Soapy.cpp | 17 +++--- src/output/Soapy.h | 6 +- src/output/UHD.cpp | 24 ++++---- src/output/UHD.h | 4 +- 12 files changed, 197 insertions(+), 183 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/output/BladeRF.cpp b/src/output/BladeRF.cpp index db29898..2dd7176 100755 --- a/src/output/BladeRF.cpp +++ b/src/output/BladeRF.cpp @@ -239,13 +239,10 @@ double BladeRF::get_bandwidth(void) const return (double)bw; } -SDRDevice::RunStatistics BladeRF::get_run_statistics(void) const +SDRDevice::run_statistics_t BladeRF::get_run_statistics(void) const { - RunStatistics rs; - rs.num_underruns = underflows; - rs.num_overruns = overflows; - rs.num_late_packets = late_packets; - rs.num_frames_modulated = num_frames_modulated; + run_statistics_t rs; + rs["frames"] = num_frames_modulated; return rs; } @@ -287,23 +284,22 @@ const char *BladeRF::device_name(void) const return "BladeRF"; } -double BladeRF::get_temperature(void) const +std::optional BladeRF::get_temperature(void) const { if (not m_device) throw runtime_error("BladeRF device not set up"); float temp = 0.0; - int status = bladerf_get_rfic_temperature(m_device, &temp); - if (status < 0) - { + if (status >= 0) { + return (double)temp; + } + else { etiLog.level(error) << "Error getting BladeRF temperature: %s " << bladerf_strerror(status); + return std::nullopt; } - - return (double)temp; } - void BladeRF::transmit_frame(struct FrameData&& frame) // SC16 frames { const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t)); diff --git a/src/output/BladeRF.h b/src/output/BladeRF.h index 1a63fbf..eb3e58b 100755 --- a/src/output/BladeRF.h +++ b/src/output/BladeRF.h @@ -75,7 +75,7 @@ class BladeRF : public Output::SDRDevice virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; virtual void transmit_frame(struct FrameData&& frame) override; - virtual RunStatistics get_run_statistics(void) const override; + virtual run_statistics_t get_run_statistics(void) const override; virtual double get_real_secs(void) const override; virtual void set_rxgain(double rxgain) override; @@ -90,7 +90,7 @@ class BladeRF : public Output::SDRDevice virtual bool is_clk_source_ok(void) const override; virtual const char* device_name(void) const override; - virtual double get_temperature(void) const override; + virtual std::optional get_temperature(void) const override; private: @@ -99,12 +99,7 @@ class BladeRF : public Output::SDRDevice bladerf_channel m_channel = BLADERF_CHANNEL_TX(0); // channel TX0 //struct bladerf_stream* m_stream; /* used for asynchronous api */ - size_t underflows = 0; - size_t overflows = 0; - size_t late_packets = 0; size_t num_frames_modulated = 0; - //size_t num_underflows_previous = 0; - //size_t num_late_packets_previous = 0; }; } // namespace Output diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index d892486..dd882f2 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -74,7 +74,7 @@ Dexter::Dexter(SDRDeviceConfig& config) : m_ctx = iio_create_local_context(); if (not m_ctx) { - throw std::runtime_error("Dexter: Unable to create iio scan context"); + throw std::runtime_error("Dexter: Unable to create iio context"); } int r; @@ -94,11 +94,11 @@ Dexter::Dexter(SDRDeviceConfig& config) : // TODO make DC offset configurable and add to RC if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "dc0", 0)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.dc0 = false: " << get_iio_error(r); + throw std::runtime_error("Failed to set dexter_dsp_tx.dc0 = false: " + get_iio_error(r)); } if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "dc1", 0)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.dc1 = false: " << get_iio_error(r); + throw std::runtime_error("Failed to set dexter_dsp_tx.dc1 = false: " + get_iio_error(r)); } if (m_conf.sampleRate != 2048000) { @@ -167,14 +167,17 @@ Dexter::Dexter(SDRDeviceConfig& config) : m_utc_seconds_at_startup = time_now.tv_sec; m_clock_count_at_startup = pps_clks2; - // Reset start_clks - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", 0)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.stream0_start_clks = " << 0 << " : " << get_iio_error(r); + // The FIFO should not contain data, but setting gain=0 before setting start_clks to zero is an additional security + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0)) != 0) { + throw std::runtime_error("Failed to set dexter_dsp_tx.gain0 = 0 : " + get_iio_error(r)); } - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0)) != 0) { - etiLog.level(error) << "Failed to set dexter_dsp_tx.gain0 = 0" << - " : " << get_iio_error(r); + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_flush_fifo_trigger", 1)) != 0) { + throw std::runtime_error("Failed to set dexter_dsp_tx.stream0_flush_fifo_trigger = 1 : " + get_iio_error(r)); + } + + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", 0)) != 0) { + throw std::runtime_error("Failed to set dexter_dsp_tx.stream0_start_clks = 0 : " + get_iio_error(r)); } constexpr int CHANNEL_INDEX = 0; @@ -208,11 +211,8 @@ Dexter::Dexter(SDRDeviceConfig& config) : " : " << get_iio_error(r); } -#warning "TODO underflow thread" - /* Disabled because it still provokes failed to push buffer Unknown error -110 m_running = true; m_underflow_read_thread = std::thread(&Dexter::underflow_read_process, this); - */ } void Dexter::channel_up() @@ -261,9 +261,18 @@ Dexter::~Dexter() m_buffer = nullptr; } + if (m_tx_channel) { + iio_channel_disable(m_tx_channel); + } + iio_context_destroy(m_ctx); m_ctx = nullptr; } + + if (m_underflow_ctx) { + iio_context_destroy(m_underflow_ctx); + m_underflow_ctx = nullptr; + } } void Dexter::tune(double lo_offset, double frequency) @@ -296,12 +305,12 @@ void Dexter::set_txgain(double txgain) { int r = 0; if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", txgain)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = " << txgain << ": " << get_iio_error(r); } long long txgain_readback = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "gain0", &txgain_readback)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to read dexter_dsp_tx.gain0: " << get_iio_error(r); } else { m_conf.txgain = txgain_readback; @@ -313,14 +322,14 @@ double Dexter::get_txgain(void) const long long txgain_readback = 0; int r = 0; if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "gain0", &txgain_readback)) != 0) { - etiLog.level(warn) << "Failed to set dexter_dsp_tx.gain0 = 0: " << get_iio_error(r); + etiLog.level(warn) << "Failed to read dexter_dsp_tx.gain0: " << get_iio_error(r); } return txgain_readback; } void Dexter::set_bandwidth(double bandwidth) { - // TODO + return; } double Dexter::get_bandwidth(void) const @@ -328,38 +337,51 @@ double Dexter::get_bandwidth(void) const return 0; } -SDRDevice::RunStatistics Dexter::get_run_statistics(void) const +SDRDevice::run_statistics_t Dexter::get_run_statistics(void) const { - RunStatistics rs; + run_statistics_t rs; { - std::unique_lock lock(m_underflows_mutex); - rs.num_underruns = underflows; + std::unique_lock lock(m_attr_thread_mutex); + rs["underruns"] = underflows; + } + rs["overruns"] = 0; + rs["late_packets"] = num_late; + rs["frames"] = num_frames_modulated; + + long long clks = 0; + int r = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "clks", &clks)) == 0) { + rs["clks"] = (size_t)clks; + } + else { + rs["clks"] = (ssize_t)-1; + etiLog.level(error) << "Failed to get dexter_dsp_tx.clks: " << get_iio_error(r); + } + + long long fifo_not_empty_clks = 0; + + if ((r = iio_device_attr_read_longlong( + m_dexter_dsp_tx, "stream0_fifo_not_empty_clks", &fifo_not_empty_clks)) == 0) { + rs["fifo_not_empty_clks"] = (size_t)fifo_not_empty_clks; + } + else { + rs["fifo_not_empty_clks"] = (ssize_t)-1; + etiLog.level(error) << "Failed to get dexter_dsp_tx.fifo_not_empty_clks: " << get_iio_error(r); } - rs.num_overruns = 0; - rs.num_late_packets = num_late; - rs.num_frames_modulated = num_frames_modulated; return rs; } double Dexter::get_real_secs(void) const { - struct timespec time_now; - fill_time(&time_now); - return (double)time_now.tv_sec + time_now.tv_nsec / 1000000000.0; - - /* We don't use actual device time, because we only have clock counter on pps edge available, not - * current clock counter. */ -#if 0 - long long pps_clks = 0; + long long clks = 0; int r = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "clks", &clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.clks: " << get_iio_error(r); throw std::runtime_error("Dexter: Cannot read IIO attribute"); } - return (double)m_utc_seconds_at_startup + (double)(pps_clks - m_clock_count_at_startup) / (double)DSP_CLOCK; -#endif + return (double)m_utc_seconds_at_startup + (double)(clks - m_clock_count_at_startup) / (double)DSP_CLOCK; } void Dexter::set_rxgain(double rxgain) @@ -395,11 +417,10 @@ const char* Dexter::device_name(void) const return "Dexter"; } -double Dexter::get_temperature(void) const +std::optional Dexter::get_temperature(void) const { - // TODO - // XADC contains temperature, but value is weird - return std::numeric_limits::quiet_NaN(); + // TODO XADC contains temperature, but value is weird + return std::nullopt; } void Dexter::transmit_frame(struct FrameData&& frame) @@ -413,29 +434,32 @@ void Dexter::transmit_frame(struct FrameData&& frame) const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); - const double margin_s = frame.ts.offset_to_system_time(); - if (not m_channel_is_up) { if (require_timestamped_tx) { - /* - uint64_t timeS = frame.ts.timestamp_sec; - etiLog.level(debug) << "Dexter: TS S " << timeS << " - " << m_utc_seconds_at_startup << " = " << - timeS - m_utc_seconds_at_startup; - */ - constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; // TIMESTAMP_PPS_PER_DSP_CLOCKS=10 because timestamp_pps is represented in 16.384 MHz clocks - uint64_t frame_ts_clocks = + uint64_t frame_start_clocks = // at second level ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + // at subsecond level (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; + const double margin_s = frame.ts.offset_to_system_time(); + + long long clks = 0; + int r = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "clks", &clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + const double margin_device_s = (double)(frame_start_clocks - clks) / DSP_CLOCK; + etiLog.level(debug) << "DEXTER FCT " << frame.ts.fct << " TS CLK " << ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << m_clock_count_at_startup << " + " << (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << - frame_ts_clocks << " DELTA " << margin_s; + frame_start_clocks << " DELTA " << margin_s << " " << margin_device_s; // Ensure we hand the frame over to HW with a bit of margin if (margin_s < 0.2) { @@ -444,9 +468,8 @@ void Dexter::transmit_frame(struct FrameData&& frame) return; } - int r; - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_ts_clocks)) != 0) { - etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_ts_clocks << " : " << get_iio_error(r); + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_start_clocks)) != 0) { + etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_start_clocks << " : " << get_iio_error(r); num_late++; return; } @@ -486,7 +509,7 @@ void Dexter::transmit_frame(struct FrameData&& frame) } { - std::unique_lock lock(m_underflows_mutex); + std::unique_lock lock(m_attr_thread_mutex); size_t u = underflows; lock.unlock(); @@ -500,19 +523,29 @@ void Dexter::transmit_frame(struct FrameData&& frame) void Dexter::underflow_read_process() { + m_underflow_ctx = iio_create_local_context(); + if (not m_underflow_ctx) { + throw std::runtime_error("Dexter: Unable to create iio context for underflow"); + } + + auto dexter_dsp_tx = iio_context_find_device(m_ctx, "dexter_dsp_tx"); + if (not dexter_dsp_tx) { + throw std::runtime_error("Dexter: Unable to find dexter_dsp_tx iio device"); + } + set_thread_name("dexter_underflow"); while (m_running) { this_thread::sleep_for(chrono::seconds(1)); - long long attr_value = 0; - int r = 0; + long long underflows_attr = 0; + + int r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &underflows_attr); - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "buffer_underflows0", &attr_value)) == 0) { - size_t underflows_new = attr_value; + if (r == 0) { + size_t underflows_new = underflows_attr; - std::unique_lock lock(m_underflows_mutex); - etiLog.level(debug) << "UNDERFLOWS INC BY " << attr_value - (ssize_t)underflows; - if (underflows_new != underflows and attr_value != 0) { + std::unique_lock lock(m_attr_thread_mutex); + if (underflows_new != underflows and underflows_attr != 0) { underflows = underflows_new; } } diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 2f0524e..7925de7 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -65,7 +65,7 @@ class Dexter : public Output::SDRDevice virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth() const override; virtual void transmit_frame(struct FrameData&& frame) override; - virtual RunStatistics get_run_statistics() const override; + virtual run_statistics_t get_run_statistics() const override; virtual double get_real_secs() const override; virtual void set_rxgain(double rxgain) override; @@ -80,7 +80,7 @@ class Dexter : public Output::SDRDevice virtual bool is_clk_source_ok() const override; virtual const char* device_name() const override; - virtual double get_temperature() const override; + virtual std::optional get_temperature() const override; private: void channel_up(); @@ -98,10 +98,11 @@ class Dexter : public Output::SDRDevice struct iio_buffer *m_buffer = nullptr; /* Underflows are counted in a separate thread */ + struct iio_context* m_underflow_ctx = nullptr; std::atomic m_running = ATOMIC_VAR_INIT(false); std::thread m_underflow_read_thread; void underflow_read_process(); - mutable std::mutex m_underflows_mutex; + mutable std::mutex m_attr_thread_mutex; size_t underflows = 0; size_t prev_underflows = 0; diff --git a/src/output/Lime.cpp b/src/output/Lime.cpp index bf466c3..83c54ad 100644 --- a/src/output/Lime.cpp +++ b/src/output/Lime.cpp @@ -323,13 +323,14 @@ double Lime::get_bandwidth(void) const return bw; } -SDRDevice::RunStatistics Lime::get_run_statistics(void) const +SDRDevice::run_statistics_t Lime::get_run_statistics(void) const { - RunStatistics rs; - rs.num_underruns = underflows; - rs.num_overruns = overflows; - rs.num_late_packets = late_packets; - rs.num_frames_modulated = num_frames_modulated; + run_statistics_t rs; + rs["underruns"] = underflows; + rs["overruns"] = overflows; + rs["dropped_packets"] = dropped_packets; + rs["frames"] = num_frames_modulated; + rs["fifo_fill"] = m_last_fifo_fill_percent * 100; return rs; } @@ -371,23 +372,21 @@ const char *Lime::device_name(void) const return "Lime"; } -double Lime::get_temperature(void) const +std::optional Lime::get_temperature(void) const { if (not m_device) throw runtime_error("Lime device not set up"); - float_type temp = numeric_limits::quiet_NaN(); - if (LMS_GetChipTemperature(m_device, 0, &temp) < 0) - { + float_type temp = 0; + if (LMS_GetChipTemperature(m_device, 0, &temp) >= 0) { + return temp; + } + else { etiLog.level(error) << "Error getting LimeSDR temperature: %s " << LMS_GetLastErrorMessage(); + return std::nullopt; } - return temp; } -float Lime::get_fifo_fill_percent(void) const -{ - return m_last_fifo_fill_percent * 100; -} void Lime::transmit_frame(struct FrameData&& frame) { @@ -411,7 +410,7 @@ void Lime::transmit_frame(struct FrameData&& frame) LMS_GetStreamStatus(&m_tx_stream, &LimeStatus); overflows += LimeStatus.overrun; underflows += LimeStatus.underrun; - late_packets += LimeStatus.droppedPackets; + dropped_packets += LimeStatus.droppedPackets; #ifdef LIMEDEBUG etiLog.level(info) << LimeStatus.fifoFilledCount << "/" << LimeStatus.fifoSize << ":" << numSamples << "Rate" << LimeStatus.linkRate / (2 * 2.0); diff --git a/src/output/Lime.h b/src/output/Lime.h index 95c5c48..e09e82d 100644 --- a/src/output/Lime.h +++ b/src/output/Lime.h @@ -67,7 +67,7 @@ class Lime : public Output::SDRDevice virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; virtual void transmit_frame(struct FrameData&& frame) override; - virtual RunStatistics get_run_statistics(void) const override; + virtual run_statistics_t get_run_statistics(void) const override; virtual double get_real_secs(void) const override; virtual void set_rxgain(double rxgain) override; @@ -82,8 +82,7 @@ class Lime : public Output::SDRDevice virtual bool is_clk_source_ok(void) const override; virtual const char *device_name(void) const override; - virtual double get_temperature(void) const override; - virtual float get_fifo_fill_percent(void) const; + virtual std::optional get_temperature(void) const override; private: SDRDeviceConfig &m_conf; @@ -95,11 +94,10 @@ class Lime : public Output::SDRDevice std::vector interpolatebuf; std::vector m_i16samples; std::atomic m_last_fifo_fill_percent = ATOMIC_VAR_INIT(0); - size_t underflows = 0; size_t overflows = 0; - size_t late_packets = 0; + size_t dropped_packets = 0; size_t num_frames_modulated = 0; }; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 726fb94..a4f5873 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -87,6 +87,11 @@ 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 + 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"); +#endif // HAVE_DEXTER } SDR::~SDR() @@ -402,6 +407,7 @@ void SDR::set_parameter(const string& parameter, const string& value) ss >> m_config.muting; } else if (parameter == "underruns" or + parameter == "overruns" or parameter == "latepackets" or parameter == "frames" or parameter == "gpsdo_num_sv" or @@ -440,55 +446,40 @@ const string SDR::get_parameter(const string& parameter) const if (not m_device) { throw ParameterError("OutputSDR has no device"); } - const double temp = m_device->get_temperature(); - if (std::isnan(temp)) { - throw ParameterError("Temperature not available"); + const std::optional temp = m_device->get_temperature(); + if (temp) { + ss << *temp; } else { - ss << temp; + throw ParameterError("Temperature not available"); } } - else if (parameter == "underruns" or - parameter == "latepackets" or - parameter == "frames" ) { - if (not m_device) { - throw ParameterError("OutputSDR has no device"); - } - const auto stat = m_device->get_run_statistics(); - - if (parameter == "underruns") { - ss << stat.num_underruns; - } - else if (parameter == "latepackets") { - ss << stat.num_late_packets; - } - else if (parameter == "frames") { - ss << stat.num_frames_modulated; + else { + if (m_device) { + const auto stat = m_device->get_run_statistics(); + try { + const auto& value = stat.at(parameter); + 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); + } + else if (std::holds_alternative(value)) { + ss << (std::get(value) ? 1 : 0); + } + else { + throw std::logic_error("variant alternative not handled"); + } + return ss.str(); + } + catch (const std::out_of_range&) { + } } - } - else if (parameter == "gpsdo_num_sv") { - const auto stat = m_device->get_run_statistics(); - ss << stat.gpsdo_num_sv; - } - else if (parameter == "gpsdo_holdover") { - const auto stat = m_device->get_run_statistics(); - ss << (stat.gpsdo_holdover ? 1 : 0); - } -#ifdef HAVE_LIMESDR - else if (parameter == "fifo_fill") { - const auto dev = std::dynamic_pointer_cast(m_device); - if (dev) { - ss << dev->get_fifo_fill_percent(); - } - else { - ss << "Parameter '" << parameter << - "' is not exported by controllable " << get_rc_name(); - throw ParameterError(ss.str()); - } - } -#endif // HAVE_LIMESDR - else { ss << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); throw ParameterError(ss.str()); diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index ffa1a3b..337d501 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2022 + Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -38,6 +38,9 @@ DESCRIPTION: #include #include #include +#include +#include +#include #include "TimestampDecoder.h" @@ -109,22 +112,15 @@ struct FrameData { // All SDR Devices must implement the SDRDevice interface class SDRDevice { public: - struct RunStatistics { - size_t num_underruns = 0; - size_t num_late_packets = 0; - size_t num_overruns = 0; - size_t num_frames_modulated = 0; - - int gpsdo_num_sv = 0; - bool gpsdo_holdover = false; - }; + using run_statistic_t = std::variant; + using run_statistics_t = std::unordered_map; virtual void tune(double lo_offset, double frequency) = 0; virtual double get_tx_freq(void) const = 0; virtual void set_txgain(double txgain) = 0; virtual double get_txgain(void) const = 0; virtual void transmit_frame(struct FrameData&& frame) = 0; - virtual RunStatistics get_run_statistics(void) const = 0; + virtual run_statistics_t get_run_statistics(void) const = 0; virtual double get_real_secs(void) const = 0; virtual void set_rxgain(double rxgain) = 0; virtual double get_rxgain(void) const = 0; @@ -136,8 +132,8 @@ class SDRDevice { frame_timestamp& ts, double timeout_secs) = 0; - // Returns device temperature in degrees C or NaN if not available - virtual double get_temperature(void) const = 0; + // Returns device temperature in degrees C + virtual std::optional get_temperature(void) const = 0; // Return true if GPS and reference clock inputs are ok virtual bool is_clk_source_ok(void) const = 0; diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index 8c52546..00df9dc 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -180,13 +180,13 @@ double Soapy::get_bandwidth(void) const return m_device->getBandwidth(SOAPY_SDR_TX, 0); } -SDRDevice::RunStatistics Soapy::get_run_statistics(void) const +SDRDevice::run_statistics_t Soapy::get_run_statistics(void) const { - RunStatistics rs; - rs.num_underruns = underflows; - rs.num_overruns = overflows; - rs.num_late_packets = late_packets; - rs.num_frames_modulated = num_frames_modulated; + run_statistics_t rs; + rs["underruns"] = underflows; + rs["overruns"] = overflows; + rs["timeouts"] = timeouts; + rs["frames"] = num_frames_modulated; return rs; } @@ -265,11 +265,11 @@ const char* Soapy::device_name(void) const return "Soapy"; } -double Soapy::get_temperature(void) const +std::optional Soapy::get_temperature(void) const { // TODO Unimplemented // LimeSDR exports 'lms7_temp' - return std::numeric_limits::quiet_NaN(); + return std::nullopt; } void Soapy::transmit_frame(struct FrameData&& frame) @@ -320,6 +320,7 @@ void Soapy::transmit_frame(struct FrameData&& frame) m_tx_stream, buffs, samps_to_send, flags, timeNs); if (num_sent == SOAPY_SDR_TIMEOUT) { + timeouts++; continue; } else if (num_sent == SOAPY_SDR_OVERFLOW) { diff --git a/src/output/Soapy.h b/src/output/Soapy.h index f3e1ee2..b98ac21 100644 --- a/src/output/Soapy.h +++ b/src/output/Soapy.h @@ -66,7 +66,7 @@ class Soapy : public Output::SDRDevice virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; virtual void transmit_frame(struct FrameData&& frame) override; - virtual RunStatistics get_run_statistics(void) const override; + virtual run_statistics_t get_run_statistics(void) const override; virtual double get_real_secs(void) const override; virtual void set_rxgain(double rxgain) override; @@ -81,7 +81,7 @@ class Soapy : public Output::SDRDevice virtual bool is_clk_source_ok(void) const override; virtual const char* device_name(void) const override; - virtual double get_temperature(void) const override; + virtual std::optional get_temperature(void) const override; private: SDRDeviceConfig& m_conf; @@ -91,9 +91,9 @@ class Soapy : public Output::SDRDevice SoapySDR::Stream *m_rx_stream = nullptr; bool m_rx_stream_active = false; + size_t timeouts = 0; size_t underflows = 0; size_t overflows = 0; - size_t late_packets = 0; size_t num_frames_modulated = 0; }; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index c325272..7f07ff2 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -377,18 +377,22 @@ void UHD::transmit_frame(struct FrameData&& frame) } -SDRDevice::RunStatistics UHD::get_run_statistics(void) const +SDRDevice::run_statistics_t UHD::get_run_statistics(void) const { - RunStatistics rs; - rs.num_underruns = num_underflows; - rs.num_overruns = num_overflows; - rs.num_late_packets = num_late_packets; - rs.num_frames_modulated = num_frames_modulated; + run_statistics_t rs; + rs["underruns"] = num_underflows; + rs["overruns"] = num_overflows; + rs["late_packets"] = num_late_packets; + rs["frames"] = 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"] = gpsdo_stat.holdover; + rs["gpsdo_num_sv"] = gpsdo_stat.num_sv; + } + else { + rs["gpsdo_holdover"] = true; + rs["gpsdo_num_sv"] = 0; } return rs; } @@ -472,13 +476,13 @@ const char* UHD::device_name(void) const return "UHD"; } -double UHD::get_temperature(void) const +std::optional UHD::get_temperature(void) const { try { return std::round(m_usrp->get_tx_sensor("temp", 0).to_real()); } catch (const uhd::lookup_error &e) { - return std::numeric_limits::quiet_NaN(); + return std::nullopt; } } diff --git a/src/output/UHD.h b/src/output/UHD.h index 164254c..5823c0e 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -80,7 +80,7 @@ class UHD : public Output::SDRDevice virtual void set_bandwidth(double bandwidth) override; virtual double get_bandwidth(void) const override; virtual void transmit_frame(struct FrameData&& frame) override; - virtual RunStatistics get_run_statistics(void) const override; + virtual run_statistics_t get_run_statistics(void) const override; virtual double get_real_secs(void) const override; virtual void set_rxgain(double rxgain) override; @@ -95,7 +95,7 @@ class UHD : public Output::SDRDevice virtual bool is_clk_source_ok(void) const override; virtual const char* device_name(void) const override; - virtual double get_temperature(void) const override; + virtual std::optional get_temperature(void) const override; private: SDRDeviceConfig& m_conf; -- cgit v1.2.3 From b17001011204a47432a1deb970cd15466c16f0bf Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 26 Apr 2023 17:03:14 +0200 Subject: Properly set TX frequency --- src/output/Dexter.cpp | 25 +++++++++++++++++-------- src/output/Dexter.h | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 2d053aa..4cb9cd8 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -87,6 +87,11 @@ Dexter::Dexter(SDRDeviceConfig& config) : throw std::runtime_error("Dexter: Unable to find dexter_dsp_tx iio device"); } + m_ad9957 = iio_context_find_device(m_ctx, "ad9957"); + if (not m_ad9957) { + throw std::runtime_error("Dexter: Unable to find ad9957 iio device"); + } + m_ad9957_tx0 = iio_context_find_device(m_ctx, "ad9957_tx0"); if (not m_ad9957_tx0) { throw std::runtime_error("Dexter: Unable to find ad9957_tx0 iio device"); @@ -281,8 +286,8 @@ void Dexter::tune(double lo_offset, double frequency) long long freq = frequency; int r = 0; - if ((r = iio_device_attr_write_longlong(m_ad9957_tx0, "center_frequency", freq)) != 0) { - etiLog.level(warn) << "Failed to set ad9957_tx0.center_frequency = " << freq << " : " << get_iio_error(r); + if ((r = iio_device_attr_write_longlong(m_ad9957, "center_frequency", freq)) != 0) { + etiLog.level(warn) << "Failed to set ad9957.center_frequency = " << freq << " : " << get_iio_error(r); } long long lo_offs = lo_offset; @@ -294,17 +299,21 @@ void Dexter::tune(double lo_offset, double frequency) double Dexter::get_tx_freq(void) const { - long long frequency = 0; + long long lo_offset = 0; int r = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "frequency0", &frequency)) != 0) { - etiLog.level(warn) << "Failed to read dexter_dsp_tx.frequency0 = " << - frequency << " : " << get_iio_error(r); + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "frequency0", &lo_offset)) != 0) { + etiLog.level(warn) << "Failed to read dexter_dsp_tx.frequency0: " << get_iio_error(r); return 0; } - else { - return frequency + 204800000; + + long long frequency = 0; + if ((r = iio_device_attr_read_longlong(m_ad9957, "center_frequency", &frequency)) != 0) { + etiLog.level(warn) << "Failed to read ad9957.center_frequency: " << get_iio_error(r); + return 0; } + + return frequency + lo_offset; } void Dexter::set_txgain(double txgain) diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 7925de7..3d47f87 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -93,6 +93,7 @@ class Dexter : public Output::SDRDevice struct iio_context* m_ctx = nullptr; struct iio_device* m_dexter_dsp_tx = nullptr; + struct iio_device* m_ad9957 = nullptr; struct iio_device* m_ad9957_tx0 = nullptr; struct iio_channel* m_tx_channel = nullptr; struct iio_buffer *m_buffer = nullptr; -- cgit v1.2.3 From 830fb3ab0a8631055b2341b8dac50b937b6e99bb Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 26 Apr 2023 18:08:54 +0200 Subject: Dexter: Add three clock states and handling --- src/ConfigParser.cpp | 14 ++- src/output/Dexter.cpp | 310 +++++++++++++++++++++++++++++++------------------- src/output/Dexter.h | 18 ++- 3 files changed, 217 insertions(+), 125 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 3e223c3..cb4dc24 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -278,7 +278,8 @@ static void parse_configfile( mod_settings.sdr_device_config = sdr_device_config; mod_settings.useUHDOutput = true; } -#endif +#endif // defined(HAVE_OUTPUT_UHD) + #if defined(HAVE_SOAPYSDR) else if (output_selected == "soapysdr") { auto& outputsoapy_conf = mod_settings.sdr_device_config; @@ -309,7 +310,8 @@ static void parse_configfile( mod_settings.useSoapyOutput = true; } -#endif +#endif // defined(HAVE_SOAPYSDR) + #if defined(HAVE_DEXTER) else if (output_selected == "dexter") { auto& outputdexter_conf = mod_settings.sdr_device_config; @@ -318,6 +320,7 @@ static void parse_configfile( outputdexter_conf.frequency = pt.GetReal("dexteroutput.frequency", 0); std::string chan = pt.Get("dexteroutput.channel", ""); outputdexter_conf.dabMode = mod_settings.dabMode; + outputdexter_conf.maxGPSHoldoverTime = pt.GetInteger("dexteroutput.max_gps_holdover_time", 0); if (outputdexter_conf.frequency == 0 && chan == "") { std::cerr << " dexter output enabled, but neither frequency nor channel defined.\n"; @@ -333,7 +336,8 @@ static void parse_configfile( mod_settings.useDexterOutput = true; } -#endif +#endif // defined(HAVE_DEXTER) + #if defined(HAVE_LIMESDR) else if (output_selected == "limesdr") { auto& outputlime_conf = mod_settings.sdr_device_config; @@ -363,7 +367,7 @@ static void parse_configfile( mod_settings.useLimeOutput = true; } -#endif +#endif // defined(HAVE_LIMESDR) #if defined(HAVE_BLADERF) else if (output_selected == "bladerf") { @@ -392,7 +396,7 @@ static void parse_configfile( mod_settings.useBladeRFOutput = true; } -#endif +#endif // defined(HAVE_BLADERF) #if defined(HAVE_ZEROMQ) else if (output_selected == "zmq") { diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 4cb9cd8..59baf7e 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -120,58 +120,6 @@ Dexter::Dexter(SDRDeviceConfig& config) : // skip: antenna - // get H/W time - /* Procedure: - * Wait 200ms after second change, fetch pps_clks attribute - * idem at the next second, and check that pps_clks incremented by DSP_CLOCK - * If ok, store the correspondence between current second change (measured in UTC clock time) - * and the counter value at pps rising edge. */ - - etiLog.level(info) << "Dexter: Waiting for second change..."; - - struct timespec time_at_startup; - fill_time(&time_at_startup); - time_at_startup.tv_nsec = 0; - - struct timespec time_now; - do { - fill_time(&time_now); - this_thread::sleep_for(chrono::milliseconds(1)); - } while (time_at_startup.tv_sec == time_now.tv_sec); - this_thread::sleep_for(chrono::milliseconds(200)); - - long long pps_clks = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); - throw std::runtime_error("Dexter: Cannot read IIO attribute"); - } - - time_t tnow = time_now.tv_sec; - etiLog.level(info) << "Dexter: pps_clks " << pps_clks << " at UTC " << - put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); - - time_at_startup.tv_sec = time_now.tv_sec; - do { - fill_time(&time_now); - this_thread::sleep_for(chrono::milliseconds(1)); - } while (time_at_startup.tv_sec == time_now.tv_sec); - this_thread::sleep_for(chrono::milliseconds(200)); - - long long pps_clks2 = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks2)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); - throw std::runtime_error("Dexter: Cannot read IIO attribute"); - } - tnow = time_now.tv_sec; - etiLog.level(info) << "Dexter: pps_clks increased by " << pps_clks2 - pps_clks << " at UTC " << - put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); - - if ((uint64_t)pps_clks + DSP_CLOCK != (uint64_t)pps_clks2) { - throw std::runtime_error("Dexter: Wrong increase of pps_clks, expected " + to_string(DSP_CLOCK)); - } - m_utc_seconds_at_startup = time_now.tv_sec; - m_clock_count_at_startup = pps_clks2; - // The FIFO should not contain data, but setting gain=0 before setting start_clks to zero is an additional security if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0)) != 0) { throw std::runtime_error("Failed to set dexter_dsp_tx.gain0 = 0 : " + get_iio_error(r)); @@ -248,35 +196,118 @@ void Dexter::channel_down() etiLog.level(debug) << "DEXTER CHANNEL_DOWN"; } - -Dexter::~Dexter() +void Dexter::handle_hw_time() { - m_running = false; - if (m_underflow_read_thread.joinable()) { - m_underflow_read_thread.join(); - } - - if (m_ctx) { - if (m_dexter_dsp_tx) { - iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0); - } - - if (m_buffer) { - iio_buffer_destroy(m_buffer); - m_buffer = nullptr; - } - - if (m_tx_channel) { - iio_channel_disable(m_tx_channel); - } - - iio_context_destroy(m_ctx); - m_ctx = nullptr; - } + /* + * On startup, wait until `gpsdo_locked==1` and `pps_loss_of_signal==0`, + * then do the clocks alignment and go to normal state. + * + * In normal state, if `pps_loss_of_signal==1`, go to holdover state. + * + * If we've been in holdover state for longer than the configured time, or + * if `pps_loss_of_signal==0` stop the mod and restart. + */ + int r; - if (m_underflow_ctx) { - iio_context_destroy(m_underflow_ctx); - m_underflow_ctx = nullptr; + switch (m_clock_state) { + case DexterClockState::Startup: + { + long long gpsdo_locked = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "gpsdo_locked", &gpsdo_locked)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.gpsdo_locked: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + long long pps_loss_of_signal = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_loss_of_signal", &pps_loss_of_signal)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_loss_of_signal: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + if (gpsdo_locked == 1 and pps_loss_of_signal == 0) { + /* Procedure: + * Wait 200ms after second change, fetch pps_clks attribute + * idem at the next second, and check that pps_clks incremented by DSP_CLOCK + * If ok, store the correspondence between current second change (measured in UTC clock time) + * and the counter value at pps rising edge. */ + + etiLog.level(info) << "Dexter: Waiting for second change..."; + + struct timespec time_at_startup; + fill_time(&time_at_startup); + time_at_startup.tv_nsec = 0; + + struct timespec time_now; + do { + fill_time(&time_now); + this_thread::sleep_for(chrono::milliseconds(1)); + } while (time_at_startup.tv_sec == time_now.tv_sec); + this_thread::sleep_for(chrono::milliseconds(200)); + + long long pps_clks = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + time_t tnow = time_now.tv_sec; + etiLog.level(info) << "Dexter: pps_clks " << pps_clks << " at UTC " << + put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); + + time_at_startup.tv_sec = time_now.tv_sec; + do { + fill_time(&time_now); + this_thread::sleep_for(chrono::milliseconds(1)); + } while (time_at_startup.tv_sec == time_now.tv_sec); + this_thread::sleep_for(chrono::milliseconds(200)); + + long long pps_clks2 = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_clks", &pps_clks2)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + tnow = time_now.tv_sec; + etiLog.level(info) << "Dexter: pps_clks increased by " << pps_clks2 - pps_clks << " at UTC " << + put_time(std::gmtime(&tnow), "%Y-%m-%d %H:%M:%S"); + + if ((uint64_t)pps_clks + DSP_CLOCK != (uint64_t)pps_clks2) { + throw std::runtime_error("Dexter: Wrong increase of pps_clks, expected " + to_string(DSP_CLOCK)); + } + + m_utc_seconds_at_startup = time_now.tv_sec; + m_clock_count_at_startup = pps_clks2; + m_holdover_since = chrono::steady_clock::time_point::min(); + m_clock_state = DexterClockState::Normal; + } + } + break; + case DexterClockState::Normal: + { + long long pps_loss_of_signal = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_loss_of_signal", &pps_loss_of_signal)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_loss_of_signal: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + if (pps_loss_of_signal == 1) { + m_holdover_since = chrono::steady_clock::now(); + m_clock_state = DexterClockState::Holdover; + } + } + break; + case DexterClockState::Holdover: + { + using namespace chrono; + const duration d = steady_clock::now() - m_holdover_since; + const auto max_holdover_duration = seconds(m_conf.maxGPSHoldoverTime); + if (d > max_holdover_duration) { + m_clock_state = DexterClockState::Startup; + m_utc_seconds_at_startup = 0; + m_clock_count_at_startup = 0; + m_holdover_since = chrono::steady_clock::time_point::min(); + } + } + break; } } @@ -526,7 +557,14 @@ double Dexter::get_real_secs(void) const throw std::runtime_error("Dexter: Cannot read IIO attribute"); } - return (double)m_utc_seconds_at_startup + (double)(clks - m_clock_count_at_startup) / (double)DSP_CLOCK; + switch (m_clock_state) { + case DexterClockState::Startup: + return 0; + case DexterClockState::Normal: + case DexterClockState::Holdover: + return (double)m_utc_seconds_at_startup + (double)(clks - m_clock_count_at_startup) / (double)DSP_CLOCK; + } + throw std::logic_error("Unhandled switch"); } void Dexter::set_rxgain(double rxgain) @@ -585,46 +623,53 @@ void Dexter::transmit_frame(struct FrameData&& frame) const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); + handle_hw_time(); + if (not m_channel_is_up) { if (require_timestamped_tx) { - constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; - // TIMESTAMP_PPS_PER_DSP_CLOCKS=10 because timestamp_pps is represented in 16.384 MHz clocks - uint64_t frame_start_clocks = - // at second level - ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + - // at subsecond level - (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; - - const double margin_s = frame.ts.offset_to_system_time(); - - long long clks = 0; - int r = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "clks", &clks)) != 0) { - etiLog.level(error) << "Failed to get dexter_dsp_tx.clks: " << get_iio_error(r); - throw std::runtime_error("Dexter: Cannot read IIO attribute"); + if (m_clock_state == DexterClockState::Startup) { + return; // not ready } - - const double margin_device_s = (double)(frame_start_clocks - clks) / DSP_CLOCK; - - etiLog.level(debug) << "DEXTER FCT " << frame.ts.fct << " TS CLK " << - ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << - m_clock_count_at_startup << " + " << - (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << - frame_start_clocks << " DELTA " << margin_s << " " << margin_device_s; - - // Ensure we hand the frame over to HW with a bit of margin - if (margin_s < 0.2) { - etiLog.level(warn) << "Skip frame short margin " << margin_s; - num_late++; - return; - } - - if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_start_clocks)) != 0) { - etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_start_clocks << " : " << get_iio_error(r); - num_late++; - return; + else { + constexpr uint64_t TIMESTAMP_PPS_PER_DSP_CLOCKS = DSP_CLOCK / 16384000; + // TIMESTAMP_PPS_PER_DSP_CLOCKS=10 because timestamp_pps is represented in 16.384 MHz clocks + uint64_t frame_start_clocks = + // at second level + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK + m_clock_count_at_startup + + // at subsecond level + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS; + + const double margin_s = frame.ts.offset_to_system_time(); + + long long clks = 0; + int r = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "clks", &clks)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.clks: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + + const double margin_device_s = (double)(frame_start_clocks - clks) / DSP_CLOCK; + + etiLog.level(debug) << "DEXTER FCT " << frame.ts.fct << " TS CLK " << + ((int64_t)frame.ts.timestamp_sec - (int64_t)m_utc_seconds_at_startup) * DSP_CLOCK << " + " << + m_clock_count_at_startup << " + " << + (uint64_t)frame.ts.timestamp_pps * TIMESTAMP_PPS_PER_DSP_CLOCKS << " = " << + frame_start_clocks << " DELTA " << margin_s << " " << margin_device_s; + + // Ensure we hand the frame over to HW with a bit of margin + if (margin_s < 0.2) { + etiLog.level(warn) << "Skip frame short margin " << margin_s; + num_late++; + return; + } + + if ((r = iio_device_attr_write_longlong(m_dexter_dsp_tx, "stream0_start_clks", frame_start_clocks)) != 0) { + etiLog.level(warn) << "Skip frame, failed to set dexter_dsp_tx.stream0_start_clks = " << frame_start_clocks << " : " << get_iio_error(r); + num_late++; + return; + } + m_require_timestamp_refresh = false; } - m_require_timestamp_refresh = false; } channel_up(); @@ -704,8 +749,37 @@ void Dexter::underflow_read_process() m_running = false; } -} // namespace Output +Dexter::~Dexter() +{ + m_running = false; + if (m_underflow_read_thread.joinable()) { + m_underflow_read_thread.join(); + } -#endif // HAVE_DEXTER + if (m_ctx) { + if (m_dexter_dsp_tx) { + iio_device_attr_write_longlong(m_dexter_dsp_tx, "gain0", 0); + } + if (m_buffer) { + iio_buffer_destroy(m_buffer); + m_buffer = nullptr; + } + if (m_tx_channel) { + iio_channel_disable(m_tx_channel); + } + + iio_context_destroy(m_ctx); + m_ctx = nullptr; + } + + if (m_underflow_ctx) { + iio_context_destroy(m_underflow_ctx); + m_underflow_ctx = nullptr; + } +} + +} // namespace Output + +#endif // HAVE_DEXTER diff --git a/src/output/Dexter.h b/src/output/Dexter.h index 3d47f87..f70bb14 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -42,6 +42,7 @@ DESCRIPTION: #include #include #include +#include #include "output/SDR.h" #include "ModPlugin.h" @@ -50,6 +51,12 @@ DESCRIPTION: namespace Output { +enum class DexterClockState { + Startup, + Normal, + Holdover +}; + class Dexter : public Output::SDRDevice { public: @@ -85,6 +92,7 @@ class Dexter : public Output::SDRDevice private: void channel_up(); void channel_down(); + void handle_hw_time(); bool m_channel_is_up = false; @@ -112,9 +120,15 @@ class Dexter : public Output::SDRDevice size_t num_buffers_pushed = 0; - uint64_t m_utc_seconds_at_startup; + DexterClockState m_clock_state = DexterClockState::Startup; + + // Only valid when m_clock_state is not Startup + uint64_t m_utc_seconds_at_startup = 0; uint64_t m_clock_count_at_startup = 0; - uint64_t m_clock_count_frame = 0; + + // Only valid when m_clock_state Holdover + std::chrono::steady_clock::time_point m_holdover_since = + std::chrono::steady_clock::time_point::min(); }; } // namespace Output -- cgit v1.2.3 From f0bb1e24952c7e261ba13907c0a5d8c3e1d198ca Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 27 Apr 2023 13:43:22 +0200 Subject: Improve dexter clock handling --- src/DabModulator.cpp | 3 ++- src/output/BladeRF.cpp | 2 +- src/output/BladeRF.h | 2 +- src/output/Dexter.cpp | 73 +++++++++++++++++++++++++++++++++++--------------- src/output/Dexter.h | 3 ++- src/output/Lime.cpp | 2 +- src/output/Lime.h | 2 +- src/output/SDR.cpp | 29 ++------------------ src/output/SDR.h | 4 --- src/output/SDRDevice.h | 2 +- src/output/Soapy.cpp | 2 +- src/output/Soapy.h | 2 +- src/output/UHD.cpp | 2 +- src/output/UHD.h | 2 +- 14 files changed, 66 insertions(+), 64 deletions(-) (limited to 'src/output/Dexter.h') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 67764ba..64ebf03 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -27,6 +27,7 @@ #include #include +#include #include "DabModulator.h" #include "PcDebug.h" @@ -348,7 +349,7 @@ int DabModulator::process(Buffer* dataOut) } shared_ptr prev_plugin = static_pointer_cast(cifSig); - const std::list > plugins({ + const std::vector > plugins({ static_pointer_cast(cifCicEq), static_pointer_cast(cifOfdm), static_pointer_cast(cifGain), diff --git a/src/output/BladeRF.cpp b/src/output/BladeRF.cpp index 2dd7176..c16b64d 100755 --- a/src/output/BladeRF.cpp +++ b/src/output/BladeRF.cpp @@ -273,7 +273,7 @@ size_t BladeRF::receive_frame( return 0; } -bool BladeRF::is_clk_source_ok() const +bool BladeRF::is_clk_source_ok() { // TODO return true; diff --git a/src/output/BladeRF.h b/src/output/BladeRF.h index eb3e58b..fa3419e 100755 --- a/src/output/BladeRF.h +++ b/src/output/BladeRF.h @@ -87,7 +87,7 @@ class BladeRF : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const override; + virtual bool is_clk_source_ok(void) override; virtual const char* device_name(void) const override; virtual std::optional get_temperature(void) const override; diff --git a/src/output/Dexter.cpp b/src/output/Dexter.cpp index 59baf7e..25de030 100644 --- a/src/output/Dexter.cpp +++ b/src/output/Dexter.cpp @@ -277,7 +277,9 @@ void Dexter::handle_hw_time() m_utc_seconds_at_startup = time_now.tv_sec; m_clock_count_at_startup = pps_clks2; m_holdover_since = chrono::steady_clock::time_point::min(); + m_holdover_since_t = 0; m_clock_state = DexterClockState::Normal; + etiLog.level(debug) << "Dexter: switch clock state Startup -> Normal"; } } break; @@ -291,20 +293,31 @@ void Dexter::handle_hw_time() if (pps_loss_of_signal == 1) { m_holdover_since = chrono::steady_clock::now(); + m_holdover_since_t = chrono::system_clock::to_time_t(chrono::system_clock::now()); m_clock_state = DexterClockState::Holdover; + etiLog.level(debug) << "Dexter: switch clock state Normal -> Holdover"; } } break; case DexterClockState::Holdover: { using namespace chrono; + + long long pps_loss_of_signal = 0; + if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, "pps_loss_of_signal", &pps_loss_of_signal)) != 0) { + etiLog.level(error) << "Failed to get dexter_dsp_tx.pps_loss_of_signal: " << get_iio_error(r); + throw std::runtime_error("Dexter: Cannot read IIO attribute"); + } + const duration d = steady_clock::now() - m_holdover_since; const auto max_holdover_duration = seconds(m_conf.maxGPSHoldoverTime); - if (d > max_holdover_duration) { + if (d > max_holdover_duration or pps_loss_of_signal == 0) { m_clock_state = DexterClockState::Startup; m_utc_seconds_at_startup = 0; m_clock_count_at_startup = 0; m_holdover_since = chrono::steady_clock::time_point::min(); + m_holdover_since_t = 0; + etiLog.level(debug) << "Dexter: switch clock state Holdover -> Startup"; } } break; @@ -383,6 +396,21 @@ double Dexter::get_bandwidth(void) const return 0; } +template +static void attr_to_stat( + Dexter::run_statistics_t& rs, iio_device *dexter_dsp_tx, + const char* attr_name, const char* stat_name) { + long long attr_value = 0; + int r = 0; + if ((r = iio_device_attr_read_longlong(dexter_dsp_tx, attr_name, &attr_value)) == 0) { + rs[stat_name] = (T)attr_value; + } + else { + rs[stat_name] = (ssize_t)-1; + etiLog.level(error) << "Failed to get dexter_dsp_tx." << attr_name << ": " << get_iio_error(r); + } +}; + SDRDevice::run_statistics_t Dexter::get_run_statistics(void) const { run_statistics_t rs; @@ -393,24 +421,25 @@ SDRDevice::run_statistics_t Dexter::get_run_statistics(void) const rs["latepackets"] = num_late; rs["frames"] = num_frames_modulated; - auto attr_to_stat = [&](const char* attr_name, const char* stat_name) { - long long attr_value = 0; - int r = 0; - if ((r = iio_device_attr_read_longlong(m_dexter_dsp_tx, attr_name, &attr_value)) == 0) { - rs[stat_name] = (size_t)attr_value; - } - else { - rs[stat_name] = (ssize_t)-1; - etiLog.level(error) << "Failed to get dexter_dsp_tx." << attr_name << ": " << get_iio_error(r); - } - }; + attr_to_stat(rs, m_dexter_dsp_tx, "clks", "clks"); + attr_to_stat(rs, m_dexter_dsp_tx, "stream0_fifo_not_empty_clks", "fifo_not_empty_clks"); + attr_to_stat(rs, m_dexter_dsp_tx, "gpsdo_locked", "gpsdo_locked"); + attr_to_stat(rs, m_dexter_dsp_tx, "pps_clk_error_hz", "pps_clk_error_hz"); + attr_to_stat(rs, m_dexter_dsp_tx, "pps_cnt", "pps_cnt"); + attr_to_stat(rs, m_dexter_dsp_tx, "pps_loss_of_signal", "pps_loss_of_signal"); + attr_to_stat(rs, m_dexter_dsp_tx, "dsp_version", "dsp_version"); - attr_to_stat("clks", "clks"); - attr_to_stat("stream0_fifo_not_empty_clks", "fifo_not_empty_clks"); - attr_to_stat("gpsdo_locked", "gpsdo_locked"); - attr_to_stat("pps_clk_error_hz", "pps_clk_error_hz"); - attr_to_stat("pps_cnt", "pps_cnt"); - attr_to_stat("dsp_version", "dsp_version"); + rs["in_holdover_since"] = 0; + switch (m_clock_state) { + case DexterClockState::Startup: + rs["clock_state"] = "startup"; break; + case DexterClockState::Normal: + rs["clock_state"] = "normal"; break; + case DexterClockState::Holdover: + rs["clock_state"] = "holdover"; + rs["in_holdover_since"] = m_holdover_since_t; + break; + } constexpr double VMINFACT = 0.85; constexpr double VMAXFACT = 1.15; @@ -589,9 +618,11 @@ size_t Dexter::receive_frame( } -bool Dexter::is_clk_source_ok() const +bool Dexter::is_clk_source_ok() { - return true; + handle_hw_time(); + + return m_clock_state != DexterClockState::Startup; } const char* Dexter::device_name(void) const @@ -623,8 +654,6 @@ void Dexter::transmit_frame(struct FrameData&& frame) const bool require_timestamped_tx = (m_conf.enableSync and frame.ts.timestamp_valid); - handle_hw_time(); - if (not m_channel_is_up) { if (require_timestamped_tx) { if (m_clock_state == DexterClockState::Startup) { diff --git a/src/output/Dexter.h b/src/output/Dexter.h index f70bb14..d4f425f 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -84,7 +84,7 @@ class Dexter : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok() const override; + virtual bool is_clk_source_ok() override; virtual const char* device_name() const override; virtual std::optional get_temperature() const override; @@ -129,6 +129,7 @@ class Dexter : public Output::SDRDevice // Only valid when m_clock_state Holdover std::chrono::steady_clock::time_point m_holdover_since = std::chrono::steady_clock::time_point::min(); + std::time_t m_holdover_since_t = 0; }; } // namespace Output diff --git a/src/output/Lime.cpp b/src/output/Lime.cpp index 83c54ad..8fb90bb 100644 --- a/src/output/Lime.cpp +++ b/src/output/Lime.cpp @@ -361,7 +361,7 @@ size_t Lime::receive_frame( return 0; } -bool Lime::is_clk_source_ok() const +bool Lime::is_clk_source_ok() { // TODO return true; diff --git a/src/output/Lime.h b/src/output/Lime.h index e09e82d..4510bf2 100644 --- a/src/output/Lime.h +++ b/src/output/Lime.h @@ -79,7 +79,7 @@ class Lime : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const override; + virtual bool is_clk_source_ok(void) override; virtual const char *device_name(void) const override; virtual std::optional get_temperature(void) const override; diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 860d8ed..11321f2 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -258,44 +258,19 @@ const char* SDR::name() return m_name.c_str(); } -void SDR::sleep_through_frame() -{ - using namespace std::chrono; - - const auto now = steady_clock::now(); - - if (not t_last_frame_initialised) { - t_last_frame = now; - t_last_frame_initialised = true; - } - - const auto delta = now - t_last_frame; - const auto wait_time = transmission_frame_duration(m_config.dabMode); - - if (wait_time > delta) { - this_thread::sleep_for(wait_time - delta); - } - - t_last_frame += wait_time; -} void SDR::handle_frame(struct FrameData&& frame) { // Assumes m_device is valid if (not m_device->is_clk_source_ok()) { - sleep_through_frame(); return; } const auto& time_spec = frame.ts; - if (m_config.enableSync and m_config.muteNoTimestamps and - not time_spec.timestamp_valid) { - sleep_through_frame(); - etiLog.log(info, - "OutputSDR: Muting sample %d : no timestamp\n", - frame.ts.fct); + if (m_config.enableSync and m_config.muteNoTimestamps and not time_spec.timestamp_valid) { + etiLog.log(info, "OutputSDR: Muting sample %d : no timestamp\n", frame.ts.fct); return; } diff --git a/src/output/SDR.h b/src/output/SDR.h index 9f08348..94c972b 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -72,7 +72,6 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { private: void process_thread_entry(void); void handle_frame(struct FrameData&& frame); - void sleep_through_frame(void); SDRDeviceConfig& m_config; @@ -91,9 +90,6 @@ class SDR : public ModOutput, public ModMetadata, public RemoteControllable { uint32_t last_tx_second = 0; uint32_t last_tx_pps = 0; size_t num_queue_overflows = 0; - - bool t_last_frame_initialised = false; - std::chrono::steady_clock::time_point t_last_frame; }; } diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index 628372a..26272be 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -135,7 +135,7 @@ class SDRDevice { virtual std::optional get_temperature(void) const = 0; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const = 0; + virtual bool is_clk_source_ok(void) = 0; virtual const char* device_name(void) const = 0; diff --git a/src/output/Soapy.cpp b/src/output/Soapy.cpp index 00df9dc..4d33e39 100644 --- a/src/output/Soapy.cpp +++ b/src/output/Soapy.cpp @@ -254,7 +254,7 @@ size_t Soapy::receive_frame( } -bool Soapy::is_clk_source_ok() const +bool Soapy::is_clk_source_ok() { // TODO return true; diff --git a/src/output/Soapy.h b/src/output/Soapy.h index b98ac21..4fce11a 100644 --- a/src/output/Soapy.h +++ b/src/output/Soapy.h @@ -78,7 +78,7 @@ class Soapy : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const override; + virtual bool is_clk_source_ok(void) override; virtual const char* device_name(void) const override; virtual std::optional get_temperature(void) const override; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 7f07ff2..6638b6c 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -439,7 +439,7 @@ size_t UHD::receive_frame( } // Return true if GPS and reference clock inputs are ok -bool UHD::is_clk_source_ok(void) const +bool UHD::is_clk_source_ok(void) { bool ok = true; diff --git a/src/output/UHD.h b/src/output/UHD.h index 97a821e..9891c7a 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -84,7 +84,7 @@ class UHD : public Output::SDRDevice double timeout_secs) override; // Return true if GPS and reference clock inputs are ok - virtual bool is_clk_source_ok(void) const override; + virtual bool is_clk_source_ok(void) override; virtual const char* device_name(void) const override; virtual std::optional get_temperature(void) const override; -- 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 'src/output/Dexter.h') 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 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 'src/output/Dexter.h') 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