mirror of https://github.com/pvnis/srsRAN_4G.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
13 KiB
C++
465 lines
13 KiB
C++
/**
|
|
*
|
|
* \section COPYRIGHT
|
|
*
|
|
* Copyright 2013-2015 Software Radio Systems Limited
|
|
*
|
|
* \section LICENSE
|
|
*
|
|
* This file is part of the srsUE library.
|
|
*
|
|
* srsUE is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* srsUE is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* A copy of the GNU Affero General Public License can be found in
|
|
* the LICENSE file in the top-level directory of this distribution
|
|
* and at http://www.gnu.org/licenses/.
|
|
*
|
|
*/
|
|
|
|
#include "srslte/srslte.h"
|
|
extern "C" {
|
|
#include "srslte/phy/rf/rf.h"
|
|
}
|
|
#include "srslte/radio/radio.h"
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
namespace srslte {
|
|
|
|
bool radio::init(char *args, char *devname, uint32_t nof_channels)
|
|
{
|
|
if (srslte_rf_open_devname(&rf_device, devname, args, nof_channels)) {
|
|
fprintf(stderr, "Error opening RF device\n");
|
|
return false;
|
|
}
|
|
|
|
tx_adv_negative = false;
|
|
agc_enabled = false;
|
|
burst_preamble_samples = 0;
|
|
burst_preamble_time_rounded = 0;
|
|
cur_tx_srate = 0;
|
|
is_start_of_burst = true;
|
|
|
|
// Suppress radio stdout
|
|
srslte_rf_suppress_stdout(&rf_device);
|
|
|
|
tx_adv_auto = true;
|
|
// Set default preamble length each known device
|
|
// We distinguish by device family, maybe we should calibrate per device
|
|
if (strstr(srslte_rf_name(&rf_device), "uhd")) {
|
|
burst_preamble_sec = uhd_default_burst_preamble_sec;
|
|
} else if (strstr(srslte_rf_name(&rf_device), "bladerf")) {
|
|
burst_preamble_sec = blade_default_burst_preamble_sec;
|
|
} else {
|
|
burst_preamble_sec = 0;
|
|
printf("\nWarning burst preamble is not calibrated for device %s. Set a value manually\n\n", srslte_rf_name(&rf_device));
|
|
}
|
|
|
|
if (args) {
|
|
strncpy(saved_args, args, 127);
|
|
}
|
|
if (devname) {
|
|
strncpy(saved_devname, devname, 127);
|
|
}
|
|
saved_nof_channels = nof_channels;
|
|
|
|
is_initialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool radio::is_init() {
|
|
return is_initialized;
|
|
}
|
|
|
|
void radio::stop()
|
|
{
|
|
srslte_rf_close(&rf_device);
|
|
}
|
|
|
|
void radio::reset()
|
|
{
|
|
printf("Resetting Radio...\n");
|
|
srslte_rf_stop_rx_stream(&rf_device);
|
|
radio_is_streaming = false;
|
|
}
|
|
|
|
void radio::set_manual_calibration(rf_cal_t* calibration)
|
|
{
|
|
srslte_rf_cal_t tx_cal;
|
|
tx_cal.dc_gain = calibration->tx_corr_dc_gain;
|
|
tx_cal.dc_phase = calibration->tx_corr_dc_phase;
|
|
tx_cal.iq_i = calibration->tx_corr_iq_i;
|
|
tx_cal.iq_q = calibration->tx_corr_iq_q;
|
|
srslte_rf_set_tx_cal(&rf_device, &tx_cal);
|
|
}
|
|
|
|
void radio::set_tx_rx_gain_offset(float offset) {
|
|
srslte_rf_set_tx_rx_gain_offset(&rf_device, offset);
|
|
}
|
|
|
|
void radio::set_burst_preamble(double preamble_us)
|
|
{
|
|
burst_preamble_sec = (double) preamble_us/1e6;
|
|
}
|
|
|
|
void radio::set_tx_adv(int nsamples)
|
|
{
|
|
tx_adv_auto = false;
|
|
tx_adv_nsamples = nsamples;
|
|
if (!nsamples) {
|
|
tx_adv_sec = 0;
|
|
}
|
|
|
|
}
|
|
|
|
void radio::set_tx_adv_neg(bool tx_adv_is_neg) {
|
|
tx_adv_negative = tx_adv_is_neg;
|
|
}
|
|
|
|
bool radio::start_agc(bool tx_gain_same_rx)
|
|
{
|
|
if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) {
|
|
fprintf(stderr, "Error opening RF device\n");
|
|
return false;
|
|
}
|
|
|
|
agc_enabled = true;
|
|
|
|
return true;
|
|
}
|
|
bool radio::rx_at(void* buffer, uint32_t nof_samples, srslte_timestamp_t rx_time)
|
|
{
|
|
fprintf(stderr, "Not implemented\n");
|
|
return false;
|
|
}
|
|
|
|
bool radio::rx_now(void* buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t* rxd_time)
|
|
{
|
|
if (!radio_is_streaming) {
|
|
srslte_rf_start_rx_stream(&rf_device, false);
|
|
radio_is_streaming = true;
|
|
}
|
|
if (srslte_rf_recv_with_time_multi(&rf_device, buffer, nof_samples, true,
|
|
rxd_time?&rxd_time->full_secs:NULL, rxd_time?&rxd_time->frac_secs:NULL) > 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void radio::get_time(srslte_timestamp_t *now) {
|
|
srslte_rf_get_time(&rf_device, &now->full_secs, &now->frac_secs);
|
|
}
|
|
|
|
// TODO: Use Calibrated values for this
|
|
float radio::set_tx_power(float power)
|
|
{
|
|
if (power > 10) {
|
|
power = 10;
|
|
}
|
|
if (power < -50) {
|
|
power = -50;
|
|
}
|
|
float gain = power + 74;
|
|
srslte_rf_set_tx_gain(&rf_device, gain);
|
|
return gain;
|
|
}
|
|
|
|
float radio::get_max_tx_power()
|
|
{
|
|
return 10;
|
|
}
|
|
|
|
float radio::get_rssi()
|
|
{
|
|
return srslte_rf_get_rssi(&rf_device);
|
|
}
|
|
|
|
bool radio::has_rssi()
|
|
{
|
|
return srslte_rf_has_rssi(&rf_device);
|
|
}
|
|
|
|
bool radio::is_first_of_burst() {
|
|
return is_start_of_burst;
|
|
}
|
|
|
|
#define BLOCKING_TX true
|
|
|
|
bool radio::tx_single(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) {
|
|
void *_buffer[SRSLTE_MAX_PORTS];
|
|
|
|
_buffer[0] = buffer;
|
|
for (int p = 1; p < SRSLTE_MAX_PORTS; p++) {
|
|
_buffer[p] = zeros;
|
|
}
|
|
|
|
return this->tx(_buffer, nof_samples, tx_time);
|
|
}
|
|
|
|
bool radio::tx(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time) {
|
|
if (!tx_adv_negative) {
|
|
srslte_timestamp_sub(&tx_time, 0, tx_adv_sec);
|
|
} else {
|
|
srslte_timestamp_add(&tx_time, 0, tx_adv_sec);
|
|
}
|
|
|
|
if (is_start_of_burst) {
|
|
if (burst_preamble_samples != 0) {
|
|
srslte_timestamp_t tx_time_pad;
|
|
srslte_timestamp_copy(&tx_time_pad, &tx_time);
|
|
srslte_timestamp_sub(&tx_time_pad, 0, burst_preamble_time_rounded);
|
|
save_trace(1, &tx_time_pad);
|
|
srslte_rf_send_timed_multi(&rf_device, buffer, burst_preamble_samples, tx_time_pad.full_secs, tx_time_pad.frac_secs, true, true, false);
|
|
is_start_of_burst = false;
|
|
}
|
|
}
|
|
|
|
// Save possible end of burst time
|
|
srslte_timestamp_copy(&end_of_burst_time, &tx_time);
|
|
srslte_timestamp_add(&end_of_burst_time, 0, (double) nof_samples/cur_tx_srate);
|
|
|
|
save_trace(0, &tx_time);
|
|
int ret = srslte_rf_send_timed_multi(&rf_device, buffer, nof_samples,
|
|
tx_time.full_secs, tx_time.frac_secs,
|
|
BLOCKING_TX, is_start_of_burst, false);
|
|
is_start_of_burst = false;
|
|
if (ret > 0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void radio::tx_end()
|
|
{
|
|
if (!is_start_of_burst) {
|
|
save_trace(2, &end_of_burst_time);
|
|
srslte_rf_send_timed2(&rf_device, zeros, 0, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, true);
|
|
is_start_of_burst = true;
|
|
}
|
|
}
|
|
|
|
void radio::start_trace() {
|
|
trace_enabled = true;
|
|
}
|
|
|
|
void radio::set_tti(uint32_t tti_) {
|
|
tti = tti_;
|
|
}
|
|
|
|
void radio::write_trace(std::string filename)
|
|
{
|
|
tr_local_time.writeToBinary(filename + ".local");
|
|
tr_is_eob.writeToBinary(filename + ".eob");
|
|
tr_usrp_time.writeToBinary(filename + ".usrp");
|
|
tr_tx_time.writeToBinary(filename + ".tx");
|
|
}
|
|
|
|
void radio::save_trace(uint32_t is_eob, srslte_timestamp_t *tx_time) {
|
|
if (trace_enabled) {
|
|
tr_local_time.push_cur_time_us(tti);
|
|
srslte_timestamp_t usrp_time;
|
|
srslte_rf_get_time(&rf_device, &usrp_time.full_secs, &usrp_time.frac_secs);
|
|
tr_usrp_time.push(tti, srslte_timestamp_uint32(&usrp_time));
|
|
tr_tx_time.push(tti, srslte_timestamp_uint32(tx_time));
|
|
tr_is_eob.push(tti, is_eob);
|
|
}
|
|
}
|
|
|
|
void radio::set_freq_offset(double freq) {
|
|
freq_offset = freq;
|
|
}
|
|
|
|
void radio::set_rx_freq(double freq)
|
|
{
|
|
rx_freq = srslte_rf_set_rx_freq(&rf_device, freq+freq_offset);
|
|
}
|
|
|
|
void radio::set_rx_gain(float gain)
|
|
{
|
|
srslte_rf_set_rx_gain(&rf_device, gain);
|
|
}
|
|
|
|
double radio::set_rx_gain_th(float gain)
|
|
{
|
|
return srslte_rf_set_rx_gain_th(&rf_device, gain);
|
|
}
|
|
|
|
void radio::set_master_clock_rate(double rate)
|
|
{
|
|
srslte_rf_set_master_clock_rate(&rf_device, rate);
|
|
}
|
|
|
|
void radio::set_rx_srate(double srate)
|
|
{
|
|
srslte_rf_stop_rx_stream(&rf_device);
|
|
srslte_rf_set_rx_srate(&rf_device, srate);
|
|
srslte_rf_start_rx_stream(&rf_device, false);
|
|
}
|
|
|
|
void radio::set_tx_freq(double freq)
|
|
{
|
|
tx_freq = srslte_rf_set_tx_freq(&rf_device, freq+freq_offset);
|
|
}
|
|
|
|
void radio::set_tx_gain(float gain)
|
|
{
|
|
srslte_rf_set_tx_gain(&rf_device, gain);
|
|
}
|
|
|
|
double radio::get_rx_freq()
|
|
{
|
|
return rx_freq;
|
|
}
|
|
|
|
double radio::get_freq_offset()
|
|
{
|
|
return freq_offset;
|
|
}
|
|
|
|
double radio::get_tx_freq()
|
|
{
|
|
return tx_freq;
|
|
}
|
|
|
|
float radio::get_tx_gain()
|
|
{
|
|
return srslte_rf_get_tx_gain(&rf_device);
|
|
}
|
|
|
|
float radio::get_rx_gain()
|
|
{
|
|
return srslte_rf_get_rx_gain(&rf_device);
|
|
}
|
|
|
|
void radio::set_tx_srate(double srate)
|
|
{
|
|
cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate);
|
|
burst_preamble_samples = (uint32_t) (cur_tx_srate * burst_preamble_sec);
|
|
if (burst_preamble_samples > burst_preamble_max_samples) {
|
|
burst_preamble_samples = burst_preamble_max_samples;
|
|
fprintf(stderr, "Error setting TX srate %.1f MHz. Maximum frequency for zero prepadding is 30.72 MHz\n", srate*1e-6);
|
|
}
|
|
burst_preamble_time_rounded = (double) burst_preamble_samples/cur_tx_srate;
|
|
|
|
int nsamples=0;
|
|
/* Set time advance for each known device if in auto mode */
|
|
if (tx_adv_auto) {
|
|
|
|
/* This values have been calibrated using the prach_test_usrp tool in srsLTE */
|
|
|
|
if (!strcmp(srslte_rf_name(&rf_device), "uhd_b200")) {
|
|
|
|
double srate_khz = round(cur_tx_srate/1e3);
|
|
if (srate_khz == 1.92e3) {
|
|
nsamples = 54;
|
|
} else if (srate_khz == 3.84e3) {
|
|
nsamples = 69;
|
|
} else if (srate_khz == 5.76e3) {
|
|
nsamples = 93;
|
|
} else if (srate_khz == 11.52e3) {
|
|
nsamples = 120;
|
|
} else if (srate_khz == 15.36e3) {
|
|
nsamples = 131;
|
|
} else if (srate_khz == 23.04e3) {
|
|
nsamples = 150;
|
|
} else {
|
|
/* Interpolate from known values */
|
|
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
|
nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec);
|
|
}
|
|
|
|
}else if(!strcmp(srslte_rf_name(&rf_device), "uhd_usrp2")) {
|
|
double srate_khz = round(cur_tx_srate/1e3);
|
|
if (srate_khz == 1.92e3) {
|
|
nsamples = 14;// estimated
|
|
} else if (srate_khz == 3.84e3) {
|
|
nsamples = 32;
|
|
} else if (srate_khz == 5.76e3) {
|
|
nsamples = 43;
|
|
} else if (srate_khz == 11.52e3) {
|
|
nsamples = 54;
|
|
} else if (srate_khz == 15.36e3) {
|
|
nsamples = 65;// to calc
|
|
} else if (srate_khz == 23.04e3) {
|
|
nsamples = 80; // to calc
|
|
} else {
|
|
/* Interpolate from known values */
|
|
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
|
nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec);
|
|
}
|
|
|
|
} else if(!strcmp(srslte_rf_name(&rf_device), "lime")) {
|
|
double srate_khz = round(cur_tx_srate/1e3);
|
|
if (srate_khz == 1.92e3) {
|
|
nsamples = 70;// estimated
|
|
} else if (srate_khz == 3.84e3) {
|
|
nsamples = 76;//estimated
|
|
} else if (srate_khz == 5.76e3) {
|
|
nsamples = 76;
|
|
} else if (srate_khz == 11.52e3) {
|
|
nsamples = 76;
|
|
} else if (srate_khz == 15.36e3) {
|
|
nsamples = 73;
|
|
} else if (srate_khz == 23.04e3) {
|
|
nsamples = 87;
|
|
} else {
|
|
/* Interpolate from known values */
|
|
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
|
nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec);
|
|
}
|
|
|
|
} else if (!strcmp(srslte_rf_name(&rf_device), "uhd_x300")) {
|
|
|
|
// In X300 TX/RX offset is independent of sampling rate
|
|
nsamples = 45;
|
|
} else if (!strcmp(srslte_rf_name(&rf_device), "bladerf")) {
|
|
|
|
double srate_khz = round(cur_tx_srate/1e3);
|
|
if (srate_khz == 1.92e3) {
|
|
nsamples = 16;
|
|
} else if (srate_khz == 3.84e3) {
|
|
nsamples = 18;
|
|
} else if (srate_khz == 5.76e3) {
|
|
nsamples = 16;
|
|
} else if (srate_khz == 11.52e3) {
|
|
nsamples = 21;
|
|
} else if (srate_khz == 15.36e3) {
|
|
nsamples = 14;
|
|
} else if (srate_khz == 23.04e3) {
|
|
nsamples = 21;
|
|
} else {
|
|
/* Interpolate from known values */
|
|
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
|
tx_adv_sec = blade_default_tx_adv_samples * (1/cur_tx_srate) + blade_default_tx_adv_offset_sec;
|
|
}
|
|
} else {
|
|
printf("\nWarning TX/RX time offset has not been calibrated for device %s. Set a value manually\n\n", srslte_rf_name(&rf_device));
|
|
}
|
|
} else {
|
|
nsamples = tx_adv_nsamples;
|
|
printf("Setting manual TX/RX offset to %d samples\n", nsamples);
|
|
}
|
|
|
|
// Calculate TX advance in seconds from samples and sampling rate
|
|
tx_adv_sec = nsamples/cur_tx_srate;
|
|
}
|
|
|
|
void radio::register_error_handler(srslte_rf_error_handler_t h)
|
|
{
|
|
srslte_rf_register_error_handler(&rf_device, h);
|
|
}
|
|
|
|
|
|
}
|
|
|