mirror of https://github.com/PentHertz/srsLTE.git
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:
parent
91502c87db
commit
b2075673e5
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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} )
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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 = {};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in New Issue