diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-03-29 16:30:06 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-03-29 17:09:21 +0200 |
commit | fd46d43f51796a6bbdd629e08535bacbc853f283 (patch) | |
tree | 832f0a36a2d54c0dba23b59297d6894c8d9a6edc /src/output | |
parent | efea1c485f178cdbadf5babdf7951e6d1a1ed801 (diff) | |
download | dabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.tar.gz dabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.tar.bz2 dabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.zip |
Replace RunStatistics to permit representation of SDR device specific stats
Diffstat (limited to 'src/output')
-rwxr-xr-x | src/output/BladeRF.cpp | 22 | ||||
-rwxr-xr-x | src/output/BladeRF.h | 9 | ||||
-rw-r--r-- | src/output/Dexter.cpp | 153 | ||||
-rw-r--r-- | src/output/Dexter.h | 7 | ||||
-rw-r--r-- | src/output/Lime.cpp | 31 | ||||
-rw-r--r-- | src/output/Lime.h | 8 | ||||
-rw-r--r-- | src/output/SDR.cpp | 77 | ||||
-rw-r--r-- | src/output/SDRDevice.h | 22 | ||||
-rw-r--r-- | src/output/Soapy.cpp | 17 | ||||
-rw-r--r-- | src/output/Soapy.h | 6 | ||||
-rw-r--r-- | src/output/UHD.cpp | 24 | ||||
-rw-r--r-- | src/output/UHD.h | 4 |
12 files changed, 197 insertions, 183 deletions
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<double> 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<double> 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<std::mutex> lock(m_underflows_mutex); - rs.num_underruns = underflows; + std::unique_lock<std::mutex> 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<double> Dexter::get_temperature(void) const { - // TODO - // XADC contains temperature, but value is weird - return std::numeric_limits<double>::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<std::mutex> lock(m_underflows_mutex); + std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<double> 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<bool> 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<double> Lime::get_temperature(void) const { if (not m_device) throw runtime_error("Lime device not set up"); - float_type temp = numeric_limits<float_type>::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<double> get_temperature(void) const override; private: SDRDeviceConfig &m_conf; @@ -95,11 +94,10 @@ class Lime : public Output::SDRDevice std::vector<complexf> interpolatebuf; std::vector<short> m_i16samples; std::atomic<float> 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<SDRDevice> 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<double> 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<string>(value)) { + ss << std::get<string>(value); + } + else if (std::holds_alternative<ssize_t>(value)) { + ss << std::get<ssize_t>(value); + } + else if (std::holds_alternative<size_t>(value)) { + ss << std::get<size_t>(value); + } + else if (std::holds_alternative<bool>(value)) { + ss << (std::get<bool>(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<Lime>(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 <string> #include <vector> #include <complex> +#include <variant> +#include <optional> +#include <unordered_map> #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<std::string, size_t, ssize_t, bool>; + using run_statistics_t = std::unordered_map<std::string, run_statistic_t>; 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<double> 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<double> Soapy::get_temperature(void) const { // TODO Unimplemented // LimeSDR exports 'lms7_temp' - return std::numeric_limits<double>::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<double> 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<double> 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<double>::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<double> get_temperature(void) const override; private: SDRDeviceConfig& m_conf; |