1745 lines
69 KiB
C
1745 lines
69 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "common/utils/assertions.h"
|
|
#include "common/utils/nr/nr_common.h"
|
|
#include "common/utils/var_array.h"
|
|
#include "executables/nr-uesoftmodem.h"
|
|
#include "executables/softmodem-common.h"
|
|
#include "LAYER2/NR_MAC_UE/mac_defs.h"
|
|
#include "LAYER2/NR_MAC_UE/mac_proto.h"
|
|
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
|
|
#include "LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.h"
|
|
#include "LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
|
|
#include "LAYER2/NR_MAC_gNB/nr_radio_config.h"
|
|
#include "NR_BCCH-BCH-Message.h"
|
|
#include "NR_BWP-Downlink.h"
|
|
#include "NR_CellGroupConfig.h"
|
|
#include "NR_MAC_COMMON/nr_mac.h"
|
|
#include "NR_MAC_COMMON/nr_mac_common.h"
|
|
#include "NR_PHY_INTERFACE/NR_IF_Module.h"
|
|
#include "NR_ReconfigurationWithSync.h"
|
|
#include "NR_ServingCellConfig.h"
|
|
#include "NR_SetupRelease.h"
|
|
#include "NR_UE_PHY_INTERFACE/NR_IF_Module.h"
|
|
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
|
|
#include "PHY/INIT/nr_phy_init.h"
|
|
#include "PHY/MODULATION/modulation_common.h"
|
|
#include "PHY/NR_REFSIG/ptrs_nr.h"
|
|
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
|
|
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
|
|
#include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h"
|
|
#include "PHY/TOOLS/tools_defs.h"
|
|
#include "PHY/defs_RU.h"
|
|
#include "PHY/defs_gNB.h"
|
|
#include "PHY/defs_nr_UE.h"
|
|
#include "PHY/defs_nr_common.h"
|
|
#include "PHY/impl_defs_nr.h"
|
|
#include "PHY/phy_vars_nr_ue.h"
|
|
#include "SCHED_NR/sched_nr.h"
|
|
#include "SCHED_NR_UE/defs.h"
|
|
#include "SCHED_NR_UE/fapi_nr_ue_l1.h"
|
|
#include "T.h"
|
|
#include "asn_internal.h"
|
|
#include "assertions.h"
|
|
#include "common/config/config_load_configmodule.h"
|
|
#include "common/ngran_types.h"
|
|
#include "common/ran_context.h"
|
|
#include "common/utils/T/T.h"
|
|
#include "common/utils/nr/nr_common.h"
|
|
#include "common/utils/var_array.h"
|
|
#include "common_lib.h"
|
|
#include "e1ap_messages_types.h"
|
|
#include "fapi_nr_ue_interface.h"
|
|
#include "nfapi_interface.h"
|
|
#include "nfapi_nr_interface.h"
|
|
#include "nfapi_nr_interface_scf.h"
|
|
#include "nr_ue_phy_meas.h"
|
|
#include "oai_asn1.h"
|
|
#include "openair1/SIMULATION/NR_PHY/nr_unitary_defs.h"
|
|
#include "openair1/SIMULATION/TOOLS/sim.h"
|
|
#include "thread-pool.h"
|
|
#include "time_meas.h"
|
|
#include "utils.h"
|
|
#define inMicroS(a) (((double)(a))/(get_cpu_freq_GHz()*1000.0))
|
|
#include "SIMULATION/LTE_PHY/common_sim.h"
|
|
|
|
#ifdef ENABLE_CUDA
|
|
#include <cuda_runtime.h>
|
|
#include "SIMULATION/TOOLS/oai_cuda.h"
|
|
#endif
|
|
|
|
const char *__asan_default_options()
|
|
{
|
|
/* don't do leak checking in nr_ulsim, not finished yet */
|
|
return "detect_leaks=0";
|
|
}
|
|
|
|
PHY_VARS_gNB *gNB;
|
|
PHY_VARS_NR_UE *UE;
|
|
RAN_CONTEXT_t RC;
|
|
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
|
|
double cpuf;
|
|
char *uecap_file;
|
|
|
|
//uint8_t nfapi_mode = 0;
|
|
uint64_t downlink_frequency[MAX_NUM_CCs][4];
|
|
THREAD_STRUCT thread_struct;
|
|
nfapi_ue_release_request_body_t release_rntis;
|
|
//Fixme: Uniq dirty DU instance, by global var, datamodel need better management
|
|
instance_t DUuniqInstance=0;
|
|
instance_t CUuniqInstance=0;
|
|
|
|
// NTN cellSpecificKoffset-r17, but in slots for DL SCS
|
|
unsigned int NTN_UE_Koffset = 0;
|
|
|
|
void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t key[32], uint8_t *key_ng_ran_star)
|
|
{
|
|
}
|
|
|
|
/* this is a hack, but necessary for E2 agent. We compile in all of RRC
|
|
* (because of CMakeLists.txt), but we don't need it (only nr_radio_config.c).
|
|
* however, if E2 agent is defined, the following functions are used in
|
|
* rrc_gNB.c, but defined in RAN functions. In order to avoid pulling this in
|
|
* here as well, only provide a prototype (and abort if they are ever called). */
|
|
void signal_rrc_msg(void /*const nr_rrc_class_e nr_channel, const uint32_t rrc_msg_id, const byte_array_t rrc_ba*/ ) { abort(); }
|
|
void signal_rrc_state_changed_to(void /* const gNB_RRC_UE_t *rrc_ue_context, const rrc_state_e2sm_rc_e rrc_state */) { abort(); }
|
|
void signal_ue_id(void /* const gNB_RRC_UE_t *rrc_ue_context, const uint16_t class, const uint32_t msg_id */) { abort(); }
|
|
|
|
/* Function to set or overwrite PTRS DL RRC parameters */
|
|
static void rrc_config_dl_ptrs_params(NR_BWP_Downlink_t *bwp, long *ptrsNrb, long *ptrsMcs, long *epre_Ratio, long *reOffset)
|
|
{
|
|
int i=0;
|
|
NR_DMRS_DownlinkConfig_t *tmp = bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
|
|
// struct NR_SetupRelease_PTRS_DownlinkConfig *tmp=bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS;
|
|
/* check for memory allocation */
|
|
if (tmp->phaseTrackingRS == NULL) {
|
|
asn1cCalloc(tmp->phaseTrackingRS, TrackingRS);
|
|
TrackingRS->present = NR_SetupRelease_PTRS_DownlinkConfig_PR_setup;
|
|
asn1cCalloc(TrackingRS->choice.setup, setup);
|
|
asn1cCalloc(setup->frequencyDensity, freqD);
|
|
/* Fill the given values */
|
|
for(i = 0; i < 2; i++) {
|
|
asn1cSequenceAdd(freqD->list, long, nbr);
|
|
*nbr = ptrsNrb[i];
|
|
}
|
|
asn1cCalloc(setup->timeDensity, timeD);
|
|
for(i = 0; i < 3; i++) {
|
|
asn1cSequenceAdd(timeD->list, long, mcs);
|
|
*mcs = ptrsMcs[i];
|
|
}
|
|
asn1cCallocOne(setup->epre_Ratio, epre_Ratio[0]);
|
|
asn1cCallocOne(setup->resourceElementOffset, reOffset[0]);
|
|
} else {
|
|
NR_PTRS_DownlinkConfig_t *TrackingRS = tmp->phaseTrackingRS->choice.setup;
|
|
for(i = 0; i < 2; i++) {
|
|
*TrackingRS->frequencyDensity->list.array[i] = ptrsNrb[i];
|
|
}
|
|
for(i = 0; i < 3; i++) {
|
|
*TrackingRS->timeDensity->list.array[i] = ptrsMcs[i];
|
|
}
|
|
*TrackingRS->epre_Ratio = epre_Ratio[0];
|
|
*TrackingRS->resourceElementOffset = reOffset[0];
|
|
}
|
|
}
|
|
|
|
int dummy_nr_ue_ul_indication(nr_uplink_indication_t *ul_info) { return(0); }
|
|
|
|
void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); }
|
|
void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); }
|
|
void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) { abort(); }
|
|
|
|
int8_t nr_rrc_RA_succeeded(const module_id_t mod_id, const uint8_t gNB_index) {
|
|
return 0;
|
|
}
|
|
|
|
void nr_derive_key(int alg_type, uint8_t alg_id, const uint8_t key[32], uint8_t out[16])
|
|
{
|
|
(void)alg_type;
|
|
}
|
|
|
|
void processSlotTX(void *arg) {}
|
|
|
|
// needed for some functions
|
|
openair0_config_t openair0_cfg[MAX_CARDS];
|
|
void update_ptrs_config(NR_CellGroupConfig_t *secondaryCellGroup, uint16_t *rbSize, uint8_t *mcsIndex,int8_t *ptrs_arg);
|
|
void update_dmrs_config(NR_CellGroupConfig_t *scg, int8_t* dmrs_arg);
|
|
extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration
|
|
|
|
/* specific dlsim DL preprocessor: uses rbStart/rbSize/mcs/nrOfLayers from command line of dlsim */
|
|
int g_mcsIndex = -1, g_mcsTableIdx = 0, g_rbStart = -1, g_rbSize = -1, g_nrOfLayers = 1, g_pmi = 0;
|
|
|
|
void nr_dlsim_preprocessor(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch)
|
|
{
|
|
NR_UE_info_t *UE_info = nr_mac->UE_info.connected_ue_list[0];
|
|
AssertFatal(nr_mac->UE_info.connected_ue_list[1] == NULL, "Only single UE allowed in dlsim\n");
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl;
|
|
NR_UE_DL_BWP_t *current_BWP = &UE_info->current_DL_BWP;
|
|
NR_ServingCellConfigCommon_t *scc = nr_mac->common_channels[0].ServingCellConfigCommon;
|
|
|
|
int nr_of_candidates = 0;
|
|
if (g_mcsIndex < 4) {
|
|
find_aggregation_candidates(&sched_ctrl->aggregation_level, &nr_of_candidates, sched_ctrl->search_space, 8);
|
|
}
|
|
if (nr_of_candidates == 0) {
|
|
find_aggregation_candidates(&sched_ctrl->aggregation_level, &nr_of_candidates, sched_ctrl->search_space, 4);
|
|
}
|
|
uint32_t Y = get_Y(sched_ctrl->search_space, pp_pdsch->slot, UE_info->rnti);
|
|
int CCEIndex = find_pdcch_candidate(nr_mac,
|
|
/* CC_id = */ 0,
|
|
sched_ctrl->aggregation_level,
|
|
nr_of_candidates,
|
|
0,
|
|
&sched_ctrl->sched_pdcch,
|
|
sched_ctrl->coreset,
|
|
Y);
|
|
AssertFatal(CCEIndex >= 0,
|
|
"%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n",
|
|
pp_pdsch->frame,
|
|
pp_pdsch->slot,
|
|
0,
|
|
UE_info->rnti);
|
|
sched_ctrl->cce_index = CCEIndex;
|
|
|
|
NR_sched_pdsch_t sched_pdsch = {
|
|
.rbStart = g_rbStart,
|
|
.rbSize = g_rbSize,
|
|
.bwp_info = get_pdsch_bwp_start_size(nr_mac, UE_info),
|
|
.mcs = g_mcsIndex,
|
|
.nrOfLayers = g_nrOfLayers,
|
|
.pm_index = g_pmi,
|
|
};
|
|
/* the following might override the table that is mandated by RRC
|
|
* configuration */
|
|
current_BWP->mcsTableIdx = g_mcsTableIdx;
|
|
sched_pdsch.time_domain_allocation = get_dl_tda(nr_mac, pp_pdsch->slot);
|
|
AssertFatal(sched_pdsch.time_domain_allocation >= 0,"Unable to find PDSCH time domain allocation in list\n");
|
|
|
|
sched_pdsch.tda_info = get_dl_tda_info(current_BWP,
|
|
sched_ctrl->search_space->searchSpaceType->present,
|
|
sched_pdsch.time_domain_allocation,
|
|
NR_MIB__dmrs_TypeA_Position_pos2,
|
|
1,
|
|
TYPE_C_RNTI_,
|
|
sched_ctrl->coreset->controlResourceSetId,
|
|
false);
|
|
|
|
sched_pdsch.dmrs_parms = get_dl_dmrs_params(scc,
|
|
current_BWP,
|
|
&sched_pdsch.tda_info,
|
|
sched_pdsch.nrOfLayers);
|
|
|
|
sched_pdsch.Qm = nr_get_Qm_dl(sched_pdsch.mcs, current_BWP->mcsTableIdx);
|
|
sched_pdsch.R = nr_get_code_rate_dl(sched_pdsch.mcs, current_BWP->mcsTableIdx);
|
|
update_sched_pdsch_codeword_tbs(&sched_pdsch);
|
|
|
|
/* the simulator assumes the HARQ PID is equal to the slot number */
|
|
sched_pdsch.dl_harq_pid = pp_pdsch->slot;
|
|
|
|
/* The scheduler uses lists to track whether a HARQ process is
|
|
* free/busy/awaiting retransmission, and updates the HARQ process states.
|
|
* However, in the simulation, we never get ack or nack for any HARQ process,
|
|
* thus the list and HARQ states don't match what the scheduler expects.
|
|
* Therefore, below lines just "repair" everything so that the scheduler
|
|
* won't remark that there is no HARQ feedback */
|
|
sched_ctrl->feedback_dl_harq.head = -1; // always overwrite feedback HARQ process
|
|
if (sched_ctrl->harq_processes[pp_pdsch->slot].round == 0) // depending on round set in simulation ...
|
|
add_front_nr_list(&sched_ctrl->available_dl_harq, pp_pdsch->slot); // ... make PID available
|
|
else
|
|
add_front_nr_list(&sched_ctrl->retrans_dl_harq, pp_pdsch->slot); // ... make PID retransmission
|
|
sched_ctrl->harq_processes[pp_pdsch->slot].is_waiting = false;
|
|
AssertFatal(sched_pdsch.rbStart >= 0, "invalid rbStart %d\n", sched_pdsch.rbStart);
|
|
AssertFatal(sched_pdsch.rbSize > 0, "invalid rbSize %d\n", sched_pdsch.rbSize);
|
|
AssertFatal(sched_pdsch.mcs >= 0, "invalid mcs %d\n", sched_pdsch.mcs);
|
|
AssertFatal(current_BWP->mcsTableIdx >= 0 && current_BWP->mcsTableIdx <= 2, "invalid mcsTableIdx %d\n", current_BWP->mcsTableIdx);
|
|
|
|
post_process_dlsch(nr_mac, pp_pdsch, UE_info, &sched_pdsch);
|
|
}
|
|
|
|
nrUE_params_t nrUE_params;
|
|
|
|
nrUE_params_t *get_nrUE_params(void) {
|
|
return &nrUE_params;
|
|
}
|
|
|
|
void validate_input_pmi(nfapi_nr_config_request_scf_t *gNB_config,
|
|
nr_pdsch_AntennaPorts_t pdsch_AntennaPorts,
|
|
int nrOfLayers,
|
|
int pmi)
|
|
{
|
|
if (pmi == 0)
|
|
return;
|
|
|
|
nfapi_nr_pm_pdu_t *pmi_pdu = &gNB_config->pmi_list.pmi_pdu[pmi - 1]; // pmi 0 is identity matrix
|
|
AssertFatal(pmi == pmi_pdu->pm_idx, "PMI %d doesn't match to the one in precoding matrix %d\n", pmi, pmi_pdu->pm_idx);
|
|
AssertFatal(nrOfLayers == pmi_pdu->numLayers, "Number of layers %d doesn't match to the one in precoding matrix %d for PMI %d\n",
|
|
nrOfLayers, pmi_pdu->numLayers, pmi);
|
|
int num_antenna_ports = pdsch_AntennaPorts.N1 * pdsch_AntennaPorts.N2 * pdsch_AntennaPorts.XP;
|
|
AssertFatal(num_antenna_ports == pmi_pdu->num_ant_ports, "Configured antenna ports %d does not match precoding matrix AP size %d for PMI %d\n",
|
|
num_antenna_ports, pmi_pdu->num_ant_ports, pmi);
|
|
}
|
|
|
|
|
|
configmodule_interface_t *uniqCfg = NULL;
|
|
int main(int argc, char **argv)
|
|
{
|
|
stop = false;
|
|
__attribute__((unused)) struct sigaction oldaction;
|
|
sigaction(SIGINT, &sigint_action, &oldaction);
|
|
|
|
FILE *csv_file = NULL;
|
|
char *filename_csv = NULL;
|
|
setbuf(stdout, NULL);
|
|
int c;
|
|
int i,aa;//,l;
|
|
double SNR, snr0 = -2.0, snr1 = 2.0;
|
|
uint8_t snr1set=0;
|
|
double effRate;
|
|
//float psnr;
|
|
double eff_tp_check = 0.7;
|
|
uint32_t TBS = 0;
|
|
c16_t **txdata;
|
|
float **s_interleaved, **r_re, **r_im;
|
|
//double iqim = 0.0;
|
|
//unsigned char pbch_pdu[6];
|
|
// int sync_pos, sync_pos_slot;
|
|
// FILE *rx_frame_file;
|
|
FILE *output_fd = NULL;
|
|
//uint8_t write_output_file=0;
|
|
//int result;
|
|
//int freq_offset;
|
|
// int subframe_offset;
|
|
// char fname[40], vname[40];
|
|
int trial, n_trials = 1, n_false_positive = 0;
|
|
//int n_errors2, n_alamouti;
|
|
uint8_t n_tx=1,n_rx=1;
|
|
uint8_t round;
|
|
uint8_t num_rounds = 4;
|
|
char gNBthreads[128]="n";
|
|
|
|
channel_desc_t *gNB2UE;
|
|
//uint32_t nsymb,tx_lev,tx_lev1 = 0,tx_lev2 = 0;
|
|
//uint8_t extended_prefix_flag=0;
|
|
//int8_t interf1=-21,interf2=-21;
|
|
|
|
FILE *input_fd=NULL,*pbch_file_fd=NULL;
|
|
//char input_val_str[50],input_val_str2[50];
|
|
|
|
//uint8_t frame_mod4,num_pdcch_symbols = 0;
|
|
|
|
SCM_t channel_model = AWGN; // AWGN Rayleigh1 Rayleigh1_anticorr;
|
|
double DS_TDL = .03;
|
|
int delay = 0;
|
|
|
|
//double pbch_sinr;
|
|
//int pbch_tx_ant;
|
|
int N_RB_DL=106,mu=1;
|
|
|
|
//unsigned char frame_type = 0;
|
|
|
|
int frame=1,slot=1;
|
|
int frame_length_complex_samples;
|
|
//int frame_length_complex_samples_no_prefix;
|
|
NR_DL_FRAME_PARMS *frame_parms;
|
|
UE_nr_rxtx_proc_t UE_proc;
|
|
NR_Sched_Rsp_t *Sched_INFO;
|
|
gNB_MAC_INST *gNB_mac;
|
|
NR_UE_MAC_INST_t *UE_mac;
|
|
int cyclic_prefix_type = NFAPI_CP_NORMAL;
|
|
int loglvl=OAILOG_WARNING;
|
|
|
|
//float target_error_rate = 0.01;
|
|
cpuf = get_cpu_freq_GHz();
|
|
int8_t enable_ptrs = 0;
|
|
int8_t modify_dmrs = 0;
|
|
|
|
int8_t dmrs_arg[3] = {-1,-1,-1};// Invalid values
|
|
/* L_PTRS = ptrs_arg[0], K_PTRS = ptrs_arg[1] */
|
|
int8_t ptrs_arg[2] = {-1,-1};// Invalid values
|
|
|
|
uint16_t ptrsRePerSymb = 0;
|
|
uint16_t pdu_bit_map = 0x0;
|
|
uint16_t dlPtrsSymPos = 0;
|
|
uint16_t ptrsSymbPerSlot = 0;
|
|
uint16_t rbSize = 106;
|
|
uint8_t mcsIndex = 9;
|
|
uint8_t dlsch_threads = 0;
|
|
int chest_type[2] = {0};
|
|
uint8_t max_ldpc_iterations = 5;
|
|
// number of PDSCH symbols per thread = 0 means do not use thread pool
|
|
int num_pdsch_symbols_per_thread = 0;
|
|
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
|
|
exit_fun("[NR_DLSIM] Error, configuration module init failed\n");
|
|
}
|
|
|
|
randominit();
|
|
|
|
int print_perf = 0;
|
|
|
|
int use_cuda = 0;
|
|
|
|
void *h_tx_sig_pinned = NULL;
|
|
|
|
#ifdef ENABLE_CUDA
|
|
void *d_tx_sig = NULL, *d_intermediate_sig = NULL, *d_final_output = NULL;
|
|
void *d_curand_states = NULL;
|
|
void *h_final_output_pinned = NULL;
|
|
void *d_channel_coeffs_gpu = NULL;
|
|
#endif
|
|
|
|
while ((c = getopt(argc, argv, "--:O:f:hA:p:f:g:i:n:s:S:t:v:x:y:z:o:H:M:N:F:GR:d:PI:L:a:b:e:m:w:T:U:q:X:Y:Z:")) != -1) {
|
|
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
|
|
/* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */
|
|
if (c == 1 || c == '-' || c == 'O')
|
|
continue;
|
|
|
|
printf("handling optarg %c\n",c);
|
|
switch (c) {
|
|
case 'f':
|
|
#ifdef ENABLE_CUDA
|
|
if (strcmp(optarg, "cuda") == 0) {
|
|
use_cuda = 1;
|
|
} else
|
|
#endif
|
|
{
|
|
printf("Unsupported flag '%s' for -f. Run '-h' to see the list of available options.\n", optarg);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
|
|
case 'g':
|
|
switch ((char)*optarg) {
|
|
case 'A':
|
|
channel_model = TDL_A;
|
|
DS_TDL = 0.030; // 30 ns
|
|
printf("Channel model: TDLA30\n");
|
|
break;
|
|
case 'B':
|
|
channel_model = TDL_B;
|
|
DS_TDL = 0.100; // 100ns
|
|
printf("Channel model: TDLB100\n");
|
|
break;
|
|
case 'C':
|
|
channel_model = TDL_C;
|
|
DS_TDL = 0.300; // 300 ns
|
|
printf("Channel model: TDLC300\n");
|
|
break;
|
|
default:
|
|
printf("Unsupported channel model!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
for(i=0; i < atoi(optarg); i++){
|
|
chest_type[i] = atoi(argv[optind++]);
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
n_trials = atoi(optarg);
|
|
break;
|
|
|
|
case 's':
|
|
snr0 = atof(optarg);
|
|
printf("Setting SNR0 to %f\n",snr0);
|
|
break;
|
|
|
|
case 'S':
|
|
snr1 = atof(optarg);
|
|
snr1set=1;
|
|
printf("Setting SNR1 to %f\n",snr1);
|
|
break;
|
|
|
|
case 'x':
|
|
g_nrOfLayers = atoi(optarg);
|
|
|
|
if ((g_nrOfLayers == 0) || (g_nrOfLayers > NR_MAX_NB_LAYERS)) {
|
|
printf("Unsupported nr Of Layers %d\n", g_nrOfLayers);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
g_pmi = atoi(optarg);
|
|
break;
|
|
|
|
case 'v':
|
|
num_rounds = atoi(optarg);
|
|
|
|
AssertFatal(num_rounds > 0 && num_rounds < 16, "Unsupported number of rounds %d, should be in [1,16]\n", num_rounds);
|
|
break;
|
|
|
|
case 'y':
|
|
n_tx=atoi(optarg);
|
|
|
|
if ((n_tx == 0) || (n_tx > NR_MAX_NB_LAYERS)) {
|
|
printf("Unsupported number of tx antennas %d\n",n_tx);
|
|
exit(-1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
n_rx=atoi(optarg);
|
|
|
|
if ((n_rx == 0) || (n_rx > NR_MAX_NB_LAYERS)) {
|
|
printf("Unsupported number of rx antennas %d\n",n_rx);
|
|
exit(-1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
N_RB_DL = atoi(optarg);
|
|
break;
|
|
|
|
case 'F':
|
|
input_fd = fopen(optarg,"r");
|
|
|
|
if (input_fd==NULL) {
|
|
printf("Problem with filename %s\n",optarg);
|
|
exit(-1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
print_perf=1;
|
|
cpu_meas_enabled = 1;
|
|
break;
|
|
|
|
case 'I':
|
|
max_ldpc_iterations = atoi(optarg);
|
|
break;
|
|
|
|
case 'L':
|
|
loglvl = atoi(optarg);
|
|
break;
|
|
|
|
case 'a':
|
|
g_rbStart = atoi(optarg);
|
|
break;
|
|
|
|
case 'b':
|
|
g_rbSize = atoi(optarg);
|
|
break;
|
|
|
|
case 'd':
|
|
dlsch_threads = atoi(optarg);
|
|
break;
|
|
|
|
case 'e':
|
|
g_mcsIndex = atoi(optarg);
|
|
break;
|
|
|
|
case 'q':
|
|
g_mcsTableIdx = atoi(optarg);
|
|
break;
|
|
|
|
case 'm':
|
|
mu = atoi(optarg);
|
|
break;
|
|
|
|
case 't':
|
|
eff_tp_check = atof(optarg) / 100.0;
|
|
break;
|
|
|
|
case 'w':
|
|
output_fd = fopen("txdata.dat", "w+");
|
|
break;
|
|
|
|
case 'T':
|
|
enable_ptrs=1;
|
|
for(i=0; i < atoi(optarg); i++) {
|
|
ptrs_arg[i] = atoi(argv[optind++]);
|
|
}
|
|
break;
|
|
|
|
case 'U':
|
|
modify_dmrs = 1;
|
|
for(i=0; i < atoi(optarg); i++) {
|
|
dmrs_arg[i] = atoi(argv[optind++]);
|
|
}
|
|
break;
|
|
|
|
case 'X':
|
|
strncpy(gNBthreads, optarg, sizeof(gNBthreads)-1);
|
|
gNBthreads[sizeof(gNBthreads)-1]=0;
|
|
break;
|
|
|
|
case 'Y':
|
|
num_pdsch_symbols_per_thread = atoi(optarg);
|
|
break;
|
|
|
|
case 'Z' :
|
|
filename_csv = strdup(optarg);
|
|
AssertFatal(filename_csv != NULL, "strdup() error: errno %d\n", errno);
|
|
break;
|
|
|
|
case 'o':
|
|
delay = atoi(optarg);
|
|
break;
|
|
|
|
case 'H':
|
|
slot = atoi(optarg);
|
|
break;
|
|
|
|
default:
|
|
case 'h':
|
|
printf("%s -h(elp) -p(extended_prefix) -N cell_id -f output_filename -F input_filename -g channel_model -n n_frames -s snr0 -S snr1 -x transmission_mode -y TXant -z RXant -i Intefrence0 -j Interference1 -A interpolation_file -C(alibration offset dB) -N CellId\n",
|
|
argv[0]);
|
|
printf("-h This message\n");
|
|
printf("-L <log level, 0(errors), 1(warning), 2(analysis), 3(info), 4(debug), 5(trace)>\n");
|
|
//printf("-p Use extended prefix mode\n");
|
|
//printf("-d Use TDD\n");
|
|
printf("-n Number of frames to simulate\n");
|
|
printf("-s Starting SNR, runs from SNR0 to SNR0 + 5 dB. If n_frames is 1 then just SNR is simulated\n");
|
|
printf("-S Ending SNR, runs from SNR0 to SNR1\n");
|
|
//printf("-t Delay spread for multipath channel\n");
|
|
printf("-g Channel model: [A] TDLA30, [B] TDLB100, [C] TDLC300, e.g. -g A\n");
|
|
printf("-o Introduce delay in terms of number of samples\n");
|
|
printf("-y Number of TX antennas used in gNB\n");
|
|
printf("-z Number of RX antennas used in UE\n");
|
|
printf("-x Num of layer for PDSCH\n");
|
|
printf("-p Precoding matrix index\n");
|
|
printf("-i Change channel estimation technique. Arguments list: Frequency domain {0:Linear interpolation, 1:PRB based averaging}, Time domain {0:Estimates of last DMRS symbol, 1:Average of DMRS symbols}\n");
|
|
//printf("-j Relative strength of second intefering gNB (in dB) - cell_id mod 3 = 2\n");
|
|
printf("-R N_RB_DL\n");
|
|
printf("-O oversampling factor (1,2,4,8,16)\n");
|
|
printf("-A Interpolation_filname Run with Abstraction to generate Scatter plot using interpolation polynomial in file\n");
|
|
//printf("-C Generate Calibration information for Abstraction (effective SNR adjustment to remove Pe bias w.r.t. AWGN)\n");
|
|
printf("-f raw file containing RRC configuration (generated by gNB)\n");
|
|
printf("-F Input filename (.txt format) for RX conformance testing\n");
|
|
printf("-a Start PRB for PDSCH\n");
|
|
printf("-b Number of PRB for PDSCH\n");
|
|
printf("-d number of dlsch threads, 0: no dlsch parallelization\n");
|
|
printf("-e MSC index\n");
|
|
printf("-f <flag> Enable optional feature flag. Available flags:\n");
|
|
#ifdef ENABLE_CUDA
|
|
printf(" cuda Enable CUDA channel simulation\n");
|
|
#else
|
|
printf(" (none) No optional features were compiled into this executable\n");
|
|
#endif
|
|
printf("-g Channel model: [A] TDLA30, [B] TDLB100, [C] TDLC300, e.g. -g A\n");
|
|
printf("-h This message\n");
|
|
printf("-H Slot number\n");
|
|
printf("-i Change channel estimation technique. Arguments list: Frequency domain {0:Linear interpolation, 1:PRB based averaging}, Time domain {0:Estimates of last DMRS symbol, 1:Average of DMRS symbols}\n");
|
|
printf("-m Numerology\n");
|
|
printf("-n Number of frames to simulate\n");
|
|
printf("-o Introduce delay in terms of number of samples\n");
|
|
printf("-p Precoding matrix index\n");
|
|
printf("-q MCS Table index\n");
|
|
printf("-s Starting SNR, runs from SNR0 to SNR0 + 5 dB. If n_frames is 1 then just SNR is simulated\n");
|
|
printf("-S Ending SNR, runs from SNR0 to SNR1\n");
|
|
printf("-t Acceptable effective throughput (in percentage)\n");
|
|
printf("-v Maximum number of rounds\n");
|
|
printf("-w Write txdata to binary file (one frame)\n");
|
|
printf("-x Num of layer for PDSCH\n");
|
|
printf("-y Number of TX antennas used in gNB\n");
|
|
printf("-z Number of RX antennas used in UE\n");
|
|
printf("-F Input filename (.txt format) for RX conformance testing\n");
|
|
printf("-I Maximum LDPC decoder iterations\n");
|
|
printf("-L <log level, 0(errors), 1(warning), 2(analysis), 3(info), 4(debug), 5(trace)>\n");
|
|
printf("-P Print DLSCH performances\n");
|
|
printf("-R N_RB_DL\n");
|
|
printf("-T Enable PTRS, arguments list L_PTRS{0,1,2} K_PTRS{2,4}, e.g. -T 2 0 2 \n");
|
|
printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:2} DMRS ConfType{1:2}, e.g. -U 3 0 2 1 \n");
|
|
printf("-X gNB thread pool configuration, n => no threads\n");
|
|
printf("-Y Number of symbols processed per PDSCH generation thread\n");
|
|
printf("-Z Output filename (.csv format) for stats\n");
|
|
exit (-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
logInit();
|
|
set_glog(loglvl);
|
|
/* initialize the sin table */
|
|
InitSinLUT();
|
|
|
|
#ifdef ENABLE_CUDA
|
|
init_cuda_chsim_buffers(use_cuda,
|
|
n_tx,
|
|
n_rx,
|
|
&d_tx_sig,
|
|
&d_intermediate_sig,
|
|
&d_final_output,
|
|
&d_curand_states,
|
|
&h_tx_sig_pinned,
|
|
&h_final_output_pinned,
|
|
&d_channel_coeffs_gpu);
|
|
#endif
|
|
|
|
#if !defined(ENABLE_CUDA) || !use_cuda
|
|
printf("Pre-allocating padded host memory for the CPU channel pipeline...\n");
|
|
int num_samples_alloc = 153600;
|
|
const int max_padding_alloc = 256 - 1;
|
|
size_t padded_tx_alloc_bytes = n_tx * (num_samples_alloc + max_padding_alloc) * 2 * sizeof(float);
|
|
h_tx_sig_pinned = malloc(padded_tx_alloc_bytes);
|
|
if (h_tx_sig_pinned == NULL) {
|
|
printf("Error: Failed to allocate host buffer for CPU path\n");
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
|
|
get_softmodem_params()->phy_test = 1;
|
|
get_softmodem_params()->do_ra = 0;
|
|
IS_SOFTMODEM_DLSIM = true;
|
|
|
|
if (snr1set==0)
|
|
snr1 = snr0+10;
|
|
|
|
RC.gNB = (PHY_VARS_gNB**) malloc(sizeof(PHY_VARS_gNB *));
|
|
RC.gNB[0] = (PHY_VARS_gNB*) malloc(sizeof(PHY_VARS_gNB ));
|
|
memset(RC.gNB[0],0,sizeof(PHY_VARS_gNB));
|
|
|
|
gNB = RC.gNB[0];
|
|
gNB->ofdm_offset_divisor = UINT_MAX;
|
|
gNB->phase_comp = true; // we need to perform phase compensation, otherwise everything will fail
|
|
frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH)
|
|
frame_parms->nb_antennas_tx = n_tx;
|
|
frame_parms->nb_antennas_rx = n_rx;
|
|
frame_parms->N_RB_DL = N_RB_DL;
|
|
frame_parms->N_RB_UL = N_RB_DL;
|
|
|
|
AssertFatal((gNB->if_inst = NR_IF_Module_init(0)) != NULL, "Cannot register interface");
|
|
gNB->if_inst->NR_PHY_config_req = nr_phy_config_request;
|
|
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
|
|
|
|
NR_ServingCellConfigCommon_t *scc = calloc(1,sizeof(*scc));;
|
|
prepare_scc(scc);
|
|
uint64_t ssb_bitmap = 1; // Enable only first SSB with index ssb_indx=0
|
|
fill_scc_sim(scc, &ssb_bitmap, N_RB_DL, N_RB_DL, mu, mu);
|
|
fix_scc(scc, ssb_bitmap);
|
|
|
|
frame_structure_t frame_structure = {0};
|
|
frame_type_t frame_type = TDD;
|
|
config_frame_structure(mu,
|
|
scc->tdd_UL_DL_ConfigurationCommon,
|
|
get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon),
|
|
frame_type,
|
|
&frame_structure);
|
|
AssertFatal(is_dl_slot(slot, &frame_structure), "The slot selected is not DL. Can't run DLSIM\n");
|
|
|
|
// TODO do a UECAP for phy-sim
|
|
nr_pdsch_AntennaPorts_t pdsch_AntennaPorts = {0};
|
|
pdsch_AntennaPorts.N1 = n_tx > 1 ? n_tx >> 1 : 1;
|
|
pdsch_AntennaPorts.N2 = 1;
|
|
pdsch_AntennaPorts.XP = n_tx > 1 ? 2 : 1;
|
|
const nr_mac_config_t conf = {.pdsch_AntennaPorts = pdsch_AntennaPorts,
|
|
.pusch_AntennaPorts = n_tx,
|
|
.minRXTXTIME = 6,
|
|
.do_CSIRS = 0,
|
|
.do_SRS = 0,
|
|
.num_dlharq = 16,
|
|
.num_ulharq = 16,
|
|
.maxMIMO_layers = g_nrOfLayers,
|
|
.pdsch_dmrs_additional_position = 1,
|
|
.force_256qam_off = false,
|
|
.timer_config.sr_ProhibitTimer = 0,
|
|
.timer_config.sr_TransMax = 64,
|
|
.timer_config.sr_ProhibitTimer_v1700 = 0,
|
|
.timer_config.t300 = 400,
|
|
.timer_config.t301 = 400,
|
|
.timer_config.t310 = 2000,
|
|
.timer_config.n310 = 10,
|
|
.timer_config.t311 = 3000,
|
|
.timer_config.n311 = 1,
|
|
.timer_config.t319 = 400,
|
|
.num_agg_level_candidates = {0, 0, 1, 1, 0}};
|
|
const nr_rlc_configuration_t rlc_config = {
|
|
.srb = {
|
|
.t_poll_retransmit = 45,
|
|
.t_reassembly = 35,
|
|
.t_status_prohibit = 0,
|
|
.poll_pdu = -1,
|
|
.poll_byte = -1,
|
|
.max_retx_threshold = 8,
|
|
.sn_field_length = 12,
|
|
},
|
|
.drb_am = {
|
|
.t_poll_retransmit = 45,
|
|
.t_reassembly = 15,
|
|
.t_status_prohibit = 15,
|
|
.poll_pdu = 64,
|
|
.poll_byte = 1024 * 500,
|
|
.max_retx_threshold = 32,
|
|
.sn_field_length = 18,
|
|
},
|
|
.drb_um = {
|
|
.t_reassembly = 15,
|
|
.sn_field_length = 12,
|
|
}
|
|
};
|
|
|
|
RC.nb_nr_macrlc_inst = 1;
|
|
mac_top_init_gNB(ngran_gNB, scc, &conf, &rlc_config);
|
|
gNB_mac = RC.nrmac[0];
|
|
nr_mac_config_scc(RC.nrmac[0], scc, &conf);
|
|
|
|
gNB_mac->dl_bler.harq_round_max = num_rounds;
|
|
|
|
gNB->ap_N1 = pdsch_AntennaPorts.N1;
|
|
gNB->ap_N2 = pdsch_AntennaPorts.N2;
|
|
gNB->ap_XP = pdsch_AntennaPorts.XP;
|
|
|
|
validate_input_pmi(&gNB_mac->config[0], pdsch_AntennaPorts, g_nrOfLayers, g_pmi);
|
|
|
|
NR_UE_NR_Capability_t *UE_Capability_nr = CALLOC(1,sizeof(NR_UE_NR_Capability_t));
|
|
prepare_sim_uecap(UE_Capability_nr, scc, mu, N_RB_DL, g_mcsTableIdx, 0);
|
|
rnti_t rnti = 0x1234;
|
|
int uid = 0;
|
|
int ssb_index = 0;
|
|
NR_CellGroupConfig_t *secondaryCellGroup = get_default_secondaryCellGroup(scc, UE_Capability_nr, 0, 1, &conf, uid, ssb_index);
|
|
secondaryCellGroup->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(rnti, uid, scc, frame);
|
|
|
|
/* -U option modify DMRS */
|
|
if(modify_dmrs) {
|
|
update_dmrs_config(secondaryCellGroup, dmrs_arg);
|
|
}
|
|
/* -T option enable PTRS */
|
|
if(enable_ptrs) {
|
|
update_ptrs_config(secondaryCellGroup, &rbSize, &mcsIndex, ptrs_arg);
|
|
}
|
|
|
|
|
|
//xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)secondaryCellGroup);
|
|
|
|
// UE dedicated configuration
|
|
nr_mac_add_test_ue(RC.nrmac[0], rnti, secondaryCellGroup);
|
|
// reset preprocessor to the one of DLSIM after it has been set during
|
|
// nr_mac_config_scc()
|
|
gNB_mac->pre_processor_dl = nr_dlsim_preprocessor;
|
|
phy_init_nr_gNB(gNB);
|
|
N_RB_DL = gNB->frame_parms.N_RB_DL;
|
|
NR_UE_info_t *UE_info = RC.nrmac[0]->UE_info.connected_ue_list[0];
|
|
|
|
configure_UE_BWP(RC.nrmac[0], scc, UE_info, false, NR_SearchSpace__searchSpaceType_PR_ue_Specific, -1, -1);
|
|
|
|
// stub to configure frame_parms
|
|
// nr_phy_config_request_sim(gNB,N_RB_DL,N_RB_DL,mu,Nid_cell,SSB_positions);
|
|
// call MAC to configure common parameters
|
|
|
|
/* nr_mac_add_test_ue() has created one user, so set the scheduling
|
|
* parameters from command line in global variables that will be picked up by
|
|
* scheduling preprocessor */
|
|
if (g_mcsIndex < 0) g_mcsIndex = 9;
|
|
if (g_rbStart < 0) g_rbStart=0;
|
|
if (g_rbSize < 0) g_rbSize = N_RB_DL - g_rbStart;
|
|
|
|
double fs,txbw,rxbw;
|
|
get_samplerate_and_bw(mu,
|
|
N_RB_DL,
|
|
frame_parms->threequarter_fs,
|
|
&fs,
|
|
&txbw,
|
|
&rxbw);
|
|
|
|
gNB2UE = new_channel_desc_scm(n_tx,
|
|
n_rx,
|
|
channel_model,
|
|
fs/1e6,//sampling frequency in MHz
|
|
0,
|
|
txbw,
|
|
DS_TDL,
|
|
0.0,
|
|
CORR_LEVEL_LOW,
|
|
0,
|
|
delay,
|
|
0,
|
|
0);
|
|
|
|
#ifdef ENABLE_CUDA
|
|
float *h_channel_coeffs = NULL;
|
|
if (use_cuda) {
|
|
int num_links = n_tx * n_rx;
|
|
h_channel_coeffs = (float *)malloc(num_links * gNB2UE->channel_length * sizeof(float) * 2);
|
|
}
|
|
#endif
|
|
if (gNB2UE==NULL) {
|
|
printf("Problem generating channel model. Exiting.\n");
|
|
exit(-1);
|
|
}
|
|
|
|
frame_length_complex_samples = frame_parms->samples_per_subframe*NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
|
//frame_length_complex_samples_no_prefix = frame_parms->samples_per_subframe_wCP*NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
|
int slot_offset = get_samples_slot_timestamp(frame_parms, slot);
|
|
int slot_length = slot_offset - get_samples_slot_timestamp(frame_parms, slot - 1);
|
|
|
|
s_interleaved = malloc(n_tx * sizeof(float *));
|
|
r_re = malloc(n_rx * sizeof(float *));
|
|
r_im = malloc(n_rx * sizeof(float *));
|
|
txdata = malloc(n_tx * sizeof(int *));
|
|
|
|
for (i = 0; i < n_tx; i++) {
|
|
s_interleaved[i] = malloc(slot_length * 2 * sizeof(float));
|
|
printf("Allocating %d samples for txdata\n", frame_length_complex_samples);
|
|
txdata[i] = calloc(1, frame_length_complex_samples * sizeof(int));
|
|
}
|
|
|
|
for (i = 0; i < n_rx; i++) {
|
|
r_re[i] = calloc(1, slot_length * sizeof(float));
|
|
r_im[i] = calloc(1, slot_length * sizeof(float));
|
|
}
|
|
|
|
if (pbch_file_fd!=NULL) {
|
|
load_pbch_desc(pbch_file_fd);
|
|
}
|
|
|
|
|
|
//configure UE
|
|
UE = malloc(sizeof(PHY_VARS_NR_UE));
|
|
memset((void*)UE,0,sizeof(PHY_VARS_NR_UE));
|
|
PHY_vars_UE_g = malloc(sizeof(PHY_VARS_NR_UE**));
|
|
PHY_vars_UE_g[0] = malloc(sizeof(PHY_VARS_NR_UE*));
|
|
PHY_vars_UE_g[0][0] = UE;
|
|
memcpy(&UE->frame_parms,frame_parms,sizeof(NR_DL_FRAME_PARMS));
|
|
UE->frame_parms.nb_antennas_rx = n_rx;
|
|
UE->frame_parms.nb_antenna_ports_gNB = n_tx;
|
|
UE->nrLDPC_coding_interface = gNB->nrLDPC_coding_interface;
|
|
UE->max_ldpc_iterations = max_ldpc_iterations;
|
|
init_nr_ue_phy_cpu_stats(&UE->phy_cpu_stats);
|
|
UE->is_synchronized = 1;
|
|
|
|
if (init_nr_ue_signal(UE, 1) != 0)
|
|
{
|
|
printf("Error at UE NR initialisation\n");
|
|
exit(-1);
|
|
}
|
|
|
|
init_nr_ue_transport(UE);
|
|
|
|
UE_mac = nr_l2_init_ue(0, mu);
|
|
ue_init_config_request(UE_mac, get_slots_per_frame_from_scs(mu));
|
|
|
|
UE->if_inst = nr_ue_if_module_init(0);
|
|
UE->if_inst->scheduled_response = nr_ue_scheduled_response;
|
|
UE->if_inst->phy_config_request = nr_ue_phy_config_request;
|
|
UE->if_inst->dl_indication = nr_ue_dl_indication;
|
|
UE->if_inst->ul_indication = dummy_nr_ue_ul_indication;
|
|
UE->chest_freq = chest_type[0];
|
|
UE->chest_time = chest_type[1];
|
|
|
|
UE_mac->if_module = nr_ue_if_module_init(0);
|
|
|
|
unsigned int available_bits=0;
|
|
unsigned int errors_bit = 0;
|
|
|
|
initFloatingCoresTpool(dlsch_threads, &nrUE_params.Tpool, false, "UE-tpool");
|
|
|
|
// generate signal
|
|
AssertFatal(input_fd==NULL,"Not ready for input signal file\n");
|
|
|
|
// clone CellGroup to have a separate copy at UE
|
|
NR_CellGroupConfig_t *UE_CellGroup = clone_CellGroupConfig(secondaryCellGroup);
|
|
|
|
//Configure UE
|
|
NR_BCCH_BCH_Message_t *mib = get_new_MIB_NR(scc);
|
|
nr_rrc_mac_config_req_mib(0, 0, mib->message.choice.mib, false);
|
|
nr_rrc_mac_config_req_cg(0, 0, 0, 0, UE_CellGroup, UE_Capability_nr);
|
|
|
|
asn1cFreeStruc(asn_DEF_NR_CellGroupConfig, UE_CellGroup);
|
|
|
|
UE_mac->state = UE_CONNECTED;
|
|
UE_mac->ra.ra_state = nrRA_SUCCEEDED;
|
|
|
|
nr_phy_data_t phy_data = {0};
|
|
fapi_nr_dl_config_request_t dl_config = {.sfn = frame, .slot = slot};
|
|
nr_scheduled_response_t scheduled_response = {.dl_config = &dl_config, .phy_data = &phy_data, .mac = UE_mac};
|
|
|
|
nr_ue_phy_config_request(&UE_mac->phy_config);
|
|
//NR_COMMON_channels_t *cc = RC.nrmac[0]->common_channels;
|
|
int ret = 1;
|
|
initNamedTpool(gNBthreads, &gNB->threadPool, true, "gNB-tpool");
|
|
initNotifiedFIFO(&gNB->L1_tx_out);
|
|
|
|
// Buffers to store internal memory of slot process
|
|
int rx_size = (((14 * UE->frame_parms.N_RB_DL * 12 * sizeof(int32_t)) + 15) >> 4) << 4;
|
|
UE->phy_sim_rxdataF = calloc(sizeof(int32_t *) * UE->frame_parms.nb_antennas_rx * g_nrOfLayers,
|
|
UE->frame_parms.samples_per_slot_wCP * sizeof(int32_t));
|
|
UE->phy_sim_pdsch_llr = calloc(1, (8 * (3 * 8 * 8448)) * sizeof(int16_t)); // Max length
|
|
UE->phy_sim_pdsch_rxdataF_ext = calloc(sizeof(int32_t *) * UE->frame_parms.nb_antennas_rx * g_nrOfLayers, rx_size);
|
|
UE->phy_sim_pdsch_rxdataF_comp = calloc(sizeof(int32_t *) * UE->frame_parms.nb_antennas_rx * g_nrOfLayers, rx_size);
|
|
UE->phy_sim_pdsch_dl_ch_estimates = calloc(sizeof(int32_t *) * UE->frame_parms.nb_antennas_rx * g_nrOfLayers, rx_size);
|
|
UE->phy_sim_pdsch_dl_ch_estimates_ext = calloc(sizeof(int32_t *) * UE->frame_parms.nb_antennas_rx * g_nrOfLayers, rx_size);
|
|
int a_segments = MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated
|
|
if (g_rbSize != 273) {
|
|
a_segments = a_segments*g_rbSize;
|
|
a_segments = (a_segments/273)+1;
|
|
}
|
|
uint32_t dlsch_bytes = a_segments*1056; // allocated bytes per segment
|
|
UE->phy_sim_dlsch_b = calloc(NR_MAX_NB_CODEWORDS, dlsch_bytes);
|
|
|
|
// csv file
|
|
if (filename_csv != NULL) {
|
|
csv_file = fopen(filename_csv, "a");
|
|
if (csv_file == NULL) {
|
|
printf("Can't open file \"%s\", errno %d\n", filename_csv, errno);
|
|
free(s_interleaved);
|
|
free(r_re);
|
|
free(r_im);
|
|
free(txdata);
|
|
return 1;
|
|
}
|
|
// adding name of parameters into file
|
|
fprintf(csv_file,"SNR,false_positive,");
|
|
for (int r = 0; r < num_rounds; r++)
|
|
fprintf(csv_file,"n_errors_%d,errors_scrambling_%d,channel_bler_%d,channel_ber_%d,",r,r,r,r);
|
|
fprintf(csv_file,"avg_round,eff_rate,eff_throughput,TBS\n");
|
|
}
|
|
//---------------
|
|
|
|
Sched_INFO = memalign(32, sizeof(*Sched_INFO));
|
|
if (Sched_INFO == NULL) {
|
|
LOG_E(PHY, "out of memory\n");
|
|
exit(1);
|
|
}
|
|
|
|
time_stats_t channel_stats = {0};
|
|
time_stats_t noise_stats = {0};
|
|
time_stats_t pipeline_stats = {0};
|
|
|
|
for (SNR = snr0; SNR < snr1 && !stop; SNR += .2) {
|
|
|
|
varArray_t *table_tx=initVarArray(1000,sizeof(double));
|
|
reset_meas(&gNB->phy_proc_tx);
|
|
reset_meas(&gNB->dlsch_scrambling_stats);
|
|
reset_meas(&gNB->dlsch_interleaving_stats);
|
|
reset_meas(&gNB->dlsch_rate_matching_stats);
|
|
reset_meas(&gNB->dlsch_segmentation_stats);
|
|
reset_meas(&gNB->dlsch_modulation_stats);
|
|
reset_meas(&gNB->dlsch_pdsch_generation_stats);
|
|
reset_meas(&gNB->dlsch_precoding_stats);
|
|
reset_meas(&gNB->dlsch_layer_mapping_stats);
|
|
reset_meas(&gNB->dlsch_resource_mapping_stats);
|
|
reset_meas(&gNB->dlsch_encoding_stats);
|
|
reset_meas(&gNB->dci_generation_stats);
|
|
reset_meas(&gNB->tinput);
|
|
reset_meas(&gNB->tprep);
|
|
reset_meas(&gNB->tparity);
|
|
reset_meas(&gNB->toutput);
|
|
reset_meas(&gNB->phase_comp_stats);
|
|
|
|
uint32_t errors_scrambling[16] = {0};
|
|
int n_errors[16] = {0};
|
|
int round_trials[16] = {0};
|
|
double roundStats = {0};
|
|
double blerStats[16] = {0};
|
|
double berStats[16] = {0};
|
|
|
|
effRate = 0;
|
|
//n_errors2 = 0;
|
|
//n_alamouti = 0;
|
|
n_false_positive = 0;
|
|
if (n_trials== 1) num_rounds = 1;
|
|
|
|
NR_gNB_DLSCH_t *gNB_dlsch = &gNB->dlsch[0];
|
|
memset(Sched_INFO, 0, sizeof(*Sched_INFO));
|
|
nfapi_nr_dl_tti_request_body_t *dl_req = &Sched_INFO->DL_req.dl_tti_request_body;
|
|
// scheduler will place PDSCH second (after PDCCH), verification below
|
|
nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[1];
|
|
nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15;
|
|
|
|
for (trial = 0; trial < n_trials && !stop; trial++) {
|
|
|
|
errors_bit = 0;
|
|
//multipath channel
|
|
//multipath_channel(gNB2UE,s_re,s_im,r_re,r_im,frame_length_complex_samples,0);
|
|
|
|
UE_proc.frame_rx = frame;
|
|
UE_proc.nr_slot_rx = slot;
|
|
UE_proc.gNB_id = 0;
|
|
|
|
int harq_pid = slot;
|
|
NR_DL_UE_HARQ_t *UE_harq_process[NR_MAX_NB_CODEWORDS];
|
|
round = 0;
|
|
for (int c = 0; c < NR_MAX_NB_CODEWORDS; c++) {
|
|
UE_harq_process[c] = &UE->dl_harq_processes[c][harq_pid];
|
|
UE_harq_process[c]->decodeResult = false;
|
|
UE_harq_process[c]->DLround = round;
|
|
UE_harq_process[c]->first_rx = 1;
|
|
}
|
|
int n_codewords = 0;
|
|
bool all_codewords_decoded = false;
|
|
|
|
while (round < num_rounds && !all_codewords_decoded && !stop) {
|
|
reset_sched_response(Sched_INFO, frame, slot, 0, 0);
|
|
clear_nr_nfapi_information(RC.nrmac[0], 0, frame, slot);
|
|
UE_info->UE_sched_ctrl.harq_processes[harq_pid].ndi = !(trial&1);
|
|
/* The high-rank PDSCH carries two independently HARQ-managed TBs.
|
|
* Advance both NDIs between independent trials; leaving CW1's NDI
|
|
* unchanged makes the UE treat every later trial as a retransmission. */
|
|
UE_info->UE_sched_ctrl.harq_processes[harq_pid].ndi2 = !(trial&1);
|
|
UE_info->UE_sched_ctrl.harq_processes[harq_pid].round = round;
|
|
|
|
// nr_schedule_ue_spec() requires the mutex to be locked
|
|
NR_SCHED_LOCK(&gNB_mac->sched_lock);
|
|
nr_schedule_ue_spec(0, frame, slot, &Sched_INFO->DL_req, &Sched_INFO->TX_req);
|
|
NR_SCHED_UNLOCK(&gNB_mac->sched_lock);
|
|
|
|
/* check that second message is indeed PDSCH */
|
|
DevAssert(dl_tti_pdsch_pdu->PDUType == NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE);
|
|
|
|
/* PTRS values for DLSIM calculations */
|
|
pdu_bit_map = pdsch_pdu_rel15->pduBitmap;
|
|
// This is already in DL procedures??
|
|
if(pdu_bit_map & 0x1) {
|
|
set_ptrs_symb_idx(&dlPtrsSymPos,
|
|
pdsch_pdu_rel15->NrOfSymbols,
|
|
pdsch_pdu_rel15->StartSymbolIndex,
|
|
1<<pdsch_pdu_rel15->PTRSTimeDensity,
|
|
pdsch_pdu_rel15->dlDmrsSymbPos);
|
|
ptrsSymbPerSlot = get_ptrs_symbols_in_slot(dlPtrsSymPos, pdsch_pdu_rel15->StartSymbolIndex, pdsch_pdu_rel15->NrOfSymbols);
|
|
ptrsRePerSymb = ((pdsch_pdu_rel15->rbSize + pdsch_pdu_rel15->PTRSFreqDensity - 1) / pdsch_pdu_rel15->PTRSFreqDensity);
|
|
LOG_D(PHY,"[DLSIM] PTRS Symbols in a slot: %2u, RE per Symbol: %3u, RE in a slot %4d\n", ptrsSymbPerSlot, ptrsRePerSymb, ptrsSymbPerSlot * ptrsRePerSymb);
|
|
}
|
|
|
|
start_meas(&gNB->phy_proc_tx);
|
|
phy_procedures_gNB_TX(gNB, &Sched_INFO->DL_req, &Sched_INFO->TX_req, &Sched_INFO->UL_dci_req, frame, slot);
|
|
stop_meas(&gNB->phy_proc_tx);
|
|
|
|
if (n_trials==1) {
|
|
LOG_M("txsigF0.m","txsF0=",
|
|
&gNB->common_vars.txdataF[0][0][2 * frame_parms->ofdm_symbol_size],
|
|
frame_parms->ofdm_symbol_size,
|
|
1,
|
|
1);
|
|
if (gNB->frame_parms.nb_antennas_tx>1)
|
|
LOG_M("txsigF1.m","txsF1=",
|
|
&gNB->common_vars.txdataF[0][1][2 * frame_parms->ofdm_symbol_size],
|
|
frame_parms->ofdm_symbol_size,
|
|
1,
|
|
1);
|
|
}
|
|
if (n_trials == 1)
|
|
printf("slot_offset %d\n", slot_offset);
|
|
|
|
//TODO: loop over slots
|
|
for (aa=0; aa<gNB->frame_parms.nb_antennas_tx; aa++) {
|
|
c16_t fft_in_buff[frame_parms->ofdm_symbol_size * frame_parms->symbols_per_slot] __attribute__((aligned(64)));
|
|
memset(fft_in_buff, 0, sizeof(fft_in_buff));
|
|
if (cyclic_prefix_type == 1) {
|
|
fft_shift(gNB->common_vars.txdataF[0][aa],
|
|
frame_parms->ofdm_symbol_size,
|
|
frame_parms->N_RB_DL,
|
|
fft_in_buff,
|
|
frame_parms->ofdm_symbol_size,
|
|
0,
|
|
12);
|
|
PHY_ofdm_mod((int *)fft_in_buff,
|
|
(int *)&txdata[aa][slot_offset],
|
|
frame_parms->ofdm_symbol_size,
|
|
12,
|
|
frame_parms->nb_prefix_samples,
|
|
CYCLIC_PREFIX);
|
|
} else {
|
|
bool was_symbol_used[NR_SYMBOLS_PER_SLOT];
|
|
for (int i = 0; i < 14; i++) {
|
|
was_symbol_used[i] = true;
|
|
}
|
|
fft_shift(gNB->common_vars.txdataF[0][aa],
|
|
frame_parms->ofdm_symbol_size,
|
|
frame_parms->N_RB_DL,
|
|
fft_in_buff,
|
|
frame_parms->ofdm_symbol_size,
|
|
0,
|
|
14);
|
|
nr_normal_prefix_mod(fft_in_buff,
|
|
&txdata[aa][slot_offset],
|
|
14,
|
|
frame_parms,
|
|
slot,
|
|
was_symbol_used);
|
|
}
|
|
}
|
|
if (n_trials==1) {
|
|
char filename[100];//LOG_M
|
|
for (aa=0;aa<n_tx;aa++) {
|
|
sprintf(filename,"txsig%d.m", aa);//LOG_M
|
|
LOG_M(filename,"txs", &txdata[aa][slot_offset +frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples0],6*(frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples),1,1);
|
|
}
|
|
if (output_fd) {
|
|
printf("writing txdata to binary file\n");
|
|
fwrite(txdata[0], sizeof(int32_t), frame_length_complex_samples, output_fd);
|
|
}
|
|
}
|
|
|
|
// Compute transmitter energy level
|
|
int l_ofdm = 6;
|
|
int symbol_offset = slot_offset + l_ofdm * frame_parms->ofdm_symbol_size + (l_ofdm - 1) * frame_parms->nb_prefix_samples
|
|
+ frame_parms->nb_prefix_samples0;
|
|
int symbol_length = frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples;
|
|
double txlev_sum = compute_tx_energy_level(txdata, n_tx, symbol_offset, symbol_length, n_trials);
|
|
|
|
for (int aa = 0; aa < frame_parms->nb_antennas_tx; aa++) {
|
|
for (int i = 0; i < slot_length; i++) {
|
|
s_interleaved[aa][2 * i] = (float)((short *)&txdata[aa][slot_offset])[2 * i];
|
|
s_interleaved[aa][2 * i + 1] = (float)((short *)&txdata[aa][slot_offset])[2 * i + 1];
|
|
}
|
|
}
|
|
|
|
double ts = 1.0/(frame_parms->subcarrier_spacing * frame_parms->ofdm_symbol_size);
|
|
|
|
// Estimate noise power from the transmitter level and SNR
|
|
double sigma2 =
|
|
compute_noise_variance(txlev_sum, UE->frame_parms.ofdm_symbol_size, pdsch_pdu_rel15->rbSize, 1, SNR, n_trials);
|
|
const int padding_len = gNB2UE->channel_length - 1;
|
|
const int padded_slot_length = slot_length + padding_len;
|
|
float *h_tx_ptr = (float *)h_tx_sig_pinned;
|
|
size_t total_padded_bytes_for_slot = n_tx * padded_slot_length * 2 * sizeof(float);
|
|
memset(h_tx_ptr, 0, total_padded_bytes_for_slot);
|
|
|
|
for (int j = 0; j < n_tx; j++) {
|
|
float *data_start_ptr = h_tx_ptr + (j * padded_slot_length + padding_len) * 2;
|
|
memcpy(data_start_ptr, s_interleaved[j], slot_length * 2 * sizeof(float));
|
|
}
|
|
|
|
for (aa = 0; aa < n_rx; aa++) {
|
|
bzero(r_re[aa], slot_length * sizeof(float));
|
|
bzero(r_im[aa], slot_length * sizeof(float));
|
|
}
|
|
|
|
// Apply MIMO Channel
|
|
#ifdef ENABLE_CUDA
|
|
if (use_cuda) {
|
|
#if defined(USE_UNIFIED_MEMORY)
|
|
int deviceId;
|
|
cudaGetDevice(&deviceId);
|
|
cudaMemPrefetchAsync(d_tx_sig, n_tx * padded_slot_length * sizeof(float) * 2, deviceId, 0);
|
|
#endif
|
|
start_meas(&pipeline_stats);
|
|
random_channel(gNB2UE, 0);
|
|
int num_links = gNB2UE->nb_tx * gNB2UE->nb_rx;
|
|
for (int link = 0; link < num_links; link++) {
|
|
for (int l = 0; l < gNB2UE->channel_length; l++) {
|
|
int idx = link * gNB2UE->channel_length + l;
|
|
((float2 *)h_channel_coeffs)[idx].x = (float)gNB2UE->ch[link][l].r;
|
|
((float2 *)h_channel_coeffs)[idx].y = (float)gNB2UE->ch[link][l].i;
|
|
}
|
|
}
|
|
run_channel_pipeline_cuda(UE->common_vars.rxdata,
|
|
n_tx,
|
|
n_rx,
|
|
gNB2UE->channel_length,
|
|
slot_length,
|
|
h_channel_coeffs,
|
|
(float)sigma2,
|
|
ts,
|
|
pdu_bit_map,
|
|
0x1,
|
|
slot_offset,
|
|
delay,
|
|
d_tx_sig,
|
|
d_intermediate_sig,
|
|
d_final_output,
|
|
d_curand_states,
|
|
h_tx_sig_pinned,
|
|
h_final_output_pinned,
|
|
d_channel_coeffs_gpu);
|
|
cudaDeviceSynchronize();
|
|
stop_meas(&pipeline_stats);
|
|
} else
|
|
#endif
|
|
{
|
|
float **tx_sig_for_cpu = malloc(n_tx * sizeof(float *));
|
|
float *h_tx_ptr = (float *)h_tx_sig_pinned;
|
|
const int padding_len = gNB2UE->channel_length - 1;
|
|
const int padded_slot_length = slot_length + padding_len;
|
|
|
|
for (int j = 0; j < n_tx; j++) {
|
|
tx_sig_for_cpu[j] = h_tx_ptr + (j * padded_slot_length + padding_len) * 2;
|
|
}
|
|
|
|
for (aa = 0; aa < n_rx; aa++) {
|
|
bzero(r_re[aa], slot_length * sizeof(float));
|
|
bzero(r_im[aa], slot_length * sizeof(float));
|
|
}
|
|
start_meas(&channel_stats);
|
|
multipath_channel_float(gNB2UE, tx_sig_for_cpu, r_re, r_im, slot_length, 0, (n_trials == 1) ? 1 : 0);
|
|
stop_meas(&channel_stats);
|
|
|
|
free(tx_sig_for_cpu);
|
|
|
|
bool apply_phase_noise = (pdu_bit_map & 0x1);
|
|
start_meas(&noise_stats);
|
|
add_noise_float(UE->common_vars.rxdata,
|
|
(const float **)r_re,
|
|
(const float **)r_im,
|
|
(float)sigma2,
|
|
slot_length,
|
|
slot_offset,
|
|
ts,
|
|
delay,
|
|
apply_phase_noise,
|
|
UE->frame_parms.nb_antennas_rx);
|
|
stop_meas(&noise_stats);
|
|
}
|
|
|
|
dl_config.sfn = frame;
|
|
dl_config.slot = slot;
|
|
ue_dci_configuration(UE_mac, &dl_config, frame, slot);
|
|
nr_ue_scheduled_response(&scheduled_response);
|
|
|
|
pbch_processing(UE, &UE_proc, &phy_data);
|
|
pdcch_processing(UE, &UE_proc, &phy_data);
|
|
pdsch_processing(UE,
|
|
&UE_proc,
|
|
&phy_data);
|
|
//----------------------------------------------------------
|
|
//---------------------- count errors ----------------------
|
|
//----------------------------------------------------------
|
|
|
|
n_codewords = phy_data.n_dlsch_codewords;
|
|
AssertFatal(n_codewords >= 1 && n_codewords <= NR_MAX_NB_CODEWORDS,
|
|
"Invalid number of decoded PDSCH codewords %d\n",
|
|
n_codewords);
|
|
bool block_error = false;
|
|
bool any_active_codeword = false;
|
|
all_codewords_decoded = true;
|
|
for (int c = 0; c < n_codewords; c++) {
|
|
NR_UE_DLSCH_t *dlsch = &phy_data.dlsch[c];
|
|
any_active_codeword |= dlsch->active;
|
|
block_error |= dlsch->active && dlsch->last_iteration_cnt >= dlsch->max_ldpc_iterations;
|
|
all_codewords_decoded &= UE_harq_process[c]->decodeResult;
|
|
}
|
|
if (block_error)
|
|
n_errors[round]++;
|
|
if (any_active_codeword)
|
|
round_trials[round]++;
|
|
|
|
int16_t *UE_llr = (int16_t*)UE->phy_sim_pdsch_llr;
|
|
|
|
TBS = 0;
|
|
uint16_t length_dmrs = get_num_dmrs(phy_data.dlsch_config.dlDmrsSymbPos);
|
|
uint16_t nb_rb = phy_data.dlsch_config.number_rbs;
|
|
uint8_t nb_re_dmrs = phy_data.dlsch_config.dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ?
|
|
6 * phy_data.dlsch_config.n_dmrs_cdm_groups :
|
|
4 * phy_data.dlsch_config.n_dmrs_cdm_groups;
|
|
uint8_t nb_symb_sch = phy_data.dlsch_config.number_symbols;
|
|
uint32_t unav_res = ptrsSymbPerSlot * ptrsRePerSymb;
|
|
available_bits = 0;
|
|
for (int c = 0; c < n_codewords; c++) {
|
|
const fapi_nr_dl_cw_info_t *cw_info = &phy_data.dlsch_config.cw_info[c];
|
|
TBS += cw_info->TBS;
|
|
available_bits += nr_get_G(nb_rb,
|
|
nb_symb_sch,
|
|
nb_re_dmrs,
|
|
length_dmrs,
|
|
unav_res,
|
|
cw_info->qamModOrder,
|
|
cw_info->Nl);
|
|
}
|
|
if (pdu_bit_map & 0x1) {
|
|
if (trial == 0 && round == 0) {
|
|
printf("[DLSIM][PTRS] Available bits are: %5u, removed PTRS REs are: %5u \n",
|
|
available_bits,
|
|
ptrsSymbPerSlot * ptrsRePerSymb * pdsch_pdu_rel15->nrOfLayers);
|
|
}
|
|
}
|
|
|
|
uint32_t llr_offset = 0;
|
|
for (int c = 0; c < n_codewords; c++) {
|
|
const fapi_nr_dl_cw_info_t *cw_info = &phy_data.dlsch_config.cw_info[c];
|
|
const uint32_t codeword_bits = nr_get_G(nb_rb,
|
|
nb_symb_sch,
|
|
nb_re_dmrs,
|
|
length_dmrs,
|
|
unav_res,
|
|
cw_info->qamModOrder,
|
|
cw_info->Nl);
|
|
for (i = 0; i < codeword_bits; i++) {
|
|
const uint8_t current_bit = (gNB_dlsch->f[c][i / 8] & (1 << (i & 7))) >> (i & 7);
|
|
if (((current_bit == 0) && (UE_llr[llr_offset + i] <= 0))
|
|
|| ((current_bit == 1) && (UE_llr[llr_offset + i] >= 0))) {
|
|
if (errors_scrambling[round] == 0)
|
|
LOG_D(PHY, "First bit in error in unscrambling = CW%d bit %d\n", c, i);
|
|
errors_scrambling[round]++;
|
|
}
|
|
}
|
|
llr_offset += codeword_bits;
|
|
}
|
|
AssertFatal(llr_offset == available_bits,
|
|
"PDSCH LLR accounting mismatch: %u of %u bits\n",
|
|
llr_offset,
|
|
available_bits);
|
|
|
|
//printf("dlsim round %d ends\n",round);
|
|
round++;
|
|
} // round
|
|
|
|
for (int c = 0; c < n_codewords; c++) {
|
|
const uint32_t codeword_tbs = phy_data.dlsch_config.cw_info[c].TBS;
|
|
const uint8_t *decoded = UE->phy_sim_dlsch_b + c * dlsch_bytes;
|
|
for (i = 0; i < codeword_tbs; i++) {
|
|
const uint8_t estimated_output_bit = (decoded[i / 8] & (1 << (i & 7))) >> (i & 7);
|
|
const uint8_t test_input_bit = (gNB_dlsch->b[c][i / 8] & (1 << (i & 7))) >> (i & 7);
|
|
|
|
if (estimated_output_bit != test_input_bit) {
|
|
if (errors_bit == 0)
|
|
LOG_D(PHY, "First bit in error in decoding = CW%d bit %d\n", c, i);
|
|
errors_bit++;
|
|
}
|
|
}
|
|
}
|
|
////////////////////////////////////////////////////////////
|
|
|
|
if (errors_bit > 0) {
|
|
n_false_positive++;
|
|
if (n_trials == 1)
|
|
printf("errors_bit = %u (trial %d)\n", errors_bit, trial);
|
|
}
|
|
roundStats += round;
|
|
if (all_codewords_decoded)
|
|
effRate += ((double)TBS) / round;
|
|
} // noise trials
|
|
|
|
roundStats /= n_trials;
|
|
|
|
for (int r = 0; r < num_rounds; r++) {
|
|
blerStats[r] = (double)n_errors[r] / round_trials[r];
|
|
berStats[r] = (double)errors_scrambling[r] / available_bits / round_trials[r];
|
|
}
|
|
|
|
effRate /= n_trials;
|
|
printf("*****************************************\n");
|
|
printf("SNR %f: n_errors (%d/%d", SNR, n_errors[0], round_trials[0]);
|
|
for (int r = 1; r < num_rounds; r++)
|
|
printf(",%d/%d", n_errors[r], round_trials[r]);
|
|
printf(") (negative CRC), false_positive %d/%d, errors_scrambling (%u/%u", n_false_positive, n_trials, errors_scrambling[0], available_bits * round_trials[0]);
|
|
for (int r = 1; r < num_rounds; r++)
|
|
printf(",%u/%u", errors_scrambling[r], available_bits * round_trials[r]);
|
|
printf(")\n\n");
|
|
dump_pdsch_stats(stdout,gNB);
|
|
printf("SNR %f: Channel BLER (%e", SNR, blerStats[0]);
|
|
for (int r = 1; r < num_rounds; r++)
|
|
printf(",%e", blerStats[r]);
|
|
printf("), Channel BER (%e", berStats[0]);
|
|
for (int r = 1; r < num_rounds; r++)
|
|
printf(",%e", berStats[r]);
|
|
printf(") Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %u bits/slot\n",
|
|
roundStats,
|
|
effRate,
|
|
effRate / TBS * 100,
|
|
TBS);
|
|
printf("*****************************************\n");
|
|
printf("\n");
|
|
// writing to csv file
|
|
if (filename_csv != NULL) { // means we are asked to print stats to CSV
|
|
fprintf(csv_file,"%f,%d/%d,",SNR,n_false_positive,n_trials);
|
|
for (int r = 0; r < num_rounds; r++)
|
|
fprintf(csv_file,"%d/%d,%u/%u,%f,%e,",n_errors[r], round_trials[r], errors_scrambling[r], available_bits * round_trials[r],blerStats[r],berStats[r]);
|
|
fprintf(csv_file, "%.2f,%.4f,%.2f,%u\n", roundStats, effRate, effRate / TBS * 100, TBS);
|
|
}
|
|
if (print_perf==1) {
|
|
printf("\ngNB TX function statistics (per %d us slot, NPRB %d, mcs %d, C %d, block %d)\n",
|
|
1000 >> *scc->ssbSubcarrierSpacing,
|
|
g_rbSize,
|
|
g_mcsIndex,
|
|
UE->dl_harq_processes[0][slot].C,
|
|
TBS);
|
|
printDistribution(&gNB->phy_proc_tx,table_tx,"PHY proc tx");
|
|
printStatIndent2(&gNB->dci_generation_stats, "DCI encoding time");
|
|
printStatIndent2(&gNB->dlsch_encoding_stats,"DLSCH encoding time");
|
|
printStatIndent3(&gNB->dlsch_segmentation_stats,"DLSCH segmentation time");
|
|
printStatIndent3(&gNB->tinput,"DLSCH LDPC input processing time");
|
|
printStatIndent3(&gNB->tprep,"DLSCH LDPC input preparation time");
|
|
printStatIndent3(&gNB->tparity,"DLSCH LDPC parity generation time");
|
|
printStatIndent3(&gNB->toutput,"DLSCH LDPC output generation time");
|
|
printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Matching time");
|
|
printStatIndent3(&gNB->dlsch_interleaving_stats, "DLSCH Interleaving time");
|
|
printStatIndent2(&gNB->dlsch_modulation_stats,"DLSCH modulation time");
|
|
printStatIndent2(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time");
|
|
printStatIndent2(&gNB->dlsch_pdsch_generation_stats,"DLSCH PDSCH Generation time");
|
|
printStatIndent3(&gNB->dlsch_layer_mapping_stats,"DLSCH Layer Mapping time");
|
|
gNB->dlsch_resource_mapping_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
|
|
printStatIndent3(&gNB->dlsch_resource_mapping_stats,"DLSCH Resource Mapping time");
|
|
gNB->dlsch_precoding_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
|
|
printStatIndent3(&gNB->dlsch_precoding_stats,"DLSCH Precoding time");
|
|
if (gNB->phase_comp)
|
|
printStatIndent2(&gNB->phase_comp_stats, "Phase Compensation");
|
|
|
|
if (use_cuda) {
|
|
printStatIndent(&pipeline_stats, "GPU Channel Pipeline");
|
|
} else {
|
|
printStatIndent(&channel_stats, "Multipath Channel (CPU)");
|
|
printStatIndent(&noise_stats, "Add Noise (CPU)");
|
|
}
|
|
|
|
printf("\nUE function statistics (per %d us slot)\n", 1000 >> *scc->ssbSubcarrierSpacing);
|
|
for (int i = RX_PDSCH_STATS; i <= DLSCH_PROCEDURES_STATS; i++) {
|
|
printStatIndent(&UE->phy_cpu_stats.cpu_time_stats[i], UE->phy_cpu_stats.cpu_time_stats[i].meas_name);
|
|
}
|
|
}
|
|
|
|
if (n_trials == 1) {
|
|
unsigned int op_format = 1;
|
|
unsigned int dec = 1;
|
|
if (output_fd) { // Write in bin format
|
|
op_format |= MATLAB_RAW;
|
|
}
|
|
|
|
LOG_M("rxsig0.m", "rxs0", UE->common_vars.rxdata[0], frame_length_complex_samples, dec, op_format);
|
|
if (UE->frame_parms.nb_antennas_rx>1)
|
|
LOG_M("rxsig1.m", "rxs1", UE->common_vars.rxdata[1], frame_length_complex_samples, dec, op_format);
|
|
LOG_M("rxF0.m", "rxF0", UE->phy_sim_rxdataF, frame_parms->samples_per_slot_wCP, dec, op_format);
|
|
const uint32_t numReSym = (g_rbSize * 12 + 15) & (~15);
|
|
for (int l = 0; l < g_nrOfLayers; l++) {
|
|
for (int r = 0; r < n_rx; r++) {
|
|
const int s = pdsch_pdu_rel15->StartSymbolIndex;
|
|
const int n = pdsch_pdu_rel15->NrOfSymbols;
|
|
for (int i = s; i < s + n; i++) {
|
|
const uint32_t dmrsBitMap = phy_data.dlsch_config.dlDmrsSymbPos;
|
|
const uint32_t dmrsCfg = phy_data.dlsch_config.dmrsConfigType;
|
|
const uint32_t nrb = phy_data.dlsch_config.number_rbs;
|
|
const uint32_t ncdmg = phy_data.dlsch_config.n_dmrs_cdm_groups;
|
|
const uint32_t numValidReSym = ((dmrsBitMap >> i) & 1)
|
|
? ((dmrsCfg == NFAPI_NR_DMRS_TYPE1) ? nrb * (12 - 6 * ncdmg) : nrb * (12 - 4 * ncdmg))
|
|
: (nrb * 12);
|
|
char fName[50];
|
|
snprintf(fName, sizeof(fName), "chestF_ext_l%d_r%d_s%d.m", l, r, i);
|
|
uint32_t buff_offset = (i * g_nrOfLayers * n_rx * numReSym) + (l * n_rx * numReSym) + (r * numReSym);
|
|
LOG_M(fName,
|
|
"chF0_ext",
|
|
((c16_t *)UE->phy_sim_pdsch_dl_ch_estimates_ext) + buff_offset,
|
|
numValidReSym,
|
|
dec,
|
|
op_format);
|
|
snprintf(fName, sizeof(fName), "rxF_comp_l%d_s%d.m", l, i);
|
|
LOG_M(fName, "rxFc", ((c16_t *)UE->phy_sim_pdsch_rxdataF_comp) + (l * NR_SYMBOLS_PER_SLOT * numReSym) + (i * numReSym), numValidReSym, dec, op_format);
|
|
if (l == 0) {
|
|
snprintf(fName, sizeof(fName), "rxF_ext_r%d_s%d.m", r, i);
|
|
buff_offset = (i * n_rx * numReSym) + (r * numReSym);
|
|
LOG_M(fName, "rxFext", ((c16_t *)UE->phy_sim_pdsch_rxdataF_ext) + buff_offset, numValidReSym, dec, op_format);
|
|
}
|
|
snprintf(fName, sizeof(fName), "chestF0_s%d.m", i);
|
|
LOG_M(fName, "chF0", ((c16_t *)UE->phy_sim_pdsch_dl_ch_estimates) + i * frame_parms->ofdm_symbol_size, nrb*12, dec, op_format);
|
|
}
|
|
}
|
|
}
|
|
LOG_M("chestF0.m", "chF0", UE->phy_sim_pdsch_dl_ch_estimates, frame_parms->ofdm_symbol_size * 14, dec, op_format);
|
|
op_format = (op_format & (~1));
|
|
LOG_M("rxF_llr.m", "rxFllr", UE->phy_sim_pdsch_llr, available_bits, dec, op_format);
|
|
ret = n_errors[0] == 0 && n_false_positive == 0 ? 0 : 1;
|
|
break;
|
|
}
|
|
|
|
if (effRate >= eff_tp_check * TBS) {
|
|
printf("PDSCH test OK\n");
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
} // NSR
|
|
|
|
free(Sched_INFO);
|
|
|
|
free_channel_desc_scm(gNB2UE);
|
|
|
|
for (i = 0; i < n_tx; i++) {
|
|
free(s_interleaved[i]);
|
|
free(txdata[i]);
|
|
}
|
|
for (i = 0; i < n_rx; i++) {
|
|
free(r_re[i]);
|
|
free(r_im[i]);
|
|
}
|
|
|
|
#ifdef ENABLE_CUDA
|
|
free_cuda_chsim_buffers(use_cuda,
|
|
&d_tx_sig,
|
|
&d_intermediate_sig,
|
|
&d_final_output,
|
|
&d_curand_states,
|
|
&h_tx_sig_pinned,
|
|
&h_final_output_pinned,
|
|
&h_channel_coeffs,
|
|
&d_channel_coeffs_gpu);
|
|
#endif
|
|
|
|
free(s_interleaved);
|
|
free(r_re);
|
|
free(r_im);
|
|
free(txdata);
|
|
free(UE->phy_sim_rxdataF);
|
|
free(UE->phy_sim_pdsch_llr);
|
|
free(UE->phy_sim_pdsch_rxdataF_ext);
|
|
free(UE->phy_sim_pdsch_rxdataF_comp);
|
|
free(UE->phy_sim_pdsch_dl_ch_estimates);
|
|
free(UE->phy_sim_pdsch_dl_ch_estimates_ext);
|
|
free(UE->phy_sim_dlsch_b);
|
|
|
|
free_nrLDPC_coding_interface(&gNB->nrLDPC_coding_interface);
|
|
|
|
if (output_fd)
|
|
fclose(output_fd);
|
|
|
|
if (input_fd)
|
|
fclose(input_fd);
|
|
|
|
// closing csv file
|
|
if (filename_csv != NULL) { // means we are asked to print stats to CSV
|
|
fclose(csv_file);
|
|
free(filename_csv);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void update_ptrs_config(NR_CellGroupConfig_t *secondaryCellGroup, uint16_t *rbSize, uint8_t *mcsIndex, int8_t *ptrs_arg)
|
|
{
|
|
NR_BWP_Downlink_t *bwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[0];
|
|
long ptrsFreqDenst[] = {25, 115};
|
|
long ptrsTimeDenst[] = {2, 4, 10};
|
|
|
|
long epre_Ratio = 0;
|
|
long reOffset = 0;
|
|
|
|
if(ptrs_arg[0] ==0) {
|
|
ptrsTimeDenst[2]= *mcsIndex -1;
|
|
}
|
|
else if(ptrs_arg[0] == 1) {
|
|
ptrsTimeDenst[1]= *mcsIndex - 1;
|
|
ptrsTimeDenst[2]= *mcsIndex + 1;
|
|
}
|
|
else if(ptrs_arg[0] ==2) {
|
|
ptrsTimeDenst[0]= *mcsIndex - 1;
|
|
ptrsTimeDenst[1]= *mcsIndex + 1;
|
|
}
|
|
else {
|
|
printf("[DLSIM] Wrong L_PTRS value, using default values 1\n");
|
|
}
|
|
/* L = 4 if Imcs < MCS4 */
|
|
if(ptrs_arg[1] ==2) {
|
|
ptrsFreqDenst[0]= *rbSize - 1;
|
|
ptrsFreqDenst[1]= *rbSize + 1;
|
|
}
|
|
else if(ptrs_arg[1] == 4) {
|
|
ptrsFreqDenst[1]= *rbSize - 1;
|
|
}
|
|
else {
|
|
printf("[DLSIM] Wrong K_PTRS value, using default values 2\n");
|
|
}
|
|
printf("[DLSIM] PTRS Enabled with L %d, K %d \n", 1<<ptrs_arg[0], ptrs_arg[1] );
|
|
/* overwrite the values */
|
|
rrc_config_dl_ptrs_params(bwp, ptrsFreqDenst, ptrsTimeDenst, &epre_Ratio, &reOffset);
|
|
}
|
|
|
|
void update_dmrs_config(NR_CellGroupConfig_t *scg, int8_t* dmrs_arg)
|
|
{
|
|
int8_t mapping_type = typeA;//default value
|
|
int8_t add_pos = pdsch_dmrs_pos0;//default value
|
|
int8_t dmrs_config_type = NFAPI_NR_DMRS_TYPE1;//default value
|
|
|
|
if(dmrs_arg[0] == 0) {
|
|
mapping_type = typeA;
|
|
}
|
|
else if (dmrs_arg[0] == 1) {
|
|
mapping_type = typeB;
|
|
} else {
|
|
AssertFatal(1==0,"Incorrect Mappingtype, valid options 0-typeA, 1-typeB\n");
|
|
}
|
|
|
|
/* Additional DMRS positions 0 ,1 ,2 and 3 */
|
|
if (dmrs_arg[1] >= 0 && dmrs_arg[1] < 4) {
|
|
add_pos = dmrs_arg[1];
|
|
} else {
|
|
AssertFatal(1==0,"Incorrect Additional Position, valid options 0-pos1, 1-pos1, 2-pos2, 3-pos3\n");
|
|
}
|
|
|
|
/* DMRS Conf Type 1 or 2 */
|
|
if(dmrs_arg[2] == 1) {
|
|
dmrs_config_type = NFAPI_NR_DMRS_TYPE1;
|
|
} else if(dmrs_arg[2] == 2) {
|
|
dmrs_config_type = NFAPI_NR_DMRS_TYPE2;
|
|
}
|
|
|
|
NR_BWP_Downlink_t *bwp = scg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[0];
|
|
|
|
AssertFatal((bwp->bwp_Dedicated->pdsch_Config != NULL && bwp->bwp_Dedicated->pdsch_Config->choice.setup != NULL), "Base RRC reconfig structures are not allocated.\n");
|
|
|
|
if(mapping_type == typeA) {
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA));
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup));
|
|
if (dmrs_config_type == NFAPI_NR_DMRS_TYPE2)
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type));
|
|
else
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type = NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NULL;
|
|
printf("DLSIM: Allocated Mapping TypeA in RRC reconfig message\n");
|
|
}
|
|
|
|
if(mapping_type == typeB) {
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB));
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup));
|
|
if (dmrs_config_type == NFAPI_NR_DMRS_TYPE2)
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_Type = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_Type));
|
|
else
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_Type = NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->maxLength=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->scramblingID0=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->scramblingID1=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->phaseTrackingRS=NULL;
|
|
bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_AdditionalPosition = NULL;
|
|
printf("DLSIM: Allocated Mapping TypeB in RRC reconfig message\n");
|
|
}
|
|
|
|
struct NR_SetupRelease_DMRS_DownlinkConfig *dmrs_MappingtypeA = bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA;
|
|
struct NR_SetupRelease_DMRS_DownlinkConfig *dmrs_MappingtypeB = bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeB;
|
|
|
|
|
|
NR_DMRS_DownlinkConfig_t *dmrs_config = (mapping_type == typeA) ? dmrs_MappingtypeA->choice.setup : dmrs_MappingtypeB->choice.setup;
|
|
|
|
/* Ranks 5--8 need the two-symbol Type-1 DMRS that provides ports 1000--1007.
|
|
* Keep that mandatory maxLength setting when -U changes only the additional
|
|
* DMRS position; otherwise the high-rank scheduler correctly rejects the
|
|
* inconsistent single-symbol configuration. */
|
|
if (g_nrOfLayers > 4)
|
|
asn1cCallocOne(dmrs_config->maxLength, NR_DMRS_DownlinkConfig__maxLength_len2);
|
|
|
|
if (add_pos != 2) { // pos0,pos1,pos3
|
|
if (dmrs_config->dmrs_AdditionalPosition == NULL) {
|
|
dmrs_config->dmrs_AdditionalPosition = calloc(1,sizeof(*dmrs_MappingtypeA->choice.setup->dmrs_AdditionalPosition));
|
|
}
|
|
switch (add_pos) {
|
|
case 0:
|
|
*dmrs_config->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0;
|
|
break;
|
|
case 1:
|
|
*dmrs_config->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos1;
|
|
break;
|
|
case 3:
|
|
*dmrs_config->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos3;
|
|
break;
|
|
default:
|
|
AssertFatal(false, "DMRS additional position %d not valid\n", add_pos);
|
|
}
|
|
} else { // if NULL, Value pos2
|
|
free(dmrs_config->dmrs_AdditionalPosition);
|
|
dmrs_config->dmrs_AdditionalPosition = NULL;
|
|
}
|
|
|
|
for (int i=0;i<bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) {
|
|
bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType = mapping_type;
|
|
}
|
|
|
|
printf("[DLSIM] DMRS Config is modified with Mapping Type %d, Additional Positions %d Config. Type %d \n", mapping_type, add_pos, dmrs_config_type);
|
|
}
|