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:
Jaroslava Fiedlerova
2026-03-26 13:57:27 +01:00
23 changed files with 546 additions and 300 deletions
@@ -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;
+19 -16
View File
@@ -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
+1 -1
View File
@@ -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;
}
+25 -11
View File
@@ -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
+116 -187
View File
@@ -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,
+10 -4
View File
@@ -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;
+10 -4
View File
@@ -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,
+27 -26
View File
@@ -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;