Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SC16 sample depth not fully utilised N310 #625

Open
asut6330 opened this issue Aug 23, 2022 · 0 comments
Open

SC16 sample depth not fully utilised N310 #625

asut6330 opened this issue Aug 23, 2022 · 0 comments

Comments

@asut6330
Copy link

asut6330 commented Aug 23, 2022

Issue Description

Currently streaming 4RX channels with a N310 (GNSS reflectometry application). I can record and then navigate with the produced IF data files. When analysing these files and creating histrograms I've noticed that the samples are only occupying a small part of the SC16 sample bit depth. Shown by the following image (histograms for I and Q):
usrp_samples

I've tried sampling a CW tone at -20dBm and the histogram is also limited to only +-40 compared to the full +-32767 of a signed short. Clearly this is saturating the ADC but why is it bounded by 40?
usrp_samples

Interested to see whether anyone else can replicate this behaviour. I've attached my code below. Let me know if I'm doing something silly. I have a hunch this is why I'm receiving low C/No values when navigating with the produced baseband IF files.

Setup Details

[UHD] linux; GNU C++ version 9.4.0; Boost_107100; UHD_4.3.0.0-15-g5333d3d1
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
    serial: 322C655
    addr: 192.168.20.2
    claimed: False
    fpga: HG
    mgmt_addr: 192.168.1.106
    mgmt_addr: 192.168.20.2
    name: ni-n3xx-322C655
    product: n310
    type: n3xx


./4rx_streamer --args "type=n3xx,mgmt_addr=192.168.1.106,addr=192.168.20.2,master_clock_rate=125e6,rx_gain_profile=manual" --rate 6.25e6 --nsamps 100000 --subdev "A:0 A:1 B:0 B:1" --channels "0,1,2,3"
#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 <boost/program_options.hpp>
#include <chrono>
#include <complex>
#include <fstream>
#include <iostream>
#include <thread>

namespace po = boost::program_options;
#define DBG_OUT(x) std::cerr << #x << " = " << x << std::endl

int UHD_SAFE_MAIN(int argc, char* argv[])
{
    // variables to be set by po
    std::string args, sync, subdev, channel_list, prefix;
    double seconds_in_future;
    size_t total_num_samps;
    double rate;

    // setup the program options
    po::options_description desc("Allowed options");
    // clang-format off
    desc.add_options()
        ("help", "help message")
        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")
        ("prefix", po::value<std::string>(&prefix)->default_value("usrp_samples_"), "name of the file to write binary samples to")
        ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to receive")
        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to receive")
        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples")
        ("sync", po::value<std::string>(&sync)->default_value("now"), "synchronization method: now, pps, mimo")
        ("subdev", po::value<std::string>(&subdev), "subdev spec (homogeneous across motherboards)")
        ("dilv", "specify to disable inner-loop verbose")
        ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)")
    ;
    // clang-format on
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    // print the help message
    if (vm.count("help")) {
        std::cout << boost::format("UHD RX Multi Samples %s") % desc << std::endl;
        std::cout
            << "    This is a demonstration of how to receive aligned data from multiple "
               "channels.\n"
               "    This example can receive from multiple DSPs, multiple motherboards, "
               "or both.\n"
               "    The MIMO cable or PPS can be used to synchronize the configuration. "
               "See --sync\n"
               "\n"
               "    Specify --subdev to select multiple channels per motherboard.\n"
               "      Ex: --subdev=\"0:A 0:B\" to get 2 channels on a Basic RX.\n"
               "\n"
               "    Specify --args to select multiple motherboards in a configuration.\n"
               "      Ex: --args=\"addr0=192.168.10.2, addr1=192.168.10.3\"\n"
            << std::endl;
        return ~0;
    }

    bool verbose = vm.count("dilv") == 0;

    // 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);

    // always select the subdevice first, the channel mapping affects the other settings
    if (vm.count("subdev"))
        usrp->set_rx_subdev_spec(subdev); // sets across all mboards

    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;

    // 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(0) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting device timestamp to 0...") << std::endl;
    if (sync == "now") {
        // This is not a true time lock, the devices will be off by a few RTT.
        // Rather, this is just to allow for demonstration of the code below.
        usrp->set_time_now(uhd::time_spec_t(0.0));
    } else if (sync == "pps") {
        usrp->set_time_source("external");
        usrp->set_time_unknown_pps(uhd::time_spec_t(0.0));
        std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for pps sync pulse
    } else if (sync == "mimo") {
        UHD_ASSERT_THROW(usrp->get_num_mboards() == 2);

        // make mboard 1 a slave over the MIMO Cable
        usrp->set_clock_source("mimo", 1);
        usrp->set_time_source("mimo", 1);

        // set time on the master (mboard 0)
        usrp->set_time_now(uhd::time_spec_t(0.0), 0);

        // sleep a bit while the slave locks its time to the master
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::ofstream outfile0;
    std::ofstream outfile1;
    std::ofstream outfile2;
    std::ofstream outfile3;

    outfile0.open(prefix + "ch_" + boost::lexical_cast<std::string>(0) + ".bin", std::ofstream::binary);
    outfile1.open(prefix + "ch_" + boost::lexical_cast<std::string>(1) + ".bin", std::ofstream::binary);
    outfile2.open(prefix + "ch_" + boost::lexical_cast<std::string>(2) + ".bin", std::ofstream::binary);
    outfile3.open(prefix + "ch_" + boost::lexical_cast<std::string>(3) + ".bin", std::ofstream::binary);

    // 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]));
            usrp->set_rx_antenna("RX2", ch);
        }
    }
    DBG_OUT(channel_nums.size());

    // set the center frequency
    for (size_t ch = 0; ch < channel_strings.size(); ch++) {
        if (ch <= 1) {
            printf("Setting RX Freq for channel %ld: %f MHz...\n", ch, (1575420000 / 1e6));
            uhd::tune_request_t tune_request(1575420000, 0);
            usrp->set_rx_freq(tune_request, ch);
        } else {
            printf("Setting RX Freq for channel %ld: %f MHz...\n", ch, (1227600000 / 1e6));
            uhd::tune_request_t tune_request(1227600000, 0);
            usrp->set_rx_freq(tune_request, ch);
        }
        printf("Actual RX Freq for channel %ld: %f MHz...\n", ch, (usrp->get_rx_freq(ch)/1e6));
    }

    std::vector<std::string> gain_names = usrp->get_rx_gain_names(0); 

    for (int i = 0; i<gain_names.size(); i++)
        std::cout << gain_names[i] << ' ';
    std::cout << std::endl;

    printf("RX bandwidth: %f Hz\n", usrp->get_rx_bandwidth(0));
    
    printf("Gain range start rfic: %f dB\n", usrp->get_rx_gain_range("rfic", 0).start());
    printf("Gain range end rfic: %f dB\n", usrp->get_rx_gain_range("rfic", 0).stop());
    usrp->set_rx_gain(0, "rfic", 0);
    printf("Gain rfic: %f dB\n", usrp->get_rx_gain("rfic", 0));

    printf("Gain range start dsa: %f dB\n", usrp->get_rx_gain_range("dsa", 0).start());
    printf("Gain range end dsa: %f dB\n", usrp->get_rx_gain_range("dsa", 0).stop());
    printf("Gain dsa: %f dB\n", usrp->get_rx_gain("dsa", 0));

    printf("Gain range start amp: %f dB\n", usrp->get_rx_gain_range("amp", 0).start());
    printf("Gain range end amp: %f dB\n", usrp->get_rx_gain_range("amp", 0).stop());
    usrp->set_rx_gain(10, "amp", 0);
    printf("Gain amp: %f dB\n", usrp->get_rx_gain("amp", 0));


    // size_t gain = 0;
    // // set the rf gain
    // for (size_t ch = 0; ch < channel_strings.size(); ch++) {
    //     //std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
    //     //usrp->set_rx_gain(gain, ch);
    //     std::cout << boost::format("Actual RX Gain: %f dB...")
    //                      % usrp->get_rx_gain(ch)
    //               << std::endl
    //               << std::endl;
    // }

    // create a receive streamer
    // linearly map channels (index0 = channel0, index1 = channel1, ...)
    uhd::stream_args_t stream_args("sc16","sc16"); // complex shorts
    // stream_args.args["peak"] = "0.1"; 
    // std::vector<size_t> channel_numbers {0,1};
    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();
    DBG_OUT(samps_per_buff);

    size_t recv_count    = 0;
    size_t channel_count = usrp->get_rx_num_channels();
    printf("Channel count: %ld, samples per buffer: %ld, recv count: %ld\n",
        channel_count,
        samps_per_buff,
        recv_count);

    typedef std::vector<std::vector<std::complex<short>>> MultiDeviceBufferType;
    MultiDeviceBufferType MultiDeviceBuffer(
        channel_count, std::vector<std::complex<short>>(samps_per_buff));

    typedef std::vector<std::complex<short>*> BufferPointerType;
    BufferPointerType DeviceBufferPtrs;

    for (size_t ch = 0; ch < channel_count; ch++){
        DeviceBufferPtrs.push_back(MultiDeviceBuffer.at(ch).data());
    }

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

    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(DeviceBufferPtrs,
                samps_per_buff,
                md,
                timeout);

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

        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
            std::cout << boost::format("Timeout while streaming") << std::endl;
            break;
        }
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
                std::cerr
                    << boost::format(
                           "Got an overflow indication. Please consider the following:\n"
                           "  Your write medium must sustain a rate of %fMB/s.\n"
                           "  Dropped samples will not be written to the file.\n"
                           "  Please modify this example for your purposes.\n"
                           "  This message will not appear again.\n")
                           % (usrp->get_rx_rate(0) * 4 / 1e6);
            continue;
        }
        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
            std::string error = str(boost::format("Receiver error: %s") % md.strerror());
                throw std::runtime_error(error);
        }

        if (verbose)
            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;

        outfile0.write((char*)DeviceBufferPtrs[0],
            sizeof(MultiDeviceBuffer.at(0).at(0)) * samps_per_buff);
        outfile1.write((char*)DeviceBufferPtrs[1], sizeof(MultiDeviceBuffer.at(1).at(0)) * samps_per_buff);
        outfile2.write((char*)DeviceBufferPtrs[2], sizeof(MultiDeviceBuffer.at(2).at(0)) * samps_per_buff);
        outfile3.write((char*)DeviceBufferPtrs[3], sizeof(MultiDeviceBuffer.at(3).at(0)) * samps_per_buff);

        printf("%ld, %ld, %ld, %ld\n",
            recv_count,
            sizeof(MultiDeviceBuffer.at(0).at(0)),
            num_acc_samps,
            samps_per_buff * sizeof(short) * 2);
        recv_count++;
    }

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

    outfile0.close();
    outfile1.close();
    outfile2.close();
    outfile3.close();

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

    return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants