diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 8f0fcb4594bb9d90a0f0ba5d014e0cb2f0e0f72a..520dce1ace09e4120da48ade2aee5d21311e361d 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -356,8 +356,8 @@ bool compare_relative_ul_channel_bw(int nr_band, int scs, int nb_ul, frame_type_ int band_size_khz = get_supported_bw_mhz(nr_band > 256 ? FR2 : FR1, scs, nb_ul) * 1000; float limit = frame_type == TDD ? 0.04 : 0.03; - float rel_bw = (float) (2 * band_size_khz) / (float) (nr_bandtable[index].ul_max + nr_bandtable[index].ul_min); - return rel_bw <= limit; + float rel_bw = (float) (band_size_khz) / (float) (nr_bandtable[index].ul_max - nr_bandtable[index].ul_min); + return rel_bw > limit; } uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex) diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 3ffcc3495c935a2acd696c186f7e60d0949098f6..1bbdba61eb24774c40da0692d30964f97f408faa 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -227,16 +227,16 @@ int nr_ue_configure_pucch(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, fapi_nr_ul_config_pucch_pdu *pucch_pdu); -int nr_get_Pcmax(int p_Max, - uint16_t nr_band, - frequency_range_t frequency_range, - int Qm, - bool powerBoostPi2BPSK, - int scs, - int N_RB_UL, - bool is_transform_precoding, - int n_prbs, - int start_prb); +float nr_get_Pcmax(int p_Max, + uint16_t nr_band, + frequency_range_t frequency_range, + int Qm, + bool powerBoostPi2BPSK, + int scs, + int N_RB_UL, + bool is_transform_precoding, + int n_prbs, + int start_prb); int get_sum_delta_pucch(NR_UE_MAC_INST_t *mac, int slot, frame_t frame); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index e826d95e4c35e3e5d30d21666eb8444d8ace38f2..e874af44f27ee6c9e11208dfabfd6654d5b08ff3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -65,62 +65,34 @@ static int get_deltatf(uint16_t nb_of_prbs, int N_sc_ctrl_RB, int O_UCI); -// Implementation of 6.2.4 Configured ransmitted power -// 3GPP TS 38.101-1 version 16.5.0 Release 16 -// - -// The UE is allowed to set its configured maximum output power PCMAX,f,c for carrier f of serving cell c in each slot. -// The configured maximum output power PCMAX,f,c is set within the following bounds: PCMAX_L,f,c <= PCMAX,f,c <= PCMAX_H,f,c -// - -// Measurement units: -// - p_max: dBm -// - delta_TC_c: dB -// - P_powerclass: dBm -// - delta_P_powerclass: dB -// - MPR_c: dB -// - delta_MPR_c: dB -// - delta_T_IB_c dB -// - delta_rx_SRS dB -// note: -// - Assuming: -// -- Powerclass 3 capable UE (which is default power class unless otherwise stated) -// -- Maximum power reduction (MPR_c) for power class 3 -// -- no additional MPR (A_MPR_c) -int nr_get_Pcmax(int p_Max, - uint16_t nr_band, - frequency_range_t frequency_range, - int Qm, - bool powerBoostPi2BPSK, - int scs, - int N_RB_UL, - bool is_transform_precoding, - int n_prbs, - int start_prb) +// ∆MPR according to Table 6.2.2-3 38.101-1 +static float get_delta_mpr(uint16_t nr_band, int scs, int N_RB_UL, int n_prbs, int start_prb, int power_class) { - if (frequency_range == FR1) { - // TODO configure P-MAX from the upper layers according to 38.331 - int p_powerclass = 23; // dBm assuming poweclass 3 UE - int p_emax = p_Max != INT_MIN ? p_Max : p_powerclass; - int delta_P_powerclass = 0; // for powerclass 2 needs to be changed - if (p_Max && Qm == 1 && powerBoostPi2BPSK - && (nr_band == 40 || nr_band == 41 || nr_band == 77 || nr_band == 78 || nr_band == 79)) { - p_emax += 3; - delta_P_powerclass -= 3; + frame_type_t frame_type = get_frame_type(nr_band, scs); + if (compare_relative_ul_channel_bw(nr_band, scs, N_RB_UL, frame_type)) { + if (power_class == 3) { + if ((nr_band == 28 || nr_band == 83) && get_supported_bw_mhz(nr_band > 256 ? FR2 : FR1, scs, N_RB_UL) == 30) { + return 0.5f; + } } + if (power_class == 3 || power_class == 2) { + if ((nr_band == 40 || nr_band == 97) && get_supported_bw_mhz(nr_band > 256 ? FR2 : FR1, scs, N_RB_UL) == 100) { + return 1.0f; + } + } + } + return 0; +} - // TODO to be set for CA and DC - int delta_T_IB = 0; - - // TODO in case of band 41 and PRB allocation within 4MHz of the upper or lower limit of the band -> delta_TC = 1.5 - if (nr_band == 41) - LOG_E(NR_MAC, "Need to implement delta_TC for band 41\n"); - int delta_TC = 0; - - float MPR = 0; - frame_type_t frame_type = get_frame_type(nr_band, scs); - if (compare_relative_ul_channel_bw(nr_band, scs, N_RB_UL, frame_type)) { - int rb_low = (n_prbs / 2) > 1 ? (n_prbs / 2) : 1; - int rb_high = N_RB_UL - rb_low - n_prbs; - bool is_inner_rb = start_prb >= rb_low && start_prb <= rb_high && n_prbs <= ((N_RB_UL / 2) + (N_RB_UL & 1)); +// MPR according to 38-101 6.2.2 +static float get_mpr(int Qm, int N_RB_UL, bool is_transform_precoding, int n_prbs, int start_prb, int power_class) +{ + float MPR = 0; + int rb_low = (n_prbs / 2) > 1 ? (n_prbs / 2) : 1; + int rb_high = N_RB_UL - rb_low - n_prbs; + bool is_inner_rb = start_prb >= rb_low && start_prb <= rb_high && n_prbs <= ((N_RB_UL / 2) + (N_RB_UL & 1)); + switch (power_class) { + case 3: // Table 6.2.2-1 in 38.101 switch (Qm) { case 1: @@ -166,16 +138,73 @@ int nr_get_Pcmax(int p_Max, default: AssertFatal(false, "Invalid Qm %d\n", Qm); } + break; + default: + AssertFatal(false, "PowerClass != 3 not implemented\n"); + } + return MPR; +} + +// Implementation of 6.2.4 Configured ransmitted power +// 3GPP TS 38.101-1 version 16.5.0 Release 16 +// - +// The UE is allowed to set its configured maximum output power PCMAX,f,c for carrier f of serving cell c in each slot. +// The configured maximum output power PCMAX,f,c is set within the following bounds: PCMAX_L,f,c <= PCMAX,f,c <= PCMAX_H,f,c +// - +// Measurement units: +// - p_max: dBm +// - delta_TC_c: dB +// - P_powerclass: dBm +// - delta_P_powerclass: dB +// - MPR_c: dB +// - delta_MPR_c: dB +// - delta_T_IB_c dB +// - delta_rx_SRS dB +// note: +// - Assuming: +// -- Powerclass 3 capable UE (which is default power class unless otherwise stated) +// -- Maximum power reduction (MPR_c) for power class 3 +// -- no additional MPR (A_MPR_c) +float nr_get_Pcmax(int p_Max, + uint16_t nr_band, + frequency_range_t frequency_range, + int Qm, + bool powerBoostPi2BPSK, + int scs, + int N_RB_UL, + bool is_transform_precoding, + int n_prbs, + int start_prb) +{ + const int power_class = 3; // Assume power class 3 + if (frequency_range == FR1) { + // TODO configure P-MAX from the upper layers according to 38.331 + int p_powerclass = 23; // dBm assuming poweclass 3 UE + int p_emax = p_Max != INT_MIN ? p_Max : p_powerclass; + int delta_P_powerclass = 0; // for powerclass 2 needs to be changed + if (p_Max && Qm == 1 && powerBoostPi2BPSK + && (nr_band == 40 || nr_band == 41 || nr_band == 77 || nr_band == 78 || nr_band == 79)) { + p_emax += 3; + delta_P_powerclass -= 3; } + // TODO to be set for CA and DC + int delta_T_IB = 0; + + // TODO in case of band 41 and PRB allocation within 4MHz of the upper or lower limit of the band -> delta_TC = 1.5 + if (nr_band == 41) + LOG_E(NR_MAC, "Need to implement delta_TC for band 41\n"); + int delta_TC = 0; + + float MPR = get_mpr(Qm, N_RB_UL, is_transform_precoding, n_prbs, start_prb, power_class); + float delta_MPR = get_delta_mpr(nr_band, scs, N_RB_UL, n_prbs, start_prb, power_class); int A_MPR = 0; // TODO too complicated to implement for now (see 6.2.3 in 38.101-1) int delta_rx_SRS = 0; // TODO for SRS int P_MPR = 0; // to ensure compliance with applicable electromagnetic energy absorption requirements - float total_reduction = (MPR > A_MPR ? MPR : A_MPR) + delta_T_IB + delta_TC + delta_rx_SRS; - if (P_MPR > total_reduction) - total_reduction = P_MPR; - int pcmax_high, pcmax_low; + float total_reduction = max(max(MPR + delta_MPR, A_MPR) + delta_T_IB + delta_TC + delta_rx_SRS, P_MPR); + + float pcmax_high, pcmax_low; if (p_Max) { pcmax_high = p_emax < (p_powerclass - delta_P_powerclass) ? p_emax : (p_powerclass - delta_P_powerclass); pcmax_low = (p_emax - delta_TC) < (p_powerclass - delta_P_powerclass - total_reduction) @@ -186,8 +215,8 @@ int nr_get_Pcmax(int p_Max, pcmax_low = p_powerclass - delta_P_powerclass - total_reduction; } // TODO we need a strategy to select a value between minimum and maximum allowed PC_max - int pcmax = (pcmax_low + pcmax_high) / 2; - LOG_D(MAC, "Configured maximum output power: %d dBm <= PCMAX %d dBm <= %d dBm \n", pcmax_low, pcmax, pcmax_high); + float pcmax = (pcmax_low + pcmax_high) / 2; + LOG_D(MAC, "Configured maximum output power: %f dBm <= PCMAX %f dBm <= %f dBm \n", pcmax_low, pcmax, pcmax_high); return pcmax; } else { // FR2 TODO it is even more complex because it is radiated power @@ -297,8 +326,9 @@ int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, delta_F_PUCCH = *delta_F_PUCCH_config; } - // PUCCH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1) - // TODO: P_CMAX for format 2 + // 38.101-1: The allowed MPR for SRS, PUCCH formats 0, 1, 3 and 4, and PRACH shall be as specified for QPSK modulated DFT- + // s-OFDM of equivalent RB allocation. The allowed MPR for PUCCH format 2 shall be as specified for QPSK + // modulated CP-OFDM of equivalent RB allocation. int P_CMAX = nr_get_Pcmax(mac->p_Max, mac->nr_band, mac->frequency_range, @@ -306,7 +336,7 @@ int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, false, mac->current_UL_BWP->scs, mac->current_UL_BWP->BWPSize, - true, + format_type == 2, 1, start_prb); int P_CMIN = -40; // TODO: minimum TX power, possibly 38.101-1 6.3.1 diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp index 179d94bd1b1e37f76451de653c7cca82f42c13fa..67b63e55154b2a3823c3fef5990a42f18fe0a63b 100644 --- a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp @@ -19,43 +19,152 @@ * contact@openairinterface.org */ - #include "gtest/gtest.h" extern "C" { #include "openair2/LAYER2/NR_MAC_UE/mac_proto.h" #include "executables/softmodem-common.h" -uint64_t get_softmodem_optmask(void) {return 0;} +uint64_t get_softmodem_optmask(void) +{ + return 0; +} static softmodem_params_t softmodem_params; -softmodem_params_t *get_softmodem_params(void) { +softmodem_params_t* get_softmodem_params(void) +{ return &softmodem_params; - } } #include <cstdio> #include "common/utils/LOG/log.h" -TEST(power_procedures_fr1, test_prach_max_tx_power_mpr) +TEST(test_pcmax, test_mpr) { - // Inner PRB, MPR = 1.5 + // Inner PRB, MPR = 1.5, no delta MPR int prb_start = 4; int N_RB_UL = 51; // 10Mhz - EXPECT_EQ(22, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); + int nr_band = 20; + float expected_power = 23 - (1.5 / 2); + EXPECT_EQ(expected_power, nr_get_Pcmax(23, nr_band, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); - // Outer PRB, MPR = 3 + // Outer PRB, MPR = 3, no delta MPR prb_start = 0; - EXPECT_EQ(21, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); + expected_power = 23 - (3.0 / 2); + EXPECT_EQ(expected_power, nr_get_Pcmax(23, nr_band, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); - // Channel bandwidth conditon not met, no MPR - N_RB_UL = 106; - EXPECT_EQ(23, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); + // Outer PRB on band 28, MPR = 3, delta MPR = 0.5 dB + N_RB_UL = 78; + nr_band = 28; + expected_power = 23 - ((3.0 + 0.5) / 2); + EXPECT_EQ(expected_power, nr_get_Pcmax(23, nr_band, FR1, 2, false, 1, N_RB_UL, false, 100, prb_start)); } -TEST(power_procedures_fr1, test_not_implemented) +TEST(test_pcmax, test_not_implemented) { int N_RB_UL = 51; EXPECT_DEATH(nr_get_Pcmax(23, 20, FR1, 1, false, 1, N_RB_UL, false, 6, 0), "MPR for Pi/2 BPSK not implemented yet"); } +TEST(test_pcmax, test_pucch_max_power) +{ + // Format 2, transform precoding, MPR = 1 + int prb_start = 0; + int N_RB_UL = 51; // 10Mhz + float expected_power = 23 - (1.0 / 2); + EXPECT_EQ(expected_power, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, true, 1, prb_start)); + + // Other fromats, no transform precoding, MPR = 3 + expected_power = 23 - (3.0 / 2); + EXPECT_EQ(expected_power, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 1, prb_start)); +} + +TEST(test_pucch_power_state, test_accumulated_delta_pucch) +{ + NR_UE_MAC_INST_t mac = {0}; + NR_UE_UL_BWP_t current_UL_BWP = {0}; + current_UL_BWP.scs = 1; + current_UL_BWP.BWPSize = 106; + NR_PUCCH_ConfigCommon_t pucch_ConfigCommon = {0}; + mac.current_UL_BWP = ¤t_UL_BWP; + mac.current_UL_BWP->pucch_ConfigCommon = &pucch_ConfigCommon; + mac.nr_band = 20; + NR_PUCCH_Config_t pucch_Config = {0}; + struct NR_PUCCH_PowerControl power_config; + pucch_Config.pucch_PowerControl = &power_config; + mac.G_b_f_c = 0; + mac.pucch_power_control_initialized = true; + + int scs = 1; + int sum_delta_pucch = 3; + uint8_t format_type = 1; + uint16_t nb_of_prbs = 1; + uint8_t freq_hop_flag = 0; + uint8_t add_dmrs_flag = 0; + uint8_t N_symb_PUCCH = 12; + int subframe_number = 0; + int O_uci = 2; + uint16_t start_prb = 0; + int P_CMAX = + nr_get_Pcmax(23, mac.nr_band, FR1, 2, false, current_UL_BWP.scs, current_UL_BWP.BWPSize, false, nb_of_prbs, start_prb); + + int pucch_power_prev = get_pucch_tx_power_ue(&mac, + scs, + &pucch_Config, + sum_delta_pucch, + format_type, + nb_of_prbs, + freq_hop_flag, + add_dmrs_flag, + N_symb_PUCCH, + subframe_number, + O_uci, + start_prb); + EXPECT_LT(pucch_power_prev, P_CMAX); + for (int i = 0; i < 10; i++) { + int pucch_power_state = mac.G_b_f_c; + int pucch_power = get_pucch_tx_power_ue(&mac, + scs, + &pucch_Config, + sum_delta_pucch, + format_type, + nb_of_prbs, + freq_hop_flag, + add_dmrs_flag, + N_symb_PUCCH, + subframe_number, + O_uci, + start_prb); + if (pucch_power_prev == P_CMAX) { + EXPECT_EQ(pucch_power_state, mac.G_b_f_c) << "PUCCH power control state increased after reaching max TX power"; + } + EXPECT_LE(pucch_power, P_CMAX) << "PUUCH TX power above P_CMAX"; + EXPECT_EQ(std::min(P_CMAX, pucch_power_prev + sum_delta_pucch), pucch_power) + << "PUCCH power expected to change by delta pucch only, between P_CMAX and P_CMIN"; + pucch_power_prev = pucch_power; + + if (i > 5) { + EXPECT_EQ(pucch_power, P_CMAX) << "Expected to reach MAX PUCCH TX power"; + } + } + + sum_delta_pucch = -15; + for (int i = 0; i < 10; i++) { + int pucch_power_state = mac.G_b_f_c; + int pucch_power = get_pucch_tx_power_ue(&mac, + scs, + &pucch_Config, + sum_delta_pucch, + format_type, + nb_of_prbs, + freq_hop_flag, + add_dmrs_flag, + N_symb_PUCCH, + subframe_number, + O_uci, + start_prb); + EXPECT_LE(mac.G_b_f_c, pucch_power_state) << "PUCCH power control state increased with negative delta pucch"; + pucch_power_prev = pucch_power; + } +} + int main(int argc, char** argv) { logInit();