Add file-based RF device for rx from arbitrary FILE*

It comes with the following changes:

* Add RF frontend API function "srslte_rf_open_file()"
* Open file-based RF via explicit call of custom
  "rf_file_open_file()", instead of "srslte_rf_open_multi()"
  function pointer as for regular devices.
* Introduce device name "file"
* Introduce new SRSLTE_ERROR_RX_EOF error code
* ZMQ: make update_rates() static to resolve conflicts
master
Robert Falkenberg 3 years ago committed by Robert Falkenberg
parent 9f25a91282
commit d3eca325f7

@ -53,6 +53,7 @@
#define SRSRAN_ERROR_OUT_OF_BOUNDS -5 #define SRSRAN_ERROR_OUT_OF_BOUNDS -5
#define SRSRAN_ERROR_CANT_START -6 #define SRSRAN_ERROR_CANT_START -6
#define SRSRAN_ERROR_ALREADY_STARTED -7 #define SRSRAN_ERROR_ALREADY_STARTED -7
#define SRSRAN_ERROR_RX_EOF -8
// cf_t definition // cf_t definition
typedef _Complex float cf_t; typedef _Complex float cf_t;

@ -16,6 +16,7 @@
#include <pthread.h> #include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <sys/time.h> #include <sys/time.h>
#include "srsran/config.h" #include "srsran/config.h"
@ -68,6 +69,18 @@ SRSRAN_API int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_cha
SRSRAN_API int srsran_rf_open_devname(srsran_rf_t* h, const char* devname, char* args, uint32_t nof_channels); SRSRAN_API int srsran_rf_open_devname(srsran_rf_t* h, const char* devname, char* args, uint32_t nof_channels);
/**
* @brief Opens a file-based RF abstraction
* @param[out] rf Device handle
* @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable
* @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable
* @param[in] nof_channels Number of channels per direction
* @param[in] base_srate Sample rate of RX and TX files
* @return SRSRAN_SUCCESS on success, otherwise error code
*/
SRSRAN_API int
srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
SRSRAN_API const char* srsran_rf_name(srsran_rf_t* h); SRSRAN_API const char* srsran_rf_name(srsran_rf_t* h);
SRSRAN_API int srsran_rf_start_gain_thread(srsran_rf_t* rf, bool tx_gain_same_rx); SRSRAN_API int srsran_rf_start_gain_thread(srsran_rf_t* rf, bool tx_gain_same_rx);

@ -59,6 +59,8 @@ 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)
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)

@ -216,6 +216,40 @@ static rf_dev_t dev_zmq = {"zmq",
.srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi};
#endif #endif
/* Define implementation for file-based RF */
#include "rf_file_imp.h"
static rf_dev_t dev_file = {"file",
rf_file_devname,
rf_file_start_rx_stream,
rf_file_stop_rx_stream,
rf_file_flush_buffer,
rf_file_has_rssi,
rf_file_get_rssi,
rf_file_suppress_stdout,
rf_file_register_error_handler,
rf_file_open,
.srsran_rf_open_multi = rf_file_open_multi,
rf_file_close,
rf_file_set_rx_srate,
rf_file_set_rx_gain,
rf_file_set_rx_gain_ch,
rf_file_set_tx_gain,
rf_file_set_tx_gain_ch,
rf_file_get_rx_gain,
rf_file_get_tx_gain,
rf_file_get_info,
rf_file_set_rx_freq,
rf_file_set_tx_srate,
rf_file_set_tx_freq,
rf_file_get_time,
NULL,
rf_file_recv_with_time,
rf_file_recv_with_time_multi,
rf_file_send_timed,
.srsran_rf_send_timed_multi = rf_file_send_timed_multi};
/* Define implementation for Sidekiq */ /* Define implementation for Sidekiq */
#ifdef ENABLE_SIDEKIQ #ifdef ENABLE_SIDEKIQ
@ -267,6 +301,9 @@ static rf_dev_t dev_dummy = {"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_f
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc}; dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc};
#endif #endif
/**
* Collection of all currently supported RF devices
*/
static rf_dev_t* available_devices[] = { static rf_dev_t* available_devices[] = {
#ifdef ENABLE_UHD #ifdef ENABLE_UHD

@ -0,0 +1,597 @@
/**
*
* \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.
*
*/
#ifndef SRSRAN_RF_IMP_TRX_H
#define SRSRAN_RF_IMP_TRX_H
#include "rf_file_imp.h"
#include "rf_file_imp_trx.h"
#include "rf_helper.h"
#include <math.h>
#include <srsran/phy/common/phy_common.h>
#include <srsran/phy/common/timestamp.h>
#include <srsran/phy/utils/vector.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct {
// Common attributes
char* devname;
srsran_rf_info_t info;
uint32_t nof_channels;
// RF State
uint32_t srate; // radio rate configured by upper layers
uint32_t base_srate;
uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate
double rx_gain;
uint32_t tx_freq_mhz[SRSRAN_MAX_PORTS];
uint32_t rx_freq_mhz[SRSRAN_MAX_PORTS];
bool tx_used;
// FILEs
rf_file_tx_t transmitter[SRSRAN_MAX_PORTS];
rf_file_rx_t receiver[SRSRAN_MAX_PORTS];
char id[PARAM_LEN_SHORT];
// Various sample buffers
cf_t* buffer_decimation[SRSRAN_MAX_PORTS];
cf_t* buffer_tx;
// Rx timestamp
uint64_t next_rx_ts;
pthread_mutex_t tx_config_mutex;
pthread_mutex_t rx_config_mutex;
pthread_mutex_t decim_mutex;
} rf_file_handler_t;
/*
* Static methods
*/
static void update_rates(rf_file_handler_t* handler, double srate);
static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir)
{
int ret = SRSRAN_ERROR;
if (h && nsamples > 0) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
(*ts) += nsamples;
srsran_timestamp_t _ts = {};
srsran_timestamp_init_uint64(&_ts, *ts, handler->base_srate);
ret = SRSRAN_SUCCESS;
}
return ret;
}
int rf_file_handle_error(char* id, const char* text)
{
// Not implemented
return SRSRAN_ERROR;
}
/*
* Public methods
*/
const char* rf_file_devname(void* h)
{
return DEVNAME_FILE;
}
int rf_file_start_rx_stream(void* h, bool now)
{
return SRSRAN_SUCCESS;
}
int rf_file_stop_rx_stream(void* h)
{
return SRSRAN_SUCCESS;
}
void rf_file_flush_buffer(void* h)
{
printf("%s\n", __FUNCTION__);
}
bool rf_file_has_rssi(void* h)
{
return false;
}
float rf_file_get_rssi(void* h)
{
return 0.0;
}
void rf_file_suppress_stdout(void* h)
{
// do nothing
}
void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg)
{
// do nothing
}
int rf_file_open(char* args, void** h)
{
return rf_file_open_multi(args, h, 1);
}
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.");
return SRSRAN_ERROR_INVALID_COMMAND;
}
int rf_file_open_file(void** h, FILE **rx_files, FILE **tx_files, uint32_t nof_channels, uint32_t base_srate)
{
int ret = SRSRAN_ERROR;
if (h) {
*h = NULL;
rf_file_handler_t* handler = (rf_file_handler_t*)malloc(sizeof(rf_file_handler_t));
if (!handler) {
perror("malloc");
return SRSRAN_ERROR;
}
memset(handler, 0, sizeof(rf_file_handler_t));
*h = handler;
handler->base_srate = FILE_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell
handler->rx_gain = 0.0;
handler->info.max_rx_gain = FILE_MAX_GAIN_DB;
handler->info.min_rx_gain = FILE_MIN_GAIN_DB;
handler->info.max_tx_gain = FILE_MAX_GAIN_DB;
handler->info.min_tx_gain = FILE_MIN_GAIN_DB;
handler->nof_channels = nof_channels;
strcpy(handler->id, "file\0");
rf_file_opts_t rx_opts = {};
rf_file_opts_t tx_opts = {};
tx_opts.id = handler->id;
rx_opts.id = handler->id;
if (pthread_mutex_init(&handler->tx_config_mutex, NULL)) {
perror("Mutex init");
}
if (pthread_mutex_init(&handler->rx_config_mutex, NULL)) {
perror("Mutex init");
}
if (pthread_mutex_init(&handler->decim_mutex, NULL)) {
perror("Mutex init");
}
// base_srate
handler->base_srate = base_srate;
// id
// TODO: set some meaningful ID in handler->id
// rx_format, tx_format
// TODO: add other formats
rx_opts.sample_format = FILERF_TYPE_FC32;
tx_opts.sample_format = FILERF_TYPE_FC32;
update_rates(handler, 1.92e6);
// Create channels
for (int i = 0; i < handler->nof_channels; i++) {
if (rx_files != NULL) {
rx_opts.file = rx_files[i];
if (rf_file_rx_open(&handler->receiver[i], rx_opts) != SRSRAN_SUCCESS) {
fprintf(stderr, "[file] Error: opening receiver\n");
goto clean_exit;
}
} else {
fprintf(stdout, "[file] %s Rx file not specified. Disabling receiver.\n", handler->id);
}
if (tx_files != NULL) {
tx_opts.file = tx_files[i];
// TX is not implemented yet
// if(rf_file_tx_open(&handler->transmitter[i], tx_opts) != SRSRAN_SUCCESS) {
// fprintf(stderr, "[file] Error: opening transmitter\n");
// goto clean_exit;
// }
} else {
// no tx_files provided
fprintf(stdout, "[file] %s Tx file not specified. Disabling transmitter.\n", handler->id);
}
if (!handler->transmitter[i].running && !handler->receiver[i].running) {
fprintf(stderr, "[file] Error: Neither Tx file nor Rx file specified.\n");
goto clean_exit;
}
}
// Create decimation and overflow buffer
for (uint32_t i = 0; i < handler->nof_channels; i++) {
handler->buffer_decimation[i] = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!handler->buffer_decimation[i]) {
fprintf(stderr, "Error: allocating decimation buffer\n");
goto clean_exit;
}
}
handler->buffer_tx = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!handler->buffer_tx) {
fprintf(stderr, "Error: allocating tx buffer\n");
goto clean_exit;
}
ret = SRSRAN_SUCCESS;
clean_exit:
if (ret) {
rf_file_close(handler);
}
}
return ret;
}
int rf_file_close(void* h)
{
rf_file_handler_t* handler = (rf_file_handler_t*)h;
for (uint32_t i = 0; i < handler->nof_channels; i++) {
if (handler->buffer_decimation[i]) {
free(handler->buffer_decimation[i]);
}
}
if (handler->buffer_tx) {
free(handler->buffer_tx);
}
pthread_mutex_destroy(&handler->tx_config_mutex);
pthread_mutex_destroy(&handler->rx_config_mutex);
pthread_mutex_destroy(&handler->decim_mutex);
// Free all
free(handler);
return SRSRAN_SUCCESS;
}
void update_rates(rf_file_handler_t* handler, double srate)
{
pthread_mutex_lock(&handler->decim_mutex);
if (handler) {
// Decimation must be full integer
if (((uint64_t)handler->base_srate % (uint64_t)srate) == 0) {
handler->srate = (uint32_t)srate;
handler->decim_factor = handler->base_srate / handler->srate;
} else {
fprintf(stderr,
"Error: couldn't update sample rate. %.2f is not divisible by %.2f\n",
srate / 1e6,
handler->base_srate / 1e6);
}
printf("Current sample rate is %.2f MHz with a base rate of %.2f MHz (x%d decimation)\n",
handler->srate / 1e6,
handler->base_srate / 1e6,
handler->decim_factor);
}
pthread_mutex_unlock(&handler->decim_mutex);
}
double rf_file_set_rx_srate(void* h, double srate)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
update_rates(handler, srate);
ret = handler->srate;
}
return ret;
}
int rf_file_set_rx_gain(void* h, double gain)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
handler->rx_gain = gain;
ret = gain;
}
return ret;
}
int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain)
{
return rf_file_set_rx_gain(h, gain);
}
int rf_file_set_tx_gain(void* h, double gain)
{
return 0.0;
}
int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain)
{
return rf_file_set_tx_gain(h, gain);
}
double rf_file_get_rx_gain(void* h)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
ret = handler->rx_gain;
}
return ret;
}
double rf_file_get_tx_gain(void* h)
{
return 0.0;
}
srsran_rf_info_t* rf_file_get_info(void* h)
{
srsran_rf_info_t* info = NULL;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
info = &handler->info;
}
return info;
}
double rf_file_set_rx_freq(void* h, uint32_t ch, double freq)
{
double ret = NAN;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
pthread_mutex_lock(&handler->rx_config_mutex);
if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) {
handler->rx_freq_mhz[ch] = (uint32_t)(freq / 1e6);
ret = freq;
}
pthread_mutex_unlock(&handler->rx_config_mutex);
}
return ret;
}
double rf_file_set_tx_srate(void* h, double srate)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
update_rates(handler, srate);
ret = srate;
}
return ret;
}
double rf_file_set_tx_freq(void* h, uint32_t ch, double freq)
{
double ret = NAN;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
pthread_mutex_lock(&handler->tx_config_mutex);
if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) {
handler->tx_freq_mhz[ch] = (uint32_t)(freq / 1e6);
ret = freq;
}
pthread_mutex_unlock(&handler->tx_config_mutex);
}
return ret;
}
void rf_file_get_time(void* h, time_t* secs, double* frac_secs)
{
if (h) {
if (secs) {
*secs = 0;
}
if (frac_secs) {
*frac_secs = 0;
}
}
}
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};
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,
void* data[SRSRAN_MAX_PORTS],
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs)
{
int ret = SRSRAN_ERROR;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
// Map ports to data buffers according to the selected frequencies
pthread_mutex_lock(&handler->rx_config_mutex);
cf_t* buffers[SRSRAN_MAX_PORTS] = {}; // Buffer pointers, NULL if unmatched
for (uint32_t i = 0; i < handler->nof_channels; i++) {
bool mapped = false;
// Find first matching frequency
for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) {
// Traverse all channels, break if mapped
if (buffers[j] == NULL && rf_file_rx_match_freq(&handler->receiver[j], handler->rx_freq_mhz[i])) {
// Available buffer and matched frequency with receiver
buffers[j] = (cf_t*)data[i];
mapped = true;
}
}
// If no matching frequency found; set data to zeros
if (!mapped && data[i]) {
memset(data[i], 0, sizeof(cf_t) * nsamples);
}
}
pthread_mutex_unlock(&handler->rx_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 * decim_factor);
uint32_t nsamples_baserate = nsamples * decim_factor;
// set timestamp for this reception
if (secs != NULL && frac_secs != NULL) {
srsran_timestamp_t ts = {};
srsran_timestamp_init_uint64(&ts, handler->next_rx_ts, handler->base_srate);
*secs = ts.full_secs;
*frac_secs = ts.frac_secs;
}
// return if receiver is turned off
if (!handler->receiver[0].running) {
update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx");
return nsamples;
}
// Check available buffer size
if (nbytes > FILE_MAX_BUFFER_SIZE) {
fprintf(stderr,
"[file] Error: Trying to receive %d B but buffer is only %zu B at channel %d.\n",
nbytes,
FILE_MAX_BUFFER_SIZE,
0);
goto clean_exit;
}
// receive samples
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_rx, handler->next_rx_ts, handler->base_srate);
// copy from rx buffer as many samples as requested into provided buffer
bool completed = false;
int32_t count[SRSRAN_MAX_PORTS] = {};
while (!completed) {
uint32_t completed_count = 0;
// Iterate channels
for (uint32_t i = 0; i < handler->nof_channels; i++) {
cf_t* ptr = (decim_factor != 1 || buffers[i] == NULL) ? handler->buffer_decimation[i] : buffers[i];
// Completed condition
if (count[i] < nsamples_baserate && handler->receiver[i].running) {
// Keep receiving
int32_t n = rf_file_rx_baseband(&handler->receiver[i], &ptr[count[i]], nsamples_baserate - count[i]);
if (n > 0) {
// No error
count[i] += n;
} else {
if (n != SRSRAN_ERROR_RX_EOF) {
// Other error, exit
fprintf(stderr, "Error: receiving data.\n");
}
ret = n;
goto clean_exit;
}
} else {
// Completed, count it
completed_count++;
}
}
// Check if all channels are completed
completed = (completed_count == handler->nof_channels);
}
// decimate if needed
if (decim_factor != 1) {
for (uint32_t c = 0; c < handler->nof_channels; c++) {
// skip if buffer is not available
if (buffers[c]) {
cf_t* dst = buffers[c];
cf_t* ptr = handler->buffer_decimation[c];
for (uint32_t i = 0, n = 0; i < nsamples; i++) {
// Averaging decimation
cf_t avg = 0.0f;
for (int j = 0; j < decim_factor; j++, n++) {
avg += ptr[n];
}
dst[i] = avg;
}
}
}
}
// Set gain
float scale = srsran_convert_dB_to_amplitude(handler->rx_gain);
for (uint32_t c = 0; c < handler->nof_channels; c++) {
if (buffers[c]) {
srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples);
}
}
// update rx time
update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx");
}
ret = nsamples;
clean_exit:
return ret;
}
int rf_file_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
void* _data[4] = {data, NULL, NULL, NULL};
return rf_file_send_timed_multi(
h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst);
}
int rf_file_send_timed_multi(void* h,
void* data[4],
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
// Not implemented
fprintf(stderr, "Error: rf_file_send_timed_multi not implemented.\n");
return SRSRAN_ERROR;
}
#endif

@ -0,0 +1,136 @@
/**
*
* \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.
*
*/
#ifndef SRSRAN_RF_FILE_IMP_H
#define SRSRAN_RF_FILE_IMP_H
#include <inttypes.h>
#include "srsran/config.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/rf/rf.h"
#define DEVNAME_FILE "file"
#define PARAM_LEN (128)
#define PARAM_LEN_SHORT (PARAM_LEN / 2)
SRSRAN_API const char* rf_file_devname(void* h);
SRSRAN_API int rf_file_start_rx_stream(void* h, bool now);
// SRSRAN_API int rf_file_start_rx_stream_nsamples(void* h, uint32_t nsamples);
SRSRAN_API int rf_file_stop_rx_stream(void* h);
SRSRAN_API void rf_file_flush_buffer(void* h);
SRSRAN_API bool rf_file_has_rssi(void* h);
SRSRAN_API float rf_file_get_rssi(void* h);
SRSRAN_API void rf_file_suppress_stdout(void* h);
SRSRAN_API void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg);
/**
* @brief This function is not supported for file-based RF abstraction
*
* Use @c rf_file_open_file() to open this device
*
* @param args not used
* @param h not used
* @return SRSRAN_ERROR_INVALID_COMMAND
*/
SRSRAN_API int rf_file_open(char* args, void** h);
/**
* @brief This function is not supported for file-based RF abstraction
*
* Use @c rf_file_open_file() to open this device
*
* @param args not used
* @param h not used
* @param nof_channels not used
* @return SRSRAN_ERROR_INVALID_COMMAND
*/
SRSRAN_API int rf_file_open_multi(char* args, void** h, uint32_t nof_channels);
SRSRAN_API int rf_file_close(void* h);
SRSRAN_API double rf_file_set_rx_srate(void* h, double srate);
SRSRAN_API int rf_file_set_rx_gain(void* h, double gain);
SRSRAN_API int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain);
SRSRAN_API int rf_file_set_tx_gain(void* h, double gain);
SRSRAN_API int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain);
SRSRAN_API double rf_file_get_rx_gain(void* h);
SRSRAN_API double rf_file_get_tx_gain(void* h);
SRSRAN_API srsran_rf_info_t* rf_file_get_info(void* h);
SRSRAN_API double rf_file_set_rx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API double rf_file_set_tx_srate(void* h, double srate);
SRSRAN_API double rf_file_set_tx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API void rf_file_get_time(void* h, time_t* secs, double* frac_secs);
// srsran_rf_sync_pps
SRSRAN_API int
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,
void* data[SRSRAN_MAX_PORTS],
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs);
SRSRAN_API int rf_file_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
SRSRAN_API int rf_file_send_timed_multi(void* h,
void* data[4],
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
/**
* @brief Dedicated function to open a file-based RF abstraction
* @param[out] h Resulting object handle
* @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable
* @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable
* @param[in] nof_channels Number of channels per direction
* @param[in] base_srate Sample rate of RX and TX files
* @return SRSRAN_SUCCESS on success, otherwise error code
*/
SRSRAN_API int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
#endif // SRSRAN_RF_FILE_IMP_H

@ -0,0 +1,97 @@
/**
*
* \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 <srsran/phy/utils/vector.h>
#include <stdlib.h>
#include <string.h>
int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts)
{
int ret = SRSRAN_ERROR;
if (q) {
// Zero object
bzero(q, sizeof(rf_file_rx_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 = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->temp_buffer) {
fprintf(stderr, "Error: allocating rx buffer\n");
goto clean_exit;
}
q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->temp_buffer_convert) {
fprintf(stderr, "Error: allocating rx buffer\n");
goto clean_exit;
}
if (pthread_mutex_init(&q->mutex, NULL)) {
fprintf(stderr, "Error: creating mutex\n");
goto clean_exit;
}
q->running = true;
ret = SRSRAN_SUCCESS;
}
clean_exit:
return ret;
}
int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples)
{
uint32_t sample_sz = sizeof(cf_t);
int ret = fread(buffer, sample_sz, nsamples, q->file);
if (ret > 0) {
return ret;
} else {
return SRSRAN_ERROR_RX_EOF;
}
}
bool rf_file_rx_match_freq(rf_file_rx_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_rx_close(rf_file_rx_t* q)
{
q->running = false;
if (q->temp_buffer) {
free(q->temp_buffer);
}
if (q->temp_buffer_convert) {
free(q->temp_buffer_convert);
}
q->file = NULL;
}

@ -0,0 +1,84 @@
/**
*
* \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.
*
*/
#ifndef SRSRAN_RF_FILE_IMP_TRX_H
#define SRSRAN_RF_FILE_IMP_TRX_H
#include "srsran/config.h"
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/* Definitions */
#define VERBOSE (0)
#define NSAMPLES2NBYTES(X) (((uint32_t)(X)) * sizeof(cf_t))
#define NBYTES2NSAMPLES(X) ((X) / sizeof(cf_t))
#define FILE_MAX_BUFFER_SIZE (NSAMPLES2NBYTES(3072000)) // 10 subframes at 20 MHz
#define FILE_TIMEOUT_MS (1000)
#define FILE_BASERATE_DEFAULT_HZ (23040000)
#define FILE_ID_STRLEN 16
#define FILE_MAX_GAIN_DB (30.0f)
#define FILE_MIN_GAIN_DB (0.0f)
typedef enum { FILERF_TYPE_FC32 = 0, FILERF_TYPE_SC16 } rf_file_format_t;
typedef struct {
char id[FILE_ID_STRLEN];
rf_file_format_t sample_format;
FILE* file;
uint64_t nsamples;
bool running;
pthread_mutex_t mutex;
cf_t* zeros;
void* temp_buffer_convert;
uint32_t frequency_hz_mhz;
} rf_file_tx_t;
typedef struct {
char id[FILE_ID_STRLEN];
rf_file_format_t sample_format;
FILE* file;
uint64_t nsamples;
bool running;
pthread_t thread;
pthread_mutex_t mutex;
cf_t* temp_buffer;
void* temp_buffer_convert;
uint32_t frequency_mhz;
} rf_file_rx_t;
typedef struct {
const char* id;
rf_file_format_t sample_format;
FILE* file;
uint32_t frequency_mhz;
} rf_file_opts_t;
/*
* Common functions
*/
SRSRAN_API int rf_file_handle_error(char* id, const char* text);
/*
* Receiver functions
*/
SRSRAN_API int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts);
SRSRAN_API int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples);
SRSRAN_API bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz);
SRSRAN_API void rf_file_rx_close(rf_file_rx_t* q);
#endif // SRSRAN_RF_FILE_IMP_TRX_H

@ -139,6 +139,14 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
int srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
{
rf->dev = &dev_file;
// file abstraction has custom "open" function with file-related args
return rf_file_open_file(&rf->handler, rx_files, tx_files, nof_channels, base_srate);
}
const char* srsran_rf_name(srsran_rf_t* rf) const char* srsran_rf_name(srsran_rf_t* rf)
{ {
return ((rf_dev_t*)rf->dev)->srsran_rf_devname(rf->handler); return ((rf_dev_t*)rf->dev)->srsran_rf_devname(rf->handler);

@ -57,7 +57,7 @@ typedef struct {
pthread_mutex_t rx_gain_mutex; pthread_mutex_t rx_gain_mutex;
} rf_zmq_handler_t; } rf_zmq_handler_t;
void update_rates(rf_zmq_handler_t* handler, double srate); static void update_rates(rf_zmq_handler_t* handler, double srate);
/* /*
* Static Atributes * Static Atributes

Loading…
Cancel
Save