[USRP-users] Synched TX/RX on B210 NOT POSSIBLE????

Marcus D. Leech mleech at ripnet.com
Mon Oct 27 21:16:02 EDT 2014


On 10/27/2014 09:04 PM, Michael West wrote:
> I also see that you are not setting the start times on the TX and RX 
> streams to the same value:
>
> tx_md.time_spec = 
> uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();
> ...
> stream_cmd.time_spec = 
> uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();
>
> You should have a single value assigned to both:
>
> uhd::time_spec_t start_time = 
> uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();
> ...
> tx_md_time_spec = start_time;
> ...
> stream_cmd.time_spec = start_time;
>
> Regards,
> Michael E. West
>
Also, I should note that the TX and RX synthesizers on the B210 are 
separate, and so they'll not be in the same phase-state either with each 
other, or
   repeatably across different sessions, even if you get the gross 
sample-timing correct, if there's a requirement that TX and RX have 
either zero
   relative phase offset, or some predictable, fixed, phase-offset 
value, the synthesizers on the AD9361 simply aren't designed for that.

In fact, it's very rare that synthesizers, in general, have predictable 
phase states after tuning.  So getting any two of them to agree on 
phase-state,
   even when using a common reference clock, is usually not possible.  
There are exceptions, for example, the SBX card, when used on the N2xx,
   and I believe X3xx platforms have a special "phase-resynch" feature 
that is enabled through "timed commands".  But this is a very-special 
hardware
   feature that is unusual in general-purpose synthesizers.


>
> On Mon, Oct 27, 2014 at 5:54 PM, Marcus D. Leech via USRP-users 
> <usrp-users at lists.ettus.com <mailto:usrp-users at lists.ettus.com>> wrote:
>
>     On 10/27/2014 08:47 PM, Eleftherios(Lef) Kampianakis via
>     USRP-users wrote:
>>     Hello forum,
>>
>>     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.
>>
>>     I have developed (following the advice of Stephen Graham) 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.
>>
>>     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?
>>
>     OK, so, I"m not exactly clear on what you mean by "synchronized"
>     and "at the same time".
>
>     Clearly, if the samples are travelling through the computer up the
>     RX chain, into your application, and then out the TX side,
>     there'll be some latency.
>       Said latency will be unpredictable.  That is unrelated to the
>     B210 hardware, but, rather, to the entire concept of doing SDR on
>     a host platform with
>       a garden-variety OS handling the signal processing.
>
>     While sharing the code is great, sharing a *detailed* description
>     of what it is  you're trying to accomplish, and where the
>     shortcomings are, from your
>       perspective.
>
>
>>     Thank you in advance
>>
>>     CODE:
>>     //Author Eleftherios(Lef) Kampianakis
>>
>>     //V0: Tx Rx wokrking but not synched
>>     //V1: effort for synching (failed)
>>     //V2: used threads to start tx asynch
>>
>>     #include <uhd/types/tune_request.hpp>
>>     #include <uhd/utils/thread_priority.hpp>
>>     #include <uhd/utils/safe_main.hpp>
>>     #include <uhd/usrp/multi_usrp.hpp>
>>     #include <boost/program_options.hpp>
>>     #include <boost/format.hpp>
>>     #include <boost/thread.hpp>
>>     #include <iostream>
>>     #include <fstream>
>>     #include <complex>
>>     #include <csignal>
>>
>>     //------------------MACROS------------------
>>     #define DEB(x) std::cout << "DEB:" << x << std::endl
>>
>>
>>     //------------------SETUP------------------
>>
>>     //Assign the namespace po from boost::program_options
>>     //This is done to work with the terminal inputs cause
>>     boost::program_options
>>     // Question: why use namespace instead of .hpp
>>     namespace po = boost::program_options;
>>
>>
>>
>>     #define CPU_FORMAT "fc32"
>>     #define WIRE_FORMAT "sc16"
>>     #define REF_CLOCK "internal"
>>     #define SAMP_RATE 200e3
>>     #define CENT_FREQ 915e6
>>     #define TX_GAIN  10 //dB
>>     #define RX_GAIN 10   //dB
>>     #define SAMPLES_PER_BUFFER 1000
>>     #define ARGS ""
>>     #define TX_FILENAME
>>     "/Users/kampianakis/Desktop/SGCC/Demos/UHD/uhd/host/build/examples/data/sin_IQ_TX"
>>     #define RX_FILENAME
>>     "/Users/kampianakis/Desktop/SGCC/Demos/UHD/uhd/host/build/examples/data/sin_IQ_RX"
>>     #define SN std::endl
>>     #define RX_BW SAMP_RATE/2;
>>     #define TX 1
>>     #define RX_CONT 0
>>     #define TOTAL_SAMPLES 1e6
>>     #define SYNCH_DELAY 2 //Seconds
>>     #define DEBUG 0
>>     #define RX_TIMEOUT 3
>>
>>     //Stuff for tx asynch. Declare as global to avoid input in thread
>>     (chicken)
>>     uhd::tx_streamer::sptr tx_stream;
>>     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.
>>     size_t samples_per_buff = SAMPLES_PER_BUFFER;
>>     size_t num_tx_samps = 0;
>>     std::vector<std::complex<float> > small_tx_buff(samples_per_buff);
>>
>>
>>
>>     void thread_startTx(){
>>         num_tx_samps = tx_stream->send(&small_tx_buff.front(),
>>     small_tx_buff.size(), tx_md);
>>     }
>>
>>
>>
>>
>>     typedef boost::function<uhd::sensor_value_t (const std::string&)>
>>     get_sensor_fn_t;
>>     bool check_locked_sensor(std::vector<std::string> sensor_names,
>>     const char* sensor_name, get_sensor_fn_t get_sensor_fn, double
>>     setup_time){
>>         if (std::find(sensor_names.begin(), sensor_names.end(),
>>     sensor_name) == sensor_names.end())
>>             return false;
>>
>>         boost::system_time start = boost::get_system_time();
>>         boost::system_time first_lock_time;
>>
>>         std::cout << boost::format("Waiting for \"%s\": ") % sensor_name;
>>         std::cout.flush();
>>
>>         while (true){
>>             if ((not first_lock_time.is_not_a_date_time()) and
>>                 (boost::get_system_time() > (first_lock_time +
>>     boost::posix_time::seconds(setup_time))))
>>             {
>>                 std::cout << " locked." << std::endl;
>>                 break;
>>             }
>>             if (get_sensor_fn(sensor_name).to_bool()){
>>                 if (first_lock_time.is_not_a_date_time())
>>                     first_lock_time = boost::get_system_time();
>>                 std::cout << "+";
>>                 std::cout.flush();
>>             }
>>             else{
>>                 first_lock_time = boost::system_time(); //reset to
>>     'not a date time'
>>
>>                 if (boost::get_system_time() > (start +
>>     boost::posix_time::seconds(setup_time))){
>>                     std::cout << std::endl;
>>                     throw std::runtime_error(str(boost::format("timed
>>     out waiting for consecutive locks on sensor \"%s\"") % sensor_name));
>>                 }
>>                 std::cout << "_";
>>                 std::cout.flush();
>>             }
>>     boost::this_thread::sleep(boost::posix_time::milliseconds(100));
>>         }
>>         std::cout << std::endl;
>>         return true;
>>     }
>>
>>
>>
>>     int UHD_SAFE_MAIN(int argc, char *argv[]){
>>         uhd::set_thread_priority_safe();
>>
>>         std::string args, tx_file, rx_file, type, ref, wire_format,
>>     cpu_format;
>>         double rate, freq, tx_gain, rx_gain, rx_bw, delay,
>>     lo_off,seconds_in_future, rx_timeout;
>>
>>         rx_bw = RX_BW;
>>         rx_gain = RX_GAIN;
>>         wire_format = WIRE_FORMAT;
>>         cpu_format = CPU_FORMAT;
>>         rate = SAMP_RATE;
>>         args = ARGS;
>>         ref = REF_CLOCK;
>>         freq = CENT_FREQ;
>>         tx_gain = TX_GAIN;
>>         // samples_per_buff = SAMPLES_PER_BUFFER;
>>         tx_file = TX_FILENAME;
>>         rx_file = RX_FILENAME;
>>         seconds_in_future = SYNCH_DELAY;
>>         rx_timeout = RX_TIMEOUT;
>>
>>         //------------------INIT TX------------------
>>     //Set the scheduling priority on the current thread. Same as
>>     set_thread_priority but does not throw on failure.
>>         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); //Make the usrp by calling the
>>     constructor with param the args
>>
>>         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.
>>         std::cout << boost::format("Setting TX Rate: %f Msps...") %
>>     (rate/1e6) << std::endl;
>>         usrp->set_tx_rate(rate);                              //Set
>>     the sample rate
>>         std::cout << boost::format("Actual TX Rate: %f Msps...") %
>>     (usrp->get_tx_rate()/1e6) << std::endl << std::endl;
>>
>>         std::cout << boost::format("Setting TX Freq: %f MHz...") %
>>     (freq/1e6) << std::endl;              //Set up tuning frequency
>>         uhd::tune_request_t tune_request;
>>         tune_request = uhd::tune_request_t(freq);      //Generate the
>>     tune request
>>         usrp->set_tx_freq(tune_request);                            
>>      //Tune to CENT_FREQ
>>         std::cout << boost::format("Actual TX Freq: %f MHz...") %
>>     (usrp->get_tx_freq()/1e6) << std::endl << std::endl;  //PRINT
>>     Actual CENT_FREQ
>>
>>         std::cout << boost::format("Setting TX Gain: %f dB...") %
>>     tx_gain << std::endl;
>>         usrp->set_tx_gain(tx_gain);                              
>>     //Set the tx_gain
>>         std::cout << boost::format("Actual TX Gain: %f dB...") %
>>     usrp->get_tx_gain() << std::endl << std::endl;
>>         //------------------CHECK STUFF------------------
>>         //Check Ref and LO Lock detect
>>         std::vector<std::string> sensor_names;
>>         sensor_names = usrp->get_tx_sensor_names(0);
>>         if (std::find(sensor_names.begin(), sensor_names.end(),
>>     "lo_locked") != sensor_names.end()) {
>>             uhd::sensor_value_t lo_locked =
>>     usrp->get_tx_sensor("lo_locked",0);
>>             std::cout << boost::format("Checking TX: %s ...") %
>>     lo_locked.to_pp_string() << std::endl;
>>     UHD_ASSERT_THROW(lo_locked.to_bool());
>>         }
>>
>>
>>
>>
>>
>>         //------------------INIT RX------------------
>>
>>         //IS THIS NECESSARY?
>>         //always select the subdevice first, the channel mapping
>>     affects the other settings
>>         //usrp->set_rx_subdev_spec(subdev);
>>
>>         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;
>>
>>         //set the center frequency
>>         std::cout << boost::format("Setting RX Freq: %f MHz...") %
>>     (freq/1e6) << std::endl;
>>         usrp->set_rx_freq(tune_request);
>>         std::cout << boost::format("Actual RX Freq: %f MHz...") %
>>     (usrp->get_rx_freq()/1e6) << std::endl << std::endl;
>>         std::cout << boost::format("Setting RX Gain: %f dB...") %
>>     rx_gain << std::endl;
>>         usrp->set_rx_gain(rx_gain);
>>         std::cout << boost::format("Actual RX Gain: %f dB...") %
>>     usrp->get_rx_gain() << std::endl << std::endl;
>>
>>     boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow
>>     1sec  setup time
>>         //------------------CHECK STUFF------------------
>>         //Always check for locked sensor
>>     check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked",
>>     boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), 1);
>>
>>         //------------------INIT FILES---------------
>>
>>         std::ofstream outfile;
>>         outfile.open(rx_file.c_str(), std::ofstream::binary);
>>         if(!outfile.good()){
>>             std::cout << "OUT File error\n";
>>             return 0;
>>         }
>>
>>         std::ifstream infile(tx_file.c_str(), std::ifstream::binary);
>>
>>         if(!infile.good()){
>>             std::cout << "IN File error\n";
>>             return 0;
>>         }
>>
>>         //------------------INIT STREAMS---------------
>>
>>         //Stream ARGS
>>         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)
>>
>>         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
>>         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
>>
>>
>>
>>
>>         //Setup metadata
>>
>>         //Setup tx_metadata
>>
>>         tx_md.start_of_burst = true;                                
>>        //Set start of burst to true for the first packet in the chain. ?
>>         tx_md.end_of_burst =   false;
>>
>>         #define TIMED_TX 0
>>         #define TIMED_RX 0
>>         if(TIMED_TX){
>>
>>             tx_md.has_time_spec = true;
>>             tx_md.time_spec =
>>     uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();
>>
>>         }else{
>>             tx_md.has_time_spec = false;
>>         }
>>
>>         //Setup rx_metadata
>>         uhd::rx_metadata_t rx_md;
>>
>>         //Setup stream command ONLY FOR RX
>>         uhd::stream_cmd_t
>>     stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
>>         stream_cmd.num_samps = samples_per_buff;
>>
>>         if(TIMED_RX){
>>             stream_cmd.stream_now = false;
>>             stream_cmd.time_spec =
>>     uhd::time_spec_t(seconds_in_future)+usrp->get_time_now();
>>         }else{
>>             stream_cmd.stream_now = true;
>>             stream_cmd.time_spec = uhd::time_spec_t();
>>         }
>>
>>
>>
>>
>>         //Create the  buffs
>>     std::vector<std::complex<float> > small_rx_buff(samples_per_buff);
>>
>>         //Fill the  TX buffer
>>         for (int i = 0; i < samples_per_buff; ++i){
>>             infile.read((char*)&small_tx_buff.at
>>     <http://small_tx_buff.at>(i),
>>     small_tx_buff.size()*sizeof(std::complex<float>));
>>
>>         }
>>         infile.close();                                //Close the
>>     file pointer
>>
>>         //Issue the  stream command
>>     rx_stream->issue_stream_cmd(stream_cmd);
>>
>>         //Print number of maximum buffer size
>>         printf("MAX TX: %d\n", (int)tx_stream->get_max_num_samps());
>>         printf("MAX RX %d\n", (int)rx_stream->get_max_num_samps());
>>
>>
>>         size_t num_rx_samps = 0;
>>         size_t num_tx_samps = 0;
>>
>>
>>         boost::thread txThread(thread_startTx);
>>         //receivotrnsmit
>>         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.
>>         txThread.join();  //Strart the thread for tx  (tx is f blocking)
>>
>>         double rx_stamp = rx_md.time_spec.get_full_secs() +
>>     rx_md.time_spec.get_frac_secs();
>>         double tx_stamp = tx_md.time_spec.get_full_secs() +
>>     tx_md.time_spec.get_frac_secs();
>>         double t_diff = rx_stamp - tx_stamp;
>>
>>
>>         printf("RX Time stamp: %.12lf\n ΤX Time stamp: %.12lf\n Diff:
>>     %.12lf\n",rx_stamp, tx_stamp, t_diff);
>>
>>
>>
>>         switch ( rx_md.error_code ) {
>>
>>             case uhd::rx_metadata_t::ERROR_CODE_NONE:
>>                 printf("No error:)\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
>>                 printf("MDError 2\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
>>                 printf("MDError 3\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
>>                 printf("MDError 4\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
>>                 printf("MDError 5\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
>>                 printf("MDError 6\n");
>>                 break;
>>             case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
>>                 printf("MDError 7\n");
>>                 break;
>>
>>             default:
>>                 printf("WUT\n");
>>                 break;
>>
>>         }
>>
>>
>>
>>
>>
>>         //write the  samples
>>         if (outfile.is_open()){
>>             outfile.write((const char*)&small_rx_buff.front(),
>>     num_rx_samps*sizeof(std::complex<float>));
>>         }
>>
>>
>>
>>         outfile.close();                                  //Close the
>>     file pointer
>>
>>
>>
>>         //print
>>         // std::cout << "Transmitted samples: " << num_tx_samps << '\n';
>>         std::cout << "Received samples: " << num_rx_samps << '\n';
>>
>>
>>
>>
>>         return EXIT_SUCCESS;
>>     }
>>
>>
>>     -- 
>>     Eleftherios(Lef) Kampianakis
>>     Electronics and Computer Engineer
>>     PHD Candidate and Researcher at Sensing Computing Communications
>>     Group (SGCC)
>>     Department of Electrical Engineering
>>     University of Washington
>>     3927 Adams Lane, NE, Mercer Court D805B, 98105
>>     website: http://users.isc.tuc.gr/~ekabianakis/
>>     <http://users.isc.tuc.gr/%7Eekabianakis/>
>>     mail:e.kampianakis.ee at ieee.org
>>     <mailto:mail%3Ae.kampianakis.ee at ieee.org>
>>
>>
>>     _______________________________________________
>>     USRP-users mailing list
>>     USRP-users at lists.ettus.com  <mailto:USRP-users at lists.ettus.com>
>>     http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com
>
>
>     _______________________________________________
>     USRP-users mailing list
>     USRP-users at lists.ettus.com <mailto:USRP-users at lists.ettus.com>
>     http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ettus.com/pipermail/usrp-users_lists.ettus.com/attachments/20141027/46972234/attachment-0002.html>


More information about the USRP-users mailing list