Files
openairinterface5g/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c
T

1926 lines
81 KiB
C

/*
* 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.0 (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
*/
/*! \file nr_dlsch_demodulation.c
* \brief Top-level routines for demodulating the PDSCH physical channel from 38-211, V15.2 2018-06
* \author H.Wang
* \date 2018
* \version 0.1
* \company Eurecom
* \note
* \warning
*/
#include "PHY/defs_nr_UE.h"
#include "PHY/phy_extern.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "nr_transport_proto_ue.h"
#include "PHY/sse_intrin.h"
#include "T.h"
#include "openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h"
#include "openair1/PHY/NR_TRANSPORT/nr_dlsch.h"
#include "PHY/NR_REFSIG/nr_refsig.h"
#include "PHY/NR_REFSIG/dmrs_nr.h"
#include "common/utils/nr/nr_common.h"
#include <complex.h>
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h"
/* dynamic shift for LLR computation for TM3/4
* set as command line argument, see lte-softmodem.c
* default value: 0
*/
int32_t nr_dlsch_demod_shift = 0;
//int16_t interf_unaw_shift = 13;
// #define DEBUG_HARQ(a...) printf(a)
#define DEBUG_HARQ(...)
//#define DEBUG_DLSCH_DEMOD
//#define DEBUG_PDSCH_RX
// [MCS][i_mod (0,1,2) = (2,4,6)]
//unsigned char offset_mumimo_llr_drange_fix=0;
//inferference-free case
/*unsigned char interf_unaw_shift_tm4_mcs[29]={5, 3, 4, 3, 3, 2, 1, 1, 2, 0, 1, 1, 1, 1, 0, 0,
1, 1, 1, 1, 0, 2, 1, 0, 1, 0, 1, 0, 0} ;*/
//unsigned char interf_unaw_shift_tm1_mcs[29]={5, 5, 4, 3, 3, 3, 2, 2, 4, 4, 2, 3, 3, 3, 1, 1,
// 0, 1, 1, 2, 5, 4, 4, 6, 5, 1, 0, 5, 6} ; // mcs 21, 26, 28 seem to be errorneous
/*
unsigned char offset_mumimo_llr_drange[29][3]={{8,8,8},{7,7,7},{7,7,7},{7,7,7},{6,6,6},{6,6,6},{6,6,6},{5,5,5},{4,4,4},{1,2,4}, // QPSK
{5,5,4},{5,5,5},{5,5,5},{3,3,3},{2,2,2},{2,2,2},{2,2,2}, // 16-QAM
{2,2,1},{3,3,3},{3,3,3},{3,3,1},{2,2,2},{2,2,2},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}; //64-QAM
*/
/*
//first optimization try
unsigned char offset_mumimo_llr_drange[29][3]={{7, 8, 7},{6, 6, 7},{6, 6, 7},{6, 6, 6},{5, 6, 6},{5, 5, 6},{5, 5, 6},{4, 5, 4},{4, 3, 4},{3, 2, 2},{6, 5, 5},{5, 4, 4},{5, 5, 4},{3, 3, 2},{2, 2, 1},{2, 1, 1},{2, 2, 2},{3, 3, 3},{3, 3, 2},{3, 3, 2},{3, 2, 1},{2, 2, 2},{2, 2, 2},{0, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0}};
*/
//second optimization try
/*
unsigned char offset_mumimo_llr_drange[29][3]={{5, 8, 7},{4, 6, 8},{3, 6, 7},{7, 7, 6},{4, 7, 8},{4, 7, 4},{6, 6, 6},{3, 6, 6},{3, 6, 6},{1, 3, 4},{1, 1, 0},{3, 3, 2},{3, 4, 1},{4, 0, 1},{4, 2, 2},{3, 1, 2},{2, 1, 0},{2, 1, 1},{1, 0, 1},{1, 0, 1},{0, 0, 0},{1, 0, 0},{0, 0, 0},{0, 1, 0},{1, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0}}; w
*/
//unsigned char offset_mumimo_llr_drange[29][3]= {{0, 6, 5},{0, 4, 5},{0, 4, 5},{0, 5, 4},{0, 5, 6},{0, 5, 3},{0, 4, 4},{0, 4, 4},{0, 3, 3},{0, 1, 2},{1, 1, 0},{1, 3, 2},{3, 4, 1},{2, 0, 0},{2, 2, 2},{1, 1, 1},{2, 1, 0},{2, 1, 1},{1, 0, 1},{1, 0, 1},{0, 0, 0},{1, 0, 0},{0, 0, 0},{0, 1, 0},{1, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0},{0, 0, 0}};
#define print_ints(s,x) printf("%s = %d %d %d %d\n",s,(x)[0],(x)[1],(x)[2],(x)[3])
#define print_shorts(s,x) printf("%s = [%d+j*%d, %d+j*%d, %d+j*%d, %d+j*%d]\n",s,(x)[0],(x)[1],(x)[2],(x)[3],(x)[4],(x)[5],(x)[6],(x)[7])
/* compute H_h_H matrix inversion up to 4x4 matrices */
uint8_t nr_zero_forcing_rx(uint32_t rx_size_symbol,
unsigned char n_rx,
unsigned char n_tx,//number of layer
int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int32_t dl_ch_mag[][n_rx][rx_size_symbol],
int32_t dl_ch_magb[][n_rx][rx_size_symbol],
int32_t dl_ch_magr[][n_rx][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
unsigned short nb_rb,
unsigned char mod_order,
int shift,
unsigned char symbol,
int length);
/* Apply layer demapping */
static void nr_dlsch_layer_demapping(int16_t *llr_cw[2],
uint8_t Nl,
uint8_t mod_order,
uint32_t length,
int32_t codeword_TB0,
int32_t codeword_TB1,
uint sz,
int16_t llr_layers[][sz]);
/* compute LLR */
static int nr_dlsch_llr(uint32_t rx_size_symbol,
int nbRx,
uint sz,
int16_t layer_llr[][sz],
NR_DL_FRAME_PARMS *frame_parms,
int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int32_t dl_ch_mag[rx_size_symbol],
int32_t dl_ch_magb[rx_size_symbol],
int32_t dl_ch_magr[rx_size_symbol],
NR_DL_UE_HARQ_t *dlsch0_harq,
NR_DL_UE_HARQ_t *dlsch1_harq,
unsigned char harq_pid,
unsigned char first_symbol_flag,
unsigned char symbol,
unsigned short nb_rb,
int32_t codeword_TB0,
int32_t codeword_TB1,
uint32_t len,
uint8_t nr_slot_rx,
NR_UE_DLSCH_t dlsch[2],
uint32_t llr_offset[NR_SYMBOLS_PER_SLOT]);
/** \fn nr_dlsch_extract_rbs
\brief This function extracts the received resource blocks, both channel estimates and data symbols, for the current
allocation and for multiple layer antenna gNB transmission.
@param rxdataF Raw FFT output of received signal
@param dl_ch_estimates Channel estimates of current slot
@param rxdataF_ext FFT output for RBs in this allocation
@param dl_ch_estimates_ext Channel estimates for RBs in this allocation
@param Nl nb of antenna layers
@param symbol Symbol to extract
@param n_dmrs_cdm_groups
@param frame_parms Pointer to frame descriptor
*/
static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz,
c16_t rxdataF[][rxdataF_sz],
uint32_t rx_size_symbol,
uint32_t pdsch_est_size,
int32_t dl_ch_estimates[][pdsch_est_size],
c16_t rxdataF_ext[][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
unsigned char symbol,
uint8_t pilots,
uint8_t config_type,
unsigned short start_rb,
unsigned short nb_rb_pdsch,
uint8_t n_dmrs_cdm_groups,
uint8_t Nl,
NR_DL_FRAME_PARMS *frame_parms,
uint16_t dlDmrsSymbPos,
int chest_time_type);
static void nr_dlsch_channel_level_median(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
int32_t median[MAX_ANT][MAX_ANT],
int n_tx,
int n_rx,
int length);
/** \brief This function performs channel compensation (matched filtering) on the received RBs for this allocation. In addition, it computes the squared-magnitude of the channel with weightings for
16QAM/64QAM detection as well as dual-stream detection (cross-correlation)
@param rxdataF_ext Frequency-domain received signal in RBs to be demodulated
@param dl_ch_estimates_ext Frequency-domain channel estimates in RBs to be demodulated
@param dl_ch_mag First Channel magnitudes (16QAM/64QAM)
@param dl_ch_magb Second weighted Channel magnitudes (64QAM)
@param rxdataF_comp Compensated received waveform
@param rho Cross-correlation between two spatial channels on each RX antenna
@param frame_parms Pointer to frame descriptor
@param symbol Symbol on which to operate
@param first_symbol_flag set to 1 on first DLSCH symbol
@param mod_order Modulation order of allocation
@param nb_rb Number of RBs in allocation
@param output_shift Rescaling for compensated output (should be energy-normalizing)
@param phy_measurements Pointer to UE PHY measurements
*/
static void nr_dlsch_channel_compensation(uint32_t rx_size_symbol,
int nbRx,
c16_t rxdataF_ext[][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
int32_t dl_ch_mag[][nbRx][rx_size_symbol],
int32_t dl_ch_magb[][nbRx][rx_size_symbol],
int32_t dl_ch_magr[][nbRx][rx_size_symbol],
int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int ***rho,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_layers,
unsigned char symbol,
int length,
bool first_symbol_flag,
unsigned char mod_order,
unsigned short nb_rb,
unsigned char output_shift,
PHY_NR_MEASUREMENTS *measurements);
/** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal
@param dl_ch_estimates_ext Channel estimates in allocated RBs
@param frame_parms Pointer to frame descriptor
@param avg Pointer to average signal strength
@param pilots_flag Flag to indicate pilots in symbol
@param nb_rb Number of allocated RBs
*/
static void nr_dlsch_channel_level(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_tx,
int32_t avg[MAX_ANT][MAX_ANT],
uint8_t symbol,
uint32_t len,
unsigned short nb_rb);
void nr_dlsch_scale_channel(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_tx,
uint8_t n_rx,
uint8_t symbol,
uint8_t pilots,
uint32_t len,
unsigned short nb_rb);
void nr_dlsch_detection_mrc(uint32_t rx_size_symbol,
short n_tx,
short n_rx,
int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int ***rho,
int32_t dl_ch_mag[][n_rx][rx_size_symbol],
int32_t dl_ch_magb[][n_rx][rx_size_symbol],
int32_t dl_ch_magr[][n_rx][rx_size_symbol],
unsigned char symbol,
unsigned short nb_rb,
int length);
/* Main Function */
int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
NR_UE_DLSCH_t dlsch[2],
unsigned char symbol,
bool first_symbol_flag,
unsigned char harq_pid,
uint32_t pdsch_est_size,
int32_t dl_ch_estimates[][pdsch_est_size],
int16_t *llr[2],
uint32_t dl_valid_re[NR_SYMBOLS_PER_SLOT],
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
uint32_t llr_offset[NR_SYMBOLS_PER_SLOT],
int32_t *log2_maxh,
int rx_size_symbol,
int nbRx,
int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
c16_t ptrs_phase_per_slot[][NR_SYMBOLS_PER_SLOT],
int32_t ptrs_re_per_slot[][NR_SYMBOLS_PER_SLOT],
int G)
{
const int nl = dlsch[0].Nl;
const int matrixSz = ue->frame_parms.nb_antennas_rx * nl;
__attribute__((aligned(32))) int32_t dl_ch_estimates_ext[matrixSz][rx_size_symbol];
memset(dl_ch_estimates_ext, 0, sizeof(dl_ch_estimates_ext));
__attribute__((aligned(32))) int32_t dl_ch_mag[nl][ue->frame_parms.nb_antennas_rx][rx_size_symbol];
memset(dl_ch_mag, 0, sizeof(dl_ch_mag));
__attribute__((aligned(32))) int32_t dl_ch_magb[nl][nbRx][rx_size_symbol];
memset(dl_ch_magb, 0, sizeof(dl_ch_magb));
__attribute__((aligned(32))) int32_t dl_ch_magr[nl][nbRx][rx_size_symbol];
memset(dl_ch_magr, 0, sizeof(dl_ch_magr));
NR_UE_COMMON *common_vars = &ue->common_vars;
NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
PHY_NR_MEASUREMENTS *measurements = &ue->measurements;
const int frame = proc->frame_rx;
const int nr_slot_rx = proc->nr_slot_rx;
const int gNB_id = proc->gNB_id;
uint8_t slot = 0;
int32_t codeword_TB0 = -1;
int32_t codeword_TB1 = -1;
uint32_t nb_re_pdsch = -1;
NR_DL_UE_HARQ_t *dlsch0_harq, *dlsch1_harq = NULL;
dlsch0_harq = &ue->dl_harq_processes[0][harq_pid];
if (NR_MAX_NB_LAYERS>4)
dlsch1_harq = &ue->dl_harq_processes[1][harq_pid];
if (dlsch0_harq && dlsch1_harq){
LOG_D(PHY,"AbsSubframe %d.%d / Sym %d harq_pid %d, harq status %d.%d \n", frame, nr_slot_rx, symbol, harq_pid, dlsch0_harq->status, dlsch1_harq->status);
if ((dlsch0_harq->status == ACTIVE) && (dlsch1_harq->status == ACTIVE)){
codeword_TB0 = dlsch0_harq->codeword; // SV: where is this set? revisit for DL MIMO.
codeword_TB1 = dlsch1_harq->codeword;
dlsch0_harq = &ue->dl_harq_processes[codeword_TB0][harq_pid];
dlsch1_harq = &ue->dl_harq_processes[codeword_TB1][harq_pid];
DEBUG_HARQ("[DEMOD] I am assuming both TBs are active, in cw0 %d and cw1 %d \n", codeword_TB0, codeword_TB1);
} else if ((dlsch0_harq->status == ACTIVE) && (dlsch1_harq->status != ACTIVE) ) {
codeword_TB0 = dlsch0_harq->codeword;
dlsch0_harq = &ue->dl_harq_processes[codeword_TB0][harq_pid];
dlsch1_harq = NULL;
DEBUG_HARQ("[DEMOD] I am assuming only TB0 is active, in cw %d \n", codeword_TB0);
} else if ((dlsch0_harq->status != ACTIVE) && (dlsch1_harq->status == ACTIVE)){
codeword_TB1 = dlsch1_harq->codeword;
dlsch0_harq = NULL;
dlsch1_harq = &ue->dl_harq_processes[codeword_TB1][harq_pid];
DEBUG_HARQ("[DEMOD] I am assuming only TB1 is active, it is in cw %d\n", codeword_TB1);
LOG_E(PHY, "[DEMOD] slot %d TB0 not active and TB1 active case is not supported\n", nr_slot_rx);
return -1;
} else {
LOG_E(PHY, "[DEMOD] slot %d: no active DLSCH (2 layers case)\n", nr_slot_rx);
return (-1);
}
} else if (dlsch0_harq) {
if (dlsch0_harq->status == ACTIVE) {
codeword_TB0 = dlsch0_harq->codeword;
dlsch0_harq = &ue->dl_harq_processes[0][harq_pid];
DEBUG_HARQ("[DEMOD] I am assuming only TB0 is active\n");
} else {
LOG_E(PHY, "[DEMOD] slot %d nr_rx_pdsch no active DLSCH (one layer case)\n", nr_slot_rx);
return (-1);
}
} else {
LOG_E(PHY, "[DEMOD] slot %d Inconsistent call to nr_rx_pdsch (no layer 0)\n", nr_slot_rx);
return -1;
}
DEBUG_HARQ("[DEMOD] cw for TB0 = %d, cw for TB1 = %d\n", codeword_TB0, codeword_TB1);
fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config = &dlsch[0].dlsch_config;
int start_rb = dlsch_config->start_rb;
int nb_rb_pdsch = dlsch_config->number_rbs;
DevAssert(dlsch0_harq);
if (gNB_id > 2) {
LOG_E(PHY, "In %s: Illegal gNB_id %d\n", __FUNCTION__, gNB_id);
return(-1);
}
if (!common_vars) {
LOG_E(PHY, "dlsch_demodulation.c: Null common_vars\n");
return(-1);
}
if (!frame_parms) {
LOG_E(PHY, "dlsch_demodulation.c: Null frame_parms\n");
return(-1);
}
if(symbol > ue->frame_parms.symbols_per_slot>>1)
{
slot = 1;
}
uint8_t pilots = (dlsch_config->dlDmrsSymbPos >> symbol) & 1;
uint8_t config_type = dlsch_config->dmrsConfigType;
//----------------------------------------------------------
//--------------------- RBs extraction ---------------------
//----------------------------------------------------------
const int n_rx = frame_parms->nb_antennas_rx;
time_stats_t meas = {0};
const bool meas_enabled = cpumeas(CPUMEAS_GETSTATE);
if (meas_enabled)
start_meas(&meas);
{
__attribute__((aligned(32))) c16_t rxdataF_ext[nbRx][rx_size_symbol];
memset(rxdataF_ext, 0, sizeof(rxdataF_ext));
nr_dlsch_extract_rbs(ue->frame_parms.samples_per_slot_wCP,
rxdataF,
rx_size_symbol,
pdsch_est_size,
dl_ch_estimates,
rxdataF_ext,
dl_ch_estimates_ext,
symbol,
pilots,
config_type,
start_rb + dlsch_config->BWPStart,
nb_rb_pdsch,
dlsch_config->n_dmrs_cdm_groups,
nl,
frame_parms,
dlsch_config->dlDmrsSymbPos,
ue->chest_time);
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Pilot/Data extraction %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
meas.p_time / (cpuf * 1000.0));
}
if (ue->phy_sim_pdsch_rxdataF_ext)
for (unsigned char aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
int offset = ((void *)rxdataF_ext[aarx] - (void *)rxdataF_ext) + symbol * rx_size_symbol;
memcpy(ue->phy_sim_pdsch_rxdataF_ext + offset, rxdataF_ext, rx_size_symbol * sizeof(c16_t));
}
nb_re_pdsch = (pilots == 1) ? ((config_type == NFAPI_NR_DMRS_TYPE1) ? nb_rb_pdsch * (12 - 6 * dlsch_config->n_dmrs_cdm_groups)
: nb_rb_pdsch * (12 - 4 * dlsch_config->n_dmrs_cdm_groups))
: (nb_rb_pdsch * 12);
//----------------------------------------------------------
//--------------------- Channel Scaling --------------------
//----------------------------------------------------------
if (meas_enabled)
start_meas(&meas);
nr_dlsch_scale_channel(rx_size_symbol, dl_ch_estimates_ext, frame_parms, nl, n_rx, symbol, pilots, nb_re_pdsch, nb_rb_pdsch);
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
meas.p_time / (cpuf * 1000.0));
}
//----------------------------------------------------------
//--------------------- Channel Level Calc. ----------------
//----------------------------------------------------------
if (meas_enabled)
start_meas(&meas);
if (first_symbol_flag) {
int32_t avg[MAX_ANT][MAX_ANT];
nr_dlsch_channel_level(rx_size_symbol, dl_ch_estimates_ext, frame_parms, nl, avg, symbol, nb_re_pdsch, nb_rb_pdsch);
int avgs = 0;
int32_t median[MAX_ANT][MAX_ANT];
for (int aatx = 0; aatx < nl; aatx++)
for (int aarx = 0; aarx < n_rx; aarx++) {
// LOG_I(PHY, "nb_rb %d len %d avg_%d_%d Power per SC is %d\n",nb_rb, len,aarx, aatx,avg[aatx*n_rx+aarx]);
avgs = cmax(avgs, avg[aatx][aarx]);
// LOG_I(PHY, "avgs Power per SC is %d\n", avgs);
median[aatx][aarx] = avg[aatx][aarx];
}
if (nl > 1) {
nr_dlsch_channel_level_median(rx_size_symbol, dl_ch_estimates_ext, median, nl, n_rx, nb_re_pdsch);
for (int aatx = 0; aatx < nl; aatx++) {
for (int aarx = 0; aarx < n_rx; aarx++) {
avgs = cmax(avgs, median[aatx][aarx]);
}
}
}
*log2_maxh = (log2_approx(avgs) / 2) + 1;
// LOG_I(PHY, "avgs Power per SC is %d lg2_maxh %d\n", avgs, log2_maxh);
LOG_D(PHY, "[DLSCH] AbsSubframe %d.%d log2_maxh = %d (%d)\n", frame % 1024, nr_slot_rx, *log2_maxh, avgs);
}
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
first_symbol_flag,
meas.p_time / (cpuf * 1000.0));
}
#if T_TRACER
T(T_UE_PHY_PDSCH_ENERGY, T_INT(gNB_id), T_INT(0), T_INT(frame % 1024), T_INT(nr_slot_rx));
#endif
//----------------------------------------------------------
//--------------------- channel compensation ---------------
//----------------------------------------------------------
// Disable correlation measurement for optimizing UE
if (meas_enabled)
start_meas(&meas);
nr_dlsch_channel_compensation(rx_size_symbol,
nbRx,
rxdataF_ext,
dl_ch_estimates_ext,
dl_ch_mag,
dl_ch_magb,
dl_ch_magr,
rxdataF_comp,
NULL,
frame_parms,
nl,
symbol,
nb_re_pdsch,
first_symbol_flag,
dlsch_config->qamModOrder,
nb_rb_pdsch,
*log2_maxh,
measurements); // log2_maxh+I0_shift
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d Channel Comp %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
*log2_maxh,
meas.p_time / (cpuf * 1000.0));
}
}
if (meas_enabled)
start_meas(&meas);
if (n_rx > 1) {
nr_dlsch_detection_mrc(rx_size_symbol,
nl,
n_rx,
rxdataF_comp,
NULL,
dl_ch_mag,
dl_ch_magb,
dl_ch_magr,
symbol,
nb_rb_pdsch,
nb_re_pdsch);
if (nl >= 2) // Apply zero forcing for 2, 3, and 4 Tx layers
nr_zero_forcing_rx(rx_size_symbol,
n_rx,
nl,
rxdataF_comp,
dl_ch_mag,
dl_ch_magb,
dl_ch_magr,
dl_ch_estimates_ext,
nb_rb_pdsch,
dlsch_config->qamModOrder,
*log2_maxh,
symbol,
nb_re_pdsch);
}
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Channel Combine and zero forcing %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
meas.p_time / (cpuf * 1000.0));
}
if (meas_enabled)
start_meas(&meas);
/* Store the valid DL RE's */
dl_valid_re[symbol-1] = nb_re_pdsch;
int startSymbIdx = 0;
int nbSymb = 0;
int pduBitmap = 0;
if(dlsch0_harq->status == ACTIVE) {
startSymbIdx = dlsch_config->start_symbol;
nbSymb = dlsch_config->number_symbols;
pduBitmap = dlsch_config->pduBitmap;
}
/* Check for PTRS bitmap and process it respectively */
if((pduBitmap & 0x1) && (dlsch[0].rnti_type == TYPE_C_RNTI_)) {
nr_pdsch_ptrs_processing(
ue, nbRx, ptrs_phase_per_slot, ptrs_re_per_slot, rx_size_symbol, rxdataF_comp, frame_parms, dlsch0_harq, dlsch1_harq, gNB_id, nr_slot_rx, symbol, (nb_rb_pdsch * 12), dlsch[0].rnti, dlsch);
dl_valid_re[symbol-1] -= ptrs_re_per_slot[0][symbol];
}
/* at last symbol in a slot calculate LLR's for whole slot */
if(symbol == (startSymbIdx + nbSymb -1)) {
const uint32_t rx_llr_layer_size = (G + dlsch[0].Nl - 1) / dlsch[0].Nl;
if (dlsch[0].Nl == 0 || rx_llr_layer_size == 0 || rx_llr_layer_size > 10 * 1000 * 1000) {
LOG_E(PHY, "rx_llr_layer_size %d, G %d, Nl, %d, discarding this pdsch\n", rx_llr_layer_size, G, dlsch[0].Nl);
return -1;
}
int16_t layer_llr[dlsch[0].Nl][rx_llr_layer_size];
for(int i = startSymbIdx; i < startSymbIdx+nbSymb; i++) {
/* re evaluating the first symbol flag as LLR's are done in symbol loop */
if(i == startSymbIdx && i < 3)
first_symbol_flag = 1;
else
first_symbol_flag = 0;
/* Calculate LLR's for each symbol */
nr_dlsch_llr(rx_size_symbol,
nbRx,
rx_llr_layer_size,
layer_llr,
frame_parms,
rxdataF_comp,
dl_ch_mag[0][0],
dl_ch_magb[0][0],
dl_ch_magr[0][0],
dlsch0_harq,
dlsch1_harq,
harq_pid,
first_symbol_flag,
i,
nb_rb_pdsch,
codeword_TB0,
codeword_TB1,
dl_valid_re[i - 1],
nr_slot_rx,
dlsch,
llr_offset);
}
nr_dlsch_layer_demapping(llr,
dlsch[0].Nl,
dlsch[0].dlsch_config.qamModOrder,
G,
codeword_TB0,
codeword_TB1,
rx_llr_layer_size,
layer_llr);
// Please keep it: useful for debugging
#ifdef DEBUG_PDSCH_RX
char filename[50];
uint8_t aa = 0;
snprintf(filename, 50, "rxdataF0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF0", &rxdataF[0][0], NR_SYMBOLS_PER_SLOT*frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "dl_ch_estimates0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates", &dl_ch_estimates[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "rxdataF_ext0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "rxdataF_ext", &rxdataF_ext[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
snprintf(filename, 50, "dl_ch_estimates_ext0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates_ext00", &dl_ch_estimates_ext[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
snprintf(filename, 50, "rxdataF_comp0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "rxdataF_comp00", &rxdataF_comp[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
/*
for (int i=0; i < 2; i++){
snprintf(filename, 50, "llr%d_symb_%d_nr_slot_rx_%d.m", i, symbol, nr_slot_rx);
write_output(filename,"llr", &llr[i][0], (NR_SYMBOLS_PER_SLOT*nb_rb_pdsch*NR_NB_SC_PER_RB*dlsch1_harq->Qm) - 4*(nb_rb_pdsch*4*dlsch1_harq->Qm), 1, 0);
}
*/
#endif
}
if (meas_enabled) {
stop_meas(&meas);
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: LLR Computation %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
meas.p_time / (cpuf * 1000.0));
}
#if T_TRACER
T(T_UE_PHY_PDSCH_IQ,
T_INT(gNB_id),
T_INT(ue->Mod_id),
T_INT(frame % 1024),
T_INT(nr_slot_rx),
T_INT(nb_rb_pdsch),
T_INT(frame_parms->N_RB_UL),
T_INT(frame_parms->symbols_per_slot),
T_BUFFER(&rxdataF_comp[gNB_id][0], 2 * /* ulsch[UE_id]->harq_processes[harq_pid]->nb_rb */ frame_parms->N_RB_UL * 12 * 2));
#endif
UEscopeCopy(ue, pdschRxdataF_comp, rxdataF_comp[0], sizeof(c16_t), nbRx, rx_size_symbol * NR_SYMBOLS_PER_SLOT, 0);
if (ue->phy_sim_pdsch_rxdataF_comp)
for (int a = 0; a < nbRx; a++) {
int offset = (void *)rxdataF_comp[0][a] - (void *)rxdataF_comp[0] + symbol * rx_size_symbol * sizeof(c16_t);
memcpy(ue->phy_sim_pdsch_rxdataF_comp + offset, rxdataF_comp[0][a] + symbol * rx_size_symbol, sizeof(c16_t) * rx_size_symbol);
memcpy(ue->phy_sim_pdsch_dl_ch_estimates + offset, dl_ch_estimates, pdsch_est_size * sizeof(c16_t));
}
if (ue->phy_sim_pdsch_dl_ch_estimates_ext)
memcpy(ue->phy_sim_pdsch_dl_ch_estimates_ext + symbol * rx_size_symbol, dl_ch_estimates_ext, sizeof(dl_ch_estimates_ext));
return (0);
}
void nr_dlsch_deinterleaving(uint8_t symbol,
uint8_t start_symbol,
uint16_t L,
uint16_t *llr,
uint16_t *llr_deint,
uint16_t nb_rb_pdsch)
{
uint32_t bundle_idx, N_bundle, R, C, r,c;
int32_t m,k;
uint8_t nb_re;
R=2;
N_bundle = nb_rb_pdsch/L;
C=N_bundle/R;
uint32_t bundle_deint[N_bundle];
memset(bundle_deint, 0 , sizeof(bundle_deint));
printf("N_bundle %u L %d nb_rb_pdsch %d\n",N_bundle, L,nb_rb_pdsch);
if (symbol==start_symbol)
nb_re = 6;
else
nb_re = 12;
AssertFatal(llr!=NULL,"nr_dlsch_deinterleaving: FATAL llr is Null\n");
for (c =0; c< C; c++){
for (r=0; r<R;r++){
bundle_idx = r*C+c;
bundle_deint[bundle_idx] = c*R+r;
//printf("c %u r %u bundle_idx %u bundle_deinter %u\n", c, r, bundle_idx, bundle_deint[bundle_idx]);
}
}
for (k=0; k<N_bundle;k++)
{
for (m=0; m<nb_re*L;m++){
llr_deint[bundle_deint[k]*nb_re*L+m]= llr[k*nb_re*L+m];
//printf("k %d m %d bundle_deint %d llr_deint %d\n", k, m, bundle_deint[k], llr_deint[bundle_deint[k]*nb_re*L+m]);
}
}
}
//==============================================================================================
// Pre-processing for LLR computation
//==============================================================================================
simde__m128i nr_dlsch_a_mult_conjb(simde__m128i a, simde__m128i b, unsigned char output_shift)
{
simde__m128i mmtmpD0 = simde_mm_madd_epi16(b, a);
simde__m128i mmtmpD1 = simde_mm_shufflelo_epi16(b, SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1, SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_sign_epi16(mmtmpD1, *(simde__m128i *)&conjugate[0]);
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1, a);
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1, output_shift);
simde__m128i mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0, mmtmpD1);
simde__m128i mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0, mmtmpD1);
return simde_mm_packs_epi32(mmtmpD2, mmtmpD3);
}
static void nr_dlsch_channel_compensation(uint32_t rx_size_symbol,
int nbRx,
c16_t rxdataF_ext[][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
int32_t dl_ch_mag[][nbRx][rx_size_symbol],
int32_t dl_ch_magb[][nbRx][rx_size_symbol],
int32_t dl_ch_magr[][nbRx][rx_size_symbol],
int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int ***rho,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_layers,
unsigned char symbol,
int length,
bool first_symbol_flag,
unsigned char mod_order,
unsigned short nb_rb,
unsigned char output_shift,
PHY_NR_MEASUREMENTS *measurements)
{
simde__m128i *dl_ch128, *dl_ch128_2, *dl_ch_mag128, *dl_ch_mag128b, *dl_ch_mag128r, *rxdataF128, *rxdataF_comp128, *rho128;
simde__m128i mmtmpD0, mmtmpD1, mmtmpD2, mmtmpD3, QAM_amp128 = {0}, QAM_amp128b = {0}, QAM_amp128r = {0};
uint32_t nb_rb_0 = length / 12 + ((length % 12) ? 1 : 0);
for (int l = 0; l < n_layers; l++) {
if (mod_order == 4) {
QAM_amp128 = simde_mm_set1_epi16(QAM16_n1); // 2/sqrt(10)
QAM_amp128b = simde_mm_setzero_si128();
QAM_amp128r = simde_mm_setzero_si128();
} else if (mod_order == 6) {
QAM_amp128 = simde_mm_set1_epi16(QAM64_n1); //
QAM_amp128b = simde_mm_set1_epi16(QAM64_n2);
QAM_amp128r = simde_mm_setzero_si128();
} else if (mod_order == 8) {
QAM_amp128 = simde_mm_set1_epi16(QAM256_n1);
QAM_amp128b = simde_mm_set1_epi16(QAM256_n2);
QAM_amp128r = simde_mm_set1_epi16(QAM256_n3);
}
// printf("comp: rxdataF_comp %p, symbol %d\n",rxdataF_comp[0],symbol);
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(l * frame_parms->nb_antennas_rx) + aarx];
dl_ch_mag128 = (simde__m128i *)dl_ch_mag[l][aarx];
dl_ch_mag128b = (simde__m128i *)dl_ch_magb[l][aarx];
dl_ch_mag128r = (simde__m128i *)dl_ch_magr[l][aarx];
rxdataF128 = (simde__m128i *)rxdataF_ext[aarx];
rxdataF_comp128 = (simde__m128i *)(rxdataF_comp[l][aarx] + symbol * nb_rb * 12);
for (int rb = 0; rb < nb_rb_0; rb++) {
if (mod_order > 2) {
// get channel amplitude if not QPSK
mmtmpD0 = simde_mm_madd_epi16(dl_ch128[0], dl_ch128[0]);
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
mmtmpD1 = simde_mm_madd_epi16(dl_ch128[1], dl_ch128[1]);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1, output_shift);
mmtmpD0 = simde_mm_packs_epi32(mmtmpD0, mmtmpD1); //|H[0]|^2 |H[1]|^2 |H[2]|^2 |H[3]|^2 |H[4]|^2 |H[5]|^2 |H[6]|^2 |H[7]|^2
// store channel magnitude here in a new field of dlsch
dl_ch_mag128[0] = simde_mm_unpacklo_epi16(mmtmpD0, mmtmpD0);
dl_ch_mag128b[0] = dl_ch_mag128[0];
dl_ch_mag128r[0] = dl_ch_mag128[0];
dl_ch_mag128[0] = simde_mm_mulhrs_epi16(dl_ch_mag128[0], QAM_amp128);
dl_ch_mag128b[0] = simde_mm_mulhrs_epi16(dl_ch_mag128b[0], QAM_amp128b);
dl_ch_mag128r[0] = simde_mm_mulhrs_epi16(dl_ch_mag128r[0], QAM_amp128r);
dl_ch_mag128[1] = simde_mm_unpackhi_epi16(mmtmpD0, mmtmpD0);
dl_ch_mag128b[1] = dl_ch_mag128[1];
dl_ch_mag128r[1] = dl_ch_mag128[1];
dl_ch_mag128[1] = simde_mm_mulhrs_epi16(dl_ch_mag128[1], QAM_amp128);
dl_ch_mag128b[1] = simde_mm_mulhrs_epi16(dl_ch_mag128b[1], QAM_amp128b);
dl_ch_mag128r[1] = simde_mm_mulhrs_epi16(dl_ch_mag128r[1], QAM_amp128r);
mmtmpD0 = simde_mm_madd_epi16(dl_ch128[2], dl_ch128[2]);
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
mmtmpD1 = simde_mm_packs_epi32(mmtmpD0, mmtmpD0);
dl_ch_mag128[2] = simde_mm_unpacklo_epi16(mmtmpD1, mmtmpD1);
dl_ch_mag128b[2] = dl_ch_mag128[2];
dl_ch_mag128r[2] = dl_ch_mag128[2];
dl_ch_mag128[2] = simde_mm_mulhrs_epi16(dl_ch_mag128[2], QAM_amp128);
dl_ch_mag128b[2] = simde_mm_mulhrs_epi16(dl_ch_mag128b[2], QAM_amp128b);
dl_ch_mag128r[2] = simde_mm_mulhrs_epi16(dl_ch_mag128r[2], QAM_amp128r);
}
// Multiply received data by conjugated channel
rxdataF_comp128[0] = nr_dlsch_a_mult_conjb(rxdataF128[0], dl_ch128[0], output_shift);
rxdataF_comp128[1] = nr_dlsch_a_mult_conjb(rxdataF128[1], dl_ch128[1], output_shift);
rxdataF_comp128[2] = nr_dlsch_a_mult_conjb(rxdataF128[2], dl_ch128[2], output_shift);
dl_ch128 += 3;
dl_ch_mag128 += 3;
dl_ch_mag128b += 3;
dl_ch_mag128r += 3;
rxdataF128 += 3;
rxdataF_comp128 += 3;
}
}
}
if (rho) {
// we compute the Tx correlation matrix for each Rx antenna
// As an example the 2x2 MIMO case requires
// rho[aarx][nl*nl] = [cov(H_aarx_0,H_aarx_0) cov(H_aarx_0,H_aarx_1)
// cov(H_aarx_1,H_aarx_0) cov(H_aarx_1,H_aarx_1)], aarx=0,...,nb_antennas_rx-1
// int avg_rho_re[frame_parms->nb_antennas_rx][nl*nl];
// int avg_rho_im[frame_parms->nb_antennas_rx][nl*nl];
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
for (int l = 0; l < n_layers; l++) {
for (int atx = 0; atx < n_layers; atx++) {
// avg_rho_re[aarx][l*n_layers+atx] = 0;
// avg_rho_im[aarx][l*n_layers+atx] = 0;
rho128 = (simde__m128i *)&rho[aarx][l * n_layers + atx][symbol * nb_rb * 12];
dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[l * frame_parms->nb_antennas_rx + aarx];
dl_ch128_2 = (simde__m128i *)dl_ch_estimates_ext[atx * frame_parms->nb_antennas_rx + aarx];
for (int rb = 0; rb < nb_rb_0; rb++) {
// multiply by conjugated channel
mmtmpD0 = simde_mm_madd_epi16(dl_ch128[0], dl_ch128_2[0]);
// print_ints("re",&mmtmpD0);
// mmtmpD0 contains real part of 4 consecutive outputs (32-bit)
mmtmpD1 = simde_mm_shufflelo_epi16(dl_ch128[0], SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1, SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_sign_epi16(mmtmpD1, *(simde__m128i *)&conjugate[0]);
// print_ints("im",&mmtmpD1);
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1, dl_ch128_2[0]);
// mmtmpD1 contains imag part of 4 consecutive outputs (32-bit)
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
// print_ints("re(shift)",&mmtmpD0);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1, output_shift);
// print_ints("im(shift)",&mmtmpD1);
mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0, mmtmpD1);
mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0, mmtmpD1);
// print_ints("c0",&mmtmpD2);
// print_ints("c1",&mmtmpD3);
rho128[0] = simde_mm_packs_epi32(mmtmpD2, mmtmpD3);
// print_shorts("rx:",dl_ch128_2);
// print_shorts("ch:",dl_ch128);
// print_shorts("pack:",rho128);
/*avg_rho_re[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[0])[0]+
((int16_t*)&rho128[0])[2] +
((int16_t*)&rho128[0])[4] +
((int16_t*)&rho128[0])[6])/16;*/
/*avg_rho_im[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[0])[1]+
((int16_t*)&rho128[0])[3] +
((int16_t*)&rho128[0])[5] +
((int16_t*)&rho128[0])[7])/16;*/
// multiply by conjugated channel
mmtmpD0 = simde_mm_madd_epi16(dl_ch128[1], dl_ch128_2[1]);
// mmtmpD0 contains real part of 4 consecutive outputs (32-bit)
mmtmpD1 = simde_mm_shufflelo_epi16(dl_ch128[1], SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1, SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_sign_epi16(mmtmpD1, *(simde__m128i *)conjugate);
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1, dl_ch128_2[1]);
// mmtmpD1 contains imag part of 4 consecutive outputs (32-bit)
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1, output_shift);
mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0, mmtmpD1);
mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0, mmtmpD1);
rho128[1] = simde_mm_packs_epi32(mmtmpD2, mmtmpD3);
// print_shorts("rx:",dl_ch128_2+1);
// print_shorts("ch:",dl_ch128+1);
// print_shorts("pack:",rho128+1);
// multiply by conjugated channel
/*avg_rho_re[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[1])[0]+
((int16_t*)&rho128[1])[2] +
((int16_t*)&rho128[1])[4] +
((int16_t*)&rho128[1])[6])/16;*/
/*avg_rho_im[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[1])[1]+
((int16_t*)&rho128[1])[3] +
((int16_t*)&rho128[1])[5] +
((int16_t*)&rho128[1])[7])/16;*/
mmtmpD0 = simde_mm_madd_epi16(dl_ch128[2], dl_ch128_2[2]);
// mmtmpD0 contains real part of 4 consecutive outputs (32-bit)
mmtmpD1 = simde_mm_shufflelo_epi16(dl_ch128[2], SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1, SIMDE_MM_SHUFFLE(2, 3, 0, 1));
mmtmpD1 = simde_mm_sign_epi16(mmtmpD1, *(simde__m128i *)conjugate);
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1, dl_ch128_2[2]);
// mmtmpD1 contains imag part of 4 consecutive outputs (32-bit)
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0, output_shift);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1, output_shift);
mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0, mmtmpD1);
mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0, mmtmpD1);
rho128[2] = simde_mm_packs_epi32(mmtmpD2, mmtmpD3);
// print_shorts("rx:",dl_ch128_2+2);
// print_shorts("ch:",dl_ch128+2);
// print_shorts("pack:",rho128+2);
/*avg_rho_re[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[2])[0]+
((int16_t*)&rho128[2])[2] +
((int16_t*)&rho128[2])[4] +
((int16_t*)&rho128[2])[6])/16;*/
/*avg_rho_im[aarx][l*n_layers+atx] +=(((int16_t*)&rho128[2])[1]+
((int16_t*)&rho128[2])[3] +
((int16_t*)&rho128[2])[5] +
((int16_t*)&rho128[2])[7])/16;*/
dl_ch128+=3;
dl_ch128_2+=3;
rho128+=3;
}
if (first_symbol_flag) {
//rho_nm = H_arx_n.conj(H_arx_m)
//rho_rx_corr[arx][nm] = |H_arx_n|^2.|H_arx_m|^2 &rho[aarx][l*n_layers+atx][symbol*nb_rb*12]
measurements->rx_correlation[0][aarx][l * n_layers + atx] = signal_energy(&rho[aarx][l * n_layers + atx][symbol * nb_rb * 12],length);
//avg_rho_re[aarx][l*n_layers+atx] = 16*avg_rho_re[aarx][l*n_layers+atx]/length;
//avg_rho_im[aarx][l*n_layers+atx] = 16*avg_rho_im[aarx][l*n_layers+atx]/length;
//printf("rho[rx]%d tx%d tx%d = Re: %d Im: %d\n",aarx, l,atx, avg_rho_re[aarx][l*n_layers+atx], avg_rho_im[aarx][l*n_layers+atx]);
//printf("rho_corr[rx]%d tx%d tx%d = %d ...\n",aarx, l,atx, measurements->rx_correlation[0][aarx][l*n_layers+atx]);
}
}
}
}
}
}
void nr_dlsch_scale_channel(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_tx,
uint8_t n_rx,
uint8_t symbol,
uint8_t pilots,
uint32_t len,
unsigned short nb_rb)
{
short rb, ch_amp;
unsigned char aatx,aarx;
simde__m128i *dl_ch128, ch_amp128;
uint32_t nb_rb_0 = len/12 + ((len%12)?1:0);
// Determine scaling amplitude based the symbol
ch_amp = 1024*8; //((pilots) ? (dlsch_ue[0]->sqrt_rho_b) : (dlsch_ue[0]->sqrt_rho_a));
LOG_D(PHY,"Scaling PDSCH Chest in OFDM symbol %d by %d, pilots %d nb_rb %d NCP %d symbol %d\n",symbol,ch_amp,pilots,nb_rb,frame_parms->Ncp,symbol);
// printf("Scaling PDSCH Chest in OFDM symbol %d by %d\n",symbol_mod,ch_amp);
ch_amp128 = simde_mm_set1_epi16(ch_amp); // Q3.13
for (aatx=0; aatx<n_tx; aatx++) {
for (aarx=0; aarx<n_rx; aarx++) {
dl_ch128=(simde__m128i *)dl_ch_estimates_ext[(aatx*n_rx)+aarx];
for (rb=0;rb<nb_rb_0;rb++) {
dl_ch128[0] = simde_mm_mulhi_epi16(dl_ch128[0],ch_amp128);
dl_ch128[0] = simde_mm_slli_epi16(dl_ch128[0],3);
dl_ch128[1] = simde_mm_mulhi_epi16(dl_ch128[1],ch_amp128);
dl_ch128[1] = simde_mm_slli_epi16(dl_ch128[1],3);
dl_ch128[2] = simde_mm_mulhi_epi16(dl_ch128[2],ch_amp128);
dl_ch128[2] = simde_mm_slli_epi16(dl_ch128[2],3);
dl_ch128+=3;
}
}
}
}
//compute average channel_level on each (TX,RX) antenna pair
void nr_dlsch_channel_level(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
NR_DL_FRAME_PARMS *frame_parms,
uint8_t n_tx,
int32_t avg[MAX_ANT][MAX_ANT],
uint8_t symbol,
uint32_t len,
unsigned short nb_rb)
{
simde__m128i *dl_ch128, avg128D;
//nb_rb*nre = y * 2^x
int16_t x = factor2(len);
//x = (x>4) ? 4 : x;
int16_t y = (len)>>x;
//printf("len = %d = %d * 2^(%d)\n",len,y,x);
uint32_t nb_rb_0 = len/12 + ((len%12)?1:0);
AssertFatal(y!=0,"Cannot divide by zero: in function %s of file %s\n", __func__, __FILE__);
for (int aatx = 0; aatx < n_tx; aatx++) {
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
//clear average level
avg128D = simde_mm_setzero_si128();
dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(aatx * frame_parms->nb_antennas_rx) + aarx];
for (int rb = 0; rb < nb_rb_0; rb++) {
avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[0],dl_ch128[0]),x));
avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[1],dl_ch128[1]),x));
avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[2],dl_ch128[2]),x));
dl_ch128+=3;
}
int32_t *tmp = (int32_t *)&avg128D;
avg[aatx][aarx] = ((int64_t)tmp[0] + tmp[1] + tmp[2] + tmp[3]) / y;
// printf("Channel level : %d\n",avg[(aatx<<1)+aarx]);
}
}
}
static void nr_dlsch_channel_level_median(uint32_t rx_size_symbol,
int32_t dl_ch_estimates_ext[][rx_size_symbol],
int32_t median[MAX_ANT][MAX_ANT],
int n_tx,
int n_rx,
int length)
{
for (int aatx = 0; aatx < n_tx; aatx++) {
for (int aarx = 0; aarx < n_rx; aarx++) {
int64_t max = median[aatx][aarx]; // initialize the med point for max
int64_t min = median[aatx][aarx]; // initialize the med point for min
simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[aatx * n_rx + aarx];
const int length2 = length >> 2; // length = number of REs, hence length2=nb_REs*(32/128) in SIMD loop
for (int ii = 0; ii < length2; ii++) {
simde__m128i norm128D =
simde_mm_srai_epi32(simde_mm_madd_epi16(*dl_ch128, *dl_ch128), 2); //[|H_0|²/4 |H_1|²/4 |H_2|²/4 |H_3|²/4]
int32_t *tmp = (int32_t *)&norm128D;
int64_t norm_pack = (int64_t)tmp[0] + tmp[1] + tmp[2] + tmp[3];
if (norm_pack > max)
max = norm_pack;
if (norm_pack < min)
min = norm_pack;
dl_ch128+=1;
}
median[aatx][aarx] = (max + min) >> 1;
//printf("Channel level median [%d]: %d max = %d min = %d\n",aatx*n_rx + aarx, median[aatx*n_rx + aarx],max,min);
}
}
}
//==============================================================================================
// Extraction functions
//==============================================================================================
static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz,
c16_t rxdataF[][rxdataF_sz],
uint32_t rx_size_symbol,
uint32_t pdsch_est_size,
int32_t dl_ch_estimates[][pdsch_est_size],
c16_t rxdataF_ext[][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
unsigned char symbol,
uint8_t pilots,
uint8_t config_type,
unsigned short start_rb,
unsigned short nb_rb_pdsch,
uint8_t n_dmrs_cdm_groups,
uint8_t Nl,
NR_DL_FRAME_PARMS *frame_parms,
uint16_t dlDmrsSymbPos,
int chest_time_type)
{
if (config_type == NFAPI_NR_DMRS_TYPE1) {
AssertFatal(n_dmrs_cdm_groups == 1 || n_dmrs_cdm_groups == 2,
"n_dmrs_cdm_groups %d is illegal\n",n_dmrs_cdm_groups);
} else {
AssertFatal(n_dmrs_cdm_groups == 1 || n_dmrs_cdm_groups == 2 || n_dmrs_cdm_groups == 3,
"n_dmrs_cdm_groups %d is illegal\n",n_dmrs_cdm_groups);
}
const unsigned short start_re = (frame_parms->first_carrier_offset + start_rb * NR_NB_SC_PER_RB) % frame_parms->ofdm_symbol_size;
int8_t validDmrsEst;
if (chest_time_type == 0)
validDmrsEst = get_valid_dmrs_idx_for_channel_est(dlDmrsSymbPos,symbol);
else
validDmrsEst = get_next_dmrs_symbol_in_slot(dlDmrsSymbPos,0,14); // get first dmrs symbol index
for (unsigned char aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
c16_t *rxF_ext = rxdataF_ext[aarx];
c16_t *rxF = &rxdataF[aarx][symbol * frame_parms->ofdm_symbol_size];
for (unsigned char l = 0; l < Nl; l++) {
int32_t *dl_ch0 = &dl_ch_estimates[(l * frame_parms->nb_antennas_rx) + aarx][validDmrsEst * frame_parms->ofdm_symbol_size];
int32_t *dl_ch0_ext = dl_ch_estimates_ext[(l * frame_parms->nb_antennas_rx) + aarx];
if (pilots == 0) { //data symbol only
if (l == 0) {
if (start_re + nb_rb_pdsch * NR_NB_SC_PER_RB <= frame_parms->ofdm_symbol_size) {
memcpy(rxF_ext, &rxF[start_re], nb_rb_pdsch * NR_NB_SC_PER_RB * sizeof(int32_t));
} else {
int neg_length = frame_parms->ofdm_symbol_size - start_re;
int pos_length = nb_rb_pdsch * NR_NB_SC_PER_RB - neg_length;
memcpy(rxF_ext, &rxF[start_re], neg_length * sizeof(int32_t));
memcpy(&rxF_ext[neg_length], rxF, pos_length * sizeof(int32_t));
}
}
memcpy(dl_ch0_ext, dl_ch0, nb_rb_pdsch * NR_NB_SC_PER_RB * sizeof(int32_t));
}
else if (config_type == NFAPI_NR_DMRS_TYPE1){
if (n_dmrs_cdm_groups == 1) { //data is multiplexed
if (l == 0) {
unsigned short k = start_re;
for (unsigned short j = 0; j < 6*nb_rb_pdsch; j += 3) {
rxF_ext[j] = rxF[k+1];
rxF_ext[j+1] = rxF[k+3];
rxF_ext[j+2] = rxF[k+5];
k += 6;
if (k >= frame_parms->ofdm_symbol_size)
k -= frame_parms->ofdm_symbol_size;
}
}
for (unsigned short j = 0; j < 6*nb_rb_pdsch; j += 3) {
dl_ch0_ext[j] = dl_ch0[1];
dl_ch0_ext[j+1] = dl_ch0[3];
dl_ch0_ext[j+2] = dl_ch0[5];
dl_ch0 += 6;
}
}
}
else {//NFAPI_NR_DMRS_TYPE2
if (n_dmrs_cdm_groups == 1) { //data is multiplexed
if (l == 0) {
unsigned short k = start_re;
for (unsigned short j = 0; j < 8*nb_rb_pdsch; j += 4) {
rxF_ext[j] = rxF[k+2];
rxF_ext[j+1] = rxF[k+3];
rxF_ext[j+2] = rxF[k+4];
rxF_ext[j+3] = rxF[k+5];
k += 6;
if (k >= frame_parms->ofdm_symbol_size)
k -= frame_parms->ofdm_symbol_size;
}
}
for (unsigned short j = 0; j < 8*nb_rb_pdsch; j += 4) {
dl_ch0_ext[j] = dl_ch0[2];
dl_ch0_ext[j+1] = dl_ch0[3];
dl_ch0_ext[j+2] = dl_ch0[4];
dl_ch0_ext[j+3] = dl_ch0[5];
dl_ch0 += 6;
}
}
else if (n_dmrs_cdm_groups == 2) { //data is multiplexed
if (l == 0) {
unsigned short k = start_re;
for (unsigned short j = 0; j < 4*nb_rb_pdsch; j += 2) {
rxF_ext[j] = rxF[k+4];
rxF_ext[j+1] = rxF[k+5];
k += 6;
if (k >= frame_parms->ofdm_symbol_size)
k -= frame_parms->ofdm_symbol_size;
}
}
for (unsigned short j = 0; j < 4*nb_rb_pdsch; j += 2) {
dl_ch0_ext[j] = dl_ch0[4];
dl_ch0_ext[j+1] = dl_ch0[5];
dl_ch0 += 6;
}
}
}
}
}
}
void nr_dlsch_detection_mrc(uint32_t rx_size_symbol,
short n_tx,
short n_rx,
int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int ***rho,
int32_t dl_ch_mag[][n_rx][rx_size_symbol],
int32_t dl_ch_magb[][n_rx][rx_size_symbol],
int32_t dl_ch_magr[][n_rx][rx_size_symbol],
unsigned char symbol,
unsigned short nb_rb,
int length) {
unsigned char aatx, aarx;
int i;
simde__m128i *rxdataF_comp128_0,*rxdataF_comp128_1,*dl_ch_mag128_0,*dl_ch_mag128_1,*dl_ch_mag128_0b,*dl_ch_mag128_1b,*dl_ch_mag128_0r,*dl_ch_mag128_1r;
uint32_t nb_rb_0 = length/12 + ((length%12)?1:0);
if (n_rx>1) {
for (aatx=0; aatx<n_tx; aatx++) {
rxdataF_comp128_0 = (simde__m128i *)(rxdataF_comp[aatx][0] + symbol * nb_rb * 12);
dl_ch_mag128_0 = (simde__m128i *)dl_ch_mag[aatx][0];
dl_ch_mag128_0b = (simde__m128i *)dl_ch_magb[aatx][0];
dl_ch_mag128_0r = (simde__m128i *)dl_ch_magr[aatx][0];
for (aarx=1; aarx<n_rx; aarx++) {
rxdataF_comp128_1 = (simde__m128i *)(rxdataF_comp[aatx][aarx] + symbol * nb_rb * 12);
dl_ch_mag128_1 = (simde__m128i *)dl_ch_mag[aatx][aarx];
dl_ch_mag128_1b = (simde__m128i *)dl_ch_magb[aatx][aarx];
dl_ch_mag128_1r = (simde__m128i *)dl_ch_magr[aatx][aarx];
// MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM/256 llr computation)
for (i=0; i<nb_rb_0*3; i++) {
rxdataF_comp128_0[i] = simde_mm_adds_epi16(rxdataF_comp128_0[i],rxdataF_comp128_1[i]);
dl_ch_mag128_0[i] = simde_mm_adds_epi16(dl_ch_mag128_0[i],dl_ch_mag128_1[i]);
dl_ch_mag128_0b[i] = simde_mm_adds_epi16(dl_ch_mag128_0b[i],dl_ch_mag128_1b[i]);
dl_ch_mag128_0r[i] = simde_mm_adds_epi16(dl_ch_mag128_0r[i],dl_ch_mag128_1r[i]);
}
}
}
#ifdef DEBUG_DLSCH_DEMOD
for (i=0; i<nb_rb_0*3; i++) {
printf("symbol%d RB %d\n",symbol,i/3);
rxdataF_comp128_0 = (simde__m128i *)(rxdataF_comp[0][0] + symbol * nb_rb * 12);
rxdataF_comp128_1 = (simde__m128i *)(rxdataF_comp[0][n_rx] + symbol * nb_rb * 12);
print_shorts("tx 1 mrc_re/mrc_Im:",(int16_t*)&rxdataF_comp128_0[i]);
print_shorts("tx 2 mrc_re/mrc_Im:",(int16_t*)&rxdataF_comp128_1[i]);
// printf("mrc mag0 = %d = %d \n",((int16_t*)&dl_ch_mag128_0[0])[0],((int16_t*)&dl_ch_mag128_0[0])[1]);
// printf("mrc mag0b = %d = %d \n",((int16_t*)&dl_ch_mag128_0b[0])[0],((int16_t*)&dl_ch_mag128_0b[0])[1]);
}
#endif
if (rho) {
/*rho128_0 = (simde__m128i *) &rho[0][symbol*frame_parms->N_RB_DL*12];
rho128_1 = (simde__m128i *) &rho[1][symbol*frame_parms->N_RB_DL*12];
for (i=0; i<nb_rb_0*3; i++) {
// print_shorts("mrc rho0:",&rho128_0[i]);
// print_shorts("mrc rho1:",&rho128_1[i]);
rho128_0[i] = simde_mm_adds_epi16(simde_mm_srai_epi16(rho128_0[i],1),simde_mm_srai_epi16(rho128_1[i],1));
}*/
}
simde_mm_empty();
simde_m_empty();
}
}
/* Zero Forcing Rx function: nr_a_sum_b()
* Compute the complex addition x=x+y
*
* */
void nr_a_sum_b(c16_t *input_x, c16_t *input_y, unsigned short nb_rb)
{
unsigned short rb;
simde__m128i *x = (simde__m128i *)input_x;
simde__m128i *y = (simde__m128i *)input_y;
for (rb=0; rb<nb_rb; rb++) {
x[0] = simde_mm_adds_epi16(x[0], y[0]);
x[1] = simde_mm_adds_epi16(x[1], y[1]);
x[2] = simde_mm_adds_epi16(x[2], y[2]);
x += 3;
y += 3;
}
}
/* Zero Forcing Rx function: nr_a_mult_b()
* Compute the complex Multiplication c=a*b
*
* */
void nr_a_mult_b(c16_t *a, c16_t *b, c16_t *c, unsigned short nb_rb, unsigned char output_shift0)
{
//This function is used to compute complex multiplications
short nr_conjugate[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1};
unsigned short rb;
simde__m128i *a_128,*b_128, *c_128, mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3;
a_128 = (simde__m128i *)a;
b_128 = (simde__m128i *)b;
c_128 = (simde__m128i *)c;
for (rb=0; rb<3*nb_rb; rb++) {
// the real part
mmtmpD0 = simde_mm_sign_epi16(a_128[0],*(simde__m128i*)&nr_conjugate[0]);
mmtmpD0 = simde_mm_madd_epi16(mmtmpD0,b_128[0]); //Re: (a_re*b_re - a_im*b_im)
// the imag part
mmtmpD1 = simde_mm_shufflelo_epi16(a_128[0],SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1,SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1,b_128[0]);//Im: (x_im*y_re + x_re*y_im)
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0,output_shift0);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1,output_shift0);
mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0,mmtmpD1);
mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0,mmtmpD1);
c_128[0] = simde_mm_packs_epi32(mmtmpD2,mmtmpD3);
/*printf("\n Computing mult \n");
print_shorts("a:",(int16_t*)&a_128[0]);
print_shorts("b:",(int16_t*)&b_128[0]);
print_shorts("pack:",(int16_t*)&c_128[0]);*/
a_128+=1;
b_128+=1;
c_128+=1;
}
}
/* Zero Forcing Rx function: nr_element_sign()
* Compute b=sign*a
*
* */
static inline void nr_element_sign(c16_t *a, // a
c16_t *b, // b
unsigned short nb_rb,
int32_t sign)
{
const int16_t nr_sign[8] __attribute__((aligned(16))) = {-1, -1, -1, -1, -1, -1, -1, -1};
simde__m128i *a_128,*b_128;
a_128 = (simde__m128i *)a;
b_128 = (simde__m128i *)b;
for (int rb = 0; rb < 3 * nb_rb; rb++) {
if (sign < 0)
b_128[rb] = simde_mm_sign_epi16(a_128[rb], ((simde__m128i *)nr_sign)[0]);
else
b_128[rb] = a_128[rb];
#ifdef DEBUG_DLSCH_DEMOD
print_shorts("b:", (int16_t *)b_128);
#endif
}
}
/* Zero Forcing Rx function: nr_det_4x4()
* Compute the matrix determinant for 4x4 Matrix
*
* */
static void nr_determin(int size,
c16_t *a44[][size], //
c16_t *ad_bc, // ad-bc
unsigned short nb_rb,
int32_t sign,
int32_t shift0)
{
AssertFatal(size > 0, "");
if(size==1) {
nr_element_sign(a44[0][0], // a
ad_bc, // b
nb_rb,
sign);
} else {
int16_t k, rr[size - 1], cc[size - 1];
c16_t outtemp[12 * nb_rb] __attribute__((aligned(32)));
c16_t outtemp1[12 * nb_rb] __attribute__((aligned(32)));
c16_t *sub_matrix[size - 1][size - 1];
for (int rtx=0;rtx<size;rtx++) {//row calculation for determin
int ctx=0;
//find the submatrix row and column indices
k=0;
for(int rrtx=0;rrtx<size;rrtx++)
if(rrtx != rtx) rr[k++] = rrtx;
k=0;
for(int cctx=0;cctx<size;cctx++)
if(cctx != ctx) cc[k++] = cctx;
// fill out the sub matrix corresponds to this element
for (int ridx = 0; ridx < (size - 1); ridx++)
for (int cidx = 0; cidx < (size - 1); cidx++)
sub_matrix[cidx][ridx] = a44[cc[cidx]][rr[ridx]];
nr_determin(size - 1,
sub_matrix, // a33
outtemp,
nb_rb,
((rtx & 1) == 1 ? -1 : 1) * ((ctx & 1) == 1 ? -1 : 1) * sign,
shift0);
nr_a_mult_b(a44[ctx][rtx], outtemp, rtx == 0 ? ad_bc : outtemp1, nb_rb, shift0);
if (rtx != 0)
nr_a_sum_b(ad_bc, outtemp1, nb_rb);
}
}
}
static double complex nr_determin_cpx(int32_t size, // size
double complex a44_cpx[][size], //
int32_t sign)
{
double complex outtemp, outtemp1;
//Allocate the submatrix elements
DevAssert(size > 0);
if(size==1) {
return (a44_cpx[0][0] * sign);
}else {
double complex sub_matrix[size - 1][size - 1];
int16_t k, rr[size - 1], cc[size - 1];
outtemp1 = 0;
for (int rtx=0;rtx<size;rtx++) {//row calculation for determin
int ctx=0;
//find the submatrix row and column indices
k=0;
for(int rrtx=0;rrtx<size;rrtx++)
if(rrtx != rtx) rr[k++] = rrtx;
k=0;
for(int cctx=0;cctx<size;cctx++)
if(cctx != ctx) cc[k++] = cctx;
//fill out the sub matrix corresponds to this element
for (int ridx=0;ridx<(size-1);ridx++)
for (int cidx=0;cidx<(size-1);cidx++)
sub_matrix[cidx][ridx] = a44_cpx[cc[cidx]][rr[ridx]];
outtemp = nr_determin_cpx(size - 1,
sub_matrix, // a33
((rtx & 1) == 1 ? -1 : 1) * ((ctx & 1) == 1 ? -1 : 1) * sign);
outtemp1 += a44_cpx[ctx][rtx] * outtemp;
}
return((double complex)outtemp1);
}
}
/* Zero Forcing Rx function: nr_matrix_inverse()
* Compute the matrix inverse and determinant up to 4x4 Matrix
*
* */
uint8_t nr_matrix_inverse(int32_t size,
c16_t *a44[][size], // Input matrix//conjH_H_elements[0]
c16_t *inv_H_h_H[][size], // Inverse
c16_t *ad_bc, // determin
unsigned short nb_rb,
int32_t flag, // fixed point or floating flag
int32_t shift0)
{
DevAssert(size > 1);
int16_t k,rr[size-1],cc[size-1];
if(flag) {//fixed point SIMD calc.
//Allocate the submatrix elements
c16_t *sub_matrix[size - 1][size - 1];
//Compute Matrix determinant
nr_determin(size,
a44, //
ad_bc, // determinant
nb_rb,
+1,
shift0);
//print_shorts("nr_det_",(int16_t*)&ad_bc[0]);
//Compute Inversion of the H^*H matrix
/* For 2x2 MIMO matrix, we compute
* * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)|
* * H_h_H= | |
* * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)|
* *
* *inv(H_h_H) =(1/det)*[d -b
* * -c a]
* **************************************************************************/
for (int rtx=0;rtx<size;rtx++) {//row
k=0;
for(int rrtx=0;rrtx<size;rrtx++)
if(rrtx != rtx) rr[k++] = rrtx;
for (int ctx=0;ctx<size;ctx++) {//column
k=0;
for(int cctx=0;cctx<size;cctx++)
if(cctx != ctx) cc[k++] = cctx;
//fill out the sub matrix corresponds to this element
for (int ridx=0;ridx<(size-1);ridx++)
for (int cidx=0;cidx<(size-1);cidx++)
// To verify
sub_matrix[cidx][ridx]=a44[cc[cidx]][rr[ridx]];
nr_determin(size - 1, // size
sub_matrix,
inv_H_h_H[rtx][ctx], // out transpose
nb_rb,
((rtx & 1) == 1 ? -1 : 1) * ((ctx & 1) == 1 ? -1 : 1),
shift0);
}
}
}
else {//floating point calc.
//Allocate the submatrix elements
double complex sub_matrix_cpx[size - 1][size - 1];
//Convert the IQ samples (in Q15 format) to float complex
double complex a44_cpx[size][size];
double complex inv_H_h_H_cpx[size][size];
double complex determin_cpx;
for (int i=0; i<12*nb_rb; i++) {
//Convert Q15 to floating point
for (int rtx=0;rtx<size;rtx++) {//row
for (int ctx=0;ctx<size;ctx++) {//column
a44_cpx[ctx][rtx] = ((double)(a44[ctx][rtx])[i].r) / (1 << (shift0 - 1)) + I * ((double)(a44[ctx][rtx])[i].i) / (1 << (shift0 - 1));
//if (i<4) printf("a44_cpx(%d,%d)= ((FP %d))%lf+(FP %d)j%lf \n",ctx,rtx,((short *)a44[ctx*size+rtx])[(i<<1)],creal(a44_cpx[ctx*size+rtx]),((short *)a44[ctx*size+rtx])[(i<<1)+1],cimag(a44_cpx[ctx*size+rtx]));
}
}
//Compute Matrix determinant (copy real value only)
determin_cpx = nr_determin_cpx(size,
a44_cpx, //
+1);
//if (i<4) printf("order %d nr_det_cpx = %lf+j%lf \n",log2_approx(creal(determin_cpx)),creal(determin_cpx),cimag(determin_cpx));
//Round and convert to Q15 (Out in the same format as Fixed point).
if (creal(determin_cpx)>0) {//determin of the symmetric matrix is real part only
((short*) ad_bc)[i<<1] = (short) ((creal(determin_cpx)*(1<<(shift0)))+0.5);//
//((short*) ad_bc)[(i<<1)+1] = (short) ((cimag(determin_cpx)*(1<<(shift0)))+0.5);//
} else {
((short*) ad_bc)[i<<1] = (short) ((creal(determin_cpx)*(1<<(shift0)))-0.5);//
//((short*) ad_bc)[(i<<1)+1] = (short) ((cimag(determin_cpx)*(1<<(shift0)))-0.5);//
}
//if (i<4) printf("nr_det_FP= %d+j%d \n",((short*) ad_bc)[i<<1],((short*) ad_bc)[(i<<1)+1]);
//Compute Inversion of the H^*H matrix (normalized output divide by determinant)
for (int rtx=0;rtx<size;rtx++) {//row
k=0;
for(int rrtx=0;rrtx<size;rrtx++)
if(rrtx != rtx) rr[k++] = rrtx;
for (int ctx=0;ctx<size;ctx++) {//column
k=0;
for(int cctx=0;cctx<size;cctx++)
if(cctx != ctx) cc[k++] = cctx;
//fill out the sub matrix corresponds to this element
for (int ridx=0;ridx<(size-1);ridx++)
for (int cidx=0;cidx<(size-1);cidx++)
sub_matrix_cpx[cidx][ridx] = a44_cpx[cc[cidx]][rr[ridx]];
inv_H_h_H_cpx[rtx][ctx] = nr_determin_cpx(size - 1, // size,
sub_matrix_cpx, //
((rtx & 1) == 1 ? -1 : 1) * ((ctx & 1) == 1 ? -1 : 1));
//if (i==0) printf("H_h_H(r%d,c%d)=%lf+j%lf --> inv_H_h_H(%d,%d) = %lf+j%lf \n",rtx,ctx,creal(a44_cpx[ctx*size+rtx]),cimag(a44_cpx[ctx*size+rtx]),ctx,rtx,creal(inv_H_h_H_cpx[rtx*size+ctx]),cimag(inv_H_h_H_cpx[rtx*size+ctx]));
if (creal(inv_H_h_H_cpx[rtx][ctx]) > 0)
inv_H_h_H[rtx][ctx][i].r = (short)((creal(inv_H_h_H_cpx[rtx][ctx]) * (1 << (shift0 - 1))) + 0.5); // Convert to Q 18
else
inv_H_h_H[rtx][ctx][i].r = (short)((creal(inv_H_h_H_cpx[rtx][ctx]) * (1 << (shift0 - 1))) - 0.5); //
if (cimag(inv_H_h_H_cpx[rtx][ctx]) > 0)
inv_H_h_H[rtx][ctx][i].i = (short)((cimag(inv_H_h_H_cpx[rtx][ctx]) * (1 << (shift0 - 1))) + 0.5); //
else
inv_H_h_H[rtx][ctx][i].i = (short)((cimag(inv_H_h_H_cpx[rtx][ctx]) * (1 << (shift0 - 1))) - 0.5); //
//if (i<4) printf("inv_H_h_H_FP(%d,%d)= %d+j%d \n",ctx,rtx, ((short *) inv_H_h_H[rtx*size+ctx])[i<<1],((short *) inv_H_h_H[rtx*size+ctx])[(i<<1)+1]);
}
}
}
}
return(0);
}
/* Zero Forcing Rx function: nr_conjch0_mult_ch1()
*
*
* */
void nr_conjch0_mult_ch1(int *ch0,
int *ch1,
int32_t *ch0conj_ch1,
unsigned short nb_rb,
unsigned char output_shift0)
{
//This function is used to compute multiplications in H_hermitian * H matrix
short nr_conjugate[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1};
unsigned short rb;
simde__m128i *dl_ch0_128,*dl_ch1_128, *ch0conj_ch1_128, mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3;
dl_ch0_128 = (simde__m128i *)ch0;
dl_ch1_128 = (simde__m128i *)ch1;
ch0conj_ch1_128 = (simde__m128i *)ch0conj_ch1;
for (rb=0; rb<3*nb_rb; rb++) {
mmtmpD0 = simde_mm_madd_epi16(dl_ch0_128[0],dl_ch1_128[0]);
mmtmpD1 = simde_mm_shufflelo_epi16(dl_ch0_128[0],SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD1 = simde_mm_shufflehi_epi16(mmtmpD1,SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD1 = simde_mm_sign_epi16(mmtmpD1,*(simde__m128i*)&nr_conjugate[0]);
mmtmpD1 = simde_mm_madd_epi16(mmtmpD1,dl_ch1_128[0]);
mmtmpD0 = simde_mm_srai_epi32(mmtmpD0,output_shift0);
mmtmpD1 = simde_mm_srai_epi32(mmtmpD1,output_shift0);
mmtmpD2 = simde_mm_unpacklo_epi32(mmtmpD0,mmtmpD1);
mmtmpD3 = simde_mm_unpackhi_epi32(mmtmpD0,mmtmpD1);
ch0conj_ch1_128[0] = simde_mm_packs_epi32(mmtmpD2,mmtmpD3);
/*printf("\n Computing conjugates \n");
print_shorts("ch0:",(int16_t*)&dl_ch0_128[0]);
print_shorts("ch1:",(int16_t*)&dl_ch1_128[0]);
print_shorts("pack:",(int16_t*)&ch0conj_ch1_128[0]);*/
dl_ch0_128+=1;
dl_ch1_128+=1;
ch0conj_ch1_128+=1;
}
simde_mm_empty();
simde_m_empty();
}
/* Zero Forcing Rx function: up to 4 layers
*
*
* */
uint8_t nr_zero_forcing_rx(uint32_t rx_size_symbol,
unsigned char n_rx,
unsigned char n_tx,//number of layer
int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int32_t dl_ch_mag[][n_rx][rx_size_symbol],
int32_t dl_ch_magb[][n_rx][rx_size_symbol],
int32_t dl_ch_magr[][n_rx][rx_size_symbol],
int32_t dl_ch_estimates_ext[][rx_size_symbol],
unsigned short nb_rb,
unsigned char mod_order,
int shift,
unsigned char symbol,
int length)
{
int *ch0r, *ch0c;
uint32_t nb_rb_0 = length/12 + ((length%12)?1:0);
c16_t determ_fin[12 * nb_rb_0] __attribute__((aligned(32)));
///Allocate H^*H matrix elements and sub elements
c16_t conjH_H_elements_data[n_rx][n_tx][n_tx][12 * nb_rb_0];
memset(conjH_H_elements_data, 0, sizeof(conjH_H_elements_data));
c16_t *conjH_H_elements[n_rx][n_tx][n_tx];
for (int aarx = 0; aarx < n_rx; aarx++)
for (int rtx = 0; rtx < n_tx; rtx++)
for (int ctx = 0; ctx < n_tx; ctx++)
conjH_H_elements[aarx][rtx][ctx] = conjH_H_elements_data[aarx][rtx][ctx];
//Compute H^*H matrix elements and sub elements:(1/2^log2_maxh)*conjH_H_elements
for (int rtx=0;rtx<n_tx;rtx++) {//row
for (int ctx=0;ctx<n_tx;ctx++) {//column
for (int aarx=0;aarx<n_rx;aarx++) {
ch0r = (int *)dl_ch_estimates_ext[rtx*n_rx+aarx];
ch0c = (int *)dl_ch_estimates_ext[ctx*n_rx+aarx];
nr_conjch0_mult_ch1(ch0r,
ch0c,
(int32_t *)conjH_H_elements[aarx][ctx][rtx], // sic
nb_rb_0,
shift);
if (aarx != 0)
nr_a_sum_b(conjH_H_elements[0][ctx][rtx], conjH_H_elements[aarx][ctx][rtx], nb_rb_0);
}
}
}
//Compute the inverse and determinant of the H^*H matrix
//Allocate the inverse matrix
c16_t *inv_H_h_H[n_tx][n_tx];
c16_t inv_H_h_H_data[n_tx][n_tx][12 * nb_rb_0];
memset(inv_H_h_H_data, 0, sizeof(inv_H_h_H_data));
for (int rtx = 0; rtx < n_tx; rtx++)
for (int ctx = 0; ctx < n_tx; ctx++)
inv_H_h_H[ctx][rtx] = inv_H_h_H_data[ctx][rtx];
int fp_flag = 1;//0: float point calc 1: Fixed point calc
nr_matrix_inverse(n_tx,
conjH_H_elements[0], // Input matrix
inv_H_h_H, // Inverse
determ_fin, // determin
nb_rb_0,
fp_flag, // fixed point flag
shift - (fp_flag == 1 ? 2 : 0)); // the out put is Q15
// multiply Matrix inversion pf H_h_H by the rx signal vector
c16_t outtemp[12 * nb_rb_0] __attribute__((aligned(32)));
//Allocate rxdataF for zforcing out
c16_t rxdataF_zforcing[n_tx][12 * nb_rb_0];
memset(rxdataF_zforcing, 0, sizeof(rxdataF_zforcing));
for (int rtx=0;rtx<n_tx;rtx++) {//Output Layers row
// loop over Layers rtx=0,...,N_Layers-1
for (int ctx = 0; ctx < n_tx; ctx++) { // column multi
// printf("Computing r_%d c_%d\n",rtx,ctx);
// print_shorts(" H_h_H=",(int16_t*)&conjH_H_elements[ctx*n_tx+rtx][0][0]);
// print_shorts(" Inv_H_h_H=",(int16_t*)&inv_H_h_H[ctx*n_tx+rtx][0]);
nr_a_mult_b(inv_H_h_H[ctx][rtx], (c16_t *)(rxdataF_comp[ctx][0] + symbol * nb_rb * 12), outtemp, nb_rb_0, shift - (fp_flag == 1 ? 2 : 0));
nr_a_sum_b(rxdataF_zforcing[rtx], outtemp,
nb_rb_0); // a =a + b
}
#ifdef DEBUG_DLSCH_DEMOD
printf("Computing layer_%d \n",rtx);;
print_shorts(" Rx signal:=",(int16_t*)&rxdataF_zforcing[rtx][0]);
print_shorts(" Rx signal:=",(int16_t*)&rxdataF_zforcing[rtx][4]);
print_shorts(" Rx signal:=",(int16_t*)&rxdataF_zforcing[rtx][8]);
#endif
}
//Copy zero_forcing out to output array
for (int rtx=0;rtx<n_tx;rtx++)
nr_element_sign(rxdataF_zforcing[rtx], (c16_t *)(rxdataF_comp[rtx][0] + symbol * nb_rb * 12), nb_rb_0, +1);
//Update LLR thresholds with the Matrix determinant
simde__m128i *dl_ch_mag128_0=NULL,*dl_ch_mag128b_0=NULL,*dl_ch_mag128r_0=NULL,*determ_fin_128;
simde__m128i mmtmpD2,mmtmpD3;
simde__m128i QAM_amp128={0},QAM_amp128b={0},QAM_amp128r={0};
short nr_realpart[8]__attribute__((aligned(16))) = {1,0,1,0,1,0,1,0};
determ_fin_128 = (simde__m128i *)&determ_fin[0];
if (mod_order>2) {
if (mod_order == 4) {
QAM_amp128 = simde_mm_set1_epi16(QAM16_n1); //2/sqrt(10)
QAM_amp128b = simde_mm_setzero_si128();
QAM_amp128r = simde_mm_setzero_si128();
} else if (mod_order == 6) {
QAM_amp128 = simde_mm_set1_epi16(QAM64_n1); //4/sqrt{42}
QAM_amp128b = simde_mm_set1_epi16(QAM64_n2); //2/sqrt{42}
QAM_amp128r = simde_mm_setzero_si128();
} else if (mod_order == 8) {
QAM_amp128 = simde_mm_set1_epi16(QAM256_n1); //8/sqrt{170}
QAM_amp128b = simde_mm_set1_epi16(QAM256_n2);//4/sqrt{170}
QAM_amp128r = simde_mm_set1_epi16(QAM256_n3);//2/sqrt{170}
}
dl_ch_mag128_0 = (simde__m128i *)dl_ch_mag[0][0];
dl_ch_mag128b_0 = (simde__m128i *)dl_ch_magb[0][0];
dl_ch_mag128r_0 = (simde__m128i *)dl_ch_magr[0][0];
for (int rb=0; rb<3*nb_rb_0; rb++) {
//for symmetric H_h_H matrix, the determinant is only real values
mmtmpD2 = simde_mm_sign_epi16(determ_fin_128[0],*(simde__m128i*)&nr_realpart[0]);//set imag part to 0
mmtmpD3 = simde_mm_shufflelo_epi16(mmtmpD2,SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD3 = simde_mm_shufflehi_epi16(mmtmpD3,SIMDE_MM_SHUFFLE(2,3,0,1));
mmtmpD2 = simde_mm_add_epi16(mmtmpD2,mmtmpD3);
dl_ch_mag128_0[0] = mmtmpD2;
dl_ch_mag128b_0[0] = mmtmpD2;
dl_ch_mag128r_0[0] = mmtmpD2;
dl_ch_mag128_0[0] = simde_mm_mulhi_epi16(dl_ch_mag128_0[0],QAM_amp128);
dl_ch_mag128_0[0] = simde_mm_slli_epi16(dl_ch_mag128_0[0],1);
dl_ch_mag128b_0[0] = simde_mm_mulhi_epi16(dl_ch_mag128b_0[0],QAM_amp128b);
dl_ch_mag128b_0[0] = simde_mm_slli_epi16(dl_ch_mag128b_0[0],1);
dl_ch_mag128r_0[0] = simde_mm_mulhi_epi16(dl_ch_mag128r_0[0],QAM_amp128r);
dl_ch_mag128r_0[0] = simde_mm_slli_epi16(dl_ch_mag128r_0[0],1);
determ_fin_128 += 1;
dl_ch_mag128_0 += 1;
dl_ch_mag128b_0 += 1;
dl_ch_mag128r_0 += 1;
}
}
return 0;
}
static void nr_dlsch_layer_demapping(int16_t *llr_cw[2],
uint8_t Nl,
uint8_t mod_order,
uint32_t length,
int32_t codeword_TB0,
int32_t codeword_TB1,
uint sz,
int16_t llr_layers[][sz])
{
switch (Nl) {
case 1:
if (codeword_TB1 == -1)
memcpy(llr_cw[0], llr_layers[0], (length)*sizeof(int16_t));
else if (codeword_TB0 == -1)
memcpy(llr_cw[1], llr_layers[0], (length)*sizeof(int16_t));
break;
case 2:
case 3:
case 4:
for (int i=0; i<(length/Nl/mod_order); i++){
for (int l=0; l<Nl; l++) {
for (int m=0; m<mod_order; m++){
if (codeword_TB1 == -1)
llr_cw[0][Nl*mod_order*i+l*mod_order+m] = llr_layers[l][i*mod_order+m];//i:0 -->0 1 2 3
else if (codeword_TB0 == -1)
llr_cw[1][Nl*mod_order*i+l*mod_order+m] = llr_layers[l][i*mod_order+m];//i:0 -->0 1 2 3
//if (i<4) printf("length%d: llr_layers[l%d][m%d]=%d: \n",length,l,m,llr_layers[l][i*mod_order+m]);
}
}
}
break;
default:
AssertFatal(0, "Not supported number of layers %d\n", Nl);
}
}
static int nr_dlsch_llr(uint32_t rx_size_symbol,
int nbRx,
uint sz,
int16_t layer_llr[][sz],
NR_DL_FRAME_PARMS *frame_parms,
int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT],
int32_t dl_ch_mag[rx_size_symbol],
int32_t dl_ch_magb[rx_size_symbol],
int32_t dl_ch_magr[rx_size_symbol],
NR_DL_UE_HARQ_t *dlsch0_harq,
NR_DL_UE_HARQ_t *dlsch1_harq,
unsigned char harq_pid,
unsigned char first_symbol_flag,
unsigned char symbol,
unsigned short nb_rb,
int32_t codeword_TB0,
int32_t codeword_TB1,
uint32_t len,
uint8_t nr_slot_rx,
NR_UE_DLSCH_t dlsch[2],
uint32_t llr_offset[14])
{
uint32_t llr_offset_symbol;
if (first_symbol_flag==1)
llr_offset[symbol-1] = 0;
llr_offset_symbol = llr_offset[symbol-1];
llr_offset[symbol] = len*dlsch[0].dlsch_config.qamModOrder + llr_offset_symbol;
switch (dlsch[0].dlsch_config.qamModOrder) {
case 2 :
for(int l=0; l < dlsch[0].Nl; l++)
nr_dlsch_qpsk_llr(frame_parms, rxdataF_comp[l][0], layer_llr[l] + llr_offset_symbol, symbol, len, first_symbol_flag, nb_rb);
break;
case 4 :
for(int l=0; l < dlsch[0].Nl; l++)
nr_dlsch_16qam_llr(frame_parms, rxdataF_comp[l][0], layer_llr[l] + llr_offset_symbol, dl_ch_mag, symbol, len, first_symbol_flag, nb_rb);
break;
case 6 :
for(int l=0; l < dlsch[0].Nl; l++)
nr_dlsch_64qam_llr(frame_parms, rxdataF_comp[l][0], layer_llr[l] + llr_offset_symbol, dl_ch_mag, dl_ch_magb, symbol, len, first_symbol_flag, nb_rb);
break;
case 8:
for(int l=0; l < dlsch[0].Nl; l++)
nr_dlsch_256qam_llr(frame_parms, rxdataF_comp[l][0], layer_llr[l] + llr_offset_symbol, dl_ch_mag, dl_ch_magb, dl_ch_magr, symbol, len, first_symbol_flag, nb_rb);
break;
default:
LOG_W(PHY,"rx_dlsch.c : Unknown mod_order!!!!\n");
return(-1);
break;
}
//TODO: Revisited for Nl>4
if (dlsch1_harq) {
switch (dlsch[1].dlsch_config.qamModOrder) {
case 2 :
nr_dlsch_qpsk_llr(frame_parms, rxdataF_comp[0][0], layer_llr[0] + llr_offset_symbol, symbol, len, first_symbol_flag, nb_rb);
break;
case 4:
nr_dlsch_16qam_llr(frame_parms, rxdataF_comp[0][0], layer_llr[0] + llr_offset_symbol, dl_ch_mag, symbol, len, first_symbol_flag, nb_rb);
break;
case 6 :
nr_dlsch_64qam_llr(frame_parms, rxdataF_comp[0][0], layer_llr[0] + llr_offset_symbol, dl_ch_mag, dl_ch_magb, symbol, len, first_symbol_flag, nb_rb);
break;
case 8 :
nr_dlsch_256qam_llr(frame_parms, rxdataF_comp[0][0], layer_llr[0] + llr_offset_symbol, dl_ch_mag, dl_ch_magb, dl_ch_magr, symbol, len, first_symbol_flag, nb_rb);
break;
default:
LOG_W(PHY,"rx_dlsch.c : Unknown mod_order!!!!\n");
return(-1);
break;
}
}
return 0;
}
//==============================================================================================