aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2023-03-29 16:30:06 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2023-03-29 17:09:21 +0200
commitfd46d43f51796a6bbdd629e08535bacbc853f283 (patch)
tree832f0a36a2d54c0dba23b59297d6894c8d9a6edc
parentefea1c485f178cdbadf5babdf7951e6d1a1ed801 (diff)
downloaddabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.tar.gz
dabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.tar.bz2
dabmod-fd46d43f51796a6bbdd629e08535bacbc853f283.zip
Replace RunStatistics to permit representation of SDR device specific stats
-rwxr-xr-xsrc/output/BladeRF.cpp22
-rwxr-xr-xsrc/output/BladeRF.h9
-rw-r--r--src/output/Dexter.cpp153
-rw-r--r--src/output/Dexter.h7
-rw-r--r--src/output/Lime.cpp31
-rw-r--r--src/output/Lime.h8
-rw-r--r--src/output/SDR.cpp77
-rw-r--r--src/output/SDRDevice.h22
-rw-r--r--src/output/Soapy.cpp17
-rw-r--r--src/output/Soapy.h6
-rw-r--r--src/output/UHD.cpp24
-rw-r--r--src/output/UHD.h4
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;