<div dir="ltr">Hello forum,<div><br></div><div>I have a project for which it is crucial to have minimal or none time delay between the transmitter and receiver. For my purposes I utilize a B210 and use only the TX/RX for transmit and RX for receive.</div><div><br></div><div>I have developed (following the advice of <span style="font-family:arial,sans-serif;font-size:13px;white-space:nowrap">Stephen Graham</span>) the following code that attaches a the transmit function to a thread in order to execute tx and rx concurrently (tx is blocking). For now, the TX/RX and RX channels are connected with coax cable to avoid random phase introduction from the wireless channel. I transmit and receive samples concurrently and calculate the time difference using tx_metadata.time_spec and rx_metadata.time_spec. However, when I apply a shift at the samples that I receive according to this time delay (N_samples_to_shift = F_sampling * T_delay) the signals still wont align and I still get a random time delay everytime I execute the UHD code. </div><div><br></div><div>Could someone inform me if what I am trying to do here is even remotely possible with the B210? Should I use another platform or should I implement some kind of workaround? </div><div><br></div><div>Thank you in advance</div><div><br></div><div>CODE:</div><div><div>//Author Eleftherios(Lef) Kampianakis</div><div><br></div><div>//V0: Tx Rx wokrking but not synched</div><div>//V1: effort for synching (failed)</div><div>//V2: used threads to start tx asynch</div><div><br></div><div>#include <uhd/types/tune_request.hpp></div><div>#include <uhd/utils/thread_priority.hpp></div><div>#include <uhd/utils/safe_main.hpp></div><div>#include <uhd/usrp/multi_usrp.hpp></div><div>#include <boost/program_options.hpp></div><div>#include <boost/format.hpp></div><div>#include <boost/thread.hpp></div><div>#include <iostream></div><div>#include <fstream></div><div>#include <complex></div><div>#include <csignal></div><div><br></div><div>//------------------MACROS------------------</div><div>#define DEB(x) std::cout << "DEB:" << x << std::endl</div><div><br></div><div><br></div><div>//------------------SETUP------------------</div><div><br></div><div>//Assign the namespace po from boost::program_options</div><div>//This is done to work with the terminal inputs cause boost::program_options</div><div>// Question: why use namespace instead of .hpp</div><div>namespace po = boost::program_options;</div><div><br></div><div><br></div><div><br></div><div>#define CPU_FORMAT "fc32"</div><div>#define WIRE_FORMAT "sc16"</div><div>#define REF_CLOCK "internal"</div><div>#define SAMP_RATE 200e3</div><div>#define CENT_FREQ 915e6</div><div>#define TX_GAIN  10 //dB</div><div>#define RX_GAIN 10   //dB</div><div>#define SAMPLES_PER_BUFFER 1000</div><div>#define ARGS ""</div><div>#define TX_FILENAME "/Users/kampianakis/Desktop/SGCC/Demos/UHD/uhd/host/build/examples/data/sin_IQ_TX"</div><div>#define RX_FILENAME "/Users/kampianakis/Desktop/SGCC/Demos/UHD/uhd/host/build/examples/data/sin_IQ_RX"</div><div>#define SN std::endl</div><div>#define RX_BW SAMP_RATE/2;</div><div>#define TX 1</div><div>#define RX_CONT 0</div><div>#define TOTAL_SAMPLES 1e6</div><div>#define SYNCH_DELAY 2 //Seconds</div><div>#define DEBUG 0</div><div>#define RX_TIMEOUT 3</div><div><br></div><div>//Stuff for tx asynch. Declare as global to avoid input in thread (chicken)</div><div>uhd::tx_streamer::sptr tx_stream;</div><div>uhd::tx_metadata_t tx_md;                                                  //TX metadata structure for describing received IF data. Includes time specification, and start and stop burst flags. The send routines will convert the metadata to IF data headers.        </div><div>size_t samples_per_buff = SAMPLES_PER_BUFFER;</div><div>size_t num_tx_samps = 0;</div><div>std::vector<std::complex<float> > small_tx_buff(samples_per_buff);</div><div><br></div><div><br></div><div><br></div><div>void thread_startTx(){</div><div>    num_tx_samps = tx_stream->send(&small_tx_buff.front(), small_tx_buff.size(), tx_md);</div><div>}</div><div><br></div><div><br></div><div><br></div><div><br></div><div>typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t;</div><div>bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){</div><div>    if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end())</div><div>        return false;</div><div><br></div><div>    boost::system_time start = boost::get_system_time();</div><div>    boost::system_time first_lock_time;</div><div><br></div><div>    std::cout << boost::format("Waiting for \"%s\": ") % sensor_name;</div><div>    std::cout.flush();</div><div><br></div><div>    while (true){</div><div>        if ((not first_lock_time.is_not_a_date_time()) and</div><div>            (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time))))</div><div>        {</div><div>            std::cout << " locked." << std::endl;</div><div>            break;</div><div>        }</div><div>        if (get_sensor_fn(sensor_name).to_bool()){</div><div>            if (first_lock_time.is_not_a_date_time())</div><div>                first_lock_time = boost::get_system_time();</div><div>            std::cout << "+";</div><div>            std::cout.flush();</div><div>        }</div><div>        else{</div><div>            first_lock_time = boost::system_time(); //reset to 'not a date time'</div><div><br></div><div>            if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){</div><div>                std::cout << std::endl;</div><div>                throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name));</div><div>            }</div><div>            std::cout << "_";</div><div>            std::cout.flush();</div><div>        }</div><div>        boost::this_thread::sleep(boost::posix_time::milliseconds(100));</div><div>    }</div><div>    std::cout << std::endl;</div><div>    return true;</div><div>}</div><div><br></div><div><br></div><div><br></div><div>int UHD_SAFE_MAIN(int argc, char *argv[]){</div><div>    </div><div>    uhd::set_thread_priority_safe();    </div><div><br></div><div>    std::string args, tx_file, rx_file, type, ref, wire_format, cpu_format;                </div><div>    </div><div>    double rate, freq, tx_gain, rx_gain, rx_bw, delay, lo_off,seconds_in_future, rx_timeout;</div><div><br></div><div>    rx_bw = RX_BW;</div><div>    rx_gain = RX_GAIN;</div><div>    wire_format = WIRE_FORMAT;</div><div>    cpu_format = CPU_FORMAT;</div><div>    rate = SAMP_RATE;</div><div>    args = ARGS;</div><div>    ref = REF_CLOCK;</div><div>    freq = CENT_FREQ;</div><div>    tx_gain = TX_GAIN;</div><div>    // samples_per_buff = SAMPLES_PER_BUFFER;</div><div>    tx_file = TX_FILENAME;</div><div>    rx_file = RX_FILENAME;</div><div>    seconds_in_future = SYNCH_DELAY;</div><div>    rx_timeout = RX_TIMEOUT;</div><div><br></div><div>    //------------------INIT TX------------------</div><div>                                        //Set the scheduling priority on the current thread. Same as set_thread_priority but does not throw on failure.</div><div>    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;</div><div>    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);       //Make the usrp by calling the constructor with param the args</div><div><br></div><div>    usrp->set_clock_source(ref);                                          //Set the clock source for the usrp device. This sets the source for a 10 MHz reference clock. Typical options for source: internal, external, MIMO.</div><div>    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl;                           </div><div>    usrp->set_tx_rate(rate);                                                                                        //Set the sample rate</div><div>    std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl;</div><div><br></div><div>    std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl;                              //Set up tuning frequency</div><div>    uhd::tune_request_t tune_request;                                                                               </div><div>    tune_request = uhd::tune_request_t(freq);                                                                        //Generate the tune request</div><div>    usrp->set_tx_freq(tune_request);                                                                                //Tune to CENT_FREQ</div><div>    std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl;  //PRINT Actual CENT_FREQ</div><div><br></div><div>    std::cout << boost::format("Setting TX Gain: %f dB...") % tx_gain << std::endl;                                    </div><div>    usrp->set_tx_gain(tx_gain);                                                                                     //Set the tx_gain</div><div>    std::cout << boost::format("Actual TX Gain: %f dB...") % usrp->get_tx_gain() << std::endl << std::endl;</div><div>    </div><div>    //------------------CHECK STUFF------------------</div><div>    //Check Ref and LO Lock detect</div><div>    std::vector<std::string> sensor_names;</div><div>    sensor_names = usrp->get_tx_sensor_names(0);</div><div>    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) {</div><div>        uhd::sensor_value_t lo_locked = usrp->get_tx_sensor("lo_locked",0);</div><div>        std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string() << std::endl;</div><div>        UHD_ASSERT_THROW(lo_locked.to_bool());</div><div>    }        </div><div><br></div><div><br></div><div><br></div><div>        </div><div><br></div><div>    </div><div><br></div><div>        </div><div>    //------------------INIT RX------------------</div><div><br></div><div>    //IS THIS NECESSARY?</div><div>    //always select the subdevice first, the channel mapping affects the other settings</div><div>    //usrp->set_rx_subdev_spec(subdev);</div><div><br></div><div>    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl;</div><div>    usrp->set_rx_rate(rate);</div><div>    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl;</div><div><br></div><div>    //set the center frequency</div><div>    </div><div>    std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl;</div><div>    usrp->set_rx_freq(tune_request);</div><div>    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl;</div><div>    </div><div>    </div><div>    std::cout << boost::format("Setting RX Gain: %f dB...") % rx_gain << std::endl;</div><div>    usrp->set_rx_gain(rx_gain);</div><div>    std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl;    </div><div><br></div><div>    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow 1sec  setup time</div><div>    //------------------CHECK STUFF------------------</div><div>    </div><div>    //Always check for locked sensor</div><div>    check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), 1);</div><div><br></div><div>    //------------------INIT FILES---------------</div><div>    </div><div><br></div><div>    std::ofstream outfile;    </div><div>    outfile.open(rx_file.c_str(), std::ofstream::binary);</div><div>    if(!outfile.good()){</div><div>        std::cout << "OUT File error\n";</div><div>        return 0;</div><div>    }</div><div><br></div><div>    std::ifstream infile(tx_file.c_str(), std::ifstream::binary);</div><div><br></div><div>    if(!infile.good()){</div><div>        std::cout << "IN File error\n";</div><div>        return 0;</div><div>    }</div><div><br></div><div>    //------------------INIT STREAMS---------------   </div><div><br></div><div>    //Stream ARGS</div><div>    uhd::stream_args_t stream_args(cpu_format, wire_format);                //Call the constructor of the class stream_args_t and generate the stream_args object with inputs the cpu_format and wire_format (this is the format per sample)</div><div><br></div><div>    tx_stream = usrp->get_tx_stream(stream_args);                           //Generate a tx_streamer object named tx_stream using the usrp->get_tx_stream(stream_args). Remember, usrp is already initialized</div><div>    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);    //Generate a tx_streamer object named tx_stream using the usrp->get_tx_stream(stream_args). Remember, usrp is already initialized</div><div><br></div><div><br></div><div><br></div><div><br></div><div>    //Setup metadata    </div><div><br></div><div>    //Setup tx_metadata</div><div><br></div><div>    tx_md.start_of_burst = true;                                              //Set start of burst to true for the first packet in the chain. ?</div><div>    tx_md.end_of_burst =   false;        </div><div><br></div><div>    #define TIMED_TX 0</div><div>    #define TIMED_RX 0</div><div>    </div><div>    if(TIMED_TX){</div><div><br></div><div>        tx_md.has_time_spec = true;</div><div>        tx_md.time_spec = uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();</div><div><br></div><div>    }else{</div><div>        tx_md.has_time_spec = false;</div><div>    }</div><div><br></div><div>    //Setup rx_metadata</div><div>    uhd::rx_metadata_t rx_md;</div><div><br></div><div>    </div><div>    //Setup stream command ONLY FOR RX</div><div>    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);        </div><div>    stream_cmd.num_samps = samples_per_buff;                            </div><div><br></div><div>    if(TIMED_RX){</div><div>        stream_cmd.stream_now = false;                                   </div><div>        stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();                      </div><div>    }else{</div><div>        stream_cmd.stream_now = true;                                   </div><div>        stream_cmd.time_spec = uhd::time_spec_t();  </div><div>    }</div><div><br></div><div><br></div><div><br></div><div><br></div><div>    //Create the  buffs    </div><div>    </div><div>    std::vector<std::complex<float> > small_rx_buff(samples_per_buff);</div><div><br></div><div>    </div><div>    //Fill the  TX buffer</div><div>    for (int i = 0; i < samples_per_buff; ++i){</div><div>        </div><div>        infile.read((char*)&<a href="http://small_tx_buff.at">small_tx_buff.at</a>(i), small_tx_buff.size()*sizeof(std::complex<float>));    </div><div><br></div><div>    }    </div><div>    infile.close();                                                        //Close the file pointer</div><div><br></div><div>    //Issue the  stream command</div><div>    rx_stream->issue_stream_cmd(stream_cmd);</div><div><br></div><div>    //Print number of maximum buffer size</div><div>    printf("MAX TX: %d\n", (int)tx_stream->get_max_num_samps());</div><div>    printf("MAX RX %d\n", (int)rx_stream->get_max_num_samps());</div><div><br></div><div><br></div><div>    size_t num_rx_samps = 0;</div><div>    size_t num_tx_samps = 0;</div><div><br></div><div>    </div><div><br></div><div>    boost::thread txThread(thread_startTx);</div><div>    </div><div>    //receivotrnsmit        </div><div>    num_rx_samps = rx_stream->recv(&small_rx_buff.front(), small_rx_buff.size(), rx_md, rx_timeout, false);  // Receive buffers containing samples described by the metadata.    </div><div>    txThread.join();                        //Strart the thread for tx  (tx is f blocking)</div><div>    </div><div>    </div><div>    </div><div><br></div><div>    double rx_stamp = rx_md.time_spec.get_full_secs() + rx_md.time_spec.get_frac_secs();</div><div>    double tx_stamp = tx_md.time_spec.get_full_secs() + tx_md.time_spec.get_frac_secs();</div><div>    double t_diff = rx_stamp - tx_stamp;</div><div><br></div><div><br></div><div>    printf("RX Time stamp: %.12lf\n ΤX Time stamp: %.12lf\n Diff: %.12lf\n",rx_stamp, tx_stamp, t_diff);</div><div>    </div><div><br></div><div><br></div><div><br></div><div>    switch ( rx_md.error_code ) {</div><div><br></div><div>        case uhd::rx_metadata_t::ERROR_CODE_NONE:</div><div>            printf("No error:)\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:</div><div>            printf("MDError 2\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:</div><div>            printf("MDError 3\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:</div><div>            printf("MDError 4\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:</div><div>            printf("MDError 5\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:</div><div>            printf("MDError 6\n");</div><div>            break;</div><div>        case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:</div><div>            printf("MDError 7\n");</div><div>            break;</div><div><br></div><div>        default:</div><div>            printf("WUT\n");</div><div>            break;</div><div><br></div><div>    }    </div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div>    //write the  samples</div><div>    if (outfile.is_open()){</div><div>        outfile.write((const char*)&small_rx_buff.front(), num_rx_samps*sizeof(std::complex<float>));          </div><div>    }</div><div><br></div><div><br></div><div><br></div><div>    outfile.close();                                                        //Close the file pointer</div><div><br></div><div><br></div><div><br></div><div>    //print</div><div>    // std::cout << "Transmitted samples: " << num_tx_samps << '\n';</div><div>    std::cout << "Received samples: " << num_rx_samps << '\n';</div><div><br></div><div><br></div><div><br></div><div><br></div><div> </div><div>    </div><div>    return EXIT_SUCCESS;</div><div>}</div><div><br></div><div><br></div><div>-- <br></div></div><div><div dir="ltr"><div style="font-size:small;font-family:arial">Eleftherios(Lef) Kampianakis</div><div style="font-size:small;font-family:arial">Electronics and Computer Engineer</div><div style="font-size:small;font-family:arial">PHD Candidate and Researcher at Sensing Computing Communications Group (SGCC)</div><div style="font-size:small;font-family:arial">Department of Electrical Engineering</div><div style="font-size:small;font-family:arial">University of Washington</div><div style="font-size:small;font-family:arial">3927 Adams Lane, NE, Mercer Court D805B, 98105</div><div style="font-size:small;font-family:arial">website: <a href="http://users.isc.tuc.gr/~ekabianakis/" target="_blank">http://users.isc.tuc.gr/~ekabianakis/</a></div><div style="font-size:small;font-family:arial"><a href="mailto:mail%3Ae.kampianakis.ee@ieee.org" target="_blank">mail:e.kampianakis.ee@ieee.org</a></div></div>
</div></div>