Merge remote-tracking branch 'origin/fix-delta-mcs' into integration_2026_w13 (!3912)
Fix Delta MCS mode, reimplement UL power control This change set fixes the "delta-MCS" mode, and reimplements the UL power control loops at gNB, i.e., for PUSCH and PUCCH. It further adds new T traces to log PUSCH/PUCCH power infrmation, and cleans up code. The "delta-MCS" mode in OAI refers to configuring a UE with deltaMCS in the PUSCH-PowerControl IE (see TS 38.331). In this mode, the UE adjusts the PUSCH power for transmissions to take into account for MCS (see TS 38.213 Sec. 7.1). In other words, the target SNR can be set to a somewhat low value, and the UE will still use the right power for UL transmissions. Note that the spec only foresees this adjustment of UL power when using one layer. In contrast, in the "normal" mode, the gNB sends TPC commands to keep a UE at a fixed target SNR (default: 20dB), regardless of the MCS or number of layers. Future work is planned to completely remove this fixed target SNR, which is not done in this MR yet. Further, there is a new power control loop implementation. Prior to this MR, on each UL transmissions, the gNB uses the SNR to decide about TPC sent to a UE in the next transmissions. The new implementation relies on averaging the SNR, and a "TPC in flight" average to account for the slow reaction of the average SNR. The sum of both quantities is the current SNR that is used by the UE. See the commit messages or documentation for more information. Further cleanup to group data (e.g., power control configuration, refactoring of code to save space, ...) is done. Two new T traces GNB_MAC_PUSCH_POWER_CONTROL and GNB_MAC_PUCCH_POWER_CONTROL are added; documentation exists to explain how to graphically plot corresponding graphs.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
<node>matix</node>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz</yaml_path>
|
||||
<analysis>
|
||||
<services>oai-gnb=RetxCheck=10,100,100,100 oai-gnb=EndsWithBye</services>
|
||||
<services>oai-gnb=RetxCheck=dl=10,100,100,100;ul=25,100,100,100 oai-gnb=EndsWithBye</services>
|
||||
</analysis>
|
||||
</testCase>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
+45
-9
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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__*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <config> --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.
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
function die() { echo "$@"; exit 1; }
|
||||
|
||||
[[ $# == 1 ]] || die "usage: $0 <filename>"
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user