mirror of https://github.com/pvnis/srsRAN_4G.git
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 conflictsmaster
parent
9f25a91282
commit
d3eca325f7
@ -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
|
Loading…
Reference in New Issue