This commit is contained in:
2026-02-15 22:11:22 +09:00
parent 5874aa1fe7
commit 3e58ab0d3a
6 changed files with 70 additions and 31 deletions
+13 -6
View File
@@ -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;
}
+44 -9
View File
@@ -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);
+9 -9
View File
@@ -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)
@@ -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);
@@ -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;
+4 -4
View File
@@ -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;