Merge antenna muting mechanism onto 2026.w17
Brings the O-RAN compliant antenna muting feature (originally introduced
on the 'new' branch) onto the latest upstream 2026.w17 tag.
Files added:
openair1/PHY/NR_TRANSPORT/nr_antenna_state.{c,h}
openair1/PHY/NR_TRANSPORT/nr_antenna_control.{c,h}
Files modified (integration hooks):
CMakeLists.txt, executables/nr-gnb.c
openair1/PHY/INIT/nr_init.c, openair1/PHY/defs_gNB.h
openair1/PHY/MODULATION/{beamforming.c,nr_modulation.h,nr_beamforming.c}
openair1/SCHED_NR/nr_ru_procedures.c
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_{primitives,dlsch,uci}.c
Conflicts resolved:
- defs_gNB.h: kept upstream's new headers + appended antenna_state.h
- nr_init.c: dropped reference to upstream-removed nr_srs_info init,
kept nr_init_antenna_state() call
- beamforming.c: upstream moved nr_beam_precoding into new file
nr_beamforming.c; nr_beam_precoding_with_muting added there with
upstream's signature (no slot param)
- nr_modulation.h: aligned _with_muting decl to upstream signature
- nr_ru_procedures.c: muting check applied around upstream's new
fft_shift in nr_feptx, and around per-port memcpy in nr_feptx_prec
- gNB_scheduler_uci.c: SINR-based MCS hint uses get_dl_nrOfLayers(NULL,
sched_ctrl, ...) since nrmac is not in scope at the call site
- mac_proto.h: get_dl_nrOfLayers signature updated to take gNB_MAC_INST*
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -420,7 +420,7 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
|
||||
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
|
||||
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
||||
NR_sched_pdsch_t new_sched = sched_ctrl->harq_processes[current_harq_pid].sched_pdsch;
|
||||
int layers = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
|
||||
int layers = get_dl_nrOfLayers(nr_mac, sched_ctrl, dl_bwp->dci_format);
|
||||
int pm_index = get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP);
|
||||
|
||||
// If the RI changed between current rtx and a previous transmission
|
||||
@@ -565,7 +565,6 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
|
||||
static void ack_reconfig(gNB_MAC_INST *mac, NR_UE_info_t *UE)
|
||||
{
|
||||
if (!UE->reconfigCellGroup) {
|
||||
LOG_W(NR_MAC, "Received ACK for RRCReconfiguration, but nothing to apply!\n");
|
||||
return;
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
|
||||
@@ -700,7 +699,7 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
sched_ctrl->dl_bler_stats.mcs = selected_mcs;
|
||||
} else
|
||||
selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame);
|
||||
int l = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format);
|
||||
int l = get_dl_nrOfLayers(mac, sched_ctrl, current_BWP->dci_format);
|
||||
const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcsTableIdx);
|
||||
const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcsTableIdx);
|
||||
uint32_t tbs = nr_compute_tbs(Qm,
|
||||
@@ -837,7 +836,7 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
sched_ctrl->cce_index = CCEIndex;
|
||||
fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam.idx);
|
||||
|
||||
int l = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
|
||||
int l = get_dl_nrOfLayers(mac, sched_ctrl, dl_bwp->dci_format);
|
||||
NR_sched_pdsch_t sched_pdsch = {
|
||||
// rbSize below
|
||||
.rbStart = rbStart,
|
||||
|
||||
@@ -101,14 +101,13 @@ int get_ssbidx_from_beam(gNB_MAC_INST *mac, int beam_idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
|
||||
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
|
||||
{
|
||||
// TODO check this but it should be enough for now
|
||||
// if there is not csi report activated RI is 0 from initialization
|
||||
if(dci_format == NR_DL_DCI_FORMAT_1_0)
|
||||
return 1;
|
||||
else
|
||||
return sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
|
||||
int layers = (dci_format == NR_DL_DCI_FORMAT_1_0) ? 1
|
||||
: sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
|
||||
if (mac && mac->antenna_state)
|
||||
return nr_get_effective_mimo_layers(mac->antenna_state, layers);
|
||||
return layers;
|
||||
}
|
||||
|
||||
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
|
||||
@@ -165,63 +164,88 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
|
||||
int layers,
|
||||
int xp_pdsch_antenna_ports)
|
||||
{
|
||||
if (dci_format == NR_DL_DCI_FORMAT_1_0 || nrmac->identity_pm || xp_pdsch_antenna_ports == 1)
|
||||
return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas)
|
||||
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
|
||||
const nr_csi_report_t *csi_report = &UE->csi_report_template[report_id];
|
||||
const int N1 = csi_report->N1;
|
||||
const int N2 = csi_report->N2;
|
||||
const int antenna_ports = (N1 * N2) << 1;
|
||||
if (antenna_ports < 2)
|
||||
return 0; // single antenna port
|
||||
if (dci_format == NR_DL_DCI_FORMAT_1_0 || nrmac->identity_pm || xp_pdsch_antenna_ports == 1)
|
||||
return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas)
|
||||
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
|
||||
int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1;
|
||||
const int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2;
|
||||
LOG_D(NR_MAC,"PMI report: x1 %d x2 %d layers: %d\n", x1, x2, layers);
|
||||
|
||||
int prev_layers_size = 0;
|
||||
for (int i = 1; i < layers; i++)
|
||||
prev_layers_size += nrmac->precoding_matrix_size[i - 1];
|
||||
|
||||
// need to return PM index to matrix initialized in init_DL_MIMO_codebook
|
||||
// index 0 is for identity matrix
|
||||
// order of matrices depends on layers to be transmitted
|
||||
// elements from 1 to n for 1 layer
|
||||
// elements from n+1 to m for 2 layers etc.
|
||||
if (antenna_ports == 2)
|
||||
return 1 + prev_layers_size + x2; // 0 for identity matrix
|
||||
else {
|
||||
int idx = layers - 1;
|
||||
// the order of i1x in X1 report needs to be verified
|
||||
// the standard is not very clear (Table 6.3.1.1.2-7 in 38.212)
|
||||
// it says: PMI wideband information fields X1 , from left to right
|
||||
int bitlen = csi_report->csi_meas_bitlen.pmi_i13_bitlen[idx];
|
||||
if (layers == 1 && bitlen != 0) {
|
||||
LOG_E(NR_MAC, "Invalid i13 bit length %d for single layer! It should be 0\n", bitlen);
|
||||
return 0;
|
||||
int reported_ri_layers = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
|
||||
if (layers != reported_ri_layers) {
|
||||
LOG_D(NR_MAC, "PMI mismatch: using %d layers but RI report is for %d layers, using identity matrix\n",
|
||||
layers, reported_ri_layers);
|
||||
return 0;
|
||||
}
|
||||
const int i13 = x1 & ((1 << bitlen) - 1);
|
||||
x1 >>= bitlen;
|
||||
bitlen = csi_report->csi_meas_bitlen.pmi_i12_bitlen[idx];
|
||||
const int i12 = x1 & ((1 << bitlen) - 1);
|
||||
x1 >>= bitlen;
|
||||
bitlen = csi_report->csi_meas_bitlen.pmi_i11_bitlen[idx];
|
||||
const int i11 = x1 & ((1 << bitlen) - 1);
|
||||
const int i2 = x2;
|
||||
int k1, k2;
|
||||
get_k1_k2_indices(layers, N1, N2, i13, &k1, &k2); // get indices k1 and k2 for PHY matrix (not actual k1 and k2 values)
|
||||
const int O2 = N2 == 1 ? 1 : 4;
|
||||
int O1 = 4; //Horizontal beam oversampling = 4 for more than 2 antenna ports
|
||||
int max_i2 = 0;
|
||||
int lay_index = 0;
|
||||
max_i2 = layers == 1 ? 4 : 2;
|
||||
int K1, K2;
|
||||
get_K1_K2(N1, N2, &K1, &K2, layers);
|
||||
// computing precoding matrix index according to rule set in allocation function init_codebook_gNB
|
||||
lay_index = i2 + (i11 * max_i2) + (i12 * max_i2 * N1 * O1) + (k1 * max_i2 * N1 * O1 * N2 * O2) + (k2 * max_i2 * N1 * O1 * N2 * O2 * K1);
|
||||
return 1 + prev_layers_size + lay_index;
|
||||
}
|
||||
nr_antenna_state_t* ant_state = nrmac->antenna_state;
|
||||
if (ant_state) {
|
||||
if (nr_is_in_recovery_mode(ant_state)) {
|
||||
LOG_D(NR_MAC, "[ANTENNA_MUTE] Recovery mode active, using identity matrix\n");
|
||||
return 0;
|
||||
}
|
||||
// Don't fall back to identity matrix for muted antennas.
|
||||
// Antenna muting is handled at RU level (nr_feptx zeros muted antenna buffers).
|
||||
// Using proper precoding ensures data reaches all active antennas.
|
||||
}
|
||||
|
||||
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
|
||||
const nr_csi_report_t *csi_report = &UE->csi_report_template[report_id];
|
||||
const int N1 = csi_report->N1;
|
||||
const int N2 = csi_report->N2;
|
||||
const int antenna_ports = (N1 * N2) << 1;
|
||||
if (antenna_ports < 2) return 0; // single antenna port
|
||||
|
||||
int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1;
|
||||
const int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2;
|
||||
LOG_D(NR_MAC,"PMI report: x1 %d x2 %d layers: %d\n", x1, x2, layers);
|
||||
|
||||
int prev_layers_size = 0;
|
||||
for (int i = 1; i < layers; i++)
|
||||
prev_layers_size += nrmac->precoding_matrix_size[i - 1];
|
||||
|
||||
// need to return PM index to matrix initialized in init_DL_MIMO_codebook
|
||||
// index 0 is for identity matrix
|
||||
// order of matrices depends on layers to be transmitted
|
||||
// elements from 1 to n for 1 layer
|
||||
// elements from n+1 to m for 2 layers etc.
|
||||
int pm_index;
|
||||
if (antenna_ports == 2)
|
||||
pm_index = 1 + prev_layers_size + x2; // 0 for identity matrix
|
||||
else {
|
||||
int idx = layers - 1;
|
||||
// the order of i1x in X1 report needs to be verified
|
||||
// the standard is not very clear (Table 6.3.1.1.2-7 in 38.212)
|
||||
// it says: PMI wideband information fields X1 , from left to right
|
||||
int bitlen = csi_report->csi_meas_bitlen.pmi_i13_bitlen[idx];
|
||||
if (layers == 1 && bitlen != 0) {
|
||||
LOG_E(NR_MAC, "Invalid i13 bit length %d for single layer! It should be 0\n", bitlen);
|
||||
return 0;
|
||||
}
|
||||
const int i13 = x1 & ((1 << bitlen) - 1);
|
||||
x1 >>= bitlen;
|
||||
bitlen = csi_report->csi_meas_bitlen.pmi_i12_bitlen[idx];
|
||||
const int i12 = x1 & ((1 << bitlen) - 1);
|
||||
x1 >>= bitlen;
|
||||
bitlen = csi_report->csi_meas_bitlen.pmi_i11_bitlen[idx];
|
||||
const int i11 = x1 & ((1 << bitlen) - 1);
|
||||
const int i2 = x2;
|
||||
int k1, k2;
|
||||
get_k1_k2_indices(layers, N1, N2, i13, &k1, &k2); // get indices k1 and k2 for PHY matrix (not actual k1 and k2 values)
|
||||
const int O2 = N2 == 1 ? 1 : 4;
|
||||
int O1 = 4; //Horizontal beam oversampling = 4 for more than 2 antenna ports
|
||||
int max_i2 = 0;
|
||||
int lay_index = 0;
|
||||
max_i2 = layers == 1 ? 4 : 2;
|
||||
int K1, K2;
|
||||
get_K1_K2(N1, N2, &K1, &K2, layers);
|
||||
// computing precoding matrix index according to rule set in allocation function init_codebook_gNB
|
||||
lay_index = i2 + (i11 * max_i2) + (i12 * max_i2 * N1 * O1) + (k1 * max_i2 * N1 * O1 * N2 * O2) + (k2 * max_i2 * N1 * O1 * N2 * O2 * K1);
|
||||
pm_index = 1 + prev_layers_size + lay_index;
|
||||
}
|
||||
int max_valid_pm = prev_layers_size + nrmac->precoding_matrix_size[layers - 1];
|
||||
if (pm_index < 1 || pm_index > max_valid_pm) {
|
||||
LOG_W(NR_MAC, "PM index %d out of range [1, %d] for %d layers, using identity matrix\n",
|
||||
pm_index, max_valid_pm, layers);
|
||||
return 0;
|
||||
}
|
||||
return pm_index;
|
||||
}
|
||||
|
||||
// look-up table for AMC. Based on BLER vs SNR curves from the nr_dlsim simulation
|
||||
|
||||
@@ -497,7 +497,9 @@ static void evaluate_sinr_report(NR_UE_info_t *UE,
|
||||
stats->num_sinr_meas++;
|
||||
|
||||
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
|
||||
const int nrOfLayers = get_dl_nrOfLayers(sched_ctrl, UE->current_DL_BWP.dci_format);
|
||||
/* NULL mac arg: SINR-based MCS estimation skips antenna-state rank cap
|
||||
(caller doesn't have nrmac in scope; this is only for max-MCS hint). */
|
||||
const int nrOfLayers = get_dl_nrOfLayers(NULL, sched_ctrl, UE->current_DL_BWP.dci_format);
|
||||
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers);
|
||||
|
||||
LOG_D(MAC, "Reported SSB-SINR = %01f, dl_max_mcs %d\n", sinr_report->r[0].SINRx10 / 10.0, sched_ctrl->dl_max_mcs);
|
||||
|
||||
@@ -373,7 +373,7 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
|
||||
int get_mcs_from_SINRx10(int mcs_table, int SINRx10, int Nl);
|
||||
uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx);
|
||||
|
||||
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
|
||||
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
|
||||
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
|
||||
|
||||
void free_sched_pucch_list(NR_UE_sched_ctrl_t *sched_ctrl);
|
||||
|
||||
Reference in New Issue
Block a user