From 18cda8029ff7ab1255579d81b0b27de68dacc88e Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Mon, 26 Mar 2018 14:48:12 -0700 Subject: usrp2: Re-add ability to modulate in the DAC This partially reverts b29e80cc. That commit fixed a bug with numerical overflows, but also removed the ability to tune using the DAC, effectively reducing the DSP tuning range. This commit allows to tune within +/- 200 MHz using a combination of both the DAC and the DSP tuning in the FPGA. Reviewed-by: Derek Kozek --- host/lib/usrp/usrp2/codec_ctrl.cpp | 5 +++ host/lib/usrp/usrp2/codec_ctrl.hpp | 7 ++++ host/lib/usrp/usrp2/usrp2_impl.cpp | 80 ++++++++++++++++++++++++++++++++++---- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index c3e7b332f..5c1c006fb 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -179,6 +179,11 @@ public: } } + size_t get_tx_interpolation() const + { + return 4; + } + private: ad9777_regs_t _ad9777_regs; ads62p44_regs_t _ads62p44_regs; diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index e32b508e9..ac808e357 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -56,6 +56,13 @@ public: virtual void set_rx_digital_fine_gain(double gain) = 0; + /*! Return the internal interpolation on the TX side. This is the factor + * between the rate of incoming samples and rate at which the DAC is + * actually clocked. Values > 1 will enable features, such as internal + * modulation. + */ + virtual size_t get_tx_interpolation() const = 0; + }; #endif /* INCLUDED_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 88591333a..d6ab11969 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -622,21 +622,44 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) : // create tx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].tx_dsp = tx_dsp_core_200::make( - _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID + _mbc[mb].wbiface, + U2_REG_SR_ADDR(SR_TX_DSP), + U2_REG_SR_ADDR(SR_TX_CTRL), + USRP2_TX_ASYNC_SID ); _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); + { // This scope can be removed once we're able to do named captures + auto this_tx_dsp = _mbc[mb].tx_dsp; // This can then also go away _tree->access(mb_path / "tick_rate") - .add_coerced_subscriber(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1)); + .add_coerced_subscriber([this_tx_dsp](const double rate){ + this_tx_dsp->set_tick_rate(rate); + }) + ; _tree->create(mb_path / "tx_dsps/0/rate/range") - .set_publisher(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp)); + .set_publisher([this_tx_dsp](){ + return this_tx_dsp->get_host_rates(); + }) + ; _tree->create(mb_path / "tx_dsps/0/rate/value") .set(1e6) //some default - .set_coercer(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1)) - .add_coerced_subscriber(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1)); + .set_coercer([this_tx_dsp](const double rate){ + return this_tx_dsp->set_host_rate(rate); + }) + .add_coerced_subscriber([this, mb](const double rate){ + this->update_tx_samp_rate(mb, 0, rate); + }) + ; + } // End of non-C++14 scope (to release reference to this_tx_dsp) _tree->create(mb_path / "tx_dsps/0/freq/value") - .set_coercer(boost::bind(&tx_dsp_core_200::set_freq, _mbc[mb].tx_dsp, _1)); + .set_coercer([this, mb](const double rate){ + return this->set_tx_dsp_freq(mb, rate); + }) + ; _tree->create(mb_path / "tx_dsps/0/freq/range") - .set_publisher(boost::bind(&tx_dsp_core_200::get_freq_range, _mbc[mb].tx_dsp)); + .set_publisher([this, mb](){ + return this->get_tx_dsp_freq_range(mb); + }) + ; //setup dsp flow control const double ups_per_sec = device_args_i.cast("ups_per_sec", 20); @@ -810,6 +833,49 @@ void usrp2_impl::set_tx_fe_corrections(const std::string &mb, const double lo_fr } } +double usrp2_impl::set_tx_dsp_freq( + const std::string &mb, + const double freq_ +) { + double new_freq = freq_; + const double tick_rate = + _tree->access("/mboards/"+mb+"/tick_rate").get(); + + //calculate the DAC shift (multiples of rate) + const int sign = boost::math::sign(new_freq); + const int zone = std::min(boost::math::iround(new_freq/tick_rate), 2); + const double dac_shift = sign*zone*tick_rate; + new_freq -= dac_shift; //update FPGA DSP target freq + UHD_LOG_TRACE("USRP2", + "DSP Tuning: Requested " + std::to_string(freq_/1e6) + " MHz, Using " + "Nyquist zone " + std::to_string(sign*zone) + ", leftover DSP tuning: " + + std::to_string(new_freq/1e6) + " MHz."); + + //set the DAC shift (modulation mode) + if (zone == 0) { + _mbc[mb].codec->set_tx_mod_mode(0); //no shift + } else { + _mbc[mb].codec->set_tx_mod_mode(sign*4/zone); //DAC interp = 4 + } + + return _mbc[mb].tx_dsp->set_freq(new_freq) + dac_shift; //actual freq +} + +meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb) +{ + const double dac_rate = + _tree->access("/mboards/" + mb + "/tick_rate").get() + * _mbc[mb].codec->get_tx_interpolation(); + const auto dsp_range_step = _mbc[mb].tx_dsp->get_freq_range().step(); + // The DSP tuning rate is the entire range of the DAC clock rate. The step + // size is determined by the FPGA IP, however. + return meta_range_t( + -dac_rate / 2, + +dac_rate / 2, + dsp_range_step + ); +} + #include #include -- cgit v1.2.3