Hey Wan,

On Thu, Oct 27, 2022 at 10:53 PM Wan Liu <wan....@ettus.com> wrote:

> Hello Brian,
>
> I set up my HW and SW, but I'm having some trouble reproducing after some
> initial playing around with UHD examples and gnuradio. I assume you have
> some UHD program that records RX to file and while you repeated make tune
> requests to ch 1, is this correct? To make sure we are on the same page,
> you provide a sample program that reproduces your steps for tuning and
> recording the IQ samples.
>
> You also mentioned " When trying to read the lo_locked sensor
> continuously, generating SPI traffic I presume going to the PLLs, I get
> clean spectrum." Can you also show in your sample program how to reproduce
> these steps
>
> Once I can get the same spectrogram as you, I'll also look into the "mute
> till lock detect" feature of the ADF5356.
>

Attached is a file which should reproduce it for you.  If it doesn't, let
me know off list and we can work from there.

It uses a scanning channel to go through a list of frequencies, and will
continuously read from the other channel.  When you hit ENTER, it will save
off a buffers worth of samples to disk with increasing filenames (i.e.
buffer-000.dat, buffer001.dat, etc.).  Quit the program by hitting Ctrl+C.

I set it up to save off 10ms of samples but it should be pretty easy to
change that to some arbitrary length.  I tried to make it so no overflows
would occur.

Source is attached and has a comment showing how I compiled on my system.
Source is also here, along with a picture of the birdies I am seeing:


https://drive.google.com/file/d/1Hrf15GzaO2hAlorUMkt4XT5hjv0J4tCc/view?usp=sharing

https://drive.google.com/file/d/1loVoixWgDPwo-QHPtNF6ZvHJMMS1kyVI/view?usp=sharing

You can also just generate SPI traffic, I imagine, if you only keep the
call to usrp->get_rx_sensor() in the main loop and nothing else.

Let me know if you are able to reproduce this issue or not.

Thanks,
Brian

>
// How to build:
//  $ export UHD_INCLUDE_PATH=/path/to/include
//  $ export UHD_LIBRARY_PATH=/path/to/lib
//  $ g++ -std=c++17 twinrx_issue.cpp -o twinrx_issue -I$UHD_INCLUDE_PATH 
-L$UHD_LIBRARY_PATH -lboost_program_options -lpthread -luhd

#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/utils/thread.hpp>
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <fstream>
#include <csignal>
#include <cerrno>
#include <poll.h>

namespace po = boost::program_options;

typedef std::vector<std::complex<float>> recv_buff_t;

// Global objects
static uhd::usrp::multi_usrp::sptr usrp;
static recv_buff_t buffer;
static recv_buff_t writing_buffer ;
static std::vector<double> rf_freqs;

static uhd::stream_cmd_t 
stream_start(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
static uhd::stream_cmd_t 
stream_stop(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);

bool done ;
bool save_samples ;

void sig_int_handler(int) {
    done = true ;
}

static size_t recv_chan ;
static size_t scan_chan ;

void check_user_input() {
    struct pollfd pfd = { STDIN_FILENO, POLLIN, 0 } ;
    int ret = 0 ;
    while(!done) {
        // Poll for user hitting enter to stop gracefully
        ret = poll(&pfd, 1, 100) ;
        if( ret == 1 ) {
            std::cin.ignore() ;
            std::cout << "Saving Samples!" << std::endl ;
            save_samples = true ;
        } else if( ret == -1 ) {
            std::cout << "Error: " << strerror(errno) << std::endl ;
        }
    }
}

static void write_buffer_to_file(const std::string &path) {
    std::cout << str(boost::format("Writing file %s... ") % path) << std::flush 
;
    std::ofstream ofile(path.c_str(), std::ios::binary) ;
    ofile.write((char*)writing_buffer.data(), sizeof(std::complex<float>) * 
writing_buffer.size()) ;
    std::cout << "done!" << std::endl ;
}

// This is a helper function for receiving samples from the USRP
void twinrx_recv()
{
    buffer = recv_buff_t(100e6/100) ;
    writing_buffer = recv_buff_t(buffer.size()) ;
    uhd::rx_metadata_t md;
    size_t file_number = 0 ;

    // Get an rx_streamer from the device
    uhd::stream_args_t stream_args("fc32", "sc16");
    stream_args.channels.push_back(recv_chan);
    auto rx_stream = usrp->get_rx_stream(stream_args);
    std::vector<std::thread> threads ;

    rx_stream->issue_stream_cmd(stream_start) ;

    // Repeatedly retrieve samples until the entire acquisition is received
    while (!done) {
        size_t num_recvd = rx_stream->recv(buffer.data(), buffer.size(), md, 
1.0);

        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
            std::cout << md.strerror() << std::endl;
            continue ;
        }

        if( save_samples ) {
            std::string fname = str(boost::format("buffer-%03d.dat") % 
file_number)  ;
            // Copy the buffer to the writing_buffer
            writing_buffer = buffer ;
            // Non-blocking writing
            threads.push_back(std::thread(write_buffer_to_file,fname)) ;
            save_samples = false ;
            file_number++ ;
        }
    }

    std::cout << std::endl << "Stopping the stream..." ;
    // Stop the stream
    rx_stream->issue_stream_cmd(stream_stop) ;

    // Read all the samples left in the pipeline
    do {
        size_t num_recv = rx_stream->recv(buffer.data(), buffer.size(), md, 
1.0) ;
    } while (md.error_code == uhd::rx_metadata_t::ERROR_CODE_NONE) ;
    // Rejoin all the file saving threads
    std::cout << "done!" << std::endl ;
    std::cout << "Cleaning up ... " ;
    for(auto &t : threads) {
        if( t.joinable() ) t.join() ;
    }
    std::cout << "done!" << std::endl ;
}

int UHD_SAFE_MAIN(int argc, char* argv[])
{
    // Program options
    std::string args, ant;
    double rate ;
    double start_freq, end_freq;
    done = false ;

    // Set up the program options
    po::options_description desc("Allowed options");

    // clang-format off
    desc.add_options()
            ("help", "Print this help message")
            ("args", po::value<std::string>(&args)->default_value(""), "UHD 
device args")
            ("chan", po::value<size_t>(&scan_chan)->default_value(1), "Scan 
Channel")
            ("ant", po::value<std::string>(&ant)->default_value("RX1"), 
"Antenna")
            ("start-freq", po::value<double>(&start_freq), "Start frequency 
(defaults to lowest valid frequency)")
            ("end-freq", po::value<double>(&end_freq), "End frequency (defaults 
to highest valid frequency)")
            ("rate", po::value<double>(&rate)->default_value(100e6), "Incoming 
sample rate")
            ;

    // clang-format on
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    // Receive channel is opposite of scanning channel
    if( scan_chan == 0 ) recv_chan = 1 ;
    else                 recv_chan = 0 ;

    if (vm.count("help")) {
        std::cout << "TwinRX Noise Issue - " << desc << std::endl;
        return EXIT_SUCCESS;
    }

    // Create a USRP device
    std::cout << boost::format("Creating the USRP device with args: 
\"%s\"...\n")
                     % args;
    usrp = uhd::usrp::multi_usrp::make(args);

    // Make sure the USRP is an X3xx with a TwinRX
    uhd::dict<std::string, std::string> info = 
usrp->get_usrp_rx_info(scan_chan);
    if (info.get("mboard_id").find("X3") == std::string::npos) {
        throw uhd::runtime_error(
            "This example can only be used with an X-Series motherboard.");
    }

    usrp->set_rx_subdev_spec(uhd::usrp::subdev_spec_t("B:0 B:1")) ;
    usrp->set_rx_antenna(ant, scan_chan) ;
    std::cout << boost::format("Setting sample rate to: %d\n") % rate;
    usrp->set_rx_rate(rate);
    std::cout << boost::format("Actual sample rate:     %d\n") % 
usrp->get_rx_rate();

    std::cout << "info rx_id: " << info.get("rx_id") << std::endl ;
    if (info.get("rx_id").find("TwinRX") == std::string::npos) {
        throw uhd::runtime_error(
            "This example can only be used with a TwinRX daughterboard.");
    }

    // Validate frequency range
    uhd::freq_range_t rx_freq_range = usrp->get_rx_freq_range(scan_chan);
    if (!vm.count("start-freq")) {
        start_freq = rx_freq_range.start();
    }
    if (!vm.count("end-freq")) {
        end_freq = rx_freq_range.stop();
    }
    if (start_freq < rx_freq_range.start() or end_freq > rx_freq_range.stop()) {
        throw uhd::runtime_error(
            (boost::format("Start and stop frequencies must be between %d and 
%d MHz")
                % (rx_freq_range.start() / 1e6) % (rx_freq_range.stop() / 1e6))
                .str());
    }
    if (start_freq > end_freq) {
        throw uhd::runtime_error("Start frequency must be less than end 
frequency.");
    }
    if ((end_freq - start_freq) > 0 and (end_freq - start_freq) < rate) {
        throw uhd::runtime_error("The sample rate must be less than the range 
between "
                                 "the start and end frequencies.");
    }

    for (double rx_freq = start_freq ; rx_freq < end_freq ; rx_freq += rate) {
        rf_freqs.push_back(rx_freq);
    }
    std::cout << boost::format("Total Hops: %d\n") % rf_freqs.size();

    // Hop frequencies and acquire bursts of samples at each until done sweeping
    std::cout << "Scanning..." << std::endl ;
    auto start_time = std::chrono::system_clock::now() ;
    auto stop_time = std::chrono::system_clock::now() ;

    std::signal(SIGINT, &sig_int_handler) ;
    std::cout << "Press Ctrl+C to stop, ENTER to save samples ..." << std::endl 
;

    auto user_input = std::thread(check_user_input) ;
    auto rx_stream = std::thread(twinrx_recv) ;

    while (!done) {
        auto locked = usrp->get_rx_sensor("lo_locked", scan_chan);

        start_time = std::chrono::system_clock::now() ;
        for (size_t i = 0; i < rf_freqs.size(); i++) {
            // Set the frequency
            usrp->set_rx_freq(rf_freqs[i], scan_chan) ;

            // Set maximum gain
            usrp->set_rx_gain(93, scan_chan) ;

            // Wait of the LO to be locked
            do {
                locked = usrp->get_rx_sensor("lo_locked", scan_chan) ;
            } while( !locked.to_bool() ) ;
        }
        stop_time = std::chrono::system_clock::now() ;
        auto us = 
std::chrono::duration_cast<std::chrono::microseconds>(stop_time - 
start_time).count() ;
        std::cout << boost::format("Total time: %7d microseconds  (%7.2f 
usec/hop)\r") % static_cast<float>(us) % (static_cast<float>(us) / 
rf_freqs.size()) ;
        std::cout << std::flush ;
    }

    user_input.join() ;
    rx_stream.join() ;

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

    usrp.reset();
    return EXIT_SUCCESS;
}
_______________________________________________
USRP-users mailing list -- usrp-users@lists.ettus.com
To unsubscribe send an email to usrp-users-le...@lists.ettus.com

Reply via email to