Compare commits

...

1 Commits

Author SHA1 Message Date
Gabriele Gemmi
d3fc66b826 NR MAC: enable MIMO beamforming with multiple CSI-RS ports
Add beam_id_polarization_offset config parameter to assign one beam index
per antenna port in CSI-RS and PDSCH scheduling, enabling MIMO operation
with dual-polarization and larger port configurations.
2026-04-06 16:57:51 +00:00
6 changed files with 49 additions and 7 deletions

View File

@@ -51,10 +51,12 @@
#define MACRLC_BEAMS_PERIOD "beams_per_period"
#define MACRLC_BEAM_WEIGHTS_LIST "beam_weights"
#define MACRLC_DBT_FILE "dbt_file"
#define MACRLC_BEAM_POL_OFFSET "beam_id_polarization_offset"
#define MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold"
#define MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold"
#define MACRLC_STATS_MAX_UE "stats_max_ue"
#define HLP_MACRLC_UL_PRBBLACK "SNR threshold to decide whether a PRB will be blacklisted or not"
#define HLP_MACRLC_DL_BLER_UP "Upper threshold of BLER to decrease DL MCS"
#define HLP_MACRLC_DL_BLER_LO "Lower threshold of BLER to increase DL MCS"
@@ -72,6 +74,7 @@
#define HLP_MACRLC_BEAM_DURATION "number of consecutive slots for a given set of beams"
#define HLP_MACRLC_BEAMS_PERIOD "set of beams that can be simultaneously allocated in a period"
#define HLP_MACRLC_DBT_FILE "File path to CSV file to read digital beamforming table"
#define HLP_MACRLC_BEAM_POL_OFFSET "beamID offset for dual polarization (0=disable, 1=consecutive IDs)"
#define HLP_MACRLC_PUSCH_RSSI_THRESHOLD "Limits PUSCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
#define HLP_MACRLC_PUCCH_RSSI_THRESHOLD "Limits PUCCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
#define HLP_MACRLC_STATS_MAX_UE "Maximum number of UEs before disabling periodical output (0 to disable)"
@@ -118,6 +121,7 @@
{MACRLC_BEAMS_PERIOD, HLP_MACRLC_BEAMS_PERIOD, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \
{MACRLC_BEAM_WEIGHTS_LIST, NULL, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY,0}, \
{MACRLC_DBT_FILE, HLP_MACRLC_DBT_FILE, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRING, 0}, \
{MACRLC_BEAM_POL_OFFSET, HLP_MACRLC_BEAM_POL_OFFSET, 0, .u8ptr=NULL, .defintval=0, TYPE_UINT8, 0}, \
{MACRLC_PUSCH_RSSI_THRESHOLD, HLP_MACRLC_PUSCH_RSSI_THRESHOLD, \
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \
@@ -166,6 +170,7 @@
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, /* beam_id_polarization_offset */ \
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \
{ .s5 = { NULL } }, \

View File

@@ -1747,6 +1747,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
beam_info->beam_allocation = malloc16(beams_per_period * sizeof(beam_info->beam_allocation));
beam_info->beam_duration = *gpd(params, np, MACRLC_BEAM_DURATION)->u8ptr;
beam_info->beams_per_period = beams_per_period;
beam_info->beam_id_polarization_offset = *gpd(params, np, MACRLC_BEAM_POL_OFFSET)->u8ptr;
beam_info->beam_allocation_size = -1; // to be initialized once we have information on frame configuration
}
bool das_enabled = false;

View File

@@ -992,9 +992,21 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu
// Precoding and beamforming
pdsch_pdu->precodingAndBeamforming.num_prgs = 1;
pdsch_pdu->precodingAndBeamforming.prg_size = pdsch_pdu->rbSize;
pdsch_pdu->precodingAndBeamforming.dig_bf_interfaces = 1;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].pm_idx = sched_pdsch->pm_index;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = beam_index;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].pm_idx = sched_pdsch->pm_index;
const int pol_offset = mac->beam_info.beam_id_polarization_offset;
const int numPorts = mac->radio_config.pdsch_AntennaPorts.XP * mac->radio_config.pdsch_AntennaPorts.N1 * mac->radio_config.pdsch_AntennaPorts.N2;
if (numPorts > 1 && pol_offset > 0) {
pdsch_pdu->precodingAndBeamforming.dig_bf_interfaces = numPorts;
for (int port = 0; port < numPorts; port++){
pdsch_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[port].beam_idx = beam_index + 1 + (port * pol_offset);
}
}
else {
pdsch_pdu->precodingAndBeamforming.dig_bf_interfaces = 1;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = beam_index;
}
return pdsch_pdu;
}

View File

@@ -3287,10 +3287,27 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt
nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csirs_pdu_rel15 = &dl_tti_csirs_pdu->csi_rs_pdu.csi_rs_pdu_rel15;
csirs_pdu_rel15->precodingAndBeamforming.num_prgs = 1;
csirs_pdu_rel15->precodingAndBeamforming.prg_size = resourceMapping.freqBand.nrofRBs; //1 PRG of max size
csirs_pdu_rel15->precodingAndBeamforming.dig_bf_interfaces = 1;
csirs_pdu_rel15->precodingAndBeamforming.prgs_list[0].pm_idx = 0;
const uint16_t fapi_beam = convert_to_fapi_beam(UE->UE_beam_index, gNB_mac->beam_info.beam_mode);
csirs_pdu_rel15->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = fapi_beam;
uint16_t fapi_beam = convert_to_fapi_beam(UE->UE_beam_index, gNB_mac->beam_info.beam_mode);
const int nrofPorts_to_num[] = {1, 2, 4, 8, 12, 16, 24, 32};
const int numPorts = nrofPorts_to_num[resourceMapping.nrofPorts];
int pol_offset = gNB_mac->beam_info.beam_id_polarization_offset;
if (numPorts > 1 && pol_offset > 0) {
csirs_pdu_rel15->precodingAndBeamforming.dig_bf_interfaces = numPorts;
for (int port = 0; port < numPorts; port++) {
// For SISO Beam with ID n, [n+p for p in numPorts] is the set of MIMO beams associated to it
// MIMO beams can be spliting the array in subarrays, use different polarizations, etc
int new_beam = UE->UE_beam_index + 1 + (port * pol_offset);
fapi_beam = convert_to_fapi_beam(new_beam, gNB_mac->beam_info.beam_mode);
csirs_pdu_rel15->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[port].beam_idx = fapi_beam;
}
} else {
csirs_pdu_rel15->precodingAndBeamforming.dig_bf_interfaces = 1;
csirs_pdu_rel15->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = fapi_beam;
}
csirs_pdu_rel15->bwp_size = dl_bwp->BWPSize;
csirs_pdu_rel15->bwp_start = dl_bwp->BWPStart;
csirs_pdu_rel15->subcarrier_spacing = dl_bwp->scs;

View File

@@ -118,7 +118,12 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
const int avg_rsrp = stats->num_rsrp_meas > 0 ? stats->cumul_rsrp / stats->num_rsrp_meas : 0;
const int avg_sinrx10 = stats->num_sinr_meas > 0 ? stats->cumul_sinrx10 / stats->num_sinr_meas : 0;
output = st_append(output, end, "UE RNTI %04x CU-UE-ID ", UE->rnti);
//Display beam information when beamforming is active
if (gNB->beam_info.beam_mode != NO_BEAM_MODE) {
output = st_append(output, end, "UE RNTI %04x BeamId %d CU-UE-ID ", UE->rnti, UE->UE_beam_index);
} else {
output = st_append(output, end, "UE RNTI %04x CU-UE-ID ", UE->rnti);
}
if (du_exists_f1_ue_data(UE->rnti)) {
f1_ue_data_t ued = du_get_f1_ue_data(UE->rnti);
output = st_append(output, end, "%d", ued.secondary_ue);

View File

@@ -819,6 +819,8 @@ typedef struct {
int beams_per_period;
int beam_allocation_size;
nr_beam_mode_t beam_mode;
/// beamID offset for dual polarization (0=disabled, typical value is 1)
int beam_id_polarization_offset;
} NR_beam_info_t;
#define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr=BaSe, *VaR=*VaR##pptr; VaR; VaR=*(++VaR##pptr))