This means that as of this commit, the signal has to be permanently regenerated. In fact, with the SRS queue to be introduced in a follow-up commit, we don't have an easy way to cache the sequence generation, as there is no "index" to which we could refer to (hence, caching would be tricky). Instead, the next commits further refactor this to reduce the time for SRS sequence generation as well as the memory footprint. Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
1404 lines
60 KiB
C
1404 lines
60 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
#include "common/utils/fsn.h"
|
|
#include "PHY/defs_gNB.h"
|
|
#include "sched_nr.h"
|
|
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
|
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
|
|
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
|
|
#include "PHY/NR_TRANSPORT/nr_dci.h"
|
|
#include "PHY/NR_ESTIMATION/nr_ul_estimation.h"
|
|
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h"
|
|
#include "common/utils/LOG/log.h"
|
|
#include "PHY/INIT/nr_phy_init.h"
|
|
#include "PHY/MODULATION/nr_modulation.h"
|
|
#include "T.h"
|
|
#include "executables/nr-softmodem.h"
|
|
#include "executables/softmodem-common.h"
|
|
#include "nfapi/oai_integration/vendor_ext.h"
|
|
#include "assertions.h"
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <stdint.h>
|
|
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
|
|
#include "PHY/log_tools.h"
|
|
|
|
//#define DEBUG_RXDATA
|
|
//#define SRS_IND_DEBUG
|
|
|
|
static void nr_fill_indication(const PHY_VARS_gNB *gNB,
|
|
int frame,
|
|
int slot_rx,
|
|
const NR_gNB_PUSCH *pusch,
|
|
const nfapi_nr_pusch_pdu_t *pusch_pdu,
|
|
NR_gNB_PHY_STATS_t *stats,
|
|
uint8_t *payload,
|
|
int dtx_flag,
|
|
nfapi_nr_crc_t *crc,
|
|
nfapi_nr_rx_data_pdu_t *pdu);
|
|
|
|
int beam_index_allocation(bool das,
|
|
int fapi_beam_index,
|
|
NR_gNB_COMMON *common_vars,
|
|
int slot,
|
|
int symbols_per_slot,
|
|
int bitmap_symbols)
|
|
{
|
|
if (!common_vars->beam_id)
|
|
return 0;
|
|
|
|
AssertFatal(IS_BIT_SET(fapi_beam_index, 15), "Can't handle preconfigured DBM yet\n");
|
|
int ru_beam_idx = fapi_beam_index & 0x7fff;
|
|
if (das)
|
|
return ru_beam_idx;
|
|
|
|
int idx = -1;
|
|
for (int j = 0; j < common_vars->num_beams_period; j++) {
|
|
// L2 analog beam implementation is slot based, so we need to verify occupancy for the whole slot
|
|
for (int i = 0; i < symbols_per_slot; i++) {
|
|
int current_beam = common_vars->beam_id[j][slot * symbols_per_slot + i];
|
|
if (current_beam == -1 || current_beam == ru_beam_idx)
|
|
idx = j;
|
|
else {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
}
|
|
if (idx != -1)
|
|
break;
|
|
}
|
|
AssertFatal(idx >= 0, "Couldn't allocate beam ID %d\n", ru_beam_idx);
|
|
for (int j = 0; j < symbols_per_slot; j++) {
|
|
if (((bitmap_symbols >> j) & 0x01))
|
|
common_vars->beam_id[idx][slot * symbols_per_slot + j] = ru_beam_idx;
|
|
}
|
|
LOG_D(PHY, "Allocating beam_id[%d] %d in slot %d\n", idx, ru_beam_idx, slot);
|
|
return idx;
|
|
}
|
|
|
|
void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu)
|
|
{
|
|
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
|
|
const nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu->ssb_pdu_rel15;
|
|
uint8_t ssb_index = pdu->SsbBlockIndex;
|
|
LOG_D(PHY,"common_signal_procedures: frame %d, slot %d ssb index %d\n", frame, slot, ssb_index);
|
|
|
|
int ssb_start_symbol_abs = nr_get_ssb_start_symbol(fp, ssb_index); // computing the starting symbol for current ssb
|
|
uint16_t ssb_start_symbol = ssb_start_symbol_abs % fp->symbols_per_slot; // start symbol wrt slot
|
|
|
|
// Setting the first subcarrier
|
|
// 3GPP TS 38.211 sections 7.4.3.1 and 4.4.4.2
|
|
// for FR1 offsetToPointA and k_SSB are expressed in terms of 15 kHz SCS
|
|
// for FR2 offsetToPointA is expressed in terms of 60 kHz SCS and k_SSB expressed in terms of the subcarrier spacing provided
|
|
// by the higher-layer parameter subCarrierSpacingCommon
|
|
nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config;
|
|
const int scs = cfg->ssb_config.scs_common.value;
|
|
fp->ssb_start_subcarrier = nr_get_ssb_start_sc(scs,
|
|
pdu->ssbOffsetPointA,
|
|
pdu->SsbSubcarrierOffset,
|
|
fp->freq_range);
|
|
|
|
if (fp->print_ue_help_cmdline_log && IS_SA_MODE(get_softmodem_params())) {
|
|
fp->print_ue_help_cmdline_log = false;
|
|
if (fp->dl_CarrierFreq != fp->ul_CarrierFreq)
|
|
LOG_A(PHY,
|
|
"Command line parameters for OAI UE: -C %lu --CO %ld -r %d --numerology %d --ssb %d %s\n",
|
|
fp->dl_CarrierFreq,
|
|
fp->ul_CarrierFreq - fp->dl_CarrierFreq,
|
|
fp->N_RB_DL,
|
|
scs,
|
|
fp->ssb_start_subcarrier,
|
|
fp->threequarter_fs ? "-E" : "");
|
|
else
|
|
LOG_A(PHY,
|
|
"Command line parameters for OAI UE: -C %lu -r %d --numerology %d --ssb %d %s\n",
|
|
fp->dl_CarrierFreq,
|
|
fp->N_RB_DL,
|
|
scs,
|
|
fp->ssb_start_subcarrier,
|
|
fp->threequarter_fs ? "-E" : "");
|
|
}
|
|
LOG_D(PHY,"SS TX: frame %d, slot %d, start_symbol %d\n", frame, slot, ssb_start_symbol);
|
|
const nfapi_nr_tx_precoding_and_beamforming_t *pb = &pdu->precoding_and_beamforming;
|
|
c16_t ***txdataF = gNB->common_vars.txdataF;
|
|
// beam number in a scenario with multiple concurrent beams
|
|
int bitmap = SL_to_bitmap(ssb_start_symbol, 4); // 4 ssb symbols
|
|
int beam_nb = beam_index_allocation(gNB->enable_analog_das,
|
|
pb->prgs_list[0].dig_bf_interface_list[0].beam_idx,
|
|
&gNB->common_vars,
|
|
slot,
|
|
fp->symbols_per_slot,
|
|
bitmap);
|
|
|
|
nr_generate_pss(txdataF[beam_nb][0], gNB->TX_AMP, ssb_start_symbol, cfg, fp);
|
|
nr_generate_sss(txdataF[beam_nb][0], gNB->TX_AMP, ssb_start_symbol, cfg->cell_config.phy_cell_id.value, fp);
|
|
|
|
uint16_t slots_per_hf = (fp->slots_per_frame) >> 1;
|
|
int n_hf = slot < slots_per_hf ? 0 : 1;
|
|
|
|
int hf = fp->Lmax == 4 ? n_hf : 0;
|
|
nr_generate_pbch_dmrs(nr_gold_pbch(fp->Lmax, gNB->gNB_config.cell_config.phy_cell_id.value, hf, ssb_index & 7),
|
|
txdataF[beam_nb][0],
|
|
gNB->TX_AMP,
|
|
ssb_start_symbol,
|
|
cfg,
|
|
fp);
|
|
|
|
#if T_TRACER
|
|
if (T_ACTIVE(T_GNB_PHY_MIB)) {
|
|
unsigned char bch[3];
|
|
bch[0] = pdu->bchPayload & 0xff;
|
|
bch[1] = (pdu->bchPayload >> 8) & 0xff;
|
|
bch[2] = (pdu->bchPayload >> 16) & 0xff;
|
|
T(T_GNB_PHY_MIB, T_INT(0) /* module ID */, T_INT(frame), T_INT(slot), T_BUFFER(bch, 3));
|
|
}
|
|
#endif
|
|
|
|
nr_generate_pbch(gNB,
|
|
ssb_pdu,
|
|
txdataF[beam_nb][0],
|
|
ssb_start_symbol,
|
|
n_hf,
|
|
frame,
|
|
cfg,
|
|
fp);
|
|
}
|
|
|
|
// clearing beam information to be provided to RU for all slots (DL and UL)
|
|
void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot)
|
|
{
|
|
LOG_D(PHY, "Clearing beam_id structure for slot %d\n", slot);
|
|
int slot_sz = gNB->frame_parms.symbols_per_slot;
|
|
for (int i = 0; i < gNB->common_vars.num_beams_period; i++) {
|
|
if (gNB->common_vars.beam_id)
|
|
memset(&gNB->common_vars.beam_id[i][slot * slot_sz], -1, slot_sz * sizeof(**gNB->common_vars.beam_id));
|
|
}
|
|
}
|
|
|
|
static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, int slot, const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu)
|
|
{
|
|
const nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csi_params = &csi_rs_pdu->csi_rs_pdu_rel15;
|
|
if (csi_params->csi_type == 2) // ZP-CSI
|
|
return;
|
|
|
|
csi_mapping_parms_t mapping_parms =
|
|
get_csi_mapping_parms(csi_params->row, csi_params->freq_domain, csi_params->symb_l0, csi_params->symb_l1);
|
|
const nfapi_nr_tx_precoding_and_beamforming_t *pb = &csi_params->precodingAndBeamforming;
|
|
int csi_bitmap = 0;
|
|
int lprime_num = mapping_parms.lprime + 1;
|
|
for (int j = 0; j < mapping_parms.size; j++)
|
|
csi_bitmap |= ((1 << lprime_num) - 1) << mapping_parms.loverline[j];
|
|
int beam_nb = beam_index_allocation(gNB->enable_analog_das,
|
|
pb->prgs_list[0].dig_bf_interface_list[0].beam_idx,
|
|
&gNB->common_vars,
|
|
slot,
|
|
gNB->frame_parms.symbols_per_slot,
|
|
csi_bitmap);
|
|
|
|
nr_generate_csi_rs(&gNB->frame_parms,
|
|
&mapping_parms,
|
|
gNB->TX_AMP,
|
|
slot,
|
|
csi_params->freq_density,
|
|
csi_params->start_rb,
|
|
csi_params->nr_of_rbs,
|
|
csi_params->symb_l0,
|
|
csi_params->symb_l1,
|
|
csi_params->row,
|
|
csi_params->scramb_id,
|
|
csi_params->power_control_offset_ss,
|
|
csi_params->cdm_type,
|
|
gNB->common_vars.txdataF[beam_nb]);
|
|
}
|
|
|
|
void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
|
|
const nfapi_nr_dl_tti_request_t *DL_req,
|
|
const nfapi_nr_tx_data_request_t *TX_req,
|
|
const nfapi_nr_ul_dci_request_t *UL_dci_req,
|
|
int frame,
|
|
int slot)
|
|
{
|
|
const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
|
|
nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config;
|
|
|
|
if ((cfg->cell_config.frame_duplex_type.value == TDD) && (nr_slot_select(cfg,frame,slot) == NR_UPLINK_SLOT))
|
|
return;
|
|
|
|
// clear the transmit data array and beam index for the current slot
|
|
for (int i = 0; i < gNB->common_vars.num_beams_period; i++) {
|
|
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
|
|
memset(gNB->common_vars.txdataF[i][aa], 0, fp->samples_per_slot_wCP * sizeof(***gNB->common_vars.txdataF));
|
|
}
|
|
}
|
|
|
|
// Check for PRS slot - section 7.4.1.7.4 in 3GPP rel16 38.211
|
|
for(int rsc_id = 0; rsc_id < gNB->prs_vars.NumPRSResources; rsc_id++)
|
|
{
|
|
prs_config_t *prs_config = &gNB->prs_vars.prs_cfg[rsc_id];
|
|
for (int i = 0; i < prs_config->PRSResourceRepetition; i++)
|
|
{
|
|
if( (((frame*fp->slots_per_frame + slot) - (prs_config->PRSResourceSetPeriod[1] + prs_config->PRSResourceOffset)+prs_config->PRSResourceSetPeriod[0])%prs_config->PRSResourceSetPeriod[0]) == i*prs_config->PRSResourceTimeGap )
|
|
{
|
|
int slot_prs = (slot - i * prs_config->PRSResourceTimeGap + fp->slots_per_frame) % fp->slots_per_frame;
|
|
LOG_D(PHY,"gNB_TX: frame %d, slot %d, slot_prs %d, PRS Resource ID %d\n",frame, slot, slot_prs, rsc_id);
|
|
nr_generate_prs(slot_prs, gNB->common_vars.txdataF[0][0], AMP, prs_config, fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < UL_dci_req->numPdus; ++i)
|
|
nr_generate_dci(gNB, &UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
|
|
|
|
int num_pdsch = 0;
|
|
for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) {
|
|
const nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i];
|
|
switch (dl_tti_pdu->PDUType) {
|
|
case NFAPI_NR_DL_TTI_SSB_PDU_TYPE:
|
|
nr_common_signal_procedures(gNB, frame, slot, &dl_tti_pdu->ssb_pdu);
|
|
break;
|
|
case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE:
|
|
nr_generate_dci(gNB, &dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
|
|
break;
|
|
case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE:
|
|
nr_generate_csi_rs_gNB(gNB, slot, &dl_tti_pdu->csi_rs_pdu);
|
|
break;
|
|
case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: {
|
|
int tx_data_idx = dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15.pduIndex;
|
|
if (tx_data_idx < TX_req->Number_of_PDUs && TX_req->pdu_list[tx_data_idx].PDU_index == tx_data_idx) {
|
|
// reuse dlsch variables, as there are multiple very large memory
|
|
// buffers
|
|
gNB->dlsch[num_pdsch].pdsch_pdu = &dl_tti_pdu->pdsch_pdu;
|
|
gNB->dlsch[num_pdsch].pdu = (uint8_t *)TX_req->pdu_list[tx_data_idx].TLVs[0].value.direct;
|
|
DevAssert(num_pdsch < gNB->max_nb_pdsch);
|
|
num_pdsch++;
|
|
} else {
|
|
LOG_E(NR_PHY,
|
|
"%4d.%2d no corresponding tx_data.request for dl_tti.request index %d (out of %d)\n",
|
|
frame,
|
|
slot,
|
|
tx_data_idx,
|
|
TX_req->Number_of_PDUs);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (num_pdsch > 0) {
|
|
LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot);
|
|
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot);
|
|
}
|
|
|
|
//apply the OFDM symbol rotation here
|
|
start_meas(&gNB->phase_comp_stats);
|
|
for (int i = 0; i < gNB->common_vars.num_beams_period; ++i) {
|
|
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
|
|
if (gNB->phase_comp) {
|
|
apply_nr_rotation_TX(fp,
|
|
gNB->common_vars.txdataF[i][aa],
|
|
true,
|
|
fp->symbol_rotation[0],
|
|
slot,
|
|
fp->N_RB_DL,
|
|
0,
|
|
fp->Ncp == NR_EXTENDED ? 12 : 14);
|
|
}
|
|
T(T_GNB_PHY_DL_OUTPUT_SIGNAL,
|
|
T_INT(0),
|
|
T_INT(frame),
|
|
T_INT(slot),
|
|
T_INT(aa),
|
|
T_BUFFER(gNB->common_vars.txdataF[i][aa], fp->samples_per_slot_wCP * sizeof(int32_t)));
|
|
}
|
|
}
|
|
stop_meas(&gNB->phase_comp_stats);
|
|
}
|
|
|
|
static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int *ulsch_to_decode, int nb_pusch, NR_UL_IND_t *UL_INFO)
|
|
{
|
|
DevAssert(nb_pusch > 0);
|
|
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
|
|
|
//----------------------------------------------------------
|
|
//--------------------- ULSCH decoding ---------------------
|
|
//----------------------------------------------------------
|
|
|
|
int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, ulsch_to_decode, nb_pusch);
|
|
|
|
// CRC check per uplink shared channel
|
|
for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) {
|
|
uint8_t ULSCH_id = ulsch_to_decode[pusch_id];
|
|
NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id];
|
|
NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id];
|
|
NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process;
|
|
NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti);
|
|
const nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu;
|
|
|
|
bool crc_valid = false;
|
|
|
|
// if all segments are done
|
|
if (ulsch_harq->processedSegments == ulsch_harq->C) {
|
|
if (ulsch_harq->C > 1) {
|
|
int tbs = pusch_pdu->pusch_data.tb_size;
|
|
crc_valid = check_crc(ulsch_harq->b, lenWithCrc(1, tbs << 3), crcType(1, tbs << 3));
|
|
} else {
|
|
// When the number of code blocks is 1 (C = 1) and ulsch_harq->processedSegments = 1, we can assume a good TB because of the
|
|
// CRC check made by the LDPC for early termination, so, no need to perform CRC check twice for a single code block
|
|
crc_valid = true;
|
|
}
|
|
}
|
|
#if T_TRACER
|
|
if (T_ACTIVE(T_GNB_PHY_UL_PAYLOAD_RX_BITS)) {
|
|
// capture Rx Payload via T-Tracer for both CRC valid and invalid cases
|
|
// Get Time Stamp for T-tracer messages
|
|
char trace_rx_payload_time_stamp_str[30];
|
|
get_time_stamp_usec(trace_rx_payload_time_stamp_str);
|
|
// trace_rx_payload_time_stamp_str = 8 bytes timestamp = YYYYMMDD
|
|
// + 9 bytes timestamp = HHMMSSMMM
|
|
|
|
// Log GNB_PHY_UL_PAYLOAD_RX_BITS using T-Tracer if activated
|
|
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
|
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
|
// int,Nid_cell : int,rnti :
|
|
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
|
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
|
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port :
|
|
// int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
|
|
|
|
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
|
int dmrs_port = get_dmrs_port(0, pusch_pdu->dmrs_ports);
|
|
// int num_bytes = rdata->Kr_bytes - (ulsch_harq->F >> 3) - ((ulsch_harq->C > 1) ? 3 : 0);
|
|
// printf("num_bytes %d, len with CRC %d, data len %d\n", num_bytes, lenWithCrc(1, rdata->A), rdata->A);
|
|
// calculate the number of dmrs symbols in the slot
|
|
int number_dmrs_symbols = 0;
|
|
for (int l = pusch_pdu->start_symbol_index; l < pusch_pdu->start_symbol_index + pusch_pdu->nr_of_symbols; l++)
|
|
number_dmrs_symbols += ((pusch_pdu->ul_dmrs_symb_pos) >> l) & 0x01;
|
|
|
|
// Log GNB_PHY_UL_PAYLOAD_RX_BITS using T-Tracer if activated
|
|
T(T_GNB_PHY_UL_PAYLOAD_RX_BITS,
|
|
T_INT((int)ulsch->frame),
|
|
T_INT((int)ulsch->slot),
|
|
T_INT((int)split_time_stamp_and_convert_to_int(trace_rx_payload_time_stamp_str, 0, 8)),
|
|
T_INT((int)split_time_stamp_and_convert_to_int(trace_rx_payload_time_stamp_str, 8, 9)),
|
|
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
|
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
|
T_INT((int)pusch_pdu->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
|
T_INT((int)pusch_pdu->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
|
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
|
T_INT((int)frame_parms->Nid_cell),
|
|
T_INT((int)pusch_pdu->rnti),
|
|
T_INT((int)pusch_pdu->rb_size),
|
|
T_INT((int)pusch_pdu->rb_start),
|
|
T_INT((int)pusch_pdu->start_symbol_index), // start_ofdm_symbol
|
|
T_INT((int)pusch_pdu->nr_of_symbols), // num_ofdm_symbols
|
|
T_INT((int)pusch_pdu->qam_mod_order), // modulation
|
|
T_INT((int)pusch_pdu->mcs_index), // mcs
|
|
T_INT((int)pusch_pdu->mcs_table), // mcs_table_index
|
|
T_INT((int)pusch_pdu->nrOfLayers), // num_layer
|
|
T_INT((int)pusch_pdu->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
|
T_INT((int)pusch_pdu->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
|
T_INT((int)pusch_pdu->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
|
T_INT((int)number_dmrs_symbols),
|
|
// dmrs_start_ofdm_symbol
|
|
// dmrs_duration_num_ofdm_symbols
|
|
// dmrs_num_add_positions
|
|
T_INT((int)dmrs_port), // dmrs_antenna_port
|
|
T_INT((int)pusch_pdu->scid), // dmrs_nscid
|
|
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
|
T_INT((pusch_pdu->pusch_data.tb_size << 3)), // number_of_bits
|
|
T_BUFFER((uint8_t *)((ulsch_harq->b)), pusch_pdu->pusch_data.tb_size)); // data
|
|
}
|
|
#endif
|
|
|
|
nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++];
|
|
nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
|
|
if (crc_valid && !check_abort(&ulsch_harq->abort_decode) && !pusch->DTX) {
|
|
LOG_D(NR_PHY,
|
|
"[gNB %d] ULSCH %d: Setting ACK for SFN/SF %d.%d (rnti %x, pid %d, ndi %d, status %d, round %d, TBS %d, Max interation "
|
|
"(all seg) %d)\n",
|
|
gNB->Mod_id,
|
|
ULSCH_id,
|
|
ulsch->frame,
|
|
ulsch->slot,
|
|
ulsch->rnti,
|
|
ulsch->harq_pid,
|
|
pusch_pdu->pusch_data.new_data_indicator,
|
|
ulsch->active,
|
|
ulsch_harq->round,
|
|
pusch_pdu->pusch_data.tb_size,
|
|
ulsch->max_ldpc_iterations);
|
|
nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, ulsch->harq_process->b, 0, crc, pdu);
|
|
LOG_D(PHY, "ULSCH received ok \n");
|
|
ulsch->active = false;
|
|
ulsch_harq->round = 0;
|
|
ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations - 1; // Setting to max_ldpc_iterations - 1 is sufficient given that this variable is only used for checking for failure
|
|
} else {
|
|
LOG_D(PHY,
|
|
"[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, ndi %d, status %d, round %d, RV %d, prb_start %d, prb_size %d, "
|
|
"TBS %d)\n",
|
|
gNB->Mod_id,
|
|
ulsch->frame,
|
|
ulsch->slot,
|
|
ulsch->harq_pid,
|
|
pusch_pdu->pusch_data.new_data_indicator,
|
|
ulsch->active,
|
|
ulsch_harq->round,
|
|
ulsch_harq->ulsch_pdu.pusch_data.rv_index,
|
|
ulsch_harq->ulsch_pdu.rb_start,
|
|
ulsch_harq->ulsch_pdu.rb_size,
|
|
pusch_pdu->pusch_data.tb_size);
|
|
nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, NULL, 0, crc, pdu);
|
|
gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK");
|
|
LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id);
|
|
ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations; // Setting to max_ldpc_iterations is sufficient given that this variable is only used for checking for failure
|
|
}
|
|
}
|
|
|
|
return ret_nr_ulsch_decoding;
|
|
}
|
|
|
|
static void nr_fill_indication(const PHY_VARS_gNB *gNB,
|
|
int frame,
|
|
int slot_rx,
|
|
const NR_gNB_PUSCH *pusch,
|
|
const nfapi_nr_pusch_pdu_t *pusch_pdu,
|
|
NR_gNB_PHY_STATS_t *stats,
|
|
uint8_t *payload,
|
|
int dtx_flag,
|
|
nfapi_nr_crc_t *crc,
|
|
nfapi_nr_rx_data_pdu_t *pdu)
|
|
{
|
|
// Get estimated timing advance for MAC
|
|
const int sync_pos = pusch->delay.est_delay;
|
|
|
|
// scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size
|
|
uint16_t bw_scaling = 16 * gNB->frame_parms.ofdm_symbol_size / 2048;
|
|
|
|
// do some integer rounding to improve TA accuracy
|
|
int sync_pos_rounded;
|
|
if (sync_pos > 0)
|
|
sync_pos_rounded = sync_pos + (bw_scaling / 2) - 1;
|
|
else
|
|
sync_pos_rounded = sync_pos - (bw_scaling / 2) + 1;
|
|
if (stats)
|
|
stats->ulsch_stats.sync_pos = sync_pos;
|
|
|
|
int timing_advance_update = sync_pos_rounded / bw_scaling;
|
|
|
|
// put timing advance command in 0..63 range
|
|
timing_advance_update += 31;
|
|
timing_advance_update = max(timing_advance_update, 0);
|
|
timing_advance_update = min(timing_advance_update, 63);
|
|
|
|
// estimate UL_CQI for MAC
|
|
int SNRtimes10 = dB_fixed_x10(pusch->ulsch_power_tot) - dB_fixed_x10(pusch->ulsch_noise_power_tot);
|
|
|
|
LOG_D(PHY,
|
|
"%d.%d: Estimated SNR for PUSCH is = %f dB (ulsch_power %f, noise %f) delay %d\n",
|
|
frame,
|
|
slot_rx,
|
|
SNRtimes10 / 10.0,
|
|
dB_fixed_x10(pusch->ulsch_power_tot) / 10.0,
|
|
dB_fixed_x10(pusch->ulsch_noise_power_tot) / 10.0,
|
|
sync_pos);
|
|
|
|
int cqi;
|
|
if (SNRtimes10 < -640) cqi=0;
|
|
else if (SNRtimes10 > 635) cqi=255;
|
|
else cqi=(640+SNRtimes10)/5;
|
|
|
|
crc->handle = pusch_pdu->handle;
|
|
crc->rnti = pusch_pdu->rnti;
|
|
crc->harq_id = pusch_pdu->pusch_data.harq_process_id;
|
|
crc->tb_crc_status = payload == NULL; // 222.10.02/04: pass 0, fail 1
|
|
crc->num_cb = pusch_pdu->pusch_data.num_cb;
|
|
crc->ul_cqi = cqi;
|
|
crc->timing_advance = timing_advance_update;
|
|
// in terms of dBFS range -128 to 0 with 0.1 step
|
|
crc->rssi = (dtx_flag == 0) ? 1280 - (10 * dB_fixed(32767 * 32767) - dB_fixed_times10(pusch->ulsch_power[0])) : 0;
|
|
|
|
pdu->handle = pusch_pdu->handle;
|
|
pdu->rnti = pusch_pdu->rnti;
|
|
pdu->harq_id = pusch_pdu->pusch_data.harq_process_id;
|
|
pdu->ul_cqi = cqi;
|
|
pdu->timing_advance = timing_advance_update;
|
|
pdu->rssi = crc->rssi;
|
|
pdu->pdu_length = payload != NULL ? pusch_pdu->pusch_data.tb_size : 0;
|
|
pdu->pdu = payload;
|
|
}
|
|
|
|
// Function to fill UL RB mask to be used for N0 measurements
|
|
static void fill_ul_rb_mask(PHY_VARS_gNB *gNB,
|
|
fsn_t now,
|
|
uint32_t rb_mask_ul[14][9],
|
|
nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf,
|
|
const NR_gNB_PUCCH_job_t *pucch,
|
|
int n_pucch,
|
|
const NR_gNB_PUSCH_job_t *pusch,
|
|
int n_pusch)
|
|
{
|
|
for (int symbol = 0; symbol < 14; symbol++) {
|
|
for (int m = 0; m < 9; m++) {
|
|
rb_mask_ul[symbol][m] = 0;
|
|
for (int i = 0; i < 32; i++) {
|
|
int prbpos = (m * 32) + i;
|
|
if (prbpos > gNB->frame_parms.N_RB_UL)
|
|
break;
|
|
rb_mask_ul[symbol][m] |= (gNB->ulprbbl[prbpos] > 0 ? 1U : 0) << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < n_pucch; ++i) {
|
|
const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu;
|
|
const int start = pucch_pdu->start_symbol_index;
|
|
for (int symbol = start; symbol < (start + pucch_pdu->nr_of_symbols); symbol++) {
|
|
if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) {
|
|
for (int rb = 0; rb < pucch_pdu->prb_size; rb++) {
|
|
int rb2 =
|
|
rb + pucch_pdu->bwp_start
|
|
+ ((symbol < start + (pucch_pdu->nr_of_symbols >> 1)) || (pucch_pdu->freq_hop_flag == 0) ? pucch_pdu->prb_start
|
|
: pucch_pdu->second_hop_prb);
|
|
rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < n_pusch; i++) {
|
|
const nfapi_nr_pusch_pdu_t *pusch_pdu = &pusch[i].pusch_pdu;
|
|
uint8_t symbol_start = pusch_pdu->start_symbol_index;
|
|
uint8_t symbol_end = symbol_start + pusch_pdu->nr_of_symbols;
|
|
for (int symbol = symbol_start; symbol < symbol_end; symbol++) {
|
|
if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) {
|
|
LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, pusch_pdu->rb_size);
|
|
for (int rb = 0; rb < pusch_pdu->rb_size; rb++) {
|
|
int rb2 = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start;
|
|
rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < gNB->max_nb_srs; i++) {
|
|
NR_gNB_SRS_t *srs = &gNB->srs[i];
|
|
if (!(srs && srs->active && srs->frame == now.f && srs->slot == now.s))
|
|
continue;
|
|
nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu;
|
|
const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position;
|
|
for (int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) {
|
|
for (int rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start + srs_pdu->bwp_size); rb++) {
|
|
rb_mask_ul[l0 + symbol][rb >> 5] |= 1U << (rb & 31);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int fill_srs_reported_symbol(nfapi_nr_srs_reported_symbol_t *reported_symbol,
|
|
const nfapi_nr_srs_pdu_t *srs_pdu,
|
|
const int16_t *snr_per_rb,
|
|
const int srs_est)
|
|
{
|
|
reported_symbol->num_prgs = srs_pdu->beamforming.num_prgs;
|
|
for (int prg_idx = 0; prg_idx < reported_symbol->num_prgs; prg_idx++) {
|
|
uint8_t *snr = &reported_symbol->prg_list[prg_idx].rb_snr;
|
|
if (srs_est < 0) {
|
|
*snr = 0xFF;
|
|
} else if (snr_per_rb[prg_idx] < -64) {
|
|
*snr = 0;
|
|
} else if (snr_per_rb[prg_idx] > 63) {
|
|
*snr = 0xFE;
|
|
} else {
|
|
*snr = (snr_per_rb[prg_idx] + 64) << 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fill_srs_channel_matrix(
|
|
nfapi_nr_srs_normalized_channel_iq_matrix_t *nr_srs_channel_iq_matrix,
|
|
const nfapi_nr_srs_pdu_t *srs_pdu,
|
|
const nr_srs_info_t *nr_srs_info,
|
|
const uint8_t normalized_iq_representation,
|
|
const uint16_t num_gnb_antenna_elements,
|
|
const uint16_t num_ue_srs_ports,
|
|
const uint16_t prg_size,
|
|
const uint16_t num_prgs,
|
|
const NR_DL_FRAME_PARMS *frame_parms,
|
|
const c16_t srs_estimated_channel_freq[][1 << srs_pdu->num_ant_ports]
|
|
[frame_parms->ofdm_symbol_size * (1 << srs_pdu->num_symbols)])
|
|
{
|
|
nr_srs_channel_iq_matrix->normalized_iq_representation = normalized_iq_representation;
|
|
nr_srs_channel_iq_matrix->num_gnb_antenna_elements = num_gnb_antenna_elements;
|
|
nr_srs_channel_iq_matrix->num_ue_srs_ports = num_ue_srs_ports;
|
|
nr_srs_channel_iq_matrix->prg_size = prg_size;
|
|
nr_srs_channel_iq_matrix->num_prgs = num_prgs;
|
|
uint8_t *channel_matrix = nr_srs_channel_iq_matrix->channel_matrix;
|
|
const uint64_t subcarrier_offset = frame_parms->first_carrier_offset + srs_pdu->bwp_start*NR_NB_SC_PER_RB;
|
|
const uint16_t step = prg_size*NR_NB_SC_PER_RB;
|
|
|
|
c16_t *channel_matrix16 = (c16_t*)channel_matrix;
|
|
c8_t *channel_matrix8 = (c8_t*)channel_matrix;
|
|
|
|
for(int uI = 0; uI < num_ue_srs_ports; uI++) {
|
|
for(int gI = 0; gI < num_gnb_antenna_elements; gI++) {
|
|
|
|
uint16_t subcarrier = subcarrier_offset + nr_srs_info->k_0_p[uI][0];
|
|
if (subcarrier >= frame_parms->ofdm_symbol_size) {
|
|
subcarrier -= frame_parms->ofdm_symbol_size;
|
|
}
|
|
|
|
for(int pI = 0; pI < num_prgs; pI++) {
|
|
const c16_t *srs_estimated_channel16 = srs_estimated_channel_freq[gI][uI] + subcarrier;
|
|
uint16_t index = uI*num_gnb_antenna_elements*num_prgs + gI*num_prgs + pI;
|
|
|
|
if (normalized_iq_representation == 0) {
|
|
channel_matrix8[index].r = (int8_t)(srs_estimated_channel16->r>>8);
|
|
channel_matrix8[index].i = (int8_t)(srs_estimated_channel16->i>>8);
|
|
} else {
|
|
channel_matrix16[index].r = srs_estimated_channel16->r;
|
|
channel_matrix16[index].i = srs_estimated_channel16->i;
|
|
}
|
|
|
|
// Subcarrier increment
|
|
subcarrier += step;
|
|
if (subcarrier >= frame_parms->ofdm_symbol_size) {
|
|
subcarrier -= frame_parms->ofdm_symbol_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void copy_srs_info(const nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_t *nr_srs_info)
|
|
{
|
|
nr_srs_info->B_SRS = srs_config_pdu->bandwidth_index;
|
|
nr_srs_info->C_SRS = srs_config_pdu->config_index;
|
|
nr_srs_info->b_hop = srs_config_pdu->frequency_hopping;
|
|
nr_srs_info->comb_size = srs_config_pdu->comb_size;
|
|
nr_srs_info->K_TC_overbar = srs_config_pdu->comb_offset;
|
|
nr_srs_info->n_SRS_cs = srs_config_pdu->cyclic_shift;
|
|
nr_srs_info->n_ID_SRS = srs_config_pdu->sequence_id;
|
|
// It adjusts the SRS allocation to align with the common resource block grid in multiples of four
|
|
nr_srs_info->n_shift = srs_config_pdu->frequency_position;
|
|
nr_srs_info->n_RRC = srs_config_pdu->frequency_shift;
|
|
nr_srs_info->groupOrSequenceHopping = srs_config_pdu->group_or_sequence_hopping;
|
|
nr_srs_info->l_offset = srs_config_pdu->time_start_position;
|
|
nr_srs_info->T_SRS = srs_config_pdu->t_srs;
|
|
nr_srs_info->T_offset = srs_config_pdu->t_offset;
|
|
nr_srs_info->R = 1 << srs_config_pdu->num_repetitions;
|
|
nr_srs_info->N_symb_SRS = 1 << srs_config_pdu->num_symbols; // Number of consecutive OFDM symbols
|
|
nr_srs_info->n_srs_ports = 1 << srs_config_pdu->num_ant_ports; // Number of antenna port for transmission
|
|
nr_srs_info->resource_type = srs_config_pdu->resource_type;
|
|
}
|
|
|
|
nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB,
|
|
int frame_rx,
|
|
int slot_rx,
|
|
uint8_t nb_antennas_rx,
|
|
uint8_t N_ap,
|
|
uint8_t N_symb_SRS,
|
|
uint16_t ofdm_symbol_size,
|
|
NR_gNB_SRS_t *srs,
|
|
int *srs_est,
|
|
c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS],
|
|
c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size],
|
|
int16_t *snr_per_rb,
|
|
uint16_t *timing_advance_offset,
|
|
int16_t *timing_advance_offset_nsec)
|
|
{
|
|
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
|
nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu;
|
|
c16_t srs_received_signal[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS];
|
|
c16_t srs_received_noise[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS];
|
|
c16_t srs_estimated_channel_time_shifted[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size];
|
|
|
|
start_meas(&gNB->generate_srs_stats);
|
|
|
|
nr_srs_info_t nr_srs_info = {0};
|
|
copy_srs_info(srs_pdu, &nr_srs_info);
|
|
|
|
// TODO permanently allocate?
|
|
c16_t srs_generated_signal_tmp[N_ap][ofdm_symbol_size * MAX_NUM_NR_SRS_SYMBOLS];
|
|
c16_t *srs_generated_signal[N_ap];
|
|
for (int i = 0; i < N_ap; ++i)
|
|
srs_generated_signal[i] = srs_generated_signal_tmp[i];
|
|
if (true) {
|
|
generate_srs_nr(frame_parms,
|
|
srs_generated_signal,
|
|
0,
|
|
srs_pdu->bwp_start,
|
|
&nr_srs_info,
|
|
AMP,
|
|
frame_rx,
|
|
slot_rx,
|
|
nb_antennas_rx);
|
|
}
|
|
|
|
stop_meas(&gNB->generate_srs_stats);
|
|
c16_t **rxdataF = gNB->common_vars.rxdataF[srs->beam_nb];
|
|
start_meas(&gNB->get_srs_signal_stats);
|
|
*srs_est = nr_get_srs_signal(gNB, rxdataF, slot_rx, srs_pdu, &nr_srs_info, srs_received_signal, srs_received_noise);
|
|
stop_meas(&gNB->get_srs_signal_stats);
|
|
|
|
uint32_t signal_power_avg = 0;
|
|
c16_t srs_ls_estimated_channel[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS];
|
|
|
|
if (*srs_est >= 0) {
|
|
start_meas(&gNB->srs_channel_estimation_stats);
|
|
|
|
delay_t delay = {0};
|
|
for (int ant_rx_ind = 0; ant_rx_ind < nb_antennas_rx; ant_rx_ind++) {
|
|
for (int p_ind = 0; p_ind < N_ap; p_ind++) {
|
|
delay_t delay_aux = {0};
|
|
nr_srs_ls_channel_estimation(ant_rx_ind,
|
|
p_ind,
|
|
ofdm_symbol_size,
|
|
frame_parms->first_carrier_offset,
|
|
N_symb_SRS,
|
|
srs_pdu,
|
|
&nr_srs_info,
|
|
srs_generated_signal[p_ind],
|
|
srs_received_signal[ant_rx_ind],
|
|
srs_ls_estimated_channel[ant_rx_ind][p_ind],
|
|
&delay_aux);
|
|
if (delay_aux.delay_max_val > delay.delay_max_val)
|
|
delay = delay_aux;
|
|
}
|
|
}
|
|
|
|
for (int ant_rx_ind = 0; ant_rx_ind < nb_antennas_rx; ant_rx_ind++) {
|
|
for (int p_ind = 0; p_ind < N_ap; p_ind++) {
|
|
uint32_t signal_power = 0;
|
|
nr_srs_channel_interpolation(ant_rx_ind,
|
|
p_ind,
|
|
ofdm_symbol_size,
|
|
frame_parms->first_carrier_offset,
|
|
N_symb_SRS,
|
|
srs_pdu,
|
|
&nr_srs_info,
|
|
srs_ls_estimated_channel[ant_rx_ind][p_ind],
|
|
delay.est_delay,
|
|
srs_received_noise[ant_rx_ind],
|
|
srs_estimated_channel_freq[ant_rx_ind][p_ind],
|
|
srs_estimated_channel_time[ant_rx_ind][p_ind],
|
|
srs_estimated_channel_time_shifted[ant_rx_ind][p_ind],
|
|
&signal_power,
|
|
frame_parms->delay_table);
|
|
|
|
signal_power_avg += signal_power;
|
|
|
|
T(T_GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE,
|
|
T_INT(gNB->Mod_id),
|
|
T_INT(srs_pdu->rnti),
|
|
T_INT(frame_rx),
|
|
T_INT(0),
|
|
T_INT(ant_rx_ind),
|
|
T_INT(p_ind),
|
|
T_BUFFER(srs_estimated_channel_freq[ant_rx_ind][p_ind], N_symb_SRS * ofdm_symbol_size * sizeof(c16_t)));
|
|
|
|
T(T_GNB_PHY_UL_TIME_CHANNEL_ESTIMATE,
|
|
T_INT(gNB->Mod_id),
|
|
T_INT(srs_pdu->rnti),
|
|
T_INT(frame_rx),
|
|
T_INT(0),
|
|
T_INT(ant_rx_ind),
|
|
T_INT(p_ind),
|
|
T_BUFFER(srs_estimated_channel_time_shifted[ant_rx_ind][p_ind],
|
|
NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size * sizeof(c16_t)));
|
|
}
|
|
}
|
|
|
|
signal_power_avg /= (nb_antennas_rx * N_ap);
|
|
signal_power_avg = max(signal_power_avg, 1);
|
|
|
|
uint32_t noise_power_avg = 0;
|
|
int16_t noise_power_per_rb[srs_pdu->bwp_size];
|
|
memset(noise_power_per_rb, 0, srs_pdu->bwp_size * sizeof(int16_t));
|
|
for (int ant_rx_ind = 0; ant_rx_ind < nb_antennas_rx; ant_rx_ind++) {
|
|
uint32_t noise_power_per_ant = 0;
|
|
nr_srs_noise_power_estimation(ofdm_symbol_size,
|
|
frame_parms->first_carrier_offset,
|
|
N_symb_SRS,
|
|
srs_pdu,
|
|
&nr_srs_info,
|
|
signal_power_avg,
|
|
srs_received_noise[ant_rx_ind],
|
|
&noise_power_per_ant,
|
|
noise_power_per_rb);
|
|
noise_power_avg += noise_power_per_ant;
|
|
}
|
|
|
|
noise_power_avg /= nb_antennas_rx;
|
|
gNB->srs->snr = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_avg, 1));
|
|
|
|
const uint16_t m_SRS_b = get_m_srs(srs_pdu->config_index, srs_pdu->bandwidth_index);
|
|
for (int rb = 0; rb < m_SRS_b; rb++) {
|
|
snr_per_rb[rb] = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_per_rb[rb] / nb_antennas_rx, 1));
|
|
}
|
|
stop_meas(&gNB->srs_channel_estimation_stats);
|
|
|
|
start_meas(&gNB->srs_timing_advance_stats);
|
|
for (int ant_rx_ind = 0; ant_rx_ind < nb_antennas_rx; ant_rx_ind++) {
|
|
nr_est_srs_timing_advance_offset(ofdm_symbol_size,
|
|
srs_estimated_channel_time[ant_rx_ind],
|
|
ant_rx_ind,
|
|
N_ap,
|
|
frame_parms->samples_per_frame,
|
|
timing_advance_offset,
|
|
&timing_advance_offset_nsec[ant_rx_ind]);
|
|
}
|
|
stop_meas(&gNB->srs_timing_advance_stats);
|
|
|
|
T(T_GNB_PHY_UL_SNR_ESTIMATE,
|
|
T_INT(0),
|
|
T_INT(srs_pdu->rnti),
|
|
T_INT(frame_rx),
|
|
T_INT(0),
|
|
T_INT(0),
|
|
T_BUFFER(snr_per_rb, srs_pdu->bwp_size * sizeof(int16_t)));
|
|
|
|
T(T_GNB_PHY_UL_SRS_TOA_NS,
|
|
T_INT(gNB->Mod_id),
|
|
T_INT(srs_pdu->rnti),
|
|
T_INT(frame_rx),
|
|
T_INT(slot_rx),
|
|
T_BUFFER(timing_advance_offset_nsec, nb_antennas_rx * sizeof(int16_t)));
|
|
}
|
|
return nr_srs_info;
|
|
}
|
|
|
|
static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci)
|
|
{
|
|
const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu;
|
|
|
|
switch (pucch_pdu->format_type) {
|
|
case 0:
|
|
uci->pdu_type = NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE;
|
|
uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_0_1_t);
|
|
nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu_format0 = &uci->pucch_pdu_format_0_1;
|
|
nr_decode_pucch0(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format0, pucch_pdu);
|
|
break;
|
|
case 2:
|
|
uci->pdu_type = NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE;
|
|
uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_2_3_4_t);
|
|
nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu_format2 = &uci->pucch_pdu_format_2_3_4;
|
|
nr_decode_pucch2(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format2, pucch_pdu);
|
|
break;
|
|
default:
|
|
AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n");
|
|
}
|
|
}
|
|
|
|
static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, NR_gNB_ULSCH_t *ulsch, NR_UL_IND_t *UL_INFO, int *pusch_DTX)
|
|
{
|
|
NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process;
|
|
AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid);
|
|
const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu;
|
|
|
|
#ifdef DEBUG_RXDATA
|
|
RU_t *ru = gNB->RU_list[0];
|
|
int slot_offset = frame_parms->get_samples_slot_timestamp(ulsch->slot, frame_parms, 0);
|
|
slot_offset -= ru->N_TA_offset;
|
|
int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset;
|
|
int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset];
|
|
buf[0] = (int16_t)ulsch->rnti;
|
|
buf[1] = (int16_t)pdu->rb_size;
|
|
buf[2] = (int16_t)pdu->rb_start;
|
|
buf[3] = (int16_t)pdu->nr_of_symbols;
|
|
buf[4] = (int16_t)pdu->start_symbol_index;
|
|
buf[5] = (int16_t)pdu->mcs_index;
|
|
buf[6] = (int16_t)pdu->pusch_data.rv_index;
|
|
buf[7] = (int16_t)ulsch->harq_pid;
|
|
memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4],
|
|
&ru->common.rxdata[0][slot_offset],
|
|
frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) * sizeof(int32_t));
|
|
gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4);
|
|
if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 2) * 20)) {
|
|
FILE *f;
|
|
f = fopen("rxdata_buff.raw", "w");
|
|
if (f == NULL)
|
|
exit(1);
|
|
fwrite((int16_t *)gNB->common_vars.debugBuff,
|
|
2,
|
|
(frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4) * 20 * 2,
|
|
f);
|
|
fclose(f);
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
|
|
start_meas(&gNB->rx_pusch_stats);
|
|
nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, ulsch->frame, ulsch->slot, ulsch->beam_nb);
|
|
pusch_vars->ulsch_power_tot = 0;
|
|
pusch_vars->ulsch_noise_power_tot = 0;
|
|
for (int aarx = 0; aarx < gNB->frame_parms.nb_antennas_rx; aarx++) {
|
|
pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx];
|
|
pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx];
|
|
}
|
|
if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) {
|
|
NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti);
|
|
|
|
LOG_D(PHY,
|
|
"PUSCH not detected in %d.%d (%d,%d,%d)\n",
|
|
ulsch->frame,
|
|
ulsch->slot,
|
|
dB_fixed_x10(pusch_vars->ulsch_power_tot),
|
|
dB_fixed_x10(pusch_vars->ulsch_noise_power_tot),
|
|
gNB->pusch_thres);
|
|
pusch_vars->ulsch_power_tot = pusch_vars->ulsch_noise_power_tot;
|
|
pusch_vars->DTX = 1;
|
|
if (stats)
|
|
stats->ulsch_stats.DTX++;
|
|
if (!get_softmodem_params()->phy_test) {
|
|
/* in case of phy_test mode, we still want to decode to measure execution time.
|
|
Therefore, we don't yet call nr_fill_indication, it will be called later */
|
|
nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++];
|
|
nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
|
|
nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch_vars, pdu, stats, NULL, 1, crc, rx);
|
|
(*pusch_DTX)++;
|
|
gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX");
|
|
return false;
|
|
}
|
|
} else {
|
|
LOG_D(PHY,
|
|
"PUSCH detected in %d.%d (%d,%d,%d)\n",
|
|
ulsch->frame,
|
|
ulsch->slot,
|
|
dB_fixed_x10(pusch_vars->ulsch_power_tot),
|
|
dB_fixed_x10(pusch_vars->ulsch_noise_power_tot),
|
|
gNB->pusch_thres);
|
|
|
|
pusch_vars->DTX = 0;
|
|
}
|
|
stop_meas(&gNB->rx_pusch_stats);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool drop_old_pucch(const void *data, void *user)
|
|
{
|
|
const NR_gNB_PUCCH_job_t *pucch = data;
|
|
const fsn_t *now = user;
|
|
const fsn_t t = {pucch->frame, pucch->slot, now->mu};
|
|
bool drop = fsn_in_the_past(t, *now);
|
|
if (drop)
|
|
LOG_E(NR_PHY, "%4d.%2d PUCCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pucch->pucch_pdu.rnti, t.f, t.s);
|
|
return drop;
|
|
}
|
|
|
|
static bool get_current_pucch(const void *data, void *user)
|
|
{
|
|
const NR_gNB_PUCCH_job_t *pucch = data;
|
|
const fsn_t *now = user;
|
|
const fsn_t t = {pucch->frame, pucch->slot, now->mu};
|
|
return fsn_equal(t, *now);
|
|
}
|
|
|
|
static bool drop_old_pusch(const void *data, void *user)
|
|
{
|
|
const NR_gNB_PUSCH_job_t *pusch = data;
|
|
const fsn_t *now = user;
|
|
const fsn_t t = {pusch->frame, pusch->slot, now->mu};
|
|
bool drop = fsn_in_the_past(t, *now);
|
|
if (drop)
|
|
LOG_E(NR_PHY, "%4d.%2d PUSCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pusch->pusch_pdu.rnti, t.f, t.s);
|
|
return drop;
|
|
}
|
|
|
|
static bool get_current_pusch(const void *data, void *user)
|
|
{
|
|
const NR_gNB_PUSCH_job_t *pusch = data;
|
|
const fsn_t *now = user;
|
|
const fsn_t t = {pusch->frame, pusch->slot, now->mu};
|
|
return fsn_equal(t, *now);
|
|
}
|
|
|
|
static int find_nr_ulsch_idx(PHY_VARS_gNB *gNB, uint16_t rnti, int pid)
|
|
{
|
|
AssertFatal(gNB != NULL, "gNB is null\n");
|
|
|
|
int16_t first_free_index = -1;
|
|
for (int i = 0; i < gNB->max_nb_pusch; i++) {
|
|
const NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[i];
|
|
AssertFatal(ulsch, "gNB->ulsch[%d] is null\n", i);
|
|
if (!ulsch->active) {
|
|
if (first_free_index == -1)
|
|
first_free_index = i;
|
|
} else {
|
|
// if there is already an active ULSCH for this RNTI and HARQ_PID
|
|
if ((ulsch->harq_pid == pid) && (ulsch->rnti == rnti))
|
|
return i;
|
|
}
|
|
}
|
|
return first_free_index;
|
|
}
|
|
static int handle_pusch_job_trigger(PHY_VARS_gNB *gNB, const NR_gNB_PUSCH_job_t *job)
|
|
{
|
|
const nfapi_nr_pusch_pdu_t *pdu = &job->pusch_pdu;
|
|
int pid = pdu->pusch_data.harq_process_id;
|
|
int ULSCH_id = find_nr_ulsch_idx(gNB, pdu->rnti, pid);
|
|
if (ULSCH_id < 0) {
|
|
LOG_E(NR_PHY, "%4d.%2d cannot handle PUSCH job of RNTI %04x: no space\n", job->frame, job->slot, pdu->rnti);
|
|
return -1;
|
|
}
|
|
/* (re-)initialize this PUSCH context from job data */
|
|
NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id];
|
|
ulsch->active = true;
|
|
ulsch->frame = job->frame;
|
|
ulsch->slot = job->slot;
|
|
ulsch->rnti = pdu->rnti;
|
|
ulsch->harq_pid = pid;
|
|
ulsch->beam_nb = job->beam_nb;
|
|
ulsch->harq_process->ulsch_pdu = job->pusch_pdu;
|
|
if (pdu->pusch_data.new_data_indicator) {
|
|
ulsch->harq_process->harq_to_be_cleared = true;
|
|
ulsch->harq_process->round = 0;
|
|
} else {
|
|
ulsch->harq_process->round++;
|
|
}
|
|
return ULSCH_id;
|
|
}
|
|
|
|
int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO)
|
|
{
|
|
/* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */
|
|
int pusch_DTX = 0;
|
|
|
|
const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
|
const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size;
|
|
const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
|
|
|
|
fsn_t now = {frame_rx, slot_rx, frame_parms->numerology_index};
|
|
|
|
spsc_q_drop_while(&gNB->pucch_queue, drop_old_pucch, &now);
|
|
NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS];
|
|
int n_pucch = spsc_q_get_while(&gNB->pucch_queue, get_current_pucch, &now, pucch, sizeof(*pucch), MAX_NUM_NR_UCI_PDUS);
|
|
|
|
spsc_q_drop_while(&gNB->pusch_queue, drop_old_pusch, &now);
|
|
NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT];
|
|
int n_pusch_jobs = spsc_q_get_while(&gNB->pusch_queue, get_current_pusch, &now, pusch, sizeof(*pusch), MAX_UL_PDUS_PER_SLOT);
|
|
|
|
LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx);
|
|
{
|
|
// Mask of occupied RBs, per symbol and PRB
|
|
uint32_t rb_mask_ul[14][9];
|
|
nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL;
|
|
if (frame_parms->frame_type == TDD)
|
|
slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list;
|
|
fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs);
|
|
|
|
int first_symb = 0, num_symb = 0;
|
|
if (frame_parms->frame_type == TDD)
|
|
for (int symbol_count = 0; symbol_count < frame_parms->symbols_per_slot; symbol_count++) {
|
|
if (slot_conf[symbol_count].slot_config.value == 1) {
|
|
if (num_symb == 0)
|
|
first_symb = symbol_count;
|
|
num_symb++;
|
|
}
|
|
}
|
|
else
|
|
num_symb = frame_parms->symbols_per_slot;
|
|
gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul);
|
|
}
|
|
|
|
start_meas(&gNB->phy_proc_rx);
|
|
UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list;
|
|
UL_INFO->uci_ind.sfn = frame_rx;
|
|
UL_INFO->uci_ind.slot = slot_rx;
|
|
UL_INFO->uci_ind.num_ucis = n_pucch;
|
|
nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list;
|
|
for (int i = 0; i < n_pucch; ++i) {
|
|
c16_t **rxdataF = gNB->common_vars.rxdataF[pucch->beam_nb];
|
|
handle_pucch(gNB, rxdataF, &pucch[i], uci++);
|
|
}
|
|
|
|
UL_INFO->crc_ind.sfn = frame_rx;
|
|
UL_INFO->crc_ind.slot = slot_rx;
|
|
UL_INFO->crc_ind.crc_list = UL_INFO->crc_pdu_list;
|
|
UL_INFO->rx_ind.sfn = frame_rx;
|
|
UL_INFO->rx_ind.slot = slot_rx;
|
|
UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list;
|
|
int ulsch_idx_to_decode[MAX_UL_PDUS_PER_SLOT];
|
|
int num_pusch = 0;
|
|
for (int i = 0; i < n_pusch_jobs; ++i) {
|
|
int ULSCH_id = handle_pusch_job_trigger(gNB, &pusch[i]);
|
|
if (ULSCH_id < 0)
|
|
continue;
|
|
NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id];
|
|
NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id];
|
|
if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX))
|
|
ulsch_idx_to_decode[num_pusch++] = ULSCH_id;
|
|
}
|
|
|
|
/* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1
|
|
* (valid for unitary physical simulators). ULSCH processing lopp is then executed
|
|
* only once, which ensures exactly one start and stop of the ULSCH decoding time
|
|
* measurement per processed TB.*/
|
|
if (gNB->max_nb_pusch == 1)
|
|
start_meas(&gNB->ulsch_decoding_stats);
|
|
|
|
if (num_pusch > 0) {
|
|
int ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_idx_to_decode, num_pusch, UL_INFO);
|
|
if (ret_nr_ulsch_procedures != 0)
|
|
LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures);
|
|
}
|
|
|
|
/* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1
|
|
* (valid for unitary physical simulators). ULSCH processing loop is then executed
|
|
* only once, which ensures exactly one start and stop of the ULSCH decoding time
|
|
* measurement per processed TB.*/
|
|
if (gNB->max_nb_pusch == 1)
|
|
stop_meas(&gNB->ulsch_decoding_stats);
|
|
|
|
for (int i = 0; i < gNB->max_nb_srs; i++) {
|
|
NR_gNB_SRS_t *srs = &gNB->srs[i];
|
|
if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx))
|
|
continue;
|
|
LOG_D(NR_PHY, "(%d.%d) gNB is waiting for SRS, id = %i\n", frame_rx, slot_rx, i);
|
|
|
|
start_meas(&gNB->rx_srs_stats);
|
|
|
|
uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols;
|
|
uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports;
|
|
int16_t snr_per_rb[srs->srs_pdu.bwp_size];
|
|
nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu;
|
|
uint16_t timing_advance_offset;
|
|
int16_t timing_advance_offset_nsec[nb_antennas_rx];
|
|
int srs_est;
|
|
|
|
c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32)));
|
|
c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size]
|
|
__attribute__((aligned(32)));
|
|
|
|
nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB,
|
|
frame_rx,
|
|
slot_rx,
|
|
nb_antennas_rx,
|
|
N_ap,
|
|
N_symb_SRS,
|
|
ofdm_symbol_size,
|
|
srs,
|
|
&srs_est,
|
|
srs_estimated_channel_freq,
|
|
srs_estimated_channel_time,
|
|
snr_per_rb,
|
|
&timing_advance_offset,
|
|
timing_advance_offset_nsec);
|
|
|
|
if ((gNB->srs->snr * 10) < gNB->srs_thres) {
|
|
srs_est = -1;
|
|
}
|
|
|
|
UL_INFO->srs_ind.sfn = frame_rx;
|
|
UL_INFO->srs_ind.slot = slot_rx;
|
|
|
|
// data model difficult to understand, nfapi do malloc for this pointer
|
|
UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list;
|
|
nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++;
|
|
srs_indication->handle = srs_pdu->handle;
|
|
srs_indication->rnti = srs_pdu->rnti;
|
|
srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF;
|
|
// TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas
|
|
srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000;
|
|
switch (srs_pdu->srs_parameters_v4.usage) {
|
|
case 0:
|
|
LOG_W(NR_PHY, "SRS report was not requested by MAC\n");
|
|
return 0;
|
|
case 1 << NFAPI_NR_SRS_BEAMMANAGEMENT:
|
|
srs_indication->srs_usage = NFAPI_NR_SRS_BEAMMANAGEMENT;
|
|
break;
|
|
case 1 << NFAPI_NR_SRS_CODEBOOK:
|
|
srs_indication->srs_usage = NFAPI_NR_SRS_CODEBOOK;
|
|
break;
|
|
case 1 << NFAPI_NR_SRS_NONCODEBOOK:
|
|
srs_indication->srs_usage = NFAPI_NR_SRS_NONCODEBOOK;
|
|
break;
|
|
case 1 << NFAPI_NR_SRS_ANTENNASWITCH:
|
|
srs_indication->srs_usage = NFAPI_NR_SRS_ANTENNASWITCH;
|
|
break;
|
|
default:
|
|
LOG_E(NR_PHY, "Invalid srs_pdu->srs_parameters_v4.usage %i\n", srs_pdu->srs_parameters_v4.usage);
|
|
}
|
|
srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0];
|
|
|
|
#ifdef SRS_IND_DEBUG
|
|
LOG_I(NR_PHY, "UL_INFO->srs_ind.sfn = %i\n", UL_INFO->srs_ind.sfn);
|
|
LOG_I(NR_PHY, "UL_INFO->srs_ind.slot = %i\n", UL_INFO->srs_ind.slot);
|
|
LOG_I(NR_PHY, "srs_indication->rnti = %04x\n", srs_indication->rnti);
|
|
LOG_I(NR_PHY, "srs_indication->timing_advance = %i\n", srs_indication->timing_advance_offset);
|
|
LOG_I(NR_PHY, "srs_indication->timing_advance_offset_nsec = %i\n", srs_indication->timing_advance_offset_nsec);
|
|
LOG_I(NR_PHY, "srs_indication->srs_usage = %i\n", srs_indication->srs_usage);
|
|
LOG_I(NR_PHY, "srs_indication->report_type = %i\n", srs_indication->report_type);
|
|
#endif
|
|
|
|
nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv;
|
|
report_tlv->tag = 0;
|
|
report_tlv->length = 0;
|
|
|
|
start_meas(&gNB->srs_report_tlv_stats);
|
|
switch (srs_indication->srs_usage) {
|
|
case NFAPI_NR_SRS_BEAMMANAGEMENT: {
|
|
start_meas(&gNB->srs_beam_report_stats);
|
|
nfapi_nr_srs_beamforming_report_t nr_srs_bf_report;
|
|
nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size;
|
|
nr_srs_bf_report.num_symbols = N_symb_SRS;
|
|
nr_srs_bf_report.wide_band_snr =
|
|
srs_est >= 0 ? (gNB->srs->snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid
|
|
nr_srs_bf_report.num_reported_symbols = N_symb_SRS;
|
|
AssertFatal(nr_srs_bf_report.num_reported_symbols == 1,
|
|
"nr_srs_bf_report.num_reported_symbols %i not handled yet!\n",
|
|
nr_srs_bf_report.num_reported_symbols);
|
|
fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est);
|
|
|
|
#ifdef SRS_IND_DEBUG
|
|
LOG_I(NR_PHY, "nr_srs_bf_report.prg_size = %i\n", nr_srs_bf_report.prg_size);
|
|
LOG_I(NR_PHY, "nr_srs_bf_report.num_symbols = %i\n", nr_srs_bf_report.num_symbols);
|
|
LOG_I(NR_PHY,
|
|
"nr_srs_bf_report.wide_band_snr = %i (%i dB)\n",
|
|
nr_srs_bf_report.wide_band_snr,
|
|
(nr_srs_bf_report.wide_band_snr >> 1) - 64);
|
|
LOG_I(NR_PHY, "nr_srs_bf_report.num_reported_symbols = %i\n", nr_srs_bf_report.num_reported_symbols);
|
|
LOG_I(NR_PHY,
|
|
"nr_srs_bf_report.reported_symbol_list[0].num_prgs = %i\n",
|
|
nr_srs_bf_report.reported_symbol_list[0].num_prgs);
|
|
for (int prg_idx = 0; prg_idx < nr_srs_bf_report.reported_symbol_list[0].num_prgs; prg_idx++) {
|
|
LOG_I(NR_PHY,
|
|
"nr_srs_beamforming_report.reported_symbol_list[0].prg_list[%3i].rb_snr = %i (%i dB)\n",
|
|
prg_idx,
|
|
nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr,
|
|
(nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr >> 1) - 64);
|
|
}
|
|
#endif
|
|
|
|
report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value));
|
|
stop_meas(&gNB->srs_beam_report_stats);
|
|
break;
|
|
}
|
|
|
|
case NFAPI_NR_SRS_CODEBOOK: {
|
|
start_meas(&gNB->srs_iq_matrix_stats);
|
|
nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix;
|
|
fill_srs_channel_matrix(&nr_srs_channel_iq_matrix,
|
|
srs_pdu,
|
|
&srs_info,
|
|
srs_pdu->srs_parameters_v4.iq_representation,
|
|
nb_antennas_rx,
|
|
srs_pdu->srs_parameters_v4.num_total_ue_antennas,
|
|
srs_pdu->srs_parameters_v4.prg_size,
|
|
srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size,
|
|
frame_parms,
|
|
srs_estimated_channel_freq);
|
|
|
|
#ifdef SRS_IND_DEBUG
|
|
LOG_I(NR_PHY,
|
|
"nr_srs_channel_iq_matrix.normalized_iq_representation = %i\n",
|
|
nr_srs_channel_iq_matrix.normalized_iq_representation);
|
|
LOG_I(NR_PHY,
|
|
"nr_srs_channel_iq_matrix.num_gnb_antenna_elements = %i\n",
|
|
nr_srs_channel_iq_matrix.num_gnb_antenna_elements);
|
|
LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_ue_srs_ports = %i\n", nr_srs_channel_iq_matrix.num_ue_srs_ports);
|
|
LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.prg_size = %i\n", nr_srs_channel_iq_matrix.prg_size);
|
|
LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_prgs = %i\n", nr_srs_channel_iq_matrix.num_prgs);
|
|
c16_t *channel_matrix16 = (c16_t *)nr_srs_channel_iq_matrix.channel_matrix;
|
|
c8_t *channel_matrix8 = (c8_t *)nr_srs_channel_iq_matrix.channel_matrix;
|
|
for (int uI = 0; uI < nr_srs_channel_iq_matrix.num_ue_srs_ports; uI++) {
|
|
for (int gI = 0; gI < nr_srs_channel_iq_matrix.num_gnb_antenna_elements; gI++) {
|
|
for (int pI = 0; pI < nr_srs_channel_iq_matrix.num_prgs; pI++) {
|
|
uint16_t index = uI * nr_srs_channel_iq_matrix.num_gnb_antenna_elements * nr_srs_channel_iq_matrix.num_prgs
|
|
+ gI * nr_srs_channel_iq_matrix.num_prgs + pI;
|
|
LOG_I(
|
|
NR_PHY,
|
|
"(uI %i, gI %i, pI %i) channel_matrix --> real %i, imag %i\n",
|
|
uI,
|
|
gI,
|
|
pI,
|
|
nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].r : channel_matrix16[index].r,
|
|
nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].i
|
|
: channel_matrix16[index].i);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
report_tlv->length =
|
|
pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value));
|
|
stop_meas(&gNB->srs_iq_matrix_stats);
|
|
break;
|
|
}
|
|
|
|
case NFAPI_NR_SRS_NONCODEBOOK:
|
|
case NFAPI_NR_SRS_ANTENNASWITCH:
|
|
LOG_W(NR_PHY, "PHY procedures for this SRS usage are not implemented yet!\n");
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(1 == 0, "Invalid SRS usage\n");
|
|
}
|
|
stop_meas(&gNB->srs_report_tlv_stats);
|
|
|
|
#ifdef SRS_IND_DEBUG
|
|
LOG_I(NR_PHY, "report_tlv->tag = %i\n", report_tlv->tag);
|
|
LOG_I(NR_PHY, "report_tlv->length = %i\n", report_tlv->length);
|
|
char *value = (char *)report_tlv->value;
|
|
for (int b = 0; b < report_tlv->length; b++) {
|
|
LOG_I(NR_PHY, "value[%i] = 0x%02x\n", b, value[b] & 0xFF);
|
|
}
|
|
#endif
|
|
srs->active = false;
|
|
stop_meas(&gNB->rx_srs_stats);
|
|
}
|
|
|
|
stop_meas(&gNB->phy_proc_rx);
|
|
|
|
if (n_pucch > 0 || num_pusch > 0) {
|
|
T(T_GNB_PHY_PUCCH_PUSCH_IQ,
|
|
T_INT(frame_rx),
|
|
T_INT(slot_rx),
|
|
T_BUFFER(&gNB->common_vars.rxdataF[0][0][0], frame_parms->symbols_per_slot * ofdm_symbol_size * 4));
|
|
}
|
|
|
|
return pusch_DTX;
|
|
}
|
|
|
|
void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req)
|
|
{
|
|
DevAssert(gNB != NULL);
|
|
DevAssert(UL_tti_req != NULL);
|
|
|
|
int frame = UL_tti_req->SFN;
|
|
int slot = UL_tti_req->Slot;
|
|
|
|
for (int i = 0; i < UL_tti_req->n_pdus; i++) {
|
|
int type = UL_tti_req->pdus_list[i].pdu_type;
|
|
LOG_D(NR_PHY,
|
|
"frame %d, slot %d got %s for %d.%d\n",
|
|
frame,
|
|
slot,
|
|
txt_nfapi_nr_ul_config_pdu_type[type],
|
|
UL_tti_req->SFN,
|
|
UL_tti_req->Slot);
|
|
switch (type) {
|
|
case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE:
|
|
nr_fill_ulsch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pusch_pdu);
|
|
break;
|
|
case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE:
|
|
nr_fill_pucch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pucch_pdu);
|
|
break;
|
|
case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: {
|
|
nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu;
|
|
prach_item_t *prach = nr_schedule_rx_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu);
|
|
if (!prach)
|
|
LOG_W(NR_PHY_RACH, "Error in scheduling rach\n");
|
|
} break;
|
|
case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE:
|
|
nr_fill_srs(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].srs_pdu);
|
|
break;
|
|
}
|
|
}
|
|
}
|