From f555bf2957245b3f53050b166d286606d6e49d3c Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 10 Feb 2025 09:46:49 +0100 Subject: [PATCH] analog beamforming based distributed antenna system --- doc/analog_beamforming.md | 8 ++++- executables/nr-ru.c | 37 +++++++++++------------ openair1/PHY/NR_TRANSPORT/nr_dci.c | 3 +- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 3 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 3 +- openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 3 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 8 ++--- openair1/PHY/NR_TRANSPORT/srs_rx.c | 3 +- openair1/PHY/defs_gNB.h | 2 ++ openair1/SCHED_NR/phy_procedures_nr_gNB.c | 13 +++++--- openair1/SCHED_NR/sched_nr.h | 3 +- openair2/GNB_APP/L1_nr_paramdef.h | 10 ++++-- openair2/GNB_APP/gnb_config.c | 1 + 13 files changed, 59 insertions(+), 38 deletions(-) diff --git a/doc/analog_beamforming.md b/doc/analog_beamforming.md index 3c695c5464..e953e02fb3 100644 --- a/doc/analog_beamforming.md +++ b/doc/analog_beamforming.md @@ -10,7 +10,7 @@ There are three main beamforming techinques: analog, digital and hybrid. The nam The presence of a limited number of predefined beams at RU poses constraints to the scheduler at gNB. As a matter of fact, the scheduler can serve only a limited number of beams, depending on the RU characteristics (possibly only 1), in a given time scale, that also depends on the RU characteristics (e.g. 1 slot or 1 symbol). This limitation doesn't exist for digital beamforming. -To be noted that in our implementation analog beamforming is only supported in non-split/monolithic mode because we don't support yet SCF P19 interface that would be needed to manage these procedure in a split scenario with SCF FAPI. +Analog beamforming implementation also allows to enable distributed antenna systems (DAS), where each beam corrisponds to one antenna (or a set of antennas) of the system. In this scenario, the scheduler constaint is alleviated because normally the number of concurrent beams allowed equals the total number of beams. # Configuration file fields for analog beamforming @@ -22,6 +22,8 @@ In the `MACRLC` section of configuration files, there are three new parameters: - `beams_per_period` is the number of concurrent beams the RU can handle in the beam duration (default value is 1) - `beam_weights` is a vector field containing the set of beam indices to be provided by the OAI L1 to the RU is also required. In current implementation, the number of beam indices should be equal to the number of SSBs transmitted. +DAS is enabled by setting to 1 the parameter `enable_das` in the L1 section of the configuration file. + # Implementation in OAI scheduler A new MAC structure `NR_beam_info_t` controls the behavior of the scheduler in presence of analog beamforming. Besides the already mentioned parameters `beam_duration` and `beams_per_period`, the structure also holds a matrix `beam_allocation[i][j]`, whose indices `i` and `j` stands respectively for the number of beams in the period and the slot index (the size of the latter depends on the frame characteristics). @@ -32,6 +34,8 @@ It is important to note that in current implementation, there are several period # FAPI implementation +To be noted that in our implementation analog beamforming is only supported in non-split/monolithic mode because we don't support yet SCF P19 interface that would be needed to manage these procedure in a split scenario with SCF FAPI. + In `config_request` structure, a vendor extension (`nfapi_nr_analog_beamforming_ve_t`) configures the lower layers at initialization with the following information: - `analog_bf_vendor_ext` which can assume values 1 or 0 for enabling or disabling analog beamforming - `num_beams_period_vendor_ext` which corresponds to the configuration parameter `beams_per_period` @@ -45,6 +49,8 @@ Additionally, L2 provides in each channel FAPI message information about the bea To handle multiple concurrent beams, the buffers containing Tx and Rx data in frequency domain (`txdataF` and `rxdataF`) have been extended by one dimension to contain multiple concurrent beams to be transmitted/received. The function `beam_index_allocation`, called by every L1 channel, is responsible to match the FAPI analog beam index to the RU beam index and to store the latter `beam_id` structure, which allocates the beams per symbol, despite L2 only supporting beam change at slot level. At the same time, the function returns the concurrent beam index, to be used to store data in frequency domain buffers. While doing so, the function also checks if there is room for current beam in the list of concurrent beams, which should always be the case, if L2 properly allocated the channels. +In case of DAS, since each beam corresponds to a specific antenna port, the `beam_index_allocation` function is simplified in the sense that the beam index corresponds to the antenna port index of the frequency domain buffers. + # RU implementation The implementation is still work in progress. diff --git a/executables/nr-ru.c b/executables/nr-ru.c index a2b7fc858d..910de225b7 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -846,7 +846,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) static void fill_rf_config(RU_t *ru, char *rf_config_file) { - int i; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index openair0_config_t *cfg = &ru->openair0_cfg; @@ -872,13 +871,13 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) AssertFatal(ru->nb_rx > 0 && ru->nb_rx <= 8, "openair0 does not support more than 8 antennas\n"); cfg->Mod_id = 0; - cfg->num_rb_dl=N_RB; - cfg->tx_num_channels=ru->nb_tx; - cfg->rx_num_channels=ru->nb_rx; + cfg->num_rb_dl = N_RB; + cfg->tx_num_channels = ru->nb_tx * ru->num_beams_period; + cfg->rx_num_channels = ru->nb_rx * ru->num_beams_period; LOG_I(PHY,"Setting RF config for N_RB %d, NB_RX %d, NB_TX %d\n",cfg->num_rb_dl,cfg->rx_num_channels,cfg->tx_num_channels); LOG_I(PHY,"tune_offset %.0f Hz, sample_rate %.0f Hz\n",cfg->tune_offset,cfg->sample_rate); - for (i=0; inb_tx; i++) { + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { cfg->tx_freq[i] = (double)fp->dl_CarrierFreq; } else if (ru->if_freq_offset) { @@ -893,7 +892,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) i, cfg->tx_gain[i],cfg->tx_freq[i]); } - for (i=0; inb_rx; i++) { + for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { cfg->rx_freq[i] = (double)fp->ul_CarrierFreq; } else if (ru->if_freq_offset) { @@ -939,8 +938,8 @@ static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_ Each rf chain is is addressed by the card number and the chain on the card. The rf_map specifies for each antenna port, on which rf chain the mapping should start. Multiple antennas are mapped to successive RF chains on the same card. */ -int setup_RU_buffers(RU_t *ru) { - int i,j; +int setup_RU_buffers(RU_t *ru) +{ int card,ant; //uint16_t N_TA_offset = 0; NR_DL_FRAME_PARMS *fp; @@ -962,27 +961,27 @@ int setup_RU_buffers(RU_t *ru) { if (ru->openair0_cfg.mmapped_dma == 1) { // replace RX signal buffers with mmaped HW versions - for (i=0; inb_rx; i++) { - card = i/4; - ant = i%4; + for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { + card = i / 4; + ant = i % 4; LOG_D(PHY, "Mapping RU id %u, rx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.rxdata[i]); ru->common.rxdata[i] = ru->openair0_cfg.rxbase[ru->rf_map.chain + ant]; - for (j = 0; j < 16; j++) { - ru->common.rxdata[i][j] = 16-j; + for (int j = 0; j < 16; j++) { + ru->common.rxdata[i][j] = 16 - j; } } - for (i=0; inb_tx; i++) { - card = i/4; - ant = i%4; + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { + card = i / 4; + ant = i % 4; LOG_D(PHY, "Mapping RU id %u, tx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.txdata[i]); ru->common.txdata[i] = ru->openair0_cfg.txbase[ru->rf_map.chain + ant]; - for (j = 0; j < 16; j++) { - ru->common.txdata[i][j] = 16-j; + for (int j = 0; j < 16; j++) { + ru->common.txdata[i][j] = 16 - j; } } } else { // not memory-mapped DMA @@ -1049,7 +1048,7 @@ void ru_tx_func(void *param) ru->fh_north_out(ru); } else { if(frame_tx == print_frame) { - for (int i=0; inb_tx; i++) { + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if(slot_tx == 0) { sprintf(filename,"gNBdataF_frame%d_sl%d.m", print_frame, slot_tx); LOG_M(filename,"txdataF_frame",&ru->gNB_list[0]->common_vars.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index 38a671d50c..bea9cb63c5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -81,7 +81,8 @@ static void nr_generate_dci(PHY_VARS_gNB *gNB, int dci_idx = 0; // multi-beam number (for concurrent beams) int bitmap = SL_to_bitmap(cset_start_symb, pdcch_pdu_rel15->DurationSymbols); - int beam_nb = beam_index_allocation(dci_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + dci_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 7e3d68f282..58d11413f4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -680,7 +680,8 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; // beam number in multi-beam scenario (concurrent beams) int bitmap = SL_to_bitmap(rel15->StartSymbolIndex, rel15->NrOfSymbols); - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 55a235737a..6398c59faa 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -85,7 +85,8 @@ int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *pr int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[i].beam_idx; int start_symb = prach_pdu->prach_start_symbol + i * n_symb; int bitmap = SL_to_bitmap(start_symb, n_symb); - prach->beam_nb[i] = beam_index_allocation(fapi_beam_idx, + prach->beam_nb[i] = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, Slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index f63bec4d5b..4621e184a6 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -76,7 +76,8 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t if (gNB->common_vars.beam_id) { int fapi_beam_idx = ulsch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; int bitmap = SL_to_bitmap(ulsch_pdu->start_symbol_index, ulsch_pdu->nr_of_symbols); - ulsch->beam_nb = beam_index_allocation(fapi_beam_idx, + ulsch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index a16b68f708..a9639ce0d7 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -57,10 +57,7 @@ //#define DEBUG_NR_PUCCH_RX 1 -void nr_fill_pucch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pucch_pdu_t *pucch_pdu) +void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu) { bool found = false; for (int i = 0; i < gNB->max_nb_pucch; i++) { @@ -73,7 +70,8 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, if (gNB->common_vars.beam_id) { int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); - pucch->beam_nb = beam_index_allocation(fapi_beam_idx, + pucch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index 16b92be946..c8b6d99c30 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -59,7 +59,8 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu if (gNB->common_vars.beam_id) { int bitmap = SL_to_bitmap(srs_pdu->time_start_position, 1 << srs_pdu->num_symbols); int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - srs->beam_nb = beam_index_allocation(fapi_beam_idx, + srs->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 975588ec65..ad17c243ae 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -492,6 +492,8 @@ typedef struct PHY_VARS_gNB_s { int num_ulprbbl; uint16_t ulprbbl [MAX_BWP_SIZE]; + bool enable_analog_das; + time_stats_t phy_proc_tx; time_stats_t phy_proc_rx; time_stats_t rx_prach; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index b626aa59cd..ad75cc733e 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -45,7 +45,8 @@ //#define DEBUG_RXDATA //#define SRS_IND_DEBUG -int beam_index_allocation(int fapi_beam_index, +int beam_index_allocation(bool das, + int fapi_beam_index, nfapi_nr_analog_beamforming_ve_t *analog_bf, NR_gNB_COMMON *common_vars, int slot, @@ -55,8 +56,10 @@ int beam_index_allocation(int fapi_beam_index, if (!common_vars->beam_id) return 0; - int idx = -1; int ru_beam_idx = analog_bf->analog_beam_list[fapi_beam_index].value; + if (das) + return ru_beam_idx; + int idx = -1; for (int j = 0; j < common_vars->num_beams_period; j++) { // L2 analog beam implementation is slot based, so we need to verify occupancy for the whole slot for (int i = 0; i < symbols_per_slot; i++) { @@ -136,7 +139,8 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_n int txdataF_offset = slot * fp->samples_per_slot_wCP; // beam number in a scenario with multiple concurrent beams int bitmap = SL_to_bitmap(ssb_start_symbol, 4); // 4 ssb symbols - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, &cfg->analog_beamforming_ve, &gNB->common_vars, slot, @@ -278,7 +282,8 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int lprime_num = mapping_parms.lprime + 1; for (int j = 0; j < mapping_parms.size; j++) csi_bitmap |= ((1 << lprime_num) - 1) << mapping_parms.loverline[j]; - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, &cfg->analog_beamforming_ve, &gNB->common_vars, slot, diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index 7450518f19..40c32c2199 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -47,7 +47,8 @@ void feptx_prec(RU_t *ru,int frame_tx,int tti_tx); void nr_phy_init_RU(RU_t *ru); void nr_phy_free_RU(RU_t *ru); void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot); -int beam_index_allocation(int fapi_beam_index, +int beam_index_allocation(bool das, + int fapi_beam_index, nfapi_nr_analog_beamforming_ve_t *analog_bf, NR_gNB_COMMON *common_vars, int slot, diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h index e313818ad2..b2b4ab93c1 100644 --- a/openair2/GNB_APP/L1_nr_paramdef.h +++ b/openair2/GNB_APP/L1_nr_paramdef.h @@ -61,6 +61,8 @@ #define HLP_L1_PHASE_COMP "Apply NR symbolwise phase rotation" #define CONFIG_STRING_NUM_ANTENNAS_PER_THREAD "dmrs_num_antennas_per_thread" #define HLP_NUM_ARX "Number of antennas per thread for PUSCH channel estimation" +#define CONFIG_STRING_ANALOG_DAS "enable_das" + /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ /* L1 configuration parameters */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ @@ -81,12 +83,13 @@ {CONFIG_STRING_L1_PRACH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=150, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=50, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_SRS_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=30, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_TX_AMP_BACKOFF_dB, HLP_L1TX_BO,0, .uptr=NULL, .defintval=36, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PHASE_COMP, HLP_L1_PHASE_COMP,PARAMFLAG_BOOL, .uptr=NULL,.defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_NUM_ANTENNAS_PER_THREAD, HLP_NUM_ARX,0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ + {CONFIG_STRING_ANALOG_DAS, NULL, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \ } // clang-format on #define L1_CC_IDX 0 @@ -109,6 +112,7 @@ #define L1_TX_AMP_BACKOFF_dB 17 #define L1_PHASE_COMP 18 #define NUM_ANTENNAS_PER_THREAD 19 +#define L1_ANALOG_DAS 20 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ #endif diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index aab1ca831c..4a68aa1f78 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1061,6 +1061,7 @@ void RCconfig_NR_L1(void) gNB->TX_AMP = min(32767.0 / pow(10.0, .05 * (double)(*L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr)), INT16_MAX); gNB->phase_comp = *L1_ParamList.paramarray[j][L1_PHASE_COMP].uptr; gNB->dmrs_num_antennas_per_thread = *(L1_ParamList.paramarray[j][NUM_ANTENNAS_PER_THREAD].uptr); + gNB->enable_analog_das = *(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr); LOG_I(NR_PHY, "TX_AMP = %d (-%d dBFS)\n", gNB->TX_AMP, *L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr); AssertFatal(gNB->TX_AMP > 300, "TX_AMP is too small, must be larger than 300 (is %d)\n", gNB->TX_AMP); // Midhaul configuration