Commit 4472b9d8 authored by Jaroslava Fiedlerova's avatar Jaroslava Fiedlerova

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.
parents 6a725d6e 1428aeac
......@@ -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},
};
......
......@@ -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
......
......@@ -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
![image](./ber_compare.svg)
## `plot_power_control.sh`
This plots the relevant graphs for power control at the gNB, exported through T
and recorded via the [`csv` tracer](../../common/utils/T/DOC/T/csv.md) and
plotted with `gnuplot`.
Run first the `csv` tracer in one terminal, selecting the desired quantities, and
write them out to a file:
./common/utils/T/tracer/csv -d ../common/utils/T/T_messages.txt -s $'\t' -t time GNB_MAC_PUSCH_POWER_CONTROL time snrx10 phr tpc tb_size txpower_calc rbSize mcs > /tmp/pusch.csv
in a second terminal, run the gNB with desired configuration, and pass the
`--T_stdout 2` option to connect to the `csv` tracer:
sudo ./nr-softmodem -O <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.
#!/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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment