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

+
+
+## `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