From 6af36c3c20b82efcf0bd0fe80f3f03de9752d0c0 Mon Sep 17 00:00:00 2001
From: Josh Morman <jmorman@gnuradio.org>
Date: Wed, 27 Oct 2021 13:08:50 -0400
Subject: iio: iio_param_t for parameter setting

Signed-off-by: Josh Morman <jmorman@gnuradio.org>
---
 gr-iio/include/gnuradio/iio/device_sink.h          |  5 +-
 gr-iio/include/gnuradio/iio/device_source.h        |  6 ++-
 gr-iio/include/gnuradio/iio/iio_types.h            | 30 ++++++++++++
 gr-iio/lib/CMakeLists.txt                          |  1 +
 gr-iio/lib/device_sink_impl.cc                     |  8 ++--
 gr-iio/lib/device_sink_impl.h                      |  4 +-
 gr-iio/lib/device_source_impl.cc                   | 26 ++++------
 gr-iio/lib/device_source_impl.h                    |  6 +--
 gr-iio/lib/fmcomms2_sink_impl.cc                   | 38 +++++++--------
 gr-iio/lib/fmcomms2_source_impl.cc                 | 46 ++++++++----------
 gr-iio/lib/fmcomms5_sink_impl.cc                   | 16 +++----
 gr-iio/lib/fmcomms5_source_impl.cc                 | 26 +++++-----
 gr-iio/lib/iio_types.cc                            | 56 ++++++++++++++++++++++
 gr-iio/python/iio/bindings/device_sink_python.cc   |  2 +-
 gr-iio/python/iio/bindings/device_source_python.cc |  2 +-
 gr-iio/python/iio/bindings/iio_types_python.cc     |  9 +++-
 16 files changed, 178 insertions(+), 103 deletions(-)
 create mode 100644 gr-iio/lib/iio_types.cc

diff --git a/gr-iio/include/gnuradio/iio/device_sink.h b/gr-iio/include/gnuradio/iio/device_sink.h
index bf9d5f1d9f..a7f2c1d381 100644
--- a/gr-iio/include/gnuradio/iio/device_sink.h
+++ b/gr-iio/include/gnuradio/iio/device_sink.h
@@ -12,6 +12,7 @@
 #define INCLUDED_IIO_DEVICE_SINK_H
 
 #include <gnuradio/iio/api.h>
+#include <gnuradio/iio/iio_types.h>
 #include <gnuradio/sync_block.h>
 
 #include <string>
@@ -60,7 +61,7 @@ public:
                      const std::string& device,
                      const std::vector<std::string>& channels,
                      const std::string& device_phy,
-                     const std::vector<std::string>& params,
+                     const iio_param_vec_t& params,
                      unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                      unsigned int interpolation = 0,
                      bool cyclic = false);
@@ -69,7 +70,7 @@ public:
                           const std::string& device,
                           const std::vector<std::string>& channels,
                           const std::string& device_phy,
-                          const std::vector<std::string>& params,
+                          const iio_param_vec_t& params,
                           unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                           unsigned int interpolation = 0,
                           bool cyclic = false);
diff --git a/gr-iio/include/gnuradio/iio/device_source.h b/gr-iio/include/gnuradio/iio/device_source.h
index aa18e3fdca..f878e7feec 100644
--- a/gr-iio/include/gnuradio/iio/device_source.h
+++ b/gr-iio/include/gnuradio/iio/device_source.h
@@ -12,8 +12,10 @@
 #define INCLUDED_IIO_DEVICE_SOURCE_H
 
 #include <gnuradio/iio/api.h>
+#include <gnuradio/iio/iio_types.h>
 #include <gnuradio/sync_block.h>
 
+
 #define DEFAULT_BUFFER_SIZE 0x8000
 
 extern "C" {
@@ -55,7 +57,7 @@ public:
                      const std::string& device,
                      const std::vector<std::string>& channels,
                      const std::string& device_phy,
-                     const std::vector<std::string>& params,
+                     const iio_param_vec_t& params,
                      unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                      unsigned int decimation = 0);
 
@@ -63,7 +65,7 @@ public:
                           const std::string& device,
                           const std::vector<std::string>& channels,
                           const std::string& device_phy,
-                          const std::vector<std::string>& params,
+                          const iio_param_vec_t& params,
                           unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                           unsigned int decimation = 0);
 
diff --git a/gr-iio/include/gnuradio/iio/iio_types.h b/gr-iio/include/gnuradio/iio/iio_types.h
index f4b1d37b4b..4f1f500b0a 100644
--- a/gr-iio/include/gnuradio/iio/iio_types.h
+++ b/gr-iio/include/gnuradio/iio/iio_types.h
@@ -6,6 +6,16 @@
  *
  */
 
+#ifndef _INCLUDED_IIO_TYPES_H
+#define _INCLUDED_IIO_TYPES_H
+
+#include <gnuradio/iio/api.h>
+#include <boost/tokenizer.hpp>
+#include <charconv>
+#include <string>
+#include <variant>
+#include <vector>
+
 namespace gr {
 namespace iio {
 
@@ -19,5 +29,25 @@ enum class attr_type_t {
     DIRECT_REGISTER_ACCESS = 4
 };
 
+typedef std::variant<long long unsigned int, long unsigned int, int, double, std::string>
+    iio_param_value_t;
+
+#define tokenizer(inp, sep) \
+    boost::tokenizer<boost::char_separator<char>>(inp, boost::char_separator<char>(sep))
+
+class IIO_API iio_param_t : public std::pair<std::string, std::string>
+{
+public:
+    iio_param_t(const std::string& key, iio_param_value_t value);
+    iio_param_t(const std::string& kvpair);
+    ~iio_param_t() {}
+
+    static std::string to_string(iio_param_value_t value);
+};
+
+typedef std::vector<iio_param_t> iio_param_vec_t;
+
 } // namespace iio
 } // namespace gr
+
+#endif
\ No newline at end of file
diff --git a/gr-iio/lib/CMakeLists.txt b/gr-iio/lib/CMakeLists.txt
index d074e3cbe5..08ffcb2504 100644
--- a/gr-iio/lib/CMakeLists.txt
+++ b/gr-iio/lib/CMakeLists.txt
@@ -16,6 +16,7 @@ add_library(gnuradio-iio
   dds_control_impl.cc
   device_sink_impl.cc
   device_source_impl.cc
+  iio_types.cc
 )
 
 target_include_directories(gnuradio-iio
diff --git a/gr-iio/lib/device_sink_impl.cc b/gr-iio/lib/device_sink_impl.cc
index 6a6cde007d..e0c4da0e89 100644
--- a/gr-iio/lib/device_sink_impl.cc
+++ b/gr-iio/lib/device_sink_impl.cc
@@ -27,7 +27,7 @@ device_sink::sptr device_sink::make(const std::string& uri,
                                     const std::string& device,
                                     const std::vector<std::string>& channels,
                                     const std::string& device_phy,
-                                    const std::vector<std::string>& params,
+                                    const iio_param_vec_t& params,
                                     unsigned int buffer_size,
                                     unsigned int interpolation,
                                     bool cyclic)
@@ -48,7 +48,7 @@ device_sink::sptr device_sink::make_from(iio_context* ctx,
                                          const std::string& device,
                                          const std::vector<std::string>& channels,
                                          const std::string& device_phy,
-                                         const std::vector<std::string>& params,
+                                         const iio_param_vec_t& params,
                                          unsigned int buffer_size,
                                          unsigned int interpolation,
                                          bool cyclic)
@@ -64,7 +64,7 @@ device_sink::sptr device_sink::make_from(iio_context* ctx,
                                                        cyclic);
 }
 
-void device_sink_impl::set_params(const std::vector<std::string>& params)
+void device_sink_impl::set_params(const iio_param_vec_t& params)
 {
     device_source_impl::set_params(this->phy, params);
 }
@@ -77,7 +77,7 @@ device_sink_impl::device_sink_impl(iio_context* ctx,
                                    const std::string& device,
                                    const std::vector<std::string>& channels,
                                    const std::string& device_phy,
-                                   const std::vector<std::string>& params,
+                                   const iio_param_vec_t& params,
                                    unsigned int buffer_size,
                                    unsigned int interpolation,
                                    bool cyclic)
diff --git a/gr-iio/lib/device_sink_impl.h b/gr-iio/lib/device_sink_impl.h
index fdea63ae99..a9b8bfe61c 100644
--- a/gr-iio/lib/device_sink_impl.h
+++ b/gr-iio/lib/device_sink_impl.h
@@ -41,14 +41,14 @@ public:
                      const std::string& device,
                      const std::vector<std::string>& channels,
                      const std::string& device_phy,
-                     const std::vector<std::string>& params,
+                     const iio_param_vec_t& params,
                      unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                      unsigned int interpolation = 0,
                      bool cyclic = false);
 
     ~device_sink_impl();
 
-    void set_params(const std::vector<std::string>& params);
+    void set_params(const iio_param_vec_t& params);
 
     void set_len_tag_key(const std::string& len_tag_key) override;
 
diff --git a/gr-iio/lib/device_source_impl.cc b/gr-iio/lib/device_source_impl.cc
index 27268536e2..a2d137d1cd 100644
--- a/gr-iio/lib/device_source_impl.cc
+++ b/gr-iio/lib/device_source_impl.cc
@@ -29,7 +29,7 @@ device_source::sptr device_source::make(const std::string& uri,
                                         const std::string& device,
                                         const std::vector<std::string>& channels,
                                         const std::string& device_phy,
-                                        const std::vector<std::string>& params,
+                                        const iio_param_vec_t& params,
                                         unsigned int buffer_size,
                                         unsigned int decimation)
 {
@@ -48,7 +48,7 @@ device_source::sptr device_source::make_from(iio_context* ctx,
                                              const std::string& device,
                                              const std::vector<std::string>& channels,
                                              const std::string& device_phy,
-                                             const std::vector<std::string>& params,
+                                             const iio_param_vec_t& params,
                                              unsigned int buffer_size,
                                              unsigned int decimation)
 {
@@ -56,27 +56,17 @@ device_source::sptr device_source::make_from(iio_context* ctx,
         ctx, false, device, channels, device_phy, params, buffer_size, decimation);
 }
 
-void device_source_impl::set_params(iio_device* phy,
-                                    const std::vector<std::string>& params)
+void device_source_impl::set_params(iio_device* phy, const iio_param_vec_t& params)
 {
     static GR_LOG_GET_CONFIGURED_LOGGER(logger, "iio::device::set_params");
 
-    for (std::vector<std::string>::const_iterator it = params.begin(); it != params.end();
-         ++it) {
+    for (auto& param : params) {
         iio_channel* chn = NULL;
         const char* attr = NULL;
-        size_t pos;
         int ret;
 
-        pos = it->find('=');
-        if (pos == std::string::npos) {
-            GR_LOG_WARN(logger,
-                        boost::format("Malformed parameter update line: %s") % *it);
-            continue;
-        }
-
-        std::string key = it->substr(0, pos);
-        std::string val = it->substr(pos + 1, std::string::npos);
+        std::string key = param.first;
+        std::string val = param.second;
 
         ret = iio_device_identify_filename(phy, key.c_str(), &chn, &attr);
         if (ret) {
@@ -98,7 +88,7 @@ void device_source_impl::set_params(iio_device* phy,
     }
 }
 
-void device_source_impl::set_params(const std::vector<std::string>& params)
+void device_source_impl::set_params(const iio_param_vec_t& params)
 {
     set_params(this->phy, params);
 }
@@ -173,7 +163,7 @@ device_source_impl::device_source_impl(iio_context* ctx,
                                        const std::string& device,
                                        const std::vector<std::string>& channels,
                                        const std::string& device_phy,
-                                       const std::vector<std::string>& params,
+                                       const iio_param_vec_t& params,
                                        unsigned int buffer_size,
                                        unsigned int decimation)
     : gr::sync_block("device_source",
diff --git a/gr-iio/lib/device_source_impl.h b/gr-iio/lib/device_source_impl.h
index 6bcacbe913..49db936312 100644
--- a/gr-iio/lib/device_source_impl.h
+++ b/gr-iio/lib/device_source_impl.h
@@ -70,17 +70,17 @@ public:
                        const std::string& device,
                        const std::vector<std::string>& channels,
                        const std::string& device_phy,
-                       const std::vector<std::string>& params,
+                       const iio_param_vec_t& params,
                        unsigned int buffer_size = DEFAULT_BUFFER_SIZE,
                        unsigned int decimation = 0);
 
     ~device_source_impl();
 
-    static void set_params(iio_device* phy, const std::vector<std::string>& params);
+    static void set_params(iio_device* phy, const iio_param_vec_t& params);
 
     void set_len_tag_key(const std::string& len_tag_key) override;
 
-    void set_params(const std::vector<std::string>& params);
+    void set_params(const iio_param_vec_t& params);
     void set_buffer_size(unsigned int buffer_size);
     void set_timeout_ms(unsigned long timeout);
 
diff --git a/gr-iio/lib/fmcomms2_sink_impl.cc b/gr-iio/lib/fmcomms2_sink_impl.cc
index b2523b0e47..ee72aca655 100644
--- a/gr-iio/lib/fmcomms2_sink_impl.cc
+++ b/gr-iio/lib/fmcomms2_sink_impl.cc
@@ -85,7 +85,7 @@ fmcomms2_sink_impl<int16_t>::fmcomms2_sink_impl(iio_context* ctx,
                        "cf-ad9361-dds-core-lpc",
                        get_channels_vector(ch_en),
                        "ad9361-phy",
-                       std::vector<std::string>(),
+                       iio_param_vec_t(),
                        buffer_size,
                        0,
                        cyclic),
@@ -108,7 +108,7 @@ fmcomms2_sink_impl<T>::fmcomms2_sink_impl(iio_context* ctx,
                        "cf-ad9361-dds-core-lpc",
                        get_channels_vector(ch_en),
                        "ad9361-phy",
-                       std::vector<std::string>(),
+                       iio_param_vec_t(),
                        buffer_size,
                        0,
                        cyclic),
@@ -178,8 +178,8 @@ void fmcomms2_sink_impl<T>::check_underflow(void)
 template <typename T>
 void fmcomms2_sink_impl<T>::set_bandwidth(unsigned long bandwidth)
 {
-    std::vector<std::string> params;
-    params.push_back("out_voltage_rf_bandwidth=" + std::to_string(bandwidth));
+    iio_param_vec_t params;
+    params.emplace_back("out_voltage_rf_bandwidth", bandwidth);
     device_source_impl::set_params(this->phy, params);
     d_bandwidth = bandwidth;
 }
@@ -187,8 +187,8 @@ void fmcomms2_sink_impl<T>::set_bandwidth(unsigned long bandwidth)
 template <typename T>
 void fmcomms2_sink_impl<T>::set_rf_port_select(const std::string& rf_port_select)
 {
-    std::vector<std::string> params;
-    params.push_back("out_voltage0_rf_port_select=" + rf_port_select);
+    iio_param_vec_t params;
+    params.emplace_back("out_voltage0_rf_port_select", rf_port_select);
     device_source_impl::set_params(this->phy, params);
     d_rf_port_select = rf_port_select;
 }
@@ -196,8 +196,8 @@ void fmcomms2_sink_impl<T>::set_rf_port_select(const std::string& rf_port_select
 template <typename T>
 void fmcomms2_sink_impl<T>::set_frequency(unsigned long long frequency)
 {
-    std::vector<std::string> params;
-    params.push_back("out_altvoltage1_TX_LO_frequency=" + std::to_string(frequency));
+    iio_param_vec_t params;
+    params.emplace_back("out_altvoltage1_TX_LO_frequency", frequency);
     device_source_impl::set_params(this->phy, params);
     d_frequency = frequency;
 }
@@ -205,7 +205,7 @@ void fmcomms2_sink_impl<T>::set_frequency(unsigned long long frequency)
 template <typename T>
 void fmcomms2_sink_impl<T>::set_samplerate(unsigned long samplerate)
 {
-    std::vector<std::string> params;
+    iio_param_vec_t params;
     if (samplerate < MIN_RATE) {
         int ret;
         samplerate = samplerate * DECINT_RATIO;
@@ -231,14 +231,9 @@ void fmcomms2_sink_impl<T>::set_attenuation(size_t chan, double attenuation)
     if ((!is_fmcomms4 && chan > 0) || chan > 1) {
         throw std::runtime_error("Channel out of range for this device");
     }
-    std::vector<std::string> params;
-    std::string att_value = std::to_string(-attenuation);
-    std::string::size_type idx = att_value.find(',');
-    if (idx != std::string::npos) // found , as decimal separator, so change to .
-        att_value.replace(idx, 1, ".");
-    params.push_back("out_voltage" + std::to_string(chan) + "_hardwaregain=" + att_value);
-
-
+    iio_param_vec_t params;
+    params.emplace_back("out_voltage" + std::to_string(chan) + "_hardwaregain",
+                        -attenuation);
     device_source_impl::set_params(this->phy, params);
 
     d_attenuation[chan] = attenuation;
@@ -247,17 +242,16 @@ void fmcomms2_sink_impl<T>::set_attenuation(size_t chan, double attenuation)
 template <typename T>
 void fmcomms2_sink_impl<T>::update_dependent_params()
 {
-    std::vector<std::string> params;
+    iio_param_vec_t params;
     // Set rate configuration
     if (d_filter_source.compare("Off") == 0) {
-        params.push_back("out_voltage_sampling_frequency=" +
-                         std::to_string(d_samplerate));
-        params.push_back("out_voltage_rf_bandwidth=" + std::to_string(d_bandwidth));
+        params.emplace_back("out_voltage_sampling_frequency", d_samplerate);
+        params.emplace_back("out_voltage_rf_bandwidth", d_bandwidth);
     } else if (d_filter_source.compare("Auto") == 0) {
         int ret = ad9361_set_bb_rate(phy, d_samplerate);
         if (ret) {
             throw std::runtime_error("Unable to set BB rate");
-            params.push_back("out_voltage_rf_bandwidth=" + std::to_string(d_bandwidth));
+            params.emplace_back("out_voltage_rf_bandwidth", d_bandwidth);
         }
     } else if (d_filter_source.compare("File") == 0) {
         std::string filt(d_filter_filename);
diff --git a/gr-iio/lib/fmcomms2_source_impl.cc b/gr-iio/lib/fmcomms2_source_impl.cc
index e6651e49bc..b3c7d39f91 100644
--- a/gr-iio/lib/fmcomms2_source_impl.cc
+++ b/gr-iio/lib/fmcomms2_source_impl.cc
@@ -83,7 +83,7 @@ fmcomms2_source_impl<int16_t>::fmcomms2_source_impl(iio_context* ctx,
                          "cf-ad9361-lpc",
                          get_channels_vector(ch_en),
                          "ad9361-phy",
-                         std::vector<std::string>(),
+                         iio_param_vec_t(),
                          buffer_size,
                          0)
 {
@@ -102,7 +102,7 @@ fmcomms2_source_impl<T>::fmcomms2_source_impl(iio_context* ctx,
                          "cf-ad9361-lpc",
                          get_channels_vector(ch_en),
                          "ad9361-phy",
-                         std::vector<std::string>(),
+                         iio_param_vec_t(),
                          buffer_size,
                          0)
 {
@@ -261,16 +261,16 @@ int fmcomms2_source_impl<T>::work(int noutput_items,
 template <typename T>
 void fmcomms2_source_impl<T>::update_dependent_params()
 {
-    std::vector<std::string> params;
+    iio_param_vec_t params;
     // Set rate configuration
     if (d_filter_source.compare("Off") == 0) {
-        params.push_back("in_voltage_sampling_frequency=" + std::to_string(d_samplerate));
-        params.push_back("in_voltage_rf_bandwidth=" + std::to_string(d_bandwidth));
+        params.emplace_back("in_voltage_sampling_frequency", d_samplerate);
+        params.emplace_back("in_voltage_rf_bandwidth", d_bandwidth);
     } else if (d_filter_source.compare("Auto") == 0) {
         int ret = ad9361_set_bb_rate(phy, d_samplerate);
         if (ret) {
             throw std::runtime_error("Unable to set BB rate");
-            params.push_back("in_voltage_rf_bandwidth=" + std::to_string(d_bandwidth));
+            params.emplace_back("in_voltage_rf_bandwidth", d_bandwidth);
         }
     } else if (d_filter_source.compare("File") == 0) {
         std::string filt(d_filter_filename);
@@ -304,15 +304,14 @@ void fmcomms2_source_impl<T>::set_len_tag_key(const std::string& len_tag_key)
 template <typename T>
 void fmcomms2_source_impl<T>::set_frequency(unsigned long long frequency)
 {
-    std::vector<std::string> params;
-    params.push_back("out_altvoltage0_RX_LO_frequency=" + std::to_string(frequency));
+    iio_param_vec_t params;
+    params.emplace_back("out_altvoltage0_RX_LO_frequency", frequency);
     device_source_impl::set_params(params);
 }
 
 template <typename T>
 void fmcomms2_source_impl<T>::set_samplerate(unsigned long samplerate)
 {
-    std::vector<std::string> params;
     if (samplerate < MIN_RATE) {
         int ret;
         samplerate = samplerate * DECINT_RATIO;
@@ -326,7 +325,6 @@ void fmcomms2_source_impl<T>::set_samplerate(unsigned long samplerate)
             samplerate, "voltage0", "sampling_frequency", dev, true, false);
     }
 
-    device_source_impl::set_params(params);
     d_samplerate = samplerate;
     update_dependent_params();
 }
@@ -338,10 +336,10 @@ void fmcomms2_source_impl<T>::set_gain_mode(size_t chan, const std::string& mode
     if ((!is_fmcomms4 && chan > 0) || chan > 1) {
         throw std::runtime_error("Channel out of range for this device");
     }
-    std::vector<std::string> params;
+    iio_param_vec_t params;
 
-    params.push_back("in_voltage" + std::to_string(chan) +
-                     "_gain_control_mode=" + d_gain_mode[chan]);
+    params.emplace_back("in_voltage" + std::to_string(chan) +
+                        "_gain_control_mode=" + d_gain_mode[chan]);
 
     device_source_impl::set_params(params);
     d_gain_mode[chan] = mode;
@@ -354,15 +352,11 @@ void fmcomms2_source_impl<T>::set_gain(size_t chan, double gain_value)
     if ((!is_fmcomms4 && chan > 0) || chan > 1) {
         throw std::runtime_error("Channel out of range for this device");
     }
-    std::vector<std::string> params;
+    iio_param_vec_t params;
 
     if (d_gain_mode[chan].compare("manual") == 0) {
-        std::string gain_string = std::to_string(gain_value);
-        std::string::size_type idx = gain_string.find(',');
-        if (idx != std::string::npos) // found , as decimal separator, so change to .
-            gain_string.replace(idx, 1, ".");
-        params.push_back("in_voltage" + std::to_string(chan) +
-                         "_hardwaregain=" + gain_string);
+        params.emplace_back("in_voltage" + std::to_string(chan) + "_hardwaregain",
+                            gain_value);
     }
     device_source_impl::set_params(params);
     d_gain_value[chan] = gain_value;
@@ -371,8 +365,8 @@ void fmcomms2_source_impl<T>::set_gain(size_t chan, double gain_value)
 template <typename T>
 void fmcomms2_source_impl<T>::set_quadrature(bool quadrature)
 {
-    std::vector<std::string> params;
-    params.push_back("in_voltage_quadrature_tracking_en=" + std::to_string(quadrature));
+    iio_param_vec_t params;
+    params.emplace_back("in_voltage_quadrature_tracking_en", quadrature);
     device_source_impl::set_params(params);
     d_quadrature = quadrature;
 }
@@ -380,8 +374,8 @@ void fmcomms2_source_impl<T>::set_quadrature(bool quadrature)
 template <typename T>
 void fmcomms2_source_impl<T>::set_rfdc(bool rfdc)
 {
-    std::vector<std::string> params;
-    params.push_back("in_voltage_rf_dc_offset_tracking_en=" + std::to_string(rfdc));
+    iio_param_vec_t params;
+    params.emplace_back("in_voltage_rf_dc_offset_tracking_en", rfdc);
     device_source_impl::set_params(params);
     d_rfdc = rfdc;
 }
@@ -389,8 +383,8 @@ void fmcomms2_source_impl<T>::set_rfdc(bool rfdc)
 template <typename T>
 void fmcomms2_source_impl<T>::set_bbdc(bool bbdc)
 {
-    std::vector<std::string> params;
-    params.push_back("in_voltage_bb_dc_offset_tracking_en=" + std::to_string(bbdc));
+    iio_param_vec_t params;
+    params.emplace_back("in_voltage_bb_dc_offset_tracking_en", bbdc);
     device_source_impl::set_params(params);
     d_bbdc = bbdc;
 }
diff --git a/gr-iio/lib/fmcomms5_sink_impl.cc b/gr-iio/lib/fmcomms5_sink_impl.cc
index 641cf26017..a34c5a49cb 100644
--- a/gr-iio/lib/fmcomms5_sink_impl.cc
+++ b/gr-iio/lib/fmcomms5_sink_impl.cc
@@ -259,21 +259,21 @@ void fmcomms5_sink_impl::set_params(iio_device* phy_device,
                                     float Fpass,
                                     float Fstop)
 {
-    std::vector<std::string> params;
+    iio_param_vec_t params;
     int ret;
 
-    params.push_back("out_altvoltage1_TX_LO_frequency=" + std::to_string(frequency));
-    params.push_back("out_voltage0_rf_port_select=" + std::to_string(rf_port_select));
-    params.push_back("out_voltage0_hardwaregain=" + std::to_string(-attenuation1));
-    params.push_back("out_voltage1_hardwaregain=" + std::to_string(-attenuation2));
+    params.emplace_back("out_altvoltage1_TX_LO_frequency", frequency);
+    params.emplace_back("out_voltage0_rf_port_select", rf_port_select);
+    params.emplace_back("out_voltage0_hardwaregain", -attenuation1);
+    params.emplace_back("out_voltage1_hardwaregain", -attenuation2);
 
     // Set rate configuration
     std::string filt_config(filter_source);
     if (filt_config.compare("Off") == 0) {
-        params.push_back("in_voltage_sampling_frequency=" + std::to_string(samplerate));
-        params.push_back("in_voltage_rf_bandwidth=" + std::to_string(bandwidth));
+        params.emplace_back("in_voltage_sampling_frequency", samplerate);
+        params.emplace_back("in_voltage_rf_bandwidth", bandwidth);
     } else if (filt_config.compare("Auto") == 0) {
-        params.push_back("in_voltage_rf_bandwidth=" + std::to_string(bandwidth));
+        params.emplace_back("in_voltage_rf_bandwidth", bandwidth);
         ret = ad9361_set_bb_rate(phy_device, samplerate);
         if (ret)
             throw std::runtime_error("Unable to set BB rate");
diff --git a/gr-iio/lib/fmcomms5_source_impl.cc b/gr-iio/lib/fmcomms5_source_impl.cc
index 9cd7ff94b8..23d072a30e 100644
--- a/gr-iio/lib/fmcomms5_source_impl.cc
+++ b/gr-iio/lib/fmcomms5_source_impl.cc
@@ -298,30 +298,30 @@ void fmcomms5_source_impl::set_params(iio_device* phy_device,
                                       float Fpass,
                                       float Fstop)
 {
-    std::vector<std::string> params;
+    iio_param_vec_t params;
     std::string gain1_str(gain1);
     std::string gain2_str(gain2);
     int ret;
 
-    params.push_back("out_altvoltage0_RX_LO_frequency=" + std::to_string(frequency));
-    params.push_back("in_voltage_quadrature_tracking_en=" + std::to_string(quadrature));
-    params.push_back("in_voltage_rf_dc_offset_tracking_en=" + std::to_string(rfdc));
-    params.push_back("in_voltage_bb_dc_offset_tracking_en=" + std::to_string(bbdc));
-    params.push_back("in_voltage0_gain_control_mode=" + std::to_string(gain1));
+    params.emplace_back("out_altvoltage0_RX_LO_frequency", frequency);
+    params.emplace_back("in_voltage_quadrature_tracking_en", quadrature);
+    params.emplace_back("in_voltage_rf_dc_offset_tracking_en", rfdc);
+    params.emplace_back("in_voltage_bb_dc_offset_tracking_en", bbdc);
+    params.emplace_back("in_voltage0_gain_control_mode", gain1);
     if (gain1_str.compare("manual") == 0)
-        params.push_back("in_voltage0_hardwaregain=" + std::to_string(gain1_value));
-    params.push_back("in_voltage1_gain_control_mode=" + std::to_string(gain2));
+        params.emplace_back("in_voltage0_hardwaregain", gain1_value);
+    params.emplace_back("in_voltage1_gain_control_mode", gain2);
     if (gain2_str.compare("manual") == 0)
-        params.push_back("in_voltage1_hardwaregain=" + std::to_string(gain2_value));
-    params.push_back("in_voltage0_rf_port_select=" + std::to_string(port_select));
+        params.emplace_back("in_voltage1_hardwaregain", gain2_value);
+    params.emplace_back("in_voltage0_rf_port_select", port_select);
 
     // Set rate configuration
     std::string filt_config(filter_source);
     if (filt_config.compare("Off") == 0) {
-        params.push_back("in_voltage_sampling_frequency=" + std::to_string(samplerate));
-        params.push_back("in_voltage_rf_bandwidth=" + std::to_string(bandwidth));
+        params.emplace_back("in_voltage_sampling_frequency", samplerate);
+        params.emplace_back("in_voltage_rf_bandwidth", bandwidth);
     } else if (filt_config.compare("Auto") == 0) {
-        params.push_back("in_voltage_rf_bandwidth=" + std::to_string(bandwidth));
+        params.emplace_back("in_voltage_rf_bandwidth", bandwidth);
         ret = ad9361_set_bb_rate(phy_device, samplerate);
         if (ret)
             throw std::runtime_error("Unable to set BB rate");
diff --git a/gr-iio/lib/iio_types.cc b/gr-iio/lib/iio_types.cc
new file mode 100644
index 0000000000..848db282ff
--- /dev/null
+++ b/gr-iio/lib/iio_types.cc
@@ -0,0 +1,56 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 Josh Morman
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#include <gnuradio/iio/iio_types.h>
+
+namespace gr {
+namespace iio {
+
+
+iio_param_t::iio_param_t(const std::string& key, iio_param_value_t value)
+{
+    this->first = key;
+    this->second = to_string(value);
+}
+iio_param_t::iio_param_t(const std::string& kvpair)
+{
+    std::vector<std::string> toks;
+    for (const std::string& tok : tokenizer(kvpair, "=")) {
+        toks.push_back(tok);
+    }
+    if (toks.size() == 2 && !toks[0].empty()) { // only valid combination
+        this->first = toks[0];
+        this->second = toks[1];
+    } else
+        throw std::invalid_argument("invalid parameter string: " +
+                                    kvpair); // otherwise error
+}
+
+std::string iio_param_t::to_string(iio_param_value_t value)
+{
+    // long long unsigned int, long unsigned int, int, double, std::string
+    if (std::holds_alternative<long long unsigned int>(value)) {
+        return std::to_string(std::get<long long unsigned int>(value));
+    } else if (std::holds_alternative<long unsigned int>(value)) {
+        return std::to_string(std::get<long unsigned int>(value));
+    } else if (std::holds_alternative<int>(value)) {
+        return std::to_string(std::get<int>(value));
+    } else if (std::holds_alternative<double>(value)) {
+        std::string value_string = std::to_string(std::get<double>(value));
+        std::string::size_type idx = value_string.find(',');
+        if (idx != std::string::npos) // found , as decimal separator, so change to .
+            value_string.replace(idx, 1, ".");
+        return value_string;
+    } else {
+        return std::get<std::string>(value);
+    }
+}
+
+
+} // namespace iio
+} // namespace gr
diff --git a/gr-iio/python/iio/bindings/device_sink_python.cc b/gr-iio/python/iio/bindings/device_sink_python.cc
index f3cadc6235..95815b88a4 100644
--- a/gr-iio/python/iio/bindings/device_sink_python.cc
+++ b/gr-iio/python/iio/bindings/device_sink_python.cc
@@ -14,7 +14,7 @@
 /* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
 /* BINDTOOL_USE_PYGCCXML(0)                                                        */
 /* BINDTOOL_HEADER_FILE(device_sink.h)                                             */
-/* BINDTOOL_HEADER_FILE_HASH(955a5cf5cd09be457c60c8ce2dfe96d8)                     */
+/* BINDTOOL_HEADER_FILE_HASH(0ea102fb1e1158dd3d98e1ed56f3861e)                     */
 /***********************************************************************************/
 
 #include <pybind11/complex.h>
diff --git a/gr-iio/python/iio/bindings/device_source_python.cc b/gr-iio/python/iio/bindings/device_source_python.cc
index 4219aac920..5b48e1ac83 100644
--- a/gr-iio/python/iio/bindings/device_source_python.cc
+++ b/gr-iio/python/iio/bindings/device_source_python.cc
@@ -14,7 +14,7 @@
 /* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
 /* BINDTOOL_USE_PYGCCXML(0)                                                        */
 /* BINDTOOL_HEADER_FILE(device_source.h)                                           */
-/* BINDTOOL_HEADER_FILE_HASH(d959417ab79d21ca10ef0b9f6161d116)                     */
+/* BINDTOOL_HEADER_FILE_HASH(24b73e05146a021958160a913d26e745)                     */
 /***********************************************************************************/
 
 #include <pybind11/complex.h>
diff --git a/gr-iio/python/iio/bindings/iio_types_python.cc b/gr-iio/python/iio/bindings/iio_types_python.cc
index c800b59d12..6bf5bb7a2b 100644
--- a/gr-iio/python/iio/bindings/iio_types_python.cc
+++ b/gr-iio/python/iio/bindings/iio_types_python.cc
@@ -14,7 +14,7 @@
 /* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
 /* BINDTOOL_USE_PYGCCXML(0)                                                        */
 /* BINDTOOL_HEADER_FILE(iio_types.h)                                        */
-/* BINDTOOL_HEADER_FILE_HASH(c42dd62276a1716a7672f12704dabd08)                     */
+/* BINDTOOL_HEADER_FILE_HASH(2e0a29b6c915504e7bac45fa7fa47949)                     */
 /***********************************************************************************/
 
 #include <pybind11/complex.h>
@@ -45,4 +45,11 @@ void bind_iio_types(py::module& m)
 
     py::implicitly_convertible<int, gr::iio::data_type_t>();
     py::implicitly_convertible<int, gr::iio::attr_type_t>();
+
+    py::class_<gr::iio::iio_param_t, std::shared_ptr<gr::iio::iio_param_t>>(m,
+                                                                            "iio_param_t")
+        .def(py::init<const std::string&>())
+        .def(py::init<const std::string&, gr::iio::iio_param_value_t>());
+
+    py::implicitly_convertible<std::string, gr::iio::iio_param_t>();
 }
-- 
cgit v1.2.3