diff --git a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c index 56e874d206..1c5a3df415 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c +++ b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c @@ -213,12 +213,19 @@ void nr_unmute_rx_antenna(nr_antenna_state_t *state, uint8_t ant_id) int nr_get_effective_mimo_layers(const nr_antenna_state_t *state, int requested_layers) { - // Don't reduce MIMO layers based on muted antennas. - // Antenna muting happens at the RU level (in nr_feptx) after precoding, - // so we keep using full layer count to maintain proper precoding matrices. - // This avoids the identity matrix fallback which doesn't account for - // which specific antennas are muted. - (void)state; // unused - muting is handled at RU level + if (state == NULL) + return requested_layers; + + // Reduce MIMO layers to match the number of active TX antennas. + // When antennas are muted, the RU blanks the corresponding TX buffers. + // If the scheduler still uses full layer count, the precoded signal is + // distributed across all antenna ports (including muted ones), which + // corrupts the UE's MIMO reception. By reducing layers to match active + // antennas, the PMI fallback to identity precoding maps layers 1:1 to + // active ports, avoiding signal loss on muted ports. + int active = nr_get_active_tx_antenna_count(state); + if (active > 0 && active < requested_layers) + return active; return requested_layers; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c b/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c index 99ef532cf3..c6524630f9 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c +++ b/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c @@ -145,6 +145,7 @@ static void nr_hermitian_eigenvalues(const int N, float A_re[4][4], float A_im[4 // Multiply column q by e^{-j*phi} and row q by e^{+j*phi} for (int k = 0; k < N; k++) { + if (k == q) continue; float old_re = A_re[k][q], old_im = A_im[k][q]; A_re[k][q] = old_re * cos_phi + old_im * sin_phi; A_im[k][q] = -old_re * sin_phi + old_im * cos_phi; @@ -201,7 +202,7 @@ static void nr_hermitian_eigenvalues(const int N, float A_re[4][4], float A_im[4 } for (int i = 0; i < N; i++) - eigenvalues[i] = A_re[i][i]; + eigenvalues[i] = A_re[i][i] > 0.0f ? A_re[i][i] : 0.0f; // Sort descending for (int i = 0; i < N - 1; i++) @@ -651,14 +652,24 @@ int nr_csi_rs_ri_estimation(const PHY_VARS_NR_UE *ue, } } } else { - // ---- General path (N_ports > 2): eigenvalue-based rank estimation ---- + // ---- General path (N_ports > 2): float Gram matrix + eigenvalue-based rank estimation ---- + // Compute Gram matrix directly in float from channel estimates to avoid int16 saturation for (int sc_idx = 0; sc_idx < NR_NB_SC_PER_RB; sc_idx++) { float A_re[4][4] = {{0}}, A_im[4][4] = {{0}}; - for (int i = 0; i < N_ports; i++) { - for (int j = 0; j < N_ports; j++) { - c16_t val = *((c16_t *)&csi_rs_estimated_A_MF[i][j][k_offset + sc_idx]); - A_re[i][j] = (float)val.r; - A_im[i][j] = (float)val.i; + // H^H * H: A[p1][p2] = sum_rx conj(H[rx][p1]) * H[rx][p2] + for (int rx = 0; rx < frame_parms->nb_antennas_rx; rx++) { + for (int p1 = 0; p1 < N_ports; p1++) { + c16_t h1 = csi_rs_estimated_channel_freq[rx][p1][k_offset + sc_idx]; + float h1_re = (float)h1.r; + float h1_im = (float)h1.i; + for (int p2 = 0; p2 < N_ports; p2++) { + c16_t h2 = csi_rs_estimated_channel_freq[rx][p2][k_offset + sc_idx]; + float h2_re = (float)h2.r; + float h2_im = (float)h2.i; + // conj(h1) * h2 = (h1_re - j*h1_im) * (h2_re + j*h2_im) + A_re[p1][p2] += h1_re * h2_re + h1_im * h2_im; + A_im[p1][p2] += -h1_im * h2_re + h1_re * h2_im; + } } } @@ -970,8 +981,32 @@ int nr_csi_rs_pmi_estimation(const PHY_VARS_NR_UE *ue, *precoded_sinr_dB = dB_fixed((best_sinr / 2) / interference_plus_noise_power); } else { - LOG_W(NR_PHY, "PMI computation is not implemented for rank indicator %i with 4 ports\n", rank_indicator+1); - return -1; + // RI=3 or RI=4: Use identity-like precoder (i1=0, i2=0) + // Compute SINR from per-layer effective channel power with identity precoding + // Each layer p sees channel H[:,p] across all RX antennas + int nlayers = rank_indicator + 1; + int64_t total_power = 0; + + for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb + csirs_config_pdu->nr_of_rbs); rb++) { + if (csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != (rb % 2)) + continue; + uint16_t k = (frame_parms->first_carrier_offset + rb * NR_NB_SC_PER_RB) % frame_parms->ofdm_symbol_size; + uint16_t k_off = k + mem_offset; + + for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) { + for (int p = 0; p < nlayers; p++) { + const c16_t h = csi_rs_estimated_channel_freq[ant_rx][p][k_off]; + total_power += (int64_t)h.r * h.r + (int64_t)h.i * h.i; + } + } + } + + i1[0] = 0; + i1[1] = 0; + i1[2] = 0; + i2[0] = 0; + if (total_power > 0) + *precoded_sinr_dB = dB_fixed((total_power / nlayers) / interference_plus_noise_power); } } else { LOG_W(NR_PHY, "PMI computation is not implemented for %i ports\n", N_ports); diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index 063916c2ee..e1de1eab44 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -285,16 +285,16 @@ void nr_feptx(void *arg) AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n"); } - const c16_t *dst = (const c16_t *)&ru->common.txdataF_BF[tx_idx][txdataF_BF_offset]; - const int dst_nnz = count_nnz_c16(dst, numSamples); - if (startSymbol == 0 && ru->proc.frame_tx % 10 == 0 && dst_nnz != 0) { - const int dst_pwr_db = dB_fixed(signal_energy_nodc((c16_t *)dst, 2 * numSamples)); + /* const c16_t *dst = (const c16_t *)&ru->common.txdataF_BF[tx_idx][txdataF_BF_offset]; */ + /* const int dst_nnz = count_nnz_c16(dst, numSamples); */ + /* if (startSymbol == 0 && ru->proc.frame_tx % 10 == 0 && dst_nnz != 0) { */ + /* const int dst_pwr_db = dB_fixed(signal_energy_nodc((c16_t *)dst, 2 * numSamples)); */ - LOG_I(PHY, - "[TXF_PWR] frame=%d slot=%d beam=%d aa=%d tx_idx=%d " - "DST pwr=%d dB nnz=%d\n", - ru->proc.frame_tx, slot, bb, aa, tx_idx, dst_pwr_db, dst_nnz); - } + /* LOG_I(PHY, */ + /* "[TXF_PWR] frame=%d slot=%d beam=%d aa=%d tx_idx=%d " */ + /* "DST pwr=%d dB nnz=%d\n", */ + /* ru->proc.frame_tx, slot, bb, aa, tx_idx, dst_pwr_db, dst_nnz); */ + /* } */ if (tx_idx == 0) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 17941d4368..b582824f3b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -597,7 +597,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); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index c271824e75..8ff8cae14a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -769,9 +769,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req) LOG_E(NR_MAC, "RRC reconfiguration outcome unsuccessful, but no rollback mechanism implemented to come back to old configuration\n"); } else if (req->reconfig_compl) { - LOG_I(NR_MAC, "DU received confirmation of successful RRC Reconfiguration\n"); if (UE->reconfigCellGroup) { - LOG_W(NR_MAC, "reconfigCellGroup still present, did we miss ACK for RRCReconfiguration?\n"); ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup); UE->CellGroup = UE->reconfigCellGroup; UE->reconfigCellGroup = NULL; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 71547f85e7..71f2165d0f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -645,10 +645,10 @@ static void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcell pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1, sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); long l = ue_supported_dl_layers(scc, uecap); - long ue_supported_layers = l > 1 ? l : 2; // min UE supported layers = 2 - if (maxMIMO_layers == -1) + long ue_supported_layers = l > 1 ? l : 2; + if (maxMIMO_layers == -1) *pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(NR_MAX_SUPPORTED_DL_LAYERS, ue_supported_layers); - else + else *pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(maxMIMO_layers, ue_supported_layers); } @@ -1287,7 +1287,7 @@ static void config_pucch_resset1(NR_PUCCH_Config_t *pucch_Config, pucchfmt2->interslotFrequencyHopping = NULL; pucchfmt2->additionalDMRS = NULL; pucchfmt2->maxCodeRate = calloc(1,sizeof(*pucchfmt2->maxCodeRate)); - *pucchfmt2->maxCodeRate = NR_PUCCH_MaxCodeRate_zeroDot15; + *pucchfmt2->maxCodeRate = NR_PUCCH_MaxCodeRate_zeroDot25; pucchfmt2->nrofSlots = NULL; pucchfmt2->pi2BPSK = NULL;