It has been chosen to not include the full history of commits from David. He included a binary version of wireshark, probably a modified one that understands NFAPI. Wireshark is released under the GPL license, we cannot include it in the repository. We could have done a next commit to remove this binary. But then it would still be present in the history of commits, which may not be allowed. And it would take space on disk. We could edit the history to remove wireshark entirely. But this operation is too complicated. There was also a pcap capture file, which has nothing to do in the history of commits and would take space on disk. There again, it's too difficult to edit the history to remove it. There was a file .gitignore that was also removed. The original history can be found on David's repository: https://gitlab.eurecom.fr/daveprice/openairinterface5g/ The branch is: nfapi-ru-rau-split. A copy of that branch has been included in the internal OAI repository, for those who have access to it. The branch is the same. The last commit ID is 9106438239e0bc626ff1fa1d97d911caadd0fbb9. You can compare the current commit with the commit 9106... to see what differs. The current commit has to be considered non-working. The commit following the current commit will fix problems with the work in the current commit. If you use git bisect, don't spend time analyzing the current commit.
1291 lines
49 KiB
C++
1291 lines
49 KiB
C++
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/** usrp_lib.cpp
|
|
*
|
|
* \author: HongliangXU : hong-liang-xu@agilent.com
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <uhd/utils/thread_priority.hpp>
|
|
#include <uhd/usrp/multi_usrp.hpp>
|
|
#include <uhd/version.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/thread.hpp>
|
|
#include <boost/format.hpp>
|
|
#include <iostream>
|
|
#include <complex>
|
|
#include <fstream>
|
|
#include <cmath>
|
|
#include <time.h>
|
|
#include "UTIL/LOG/log_extern.h"
|
|
#include "common_lib.h"
|
|
#include "assertions.h"
|
|
|
|
#ifdef __SSE4_1__
|
|
# include <smmintrin.h>
|
|
#endif
|
|
|
|
#ifdef __AVX2__
|
|
# include <immintrin.h>
|
|
#endif
|
|
|
|
#ifdef __arm__
|
|
# include <arm_neon.h>
|
|
#endif
|
|
|
|
/** @addtogroup _USRP_PHY_RF_INTERFACE_
|
|
* @{
|
|
*/
|
|
|
|
/*! \brief USRP Configuration */
|
|
typedef struct {
|
|
|
|
// --------------------------------
|
|
// variables for USRP configuration
|
|
// --------------------------------
|
|
//! USRP device pointer
|
|
uhd::usrp::multi_usrp::sptr usrp;
|
|
|
|
//create a send streamer and a receive streamer
|
|
//! USRP TX Stream
|
|
uhd::tx_streamer::sptr tx_stream;
|
|
//! USRP RX Stream
|
|
uhd::rx_streamer::sptr rx_stream;
|
|
|
|
//! USRP TX Metadata
|
|
uhd::tx_metadata_t tx_md;
|
|
//! USRP RX Metadata
|
|
uhd::rx_metadata_t rx_md;
|
|
|
|
//! Sampling rate
|
|
double sample_rate;
|
|
|
|
//! TX forward samples. We use usrp_time_offset to get this value
|
|
int tx_forward_nsamps; //166 for 20Mhz
|
|
|
|
// --------------------------------
|
|
// Debug and output control
|
|
// --------------------------------
|
|
int num_underflows;
|
|
int num_overflows;
|
|
int num_seq_errors;
|
|
int64_t tx_count;
|
|
int64_t rx_count;
|
|
int wait_for_first_pps;
|
|
int use_gps;
|
|
//! timestamp of RX packet
|
|
openair0_timestamp rx_timestamp;
|
|
|
|
} usrp_state_t;
|
|
|
|
//void print_notes(void)
|
|
//{
|
|
// Helpful notes
|
|
// std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n");
|
|
// std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n");
|
|
// std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n");
|
|
// std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n");
|
|
// std::cout << boost::format("****************************************************************************************************************\n");
|
|
//}
|
|
|
|
static int sync_to_gps(openair0_device *device)
|
|
{
|
|
uhd::set_thread_priority_safe();
|
|
|
|
//std::string args;
|
|
|
|
//Set up program options
|
|
//po::options_description desc("Allowed options");
|
|
//desc.add_options()
|
|
//("help", "help message")
|
|
//("args", po::value<std::string>(&args)->default_value(""), "USRP device arguments")
|
|
//;
|
|
//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("Synchronize USRP to GPS %s") % desc << std::endl;
|
|
// return EXIT_FAILURE;
|
|
//}
|
|
|
|
//Create a USRP device
|
|
//std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args;
|
|
//uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
|
|
//std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string();
|
|
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
|
|
try
|
|
{
|
|
size_t num_mboards = s->usrp->get_num_mboards();
|
|
size_t num_gps_locked = 0;
|
|
for (size_t mboard = 0; mboard < num_mboards; mboard++)
|
|
{
|
|
std::cout << "Synchronizing mboard " << mboard << ": " << s->usrp->get_mboard_name(mboard) << std::endl;
|
|
|
|
//Set references to GPSDO
|
|
s->usrp->set_clock_source("gpsdo", mboard);
|
|
s->usrp->set_time_source("gpsdo", mboard);
|
|
|
|
//std::cout << std::endl;
|
|
//print_notes();
|
|
//std::cout << std::endl;
|
|
|
|
//Check for 10 MHz lock
|
|
std::vector<std::string> sensor_names = s->usrp->get_mboard_sensor_names(mboard);
|
|
if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())
|
|
{
|
|
std::cout << "Waiting for reference lock..." << std::flush;
|
|
bool ref_locked = false;
|
|
for (int i = 0; i < 30 and not ref_locked; i++)
|
|
{
|
|
ref_locked = s->usrp->get_mboard_sensor("ref_locked", mboard).to_bool();
|
|
if (not ref_locked)
|
|
{
|
|
std::cout << "." << std::flush;
|
|
boost::this_thread::sleep(boost::posix_time::seconds(1));
|
|
}
|
|
}
|
|
if(ref_locked)
|
|
{
|
|
std::cout << "LOCKED" << std::endl;
|
|
} else {
|
|
std::cout << "FAILED" << std::endl;
|
|
std::cout << "Failed to lock to GPSDO 10 MHz Reference. Exiting." << std::endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::cout << boost::format("ref_locked sensor not present on this board.\n");
|
|
}
|
|
|
|
//Wait for GPS lock
|
|
bool gps_locked = s->usrp->get_mboard_sensor("gps_locked", mboard).to_bool();
|
|
if(gps_locked)
|
|
{
|
|
num_gps_locked++;
|
|
std::cout << boost::format("GPS Locked\n");
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "WARNING: GPS not locked - time will not be accurate until locked" << std::endl;
|
|
}
|
|
|
|
//Set to GPS time
|
|
uhd::time_spec_t gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
|
|
//s->usrp->set_time_next_pps(gps_time+1.0, mboard);
|
|
s->usrp->set_time_next_pps(uhd::time_spec_t(0.0));
|
|
|
|
//Wait for it to apply
|
|
//The wait is 2 seconds because N-Series has a known issue where
|
|
//the time at the last PPS does not properly update at the PPS edge
|
|
//when the time is actually set.
|
|
boost::this_thread::sleep(boost::posix_time::seconds(2));
|
|
|
|
//Check times
|
|
gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
|
|
uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps(mboard);
|
|
std::cout << "USRP time: " << (boost::format("%0.9f") % time_last_pps.get_real_secs()) << std::endl;
|
|
std::cout << "GPSDO time: " << (boost::format("%0.9f") % gps_time.get_real_secs()) << std::endl;
|
|
//if (gps_time.get_real_secs() == time_last_pps.get_real_secs())
|
|
// std::cout << std::endl << "SUCCESS: USRP time synchronized to GPS time" << std::endl << std::endl;
|
|
//else
|
|
// std::cerr << std::endl << "ERROR: Failed to synchronize USRP time to GPS time" << std::endl << std::endl;
|
|
}
|
|
|
|
if (num_gps_locked == num_mboards and num_mboards > 1)
|
|
{
|
|
//Check to see if all USRP times are aligned
|
|
//First, wait for PPS.
|
|
uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps();
|
|
while (time_last_pps == s->usrp->get_time_last_pps())
|
|
{
|
|
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
|
|
}
|
|
|
|
//Sleep a little to make sure all devices have seen a PPS edge
|
|
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
|
|
|
//Compare times across all mboards
|
|
bool all_matched = true;
|
|
uhd::time_spec_t mboard0_time = s->usrp->get_time_last_pps(0);
|
|
for (size_t mboard = 1; mboard < num_mboards; mboard++)
|
|
{
|
|
uhd::time_spec_t mboard_time = s->usrp->get_time_last_pps(mboard);
|
|
if (mboard_time != mboard0_time)
|
|
{
|
|
all_matched = false;
|
|
std::cerr << (boost::format("ERROR: Times are not aligned: USRP 0=%0.9f, USRP %d=%0.9f")
|
|
% mboard0_time.get_real_secs()
|
|
% mboard
|
|
% mboard_time.get_real_secs()) << std::endl;
|
|
}
|
|
}
|
|
if (all_matched)
|
|
{
|
|
std::cout << "SUCCESS: USRP times aligned" << std::endl << std::endl;
|
|
} else {
|
|
std::cout << "ERROR: USRP times are not aligned" << std::endl << std::endl;
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::cout << boost::format("\nError: %s") % e.what();
|
|
std::cout << boost::format("This could mean that you have not installed the GPSDO correctly.\n\n");
|
|
std::cout << boost::format("Visit one of these pages if the problem persists:\n");
|
|
std::cout << boost::format(" * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html");
|
|
std::cout << boost::format(" * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n");
|
|
std::cout << boost::format(" * E3X0: http://files.ettus.com/manual/page_usrp_e3x0.html#e3x0_hw_gps\n\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
#if defined(USRP_REC_PLAY)
|
|
#include "usrp_lib.h"
|
|
static FILE *pFile = NULL;
|
|
int mmapfd = 0;
|
|
struct stat sb;
|
|
iqrec_t *ms_sample = NULL; // memory for all subframes
|
|
unsigned int nb_samples = 0;
|
|
unsigned int cur_samples = 0;
|
|
int64_t wrap_count = 0;
|
|
int64_t wrap_ts = 0;
|
|
unsigned int u_sf_mode = 0; // 1=record, 2=replay
|
|
unsigned int u_sf_record = 0; // record mode
|
|
unsigned int u_sf_replay = 0; // replay mode
|
|
char u_sf_filename[1024]; // subframes file path
|
|
unsigned int u_sf_max = DEF_NB_SF; // max number of recorded subframes
|
|
unsigned int u_sf_loops = DEF_SF_NB_LOOP; // number of loops in replay mode
|
|
unsigned int u_sf_read_delay = DEF_SF_DELAY_READ; // read delay in replay mode
|
|
unsigned int u_sf_write_delay = DEF_SF_DELAY_WRITE; // write delay in replay mode
|
|
char *tmp_filename[1]; // use an array of pointer (libconfig does not seems to work with char array yet)
|
|
|
|
char config_opt_sf_file[] = CONFIG_OPT_SF_FILE;
|
|
char config_def_sf_file[] = DEF_SF_FILE;
|
|
char config_hlp_sf_file[] = CONFIG_HLP_SF_FILE;
|
|
char config_opt_sf_rec[] = CONFIG_OPT_SF_REC;
|
|
char config_hlp_sf_rec[] = CONFIG_HLP_SF_REC;
|
|
char config_opt_sf_rep[] = CONFIG_OPT_SF_REP;
|
|
char config_hlp_sf_rep[] = CONFIG_HLP_SF_REP;
|
|
char config_opt_sf_max[] = CONFIG_OPT_SF_MAX;
|
|
char config_hlp_sf_max[] = CONFIG_HLP_SF_MAX;
|
|
char config_opt_sf_loops[] = CONFIG_OPT_SF_LOOPS;
|
|
char config_hlp_sf_loops[] = CONFIG_HLP_SF_LOOPS;
|
|
char config_opt_sf_rdelay[] = CONFIG_OPT_SF_RDELAY;
|
|
char config_hlp_sf_rdelay[] = CONFIG_HLP_SF_RDELAY;
|
|
char config_opt_sf_wdelay[] = CONFIG_OPT_SF_WDELAY;
|
|
char config_hlp_sf_wdelay[] = CONFIG_HLP_SF_WDELAY;
|
|
|
|
#endif
|
|
|
|
/*! \brief Called to start the USRP transceiver. Return 0 if OK, < 0 if error
|
|
@param device pointer to the device structure specific to the RF hardware target
|
|
*/
|
|
static int trx_usrp_start(openair0_device *device) {
|
|
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode != 2) { // not replay mode
|
|
#endif
|
|
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
|
|
|
|
// setup GPIO for TDD, GPIO(4) = ATR_RX
|
|
//set data direction register (DDR) to output
|
|
s->usrp->set_gpio_attr("FP0", "DDR", 0x1f, 0x1f);
|
|
|
|
//set control register to ATR
|
|
s->usrp->set_gpio_attr("FP0", "CTRL", 0x1f,0x1f);
|
|
|
|
//set ATR register
|
|
s->usrp->set_gpio_attr("FP0", "ATR_RX", 1<<4, 0x1f);
|
|
|
|
// init recv and send streaming
|
|
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
|
|
LOG_I(PHY,"Time in secs now: %llu \n", s->usrp->get_time_now().to_ticks(s->sample_rate));
|
|
LOG_I(PHY,"Time in secs last pps: %llu \n", s->usrp->get_time_last_pps().to_ticks(s->sample_rate));
|
|
|
|
if (s->use_gps == 1) {
|
|
s->wait_for_first_pps = 1;
|
|
cmd.time_spec = s->usrp->get_time_last_pps() + uhd::time_spec_t(1.0);
|
|
}
|
|
else {
|
|
s->wait_for_first_pps = 0;
|
|
cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
|
|
}
|
|
|
|
cmd.stream_now = false; // start at constant delay
|
|
s->rx_stream->issue_stream_cmd(cmd);
|
|
|
|
s->tx_md.time_spec = cmd.time_spec + uhd::time_spec_t(1-(double)s->tx_forward_nsamps/s->sample_rate);
|
|
s->tx_md.has_time_spec = true;
|
|
s->tx_md.start_of_burst = true;
|
|
s->tx_md.end_of_burst = false;
|
|
|
|
s->rx_count = 0;
|
|
s->tx_count = 0;
|
|
s->rx_timestamp = 0;
|
|
#if defined(USRP_REC_PLAY)
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
|
|
* \param device the hardware to use
|
|
*/
|
|
static void trx_usrp_end(openair0_device *device) {
|
|
#if defined(USRP_REC_PLAY) // For some ugly reason, this can be called several times...
|
|
static int done = 0;
|
|
if (done == 1) return;
|
|
done = 1;
|
|
if (u_sf_mode != 2) { // not subframes replay
|
|
#endif
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
|
|
s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
|
|
//send a mini EOB packet
|
|
s->tx_md.end_of_burst = true;
|
|
s->tx_stream->send("", 0, s->tx_md);
|
|
s->tx_md.end_of_burst = false;
|
|
#if defined(USRP_REC_PLAY)
|
|
}
|
|
#endif
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode == 1) { // subframes store
|
|
pFile = fopen (u_sf_filename,"wb+");
|
|
if (pFile == NULL) {
|
|
std::cerr << "Cannot open " << u_sf_filename << std::endl;
|
|
} else {
|
|
unsigned int i = 0;
|
|
unsigned int modu = 0;
|
|
if ((modu = nb_samples % 10) != 0) {
|
|
nb_samples -= modu; // store entire number of frames
|
|
}
|
|
std::cerr << "Writing " << nb_samples << " subframes to " << u_sf_filename << " ..." << std::endl;
|
|
for (i = 0; i < nb_samples; i++) {
|
|
fwrite(ms_sample+i, sizeof(unsigned char), sizeof(iqrec_t), pFile);
|
|
}
|
|
fclose (pFile);
|
|
std::cerr << "File " << u_sf_filename << " closed." << std::endl;
|
|
}
|
|
}
|
|
if (u_sf_mode == 1) { // record
|
|
if (ms_sample != NULL) {
|
|
free((void*)ms_sample);
|
|
ms_sample = NULL;
|
|
}
|
|
}
|
|
if (u_sf_mode == 2) { // replay
|
|
if (ms_sample != MAP_FAILED) {
|
|
munmap(ms_sample, sb.st_size);
|
|
ms_sample = NULL;
|
|
}
|
|
if (mmapfd != 0) {
|
|
close(mmapfd);
|
|
mmapfd = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*! \brief Called to send samples to the USRP RF target
|
|
@param device pointer to the device structure specific to the RF hardware target
|
|
@param timestamp The timestamp at which the first sample MUST be sent
|
|
@param buff Buffer which holds the samples
|
|
@param nsamps number of samples to be sent
|
|
@param antenna_id index of the antenna if the device has multiple antennas
|
|
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
|
|
*/
|
|
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
|
int ret=0;
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode != 2) { // not replay mode
|
|
#endif
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
|
|
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
|
#if defined(__x86_64) || defined(__i386__)
|
|
#ifdef __AVX2__
|
|
nsamps2 = (nsamps+7)>>3;
|
|
__m256i buff_tx[2][nsamps2];
|
|
#else
|
|
nsamps2 = (nsamps+3)>>2;
|
|
__m128i buff_tx[2][nsamps2];
|
|
#endif
|
|
#elif defined(__arm__)
|
|
nsamps2 = (nsamps+3)>>2;
|
|
int16x8_t buff_tx[2][nsamps2];
|
|
#endif
|
|
|
|
// bring RX data into 12 LSBs for softmodem RX
|
|
for (int i=0; i<cc; i++) {
|
|
for (int j=0; j<nsamps2; j++) {
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
#ifdef __AVX2__
|
|
buff_tx[i][j] = _mm256_slli_epi16(((__m256i*)buff[i])[j],4);
|
|
#else
|
|
buff_tx[i][j] = _mm_slli_epi16(((__m128i*)buff[i])[j],4);
|
|
#endif
|
|
#elif defined(__arm__)
|
|
buff_tx[i][j] = vshlq_n_s16(((int16x8_t*)buff[i])[j],4);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
|
|
s->tx_md.has_time_spec = flags;
|
|
|
|
|
|
if(flags>0)
|
|
s->tx_md.has_time_spec = true;
|
|
else
|
|
s->tx_md.has_time_spec = false;
|
|
|
|
if (flags == 2) { // start of burst
|
|
s->tx_md.start_of_burst = true;
|
|
s->tx_md.end_of_burst = false;
|
|
} else if (flags == 3) { // end of burst
|
|
s->tx_md.start_of_burst = false;
|
|
s->tx_md.end_of_burst = true;
|
|
} else if (flags == 4) { // start and end
|
|
s->tx_md.start_of_burst = true;
|
|
s->tx_md.end_of_burst = true;
|
|
} else if (flags==1) { // middle of burst
|
|
s->tx_md.start_of_burst = false;
|
|
s->tx_md.end_of_burst = false;
|
|
}
|
|
|
|
if (cc>1) {
|
|
std::vector<void *> buff_ptrs;
|
|
for (int i=0; i<cc; i++)
|
|
buff_ptrs.push_back(buff_tx[i]);
|
|
ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md,1e-3);
|
|
} else
|
|
ret = (int)s->tx_stream->send(buff_tx[0], nsamps, s->tx_md,1e-3);
|
|
|
|
|
|
|
|
if (ret != nsamps)
|
|
LOG_E(PHY,"[xmit] tx samples %d != %d\n",ret,nsamps);
|
|
#if defined(USRP_REC_PLAY)
|
|
} else {
|
|
struct timespec req;
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = u_sf_write_delay * 1000;
|
|
nanosleep(&req, NULL);
|
|
ret = nsamps;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*! \brief Receive samples from hardware.
|
|
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
|
|
* the first channel. *ptimestamp is the time at which the first sample
|
|
* was received.
|
|
* \param device the hardware to use
|
|
* \param[out] ptimestamp the time at which the first sample was received.
|
|
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
|
|
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
|
|
* \param antenna_id Index of antenna for which to receive samples
|
|
* \returns the number of sample read
|
|
*/
|
|
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
int samples_received=0,i,j;
|
|
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode != 2) { // not replay mode
|
|
#endif
|
|
#if defined(__x86_64) || defined(__i386__)
|
|
#ifdef __AVX2__
|
|
nsamps2 = (nsamps+7)>>3;
|
|
__m256i buff_tmp[2][nsamps2];
|
|
#else
|
|
nsamps2 = (nsamps+3)>>2;
|
|
__m128i buff_tmp[2][nsamps2];
|
|
#endif
|
|
#elif defined(__arm__)
|
|
nsamps2 = (nsamps+3)>>2;
|
|
int16x8_t buff_tmp[2][nsamps2];
|
|
#endif
|
|
|
|
if (device->type == USRP_B200_DEV) {
|
|
if (cc>1) {
|
|
// receive multiple channels (e.g. RF A and RF B)
|
|
std::vector<void *> buff_ptrs;
|
|
for (int i=0; i<cc; i++) buff_ptrs.push_back(buff_tmp[i]);
|
|
samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md);
|
|
} else {
|
|
// receive a single channel (e.g. from connector RF A)
|
|
samples_received=0;
|
|
while (samples_received != nsamps) {
|
|
samples_received += s->rx_stream->recv(buff_tmp[0]+samples_received,
|
|
nsamps-samples_received, s->rx_md);
|
|
if ((s->wait_for_first_pps == 0) && (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE))
|
|
break;
|
|
if ((s->wait_for_first_pps == 1) && (samples_received != nsamps)) { printf("sleep...\n");} //usleep(100);
|
|
}
|
|
if (samples_received == nsamps) s->wait_for_first_pps=0;
|
|
}
|
|
// bring RX data into 12 LSBs for softmodem RX
|
|
for (int i=0; i<cc; i++) {
|
|
for (int j=0; j<nsamps2; j++) {
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
#ifdef __AVX2__
|
|
((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
|
|
#else
|
|
((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
|
|
#endif
|
|
#elif defined(__arm__)
|
|
((int16x8_t*)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
|
|
#endif
|
|
}
|
|
}
|
|
} else if (device->type == USRP_X300_DEV) {
|
|
if (cc>1) {
|
|
// receive multiple channels (e.g. RF A and RF B)
|
|
std::vector<void *> buff_ptrs;
|
|
|
|
for (int i=0; i<cc; i++) buff_ptrs.push_back(buff[i]);
|
|
samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md);
|
|
} else {
|
|
// receive a single channel (e.g. from connector RF A)
|
|
samples_received = s->rx_stream->recv(buff[0], nsamps, s->rx_md);
|
|
}
|
|
}
|
|
if (samples_received < nsamps)
|
|
LOG_E(PHY,"[recv] received %d samples out of %d\n",samples_received,nsamps);
|
|
|
|
if ( s->rx_md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
|
|
LOG_E(PHY, "%s\n", s->rx_md.to_pp_string(true).c_str());
|
|
|
|
s->rx_count += nsamps;
|
|
s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
|
|
*ptimestamp = s->rx_timestamp;
|
|
#if defined (USRP_REC_PLAY)
|
|
}
|
|
#endif
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode == 1) { // record mode
|
|
// Copy subframes to memory (later dump on a file)
|
|
if (nb_samples < u_sf_max) {
|
|
(ms_sample+nb_samples)->header = BELL_LABS_IQ_HEADER;
|
|
(ms_sample+nb_samples)->ts = *ptimestamp;
|
|
memcpy((ms_sample+nb_samples)->samples, buff[0], nsamps*4);
|
|
nb_samples++;
|
|
}
|
|
} else if (u_sf_mode == 2) { // replay mode
|
|
if (cur_samples == nb_samples) {
|
|
cur_samples = 0;
|
|
wrap_count++;
|
|
if (wrap_count == u_sf_loops) {
|
|
std::cerr << "USRP device terminating subframes replay mode after " << u_sf_loops << " loops." << std::endl;
|
|
return 0; // should make calling process exit
|
|
}
|
|
wrap_ts = wrap_count * (nb_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000));
|
|
}
|
|
if (cur_samples < nb_samples) {
|
|
*ptimestamp = (ms_sample[0].ts + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000))) + wrap_ts;
|
|
if (cur_samples == 0) {
|
|
std::cerr << "starting subframes file with wrap_count=" << wrap_count << " wrap_ts=" << wrap_ts
|
|
<< " ts=" << *ptimestamp << std::endl;
|
|
}
|
|
memcpy(buff[0], &ms_sample[cur_samples].samples[0], nsamps*4);
|
|
cur_samples++;
|
|
}
|
|
struct timespec req;
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = u_sf_read_delay * 1000;
|
|
nanosleep(&req, NULL);
|
|
return nsamps;
|
|
}
|
|
#endif
|
|
return samples_received;
|
|
}
|
|
|
|
/*! \brief Compares two variables within precision
|
|
* \param a first variable
|
|
* \param b second variable
|
|
*/
|
|
static bool is_equal(double a, double b) {
|
|
return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
|
|
}
|
|
|
|
void *freq_thread(void *arg) {
|
|
|
|
openair0_device *device=(openair0_device *)arg;
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
|
|
s->usrp->set_tx_freq(device->openair0_cfg[0].tx_freq[0]);
|
|
s->usrp->set_rx_freq(device->openair0_cfg[0].rx_freq[0]);
|
|
}
|
|
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
|
|
* \param device the hardware to use
|
|
* \param openair0_cfg RF frontend parameters set by application
|
|
* \param dummy dummy variable not used
|
|
* \returns 0 in success
|
|
*/
|
|
int trx_usrp_set_freq(openair0_device* device, openair0_config_t *openair0_cfg, int dont_block) {
|
|
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
pthread_t f_thread;
|
|
|
|
printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);
|
|
|
|
// spawn a thread to handle the frequency change to not block the calling thread
|
|
if (dont_block == 1)
|
|
pthread_create(&f_thread,NULL,freq_thread,(void*)device);
|
|
else {
|
|
s->usrp->set_tx_freq(device->openair0_cfg[0].tx_freq[0]);
|
|
s->usrp->set_rx_freq(device->openair0_cfg[0].rx_freq[0]);
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
/*! \brief Set RX frequencies
|
|
* \param device the hardware to use
|
|
* \param openair0_cfg RF frontend parameters set by application
|
|
* \returns 0 in success
|
|
*/
|
|
int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg) {
|
|
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
static int first_call=1;
|
|
static double rf_freq,diff;
|
|
|
|
uhd::tune_request_t rx_tune_req(openair0_cfg[0].rx_freq[0]);
|
|
|
|
rx_tune_req.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
|
rx_tune_req.rf_freq = openair0_cfg[0].rx_freq[0];
|
|
rf_freq=openair0_cfg[0].rx_freq[0];
|
|
s->usrp->set_rx_freq(rx_tune_req);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
/*! \brief Set Gains (TX/RX)
|
|
* \param device the hardware to use
|
|
* \param openair0_cfg RF frontend parameters set by application
|
|
* \returns 0 in success
|
|
*/
|
|
int trx_usrp_set_gains(openair0_device* device,
|
|
openair0_config_t *openair0_cfg) {
|
|
|
|
usrp_state_t *s = (usrp_state_t*)device->priv;
|
|
::uhd::gain_range_t gain_range_tx = s->usrp->get_tx_gain_range(0);
|
|
s->usrp->set_tx_gain(gain_range_tx.stop()-openair0_cfg[0].tx_gain[0]);
|
|
::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(0);
|
|
// limit to maximum gain
|
|
if (openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0] > gain_range.stop()) {
|
|
LOG_E(PHY,"RX Gain 0 too high, reduce by %f dB\n",
|
|
openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0] - gain_range.stop());
|
|
exit(-1);
|
|
}
|
|
s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0]);
|
|
LOG_I(PHY,"Setting USRP RX gain to %f (rx_gain %f,gain_range.stop() %f)\n",
|
|
openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0],
|
|
openair0_cfg[0].rx_gain[0],gain_range.stop());
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*! \brief Stop USRP
|
|
* \param card refers to the hardware index to use
|
|
*/
|
|
int trx_usrp_stop(openair0_device* device) {
|
|
return(0);
|
|
}
|
|
|
|
/*! \brief USRPB210 RX calibration table */
|
|
rx_gain_calib_table_t calib_table_b210[] = {
|
|
{3500000000.0,44.0},
|
|
{2660000000.0,49.0},
|
|
{2300000000.0,50.0},
|
|
{1880000000.0,53.0},
|
|
{816000000.0,58.0},
|
|
{-1,0}
|
|
};
|
|
|
|
/*! \brief USRPB210 RX calibration table */
|
|
rx_gain_calib_table_t calib_table_b210_38[] = {
|
|
{3500000000.0,44.0},
|
|
{2660000000.0,49.8},
|
|
{2300000000.0,51.0},
|
|
{1880000000.0,53.0},
|
|
{816000000.0,57.0},
|
|
{-1,0}
|
|
};
|
|
|
|
/*! \brief USRPx310 RX calibration table */
|
|
rx_gain_calib_table_t calib_table_x310[] = {
|
|
{3500000000.0,77.0},
|
|
{2660000000.0,81.0},
|
|
{2300000000.0,81.0},
|
|
{1880000000.0,82.0},
|
|
{816000000.0,85.0},
|
|
{-1,0}
|
|
};
|
|
|
|
/*! \brief Set RX gain offset
|
|
* \param openair0_cfg RF frontend parameters set by application
|
|
* \param chain_index RF chain to apply settings to
|
|
* \returns 0 in success
|
|
*/
|
|
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
|
|
|
|
int i=0;
|
|
// loop through calibration table to find best adjustment factor for RX frequency
|
|
double min_diff = 6e9,diff,gain_adj=0.0;
|
|
if (bw_gain_adjust==1) {
|
|
switch ((int)openair0_cfg[0].sample_rate) {
|
|
case 30720000:
|
|
break;
|
|
case 23040000:
|
|
gain_adj=1.25;
|
|
break;
|
|
case 15360000:
|
|
gain_adj=3.0;
|
|
break;
|
|
case 7680000:
|
|
gain_adj=6.0;
|
|
break;
|
|
case 3840000:
|
|
gain_adj=9.0;
|
|
break;
|
|
case 1920000:
|
|
gain_adj=12.0;
|
|
break;
|
|
default:
|
|
LOG_E(PHY,"unknown sampling rate %d\n",(int)openair0_cfg[0].sample_rate);
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
while (openair0_cfg->rx_gain_calib_table[i].freq>0) {
|
|
diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq);
|
|
LOG_I(PHY,"cal %d: freq %f, offset %f, diff %f\n",
|
|
i,
|
|
openair0_cfg->rx_gain_calib_table[i].freq,
|
|
openair0_cfg->rx_gain_calib_table[i].offset,diff);
|
|
if (min_diff > diff) {
|
|
min_diff = diff;
|
|
openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/*! \brief print the USRP statistics
|
|
* \param device the hardware to use
|
|
* \returns 0 on success
|
|
*/
|
|
int trx_usrp_get_stats(openair0_device* device) {
|
|
return(0);
|
|
}
|
|
|
|
/*! \brief Reset the USRP statistics
|
|
* \param device the hardware to use
|
|
* \returns 0 on success
|
|
*/
|
|
int trx_usrp_reset_stats(openair0_device* device) {
|
|
return(0);
|
|
}
|
|
|
|
#if defined(USRP_REC_PLAY)
|
|
extern "C" {
|
|
/*! \brief Initializer for USRP record/playback config
|
|
* \param parameter array description
|
|
* \returns 0 on success
|
|
*/
|
|
int trx_usrp_recplay_config_init(paramdef_t *usrp_recplay_params) {
|
|
// --subframes-file
|
|
memcpy(usrp_recplay_params[0].optname, config_opt_sf_file, strlen(config_opt_sf_file));
|
|
usrp_recplay_params[0].helpstr = config_hlp_sf_file;
|
|
usrp_recplay_params[0].paramflags=PARAMFLAG_NOFREE;
|
|
usrp_recplay_params[0].strptr=(char **)&tmp_filename[0];
|
|
usrp_recplay_params[0].defstrval = NULL;
|
|
usrp_recplay_params[0].type=TYPE_STRING;
|
|
usrp_recplay_params[0].numelt=sizeof(u_sf_filename);
|
|
// --subframes-record
|
|
memcpy(usrp_recplay_params[1].optname, config_opt_sf_rec, strlen(config_opt_sf_rec));
|
|
usrp_recplay_params[1].helpstr = config_hlp_sf_rec;
|
|
usrp_recplay_params[1].paramflags=PARAMFLAG_BOOL;
|
|
usrp_recplay_params[1].uptr=&u_sf_record;
|
|
usrp_recplay_params[1].defuintval=0;
|
|
usrp_recplay_params[1].type=TYPE_UINT;
|
|
usrp_recplay_params[1].numelt=0;
|
|
// --subframes-replay
|
|
memcpy(usrp_recplay_params[2].optname, config_opt_sf_rep, strlen(config_opt_sf_rep));
|
|
usrp_recplay_params[2].helpstr = config_hlp_sf_rep;
|
|
usrp_recplay_params[2].paramflags=PARAMFLAG_BOOL;
|
|
usrp_recplay_params[2].uptr=&u_sf_replay;
|
|
usrp_recplay_params[2].defuintval=0;
|
|
usrp_recplay_params[2].type=TYPE_UINT;
|
|
usrp_recplay_params[2].numelt=0;
|
|
// --subframes-max
|
|
memcpy(usrp_recplay_params[3].optname, config_opt_sf_max, strlen(config_opt_sf_max));
|
|
usrp_recplay_params[3].helpstr = config_hlp_sf_max;
|
|
usrp_recplay_params[3].paramflags=0;
|
|
usrp_recplay_params[3].uptr=&u_sf_max;
|
|
usrp_recplay_params[3].defuintval=DEF_NB_SF;
|
|
usrp_recplay_params[3].type=TYPE_UINT;
|
|
usrp_recplay_params[3].numelt=0;
|
|
// --subframes-loops
|
|
memcpy(usrp_recplay_params[4].optname, config_opt_sf_loops, strlen(config_opt_sf_loops));
|
|
usrp_recplay_params[4].helpstr = config_hlp_sf_loops;
|
|
usrp_recplay_params[4].paramflags=0;
|
|
usrp_recplay_params[4].uptr=&u_sf_loops;
|
|
usrp_recplay_params[4].defuintval=DEF_SF_NB_LOOP;
|
|
usrp_recplay_params[4].type=TYPE_UINT;
|
|
usrp_recplay_params[4].numelt=0;
|
|
// --subframes-read-delay
|
|
memcpy(usrp_recplay_params[5].optname, config_opt_sf_rdelay, strlen(config_opt_sf_rdelay));
|
|
usrp_recplay_params[5].helpstr = config_hlp_sf_rdelay;
|
|
usrp_recplay_params[5].paramflags=0;
|
|
usrp_recplay_params[5].uptr=&u_sf_read_delay;
|
|
usrp_recplay_params[5].defuintval=DEF_SF_DELAY_READ;
|
|
usrp_recplay_params[5].type=TYPE_UINT;
|
|
usrp_recplay_params[5].numelt=0;
|
|
// --subframes-write-delay
|
|
memcpy(usrp_recplay_params[6].optname, config_opt_sf_wdelay, strlen(config_opt_sf_wdelay));
|
|
usrp_recplay_params[6].helpstr = config_hlp_sf_wdelay;
|
|
usrp_recplay_params[6].paramflags=0;
|
|
usrp_recplay_params[6].uptr=&u_sf_write_delay;
|
|
usrp_recplay_params[6].defuintval=DEF_SF_DELAY_WRITE;
|
|
usrp_recplay_params[6].type=TYPE_UINT;
|
|
usrp_recplay_params[6].numelt=0;
|
|
|
|
return 0; // always ok
|
|
}
|
|
}
|
|
#endif
|
|
|
|
extern "C" {
|
|
/*! \brief Initialize Openair USRP target. It returns 0 if OK
|
|
* \param device the hardware to use
|
|
* \param openair0_cfg RF frontend parameters set by application
|
|
*/
|
|
int device_init(openair0_device* device, openair0_config_t *openair0_cfg) {
|
|
#if defined(USRP_REC_PLAY)
|
|
paramdef_t usrp_recplay_params[7];
|
|
// to check
|
|
static int done = 0;
|
|
if (done == 1) {
|
|
return 0;
|
|
} // prevent from multiple init
|
|
done = 1;
|
|
// end to check
|
|
memset(usrp_recplay_params, 0, 7*sizeof(paramdef_t));
|
|
memset(&u_sf_filename[0], 0, 1024);
|
|
tmp_filename[0] = u_sf_filename;
|
|
if (trx_usrp_recplay_config_init(usrp_recplay_params) != 0) {
|
|
std::cerr << "USRP device record/replay mode configuration error exiting" << std::endl;
|
|
return -1;
|
|
}
|
|
config_process_cmdline(usrp_recplay_params,sizeof(usrp_recplay_params)/sizeof(paramdef_t),NULL);
|
|
|
|
if (strlen(tmp_filename[0]) != 0) {
|
|
(void) strcpy(u_sf_filename, tmp_filename[0]);
|
|
} else {
|
|
(void) strcpy(u_sf_filename, DEF_SF_FILE);
|
|
}
|
|
|
|
if (u_sf_replay == 1) u_sf_mode = 2;
|
|
if (u_sf_record == 1) u_sf_mode = 1;
|
|
|
|
if (u_sf_mode == 2) {
|
|
// Replay subframes from from file
|
|
int bw_gain_adjust=0;
|
|
device->openair0_cfg = openair0_cfg;
|
|
device->type = USRP_B200_DEV;
|
|
openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
|
|
bw_gain_adjust=1;
|
|
openair0_cfg[0].tx_sample_advance = 80;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
openair0_cfg[0].iq_txshift = 4;//shift
|
|
openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
|
set_rx_gain_offset(&openair0_cfg[0],0,bw_gain_adjust);
|
|
device->priv = NULL;
|
|
device->trx_start_func = trx_usrp_start;
|
|
device->trx_write_func = trx_usrp_write;
|
|
device->trx_read_func = trx_usrp_read;
|
|
device->trx_get_stats_func = trx_usrp_get_stats;
|
|
device->trx_reset_stats_func = trx_usrp_reset_stats;
|
|
device->trx_end_func = trx_usrp_end;
|
|
device->trx_stop_func = trx_usrp_stop;
|
|
device->trx_set_freq_func = trx_usrp_set_freq;
|
|
device->trx_set_gains_func = trx_usrp_set_gains;
|
|
device->openair0_cfg = openair0_cfg;
|
|
std::cerr << "USRP device initialized in subframes replay mode for " << u_sf_loops << " loops." << std::endl;
|
|
} else {
|
|
#endif
|
|
uhd::set_thread_priority_safe(1.0);
|
|
usrp_state_t *s = (usrp_state_t*)calloc(sizeof(usrp_state_t),1);
|
|
|
|
if (openair0_cfg[0].clock_source==gpsdo)
|
|
s->use_gps =1;
|
|
|
|
// Initialize USRP device
|
|
device->openair0_cfg = openair0_cfg;
|
|
|
|
std::string args = "type=b200";
|
|
uhd::device_addrs_t device_adds = uhd::device::find(args);
|
|
|
|
int vers=0,subvers=0,subsubvers=0;
|
|
int bw_gain_adjust=0;
|
|
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode == 1) {
|
|
std::cerr << "USRP device initialized in subframes record mode" << std::endl;
|
|
}
|
|
#endif
|
|
sscanf(uhd::get_version_string().c_str(),"%d.%d.%d",&vers,&subvers,&subsubvers);
|
|
LOG_I(PHY,"Checking for USRPs : UHD %s (%d.%d.%d)\n",
|
|
uhd::get_version_string().c_str(),vers,subvers,subsubvers);
|
|
|
|
if(device_adds.size() == 0) {
|
|
double usrp_master_clock = 184.32e6;
|
|
std::string args = "type=x300";
|
|
|
|
// workaround for an api problem, master clock has to be set with the constructor not via set_master_clock_rate
|
|
args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
|
|
|
|
// args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
|
|
|
|
// args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
|
|
uhd::device_addrs_t device_adds = uhd::device::find(args);
|
|
|
|
if(device_adds.size() == 0) {
|
|
std::cerr<<"No USRP Device Found. " << std::endl;
|
|
free(s);
|
|
return -1;
|
|
}
|
|
LOG_I(PHY,"Found USRP X300\n");
|
|
s->usrp = uhd::usrp::multi_usrp::make(args);
|
|
// lock mboard clocks
|
|
if (openair0_cfg[0].clock_source == internal)
|
|
s->usrp->set_clock_source("internal");
|
|
else
|
|
s->usrp->set_clock_source("external");
|
|
|
|
//Setting device type to USRP X300/X310
|
|
device->type=USRP_X300_DEV;
|
|
|
|
// this is not working yet, master clock has to be set via constructor
|
|
// set master clock rate and sample rate for tx & rx for streaming
|
|
//s->usrp->set_master_clock_rate(usrp_master_clock);
|
|
|
|
openair0_cfg[0].rx_gain_calib_table = calib_table_x310;
|
|
|
|
LOG_I(PHY,"%s() sample_rate:%u\n", __FUNCTION__, (int)openair0_cfg[0].sample_rate);
|
|
|
|
switch ((int)openair0_cfg[0].sample_rate) {
|
|
case 30720000:
|
|
// from usrp_time_offset
|
|
//openair0_cfg[0].samples_per_packet = 2048;
|
|
openair0_cfg[0].tx_sample_advance = 15;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
case 15360000:
|
|
//openair0_cfg[0].samples_per_packet = 2048;
|
|
openair0_cfg[0].tx_sample_advance = 45;
|
|
openair0_cfg[0].tx_bw = 10e6;
|
|
openair0_cfg[0].rx_bw = 10e6;
|
|
break;
|
|
case 7680000:
|
|
//openair0_cfg[0].samples_per_packet = 2048;
|
|
openair0_cfg[0].tx_sample_advance = 50;
|
|
openair0_cfg[0].tx_bw = 5e6;
|
|
openair0_cfg[0].rx_bw = 5e6;
|
|
break;
|
|
case 1920000:
|
|
//openair0_cfg[0].samples_per_packet = 2048;
|
|
openair0_cfg[0].tx_sample_advance = 50;
|
|
openair0_cfg[0].tx_bw = 1.25e6;
|
|
openair0_cfg[0].rx_bw = 1.25e6;
|
|
break;
|
|
default:
|
|
LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
|
exit(-1);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
LOG_I(PHY,"Found USRP B200\n");
|
|
args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=15360, recv_frame_size=15360" ;
|
|
s->usrp = uhd::usrp::multi_usrp::make(args);
|
|
|
|
// s->usrp->set_rx_subdev_spec(rx_subdev);
|
|
// s->usrp->set_tx_subdev_spec(tx_subdev);
|
|
|
|
// do not explicitly set the clock to "internal", because this will disable the gpsdo
|
|
// // lock mboard clocks
|
|
// s->usrp->set_clock_source("internal");
|
|
// set master clock rate and sample rate for tx & rx for streaming
|
|
|
|
// lock mboard clocks
|
|
if (openair0_cfg[0].clock_source == internal){
|
|
s->usrp->set_clock_source("internal");
|
|
}
|
|
else{
|
|
s->usrp->set_clock_source("external");
|
|
s->usrp->set_time_source("external");
|
|
}
|
|
|
|
device->type = USRP_B200_DEV;
|
|
if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
|
|
openair0_cfg[0].rx_gain_calib_table = calib_table_b210;
|
|
bw_gain_adjust=0;
|
|
#if defined(USRP_REC_PLAY)
|
|
std::cerr << "-- Using calibration table: calib_table_b210" << std::endl; // Bell Labs info
|
|
#endif
|
|
} else {
|
|
openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
|
|
bw_gain_adjust=1;
|
|
#if defined(USRP_REC_PLAY)
|
|
std::cerr << "-- Using calibration table: calib_table_b210_38" << std::endl; // Bell Labs info
|
|
#endif
|
|
}
|
|
|
|
switch ((int)openair0_cfg[0].sample_rate) {
|
|
case 30720000:
|
|
s->usrp->set_master_clock_rate(30.72e6);
|
|
//openair0_cfg[0].samples_per_packet = 1024;
|
|
openair0_cfg[0].tx_sample_advance = 115;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
case 23040000:
|
|
s->usrp->set_master_clock_rate(23.04e6); //to be checked
|
|
//openair0_cfg[0].samples_per_packet = 1024;
|
|
openair0_cfg[0].tx_sample_advance = 113;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
case 15360000:
|
|
s->usrp->set_master_clock_rate(30.72e06);
|
|
//openair0_cfg[0].samples_per_packet = 1024;
|
|
openair0_cfg[0].tx_sample_advance = 103;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
case 7680000:
|
|
s->usrp->set_master_clock_rate(30.72e6);
|
|
//openair0_cfg[0].samples_per_packet = 1024;
|
|
openair0_cfg[0].tx_sample_advance = 80;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
case 1920000:
|
|
s->usrp->set_master_clock_rate(30.72e6);
|
|
//openair0_cfg[0].samples_per_packet = 1024;
|
|
openair0_cfg[0].tx_sample_advance = 40;
|
|
openair0_cfg[0].tx_bw = 20e6;
|
|
openair0_cfg[0].rx_bw = 20e6;
|
|
break;
|
|
default:
|
|
LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* device specific */
|
|
//openair0_cfg[0].txlaunch_wait = 1;//manage when TX processing is triggered
|
|
//openair0_cfg[0].txlaunch_wait_slotcount = 1; //manage when TX processing is triggered
|
|
openair0_cfg[0].iq_txshift = 4;//shift
|
|
openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
|
|
|
for(int i=0; i<s->usrp->get_rx_num_channels(); i++) {
|
|
if (i<openair0_cfg[0].rx_num_channels) {
|
|
s->usrp->set_rx_rate(openair0_cfg[0].sample_rate,i);
|
|
s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[i],i);
|
|
set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust);
|
|
|
|
::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
|
|
// limit to maximum gain
|
|
AssertFatal( openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] <= gain_range.stop(),
|
|
"RX Gain too high, lower by %f dB\n",
|
|
openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] - gain_range.stop());
|
|
s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],i);
|
|
LOG_I(PHY,"RX Gain %d %f (%f) => %f (max %f)\n",i,
|
|
openair0_cfg[0].rx_gain[i],openair0_cfg[0].rx_gain_offset[i],
|
|
openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],gain_range.stop());
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<s->usrp->get_tx_num_channels(); i++) {
|
|
::uhd::gain_range_t gain_range_tx = s->usrp->get_tx_gain_range(i);
|
|
if (i<openair0_cfg[0].tx_num_channels) {
|
|
s->usrp->set_tx_rate(openair0_cfg[0].sample_rate,i);
|
|
s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[i],i);
|
|
s->usrp->set_tx_gain(gain_range_tx.stop()-openair0_cfg[0].tx_gain[i],i);
|
|
|
|
LOG_I(PHY,"USRP TX_GAIN:%3.2lf gain_range:%3.2lf tx_gain:%3.2lf\n", gain_range_tx.stop()-openair0_cfg[0].tx_gain[i], gain_range_tx.stop(), openair0_cfg[0].tx_gain[i]);
|
|
}
|
|
}
|
|
|
|
//s->usrp->set_clock_source("external");
|
|
//s->usrp->set_time_source("external");
|
|
|
|
// display USRP settings
|
|
LOG_I(PHY,"Actual master clock: %fMHz...\n",s->usrp->get_master_clock_rate()/1e6);
|
|
sleep(1);
|
|
|
|
// create tx & rx streamer
|
|
uhd::stream_args_t stream_args_rx("sc16", "sc16");
|
|
int samples=openair0_cfg[0].sample_rate;
|
|
int max=s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps();
|
|
samples/=10000;
|
|
LOG_I(PHY,"RF board max packet size %u, size for 100µs jitter %d \n", max, samples);
|
|
if ( samples < max )
|
|
stream_args_rx.args["spp"] = str(boost::format("%d") % samples );
|
|
LOG_I(PHY,"rx_max_num_samps %zu\n",
|
|
s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps());
|
|
|
|
for (int i = 0; i<openair0_cfg[0].rx_num_channels; i++)
|
|
stream_args_rx.channels.push_back(i);
|
|
s->rx_stream = s->usrp->get_rx_stream(stream_args_rx);
|
|
|
|
uhd::stream_args_t stream_args_tx("sc16", "sc16");
|
|
for (int i = 0; i<openair0_cfg[0].tx_num_channels; i++)
|
|
stream_args_tx.channels.push_back(i);
|
|
s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);
|
|
|
|
/* Setting TX/RX BW after streamers are created due to USRP calibration issue */
|
|
for(int i=0; i<s->usrp->get_tx_num_channels() && i<openair0_cfg[0].tx_num_channels; i++)
|
|
s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
|
|
|
|
for(int i=0; i<s->usrp->get_rx_num_channels() && i<openair0_cfg[0].rx_num_channels; i++)
|
|
s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
|
|
|
|
for (int i=0; i<openair0_cfg[0].rx_num_channels; i++) {
|
|
LOG_I(PHY,"RX Channel %d\n",i);
|
|
LOG_I(PHY," Actual RX sample rate: %fMSps...\n",s->usrp->get_rx_rate(i)/1e6);
|
|
LOG_I(PHY," Actual RX frequency: %fGHz...\n", s->usrp->get_rx_freq(i)/1e9);
|
|
LOG_I(PHY," Actual RX gain: %f...\n", s->usrp->get_rx_gain(i));
|
|
LOG_I(PHY," Actual RX bandwidth: %fM...\n", s->usrp->get_rx_bandwidth(i)/1e6);
|
|
LOG_I(PHY," Actual RX antenna: %s...\n", s->usrp->get_rx_antenna(i).c_str());
|
|
}
|
|
|
|
for (int i=0; i<openair0_cfg[0].tx_num_channels; i++) {
|
|
LOG_I(PHY,"TX Channel %d\n",i);
|
|
LOG_I(PHY," Actual TX sample rate: %fMSps...\n", s->usrp->get_tx_rate(i)/1e6);
|
|
LOG_I(PHY," Actual TX frequency: %fGHz...\n", s->usrp->get_tx_freq(i)/1e9);
|
|
LOG_I(PHY," Actual TX gain: %f...\n", s->usrp->get_tx_gain(i));
|
|
LOG_I(PHY," Actual TX bandwidth: %fM...\n", s->usrp->get_tx_bandwidth(i)/1e6);
|
|
LOG_I(PHY," Actual TX antenna: %s...\n", s->usrp->get_tx_antenna(i).c_str());
|
|
}
|
|
|
|
LOG_I(PHY,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());
|
|
|
|
device->priv = s;
|
|
device->trx_start_func = trx_usrp_start;
|
|
device->trx_write_func = trx_usrp_write;
|
|
device->trx_read_func = trx_usrp_read;
|
|
device->trx_get_stats_func = trx_usrp_get_stats;
|
|
device->trx_reset_stats_func = trx_usrp_reset_stats;
|
|
device->trx_end_func = trx_usrp_end;
|
|
device->trx_stop_func = trx_usrp_stop;
|
|
device->trx_set_freq_func = trx_usrp_set_freq;
|
|
device->trx_set_gains_func = trx_usrp_set_gains;
|
|
device->openair0_cfg = openair0_cfg;
|
|
|
|
s->sample_rate = openair0_cfg[0].sample_rate;
|
|
// TODO:
|
|
// init tx_forward_nsamps based usrp_time_offset ex
|
|
if(is_equal(s->sample_rate, (double)30.72e6))
|
|
s->tx_forward_nsamps = 176;
|
|
if(is_equal(s->sample_rate, (double)15.36e6))
|
|
s->tx_forward_nsamps = 90;
|
|
if(is_equal(s->sample_rate, (double)7.68e6))
|
|
s->tx_forward_nsamps = 50;
|
|
|
|
if (s->use_gps == 1) {
|
|
if (sync_to_gps(device)) {
|
|
LOG_I(PHY,"USRP fails to sync with GPS...\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
#if defined(USRP_REC_PLAY)
|
|
}
|
|
#endif
|
|
#if defined(USRP_REC_PLAY)
|
|
if (u_sf_mode == 1) { // record mode
|
|
ms_sample = (iqrec_t*) malloc(u_sf_max * sizeof(iqrec_t));
|
|
if (ms_sample == NULL) {
|
|
std::cerr<< "Memory allocation failed for subframe record or replay mode." << std::endl;
|
|
exit(-1);
|
|
}
|
|
memset(ms_sample, 0, u_sf_max * BELL_LABS_IQ_BYTES_PER_SF);
|
|
}
|
|
if (u_sf_mode == 2) {
|
|
// use mmap
|
|
mmapfd = open(u_sf_filename, O_RDONLY | O_LARGEFILE);
|
|
if (mmapfd != 0) {
|
|
fstat(mmapfd, &sb);
|
|
std::cerr << "Loading subframes using mmap() from " << u_sf_filename << " size=" << (uint64_t)sb.st_size << " bytes ..." << std::endl;
|
|
ms_sample = (iqrec_t*) mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, mmapfd, 0);
|
|
if (ms_sample != MAP_FAILED) {
|
|
nb_samples = (sb.st_size / sizeof(iqrec_t));
|
|
int aligned = (((unsigned long)ms_sample & 31) == 0)? 1:0;
|
|
std::cerr<< "Loaded "<< nb_samples << " subframes." << std::endl;
|
|
if (aligned == 0) {
|
|
std::cerr<< "mmap address is not 32 bytes aligned, exiting." << std::endl;
|
|
close(mmapfd);
|
|
exit(-1);
|
|
}
|
|
} else {
|
|
std::cerr << "Cannot mmap file, exiting." << std::endl;
|
|
close(mmapfd);
|
|
exit(-1);
|
|
}
|
|
} else {
|
|
std::cerr << "Cannot open " << u_sf_filename << " , exiting." << std::endl;
|
|
exit(-1);
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
/*@}*/
|