Thanks for the reply and sorry for not providing much information. My case
is that I want to do a relay transmission with the setup: USRP 1 transmits
--> USRP 2 receives and retransmits with a shifted fc --> USRP 3 receives.
The problem is in USRP 2, as you pointed out in your post. I'm testing on a
simple flowgraph RX --> DDC --> DmaFIFO --> DUC (frequency shifted by 5MHz)
--> TX. I used your approach [1] in step 1 of your tutorial (also did step
2 and 3) and rebuilt.
At first the flowgraph runs without errors. The TX and RX lights were on,
so it seemed to transmit something. But in USRP 3 I didn't see the relay
signals (only the original signal that USRP1 transmits).
Also, in the following runs, it raised the errors:
[INFO] [UHDlinux; GNU C++ version 5.4.0 20160609; Boost_105800;
UHD_4.0.0.rfnoc-devel-409-gec9138eb]
[INFO] [X300] X300 initialization sequence...
[INFO] [X300] Determining maximum frame size...
[INFO] [X300] Maximum frame size: 1472 bytes.
[INFO] [X300] Setup basic communication...
[INFO] [X300] Loading values from EEPROM...
[INFO] [X300] Setup RF frontend clocking...
[INFO] [X300] Radio 1x clock:200
[INFO] [RFNOC] [DMA FIFO] Running BIST for FIFO 0...
[INFO] [RFNOC] pass (Throughput: 1302.9MB/s)
[INFO] [RFNOC] [DMA FIFO] Running BIST for FIFO 1...
[INFO] [RFNOC] pass (Throughput: 1293.6MB/s)
[INFO] [RFNOC RADIO] Register loopback test passed
[INFO] [RFNOC RADIO] Register loopback test passed
[INFO] [RFNOC RADIO] Register loopback test passed
[INFO] [RFNOC RADIO] Register loopback test passed
[INFO] [CORES] Performing timer loopback test...
[INFO] [CORES] Timer loopback test passed
[INFO] [CORES] Performing timer loopback test...
[INFO] [CORES] Timer loopback test passed
[INFO] [RFNOC] Assuming max packet size for 0/DDC_0
[INFO] [RFNOC] Assuming max packet size for 0/DmaFIFO_0
[INFO] [RFNOC] Assuming max packet size for 0/DUC_0
[INFO] [RFNOC] Assuming max packet size for 0/Radio_0
kei@kei-Precision-T7610:~/test_rfnoc$ ./relay.py
[INFO] [UHDlinux; GNU C++ version 5.4.0 20160609; Boost_105800;
UHD_4.0.0.rfnoc-devel-409-gec9138eb]
[INFO] [X300] X300 initialization sequence...
[INFO] [X300] Determining maximum frame size...
[INFO] [X300] Maximum frame size: 1472 bytes.
[INFO] [X300] Setup basic communication...
[INFO] [X300] Loading values from EEPROM...
[INFO] [X300] Setup RF frontend clocking...
[INFO] [X300] Radio 1x clock:200
[ERROR] [UHD] Exception caught in safe-call.
  in virtual ctrl_iface_impl::~ctrl_iface_impl()
  at /home/kei/rfnoc/src/uhd/host/lib/rfnoc/ctrl_iface.cpp:76
this->peek32(0); -> EnvironmentError: IOError: Block ctrl (CE_00_Port_30)
packet parse error - EnvironmentError: IOError: Expected packet index: 3
Received index: 4
Traceback (most recent call last):
  File "./relay.py", line 291, in <module>
    main()
  File "./relay.py", line 279, in main
    tb = top_block_cls(freq=options.freq)
  File "./relay.py", line 72, in __init__
    self.device3 = device3 = ettus.device3(uhd.device_addr_t(
",".join(('type=x300', "")) ))
  File "/home/kei/rfnoc/lib/python2.7/dist-packages/ettus/ettus_swig.py",
line 1610, in make
    return _ettus_swig.device3_make(device_addr)
RuntimeError: EnvironmentError: IOError: [0/DmaFIFO_0] sr_read64() failed:
EnvironmentError: IOError: Block ctrl (CE_00_Port_30) packet parse error -
EnvironmentError: IOError: Expected SID: 02:30>00:00  Received SID:
02:c0>00:00


which is exactly the errors that Christophe faced in the previous post. I
had to power cycle to run the flowgraph again.
Attachments are the changed files. I'm using uhd version UHD
4.0.0.rfnoc-devel-409-gec9138eb and gnuradio version
3.7.12git-295-ga0adcd33.
Sorry for the long post.



On Fri, Mar 9, 2018 at 12:21 PM, Nick Foster <bistrom...@gmail.com> wrote:

> You're going to have to ask a better question than that. Please provide
> the following:
>
> * What you are trying to accomplish
> * What hardware you are using
> * What UHD and Gnuradio version you are using
> * The approach you followed
> * The result you expected
> * The result you actually got
> * A flowgraph which exhibits the problem
>
> If you put effort into your question, we can put effort into answering you.
>
> Nick
>
> On Thu, Mar 8, 2018 at 10:39 AM Kei Nguyen via USRP-users <
> usrp-users@lists.ettus.com> wrote:
>
>> Hello, is this problem resolved? I am facing the same issue in the last
>> email. Also, I tried to receive the relay signal with another usrp but it
>> doesn't receive anything in my transmit frequency.
>>
>> Hai
>> _______________________________________________
>> USRP-users mailing list
>> USRP-users@lists.ettus.com
>> http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com
>>
>
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
##################################################
# GNU Radio Python Flow Graph
# Title: Relay
# Generated: Fri Mar  9 13:08:00 2018
##################################################

if __name__ == '__main__':
    import ctypes
    import sys
    if sys.platform.startswith('linux'):
        try:
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
            x11.XInitThreads()
        except:
            print "Warning: failed to XInitThreads()"

from PyQt4 import Qt
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio import uhd
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
from gnuradio.qtgui import Range, RangeWidget
from optparse import OptionParser
import ettus
import sys
from gnuradio import qtgui


class relay(gr.top_block, Qt.QWidget):

    def __init__(self, freq=2412e6):
        gr.top_block.__init__(self, "Relay")
        Qt.QWidget.__init__(self)
        self.setWindowTitle("Relay")
        qtgui.util.check_set_qss()
        try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
        except:
            pass
        self.top_scroll_layout = Qt.QVBoxLayout()
        self.setLayout(self.top_scroll_layout)
        self.top_scroll = Qt.QScrollArea()
        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
        self.top_scroll_layout.addWidget(self.top_scroll)
        self.top_scroll.setWidgetResizable(True)
        self.top_widget = Qt.QWidget()
        self.top_scroll.setWidget(self.top_widget)
        self.top_layout = Qt.QVBoxLayout(self.top_widget)
        self.top_grid_layout = Qt.QGridLayout()
        self.top_layout.addLayout(self.top_grid_layout)

        self.settings = Qt.QSettings("GNU Radio", "relay")
        self.restoreGeometry(self.settings.value("geometry").toByteArray())


        ##################################################
        # Parameters
        ##################################################
        self.freq = freq

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 20e6
        self.rx_gain = rx_gain = 20
        self.rx_freq = rx_freq = 98100000
        self.rx_digital_gain = rx_digital_gain = 1
        self.master_clk_rate = master_clk_rate = 200e6
        self.device3 = device3 = ettus.device3(uhd.device_addr_t( ",".join(('type=x300', "")) ))

        ##################################################
        # Blocks
        ##################################################
        self.uhd_rfnoc_streamer_radio_1 = ettus.rfnoc_radio(
            self.device3,
            uhd.stream_args( # Tx Stream Args
                cpu_format="fc32",
                otw_format="sc16",
        	args='',
            ),
            uhd.stream_args( # Rx Stream Args
                cpu_format="fc32",
                otw_format="sc16",
                args="", # empty
            ),
            0, -1
        )
        self.uhd_rfnoc_streamer_radio_1.set_rate(master_clk_rate)
        for i in xrange(1):
            self.uhd_rfnoc_streamer_radio_1.set_tx_freq(2412e6, i)
            self.uhd_rfnoc_streamer_radio_1.set_tx_gain(20, i)
            self.uhd_rfnoc_streamer_radio_1.set_tx_dc_offset(True, i)

        self.uhd_rfnoc_streamer_radio_1.set_tx_antenna("TX/RX", 0)

        self.uhd_rfnoc_streamer_radio_1.set_clock_source("internal")
        self.uhd_rfnoc_streamer_radio_0 = ettus.rfnoc_radio(
            self.device3,
            uhd.stream_args( # Tx Stream Args
                cpu_format="fc32",
                otw_format="sc16",
                args="", # empty
            ),
            uhd.stream_args( # Rx Stream Args
                cpu_format="fc32",
                otw_format="sc16",
        	args='',
            ),
            0, -1
        )
        self.uhd_rfnoc_streamer_radio_0.set_rate(master_clk_rate)
        for i in xrange(1):
            self.uhd_rfnoc_streamer_radio_0.set_rx_freq(freq, i)
            self.uhd_rfnoc_streamer_radio_0.set_rx_gain(20, i)
            self.uhd_rfnoc_streamer_radio_0.set_rx_dc_offset(True, i)

        self.uhd_rfnoc_streamer_radio_0.set_rx_bandwidth(56e6, 0)

        self.uhd_rfnoc_streamer_radio_0.set_rx_antenna("RX2", 0)

        self.uhd_rfnoc_streamer_radio_0.set_clock_source("internal")
        self.uhd_rfnoc_streamer_duc_0 = ettus.rfnoc_generic(
            self.device3,
            uhd.stream_args( # TX Stream Args
                cpu_format="fc32", # TODO: This must be made an option
                otw_format="sc16",
                args="input_rate={},output_rate={},fullscale={},freq={}".format(samp_rate, master_clk_rate, 1.0, 5e6),
            ),
            uhd.stream_args( # RX Stream Args
                cpu_format="fc32", # TODO: This must be made an option
                otw_format="sc16",
                args="",
            ),
            "DUC", -1, -1,
        )
        self.uhd_rfnoc_streamer_dma_fifo_0 = ettus.rfnoc_generic(
            self.device3,
            uhd.stream_args( # TX Stream Args
                cpu_format="fc32",
                otw_format="sc16",
                args="gr_vlen={0},{1}".format(1, "" if 1 == 1 else "spp={0}".format(1)),
            ),
            uhd.stream_args( # RX Stream Args
                cpu_format="fc32",
                otw_format="sc16",
                args="gr_vlen={0},{1}".format(1, "" if 1 == 1 else "spp={0}".format(1)),
            ),
            "DmaFIFO", -1, -1,
        )
        self.uhd_rfnoc_streamer_dma_fifo_0.set_arg("base_addr",0,0)
        self.uhd_rfnoc_streamer_dma_fifo_0.set_arg("depth",33554432,0)
        self.uhd_rfnoc_streamer_dma_fifo_0.set_arg("base_addr",33554432,1)
        self.uhd_rfnoc_streamer_dma_fifo_0.set_arg("depth",33554432,1)
        self.uhd_rfnoc_streamer_ddc_0 = ettus.rfnoc_generic(
            self.device3,
            uhd.stream_args( # TX Stream Args
                cpu_format="fc32", # TODO: This must be made an option
                otw_format="sc16",
                channels=range(1),
                args="input_rate={},output_rate={},fullscale={},freq={}".format(master_clk_rate, samp_rate, 1.0, 0),
            ),
            uhd.stream_args( # RX Stream Args
                cpu_format="fc32", # TODO: This must be made an option
                otw_format="sc16",
                channels=range(1),
                args="",
            ),
            "DDC", -1, -1,
        )
        for chan in xrange(1):
            self.uhd_rfnoc_streamer_ddc_0.set_arg("input_rate", float(master_clk_rate), chan)
            self.uhd_rfnoc_streamer_ddc_0.set_arg("output_rate", float(samp_rate), chan)
            self.uhd_rfnoc_streamer_ddc_0.set_arg("fullscale", 1.0, chan)
            self.uhd_rfnoc_streamer_ddc_0.set_arg("freq", 0, chan)
        self._rx_gain_range = Range(0, 30, 1, 20, 200)
        self._rx_gain_win = RangeWidget(self._rx_gain_range, self.set_rx_gain, 'RX Gain', "counter_slider", float)
        self.top_layout.addWidget(self._rx_gain_win)
        self._rx_freq_range = Range(50000000, 6000000000, 1000000, 98100000, 200)
        self._rx_freq_win = RangeWidget(self._rx_freq_range, self.set_rx_freq, 'RX Frequency', "counter_slider", float)
        self.top_layout.addWidget(self._rx_freq_win)
        self._rx_digital_gain_range = Range(0, 30, 1, 1, 200)
        self._rx_digital_gain_win = RangeWidget(self._rx_digital_gain_range, self.set_rx_digital_gain, 'RX Digital Gain', "counter_slider", float)
        self.top_layout.addWidget(self._rx_digital_gain_win)



        ##################################################
        # Connections
        ##################################################
        self.device3.connect(self.uhd_rfnoc_streamer_ddc_0.get_block_id(), 0, self.uhd_rfnoc_streamer_dma_fifo_0.get_block_id(), 0)
        self.device3.connect(self.uhd_rfnoc_streamer_dma_fifo_0.get_block_id(), 0, self.uhd_rfnoc_streamer_duc_0.get_block_id(), 0)
        self.device3.connect(self.uhd_rfnoc_streamer_duc_0.get_block_id(), 0, self.uhd_rfnoc_streamer_radio_1.get_block_id(), 0)
        self.device3.connect(self.uhd_rfnoc_streamer_radio_0.get_block_id(), 0, self.uhd_rfnoc_streamer_ddc_0.get_block_id(), 0)

	#ADD THESE TO HANDLE ACTIVE STREAMER
	self.uhd_rfnoc_streamer_radio_0.set_rx_streamer(True, 0)
	stream_cmd = uhd.stream_cmd_t(uhd.stream_cmd_t.STREAM_MODE_START_CONTINUOUS)
	self.uhd_rfnoc_streamer_radio_0.issue_stream_cmd(stream_cmd)

    def closeEvent(self, event):
        self.settings = Qt.QSettings("GNU Radio", "relay")
        self.settings.setValue("geometry", self.saveGeometry())
        event.accept()

    def get_freq(self):
        return self.freq

    def set_freq(self, freq):
        self.freq = freq
        for i in xrange(1):
            self.uhd_rfnoc_streamer_radio_0.set_rx_freq(self.freq, i)

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.uhd_rfnoc_streamer_duc_0.set_arg("input_rate", float(self.samp_rate))
        for i in xrange(1):
            self.uhd_rfnoc_streamer_ddc_0.set_arg("output_rate", float(self.samp_rate), i)

    def get_rx_gain(self):
        return self.rx_gain

    def set_rx_gain(self, rx_gain):
        self.rx_gain = rx_gain

    def get_rx_freq(self):
        return self.rx_freq

    def set_rx_freq(self, rx_freq):
        self.rx_freq = rx_freq

    def get_rx_digital_gain(self):
        return self.rx_digital_gain

    def set_rx_digital_gain(self, rx_digital_gain):
        self.rx_digital_gain = rx_digital_gain

    def get_master_clk_rate(self):
        return self.master_clk_rate

    def set_master_clk_rate(self, master_clk_rate):
        self.master_clk_rate = master_clk_rate
        self.uhd_rfnoc_streamer_radio_1.set_rate(self.master_clk_rate)
        self.uhd_rfnoc_streamer_radio_0.set_rate(self.master_clk_rate)
        self.uhd_rfnoc_streamer_duc_0.set_arg("output_rate", float(self.master_clk_rate))
        for i in xrange(1):
            self.uhd_rfnoc_streamer_ddc_0.set_arg("input_rate", float(self.master_clk_rate), i)

    def get_device3(self):
        return self.device3

    def set_device3(self, device3):
        self.device3 = device3


def argument_parser():
    parser = OptionParser(usage="%prog: [options]", option_class=eng_option)
    parser.add_option(
        "", "--freq", dest="freq", type="eng_float", default=eng_notation.num_to_str(2412e6),
        help="Set freq [default=%default]")
    return parser


def main(top_block_cls=relay, options=None):
    if options is None:
        options, _ = argument_parser().parse_args()

    from distutils.version import StrictVersion
    if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
        Qt.QApplication.setGraphicsSystem(style)
    qapp = Qt.QApplication(sys.argv)

    tb = top_block_cls(freq=options.freq)
    tb.start()
    tb.show()

    def quitting():
        tb.stop()
        tb.wait()
    qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting)
    qapp.exec_()


if __name__ == '__main__':
    main()
	/* -*- c++ -*- */
/* Copyright 2015 Ettus Research
 * 
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 * 
 * gr-ettus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with gr-ettus; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */


#ifndef INCLUDED_ETTUS_RFNOC_RADIO_H
#define INCLUDED_ETTUS_RFNOC_RADIO_H

#include <ettus/api.h>
#include <ettus/device3.h>
#include <ettus/rfnoc_block.h>
#include <uhd/stream.hpp>
#include <uhd/types/ranges.hpp>

namespace gr {
  namespace ettus {

    /*!
     * \brief <+description of block+>
     * \ingroup ettus
     *
     */
    class ETTUS_API rfnoc_radio : virtual public rfnoc_block
    {
     public:
      typedef boost::shared_ptr<rfnoc_radio> sptr;

      /*!
       * \brief Return a shared_ptr to a new instance of ettus::rfnoc_radio.
       *
       * To avoid accidental use of raw pointers, ettus::rfnoc_radio's
       * constructor is in a private implementation
       * class. ettus::rfnoc_radio::make is the public interface for
       * creating new instances.
       */
      static sptr make(
          const device3::sptr &dev,
          const ::uhd::stream_args_t &tx_stream_args,
          const ::uhd::stream_args_t &rx_stream_args,
          const int radio_select,
          const int device_select=-1
      );

      virtual void set_rate(const double rate) = 0;
      virtual void set_tx_freq(const double freq, const size_t chan=0) = 0;
      virtual void set_rx_freq(const double freq, const size_t chan=0) = 0;
      virtual void set_tx_gain(const double gain, const size_t chan=0) = 0;
      virtual void set_rx_gain(const double gain, const size_t chan=0) = 0;
      virtual void set_tx_antenna(const std::string &ant, const size_t chan=0) = 0;
      virtual void set_rx_antenna(const std::string &ant, const size_t chan=0) = 0;
      virtual void set_tx_dc_offset(bool enable, const size_t chan=0) = 0;
      virtual void set_tx_dc_offset(const std::complex< double > &offset, const size_t chan=0) = 0;
      virtual void set_rx_dc_offset(bool enable, const size_t chan=0) = 0;
      virtual void set_rx_dc_offset(const std::complex< double > &offset, const size_t chan=0) = 0;
      virtual void set_rx_bandwidth(const double bandwidth, const size_t chan=0) = 0;

      // ADD THIS TO ENABLE ACTIVE STREAMER https://corvid.io/2017/04/22/stupid-rfnoc-tricks-loopback/
      virtual void set_tx_streamer(bool active, const size_t port) = 0;
      virtual void set_rx_streamer(bool active, const size_t port) = 0;
      virtual void issue_stream_cmd(const uhd::stream_cmd_t &cmd, const size_t chan=0) = 0;

      virtual double get_rate() = 0;
      virtual double get_tx_freq(const size_t chan=0) = 0;
      virtual double get_rx_freq(const size_t chan=0) = 0;
      virtual double get_tx_gain(const size_t chan=0) = 0;
      virtual double get_rx_gain(const size_t chan=0) = 0;
      virtual std::string get_tx_antenna(const size_t chan=0) = 0;
      virtual std::string get_rx_antenna(const size_t chan=0) = 0;

      virtual std::vector<std::string> get_rx_lo_names(const size_t chan=0) = 0;
      virtual std::vector<std::string> get_rx_lo_sources(const std::string &name, const size_t chan=0) = 0;
      virtual uhd::freq_range_t get_rx_lo_freq_range(const std::string &name, const size_t chan=0) = 0;

      virtual void set_rx_lo_source(const std::string &src, const std::string &name, const size_t chan=0) = 0;
      virtual const std::string get_rx_lo_source(const std::string &name, const size_t chan=0) = 0;

      virtual void set_rx_lo_export_enabled(bool enabled, const std::string &name, const size_t chan=0) = 0;
      virtual bool get_rx_lo_export_enabled(const std::string &name, const size_t chan=0) = 0;

      virtual double set_rx_lo_freq(double freq, const std::string &name, const size_t chan=0) = 0;
      virtual double get_rx_lo_freq(const std::string &name, const size_t chan=0) = 0;

      virtual void set_clock_source(const std::string &source) = 0;
      virtual std::string get_clock_source() = 0;
      
      virtual std::vector<std::string> get_gpio_banks() const = 0;
      virtual void set_gpio_attr(
          const std::string &bank,
          const std::string &attr,
          const uint32_t value,
          const uint32_t mask
      ) = 0;
      virtual uint32_t get_gpio_attr(const std::string &bank, const std::string &attr) = 0;

      virtual void set_time_next_pps(const uhd::time_spec_t &spec) = 0;
      virtual uhd::time_spec_t get_time_now(void) = 0;
      virtual uhd::time_spec_t get_time_last_pps(void) = 0;
      virtual void set_command_time(const uhd::time_spec_t &time, const size_t chan=0) = 0;
      virtual void clear_command_time(const size_t chan=0) = 0;
    };

  } // namespace ettus
} // namespace gr

#endif /* INCLUDED_ETTUS_RFNOC_RADIO_H */

/* -*- c++ -*- */
/* Copyright 2015 Ettus Research
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * gr-ettus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gr-ettus; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnuradio/io_signature.h>
#include "rfnoc_radio_impl.h"

namespace gr {
  namespace ettus {

    rfnoc_radio::sptr
    rfnoc_radio::make(
          const device3::sptr &dev,
          const ::uhd::stream_args_t &tx_stream_args,
          const ::uhd::stream_args_t &rx_stream_args,
          const int radio_select,
          const int device_select
    ) {
      return gnuradio::get_initial_sptr(
          new rfnoc_radio_impl(dev, tx_stream_args, rx_stream_args, radio_select, device_select)
      );
    }

    rfnoc_radio_impl::rfnoc_radio_impl(
        const device3::sptr &dev,
        const ::uhd::stream_args_t &tx_stream_args,
        const ::uhd::stream_args_t &rx_stream_args,
        const int radio_select,
        const int device_select
    ) : rfnoc_block("rfnoc_radio"),
        rfnoc_block_impl(
            dev,
            rfnoc_block_impl::make_block_id("Radio", radio_select, device_select),
            tx_stream_args, rx_stream_args
        )
    {
      _radio_ctrl = get_block_ctrl_throw< ::uhd::rfnoc::radio_ctrl >();
    }

    rfnoc_radio_impl::~rfnoc_radio_impl()
    {
      /* nop */
    }

    double rfnoc_radio_impl::get_rate()
    {
      return _radio_ctrl->get_rate();
    }

    void rfnoc_radio_impl::set_rate(const double rate)
    {
      _radio_ctrl->set_rate(rate);
    }

    double rfnoc_radio_impl::get_tx_freq(const size_t chan)
    {
      return _radio_ctrl->get_tx_frequency(chan);
    }

    void rfnoc_radio_impl::set_tx_freq(const double freq, const size_t chan)
    {
      _radio_ctrl->set_tx_frequency(freq, chan);
    }

    double rfnoc_radio_impl::get_rx_freq(const size_t chan)
    {
      return _radio_ctrl->get_rx_frequency(chan);
    }

    void rfnoc_radio_impl::set_rx_freq(const double freq, const size_t chan)
    {
      _radio_ctrl->set_rx_frequency(freq, chan);
    }

    double rfnoc_radio_impl::get_tx_gain(const size_t chan)
    {
      return _radio_ctrl->get_tx_gain(chan);
    }

    void rfnoc_radio_impl::set_tx_gain(const double gain, const size_t chan)
    {
      _radio_ctrl->set_tx_gain(gain, chan);
    }

    double rfnoc_radio_impl::get_rx_gain(const size_t chan)
    {
      return _radio_ctrl->get_rx_gain(chan);
    }

    void rfnoc_radio_impl::set_rx_gain(const double gain, const size_t chan)
    {
      _radio_ctrl->set_rx_gain(gain, chan);
    }

    double rfnoc_radio_impl::get_rx_bandwidth(const size_t chan)
    {
      return _radio_ctrl->get_rx_bandwidth(chan);
    }

    void rfnoc_radio_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
    {
      _radio_ctrl->set_rx_bandwidth(bandwidth, chan);
    }

    std::string rfnoc_radio_impl::get_tx_antenna(const size_t chan)
    {
      return _radio_ctrl->get_tx_antenna(chan);
    }

    void rfnoc_radio_impl::set_tx_antenna(const std::string &ant, const size_t chan)
    {
      _radio_ctrl->set_tx_antenna(ant, chan);
    }

    std::string rfnoc_radio_impl::get_rx_antenna(const size_t chan)
    {
      return _radio_ctrl->get_rx_antenna(chan);
    }

    void rfnoc_radio_impl::set_rx_antenna(const std::string &ant, const size_t chan)
    {
      _radio_ctrl->set_rx_antenna(ant, chan);
    }

    //ADD THESE TO ENABLE ACTIVE STREAMING https://corvid.io/2017/04/22/stupid-rfnoc-tricks-loopback/

    void rfnoc_radio_impl::set_tx_streamer(bool active, const size_t port)
    {
      _radio_ctrl->set_tx_streamer(active, port);
    }

    void rfnoc_radio_impl::set_rx_streamer(bool active, const size_t port)
    {
      _radio_ctrl->set_rx_streamer(active, port);
    }

    void rfnoc_radio_impl::issue_stream_cmd(const uhd::stream_cmd_t &cmd, const size_t chan)
    {
      _radio_ctrl->issue_stream_cmd(cmd, chan);
    }

    // FIXME everything down from here needs to be mapped on to the block API
    void rfnoc_radio_impl::set_tx_dc_offset(bool enable, const size_t chan)
    {
      //get_device()->set_tx_dc_offset(enable, chan);
    }

    void rfnoc_radio_impl::set_tx_dc_offset(const std::complex< double > &offset, const size_t chan)
    {
      //get_device()->set_tx_dc_offset(offset, chan);
    }

    void rfnoc_radio_impl::set_rx_dc_offset(bool enable, const size_t chan)
    {
      //get_device()->set_rx_dc_offset(enable, chan);
    }

    void rfnoc_radio_impl::set_rx_dc_offset(const std::complex< double > &offset, const size_t chan)
    {
      //get_device()->set_rx_dc_offset(offset, chan);
    }

    std::vector<std::string> rfnoc_radio_impl::get_rx_lo_names(const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_names(chan);
    }

    std::vector<std::string> rfnoc_radio_impl::get_rx_lo_sources(const std::string &name, const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_sources(name, chan);
    }

    uhd::freq_range_t rfnoc_radio_impl::get_rx_lo_freq_range(const std::string &name, const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_freq_range(name, chan);
    }

    void rfnoc_radio_impl::set_rx_lo_source(const std::string &src, const std::string &name, const size_t chan)
    {
        _radio_ctrl->set_rx_lo_source(src, name, chan);
    }

    const std::string rfnoc_radio_impl::get_rx_lo_source(const std::string &name, const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_source(name, chan);
    }

    void rfnoc_radio_impl::set_rx_lo_export_enabled(bool enabled, const std::string &name, const size_t chan)
    {
        _radio_ctrl->set_rx_lo_export_enabled(enabled, "all", chan);
    }

    bool rfnoc_radio_impl::get_rx_lo_export_enabled(const std::string &name, const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_export_enabled(name, chan);
    }

    double rfnoc_radio_impl::set_rx_lo_freq(double freq, const std::string &name, const size_t chan)
    {
        return _radio_ctrl->set_rx_lo_freq(freq, name, chan);
    }

    double rfnoc_radio_impl::get_rx_lo_freq(const std::string &name, const size_t chan)
    {
        return _radio_ctrl->get_rx_lo_freq(name, chan);
    }

    void rfnoc_radio_impl::set_clock_source(const std::string &source)
    {
      _radio_ctrl->set_clock_source(source);
    }
    
    std::string rfnoc_radio_impl::get_clock_source()
    {
      return _radio_ctrl->get_clock_source();
    }

    std::vector<std::string> rfnoc_radio_impl::get_gpio_banks() const
    {
      return _radio_ctrl->get_gpio_banks();
    }

    void rfnoc_radio_impl::set_gpio_attr(
        const std::string &bank,
        const std::string &attr,
        const uint32_t value,
        const uint32_t mask
    ) {
      _radio_ctrl->set_gpio_attr(bank, attr, value, mask);
    }

    uint32_t rfnoc_radio_impl::get_gpio_attr(const std::string &bank, const std::string &attr)
    {
      return _radio_ctrl->get_gpio_attr(bank, attr);
    }

    void rfnoc_radio_impl::set_time_next_pps(const uhd::time_spec_t &spec)
    {
      _radio_ctrl->set_time_next_pps(spec);
    }

    uhd::time_spec_t rfnoc_radio_impl::get_time_now(void)
    {
      return _radio_ctrl->get_time_now();
    }

    uhd::time_spec_t rfnoc_radio_impl::get_time_last_pps(void)
    {
      return _radio_ctrl->get_time_last_pps();
    }

    uhd::time_spec_t rfnoc_radio_impl::get_command_time(const size_t chan)
    {
      return _radio_ctrl->get_command_time(chan);
    }

    void rfnoc_radio_impl::set_command_time(const uhd::time_spec_t &time, const size_t chan)
    {
      _radio_ctrl->set_command_time(time, chan);
    }

    void rfnoc_radio_impl::clear_command_time(const size_t chan)
    {
      _radio_ctrl->clear_command_time(chan);
    }

    void rfnoc_radio_impl::set_command_tick_rate(const double tick_rate, const size_t chan)
    {
      _radio_ctrl->set_command_tick_rate(chan);
    }


  } /* namespace ettus */
} /* namespace gr */

/* -*- c++ -*- */
/* Copyright 2015 Ettus Research
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * gr-ettus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gr-ettus; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef INCLUDED_ETTUS_RFNOC_RADIO_IMPL_H
#define INCLUDED_ETTUS_RFNOC_RADIO_IMPL_H

#include <ettus/rfnoc_radio.h>
#include <ettus/rfnoc_block_impl.h>
#include <uhd/rfnoc/radio_ctrl.hpp>
#include <uhd/types/ranges.hpp>

namespace gr {
  namespace ettus {

    class rfnoc_radio_impl : public rfnoc_radio, public rfnoc_block_impl
    {
     public:
      rfnoc_radio_impl(
            const device3::sptr &dev,
            const ::uhd::stream_args_t &tx_stream_args,
            const ::uhd::stream_args_t &rx_stream_args,
            const int radio_select,
            const int device_select
      );
      ~rfnoc_radio_impl();

      double get_rate();
      double get_tx_freq(const size_t chan);
      double get_rx_freq(const size_t chan);
      double get_tx_gain(const size_t chan);
      double get_rx_gain(const size_t chan);
      double get_rx_bandwidth(const size_t chan);
      std::string get_tx_antenna(const size_t chan);
      std::string get_rx_antenna(const size_t chan);

      void set_rate(const double rate);
      void set_tx_freq(const double freq, const size_t chan);
      void set_rx_freq(const double freq, const size_t chan);
      void set_tx_gain(const double gain, const size_t chan);
      void set_rx_gain(const double gain, const size_t chan);
      void set_rx_bandwidth(const double bandwidth, const size_t chan);
      void set_tx_antenna(const std::string &ant, const size_t chan);
      void set_rx_antenna(const std::string &ant, const size_t chan);
      void set_tx_dc_offset(bool enable, const size_t chan);
      void set_tx_dc_offset(const std::complex< double > &offset, const size_t chan);
      void set_rx_dc_offset(bool enable, const size_t chan);
      void set_rx_dc_offset(const std::complex< double > &offset, const size_t chan);

      // ADD THESE TO ENABLE ACTIVE STREAMING https://corvid.io/2017/04/22/stupid-rfnoc-tricks-loopback/
      void set_tx_streamer(bool active, const size_t port);
      void set_rx_streamer(bool active, const size_t port);
      void issue_stream_cmd(const uhd::stream_cmd_t &cmd, const size_t chan=0);

      std::vector<std::string> get_rx_lo_names(const size_t chan);
      std::vector<std::string> get_rx_lo_sources(const std::string &name, const size_t chan);
      uhd::freq_range_t get_rx_lo_freq_range(const std::string &name, const size_t chan);

      void set_rx_lo_source(const std::string &src, const std::string &name, const size_t chan);
      const std::string get_rx_lo_source(const std::string &name, const size_t chan);

      void set_rx_lo_export_enabled(bool enabled, const std::string &name, const size_t chan);
      bool get_rx_lo_export_enabled(const std::string &name, const size_t chan);

      double set_rx_lo_freq(double freq, const std::string &name, const size_t chan);
      double get_rx_lo_freq(const std::string &name, const size_t chan);

      void set_clock_source(const std::string &source);
      std::string get_clock_source();
      
      std::vector<std::string> get_gpio_banks() const;
      void set_gpio_attr(
          const std::string &bank,
          const std::string &attr,
          const uint32_t value,
          const uint32_t mask
      );
      uint32_t get_gpio_attr(const std::string &bank, const std::string &attr);

      void set_command_tick_rate(const double tick_rate, const size_t chan);

      uhd::time_spec_t get_time_now(void);
      uhd::time_spec_t get_time_last_pps(void);
      void set_time_next_pps(const uhd::time_spec_t &spec);

      uhd::time_spec_t get_command_time(const size_t chan);
      void set_command_time(const uhd::time_spec_t &time, const size_t chan);
      void clear_command_time(const size_t chan);

     private:
      ::uhd::rfnoc::radio_ctrl::sptr _radio_ctrl;

      bool _start_time_set = false;
      uhd::time_spec_t _start_time;
    };

  } // namespace ettus
} // namespace gr

#endif /* INCLUDED_ETTUS_RFNOC_RADIO_IMPL_H */

//
// Copyright 2014-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include "wb_iface_adapter.hpp"
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <uhd/convert.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/types/ranges.hpp>
#include <uhd/types/direction.hpp>
#include "radio_ctrl_impl.hpp"
#include "../../transport/super_recv_packet_handler.hpp"

using namespace uhd;
using namespace uhd::rfnoc;

static const size_t BYTES_PER_SAMPLE = 4;
const std::string radio_ctrl::ALL_LOS = "all";

/****************************************************************************
 * Structors and init
 ***************************************************************************/
// Note: block_ctrl_base must be called before this, but has to be called by
// the derived class because of virtual inheritance
radio_ctrl_impl::radio_ctrl_impl() :
    _tick_rate(rfnoc::rate_node_ctrl::RATE_UNDEFINED)
{
    _num_rx_channels = get_output_ports().size();
    _num_tx_channels = get_input_ports().size();
    _continuous_streaming = std::vector<bool>(2, false);

    for (size_t i = 0; i < _num_rx_channels; i++) {
        _rx_streamer_active[i] = false;
    }
    for (size_t i = 0; i < _num_tx_channels; i++) {
        _tx_streamer_active[i] = false;
    }

    /////////////////////////////////////////////////////////////////////////
    // Setup peripherals
    /////////////////////////////////////////////////////////////////////////
    for (size_t i = 0; i < _get_num_radios(); i++) {
        _register_loopback_self_test(i);
        _perifs[i].ctrl = boost::make_shared<wb_iface_adapter>(
            // poke32 functor
            boost::bind(
                static_cast< void (block_ctrl_base::*)(const uint32_t, const uint32_t, const size_t) >(&block_ctrl_base::sr_write),
                this, _1, _2, i
            ),
            // peek32 functor
            boost::bind(
                static_cast< uint32_t (block_ctrl_base::*)(const uint32_t, const size_t) >(&block_ctrl_base::user_reg_read32),
                this,
                _1, i
            ),
            // peek64 functor
            boost::bind(
                static_cast< uint64_t (block_ctrl_base::*)(const uint32_t, const size_t) >(&block_ctrl_base::user_reg_read64),
                this,
                _1, i
            ),
            // get_time functor
            boost::bind(
                static_cast< time_spec_t (block_ctrl_base::*)(const size_t) >(&block_ctrl_base::get_command_time),
                this, i
            ),
            // set_time functor
            boost::bind(
                static_cast< void (block_ctrl_base::*)(const time_spec_t&, const size_t) >(&block_ctrl_base::set_command_time),
                this,
                _1, i
            )
        );

        // FIXME there's currently no way to set the underflow policy

        if (i == 0) {
            time_core_3000::readback_bases_type time64_rb_bases;
            time64_rb_bases.rb_now = regs::RB_TIME_NOW;
            time64_rb_bases.rb_pps = regs::RB_TIME_PPS;
            _time64 = time_core_3000::make(_perifs[i].ctrl, regs::sr_addr(regs::TIME), time64_rb_bases);
            this->set_time_now(0.0);
        }

        //Reset the RX control engine
        sr_write(regs::RX_CTRL_HALT, 1, i);
    }

    ////////////////////////////////////////////////////////////////////
    // Register the time keeper
    ////////////////////////////////////////////////////////////////////
    if (not _tree->exists(fs_path("time") / "now")) {
        _tree->create<time_spec_t>(fs_path("time") / "now")
            .set_publisher(boost::bind(&radio_ctrl_impl::get_time_now, this))
        ;
    }
    if (not _tree->exists(fs_path("time") / "pps")) {
        _tree->create<time_spec_t>(fs_path("time") / "pps")
            .set_publisher(boost::bind(&radio_ctrl_impl::get_time_last_pps, this))
        ;
    }
    if (not _tree->exists(fs_path("time") / "cmd")) {
        _tree->create<time_spec_t>(fs_path("time") / "cmd");
    }
    _tree->access<time_spec_t>(fs_path("time") / "now")
        .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::set_time_now, this, _1))
    ;
    _tree->access<time_spec_t>(fs_path("time") / "pps")
        .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::set_time_next_pps, this, _1))
    ;
    for (size_t i = 0; i < _get_num_radios(); i++) {
        _tree->access<time_spec_t>("time/cmd")
            .add_coerced_subscriber(boost::bind(&block_ctrl_base::set_command_tick_rate, this, boost::ref(_tick_rate), i))
            .add_coerced_subscriber(boost::bind(&block_ctrl_base::set_command_time, this, _1, i))
        ;
    }
    // spp gets created in the XML file
    _tree->access<int>(get_arg_path("spp") / "value")
        .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::_update_spp, this, _1))
        .update()
    ;
}

void radio_ctrl_impl::_register_loopback_self_test(size_t chan)
{
    size_t hash = size_t(time(NULL));
    for (size_t i = 0; i < 100; i++)
    {
        boost::hash_combine(hash, i);
        sr_write(regs::TEST, uint32_t(hash), chan);
        uint32_t result = user_reg_read32(regs::RB_TEST, chan);
        if (result != uint32_t(hash)) {
            UHD_LOGGER_ERROR("RFNOC RADIO") << "Register loopback test failed";
            UHD_LOGGER_ERROR("RFNOC RADIO") << boost::format("expected: %x result: %x") % uint32_t(hash) % result ;
            return; // exit on any failure
        }
    }
    UHD_LOGGER_INFO("RFNOC RADIO") << "Register loopback test passed";
}

/****************************************************************************
 * API calls
 ***************************************************************************/
double radio_ctrl_impl::set_rate(double rate)
{
    boost::mutex::scoped_lock lock(_mutex);
    _tick_rate = rate;
    _time64->set_tick_rate(_tick_rate);
    _time64->self_test();
    set_command_tick_rate(rate);
    return _tick_rate;
}

void radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan)
{
    _tx_antenna[chan] = ant;
}

void radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan)
{
    _rx_antenna[chan] = ant;
}

double radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan)
{
    return _tx_freq[chan] = freq;
}

double radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan)
{
    return _rx_freq[chan] = freq;
}

double radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan)
{
    return _tx_gain[chan] = gain;
}

double radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan)
{
    return _rx_gain[chan] = gain;
}

double radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
{
    return _rx_bandwidth[chan] = bandwidth;
}

void radio_ctrl_impl::set_time_sync(const uhd::time_spec_t &time)
{
    _time64->set_time_sync(time);
}

double radio_ctrl_impl::get_rate() const
{
    return _tick_rate;
}

std::string radio_ctrl_impl::get_tx_antenna(const size_t chan) /* const */
{
    return _tx_antenna[chan];
}

std::string radio_ctrl_impl::get_rx_antenna(const size_t chan) /* const */
{
    return _rx_antenna[chan];
}

double radio_ctrl_impl::get_tx_frequency(const size_t chan) /* const */
{
    return _tx_freq[chan];
}

double radio_ctrl_impl::get_rx_frequency(const size_t chan) /* const */
{
    return _rx_freq[chan];
}

double radio_ctrl_impl::get_tx_gain(const size_t chan) /* const */
{
    return _tx_gain[chan];
}

double radio_ctrl_impl::get_rx_gain(const size_t chan) /* const */
{
    return _rx_gain[chan];
}

double radio_ctrl_impl::get_rx_bandwidth(const size_t chan) /* const */
{
    return _rx_bandwidth[chan];
}
    
std::vector<std::string> radio_ctrl_impl::get_rx_lo_names(const size_t /* chan */)
{
    return std::vector<std::string>();
}

std::vector<std::string> radio_ctrl_impl::get_rx_lo_sources(const std::string & /* name */, const size_t /* chan */)
{
    return std::vector<std::string>();
}

freq_range_t radio_ctrl_impl::get_rx_lo_freq_range(const std::string & /* name */, const size_t /* chan */)
{
    return freq_range_t();
}

void radio_ctrl_impl::set_rx_lo_source(const std::string & /* src */, const std::string & /* name */, const size_t /* chan */)
{
    throw uhd::not_implemented_error("set_rx_lo_source is not supported on this radio");
}

const std::string radio_ctrl_impl::get_rx_lo_source(const std::string & /* name */, const size_t /* chan */)
{
    return "internal";
}

void radio_ctrl_impl::set_rx_lo_export_enabled(bool /* enabled */, const std::string & /* name */, const size_t /* chan */)
{
    throw uhd::not_implemented_error("set_rx_lo_export_enabled is not supported on this radio");
}

bool radio_ctrl_impl::get_rx_lo_export_enabled(const std::string & /* name */, const size_t /* chan */)
{
    return false; // Not exporting non-existant LOs
}

double radio_ctrl_impl::set_rx_lo_freq(double /* freq */, const std::string & /* name */, const size_t /* chan */)
{
    throw uhd::not_implemented_error("set_rx_lo_freq is not supported on this radio");
}

double radio_ctrl_impl::get_rx_lo_freq(const std::string & /* name */, const size_t /* chan */)
{
    return 0;
}

/***********************************************************************
 * RX Streamer-related methods (from source_block_ctrl_base)
 **********************************************************************/
//! Pass stream commands to the radio
void radio_ctrl_impl::issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t chan)
{
    boost::mutex::scoped_lock lock(_mutex);
    UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() " << chan << " " << char(stream_cmd.stream_mode) ;
    if (not _is_streamer_active(uhd::RX_DIRECTION, chan)) {
        UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() called on inactive channel. Skipping." ;
        return;
    }
    UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff);
    _continuous_streaming[chan] = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS);

    //setup the mode to instruction flags
    typedef boost::tuple<bool, bool, bool, bool> inst_t;
    static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of
                                                            //reload, chain, samps, stop
        (stream_cmd_t::STREAM_MODE_START_CONTINUOUS,   inst_t(true,  true,  false, false))
        (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS,    inst_t(false, false, false, true))
        (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true,  false))
        (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true,  true,  false))
    ;

    //setup the instruction flag values
    bool inst_reload, inst_chain, inst_samps, inst_stop;
    boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode];

    //calculate the word from flags and length
    uint32_t cmd_word = 0;
    cmd_word |= uint32_t((stream_cmd.stream_now)? 1 : 0) << 31;
    cmd_word |= uint32_t((inst_chain)?            1 : 0) << 30;
    cmd_word |= uint32_t((inst_reload)?           1 : 0) << 29;
    cmd_word |= uint32_t((inst_stop)?             1 : 0) << 28;
    cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1);

    //WRITE THIS TO DISABLE RX TIMESTAMPING https://corvid.io/2017/04/22/stupid-rfnoc-tricks-loopback/
    sr_write(regs::RX_CTRL_OUTPUT_FORMAT, boost::uint32_t(0), chan);

    //issue the stream command
    const uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(get_rate());
    sr_write(regs::RX_CTRL_CMD, cmd_word, chan);
    sr_write(regs::RX_CTRL_TIME_HI, uint32_t(ticks >> 32), chan);
    sr_write(regs::RX_CTRL_TIME_LO, uint32_t(ticks >> 0),  chan); //latches the command
}

std::vector<size_t> radio_ctrl_impl::get_active_rx_ports()
{
    std::vector<size_t> active_rx_ports;
    typedef std::map<size_t, bool> map_t;
    for(map_t::value_type &m:  _rx_streamer_active) {
        if (m.second) {
            active_rx_ports.push_back(m.first);
        }
    }
    return active_rx_ports;
}

/***********************************************************************
 * Radio controls (radio_ctrl specific)
 **********************************************************************/
void radio_ctrl_impl::set_rx_streamer(bool active, const size_t port)
{
    UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_rx_streamer() " << port << " -> " << active ;
    if (port > _num_rx_channels) {
        throw uhd::value_error(str(
            boost::format("[%s] Can't (un)register RX streamer on port %d (invalid port)")
            % unique_id() % port
        ));
    }
    _rx_streamer_active[port] = active;
    if (not check_radio_config()) {
        throw std::runtime_error(str(
            boost::format("[%s]: Invalid radio configuration.")
            % unique_id()
        ));
    }
}

void radio_ctrl_impl::set_tx_streamer(bool active, const size_t port)
{
    UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_tx_streamer() " << port << " -> " << active ;
    if (port > _num_tx_channels) {
        throw uhd::value_error(str(
            boost::format("[%s] Can't (un)register TX streamer on port %d (invalid port)")
            % unique_id() % port
        ));
    }
    _tx_streamer_active[port] = active;
    if (not check_radio_config()) {
        throw std::runtime_error(str(
            boost::format("[%s]: Invalid radio configuration.")
            % unique_id()
        ));
    }
}

// Subscribers to block args:
// TODO move to nocscript
void radio_ctrl_impl::_update_spp(int spp)
{
    boost::mutex::scoped_lock lock(_mutex);
    UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::_update_spp(): Requested spp: " << spp ;
    if (spp == 0) {
        spp = DEFAULT_PACKET_SIZE / BYTES_PER_SAMPLE;
    }
    UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::_update_spp(): Setting spp to: " << spp ;
    for (size_t i = 0; i < _num_rx_channels; i++) {
        sr_write(regs::RX_CTRL_MAXLEN, uint32_t(spp), i);
    }
}

void radio_ctrl_impl::set_time_now(const time_spec_t &time_spec)
{
    _time64->set_time_now(time_spec);
}

void radio_ctrl_impl::set_time_next_pps(const time_spec_t &time_spec)
{
    _time64->set_time_next_pps(time_spec);
}

time_spec_t radio_ctrl_impl::get_time_now()
{
    return _time64->get_time_now();
}

time_spec_t radio_ctrl_impl::get_time_last_pps()
{
    return _time64->get_time_last_pps();
}

void radio_ctrl_impl::set_time_source(const std::string &source)
{
    _tree->access<std::string>("time_source/value").set(source);
}

std::string radio_ctrl_impl::get_time_source()
{
    return _tree->access<std::string>("time_source/value").get();
}

std::vector<std::string> radio_ctrl_impl::get_time_sources()
{
    return _tree->access<std::vector<std::string>>("time_source/options").get();
}

void radio_ctrl_impl::set_clock_source(const std::string &source)
{
    _tree->access<std::string>("clock_source/value").set(source);
}

std::string radio_ctrl_impl::get_clock_source()
{
    return _tree->access<std::string>("clock_source/value").get();
}

std::vector<std::string> radio_ctrl_impl::get_clock_sources()
{
    return _tree->access<std::vector<std::string>>("clock_source/options").get();
}


std::vector<std::string> radio_ctrl_impl::get_gpio_banks() const
{
    return std::vector<std::string>();
}

void radio_ctrl_impl::set_gpio_attr(
        const std::string &,
        const std::string &,
        const uint32_t,
        const uint32_t
) {
    throw uhd::not_implemented_error("set_gpio_attr was not defined for this radio");
}

uint32_t radio_ctrl_impl::get_gpio_attr(const std::string &, const std::string &)
{
    throw uhd::not_implemented_error("get_gpio_attr was not defined for this radio");
}
//
// Copyright 2014-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP
#define INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP

#include "rx_vita_core_3000.hpp"
#include "tx_vita_core_3000.hpp"
#include "time_core_3000.hpp"
#include "gpio_atr_3000.hpp"
#include <uhd/rfnoc/radio_ctrl.hpp>
#include <uhd/types/direction.hpp>
#include <boost/thread.hpp>

//! Shorthand for radio block constructor
#define UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(CLASS_NAME) \
    CLASS_NAME##_impl(const make_args_t &make_args);

#define UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(CLASS_NAME) \
    CLASS_NAME##_impl::CLASS_NAME##_impl( \
        const make_args_t &make_args \
    ) : block_ctrl_base(make_args), radio_ctrl_impl()

namespace uhd {
    namespace rfnoc {

/*! \brief Provide access to a radio.
 *
 */
class radio_ctrl_impl : public radio_ctrl
{
public:
    /************************************************************************
     * Structors
     ***********************************************************************/
    radio_ctrl_impl();
    virtual ~radio_ctrl_impl() {};

    /************************************************************************
     * Public Radio API calls
     ***********************************************************************/
    virtual double set_rate(double rate);
    virtual void set_tx_antenna(const std::string &ant, const size_t chan);
    virtual void set_rx_antenna(const std::string &ant, const size_t chan);
    virtual double set_tx_frequency(const double freq, const size_t chan);
    virtual double set_rx_frequency(const double freq, const size_t chan);
    virtual double set_tx_gain(const double gain, const size_t chan);
    virtual double set_rx_gain(const double gain, const size_t chan);
    virtual double set_rx_bandwidth(const double bandwidth, const size_t chan);

    virtual double get_rate() const;
    virtual std::string get_tx_antenna(const size_t chan) /* const */;
    virtual std::string get_rx_antenna(const size_t chan) /* const */;
    virtual double get_tx_frequency(const size_t) /* const */;
    virtual double get_rx_frequency(const size_t) /* const */;
    virtual double get_tx_gain(const size_t) /* const */;
    virtual double get_rx_gain(const size_t) /* const */;
    virtual double get_rx_bandwidth(const size_t) /* const */;

    virtual std::vector<std::string> get_rx_lo_names(const size_t chan);
    virtual std::vector<std::string> get_rx_lo_sources(const std::string &name, const size_t chan);
    virtual freq_range_t get_rx_lo_freq_range(const std::string &name, const size_t chan);

    virtual void set_rx_lo_source(const std::string &src, const std::string &name, const size_t chan);
    virtual const std::string get_rx_lo_source(const std::string &name, const size_t chan);

    virtual void set_rx_lo_export_enabled(bool enabled, const std::string &name, const size_t chan);
    virtual bool get_rx_lo_export_enabled(const std::string &name, const size_t chan);

    virtual double set_rx_lo_freq(double freq, const std::string &name, const size_t chan);
    virtual double get_rx_lo_freq(const std::string &name, const size_t chan);

    void set_time_now(const time_spec_t &time_spec);
    void set_time_next_pps(const time_spec_t &time_spec);
    void set_time_sync(const uhd::time_spec_t &time);
    time_spec_t get_time_now();
    time_spec_t get_time_last_pps();
    virtual void set_time_source(const std::string &source);
    virtual std::string get_time_source();
    virtual std::vector<std::string> get_time_sources();
    virtual void set_clock_source(const std::string &source);
    virtual std::string get_clock_source();
    virtual std::vector<std::string> get_clock_sources();

    virtual std::vector<std::string> get_gpio_banks() const;
    virtual void set_gpio_attr(
            const std::string &bank,
            const std::string &attr,
            const uint32_t value,
            const uint32_t mask
    );
    virtual uint32_t get_gpio_attr(const std::string &bank, const std::string &attr);

    /***********************************************************************
     * Block control API calls
     **********************************************************************/
    void set_rx_streamer(bool active, const size_t port);
    void set_tx_streamer(bool active, const size_t port);

    void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t port);

    virtual double get_input_samp_rate(size_t /* port */) { return get_rate(); }
    virtual double get_output_samp_rate(size_t /* port */) { return get_rate(); }
    double _get_tick_rate() { return get_rate(); }

    std::vector<size_t> get_active_rx_ports();
    bool in_continuous_streaming_mode(const size_t chan) { return _continuous_streaming.at(chan); }
    void rx_ctrl_clear_cmds(const size_t port) { sr_write(regs::RX_CTRL_CLEAR_CMDS, 0, port); }

protected: // TODO see what's protected and what's private
    void _register_loopback_self_test(size_t chan);

    /***********************************************************************
     * Registers
     **********************************************************************/
    struct regs {
        static inline uint32_t sr_addr(const uint32_t offset)
        {
            return offset * 4;
        }

        static const uint32_t BASE       = 128;

        // defined in radio_core_regs.vh
        static const uint32_t TIME                 = 128; // time hi - 128, time lo - 129, ctrl - 130
        static const uint32_t CLEAR_CMDS           = 131; // Any write to this reg clears the command FIFO
        static const uint32_t LOOPBACK             = 132;
        static const uint32_t TEST                 = 133;
        static const uint32_t CODEC_IDLE           = 134;
        static const uint32_t TX_CTRL_ERROR_POLICY = 144;
        static const uint32_t RX_CTRL_CMD          = 152;
        static const uint32_t RX_CTRL_TIME_HI      = 153;
        static const uint32_t RX_CTRL_TIME_LO      = 154;
        static const uint32_t RX_CTRL_HALT         = 155;
        static const uint32_t RX_CTRL_MAXLEN       = 156;
        static const uint32_t RX_CTRL_CLEAR_CMDS   = 157;
        static const uint32_t RX_CTRL_OUTPUT_FORMAT= 158;   //ADD THIS TO DISABLE RX TIMESTAMP https://corvid.io/2017/04/22/stupid-rfnoc-tricks-loopback/
        static const uint32_t MISC_OUTS            = 160;
        static const uint32_t DACSYNC              = 161;
        static const uint32_t SPI                  = 168;
        static const uint32_t LEDS                 = 176;
        static const uint32_t FP_GPIO              = 184;
        static const uint32_t GPIO                 = 192;
        // NOTE: Upper 32 registers (224-255) are reserved for the output settings bus for use with
        //       device specific front end control

        // frontend control: needs rethinking TODO
        //static const uint32_t TX_FRONT             = BASE + 96;
        //static const uint32_t RX_FRONT             = BASE + 112;
        //static const uint32_t READBACK             = BASE + 127;

        static const uint32_t RB_TIME_NOW        = 0;
        static const uint32_t RB_TIME_PPS        = 1;
        static const uint32_t RB_TEST            = 2;
        static const uint32_t RB_CODEC_READBACK  = 3;
        static const uint32_t RB_RADIO_NUM       = 4;
        static const uint32_t RB_MISC_IO         = 16;
        static const uint32_t RB_SPI             = 17;
        static const uint32_t RB_LEDS            = 18;
        static const uint32_t RB_DB_GPIO         = 19;
        static const uint32_t RB_FP_GPIO         = 20;
    };

    /***********************************************************************
     * Block control API calls
     **********************************************************************/
    void _update_spp(int spp);

    inline size_t _get_num_radios() const {
       return  std::max(_num_rx_channels, _num_tx_channels);
    }

    inline timed_wb_iface::sptr _get_ctrl(size_t radio_num) const {
        return _perifs.at(radio_num).ctrl;
    }

    inline bool _is_streamer_active(uhd::direction_t dir, const size_t chan) const {
        switch (dir) {
        case uhd::TX_DIRECTION:
            return _tx_streamer_active.at(chan);
        case uhd::RX_DIRECTION:
            return _rx_streamer_active.at(chan);
        case uhd::DX_DIRECTION:
            return _rx_streamer_active.at(chan) and _tx_streamer_active.at(chan);
        default:
            return false;
        }
    }

    virtual bool check_radio_config() { return true; };

    //! There is always only one time core per radio
    time_core_3000::sptr         _time64;

    boost::mutex _mutex;

private:
    /************************************************************************
     * Peripherals
     ***********************************************************************/
    //! Stores pointers to all streaming-related radio cores
    struct radio_perifs_t
    {
        timed_wb_iface::sptr     ctrl;
    };
    std::map<size_t, radio_perifs_t> _perifs;

    size_t _num_tx_channels;
    size_t _num_rx_channels;

    // Cached values
    double _tick_rate;
    std::map<size_t, std::string> _tx_antenna;
    std::map<size_t, std::string> _rx_antenna;
    std::map<size_t, double> _tx_freq;
    std::map<size_t, double> _rx_freq;
    std::map<size_t, double> _tx_gain;
    std::map<size_t, double> _rx_gain;
    std::map<size_t, double> _rx_bandwidth;

    std::vector<bool> _continuous_streaming;
}; /* class radio_ctrl_impl */

}} /* namespace uhd::rfnoc */

#endif /* INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP */
// vim: sw=4 et:
_______________________________________________
USRP-users mailing list
USRP-users@lists.ettus.com
http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com

Reply via email to