diff --git a/ci-scripts/conf_files/gnb.sa.band77.273prb.usrpn310.2x2.conf b/ci-scripts/conf_files/gnb.sa.band77.273prb.usrpn310.2x2.conf index 5928ea4452..1b871371be 100644 --- a/ci-scripts/conf_files/gnb.sa.band77.273prb.usrpn310.2x2.conf +++ b/ci-scripts/conf_files/gnb.sa.band77.273prb.usrpn310.2x2.conf @@ -172,9 +172,11 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 270; - pucch_TargetSNRx10 = 270; + pusch_TargetSNRx10 = 320; + pucch_TargetSNRx10 = 200; pusch_FailureThres = 1000; + ul_bler_target_upper = 0.25; + ul_bler_target_lower = 0.15; ul_max_mcs = 25; } ); @@ -194,7 +196,7 @@ RUs = ( local_rf = "yes" nb_tx = 2; nb_rx = 2; - att_tx = 15; + att_tx = 0; att_rx = 0; bands = [77]; max_pdschReferenceSignalPower = -27; diff --git a/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml b/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml index 6823b553c4..98c20a5a6e 100644 --- a/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml +++ b/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml @@ -124,7 +124,7 @@ matix ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz - oai-gnb=RetxCheck=10,100,100,100 oai-gnb=EndsWithBye + oai-gnb=RetxCheck=dl=10,100,100,100;ul=25,100,100,100 oai-gnb=EndsWithBye diff --git a/common/ran_context.h b/common/ran_context.h index 91baa78d84..f6784c733a 100644 --- a/common/ran_context.h +++ b/common/ran_context.h @@ -69,8 +69,6 @@ typedef struct { int nb_nr_macrlc_inst; /// Number of component carriers per instance in this node int *nb_mac_CC; - /// Number of component carriers per instance in this node - int *nb_nr_mac_CC; /// Number of L1 instances in this node int nb_L1_inst; /// Number of NB_IoT L1 instances in this node diff --git a/common/utils/T/T_messages.txt b/common/utils/T/T_messages.txt index 432ad30262..918982a8c8 100644 --- a/common/utils/T/T_messages.txt +++ b/common/utils/T/T_messages.txt @@ -213,6 +213,14 @@ ID = GNB_MAC_UL_PDU_WITH_DATA DESC = NR MAC uplink PDU for an UE GROUP = ALL:MAC:GNB:WIRESHARK FORMAT = int,gNB_ID : int,CC_id : int,rnti : int,frame : int,slot : int,harq_pid : buffer,data +ID = GNB_MAC_PUSCH_POWER_CONTROL + DESC = NR MAC PUSCH power control-related data + GROUP = ALL:MAC:GNB:CSV + FORMAT = int,rnti : int,frame : int,slot : int,snrx10 : int,phr : int,tpc : int,tb_size : int,txpower_calc : int,rbSize : int,mcs : int,rssi +ID = GNB_MAC_PUCCH_POWER_CONTROL + DESC = NR MAC PUCCH power control-related data + GROUP = ALL:MAC:GNB:CSV + FORMAT = int,rnti : int,frame : int,slot : int,snrx10 : int,tpc : int,rssi ID = GNB_MAC_DL_RAR_PDU_WITH_DATA DESC = NR MAC downlink PDU for RAR GROUP = ALL:MAC:GNB:WIRESHARK diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c index 084e3b93f3..fdef246496 100644 --- a/common/utils/telnetsrv/telnetsrv_ci.c +++ b/common/utils/telnetsrv/telnetsrv_ci.c @@ -393,6 +393,27 @@ static int trigger_bwp_switch(char *buf, int debug, telnet_printfunc_t prnt) } } +static int set_pusch_target_snr(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!buf) + ERROR_MSG_RET("need an SNR to read\n"); + + char *end; + long new_snr = strtol(buf, &end, 0); + if (*end != 0) + ERROR_MSG_RET("error: could not parse number in '%s'\n", buf); + + gNB_MAC_INST *nrmac = RC.nrmac[0]; + NR_SCHED_LOCK(&nrmac->sched_lock); + UE_iterator(nrmac->UE_info.connected_ue_list, it) { + nr_mac_set_target_snrx10(&it->UE_sched_ctrl.pusch_pc, new_snr * 10); + } + NR_SCHED_UNLOCK(&nrmac->sched_lock); + prnt("set new PUSCH target SNR %d for all UEs\n", new_snr); + + return 0; +} + static telnetshell_cmddef_t cicmds[] = { {"get_single_rnti", "", get_single_rnti}, {"force_reestab", "[rnti(hex,opt)]", trigger_reestab}, @@ -404,6 +425,7 @@ static telnetshell_cmddef_t cicmds[] = { {"get_current_bwp", "[rnti(hex,opt)]", get_current_bwp}, {"trigger_bwp_switch", "newBWPId [rnti(hex,opt)]", trigger_bwp_switch}, {"trigger_n2_ho", "[neighbour_pci(uint32_t),ueId(uint32_t)]", rrc_gNB_trigger_n2_ho}, + {"set_pusch_target_snr", "[somelongSNR(dec)]", set_pusch_target_snr}, {"pdu_session_release", "[gNB_ue_ngap_id(int,opt)]", trigger_ngap_pdu_session_release}, {"", "", NULL}, }; diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md index 94c813d7ac..a1fa90d9b6 100644 --- a/doc/MAC/mac-usage.md +++ b/doc/MAC/mac-usage.md @@ -67,6 +67,43 @@ happen. Say we have 0% PDCCH success rate (radio link failure scenario) but `pdcch_cl_adjust` is 0 indicating perfect PDCCH channel. it would take ~18 PDCCH failures to reach maximum aggregation level. +## Power control + +The gNB tracks the average SNR used by the UE for PUSCH/PUCCH, and sends TPC +commands to maintain the UE at a specific target SNR. Internally, it maintains +an average of measured SNR and RSSI. It also updates `tpc_in_flight`, which +tracks TPC changes that don't show up in the average yet. For instance, imagine +that the target SNR of 15 changes to 20. Three successive TPC commands need to +be sent (+3, +1, +1), but it will take time to show up in the average SNR. To +account for this, `tpc_in_flight` is updated by the TPCs sent, and an average +will make it go down back to zero at the same pace as the average SNR +approaches the target SNR. The sum of average SNR and `tpc_in_flight` sums up +to the actual, current SNR, which approximates the target SNR. The power +control tries to keep the SNR within -1<=targetSNR<=+2dB to avoid too many TPC +changes. On each DTX, `tpc_in_flight` is lowered by 1dB, correspondingly +lowering the current SNR by 1dB, which result in "boosting" the UE's target +SNR. + +For PUCCH, the gNB will try to keep the UE at the target SNR as configured by +`pucch_TargetSNRx10`. + +For PUSCH, two modes are available currently. Both have in common that +`pusch_TargetSNRx10` is used to configure a specific target SNR, but the +meaning of the target SNR changes depending on the mode. (For the following, +note that as per 38.213 Sec 7.1, the number of PRBs used for a PUSCH +transmission is always taken into account by the UE). + +1. the "normal" mode (default), in which the gNB tries to keep the UE at the + target SNR, regardless of MCS (and layers) used. This is the default. + Depending on the target MCS and number of layers, the target SNR should be + placed in the 20-30dB range. +2. the "deltaMCS" mode (`deltaMCS=1`, see below). In this mode, the UE accounts + for the MCS in the power used for PUSCH transmissions automatically (see + delta_TF factor in TS 38.213 Sec 7.1) when using only one layer. Thus it is + sufficient to set a lower target SNR (5-10dB). As the spec foresees that + this only works for one layer, it is suggested to disable SRS to disable + multi-layer operation in UL. + ## Periodic output and interpretation The scheduler periodically outputs statistics that can help you judge the radio @@ -81,8 +118,8 @@ Example: UE RNTI 2460 CU-UE-ID 2 in-sync PH 28 dB PCMAX 24 dBm, average RSRP -74 (8 meas), average SINR 40.0 (32 meas) UE 2460: CQI 15, RI 2, PMI (14,1) UE 2460: UL-RI 2 TPMI 0 -UE 2460: dlsch_rounds 32917/5113/1504/560, dlsch_errors 211, pucch0_DTX 1385, BLER 0.19557 MCS (1) 23 CCE fail 3 -UE 2460: ulsch_rounds 3756/353/182/179, ulsch_errors 170, ulsch_DTX 285, BLER 0.33021 MCS (1) 27 (Qm 8 dB) NPRB 5 SNR 31.0 dB CCE fail 0 +UE 2460: dlsch_rounds 32917/5113/1504/560, dlsch_errors 211, pucch0_DTX 1385 (SNR 19.8+0.2 dB), BLER 0.19557 MCS (1) 23 CCE fail 3 +UE 2460: ulsch_rounds 3756/353/182/179, ulsch_errors 170, ulsch_DTX 285, BLER 0.33021 MCS (1) 27 (Qm 8 dB) NPRB 5 SNR 31.0 (-1.0) dB CCE fail 0 UE 2460: MAC: TX 1530943191 RX 194148 bytes UE 2460: LCID 1: TX 651 RX 3031 bytes UE 2460: LCID 2: TX 0 RX 0 bytes @@ -139,6 +176,8 @@ The fourth and fifth line show HARQ-related information: and ACK/NAK cannot be conveyed properly or DL DCIs are missed by the UE. This is also something that should be very small compared to `A` in `dlsch_rounds`. +* `(SNR x+y dB)`: PUCCH SNR where `x` is the average PUCCH SNR and `y` the + difference to the target (positive: above SNR, negative: below) * `DLSCH BLER` is the current measured block-error rate of the DLSCH. Basically a moving average of `B`/`A` in `dlsch_rounds`. This is something that should always be close to the target bler that the MAC scheduler uses. typically 10-30% if @@ -162,10 +201,9 @@ The fourth and fifth line show HARQ-related information: fluctuate a lot but when doing high throughput with iperf, it indicates the number of PRBs that the UE is actually able to use with its power budget and should be high. -* ULSCH `SNR`: the current SNR that the gNB receives the UE signal with. It - should be close to the target in the gNB configuration file, - `pusch_TargetSNRx10`, which should be around 10 times the value shown in the - log +* ULSCH `SNR`: the current SNR that the gNB receives the UE PUSCH signal with. + This value should be close to the target SNR; in paranthesis, the difference + to the target SNR. * Both ULSCH/DLSCH `CCE fail`: lists the number of failed CCE attempts. If this number gets high, it signifies that the scheduler tried to scheduled this UE, but could not allocate the DCI. @@ -276,9 +314,7 @@ configuration](../RRC/rrc-usage.md) as well for SIB configuration. * `disable_harq` (default 0=false): flag whether to disable HARQ completely (useful for NTN operation, see <../RUNMODEM.md>). **this is a Rel-17 feature and you need to have a capable UE for this** -* `use_deltaMCS` (default 0=false): flag whether to enable deltaMCS (**this is not fully tested - and might not work** and you might need to adjust other parameters such as - target SNRs) +* `use_deltaMCS` (default 0=false): flag whether to enable deltaMCS * `num_dlharq` (default 16): number of HARQ processes to use in DL (other valid options are 2, 4, 6, 8, 10, 12, 32; **32 is a Rel-17 features**) * `num_ulharq` (default 16): as `num_dlharq` for UL (other valid option is 32; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index df97840e24..b83d11db98 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -799,9 +799,6 @@ int main(int argc, char **argv) }; RC.nb_nr_macrlc_inst = 1; - RC.nb_nr_mac_CC = (int*)malloc(RC.nb_nr_macrlc_inst*sizeof(int)); - for (i = 0; i < RC.nb_nr_macrlc_inst; i++) - RC.nb_nr_mac_CC[i] = 1; mac_top_init_gNB(ngran_gNB, scc, &conf, &rlc_config); gNB_mac = RC.nrmac[0]; nr_mac_config_scc(RC.nrmac[0], scc, &conf); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index fa8643023f..3e6cbac8b6 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -832,9 +832,6 @@ int main(int argc, char *argv[]) }; RC.nb_nr_macrlc_inst = 1; - RC.nb_nr_mac_CC = (int*)malloc(RC.nb_nr_macrlc_inst*sizeof(int)); - for (i = 0; i < RC.nb_nr_macrlc_inst; i++) - RC.nb_nr_mac_CC[i] = 1; mac_top_init_gNB(ngran_gNB, scc, &conf, &rlc_config); nr_mac_config_scc(RC.nrmac[0], scc, &conf); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c index 13b6306a1c..421a7f5740 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c @@ -81,8 +81,8 @@ bool read_mac_sm(void* data) rd->dl_aggr_sdus = UE->mac_stats.dl.num_mac_sdu; rd->ul_aggr_sdus = UE->mac_stats.ul.num_mac_sdu; - rd->pusch_snr = (float) sched_ctrl->pusch_snrx10 / 10; //: float = -64; - rd->pucch_snr = (float) sched_ctrl->pucch_snrx10 / 10; //: float = -64; + rd->pusch_snr = sched_ctrl->pusch_pc.avg_snr; //: float = -64; + rd->pucch_snr = sched_ctrl->pucch_pc.avg_snr; //: float = -64; rd->wb_cqi = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb; rd->dl_mcs1 = sched_ctrl->dl_bler_stats.mcs; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index d956f9abe2..9b89b536b2 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1660,30 +1660,33 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) AssertFatal(config.first_active_bwp <= config.num_additional_bwps, "1st active BWP does not belog to the configured BWPs\n"); if (MacRLC_ParamList.numelt > 0) { + AssertFatal(MacRLC_ParamList.numelt == 1, "only one MACRLCs section supported!\n"); + AssertFatal(MacRLC_ParamList.numelt == RC.nb_nr_macrlc_inst, "only one MACRLCs section supported!\n"); /* NR RLC config is needed by mac_top_init_gNB() */ nr_rlc_configuration_t default_rlc_config; config_rlc(cfg, &default_rlc_config); + config.pusch.target_snrx10 = *(MacRLC_ParamList.paramarray[0][MACRLC_PUSCHTARGETSNRX10_IDX].iptr); + config.pusch.rssi_threshold = *(MacRLC_ParamList.paramarray[0][MACRLC_PUSCH_RSSI_THRES_IDX].iptr); + config.pucch.rssi_threshold = *(MacRLC_ParamList.paramarray[0][MACRLC_PUCCH_RSSI_THRES_IDX].iptr); + config.pucch.target_snrx10 = *(MacRLC_ParamList.paramarray[0][MACRLC_PUCCHTARGETSNRX10_IDX].iptr); + config.ul_prbblack_SNR_threshold = *(MacRLC_ParamList.paramarray[0][MACRLC_UL_PRBBLACK_SNR_THRESHOLD_IDX].iptr); + config.pucch.failure_thres = *(MacRLC_ParamList.paramarray[0][MACRLC_PUCCHFAILURETHRES_IDX].iptr); + config.pusch.failure_thres = *(MacRLC_ParamList.paramarray[0][MACRLC_PUSCHFAILURETHRES_IDX].iptr); + + LOG_I(NR_MAC, + "PUSCH Target %d RSSI thresh %d Failure %d, PUCCH Target %d RSSI thresh %d Failure %d\n", + config.pusch.target_snrx10, + config.pusch.rssi_threshold, + config.pusch.failure_thres, + config.pucch.target_snrx10, + config.pucch.rssi_threshold, + config.pucch.failure_thres); + ngran_node_t node_type = get_node_type(); mac_top_init_gNB(node_type, scc, &config, &default_rlc_config); - RC.nb_nr_mac_CC = (int *)malloc(RC.nb_nr_macrlc_inst * sizeof(int)); for (j = 0; j < RC.nb_nr_macrlc_inst; j++) { - RC.nb_nr_mac_CC[j] = *(MacRLC_ParamList.paramarray[j][MACRLC_CC_IDX].iptr); - RC.nrmac[j]->pusch_target_snrx10 = *(MacRLC_ParamList.paramarray[j][MACRLC_PUSCHTARGETSNRX10_IDX].iptr); - RC.nrmac[j]->pusch_rssi_threshold = *(MacRLC_ParamList.paramarray[j][MACRLC_PUSCH_RSSI_THRES_IDX].iptr); - RC.nrmac[j]->pucch_rssi_threshold = *(MacRLC_ParamList.paramarray[j][MACRLC_PUCCH_RSSI_THRES_IDX].iptr); - RC.nrmac[j]->pucch_target_snrx10 = *(MacRLC_ParamList.paramarray[j][MACRLC_PUCCHTARGETSNRX10_IDX].iptr); - RC.nrmac[j]->ul_prbblack_SNR_threshold = *(MacRLC_ParamList.paramarray[j][MACRLC_UL_PRBBLACK_SNR_THRESHOLD_IDX].iptr); - RC.nrmac[j]->pucch_failure_thres = *(MacRLC_ParamList.paramarray[j][MACRLC_PUCCHFAILURETHRES_IDX].iptr); - RC.nrmac[j]->pusch_failure_thres = *(MacRLC_ParamList.paramarray[j][MACRLC_PUSCHFAILURETHRES_IDX].iptr); - - LOG_I(NR_MAC, - "PUSCH Target %d, PUCCH Target %d, PUCCH Failure %d, PUSCH Failure %d\n", - RC.nrmac[j]->pusch_target_snrx10, - RC.nrmac[j]->pucch_target_snrx10, - RC.nrmac[j]->pucch_failure_thres, - RC.nrmac[j]->pusch_failure_thres); if (strcmp(*(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_N_PREFERENCE_IDX].strptr), "local_RRC") == 0) { // check number of instances is same as RRC/PDCP diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 643b607d25..4a755246db 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -1230,7 +1230,7 @@ bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t DevAssert(get_softmodem_params()->phy_test); NR_SCHED_LOCK(&nrmac->sched_lock); - NR_UE_info_t *UE = get_new_nr_ue_inst(&nrmac->UE_info.uid_allocator, rnti, CellGroup); + NR_UE_info_t *UE = get_new_nr_ue_inst(&nrmac->UE_info.uid_allocator, rnti, CellGroup, &nrmac->radio_config); DevAssert(UE->uid < MAX_MOBILES_PER_GNB); // physical simulators: we assume we can always create a UE free_and_zero(UE->ra); // physical simulators: UE will not do RA UE->local_bwp_id = 1; // for physical simulators diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index d123d53063..83e1b6c699 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -604,7 +604,7 @@ int nr_fill_successrar(const NR_UE_sched_ctrl_t *ue_sched_ctl, successRAR->CONT_RES_6 = ue_cont_res_id[5]; successRAR->R = 0; successRAR->CH_ACESS_CPEXT = 1; - successRAR->TPC = ue_sched_ctl->tpc0; + successRAR->TPC = 1; // 0dB change, don't know how to determine this. successRAR->HARQ_FTI = timing_indicator; successRAR->PUCCH_RI = resource_indicator; successRAR->TA1 = (uint8_t)(timing_advance_cmd >> 8); // 4 MSBs of timing advance; @@ -718,7 +718,7 @@ void nr_initiate_ra_proc(module_id_t module_idP, return; } - UE = get_new_nr_ue_inst(&nr_mac->UE_info.uid_allocator, rnti, NULL); + UE = get_new_nr_ue_inst(&nr_mac->UE_info.uid_allocator, rnti, NULL, &nr_mac->radio_config); if (!add_new_UE_RA(nr_mac, UE)) { LOG_E(NR_MAC, "FAILURE: %4d.%2d initiating RA procedure for preamble index %d: no free RA process\n", frame, slot, preamble_index); delete_nr_ue_data(UE, NULL, &nr_mac->UE_info.uid_allocator); @@ -1337,6 +1337,7 @@ static void prepare_dl_pdus(gNB_MAC_INST *nr_mac, prepare_dci_pdu(pdcch_pdu_rel15, scc, sched_ctrl->search_space, coreset, aggregation_level, CCEIndex, fapi_beam, rnti); pdcch_pdu_rel15->numDlDci++; + int tpc = 1; // 0dB change, don't know how to determine this. dci_pdu_rel15_t dci_payload = prepare_dci_dl_payload(nr_mac, UE, rnti_type, @@ -1344,6 +1345,7 @@ static void prepare_dl_pdus(gNB_MAC_INST *nr_mac, pdsch_pdu_rel15, sched_pdsch, pucch, + tpc, current_harq_pid, tb_scaling, false); @@ -1894,9 +1896,6 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, tb_scaling, pduindex); - // Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR - sched_ctrl->tpc1 = 1; - // Add padding header and zero rest out if there is space left if (ra->mac_pdu_length < harq->tb_size) { NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) &buf[ra->mac_pdu_length]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 87584b5709..564038e7bc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -399,6 +399,7 @@ static void nr_fill_nfapi_dl_SIB_pdu(gNB_MAC_INST *gNB_mac, pdsch_pdu_rel15, pdsch, NULL, + 1, 0, 0, is_sib1); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index dd12cfe4d2..7a0ad19b7a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -326,7 +326,7 @@ static uint32_t update_dlsch_buffer(frame_t frame, slot_t slot, NR_UE_info_t *UE { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; sched_ctrl->num_total_bytes = 0; - sched_ctrl->dl_pdus_total = 0; + int dl_pdus_total = 0; logical_chan_id_t ch[NR_MAX_NUM_LCID] = {0}; int n = 0; @@ -350,7 +350,7 @@ static uint32_t update_dlsch_buffer(frame_t frame, slot_t slot, NR_UE_info_t *UE continue; sched_ctrl->rlc_status[lcid] = ret[i]; - sched_ctrl->dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer; + dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer; sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; LOG_D(MAC, "%4d.%2d UE %04x LCID %d status: %d bytes, %d PDUs, total buffer %d bytes %d PDUs\n", @@ -361,7 +361,7 @@ static uint32_t update_dlsch_buffer(frame_t frame, slot_t slot, NR_UE_info_t *UE ret[i].bytes_in_buffer, ret[i].pdus_in_buffer, sched_ctrl->num_total_bytes, - sched_ctrl->dl_pdus_total); + dl_pdus_total); } return sched_ctrl->num_total_bytes; } @@ -889,7 +889,6 @@ static void pf_dl(gNB_MAC_INST *mac, // (for 4 PDUs) and optionally + 2 for TA. Once RLC gives the number of // PDUs, we replace with 3 * numPDUs const int oh = 3 * 4 + (sched_ctrl->ta_apply ? 2 : 0); - //const int oh = 3 * sched_ctrl->dl_pdus_total + (sched_ctrl->ta_apply ? 2 : 0); nr_find_nb_rb(sched_pdsch.Qm, sched_pdsch.R, 1, // no transform precoding for DL @@ -1071,6 +1070,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE harq->is_waiting = true; } UE->mac_stats.dl.rounds[harq->round]++; + int tpc = nr_mac_get_tpc(&sched_ctrl->pucch_pc); LOG_D(NR_MAC, "%4d.%2d [DLSCH/PDSCH/PUCCH] RNTI %04x DCI L %d start %3d RBs %3d startSymbol %2d nb_symbol %2d dmrspos %x MCS %2d nrOfLayers %d TBS %4d HARQ PID %2d round %d RV %d NDI %d dl_data_to_ULACK %d (%d.%d) PUCCH allocation %d TPC %d\n", frame, @@ -1093,7 +1093,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE pucch ? pucch->frame : 0, pucch ? pucch->ul_slot : 0, sched_pdsch->pucch_allocation, - sched_ctrl->tpc1); + tpc); DevAssert(sched_pdsch->rbSize > 0); const int bwp_id = current_BWP->bwp_id; @@ -1171,12 +1171,11 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE pdsch_pdu, sched_pdsch, pucch, + tpc, current_harq_pid, 0, false); - // Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR - sched_ctrl->tpc1 = 1; NR_PDSCH_Config_t *pdsch_Config = current_BWP->pdsch_Config; AssertFatal(pdsch_Config == NULL || pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index d103e38731..57124286b6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -908,6 +908,7 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac, const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu, const NR_sched_pdsch_t *sched_pdsch, const NR_sched_pucch_t *pucch, + int tpc, int harq_pid, int tb_scaling, bool is_sib1) @@ -941,7 +942,7 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac, const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID; dci_payload.antenna_ports.val = sched_pdsch->dmrs_parms.dmrs_ports_id; - dci_payload.tpc = sched_ctrl->tpc1; + dci_payload.tpc = tpc; const NR_UE_harq_t *harq = &sched_ctrl->harq_processes[harq_pid]; AssertFatal(harq, "HARQ process should be available for DCI with RNTI %s\n", rnti_types(rnti_type)); dci_payload.harq_pid.val = harq_pid; @@ -3012,7 +3013,7 @@ static void init_bler_stats(const NR_bler_options_t *bler_options, NR_bler_stats * It will be typically added to the access_ue_list, but not always (e.g., * phytest mode), so this is not done in this function (and also, to allow * error handling). Remove with delete_nr_ue_data(). */ -NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup) +NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup, const nr_mac_config_t *config) { NR_UE_info_t *UE = calloc_or_fail(1, sizeof(NR_UE_info_t)); UE->uid = uid_linear_allocator_new(uia); @@ -3022,6 +3023,13 @@ NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroup NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; sched_ctrl->ta_update = 31; + nr_mac_set_target_snrx10(&sched_ctrl->pucch_pc, config->pucch.target_snrx10); + sched_ctrl->pucch_pc.avg_snr = config->pucch.target_snrx10 / 10.0f; // set initial SNR to what we would expect on average + nr_mac_set_rssi_threshold(&sched_ctrl->pucch_pc, config->pucch.rssi_threshold); + nr_mac_set_target_snrx10(&sched_ctrl->pusch_pc, config->pusch.target_snrx10); + sched_ctrl->pusch_pc.avg_snr = config->pusch.target_snrx10 / 10.0f; // set initial SNR to what we would expect on average + nr_mac_set_rssi_threshold(&sched_ctrl->pusch_pc, config->pusch.rssi_threshold); + /* Set default BWPs */ AssertFatal(UE->sc_info.n_ul_bwp <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", UE->sc_info.n_ul_bwp); @@ -3160,17 +3168,17 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti) nr_release_ra_UE(nr_mac, rnti); } -// all values passed to this function are in dB x10 -uint8_t nr_get_tpc(int target, uint8_t cqi, int incr, int tx_power) +/** + * @brief Returns the number of dBs for given transmit power control (TPC) + * command. Asserts on illegal TPC. + * @param tpc TPC command in range 0..3 + * @return The dB value expressed by the TPC command + */ +static int tpc_to_db(int tpc) { - // al values passed to this function are x10 - int snrx10 = (cqi * 5) - 640 - (tx_power * 10); - LOG_D(NR_MAC, "tpc : target %d, cqi %d, snrx10 %d, tx_power %d\n", target, ((int)cqi * 5) - 640, snrx10, tx_power); - if (snrx10 > target + incr) return 0; // decrease 1dB - if (snrx10 < target - (3*incr)) return 3; // increase 3dB - if (snrx10 < target - incr) return 2; // increase 1dB - LOG_D(NR_MAC,"tpc : target %d, snrx10 %d\n",target,snrx10); - return 1; // no change + DevAssert(tpc >= 0 && tpc <= 3); + const int db[] = {-1, 0, 1, 3}; + return db[tpc]; } /** @@ -3185,7 +3193,7 @@ uint8_t nr_get_tpc(int target, uint8_t cqi, int incr, int tx_power) * @param rssi_threshold RSSI threshold in 0.1 dBm/dBFS, range -1280 to 0 * @return The adjusted TPC command after applying the RSSI threshold check. */ -uint8_t nr_limit_tpc(int tpc, int rssi, int rssi_threshold) +static uint8_t nr_limit_tpc(int tpc, int rssi, int rssi_threshold) { if (rssi == 0xFFFF) { // RSSI not available, keep tpc @@ -3195,11 +3203,10 @@ uint8_t nr_limit_tpc(int tpc, int rssi, int rssi_threshold) const int fapi_rssi_0dBm_or_0dBFS = 1280; int rssi_fapi_threshold = fapi_rssi_0dBm_or_0dBFS + rssi_threshold; // Further limit TPC if above or near RSSI threshold - int tpc_to_db[] = {-1, 0, 1, 3}; if (rssi > rssi_fapi_threshold) { // RSSI above theshold, reduce power return 0; - } else if (rssi + tpc_to_db[tpc] * 10 > rssi_fapi_threshold) { + } else if (rssi + tpc_to_db(tpc) * 10 > rssi_fapi_threshold) { // Cannot apply required TPC, check 1 dB increment if (rssi + 10 > rssi_fapi_threshold) { // Still cannot apply required TPC, keep power @@ -4173,3 +4180,127 @@ void nr_mac_update_pdcch_closed_loop_adjust(NR_UE_sched_ctrl_t *sched_ctrl, bool sched_ctrl->pdcch_cl_adjust = max(0, sched_ctrl->pdcch_cl_adjust - 0.01); } } + +/** + * @brief Calculate the current average SNR for the given power control loop. + * @param pc the power control loop + * @return the current average SNR + */ +float nr_mac_get_snr(const nr_power_control_t *pc) +{ + return pc->avg_snr + pc->tpc_in_flight; +} + +/** + * @brief Calculates the difference of current average SNR to the target SNR + * set in the power control loop. + * @param pc the power control loop + * @return the SNR difference + */ +static float get_snr_diff(const nr_power_control_t *pc) +{ + float snr = nr_mac_get_snr(pc); + float delta = (snr * 10.0f - pc->target_snrx10) / 10.0f; + LOG_D(NR_MAC, "target %.2f snr %.2f delta %.2f\n", pc->target_snrx10 / 10.0f, snr, delta); + return delta; +} + +/// averaging constant for power control (used for SNR, RSSI). +#define PC_AVG_CNST 0.975f +/** + * @brief Enter new SNR and RSSI value for the latest UL transmission, and + * update the average SNR and average RSSI of the corresponding UE. + * + * Since the SNR is averaged, any changes through transmit power control (TPC) + * commands will take time to reflect in the averaged SNR. To allow a + * continuous tracking of necessary TPC, as well as a continuous adjustment of + * the target SNR, the algorithm keeps track of "TPC in flight" which is a + * moving average of the last TPC commands sent to the UE. The sum of the + * averaged SNR and "TPC in flight" is the actual, current SNR. The RSSI in + * turn is used to limit TPC commands (see also nr_limit_tpc()). + * + * @param pc the power control loop + * @param snrx10 the current SNR measurement multiplied by 10 + * @param rssi the current RSSI measurement + */ +void nr_mac_pc_snr(nr_power_control_t *pc, int snrx10, int rssi) +{ + pc->avg_snr = PC_AVG_CNST * pc->avg_snr + (1.0f - PC_AVG_CNST) * 0.1 * snrx10; + pc->avg_rssi = PC_AVG_CNST * pc->avg_rssi + (1.0f - PC_AVG_CNST) * rssi; + // use an EMA to average out tpc_in_flight as fast as EMA for avg_snr. + // this will ensure that on TPC change, avg_snr approximates real SNR as + // fast as tpc_in_flight returns to 0. + pc->tpc_in_flight = PC_AVG_CNST * pc->tpc_in_flight; // + (1 - PC_AVG_CNST) * 0.0f +} + +/** + * @brief Set a new target SNR for this power control loop, which can be + * updated on a continuous basis, and will be reflected immediately upon + * calls to nr_mac_get_tpc() (no "settle time" necessary). + * + * @param pc the power control loop + * @param target_snrx10 the target SNR to be set times 10. + */ +void nr_mac_set_target_snrx10(nr_power_control_t *pc, int target_snrx10) +{ + pc->target_snrx10 = target_snrx10; +} + +/** + * @brif Set a new RSSI threshold for this power control loop. Will be used to + * limit TPCs, see also nr_limit_tpc(). + * + * @param pc the power control loop + * @param rssi_threshold the RSSI threshold + */ +void nr_mac_set_rssi_threshold(nr_power_control_t *pc, int rssi_threshold) +{ + pc->rssi_threshold = rssi_threshold; +} + +/** + * @brief Signal that this power control loop experienced a discontinuous + * transmission (DTC), aka "no reception".. This will decrease the "TPC in + * flight" to artificially reduce the current SNR, which will trigger a TPC + * command to increase the power. Repeated DTX will lead to more TPC. + * + * @param pc the power control loop + */ +void nr_mac_signal_dtx(nr_power_control_t *pc) +{ + // repeated DTX will make nr_mac_get_tpc() return positive TPCs, see + // nr_mac_pc_snr(). The more DTX, the more TPC increase, which will + // eventually zero out and return back to target SNR (change is temporary). + // Also, this will only send positive TPC changes. + pc->tpc_in_flight = max(pc->tpc_in_flight - 1.0f, -5.0f); // TODO threshold good? +} + +/** + * @brief Get a transmit power control (TPC) command for the given power + * control loop according to current and target SNR. This function can be + * called continuously on every UL transmission occasion, and keeps track of + * past TPC commands. It also limits TPC commands according to RSSI, see + * nr_limit_tpc() for more information. + * + * @param pc the power control loop + * @return the next TPC command. + */ +int nr_mac_get_tpc(nr_power_control_t *pc) +{ + float diff = get_snr_diff(pc); + int tpc = 1; + if (diff <= -3.0f) { + tpc = 3; // increase 3dB + } else if (diff <= -1.0f) { + tpc = 2; // increase 1dB + } else if (diff >= 2.0f) { + tpc = 0; // decrease 1dB + } else { + tpc = 1; // within -1<=target<=2dB => is ok. + } + tpc = nr_limit_tpc(tpc, pc->avg_rssi, pc->rssi_threshold); + int change = tpc_to_db(tpc); + pc->tpc_in_flight += change; + LOG_D(NR_MAC, "tpc %d => %d\n", tpc, change); + return tpc; +} diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index 2202dfa646..48488b9099 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -885,7 +885,6 @@ static NR_UE_harq_t *find_harq(frame_t frame, slot_t slot, NR_UE_info_t * UE, in void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, const nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_01) { gNB_MAC_INST *nrmac = RC.nrmac[mod_id]; - int rssi_threshold = nrmac->pucch_rssi_threshold; NR_SCHED_LOCK(&nrmac->sched_lock); NR_UE_info_t *UE = find_nr_UE(&nrmac->UE_info, uci_01->rnti); bool is_ra = false; @@ -931,17 +930,26 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con return; } } - if (harq_confidence == 1) + if (harq_confidence == 1) { UE->mac_stats.pucch0_DTX++; + // DTX for each _bit_, but maybe once for PUCCH is enough? not sure + nr_mac_signal_dtx(&sched_ctrl->pucch_pc); + } } // tpc (power control) only if we received AckNack if (uci_01->harq.harq_confidence_level == 0 && uci_01->ul_cqi != 0xff) { - sched_ctrl->pucch_snrx10 = uci_01->ul_cqi * 5 - 640; - sched_ctrl->tpc1 = nr_get_tpc(nrmac->pucch_target_snrx10, uci_01->ul_cqi, 30, 0); - } else - sched_ctrl->tpc1 = 1; - sched_ctrl->tpc1 = nr_limit_tpc(sched_ctrl->tpc1, uci_01->rssi, rssi_threshold); + int pucch_snrx10 = uci_01->ul_cqi * 5 - 640; + nr_mac_pc_snr(&sched_ctrl->pucch_pc, pucch_snrx10, uci_01->rssi); + + T(T_GNB_MAC_PUCCH_POWER_CONTROL, + T_INT(uci_01->rnti), + T_INT(frame), + T_INT(slot), + T_INT(pucch_snrx10), + T_INT(1), + T_INT(uci_01->rssi)); + } } // check scheduling request result, confidence_level == 0 is good @@ -960,7 +968,6 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c { gNB_MAC_INST *nrmac = RC.nrmac[mod_id]; NR_SCHED_LOCK(&nrmac->sched_lock); - int rssi_threshold = nrmac->pucch_rssi_threshold; NR_UE_info_t *UE = find_nr_UE(&nrmac->UE_info, uci_234->rnti); if (!UE) { @@ -974,9 +981,16 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c // tpc (power control) // TODO PUCCH2 SNR computation is not correct -> ignore the following if (uci_234->ul_cqi != 0xff) { - sched_ctrl->pucch_snrx10 = uci_234->ul_cqi * 5 - 640; - sched_ctrl->tpc1 = nr_get_tpc(nrmac->pucch_target_snrx10, uci_234->ul_cqi, 30, 0); - sched_ctrl->tpc1 = nr_limit_tpc(sched_ctrl->tpc1, uci_234->rssi, rssi_threshold); + int pucch_snrx10 = uci_234->ul_cqi * 5 - 640; + nr_mac_pc_snr(&sched_ctrl->pucch_pc, pucch_snrx10, uci_234->rssi); + + T(T_GNB_MAC_PUCCH_POWER_CONTROL, + T_INT(uci_234->rnti), + T_INT(frame), + T_INT(slot), + T_INT(pucch_snrx10), + T_INT(1), + T_INT(uci_234->rssi)); } // TODO: handle SR diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3b913084f8..09f59775c4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -168,30 +168,25 @@ bwp_info_t get_pusch_bwp_start_size(NR_UE_info_t *UE) return bwp_info; } -static int compute_ph_factor(int mu, int tbs_bits, int rb, int n_layers, int n_symbols, int n_dmrs, long *deltaMCS, bool include_bw) +float compute_ph_rb_factor(int mu, int rb) +{ + return roundf(10 * log10(rb << mu)); +} + +float compute_ph_mcs_factor(const NR_sched_pusch_t *pusch) { // 38.213 7.1.1 // if the PUSCH transmission is over more than one layer delta_tf = 0 - float delta_tf = 0; - if(deltaMCS != NULL && n_layers == 1) { - const int n_re = (NR_NB_SC_PER_RB * n_symbols - n_dmrs) * rb; - const float BPRE = (float) tbs_bits/n_re; //TODO change for PUSCH with CSI - const float f = pow(2, BPRE * 1.25); - const float beta = 1.0f; //TODO change for PUSCH with CSI - delta_tf = (10 * log10((f - 1) * beta)); - LOG_D(NR_MAC, - "PH factor delta_tf %f (n_re %d, n_rb %d, n_dmrs %d, n_symbols %d, tbs %d BPRE %f f %f)\n", - delta_tf, - n_re, - rb, - n_dmrs, - n_symbols, - tbs_bits, - BPRE, - f); - } - const float bw_factor = (include_bw) ? 10 * log10(rb << mu) : 0; - return ((int)roundf(delta_tf + bw_factor)); + if (pusch->nrOfLayers != 1) + return 0.0f; + + const NR_pusch_dmrs_t *dmrs = &pusch->dmrs_info; + const int n_dmrs = dmrs->num_dmrs_symb * dmrs->N_PRB_DMRS; + const int n_re = (NR_NB_SC_PER_RB * pusch->tda_info.nrOfSymbols - n_dmrs) * pusch->rbSize; + const float BPRE = (float)(pusch->tb_size << 3) / n_re; // TODO change for PUSCH with CSI + const float f = pow(2, BPRE * 1.25); + const float beta = 1.0f; // TODO change for PUSCH with CSI + return 10 * log10((f - 1) * beta); } /* \brief over-estimate the BSR index, given real_index. @@ -558,17 +553,10 @@ static int nr_process_mac_pdu(instance_t module_idP, PH = 38; } // in sched_ctrl we set normalized PH wrt MCS and PRBs - long *deltaMCS = ul_bwp->pusch_Config ? ul_bwp->pusch_Config->pusch_PowerControl->deltaMCS : NULL; - sched_ctrl->ph = PH - + compute_ph_factor(ul_bwp->scs, - sched_pusch->tb_size << 3, - sched_pusch->rbSize, - sched_pusch->nrOfLayers, - sched_pusch->tda_info.nrOfSymbols, // n_symbols - sched_pusch->dmrs_info.num_dmrs_symb * sched_pusch->dmrs_info.N_PRB_DMRS, // n_dmrs - deltaMCS, - true); - sched_ctrl->ph0 = PH; + sched_ctrl->ph = PH + compute_ph_rb_factor(ul_bwp->scs, sched_pusch->rbSize); + bool hasDeltaMCS = ul_bwp->pusch_Config && ul_bwp->pusch_Config->pusch_PowerControl->deltaMCS; + if (hasDeltaMCS) + sched_ctrl->ph += compute_ph_mcs_factor(sched_pusch); /* 38.133 Table10.1.18.1-1 */ sched_ctrl->pcmax = PCMAX - 29; LOG_D(NR_MAC, @@ -796,7 +784,14 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, } } - const int target_snrx10 = mac->pusch_target_snrx10; + if (ul_cqi != 0xff) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + // Msg3: reset average. If this fails (e.g., ul_cqi == 0xff) + // everything starts from predetermined value + sched_ctrl->pusch_pc.avg_snr = 0.1 * (ul_cqi * 5 - 640); + sched_ctrl->pusch_pc.avg_rssi = rssi; + } + if (!sdu) { // NACK if (cfra) // no Msg3 on CFRA, no problem return; @@ -808,7 +803,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, return; if (ul_cqi != 0xff) - ra->msg3_TPC = nr_get_tpc(target_snrx10, ul_cqi, 30, 0); + ra->msg3_TPC = nr_mac_get_tpc(&UE->UE_sched_ctrl.pusch_pc); handle_msg3_failed_rx(mac, ra, rnti, mac->ul_bler.harq_round_max); return; @@ -849,15 +844,8 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, NR_UE_sched_ctrl_t *UE_scheduling_control = &UE->UE_sched_ctrl; DevAssert(harq_pid >= 0 && harq_pid < 8); - if (ul_cqi != 0xff) { - NR_UE_ul_harq_t *harq = &UE_scheduling_control->ul_harq_processes[harq_pid]; - UE_scheduling_control->tpc0 = nr_get_tpc(target_snrx10, ul_cqi, 30, harq->sched_pusch.phr_txpower_calc); - UE_scheduling_control->pusch_snrx10 = ul_cqi * 5 - 640 - harq->sched_pusch.phr_txpower_calc * 10; - } if (timing_advance != 0xffff) UE_scheduling_control->ta_update = timing_advance; - UE_scheduling_control->raw_rssi = rssi; - LOG_D(NR_MAC, "[UE %04x] PUSCH TPC %d and TA %d\n", UE->rnti, UE_scheduling_control->tpc0, UE_scheduling_control->ta_update); LOG_D(NR_MAC, "[RAPROC] Received %s:\n", ra->ra_type == RA_2_STEP ? "MsgA-PUSCH" : "Msg3"); for (uint32_t k = 0; k < sdu_len; k++) { @@ -945,9 +933,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, gNB_MAC_INST *gNB_mac = RC.nrmac[gnb_mod_idP]; const int current_rnti = rntiP; LOG_D(NR_MAC, "rx_sdu for rnti %04x\n", current_rnti); - const int target_snrx10 = gNB_mac->pusch_target_snrx10; - const int rssi_threshold = gNB_mac->pusch_rssi_threshold; - const int pusch_failure_thres = gNB_mac->pusch_failure_thres; + const int pusch_failure_thres = gNB_mac->radio_config.pusch.failure_thres; NR_UE_info_t *UE = find_nr_UE(&gNB_mac->UE_info, current_rnti); if (UE) { NR_UE_sched_ctrl_t *UE_scheduling_control = &UE->UE_sched_ctrl; @@ -978,40 +964,25 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, int txpower_calc = UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.phr_txpower_calc; UE->mac_stats.deltaMCS = txpower_calc; UE->mac_stats.NPRB = UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.rbSize; - if (ul_cqi != 0xff) - UE_scheduling_control->tpc0 = nr_get_tpc(target_snrx10, ul_cqi, 30, txpower_calc); - if (UE_scheduling_control->ph < 0 && UE_scheduling_control->tpc0 > 1) - UE_scheduling_control->tpc0 = 1; - UE_scheduling_control->tpc0 = nr_limit_tpc(UE_scheduling_control->tpc0, rssi, rssi_threshold); + int pusch_snrx10 = ul_cqi * 5 - 640 - txpower_calc * 10; // TODO don't log T? + if (ul_cqi != 0xff) + nr_mac_pc_snr(&UE_scheduling_control->pusch_pc, pusch_snrx10, rssi); if (timing_advance != 0xffff) UE_scheduling_control->ta_update = timing_advance; - UE_scheduling_control->raw_rssi = rssi; - UE_scheduling_control->pusch_snrx10 = ul_cqi * 5 - 640 - (txpower_calc * 10); - if (UE_scheduling_control->tpc0 > 1) - LOG_D(NR_MAC, - "[UE %04x] %d.%d. PUSCH TPC %d and TA %d pusch_snrx10 %d rssi %d phrx_tx_power %d PHR (1PRB) %d mcs %d, nb_rb %d\n", - UE->rnti, - frameP, - slotP, - UE_scheduling_control->tpc0, - UE_scheduling_control->ta_update, - UE_scheduling_control->pusch_snrx10, - UE_scheduling_control->raw_rssi, - txpower_calc, - UE_scheduling_control->ph, - UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.mcs, - UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.rbSize); - NR_UE_ul_harq_t *cur_harq = &UE_scheduling_control->ul_harq_processes[harq_pid]; - if (cur_harq->round == 0) - UE->mac_stats.pusch_snrx10 = UE_scheduling_control->pusch_snrx10; - LOG_D(NR_MAC, "[UE %04x] PUSCH TPC %d and TA %d\n",UE->rnti,UE_scheduling_control->tpc0,UE_scheduling_control->ta_update); - } - else{ - LOG_D(NR_MAC,"[UE %04x] Detected DTX : increasing UE TX power\n",UE->rnti); - UE_scheduling_control->tpc0 = 1; + const NR_sched_pusch_t *sched_pusch = &UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch; + (void) sched_pusch; // avoids warnings of unused sched_pusch when compiling without T + T(T_GNB_MAC_PUSCH_POWER_CONTROL, T_INT(rntiP), T_INT(frameP), T_INT(slotP), + T_INT(pusch_snrx10), + T_INT(UE_scheduling_control->ph), + T_INT(sched_pusch->tpc_pusch), + T_INT(sched_pusch->tb_size), + T_INT(txpower_calc), + T_INT(sched_pusch->rbSize), + T_INT(sched_pusch->mcs), + T_INT(rssi)); } #if defined(ENABLE_MAC_PAYLOAD_DEBUG) @@ -1038,6 +1009,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, if (ul_cqi == 0xff || ul_cqi <= 128) { UE->UE_sched_ctrl.pusch_consecutive_dtx_cnt++; UE->mac_stats.ulsch_DTX++; + nr_mac_signal_dtx(&UE_scheduling_control->pusch_pc); } if (!get_softmodem_params()->phy_test && UE->UE_sched_ctrl.pusch_consecutive_dtx_cnt >= pusch_failure_thres) { @@ -1584,7 +1556,7 @@ void handle_nr_srs_measurements(const module_id_t module_id, sprintf(stats->srs_stats, "UL-SNR %i dB", wide_band_snr_dB); - const int ul_prbblack_SNR_threshold = nr_mac->ul_prbblack_SNR_threshold; + const int ul_prbblack_SNR_threshold = nr_mac->radio_config.ul_prbblack_SNR_threshold; uint16_t *ulprbbl = nr_mac->ulprbbl; uint16_t num_rbs = nr_srs_bf_report.prg_size * nr_srs_bf_report.reported_symbol_list[0].num_prgs; @@ -1723,70 +1695,47 @@ static void nr_ue_max_mcs_min_rb(int mu, AssertFatal(*Rb >= minRb, "illegal Rb %d < minRb %d\n", *Rb, minRb); AssertFatal(*mcs >= 0 && *mcs <= 28, "illegal MCS %d\n", *mcs); - int tbs_bits = tbs << 3; + NR_sched_pusch_t pot = *sched_pusch; // "potential" PUSCH allocation + pot.tb_size = tbs; + pot.rbSize = *Rb; + pot.mcs = *mcs; + uint16_t R; uint8_t Qm; - update_ul_ue_R_Qm(*mcs, ul_bwp->mcs_table, ul_bwp->pusch_Config, &R, &Qm); + update_ul_ue_R_Qm(pot.mcs, ul_bwp->mcs_table, ul_bwp->pusch_Config, &R, &Qm); - long *deltaMCS = ul_bwp->pusch_Config ? ul_bwp->pusch_Config->pusch_PowerControl->deltaMCS : NULL; - tbs_bits = nr_compute_tbs(Qm, R, *Rb, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - 0, // nb_rb_oh - 0, - sched_pusch->nrOfLayers); + bool hasDeltaMCS = ul_bwp->pusch_Config && ul_bwp->pusch_Config->pusch_PowerControl->deltaMCS; + int n_dmrs = pot.dmrs_info.N_PRB_DMRS * pot.dmrs_info.num_dmrs_symb; + pot.tb_size = nr_compute_tbs(Qm, R, pot.rbSize, pot.tda_info.nrOfSymbols, n_dmrs, 0, 0, pot.nrOfLayers) >> 3; + int tx_power = compute_ph_rb_factor(mu, pot.rbSize) + (hasDeltaMCS ? compute_ph_mcs_factor(&pot) : 0); - int tx_power = compute_ph_factor(mu, - tbs_bits, - *Rb, - sched_pusch->nrOfLayers, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - deltaMCS, - true); - - while (ph_limit < tx_power && *Rb > minRb) { - (*Rb)--; - tbs_bits = nr_compute_tbs(Qm, R, *Rb, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - 0, // nb_rb_oh - 0, - sched_pusch->nrOfLayers); - tx_power = compute_ph_factor(mu, - tbs_bits, - *Rb, - sched_pusch->nrOfLayers, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - deltaMCS, - true); - LOG_D(NR_MAC, "Checking %d RBs, MCS %d, ph_limit %d, tx_power %d\n",*Rb,*mcs,ph_limit,tx_power); + // reduce first RBs till minimum RB to fulfill PHR constraint while staying + // spectrally efficient + while (ph_limit < tx_power && pot.rbSize > minRb) { + pot.rbSize--; + pot.tb_size = nr_compute_tbs(Qm, R, pot.rbSize, pot.tda_info.nrOfSymbols, n_dmrs, 0, 0, pot.nrOfLayers) >> 3; + tx_power = compute_ph_rb_factor(mu, pot.rbSize) + (hasDeltaMCS ? compute_ph_mcs_factor(&pot) : 0); + LOG_D(NR_MAC, "Checking %d RBs, MCS %d, ph_limit %d, tx_power %d\n", pot.rbSize, pot.mcs, ph_limit, tx_power); } - while (ph_limit < tx_power && *mcs > 0) { - (*mcs)--; - update_ul_ue_R_Qm(*mcs, ul_bwp->mcs_table, ul_bwp->pusch_Config, &R, &Qm); - tbs_bits = nr_compute_tbs(Qm, R, *Rb, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - 0, // nb_rb_oh - 0, - sched_pusch->nrOfLayers); - tx_power = compute_ph_factor(mu, - tbs_bits, - *Rb, - sched_pusch->nrOfLayers, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - deltaMCS, - true); - LOG_D(NR_MAC, "Checking %d RBs, MCS %d, ph_limit %d, tx_power %d\n",*Rb,*mcs,ph_limit,tx_power); + // if this was not enough, further reduce MCS till it is enough + while (ph_limit < tx_power && pot.mcs > 0) { + pot.mcs--; + update_ul_ue_R_Qm(pot.mcs, ul_bwp->mcs_table, ul_bwp->pusch_Config, &R, &Qm); + pot.tb_size = nr_compute_tbs(Qm, R, pot.rbSize, pot.tda_info.nrOfSymbols, n_dmrs, 0, 0, pot.nrOfLayers) >> 3; + tx_power = compute_ph_rb_factor(mu, pot.rbSize) + (hasDeltaMCS ? compute_ph_mcs_factor(&pot) : 0); + LOG_D(NR_MAC, "Checking %d RBs, MCS %d, ph_limit %d, tx_power %d\n", pot.rbSize, pot.mcs, ph_limit, tx_power); } if (ph_limit < tx_power) - LOG_D(NR_MAC, "Normalized power %d based on current resources (RBs %d, MCS %d) exceed reported PHR %d (normalized value)\n", - tx_power, *Rb, *mcs, ph_limit); + LOG_D(NR_MAC, + "Normalized power %d based on current resources (RBs %d, MCS %d) exceed reported PHR %d (normalized value)\n", + tx_power, + pot.rbSize, + pot.mcs, + ph_limit); + *Rb = pot.rbSize; + *mcs = pot.mcs; } static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac, @@ -2087,7 +2036,7 @@ static int pf_ul(gNB_MAC_INST *nrmac, int selected_mcs; int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format); if (bo->harq_round_max == 1) { - selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_snrx10, nrOfLayers); + selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_pc.avg_snr * 10, nrOfLayers); selected_mcs = min(max_mcs, selected_mcs); selected_mcs = max(bo->min_mcs, selected_mcs); sched_ctrl->ul_bler_stats.mcs = selected_mcs; @@ -2264,19 +2213,6 @@ static int pf_ul(gNB_MAC_INST *nrmac, >> 3; } - // Calacualte the normalized tx_power for PHR - long *deltaMCS = current_BWP->pusch_Config ? current_BWP->pusch_Config->pusch_PowerControl->deltaMCS : NULL; - int tbs_bits = sched.tb_size << 3; - - sched.phr_txpower_calc = compute_ph_factor(current_BWP->scs, - tbs_bits, - sched.rbSize, - sched.nrOfLayers, - sched.tda_info.nrOfSymbols, - sched.dmrs_info.N_PRB_DMRS * sched.dmrs_info.num_dmrs_symb, - deltaMCS, - false); - LOG_D(NR_MAC, "rbSize %d (available_rb %d), TBS %d, est buf %d, sched_ul %d, B %d, CCE %d, num_dmrs_symb %d, N_PRB_DMRS %d\n", sched.rbSize, @@ -2455,9 +2391,6 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE /* Statistics */ AssertFatal(cur_harq->round < nr_mac->ul_bler.harq_round_max, "Indexing ulsch_rounds[%d] is out of bounds\n", cur_harq->round); UE->mac_stats.ul.rounds[cur_harq->round]++; - /* Save information on MCS, TBS etc for the current initial transmission - * so we have access to it when retransmitting */ - cur_harq->sched_pusch = *sched_pusch; if (cur_harq->round == 0) { UE->mac_stats.ulsch_total_bytes_scheduled += sched_pusch->tb_size; sched_ctrl->sched_ul_bytes += sched_pusch->tb_size; @@ -2472,34 +2405,6 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE sched_ctrl->last_ul_frame = sched_pusch->frame; sched_ctrl->last_ul_slot = sched_pusch->slot; - LOG_D(NR_MAC, - "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d TDA %2d dmrs_pos %x MCS " - "Table %2d MCS %2d nrOfLayers %2d num_dmrs_cdm_grps_no_data %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched " - "%6d est BSR %6d TPC %d\n", - frame, - slot, - UE->rnti, - sched_pusch->frame, - sched_pusch->slot, - sched_ctrl->aggregation_level, - sched_pusch->rbStart, - sched_pusch->rbSize, - sched_pusch->time_domain_allocation, - sched_pusch->dmrs_info.ul_dmrs_symb_pos, - current_BWP->mcs_table, - sched_pusch->mcs, - sched_pusch->nrOfLayers, - sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data, - sched_pusch->tb_size, - harq_id, - cur_harq->round, - nr_get_rv(cur_harq->round % 4), - cur_harq->ndi, - sched_ctrl->estimated_ul_buffer, - sched_ctrl->sched_ul_bytes, - sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes, - sched_ctrl->tpc0); - T(T_GNB_MAC_UL, T_INT(UE->rnti), T_INT(frame), T_INT(slot), T_INT(sched_pusch->mcs), T_INT(sched_pusch->tb_size)); DevAssert(sched_pusch->nrOfLayers >= 1 && sched_pusch->nrOfLayers <= 8); @@ -2542,16 +2447,41 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE req->n_pdus += 1; // Calculate the normalized tx_power for PHR - long *deltaMCS = current_BWP->pusch_Config ? current_BWP->pusch_Config->pusch_PowerControl->deltaMCS : NULL; - int tbs_bits = pusch_pdu->pusch_data.tb_size << 3; - sched_pusch->phr_txpower_calc = compute_ph_factor(current_BWP->scs, - tbs_bits, - sched_pusch->rbSize, - sched_pusch->nrOfLayers, - sched_pusch->tda_info.nrOfSymbols, - sched_pusch->dmrs_info.N_PRB_DMRS * sched_pusch->dmrs_info.num_dmrs_symb, - deltaMCS, - false); + bool hasDeltaMCS = current_BWP->pusch_Config && current_BWP->pusch_Config->pusch_PowerControl->deltaMCS; + sched_pusch->phr_txpower_calc = hasDeltaMCS ? compute_ph_mcs_factor(sched_pusch) : 0; + + int tpc = nr_mac_get_tpc(&sched_ctrl->pusch_pc); + LOG_D(NR_MAC, + "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d TDA %2d dmrs_pos %x MCS " + "Table %2d MCS %2d nrOfLayers %2d num_dmrs_cdm_grps_no_data %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched " + "%6d est BSR %6d TPC %d\n", + frame, + slot, + UE->rnti, + sched_pusch->frame, + sched_pusch->slot, + sched_ctrl->aggregation_level, + sched_pusch->rbStart, + sched_pusch->rbSize, + sched_pusch->time_domain_allocation, + sched_pusch->dmrs_info.ul_dmrs_symb_pos, + current_BWP->mcs_table, + sched_pusch->mcs, + sched_pusch->nrOfLayers, + sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data, + sched_pusch->tb_size, + harq_id, + cur_harq->round, + nr_get_rv(cur_harq->round % 4), + cur_harq->ndi, + sched_ctrl->estimated_ul_buffer, + sched_ctrl->sched_ul_bytes, + sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes, + tpc); + + /* Save information on MCS, TBS etc for the current initial transmission + * so we have access to it when retransmitting */ + cur_harq->sched_pusch = *sched_pusch; /* a PDCCH PDU groups DCIs per BWP and CORESET. Save a pointer to each * allocated PDCCH so we can easily allocate UE's DCIs independent of any @@ -2607,13 +2537,12 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE &sched_ctrl->srs_feedback, tpmi, sched_pusch->time_domain_allocation, - UE->UE_sched_ctrl.tpc0, + tpc, cur_harq->ndi, current_BWP, ss->searchSpaceType->present); - // Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR - UE->UE_sched_ctrl.tpc0 = 1; + cur_harq->sched_pusch.tpc_pusch = tpc; fill_dci_pdu_rel15(&UE->sc_info, &UE->current_DL_BWP, diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 27bab01d65..da45663893 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -169,6 +169,7 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac, const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu, const NR_sched_pdsch_t *sched_pdsch, const NR_sched_pucch_t *pucch, + int tpc, int harq_pid, int tb_scaling, bool is_sib1); @@ -285,9 +286,6 @@ NR_pusch_dmrs_t get_ul_dmrs_params(const NR_ServingCellConfigCommon_t *scc, const NR_tda_info_t *tda_info, const int Layers); -uint8_t nr_get_tpc(int target, uint8_t cqi, int incr, int tx_power); -uint8_t nr_limit_tpc(int tpc, int rssi, int rssi_threshold); - int get_spf(nfapi_nr_config_request_scf_t *cfg); int to_absslot(nfapi_nr_config_request_scf_t *cfg,int frame,int slot); @@ -321,7 +319,7 @@ bool transition_ra_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE); bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE); bool nr_check_Msg4_MsgB_Ack(module_id_t module_id, frame_t frame, slot_t slot, NR_UE_info_t *UE, bool success); void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti); -NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup); +NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup, const nr_mac_config_t *config); int nr_get_default_pucch_res(int pucch_ResourceCommon); nfapi_nr_pusch_pdu_t *prepare_pusch_pdu(nfapi_nr_ul_tti_request_t *future_ul_tti_req, const NR_UE_info_t *UE, @@ -523,4 +521,12 @@ void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group); void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch); void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE_info_t *UE, NR_sched_pusch_t *sched_pusch); + +float nr_mac_get_snr(const nr_power_control_t *pc); +void nr_mac_pc_snr(nr_power_control_t *pc, int snrx10, int rssi); +void nr_mac_set_target_snrx10(nr_power_control_t *pc, int target_snrx10); +void nr_mac_set_rssi_threshold(nr_power_control_t *pc, int rssi_threshold); +void nr_mac_signal_dtx(nr_power_control_t *pc); +int nr_mac_get_tpc(nr_power_control_t *pc); + #endif /*__LAYER2_NR_MAC_PROTO_H__*/ diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index d14696db74..19fe908906 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -560,7 +560,7 @@ static NR_UE_info_t *create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id, const NR_C bool success = du_add_f1_ue_data(rnti, &new_ue_data); DevAssert(success); - NR_UE_info_t *UE = get_new_nr_ue_inst(&mac->UE_info.uid_allocator, rnti, NULL); + NR_UE_info_t *UE = get_new_nr_ue_inst(&mac->UE_info.uid_allocator, rnti, NULL, &mac->radio_config); AssertFatal(UE->uid < MAX_MOBILES_PER_GNB, "cannot create UE context, UE context setup failure not implemented\n"); NR_CellGroupConfig_t *cellGroupConfig = NULL; diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 7b32665717..f13014e408 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -197,11 +197,15 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset for (int i = 1; i < gNB->dl_bler.harq_round_max; i++) output = st_append(output, end, "/%"PRIu64, stats->dl.rounds[i]); + float pucch_snr = nr_mac_get_snr(&sched_ctrl->pucch_pc); + float pucch_snr_diff = (pucch_snr * 10.0f - sched_ctrl->pucch_pc.target_snrx10) / 10.0f; output = st_append(output, end, - ", dlsch_errors %"PRIu64", pucch0_DTX %d, BLER %.5f MCS (%d) %d CCE fail %d\n", + ", dlsch_errors %"PRIu64", pucch0_DTX %d (SNR %.1f%+.1f dB), BLER %.5f MCS (%d) %d CCE fail %d\n", stats->dl.errors, stats->pucch0_DTX, + pucch_snr, + pucch_snr_diff, sched_ctrl->dl_bler_stats.bler, UE->current_DL_BWP.mcsTableIdx, sched_ctrl->dl_bler_stats.mcs, @@ -219,9 +223,11 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset for (int i = 1; i < gNB->ul_bler.harq_round_max; i++) output = st_append(output, end, "/%"PRIu64, stats->ul.rounds[i]); + float snr = nr_mac_get_snr(&sched_ctrl->pusch_pc); + float diff_target = (snr * 10.0f - sched_ctrl->pusch_pc.target_snrx10) / 10.0f; output = st_append(output, end, - ", ulsch_errors %"PRIu64", ulsch_DTX %d, BLER %.5f MCS (%d) %d (Qm %d deltaMCS %d dB) NPRB %d SNR %d.%d dB CCE fail %d\n", + ", ulsch_errors %"PRIu64", ulsch_DTX %d, BLER %.5f MCS (%d) %d (Qm %d deltaMCS %d dB) NPRB %d SNR %.1f (%+.1f) dB CCE fail %d\n", stats->ul.errors, stats->ulsch_DTX, sched_ctrl->ul_bler_stats.bler, @@ -230,8 +236,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset nr_get_Qm_ul(sched_ctrl->ul_bler_stats.mcs,UE->current_UL_BWP.mcs_table), UE->mac_stats.deltaMCS, UE->mac_stats.NPRB, - sched_ctrl->pusch_snrx10 / 10, - sched_ctrl->pusch_snrx10 % 10, + snr, + diff_target, sched_ctrl->ul_cce_fail); output = st_append(output, end, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 022b72d119..08d2ab4d08 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -192,6 +192,15 @@ typedef struct nr_beam_table { double complex **beam_weights; } nr_beam_table_t; +typedef struct nr_power_config { + /// target SNR + int target_snrx10; + /// RSSI threshold for power control. Limits power control commands when RSSI reaches threshold. + int rssi_threshold; + /// Failure threshold (compared to consecutive PUSCH DTX) + int failure_thres; +} nr_power_config_t; + typedef struct nr_mac_config_s { nr_pdsch_AntennaPorts_t pdsch_AntennaPorts; int pusch_AntennaPorts; @@ -205,8 +214,10 @@ typedef struct nr_mac_config_s { bool use_deltaMCS; int maxMIMO_layers; bool disable_harq; - //int pusch_TargetSNRx10; - //int pucch_TargetSNRx10; + nr_power_config_t pusch; + /// SNR threshold needed to put or not a PRB in the black list + int ul_prbblack_SNR_threshold; + nr_power_config_t pucch; nr_mac_timers_t timer_config; int num_dlharq; int num_ulharq; @@ -467,6 +478,9 @@ typedef struct NR_sched_pusch { NR_pusch_dmrs_t dmrs_info; bwp_info_t bwp_info; int phr_txpower_calc; + + /// TPC command for this PUSCH + int tpc_pusch; } NR_sched_pusch_t; typedef struct NR_pdsch_dmrs { @@ -611,6 +625,14 @@ typedef struct nr_lc_config { NR_QoS_config_t qos_config[NR_MAX_NUM_QFI]; } nr_lc_config_t; +typedef struct nr_power_control { + float avg_snr; /// average SNR (in dB) + int target_snrx10; /// UE-specific target SNR x10 + float avg_rssi; /// average RSSI + int rssi_threshold; /// UE-specific RSSI threshld in 0.1dBm/dBFS, range -1280 to 0 + float tpc_in_flight; /// TPCs applied by UE but not yet in average SNR +} nr_power_control_t; + /*! \brief scheduling control information set through an API */ typedef struct { /// CCE index and aggregation, should be coherent with cce_list @@ -637,8 +659,6 @@ typedef struct { /// PHR info: power headroom level (dB) int ph; - /// PHR info: power headroom level (dB) for 1 PRB - int ph0; /// PHR info: nominal UE transmit power levels (dBm) int pcmax; @@ -652,7 +672,6 @@ typedef struct { /// total amount of data awaiting for this UE uint32_t num_total_bytes; - uint16_t dl_pdus_total; /// per-LC status data mac_rlc_status_resp_t rlc_status[NR_MAX_NUM_LCID]; @@ -663,12 +682,6 @@ typedef struct { uint16_t ta_frame; int16_t ta_update; bool ta_apply; - uint8_t tpc0; - uint8_t tpc1; - int raw_rssi; - int pusch_snrx10; - int pucch_snrx10; - uint16_t ul_rssi; int pusch_consecutive_dtx_cnt; int pucch_consecutive_dtx_cnt; bool ul_failure; @@ -711,6 +724,9 @@ typedef struct { // pdcch closed loop adjust for PDCCH aggregation level, range <0, 1> // 0 - good channel, 1 - bad channel float pdcch_cl_adjust; + + nr_power_control_t pusch_pc; + nr_power_control_t pucch_pc; } NR_UE_sched_ctrl_t; typedef struct NR_mac_dir_stats { @@ -737,7 +753,6 @@ typedef struct NR_mac_stats { int cumul_sinrx10; uint8_t num_sinr_meas; char srs_stats[50]; // Statistics may differ depending on SRS usage - int pusch_snrx10; int deltaMCS; int NPRB; } NR_mac_stats_t; @@ -929,20 +944,6 @@ typedef struct gNB_MAC_INST_s { /// Pointer to IF module instance for PHY NR_IF_Module_t *if_inst; pthread_t stats_thread; - /// Pusch target SNR - int pusch_target_snrx10; - /// RSSI threshold for power control. Limits power control commands when RSSI reaches threshold. - int pusch_rssi_threshold; - /// Pucch target SNR - int pucch_target_snrx10; - /// RSSI threshold for PUCCH power control. Limits power control commands when RSSI reaches threshold. - int pucch_rssi_threshold; - /// SNR threshold needed to put or not a PRB in the black list - int ul_prbblack_SNR_threshold; - /// PUCCH Failure threshold (compared to consecutive PUCCH DTX) - int pucch_failure_thres; - /// PUSCH Failure threshold (compared to consecutive PUSCH DTX) - int pusch_failure_thres; /// Subcarrier Offset int ssb_SubcarrierOffset; int ssb_OffsetPointA; diff --git a/tools/plots/README.md b/tools/plots/README.md index 39f9fab6f1..a04043d097 100644 --- a/tools/plots/README.md +++ b/tools/plots/README.md @@ -32,3 +32,51 @@ The plots are saved in three formats: `.png`, `.pdf` and `.svg`. ### Example graphs: #### BER ![image](./ber_compare.svg) + + +## `plot_power_control.sh` + +This plots the relevant graphs for power control at the gNB, exported through T +and recorded via the [`csv` tracer](../../common/utils/T/DOC/T/csv.md) and +plotted with `gnuplot`. + +Run first the `csv` tracer in one terminal, selecting the desired quantities, and +write them out to a file: + + ./common/utils/T/tracer/csv -d ../common/utils/T/T_messages.txt -s $'\t' -t time GNB_MAC_PUSCH_POWER_CONTROL time snrx10 phr tpc tb_size txpower_calc rbSize mcs > /tmp/pusch.csv + +in a second terminal, run the gNB with desired configuration, and pass the +`--T_stdout 2` option to connect to the `csv` tracer: + + sudo ./nr-softmodem -O --T_stdout 2 + +Note that you could also run both in one window, connecting both commands with +a `&`. In that case, it's important to run the `nr-softmodem` at second +position, which will receive signals and stop the `csv` tracer when stopping +the main process. + +The script might also be used to print PUCCH SNR. This can be achieved by only +recording the relevant fields with the `csv` tracer for trace +`GNB_MAC_PUCCH_POWER_CONTROL + + ./common/utils/T/tracer/record -d ../common/utils/T/T_messages.txt -OFF -on GNB_MAC_PUSCH_POWER_CONTROL -on GNB_MAC_PUCCH_POWER_CONTROL -o /tmp/record.raw + +Use the `replay` tracer and `csv` to separate into PUSCH and PUCCH traces. + +After recovering the `pusch.csv` file, you could plot it with a script. + + ./plot-power-control.gp.sh /tmp/pusch.csv + +The script prints four graphs: + +1. prints instantaneous SNR, and "deltaMCS" (the power used for the + transmission if in deltaMCS mode that accounts for MCS). It also prints an + average SNR, but this has to be manually post-processed from the instanteous + SNR values (`snrx10` above) +2. TPC commands and PHR of the UE +3. Resource blocks used for all transmissions, and the MCS +4. transport block size of the transmission, to estimate the amount of traffic. + +The script should either auto-scale graphs or have sensible defaults. It prints +on screen. There is code to print to a file; uncomment the relevant lines to +achieve this. diff --git a/tools/plots/plot-power-control.gp.sh b/tools/plots/plot-power-control.gp.sh new file mode 100755 index 0000000000..33329ad212 --- /dev/null +++ b/tools/plots/plot-power-control.gp.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +function die() { echo "$@"; exit 1; } + +[[ $# == 1 ]] || die "usage: $0 " + +FILE=${1} + +#OUTPUT=/tmp/results.png +#echo "dumping to $OUTPUT" +gnuplot << EOF + +#set terminal pngcairo size 2000,2000 enhanced font 'Verdana,10' +#set output "$OUTPUT" + +set format x '%H:%M:%S' +set timefmt '%H:%M:%S' +set datafile separator "\t" +set xdata time +#set xrange ['08:43:17':'08:43:18'] +set grid + +set multiplot layout 4,1 + +set yrange [0:300] +set ylabel "dB" +plot "$FILE" using 1:2 w l title "SNRx10 (inst)", "$FILE" using 1:9 w l title "SNRx10 (smooth)", "$FILE" using 1:6 w l title "txpower-calc" + +set yrange [5:50] +set ylabel "PHR (dB)" +set y2range [0:3] +set y2label "TPC" +set boxwidth 0.1 +plot "$FILE" using 1:4 w points pt 7 axes x1y2 title "TPC", "$FILE" using 1:3 w l title "PHR" + +set yrange [0:60] +set ylabel "RB" +set y2range [0:27] +set y2label "MCS" +plot "$FILE" using 1:7 w l title "RB", "$FILE" using 1:8 w l axes x1y2 title "MCS" + +set yrange [0:1000] +set ylabel "TB size (B)" +plot "$FILE" using 1:5 w l + +unset multiplot + +pause mouse close +EOF