/*********************************************************************** * Main **********************************************************************/ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string args; double tx_wave_ampl, tx_offset; double freq_start, freq_stop, freq_step; size_t nsamps; po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("verbose", "enable some verbose") ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts") ("tx_offset", po::value<double>(&tx_offset)->default_value(.9344e6), "TX LO offset from the RX LO in Hz") ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)") ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)") ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz") ("nsamps", po::value<size_t>(&nsamps)->default_value(default_num_samps), "Samples per data capture") ; 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("USRP Generate RX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" << std::endl; return ~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); //set the antennas to cal if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); } usrp->set_rx_antenna("CAL"); usrp->set_tx_antenna("CAL"); //set optimum defaults set_optimum_defaults(usrp); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //create a transmitter thread boost::thread_group threads; threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_ampl)); //re-usable buffer for samples std::vector<samp_type> buff; //store the results here std::vector<result_t> results; if (not vm.count("freq_start")) freq_start = usrp->get_rx_freq_range().start() + 50e6; if (not vm.count("freq_stop")) freq_stop = usrp->get_rx_freq_range().stop() - 50e6; for (double rx_lo_i = freq_start; rx_lo_i <= freq_stop; rx_lo_i += freq_step){ const double rx_lo = tune_rx_and_tx(usrp, rx_lo_i, tx_offset); //frequency constants for this tune event const double actual_rx_rate = usrp->get_rx_rate(); const double actual_tx_freq = usrp->get_tx_freq(); const double actual_rx_freq = usrp->get_rx_freq(); const double bb_tone_freq = actual_tx_freq - actual_rx_freq; const double bb_imag_freq = -bb_tone_freq; //capture initial uncorrected value usrp->set_rx_iq_balance(0.0); capture_samples(usrp, rx_stream, buff, nsamps); const double initial_suppression = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate) - compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate); //bounds and results from searching std::complex<double> best_correction; double phase_corr_start = -.3, phase_corr_stop = .3, phase_corr_step; double ampl_corr_start = -.3, ampl_corr_stop = .3, ampl_corr_step; double best_suppression = 0, best_phase_corr = 0, best_ampl_corr = 0; for (size_t i = 0; i < num_search_iters; i++){ phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps-1); ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps-1); for (double phase_corr = phase_corr_start; phase_corr <= phase_corr_stop + phase_corr_step/2; phase_corr += phase_corr_step){ for (double ampl_corr = ampl_corr_start; ampl_corr <= ampl_corr_stop + ampl_corr_step/2; ampl_corr += ampl_corr_step){ const std::complex<double> correction(ampl_corr, phase_corr); usrp->set_rx_iq_balance(correction); //receive some samples capture_samples(usrp, rx_stream, buff, nsamps); const double tone_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate); const double imag_dbrms = compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate); const double suppression = tone_dbrms - imag_dbrms; if (suppression > best_suppression){ best_correction = correction; best_suppression = suppression; best_phase_corr = phase_corr; best_ampl_corr = ampl_corr; } }} //std::cout << "best_phase_corr " << best_phase_corr << std::endl; //std::cout << "best_ampl_corr " << best_ampl_corr << std::endl; //std::cout << "best_suppression " << best_suppression << std::endl; phase_corr_start = best_phase_corr - phase_corr_step; phase_corr_stop = best_phase_corr + phase_corr_step; ampl_corr_start = best_ampl_corr - ampl_corr_step; ampl_corr_stop = best_ampl_corr + ampl_corr_step; } if (best_suppression > 30){ //most likely valid, keep result result_t result; result.freq = rx_lo; result.real_corr = best_correction.real(); result.imag_corr = best_correction.imag(); result.best = best_suppression; result.delta = best_suppression - initial_suppression; results.push_back(result); if (vm.count("verbose")){ std::cout << boost::format("RX IQ: %f MHz: best suppression %f dB, corrected %f dB") % (rx_lo/1e6) % result.best % result.delta << std::endl; } else std::cout << "." << std::flush; } } std::cout << std::endl; //stop the transmitter threads.interrupt_all(); threads.join_all(); store_results(usrp, results, "RX", "rx", "iq"); return 0; }
/*********************************************************************** * Function to find optimal RX gain setting (for the current frequency) **********************************************************************/ UHD_INLINE void set_optimal_rx_gain( uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, double wave_freq = 0.0) { const double gain_step = 3.0; const double gain_compression_threshold = gain_step * 0.5; const double actual_rx_rate = usrp->get_rx_rate(); const double actual_tx_freq = usrp->get_tx_freq(); const double actual_rx_freq = usrp->get_rx_freq(); const double bb_tone_freq = actual_tx_freq - actual_rx_freq + wave_freq; const size_t nsamps = size_t(actual_rx_rate / default_fft_bin_size); std::vector<samp_type> buff(nsamps); uhd::gain_range_t rx_gain_range = usrp->get_rx_gain_range(); double rx_gain = rx_gain_range.start() + gain_step; double curr_dbrms = 0.0; double prev_dbrms = 0.0; double delta = 0.0; // No sense in setting the gain where this is no gain range if (rx_gain_range.stop() - rx_gain_range.start() < gain_step) return; // The algorithm below cycles through the RX gain range // looking for the point where the signal begins to get // clipped and the gain begins to be compressed. It does // this by looking for the gain setting where the increase // in the tone is less than the gain step by more than the // gain compression threshold (curr - prev < gain - threshold). // Initialize prev_dbrms value usrp->set_rx_gain(rx_gain); capture_samples(usrp, rx_stream, buff, nsamps); prev_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate); rx_gain += gain_step; // Find RX gain where signal begins to clip while (rx_gain <= rx_gain_range.stop()) { usrp->set_rx_gain(rx_gain); capture_samples(usrp, rx_stream, buff, nsamps); curr_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate); delta = curr_dbrms - prev_dbrms; // check if the gain is compressed beyone the threshold if (delta < gain_step - gain_compression_threshold) break; // if so, we are done prev_dbrms = curr_dbrms; rx_gain += gain_step; } // The rx_gain value at this point is the gain setting where clipping // occurs or the gain setting that is just beyond the gain range. // The gain is reduced by 2 steps to make sure it is within the range and // under the point where it is clipped with enough room to make adjustments. rx_gain -= 2 * gain_step; // Make sure the gain is within the range. rx_gain = rx_gain_range.clip(rx_gain); // Finally, set the gain. usrp->set_rx_gain(rx_gain); }
/*********************************************************************** * Main **********************************************************************/ int UHD_SAFE_MAIN(int argc, char *argv[]) { std::string args, subdev, serial; double tx_wave_freq, tx_wave_ampl, rx_offset; double freq_start, freq_stop, freq_step; size_t nsamps; double precision; po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("verbose", "enable some verbose") ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") ("subdev", po::value<std::string>(&subdev), "Subdevice specification (default: first subdevice, often 'A')") ("tx_wave_freq", po::value<double>(&tx_wave_freq)->default_value(507.123e3), "Transmit wave frequency in Hz") ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts") ("rx_offset", po::value<double>(&rx_offset)->default_value(.9344e6), "RX LO offset from the TX LO in Hz") ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)") ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)") ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz") ("nsamps", po::value<size_t>(&nsamps), "Samples per data capture") ("precision", po::value<double>(&precision)->default_value(default_precision), "Correction precision (default=0.0001)") ; 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("USRP Generate TX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } // Create a USRP device uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); if (not vm.count("nsamps")) nsamps = size_t(usrp->get_rx_rate() / default_fft_bin_size); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //create a transmitter thread boost::thread_group threads; threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_freq, tx_wave_ampl)); //re-usable buffer for samples std::vector<samp_type> buff; //store the results here std::vector<result_t> results; if (not vm.count("freq_start")) freq_start = usrp->get_fe_tx_freq_range().start(); if (not vm.count("freq_stop")) freq_stop = usrp->get_fe_tx_freq_range().stop(); //check start and stop frequencies if (freq_start < usrp->get_fe_tx_freq_range().start()) { std::cerr << "freq_start must be " << usrp->get_fe_tx_freq_range().start() << " or greater for this daughter board" << std::endl; return EXIT_FAILURE; } if (freq_stop > usrp->get_fe_tx_freq_range().stop()) { std::cerr << "freq_stop must be " << usrp->get_fe_tx_freq_range().stop() << " or less for this daughter board" << std::endl; return EXIT_FAILURE; } //check rx_offset double min_rx_offset = usrp->get_rx_freq_range().start() - usrp->get_fe_tx_freq_range().start(); double max_rx_offset = usrp->get_rx_freq_range().stop() - usrp->get_fe_tx_freq_range().stop(); if (rx_offset < min_rx_offset or rx_offset > max_rx_offset) { std::cerr << "rx_offset must be between " << min_rx_offset << " and " << max_rx_offset << " for this daughter board" << std::endl; return EXIT_FAILURE; } UHD_MSG(status) << boost::format("Calibration frequency range: %d MHz -> %d MHz") % (freq_start/1e6) % (freq_stop/1e6) << std::endl; for (double tx_lo_i = freq_start; tx_lo_i <= freq_stop; tx_lo_i += freq_step) { const double tx_lo = tune_rx_and_tx(usrp, tx_lo_i, rx_offset); //frequency constants for this tune event const double actual_rx_rate = usrp->get_rx_rate(); const double actual_tx_freq = usrp->get_tx_freq(); const double actual_rx_freq = usrp->get_rx_freq(); const double bb_tone_freq = actual_tx_freq + tx_wave_freq - actual_rx_freq; const double bb_imag_freq = actual_tx_freq - tx_wave_freq - actual_rx_freq; //reset TX IQ balance usrp->set_tx_iq_balance(0.0); //set optimal RX gain setting for this frequency set_optimal_rx_gain(usrp, rx_stream, tx_wave_freq); //capture initial uncorrected value capture_samples(usrp, rx_stream, buff, nsamps); const double initial_suppression = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate) - compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate); //bounds and results from searching double phase_corr_start = -1.0; double phase_corr_stop = 1.0; double phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps+1); double ampl_corr_start = -1.0; double ampl_corr_stop = 1.0; double ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps+1); double best_suppression = 0; double best_phase_corr = 0; double best_ampl_corr = 0; while (phase_corr_step >= precision or ampl_corr_step >= precision) { for (double phase_corr = phase_corr_start + phase_corr_step; phase_corr <= phase_corr_stop - phase_corr_step; phase_corr += phase_corr_step) { for (double ampl_corr = ampl_corr_start + ampl_corr_step; ampl_corr <= ampl_corr_stop - ampl_corr_step; ampl_corr += ampl_corr_step) { const std::complex<double> correction(ampl_corr, phase_corr); usrp->set_tx_iq_balance(correction); //receive some samples capture_samples(usrp, rx_stream, buff, nsamps); const double tone_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate); const double imag_dbrms = compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate); const double suppression = tone_dbrms - imag_dbrms; if (suppression > best_suppression) { best_suppression = suppression; best_phase_corr = phase_corr; best_ampl_corr = ampl_corr; } } } phase_corr_start = best_phase_corr - phase_corr_step; phase_corr_stop = best_phase_corr + phase_corr_step; phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps+1); ampl_corr_start = best_ampl_corr - ampl_corr_step; ampl_corr_stop = best_ampl_corr + ampl_corr_step; ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps+1); } if (best_suppression > initial_suppression) //keep result { result_t result; result.freq = tx_lo; result.real_corr = best_ampl_corr; result.imag_corr = best_phase_corr; result.best = best_suppression; result.delta = best_suppression - initial_suppression; results.push_back(result); if (vm.count("verbose")) std::cout << boost::format("TX IQ: %f MHz: best suppression %f dB, corrected %f dB") % (tx_lo/1e6) % result.best % result.delta << std::endl; else std::cout << "." << std::flush; } } std::cout << std::endl; //stop the transmitter threads.interrupt_all(); boost::this_thread::sleep(boost::posix_time::milliseconds(500)); //wait for threads to finish threads.join_all(); store_results(results, "TX", "tx", "iq", serial); return EXIT_SUCCESS; }
/*********************************************************************** * Main **********************************************************************/ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string args; double tx_wave_freq, tx_wave_ampl, rx_offset; double freq_start, freq_stop, freq_step; size_t nsamps; po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("verbose", "enable some verbose") ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") ("tx_wave_freq", po::value<double>(&tx_wave_freq)->default_value(507.123e3), "Transmit wave frequency in Hz") ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts") ("rx_offset", po::value<double>(&rx_offset)->default_value(.9344e6), "RX LO offset from the TX LO in Hz") ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)") ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)") ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz") ("nsamps", po::value<size_t>(&nsamps)->default_value(default_num_samps), "Samples per data capture") ; 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("USRP Generate TX DC Offset Calibration Table %s") % desc << std::endl; std::cout << "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" << std::endl; return ~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); //set the antennas to cal if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); } usrp->set_rx_antenna("CAL"); usrp->set_tx_antenna("CAL"); //fail if daughterboard has no serial check_for_empty_serial(usrp, "TX", "tx", args); //set optimum defaults set_optimum_defaults(usrp); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //create a transmitter thread boost::thread_group threads; threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_freq, tx_wave_ampl)); //re-usable buffer for samples std::vector<samp_type> buff; //store the results here std::vector<result_t> results; if (not vm.count("freq_start")) freq_start = usrp->get_tx_freq_range().start() + 50e6; if (not vm.count("freq_stop")) freq_stop = usrp->get_tx_freq_range().stop() - 50e6; for (double tx_lo_i = freq_start; tx_lo_i <= freq_stop; tx_lo_i += freq_step){ const double tx_lo = tune_rx_and_tx(usrp, tx_lo_i, rx_offset); //frequency constants for this tune event const double actual_rx_rate = usrp->get_rx_rate(); const double actual_tx_freq = usrp->get_tx_freq(); const double actual_rx_freq = usrp->get_rx_freq(); const double bb_dc_freq = actual_tx_freq - actual_rx_freq; //capture initial uncorrected value usrp->set_tx_dc_offset(std::complex<double>(0, 0)); capture_samples(usrp, rx_stream, buff, nsamps); const double initial_dc_dbrms = compute_tone_dbrms(buff, bb_dc_freq/actual_rx_rate); //bounds and results from searching double dc_i_start = -.01, dc_i_stop = .01, dc_i_step; double dc_q_start = -.01, dc_q_stop = .01, dc_q_step; double lowest_offset = 0, best_dc_i = 0, best_dc_q = 0; for (size_t i = 0; i < num_search_iters; i++){ dc_i_step = (dc_i_stop - dc_i_start)/(num_search_steps-1); dc_q_step = (dc_q_stop - dc_q_start)/(num_search_steps-1); for (double dc_i = dc_i_start; dc_i <= dc_i_stop + dc_i_step/2; dc_i += dc_i_step){ for (double dc_q = dc_q_start; dc_q <= dc_q_stop + dc_q_step/2; dc_q += dc_q_step){ const std::complex<double> correction(dc_i, dc_q); usrp->set_tx_dc_offset(correction); //receive some samples capture_samples(usrp, rx_stream, buff, nsamps); const double dc_dbrms = compute_tone_dbrms(buff, bb_dc_freq/actual_rx_rate); if (dc_dbrms < lowest_offset){ lowest_offset = dc_dbrms; best_dc_i = dc_i; best_dc_q = dc_q; } }} //std::cout << "best_dc_i " << best_dc_i << std::endl; //std::cout << "best_dc_q " << best_dc_q << std::endl; //std::cout << "lowest_offset " << lowest_offset << std::endl; dc_i_start = best_dc_i - dc_i_step; dc_i_stop = best_dc_i + dc_i_step; dc_q_start = best_dc_q - dc_q_step; dc_q_stop = best_dc_q + dc_q_step; } if (lowest_offset < initial_dc_dbrms){ //most likely valid, keep result result_t result; result.freq = tx_lo; result.real_corr = best_dc_i; result.imag_corr = best_dc_q; result.best = lowest_offset; result.delta = initial_dc_dbrms - lowest_offset; results.push_back(result); if (vm.count("verbose")){ std::cout << boost::format("TX DC: %f MHz: lowest offset %f dB, corrected %f dB") % (tx_lo/1e6) % result.best % result.delta << std::endl; } else std::cout << "." << std::flush; } } std::cout << std::endl; //stop the transmitter threads.interrupt_all(); threads.join_all(); store_results(usrp, results, "TX", "tx", "dc"); return 0; }