mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Extension of the PMI calculation based on CSI-RS to other scenarios
Signed-off-by: Roberto Louro Magueta <rmagueta@allbesmart.pt>
This commit is contained in:
@@ -57,8 +57,10 @@ typedef struct {
|
||||
int rsrp_dBm;
|
||||
float sinr_dB;
|
||||
uint8_t rank_indicator;
|
||||
uint16_t i1;
|
||||
uint8_t i2;
|
||||
uint8_t i_1_1;
|
||||
uint8_t i_1_2;
|
||||
uint8_t i_1_3;
|
||||
uint8_t i_2;
|
||||
uint8_t cqi;
|
||||
rlm_t radiolink_monitoring;
|
||||
} fapi_nr_l1_measurements_t;
|
||||
|
||||
@@ -586,120 +586,358 @@ static int nr_csi_rs_ri_estimation(const PHY_VARS_NR_UE *ue,
|
||||
}
|
||||
}
|
||||
|
||||
// Type1 Single Panel PMI indices (TS 38.214 Section 5.2.2.2.1).
|
||||
typedef struct {
|
||||
uint8_t i_1_1; // first DFT beam index (4/8 ports; range depends on N1*O1)
|
||||
uint8_t i_1_2; // second DFT beam index (8 ports only; range depends on N2*O2)
|
||||
uint8_t i_1_3; // beam-pair / k1 selection (rank>=2 with 4/8 ports)
|
||||
uint8_t i_2; // co-phasing index (rank 1: 0..3; rank>=2: 0..1)
|
||||
} csi_rs_pmi_t;
|
||||
|
||||
// DFT codebook: v_l = [1, exp(j*pi*l/4)]^T, l in {0..7}. Q8 fixed-point.
|
||||
#define PMI_Q 256
|
||||
static const int vl_r[8] = {256, 181, 0, -181, -256, -181, 0, 181};
|
||||
static const int vl_i[8] = {0, 181, 256, 181, 0, -181, -256, -181};
|
||||
// Co-phasing phi_n = exp(j*pi*n/2): pure integer entries.
|
||||
static const int8_t phi_r[4] = {1, 0, -1, 0};
|
||||
static const int8_t phi_i[4] = {0, 1, 0, -1};
|
||||
|
||||
// SISO case
|
||||
static int nr_csi_rs_pmi_1port(int nb_antennas_rx,
|
||||
int N_ports,
|
||||
int osz,
|
||||
const c16_t ch_freq[][N_ports][osz],
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *cfg,
|
||||
uint32_t noise,
|
||||
int32_t *precoded_sinr_dB)
|
||||
{
|
||||
int64_t signal_power = 0;
|
||||
int num_REs = 0;
|
||||
for (int rb = cfg->start_rb; rb < cfg->start_rb + cfg->nr_of_rbs; rb++) {
|
||||
if (cfg->freq_density <= 1 && cfg->freq_density != (rb % 2))
|
||||
continue;
|
||||
int k = rb * NR_NB_SC_PER_RB;
|
||||
for (int ant_rx = 0; ant_rx < nb_antennas_rx; ant_rx++) {
|
||||
c16_t h = ch_freq[ant_rx][0][k];
|
||||
signal_power += (int64_t)h.r * h.r + (int64_t)h.i * h.i;
|
||||
}
|
||||
num_REs++;
|
||||
}
|
||||
if (num_REs > 0)
|
||||
*precoded_sinr_dB = dB_fixed((signal_power / num_REs) / noise);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rank 1 or 2; 2 ports (TS 38.214 - Table 5.2.2.2.1-1)
|
||||
static int nr_csi_rs_pmi_2ports(int nb_antennas_rx,
|
||||
int N_ports,
|
||||
int osz,
|
||||
const c16_t ch_freq[][N_ports][osz],
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *cfg,
|
||||
uint32_t noise,
|
||||
uint8_t rank_indicator,
|
||||
csi_rs_pmi_t *pmi,
|
||||
int32_t *precoded_sinr_dB)
|
||||
{
|
||||
if (rank_indicator > 1) {
|
||||
LOG_W(NR_PHY, "PMI not implemented for 2 ports and rank %d\n", rank_indicator + 1);
|
||||
return -1;
|
||||
}
|
||||
// R = sum H^H H, 2x2 Hermitian (only R[0][0], R[0][1], R[1][1] needed)
|
||||
c64_t R[2][2] = {{{0}}};
|
||||
int N_RE_total = 0;
|
||||
for (int rb = cfg->start_rb; rb < cfg->start_rb + cfg->nr_of_rbs; rb++) {
|
||||
if (cfg->freq_density <= 1 && cfg->freq_density != (rb % 2))
|
||||
continue;
|
||||
int k = rb * NR_NB_SC_PER_RB;
|
||||
for (int ant_rx = 0; ant_rx < nb_antennas_rx; ant_rx++) {
|
||||
c16_t h0 = ch_freq[ant_rx][0][k], h1 = ch_freq[ant_rx][1][k];
|
||||
R[0][0].r += (int64_t)h0.r * h0.r + (int64_t)h0.i * h0.i; // |h0|^2 (real)
|
||||
R[1][1].r += (int64_t)h1.r * h1.r + (int64_t)h1.i * h1.i; // |h1|^2 (real)
|
||||
R[0][1].r += (int64_t)h0.r * h1.r + (int64_t)h0.i * h1.i; // Re(conj(h0)*h1)
|
||||
R[0][1].i += (int64_t)h0.r * h1.i - (int64_t)h0.i * h1.r; // Im(conj(h0)*h1)
|
||||
N_RE_total++;
|
||||
}
|
||||
}
|
||||
if (N_RE_total == 0)
|
||||
return -1;
|
||||
|
||||
// total power, constant for all n
|
||||
const int64_t trace = R[0][0].r + R[1][1].r;
|
||||
|
||||
if (rank_indicator == 0) {
|
||||
// Rank 1 (Table 5.2.2.2.1-1): 4 hypotheses, W_n = (1/sqrt(2))*[1; phi_n]
|
||||
// W^H R W = (1/2) * (trace + 2*Re(phi_n * R[0][1]))
|
||||
int64_t best = INT64_MIN, best_signal = 0;
|
||||
for (int n = 0; n < 4; n++) {
|
||||
int64_t cross = (int64_t)phi_r[n] * R[0][1].r - (int64_t)phi_i[n] * R[0][1].i;
|
||||
int64_t m = trace + 2 * cross;
|
||||
if (m > best) {
|
||||
best = m;
|
||||
pmi->i_2 = n;
|
||||
best_signal = m;
|
||||
}
|
||||
}
|
||||
// Average signal power per RE = best / (2 * N_RE_total) (1/2 do W^H R W)
|
||||
int64_t sp = best_signal / (2LL * N_RE_total);
|
||||
*precoded_sinr_dB = dB_fixed(sp / noise);
|
||||
} else {
|
||||
// Rank 2 (Table 5.2.2.2.1-1): 2 hypotheses.
|
||||
// The two PMI hypotheses span the same 2D subspace, giving identical trace, determinant and eigenvalues of W^H R W.
|
||||
// They differ only in how the basis is rotated:
|
||||
// - i_2=0 uses W = (1/2)[[1, 1],[1, -1]] -> off-diag picks up Im(R_01)
|
||||
// - i_2=1 uses W = (1/2)[[1, 1],[j, -j]] -> off-diag picks up Re(R_01)
|
||||
// We will choose the rotation that minimises the off-diagonal magnitude (smaller cross-layer interference for sub-MMSE receivers).
|
||||
pmi->i_2 = (llabs(R[0][1].i) < llabs(R[0][1].r)) ? 0 : 1;
|
||||
int64_t signal_per_layer = trace / (2LL * N_RE_total);
|
||||
*precoded_sinr_dB = dB_fixed(signal_per_layer / noise);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rank 1 or 2; 4 ports (TS 38.214 - Tables 5.2.2.2.1-5 and -6)
|
||||
static int nr_csi_rs_pmi_4ports(int nb_antennas_rx,
|
||||
int N_ports,
|
||||
int osz,
|
||||
const c16_t ch_freq[][N_ports][osz],
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *cfg,
|
||||
uint32_t noise,
|
||||
uint8_t rank_indicator,
|
||||
csi_rs_pmi_t *pmi,
|
||||
int32_t *precoded_sinr_dB)
|
||||
{
|
||||
if (rank_indicator > 1) {
|
||||
LOG_W(NR_PHY, "PMI not implemented for 4 ports and rank %d\n", rank_indicator + 1);
|
||||
return -1;
|
||||
}
|
||||
// R = sum H^H H, 4x4 Hermitian, upper triangle only.
|
||||
c64_t R[4][4] = {{{0}}};
|
||||
int N_RE_total = 0;
|
||||
for (int rb = cfg->start_rb; rb < cfg->start_rb + cfg->nr_of_rbs; rb++) {
|
||||
if (cfg->freq_density <= 1 && cfg->freq_density != (rb % 2))
|
||||
continue;
|
||||
int k = rb * NR_NB_SC_PER_RB;
|
||||
for (int ant_rx = 0; ant_rx < nb_antennas_rx; ant_rx++) {
|
||||
c16_t h[4];
|
||||
for (int p = 0; p < 4; p++)
|
||||
h[p] = ch_freq[ant_rx][p][k];
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = i; j < 4; j++) {
|
||||
R[i][j].r += (int64_t)h[i].r * h[j].r + (int64_t)h[i].i * h[j].i;
|
||||
R[i][j].i += (int64_t)h[i].r * h[j].i - (int64_t)h[i].i * h[j].r;
|
||||
}
|
||||
N_RE_total++;
|
||||
}
|
||||
}
|
||||
if (N_RE_total == 0)
|
||||
return -1;
|
||||
|
||||
// For W = c*[v_l; phi_n*v_l], W^H R W = (1/4)[A_l + B_l + 2*Re(phi_n*D_l)]
|
||||
// A_l = v_l^H R[0:2,0:2] v_l (Hermitian -> real)
|
||||
// B_l = v_l^H R[2:4,2:4] v_l (Hermitian -> real)
|
||||
// D_l = v_l^H R[0:2,2:4] v_l (general -> complex)
|
||||
int64_t A[8], B[8];
|
||||
c64_t D[8];
|
||||
for (int l = 0; l < 8; l++) {
|
||||
const int64_t vr = vl_r[l], vi = vl_i[l];
|
||||
A[l] = (int64_t)PMI_Q * PMI_Q * (R[0][0].r + R[1][1].r) + 2LL * PMI_Q * (R[0][1].r * vr - R[0][1].i * vi);
|
||||
B[l] = (int64_t)PMI_Q * PMI_Q * (R[2][2].r + R[3][3].r) + 2LL * PMI_Q * (R[2][3].r * vr - R[2][3].i * vi);
|
||||
D[l].r = (int64_t)PMI_Q * PMI_Q * (R[0][2].r + R[1][3].r)
|
||||
+ (int64_t)PMI_Q * ((R[0][3].r + R[1][2].r) * vr + (R[1][2].i - R[0][3].i) * vi);
|
||||
D[l].i = (int64_t)PMI_Q * PMI_Q * (R[0][2].i + R[1][3].i)
|
||||
+ (int64_t)PMI_Q * ((R[0][3].i + R[1][2].i) * vr + (R[0][3].r - R[1][2].r) * vi);
|
||||
}
|
||||
|
||||
int64_t best = INT64_MIN, best_signal = 0;
|
||||
if (rank_indicator == 0) {
|
||||
// Rank 1, Table 5.2.2.2.1-5: 32 entries indexed by (l, n).
|
||||
for (int l = 0; l < 8; l++) {
|
||||
const int64_t AB = A[l] + B[l];
|
||||
for (int n = 0; n < 4; n++) {
|
||||
int64_t cross = (int64_t)phi_r[n] * D[l].r - (int64_t)phi_i[n] * D[l].i;
|
||||
int64_t m = AB + 2 * cross;
|
||||
if (m > best) {
|
||||
best = m;
|
||||
pmi->i_1_1 = l;
|
||||
pmi->i_2 = n;
|
||||
best_signal = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Rank 2, Table 5.2.2.2.1-6: 32 entries indexed by (l, i13, n). l' = (l + 4*i13) mod 8.
|
||||
// trace(W^H R W) = (1/8)[A_l + A_l' + B_l + B_l' + 2*Re(phi_n*(D_l - D_l'))]
|
||||
for (int l = 0; l < 8; l++)
|
||||
for (int i13 = 0; i13 < 2; i13++) {
|
||||
const int lp = (l + 4 * i13) & 7;
|
||||
const int64_t AB = (A[l] + A[lp]) + (B[l] + B[lp]);
|
||||
const int64_t dDr = D[l].r - D[lp].r, dDi = D[l].i - D[lp].i;
|
||||
for (int n = 0; n < 2; n++) {
|
||||
int64_t cross = (int64_t)phi_r[n] * dDr - (int64_t)phi_i[n] * dDi;
|
||||
int64_t m = AB + 2 * cross;
|
||||
if (m > best) {
|
||||
best = m;
|
||||
pmi->i_1_1 = l;
|
||||
pmi->i_1_3 = i13;
|
||||
pmi->i_2 = n;
|
||||
best_signal = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Average signal power per RE = best / (4 * Q^2 * N_RE_total) [rank 1]
|
||||
// For rank 2 the (1/8) cancels in best, but we keep the (1/4) here for SINR consistency.
|
||||
int64_t sp = best_signal / (4LL * PMI_Q * PMI_Q * N_RE_total);
|
||||
*precoded_sinr_dB = dB_fixed(sp / noise);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rank 1; 8 ports (TS 38.214 - Table 5.2.2.2.1-9)
|
||||
static int nr_csi_rs_pmi_8ports(int nb_antennas_rx,
|
||||
int N_ports,
|
||||
int osz,
|
||||
const c16_t ch_freq[][N_ports][osz],
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *cfg,
|
||||
uint32_t noise,
|
||||
uint8_t rank_indicator,
|
||||
csi_rs_pmi_t *pmi,
|
||||
int32_t *precoded_sinr_dB)
|
||||
{
|
||||
if (rank_indicator != 0) {
|
||||
LOG_W(NR_PHY, "PMI not implemented for 8 ports and rank %d\n", rank_indicator + 1);
|
||||
return -1;
|
||||
}
|
||||
// R = sum H^H H, 8x8 Hermitian, upper triangle only.
|
||||
c64_t R[8][8] = {{{0}}};
|
||||
int N_RE_total = 0;
|
||||
for (int rb = cfg->start_rb; rb < cfg->start_rb + cfg->nr_of_rbs; rb++) {
|
||||
if (cfg->freq_density <= 1 && cfg->freq_density != (rb % 2))
|
||||
continue;
|
||||
int k = rb * NR_NB_SC_PER_RB;
|
||||
for (int ant_rx = 0; ant_rx < nb_antennas_rx; ant_rx++) {
|
||||
c16_t h[8];
|
||||
for (int p = 0; p < 8; p++)
|
||||
h[p] = ch_freq[ant_rx][p][k];
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = i; j < 8; j++) {
|
||||
R[i][j].r += (int64_t)h[i].r * h[j].r + (int64_t)h[i].i * h[j].i;
|
||||
R[i][j].i += (int64_t)h[i].r * h[j].i - (int64_t)h[i].i * h[j].r;
|
||||
}
|
||||
N_RE_total++;
|
||||
}
|
||||
}
|
||||
if (N_RE_total == 0)
|
||||
return -1;
|
||||
|
||||
// For W = c*[v_lm; phi_n*v_lm] with v_lm = v_l1 (x) u_m2 (4x1, |v_lm[i]|=Q), decompose R into 4x4 blocks
|
||||
// R_TL = R[0:4,0:4], R_TR = R[0:4,4:8], R_BR = R[4:8,4:8],
|
||||
// and precompute A_lm, B_lm, D_lm for each (l1, m2).
|
||||
// The Kronecker structure means v_lm = [1, exp(j*pi*m2/4), exp(j*pi*l1/4), exp(j*pi*(l1+m2)/4)],
|
||||
// so the fourth entry is just a lookup at index (l1+m2)%8.
|
||||
int64_t A[8][8], B[8][8];
|
||||
c64_t D[8][8];
|
||||
for (int l1 = 0; l1 < 8; l1++) {
|
||||
for (int m2 = 0; m2 < 8; m2++) {
|
||||
const int vr[4] = {PMI_Q, vl_r[m2], vl_r[l1], vl_r[(l1 + m2) & 7]};
|
||||
const int vi[4] = {0, vl_i[m2], vl_i[l1], vl_i[(l1 + m2) & 7]};
|
||||
int64_t Aval = (int64_t)PMI_Q * PMI_Q * (R[0][0].r + R[1][1].r + R[2][2].r + R[3][3].r);
|
||||
int64_t Bval = (int64_t)PMI_Q * PMI_Q * (R[4][4].r + R[5][5].r + R[6][6].r + R[7][7].r);
|
||||
int64_t Dr = (int64_t)PMI_Q * PMI_Q * (R[0][4].r + R[1][5].r + R[2][6].r + R[3][7].r);
|
||||
int64_t Di = (int64_t)PMI_Q * PMI_Q * (R[0][4].i + R[1][5].i + R[2][6].i + R[3][7].i);
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = i + 1; j < 4; j++) {
|
||||
// c = conj(v[i]) * v[j]
|
||||
int64_t cr = (int64_t)vr[i] * vr[j] + (int64_t)vi[i] * vi[j];
|
||||
int64_t ci = (int64_t)vr[i] * vi[j] - (int64_t)vi[i] * vr[j];
|
||||
// Hermitian blocks: 2 * Re(c * M[i][j])
|
||||
Aval += 2 * (cr * R[i][j].r - ci * R[i][j].i);
|
||||
Bval += 2 * (cr * R[i + 4][j + 4].r - ci * R[i + 4][j + 4].i);
|
||||
// General block R_TR: c * R[i][j+4] + conj(c) * R[j][i+4]
|
||||
Dr += cr * (R[i][j + 4].r + R[j][i + 4].r) + ci * (R[j][i + 4].i - R[i][j + 4].i);
|
||||
Di += cr * (R[i][j + 4].i + R[j][i + 4].i) + ci * (R[i][j + 4].r - R[j][i + 4].r);
|
||||
}
|
||||
A[l1][m2] = Aval;
|
||||
B[l1][m2] = Bval;
|
||||
D[l1][m2].r = Dr;
|
||||
D[l1][m2].i = Di;
|
||||
}
|
||||
}
|
||||
|
||||
// Enumerate (l1, m2, n) - 256 hypotheses, each evaluation is O(1).
|
||||
int64_t best = INT64_MIN, best_signal = 0;
|
||||
for (int l1 = 0; l1 < 8; l1++)
|
||||
for (int m2 = 0; m2 < 8; m2++) {
|
||||
const int64_t AB = A[l1][m2] + B[l1][m2];
|
||||
for (int n = 0; n < 4; n++) {
|
||||
int64_t cross = (int64_t)phi_r[n] * D[l1][m2].r - (int64_t)phi_i[n] * D[l1][m2].i;
|
||||
int64_t m = AB + 2 * cross;
|
||||
if (m > best) {
|
||||
best = m;
|
||||
pmi->i_1_1 = l1;
|
||||
pmi->i_1_2 = m2;
|
||||
pmi->i_2 = n;
|
||||
best_signal = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Average signal power per RE = best / (8 * Q^2 * N_RE_total)
|
||||
int64_t sp = best_signal / (8LL * PMI_Q * PMI_Q * (int64_t)N_RE_total);
|
||||
*precoded_sinr_dB = dB_fixed(sp / noise);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nr_csi_rs_pmi_estimation(const PHY_VARS_NR_UE *ue,
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
const uint8_t N_ports,
|
||||
const c16_t csi_rs_estimated_channel_freq[][N_ports][ue->frame_parms.ofdm_symbol_size],
|
||||
const uint32_t interference_plus_noise_power,
|
||||
const uint8_t rank_indicator,
|
||||
const int16_t log2_re,
|
||||
uint8_t *i2,
|
||||
csi_rs_pmi_t *pmi,
|
||||
int32_t *precoded_sinr_dB)
|
||||
{
|
||||
const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
|
||||
memset(pmi, 0, sizeof(*pmi));
|
||||
const NR_DL_FRAME_PARMS *fp = &ue->frame_parms;
|
||||
const int nrx = fp->nb_antennas_rx;
|
||||
const int osz = fp->ofdm_symbol_size;
|
||||
const uint32_t noise = (interference_plus_noise_power == 0) ? 1 : interference_plus_noise_power;
|
||||
|
||||
// i1 is a three-element vector in the form of [i11 i12 i13], when CodebookType is specified as 'Type1SinglePanel'.
|
||||
// Note that i13 is not applicable when the number of transmission layers is one of {1, 5, 6, 7, 8}.
|
||||
// i2, for 'Type1SinglePanel' codebook type, it is a scalar when PMIMode is specified as 'wideband', and when PMIMode
|
||||
// is specified as 'subband' or when PRGSize, the length of the i2 vector equals to the number of subbands or PRGs.
|
||||
// Note that when the number of CSI-RS ports is 2, the applicable codebook type is 'Type1SinglePanel'. In this case,
|
||||
// the precoding matrix is obtained by a single index (i2 field here) based on TS 38.214 Table 5.2.2.2.1-1.
|
||||
// The first column is applicable if the UE is reporting a Rank = 1, whereas the second column is applicable if the
|
||||
// UE is reporting a Rank = 2.
|
||||
|
||||
if (N_ports == 1) {
|
||||
// SISO case: SINR = E[|h|^2] / noise_power. No PMI to estimate.
|
||||
int64_t signal_power = 0;
|
||||
int count = 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 = rb * NR_NB_SC_PER_RB;
|
||||
|
||||
const c16_t h = csi_rs_estimated_channel_freq[0][0][k];
|
||||
signal_power += (int64_t)h.r * h.r + (int64_t)h.i * h.i;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
const int64_t avg_signal_power = signal_power / count;
|
||||
// Non RF devices like ZMQ has virtually zero noise. So here we make noise as 1 to return maximum sinr.
|
||||
const uint32_t sinr = avg_signal_power / ((interference_plus_noise_power == 0) ? 1 : interference_plus_noise_power);
|
||||
*precoded_sinr_dB = dB_fixed(sinr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
switch (N_ports) {
|
||||
case 1:
|
||||
return nr_csi_rs_pmi_1port(nrx, N_ports, osz, csi_rs_estimated_channel_freq, csirs_config_pdu, noise, precoded_sinr_dB);
|
||||
case 2:
|
||||
return nr_csi_rs_pmi_2ports(nrx,
|
||||
N_ports,
|
||||
osz,
|
||||
csi_rs_estimated_channel_freq,
|
||||
csirs_config_pdu,
|
||||
noise,
|
||||
rank_indicator,
|
||||
pmi,
|
||||
precoded_sinr_dB);
|
||||
case 4:
|
||||
return nr_csi_rs_pmi_4ports(nrx,
|
||||
N_ports,
|
||||
osz,
|
||||
csi_rs_estimated_channel_freq,
|
||||
csirs_config_pdu,
|
||||
noise,
|
||||
rank_indicator,
|
||||
pmi,
|
||||
precoded_sinr_dB);
|
||||
case 8:
|
||||
return nr_csi_rs_pmi_8ports(nrx,
|
||||
N_ports,
|
||||
osz,
|
||||
csi_rs_estimated_channel_freq,
|
||||
csirs_config_pdu,
|
||||
noise,
|
||||
rank_indicator,
|
||||
pmi,
|
||||
precoded_sinr_dB);
|
||||
default:
|
||||
LOG_W(NR_PHY, "PMI not implemented for %d antenna ports\n", N_ports);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(rank_indicator == 0 || rank_indicator == 1) {
|
||||
c64_t sum[4] = {0};
|
||||
c64_t sum2[4] = {0};
|
||||
int64_t tested_precoded_sinr[4] = {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 = rb * NR_NB_SC_PER_RB;
|
||||
for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) {
|
||||
const c16_t p0 = csi_rs_estimated_channel_freq[ant_rx][0][k];
|
||||
const c16_t p1 = csi_rs_estimated_channel_freq[ant_rx][1][k];
|
||||
|
||||
// H_p0 + 1*H_p1 = (H_p0_re + H_p1_re) + 1j*(H_p0_im + H_p1_im)
|
||||
sum[0].r += (p0.r + p1.r);
|
||||
sum[0].i += (p0.i + p1.i);
|
||||
sum2[0].r += (sum[0].r * sum[0].r) >> log2_re;
|
||||
sum2[0].i += (sum[0].i * sum[0].i) >> log2_re;
|
||||
|
||||
// H_p0 + 1j*H_p1 = (H_p0_re - H_p1_im) + 1j*(H_p0_im + H_p1_re)
|
||||
sum[1].r += (p0.r - p1.i);
|
||||
sum[1].i += (p0.i + p1.r);
|
||||
sum2[1].r += (sum[1].r * sum[1].r) >> log2_re;
|
||||
sum2[1].i += (sum[1].i * sum[1].i) >> log2_re;
|
||||
|
||||
// H_p0 - 1*H_p1 = (H_p0_re - H_p1_re) + 1j*(H_p0_im - H_p1_im)
|
||||
sum[2].r += (p0.r - p1.r);
|
||||
sum[2].i += (p0.i - p1.i);
|
||||
sum2[2].r += (sum[2].r * sum[2].r) >> log2_re;
|
||||
sum2[2].i += (sum[2].i * sum[2].i) >> log2_re;
|
||||
|
||||
// H_p0 - 1j*H_p1 = (H_p0_re + H_p1_im) + 1j*(H_p0_im - H_p1_re)
|
||||
sum[3].r += (p0.r + p1.i);
|
||||
sum[3].i += (p0.i - p1.r);
|
||||
sum2[3].r += (sum[3].r * sum[3].r) >> log2_re;
|
||||
sum2[3].i += (sum[3].i * sum[3].i) >> log2_re;
|
||||
}
|
||||
}
|
||||
|
||||
// We should perform >>nr_csi_info->log2_re here for all terms, but since sum2_re and sum2_im can be high values,
|
||||
// we performed this above.
|
||||
for(int p = 0; p<4; p++) {
|
||||
int64_t power_re = sum2[p].r - (sum[p].r >> log2_re) * (sum[p].r >> log2_re);
|
||||
int64_t power_im = sum2[p].i - (sum[p].i >> log2_re) * (sum[p].i >> log2_re);
|
||||
tested_precoded_sinr[p] = (power_re + power_im) / interference_plus_noise_power;
|
||||
}
|
||||
|
||||
if(rank_indicator == 0) {
|
||||
for(int tested_i2 = 0; tested_i2 < 4; tested_i2++) {
|
||||
if(tested_precoded_sinr[tested_i2] > tested_precoded_sinr[i2[0]]) {
|
||||
i2[0] = tested_i2;
|
||||
}
|
||||
}
|
||||
*precoded_sinr_dB = dB_fixed(tested_precoded_sinr[i2[0]]);
|
||||
} else {
|
||||
i2[0] = tested_precoded_sinr[0]+tested_precoded_sinr[2] > tested_precoded_sinr[1]+tested_precoded_sinr[3] ? 0 : 1;
|
||||
*precoded_sinr_dB = dB_fixed((tested_precoded_sinr[i2[0]] + tested_precoded_sinr[i2[0]+2])>>1);
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_W(NR_PHY, "PMI computation is not implemented for rank indicator %i\n", rank_indicator+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nr_csi_rs_cqi_estimation(const uint32_t precoded_sinr,
|
||||
@@ -939,8 +1177,7 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
rank_indicator = nr_csi_rs_ri_estimation(ue, csirs_config_pdu, mapping_parms.ports, csi_rs_estimated_channel_freq, log2_maxh);
|
||||
}
|
||||
|
||||
uint8_t i1[3] = {0};
|
||||
uint8_t i2[1] = {0};
|
||||
csi_rs_pmi_t pmi = {0};
|
||||
uint8_t cqi = 0;
|
||||
int32_t precoded_sinr_dB = 0;
|
||||
// bit 3 in bitmap to indicate RI measurment
|
||||
@@ -951,8 +1188,7 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
csi_rs_estimated_channel_freq,
|
||||
csi_info->csi_im_meas_computed ? csi_info->interference_plus_noise_power : noise_power,
|
||||
rank_indicator,
|
||||
log2_re,
|
||||
i2,
|
||||
&pmi,
|
||||
&precoded_sinr_dB);
|
||||
|
||||
// bit 4 in bitmap to indicate RI measurment
|
||||
@@ -966,11 +1202,11 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
break;
|
||||
case 26 :
|
||||
LOG_I(NR_PHY, "RI = %i i1 = %i.%i.%i, i2 = %i, SINR = %i dB, CQI = %i\n",
|
||||
rank_indicator + 1, i1[0], i1[1], i1[2], i2[0], precoded_sinr_dB, cqi);
|
||||
rank_indicator + 1, pmi.i_1_1, pmi.i_1_2, pmi.i_1_3, pmi.i_2, precoded_sinr_dB, cqi);
|
||||
break;
|
||||
case 27 :
|
||||
LOG_I(NR_PHY, "RSRP = %i dBm, RI = %i i1 = %i.%i.%i, i2 = %i, SINR = %i dB, CQI = %i\n",
|
||||
rsrp_dBm, rank_indicator + 1, i1[0], i1[1], i1[2], i2[0], precoded_sinr_dB, cqi);
|
||||
rsrp_dBm, rank_indicator + 1, pmi.i_1_1, pmi.i_1_2, pmi.i_1_3, pmi.i_2, precoded_sinr_dB, cqi);
|
||||
break;
|
||||
default :
|
||||
AssertFatal(false, "Not supported measurement configuration\n");
|
||||
@@ -987,8 +1223,10 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
.is_neighboring_cell = false,
|
||||
.rsrp_dBm = rsrp_dBm,
|
||||
.rank_indicator = rank_indicator,
|
||||
.i1 = *i1,
|
||||
.i2 = *i2,
|
||||
.i_1_1 = pmi.i_1_1,
|
||||
.i_1_2 = pmi.i_1_2,
|
||||
.i_1_3 = pmi.i_1_3,
|
||||
.i_2 = pmi.i_2,
|
||||
.cqi = cqi,
|
||||
.radiolink_monitoring = RLM_no_monitoring, // TODO do be activated in case of RLM based on CSI-RS
|
||||
};
|
||||
|
||||
@@ -413,8 +413,10 @@ typedef struct {
|
||||
typedef struct {
|
||||
int rsrp_dBm;
|
||||
uint8_t ri;
|
||||
uint16_t i1;
|
||||
uint8_t i2;
|
||||
uint8_t i_1_1;
|
||||
uint8_t i_1_2;
|
||||
uint8_t i_1_3;
|
||||
uint8_t i_2;
|
||||
uint8_t cqi;
|
||||
} NR_CSIRS_meas_t;
|
||||
|
||||
|
||||
@@ -1547,8 +1547,10 @@ void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slo
|
||||
mac->ssb_measurements[ssb_index].ssb_sinr_dB = l1_measurements->sinr_dB;
|
||||
} else if (csi_meas) {
|
||||
mac->csirs_measurements.rsrp_dBm = l1_measurements->rsrp_dBm;
|
||||
mac->csirs_measurements.i1 = l1_measurements->i1;
|
||||
mac->csirs_measurements.i2 = l1_measurements->i2;
|
||||
mac->csirs_measurements.i_1_1 = l1_measurements->i_1_1;
|
||||
mac->csirs_measurements.i_1_2 = l1_measurements->i_1_2;
|
||||
mac->csirs_measurements.i_1_3 = l1_measurements->i_1_3;
|
||||
mac->csirs_measurements.i_2 = l1_measurements->i_2;
|
||||
mac->csirs_measurements.cqi = l1_measurements->cqi;
|
||||
mac->csirs_measurements.ri = l1_measurements->rank_indicator;
|
||||
}
|
||||
@@ -2929,11 +2931,46 @@ static nfapi_nr_ue_csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
return csi;
|
||||
}
|
||||
|
||||
// Pack i_1,1 || i_1,2 || i_1,3 into the X1 PMI field, with i_1,1 in the most significant bits
|
||||
// (ordering per TS 38.212 Sections 6.3.1.1.2 and 6.3.2.1.2).
|
||||
// Sub-field widths are inferred from the total pmi_x1_bitlen, which is unique across the Type1 Single Panel configurations
|
||||
// covered by the PMI estimator:
|
||||
// ports | rank | pmi_x1_bitlen | i_1,1 | i_1,2 | i_1,3
|
||||
// 2 | 1,2 | 0 | - | - | -
|
||||
// 4 | 1 | 3 | 3 | 0 | 0
|
||||
// 4 | 2 | 4 | 3 | 0 | 1
|
||||
// 8 | 1 | 6 | 3 | 3 | 0
|
||||
// 8 | 2 | 8 | 3 | 3 | 2
|
||||
static uint16_t pack_pmi_x1(int pmi_x1_bitlen, uint8_t i_1_1, uint8_t i_1_2, uint8_t i_1_3)
|
||||
{
|
||||
int bits_12 = 0, bits_13 = 0;
|
||||
switch (pmi_x1_bitlen) {
|
||||
case 0:
|
||||
return 0; // 2-port: no X1
|
||||
case 3:
|
||||
break; // 4-port rank 1
|
||||
case 4:
|
||||
bits_13 = 1;
|
||||
break; // 4-port rank 2
|
||||
case 6:
|
||||
bits_12 = 3;
|
||||
break; // 8-port rank 1
|
||||
case 8:
|
||||
bits_12 = 3;
|
||||
bits_13 = 2;
|
||||
break; // 8-port rank 2
|
||||
default:
|
||||
LOG_W(NR_MAC, "pack_pmi_x1: unsupported pmi_x1_bitlen %d (Type1 SinglePanel only)\n", pmi_x1_bitlen);
|
||||
return 0;
|
||||
}
|
||||
return ((uint16_t)i_1_1 << (bits_12 + bits_13)) | ((uint16_t)i_1_2 << bits_13) | (uint16_t)i_1_3;
|
||||
}
|
||||
|
||||
static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
const struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
const CSI_mapping_t mapping_type)
|
||||
const struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
const NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
const CSI_mapping_t mapping_type)
|
||||
{
|
||||
int p1_bits = 0;
|
||||
int p2_bits = 0;
|
||||
@@ -2942,66 +2979,81 @@ static nfapi_nr_ue_csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *
|
||||
AssertFatal(mapping_type != SUBBAND_ON_PUCCH, "CSI mapping for subband PMI and CQI not implemented\n");
|
||||
|
||||
for (int csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
|
||||
|
||||
struct NR_CSI_ResourceConfig *csi_resourceconfig = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx];
|
||||
if (csi_resourceconfig->csi_ResourceConfigId == csi_ResourceConfigId) {
|
||||
if (csi_resourceconfig->csi_ResourceConfigId != csi_ResourceConfigId)
|
||||
continue;
|
||||
|
||||
for (int csi_idx = 0; csi_idx < csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.count; csi_idx++) {
|
||||
if (csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.array[csi_idx]->nzp_CSI_ResourceSetId ==
|
||||
*(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[0])) {
|
||||
for (int csi_idx = 0; csi_idx < csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.count; csi_idx++) {
|
||||
if (csi_MeasConfig->nzp_CSI_RS_ResourceSetToAddModList->list.array[csi_idx]->nzp_CSI_ResourceSetId
|
||||
!= *(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[0]))
|
||||
continue;
|
||||
|
||||
nr_csi_report_t *csi_report = NULL;
|
||||
for (int i = 0; i < MAX_CSI_REPORTCONFIG; i++) {
|
||||
if (mac->csi_report_template[i].reportConfigId == csi_reportconfig->reportConfigId) {
|
||||
csi_report = &mac->csi_report_template[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId);
|
||||
int cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
||||
int ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->csirs_measurements.ri];
|
||||
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.ri];
|
||||
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.ri];
|
||||
int padding_bitlen = 0;
|
||||
// TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0
|
||||
if (mapping_type == ON_PUSCH) {
|
||||
p1_bits = cri_bitlen + ri_bitlen + cqi_bitlen;
|
||||
p2_bits = pmi_x1_bitlen + pmi_x2_bitlen;
|
||||
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + ri_bitlen)) |
|
||||
(mac->csirs_measurements.ri << cqi_bitlen) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) |
|
||||
mac->csirs_measurements.i2;
|
||||
}
|
||||
else {
|
||||
p1_bits = nr_get_csi_bitlen(csi_report);
|
||||
padding_bitlen = p1_bits - (cri_bitlen + ri_bitlen + pmi_x1_bitlen + pmi_x2_bitlen + cqi_bitlen);
|
||||
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen + ri_bitlen)) |
|
||||
(mac->csirs_measurements.ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) |
|
||||
(mac->csirs_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) |
|
||||
(mac->csirs_measurements.i2 << (cqi_bitlen)) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
}
|
||||
|
||||
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
|
||||
temp_payload_2 = reverse_bits(temp_payload_2, p2_bits);
|
||||
LOG_D(NR_MAC, "cri_bitlen = %d\n", cri_bitlen);
|
||||
LOG_D(NR_MAC, "ri_bitlen = %d\n", ri_bitlen);
|
||||
LOG_D(NR_MAC, "pmi_x1_bitlen = %d\n", pmi_x1_bitlen);
|
||||
LOG_D(NR_MAC, "pmi_x2_bitlen = %d\n", pmi_x2_bitlen);
|
||||
LOG_D(NR_MAC, "cqi_bitlen = %d\n", cqi_bitlen);
|
||||
LOG_D(NR_MAC, "csi_part1_payload = 0x%lx\n", temp_payload_1);
|
||||
LOG_D(NR_MAC, "csi_part2_payload = 0x%lx\n", temp_payload_2);
|
||||
LOG_D(NR_MAC, "part1_bits = %d\n", p1_bits);
|
||||
LOG_D(NR_MAC, "part2_bits = %d\n", p2_bits);
|
||||
nr_csi_report_t *csi_report = NULL;
|
||||
for (int i = 0; i < MAX_CSI_REPORTCONFIG; i++) {
|
||||
if (mac->csi_report_template[i].reportConfigId == csi_reportconfig->reportConfigId) {
|
||||
csi_report = &mac->csi_report_template[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId);
|
||||
|
||||
const uint8_t ri = mac->csirs_measurements.ri;
|
||||
const int cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
||||
const int ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
const int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[ri];
|
||||
const int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[ri];
|
||||
const int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[ri];
|
||||
int padding_bitlen = 0;
|
||||
|
||||
// Reconstruct X1 from the separate i_1,1 / i_1,2 / i_1,3 fields; X2 is just i_2.
|
||||
const uint16_t pmi_x1 =
|
||||
pack_pmi_x1(pmi_x1_bitlen, mac->csirs_measurements.i_1_1, mac->csirs_measurements.i_1_2, mac->csirs_measurements.i_1_3);
|
||||
const uint8_t pmi_x2 = mac->csirs_measurements.i_2;
|
||||
|
||||
// TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0
|
||||
if (mapping_type == ON_PUSCH) {
|
||||
p1_bits = cri_bitlen + ri_bitlen + cqi_bitlen;
|
||||
p2_bits = pmi_x1_bitlen + pmi_x2_bitlen;
|
||||
temp_payload_1 = ((uint64_t)0 /* cri */ << (cqi_bitlen + ri_bitlen)) | ((uint64_t)ri << cqi_bitlen)
|
||||
| (uint64_t)mac->csirs_measurements.cqi;
|
||||
temp_payload_2 = ((uint64_t)pmi_x1 << pmi_x2_bitlen) | (uint64_t)pmi_x2;
|
||||
} else {
|
||||
p1_bits = nr_get_csi_bitlen(csi_report);
|
||||
padding_bitlen = p1_bits - (cri_bitlen + ri_bitlen + pmi_x1_bitlen + pmi_x2_bitlen + cqi_bitlen);
|
||||
temp_payload_1 = ((uint64_t)0 /* cri */ << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen + ri_bitlen))
|
||||
| ((uint64_t)ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen))
|
||||
| ((uint64_t)pmi_x1 << (cqi_bitlen + pmi_x2_bitlen)) | ((uint64_t)pmi_x2 << cqi_bitlen)
|
||||
| (uint64_t)mac->csirs_measurements.cqi;
|
||||
}
|
||||
|
||||
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
|
||||
temp_payload_2 = reverse_bits(temp_payload_2, p2_bits);
|
||||
|
||||
LOG_D(NR_MAC, "cri_bitlen = %d\n", cri_bitlen);
|
||||
LOG_D(NR_MAC, "ri_bitlen = %d\n", ri_bitlen);
|
||||
LOG_D(NR_MAC,
|
||||
"pmi_x1_bitlen = %d (i_1,1=%u i_1,2=%u i_1,3=%u -> X1=0x%x)\n",
|
||||
pmi_x1_bitlen,
|
||||
mac->csirs_measurements.i_1_1,
|
||||
mac->csirs_measurements.i_1_2,
|
||||
mac->csirs_measurements.i_1_3,
|
||||
pmi_x1);
|
||||
LOG_D(NR_MAC, "pmi_x2_bitlen = %d (i_2=%u)\n", pmi_x2_bitlen, pmi_x2);
|
||||
LOG_D(NR_MAC, "cqi_bitlen = %d\n", cqi_bitlen);
|
||||
LOG_D(NR_MAC, "csi_part1_payload = 0x%lx\n", temp_payload_1);
|
||||
LOG_D(NR_MAC, "csi_part2_payload = 0x%lx\n", temp_payload_2);
|
||||
LOG_D(NR_MAC, "part1_bits = %d\n", p1_bits);
|
||||
LOG_D(NR_MAC, "part2_bits = %d\n", p2_bits);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertFatal(p1_bits <= 32 && p2_bits <= 32, "Not supporting CSI report with more than 32 bits\n");
|
||||
nfapi_nr_ue_csi_payload_t csi = {.part1_payload = temp_payload_1, .part2_payload = temp_payload_2, .p1_bits = p1_bits, csi.p2_bits = p2_bits};
|
||||
nfapi_nr_ue_csi_payload_t csi = {
|
||||
.part1_payload = temp_payload_1,
|
||||
.part2_payload = temp_payload_2,
|
||||
.p1_bits = p1_bits,
|
||||
.p2_bits = p2_bits,
|
||||
};
|
||||
return csi;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user