diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 8f0fcb4594..520dce1ace 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/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index d9d63a9704..a98595fdd5 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -359,6 +359,7 @@ typedef struct nfapi_nr_ue_ul_beamforming_t beamforming; //OAI specific int8_t absolute_delta_PUSCH; + int16_t tx_power; fapi_nr_tx_request_body_t tx_request_body; } nfapi_nr_ue_pusch_pdu_t; diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h index c8d8232382..dba0f8bcd5 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h @@ -571,6 +571,7 @@ typedef struct NR_UE_UL_BWP { uint8_t mcs_table; nr_dci_format_t dci_format; int max_fb_time; + long *p0_NominalWithGrant; } NR_UE_UL_BWP_t; // non-BWP serving cell configuration diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 52d45c938b..50577fc4f7 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -1409,11 +1409,14 @@ static void configure_common_BWP_ul(NR_UE_MAC_INST_t *mac, int bwp_id, NR_BWP_Up ul_common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList, NR_PUSCH_TimeDomainResourceAllocationList_t); UPDATE_IE(bwp->msg3_DeltaPreamble, ul_common->pusch_ConfigCommon->choice.setup->msg3_DeltaPreamble, long); + UPDATE_IE(bwp->p0_NominalWithGrant, ul_common->pusch_ConfigCommon->choice.setup->p0_NominalWithGrant, long); } if (ul_common->pusch_ConfigCommon->present == NR_SetupRelease_PUSCH_ConfigCommon_PR_release) { asn1cFreeStruc(asn_DEF_NR_PUSCH_TimeDomainResourceAllocationList, bwp->tdaList_Common); free(bwp->msg3_DeltaPreamble); bwp->msg3_DeltaPreamble = NULL; + free(bwp->p0_NominalWithGrant); + bwp->p0_NominalWithGrant = NULL; } } } @@ -2130,6 +2133,8 @@ void release_ul_BWP(NR_UE_MAC_INST_t *mac, int index) asn1cFreeStruc(asn_DEF_NR_SRS_Config, bwp->srs_Config); free(bwp->msg3_DeltaPreamble); bwp->msg3_DeltaPreamble = NULL; + free(bwp->p0_NominalWithGrant); + bwp->p0_NominalWithGrant = NULL; free(bwp); } diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 3ffcc3495c..88973af849 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -219,6 +219,20 @@ int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, int subframe_number, int O_uci, uint16_t start_prb); +int get_pusch_tx_power_ue( + NR_UE_MAC_INST_t *mac, + int num_rb, + int start_prb, + uint16_t nb_symb_sch, + uint16_t nb_dmrs_prb, + uint16_t nb_ptrs_prb, + uint16_t qm, + uint16_t R, + uint16_t beta_offset_csi1, + uint32_t sum_bits_in_codeblocks, + int delta_pusch, + bool is_rar_tx_retx, + bool transform_precoding); int nr_ue_configure_pucch(NR_UE_MAC_INST_t *mac, int slot, @@ -227,16 +241,18 @@ 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); + +float nr_get_Pcmin(int scs, int nr_band, int N_RB_UL); 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 e826d95e4c..0dbaa5debf 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 @@ -195,6 +224,14 @@ int nr_get_Pcmax(int p_Max, } } +float nr_get_Pcmin(int scs, int nr_band, int N_RB_UL) { + int band_index = get_supported_band_index(nr_band > 256 ? FR2 : FR1, scs, N_RB_UL); + const float table_38101_6_3_1_1[] = { + -40, -40, -40, -40, -39, -38.2, -37.5, -37, -36.5, -35.2, -34.6, -34, -33.5, -33 + }; + return table_38101_6_3_1_1[band_index]; +} + // This is not entirely correct. In certain k2/k1/k0 settings we might postpone accumulating delta_PUCCH until next HARQ feedback // slot. The correct way to do this would be to calculate the K_PUCCH (delta_PUCCH summation window end) for each PUCCH occasion and // compare PUCCH transmission symbol with the reception symbol of the DCI containing delta_PUCCH to determine if the delta_PUCCH @@ -297,8 +334,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,10 +344,10 @@ 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 + int P_CMIN = nr_get_Pcmin(mac->current_UL_BWP->scs, mac->nr_band, mac->current_UL_BWP->BWPSize); int16_t pathloss = compute_nr_SSB_PL(mac, mac->ssb_measurements.ssb_rsrp_dBm); if (power_config->twoPUCCH_PC_AdjustmentStates && *power_config->twoPUCCH_PC_AdjustmentStates > 1) { @@ -396,3 +434,136 @@ int16_t compute_nr_SSB_PL(NR_UE_MAC_INST_t *mac, short ssb_rsrp_dBm) return pathloss; } + +// PUSCH transmission power according to 38.213 7.1 +int get_pusch_tx_power_ue(NR_UE_MAC_INST_t *mac, + int num_rb, + int start_prb, + uint16_t nb_symb_sch, + uint16_t nb_dmrs_prb, + uint16_t nb_ptrs_prb, + uint16_t qm, + uint16_t R, + uint16_t beta_offset_csi1, + uint32_t sum_bits_in_codeblocks, + int delta_pusch, + bool is_rar_tx_retx, + bool transform_precoding) +{ + LOG_D(NR_MAC, + "PUSCH tx power determination num_rb=%d start_prb=%d nb_symb_sch=%u nb_dmrs_prb=%u nb_ptrs_prb=%u Qm=%u R= %u " + "beta_offset_cs1=%u sum_bits_in_codeblocks=%u delta_pusch=%d is_rar_tx_retx=%d transform_precoding=%d\n", + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; + AssertFatal(current_UL_BWP, "Missing configuration: need UL_BWP to calculate PUSCH tx power\n"); + NR_PUSCH_Config_t *pusch_Config = current_UL_BWP->pusch_Config; + bool has_pusch_config = pusch_Config != NULL; + bool has_pusch_power_control_config = has_pusch_config && pusch_Config->pusch_PowerControl != NULL; + bool is_provided_alpha_sets = has_pusch_power_control_config && pusch_Config->pusch_PowerControl->p0_AlphaSets != NULL; + AssertFatal(!has_pusch_power_control_config || pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList == NULL, + "SRI-PUSCH-PowerControl handling not implemented\n"); + + int P_O_NOMINAL_PUSCH; + float alpha; + const float alpha_factor_table[8] = {0.0f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f}; + if (is_rar_tx_retx || !is_provided_alpha_sets || current_UL_BWP->p0_NominalWithGrant == NULL) { + int DELTA_PREAMBLE_MSG3 = 0; + if (current_UL_BWP->msg3_DeltaPreamble) { + DELTA_PREAMBLE_MSG3 = *current_UL_BWP->msg3_DeltaPreamble; + } + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = current_UL_BWP->rach_ConfigCommon; + long preambleReceivedTargetPower = nr_rach_ConfigCommon->rach_ConfigGeneric.preambleReceivedTargetPower; + int P_O_PRE = preambleReceivedTargetPower; + P_O_NOMINAL_PUSCH = P_O_PRE + DELTA_PREAMBLE_MSG3; + + if (has_pusch_power_control_config && pusch_Config->pusch_PowerControl->msg3_Alpha) { + alpha = alpha_factor_table[*pusch_Config->pusch_PowerControl->msg3_Alpha]; + } else { + alpha = 1.0f; + } + } else { + P_O_NOMINAL_PUSCH = *current_UL_BWP->p0_NominalWithGrant; + if (pusch_Config->pusch_PowerControl->p0_AlphaSets->list.array[0]->alpha) { + alpha = alpha_factor_table[*pusch_Config->pusch_PowerControl->p0_AlphaSets->list.array[0]->alpha]; + } else { + // Default according to 38.331 P0-PUSCH-AlphaSet field descriptions + alpha = 1.0f; + } + } + + int P_O_UE_PUSCH; + if (is_rar_tx_retx || !is_provided_alpha_sets) { + P_O_UE_PUSCH = 0; + } else { + if (pusch_Config->pusch_PowerControl->p0_AlphaSets->list.array[0]->p0) { + P_O_UE_PUSCH = *pusch_Config->pusch_PowerControl->p0_AlphaSets->list.array[0]->p0; + } else { + // Default according to 38.331 P0-PUSCH-AlphaSet field descriptions + P_O_UE_PUSCH = 0; + } + } + + int mu = current_UL_BWP->scs; + + int M_pusch_component = 10 * log10((pow(2, mu)) * num_rb); + int P_CMAX = nr_get_Pcmax(mac->p_Max, + mac->nr_band, + mac->frequency_range, + 2, + false, + mac->current_UL_BWP->scs, + mac->current_UL_BWP->BWPSize, + transform_precoding, + 1, + start_prb); + + int P_O_PUSCH = P_O_NOMINAL_PUSCH + P_O_UE_PUSCH; + + float DELTA_TF = 0; + if (has_pusch_power_control_config && pusch_Config->pusch_PowerControl->deltaMCS) { + float beta_offset = 1; + float BPRE; + if (sum_bits_in_codeblocks == 0) { + float table_38_213_9_3_2[] = { + 1.125, 11.250, 21.375, 31.625, 41.750, 52.000, 62.250, 72.500, 82.875, 93.125, + 103.500, 114.000, 125.000, 136.250, 148.000, 1510.000, 1612.625, 1715.875, 1820.000, + }; + beta_offset = table_38_213_9_3_2[beta_offset_csi1]; + BPRE = (qm * R / beta_offset) / 1024; + } else { + const int nb_subcarrier_per_rb = 12; + const uint32_t N_RE = nb_subcarrier_per_rb * nb_symb_sch - nb_dmrs_prb - nb_ptrs_prb; + BPRE = sum_bits_in_codeblocks / (float)(N_RE * num_rb); + } + DELTA_TF = 10 * log10(pow(2, BPRE * 1.25f) * beta_offset); + } + + // TODO: compute pathoss using correct reference + int16_t pathloss = compute_nr_SSB_PL(mac, mac->ssb_measurements.ssb_rsrp_dBm); + + int f_b_f_c = 0; + if (has_pusch_power_control_config && pusch_Config->pusch_PowerControl->tpc_Accumulation) { + f_b_f_c = delta_pusch; + } else { + // TODO: PUSCH power control state + } + LOG_D(NR_MAC, + "PUSCH tx power components P_O_PUSCH=%d, M_pusch_component=%d, alpha*pathloss=%f, delta_TF=%f, f_b_f_c=%d\n", + P_O_PUSCH, + M_pusch_component, + alpha * pathloss, + DELTA_TF, + f_b_f_c); + return min(P_CMAX, P_O_PUSCH + M_pusch_component + alpha * pathloss + DELTA_TF + f_b_f_c); +} diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 29b36d3e9d..20b0e80f61 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -913,6 +913,22 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->pusch_data.tb_size = mac->ul_harq_info[pid].TBS; } + bool is_rar_tx_retx = rnti_type == TYPE_TC_RNTI_; + + pusch_config_pdu->tx_power = get_pusch_tx_power_ue(mac, + pusch_config_pdu->rb_size, + pusch_config_pdu->rb_start, + pusch_config_pdu->nr_of_symbols, + nb_dmrs_re_per_rb * number_dmrs_symbols, + 0, //TODO: count PTRS per RB + pusch_config_pdu->qam_mod_order, + pusch_config_pdu->target_code_rate, + pusch_config_pdu->pusch_uci.beta_offset_csi1, + pusch_config_pdu->pusch_data.tb_size << 3, + pusch_config_pdu->absolute_delta_PUSCH, + is_rar_tx_retx, + pusch_config_pdu->transform_precoding); + pusch_config_pdu->ldpcBaseGraph = get_BG(pusch_config_pdu->pusch_data.tb_size << 3, pusch_config_pdu->target_code_rate); //The MAC entity shall restart retxBSR-Timer upon reception of a grant for transmission of new data on any UL-SCH 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 179d94bd1b..49f2d58f27 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,314 @@ * 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 #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; + } +} + +TEST(pc_min, check_all_bw_indexes) +{ + const int NB_RB_UL[] = {11, 24, 38, 51, 65, 78, 106, 133, 162, 217, 245, 273}; + for (auto i = 0U; i < sizeof(NB_RB_UL) / sizeof(NB_RB_UL[0]); i++) { + (void)nr_get_Pcmin(1, 20, NB_RB_UL[i]); + } +} + +TEST(pusch_power_control, pusch_power_control_msg3) +{ + 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; + mac.current_UL_BWP = ¤t_UL_BWP; + NR_RACH_ConfigCommon_t nr_rach_ConfigCommon = {0}; + current_UL_BWP.rach_ConfigCommon = &nr_rach_ConfigCommon; + mac.nr_band = 78; + NR_PUSCH_Config_t pusch_Config = {0}; + current_UL_BWP.pusch_Config = &pusch_Config; + NR_PUSCH_PowerControl pusch_PowerControl = {0}; + pusch_Config.pusch_PowerControl = &pusch_PowerControl; + pusch_PowerControl.tpc_Accumulation = (long*)1; + + // msg3 cofiguration as in 5g_rfsimulator testcase + int num_rb = 8; + int start_prb = 0; + uint16_t nb_symb_sch = 3; + uint16_t nb_dmrs_prb = 12; + uint16_t nb_ptrs_prb = 0; + uint16_t Qm = 2; + uint16_t R = 1570; + uint16_t beta_offset_csi1 = 0; + uint32_t sum_bits_in_codeblocks = 56; + int delta_pusch = 0; + bool is_rar_tx_retx = true; + + int P_CMAX = nr_get_Pcmax(23, mac.nr_band, FR1, Qm, false, current_UL_BWP.scs, current_UL_BWP.BWPSize, false, num_rb, start_prb); + + long preambleReceivedTargetPower = -96; + nr_rach_ConfigCommon.rach_ConfigGeneric.preambleReceivedTargetPower = preambleReceivedTargetPower; + + int power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + false); + EXPECT_EQ(power, -84); + EXPECT_LT(power, P_CMAX); + nr_rach_ConfigCommon.rach_ConfigGeneric.preambleReceivedTargetPower -= 2; + + int reduced_power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + false); + EXPECT_EQ(std::min(P_CMAX, power - 2), reduced_power) + << "Incorrect handling of preambleReceivedTargetPower"; + EXPECT_LT(reduced_power, P_CMAX) << "Power above P_CMAX"; + + delta_pusch = 4; + int increased_power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + false); + EXPECT_EQ(std::min(P_CMAX, reduced_power + delta_pusch), increased_power) + << "delta_pusch should increase tx power"; + EXPECT_LT(increased_power, P_CMAX) << "Power above P_CMAX"; +} + +TEST(pusch_power_control, pusch_power_data) +{ + 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; + mac.current_UL_BWP = ¤t_UL_BWP; + NR_RACH_ConfigCommon_t nr_rach_ConfigCommon = {0}; + current_UL_BWP.rach_ConfigCommon = &nr_rach_ConfigCommon; + mac.nr_band = 78; + + bool is_rar_tx_retx = false; + int num_rb = 5; + int start_prb = 0; + uint16_t nb_symb_sch = 3; + uint16_t nb_dmrs_prb = 6; + uint16_t nb_ptrs_prb = 0; + uint16_t Qm = 2; + uint16_t R = 6790; + uint16_t beta_offset_csi1 = 0; + uint32_t sum_bits_in_codeblocks = 192; + int delta_pusch = 4; + bool transform_precoding = false; + NR_PUSCH_Config_t pusch_Config = {0}; + current_UL_BWP.pusch_Config = &pusch_Config; + NR_PUSCH_PowerControl pusch_PowerControl = {0}; + pusch_Config.pusch_PowerControl = &pusch_PowerControl; + pusch_PowerControl.tpc_Accumulation = (long*)1; + long p0_NominalWithGrant = 0; + current_UL_BWP.p0_NominalWithGrant = &p0_NominalWithGrant; + pusch_PowerControl.deltaMCS = (long*)1; + + int P_CMAX = nr_get_Pcmax(23, mac.nr_band, FR1, Qm, false, current_UL_BWP.scs, current_UL_BWP.BWPSize, transform_precoding, num_rb, start_prb); + + int power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_LE(power, P_CMAX); + EXPECT_EQ(power, 18); + + const int BETA_OFFSET_CSI1_DEFAULT = 13; + sum_bits_in_codeblocks = 0; // CSI-only + power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + BETA_OFFSET_CSI1_DEFAULT, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_EQ(power, P_CMAX) << "Expecting max tx power because of deltaMCS with CSI-only"; +} + int main(int argc, char** argv) { logInit();