csi_rx.c 43.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/***********************************************************************
*
* FILENAME    :  csi_rx.c
*
* MODULE      :
*
* DESCRIPTION :  function to receive the channel state information
*
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

36
#include "executables/nr-softmodem-common.h"
37 38 39
#include "nr_transport_proto_ue.h"
#include "PHY/phy_extern_nr_ue.h"
#include "common/utils/nr/nr_common.h"
40
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
41
#include "PHY/NR_UE_ESTIMATION/filt16a_32.h"
42

43 44
// 10*log10(pow(2,30))
#define pow_2_30_dB 90
45

46 47 48
// Additional memory allocation, because of applying the filter and the memory offset to ensure memory alignment
#define FILTER_MARGIN 32

49
//#define NR_CSIRS_DEBUG
50
//#define NR_CSIIM_DEBUG
51

52 53 54 55 56
void nr_det_A_MF_2x2(int32_t *a_mf_00,
                     int32_t *a_mf_01,
                     int32_t *a_mf_10,
                     int32_t *a_mf_11,
                     int32_t *det_fin,
57
                     const unsigned short nb_rb) {
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

  int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ;

  __m128i ad_re_128, bc_re_128, det_re_128;

  __m128i *a_mf_00_128 = (__m128i *)a_mf_00;
  __m128i *a_mf_01_128 = (__m128i *)a_mf_01;
  __m128i *a_mf_10_128 = (__m128i *)a_mf_10;
  __m128i *a_mf_11_128 = (__m128i *)a_mf_11;
  __m128i *det_fin_128 = (__m128i *)det_fin;

  for (int rb = 0; rb<3*nb_rb; rb++) {

    //complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d)
    //The imag part is often zero, we compute only the real part
    ad_re_128 = _mm_sign_epi16(a_mf_00_128[0],*(__m128i*)&nr_conjug2[0]);
    ad_re_128 = _mm_madd_epi16(ad_re_128,a_mf_11_128[0]); //Re: I_a0*I_d0 - Q_a1*Q_d1

    //complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c)
    //The imag part is often zero, we compute only the real part
    bc_re_128 = _mm_sign_epi16(a_mf_01_128[0],*(__m128i*)&nr_conjug2[0]);
    bc_re_128 = _mm_madd_epi16(bc_re_128,a_mf_10_128[0]); //Re: I_b0*I_c0 - Q_b1*Q_c1

    det_re_128 = _mm_sub_epi32(ad_re_128, bc_re_128);

    //det in Q30 format
    det_fin_128[0] = _mm_abs_epi32(det_re_128);

    det_fin_128+=1;
    a_mf_00_128+=1;
    a_mf_01_128+=1;
    a_mf_10_128+=1;
    a_mf_11_128+=1;
  }
  _mm_empty();
  _m_empty();
}

void nr_squared_matrix_element(int32_t *a,
                               int32_t *a_sq,
98
                               const unsigned short nb_rb) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
  __m128i *a_128 = (__m128i *)a;
  __m128i *a_sq_128 = (__m128i *)a_sq;
  for (int rb=0; rb<3*nb_rb; rb++) {
    a_sq_128[0] = _mm_madd_epi16(a_128[0], a_128[0]);
    a_sq_128+=1;
    a_128+=1;
  }
  _mm_empty();
  _m_empty();
}

void nr_numer_2x2(int32_t *a_00_sq,
                  int32_t *a_01_sq,
                  int32_t *a_10_sq,
                  int32_t *a_11_sq,
                  int32_t *num_fin,
115
                  const unsigned short nb_rb) {
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
  __m128i *a_00_sq_128 = (__m128i *)a_00_sq;
  __m128i *a_01_sq_128 = (__m128i *)a_01_sq;
  __m128i *a_10_sq_128 = (__m128i *)a_10_sq;
  __m128i *a_11_sq_128 = (__m128i *)a_11_sq;
  __m128i *num_fin_128 = (__m128i *)num_fin;
  for (int rb=0; rb<3*nb_rb; rb++) {
    __m128i sq_a_plus_sq_d_128 = _mm_add_epi32(a_00_sq_128[0], a_11_sq_128[0]);
    __m128i sq_b_plus_sq_c_128 = _mm_add_epi32(a_01_sq_128[0], a_10_sq_128[0]);
    num_fin_128[0] = _mm_add_epi32(sq_a_plus_sq_d_128, sq_b_plus_sq_c_128);
    num_fin_128+=1;
    a_00_sq_128+=1;
    a_01_sq_128+=1;
    a_10_sq_128+=1;
    a_11_sq_128+=1;
  }
  _mm_empty();
  _m_empty();
}

135
bool is_csi_rs_in_symbol(const fapi_nr_dl_config_csirs_pdu_rel15_t csirs_config_pdu, const int symbol) {
rmagueta's avatar
rmagueta committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

  bool ret = false;

  // 38.211-Table 7.4.1.5.3-1: CSI-RS locations within a slot
  switch(csirs_config_pdu.row){
    case 1:
    case 2:
    case 3:
    case 4:
    case 6:
    case 9:
      if(symbol == csirs_config_pdu.symb_l0) {
        ret = true;
      }
      break;
    case 5:
    case 7:
    case 8:
    case 10:
    case 11:
    case 12:
      if(symbol == csirs_config_pdu.symb_l0 || symbol == (csirs_config_pdu.symb_l0+1) ) {
        ret = true;
      }
      break;
    case 13:
    case 14:
    case 16:
    case 17:
      if(symbol == csirs_config_pdu.symb_l0 || symbol == (csirs_config_pdu.symb_l0+1) ||
          symbol == csirs_config_pdu.symb_l1 || symbol == (csirs_config_pdu.symb_l1+1)) {
        ret = true;
      }
      break;
    case 15:
    case 18:
      if(symbol == csirs_config_pdu.symb_l0 || symbol == (csirs_config_pdu.symb_l0+1) || symbol == (csirs_config_pdu.symb_l0+2) ) {
        ret = true;
      }
      break;
    default:
      AssertFatal(0==1, "Row %d is not valid for CSI Table 7.4.1.5.3-1\n", csirs_config_pdu.row);
  }

  return ret;
}

183 184 185
int nr_get_csi_rs_signal(const PHY_VARS_NR_UE *ue,
                         const UE_nr_rxtx_proc_t *proc,
                         const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
186
                         const nr_csi_info_t *nr_csi_info,
187 188 189 190
                         const uint8_t N_cdm_groups,
                         const uint8_t CDM_group_size,
                         const uint8_t k_prime,
                         const uint8_t l_prime,
191 192 193
                         const uint8_t *j_cdm,
                         const uint8_t *k_overline,
                         const uint8_t *l_overline,
194 195
                         int32_t csi_rs_received_signal[][ue->frame_parms.samples_per_slot_wCP],
                         uint32_t *rsrp,
196
                         int *rsrp_dBm,
197
                         c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) {
198

199
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
200 201
  uint16_t meas_count = 0;
  uint32_t rsrp_sum = 0;
202

203 204 205 206 207 208
  for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) {

    for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs); rb++) {

      // for freq density 0.5 checks if even or odd RB
      if(csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != (rb % 2)) {
rmagueta's avatar
rmagueta committed
209 210
        continue;
      }
211

212 213
      for (int cdm_id = 0; cdm_id < N_cdm_groups; cdm_id++) {
        for (int s = 0; s < CDM_group_size; s++)  {
214 215

          // loop over frequency resource elements within a group
216
          for (int kp = 0; kp <= k_prime; kp++) {
217

218
            uint16_t k = (frame_parms->first_carrier_offset + (rb*NR_NB_SC_PER_RB)+k_overline[cdm_id] + kp) % frame_parms->ofdm_symbol_size;
219 220

            // loop over time resource elements within a group
221
            for (int lp = 0; lp <= l_prime; lp++) {
222
              uint16_t symb = lp + l_overline[cdm_id];
223
              uint64_t symbol_offset = symb*frame_parms->ofdm_symbol_size;
224
              c16_t *rx_signal = &rxdataF[ant_rx][symbol_offset];
225 226 227
              c16_t *rx_csi_rs_signal = (c16_t*)&csi_rs_received_signal[ant_rx][symbol_offset];
              rx_csi_rs_signal[k].r = rx_signal[k].r;
              rx_csi_rs_signal[k].i = rx_signal[k].i;
228

229 230 231 232 233
              rsrp_sum += (((int32_t)(rx_csi_rs_signal[k].r)*rx_csi_rs_signal[k].r) +
                           ((int32_t)(rx_csi_rs_signal[k].i)*rx_csi_rs_signal[k].i));

              meas_count++;

234
#ifdef NR_CSIRS_DEBUG
235
              int dataF_offset = proc->nr_slot_rx*ue->frame_parms.samples_per_slot_wCP;
236
              uint16_t port_tx = s+j_cdm[cdm_id]*CDM_group_size;
237
              c16_t *tx_csi_rs_signal = (c16_t*)&nr_csi_info->csi_rs_generated_signal[port_tx][symbol_offset+dataF_offset];
238
              LOG_I(NR_PHY, "l,k (%2d,%4d) |\tport_tx %d (%4d,%4d)\tant_rx %d (%4d,%4d)\n",
239 240 241
                    symb,
                    k,
                    port_tx+3000,
242 243
                    tx_csi_rs_signal[k].r,
                    tx_csi_rs_signal[k].i,
244
                    ant_rx,
245 246
                    rx_csi_rs_signal[k].r,
                    rx_csi_rs_signal[k].i);
247
#endif
248 249 250
            }
          }
        }
251 252 253 254
      }
    }
  }

255 256 257 258 259 260 261 262 263

  *rsrp = rsrp_sum/meas_count;
  *rsrp_dBm = dB_fixed(*rsrp) + 30 - pow_2_30_dB
      - ((int)openair0_cfg[0].rx_gain[0] - (int)openair0_cfg[0].rx_gain_offset[0]) - dB_fixed(ue->frame_parms.ofdm_symbol_size);

#ifdef NR_CSIRS_DEBUG
  LOG_I(NR_PHY, "RSRP = %i (%i dBm)\n", *rsrp, *rsrp_dBm);
#endif

264 265 266
  return 0;
}

267 268
uint32_t calc_power_csirs(const uint16_t *x, const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu)
{
269 270 271
  uint64_t sum_x = 0;
  uint64_t sum_x2 = 0;
  uint16_t size = 0;
272 273
  for (int rb = 0; rb < csirs_config_pdu->nr_of_rbs; rb++) {
    if (csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != ((rb + csirs_config_pdu->start_rb) % 2)) {
274 275
      continue;
    }
276 277
    sum_x = sum_x + x[rb];
    sum_x2 = sum_x2 + x[rb] * x[rb];
278 279
    size++;
  }
280
  return sum_x2 / size - (sum_x / size) * (sum_x / size);
281 282
}

283 284 285
int nr_csi_rs_channel_estimation(const PHY_VARS_NR_UE *ue,
                                 const UE_nr_rxtx_proc_t *proc,
                                 const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
286
                                 const nr_csi_info_t *nr_csi_info,
287
                                 const int32_t **csi_rs_generated_signal,
288
                                 const int32_t csi_rs_received_signal[][ue->frame_parms.samples_per_slot_wCP],
289 290 291 292 293
                                 const uint8_t N_cdm_groups,
                                 const uint8_t CDM_group_size,
                                 const uint8_t k_prime,
                                 const uint8_t l_prime,
                                 const uint8_t N_ports,
294 295 296
                                 const uint8_t *j_cdm,
                                 const uint8_t *k_overline,
                                 const uint8_t *l_overline,
297
                                 uint8_t mem_offset,
298
                                 int32_t csi_rs_ls_estimated_channel[][N_ports][ue->frame_parms.ofdm_symbol_size],
299
                                 int32_t csi_rs_estimated_channel_freq[][N_ports][ue->frame_parms.ofdm_symbol_size + FILTER_MARGIN],
300 301
                                 int16_t *log2_re,
                                 int16_t *log2_maxh,
302 303
                                 uint32_t *noise_power) {

304 305
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
  const int dataF_offset = proc->nr_slot_rx*ue->frame_parms.samples_per_slot_wCP;
306
  *noise_power = 0;
307
  int maxh = 0;
308
  int count = 0;
309

310
  for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) {
311

rmagueta's avatar
rmagueta committed
312 313
    /// LS channel estimation

314
    for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
315
      memset(csi_rs_ls_estimated_channel[ant_rx][port_tx], 0, frame_parms->ofdm_symbol_size*sizeof(int32_t));
316 317 318 319 320 321
    }

    for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs); rb++) {

      // for freq density 0.5 checks if even or odd RB
      if(csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != (rb % 2)) {
rmagueta's avatar
rmagueta committed
322 323
        continue;
      }
324

325 326
      for (int cdm_id = 0; cdm_id < N_cdm_groups; cdm_id++) {
        for (int s = 0; s < CDM_group_size; s++)  {
327

328
          uint16_t port_tx = s+j_cdm[cdm_id]*CDM_group_size;
329 330

          // loop over frequency resource elements within a group
331
          for (int kp = 0; kp <= k_prime; kp++) {
332

333
            uint16_t kinit = (frame_parms->first_carrier_offset + rb*NR_NB_SC_PER_RB) % frame_parms->ofdm_symbol_size;
334
            uint16_t k = kinit + k_overline[cdm_id] + kp;
335 336

            // loop over time resource elements within a group
337
            for (int lp = 0; lp <= l_prime; lp++) {
338
              uint16_t symb = lp + l_overline[cdm_id];
339
              uint64_t symbol_offset = symb*frame_parms->ofdm_symbol_size;
340 341 342
              c16_t *tx_csi_rs_signal = (c16_t*)&csi_rs_generated_signal[port_tx][symbol_offset+dataF_offset];
              c16_t *rx_csi_rs_signal = (c16_t*)&csi_rs_received_signal[ant_rx][symbol_offset];
              c16_t *csi_rs_ls_estimated_channel16 = (c16_t*)&csi_rs_ls_estimated_channel[ant_rx][port_tx][0];
343

344 345
              int16_t csi_rs_ls_estimated_channel_re = (int16_t)(((int32_t)tx_csi_rs_signal[k].r*rx_csi_rs_signal[k].r + (int32_t)tx_csi_rs_signal[k].i*rx_csi_rs_signal[k].i)>>nr_csi_info->csi_rs_generated_signal_bits);
              int16_t csi_rs_ls_estimated_channel_im = (int16_t)(((int32_t)tx_csi_rs_signal[k].r*rx_csi_rs_signal[k].i - (int32_t)tx_csi_rs_signal[k].i*rx_csi_rs_signal[k].r)>>nr_csi_info->csi_rs_generated_signal_bits);
346

347 348
              // This is not just the LS estimation for each (k,l), but also the sum of the different contributions
              // for the sake of optimizing the memory used.
349 350
              csi_rs_ls_estimated_channel16[kinit].r += csi_rs_ls_estimated_channel_re;
              csi_rs_ls_estimated_channel16[kinit].i += csi_rs_ls_estimated_channel_im;
351 352 353
            }
          }
        }
354 355
      }
    }
356

357 358 359 360 361 362 363
#ifdef NR_CSIRS_DEBUG
    for(int symb = 0; symb < NR_SYMBOLS_PER_SLOT; symb++) {
      if(!is_csi_rs_in_symbol(*csirs_config_pdu,symb)) {
        continue;
      }
      for(int k = 0; k<frame_parms->ofdm_symbol_size; k++) {
        LOG_I(NR_PHY, "l,k (%2d,%4d) | ", symb, k);
364
        for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
365
          uint64_t symbol_offset = symb*frame_parms->ofdm_symbol_size;
366 367 368
          c16_t *tx_csi_rs_signal = (c16_t*)&csi_rs_generated_signal[port_tx][symbol_offset+dataF_offset];
          c16_t *rx_csi_rs_signal = (c16_t*)&csi_rs_received_signal[ant_rx][symbol_offset];
          c16_t *csi_rs_ls_estimated_channel16 = (c16_t*)&csi_rs_ls_estimated_channel[ant_rx][port_tx][0];
369 370
          printf("port_tx %d --> ant_rx %d, tx (%4d,%4d), rx (%4d,%4d), ls (%4d,%4d) | ",
                 port_tx+3000, ant_rx,
371 372 373
                 tx_csi_rs_signal[k].r, tx_csi_rs_signal[k].i,
                 rx_csi_rs_signal[k].r, rx_csi_rs_signal[k].i,
                 csi_rs_ls_estimated_channel16[k].r, csi_rs_ls_estimated_channel16[k].i);
374 375 376 377 378 379
        }
        printf("\n");
      }
    }
#endif

380 381
    /// Channel interpolation

382
    for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
383
      memset(csi_rs_estimated_channel_freq[ant_rx][port_tx], 0, (frame_parms->ofdm_symbol_size + FILTER_MARGIN) * sizeof(int32_t));
384 385 386 387 388 389 390
    }

    for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs); rb++) {

      // for freq density 0.5 checks if even or odd RB
      if(csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != (rb % 2)) {
        continue;
391
      }
392

393 394
      count++;

395
      uint16_t k = (frame_parms->first_carrier_offset + rb*NR_NB_SC_PER_RB) % frame_parms->ofdm_symbol_size;
396
      uint16_t k_offset = k + mem_offset;
397
      for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
398
        int16_t *csi_rs_ls_estimated_channel16 = (int16_t*)&csi_rs_ls_estimated_channel[ant_rx][port_tx][k];
399
        int16_t *csi_rs_estimated_channel16 = (int16_t *)&csi_rs_estimated_channel_freq[ant_rx][port_tx][k_offset];
400
        if( (k == 0) || (k == frame_parms->first_carrier_offset) ) { // Start of OFDM symbol case or first occupied subcarrier case
401
          multadd_real_vector_complex_scalar(filt24_start, csi_rs_ls_estimated_channel16, csi_rs_estimated_channel16, 24);
402
        } else if( ( (k + NR_NB_SC_PER_RB) >= frame_parms->ofdm_symbol_size) ||
403
                   (rb == (csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs-1)) ) { // End of OFDM symbol case or Last occupied subcarrier case
404
          multadd_real_vector_complex_scalar(filt24_end, csi_rs_ls_estimated_channel16, csi_rs_estimated_channel16 - 3*sizeof(uint64_t), 24);
405
        } else { // Middle case
406
          multadd_real_vector_complex_scalar(filt24_middle, csi_rs_ls_estimated_channel16, csi_rs_estimated_channel16 - 3*sizeof(uint64_t), 24);
407
        }
408
      }
409
    }
410

411
    /// Power noise estimation
412 413
    uint16_t noise_real[frame_parms->nb_antennas_rx][N_ports][csirs_config_pdu->nr_of_rbs];
    uint16_t noise_imag[frame_parms->nb_antennas_rx][N_ports][csirs_config_pdu->nr_of_rbs];
414 415 416 417 418
    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;
419
      uint16_t k_offset = k + mem_offset;
420
      for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
421
        c16_t *csi_rs_ls_estimated_channel16 = (c16_t*)&csi_rs_ls_estimated_channel[ant_rx][port_tx][k];
422
        c16_t *csi_rs_estimated_channel16 = (c16_t *)&csi_rs_estimated_channel_freq[ant_rx][port_tx][k_offset];
423 424 425
        noise_real[ant_rx][port_tx][rb-csirs_config_pdu->start_rb] = abs(csi_rs_ls_estimated_channel16->r-csi_rs_estimated_channel16->r);
        noise_imag[ant_rx][port_tx][rb-csirs_config_pdu->start_rb] = abs(csi_rs_ls_estimated_channel16->i-csi_rs_estimated_channel16->i);
        maxh = cmax3(maxh, abs(csi_rs_estimated_channel16->r), abs(csi_rs_estimated_channel16->i));
426 427
      }
    }
428
    for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
429 430 431
      *noise_power += (calc_power_csirs(noise_real[ant_rx][port_tx], csirs_config_pdu) + calc_power_csirs(noise_imag[ant_rx][port_tx],csirs_config_pdu));
    }

432 433
#ifdef NR_CSIRS_DEBUG
    for(int k = 0; k<frame_parms->ofdm_symbol_size; k++) {
434 435 436
      int rb = k >= frame_parms->first_carrier_offset ?
               (k - frame_parms->first_carrier_offset)/NR_NB_SC_PER_RB :
               (k + frame_parms->ofdm_symbol_size - frame_parms->first_carrier_offset)/NR_NB_SC_PER_RB;
437
      LOG_I(NR_PHY, "(k = %4d) |\t", k);
438
      for(uint16_t port_tx = 0; port_tx<N_ports; port_tx++) {
439
        c16_t *csi_rs_ls_estimated_channel16 = (c16_t*)&csi_rs_ls_estimated_channel[ant_rx][port_tx][0];
440
        c16_t *csi_rs_estimated_channel16 = (c16_t *)&csi_rs_estimated_channel_freq[ant_rx][port_tx][mem_offset];
rmagueta's avatar
rmagueta committed
441
        printf("Channel port_tx %d --> ant_rx %d : ls (%4d,%4d), int (%4d,%4d), noise (%4d,%4d) | ",
442
               port_tx+3000, ant_rx,
443 444
               csi_rs_ls_estimated_channel16[k].r, csi_rs_ls_estimated_channel16[k].i,
               csi_rs_estimated_channel16[k].r, csi_rs_estimated_channel16[k].i,
445 446
               rb >= csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs ? 0 : noise_real[ant_rx][port_tx][rb-csirs_config_pdu->start_rb],
               rb >= csirs_config_pdu->start_rb+csirs_config_pdu->nr_of_rbs ? 0 : noise_imag[ant_rx][port_tx][rb-csirs_config_pdu->start_rb]);
447 448
      }
      printf("\n");
449 450 451
    }
#endif

452 453
  }

454
  *noise_power /= (frame_parms->nb_antennas_rx*N_ports);
455 456
  *log2_maxh = log2_approx(maxh-1);
  *log2_re = log2_approx(count-1);
457 458 459 460 461

#ifdef NR_CSIRS_DEBUG
  LOG_I(NR_PHY, "Noise power estimation based on CSI-RS: %i\n", *noise_power);
#endif

462 463 464
  return 0;
}

465 466
int nr_csi_rs_ri_estimation(const PHY_VARS_NR_UE *ue,
                            const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
467
                            const nr_csi_info_t *nr_csi_info,
468
                            const uint8_t N_ports,
469 470
                            uint8_t mem_offset,
                            int32_t csi_rs_estimated_channel_freq[][N_ports][ue->frame_parms.ofdm_symbol_size + FILTER_MARGIN],
471
                            const int16_t log2_maxh,
472 473
                            uint8_t *rank_indicator) {

474 475
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
  const int16_t cond_dB_threshold = 5;
rmagueta's avatar
rmagueta committed
476
  int count = 0;
477 478
  *rank_indicator = 0;

479
  if (ue->frame_parms.nb_antennas_rx == 1 || N_ports == 1) {
rmagueta's avatar
rmagueta committed
480
    return 0;
481
  } else if( !(ue->frame_parms.nb_antennas_rx == 2 && N_ports == 2) ) {
rmagueta's avatar
rmagueta committed
482
    LOG_W(NR_PHY, "Rank indicator computation is not implemented for %i x %i system\n",
483
          ue->frame_parms.nb_antennas_rx, N_ports);
rmagueta's avatar
rmagueta committed
484 485 486
    return -1;
  }

487 488 489 490 491
  /* Example 2x2: Hh x H =
  *            | conjch00 conjch10 | x | ch00 ch01 | = | conjch00*ch00+conjch10*ch10 conjch00*ch01+conjch10*ch11 |
  *            | conjch01 conjch11 |   | ch10 ch11 |   | conjch01*ch00+conjch11*ch10 conjch01*ch01+conjch11*ch11 |
  */

492 493 494 495 496
  int32_t csi_rs_estimated_conjch_ch[frame_parms->nb_antennas_rx][N_ports][frame_parms->nb_antennas_rx][N_ports][frame_parms->ofdm_symbol_size + FILTER_MARGIN] __attribute__((aligned(32)));
  int32_t csi_rs_estimated_A_MF[N_ports][N_ports][frame_parms->ofdm_symbol_size + FILTER_MARGIN] __attribute__((aligned(32)));
  int32_t csi_rs_estimated_A_MF_sq[N_ports][N_ports][frame_parms->ofdm_symbol_size + FILTER_MARGIN] __attribute__((aligned(32)));
  int32_t csi_rs_estimated_determ_fin[frame_parms->ofdm_symbol_size + FILTER_MARGIN] __attribute__((aligned(32)));
  int32_t csi_rs_estimated_numer_fin[frame_parms->ofdm_symbol_size + FILTER_MARGIN] __attribute__((aligned(32)));
497 498
  const uint8_t sum_shift = 1; // log2(2x2) = 2, which is a shift of 1 bit
  
499 500 501 502 503 504
  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;
505
    uint16_t k_offset = k + mem_offset;
506 507

    for (int ant_rx_conjch = 0; ant_rx_conjch < frame_parms->nb_antennas_rx; ant_rx_conjch++) {
508
      for(uint16_t port_tx_conjch = 0; port_tx_conjch < N_ports; port_tx_conjch++) {
509
        for (int ant_rx_ch = 0; ant_rx_ch < frame_parms->nb_antennas_rx; ant_rx_ch++) {
510
          for(uint16_t port_tx_ch = 0; port_tx_ch < N_ports; port_tx_ch++) {
511 512

            // conjch x ch computation
513 514 515
            nr_conjch0_mult_ch1(&csi_rs_estimated_channel_freq[ant_rx_conjch][port_tx_conjch][k_offset],
                                &csi_rs_estimated_channel_freq[ant_rx_ch][port_tx_ch][k_offset],
                                &csi_rs_estimated_conjch_ch[ant_rx_conjch][port_tx_conjch][ant_rx_ch][port_tx_ch][k_offset],
516
                                1,
517
                                log2_maxh);
518 519 520

            // construct Hh x H elements
            if(ant_rx_conjch == ant_rx_ch) {
521 522
              nr_a_sum_b((__m128i *)&csi_rs_estimated_A_MF[port_tx_conjch][port_tx_ch][k_offset],
                         (__m128i *)&csi_rs_estimated_conjch_ch[ant_rx_conjch][port_tx_conjch][ant_rx_ch][port_tx_ch][k_offset],
523 524
                         1);
            }
525 526 527 528 529 530
          }
        }
      }
    }

    // compute the determinant of A_MF (denominator)
531 532 533 534 535
    nr_det_A_MF_2x2(&csi_rs_estimated_A_MF[0][0][k_offset],
                    &csi_rs_estimated_A_MF[0][1][k_offset],
                    &csi_rs_estimated_A_MF[1][0][k_offset],
                    &csi_rs_estimated_A_MF[1][1][k_offset],
                    &csi_rs_estimated_determ_fin[k_offset],
536
                    1);
537 538

    // compute the square of A_MF (numerator)
539 540 541 542 543 544 545 546 547
    nr_squared_matrix_element(&csi_rs_estimated_A_MF[0][0][k_offset], &csi_rs_estimated_A_MF_sq[0][0][k_offset], 1);
    nr_squared_matrix_element(&csi_rs_estimated_A_MF[0][1][k_offset], &csi_rs_estimated_A_MF_sq[0][1][k_offset], 1);
    nr_squared_matrix_element(&csi_rs_estimated_A_MF[1][0][k_offset], &csi_rs_estimated_A_MF_sq[1][0][k_offset], 1);
    nr_squared_matrix_element(&csi_rs_estimated_A_MF[1][1][k_offset], &csi_rs_estimated_A_MF_sq[1][1][k_offset], 1);
    nr_numer_2x2(&csi_rs_estimated_A_MF_sq[0][0][k_offset],
                 &csi_rs_estimated_A_MF_sq[0][1][k_offset],
                 &csi_rs_estimated_A_MF_sq[1][0][k_offset],
                 &csi_rs_estimated_A_MF_sq[1][1][k_offset],
                 &csi_rs_estimated_numer_fin[k_offset],
548
                 1);
549

550
#ifdef NR_CSIRS_DEBUG
551 552
    for(uint16_t port_tx_conjch = 0; port_tx_conjch < N_ports; port_tx_conjch++) {
      for(uint16_t port_tx_ch = 0; port_tx_ch < N_ports; port_tx_ch++) {
553
        c16_t *csi_rs_estimated_A_MF_k = (c16_t *) &csi_rs_estimated_A_MF[port_tx_conjch][port_tx_ch][k_offset];
554
        LOG_I(NR_PHY, "(%i) csi_rs_estimated_A_MF[%i][%i] = (%i, %i)\n",
555
              k, port_tx_conjch, port_tx_ch, csi_rs_estimated_A_MF_k->r, csi_rs_estimated_A_MF_k->i);
556
        c16_t *csi_rs_estimated_A_MF_sq_k = (c16_t *) &csi_rs_estimated_A_MF_sq[port_tx_conjch][port_tx_ch][k_offset];
557
        LOG_I(NR_PHY, "(%i) csi_rs_estimated_A_MF_sq[%i][%i] = (%i, %i)\n",
558
              k, port_tx_conjch, port_tx_ch, csi_rs_estimated_A_MF_sq_k->r, csi_rs_estimated_A_MF_sq_k->i);
559 560
      }
    }
561 562
    LOG_I(NR_PHY, "(%i) csi_rs_estimated_determ_fin = %i\n", k, csi_rs_estimated_determ_fin[k_offset]);
    LOG_I(NR_PHY, "(%i) csi_rs_estimated_numer_fin = %i\n", k, csi_rs_estimated_numer_fin[k_offset]>>sum_shift);
563 564
#endif

565 566
    // compute the conditional number
    for (int sc_idx=0; sc_idx < NR_NB_SC_PER_RB; sc_idx++) {
567 568
      int8_t csi_rs_estimated_denum_db = dB_fixed(csi_rs_estimated_determ_fin[k_offset + sc_idx]);
      int8_t csi_rs_estimated_numer_db = dB_fixed(csi_rs_estimated_numer_fin[k_offset + sc_idx]>>sum_shift);
569
      int8_t cond_db = csi_rs_estimated_numer_db - csi_rs_estimated_denum_db;
570 571 572 573 574 575 576

#ifdef NR_CSIRS_DEBUG
      LOG_I(NR_PHY, "csi_rs_estimated_denum_db = %i\n", csi_rs_estimated_denum_db);
      LOG_I(NR_PHY, "csi_rs_estimated_numer_db = %i\n", csi_rs_estimated_numer_db);
      LOG_I(NR_PHY, "cond_db = %i\n", cond_db);
#endif

577 578 579 580 581 582 583 584 585 586 587 588 589
      if (cond_db < cond_dB_threshold) {
        count++;
      } else {
        count--;
      }
    }
  }

  // conditional number is lower than cond_dB_threshold in half on more REs
  if (count > 0) {
    *rank_indicator = 1;
  }

590 591 592 593 594
#ifdef NR_CSIRS_DEBUG
  LOG_I(NR_PHY, "count = %i\n", count);
  LOG_I(NR_PHY, "rank = %i\n", (*rank_indicator)+1);
#endif

595 596 597
  return 0;
}

598 599
int nr_csi_rs_pmi_estimation(const PHY_VARS_NR_UE *ue,
                             const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
600
                             const nr_csi_info_t *nr_csi_info,
601
                             const uint8_t N_ports,
602 603
                             uint8_t mem_offset,
                             const int32_t csi_rs_estimated_channel_freq[][N_ports][ue->frame_parms.ofdm_symbol_size + FILTER_MARGIN],
604 605 606
                             const uint32_t interference_plus_noise_power,
                             const uint8_t rank_indicator,
                             const int16_t log2_re,
rmagueta's avatar
rmagueta committed
607
                             uint8_t *i1,
608
                             uint8_t *i2,
rmagueta's avatar
rmagueta committed
609
                             uint32_t *precoded_sinr_dB) {
rmagueta's avatar
rmagueta committed
610

611
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
rmagueta's avatar
rmagueta committed
612 613 614 615 616 617 618 619 620
  memset(i1,0,3*sizeof(uint8_t));
  i2[0] = 0;

  // 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.
621 622 623
  // 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.

624
  if(N_ports == 1 || interference_plus_noise_power == 0) {
625 626 627
    return 0;
  }

rmagueta's avatar
rmagueta committed
628
  if(rank_indicator == 0 || rank_indicator == 1) {
629

630 631 632 633 634 635
    int32_t sum_re[4] = {0};
    int32_t sum_im[4] = {0};
    int32_t sum2_re[4] = {0};
    int32_t sum2_im[4] = {0};
    int32_t tested_precoded_sinr[4] = {0};

rmagueta's avatar
rmagueta committed
636 637 638 639 640 641
    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;
642
      uint16_t k_offset = k + mem_offset;
rmagueta's avatar
rmagueta committed
643
      for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) {
644 645
        c16_t *csi_rs_estimated_channel_p0 = (c16_t *) &csi_rs_estimated_channel_freq[ant_rx][0][k_offset];
        c16_t *csi_rs_estimated_channel_p1 = (c16_t *) &csi_rs_estimated_channel_freq[ant_rx][1][k_offset];
rmagueta's avatar
rmagueta committed
646 647

        // H_p0 + 1*H_p1 = (H_p0_re + H_p1_re) + 1j*(H_p0_im + H_p1_im)
648 649 650 651
        sum_re[0] += (csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->r);
        sum_im[0] += (csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->i);
        sum2_re[0] += ((csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->r)*(csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->r))>>log2_re;
        sum2_im[0] += ((csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->i)*(csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->i))>>log2_re;
rmagueta's avatar
rmagueta committed
652 653

        // H_p0 + 1j*H_p1 = (H_p0_re - H_p1_im) + 1j*(H_p0_im + H_p1_re)
654 655 656 657
        sum_re[1] += (csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->i);
        sum_im[1] += (csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->r);
        sum2_re[1] += ((csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->i)*(csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->i))>>log2_re;
        sum2_im[1] += ((csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->r)*(csi_rs_estimated_channel_p0->i+csi_rs_estimated_channel_p1->r))>>log2_re;
rmagueta's avatar
rmagueta committed
658 659

        // H_p0 - 1*H_p1 = (H_p0_re - H_p1_re) + 1j*(H_p0_im - H_p1_im)
660 661 662 663
        sum_re[2] += (csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->r);
        sum_im[2] += (csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->i);
        sum2_re[2] += ((csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->r)*(csi_rs_estimated_channel_p0->r-csi_rs_estimated_channel_p1->r))>>log2_re;
        sum2_im[2] += ((csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->i)*(csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->i))>>log2_re;
rmagueta's avatar
rmagueta committed
664 665

        // H_p0 - 1j*H_p1 = (H_p0_re + H_p1_im) + 1j*(H_p0_im - H_p1_re)
666 667 668 669
        sum_re[3] += (csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->i);
        sum_im[3] += (csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->r);
        sum2_re[3] += ((csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->i)*(csi_rs_estimated_channel_p0->r+csi_rs_estimated_channel_p1->i))>>log2_re;
        sum2_im[3] += ((csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->r)*(csi_rs_estimated_channel_p0->i-csi_rs_estimated_channel_p1->r))>>log2_re;
rmagueta's avatar
rmagueta committed
670 671 672
      }
    }

673
    // We should perform >>nr_csi_info->log2_re here for all terms, but since sum2_re and sum2_im can be high values,
674 675
    // we performed this above.
    for(int p = 0; p<4; p++) {
676 677
      int32_t power_re = sum2_re[p] - (sum_re[p]>>log2_re)*(sum_re[p]>>log2_re);
      int32_t power_im = sum2_im[p] - (sum_im[p]>>log2_re)*(sum_im[p]>>log2_re);
678
      tested_precoded_sinr[p] = (power_re+power_im)/(int32_t)interference_plus_noise_power;
679 680
    }

rmagueta's avatar
rmagueta committed
681 682
    if(rank_indicator == 0) {
      for(int tested_i2 = 0; tested_i2 < 4; tested_i2++) {
683
        if(tested_precoded_sinr[tested_i2] > tested_precoded_sinr[i2[0]]) {
rmagueta's avatar
rmagueta committed
684 685
          i2[0] = tested_i2;
        }
rmagueta's avatar
rmagueta committed
686
      }
rmagueta's avatar
rmagueta committed
687
      *precoded_sinr_dB = dB_fixed(tested_precoded_sinr[i2[0]]);
rmagueta's avatar
rmagueta committed
688
    } else {
689
      i2[0] = tested_precoded_sinr[0]+tested_precoded_sinr[2] > tested_precoded_sinr[1]+tested_precoded_sinr[3] ? 0 : 1;
690
      *precoded_sinr_dB = dB_fixed((tested_precoded_sinr[i2[0]] + tested_precoded_sinr[i2[0]+2])>>1);
rmagueta's avatar
rmagueta committed
691 692
    }

693
  } else {
rmagueta's avatar
rmagueta committed
694 695
    LOG_W(NR_PHY, "PMI computation is not implemented for rank indicator %i\n", rank_indicator+1);
    return -1;
696 697 698 699 700
  }

  return 0;
}

701
int nr_csi_rs_cqi_estimation(const uint32_t precoded_sinr,
rmagueta's avatar
rmagueta committed
702 703 704 705
                             uint8_t *cqi) {

  *cqi = 0;

rmagueta's avatar
rmagueta committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
  // Default SINR table for an AWGN channel for SISO scenario, considering 0.1 BLER condition and TS 38.214 Table 5.2.2.1-2
  if(precoded_sinr>0 && precoded_sinr<=2) {
    *cqi = 4;
  } else if(precoded_sinr==3) {
    *cqi = 5;
  } else if(precoded_sinr>3 && precoded_sinr<=5) {
    *cqi = 6;
  } else if(precoded_sinr>5 && precoded_sinr<=7) {
    *cqi = 7;
  } else if(precoded_sinr>7 && precoded_sinr<=9) {
    *cqi = 8;
  } else if(precoded_sinr==10) {
    *cqi = 9;
  } else if(precoded_sinr>10 && precoded_sinr<=12) {
    *cqi = 10;
  } else if(precoded_sinr>12 && precoded_sinr<=15) {
    *cqi = 11;
  } else if(precoded_sinr==16) {
    *cqi = 12;
  } else if(precoded_sinr>16 && precoded_sinr<=18) {
    *cqi = 13;
  } else if(precoded_sinr==19) {
    *cqi = 14;
  } else if(precoded_sinr>19) {
    *cqi = 15;
  }

rmagueta's avatar
rmagueta committed
733 734 735
  return 0;
}

736 737 738
int nr_csi_im_power_estimation(const PHY_VARS_NR_UE *ue,
                               const UE_nr_rxtx_proc_t *proc,
                               const fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu,
739
                               uint32_t *interference_plus_noise_power,
740
                               c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) {
741

742
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
743

744 745
  const uint16_t end_rb = csiim_config_pdu->start_rb + csiim_config_pdu->nr_of_rbs > csiim_config_pdu->bwp_size ?
                          csiim_config_pdu->bwp_size : csiim_config_pdu->start_rb + csiim_config_pdu->nr_of_rbs;
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773

  int32_t count = 0;
  int32_t sum_re = 0;
  int32_t sum_im = 0;
  int32_t sum2_re = 0;
  int32_t sum2_im = 0;

  int l_csiim[4] = {-1, -1, -1, -1};

  for(int symb_idx = 0; symb_idx < 4; symb_idx++) {

    uint8_t symb = csiim_config_pdu->l_csiim[symb_idx];
    bool done = false;
    for (int symb_idx2 = 0; symb_idx2 < symb_idx; symb_idx2++) {
      if (l_csiim[symb_idx2] == symb) {
        done = true;
      }
    }

    if (done) {
      continue;
    }

    l_csiim[symb_idx] = symb;
    uint64_t symbol_offset = symb*frame_parms->ofdm_symbol_size;

    for (int ant_rx = 0; ant_rx < frame_parms->nb_antennas_rx; ant_rx++) {

774
      c16_t *rx_signal = &rxdataF[ant_rx][symbol_offset];
775 776 777 778 779 780 781 782

      for (int rb = csiim_config_pdu->start_rb; rb < end_rb; rb++) {

        uint16_t sc0_offset = (frame_parms->first_carrier_offset + rb*NR_NB_SC_PER_RB) % frame_parms->ofdm_symbol_size;

        for (int sc_idx = 0; sc_idx<4; sc_idx++) {

          uint16_t sc = sc0_offset + csiim_config_pdu->k_csiim[sc_idx];
783 784 785
          if (sc >= frame_parms->ofdm_symbol_size) {
            sc -= frame_parms->ofdm_symbol_size;
          }
786 787

#ifdef NR_CSIIM_DEBUG
788
          LOG_I(NR_PHY, "(ant_rx %i, sc %i) real %i, imag %i\n", ant_rx, sc, rx_signal[sc].r, rx_signal[sc].i);
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
#endif

          sum_re += rx_signal[sc].r;
          sum_im += rx_signal[sc].i;
          sum2_re += rx_signal[sc].r*rx_signal[sc].r;
          sum2_im += rx_signal[sc].i*rx_signal[sc].i;
          count++;
        }
      }
    }
  }

  int32_t power_re = sum2_re/count - (sum_re/count)*(sum_re/count);
  int32_t power_im = sum2_im/count - (sum_im/count)*(sum_im/count);

  *interference_plus_noise_power = power_re + power_im;

#ifdef NR_CSIIM_DEBUG
  LOG_I(NR_PHY, "interference_plus_noise_power based on CSI-IM = %i\n", *interference_plus_noise_power);
#endif

  return 0;
}

813
int nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) {
814

815
  int gNB_id = proc->gNB_id;
816 817 818 819
  if(!ue->csiim_vars[gNB_id]->active) {
    return -1;
  }

820
  const fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu = (fapi_nr_dl_config_csiim_pdu_rel15_t*)&ue->csiim_vars[gNB_id]->csiim_config_pdu;
821 822 823 824 825 826 827 828 829 830 831

#ifdef NR_CSIIM_DEBUG
  LOG_I(NR_PHY, "csiim_config_pdu->bwp_size = %i\n", csiim_config_pdu->bwp_size);
  LOG_I(NR_PHY, "csiim_config_pdu->bwp_start = %i\n", csiim_config_pdu->bwp_start);
  LOG_I(NR_PHY, "csiim_config_pdu->subcarrier_spacing = %i\n", csiim_config_pdu->subcarrier_spacing);
  LOG_I(NR_PHY, "csiim_config_pdu->start_rb = %i\n", csiim_config_pdu->start_rb);
  LOG_I(NR_PHY, "csiim_config_pdu->nr_of_rbs = %i\n", csiim_config_pdu->nr_of_rbs);
  LOG_I(NR_PHY, "csiim_config_pdu->k_csiim = %i.%i.%i.%i\n", csiim_config_pdu->k_csiim[0], csiim_config_pdu->k_csiim[1], csiim_config_pdu->k_csiim[2], csiim_config_pdu->k_csiim[3]);
  LOG_I(NR_PHY, "csiim_config_pdu->l_csiim = %i.%i.%i.%i\n", csiim_config_pdu->l_csiim[0], csiim_config_pdu->l_csiim[1], csiim_config_pdu->l_csiim[2], csiim_config_pdu->l_csiim[3]);
#endif

832
  nr_csi_im_power_estimation(ue, proc, csiim_config_pdu, &ue->nr_csi_info->interference_plus_noise_power, rxdataF);
833
  ue->nr_csi_info->csi_im_meas_computed = true;
834

835 836 837
  return 0;
}

838
int nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) {
839

840
  int gNB_id = proc->gNB_id;
841 842 843 844
  if(!ue->csirs_vars[gNB_id]->active) {
    return -1;
  }

845
  const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu = (fapi_nr_dl_config_csirs_pdu_rel15_t*)&ue->csirs_vars[gNB_id]->csirs_config_pdu;
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863

#ifdef NR_CSIRS_DEBUG
  LOG_I(NR_PHY, "csirs_config_pdu->subcarrier_spacing = %i\n", csirs_config_pdu->subcarrier_spacing);
  LOG_I(NR_PHY, "csirs_config_pdu->cyclic_prefix = %i\n", csirs_config_pdu->cyclic_prefix);
  LOG_I(NR_PHY, "csirs_config_pdu->start_rb = %i\n", csirs_config_pdu->start_rb);
  LOG_I(NR_PHY, "csirs_config_pdu->nr_of_rbs = %i\n", csirs_config_pdu->nr_of_rbs);
  LOG_I(NR_PHY, "csirs_config_pdu->csi_type = %i (0:TRS, 1:CSI-RS NZP, 2:CSI-RS ZP)\n", csirs_config_pdu->csi_type);
  LOG_I(NR_PHY, "csirs_config_pdu->row = %i\n", csirs_config_pdu->row);
  LOG_I(NR_PHY, "csirs_config_pdu->freq_domain = %i\n", csirs_config_pdu->freq_domain);
  LOG_I(NR_PHY, "csirs_config_pdu->symb_l0 = %i\n", csirs_config_pdu->symb_l0);
  LOG_I(NR_PHY, "csirs_config_pdu->symb_l1 = %i\n", csirs_config_pdu->symb_l1);
  LOG_I(NR_PHY, "csirs_config_pdu->cdm_type = %i\n", csirs_config_pdu->cdm_type);
  LOG_I(NR_PHY, "csirs_config_pdu->freq_density = %i (0: dot5 (even RB), 1: dot5 (odd RB), 2: one, 3: three)\n", csirs_config_pdu->freq_density);
  LOG_I(NR_PHY, "csirs_config_pdu->scramb_id = %i\n", csirs_config_pdu->scramb_id);
  LOG_I(NR_PHY, "csirs_config_pdu->power_control_offset = %i\n", csirs_config_pdu->power_control_offset);
  LOG_I(NR_PHY, "csirs_config_pdu->power_control_offset_ss = %i\n", csirs_config_pdu->power_control_offset_ss);
#endif

864 865
  const NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
  int32_t csi_rs_received_signal[frame_parms->nb_antennas_rx][frame_parms->samples_per_slot_wCP];
866 867 868 869 870
  uint8_t N_cdm_groups = 0;
  uint8_t CDM_group_size = 0;
  uint8_t k_prime = 0;
  uint8_t l_prime = 0;
  uint8_t N_ports = 0;
871 872 873
  uint8_t j_cdm[16];
  uint8_t k_overline[16];
  uint8_t l_overline[16];
874 875
  int16_t log2_re = 0;
  int16_t log2_maxh = 0;
876 877
  uint32_t rsrp = 0;
  int rsrp_dBm = 0;
878 879 880 881 882 883 884
  uint32_t noise_power = 0;
  uint8_t rank_indicator = 0;
  uint32_t precoded_sinr_dB = 0;
  uint8_t cqi = 0;
  uint8_t i1[3];
  uint8_t i2[1];

885
  nr_generate_csi_rs(frame_parms,
886
                     ue->nr_csi_info->csi_rs_generated_signal,
887
                     AMP,
888
                     ue->nr_csi_info,
889
                     (nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *) csirs_config_pdu,
890 891 892 893 894
                     proc->nr_slot_rx,
                     &N_cdm_groups,
                     &CDM_group_size,
                     &k_prime,
                     &l_prime,
895 896 897 898
                     &N_ports,
                     j_cdm,
                     k_overline,
                     l_overline);
899

900
  int32_t csi_rs_ls_estimated_channel[frame_parms->nb_antennas_rx][N_ports][frame_parms->ofdm_symbol_size];
901 902 903 904 905 906
  int32_t csi_rs_estimated_channel_freq[frame_parms->nb_antennas_rx][N_ports][frame_parms->ofdm_symbol_size + FILTER_MARGIN];

  // (long)&csi_rs_estimated_channel_freq[0][0][frame_parms->first_carrier_offset] & 0x1F gives us the remainder of the integer division by 32 of the memory address
  // By subtracting the previous value of 32, we know how much is left to have a multiple of 32.
  // Doing >> 2 <=> /sizeof(int32_t), we know what is the index offset of the array.
  uint8_t mem_offset = (((32 - ((long)&csi_rs_estimated_channel_freq[0][0][frame_parms->first_carrier_offset])) & 0x1F) >> 2);
907

rmagueta's avatar
rmagueta committed
908 909 910
  nr_get_csi_rs_signal(ue,
                       proc,
                       csirs_config_pdu,
911
                       ue->nr_csi_info,
912 913 914 915
                       N_cdm_groups,
                       CDM_group_size,
                       k_prime,
                       l_prime,
916 917 918
                       j_cdm,
                       k_overline,
                       l_overline,
919 920
                       csi_rs_received_signal,
                       &rsrp,
921 922
                       &rsrp_dBm,
                       rxdataF);
923

924 925
  nr_csi_rs_channel_estimation(ue,
                               proc,
rmagueta's avatar
rmagueta committed
926
                               csirs_config_pdu,
927 928
                               ue->nr_csi_info,
                               (const int32_t **) ue->nr_csi_info->csi_rs_generated_signal,
929
                               csi_rs_received_signal,
930 931 932 933 934
                               N_cdm_groups,
                               CDM_group_size,
                               k_prime,
                               l_prime,
                               N_ports,
935 936 937
                               j_cdm,
                               k_overline,
                               l_overline,
938
                               mem_offset,
939 940
                               csi_rs_ls_estimated_channel,
                               csi_rs_estimated_channel_freq,
941 942 943
                               &log2_re,
                               &log2_maxh,
                               &noise_power);
944

rmagueta's avatar
rmagueta committed
945 946
  nr_csi_rs_ri_estimation(ue,
                          csirs_config_pdu,
947
                          ue->nr_csi_info,
948
                          N_ports,
949
                          mem_offset,
950
                          csi_rs_estimated_channel_freq,
951 952
                          log2_maxh,
                          &rank_indicator);
953

954 955
  nr_csi_rs_pmi_estimation(ue,
                           csirs_config_pdu,
956
                           ue->nr_csi_info,
957
                           N_ports,
958
                           mem_offset,
959
                           csi_rs_estimated_channel_freq,
960
                           ue->nr_csi_info->csi_im_meas_computed ? ue->nr_csi_info->interference_plus_noise_power : noise_power,
961 962 963 964 965
                           rank_indicator,
                           log2_re,
                           i1,
                           i2,
                           &precoded_sinr_dB);
rmagueta's avatar
rmagueta committed
966

967
  nr_csi_rs_cqi_estimation(precoded_sinr_dB, &cqi);
rmagueta's avatar
rmagueta committed
968

969 970
  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);
971

972 973
  // Send CSI measurements to MAC
  fapi_nr_csirs_measurements_t csirs_measurements;
974 975
  csirs_measurements.rsrp = rsrp;
  csirs_measurements.rsrp_dBm = rsrp_dBm;
976 977 978 979
  csirs_measurements.rank_indicator = rank_indicator;
  csirs_measurements.i1 = *i1;
  csirs_measurements.i2 = *i2;
  csirs_measurements.cqi = cqi;
Roberto Louro Magueta's avatar
Roberto Louro Magueta committed
980
  nr_downlink_indication_t dl_indication;
981
  fapi_nr_rx_indication_t *rx_ind = calloc(sizeof(*rx_ind),1);
982 983
  nr_fill_dl_indication(&dl_indication, NULL, rx_ind, proc, ue, NULL);
  nr_fill_rx_indication(rx_ind, FAPI_NR_CSIRS_IND, ue, NULL, NULL, 1, proc, (void *)&csirs_measurements);
984
  if (ue->if_inst && ue->if_inst->dl_indication) {
Roberto Louro Magueta's avatar
Roberto Louro Magueta committed
985
    ue->if_inst->dl_indication(&dl_indication, NULL);
986 987 988 989
  } else {
    free(rx_ind);
  }

990 991
  return 0;
}