From 029f36b4493e2761a55c4d0146f9ab93cd4d4892 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 23 Jul 2020 12:02:51 +0200 Subject: [PATCH] srsLTE: added efficient integer resampler and srsue/srsenb integration --- lib/include/srslte/common/interfaces_common.h | 1 + lib/include/srslte/phy/dft/dft.h | 8 + lib/include/srslte/phy/resampling/resampler.h | 102 ++++++ lib/include/srslte/radio/radio.h | 27 +- lib/src/phy/resampling/resampler.c | 299 ++++++++++++++++++ lib/src/phy/resampling/test/CMakeLists.txt | 13 +- lib/src/phy/resampling/test/resampler_test.c | 118 +++++++ lib/src/radio/radio.cc | 97 +++++- srsenb/hdr/phy/phy_interfaces.h | 1 + srsenb/src/main.cc | 3 +- srsue/src/main.cc | 27 +- 11 files changed, 660 insertions(+), 36 deletions(-) create mode 100644 lib/include/srslte/phy/resampling/resampler.h create mode 100644 lib/src/phy/resampling/resampler.c create mode 100644 lib/src/phy/resampling/test/resampler_test.c diff --git a/lib/include/srslte/common/interfaces_common.h b/lib/include/srslte/common/interfaces_common.h index 765661b99..47b98f961 100644 --- a/lib/include/srslte/common/interfaces_common.h +++ b/lib/include/srslte/common/interfaces_common.h @@ -42,6 +42,7 @@ typedef struct { typedef struct { std::string type; std::string log_level; + double srate_hz; float dl_freq; float ul_freq; float freq_offset; diff --git a/lib/include/srslte/phy/dft/dft.h b/lib/include/srslte/phy/dft/dft.h index 34a8b3023..89405de4d 100644 --- a/lib/include/srslte/phy/dft/dft.h +++ b/lib/include/srslte/phy/dft/dft.h @@ -43,6 +43,10 @@ * Reference: *********************************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { SRSLTE_DFT_COMPLEX, SRSLTE_REAL } srslte_dft_mode_t; typedef enum { SRSLTE_DFT_FORWARD, SRSLTE_DFT_BACKWARD } srslte_dft_dir_t; @@ -120,4 +124,8 @@ SRSLTE_API void srslte_dft_run_guru_c(srslte_dft_plan_t* plan); SRSLTE_API void srslte_dft_run_r(srslte_dft_plan_t* plan, const float* in, float* out); +#ifdef __cplusplus +} +#endif + #endif // SRSLTE_DFT_H diff --git a/lib/include/srslte/phy/resampling/resampler.h b/lib/include/srslte/phy/resampling/resampler.h new file mode 100644 index 000000000..bcd097426 --- /dev/null +++ b/lib/include/srslte/phy/resampling/resampler.h @@ -0,0 +1,102 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: resampler.h + * + * Description: Linear and vector interpolation + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_RESAMPLER_H +#define SRSLTE_RESAMPLER_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/dft/dft.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Resampler operating modes + */ +typedef enum { + SRSLTE_RESAMPLER_MODE_INTERPOLATE = 0, + SRSLTE_RESAMPLER_MODE_DECIMATE, +} srslte_resampler_mode_t; + +/** + * Resampler internal buffers and subcomponents + */ +typedef struct { + srslte_resampler_mode_t mode; + uint32_t ratio; + uint32_t window_sz; + srslte_dft_plan_t fft; + srslte_dft_plan_t ifft; + uint32_t state_len; + cf_t* in_buffer; + cf_t* out_buffer; + cf_t* state; + cf_t* filter; +} srslte_resampler_fft_t; + +/** + * Initialise an FFT based resampler which can be configured as decimator or interpolator. + * @param q Object pointer + * @param mode Determines whether the operation mode is decimation or interpolation + * @param ratio Operational ratio + * @return SRSLTE_SUCCES if no error, otherwise an SRSLTE error code + */ +SRSLTE_API int srslte_resampler_fft_init(srslte_resampler_fft_t* q, srslte_resampler_mode_t mode, uint32_t ratio); + +/** + * Get delay from the FFT based resampler. + * @param q Object pointer + * @return the delay in number of samples + */ +SRSLTE_API uint32_t srslte_resampler_fft_get_delay(srslte_resampler_fft_t* q); + +/** + * Run FFT based resampler in the initiated mode. + * @param q Object pointer, make sure it has been initialised + * @param input Points at the input complex buffer + * @param output Points at the output complex buffer + * @param nsamples Number of samples to apply the processing + */ +SRSLTE_API void srslte_resampler_fft_run(srslte_resampler_fft_t* q, const cf_t* input, cf_t* output, uint32_t nsamples); + +/** + * Free FFT based resampler buffers and subcomponents + * @param q Object pointer + */ +SRSLTE_API void srslte_resampler_fft_free(srslte_resampler_fft_t* q); + +#ifdef __cplusplus +} +#endif + +#endif // SRSLTE_RESAMPLER_H diff --git a/lib/include/srslte/radio/radio.h b/lib/include/srslte/radio/radio.h index 121ef09fe..084ba361b 100644 --- a/lib/include/srslte/radio/radio.h +++ b/lib/include/srslte/radio/radio.h @@ -26,6 +26,7 @@ #include "srslte/common/interfaces_common.h" #include "srslte/common/log_filter.h" #include "srslte/interfaces/radio_interfaces.h" +#include "srslte/phy/resampling/resampler.h" #include "srslte/phy/rf/rf.h" #include "srslte/radio/radio_base.h" #include "srslte/srslte.h" @@ -94,16 +95,20 @@ public: static void rf_msg_callback(void* arg, srslte_rf_error_t error); private: - std::vector rf_devices = {}; - std::vector rf_info = {}; - std::vector rx_offset_n = {}; - rf_metrics_t rf_metrics = {}; - log_filter log_local = {}; - log_filter* log_h = nullptr; - srslte::logger* logger = nullptr; - phy_interface_radio* phy = nullptr; - cf_t* zeros = nullptr; - std::array dummy_buffers; + std::vector rf_devices = {}; + std::vector rf_info = {}; + std::vector rx_offset_n = {}; + rf_metrics_t rf_metrics = {}; + log_filter log_local = {}; + log_filter* log_h = nullptr; + srslte::logger* logger = nullptr; + phy_interface_radio* phy = nullptr; + cf_t* zeros = nullptr; + std::array dummy_buffers; + std::array, SRSLTE_MAX_CHANNELS> tx_buffer; + std::array, SRSLTE_MAX_CHANNELS> rx_buffer; + std::array interpolators = {}; + std::array decimators = {}; rf_timestamp_t end_of_burst_time = {}; bool is_start_of_burst = false; @@ -116,6 +121,8 @@ private: bool continuous_tx = false; double freq_offset = 0.0; double cur_tx_srate = 0.0; + double cur_rx_srate = 0.0; + double fix_srate_hz = 0.0; uint32_t nof_antennas = 0; uint32_t nof_channels = 0; uint32_t nof_channels_x_dev = 0; diff --git a/lib/src/phy/resampling/resampler.c b/lib/src/phy/resampling/resampler.c new file mode 100644 index 000000000..d3c02321c --- /dev/null +++ b/lib/src/phy/resampling/resampler.c @@ -0,0 +1,299 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "srslte/phy/resampling/resampler.h" +#include "srslte/phy/utils/vector.h" + +/** + * Raised cosine filter Roll-off + * 0: Frequency sharp, long in time + * 1: Frequency relaxed, short in time + */ +#define RESAMPLER_BETA 0.45 + +/** + * The FFT size power is determined from the ratio logarithm in base 2 plus the following parameter + */ +#define RESAMPLER_FILTER_SIZE_POW 2 + +/** + * Lower bound of the filter size for ensuring a minimum of performance + */ +#define RESAMPLER_FILTER_SIZE_MIN 64 + +int srslte_resampler_fft_init(srslte_resampler_fft_t* q, srslte_resampler_mode_t mode, uint32_t ratio) +{ + if (q == NULL || ratio == 0) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Intinialising the resampler is unnecessary + if (ratio == 1) { + q->ratio = 1; + return SRSLTE_ERROR_OUT_OF_BOUNDS; + } + + // Make sure interpolator is freed + srslte_resampler_fft_free(q); + + // Initialise sizes + uint32_t base_size = + SRSLTE_MAX(RESAMPLER_FILTER_SIZE_MIN, (uint32_t)pow(2, ceilf(log2f(ratio) + RESAMPLER_FILTER_SIZE_POW))); + uint32_t input_fft_size = 0; + uint32_t output_fft_size = 0; + uint32_t high_size = base_size * ratio; + + switch (mode) { + case SRSLTE_RESAMPLER_MODE_INTERPOLATE: + input_fft_size = base_size; + output_fft_size = high_size; + break; + case SRSLTE_RESAMPLER_MODE_DECIMATE: + default: + input_fft_size = high_size; + output_fft_size = base_size; + break; + } + + q->mode = mode; + q->ratio = ratio; + q->window_sz = input_fft_size / 4; + + q->in_buffer = srslte_vec_cf_malloc(high_size); + if (q->in_buffer == NULL) { + return SRSLTE_ERROR; + } + + q->out_buffer = srslte_vec_cf_malloc(high_size); + if (q->out_buffer == NULL) { + return SRSLTE_ERROR; + } + + int err = + srslte_dft_plan_guru_c(&q->fft, input_fft_size, SRSLTE_DFT_FORWARD, q->in_buffer, q->out_buffer, 1, 1, 1, 1, 1); + if (err != SRSLTE_SUCCESS) { + ERROR("Initialising DFT\n"); + return err; + } + + err = srslte_dft_plan_guru_c( + &q->ifft, output_fft_size, SRSLTE_DFT_BACKWARD, q->in_buffer, q->out_buffer, 1, 1, 1, 1, 1); + if (err != SRSLTE_SUCCESS) { + ERROR("Initialising DFT\n"); + return err; + } + + q->state = srslte_vec_cf_malloc(output_fft_size); + if (q->state == NULL) { + return SRSLTE_ERROR; + } + + q->filter = srslte_vec_cf_malloc(high_size); + if (q->filter == NULL) { + return SRSLTE_ERROR; + } + + // Compute time domain filter coefficients, see raised cosine formula in section "1.2 Impulse Response" of + // https://dspguru.com/dsp/reference/raised-cosine-and-root-raised-cosine-formulas/ + double T = (double)1.0; + for (int32_t i = 0; i < high_size; i++) { + double t = ((double)i - (double)high_size / 2.0) / (double)ratio; + double h = 1.0 / T; + if (isnormal(t)) { + h = sin(M_PI * t / T); + h *= cos(M_PI * t * RESAMPLER_BETA / T); + h /= M_PI * t; + h /= 1.0 - 4.0 * pow(RESAMPLER_BETA, 2.0) * pow(t, 2.0) / pow(T, 2.0); + } + q->in_buffer[i] = (float)h; + } + + // Compute frequency domain coefficients, since the filter is symmetrical, it does not matter whether FFT or iFFT + if (mode == SRSLTE_RESAMPLER_MODE_INTERPOLATE) { + srslte_dft_run_guru_c(&q->ifft); + } else { + srslte_dft_run_guru_c(&q->fft); + } + + // Normalise filter + float norm = 1.0f / (cabsf(q->out_buffer[0]) * (float)input_fft_size); + srslte_vec_sc_prod_cfc(q->out_buffer, norm, q->filter, high_size); + + // Zero state + q->state_len = 0; + srslte_vec_cf_zero(q->state, output_fft_size); + + return SRSLTE_SUCCESS; +} + +static void resampler_fft_interpolate(srslte_resampler_fft_t* q, const cf_t* input, cf_t* output, uint32_t nsamples) +{ + uint32_t count = 0; + + if (q == NULL || input == NULL || output == NULL) { + return; + } + + while (count < nsamples) { + uint32_t n = SRSLTE_MIN(q->window_sz, nsamples - count); + + // Copy input samples + srslte_vec_cf_copy(q->in_buffer, &input[count], q->window_sz); + + // Pad zeroes + srslte_vec_cf_zero(&q->in_buffer[n], q->fft.size - n); + + // Execute FFT + srslte_dft_run_guru_c(&q->fft); + + // Replicate input spectrum + for (uint32_t i = 1; i < q->ratio; i++) { + srslte_vec_cf_copy(&q->out_buffer[q->fft.size * i], q->out_buffer, q->fft.size); + } + + // Apply filtering + srslte_vec_prod_ccc(q->out_buffer, q->filter, q->in_buffer, q->ifft.size); + + // Execute iFFT + srslte_dft_run_guru_c(&q->ifft); + + // Add previous state + srslte_vec_sum_ccc(q->out_buffer, q->state, q->out_buffer, q->state_len); + + // Copy output + srslte_vec_cf_copy(&output[count * q->ratio], q->out_buffer, n * q->ratio); + + // Save current state + q->state_len = q->ifft.size - n * q->ratio; + srslte_vec_cf_copy(q->state, &q->out_buffer[n * q->ratio], q->state_len); + + // Increment count + count += n; + } +} + +static void resampler_fft_decimate(srslte_resampler_fft_t* q, const cf_t* input, cf_t* output, uint32_t nsamples) +{ + uint32_t count = 0; + + if (q == NULL || input == NULL || output == NULL) { + return; + } + + while (count < nsamples) { + uint32_t n = SRSLTE_MIN(q->window_sz, nsamples - count); + + // Copy input samples + srslte_vec_cf_copy(q->in_buffer, &input[count], q->window_sz); + + // Pad zeroes + srslte_vec_cf_zero(&q->in_buffer[n], q->fft.size - n); + + // Execute FFT + srslte_dft_run_guru_c(&q->fft); + + // Apply filtering and cut + srslte_vec_prod_ccc(q->out_buffer, q->filter, q->in_buffer, q->ifft.size / 2); + srslte_vec_prod_ccc(&q->out_buffer[q->fft.size - q->ifft.size / 2], + &q->filter[q->fft.size - q->ifft.size / 2], + &q->in_buffer[q->ifft.size / 2], + q->ifft.size / 2); + + // Execute iFFT + srslte_dft_run_guru_c(&q->ifft); + + // Add previous state + srslte_vec_sum_ccc(q->out_buffer, q->state, q->out_buffer, q->state_len); + + // Copy output + srslte_vec_cf_copy(&output[count / q->ratio], q->out_buffer, n / q->ratio); + + // Save current state + q->state_len = q->ifft.size - n / q->ratio; + srslte_vec_cf_copy(q->state, &q->out_buffer[n / q->ratio], q->state_len); + + // Increment count + count += n; + } +} + +void srslte_resampler_fft_run(srslte_resampler_fft_t* q, const cf_t* input, cf_t* output, uint32_t nsamples) +{ + if (q == NULL) { + return; + } + + // If the ratio is unset (0) or 1, copy samples and return + if (q->ratio < 2) { + srslte_vec_cf_copy(output, input, nsamples); + return; + } + + switch (q->mode) { + + case SRSLTE_RESAMPLER_MODE_INTERPOLATE: + resampler_fft_interpolate(q, input, output, nsamples); + break; + case SRSLTE_RESAMPLER_MODE_DECIMATE: + default: + resampler_fft_decimate(q, input, output, nsamples); + break; + } +} + +void srslte_resampler_fft_free(srslte_resampler_fft_t* q) +{ + if (q == NULL) { + return; + } + + srslte_dft_plan_free(&q->fft); + srslte_dft_plan_free(&q->ifft); + + if (q->state) { + free(q->state); + } + if (q->in_buffer) { + free(q->in_buffer); + } + if (q->out_buffer) { + free(q->out_buffer); + } + if (q->filter) { + free(q->filter); + } + + memset(q, 0, sizeof(srslte_resampler_fft_t)); +} + +uint32_t srslte_resampler_fft_get_delay(srslte_resampler_fft_t* q) +{ + if (q == NULL) { + return UINT32_MAX; + } + + return q->ifft.size / 2; +} \ No newline at end of file diff --git a/lib/src/phy/resampling/test/CMakeLists.txt b/lib/src/phy/resampling/test/CMakeLists.txt index b3c3fba84..20a36fe32 100644 --- a/lib/src/phy/resampling/test/CMakeLists.txt +++ b/lib/src/phy/resampling/test/CMakeLists.txt @@ -29,6 +29,17 @@ add_executable(resample_arb_bench resample_arb_bench.c) target_link_libraries(resample_arb_bench srslte_phy) add_test(resample resample_arb_test) - +######################################################################## +# FFT based interpolate/decimate +######################################################################## +add_executable(resampler_test resampler_test.c) +target_link_libraries(resampler_test srslte_phy) + +add_test(resampler_test_2 resampler_test -s 1920 -r 2 -f 2) +add_test(resampler_test_3 resampler_test -s 1920 -r 2 -f 3) +add_test(resampler_test_6 resampler_test -s 1920 -r 2 -f 6) +add_test(resampler_test_8 resampler_test -s 1920 -r 2 -f 8) +add_test(resampler_test_12 resampler_test -s 1920 -r 2 -f 12) +add_test(resampler_test_16 resampler_test -s 1920 -r 2 -f 16) diff --git a/lib/src/phy/resampling/test/resampler_test.c b/lib/src/phy/resampling/test/resampler_test.c new file mode 100644 index 000000000..5184ce9a7 --- /dev/null +++ b/lib/src/phy/resampling/test/resampler_test.c @@ -0,0 +1,118 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srslte/phy/resampling/resampler.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include + +static uint32_t buffer_size = 1920; +static uint32_t factor = 2; +static uint32_t repetitions = 2; + +static void usage(char* prog) +{ + printf("Usage: %s [sfr]\n", prog); + printf("\t-s Buffer size [Default %d]\n", buffer_size); + printf("\t-f Buffer size [Default %d]\n", factor); + printf("\t-f r [Default %d]\n", repetitions); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "sfr")) != -1) { + switch (opt) { + case 's': + buffer_size = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'f': + factor = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'r': + repetitions = (uint32_t)strtol(argv[optind], NULL, 10); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + struct timeval t[3] = {}; + srslte_resampler_fft_t interp = {}; + srslte_resampler_fft_t decim = {}; + + parse_args(argc, argv); + + cf_t* src = srslte_vec_cf_malloc(buffer_size); + cf_t* interpolated = srslte_vec_cf_malloc(buffer_size * factor); + cf_t* decimated = srslte_vec_cf_malloc(buffer_size); + + if (srslte_resampler_fft_init(&interp, SRSLTE_RESAMPLER_MODE_INTERPOLATE, factor)) { + return SRSLTE_ERROR; + } + + if (srslte_resampler_fft_init(&decim, SRSLTE_RESAMPLER_MODE_DECIMATE, factor)) { + return SRSLTE_ERROR; + } + + srslte_vec_cf_zero(src, buffer_size); + srslte_vec_gen_sine(1.0f, 0.01f, src, buffer_size / 10); + + gettimeofday(&t[1], NULL); + for (uint32_t r = 0; r < repetitions; r++) { + srslte_resampler_fft_run(&interp, src, interpolated, buffer_size); + srslte_resampler_fft_run(&decim, interpolated, decimated, buffer_size * factor); + } + gettimeofday(&t[2], NULL); + get_time_interval(t); + uint64_t duration_us = (uint64_t)(t[0].tv_sec * 1000000UL + t[0].tv_usec); + printf("Done %.1f Msps\n", factor * buffer_size * repetitions / (double)duration_us); + + // printf("interp="); + // srslte_vec_fprint_c(stdout, interpolated, buffer_size * factor); + + // Check error + uint32_t delay = srslte_resampler_fft_get_delay(&decim) * 2; + uint32_t nsamples = buffer_size - delay; + srslte_vec_sub_ccc(src, &decimated[delay], interpolated, nsamples); + float mse = sqrtf(srslte_vec_avg_power_cf(interpolated, nsamples)); + printf("MSE: %f\n", mse); + + // printf("src="); + // srslte_vec_fprint_c(stdout, src, nsamples); + // printf("decim="); + // srslte_vec_fprint_c(stdout, &decimated[delay], nsamples); + + srslte_resampler_fft_free(&interp); + srslte_resampler_fft_free(&decim); + free(src); + free(interpolated); + free(decimated); + + return (mse < 0.1f) ? SRSLTE_SUCCESS : SRSLTE_ERROR; +} \ No newline at end of file diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 98abb77e6..a655ed810 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -54,11 +54,12 @@ radio::~radio() zeros = nullptr; } - for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - if (dummy_buffers[i]) { - free(dummy_buffers[i]); - dummy_buffers[i] = nullptr; - } + for (srslte_resampler_fft_t& q : interpolators) { + srslte_resampler_fft_free(&q); + } + + for (srslte_resampler_fft_t& q : decimators) { + srslte_resampler_fft_free(&q); } } @@ -96,6 +97,7 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_) nof_channels = args.nof_antennas * args.nof_carriers; nof_antennas = args.nof_antennas; nof_carriers = args.nof_carriers; + fix_srate_hz = args.srate_hz; cur_tx_freqs.resize(nof_carriers); cur_rx_freqs.resize(nof_carriers); @@ -258,7 +260,18 @@ bool radio::start_agc(bool tx_gain_same_rx) bool radio::rx_now(rf_buffer_interface& buffer, rf_timestamp_interface& rxd_time) { - bool ret = true; + bool ret = true; + rf_buffer_t buffer_rx; + uint32_t ratio = SRSLTE_MAX(1, decimators[0].ratio); + + // If the interpolator have been set, interpolate + for (uint32_t ch = 0; ch < nof_channels; ch++) { + // Use rx buffer if decimator is required + buffer_rx.set(ch, ratio > 1 ? rx_buffer[ch].data() : buffer.get(ch)); + } + + // Set new buffer size + buffer_rx.set_nof_samples(buffer.get_nof_samples() * ratio); if (not radio_is_streaming) { for (srslte_rf_t& rf_device : rf_devices) { @@ -275,7 +288,14 @@ bool radio::rx_now(rf_buffer_interface& buffer, rf_timestamp_interface& rxd_time } for (uint32_t device_idx = 0; device_idx < (uint32_t)rf_devices.size(); device_idx++) { - ret &= rx_dev(device_idx, buffer, rxd_time.get_ptr(device_idx)); + ret &= rx_dev(device_idx, buffer_rx, rxd_time.get_ptr(device_idx)); + } + + // Perform decimation + if (ratio > 1) { + for (uint32_t ch = 0; ch < nof_channels; ch++) { + srslte_resampler_fft_run(&decimators[ch], buffer_rx.get(ch), buffer.get(ch), buffer_rx.get_nof_samples()); + } } return ret; @@ -342,6 +362,20 @@ bool radio::tx(rf_buffer_interface& buffer, const rf_timestamp_interface& tx_tim { bool ret = true; + // If the interpolator have been set, interpolate + if (interpolators[0].ratio > 1) { + for (uint32_t ch = 0; ch < nof_channels; ch++) { + // Perform actual interpolation + srslte_resampler_fft_run(&interpolators[ch], buffer.get(ch), tx_buffer[ch].data(), buffer.get_nof_samples()); + + // Set the buffer pointer + buffer.set(ch, tx_buffer[ch].data()); + } + + // Set new buffer size + buffer.set_nof_samples(buffer.get_nof_samples() * interpolators[0].ratio); + } + for (uint32_t device_idx = 0; device_idx < (uint32_t)rf_devices.size(); device_idx++) { ret &= tx_dev(device_idx, buffer, tx_time.get(device_idx)); } @@ -573,8 +607,29 @@ void radio::set_rx_srate(const double& srate) if (!is_initialized) { return; } - for (srslte_rf_t& rf_device : rf_devices) { - srslte_rf_set_rx_srate(&rf_device, srate); + // If fix sampling rate... + if (std::isnormal(fix_srate_hz)) { + // If the sampling rate was not set, set it + if (not std::isnormal(cur_rx_srate)) { + for (srslte_rf_t& rf_device : rf_devices) { + cur_rx_srate = srslte_rf_set_rx_srate(&rf_device, fix_srate_hz); + } + } + + // Update decimators + uint32_t ratio = (uint32_t)ceil(cur_rx_srate / srate); + for (uint32_t ch = 0; ch < nof_channels; ch++) { + srslte_resampler_fft_init(&decimators[ch], SRSLTE_RESAMPLER_MODE_DECIMATE, ratio); + + if (rx_buffer[ch].empty()) { + rx_buffer[ch].resize(SRSLTE_SF_LEN_MAX * 5); + } + } + + } else { + for (srslte_rf_t& rf_device : rf_devices) { + cur_rx_srate = srslte_rf_set_rx_srate(&rf_device, srate); + } } } @@ -794,8 +849,28 @@ void radio::set_tx_srate(const double& srate) return; } - for (srslte_rf_t& rf_device : rf_devices) { - cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); + // If fix sampling rate... + if (std::isnormal(fix_srate_hz)) { + // If the sampling rate was not set, set it + if (not std::isnormal(cur_tx_srate)) { + for (srslte_rf_t& rf_device : rf_devices) { + cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, fix_srate_hz); + } + } + + // Update interpolators + uint32_t ratio = (uint32_t)ceil(cur_tx_srate / srate); + for (uint32_t ch = 0; ch < nof_channels; ch++) { + srslte_resampler_fft_init(&interpolators[ch], SRSLTE_RESAMPLER_MODE_INTERPOLATE, ratio); + + if (tx_buffer[ch].empty()) { + tx_buffer[ch].resize(5 * SRSLTE_SF_LEN_MAX); + } + } + } else { + for (srslte_rf_t& rf_device : rf_devices) { + cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); + } } // Get calibrated advanced diff --git a/srsenb/hdr/phy/phy_interfaces.h b/srsenb/hdr/phy/phy_interfaces.h index 83a230bee..afc484524 100644 --- a/srsenb/hdr/phy/phy_interfaces.h +++ b/srsenb/hdr/phy/phy_interfaces.h @@ -46,6 +46,7 @@ struct phy_args_t { std::string type; srslte::phy_log_args_t log; + float sampling_rate_hz = 0.0f; float max_prach_offset_us = 10; int pusch_max_its = 10; bool pusch_8bit_decoder = false; diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 0c0df775b..18d839062 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -86,7 +86,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb_files.rr_config", bpo::value(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files") ("enb_files.drb_config", bpo::value(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files") - ("rf.dl_earfcn", bpo::value(&args->enb.dl_earfcn)->default_value(0), "Force Downlink EARFCN for single cell") + ("rf.dl_earfcn", bpo::value(&args->enb.dl_earfcn)->default_value(0), "Force Downlink EARFCN for single cell") + ("rf.srate", bpo::value(&args->rf.srate_hz)->default_value(0.0), "Force Tx and Rx sampling rate in Hz") ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(50), "Front-end receiver gain") ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(70), "Front-end transmitter gain") ("rf.dl_freq", bpo::value(&args->rf.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") diff --git a/srsue/src/main.cc b/srsue/src/main.cc index bc0199248..5d20ab843 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -75,19 +75,20 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("ue.stack", bpo::value(&args->stack.type)->default_value("lte"), "Type of the upper stack [lte, nr]") ("rf.dl_earfcn", bpo::value(&args->phy.dl_earfcn)->default_value("3400"), "Downlink EARFCN list") - ("rf.ul_earfcn", bpo::value(&args->phy.ul_earfcn), "Uplink EARFCN list. Optional.") - ("rf.freq_offset", bpo::value(&args->rf.freq_offset)->default_value(0), "(optional) Frequency offset") - ("rf.dl_freq", bpo::value(&args->phy.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") - ("rf.ul_freq", bpo::value(&args->phy.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") - ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(-1), "Front-end receiver gain") - ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(-1), "Front-end transmitter gain (all channels)") - ("rf.tx_gain[0]", bpo::value(&args->rf.tx_gain_ch[0])->default_value(-1), "Front-end transmitter gain CH0") - ("rf.tx_gain[1]", bpo::value(&args->rf.tx_gain_ch[1])->default_value(-1), "Front-end transmitter gain CH1") - ("rf.tx_gain[2]", bpo::value(&args->rf.tx_gain_ch[2])->default_value(-1), "Front-end transmitter gain CH2") - ("rf.tx_gain[3]", bpo::value(&args->rf.tx_gain_ch[3])->default_value(-1), "Front-end transmitter gain CH3") - ("rf.tx_gain[4]", bpo::value(&args->rf.tx_gain_ch[4])->default_value(-1), "Front-end transmitter gain CH4") - ("rf.nof_carriers", bpo::value(&args->rf.nof_carriers)->default_value(1), "Number of carriers") - ("rf.nof_antennas", bpo::value(&args->rf.nof_antennas)->default_value(1), "Number of antennas per carrier") + ("rf.ul_earfcn", bpo::value(&args->phy.ul_earfcn), "Uplink EARFCN list. Optional.") + ("rf.srate", bpo::value(&args->rf.srate_hz)->default_value(0.0), "Force Tx and Rx sampling rate in Hz") + ("rf.freq_offset", bpo::value(&args->rf.freq_offset)->default_value(0), "(optional) Frequency offset") + ("rf.dl_freq", bpo::value(&args->phy.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") + ("rf.ul_freq", bpo::value(&args->phy.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") + ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(-1), "Front-end receiver gain") + ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(-1), "Front-end transmitter gain (all channels)") + ("rf.tx_gain[0]", bpo::value(&args->rf.tx_gain_ch[0])->default_value(-1), "Front-end transmitter gain CH0") + ("rf.tx_gain[1]", bpo::value(&args->rf.tx_gain_ch[1])->default_value(-1), "Front-end transmitter gain CH1") + ("rf.tx_gain[2]", bpo::value(&args->rf.tx_gain_ch[2])->default_value(-1), "Front-end transmitter gain CH2") + ("rf.tx_gain[3]", bpo::value(&args->rf.tx_gain_ch[3])->default_value(-1), "Front-end transmitter gain CH3") + ("rf.tx_gain[4]", bpo::value(&args->rf.tx_gain_ch[4])->default_value(-1), "Front-end transmitter gain CH4") + ("rf.nof_carriers", bpo::value(&args->rf.nof_carriers)->default_value(1), "Number of carriers") + ("rf.nof_antennas", bpo::value(&args->rf.nof_antennas)->default_value(1), "Number of antennas per carrier") ("rf.device_name", bpo::value(&args->rf.device_name)->default_value("auto"), "Front-end device name") ("rf.device_args", bpo::value(&args->rf.device_args)->default_value("auto"), "Front-end device arguments")