mirror of https://github.com/PentHertz/srsLTE.git
filerf: add tx, multi-channel, open via device string and test
This commits extends the file-based RF device as follows: * open device via device string * add tx to file * add multi-channel support (multiple files) * add rf_file_test.c to for testing
This commit is contained in:
parent
57f84d4ca4
commit
f3d144dd59
|
@ -59,7 +59,7 @@ if(RF_FOUND)
|
||||||
list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
|
list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
|
||||||
endif (ZEROMQ_FOUND)
|
endif (ZEROMQ_FOUND)
|
||||||
|
|
||||||
list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_rx.c)
|
list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c)
|
||||||
|
|
||||||
add_library(srsran_rf_object OBJECT ${SOURCES_RF})
|
add_library(srsran_rf_object OBJECT ${SOURCES_RF})
|
||||||
set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1)
|
set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1)
|
||||||
|
@ -100,5 +100,9 @@ if(RF_FOUND)
|
||||||
#add_test(rf_zmq_test rf_zmq_test)
|
#add_test(rf_zmq_test rf_zmq_test)
|
||||||
endif (ZEROMQ_FOUND)
|
endif (ZEROMQ_FOUND)
|
||||||
|
|
||||||
|
add_executable(rf_file_test rf_file_test.c)
|
||||||
|
target_link_libraries(rf_file_test srsran_rf)
|
||||||
|
add_test(rf_file_test rf_file_test)
|
||||||
|
|
||||||
INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR})
|
INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR})
|
||||||
endif(RF_FOUND)
|
endif(RF_FOUND)
|
||||||
|
|
|
@ -324,4 +324,5 @@ static rf_dev_t* available_devices[] = {
|
||||||
#ifdef ENABLE_DUMMY_DEV
|
#ifdef ENABLE_DUMMY_DEV
|
||||||
&dev_dummy,
|
&dev_dummy,
|
||||||
#endif
|
#endif
|
||||||
|
&dev_file,
|
||||||
NULL};
|
NULL};
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "rf_file_imp.h"
|
#include "rf_file_imp.h"
|
||||||
#include "rf_file_imp_trx.h"
|
#include "rf_file_imp_trx.h"
|
||||||
#include "rf_helper.h"
|
#include "rf_helper.h"
|
||||||
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <srsran/phy/common/phy_common.h>
|
#include <srsran/phy/common/phy_common.h>
|
||||||
#include <srsran/phy/common/timestamp.h>
|
#include <srsran/phy/common/timestamp.h>
|
||||||
|
@ -35,18 +36,18 @@ typedef struct {
|
||||||
uint32_t base_srate;
|
uint32_t base_srate;
|
||||||
uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate
|
uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate
|
||||||
double rx_gain;
|
double rx_gain;
|
||||||
uint32_t tx_freq_mhz[SRSRAN_MAX_PORTS];
|
uint32_t tx_freq_mhz[SRSRAN_MAX_CHANNELS];
|
||||||
uint32_t rx_freq_mhz[SRSRAN_MAX_PORTS];
|
uint32_t rx_freq_mhz[SRSRAN_MAX_CHANNELS];
|
||||||
bool tx_used;
|
bool tx_off;
|
||||||
|
char id[RF_PARAM_LEN];
|
||||||
|
|
||||||
// FILEs
|
// FILEs
|
||||||
rf_file_tx_t transmitter[SRSRAN_MAX_PORTS];
|
rf_file_tx_t transmitter[SRSRAN_MAX_CHANNELS];
|
||||||
rf_file_rx_t receiver[SRSRAN_MAX_PORTS];
|
rf_file_rx_t receiver[SRSRAN_MAX_CHANNELS];
|
||||||
|
bool close_files;
|
||||||
char id[PARAM_LEN_SHORT];
|
|
||||||
|
|
||||||
// Various sample buffers
|
// Various sample buffers
|
||||||
cf_t* buffer_decimation[SRSRAN_MAX_PORTS];
|
cf_t* buffer_decimation[SRSRAN_MAX_CHANNELS];
|
||||||
cf_t* buffer_tx;
|
cf_t* buffer_tx;
|
||||||
|
|
||||||
// Rx timestamp
|
// Rx timestamp
|
||||||
|
@ -55,6 +56,7 @@ typedef struct {
|
||||||
pthread_mutex_t tx_config_mutex;
|
pthread_mutex_t tx_config_mutex;
|
||||||
pthread_mutex_t rx_config_mutex;
|
pthread_mutex_t rx_config_mutex;
|
||||||
pthread_mutex_t decim_mutex;
|
pthread_mutex_t decim_mutex;
|
||||||
|
pthread_mutex_t rx_gain_mutex;
|
||||||
} rf_file_handler_t;
|
} rf_file_handler_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -63,6 +65,31 @@ typedef struct {
|
||||||
|
|
||||||
static void update_rates(rf_file_handler_t* handler, double srate);
|
static void update_rates(rf_file_handler_t* handler, double srate);
|
||||||
|
|
||||||
|
void rf_file_info(char* id, const char* format, ...)
|
||||||
|
{
|
||||||
|
#if VERBOSE
|
||||||
|
struct timeval t;
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
printf("[%s@%02ld.%06ld] ", id ? id : "file", t.tv_sec % 10, t.tv_usec);
|
||||||
|
vprintf(format, args);
|
||||||
|
va_end(args);
|
||||||
|
#else /* VERBOSE */
|
||||||
|
// Do nothing
|
||||||
|
#endif /* VERBOSE */
|
||||||
|
}
|
||||||
|
|
||||||
|
void rf_file_error(char* id, const char* format, ...)
|
||||||
|
{
|
||||||
|
struct timeval t;
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vfprintf(stderr, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir)
|
static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir)
|
||||||
{
|
{
|
||||||
int ret = SRSRAN_ERROR;
|
int ret = SRSRAN_ERROR;
|
||||||
|
@ -74,6 +101,8 @@ static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir
|
||||||
|
|
||||||
srsran_timestamp_t _ts = {};
|
srsran_timestamp_t _ts = {};
|
||||||
srsran_timestamp_init_uint64(&_ts, *ts, handler->base_srate);
|
srsran_timestamp_init_uint64(&_ts, *ts, handler->base_srate);
|
||||||
|
rf_file_info(
|
||||||
|
handler->id, " -> next %s time after %d samples: %d + %.3f\n", dir, nsamples, _ts.full_secs, _ts.frac_secs);
|
||||||
|
|
||||||
ret = SRSRAN_SUCCESS;
|
ret = SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -138,8 +167,73 @@ int rf_file_open(char* args, void** h)
|
||||||
|
|
||||||
int rf_file_open_multi(char* args, void** h, uint32_t nof_channels)
|
int rf_file_open_multi(char* args, void** h, uint32_t nof_channels)
|
||||||
{
|
{
|
||||||
perror("Cannot open file-based RF as regular device. Use rf_file_open_file() instead.");
|
int ret = SRSRAN_ERROR;
|
||||||
return SRSRAN_ERROR_INVALID_COMMAND;
|
|
||||||
|
FILE* rx_files[SRSRAN_MAX_CHANNELS] = {NULL};
|
||||||
|
FILE* tx_files[SRSRAN_MAX_CHANNELS] = {NULL};
|
||||||
|
|
||||||
|
if (h && nof_channels < SRSRAN_MAX_CHANNELS) {
|
||||||
|
uint32_t base_srate = FILE_BASERATE_DEFAULT_HZ;
|
||||||
|
|
||||||
|
// parse args
|
||||||
|
if (args && strlen(args)) {
|
||||||
|
// base_srate
|
||||||
|
parse_uint32(args, "base_srate", -1, &base_srate);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[file] Error: RF device args are required for file-based no-RF module\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nof_channels; i++) {
|
||||||
|
// rx_file
|
||||||
|
char rx_file[RF_PARAM_LEN] = {};
|
||||||
|
parse_string(args, "rx_file", i, rx_file);
|
||||||
|
|
||||||
|
// tx_file
|
||||||
|
char tx_file[RF_PARAM_LEN] = {};
|
||||||
|
parse_string(args, "tx_file", i, tx_file);
|
||||||
|
|
||||||
|
// initialize transmitter
|
||||||
|
if (strlen(tx_file) != 0) {
|
||||||
|
tx_files[i] = fopen(tx_file, "wb");
|
||||||
|
if (tx_files[i] == NULL) {
|
||||||
|
fprintf(stderr, "[file] Error: opening tx_file%d: %s; %s\n", i, tx_file, strerror(errno));
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize receiver
|
||||||
|
if (strlen(rx_file) != 0) {
|
||||||
|
rx_files[i] = fopen(rx_file, "rb");
|
||||||
|
if (rx_files[i] == NULL) {
|
||||||
|
fprintf(stderr, "[file] Error: opening rx_file%d: %s; %s\n", i, rx_file, strerror(errno));
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defer further initialization to open_file method
|
||||||
|
ret = rf_file_open_file(h, rx_files, tx_files, nof_channels, base_srate);
|
||||||
|
if (ret != SRSRAN_SUCCESS) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add flag to close all files when closing device
|
||||||
|
rf_file_handler_t* handler = (rf_file_handler_t*)(*h);
|
||||||
|
handler->close_files = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
for (int i = 0; i < nof_channels; i++) {
|
||||||
|
if (rx_files[i] != NULL) {
|
||||||
|
fclose(rx_files[i]);
|
||||||
|
}
|
||||||
|
if (tx_files[i] != NULL) {
|
||||||
|
fclose(tx_files[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
|
int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
|
||||||
|
@ -151,13 +245,12 @@ int rf_file_open_file(void** h, FILE **rx_files, FILE **tx_files, uint32_t nof_c
|
||||||
|
|
||||||
rf_file_handler_t* handler = (rf_file_handler_t*)malloc(sizeof(rf_file_handler_t));
|
rf_file_handler_t* handler = (rf_file_handler_t*)malloc(sizeof(rf_file_handler_t));
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
perror("malloc");
|
fprintf(stderr, "malloc: %s\n", strerror(errno));
|
||||||
return SRSRAN_ERROR;
|
return SRSRAN_ERROR;
|
||||||
}
|
}
|
||||||
memset(handler, 0, sizeof(rf_file_handler_t));
|
memset(handler, 0, sizeof(rf_file_handler_t));
|
||||||
*h = handler;
|
*h = handler;
|
||||||
handler->base_srate = FILE_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell
|
handler->base_srate = base_srate;
|
||||||
handler->rx_gain = 0.0;
|
|
||||||
handler->info.max_rx_gain = FILE_MAX_GAIN_DB;
|
handler->info.max_rx_gain = FILE_MAX_GAIN_DB;
|
||||||
handler->info.min_rx_gain = FILE_MIN_GAIN_DB;
|
handler->info.min_rx_gain = FILE_MIN_GAIN_DB;
|
||||||
handler->info.max_tx_gain = FILE_MAX_GAIN_DB;
|
handler->info.max_tx_gain = FILE_MAX_GAIN_DB;
|
||||||
|
@ -171,17 +264,21 @@ int rf_file_open_file(void** h, FILE **rx_files, FILE **tx_files, uint32_t nof_c
|
||||||
rx_opts.id = handler->id;
|
rx_opts.id = handler->id;
|
||||||
|
|
||||||
if (pthread_mutex_init(&handler->tx_config_mutex, NULL)) {
|
if (pthread_mutex_init(&handler->tx_config_mutex, NULL)) {
|
||||||
perror("Mutex init");
|
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
if (pthread_mutex_init(&handler->rx_config_mutex, NULL)) {
|
if (pthread_mutex_init(&handler->rx_config_mutex, NULL)) {
|
||||||
perror("Mutex init");
|
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
if (pthread_mutex_init(&handler->decim_mutex, NULL)) {
|
if (pthread_mutex_init(&handler->decim_mutex, NULL)) {
|
||||||
perror("Mutex init");
|
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
if (pthread_mutex_init(&handler->rx_gain_mutex, NULL)) {
|
||||||
|
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
// base_srate
|
pthread_mutex_lock(&handler->rx_gain_mutex);
|
||||||
handler->base_srate = base_srate;
|
handler->rx_gain = 0.0;
|
||||||
|
pthread_mutex_unlock(&handler->rx_gain_mutex);
|
||||||
|
|
||||||
// id
|
// id
|
||||||
// TODO: set some meaningful ID in handler->id
|
// TODO: set some meaningful ID in handler->id
|
||||||
|
@ -195,29 +292,30 @@ int rf_file_open_file(void** h, FILE **rx_files, FILE **tx_files, uint32_t nof_c
|
||||||
|
|
||||||
// Create channels
|
// Create channels
|
||||||
for (int i = 0; i < handler->nof_channels; i++) {
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
if (rx_files != NULL) {
|
if (rx_files != NULL && rx_files[i] != NULL) {
|
||||||
rx_opts.file = rx_files[i];
|
rx_opts.file = rx_files[i];
|
||||||
if (rf_file_rx_open(&handler->receiver[i], rx_opts) != SRSRAN_SUCCESS) {
|
if (rf_file_rx_open(&handler->receiver[i], rx_opts) != SRSRAN_SUCCESS) {
|
||||||
fprintf(stderr, "[file] Error: opening receiver\n");
|
fprintf(stderr, "[file] Error: opening receiver\n");
|
||||||
goto clean_exit;
|
goto clean_exit;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(stdout, "[file] %s Rx file not specified. Disabling receiver.\n", handler->id);
|
// no rx_files provided
|
||||||
|
fprintf(stdout, "[file] %s rx channel %d not specified. Disabling receiver.\n", handler->id, i);
|
||||||
}
|
}
|
||||||
if (tx_files != NULL) {
|
if (tx_files != NULL && tx_files[i] != NULL) {
|
||||||
tx_opts.file = tx_files[i];
|
tx_opts.file = tx_files[i];
|
||||||
// TX is not implemented yet
|
if (rf_file_tx_open(&handler->transmitter[i], tx_opts) != SRSRAN_SUCCESS) {
|
||||||
// if(rf_file_tx_open(&handler->transmitter[i], tx_opts) != SRSRAN_SUCCESS) {
|
fprintf(stderr, "[file] Error: opening transmitter\n");
|
||||||
// fprintf(stderr, "[file] Error: opening transmitter\n");
|
goto clean_exit;
|
||||||
// goto clean_exit;
|
}
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
// no tx_files provided
|
// no tx_files provided
|
||||||
fprintf(stdout, "[file] %s Tx file not specified. Disabling transmitter.\n", handler->id);
|
fprintf(stdout, "[file] %s tx channel %d not specified. Disabling transmitter.\n", handler->id, i);
|
||||||
|
handler->tx_off = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handler->transmitter[i].running && !handler->receiver[i].running) {
|
if (!handler->transmitter[i].running && !handler->receiver[i].running) {
|
||||||
fprintf(stderr, "[file] Error: Neither Tx file nor Rx file specified.\n");
|
fprintf(stderr, "[file] Error: Neither tx nor rx specificed for channel %d.\n", i);
|
||||||
goto clean_exit;
|
goto clean_exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,9 +347,15 @@ int rf_file_open_file(void** h, FILE **rx_files, FILE **tx_files, uint32_t nof_c
|
||||||
|
|
||||||
int rf_file_close(void* h)
|
int rf_file_close(void* h)
|
||||||
{
|
{
|
||||||
|
|
||||||
rf_file_handler_t* handler = (rf_file_handler_t*)h;
|
rf_file_handler_t* handler = (rf_file_handler_t*)h;
|
||||||
|
|
||||||
|
rf_file_info(handler->id, "Closing ...\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
|
rf_file_tx_close(&handler->transmitter[i]);
|
||||||
|
rf_file_rx_close(&handler->receiver[i]);
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < handler->nof_channels; i++) {
|
for (uint32_t i = 0; i < handler->nof_channels; i++) {
|
||||||
if (handler->buffer_decimation[i]) {
|
if (handler->buffer_decimation[i]) {
|
||||||
free(handler->buffer_decimation[i]);
|
free(handler->buffer_decimation[i]);
|
||||||
|
@ -265,6 +369,18 @@ int rf_file_close(void* h)
|
||||||
pthread_mutex_destroy(&handler->tx_config_mutex);
|
pthread_mutex_destroy(&handler->tx_config_mutex);
|
||||||
pthread_mutex_destroy(&handler->rx_config_mutex);
|
pthread_mutex_destroy(&handler->rx_config_mutex);
|
||||||
pthread_mutex_destroy(&handler->decim_mutex);
|
pthread_mutex_destroy(&handler->decim_mutex);
|
||||||
|
pthread_mutex_destroy(&handler->rx_gain_mutex);
|
||||||
|
|
||||||
|
if (handler->close_files) {
|
||||||
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
|
if (handler->receiver[i].file != NULL) {
|
||||||
|
fclose(handler->receiver[i].file);
|
||||||
|
}
|
||||||
|
if (handler->transmitter[i].file != NULL) {
|
||||||
|
fclose(handler->transmitter[i].file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Free all
|
// Free all
|
||||||
free(handler);
|
free(handler);
|
||||||
|
@ -412,13 +528,11 @@ void rf_file_get_time(void* h, time_t* secs, double* frac_secs)
|
||||||
|
|
||||||
int rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
|
int rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
|
||||||
{
|
{
|
||||||
void* data_multi[SRSRAN_MAX_PORTS] = {NULL};
|
return rf_file_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs);
|
||||||
data_multi[0] = data;
|
|
||||||
return rf_file_recv_with_time_multi(h, data_multi, nsamples, blocking, secs, frac_secs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int rf_file_recv_with_time_multi(void* h,
|
int rf_file_recv_with_time_multi(void* h,
|
||||||
void* data[SRSRAN_MAX_PORTS],
|
void** data,
|
||||||
uint32_t nsamples,
|
uint32_t nsamples,
|
||||||
bool blocking,
|
bool blocking,
|
||||||
time_t* secs,
|
time_t* secs,
|
||||||
|
@ -431,23 +545,28 @@ int rf_file_recv_with_time_multi(void* h,
|
||||||
|
|
||||||
// Map ports to data buffers according to the selected frequencies
|
// Map ports to data buffers according to the selected frequencies
|
||||||
pthread_mutex_lock(&handler->rx_config_mutex);
|
pthread_mutex_lock(&handler->rx_config_mutex);
|
||||||
cf_t* buffers[SRSRAN_MAX_PORTS] = {}; // Buffer pointers, NULL if unmatched
|
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
|
||||||
for (uint32_t i = 0; i < handler->nof_channels; i++) {
|
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched
|
||||||
bool mapped = false;
|
|
||||||
|
|
||||||
// Find first matching frequency
|
// For each logical channel...
|
||||||
for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) {
|
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
|
||||||
// Traverse all channels, break if mapped
|
bool unmatched = true;
|
||||||
if (buffers[j] == NULL && rf_file_rx_match_freq(&handler->receiver[j], handler->rx_freq_mhz[i])) {
|
|
||||||
// Available buffer and matched frequency with receiver
|
// For each physical channel...
|
||||||
buffers[j] = (cf_t*)data[i];
|
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
|
||||||
mapped = true;
|
// Consider a match if the physical channel is NOT mapped and the frequency match
|
||||||
|
if (!mapped[physical] && rf_file_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) {
|
||||||
|
// Not mapped and matched frequency with receiver
|
||||||
|
buffers[physical] = (cf_t*)data[logical];
|
||||||
|
mapped[physical] = true;
|
||||||
|
unmatched = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no matching frequency found; set data to zeros
|
// If no matching frequency found; set data to zeros
|
||||||
if (!mapped && data[i]) {
|
if (unmatched) {
|
||||||
memset(data[i], 0, sizeof(cf_t) * nsamples);
|
srsran_vec_zero(data[logical], nsamples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&handler->rx_config_mutex);
|
pthread_mutex_unlock(&handler->rx_config_mutex);
|
||||||
|
@ -460,6 +579,8 @@ int rf_file_recv_with_time_multi(void* h,
|
||||||
uint32_t nbytes = NSAMPLES2NBYTES(nsamples * decim_factor);
|
uint32_t nbytes = NSAMPLES2NBYTES(nsamples * decim_factor);
|
||||||
uint32_t nsamples_baserate = nsamples * decim_factor;
|
uint32_t nsamples_baserate = nsamples * decim_factor;
|
||||||
|
|
||||||
|
rf_file_info(handler->id, "Rx %d samples (%d B)\n", nsamples, nbytes);
|
||||||
|
|
||||||
// set timestamp for this reception
|
// set timestamp for this reception
|
||||||
if (secs != NULL && frac_secs != NULL) {
|
if (secs != NULL && frac_secs != NULL) {
|
||||||
srsran_timestamp_t ts = {};
|
srsran_timestamp_t ts = {};
|
||||||
|
@ -488,10 +609,19 @@ int rf_file_recv_with_time_multi(void* h,
|
||||||
srsran_timestamp_t ts_tx = {}, ts_rx = {};
|
srsran_timestamp_t ts_tx = {}, ts_rx = {};
|
||||||
srsran_timestamp_init_uint64(&ts_tx, handler->transmitter[0].nsamples, handler->base_srate);
|
srsran_timestamp_init_uint64(&ts_tx, handler->transmitter[0].nsamples, handler->base_srate);
|
||||||
srsran_timestamp_init_uint64(&ts_rx, handler->next_rx_ts, handler->base_srate);
|
srsran_timestamp_init_uint64(&ts_rx, handler->next_rx_ts, handler->base_srate);
|
||||||
|
rf_file_info(handler->id, " - next rx time: %d + %.3f\n", ts_rx.full_secs, ts_rx.frac_secs);
|
||||||
|
rf_file_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs);
|
||||||
|
|
||||||
|
// check for tx gap if we're also transmitting on this radio
|
||||||
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
|
if (handler->transmitter[i].running) {
|
||||||
|
rf_file_tx_align(&handler->transmitter[i], handler->next_rx_ts + nsamples_baserate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// copy from rx buffer as many samples as requested into provided buffer
|
// copy from rx buffer as many samples as requested into provided buffer
|
||||||
bool completed = false;
|
bool completed = false;
|
||||||
int32_t count[SRSRAN_MAX_PORTS] = {};
|
int32_t count[SRSRAN_MAX_CHANNELS] = {};
|
||||||
while (!completed) {
|
while (!completed) {
|
||||||
uint32_t completed_count = 0;
|
uint32_t completed_count = 0;
|
||||||
|
|
||||||
|
@ -523,6 +653,7 @@ int rf_file_recv_with_time_multi(void* h,
|
||||||
// Check if all channels are completed
|
// Check if all channels are completed
|
||||||
completed = (completed_count == handler->nof_channels);
|
completed = (completed_count == handler->nof_channels);
|
||||||
}
|
}
|
||||||
|
rf_file_info(handler->id, " - read %d samples.\n", NBYTES2NSAMPLES(nbytes));
|
||||||
|
|
||||||
// decimate if needed
|
// decimate if needed
|
||||||
if (decim_factor != 1) {
|
if (decim_factor != 1) {
|
||||||
|
@ -538,14 +669,24 @@ int rf_file_recv_with_time_multi(void* h,
|
||||||
for (int j = 0; j < decim_factor; j++, n++) {
|
for (int j = 0; j < decim_factor; j++, n++) {
|
||||||
avg += ptr[n];
|
avg += ptr[n];
|
||||||
}
|
}
|
||||||
dst[i] = avg;
|
dst[i] = avg; // divide by decim_factor later via scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rf_file_info(handler->id,
|
||||||
|
" - re-adjust bytes due to %dx decimation %d --> %d samples)\n",
|
||||||
|
decim_factor,
|
||||||
|
nsamples_baserate,
|
||||||
|
nsamples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set gain
|
// Set gain
|
||||||
|
pthread_mutex_lock(&handler->rx_gain_mutex);
|
||||||
float scale = srsran_convert_dB_to_amplitude(handler->rx_gain);
|
float scale = srsran_convert_dB_to_amplitude(handler->rx_gain);
|
||||||
|
pthread_mutex_unlock(&handler->rx_gain_mutex);
|
||||||
|
// scale shall also incorporate decim_factor
|
||||||
|
scale = scale / decim_factor;
|
||||||
for (uint32_t c = 0; c < handler->nof_channels; c++) {
|
for (uint32_t c = 0; c < handler->nof_channels; c++) {
|
||||||
if (buffers[c]) {
|
if (buffers[c]) {
|
||||||
srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples);
|
srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples);
|
||||||
|
@ -589,9 +730,127 @@ int rf_file_send_timed_multi(void* h,
|
||||||
bool is_start_of_burst,
|
bool is_start_of_burst,
|
||||||
bool is_end_of_burst)
|
bool is_end_of_burst)
|
||||||
{
|
{
|
||||||
// Not implemented
|
int ret = SRSRAN_ERROR;
|
||||||
fprintf(stderr, "Error: rf_file_send_timed_multi not implemented.\n");
|
|
||||||
return SRSRAN_ERROR;
|
if (h && data && nsamples > 0) {
|
||||||
|
rf_file_handler_t* handler = (rf_file_handler_t*)h;
|
||||||
|
|
||||||
|
// Map ports to data buffers according to the selected frequencies
|
||||||
|
pthread_mutex_lock(&handler->tx_config_mutex);
|
||||||
|
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
|
||||||
|
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission
|
||||||
|
|
||||||
|
// For each logical channel...
|
||||||
|
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
|
||||||
|
// For each physical channel...
|
||||||
|
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
|
||||||
|
// Consider a match if the physical channel is NOT mapped and the frequency match
|
||||||
|
if (!mapped[physical] &&
|
||||||
|
rf_file_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) {
|
||||||
|
// Not mapped and matched frequency with receiver
|
||||||
|
buffers[physical] = (cf_t*)data[logical];
|
||||||
|
mapped[physical] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&handler->tx_config_mutex);
|
||||||
|
|
||||||
|
// Protect the access to decim_factor since is a shared variable
|
||||||
|
pthread_mutex_lock(&handler->decim_mutex);
|
||||||
|
uint32_t decim_factor = handler->decim_factor;
|
||||||
|
pthread_mutex_unlock(&handler->decim_mutex);
|
||||||
|
|
||||||
|
uint32_t nbytes = NSAMPLES2NBYTES(nsamples);
|
||||||
|
uint32_t nsamples_baseband = nsamples * decim_factor;
|
||||||
|
uint32_t nbytes_baseband = NSAMPLES2NBYTES(nsamples_baseband);
|
||||||
|
if (nbytes_baseband > FILE_MAX_BUFFER_SIZE) {
|
||||||
|
fprintf(stderr, "Error: trying to transmit too many samples (%d > %zu).\n", nbytes, FILE_MAX_BUFFER_SIZE);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
rf_file_info(handler->id, "Tx %d samples (%d B)\n", nsamples, nbytes);
|
||||||
|
|
||||||
|
// return if transmitter is switched off
|
||||||
|
if (handler->tx_off) {
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this is a tx in the future
|
||||||
|
if (has_time_spec) {
|
||||||
|
rf_file_info(handler->id, " - tx time: %d + %.3f\n", secs, frac_secs);
|
||||||
|
|
||||||
|
srsran_timestamp_t ts = {};
|
||||||
|
srsran_timestamp_init(&ts, secs, frac_secs);
|
||||||
|
uint64_t tx_ts = srsran_timestamp_uint64(&ts, handler->base_srate);
|
||||||
|
int num_tx_gap_samples = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
|
if (handler->transmitter[i].running) {
|
||||||
|
num_tx_gap_samples = rf_file_tx_align(&handler->transmitter[i], tx_ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_tx_gap_samples < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"[file] Error: tx time is %.3f ms in the past (%" PRIu64 " < %" PRIu64 ")\n",
|
||||||
|
-1000.0 * num_tx_gap_samples / handler->base_srate,
|
||||||
|
tx_ts,
|
||||||
|
handler->transmitter[0].nsamples);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send base-band samples
|
||||||
|
for (int i = 0; i < handler->nof_channels; i++) {
|
||||||
|
if (buffers[i] != NULL) {
|
||||||
|
// Select buffer pointer depending on interpolation
|
||||||
|
cf_t* buf = (decim_factor != 1) ? handler->buffer_tx : buffers[i];
|
||||||
|
|
||||||
|
// Interpolate if required
|
||||||
|
if (decim_factor != 1) {
|
||||||
|
rf_file_info(handler->id,
|
||||||
|
" - re-adjust bytes due to %dx interpolation %d --> %d samples)\n",
|
||||||
|
decim_factor,
|
||||||
|
nsamples,
|
||||||
|
nsamples_baseband);
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
cf_t* src = buffers[i];
|
||||||
|
for (int k = 0; k < nsamples; k++) {
|
||||||
|
// perform zero order hold
|
||||||
|
for (int j = 0; j < decim_factor; j++, n++) {
|
||||||
|
buf[n] = src[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsamples_baseband != n) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Number of tx samples (%d) does not match with number of interpolated samples (%d)\n",
|
||||||
|
nsamples_baseband,
|
||||||
|
n);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = rf_file_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband);
|
||||||
|
if (n == SRSRAN_ERROR) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int n = rf_file_tx_zeros(&handler->transmitter[i], nsamples_baseband);
|
||||||
|
if (n == SRSRAN_ERROR) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SRSRAN_SUCCESS;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -95,12 +95,8 @@ SRSRAN_API void rf_file_get_time(void* h, time_t* secs, double* frac_secs);
|
||||||
SRSRAN_API int
|
SRSRAN_API int
|
||||||
rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
|
rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
|
||||||
|
|
||||||
SRSRAN_API int rf_file_recv_with_time_multi(void* h,
|
SRSRAN_API int
|
||||||
void* data[SRSRAN_MAX_PORTS],
|
rf_file_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
|
||||||
uint32_t nsamples,
|
|
||||||
bool blocking,
|
|
||||||
time_t* secs,
|
|
||||||
double* frac_secs);
|
|
||||||
|
|
||||||
SRSRAN_API int rf_file_send_timed(void* h,
|
SRSRAN_API int rf_file_send_timed(void* h,
|
||||||
void* data,
|
void* data,
|
||||||
|
|
|
@ -21,7 +21,7 @@ int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts)
|
||||||
|
|
||||||
if (q) {
|
if (q) {
|
||||||
// Zero object
|
// Zero object
|
||||||
bzero(q, sizeof(rf_file_rx_t));
|
memset(q, 0, sizeof(rf_file_rx_t));
|
||||||
|
|
||||||
// Copy id
|
// Copy id
|
||||||
strncpy(q->id, opts.id, FILE_ID_STRLEN - 1);
|
strncpy(q->id, opts.id, FILE_ID_STRLEN - 1);
|
||||||
|
@ -83,6 +83,7 @@ bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz)
|
||||||
|
|
||||||
void rf_file_rx_close(rf_file_rx_t* q)
|
void rf_file_rx_close(rf_file_rx_t* q)
|
||||||
{
|
{
|
||||||
|
rf_file_info(q->id, "Closing ...\n");
|
||||||
q->running = false;
|
q->running = false;
|
||||||
|
|
||||||
if (q->temp_buffer) {
|
if (q->temp_buffer) {
|
||||||
|
|
|
@ -42,7 +42,8 @@ typedef struct {
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
cf_t* zeros;
|
cf_t* zeros;
|
||||||
void* temp_buffer_convert;
|
void* temp_buffer_convert;
|
||||||
uint32_t frequency_hz_mhz;
|
uint32_t frequency_mhz;
|
||||||
|
int32_t sample_offset;
|
||||||
} rf_file_tx_t;
|
} rf_file_tx_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -68,8 +69,29 @@ typedef struct {
|
||||||
/*
|
/*
|
||||||
* Common functions
|
* Common functions
|
||||||
*/
|
*/
|
||||||
|
SRSRAN_API void rf_file_info(char* id, const char* format, ...);
|
||||||
|
|
||||||
|
SRSRAN_API void rf_file_error(char* id, const char* format, ...);
|
||||||
|
|
||||||
SRSRAN_API int rf_file_handle_error(char* id, const char* text);
|
SRSRAN_API int rf_file_handle_error(char* id, const char* text);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmitter functions
|
||||||
|
*/
|
||||||
|
SRSRAN_API int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts);
|
||||||
|
|
||||||
|
SRSRAN_API int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts);
|
||||||
|
|
||||||
|
SRSRAN_API int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples);
|
||||||
|
|
||||||
|
SRSRAN_API int rf_file_tx_get_nsamples(rf_file_tx_t* q);
|
||||||
|
|
||||||
|
SRSRAN_API int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples);
|
||||||
|
|
||||||
|
SRSRAN_API bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz);
|
||||||
|
|
||||||
|
SRSRAN_API void rf_file_tx_close(rf_file_tx_t* q);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Receiver functions
|
* Receiver functions
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rf_file_imp_trx.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <srsran/config.h>
|
||||||
|
#include <srsran/phy/utils/vector.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts)
|
||||||
|
{
|
||||||
|
int ret = SRSRAN_ERROR;
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
// Zero object
|
||||||
|
memset(q, 0, sizeof(rf_file_tx_t));
|
||||||
|
|
||||||
|
// Copy id
|
||||||
|
strncpy(q->id, opts.id, FILE_ID_STRLEN - 1);
|
||||||
|
q->id[FILE_ID_STRLEN - 1] = '\0';
|
||||||
|
|
||||||
|
// Assign file
|
||||||
|
q->file = opts.file;
|
||||||
|
|
||||||
|
// Configure formats
|
||||||
|
q->sample_format = opts.sample_format;
|
||||||
|
q->frequency_mhz = opts.frequency_mhz;
|
||||||
|
|
||||||
|
q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
|
||||||
|
if (!q->temp_buffer_convert) {
|
||||||
|
fprintf(stderr, "Error: allocating tx buffer\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&q->mutex, NULL)) {
|
||||||
|
fprintf(stderr, "Error: creating mutex\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->zeros = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
|
||||||
|
if (!q->zeros) {
|
||||||
|
fprintf(stderr, "Error: allocating zeros\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
memset(q->zeros, 0, FILE_MAX_BUFFER_SIZE);
|
||||||
|
|
||||||
|
q->running = true;
|
||||||
|
|
||||||
|
ret = SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples)
|
||||||
|
{
|
||||||
|
int n = SRSRAN_ERROR;
|
||||||
|
|
||||||
|
// convert samples if necessary
|
||||||
|
void* buf = (buffer) ? buffer : q->zeros;
|
||||||
|
uint32_t sample_sz = sizeof(cf_t);
|
||||||
|
|
||||||
|
if (q->sample_format == FILERF_TYPE_SC16) {
|
||||||
|
buf = q->temp_buffer_convert;
|
||||||
|
sample_sz = 2 * sizeof(short);
|
||||||
|
srsran_vec_convert_fi((float*)buffer, INT16_MAX, (short*)q->temp_buffer_convert, 2 * nsamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ret = fwrite(buf, (size_t)sample_sz, (size_t)nsamples, q->file);
|
||||||
|
if (ret < (size_t)nsamples) {
|
||||||
|
rf_file_error(q->id,
|
||||||
|
"[file] Error: transmitter expected %d bytes and sent %d. %s.\n",
|
||||||
|
NSAMPLES2NBYTES(nsamples),
|
||||||
|
ret,
|
||||||
|
strerror(errno));
|
||||||
|
n = SRSRAN_ERROR;
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment sample counter
|
||||||
|
q->nsamples += nsamples;
|
||||||
|
n = nsamples;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&q->mutex);
|
||||||
|
|
||||||
|
int64_t nsamples = (int64_t)ts - (int64_t)q->nsamples;
|
||||||
|
|
||||||
|
if (nsamples > 0) {
|
||||||
|
rf_file_info(q->id, " - Detected Tx gap of %d samples.\n", nsamples);
|
||||||
|
_rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&q->mutex);
|
||||||
|
|
||||||
|
return (int)nsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&q->mutex);
|
||||||
|
|
||||||
|
if (q->sample_offset > 0) {
|
||||||
|
_rf_file_tx_baseband(q, q->zeros, (uint32_t)q->sample_offset);
|
||||||
|
q->sample_offset = 0;
|
||||||
|
} else if (q->sample_offset < 0) {
|
||||||
|
n = SRSRAN_MIN(-q->sample_offset, nsamples);
|
||||||
|
buffer += n;
|
||||||
|
nsamples -= n;
|
||||||
|
q->sample_offset += n;
|
||||||
|
if (nsamples == 0) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = _rf_file_tx_baseband(q, buffer, nsamples);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&q->mutex);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rf_file_tx_get_nsamples(rf_file_tx_t* q)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&q->mutex);
|
||||||
|
int ret = q->nsamples;
|
||||||
|
pthread_mutex_unlock(&q->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&q->mutex);
|
||||||
|
|
||||||
|
rf_file_info(q->id, " - Tx %d Zeros.\n", nsamples);
|
||||||
|
_rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&q->mutex);
|
||||||
|
|
||||||
|
return (int)nsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (q) {
|
||||||
|
ret = (q->frequency_mhz == 0 || q->frequency_mhz == freq_hz);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rf_file_tx_close(rf_file_tx_t* q)
|
||||||
|
{
|
||||||
|
rf_file_info(q->id, "Closing ...\n");
|
||||||
|
pthread_mutex_lock(&q->mutex);
|
||||||
|
q->running = false;
|
||||||
|
pthread_mutex_unlock(&q->mutex);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&q->mutex);
|
||||||
|
|
||||||
|
if (q->zeros) {
|
||||||
|
free(q->zeros);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->temp_buffer_convert) {
|
||||||
|
free(q->temp_buffer_convert);
|
||||||
|
}
|
||||||
|
|
||||||
|
q->file = NULL;
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rf_file_imp.h"
|
||||||
|
#include "srsran/common/tsan_options.h"
|
||||||
|
#include "srsran/phy/common/timestamp.h"
|
||||||
|
#include "srsran/phy/utils/debug.h"
|
||||||
|
#include <complex.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <srsran/phy/common/phy_common.h>
|
||||||
|
#include <srsran/phy/utils/vector.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define PRINT_SAMPLES 0
|
||||||
|
#define COMPARE_BITS 0
|
||||||
|
#define COMPARE_EPSILON (1e-6f)
|
||||||
|
#define NOF_RX_ANT 4
|
||||||
|
#define NUM_SF (500)
|
||||||
|
#define SF_LEN (1920)
|
||||||
|
#define RF_BUFFER_SIZE (SF_LEN * NUM_SF)
|
||||||
|
#define TX_OFFSET_MS (4)
|
||||||
|
|
||||||
|
static cf_t ue_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
|
||||||
|
static cf_t enb_tx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
|
||||||
|
static cf_t enb_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
|
||||||
|
|
||||||
|
static srsran_rf_t ue_radio, enb_radio;
|
||||||
|
pthread_t rx_thread;
|
||||||
|
|
||||||
|
void* ue_rx_thread_function(void* args)
|
||||||
|
{
|
||||||
|
char rf_args[RF_PARAM_LEN];
|
||||||
|
strncpy(rf_args, (char*)args, RF_PARAM_LEN - 1);
|
||||||
|
rf_args[RF_PARAM_LEN - 1] = 0;
|
||||||
|
|
||||||
|
// sleep(1);
|
||||||
|
|
||||||
|
printf("opening rx device with args=%s\n", rf_args);
|
||||||
|
if (srsran_rf_open_devname(&ue_radio, "file", rf_args, NOF_RX_ANT)) {
|
||||||
|
fprintf(stderr, "Error opening rf\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive 5 subframes at once (i.e. mimic initial rx that receives one slot)
|
||||||
|
uint32_t num_slots = NUM_SF / 5;
|
||||||
|
uint32_t num_samps_per_slot = SF_LEN * 5;
|
||||||
|
uint32_t num_rxed_samps = 0;
|
||||||
|
for (uint32_t i = 0; i < num_slots; ++i) {
|
||||||
|
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL};
|
||||||
|
for (uint32_t c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
data_ptr[c] = &ue_rx_buffer[c][i * num_samps_per_slot];
|
||||||
|
}
|
||||||
|
num_rxed_samps += srsran_rf_recv_with_time_multi(&ue_radio, data_ptr, num_samps_per_slot, true, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("received %d samples.\n", num_rxed_samps);
|
||||||
|
|
||||||
|
printf("closing ue rx device\n");
|
||||||
|
srsran_rf_close(&ue_radio);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enb_tx_function(const char* tx_args, bool timed_tx)
|
||||||
|
{
|
||||||
|
char rf_args[RF_PARAM_LEN];
|
||||||
|
strncpy(rf_args, tx_args, RF_PARAM_LEN - 1);
|
||||||
|
rf_args[RF_PARAM_LEN - 1] = 0;
|
||||||
|
|
||||||
|
printf("opening tx device with args=%s\n", rf_args);
|
||||||
|
if (srsran_rf_open_devname(&enb_radio, "file", rf_args, NOF_RX_ANT)) {
|
||||||
|
fprintf(stderr, "Error opening rf\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate random tx data
|
||||||
|
for (int c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
for (int i = 0; i < RF_BUFFER_SIZE; i++) {
|
||||||
|
enb_tx_buffer[c][i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send data subframe per subframe
|
||||||
|
uint32_t num_txed_samples = 0;
|
||||||
|
|
||||||
|
// initial transmission without ts
|
||||||
|
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL};
|
||||||
|
for (int c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
data_ptr[c] = &enb_tx_buffer[c][num_txed_samples];
|
||||||
|
}
|
||||||
|
int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false);
|
||||||
|
num_txed_samples += SF_LEN;
|
||||||
|
|
||||||
|
// from here on, all transmissions are timed relative to the last rx time
|
||||||
|
srsran_timestamp_t rx_time, tx_time;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) {
|
||||||
|
// first recv samples
|
||||||
|
for (int c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
data_ptr[c] = enb_rx_buffer[c];
|
||||||
|
}
|
||||||
|
srsran_rf_recv_with_time_multi(&enb_radio, data_ptr, SF_LEN, true, &rx_time.full_secs, &rx_time.frac_secs);
|
||||||
|
|
||||||
|
// prepare data buffer
|
||||||
|
for (int c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
data_ptr[c] = &enb_tx_buffer[c][num_txed_samples];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timed_tx) {
|
||||||
|
// timed tx relative to receive time (this will cause a cap in the rx'ed samples at the UE resulting in 3 zero
|
||||||
|
// subframes)
|
||||||
|
srsran_timestamp_copy(&tx_time, &rx_time);
|
||||||
|
srsran_timestamp_add(&tx_time, 0, TX_OFFSET_MS * 1e-3);
|
||||||
|
ret = srsran_rf_send_timed_multi(
|
||||||
|
&enb_radio, (void**)data_ptr, SF_LEN, tx_time.full_secs, tx_time.frac_secs, true, true, false);
|
||||||
|
} else {
|
||||||
|
// normal tx
|
||||||
|
ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false);
|
||||||
|
}
|
||||||
|
if (ret != SRSRAN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Error sending data\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
num_txed_samples += SF_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("transmitted %d samples in %d subframes\n", num_txed_samples, NUM_SF);
|
||||||
|
|
||||||
|
printf("closing tx device\n");
|
||||||
|
srsran_rf_close(&enb_radio);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_test(const char* rx_args, const char* tx_args, bool timed_tx)
|
||||||
|
{
|
||||||
|
int ret = SRSRAN_ERROR;
|
||||||
|
|
||||||
|
// make sure we can receive in slots
|
||||||
|
if (NUM_SF % 5 != 0) {
|
||||||
|
fprintf(stderr, "number of subframes must be multiple of 5\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to file(s)
|
||||||
|
enb_tx_function(tx_args, timed_tx);
|
||||||
|
|
||||||
|
// read from file(s)
|
||||||
|
ue_rx_thread_function((void*)rx_args);
|
||||||
|
|
||||||
|
// channel-wise comparison
|
||||||
|
for (int c = 0; c < NOF_RX_ANT; c++) {
|
||||||
|
// subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx)
|
||||||
|
for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) {
|
||||||
|
uint32_t sf_offet = 0;
|
||||||
|
if (timed_tx && i >= 1) {
|
||||||
|
// for timed transmission, the enb inserts 3 zero subframes after the first untimed tx
|
||||||
|
sf_offet = (TX_OFFSET_MS - 1) * SF_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PRINT_SAMPLES
|
||||||
|
// print first 10 samples for each SF
|
||||||
|
printf("enb_tx_buffer sf%d:\n", i);
|
||||||
|
srsran_vec_fprint_c(stdout, &enb_tx_buffer[c][i * SF_LEN], 10);
|
||||||
|
printf("ue_rx_buffer sf%d:\n", i);
|
||||||
|
srsran_vec_fprint_c(stdout, &ue_rx_buffer[c][sf_offet + i * SF_LEN], 10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COMPARE_BITS
|
||||||
|
int d = memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN);
|
||||||
|
if (d) {
|
||||||
|
d = d > 0 ? d : -d;
|
||||||
|
fprintf(stderr, "data mismatch in subframe %d, sample %d\n", i, d);
|
||||||
|
printf("enb_tx_buffer sf%d:\n", i);
|
||||||
|
srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN + d], 10);
|
||||||
|
printf("ue_rx_buffer sf%d:\n", i);
|
||||||
|
srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN + d], 10);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
srsran_vec_sub_ccc(&ue_rx_buffer[c][sf_offet + i * SF_LEN],
|
||||||
|
&enb_tx_buffer[c][i * SF_LEN],
|
||||||
|
&ue_rx_buffer[c][sf_offet + i * SF_LEN],
|
||||||
|
SF_LEN);
|
||||||
|
uint32_t max_ix = srsran_vec_max_abs_ci(&ue_rx_buffer[c][sf_offet + i * SF_LEN], SF_LEN);
|
||||||
|
if (cabsf(ue_rx_buffer[c][sf_offet + i * SF_LEN + max_ix]) > COMPARE_EPSILON) {
|
||||||
|
fprintf(stderr, "data mismatch in subframe %d\n", i);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SRSRAN_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int param_test(const char* args_param, const int num_channels)
|
||||||
|
{
|
||||||
|
char rf_args[RF_PARAM_LEN] = {};
|
||||||
|
strncpy(rf_args, (char*)args_param, RF_PARAM_LEN - 1);
|
||||||
|
rf_args[RF_PARAM_LEN - 1] = 0;
|
||||||
|
|
||||||
|
printf("opening tx device with args=%s\n", rf_args);
|
||||||
|
if (srsran_rf_open_devname(&enb_radio, "file", rf_args, num_channels)) {
|
||||||
|
fprintf(stderr, "Error opening rf\n");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran_rf_close(&enb_radio);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_file(const char* filename)
|
||||||
|
{
|
||||||
|
FILE* f = fopen(filename, "w");
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// create files for testing
|
||||||
|
create_file("rx_file0");
|
||||||
|
create_file("rx_file1");
|
||||||
|
create_file("rx_file2");
|
||||||
|
create_file("rx_file3");
|
||||||
|
|
||||||
|
// two RX files
|
||||||
|
if (param_test("rx_file=rx_file0,"
|
||||||
|
"rx_file1=rx_file1",
|
||||||
|
2)) {
|
||||||
|
fprintf(stderr, "Param test failed!\n");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple RX files, no channel index provided
|
||||||
|
if (param_test("rx_file=rx_file0,"
|
||||||
|
"rx_file=rx_file1,"
|
||||||
|
"rx_file=rx_file2,"
|
||||||
|
"rx_file=rx_file3,"
|
||||||
|
"base_srate=1.92e6",
|
||||||
|
4)) {
|
||||||
|
fprintf(stderr, "Param test failed!\n");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// one RX, one TX and all generic options
|
||||||
|
if (param_test("rx_file0=rx_file0,"
|
||||||
|
"tx_file0=tx_file0,"
|
||||||
|
"base_srate=1.92e6",
|
||||||
|
1)) {
|
||||||
|
fprintf(stderr, "Param test failed!\n");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// two RX, two TX
|
||||||
|
if (param_test("rx_file0=rx_file0,"
|
||||||
|
"rx_file1=rx_file1,"
|
||||||
|
"tx_file0=tx_file0,"
|
||||||
|
"tx_file1=tx_file1",
|
||||||
|
2)) {
|
||||||
|
fprintf(stderr, "Param test failed!\n");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NOF_RX_ANT == 1
|
||||||
|
// single tx, single rx with continuous transmissions (no decimation, no timed tx)
|
||||||
|
if (run_test("rx_file=tx_file0,base_srate=1.92e6", "tx_file=tx_file0,base_srate=1.92e6", false) != SRSRAN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Single tx, single rx test failed (no decimation, no timed tx)!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// up to 4 trx radios with continous tx (no decimation, no timed tx)
|
||||||
|
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3,base_srate=1.92e6",
|
||||||
|
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3,base_srate=1.92e6",
|
||||||
|
false) != SRSRAN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Multi TRx radio test failed (no decimation, no timed tx)!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// up to 4 trx radios with continous tx (with decimation, no timed tx)
|
||||||
|
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3",
|
||||||
|
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3",
|
||||||
|
false) != SRSRAN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Multi TRx radio test failed (with decimation, no timed tx)!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// up to 4 trx radios with continous tx (with decimation, timed tx)
|
||||||
|
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3",
|
||||||
|
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3",
|
||||||
|
true) != SRSRAN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Two TRx radio test failed (with decimation, timed tx)!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "Test passed!\n");
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue