提交 a5649f03 编写于 作者: M manjaro

add spread test app

上级 ffaf5b9f
......@@ -4,5 +4,6 @@ CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
uhd_io_burst.cpp
LIBS += -luhd -lpthread -latomic
uhd_the_ark.cpp
LIBS += -luhd -lpthread -lgomp
QMAKE_CXXFLAGS_RELEASE += -fopenmp -O3 -fopenmp -mavx2
/*
* Copyright 2015 Ettus Research LLC
* Copyright 2018 Ettus Research, a National Instruments Company
*
* SPDX-License-Identifier: GPL-3.0-or-later
* 丁劲犇修改 2021
*/
#include <uhd/types/tune_request.hpp>
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/utils/thread.hpp>
#include <chrono>
#include <complex>
#include <csignal>
#include <fstream>
#include <iostream>
#include <thread>
#include <vector>
#include <cmath>
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 bool stop_signal_called (false);
void sigint_handler(int code){
(void)code;
stop_signal_called = true;
}
/*!
* \brief The tag_channelOptions struct
* 通道配置参数
*/
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<size_t> channels {0};//通道号,可以设置0,1之类的。默认subdev时,0=A:A,1=A:B,subdev被修改,则采取修改后的编号
size_t spb = 10000; //缓冲大小,太小会丢包,太大会超时
double rate = 500000; //采样率,单位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);
void do_io(
const tag_channelOptions & oprx,
const tag_channelOptions & optx,
rx_streamer::sptr rx,
tx_streamer::sptr tx);
int UHD_SAFE_MAIN(int /*argc*/, char* /*argv*/[])
{
//1.创建一个设备,可以不填写,则使用第一台设备。
string args ("");
//2.设置时钟参考 (internal, external, mimo)
string ref = "internal";
cerr << "Creating the usrp device with: " << args <<"..."<< endl;
multi_usrp::sptr usrp = multi_usrp::make(args);
usrp->set_clock_source(ref,multi_usrp::ALL_MBOARDS);
//3.配置发射
tag_channelOptions tx_op;
tx_op.freq = 230e6;
tx_op.bw = tx_op.rate;
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..." <<endl;
tune_request_t tune_request_tx = tune_request_t(tx_op.freq, tx_op.lo_offset);
if (tx_op.int_n_mod)
tune_request_tx.args = uhd::device_addr_t("mode_n=integer");
usrp->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 = rx_op.rate;
rx_op.freq = 230e6;
rx_op.gain = 30;
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..." <<endl;
tune_request_t tune_request_rx = tune_request_t(rx_op.freq, rx_op.lo_offset);
if (rx_op.int_n_mod)
tune_request_rx.args = uhd::device_addr_t("mode_n=integer");
usrp->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;
}
//收发计数
static unsigned long long rx_count (0), tx_count (0);
//消费生产计数
static unsigned long long deal_count (0), gene_count (0);
//缓存点数(IQ)
static const size_t rxbuf_points = 1000*1000*16;
static const size_t txbuf_points = 1000*1000*16;
//缓存,留有尾巴保护
static std::shared_ptr<short [][2]> rx_buf_ptr(new short[rxbuf_points+1024*1024][2]{{0,0}});
static std::shared_ptr<short [][2]> tx_buf_ptr(new short[txbuf_points+1024*1024][2]{{0,0}});
void init_wavform();
//生产者线程,for tx
void producer();
//消费者线程,for rx
void dealer();
/*!
* 范例吞吐函数,使用环形队列保持跟随收发
*/
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."<<endl;
return;
}
//初始化队列
short (*pBufRx)[2] = rx_buf_ptr.get();
short (*pBufTx)[2] = tx_buf_ptr.get();
rx_count = 0;
tx_count = 0;
deal_count = 0;
gene_count = 0;
//接收线程
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)
{
size_t red = rx->recv((void *)(pBufRx[rx_count % rxbuf_points]),oprx.spb,md_rx,10,false);
rx_count += red;
const size_t newBg = rx_count % rxbuf_points;
if (newBg < oprx.spb && newBg > 0)
memcpy(pBufRx[0], pBufRx[rxbuf_points], sizeof(short) * 2 * newBg);
//md_rx可以读取时戳
//auto tm_first = md_rx.time_spec;
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;
while (!stop_signal_called)
{
const size_t cur_bg = tx_count % txbuf_points;
const size_t new_bg = (tx_count + optx.spb) % txbuf_points;
if (new_bg < optx.spb && new_bg > 0)
memcpy(pBufTx[txbuf_points],pBufTx[0],sizeof(short) * 2 * new_bg);
const size_t sent = tx->send((void *)(pBufTx[cur_bg]),optx.spb,md_tx,1);
tx_count += sent;
if (sent != optx.spb)
cerr<<"The tx_stream timed out sending " << optx.spb << " samples (" << sent << " sent)." << endl;
}
};
init_wavform();
//启动线程
thread produce_thread(producer);
thread tx_thread(thcall_tx);
thread rx_thread(thcall_rx);
thread deal_thread(dealer);
cerr<<"Press ^C to Stop."<<endl;
//主线程不断打印状态
while (!stop_signal_called)
{
cerr<<"P " << gene_count<<" TX "<< tx_count<< " RX "<< rx_count << "D "<< deal_count<< " \r";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (rx_count >= 4*1024*1024)
stop_signal_called = true;
}
//退出
produce_thread.join();
rx_thread.join();
tx_thread.join();
deal_thread.join();
}
static const int modraten = 32;
static const int amp = 1000;
static const int spread_ratio = 16;
static const size_t wav_size = spread_ratio*modraten;
static short wav_spread[2][wav_size][2];
static short wav_xorr[2][wav_size][2];
void init_wavform()
{
const double pi = 3.14159265358979323846;
// 2FSK, 0/1, modraten samples, i,q
short wav[2][modraten][2];
for (int i=0;i<modraten;++i)
{
wav[0][i][0] = amp * cos(2 * pi * i / modraten);
wav[0][i][1] = amp * sin(2 * pi * i / modraten);
wav[1][i][0] = amp * cos(2 * pi * i / modraten);
wav[1][i][1] =-amp * sin(2 * pi * i / modraten);
}
//M序列扩频
char reg [21] = {0,1,1,0,0,1,0,1,1,0,0,1,0,1,1,0,0,1,0,1,1};
//seq[0]是 0的符号,1是1的符号。每个符号中的0,1又对应了wav的IQ路
char seq[2][spread_ratio];
int sw = 0;
//产生0,1的波形
fprintf (stderr,"0=");
for (int i=0;i<spread_ratio;++i)
{
reg[sw % 21] = reg[(sw + 3) % 21] ^ reg[(sw+20) % 21];
seq[0][i] = reg[sw % 21];
fprintf (stderr,"%d",seq[0][i]);
--sw;
if (sw < 0)
sw = 20;
for (int j=0;j<modraten;++j)
{
wav_spread[0][i*modraten+j][0] = wav[seq[0][i]][j][0];
wav_spread[0][i*modraten+j][1] = wav[seq[0][i]][j][1];
wav_xorr[0][i*modraten+j][0] = wav[seq[0][i]][j][0];
wav_xorr[0][i*modraten+j][1] = -wav[seq[0][i]][j][1];
}
}
for (int i=0;i<spread_ratio*16;++i)
{
reg[sw % 21] = reg[(sw + 3) % 21] ^ reg[(sw+20) % 21];
seq[0][i] = reg[sw % 21];
--sw;
if (sw < 0)
sw = 20;
}
fprintf (stderr,"\n1=");
for (int i=0;i<spread_ratio;++i)
{
reg[sw % 21] = reg[(sw + 3) % 21] ^ reg[(sw+20) % 21];
seq[1][i] = reg[sw % 21];
fprintf (stderr,"%d",seq[1][i]);
--sw;
if (sw < 0)
sw = 20;
for (int j=0;j<modraten;++j)
{
wav_spread[1][i*modraten+j][0] = wav[seq[1][i]][j][0];
wav_spread[1][i*modraten+j][1] = wav[seq[1][i]][j][1];
wav_xorr[1][i*modraten+j][0] = wav[seq[1][i]][j][0];
wav_xorr[1][i*modraten+j][1] = -wav[seq[1][i]][j][1];
}
}
fprintf(stderr, "\nData inited.\n");
}
//生产者线程,for tx
void producer()
{
short (*pBufTx)[2] = tx_buf_ptr.get();
//Push 0x7e
char idle[8] = {0,1,1,1,1,1,1,0};
while (!stop_signal_called)
{
if (gene_count > tx_count + txbuf_points/2)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
const unsigned long long clk_wav_sprd = gene_count % wav_size;
const unsigned long long clk_bits = (gene_count / wav_size) % 8;
const int curr_bit = idle[clk_bits];
pBufTx[gene_count % txbuf_points][0] = wav_spread[curr_bit][clk_wav_sprd][0];
pBufTx[gene_count % txbuf_points][1] = wav_spread[curr_bit][clk_wav_sprd][1];
++gene_count;
}
}
//消费者线程,for rx
void dealer()
{
unsigned long long next_test = wav_size;
short (*pBufRx)[2] = rx_buf_ptr.get();
while (!stop_signal_called)
{
if (deal_count + wav_size >= rx_count || rx_count < wav_size )
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
if (deal_count < next_test)
{
++deal_count;
continue;
}
long long cross[2][3] = {{0,0,0},{0,0,0}};
unsigned long long start_x = deal_count - wav_size;
//xorr
#pragma omp simd
for (size_t i=0;i<wav_size;++i)
{
int xb = (start_x + i) % rxbuf_points;
cross[0][0] += (long long)(pBufRx[xb][0]) * wav_xorr[0][i][0] - (long long)(pBufRx[xb][1]) * wav_xorr[0][i][1];
cross[0][1] += (long long)(pBufRx[xb][0]) * wav_xorr[0][i][1] + (long long)(pBufRx[xb][1]) * wav_xorr[0][i][0];
cross[1][0] += (long long)(pBufRx[xb][0]) * wav_xorr[1][i][0] - (long long)(pBufRx[xb][1]) * wav_xorr[1][i][1];
cross[1][1] += (long long)(pBufRx[xb][0]) * wav_xorr[1][i][1] + (long long)(pBufRx[xb][1]) * wav_xorr[1][i][0];
}
cross[0][2] = (abs(cross[0][0]) + abs(cross[0][1]));
cross[1][2] = (abs(cross[1][0]) + abs(cross[1][1])) ;
if (cross[0][2]/5 > cross[1][2] )
{
printf ("Clock %lld(%lld) > 0=%lld,1=%lld, %lf\n",deal_count,deal_count / wav_size, cross[0][2],cross[1][2]
,log((cross[0][2]+1.0)/(cross[1][2]+1.0))/log(10)*10
);
next_test = deal_count + wav_size - modraten*2;
}
if (cross[1][2]/5 > cross[0][2] )
{
printf ("Clock %lld(%lld) > 1=%lld,0=%lld, %lf\n",deal_count,deal_count / wav_size, cross[1][2],cross[0][2]
,log((cross[1][2]+1.0)/(cross[0 ][2]+1.0))/log(10)*10
);
next_test = deal_count + wav_size - modraten*2;
}
++deal_count;
}
}
//-----------------------维护代码------------------------
bool check_tx_status(const string & ref, multi_usrp::sptr usrp,const tag_channelOptions & op)
{
// Check Ref and LO Lock detect
vector<string> 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<uhd::sensor_value_t(const string&)> get_sensor_fn_t;
bool check_locked_sensor(vector<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;
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 : "<<sensor_name;
return false;
}
std::cerr.flush();
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << std::endl;
return true;
}
bool check_rx_status(const string & ref, multi_usrp::sptr usrp,const tag_channelOptions & op)
{
// check Ref and LO Lock detect
for (size_t c :op.channels)
{
check_locked_sensor(usrp->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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册