Hi all,

when using 3 X310 USRPs (TwinRX, UHD 3.15 LTS) synchronized via the CDA-2990 
Octoclock (10MHz Ref and PPS distributed with length-matched cables), we are 
observing deviations of up to two samples (e.g. one board late, one early) 
between the individual devices.

We are seeing these offsets in the measured RF signals, but to have a minimal 
example to show the effect, I've included an example where the timing offsets 
can be observed using the GPIO pins. The screenshots show the GPIO signals 
attached to the ATR-Receive signals. With every run we get different offsets 
between the boards and sometimes all boards are in sync.


Is this a known issue?


Thanks,

David
//
// Copyright 2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/utils/thread.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <chrono>
#include <complex>
#include <iostream>
#include <thread>

// set up our masks, defining the pin numbers
#define RX_GPIO_MASK   (1 << 0)

int UHD_SAFE_MAIN(int argc, char* argv[])
{
    // variables to be set by po
    std::string args = "type=x300,addr0=192.168.11.2,addr1=192.168.12.2,addr2=192.168.13.2";
    std::string subdev = "A:0 A:1 B:0 B:1";
    std::string channel_list = "0,1,2,3,4,5,6,7,8,9,10,11";
    double seconds_in_future = 5;
    size_t total_num_samps = 100;
    double rate = 100e6;

    // create a usrp device
    std::cout << std::endl;
    std::cout << boost::format("Creating the usrp device with: %s...") % args
              << std::endl;
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);


    size_t devices = usrp->get_num_mboards();
    usrp->set_rx_subdev_spec(subdev); // sets across all mboards
    
    // set the rx sample rate (sets across all channels)
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) << std::endl;
    usrp->set_rx_rate(rate);
    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate() / 1e6)
              << std::endl
              << std::endl;


    usrp->set_clock_source("external");
    usrp->set_time_source("external");
    usrp->set_time_unknown_pps(uhd::time_spec_t(0.0));
    // wait for pps sync pulse
    std::this_thread::sleep_for(std::chrono::seconds(1));
    // Checks that all time registers are approximately close but not
    // exact, given that the RTT may varying for a control packet
    // transaction.
    std::cout << (usrp->get_time_synchronized() ? "PPS synchronized."
		  : "PPS: NO SYNC!")
	      << std::endl;

    // setup ATR RX GPIO for offset measurement on all boards
    for (size_t i = 0; i < devices; i++) {
	usrp->set_gpio_attr("FP0", "CTRL", RX_GPIO_MASK, RX_GPIO_MASK, i);
	usrp->set_gpio_attr("FP0", "DDR", RX_GPIO_MASK, RX_GPIO_MASK, i);
	usrp->set_gpio_attr("FP0", "ATR_0X", 0, RX_GPIO_MASK, i);
	usrp->set_gpio_attr("FP0", "ATR_RX", 1, RX_GPIO_MASK, i);
	usrp->set_gpio_attr("FP0", "ATR_TX", 0, RX_GPIO_MASK, i);
	usrp->set_gpio_attr("FP0", "ATR_XX", 0, RX_GPIO_MASK, i);
    }
    
    // detect which channels to use
    std::vector<std::string> channel_strings;
    std::vector<size_t> channel_nums;
    boost::split(channel_strings, channel_list, boost::is_any_of("\"',"));
    for (size_t ch = 0; ch < channel_strings.size(); ch++) {
        size_t chan = std::stoi(channel_strings[ch]);
        if (chan >= usrp->get_rx_num_channels()) {
            throw std::runtime_error("Invalid channel(s) specified.");
        } else
            channel_nums.push_back(std::stoi(channel_strings[ch]));
    }

    // create a receive streamer
    // linearly map channels (index0 = channel0, index1 = channel1, ...)
    uhd::stream_args_t stream_args("fc32"); // complex floats
    stream_args.channels             = channel_nums;
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);

    // setup streaming
    std::cout << std::endl;
    std::cout << boost::format("Begin streaming %u samples, %f seconds in the future...")
                     % total_num_samps % seconds_in_future
              << std::endl;
    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
    stream_cmd.num_samps  = total_num_samps;
    stream_cmd.stream_now = false;
    stream_cmd.time_spec  = uhd::time_spec_t(seconds_in_future);
    rx_stream->issue_stream_cmd(stream_cmd); // tells all channels to stream

    // meta-data will be filled in by recv()
    uhd::rx_metadata_t md;

    // allocate buffers to receive with samples (one buffer per channel)
    const size_t samps_per_buff = rx_stream->get_max_num_samps();
    std::vector<std::vector<std::complex<float>>> buffs(
        usrp->get_rx_num_channels(), std::vector<std::complex<float>>(samps_per_buff));

    // create a vector of pointers to point to each of the channel buffers
    std::vector<std::complex<float>*> buff_ptrs;
    for (size_t i = 0; i < buffs.size(); i++)
        buff_ptrs.push_back(&buffs[i].front());

    // the first call to recv() will block this many seconds before receiving
    double timeout = seconds_in_future + 0.1; // timeout (delay before receive + padding)

    size_t num_acc_samps = 0; // number of accumulated samples
    while (num_acc_samps < total_num_samps) {
        // receive a single packet
        size_t num_rx_samps = rx_stream->recv(buff_ptrs, samps_per_buff, md, timeout);

        // use a small timeout for subsequent packets
        timeout = 0.1;

        // handle the error code
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT)
            break;
        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
            throw std::runtime_error(
                str(boost::format("Receiver error %s") % md.strerror()));
        }
	std::cout << boost::format(
	    "Received packet: %u samples, %u full secs, %f frac secs")
	    % num_rx_samps % md.time_spec.get_full_secs()
	    % md.time_spec.get_frac_secs()
		  << std::endl;

        num_acc_samps += num_rx_samps;
    }

    if (num_acc_samps < total_num_samps)
        std::cerr << "Receive timeout before all samples received..." << std::endl;

    // finished
    std::cout << std::endl << "Done!" << std::endl << std::endl;

    return EXIT_SUCCESS;
}
_______________________________________________
USRP-users mailing list
USRP-users@lists.ettus.com
http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com

Reply via email to