diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-12-05 11:17:03 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-06 10:59:28 -0800 |
commit | 77adcffe0689f120fee71854ec0f627a32991f79 (patch) | |
tree | f53f89049a2e5f8f543ccf01139d2a37d27b6125 | |
parent | 8a9da7c66aaec826e756ab2411b9d40aa3f1f10c (diff) | |
download | uhd-77adcffe0689f120fee71854ec0f627a32991f79.tar.gz uhd-77adcffe0689f120fee71854ec0f627a32991f79.tar.bz2 uhd-77adcffe0689f120fee71854ec0f627a32991f79.zip |
multi_usrp: Expose APIs for TX LO controls
Reviewed-By: Ashish Chaudhari <ashish@ettus.com>
Reviewed-By: Derek Kozel <derek.kozel@ettus.com>
-rw-r--r-- | host/include/uhd/usrp/multi_usrp.hpp | 284 | ||||
-rw-r--r-- | host/lib/usrp/multi_usrp.cpp | 220 |
2 files changed, 467 insertions, 37 deletions
diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index fee430fe0..451510f5d 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -113,7 +113,7 @@ public: //! A wildcard gain element name static const std::string ALL_GAINS; - //! A wildcard gain element name + //! A wildcard LO stage name static const std::string ALL_LOS; /*! @@ -499,90 +499,300 @@ public: */ virtual freq_range_t get_fe_rx_freq_range(size_t chan = 0) = 0; - /*! - * Get a list of possible LO stage names + /************************************************************************** + * LO controls + *************************************************************************/ + /*! Get a list of possible LO stage names + * + * Example: On the TwinRX, this will return "LO1", "LO2". These names can + * are used in other LO-related API calls, so this function can be used for + * automatically enumerating LO stages. + * An empty return value doesn't mean there are no LOs, it means that this + * radio does not have an LO API implemented, and typically means the LOs + * have no direct way of being controlled other than setting the frequency. + * * \param chan the channel index 0 to N-1 - * \return a vector of strings for possible LO names + * \return a vector of strings for possible LO names, or an empty list of + * this doesn't apply (i.e. there are no controllable LO stages) */ virtual std::vector<std::string> get_rx_lo_names(size_t chan = 0) = 0; - /*! - * Set the LO source for the usrp device. - * For usrps that support selectable LOs, this function - * allows switching between them. - * Typical options for source: internal, external. + /*! Set the LO source for the USRP device. + * + * For USRPs that support selectable LO sources, this function allows + * switching between them. Typical options for source: internal, external. + * * \param src a string representing the LO source + * \param name the name of the LO stage to update. If the wildcard value + * ALL_LOS is used, the setting will be applied to all LOs on + * this channel. + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_lo_source( + const std::string &src, + const std::string &name = ALL_LOS, + size_t chan = 0 + ) = 0; + + /*! Get the currently selected LO source. + * + * Channels without controllable LO sources will always return "internal". + * + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO source + */ + virtual const std::string get_rx_lo_source( + const std::string &name = ALL_LOS, + size_t chan = 0 + ) = 0; + + /*! Get a list of possible LO sources. + * + * Channels which do not have controllable LO sources will return + * "internal". Typical values are "internal" and "external", although the + * TwinRX has more options, such as "companion". These options are device- + * specific. + * + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible settings + */ + virtual std::vector<std::string> get_rx_lo_sources( + const std::string &name = ALL_LOS, + size_t chan = 0 + ) = 0; + + /*! Set whether the LO used by the device is exported + * + * For USRPs that support exportable LOs, this function + * configures if the LO used by chan is exported or not. + * + * \param enabled if true then export the LO * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 for the source channel + * \throws uhd::runtime_error if LO exporting is not enabled + */ + virtual void set_rx_lo_export_enabled( + bool enabled, + const std::string &name = ALL_LOS, + size_t chan = 0 + ) = 0; + + /*! Returns true if the currently selected LO is being exported. + * + * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 */ - virtual void set_rx_lo_source(const std::string &src, const std::string &name = ALL_LOS, size_t chan = 0) = 0; + virtual bool get_rx_lo_export_enabled( + const std::string &name = ALL_LOS, + size_t chan = 0 + ) = 0; - /*! - * Get the currently set LO source. - * Channels without controllable LO sources will return - * "internal" + /*! Set the RX LO frequency (Advanced). + * + * The actual behaviour is device-specific. However, as a rule of thumb, + * this will coerce the underlying driver into some state. Typical + * situations include: + * - LOs are internal, and this function is called to pin an LO to a + * certain value. This can force the driver to pick different IFs for + * different stages, and there may be situations where this behaviour + * can be used to reduce spurs in specific bands. + * - LOs are external. In this case, this function is used to notify UHD + * what the actual value of an externally provided LO is. The only time + * when calling this function is necessary is when the LO source is set + * to external, but the external LO can't be tuned to the exact value + * required by UHD to achieve a certain center frequency. In this case, + * calling set_rx_lo_freq() will let UHD know that the LO is not the + * expected value, and it's possible that UHD will find other ways to + * compensate for the LO offset. + * + * \param freq the frequency to set the LO to + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 + * \return a coerced LO frequency + */ + virtual double set_rx_lo_freq( + double freq, + const std::string &name, + size_t chan = 0 + ) = 0; + + /*! Get the current RX LO frequency (Advanced). + * + * If the channel does not have independently configurable LOs + * the current rf frequency will be returned. See also set_rx_lo_freq() for + * more information. + * + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO frequency + */ + virtual double get_rx_lo_freq( + const std::string &name, + size_t chan = 0 + ) = 0; + + /*! Get the LO frequency range of the RX LO. + * + * If the channel does not have independently configurable LOs + * the rf frequency range will be returned. + * + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_rx_lo_freq_range( + const std::string &name, + size_t chan = 0 + ) = 0; + + /*! Get a list of possible TX LO stage names + * + * See also get_rx_lo_names(). + * + * An empty return value doesn't mean there are no LOs, it means that this + * radio does not have an LO API implemented, and typically means the LOs + * have no direct way of being controlled other than setting the frequency. + * + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible LO names, or an empty list of + * this doesn't apply (i.e. there are no controllable LO stages) + */ + virtual std::vector<std::string> get_tx_lo_names(size_t chan = 0) = 0; + + /*! Set the TX LO source for the USRP device. + * + * For USRPs that support selectable LO sources, this function allows + * switching between them. Typical options for source: internal, external. + * + * \param src a string representing the LO source + * \param name the name of the LO stage to update. If the wildcard value + * ALL_LOS is used, the setting will be applied to all LOs on + * this channel. + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_lo_source( + const std::string &src, + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) = 0; + + /*! Get the currently selected TX LO source. + * + * Channels without controllable LO sources will always return "internal". + * * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 * \return the configured LO source */ - virtual const std::string get_rx_lo_source(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + virtual const std::string get_tx_lo_source( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) = 0; - /*! - * Get a list of possible LO sources. - * Channels which do not have controllable LO sources - * will return "internal". + /*! Get a list of possible LO sources. + * + * Channels which do not have controllable LO sources will return + * "internal". Typical values are "internal" and "external". + * These options are device-specific. + * * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 * \return a vector of strings for possible settings */ - virtual std::vector<std::string> get_rx_lo_sources(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + virtual std::vector<std::string> get_tx_lo_sources( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) = 0; - /*! - * Set whether the LO used by the usrp device is exported - * For usrps that support exportable LOs, this function + /*! Set whether the TX LO used by the device is exported + * + * For USRPs that support exportable LOs, this function * configures if the LO used by chan is exported or not. + * * \param enabled if true then export the LO * \param name the name of the LO stage to update * \param chan the channel index 0 to N-1 for the source channel + * \throws uhd::runtime_error if LO exporting is not enabled */ - virtual void set_rx_lo_export_enabled(bool enabled, const std::string &name = ALL_LOS, size_t chan = 0) = 0; + virtual void set_tx_lo_export_enabled( + const bool enabled, + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) = 0; - /*! - * Returns true if the currently selected LO is being exported. + /*! Returns true if the currently selected LO is being exported. + * * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 */ - virtual bool get_rx_lo_export_enabled(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + virtual bool get_tx_lo_export_enabled( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) = 0; - /*! - * Set the RX LO frequency (Advanced). + /*! Set the TX LO frequency (Advanced). + * + * The actual behaviour is device-specific. However, as a rule of thumb, + * this will coerce the underlying driver into some state. Typical + * situations include: + * - LOs are internal, and this function is called to pin an LO to a + * certain value. This can force the driver to pick different IFs for + * different stages, and there may be situations where this behaviour + * can be used to reduce spurs in specific bands. + * - LOs are external. In this case, this function is used to notify UHD + * what the actual value of an externally provided LO is. The only time + * when calling this function is necessary is when the LO source is set + * to external, but the external LO can't be tuned to the exact value + * required by UHD to achieve a certain center frequency. In this case, + * calling set_tx_lo_freq() will let UHD know that the LO is not the + * expected value, and it's possible that UHD will find other ways to + * compensate for the LO offset. + * * \param freq the frequency to set the LO to * \param name the name of the LO stage to update * \param chan the channel index 0 to N-1 * \return a coerced LO frequency */ - virtual double set_rx_lo_freq(double freq, const std::string &name, size_t chan = 0) = 0; + virtual double set_tx_lo_freq( + const double freq, + const std::string &name, + const size_t chan=0 + ) = 0; - /*! - * Get the current RX LO frequency (Advanced). + /*! Get the current TX LO frequency (Advanced). + * * If the channel does not have independently configurable LOs - * the current rf frequency will be returned. + * the current rf frequency will be returned. See also set_tx_lo_freq() for + * more information. + * * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 * \return the configured LO frequency */ - virtual double get_rx_lo_freq(const std::string &name, size_t chan = 0) = 0; + virtual double get_tx_lo_freq( + const std::string &name, + const size_t chan=0 + ) = 0; - /*! - * Get the LO frequency range of the RX LO. + /*! Get the LO frequency range of the TX LO. + * * If the channel does not have independently configurable LOs * the rf frequency range will be returned. + * * \param name the name of the LO stage to query * \param chan the channel index 0 to N-1 * \return a frequency range object */ - virtual freq_range_t get_rx_lo_freq_range(const std::string &name, size_t chan = 0) = 0; + virtual freq_range_t get_tx_lo_freq_range( + const std::string &name, + const size_t chan=0 + ) = 0; + /************************************************************************** + * Gain controls + *************************************************************************/ /*! * Set the RX gain value for the specified gain element. * For an empty name, distribute across all gain elements. diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 3dcfe3bd9..d7d925f3c 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -870,6 +870,9 @@ public: return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(); } + /************************************************************************** + * LO controls + *************************************************************************/ std::vector<std::string> get_rx_lo_names(size_t chan = 0){ std::vector<std::string> lo_names; if (_tree->exists(rx_rf_fe_root(chan) / "los")) { @@ -1035,6 +1038,223 @@ public: } } + std::vector<std::string> get_tx_lo_names(const size_t chan = 0){ + std::vector<std::string> lo_names; + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + for (const std::string &name : _tree->list(tx_rf_fe_root(chan) / "los")) { + lo_names.push_back(name); + } + } + return lo_names; + } + + void set_tx_lo_source( + const std::string &src, + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) { + // Special value ALL_LOS support atomically sets the source + // for all LOs + _tree->access<std::string>( + tx_rf_fe_root(chan) / "los" / ALL_LOS / + "source" / "value" + ).set(src); + } else { + for (const auto &n : _tree->list(tx_rf_fe_root(chan) / "los")) { + this->set_tx_lo_source(src, n, chan); + } + } + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + _tree->access<std::string>( + tx_rf_fe_root(chan) / "los" / name / "source" / + "value" + ).set(src); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + throw uhd::runtime_error("This device does not support manual " + "configuration of LOs"); + } + } + + const std::string get_tx_lo_source( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access<std::string>( + tx_rf_fe_root(chan) / "los" / name / "source" / "value" + ).get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } else { + // If the daughterboard doesn't expose its LO(s) then it can only + // be internal + return "internal"; + } + } + + std::vector<std::string> get_tx_lo_sources( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) { + // Special value ALL_LOS support atomically sets the source + // for all LOs + return _tree->access<std::vector<std::string>>( + tx_rf_fe_root(chan) / "los" / ALL_LOS / + "source" / "options" + ).get(); + } else { + return std::vector<std::string>(); + } + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access< std::vector<std::string> >(tx_rf_fe_root(chan) / "los" / name / "source" / "options").get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + // If the daughterboard doesn't expose its LO(s) then it can only + // be internal + return std::vector<std::string>(1, "internal"); + } + } + + void set_tx_lo_export_enabled( + const bool enabled, + const std::string &name = ALL_LOS, + const size_t chan=0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + if (_tree->exists(tx_rf_fe_root(chan) / "los" / ALL_LOS)) { + //Special value ALL_LOS support atomically sets the source for all LOs + _tree->access<bool>(tx_rf_fe_root(chan) / "los" / ALL_LOS / "export").set(enabled); + } else { + for(const std::string &n: _tree->list(tx_rf_fe_root(chan) / "los")) { + this->set_tx_lo_export_enabled(enabled, n, chan); + } + } + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + _tree->access<bool>(tx_rf_fe_root(chan) / "los" / name / "export").set(enabled); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + throw uhd::runtime_error("This device does not support manual configuration of LOs"); + } + } + + bool get_tx_lo_export_enabled( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access<bool>( + tx_rf_fe_root(chan) / "los" / name / "export" + ).get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } else { + // If the daughterboard doesn't expose its LO(s), assume it cannot + // export + return false; + } + } + + double set_tx_lo_freq( + const double freq, + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + throw uhd::runtime_error("LO frequency must be set for each " + "stage individually"); + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access<double>( + tx_rf_fe_root(chan) / "los" / name / "freq" / "value" + ).set(freq).get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + throw uhd::runtime_error("This device does not support manual " + "configuration of LOs"); + } + } + + double get_tx_lo_freq( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + throw uhd::runtime_error("LO frequency must be retrieved for " + "each stage individually"); + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access<double>(tx_rf_fe_root(chan) / "los" / name / "freq" / "value").get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + // Return actual RF frequency if the daughterboard doesn't expose + // its LO(s) + return _tree->access<double>( + tx_rf_fe_root(chan) / "freq" /" value" + ).get(); + } + } + + freq_range_t get_tx_lo_freq_range( + const std::string &name = ALL_LOS, + const size_t chan = 0 + ) { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + if (name == ALL_LOS) { + throw uhd::runtime_error("LO frequency range must be retrieved " + "for each stage individually"); + } else { + if (_tree->exists(tx_rf_fe_root(chan) / "los")) { + return _tree->access<freq_range_t>( + tx_rf_fe_root(chan) / "los" / name / "freq" / "range" + ).get(); + } else { + throw uhd::runtime_error("Could not find LO stage " + name); + } + } + } else { + // Return the actual RF range if the daughterboard doesn't expose + // its LO(s) + return _tree->access<meta_range_t>( + tx_rf_fe_root(chan) / "freq" / "range" + ).get(); + } + } + + /************************************************************************** + * Gain control + *************************************************************************/ void set_rx_gain(double gain, const std::string &name, size_t chan){ /* Check if any AGC mode is enable and if so warn the user */ if (chan != ALL_CHANS) { |