diff --git a/uhd_cpp/console/sdr.pro b/uhd_cpp/console/sdr.pro index cb286afb7a9a42eabdec39a5dd364d19bc907165..407c69485ba5d64d61922a7d17ef42b072a07b1b 100644 --- a/uhd_cpp/console/sdr.pro +++ b/uhd_cpp/console/sdr.pro @@ -1,8 +1,8 @@ TEMPLATE = app -CONFIG += console c++11 +CONFIG += console c++20 CONFIG -= app_bundle CONFIG -= qt SOURCES += \ uhd_io_cpp.cpp -LIBS += -luhd -lpthread +LIBS += -luhd -lpthread -latomic diff --git a/uhd_cpp/console/uhd_io_burst.cpp b/uhd_cpp/console/uhd_io_burst.cpp new file mode 100644 index 0000000000000000000000000000000000000000..905578ecb0e64c36c4e3ce5c0375a56cf34a22a4 --- /dev/null +++ b/uhd_cpp/console/uhd_io_burst.cpp @@ -0,0 +1,380 @@ +/* + * Copyright 2015 Ettus Research LLC + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + * 丁劲犇修改 2021 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using uhd::tune_request_t; +using uhd::tx_streamer; +using uhd::usrp::multi_usrp; +using uhd::rx_streamer; +using std::thread; +using std::cerr; +using std::endl; +using std::string; +using std::vector; +using std::shared_ptr; + + +static std::atomic stop_signal_called (false); +void sigint_handler(int code){ + (void)code; + stop_signal_called = true; +} +/*! + * \brief The tag_channelOptions struct + * 通道配置参数 + */ +const double sprate_all = 61.44e6/8; + +struct tag_channelOptions{ + string type = "sc16"; //样点类型,为上位机上类型: fc64, fc32, or sc16 + string subdev=""; //通道,A:A, A:B两个通道,对应B210左侧、右侧接口.一般默认配置,用信道号channels选取接口。 + string ant = "TX/RX"; //天线选取,B210 有 TX/RX 或者 RX2两个 + string wirefmt; //内部类型 (sc8 or sc16),是片上处理的类型 + vector channels {0};//通道号,可以设置0,1之类的。默认subdev时,0=A:A,1=A:B,subdev被修改,则采取修改后的编号 + size_t spb = 10000; //缓冲大小,太小会丢包,太大会超时 + double rate = sprate_all; //采样率,单位Hz + double freq = 1.0e9; //射频频率,单位Hz + double gain = 55; //射频增益,单位dB + double bw = rate; //滤波带宽,默认为采样窗口 + double lo_offset = 0; //LO偏移,单位 Hz (缺省) + bool int_n_mod = false; //int-n 模式(本例不配置) + bool docheck = true; //在开始前执行检查 + double setup_time = 1.0; //rx配置检查时间,可选。 +}; +//通道检查函数 +bool check_tx_status(const string & ref, multi_usrp::sptr usrp,const tag_channelOptions & op); +bool check_rx_status(const string & ref, multi_usrp::sptr usrp,const tag_channelOptions & op); + +/*! + * 范例吞吐函数,使用环形队列保持跟随收发 + */ +template +void do_io( + const tag_channelOptions & oprx, + const tag_channelOptions & optx, + rx_streamer::sptr rx, + tx_streamer::sptr tx) +{ + if (oprx.channels.size()>1 || optx.channels.size()>1) + { + cerr << "multi channels IO is not suitable for this simple demo."< > vec_buffer; + vector< int > vec_buffersz; + const int bufsz = 65536; + for (int i=0;i (new FMT[oprx.spb * 2])); + vec_buffersz.push_back(0); + } + //收发计数 + long long rx_count (0); + long long curr_sec = 0; + double curr_frag = 0; + std::mutex mutex_tm; + //接收线程 + auto thcall_rx = [&]()->void{ + uhd::rx_metadata_t md_rx; + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.num_samps = size_t(oprx.spb); + stream_cmd.stream_now = true; + stream_cmd.time_spec = uhd::time_spec_t(); + rx->issue_stream_cmd(stream_cmd); + while (!stop_signal_called) + { + vec_buffersz[rx_count % bufsz] = rx->recv((void *)(vec_buffer[rx_count % bufsz].get()),oprx.spb,md_rx,0.1,false); + //md_rx可以读取时戳 + mutex_tm.lock(); + curr_sec = md_rx.time_spec.get_full_secs(); + curr_frag = md_rx.time_spec.get_frac_secs(); + mutex_tm.unlock(); + ++rx_count; + if (md_rx.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) + fputs("Time out.",stderr); + else if (md_rx.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) + fputs("Over flow",stderr); + else if (md_rx.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) + cerr << "Receiver error: "<< md_rx.strerror() << endl ; + } + }; + //发射线程 + auto thcall_tx = [&]()->void{ + uhd::tx_metadata_t md_tx; + md_tx.end_of_burst = false; + md_tx.start_of_burst = false; + const int total_blocks_needed = oprx.rate / oprx.spb + 1; + while (!stop_signal_called) + { + long long send_sec = 0; + double send_frag = 0; + if (curr_sec % 5!=3 || rx_count < total_blocks_needed) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + mutex_tm.lock(); + send_sec = curr_sec; + send_frag = curr_frag; + mutex_tm.unlock(); + + md_tx.start_of_burst = true; + md_tx.end_of_burst = false; + md_tx.has_time_spec = true; + md_tx.time_spec = uhd::time_spec_t(send_sec+1,0); + fprintf(stderr,"\nSending burst...\n"); + for (int i=0;isend((void *)(vec_buffer[tx_count % bufsz].get()),vec_buffersz[tx_count % bufsz],md_tx,2); + md_tx.start_of_burst = false; + md_tx.end_of_burst = i==total_blocks_needed-2?true:false; + md_tx.has_time_spec = false; + if (samples_sent != optx.spb) + cerr<<"The tx_stream timed out sending " << optx.spb << " samples (" << samples_sent << " sent)." << endl; + } + fprintf(stderr,"\nSending burst finished.\n"); + } + }; + + //启动线程 + thread rx_thread(thcall_rx); + thread tx_thread(thcall_tx); + cerr<<"Press ^C to Stop."<set_clock_source(ref,multi_usrp::ALL_MBOARDS); + + //3.配置发射 + tag_channelOptions tx_op; + tx_op.freq = 200e6; + tx_op.bw = 400e3; + tx_op.gain=70; + tx_op.channels[0] = 0; + //3.1子设备配置(默认),一般不动它。 + if (tx_op.subdev.size()) + usrp->set_tx_subdev_spec(tx_op.subdev, multi_usrp::ALL_MBOARDS); + cerr << "TX Using Device: " << usrp->get_pp_string() << endl; + //3.2速率配置 + cerr << "Setting TX Rate: " << (tx_op.rate / 1e6) << "Msps..." << endl; + usrp->set_tx_rate(tx_op.rate, multi_usrp::ALL_CHANS); + cerr << "Actual TX Rate: " << usrp->get_tx_rate(tx_op.channels[0]) / 1e6 << "Msps..." << endl; + //3.3中心频率配置 + cerr << "Setting TX Freq: " << (tx_op.freq / 1e6) <<"MHz..." << endl; + cerr << "Setting TX LO Offset: " << (tx_op.lo_offset / 1e6) << "MHz..." <set_tx_freq(tune_request_tx,tx_op.channels[0]); + + cerr << "Actual TX Freq: " << (usrp->get_tx_freq(tx_op.channels[0]) / 1e6) << "MHz..." << endl; + //3.4增益配置 + cerr << "Setting TX Gain: " << tx_op.gain <<" dB..." << endl; + usrp->set_tx_gain(tx_op.gain,tx_op.channels[0]); + cerr << "Actual TX Gain: " << usrp->get_tx_gain(tx_op.channels[0]) << " dB..." << endl; + //3.5模拟前端滤波器带宽配置 + cerr << "Setting TX Bandwidth: " << (tx_op.bw / 1e6) << "MHz..." << endl; + usrp->set_tx_bandwidth(tx_op.bw,tx_op.channels[0]); + cerr << "Actual TX Bandwidth: " << usrp->get_tx_bandwidth(tx_op.channels[0]) / 1e6 << "MHz..." << endl; + //3.6指定天线 + if (tx_op.ant.size()) + usrp->set_tx_antenna(tx_op.ant,tx_op.channels[0]); + + //4.配置接收 + tag_channelOptions rx_op; + rx_op.ant = "RX2"; + rx_op.bw = 400e3; + rx_op.freq = 105.3e6; + rx_op.gain = 50; + rx_op.channels[0] = 1; + //4.1 子设备 + if (rx_op.subdev.size()) + usrp->set_rx_subdev_spec(rx_op.subdev,multi_usrp::ALL_MBOARDS); + cerr << "RX Using Device: " << usrp->get_pp_string() << endl; + //4.2 采样率 + cerr << "Setting RX Rate: " << (rx_op.rate / 1e6) << "Msps..." << endl; + usrp->set_rx_rate(rx_op.rate,multi_usrp::ALL_CHANS); + cerr << "Actual RX Rate: " << usrp->get_rx_rate(rx_op.channels[0]) / 1e6 << "Msps..." << endl; + //4.3 中心频率 + cerr << "Setting RX Freq: " << (rx_op.freq / 1e6) <<"MHz..." << endl; + cerr << "Setting RX LO Offset: " << (rx_op.lo_offset / 1e6) << "MHz..." <set_rx_freq(tune_request_rx,rx_op.channels[0]); + cerr << "Actual RX Freq: " << (usrp->get_rx_freq(rx_op.channels[0]) / 1e6) << "MHz..." << endl; + //4.4 增益 + cerr << "Setting RX Gain: " << rx_op.gain <<" dB..." << endl; + usrp->set_rx_gain(rx_op.gain,rx_op.channels[0]); + cerr << "Actual RX Gain: " << usrp->get_rx_gain(rx_op.channels[0]) << " dB..." << endl; + //4.5 前端模拟滤波带宽 + cerr << "Setting RX Bandwidth: " << (rx_op.bw / 1e6) << "MHz..." << endl; + usrp->set_rx_bandwidth(rx_op.bw,rx_op.channels[0]); + cerr << "Actual RX Bandwidth: " << usrp->get_rx_bandwidth(rx_op.channels[0]) / 1e6 << "MHz..." << endl; + //4.6 选择天线 + if (rx_op.ant.size()) + usrp->set_rx_antenna(rx_op.ant,rx_op.channels[0]); + + //5 检查状态 + if (tx_op.docheck) check_tx_status(ref,usrp,tx_op); + if (rx_op.docheck) check_rx_status(ref,usrp,rx_op); + + //6.创建流对象实例 + //6.1 发射 + uhd::stream_args_t stream_args_tx(tx_op.type, tx_op.wirefmt); + stream_args_tx.channels = tx_op.channels; + tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args_tx); + // 6.2 接收 + uhd::stream_args_t stream_args_rx(rx_op.type, rx_op.wirefmt); + stream_args_rx.channels = rx_op.channels; + rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args_rx); + + //开始收发循环 + do_io(rx_op,tx_op,rx_stream,tx_stream); + + // finished + cerr << endl << "Done!" << endl << endl; + + return EXIT_SUCCESS; +} + + +bool check_tx_status(const string & ref, multi_usrp::sptr usrp,const tag_channelOptions & op) +{ + // Check Ref and LO Lock detect + vector sensor_names; + for (size_t c :op.channels) + { + sensor_names = usrp->get_tx_sensor_names(c); + 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",op.channels[0]); + cerr <<"Checking TX: "<< lo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(lo_locked.to_bool()); + } + + } + sensor_names = usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") + and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") + != sensor_names.end())) { + uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); + std::cerr << "Checking TX: "<< mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") + and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") + != sensor_names.end())) { + uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked", 0); + std::cout << "Checking TX: %s ..."<< ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + return true; +} +typedef std::function get_sensor_fn_t; +bool check_locked_sensor(vector 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; + + auto setup_timeout = std::chrono::steady_clock::now() + + std::chrono::milliseconds(int64_t(setup_time * 1000)); + bool lock_detected = false; + + std::cerr << "Checking RX Waiting for: " << sensor_name; + std::cerr.flush(); + + while (true) { + if (lock_detected and (std::chrono::steady_clock::now() > setup_timeout)) { + std::cerr << " locked." << std::endl; + break; + } + if (get_sensor_fn(sensor_name).to_bool()) { + std::cerr << "+"; + std::cerr.flush(); + lock_detected = true; + } else { + if (std::chrono::steady_clock::now() > setup_timeout) { + std::cerr << std::endl; + std::cerr << "timed out waiting for consecutive locks on sensor : "<get_rx_sensor_names(c), + "lo_locked", + [usrp, op, c](const string& sensor_name) { + return usrp->get_rx_sensor(sensor_name, c); + }, + op.setup_time); + } + if (ref == "mimo") { + check_locked_sensor(usrp->get_mboard_sensor_names(0), + "mimo_locked", + [usrp](const string& sensor_name) { + return usrp->get_mboard_sensor(sensor_name); + }, + op.setup_time); + } + if (ref == "external") { + check_locked_sensor(usrp->get_mboard_sensor_names(0), + "ref_locked", + [usrp](const string& sensor_name) { + return usrp->get_mboard_sensor(sensor_name); + }, + op.setup_time); + } + return true; +}