Crest Factor Reduction feature for the phy layer (#3720)

* Add CFR module to the phy lib

* Add dynamic threshold with PAPR estimation

* Add a CFR unit test, CFR module improvements and refactoring.
Swap the gain normalization before the CFR.

* Add CFR config interface to srsenb

* Add CFR support to pdsch_enodeb

* Add DL PAPR measurement to eNB.

Co-authored-by: Cristian Balint <cristian.balint@gmail.com>

* Add test coverage to srsran_vec_gen_clip_env
This commit is contained in:
Joaquim Broquetas 2022-02-04 10:35:35 +01:00 committed by GitHub
parent 91502c87db
commit b2075673e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1442 additions and 36 deletions

View File

@ -13,6 +13,7 @@
#include "srsran/common/crash_handler.h"
#include "srsran/common/gen_mch_tables.h"
#include "srsran/srsran.h"
#include <getopt.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
@ -43,6 +44,12 @@ static char* output_file_name = NULL;
#define PAGE_UP 53
#define PAGE_DOWN 54
#define CFR_THRES_UP_KEY 't'
#define CFR_THRES_DN_KEY 'g'
#define CFR_THRES_STEP 0.05f
#define CFR_PAPR_STEP 0.1f
static srsran_cell_t cell = {
25, // nof_prb
1, // nof_ports
@ -70,6 +77,31 @@ static bool enable_256qam = false;
static float output_file_snr = +INFINITY;
static bool use_standard_lte_rate = false;
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// CFR runtime control flags
static bool cfr_thr_inc = false;
static bool cfr_thr_dec = false;
typedef struct {
int enable;
char* mode;
float manual_thres;
float strength;
float auto_target_papr;
float ema_alpha;
} cfr_args_t;
static cfr_args_t cfr_args = {.enable = 0,
.mode = cfr_manual_str,
.manual_thres = 1.0f,
.strength = 1.0f,
.auto_target_papr = 8.0f,
.ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB};
static bool null_file_sink = false;
static srsran_filesink_t fsink;
static srsran_ofdm_t ifft[SRSRAN_MAX_PORTS];
@ -85,6 +117,7 @@ static srsran_softbuffer_tx_t* softbuffers[SRSRAN_MAX_CODEWORDS];
static srsran_regs_t regs;
static srsran_dci_dl_t dci_dl;
static int rvidx[SRSRAN_MAX_CODEWORDS] = {0, 0};
static srsran_cfr_cfg_t cfr_config = {};
static cf_t * sf_buffer[SRSRAN_MAX_PORTS] = {NULL}, *output_buffer[SRSRAN_MAX_PORTS] = {NULL};
static uint32_t sf_n_re, sf_n_samples;
@ -134,14 +167,28 @@ static void usage(char* prog)
printf("\t-s output file SNR [Default %f]\n", output_file_snr);
printf("\t-q Enable/Disable 256QAM modulation (default %s)\n", enable_256qam ? "enabled" : "disabled");
printf("\t-Q Use standard LTE sample rates (default %s)\n", use_standard_lte_rate ? "enabled" : "disabled");
printf("CFR Options:\n");
printf("\t--enable_cfr Enable the CFR (default %s)\n", cfr_args.enable ? "enabled" : "disabled");
printf("\t--cfr_mode CFR mode: manual, auto_cma, auto_ema. (default %s)\n", cfr_args.mode);
printf("\t--cfr_manual_thres CFR manual threshold (default %.2f)\n", cfr_args.manual_thres);
printf("\t--cfr_strength CFR strength (default %.2f)\n", cfr_args.strength);
printf("\t--cfr_auto_papr CFR PAPR target for auto modes (default %.2f)\n", cfr_args.auto_target_papr);
printf("\t--cfr_ema_alpha CFR alpha parameter for EMA mode (default %.2f)\n", cfr_args.ema_alpha);
printf("\n");
printf("\t*: See 3GPP 36.212 Table 5.3.3.1.5-4 for more information\n");
}
struct option cfr_opts[] = {{"enable_cfr", no_argument, &cfr_args.enable, 1},
{"cfr_mode", required_argument, NULL, 'C'},
{"cfr_manual_thres", required_argument, NULL, 'T'},
{"cfr_strength", required_argument, NULL, 'S'},
{"cfr_auto_papr", required_argument, NULL, 'P'},
{"cfr_ema_alpha", required_argument, NULL, 'e'},
{0, 0, 0, 0}};
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "IadglfmoncpqvutxbwMsBQ")) != -1) {
while ((opt = getopt_long(argc, argv, "IadglfmoncpqvutxbwMsBQ", cfr_opts, NULL)) != -1) {
switch (opt) {
case 'I':
rf_dev = argv[optind];
@ -206,6 +253,24 @@ static void parse_args(int argc, char** argv)
case 'E':
cell.cp = SRSRAN_CP_EXT;
break;
case 'C':
cfr_args.mode = optarg;
break;
case 'T':
cfr_args.manual_thres = strtof(optarg, NULL);
break;
case 'S':
cfr_args.strength = strtof(optarg, NULL);
break;
case 'P':
cfr_args.auto_target_papr = strtof(optarg, NULL);
break;
case 'e':
cfr_args.ema_alpha = strtof(optarg, NULL);
break;
case 0:
/* getopt_long() set a variable, keep going */
break;
default:
usage(argv[0]);
exit(-1);
@ -219,6 +284,32 @@ static void parse_args(int argc, char** argv)
#endif
}
static int parse_cfr_args()
{
cfr_config.cfr_enable = cfr_args.enable;
cfr_config.manual_thr = cfr_args.manual_thres;
cfr_config.max_papr_db = cfr_args.auto_target_papr;
cfr_config.alpha = cfr_args.strength;
cfr_config.ema_alpha = cfr_args.ema_alpha;
if (!strcmp(cfr_args.mode, cfr_manual_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_args.mode, cfr_auto_cma_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_args.mode, cfr_auto_ema_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ERROR("CFR mode is not recognised");
return SRSRAN_ERROR;
}
if (!srsran_cfr_params_valid(&cfr_config)) {
ERROR("Invalid CFR parameters");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
static void base_init()
{
int i;
@ -316,6 +407,10 @@ static void base_init()
}
srsran_ofdm_set_normalize(&ifft[i], true);
if (srsran_ofdm_set_cfr(&ifft[i], &cfr_config)) {
ERROR("Error setting CFR object");
exit(-1);
}
}
if (srsran_ofdm_tx_init_mbsfn(&ifft_mbsfn, SRSRAN_CP_EXT, sf_buffer[0], output_buffer[0], cell.nof_prb)) {
@ -324,6 +419,10 @@ static void base_init()
}
srsran_ofdm_set_non_mbsfn_region(&ifft_mbsfn, 2);
srsran_ofdm_set_normalize(&ifft_mbsfn, true);
if (srsran_ofdm_set_cfr(&ifft_mbsfn, &cfr_config)) {
ERROR("Error setting CFR object");
exit(-1);
}
if (srsran_pbch_init(&pbch)) {
ERROR("Error creating PBCH object");
@ -474,6 +573,8 @@ static int update_radl()
{
ZERO_OBJECT(dci_dl);
int ret = SRSRAN_ERROR;
/* Configure cell and PDSCH in function of the transmission mode */
switch (transmission_mode) {
case SRSRAN_TM1:
@ -496,7 +597,7 @@ static int update_radl()
break;
default:
ERROR("Transmission mode not implemented.");
exit(-1);
goto exit;
}
dci_dl.rnti = UE_CRNTI;
@ -517,7 +618,80 @@ static int update_radl()
SRSRAN_DCI_TB_DISABLE(dci_dl.tb[1]);
}
// Increase the CFR threshold or target PAPR
if (cfr_thr_inc) {
cfr_thr_inc = false; // Reset the flag
if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
cfr_config.manual_thr += CFR_THRES_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr);
} else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) {
cfr_config.max_papr_db += CFR_PAPR_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db);
}
}
// Decrease the CFR threshold or target PAPR
if (cfr_thr_dec) {
cfr_thr_dec = false; // Reset the flag
if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
if (cfr_config.manual_thr - CFR_THRES_STEP >= 0) {
cfr_config.manual_thr -= CFR_THRES_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr);
}
} else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) {
if (cfr_config.max_papr_db - CFR_PAPR_STEP >= 0) {
cfr_config.max_papr_db -= CFR_PAPR_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db);
}
}
}
srsran_dci_dl_fprint(stdout, &dci_dl, cell.nof_prb);
printf("\nCFR controls:\n");
printf(" Param | INC | DEC |\n");
printf("------------+-----+-----+\n");
printf(" Thres/PAPR | %c | %c |\n", CFR_THRES_UP_KEY, CFR_THRES_DN_KEY);
printf("\n");
if (transmission_mode != SRSRAN_TM1) {
printf("\nTransmission mode key table:\n");
printf(" Mode | 1TB | 2TB |\n");
@ -526,13 +700,15 @@ static int update_radl()
printf(" CDD | | z |\n");
printf("Multiplex | q,w,e,r | a,s |\n");
printf("\n");
printf("Type new MCS index (0-28) or mode key and press Enter: ");
printf("Type new MCS index (0-28) or cfr/mode key and press Enter: ");
} else {
printf("Type new MCS index (0-28) and press Enter: ");
printf("Type new MCS index (0-28) or cfr key and press Enter: ");
}
fflush(stdout);
ret = SRSRAN_SUCCESS;
return 0;
exit:
return ret;
}
/* Read new MCS from stdin */
@ -626,6 +802,12 @@ static int update_control()
case 'x':
transmission_mode = SRSRAN_TM2;
break;
case CFR_THRES_UP_KEY:
cfr_thr_inc = true;
break;
case CFR_THRES_DN_KEY:
cfr_thr_dec = true;
break;
default:
last_mcs_idx = mcs_idx;
mcs_idx = strtol(input, NULL, 10);
@ -643,9 +825,9 @@ static int update_control()
} else if (n < 0) {
// error
perror("select");
return -1;
return SRSRAN_ERROR;
} else {
return 0;
return SRSRAN_SUCCESS;
}
}
@ -719,6 +901,10 @@ int main(int argc, char** argv)
#endif
parse_args(argc, argv);
if (parse_cfr_args() < SRSRAN_SUCCESS) {
ERROR("Error parsing CFR args");
exit(-1);
}
srsran_use_standard_symbol_size(use_standard_lte_rate);
@ -873,7 +1059,7 @@ int main(int argc, char** argv)
srsran_pcfich_encode(&pcfich, &dl_sf, sf_symbols);
/* Update DL resource allocation from control port */
if (update_control()) {
if (update_control() < SRSRAN_SUCCESS) {
ERROR("Error updating parameters from control port");
}

View File

@ -19,6 +19,11 @@ namespace srsenb {
class enb_command_interface
{
public:
/**
* Trigger downlink singnal measurements (currently PAPR)
*/
virtual void cmd_cell_measure() = 0;
/**
* Sets the relative gain of a cell from it's index (following rr.conf) order.
* @param cell_id Provides a cell identifier

View File

@ -0,0 +1,118 @@
/**
*
* \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_CFR_H
#define SRSRAN_CFR_H
#include "srsran/config.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/dft/dft.h"
#define CFR_EMA_INIT_AVG_PWR 0.1
/**
* @brief CFR manual threshold or PAPR limiting with Moving Average or EMA power averaging
*/
typedef enum SRSRAN_API {
SRSRAN_CFR_THR_MANUAL = 1,
SRSRAN_CFR_THR_AUTO_CMA = 2,
SRSRAN_CFR_THR_AUTO_EMA = 3
} srsran_cfr_mode_t;
/**
* @brief CFR module configuration arguments
*/
typedef struct SRSRAN_API {
bool cfr_enable;
srsran_cfr_mode_t cfr_mode;
// always used (mandatory)
uint32_t symbol_bw; ///< OFDM symbol bandwidth, in FFT bins
uint32_t symbol_sz; ///< OFDM symbol size (in samples). This is the FFT size
float alpha; ///< Alpha parameter of the clipping algorithm
bool dc_sc; ///< Take into account the DC subcarrier for the filter BW
// SRSRAN_CFR_THR_MANUAL mode parameters
float manual_thr; ///< Fixed threshold used in SRSRAN_CFR_THR_MANUAL mode
// SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA mode parameters
bool measure_out_papr; ///< Enable / disable output PAPR measurement
float max_papr_db; ///< Input PAPR threshold used in SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA modes
float ema_alpha; ///< EMA alpha parameter for avg power calculation, used in SRSRAN_CFR_THR_AUTO_EMA mode
} srsran_cfr_cfg_t;
typedef struct SRSRAN_API {
srsran_cfr_cfg_t cfg;
float max_papr_lin;
srsran_dft_plan_t fft_plan;
srsran_dft_plan_t ifft_plan;
float* lpf_spectrum; ///< FFT filter spectrum
uint32_t lpf_bw; ///< Bandwidth of the LPF
float* abs_buffer_in; ///< Store the input absolute value
float* abs_buffer_out; ///< Store the output absolute value
cf_t* peak_buffer;
float pwr_avg_in; ///< store the avg. input power with MA or EMA averaging
float pwr_avg_out; ///< store the avg. output power with MA or EMA averaging
// Power average buffers, used in SRSRAN_CFR_THR_AUTO_CMA mode
uint64_t cma_n;
} srsran_cfr_t;
SRSRAN_API int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg);
/**
* @brief Applies the CFR algorithm to the time domain OFDM symbols
*
* @attention This function must be called once per symbol, and it will process q->symbol_sz samples
*
* @param[in] q The CFR object and configuration
* @param[in] in Input buffer containing the time domain OFDM symbol without CP
* @param[out] out Output buffer with the processed OFDM symbol
* @return SRSRAN_SUCCESS if the CFR object is initialised, otherwise SRSRAN_ERROR
*/
SRSRAN_API void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out);
SRSRAN_API void srsran_cfr_free(srsran_cfr_t* q);
/**
* @brief Checks the validity of the CFR algorithm parameters.
*
* @attention Does not check symbol size and bandwidth
*
* @param[in] cfr_conf the CFR configuration
* @return true if the configuration is valid, false otherwise
*/
SRSRAN_API bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf);
/**
* @brief Sets the manual threshold of the CFR (used in manual mode).
*
* @attention this is not thread-safe
*
* @param[in] q the CFR object
* @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise
*/
SRSRAN_API int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres);
/**
* @brief Sets the papr target of the CFR (used in auto modes).
*
* @attention this is not thread-safe
*
* @param[in] q the CFR object
* @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise
*/
SRSRAN_API int srsran_cfr_set_papr(srsran_cfr_t* q, float papr);
#endif // SRSRAN_CFR_H

View File

@ -25,6 +25,7 @@
#include <strings.h>
#include "srsran/config.h"
#include "srsran/phy/cfr/cfr.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/dft/dft.h"
@ -39,18 +40,19 @@
typedef struct SRSRAN_API {
// Compulsory parameters
uint32_t nof_prb; ///< Number of Resource Block
cf_t* in_buffer; ///< Input bnuffer pointer
cf_t* in_buffer; ///< Input buffer pointer
cf_t* out_buffer; ///< Output buffer pointer
srsran_cp_t cp; ///< Cyclic prefix type
// Optional parameters
srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN
bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size
float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL)
float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only
uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB
bool keep_dc; ///< If true, it does not remove the DC
double phase_compensation_hz; ///< Carrier frequency in Hz for phase compensation, set to 0 to disable
srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN
bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size
float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL)
float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only
uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB
bool keep_dc; ///< If true, it does not remove the DC
double phase_compensation_hz; ///< Carrier frequency in Hz for phase compensation, set to 0 to disable
srsran_cfr_cfg_t cfr_tx_cfg; ///< Tx CFR configuration
} srsran_ofdm_cfg_t;
/**
@ -76,6 +78,7 @@ typedef struct SRSRAN_API {
cf_t* shift_buffer;
cf_t* window_offset_buffer;
cf_t phase_compensation[SRSRAN_MAX_NSYMB * SRSRAN_NOF_SLOTS_PER_SF];
srsran_cfr_t tx_cfr; ///< Tx CFR object
} srsran_ofdm_t;
/**
@ -136,4 +139,6 @@ SRSRAN_API int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double cente
SRSRAN_API void srsran_ofdm_set_non_mbsfn_region(srsran_ofdm_t* q, uint8_t non_mbsfn_region);
SRSRAN_API int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr);
#endif // SRSRAN_OFDM_H

View File

@ -55,7 +55,9 @@ typedef struct SRSRAN_API {
srsran_dl_sf_cfg_t dl_sf;
cf_t* sf_symbols[SRSRAN_MAX_PORTS];
srsran_cfr_cfg_t cfr_config;
cf_t* sf_symbols[SRSRAN_MAX_PORTS];
cf_t* out_buffer[SRSRAN_MAX_PORTS];
srsran_ofdm_t ifft[SRSRAN_MAX_PORTS];
srsran_ofdm_t ifft_mbsfn;
@ -93,6 +95,8 @@ SRSRAN_API void srsran_enb_dl_free(srsran_enb_dl_t* q);
SRSRAN_API int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell);
SRSRAN_API int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr);
SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc);
SRSRAN_API void srsran_enb_dl_put_base(srsran_enb_dl_t* q, srsran_dl_sf_cfg_t* dl_sf);

View File

@ -224,6 +224,7 @@ SRSRAN_API void srsran_vec_conj_cc(const cf_t* x, cf_t* y, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_ff(const float* x, const uint32_t len);
/* Correlation between complex vectors x and y */
SRSRAN_API float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len);
@ -352,6 +353,36 @@ SRSRAN_API void srsran_vec_apply_cfo(const cf_t* x, float cfo, cf_t* z, int len)
SRSRAN_API float srsran_vec_estimate_frequency(const cf_t* x, int len);
/*!
* @brief Generates an amplitude envelope that, multiplied point-wise with a vector, results in clipping
* by a specified amplitude threshold.
* @param[in] x_abs Absolute value vector of the signal to be clipped
* @param[in] thres Clipping threshold
* @param[out] clip_env The generated clipping envelope
* @param[in] len Length of the vector.
*/
SRSRAN_API void
srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len);
/*!
* @brief Calculates the PAPR of a complex vector
* @param[in] in Input vector
* @param[in] len Vector length.
*/
SRSRAN_API float srsran_vec_papr_c(const cf_t* in, const int len);
/*!
* @brief Calculates the ACPR of a signal using its baseband spectrum
* @attention The spectrum passed by x_f needs to be in FFT form
* @param[in] x_f Spectrum of the signal
* @param[in] win_pos_len Channel frequency window for the positive side of the spectrum
* @param[in] win_neg_len Channel frequency window for the negative side of the spectrum
* @param[in] len Length of the x_f vector
* @returns The ACPR in linear form
*/
SRSRAN_API float
srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len);
#ifdef __cplusplus
}
#endif

View File

@ -52,6 +52,7 @@ extern "C" {
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/cfr/cfr.h"
#include "srsran/phy/dft/dft.h"
#include "srsran/phy/dft/dft_precoding.h"
#include "srsran/phy/dft/ofdm.h"

View File

@ -24,6 +24,7 @@ add_subdirectory(scrambling)
add_subdirectory(ue)
add_subdirectory(enb)
add_subdirectory(gnb)
add_subdirectory(cfr)
set(srsran_srcs $<TARGET_OBJECTS:srsran_agc>
$<TARGET_OBJECTS:srsran_ch_estimation>
$<TARGET_OBJECTS:srsran_phy_common>
@ -41,6 +42,7 @@ set(srsran_srcs $<TARGET_OBJECTS:srsran_agc>
$<TARGET_OBJECTS:srsran_ue>
$<TARGET_OBJECTS:srsran_enb>
$<TARGET_OBJECTS:srsran_gnb>
$<TARGET_OBJECTS:srsran_cfr>
)
add_library(srsran_phy STATIC ${srsran_srcs} )

View File

@ -0,0 +1,13 @@
#
# 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.
#
set(SRCS cfr.c)
add_library(srsran_cfr OBJECT ${SRCS})
add_subdirectory(test)

367
lib/src/phy/cfr/cfr.c Normal file
View File

@ -0,0 +1,367 @@
/**
*
* \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 "srsran/phy/cfr/cfr.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
// Uncomment this to use a literal implementation of the CFR algorithm
// #define CFR_PEAK_EXTRACTION
// Uncomment this to filter by zeroing the FFT bins instead of applying a frequency window
#define CFR_LPF_WITH_ZEROS
static inline float cfr_symb_peak(float* in_abs, int len);
void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out)
{
if (q == NULL || in == NULL || out == NULL) {
return;
}
if (!q->cfg.cfr_enable) {
// If no processing, copy the input samples into the output buffer
if (in != out) {
srsran_vec_cf_copy(out, in, q->cfg.symbol_sz);
}
return;
}
const float alpha = q->cfg.alpha;
const uint32_t symbol_sz = q->cfg.symbol_sz;
float beta = 0.0f;
// Calculate absolute input values
srsran_vec_abs_cf(in, q->abs_buffer_in, symbol_sz);
// In auto modes, the beta threshold is calculated based on the measured PAPR
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
beta = q->cfg.manual_thr;
} else {
const float symb_peak = cfr_symb_peak(q->abs_buffer_in, q->cfg.symbol_sz);
const float pwr_symb_peak = symb_peak * symb_peak;
const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_in, q->cfg.symbol_sz);
float symb_papr = 0.0f;
if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) {
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) {
// Once cma_n reaches its max value, stop incrementing to prevent overflow.
// This turns the averaging into a de-facto EMA with an extremely slow time constant
q->pwr_avg_in = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_in, q->cma_n++);
q->cma_n = q->cma_n & UINT64_MAX ? q->cma_n : UINT64_MAX;
} else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) {
q->pwr_avg_in = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_in, q->cfg.ema_alpha);
}
symb_papr = pwr_symb_peak / q->pwr_avg_in;
}
float papr_reduction = symb_papr / q->max_papr_lin;
beta = (papr_reduction > 1) ? symb_peak / sqrtf(papr_reduction) : 0;
}
// Clipping algorithm
if (isnormal(beta)) {
#ifdef CFR_PEAK_EXTRACTION
srsran_vec_cf_zero(q->peak_buffer, symbol_sz);
cf_t clip_thr = 0;
for (int i = 0; i < symbol_sz; i++) {
if (q->abs_buffer_in[i] > beta) {
clip_thr = beta * (in[i] / q->abs_buffer_in[i]);
q->peak_buffer[i] = in[i] - clip_thr;
}
}
// Apply FFT filter to the peak signal
srsran_dft_run_c(&q->fft_plan, q->peak_buffer, q->peak_buffer);
#ifdef CFR_LPF_WITH_ZEROS
srsran_vec_cf_zero(q->peak_buffer + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc);
#else /* CFR_LPF_WITH_ZEROS */
srsran_vec_prod_cfc(q->peak_buffer, q->lpf_spectrum, q->peak_buffer, symbol_sz);
#endif /* CFR_LPF_WITH_ZEROS */
srsran_dft_run_c(&q->ifft_plan, q->peak_buffer, q->peak_buffer);
// Scale the peak signal according to alpha
srsran_vec_sc_prod_cfc(q->peak_buffer, alpha, q->peak_buffer, symbol_sz);
// Apply the filtered clipping
srsran_vec_sub_ccc(in, q->peak_buffer, out, symbol_sz);
#else /* CFR_PEAK_EXTRACTION */
// Generate a clipping envelope and clip the signal
srsran_vec_gen_clip_env(q->abs_buffer_in, beta, alpha, q->abs_buffer_in, symbol_sz);
srsran_vec_prod_cfc(in, q->abs_buffer_in, out, symbol_sz);
// FFT filter
srsran_dft_run_c(&q->fft_plan, out, out);
#ifdef CFR_LPF_WITH_ZEROS
srsran_vec_cf_zero(out + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc);
#else /* CFR_LPF_WITH_ZEROS */
srsran_vec_prod_cfc(out, q->lpf_spectrum, out, symbol_sz);
#endif /* CFR_LPF_WITH_ZEROS */
srsran_dft_run_c(&q->ifft_plan, out, out);
#endif /* CFR_PEAK_EXTRACTION */
} else {
// If no processing, copy the input samples into the output buffer
if (in != out) {
srsran_vec_cf_copy(out, in, symbol_sz);
}
}
if (q->cfg.cfr_mode != SRSRAN_CFR_THR_MANUAL && q->cfg.measure_out_papr) {
srsran_vec_abs_cf(in, q->abs_buffer_out, symbol_sz);
const float symb_peak = cfr_symb_peak(q->abs_buffer_out, q->cfg.symbol_sz);
const float pwr_symb_peak = symb_peak * symb_peak;
const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_out, q->cfg.symbol_sz);
float symb_papr = 0.0f;
if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) {
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) {
// Do not increment cma_n here, as it is being done when calculating input PAPR
q->pwr_avg_out = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_out, q->cma_n);
}
else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) {
q->pwr_avg_out = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_out, q->cfg.ema_alpha);
}
symb_papr = pwr_symb_peak / q->pwr_avg_out;
}
const float papr_out_db = srsran_convert_power_to_dB(symb_papr);
printf("Output PAPR: %f dB\n", papr_out_db);
}
}
int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg)
{
int ret = SRSRAN_ERROR;
if (q == NULL || cfg == NULL) {
ERROR("Error, invalid inputs");
ret = SRSRAN_ERROR_INVALID_INPUTS;
goto clean_exit;
}
if (!cfg->symbol_sz || !cfg->symbol_bw || cfg->alpha < 0 || cfg->alpha > 1) {
ERROR("Error, invalid configuration");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfg->manual_thr <= 0) {
ERROR("Error, invalid configuration for manual threshold");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfg->max_papr_db <= 0)) {
ERROR("Error, invalid configuration for CMA averaging");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA &&
(cfg->max_papr_db <= 0 || (cfg->ema_alpha < 0 || cfg->ema_alpha > 1))) {
ERROR("Error, invalid configuration for EMA averaging");
goto clean_exit;
}
// Copy all the configuration parameters
q->cfg = *cfg;
q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db);
q->pwr_avg_in = CFR_EMA_INIT_AVG_PWR;
q->cma_n = 0;
if (q->cfg.measure_out_papr) {
q->pwr_avg_out = CFR_EMA_INIT_AVG_PWR;
}
if (q->abs_buffer_in) {
free(q->abs_buffer_in);
}
q->abs_buffer_in = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->abs_buffer_in) {
ERROR("Error allocating abs_buffer_in");
goto clean_exit;
}
if (q->abs_buffer_out) {
free(q->abs_buffer_out);
}
q->abs_buffer_out = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->abs_buffer_out) {
ERROR("Error allocating abs_buffer_out");
goto clean_exit;
}
if (q->peak_buffer) {
free(q->peak_buffer);
}
q->peak_buffer = srsran_vec_cf_malloc(q->cfg.symbol_sz);
if (!q->peak_buffer) {
ERROR("Error allocating peak_buffer");
goto clean_exit;
}
// Allocate the filter
if (q->lpf_spectrum) {
free(q->lpf_spectrum);
}
q->lpf_spectrum = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->lpf_spectrum) {
ERROR("Error allocating lpf_spectrum");
goto clean_exit;
}
// The LPF bandwidth is exactly the OFDM symbol bandwidth, in number of FFT bins
q->lpf_bw = q->cfg.symbol_bw;
// Initialise the filter
srsran_vec_f_zero(q->lpf_spectrum, q->cfg.symbol_sz);
// DC subcarrier is in position 0, so the OFDM symbol can go from index 1 to q->lpf_bw / 2 + 1
for (uint32_t i = 0; i < q->lpf_bw / 2 + q->cfg.dc_sc; i++) {
q->lpf_spectrum[i] = 1;
}
for (uint32_t i = q->cfg.symbol_sz - q->lpf_bw / 2; i < q->cfg.symbol_sz; i++) {
q->lpf_spectrum[i] = 1;
}
// FFT plans, for 1 OFDM symbol
if (q->fft_plan.size) {
// Replan if it was initialised previously with bigger FFT size
if (q->fft_plan.size >= q->cfg.symbol_sz) {
if (srsran_dft_replan(&q->fft_plan, q->cfg.symbol_sz)) {
ERROR("Replaning DFT plan");
goto clean_exit;
}
} else {
srsran_dft_plan_free(&q->fft_plan);
if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
} else {
// Create plan from zero otherwise
if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
if (q->ifft_plan.size) {
if (q->ifft_plan.size >= q->cfg.symbol_sz) {
// Replan if it was initialised previously with bigger FFT size
if (srsran_dft_replan(&q->ifft_plan, q->cfg.symbol_sz)) {
ERROR("Replaning DFT plan");
goto clean_exit;
}
} else {
srsran_dft_plan_free(&q->ifft_plan);
if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
} else {
// Create plan from zero otherwise
if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
srsran_dft_plan_set_norm(&q->fft_plan, true);
srsran_dft_plan_set_norm(&q->ifft_plan, true);
srsran_vec_cf_zero(q->peak_buffer, q->cfg.symbol_sz);
srsran_vec_f_zero(q->abs_buffer_in, q->cfg.symbol_sz);
srsran_vec_f_zero(q->abs_buffer_out, q->cfg.symbol_sz);
ret = SRSRAN_SUCCESS;
clean_exit:
if (ret < SRSRAN_SUCCESS) {
srsran_cfr_free(q);
}
return ret;
}
void srsran_cfr_free(srsran_cfr_t* q)
{
if (q) {
srsran_dft_plan_free(&q->fft_plan);
srsran_dft_plan_free(&q->ifft_plan);
if (q->abs_buffer_in) {
free(q->abs_buffer_in);
}
if (q->abs_buffer_out) {
free(q->abs_buffer_out);
}
if (q->peak_buffer) {
free(q->peak_buffer);
}
if (q->lpf_spectrum) {
free(q->lpf_spectrum);
}
SRSRAN_MEM_ZERO(q, srsran_cfr_t, 1);
}
}
// Find the peak absolute value of an OFDM symbol
static inline float cfr_symb_peak(float* in_abs, int len)
{
const uint32_t max_index = srsran_vec_max_fi(in_abs, len);
return in_abs[max_index];
}
bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf)
{
if (cfr_conf == NULL) {
return false;
}
if (cfr_conf->alpha < 0 || cfr_conf->alpha > 1) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfr_conf->manual_thr <= 0) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfr_conf->max_papr_db <= 0)) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA &&
(cfr_conf->max_papr_db <= 0 || (cfr_conf->ema_alpha < 0 || cfr_conf->ema_alpha > 1))) {
return false;
}
return true;
}
int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres)
{
if (q == NULL) {
ERROR("Invalid CFR object");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (thres <= 0.0f) {
ERROR("Invalid CFR threshold");
return SRSRAN_ERROR;
}
q->cfg.manual_thr = thres;
return SRSRAN_SUCCESS;
}
int srsran_cfr_set_papr(srsran_cfr_t* q, float papr)
{
if (q == NULL) {
ERROR("Invalid CFR object");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (papr <= 0.0f) {
ERROR("Invalid CFR configuration");
return SRSRAN_ERROR;
}
q->cfg.max_papr_db = papr;
q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db);
return SRSRAN_SUCCESS;
}

View File

@ -0,0 +1,17 @@
#
# 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.
#
########################################################################
# CFR Test
########################################################################
add_executable(cfr_test cfr_test.c)
target_link_libraries(cfr_test srsran_phy)
add_test(cfr_test_default cfr_test)

View File

@ -0,0 +1,308 @@
/**
*
* \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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "srsran/phy/utils/random.h"
#include "srsran/srsran.h"
#define MAX_ACPR_DB -100
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// Default CFR type
static char* cfr_type_arg = cfr_manual_str;
static int nof_prb = -1;
static srsran_cp_t cp = SRSRAN_CP_NORM;
static int nof_repetitions = 1;
static int nof_frames = 10;
static srsran_cfr_mode_t cfr_mode = SRSRAN_CFR_THR_MANUAL;
static float alpha = 1.0f;
static bool dc_empty = true;
static float thr_manual = 1.5f;
static float max_papr_db = 8.0f;
static float ema_alpha = (float)1 / (float)SRSRAN_CP_NORM_NSYMB;
static uint32_t force_symbol_sz = 0;
static double elapsed_us(struct timeval* ts_start, struct timeval* ts_end)
{
if (ts_end->tv_usec > ts_start->tv_usec) {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec) * 1000000 + (double)ts_end->tv_usec -
(double)ts_start->tv_usec;
} else {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec - 1) * 1000000 + ((double)ts_end->tv_usec + 1000000) -
(double)ts_start->tv_usec;
}
}
static void usage(char* prog)
{
printf("Usage: %s\n", prog);
printf("\t-N Force symbol size, 0 for auto [Default %d]\n", force_symbol_sz);
printf("\t-n Force number of Resource blocks [Default All]\n");
printf("\t-e extended cyclic prefix [Default Normal]\n");
printf("\t-f Number of frames [Default %d]\n", nof_frames);
printf("\t-r Number of repetitions [Default %d]\n", nof_repetitions);
printf("\t-m CFR mode: %s, %s, %s [Default %s]\n", cfr_manual_str, cfr_auto_cma_str, cfr_auto_ema_str, cfr_type_arg);
printf("\t-d Use DC subcarrier: [Default DC empty]\n");
printf("\t-a CFR alpha: [Default %.2f]\n", alpha);
printf("\t-t CFR manual threshold: [Default %.2f]\n", thr_manual);
printf("\t-p CFR Max PAPR in dB (auto modes): [Default %.2f]\n", max_papr_db);
printf("\t-E Power avg EMA alpha (EMA mode): [Default %.2f]\n", ema_alpha);
}
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "NnerfmatdpE")) != -1) {
switch (opt) {
case 'n':
nof_prb = (int)strtol(argv[optind], NULL, 10);
break;
case 'N':
force_symbol_sz = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'e':
cp = SRSRAN_CP_EXT;
break;
case 'r':
nof_repetitions = (int)strtol(argv[optind], NULL, 10);
break;
case 'f':
nof_frames = (int)strtol(argv[optind], NULL, 10);
break;
case 'm':
cfr_type_arg = argv[optind];
break;
case 'a':
alpha = strtof(argv[optind], NULL);
break;
case 't':
thr_manual = strtof(argv[optind], NULL);
break;
case 'd':
dc_empty = false;
break;
case 'p':
max_papr_db = strtof(argv[optind], NULL);
break;
case 'E':
ema_alpha = strtof(argv[optind], NULL);
break;
default:
usage(argv[0]);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
srsran_random_t random_gen = srsran_random_init(0);
struct timeval start, end;
srsran_cfr_t cfr = {};
cf_t* input = NULL;
cf_t* output = NULL;
cf_t* error = NULL;
float* acpr_buff = NULL;
float mse_dB = 0.0f;
float nmse_dB = 0.0f;
float evm = 0.0f;
int max_prb = 0.0f;
float acpr_in_dB = 0.0f;
float acpr_out_dB = 0.0f;
srsran_dft_plan_t ofdm_ifft = {};
srsran_dft_plan_t ofdm_fft = {};
if (parse_args(argc, argv) < SRSRAN_SUCCESS) {
ERROR("Error in parse_args");
goto clean_exit;
}
if (!strcmp(cfr_type_arg, cfr_manual_str)) {
cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_type_arg, cfr_auto_cma_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_type_arg, cfr_auto_ema_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ERROR("CFR mode is not recognised");
goto clean_exit;
}
if (nof_prb == -1) {
nof_prb = 6;
max_prb = SRSRAN_MAX_PRB;
} else {
max_prb = nof_prb;
}
while (nof_prb <= max_prb) {
const uint32_t symbol_sz = (force_symbol_sz) ? force_symbol_sz : (uint32_t)srsran_symbol_sz(nof_prb);
const uint32_t symbol_bw = nof_prb * SRSRAN_NRE;
const uint32_t nof_symb_slot = SRSRAN_CP_NSYMB(cp);
const uint32_t nof_symb_frame = nof_symb_slot * SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NOF_SF_X_FRAME;
const uint32_t frame_sz = symbol_sz * nof_symb_frame;
const uint32_t total_nof_re = frame_sz * nof_frames;
const uint32_t total_nof_symb = nof_symb_frame * nof_frames;
printf("Running test for %d PRB, %d Frames: \t", nof_prb, nof_frames);
fflush(stdout);
input = srsran_vec_cf_malloc(total_nof_re);
output = srsran_vec_cf_malloc(total_nof_re);
error = srsran_vec_cf_malloc(total_nof_re);
acpr_buff = srsran_vec_f_malloc(total_nof_symb);
if (!input || !output || !error || !acpr_buff) {
perror("malloc");
goto clean_exit;
}
srsran_vec_cf_zero(input, total_nof_re);
srsran_vec_cf_zero(output, total_nof_re);
srsran_vec_cf_zero(error, total_nof_re);
srsran_vec_f_zero(acpr_buff, total_nof_symb);
// Set the parameters for the CFR.
srsran_cfr_cfg_t cfr_tx_cfg = {};
cfr_tx_cfg.cfr_enable = true;
cfr_tx_cfg.symbol_sz = symbol_sz;
cfr_tx_cfg.symbol_bw = nof_prb * SRSRAN_NRE;
cfr_tx_cfg.cfr_mode = cfr_mode;
cfr_tx_cfg.max_papr_db = max_papr_db;
cfr_tx_cfg.alpha = alpha;
cfr_tx_cfg.manual_thr = thr_manual;
cfr_tx_cfg.ema_alpha = ema_alpha;
cfr_tx_cfg.dc_sc = dc_empty;
if (srsran_cfr_init(&cfr, &cfr_tx_cfg)) {
ERROR("Error initializing CFR");
goto clean_exit;
}
if (srsran_dft_plan_c(&ofdm_ifft, (int)symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating IFFT plan");
goto clean_exit;
}
srsran_dft_plan_set_norm(&ofdm_ifft, true);
if (srsran_dft_plan_c(&ofdm_fft, (int)symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating FFT plan");
goto clean_exit;
}
srsran_dft_plan_set_norm(&ofdm_fft, true);
// Generate Random data
cf_t* ofdm_symb = NULL;
for (int i = 0; i < total_nof_symb; i++) {
ofdm_symb = input + i * symbol_sz;
srsran_random_uniform_complex_dist_vector(random_gen, ofdm_symb + dc_empty, symbol_bw / 2, -1.0f, +1.0f);
srsran_random_uniform_complex_dist_vector(
random_gen, ofdm_symb + symbol_sz - symbol_bw / 2, symbol_bw / 2, -1.0f, +1.0f);
acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz);
srsran_dft_run_c(&ofdm_ifft, ofdm_symb, ofdm_symb);
}
// compute the average intput ACPR
acpr_in_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb;
acpr_in_dB = srsran_convert_power_to_dB(acpr_in_dB);
// Execute CFR
gettimeofday(&start, NULL);
for (uint32_t i = 0; i < nof_repetitions; i++) {
for (uint32_t j = 0; j < nof_frames; j++) {
for (uint32_t k = 0; k < nof_symb_frame; k++) {
srsran_cfr_process(&cfr,
input + (size_t)((k * symbol_sz) + (j * frame_sz)),
output + (size_t)((k * symbol_sz) + (j * frame_sz)));
}
}
}
gettimeofday(&end, NULL);
printf("%.1fMsps \t", (float)(total_nof_re * nof_repetitions) / elapsed_us(&start, &end));
// Compute metrics
srsran_vec_sub_ccc(input, output, error, total_nof_re);
float power_in = srsran_vec_avg_power_cf(input, total_nof_re);
float power_err = srsran_vec_avg_power_cf(error, total_nof_re);
mse_dB = srsran_convert_power_to_dB(power_err);
nmse_dB = srsran_convert_power_to_dB(power_err / power_in);
evm = 100 * sqrtf(power_err / power_in);
float snr_dB = srsran_convert_power_to_dB(power_in / power_err);
float papr_in = srsran_convert_power_to_dB(srsran_vec_papr_c(input, total_nof_re));
float papr_out = srsran_convert_power_to_dB(srsran_vec_papr_c(output, total_nof_re));
ofdm_symb = NULL;
for (int i = 0; i < total_nof_symb; i++) {
ofdm_symb = output + i * symbol_sz;
srsran_dft_run_c(&ofdm_fft, ofdm_symb, ofdm_symb);
acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz);
}
// Compute the output average ACPR
acpr_out_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb;
acpr_out_dB = srsran_convert_power_to_dB(acpr_out_dB);
printf("MSE=%.3fdB NMSE=%.3fdB EVM=%.3f%% SNR=%.3fdB", mse_dB, nmse_dB, evm, snr_dB);
printf(" In-PAPR=%.3fdB Out-PAPR=%.3fdB", papr_in, papr_out);
printf(" In-ACPR=%.3fdB Out-ACPR=%.3fdB\n", acpr_in_dB, acpr_out_dB);
srsran_dft_plan_free(&ofdm_ifft);
srsran_dft_plan_free(&ofdm_fft);
free(input);
free(output);
free(error);
free(acpr_buff);
input = NULL;
output = NULL;
error = NULL;
acpr_buff = NULL;
++nof_prb;
if (acpr_out_dB > MAX_ACPR_DB) {
printf("ACPR too large \n");
goto clean_exit;
}
}
ret = SRSRAN_SUCCESS;
// Free resources
clean_exit:
srsran_random_free(random_gen);
srsran_cfr_free(&cfr);
srsran_dft_plan_free(&ofdm_ifft);
srsran_dft_plan_free(&ofdm_fft);
if (input) {
free(input);
}
if (output) {
free(output);
}
if (error) {
free(error);
}
if (acpr_buff) {
free(acpr_buff);
}
return ret;
}

View File

@ -68,6 +68,19 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
q->slot_sz = (uint32_t)SRSRAN_SLOT_LEN(q->cfg.symbol_sz);
q->sf_sz = (uint32_t)SRSRAN_SF_LEN(q->cfg.symbol_sz);
// Set the CFR parameters related to OFDM symbol and FFT size
q->cfg.cfr_tx_cfg.symbol_sz = symbol_sz;
q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re;
// in the DL, the DC carrier is empty but still counts when designing the filter BW
q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f));
if (q->cfg.cfr_tx_cfg.cfr_enable) {
if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) {
ERROR("Error while initialising CFR module");
return SRSRAN_ERROR;
}
}
// Plan MBSFN
if (q->fft_plan.size) {
// Replan if it was initialised previously
@ -242,6 +255,7 @@ void srsran_ofdm_free_(srsran_ofdm_t* q)
if (q->window_offset_buffer) {
free(q->window_offset_buffer);
}
srsran_cfr_free(&q->tx_cfr);
SRSRAN_MEM_ZERO(q, srsran_ofdm_t, 1);
}
@ -289,6 +303,10 @@ int srsran_ofdm_tx_init(srsran_ofdm_t* q, srsran_cp_t cp, cf_t* in_buffer, cf_t*
int srsran_ofdm_tx_init_cfg(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg)
{
if (q == NULL || cfg == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
return ofdm_init_mbsfn_(q, cfg, SRSRAN_DFT_BACKWARD);
}
@ -610,6 +628,11 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf)
srsran_vec_sc_prod_cfc(&output[cp_len], norm, &output[cp_len], symbol_sz);
}
// CFR: Process the time-domain signal without the CP
if (q->cfg.cfr_tx_cfg.cfr_enable) {
srsran_cfr_process(&q->tx_cfr, output + cp_len, output + cp_len);
}
/* add CP */
srsran_vec_cf_copy(output, &output[symbol_sz], cp_len);
output += symbol_sz + cp_len;
@ -656,3 +679,37 @@ void srsran_ofdm_tx_sf(srsran_ofdm_t* q)
srsran_vec_prod_ccc(q->cfg.out_buffer, q->shift_buffer, q->cfg.out_buffer, q->sf_sz);
}
}
int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr)
{
if (q == NULL || cfr == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->max_prb) {
ERROR("Error, ofdm object not initialised");
return SRSRAN_ERROR;
}
// Check if there is nothing to configure
if (memcmp(&q->cfg.cfr_tx_cfg, cfr, sizeof(srsran_cfr_cfg_t)) == 0) {
return SRSRAN_SUCCESS;
}
// Copy the CFR config into the OFDM object
q->cfg.cfr_tx_cfg = *cfr;
// Set the CFR parameters related to OFDM symbol and FFT size
q->cfg.cfr_tx_cfg.symbol_sz = q->cfg.symbol_sz;
q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re;
// in the DL, the DC carrier is empty but still counts when designing the filter BW
q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f));
if (q->cfg.cfr_tx_cfg.cfr_enable) {
if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) {
ERROR("Error while initialising CFR module");
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}

View File

@ -49,9 +49,9 @@ int srsran_enb_dl_init(srsran_enb_dl_t* q, cf_t* out_buffer[SRSRAN_MAX_PORTS], u
ofdm_cfg.nof_prb = max_prb;
ofdm_cfg.cp = SRSRAN_CP_EXT;
ofdm_cfg.normalize = false;
ofdm_cfg.in_buffer = q->sf_symbols[0];
ofdm_cfg.out_buffer = out_buffer[0];
ofdm_cfg.sf_type = SRSRAN_SF_MBSFN;
ofdm_cfg.in_buffer = q->sf_symbols[0];
ofdm_cfg.out_buffer = out_buffer[0];
ofdm_cfg.sf_type = SRSRAN_SF_MBSFN;
if (srsran_ofdm_tx_init_cfg(&q->ifft_mbsfn, &ofdm_cfg)) {
ERROR("Error initiating FFT");
goto clean_exit;
@ -142,7 +142,7 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
if (q->cell.nof_prb != 0) {
srsran_regs_free(&q->regs);
}
q->cell = cell;
q->cell = cell;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = q->cell.nof_prb;
ofdm_cfg.cp = cell.cp;
@ -151,6 +151,7 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
ofdm_cfg.in_buffer = q->sf_symbols[i];
ofdm_cfg.out_buffer = q->out_buffer[i];
ofdm_cfg.sf_type = SRSRAN_SF_NORM;
ofdm_cfg.cfr_tx_cfg = q->cfr_config;
if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) {
ERROR("Error initiating FFT (%d)", i);
return SRSRAN_ERROR;
@ -229,6 +230,31 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
return ret;
}
int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr)
{
if (q == NULL || cfr == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Copy the cfr config into the eNB
q->cfr_config = *cfr;
// Set the cfr for the ifft's
if (srsran_ofdm_set_cfr(&q->ifft_mbsfn, &q->cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR for ifft_mbsfn");
return SRSRAN_ERROR;
}
for (int i = 0; i < SRSRAN_MAX_PORTS; i++) {
if (srsran_ofdm_set_cfr(&q->ifft[i], &q->cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR for the IFFT (%d)", i);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
#ifdef resolve
void srsran_enb_dl_apply_power_allocation(srsran_enb_dl_t* q)
{
@ -410,22 +436,22 @@ int srsran_enb_dl_put_pmch(srsran_enb_dl_t* q, srsran_pmch_cfg_t* pmch_cfg, uint
void srsran_enb_dl_gen_signal(srsran_enb_dl_t* q)
{
// TODO: PAPR control
float norm_factor = enb_dl_get_norm_factor(q->cell.nof_prb);
// First apply the amplitude normalization, then perform the IFFT and optional CFR reduction
if (q->dl_sf.sf_type == SRSRAN_SF_MBSFN) {
srsran_ofdm_tx_sf(&q->ifft_mbsfn);
srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.out_buffer,
srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.in_buffer,
norm_factor,
q->ifft_mbsfn.cfg.out_buffer,
(uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb));
q->ifft_mbsfn.cfg.in_buffer,
SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp));
srsran_ofdm_tx_sf(&q->ifft_mbsfn);
} else {
for (int i = 0; i < q->cell.nof_ports; i++) {
srsran_ofdm_tx_sf(&q->ifft[i]);
srsran_vec_sc_prod_cfc(q->ifft[i].cfg.out_buffer,
srsran_vec_sc_prod_cfc(q->ifft[i].cfg.in_buffer,
norm_factor,
q->ifft[i].cfg.out_buffer,
(uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb));
q->ifft[i].cfg.in_buffer,
SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp));
srsran_ofdm_tx_sf(&q->ifft[i]);
}
}
}

View File

@ -836,6 +836,38 @@ TEST(
free(z);
srsran_cfo_free(&srsran_cfo);)
// This test compares the clipping method used for the CFR module in its default configuration to the original CFR
// algorithm. The original algorithm can still be used by defining CFR_PEAK_EXTRACTION in the CFR module.
TEST(
srsran_vec_gen_clip_env, MALLOC(cf_t, x); MALLOC(float, x_abs); MALLOC(float, env); float thres = 0.5f;
float alpha = 0.5f;
cf_t gold = 0.0f;
for (int i = 0; i < block_size; i++) {
x[i] = RANDOM_F();
env[i] = 0.0f;
x_abs[i] = cabsf(x[i]);
}
// current implementation generates an amplitude envelope which is then multiplied with the signal
TEST_CALL(srsran_vec_gen_clip_env(x_abs, thres, alpha, env, block_size))
// Recreates the original method for clipping the signal, skipping the low-pass filtering
for (int i = 0; i < block_size; i++) {
if (x_abs[i] <= thres) {
gold = x[i];
} else {
cf_t peak = x[i] - (thres * x[i] / x_abs[i]); // extract the peak
gold = x[i] - alpha * peak; // subtract the peak from the signal, scaled by alpha
}
// Compare the two clipping methods by applying the envelope to x and determining the error
mse += cabsf(gold - env[i] * x[i]);
} if (isnormal(mse)) { mse /= block_size; }
free(x);
free(x_abs);
free(env);)
int main(int argc, char** argv)
{
char func_names[MAX_FUNCTIONS][32];
@ -1014,6 +1046,10 @@ int main(int argc, char** argv)
test_srsran_cfo_correct_change(func_names[func_count], &timmings[func_count][size_count], block_size);
func_count++;
passed[func_count][size_count] =
test_srsran_vec_gen_clip_env(func_names[func_count], &timmings[func_count][size_count], block_size);
func_count++;
sizes[size_count] = block_size;
size_count++;
}

View File

@ -580,7 +580,11 @@ int32_t srsran_vec_dot_prod_sss(const int16_t* x, const int16_t* y, const uint32
float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len)
{
return crealf(srsran_vec_dot_prod_conj_ccc(x, x, len)) / len;
if (!len) {
return 0;
} else {
return crealf(srsran_vec_dot_prod_conj_ccc(x, x, len)) / len;
}
}
float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len)
@ -627,6 +631,17 @@ float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len)
return acc;
}
float srsran_vec_avg_power_ff(const float* x, const uint32_t len)
{
if (!len) {
return 0;
} else {
float pwr_symb_avg = srsran_vec_dot_prod_fff(x, x, len);
pwr_symb_avg /= (float)len;
return pwr_symb_avg;
}
}
// Correlation assumes zero-mean x and y
float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len)
{
@ -837,3 +852,38 @@ float srsran_vec_estimate_frequency(const cf_t* x, int len)
{
return srsran_vec_estimate_frequency_simd(x, len);
}
// TODO: implement with SIMD
void srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len)
{
for (int i = 0; i < len; i++) {
env[i] = (x_abs[i] > thres) ? (1 - alpha) + alpha * thres / x_abs[i] : 1;
}
}
float srsran_vec_papr_c(const cf_t* in, const int len)
{
uint32_t max = srsran_vec_max_abs_ci(in, len);
float peak = SRSRAN_CSQABS(in[max]);
return peak / srsran_vec_avg_power_cf(in, len);
}
float srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len)
{
// The adjacent channel cannot extend beyond the FFT len
const uint32_t ch_len = win_pos_len + win_neg_len;
const uint32_t adj_ch_len = ch_len > len / 2 ? len - ch_len : ch_len;
// Integrate positive half of the signal power spectrum
float signal_pwr = srsran_vec_dot_prod_conj_ccc(x_f, x_f, win_pos_len);
// Integrate negative halt of the signal power spectrum
signal_pwr += srsran_vec_dot_prod_conj_ccc(x_f + len - win_neg_len, x_f + len - win_neg_len, win_neg_len);
const float adj_ch_pwr = srsran_vec_dot_prod_conj_ccc(x_f + win_pos_len, x_f + win_pos_len, adj_ch_len);
if (isnormal(signal_pwr)) {
return adj_ch_pwr / signal_pwr;
} else {
return 0;
}
}

View File

@ -326,6 +326,32 @@ enable = false
#fd_hz = -750.0
#init_time_s = 0.0
#####################################################################
# CFR configuration options
#
# The CFR module provides crest factor reduction for the transmitted signal.
#
# enable: Enable or disable the CFR. Default: disabled
#
# mode: manual: CFR threshold is set by cfr_manual_thres (default).
# auto_ema: CFR threshold is adaptive based on the signal PAPR. Power avg. with Exponential Moving Average.
# The time constant of the averaging can be tweaked with the ema_alpha parameter.
# auto_cma: CFR threshold is adaptive based on the signal PAPR. Power avg. with Cumulative Moving Average.
# Use with care, as CMA's increasingly slow response may be unsuitable for most use cases.
#
# strength: Ratio between amplitude-limited vs unprocessed signal (0 to 1). Default: 1
# manual_thres: Fixed manual clipping threshold for CFR manual mode. Default: 0.5
# auto_target_papr: Signal PAPR target (in dB) in CFR auto modes. output PAPR can be higher due to peak smoothing. Default: 8
# ema_alpha: Alpha coefficient for the power average in auto_ema mode. Default: 1/7
#
#####################################################################
[cfr]
#enable = false
#mode = manual
#manual_thres = 0.5
#strength = 1
#auto_target_papr = 8
#ema_alpha = 0.0143
#####################################################################
# Expert configuration options

View File

@ -142,6 +142,8 @@ public:
// eNodeB command interface
void cmd_cell_gain(uint32_t cell_id, float gain) override;
void cmd_cell_measure() override;
void toggle_padding() override;
void tti_clock() override;

View File

@ -38,6 +38,8 @@ public:
virtual void get_metrics(std::vector<phy_metrics_t>& m) = 0;
virtual void cmd_cell_gain(uint32_t cell_idx, float gain_db) = 0;
virtual void cmd_cell_measure() = 0;
};
} // namespace srsenb

View File

@ -66,6 +66,7 @@ public:
void get_metrics(std::vector<phy_metrics_t>& metrics) override;
void cmd_cell_gain(uint32_t cell_id, float gain_db) override;
void cmd_cell_measure() override;
void radio_overflow() override{};
void radio_failure() override{};

View File

@ -166,6 +166,45 @@ public:
return c;
}
void set_cell_measure_trigger()
{
// Trigger on LTE cell
for (auto it_lte = cell_list_lte.begin(); it_lte != cell_list_lte.end(); ++it_lte) {
it_lte->dl_measure = true;
}
// Trigger on NR cell
for (auto it_nr = cell_list_nr.begin(); it_nr != cell_list_nr.end(); ++it_nr) {
it_nr->dl_measure = true;
}
}
bool get_cell_measure_trigger(uint32_t cc_idx)
{
if (cc_idx < cell_list_lte.size()) {
return cell_list_lte.at(cc_idx).dl_measure;
}
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
return cell_list_nr.at(cc_idx).dl_measure;
}
return false;
}
void clear_cell_measure_trigger(uint32_t cc_idx)
{
if (cc_idx < cell_list_lte.size()) {
cell_list_lte.at(cc_idx).dl_measure = false;
}
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
cell_list_nr.at(cc_idx).dl_measure = false;
}
}
void set_cell_gain(uint32_t cell_id, float gain_db)
{
// Find LTE cell
@ -208,6 +247,11 @@ public:
return 0.0f;
}
// Common CFR configuration
srsran_cfr_cfg_t cfr_config = {};
void set_cfr_config(srsran_cfr_cfg_t cfr_cfg) { cfr_config = cfr_cfg; }
srsran_cfr_cfg_t get_cfr_config() { return cfr_config; }
// Common Physical Uplink DMRS configuration
srsran_refsignal_dmrs_pusch_cfg_t dmrs_pusch_cfg = {};

View File

@ -32,10 +32,20 @@ struct phy_cell_cfg_t {
uint32_t root_seq_idx;
uint32_t num_ra_preambles;
float gain_db;
bool dl_measure;
};
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;
struct cfr_args_t {
bool enable = false;
srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL;
float manual_thres = 0.5f;
float strength = 1.0f;
float auto_target_papr = 8.0f;
float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB;
};
struct phy_args_t {
std::string type;
srsran::phy_log_args_t log;
@ -56,6 +66,7 @@ struct phy_args_t {
bool extended_cp = false;
srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args;
cfr_args_t cfr_args;
};
struct phy_cfg_t {
@ -69,6 +80,8 @@ struct phy_cfg_t {
asn1::rrc::pusch_cfg_common_s pusch_cnfg;
asn1::rrc::pucch_cfg_common_s pucch_cnfg;
asn1::rrc::srs_ul_cfg_common_c srs_ul_cnfg;
srsran_cfr_cfg_t cfr_config;
};
} // namespace srsenb

View File

@ -219,6 +219,11 @@ void enb::cmd_cell_gain(uint32_t cell_id, float gain)
phy->cmd_cell_gain(cell_id, gain);
}
void enb::cmd_cell_measure()
{
phy->cmd_cell_measure();
}
std::string enb::get_build_mode()
{
return std::string(srsran_get_build_mode());

View File

@ -1153,6 +1153,30 @@ int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell)
return SRSRAN_SUCCESS;
}
// Parse the relevant CFR configuration params
int parse_cfr_args(all_args_t* args, srsran_cfr_cfg_t* cfr_config)
{
cfr_config->cfr_enable = args->phy.cfr_args.enable;
cfr_config->cfr_mode = args->phy.cfr_args.mode;
cfr_config->alpha = args->phy.cfr_args.strength;
cfr_config->manual_thr = args->phy.cfr_args.manual_thres;
cfr_config->max_papr_db = args->phy.cfr_args.auto_target_papr;
cfr_config->ema_alpha = args->phy.cfr_args.ema_alpha;
if (!srsran_cfr_params_valid(cfr_config)) {
fprintf(stderr,
"Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, \n "
"max_papr_db=%.2f, ema_alpha=%.2f\n",
cfr_config->cfr_mode,
cfr_config->alpha,
cfr_config->manual_thr,
cfr_config->max_papr_db,
cfr_config->ema_alpha);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_)
{
// Parse config files
@ -1272,6 +1296,12 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
}
}
// Parse CFR args
if (parse_cfr_args(args_, &phy_cfg_->cfr_config) < SRSRAN_SUCCESS) {
fprintf(stderr, "Error parsing CFR configuration\n");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}

View File

@ -56,6 +56,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
string mcc;
string mnc;
string enb_id;
string cfr_mode;
bool use_standard_lte_rates = false;
// Command line only options
@ -212,6 +213,14 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("channel.ul.hst.fd_hz", bpo::value<float>(&args->phy.ul_channel_args.hst_fd_hz)->default_value(+750.0f), "Doppler frequency in Hz")
("channel.ul.hst.init_time_s", bpo::value<float>(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds")
/* CFR section */
("cfr.enable", bpo::value<bool>(&args->phy.cfr_args.enable)->default_value(args->phy.cfr_args.enable), "CFR enable")
("cfr.mode", bpo::value<string>(&cfr_mode)->default_value("manual"), "CFR mode")
("cfr.manual_thres", bpo::value<float>(&args->phy.cfr_args.manual_thres)->default_value(args->phy.cfr_args.manual_thres), "Fixed manual clipping threshold for CFR manual mode")
("cfr.strength", bpo::value<float>(&args->phy.cfr_args.strength)->default_value(args->phy.cfr_args.strength), "CFR ratio between amplitude-limited vs original signal (0 to 1)")
("cfr.auto_target_papr", bpo::value<float>(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes")
("cfr.ema_alpha", bpo::value<float>(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)")
/* Expert section */
("expert.metrics_period_secs", bpo::value<float>(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds.")
("expert.metrics_csv_enable", bpo::value<bool>(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file.")
@ -369,6 +378,20 @@ void parse_args(all_args_t* args, int argc, char* argv[])
exit(1);
}
// convert CFR mode
if (!cfr_mode.empty()) {
if (cfr_mode == "manual") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_MANUAL;
} else if (cfr_mode == "auto_cma") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (cfr_mode == "auto_ema") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
cout << "Error, invalid CFR mode: " << cfr_mode << endl;
exit(1);
}
}
// Apply all_level to any unset layers
if (vm.count("log.all_level")) {
if (!vm.count("log.rf_level")) {
@ -465,6 +488,9 @@ static void execute_cmd(metrics_stdout* metrics, srsenb::enb_command_interface*
cout << "Enter t to restart trace." << endl;
}
metrics->toggle_print(do_metrics);
} else if (cmd[0] == "m") {
// Trigger cell measurements
control->cmd_cell_measure();
} else if (cmd[0] == "sleep") {
if (cmd.size() != 2) {
cout << "Usage: " << cmd[0] << " [number of seconds]" << endl;
@ -507,6 +533,7 @@ static void execute_cmd(metrics_stdout* metrics, srsenb::enb_command_interface*
} else {
cout << "Available commands: " << endl;
cout << " t: starts console trace" << endl;
cout << " m: downlink signal measurements" << endl;
cout << " q: quit srsenb" << endl;
cout << " cell_gain: set relative cell gain" << endl;
cout << " sleep: pauses the commmand line operation for a given time in seconds" << endl;

View File

@ -10,6 +10,8 @@
*
*/
#include <iomanip>
#include "srsran/common/threads.h"
#include "srsran/srsran.h"
@ -77,11 +79,12 @@ FILE* f;
void cc_worker::init(phy_common* phy_, uint32_t cc_idx_)
{
phy = phy_;
cc_idx = cc_idx_;
srsran_cell_t cell = phy_->get_cell(cc_idx);
uint32_t nof_prb = phy_->get_nof_prb(cc_idx);
uint32_t sf_len = SRSRAN_SF_LEN_PRB(nof_prb);
phy = phy_;
cc_idx = cc_idx_;
srsran_cell_t cell = phy_->get_cell(cc_idx);
uint32_t nof_prb = phy_->get_nof_prb(cc_idx);
uint32_t sf_len = SRSRAN_SF_LEN_PRB(nof_prb);
srsran_cfr_cfg_t cfr_config = phy_->get_cfr_config();
// Init cell here
for (uint32_t p = 0; p < phy->get_nof_ports(cc_idx); p++) {
@ -106,6 +109,10 @@ void cc_worker::init(phy_common* phy_, uint32_t cc_idx_)
ERROR("Error initiating ENB DL (cc=%d)", cc_idx);
return;
}
if (srsran_enb_dl_set_cfr(&enb_dl, &cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR");
return;
}
if (srsran_enb_ul_init(&enb_ul, signal_buffer_rx[0], nof_prb)) {
ERROR("Error initiating ENB UL");
return;
@ -251,6 +258,20 @@ void cc_worker::work_dl(const srsran_dl_sf_cfg_t& dl_sf_cfg,
srsran_vec_sc_prod_cfc(signal_buffer_tx[i], scale, signal_buffer_tx[i], sf_len);
}
}
// Measure PAPR if flag was triggered
bool cell_meas_flag = phy->get_cell_measure_trigger(cc_idx);
if (cell_meas_flag) {
uint32_t sf_len = SRSRAN_SF_LEN_PRB(enb_dl.cell.nof_prb);
for (uint32_t i = 0; i < enb_dl.cell.nof_ports; i++) {
// PAPR measure
float papr_db = 10.0f * log10(srsran_vec_papr_c(signal_buffer_tx[i], sf_len));
std::cout << "Cell #" << cc_idx << " port #" << i << " PAPR = " << std::setprecision(4) << papr_db << " dB "
<< std::endl;
}
// clear measurement flag on cell
phy->clear_cell_measure_trigger(cc_idx);
}
}
bool cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant,

View File

@ -165,6 +165,9 @@ int phy::init_lte(const phy_args_t& args,
workers_common.params = args;
workers_common.init(cfg.phy_cell_cfg, cfg.phy_cell_cfg_nr, radio, stack_lte_);
if (cfg.cfr_config.cfr_enable) {
workers_common.set_cfr_config(cfg.cfr_config);
}
parse_common_config(cfg);
@ -272,6 +275,11 @@ void phy::cmd_cell_gain(uint32_t cell_id, float gain_db)
workers_common.set_cell_gain(cell_id, gain_db);
}
void phy::cmd_cell_measure()
{
workers_common.set_cell_measure_trigger();
}
/***** RRC->PHY interface **********/
void phy::set_config(uint16_t rnti, const phy_rrc_cfg_list_t& phy_cfg_list)

View File

@ -30,6 +30,7 @@ struct phy_cell_cfg_nr_t {
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
srsran_pdsch_cfg_t pdsch = {};
srsran_prach_cfg_t prach = {};
bool dl_measure;
};
using phy_cell_cfg_list_nr_t = std::vector<phy_cell_cfg_nr_t>;