Commit ef27c8ce authored by Raymond Knopp's avatar Raymond Knopp

initial prach implementation. Splitting of components for RU (FFTs) and L1...

initial prach implementation. Splitting of components for RU (FFTs) and L1 (detection). Update of UE to include all formats (for 30 kHz SCS case on initial BWP)
parent a66c297a
......@@ -1269,6 +1269,10 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/INIT/lte_init_ue.c
)
set(PHY_NR_SRC_COMMON
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach_common.c
)
set(PHY_NR_SRC
${OPENAIR1_DIR}/PHY/INIT/nr_init.c
${OPENAIR1_DIR}/PHY/INIT/nr_parms.c
......@@ -1283,6 +1287,7 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_decoding.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_tbs_tools.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach.c
${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_gold.c
${OPENAIR1_DIR}/PHY/TOOLS/file_output.c
${OPENAIR1_DIR}/PHY/TOOLS/cadd_vv.c
......@@ -1315,6 +1320,7 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_llr_computation.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_tbs_tools.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach_common.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_prach.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/srs_modulation_nr.c
......@@ -1326,7 +1332,7 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_gold_ue.c
${OPENAIR1_DIR}/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c
${OPENAIR1_DIR}/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c
${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_ue_measurements.c
${OPENAIR1_DIR}/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
${OPENAIR1_DIR}/PHY/TOOLS/file_output.c
${OPENAIR1_DIR}/PHY/TOOLS/cadd_vv.c
# ${OPENAIR1_DIR}/PHY/TOOLS/lte_dfts.c
......@@ -1366,6 +1372,7 @@ add_library(PHY ${PHY_SRC})
add_dependencies(PHY rrc_flag)
add_library(PHY_UE ${PHY_SRC_UE})
add_dependencies(PHY_UE rrc_flag)
add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON})
add_library(PHY_NR ${PHY_NR_SRC})
add_library(PHY_NR_UE ${PHY_NR_UE_SRC})
add_library(PHY_RU ${PHY_SRC_RU})
......@@ -2366,7 +2373,7 @@ add_executable(nr-softmodem
target_link_libraries (nr-softmodem
-Wl,--start-group
UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_RU LFDS GTPV1U SECU_CN SECU_OSA
UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS GTPV1U SECU_CN SECU_OSA
${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB}
RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB L2 L2_NR MAC_NR_COMMON
NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB
......@@ -2403,7 +2410,7 @@ add_executable(nr-softmodem-nos1
target_link_libraries (nr-softmodem-nos1
-Wl,--start-group
UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_RU LFDS GTPV1U SECU_CN SECU_OSA
UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS GTPV1U SECU_CN SECU_OSA
${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${MIH_LIB}
RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB L2 L2_NR MAC_NR_COMMON
NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB
......@@ -2438,7 +2445,7 @@ add_executable(nr-uesoftmodem
target_link_libraries (nr-uesoftmodem
-Wl,--start-group
RRC_LIB NR_RRC_LIB SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB PHY_COMMON PHY_NR_UE PHY_RU LFDS NR_L2_UE MAC_NR_COMMON
RRC_LIB NR_RRC_LIB SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB PHY_COMMON PHY_NR_COMMON PHY_NR_UE PHY_RU LFDS NR_L2_UE MAC_NR_COMMON
${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES}
-Wl,--end-group z dl)
......@@ -2469,7 +2476,7 @@ add_executable(nr-uesoftmodem-nos1
target_link_libraries (nr-uesoftmodem-nos1
-Wl,--start-group
RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB PHY_COMMON PHY_NR_UE PHY_UE PHY_RU LFDS NR_L2_UE MAC_NR_COMMON
RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB PHY_COMMON PHY_NR_COMMON PHY_NR_UE PHY_UE PHY_RU LFDS NR_L2_UE MAC_NR_COMMON
${MSC_LIB} ${RAL_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES}
-Wl,--end-group z dl)
......@@ -2524,13 +2531,13 @@ target_link_libraries (dlsim_tm4
add_executable(polartest
${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/polartest.c
${OPENAIR_DIR}/common/utils/backtrace.c)
target_link_libraries(polartest SIMU PHY PHY_NR PHY_COMMON m ${ATLAS_LIBRARIES})
target_link_libraries(polartest SIMU PHY PHY_NR PHY_COMMON PHY_NR_COMMON m ${ATLAS_LIBRARIES})
add_executable(smallblocktest
${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/smallblocktest.c
${OPENAIR_DIR}/common/utils/backtrace.c)
target_link_libraries(smallblocktest SIMU PHY PHY_NR PHY_COMMON m ${ATLAS_LIBRARIES})
target_link_libraries(smallblocktest SIMU PHY PHY_NR PHY_COMMON PHY_NR_COMMON m ${ATLAS_LIBRARIES})
add_executable(ldpctest
${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c
......@@ -2542,27 +2549,34 @@ add_executable(nr_dlschsim
${OPENAIR_DIR}/common/utils/backtrace.c
${OPENAIR_DIR}/common/utils/system.c
${T_SOURCE})
target_link_libraries(nr_dlschsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
target_link_libraries(nr_dlschsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
add_executable(nr_pbchsim
${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c
${OPENAIR_DIR}/common/utils/backtrace.c
${OPENAIR_DIR}/common/utils/system.c
${T_SOURCE})
target_link_libraries(nr_pbchsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
target_link_libraries(nr_pbchsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
add_executable(nr_dlsim
${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlsim.c
${OPENAIR_DIR}/common/utils/backtrace.c
${OPENAIR_DIR}/common/utils/system.c
${T_SOURCE})
target_link_libraries(nr_dlsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_NR MAC_UE_NR MAC_NR_COMMON RRC_LIB NR_RRC_LIB CONFIG_LIB L2_NR -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
target_link_libraries(nr_dlsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_NR MAC_UE_NR MAC_NR_COMMON RRC_LIB NR_RRC_LIB CONFIG_LIB L2_NR -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
add_executable(nr_prachsim
${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c
${OPENAIR_DIR}/common/utils/backtrace.c
${OPENAIR_DIR}/common/utils/system.c
${T_SOURCE})
target_link_libraries(nr_prachsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_RU PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
add_executable(nr_ulschsim
${OPENAIR1_DIR}/SIMULATION/NR_PHY/ulschsim.c
${OPENAIR_DIR}/common/utils/backtrace.c
${T_SOURCE})
target_link_libraries(nr_ulschsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
target_link_libraries(nr_ulschsim -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} dl)
foreach(myExe dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim pdcchsim pucchsim prachsim syncsim)
......@@ -2577,7 +2591,7 @@ foreach(myExe dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim pdcchsim pucchsim pr
${SHLIB_LOADER_SOURCES}
)
target_link_libraries (${myExe}
-Wl,--start-group SIMU UTIL SCHED_LIB SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY PHY_UE PHY_RU LFDS ${ITTI_LIB} LFDS7 -Wl,--end-group
-Wl,--start-group SIMU UTIL SCHED_LIB SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_NR_COMMON PHY PHY_UE PHY_RU LFDS ${ITTI_LIB} LFDS7 -Wl,--end-group
pthread m rt ${CONFIG_LIBRARIES} ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${T_LIB} dl
)
endforeach(myExe)
......@@ -2612,7 +2626,7 @@ add_executable(test_epc_play_scenario
)
target_include_directories(test_epc_play_scenario PUBLIC /usr/local/share/asn1c)
target_link_libraries (test_epc_play_scenario
-Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB X2AP_ENB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
-Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB X2AP_ENB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY_NR_COMMON PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
)
......@@ -2688,6 +2702,7 @@ NFAPI_USER_LIB
PHY_COMMON
PHY
PHY_UE
PHY_NR_COMMON
PHY_NR
PHY_NR_UE
PHY_RU
......
......@@ -528,7 +528,7 @@ typedef struct{
uint16_t srs_monitoring_periodicity;
uint16_t slot_monitoring_periodicity;
uint16_t slot_monitoring_offset;
uint16_t monitoring_symbols_in_slot;
uint32_t monitoring_symbols_in_slot;
uint16_t number_of_candidates[NFAPI_NR_MAX_NB_CCE_AGGREGATION_LEVELS];
} nfapi_nr_search_space_t;
......
......@@ -83,7 +83,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
NR_gNB_COMMON *const common_vars = &gNB->common_vars;
LTE_eNB_PUSCH **const pusch_vars = gNB->pusch_vars;
LTE_eNB_SRS *const srs_vars = gNB->srs_vars;
LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;
NR_gNB_PRACH *const prach_vars = &gNB->prach_vars;
int i, UE_id;
......@@ -204,10 +204,8 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
// PRACH
prach_vars->prachF = (int16_t *)malloc16_clear( 1024*2*sizeof(int16_t) );
// assume maximum of 64 RX antennas for PRACH receiver
prach_vars->prach_ifft[0] = (int32_t **)malloc16_clear(64*sizeof(int32_t *));
for (i=0; i<64; i++) prach_vars->prach_ifft[0][i] = (int32_t *)malloc16_clear(1024*2*sizeof(int32_t));
prach_vars->prach_ifft = (int32_t *)malloc16_clear(1024*2*sizeof(int32_t));
prach_vars->rxsigF[0] = (int16_t **)malloc16_clear(64*sizeof(int16_t *));
......
......@@ -985,7 +985,6 @@ void set_default_frame_parms_single(nfapi_nr_config_request_t *config, NR_DL_FRA
config->sch_config.physical_cell_id.value = 0;
frame_parms->frame_type = FDD;
frame_parms->tdd_config = 3;
//frame_parms[CC_id]->tdd_config_S = 0;
frame_parms->N_RB_DL = 100;
frame_parms->N_RB_UL = 100;
......
......@@ -30,106 +30,43 @@
* \warning
*/
void rx_nr_prach(PHY_VARS_gNB *gNB,
RU_t *ru,
uint16_t *max_preamble,
uint16_t *max_preamble_energy,
uint16_t *max_preamble_delay,
uint16_t Nf,
uint8_t tdd_mapindex
)
{
#include "PHY/defs_gNB.h"
#include "PHY/NR_TRANSPORT/nr_transport.h"
extern uint16_t NCS_unrestricted_delta_f_RA_125[16];
extern uint16_t NCS_restricted_TypeA_delta_f_RA_125[15];
extern uint16_t NCS_restricted_TypeB_delta_f_RA_125[13];
extern uint16_t NCS_unrestricted_delta_f_RA_5[16];
extern uint16_t NCS_restricted_TypeA_delta_f_RA_5[16];
extern uint16_t NCS_restricted_TypeB_delta_f_RA_5[14];
extern uint16_t NCS_unrestricted_delta_f_RA_15[16];
extern uint16_t prach_root_sequence_map_0_3[838];
extern uint16_t prach_root_sequence_map_abc[138];
extern int64_t table_6_3_3_2_2_prachConfig_Index [256][9];
extern int64_t table_6_3_3_2_3_prachConfig_Index [256][9];
extern int64_t table_6_3_3_2_4_prachConfig_Index [256][10];
extern uint16_t nr_du[838];
extern int16_t nr_ru[2*839];
void rx_nr_prach_ru(RU_t *ru,
int frame,
int subframe) {
int i;
AssertFatal(ru!=NULL,"ru is null\n");
NR_DL_FRAME_PARMS *fp;
lte_frame_type_t frame_type;
uint16_t rootSequenceIndex;
uint8_t prach_ConfigIndex;
uint8_t Ncs_config;
uint8_t restricted_set;
uint8_t n_ra_prb;
int subframe;
int16_t *prachF=NULL;
int16_t **rxsigF=NULL;
int nb_rx;
int16_t *prach2;
uint8_t preamble_index;
uint16_t NCS,NCS2;
uint16_t preamble_offset=0,preamble_offset_old;
int16_t preamble_shift=0;
uint32_t preamble_shift2;
uint16_t preamble_index0=0,n_shift_ra=0,n_shift_ra_bar;
uint16_t d_start=0;
uint16_t numshift=0;
uint16_t *prach_root_sequence_map;
uint8_t not_found;
int k=0;
uint16_t u;
int16_t *Xu=0;
uint16_t offset;
int16_t Ncp;
uint16_t first_nonzero_root_idx=0;
uint8_t new_dft=0;
uint8_t aa;
int32_t lev;
int16_t levdB;
int fft_size,log2_ifft_size;
int16_t prach_ifft_tmp[2048*2] __attribute__((aligned(32)));
int32_t *prach_ifft=(int32_t*)NULL;
int32_t **prach_ifftp=(int32_t **)NULL;
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
int prach_ifft_cnt=0;
#endif
if (ru) {
fp = ru->frame_parms;
nb_rx = ru->nb_rx;
}
else if (gNB) {
fp = &gNB->frame_parms;
nb_rx = fp->nb_antennas_rx;
}
else AssertFatal(1==0,"rx_prach called without valid RU or gNB descriptor\n");
frame_type = fp->frame_type;
rootSequenceIndex = fp->prach_config_common.rootSequenceIndex;
prach_ConfigIndex = fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
Ncs_config = fp->prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig;
restricted_set = fp->prach_config_common.prach_ConfigInfo.highSpeedFlag;
n_ra_prb = get_prach_prb_offset(fp,prach_ConfigIndex,
fp->prach_config_common.prach_ConfigInfo.prach_FreqOffset,
tdd_mapindex,Nf);
NR_DL_FRAME_PARMS *fp=ru->nr_frame_parms;
int16_t prach_ConfigIndex = fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
int16_t *prach[ru->nb_rx];
uint8_t prach_fmt = get_nr_prach_fmt(prach_ConfigIndex,fp->frame_type,fp->freq_range);
int16_t *prach[nb_rx];
uint8_t prach_fmt = get_prach_fmt(prach_ConfigIndex,frame_type);
uint16_t N_ZC = (prach_fmt <4)?839:139;
if (gNB) {
prach_ifftp = gNB->prach_vars.prach_ifft[0];
subframe = gNB->proc.subframe_prach;
prachF = gNB->prach_vars.prachF;
rxsigF = gNB->prach_vars.rxsigF[0];
if (LOG_DEBUGFLAG(PRACH)){
if (((ru->proc.frame_prach)&1023) < 20) LOG_I(PHY,"PRACH (gNB) : running rx_prach for subframe %d, prach_FreqOffset %d, prach_ConfigIndex %d , rootSequenceIndex %d\n", subframe,fp->prach_config_common.prach_ConfigInfo.prach_FreqOffset,prach_ConfigIndex,rootSequenceIndex);
}
}
else {
subframe = ru->proc.subframe_prach;
rxsigF = ru->prach_rxsigF;
if (LOG_DEBUGFLAG(PRACH)){
if (((ru->proc.frame_prach)&1023) < 20) LOG_I(PHY,"PRACH (RU) : running rx_prach for subframe %d, prach_FreqOffset %d, prach_ConfigIndex %d\n",
subframe,fp->prach_config_common.prach_ConfigInfo.prach_FreqOffset,prach_ConfigIndex);
}
if ((frame&1023) < 20) LOG_I(PHY,"PRACH (RU) : running rx_prach for subframe %d, msg1_frequencystart %d, prach_ConfigIndex %d\n",
subframe,fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart,prach_ConfigIndex);
}
AssertFatal(ru!=NULL,"ru is null\n");
for (aa=0; aa<nb_rx; aa++) {
for (int aa=0; aa<ru->nb_rx; aa++) {
if (ru->if_south == LOCAL_RF) { // set the time-domain signal if we have to use it in this node
// DJP - indexing below in subframe zero takes us off the beginning of the array???
prach[aa] = (int16_t*)&ru->common.rxdata[aa][(subframe*fp->samples_per_tti)-ru->N_TA_offset];
......@@ -147,8 +84,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
if (dB_fixed(en0)>32) {
sprintf(buffer, "rach_dBm:%d",rach_dBm);
if (prach[0]!= NULL) LOG_M("prach_rx","prach_rx",prach[0],fp->samples_per_tti,1,1);
LOG_I(PHY,"RU %d, br_flag %d ce_level %d frame %d subframe %d per_tti:%d prach:%p (energy %d) TA:%d %s rxdata:%p index:%d\n",
ru->idx,br_flag,ce_level,ru->proc.frame_prach,subframe,fp->samples_per_tti,
LOG_I(PHY,"RU %d, frame %d subframe %d per_tti:%d prach:%p (energy %d) TA:%d %s rxdata:%p index:%d\n",
ru->idx,frame,subframe,fp->samples_per_tti,
prach[aa],dbEn0,ru->N_TA_offset,buffer,ru->common.rxdata[aa],
(subframe*fp->samples_per_tti)-ru->N_TA_offset);
}
......@@ -156,28 +93,9 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
}
}
// First compute physical root sequence
if (restricted_set == 0) {
AssertFatal(Ncs_config<=15,
"Illegal Ncs_config for unrestricted format %d\n",Ncs_config);
NCS = NCS_unrestricted[Ncs_config];
} else {
AssertFatal(Ncs_config<=14,
"FATAL, Illegal Ncs_config for restricted format %d\n",Ncs_config);
NCS = NCS_restricted[Ncs_config];
}
if (gNB) start_meas(&gNB->rx_prach);
prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4;
// PDP is oversampled, e.g. 1024 sample instead of 839
// Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839
NCS2 = (N_ZC==839) ? ((NCS<<10)/839) : ((NCS<<8)/139);
if (NCS2==0)
NCS2 = N_ZC;
int mu = fp->numerology_index;
int Ncp;
int16_t *prach2;
switch (prach_fmt) {
case 0:
......@@ -233,10 +151,16 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
break;
default:
Ncp = 3168;
AssertFatal(1==0,"unknown prach format %x\n",prach_fmt);
break;
}
// Do forward transform
if (LOG_DEBUGFLAG(PRACH)) {
LOG_D(PHY,"rx_prach: Doing FFT for N_RB_UL %d nb_rx:%d Ncp:%d\n",fp->N_RB_UL, ru->nb_rx, Ncp);
}
AssertFatal(mu==1,"only 30 kHz SCS handled for now\n");
// Note: Assumes PUSCH SCS @ 30 kHz, take values for formats 0-2 and adjust for others below
int kbar = 1;
int K = 24;
......@@ -249,41 +173,33 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
K=1;
kbar=2;
}
if (((gNB!=NULL) && (ru->function != NGFI_RAU_IF4p5))||
((gNB==NULL) && (ru->function == NGFI_RRU_IF4p5))) { // compute the DFTs of the PRACH temporal resources
// Do forward transform
if (LOG_DEBUGFLAG(PRACH)) {
LOG_D(PHY,"rx_prach: Doing FFT for N_RB_UL %d nb_rx:%d Ncp:%d\n",fp->N_RB_UL, nb_rx, Ncp);
}
for (aa=0; aa<nb_rx; aa++) {
int n_ra_prb = fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart;
int k = (12*n_ra_prb) - 6*fp->N_RB_UL;
for (int aa=0; aa<ru->nb_rx; aa++) {
AssertFatal(prach[aa]!=NULL,"prach[%d] is null\n",aa);
prach2 = prach[aa] + (Ncp<<1);
// do DFT
switch (fp->N_RB_UL) {
case 6:
case 15:
case 25:
case 50:
case 75:
case 100:
if (fp->N_RB_UL <= 100)
AssertFatal(1==0,"N_RB_UL %d not support for NR PRACH yet\n",fp->N_RB_UL);
break;
case 106:
case 136:
else if (fp->N_RB_UL < 137) {
if (fp->threequarter_fs==0) {
//40 MHz @ 61.44 Ms/s
//50 MHz @ 61.44 Ms/s
prach2 = prach[aa] + (Ncp<<2);
if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2)
dft24576(prach2,rxsigF[aa],1);
dft49152(prach2,rxsigF[aa],1);
if (prach_fmt == 1 || prach_fmt == 2)
dft24576(prach2+49152,rxsigF[aa]+49152,1);
dft49152(prach2+98304,rxsigF[aa]+98304,1);
if (prach_fmt == 2) {
dft24576(prach2+(49152*2),rxsigF[aa]+(49152*2),1);
dft24576(prach2+(49152*3),rxsigF[aa]+(49152*3),1);
dft49152(prach2+(98304*2),rxsigF[aa]+(98304*2),1);
dft49152(prach2+(98304*3),rxsigF[aa]+(98304*3),1);
}
if (prach_fmt == 3)
for (int i=0;i<4;i++) dft6144(prach2+(i*12288),rxsigF[aa]+(i*12288),1);
for (int i=0;i<4;i++) dft12288(prach2+(i*12288*2),rxsigF[aa]+(i*12288*2),1);
if (prach_fmt >3) {
dft2048(prach2,rxsigF[aa],1);
if (prach_fmt != 0xc0) dft2048(prach2+4096,rxsigF[aa]+4096,1);
......@@ -300,13 +216,14 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
for (int i=6;i<11;i++) dft2048(prach2+(3072*i),rxsigF[aa]+(3072*i),1);
} else {
// 40 MHz @ 46.08 Ms/s
AssertFatal(fp->N_RB_UL == 106,"cannot do 136 PRBs with 3/4 sampling\n");
prach2 = prach[aa] + (3*Ncp);
AssertFatal(fp->N_RB_UL <= 107,"cannot do 108..136 PRBs with 3/4 sampling\n");
if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2)
dft18432(prach2,rxsigF[aa],1);
dft36864(prach2,rxsigF[aa],1);
if (prach_fmt == 1 || prach_fmt == 2)
dft18432(prach2+36864,rxsigF[aa]+36864,1);
dft36864(prach2+73728,rxsigF[aa]+73728,1);
if (prach_fmt == 3)
dft18432(prach2+(2*36864),rxsigF[aa]+(2*36864),1);
for (int i=0;i<4;i++) dft9216(prach2+(i*9216*2),rxsigF[aa]+(i*9216*2),1);
if (prach_fmt >3) {
dft1536(prach2,rxsigF[aa],1);
if (prach_fmt != 0xc0) dft1536(prach2+3072,rxsigF[aa]+3072,1);
......@@ -315,25 +232,23 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
dft1536(prach2+3072*2,rxsigF[aa]+3072*2,1);
dft1536(prach2+3072*3,rxsigF[aa]+3072*3,1);
}
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == c2) {
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == 0xc2) {
dft1536(prach2+3072*4,rxsigF[aa]+3072*4,1);
dft1536(prach2+3072*5,rxsigF[aa]+3072*5,1);
}
if (prach_fmt == 0xc2)
for (int i=6;i<11;i++) dft1536(prach2+(3072*i),rxsigF[aa]+(3072*i),1);
}
break;
case 216:
case 246:
case 272:
}
else if (fp->N_RB_UL <= 273) {
if (fp->threequarter_fs==0) {
//80,90,100 MHz @ 61.44 Ms/s
if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2)
dft49152(prach2,rxsigF[aa],1);
dft98304(prach2,rxsigF[aa],1);
if (prach_fmt == 1 || prach_fmt == 2)
dft49152(prach2+98304,rxsigF[aa]+98304,1);
dft98304(prach2+196608,rxsigF[aa]+196608,1);
if (prach_fmt == 3)
dft49152(prach2+(2*98304),rxsigF[aa]+(2*98304),1);
dft98304(prach2+(2*196608),rxsigF[aa]+(2*196608),1);
if (prach_fmt >3) {
dft4096(prach2,rxsigF[aa],1);
......@@ -343,22 +258,21 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
dft4096(prach2+8192*2,rxsigF[aa]+8192*2,1);
dft4096(prach2+8192*3,rxsigF[aa]+8192*3,1);
}
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == c2) {
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == 0xc2) {
dft4096(prach2+8192*4,rxsigF[aa]+8192*4,1);
dft4096(prach2+8192*5,rxsigF[aa]+8192*5,1);
}
if (prach_fmt == 0xc2)
for (int i=6;i<11;i++) dft4096(prach2+(8192*i),rxsigF[aa]+(8192*i),1);
} else {
AssertFatal(fp->N_RB_UL == 216,"cannot do 136 PRBs with 3/4 sampling\n");
AssertFatal(fp->N_RB_UL <= 217,"cannot do more than 217 PRBs with 3/4 sampling\n");
// 80 MHz @ 46.08 Ms/s
AssertFatal(fp->N_RB_UL == 136,"cannot do 136 PRBs with 3/4 sampling\n");
if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2)
dft36864(prach2,rxsigF[aa],1);
dft73728(prach2,rxsigF[aa],1);
if (prach_fmt == 1 || prach_fmt == 2)
dft36864(prach2+73728,rxsigF[aa]+73728,1);
dft73728(prach2+(2*73728),rxsigF[aa]+(2*73728),1);
if (prach_fmt == 3)
dft36864(prach2+(2*73728),rxsigF[aa]+(2*73728),1);
dft73728(prach2+(4*73728),rxsigF[aa]+(4*73728),1);
if (prach_fmt >3) {
dft3072(prach2,rxsigF[aa],1);
......@@ -368,21 +282,16 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
dft3072(prach2+6144*2,rxsigF[aa]+6144*2,1);
dft3072(prach2+6144*3,rxsigF[aa]+6144*3,1);
}
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == c2) {
if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == 0xc2) {
dft3072(prach2+6144*4,rxsigF[aa]+6144*4,1);
dft3072(prach2+6144*5,rxsigF[aa]+6144*5,1);
}
if (prach_fmt == 0xc2)
for (int i=6;i<11;i++) dft3072(prach2+(6144*i),rxsigF[aa]+(6144*i),1);
}
break;
}
k = (12*n_ra_prb) - 6*fp->N_RB_UL;
if (k<0) {
k+=(fp->ofdm_symbol_size);
}
if (k<0) k+=(fp->ofdm_symbol_size);
k*=K;
k+=kbar;
......@@ -399,36 +308,124 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
}
}
void rx_nr_prach(PHY_VARS_gNB *gNB,
int frame,
int subframe,
uint16_t *max_preamble,
uint16_t *max_preamble_energy,
uint16_t *max_preamble_delay
)
{
int i;
NR_DL_FRAME_PARMS *fp;
lte_frame_type_t frame_type;
uint16_t rootSequenceIndex;
uint8_t prach_ConfigIndex;
uint8_t Ncs_config;
uint8_t restricted_set;
uint8_t n_ra_prb;
int16_t *prachF=NULL;
int nb_rx;
int16_t **rxsigF = gNB->prach_vars.rxsigF;
uint8_t preamble_index;
uint16_t NCS,NCS2;
uint16_t preamble_offset=0,preamble_offset_old;
int16_t preamble_shift=0;
uint32_t preamble_shift2;
uint16_t preamble_index0=0,n_shift_ra=0,n_shift_ra_bar;
uint16_t d_start=0;
uint16_t numshift=0;
uint16_t *prach_root_sequence_map;
uint8_t not_found;
int k=0;
uint16_t u;
int16_t *Xu=0;
uint16_t offset;
int16_t Ncp;
uint16_t first_nonzero_root_idx=0;
uint8_t new_dft=0;
uint8_t aa;
int32_t lev;
int16_t levdB;
int fft_size,log2_ifft_size;
int16_t prach_ifft_tmp[2048*2] __attribute__((aligned(32)));
int32_t *prach_ifft=(int32_t*)NULL;
nr_frequency_range_e freq_range;
AssertFatal(gNB!=NULL,"Can only be called from gNB\n");
fp = &gNB->frame_parms;
nb_rx = fp->nb_antennas_rx;
frame_type = fp->frame_type;
freq_range = fp->freq_range;
rootSequenceIndex = fp->prach_config_common.rootSequenceIndex;
prach_ConfigIndex = fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
Ncs_config = fp->prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig;
restricted_set = fp->prach_config_common.prach_ConfigInfo.highSpeedFlag;
uint8_t prach_fmt = get_nr_prach_fmt(prach_ConfigIndex,frame_type,freq_range);
uint16_t N_ZC = (prach_fmt <4)?839:139;
AssertFatal(gNB!=NULL,"gNB is null\n");
prach_ifft = gNB->prach_vars.prach_ifft;
prachF = gNB->prach_vars.prachF;
if (LOG_DEBUGFLAG(PRACH)){
if ((frame&1023) < 20) LOG_I(PHY,"PRACH (gNB) : running rx_prach for subframe %d, msg1_frequencystart %d, prach_ConfigIndex %d , rootSequenceIndex %d\n", subframe,fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart,prach_ConfigIndex,rootSequenceIndex);
}
if ((eNB==NULL) && (ru!=NULL) && ru->function == NGFI_RRU_IF4p5) {
/// **** send_IF4 of rxsigF to RAU **** ///
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
if (br_flag == 1) send_IF4p5(ru, ru->proc.frame_prach, ru->proc.subframe_prach, IF4p5_PRACH+1+ce_level);
else
#endif
send_IF4p5(ru, ru->proc.frame_prach, ru->proc.subframe_prach, IF4p5_PRACH);
return;
} else if (eNB!=NULL) {
if ( LOG_DEBUGFLAG(PRACH)) {
int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840));
if ((en > 60)&&(br_flag==1)) LOG_I(PHY,"PRACH (br_flag %d,ce_level %d, n_ra_prb %d, k %d): Frame %d, Subframe %d => %d dB\n",br_flag,ce_level,n_ra_prb,k,eNB->proc.frame_rx,eNB->proc.subframe_rx,en);
int restricted_Type = 0; //this is hardcoded ('0' for restricted_TypeA; and '1' for restricted_TypeB). FIXME
if (prach_fmt<3){
if (restricted_set == 0) {
NCS = NCS_unrestricted_delta_f_RA_125[Ncs_config];
} else {
if (restricted_Type == 0) NCS = NCS_restricted_TypeA_delta_f_RA_125[Ncs_config]; // for TypeA, this is hardcoded. FIXME
if (restricted_Type == 1) NCS = NCS_restricted_TypeB_delta_f_RA_125[Ncs_config]; // for TypeB, this is hardcoded. FIXME
}
}
if (prach_fmt==3){
if (restricted_set == 0) {
NCS = NCS_unrestricted_delta_f_RA_5[Ncs_config];
} else {
if (restricted_Type == 0) NCS = NCS_restricted_TypeA_delta_f_RA_5[Ncs_config]; // for TypeA, this is hardcoded. FIXME
if (restricted_Type == 1) NCS = NCS_restricted_TypeB_delta_f_RA_5[Ncs_config]; // for TypeB, this is hardcoded. FIXME
}
}
if (prach_fmt>3){
NCS = NCS_unrestricted_delta_f_RA_15[Ncs_config];
}
// in case of RAU and prach received rx_thread wakes up prach
if (gNB) start_meas(&gNB->rx_prach);
prach_root_sequence_map = (prach_fmt<4) ? prach_root_sequence_map_0_3 : prach_root_sequence_map_abc;
// PDP is oversampled, e.g. 1024 sample instead of 839
// Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839
NCS2 = (N_ZC==839) ? ((NCS<<10)/839) : ((NCS<<8)/139);
if (NCS2==0)
NCS2 = N_ZC;
// here onwards is for eNodeB_3GPP or NGFI_RAU_IF4p5
preamble_offset_old = 99;
uint8_t update_TA = 4;
uint8_t update_TA2 = 1;
switch (eNB->frame_parms.N_RB_DL) {
switch (gNB->frame_parms.N_RB_DL) {
case 6:
update_TA = 16;
break;
......@@ -454,7 +451,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
if (LOG_DEBUGFLAG(PRACH)){
int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840));
if (en>60) LOG_I(PHY,"frame %d, subframe %d : Trying preamble %d (br_flag %d)\n",ru->proc.frame_prach,subframe,preamble_index,br_flag);
if (en>60) LOG_I(PHY,"frame %d, subframe %d : Trying preamble %d \n",frame,subframe,preamble_index);
}
if (restricted_set == 0) {
// This is the relative offset in the root sequence table (5.7.2-4 from 36.211) for the given preamble index
......@@ -491,26 +488,25 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
if (prach_fmt<4) {
// prach_root_sequence_map points to prach_root_sequence_map0_3
DevAssert( index < sizeof(prach_root_sequence_map0_3) / sizeof(prach_root_sequence_map0_3[0]) );
DevAssert( index < sizeof(prach_root_sequence_map_0_3) / sizeof(prach_root_sequence_map_0_3[0]) );
} else {
// prach_root_sequence_map points to prach_root_sequence_map4
DevAssert( index < sizeof(prach_root_sequence_map4) / sizeof(prach_root_sequence_map4[0]) );
DevAssert( index < sizeof(prach_root_sequence_map_abc) / sizeof(prach_root_sequence_map_abc[0]) );
}
u = prach_root_sequence_map[index];
uint16_t n_group_ra = 0;
if ( (du[u]<(N_ZC/3)) && (du[u]>=NCS) ) {
n_shift_ra = du[u]/NCS;
d_start = (du[u]<<1) + (n_shift_ra * NCS);
if ( (nr_du[u]<(N_ZC/3)) && (nr_du[u]>=NCS) ) {
n_shift_ra = nr_du[u]/NCS;
d_start = (nr_du[u]<<1) + (n_shift_ra * NCS);
n_group_ra = N_ZC/d_start;
n_shift_ra_bar = max(0,(N_ZC-(du[u]<<1)-(n_group_ra*d_start))/N_ZC);
} else if ( (du[u]>=(N_ZC/3)) && (du[u]<=((N_ZC - NCS)>>1)) ) {
n_shift_ra = (N_ZC - (du[u]<<1))/NCS;
d_start = N_ZC - (du[u]<<1) + (n_shift_ra * NCS);
n_group_ra = du[u]/d_start;
n_shift_ra_bar = min(n_shift_ra,max(0,(du[u]- (n_group_ra*d_start))/NCS));
n_shift_ra_bar = max(0,(N_ZC-(nr_du[u]<<1)-(n_group_ra*d_start))/N_ZC);
} else if ( (nr_du[u]>=(N_ZC/3)) && (nr_du[u]<=((N_ZC - NCS)>>1)) ) {
n_shift_ra = (N_ZC - (nr_du[u]<<1))/NCS;
d_start = N_ZC - (nr_du[u]<<1) + (n_shift_ra * NCS);
n_group_ra = nr_du[u]/d_start;
n_shift_ra_bar = min(n_shift_ra,max(0,(nr_du[u]- (n_group_ra*d_start))/NCS));
} else {
n_shift_ra = 0;
n_shift_ra_bar = 0;
......@@ -523,6 +519,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
}
}
if (n_shift_ra>0)
preamble_shift = -((d_start * (preamble_index0/n_shift_ra)) + ((preamble_index0%n_shift_ra)*NCS)); // minus because the channel is h(t -\tau + Cv)
else
......@@ -540,8 +537,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
// Compute DFT of RX signal (conjugate input, results in conjugate output) for each new rootSequenceIndex
if (LOG_DEBUGFLAG(PRACH)) {
int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840));
if (en>60) LOG_I(PHY,"frame %d, subframe %d : preamble index %d: offset %d, preamble shift %d (br_flag %d, en %d)\n",
ru->proc.frame_prach,subframe,preamble_index,preamble_offset,preamble_shift,br_flag,en);
if (en>60) LOG_I(PHY,"frame %d, subframe %d : preamble index %d: offset %d, preamble shift %d , en %d)\n",
frame,subframe,preamble_index,preamble_offset,preamble_shift,en);
}
log2_ifft_size = 10;
fft_size = 6144;
......@@ -549,24 +546,12 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
if (new_dft == 1) {
new_dft = 0;
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
if (br_flag == 1) {
Xu=(int16_t*)eNB->X_u_br[ce_level][preamble_offset-first_nonzero_root_idx];
prach_ifft = prach_ifftp[prach_ifft_cnt++];
if (eNB->prach_vars_br.repetition_number[ce_level]==1) memset(prach_ifft,0,((N_ZC==839)?2048:256)*sizeof(int32_t));
}
else
#endif
{
Xu=(int16_t*)eNB->X_u[preamble_offset-first_nonzero_root_idx];
prach_ifft = prach_ifftp[0];
Xu=(int16_t*)gNB->X_u[preamble_offset-first_nonzero_root_idx];
memset(prach_ifft,0,((N_ZC==839) ? 2048 : 256)*sizeof(int32_t));
}
memset(prachF, 0, sizeof(int16_t)*2*1024 );
if (LOG_DUMPFLAG(PRACH)) {
if (prach[0]!= NULL) LOG_M("prach_rx0.m","prach_rx0",prach[0],6144+792,1,1);
LOG_M("prach_rx1.m","prach_rx1",prach[1],6144+792,1,1);
LOG_M("prach_rxF0.m","prach_rxF0",rxsigF[0],24576,1,1);
LOG_M("prach_rxF1.m","prach_rxF1",rxsigF[1],6144,1,1);
}
......@@ -587,7 +572,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
if (N_ZC == 839) {
log2_ifft_size = 10;
idft1024(prachF,prach_ifft_tmp,1);
// compute energy and accumulate over receive antennas and repetitions for BR
// compute energy and accumulate over receive antennas
for (i=0;i<2048;i++)
prach_ifft[i] += (prach_ifft_tmp[i<<1]*prach_ifft_tmp[i<<1] + prach_ifft_tmp[1+(i<<1)]*prach_ifft_tmp[1+(i<<1)])>>10;
} else {
......@@ -606,15 +591,9 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
} // new dft
// check energy in nth time shift, for
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
if ((br_flag==0) ||
(eNB->prach_vars_br.repetition_number[ce_level]==
eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_numRepetitionPerPreambleAttempt[ce_level]))
#endif
{
if (LOG_DEBUGFLAG(PRACH)){
int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840));
if (en>60) LOG_I(PHY,"frame %d, subframe %d: Checking for peak in time-domain (br_flag %d, en %d)\n",ru->proc.frame_prach,subframe,br_flag,en);
if (en>60) LOG_I(PHY,"frame %d, subframe %d: Checking for peak in time-domain , en %d)\n",frame,subframe,en);
}
preamble_shift2 = ((preamble_shift==0) ? 0 : ((preamble_shift<<log2_ifft_size)/N_ZC));
......@@ -627,18 +606,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
*max_preamble_energy = levdB;
*max_preamble_delay = ((i*fft_size)>>log2_ifft_size)*update_TA/update_TA2;
*max_preamble = preamble_index;
if (LOG_DEBUGFLAG(PRACH)){
int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840));
if ((en>60) && (br_flag==1))
LOG_D(PHY,"frame %d, subframe %d : max_preamble_energy %d, max_preamble_delay %d, max_preamble %d (br_flag %d,ce_level %d, levdB %d, lev %d)\n",
ru->proc.frame_prach,subframe,
*max_preamble_energy,*max_preamble_delay,
*max_preamble,br_flag,ce_level,levdB,lev);
}
}
}
}
}// preamble_index
if (LOG_DUMPFLAG(PRACH)) {
......@@ -652,23 +621,13 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
k+=13;
k*=2;
if (br_flag == 0) {
LOG_M("rxsigF.m","prach_rxF",&rxsigF[0][0],12288,1,1);
LOG_M("prach_rxF_comp0.m","prach_rxF_comp0",prachF,1024,1,1);
LOG_M("Xu.m","xu",Xu,N_ZC,1,1);
LOG_M("prach_ifft0.m","prach_t0",prach_ifft,1024,1,1);
}
else {
LOG_E(PHY,"Dumping prach (br_flag %d), k = %d (n_ra_prb %d)\n",br_flag,k,n_ra_prb);
LOG_M("rxsigF_br.m","prach_rxF_br",&rxsigF[0][0],12288,1,1);
LOG_M("prach_rxF_comp0_br.m","prach_rxF_comp0_br",prachF,1024,1,1);
LOG_M("Xu_br.m","xu_br",Xu,N_ZC,1,1);
LOG_M("prach_ifft0_br.m","prach_t0_br",prach_ifft,1024,1,1);
exit(-1);
}
}
} /* LOG_DUMPFLAG(PRACH) */
if (eNB) stop_meas(&eNB->rx_prach);
if (gNB) stop_meas(&gNB->rx_prach);
}
......@@ -112,4 +112,16 @@ NR_gNB_DLSCH_t *new_gNB_dlsch(unsigned char Kmimo,
NR_DL_FRAME_PARMS *frame_parms,
nfapi_nr_config_request_t *config);
void rx_nr_prach(PHY_VARS_gNB *gNB,
int frame,
int subframe,
uint16_t *max_preamble,
uint16_t *max_preamble_energy,
uint16_t *max_preamble_delay
);
void rx_nr_prach_ru(RU_t *ru,
int frame,
int subframe);
#endif /*__NR_TRANSPORT__H__*/
......@@ -20,10 +20,10 @@
* contact@openairinterface.org
*/
/*! \file PHY/NR_TRANSPORT/nr_mcs.c
* \brief Some support routines for NR MCS computations
/*! \file PHY/NR_TRANSPORT/nr_transport_proto_common.h
* \brief Prototypes of functions common to gNB and NR UE
* \author
* \date 2018
* \date 2019
* \version 0.1
* \company Eurecom
* \email:
......@@ -34,14 +34,7 @@
#ifndef __NR_TRANSPORT_COMMON_PROTO__H__
#define __NR_TRANSPORT_COMMON_PROTO__H__
#define MAX_NUM_NR_DLSCH_SEGMENTS 16
#define MAX_NUM_NR_ULSCH_SEGMENTS MAX_NUM_NR_DLSCH_SEGMENTS
#define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056)
#define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056)
#define MAX_NUM_NR_CHANNEL_BITS (14*273*12*6) // 14 symbols, 273 RB
#define MAX_NUM_NR_RE (14*273*12)
// Functions below implement minor procedures from 38-214
......
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/NR_UE_TRANSPORT/transport_proto_ue.h
* \brief Function prototypes for PHY physical/transport channel processing and generation V8.6 2009-03
* \author R. Knopp, F. Kaltenberger
* \date 2011
* \version 0.1
* \company Eurecom
* \email: knopp@eurecom.fr
* \note
* \warning
*/
#ifndef __NR_TRANSPORT_PROTO_UE__H__
#define __NR_TRANSPORT_PROTO_UE__H__
#include "PHY/defs_nr_UE.h"
#include "SCHED_NR_UE/defs.h"
//#include "PHY/LTE_TRANSPORT/transport_common_proto.h"
#include <math.h>
#include "nfapi_interface.h"
// Functions below implement 36-211 and 36-212
/** @addtogroup _PHY_TRANSPORT_
* @{
*/
/** \fn free_ue_dlsch(NR_UE_DLSCH_t *dlsch)
\brief This function frees memory allocated for a particular DLSCH at UE
@param dlsch Pointer to DLSCH to be removed
*/
void free_nr_ue_dlsch(NR_UE_DLSCH_t *dlsch);
/** \fn new_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint8_t abstraction_flag)
\brief This function allocates structures for a particular DLSCH at UE
@returns Pointer to DLSCH to be removed
@param Kmimo Kmimo factor from 36-212/36-213
@param Mdlharq Maximum number of HARQ rounds (36-212/36-213)
@param Nsoft Soft-LLR buffer size from UE-Category
@params N_RB_DL total number of resource blocks (determine the operating BW)
@param abstraction_flag Flag to indicate abstracted interface
*/
NR_UE_DLSCH_t *new_nr_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint8_t max_turbo_iterations,uint8_t N_RB_DL, uint8_t abstraction_flag);
void free_nr_ue_ulsch(NR_UE_ULSCH_t *ulsch);
NR_UE_ULSCH_t *new_nr_ue_ulsch(unsigned char N_RB_UL, int number_of_harq_pids, uint8_t abstraction_flag);
void fill_UE_dlsch_MCH(PHY_VARS_NR_UE *ue,int mcs,int ndi,int rvidx,int eNB_id);
int rx_pmch(PHY_VARS_NR_UE *phy_vars_ue,
unsigned char eNB_id,
uint8_t subframe,
unsigned char symbol);
/** \brief Dump OCTAVE/MATLAB files for PMCH debugging
@param phy_vars_ue Pointer to UE variables
@param eNB_id index of eNB in ue variables
@param coded_bits_per_codeword G from 36.211
@param subframe Index of subframe
@returns 0 on success
*/
void dump_mch(PHY_VARS_NR_UE *phy_vars_ue,uint8_t eNB_id,uint16_t coded_bits_per_codeword,int subframe);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream QPSK/QPSK reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qpsk_qpsk(int16_t *stream0_in,
int16_t *stream1_in,
int16_t *stream0_out,
int16_t *rho01,
int32_t length);
/** \brief This function perform LLR computation for dual-stream (QPSK/QPSK) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr128p pointer to pointer to symbol in dlsch_llr*/
int32_t nr_dlsch_qpsk_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
int32_t **rho_i,
int16_t *dlsch_llr,
uint8_t symbol,
uint32_t len,
uint8_t first_symbol_flag,
uint16_t nb_rb,
uint16_t pbch_pss_sss_adj,
int16_t **llr128p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream QPSK/16QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qpsk_qam16(int16_t *stream0_in,
int16_t *stream1_in,
short *ch_mag_i,
int16_t *stream0_out,
int16_t *rho01,
int32_t length);
/** \brief This function perform LLR computation for dual-stream (QPSK/16QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr128p pointer to pointer to symbol in dlsch_llr*/
int32_t nr_dlsch_qpsk_16qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
int **dl_ch_mag_i, //|h_1|^2*(2/sqrt{10})
int32_t **rho_i,
int16_t *dlsch_llr,
uint8_t symbol,
uint8_t first_symbol_flag,
uint16_t nb_rb,
uint16_t pbch_pss_sss_adj,
int16_t **llr128p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream QPSK/64QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qpsk_qam64(int16_t *stream0_in,
int16_t *stream1_in,
short *ch_mag_i,
int16_t *stream0_out,
int16_t *rho01,
int32_t length);
/** \brief This function perform LLR computation for dual-stream (QPSK/64QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr128p pointer to pointer to symbol in dlsch_llr*/
int32_t nr_dlsch_qpsk_64qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
int **dl_ch_mag_i, //|h_1|^2*(2/sqrt{10})
int32_t **rho_i,
int16_t *dlsch_llr,
uint8_t symbol,
uint8_t first_symbol_flag,
uint16_t nb_rb,
uint16_t pbch_pss_sss_adj,
int16_t **llr128p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 16QAM/QPSK reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam16_qpsk(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *stream0_out,
short *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (16QAM/QPSK) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_16qam_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag, //|h_0|^2*(2/sqrt{10})
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
short **llr16p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 16QAM/16QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam16_qam16(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *ch_mag_i,
short *stream0_out,
short *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (16QAM/16QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_16qam_16qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag, //|h_0|^2*(2/sqrt{10})
int **dl_ch_mag_i, //|h_1|^2*(2/sqrt{10})
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
uint32_t len,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
short **llr16p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 16QAM/64QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam16_qam64(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *ch_mag_i,
short *stream0_out,
short *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (16QAM/64QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_16qam_64qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag, //|h_0|^2*(2/sqrt{10})
int **dl_ch_mag_i, //|h_1|^2*(2/sqrt{10})
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
short **llr16p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 64QAM/64QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam64_qpsk(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *stream0_out,
short *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (64QAM/64QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_64qam_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag,
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
short **llr16p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 64QAM/16QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam64_qam16(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *ch_mag_i,
short *stream0_out,
short *rho01,
int length);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 64QAM/16QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam64_qam16_avx2(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *ch_mag_i,
short *stream0_out,
short *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (64QAM/16QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_64qam_16qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag,
int **dl_ch_mag_i,
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
short **llr16p);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 64QAM/64QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam64_qam64(short *stream0_in,
short *stream1_in,
short *ch_mag,
short *ch_mag_i,
short *stream0_out,
short *rho01,
int length);
/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream 64QAM/64QAM reception.
@param stream0_in Input from channel compensated (MR combined) stream 0
@param stream1_in Input from channel compensated (MR combined) stream 1
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param stream0_out Output from LLR unit for stream0
@param rho01 Cross-correlation between channels (MR combined)
@param length in complex channel outputs*/
void qam64_qam64_avx2(int32_t *stream0_in,
int32_t *stream1_in,
int32_t *ch_mag,
int32_t *ch_mag_i,
int16_t *stream0_out,
int32_t *rho01,
int length);
/** \brief This function perform LLR computation for dual-stream (64QAM/64QAM) transmission.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param ch_mag Input from scaled channel magnitude square of h0'*g0
@param ch_mag_i Input from scaled channel magnitude square of h0'*g1
@param rho_i Correlation between channel of signal and inteference
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag flag to indicate this is the first symbol of the dlsch
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr16p pointer to pointer to symbol in dlsch_llr*/
int nr_dlsch_64qam_64qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int **rxdataF_comp_i,
int **dl_ch_mag,
int **dl_ch_mag_i,
int **rho_i,
short *dlsch_llr,
unsigned char symbol,
uint32_t len,
unsigned char first_symbol_flag,
unsigned short nb_rb,
uint16_t pbch_pss_sss_adjust,
//short **llr16p,
uint32_t llr_offset);
/** \brief This function generates log-likelihood ratios (decoder input) for single-stream QPSK received waveforms.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param dlsch_llr llr output
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS
@param llr128p pointer to pointer to symbol in dlsch_llr
@param beamforming_mode beamforming mode
*/
int32_t nr_dlsch_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int16_t *dlsch_llr,
uint8_t symbol,
uint32_t len,
uint8_t first_symbol_flag,
uint16_t nb_rb,
uint8_t beamforming_mode);
/**
\brief This function generates log-likelihood ratios (decoder input) for single-stream 16QAM received waveforms
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param dlsch_llr llr output
@param dl_ch_mag Squared-magnitude of channel in each resource element position corresponding to allocation and weighted for mid-point in 16QAM constellation
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adjust Adjustment factor in RE for PBCH/PSS/SSS allocations
@param llr128p pointer to pointer to symbol in dlsch_llr
@param beamforming_mode beamforming mode
*/
int32_t nr_dlsch_qpsk_llr_SIC(NR_DL_FRAME_PARMS *frame_parms,
int **rxdataF_comp,
int32_t **sic_buffer,
int **rho_i,
short *dlsch_llr,
uint8_t num_pdcch_symbols,
uint16_t nb_rb,
uint8_t subframe,
uint16_t mod_order_0,
uint32_t rb_alloc);
void nr_dlsch_16qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int16_t *dlsch_llr,
int32_t **dl_ch_mag,
uint8_t symbol,
uint32_t len,
uint8_t first_symbol_flag,
uint16_t nb_rb,
int16_t **llr32p,
uint8_t beamforming_mode);
/**
\brief This function generates log-likelihood ratios (decoder input) for single-stream 16QAM received waveforms
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param dlsch_llr llr output
@param dl_ch_mag Squared-magnitude of channel in each resource element position corresponding to allocation, weighted by first mid-point of 64-QAM constellation
@param dl_ch_magb Squared-magnitude of channel in each resource element position corresponding to allocation, weighted by second mid-point of 64-QAM constellation
@param symbol OFDM symbol index in sub-frame
@param first_symbol_flag
@param nb_rb number of RBs for this allocation
@param pbch_pss_sss_adjust PBCH/PSS/SSS RE adjustment (in REs)
@param beamforming_mode beamforming mode
*/
void nr_dlsch_16qam_llr_SIC (NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **sic_buffer, //Q15
int32_t **rho_i,
int16_t *dlsch_llr,
uint8_t num_pdcch_symbols,
int32_t **dl_ch_mag,
uint16_t nb_rb,
uint8_t subframe,
uint16_t mod_order_0,
uint32_t rb_alloc);
void dlsch_64qam_llr_SIC(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **sic_buffer, //Q15
int32_t **rho_i,
int16_t *dlsch_llr,
uint8_t num_pdcch_symbols,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint16_t nb_rb,
uint8_t subframe,
uint16_t mod_order_0,
uint32_t rb_alloc);
void nr_dlsch_64qam_llr(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int16_t *dlsch_llr,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint32_t len,
uint8_t first_symbol_flag,
uint16_t nb_rb,
uint32_t llr_offset,
uint8_t beamforming_mode);
/** \fn dlsch_siso(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
uint8_t l,
uint16_t nb_rb)
\brief This function does the first stage of llr computation for SISO, by just extracting the pilots, PBCH and primary/secondary synchronization sequences.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param l symbol in sub-frame
@param nb_rb Number of RBs in this allocation
*/
void dlsch_siso(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
uint8_t l,
uint16_t nb_rb);
/** \fn dlsch_alamouti(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint16_t nb_rb)
\brief This function does Alamouti combining on RX and prepares LLR inputs by skipping pilots, PBCH and primary/secondary synchronization signals.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param dl_ch_mag First squared-magnitude of channel (16QAM and 64QAM) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param dl_ch_magb Second squared-magnitude of channel (64QAM only) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param symbol Symbol in sub-frame
@param nb_rb Number of RBs in this allocation
*/
void dlsch_alamouti(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint16_t nb_rb);
/** \fn dlsch_antcyc(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint16_t nb_rb)
\brief This function does antenna selection (based on antenna cycling pattern) on RX and prepares LLR inputs by skipping pilots, PBCH and primary/secondary synchronization signals. Note that this is not LTE, it is just included for comparison purposes.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param dl_ch_mag First squared-magnitude of channel (16QAM and 64QAM) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param dl_ch_magb Second squared-magnitude of channel (64QAM only) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param symbol Symbol in sub-frame
@param nb_rb Number of RBs in this allocation
*/
void dlsch_antcyc(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint16_t nb_rb);
/** \fn dlsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
int32_t **rho,
int32_t **rho_i,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
uint8_t symbol,
uint16_t nb_rb,
uint8_t dual_stream_UE)
\brief This function does maximal-ratio combining for dual-antenna receivers.
@param frame_parms Frame descriptor structure
@param rxdataF_comp Compensated channel output
@param rxdataF_comp_i Compensated channel output for interference
@param rho Cross correlation between spatial channels
@param rho_i Cross correlation between signal and inteference channels
@param dl_ch_mag First squared-magnitude of channel (16QAM and 64QAM) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param dl_ch_magb Second squared-magnitude of channel (64QAM only) for LLR computation. Alamouti combining should be performed on this as well. Result is stored in first antenna position
@param symbol Symbol in sub-frame
@param nb_rb Number of RBs in this allocation
@param dual_stream_UE Flag to indicate dual-stream detection
*/
void dlsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms,
int32_t **rxdataF_comp,
int32_t **rxdataF_comp_i,
int32_t **rho,
int32_t **rho_i,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
int32_t **dl_ch_mag_i,
int32_t **dl_ch_magb_i,
uint8_t symbol,
uint16_t nb_rb,
uint8_t dual_stream_UE);
void dlsch_detection_mrc_TM34(NR_DL_FRAME_PARMS *frame_parms,
NR_UE_PDSCH *lte_ue_pdsch_vars,
int harq_pid,
int round,
unsigned char symbol,
unsigned short nb_rb,
unsigned char dual_stream_UE);
/** \fn dlsch_extract_rbs_single(int32_t **rxdataF,
int32_t **dl_ch_estimates,
int32_t **rxdataF_ext,
int32_t **dl_ch_estimates_ext,
uint16_t pmi,
uint8_t *pmi_ext,
uint32_t *rb_alloc,
uint8_t symbol,
uint8_t subframe,
NR_DL_FRAME_PARMS *frame_parms)
\brief This function extracts the received resource blocks, both channel estimates and data symbols,
for the current allocation and for single antenna eNB transmission.
@param rxdataF Raw FFT output of received signal
@param dl_ch_estimates Channel estimates of current slot
@param rxdataF_ext FFT output for RBs in this allocation
@param dl_ch_estimates_ext Channel estimates for RBs in this allocation
@param pmi subband Precoding matrix indicator
@param pmi_ext Extracted PMI for chosen RBs
@param rb_alloc RB allocation vector
@param symbol Symbol to extract
@param subframe Subframe number
@param vrb_type Flag to indicate distributed VRB type
@param high_speed_flag
@param frame_parms Pointer to frame descriptor
*/
/*uint16_t nr_dlsch_extract_rbs_single(int32_t **rxdataF,
int32_t **dl_ch_estimates,
int32_t **rxdataF_ext,
int32_t **dl_ch_estimates_ext,
uint16_t pmi,
uint8_t *pmi_ext,
uint32_t *rb_alloc,
uint8_t symbol,
uint8_t subframe,
uint32_t high_speed_flag,
NR_DL_FRAME_PARMS *frame_parms);*/
unsigned short nr_dlsch_extract_rbs_single(int **rxdataF,
int **dl_ch_estimates,
int **rxdataF_ext,
int **dl_ch_estimates_ext,
unsigned short pmi,
unsigned char *pmi_ext,
unsigned char symbol,
uint8_t pilots,
unsigned short start_rb,
unsigned short nb_pdsch_rb,
unsigned char nr_tti_rx,
uint32_t high_speed_flag,
NR_DL_FRAME_PARMS *frame_parms);
/** \fn dlsch_extract_rbs_dual(int32_t **rxdataF,
int32_t **dl_ch_estimates,
int32_t **rxdataF_ext,
int32_t **dl_ch_estimates_ext,
uint16_t pmi,
uint8_t *pmi_ext,
uint32_t *rb_alloc,
uint8_t symbol,
NR_DL_FRAME_PARMS *frame_parms)
\brief This function extracts the received resource blocks, both channel estimates and data symbols,
for the current allocation and for dual antenna eNB transmission.
@param rxdataF Raw FFT output of received signal
@param dl_ch_estimates Channel estimates of current slot
@param rxdataF_ext FFT output for RBs in this allocation
@param dl_ch_estimates_ext Channel estimates for RBs in this allocation
@param pmi subband Precoding matrix indicator
@param pmi_ext Extracted PMI for chosen RBs
@param rb_alloc RB allocation vector
@param symbol Symbol to extract
@param subframe Subframe index
@param high_speed_flag
@param frame_parms Pointer to frame descriptor
*/
unsigned short nr_dlsch_extract_rbs_dual(int **rxdataF,
int **dl_ch_estimates,
int **rxdataF_ext,
int **dl_ch_estimates_ext,
unsigned short pmi,
unsigned char *pmi_ext,
unsigned char symbol,
uint8_t pilots,
unsigned short start_rb,
unsigned short nb_rb_pdsch,
unsigned char nr_tti_rx,
uint32_t high_speed_flag,
NR_DL_FRAME_PARMS *frame_parms,
MIMO_mode_t mimo_mode);
/** \fn dlsch_extract_rbs_TM7(int32_t **rxdataF,
int32_t **dl_bf_ch_estimates,
int32_t **rxdataF_ext,
int32_t **dl_bf_ch_estimates_ext,
uint32_t *rb_alloc,
uint8_t symbol,
uint8_t subframe,
uint32_t high_speed_flag,
NR_DL_FRAME_PARMS *frame_parms)
\brief This function extracts the received resource blocks, both channel estimates and data symbols,
for the current allocation and for single antenna eNB transmission.
@param rxdataF Raw FFT output of received signal
@param dl_bf_ch_estimates Beamforming channel estimates of current slot
@param rxdataF_ext FFT output for RBs in this allocation
@param dl_bf_ch_estimates_ext Beamforming channel estimates for RBs in this allocation
@param rb_alloc RB allocation vector
@param symbol Symbol to extract
@param subframe Subframe number
@param high_speed_flag
@param frame_parms Pointer to frame descriptor
*/
uint16_t dlsch_extract_rbs_TM7(int32_t **rxdataF,
int32_t **dl_bf_ch_estimates,
int32_t **rxdataF_ext,
int32_t **dl_bf_ch_estimates_ext,
uint32_t *rb_alloc,
uint8_t symbol,
uint8_t subframe,
uint32_t high_speed_flag,
NR_DL_FRAME_PARMS *frame_parms);
/** \brief This function performs channel compensation (matched filtering) on the received RBs for this allocation. In addition, it computes the squared-magnitude of the channel with weightings for 16QAM/64QAM detection as well as dual-stream detection (cross-correlation)
@param rxdataF_ext Frequency-domain received signal in RBs to be demodulated
@param dl_ch_estimates_ext Frequency-domain channel estimates in RBs to be demodulated
@param dl_ch_mag First Channel magnitudes (16QAM/64QAM)
@param dl_ch_magb Second weighted Channel magnitudes (64QAM)
@param rxdataF_comp Compensated received waveform
@param rho Cross-correlation between two spatial channels on each RX antenna
@param frame_parms Pointer to frame descriptor
@param symbol Symbol on which to operate
@param first_symbol_flag set to 1 on first DLSCH symbol
@param mod_order Modulation order of allocation
@param nb_rb Number of RBs in allocation
@param output_shift Rescaling for compensated output (should be energy-normalizing)
@param phy_measurements Pointer to UE PHY measurements
*/
void nr_dlsch_channel_compensation(int32_t **rxdataF_ext,
int32_t **dl_ch_estimates_ext,
int32_t **dl_ch_mag,
int32_t **dl_ch_magb,
int32_t **rxdataF_comp,
int32_t **rho,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t symbol,
uint8_t start_symbol,
uint8_t first_symbol_flag,
uint8_t mod_order,
uint16_t nb_rb,
uint8_t output_shift,
PHY_NR_MEASUREMENTS *phy_measurements);
void nr_dlsch_channel_compensation_core(int **rxdataF_ext,
int **dl_ch_estimates_ext,
int **dl_ch_mag,
int **dl_ch_magb,
int **rxdataF_comp,
int **rho,
unsigned char n_tx,
unsigned char n_rx,
unsigned char mod_order,
unsigned char output_shift,
int length,
int start_point);
void nr_dlsch_deinterleaving(uint8_t symbol,
uint8_t start_symbol,
uint16_t L,
uint16_t *llr,
uint16_t *llr_deint,
uint16_t nb_rb_pdsch);
void dlsch_dual_stream_correlation(NR_DL_FRAME_PARMS *frame_parms,
unsigned char symbol,
unsigned short nb_rb,
int **dl_ch_estimates_ext,
int **dl_ch_estimates_ext_i,
int **dl_ch_rho_ext,
unsigned char output_shift);
void dlsch_dual_stream_correlationTM34(NR_DL_FRAME_PARMS *frame_parms,
unsigned char symbol,
unsigned short nb_rb,
int **dl_ch_estimates_ext,
int **dl_ch_estimates_ext_i,
int **dl_ch_rho_ext,
unsigned char output_shift0,
unsigned char output_shift1);
//This function is used to compute multiplications in Hhermitian * H matrix
void conjch0_mult_ch1(int *ch0,
int *ch1,
int32_t *ch0conj_ch1,
unsigned short nb_rb,
unsigned char output_shift0);
void construct_HhH_elements(int *ch0conj_ch0,
int *ch1conj_ch1,
int *ch2conj_ch2,
int *ch3conj_ch3,
int *ch0conj_ch1,
int *ch1conj_ch0,
int *ch2conj_ch3,
int *ch3conj_ch2,
int32_t *after_mf_00,
int32_t *after_mf_01,
int32_t *after_mf_10,
int32_t *after_mf_11,
unsigned short nb_rb);
void squared_matrix_element(int32_t *Hh_h_00,
int32_t *Hh_h_00_sq,
unsigned short nb_rb);
void dlsch_channel_level_TM34_meas(int *ch00,
int *ch01,
int *ch10,
int *ch11,
int *avg_0,
int *avg_1,
unsigned short nb_rb);
void nr_dlsch_channel_level_median(int **dl_ch_estimates_ext,
int32_t *median,
int n_tx,
int n_rx,
int length,
int start_point);
void nr_dlsch_detection_mrc_core(int **rxdataF_comp,
int **rxdataF_comp_i,
int **rho,
int **rho_i,
int **dl_ch_mag,
int **dl_ch_magb,
int **dl_ch_mag_i,
int **dl_ch_magb_i,
unsigned char n_tx,
unsigned char n_rx,
int length,
int start_point);
void det_HhH(int32_t *after_mf_00,
int32_t *after_mf_01,
int32_t *after_mf_10,
int32_t *after_mf_11,
int32_t *det_fin_128,
unsigned short nb_rb);
void numer(int32_t *Hh_h_00_sq,
int32_t *Hh_h_01_sq,
int32_t *Hh_h_10_sq,
int32_t *Hh_h_11_sq,
int32_t *num_fin,
unsigned short nb_rb);
uint8_t rank_estimation_tm3_tm4(int *dl_ch_estimates_00,
int *dl_ch_estimates_01,
int *dl_ch_estimates_10,
int *dl_ch_estimates_11,
unsigned short nb_rb);
void dlsch_channel_compensation_TM56(int **rxdataF_ext,
int **dl_ch_estimates_ext,
int **dl_ch_mag,
int **dl_ch_magb,
int **rxdataF_comp,
unsigned char *pmi_ext,
NR_DL_FRAME_PARMS *frame_parms,
PHY_NR_MEASUREMENTS *phy_measurements,
int eNB_id,
unsigned char symbol,
unsigned char mod_order,
unsigned short nb_rb,
unsigned char output_shift,
unsigned char dl_power_off);
void dlsch_channel_compensation_TM34(NR_DL_FRAME_PARMS *frame_parms,
NR_UE_PDSCH *lte_ue_pdsch_vars,
PHY_NR_MEASUREMENTS *phy_measurements,
int eNB_id,
unsigned char symbol,
unsigned char mod_order0,
unsigned char mod_order1,
int harq_pid,
int round,
MIMO_mode_t mimo_mode,
unsigned short nb_rb,
unsigned char output_shift0,
unsigned char output_shift1);
/** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal
@param dl_ch_estimates_ext Channel estimates in allocated RBs
@param frame_parms Pointer to frame descriptor
@param avg Pointer to average signal strength
@param pilots_flag Flag to indicate pilots in symbol
@param nb_rb Number of allocated RBs
*/
void nr_dlsch_channel_level(int **dl_ch_estimates_ext,
NR_DL_FRAME_PARMS *frame_parms,
int32_t *avg,
uint8_t symbol,
uint32_t len,
unsigned short nb_rb);
void dlsch_channel_level_TM34(int **dl_ch_estimates_ext,
NR_DL_FRAME_PARMS *frame_parms,
unsigned char *pmi_ext,
int *avg_0,
int *avg_1,
uint8_t symbol,
unsigned short nb_rb,
MIMO_mode_t mimo_mode);
void dlsch_channel_level_TM56(int32_t **dl_ch_estimates_ext,
NR_DL_FRAME_PARMS *frame_parms,
unsigned char *pmi_ext,
int32_t *avg,
uint8_t symbol_mod,
uint16_t nb_rb);
void dlsch_channel_level_TM7(int32_t **dl_bf_ch_estimates_ext,
NR_DL_FRAME_PARMS *frame_parms,
int32_t *avg,
uint8_t pilots_flag,
uint16_t nb_rb);
void nr_dlsch_scale_channel(int32_t **dl_ch_estimates_ext,
NR_DL_FRAME_PARMS *frame_parms,
NR_UE_DLSCH_t **dlsch_ue,
uint8_t symbol,
uint8_t start_symbol,
uint16_t nb_rb);
/** \brief This is the top-level entry point for DLSCH decoding in UE. It should be replicated on several
threads (on multi-core machines) corresponding to different HARQ processes. The routine first
computes the segmentation information, followed by rate dematching and sub-block deinterleaving the of the
received LLRs computed by dlsch_demodulation for each transport block segment. It then calls the
turbo-decoding algorithm for each segment and stops after either after unsuccesful decoding of at least
one segment or correct decoding of all segments. Only the segment CRCs are check for the moment, the
overall CRC is ignored. Finally transport block reassembly is performed.
@param phy_vars_ue Pointer to ue variables
@param dlsch_llr Pointer to LLR values computed by dlsch_demodulation
@param lte_frame_parms Pointer to frame descriptor
@param dlsch Pointer to DLSCH descriptor
@param frame Frame number
@param subframe Subframe number
@param num_pdcch_symbols Number of PDCCH symbols
@param is_crnti indicates if PDSCH belongs to a CRNTI (necessary for parallelizing decoding threads)
@param llr8_flag If 1, indicate that the 8-bit turbo decoder should be used
@returns 0 on success, 1 on unsuccessful decoding
*/
uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
short *dlsch_llr,
NR_DL_FRAME_PARMS *frame_parms,
NR_UE_DLSCH_t *dlsch,
NR_DL_UE_HARQ_t *harq_process,
uint32_t frame,
uint16_t nb_symb_sch,
uint8_t nr_tti_rx,
uint8_t harq_pid,
uint8_t is_crnti,
uint8_t llr8_flag);
int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch,
NR_DL_FRAME_PARMS* frame_parms,
uint8_t harq_pid);
/*! \brief Perform PUSCH scrambling. TS 38.211 V15.4.0 subclause 6.3.1.1
@param[in] in Pointer to input bits
@param[in] size of input bits
@param[in] Nid cell id
@param[in] n_RNTI CRNTI
@param[out] out the scrambled bits
*/
void nr_pusch_codeword_scrambling(uint8_t *in,
uint16_t size,
uint32_t Nid,
uint32_t n_RNTI,
uint32_t* out);
uint32_t nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
UE_nr_rxtx_proc_t *proc,
int eNB_id,
short *dlsch_llr,
NR_DL_FRAME_PARMS *frame_parms,
NR_UE_DLSCH_t *dlsch,
NR_DL_UE_HARQ_t *harq_process,
uint32_t frame,
uint16_t nb_symb_sch,
uint8_t nr_tti_rx,
uint8_t harq_pid,
uint8_t is_crnti,
uint8_t llr8_flag);
void *nr_dlsch_decoding_2thread0(void *arg);
void *nr_dlsch_decoding_2thread1(void *arg);
void nr_dlsch_unscrambling(int16_t* llr,
uint32_t size,
uint8_t q,
uint32_t Nid,
uint32_t n_RNTI);
uint32_t dlsch_decoding_emul(PHY_VARS_NR_UE *phy_vars_ue,
uint8_t subframe,
PDSCH_t dlsch_id,
uint8_t eNB_id);
/** \brief This function is the top-level entry point to PDSCH demodulation, after frequency-domain transformation and channel estimation. It performs
- RB extraction (signal and channel estimates)
- channel compensation (matched filtering)
- RE extraction (pilot, PBCH, synch. signals)
- antenna combining (MRC, Alamouti, cycling)
- LLR computation
This function supports TM1, 2, 3, 5, and 6.
@param PHY_VARS_NR_UE Pointer to PHY variables
@param type Type of PDSCH (SI_PDSCH,RA_PDSCH,PDSCH,PMCH)
@param eNB_id eNb index (Nid1) 0,1,2
@param eNB_id_i Interfering eNB index (Nid1) 0,1,2, or 3 in case of MU-MIMO IC receiver
@param subframe Subframe number
@param symbol Symbol on which to act (within sub-frame)
@param first_symbol_flag set to 1 on first DLSCH symbol
@param rx_type. rx_type=RX_IC_single_stream will enable interference cancellation of a second stream when decoding the first stream. In case of TM1, 2, 5, and this can cancel interference from a neighbouring cell given by eNB_id_i. In case of TM5, eNB_id_i should be set to n_connected_eNB to perform multi-user interference cancellation. In case of TM3, eNB_id_i should be set to eNB_id to perform co-channel interference cancellation; this option should be used together with an interference cancellation step [...]. In case of TM3, if rx_type=RX_IC_dual_stream, both streams will be decoded by applying the IC single stream receiver twice.
@param i_mod Modulation order of the interfering stream
*/
int32_t nr_rx_pdsch(PHY_VARS_NR_UE *phy_vars_ue,
PDSCH_t type,
uint8_t eNB_id,
uint8_t eNB_id_i,
uint32_t frame,
uint8_t subframe,
uint8_t symbol,
uint8_t first_symbol_flag,
RX_type_t rx_type,
uint8_t i_mod,
uint8_t harq_pid);
int32_t nr_rx_pdcch(PHY_VARS_NR_UE *ue,
uint32_t frame,
uint8_t nr_tti_rx,
uint8_t eNB_id,
MIMO_mode_t mimo_mode,
uint32_t high_speed_flag,
uint8_t is_secondary_ue,
int nb_coreset_active,
uint16_t symbol_mon,
NR_SEARCHSPACE_TYPE_t searchSpaceType);
/*! \brief Extract PSS and SSS resource elements
@param phy_vars_ue Pointer to UE variables
@param[out] pss_ext contain the PSS signals after the extraction
@param[out] sss_ext contain the SSS signals after the extraction
@returns 0 on success
*/
int pss_sss_extract(PHY_VARS_NR_UE *phy_vars_ue,
int32_t pss_ext[4][72],
int32_t sss_ext[4][72],
uint8_t subframe);
/*! \brief Extract only PSS resource elements
@param phy_vars_ue Pointer to UE variables
@param[out] pss_ext contain the PSS signals after the extraction
@returns 0 on success
*/
int pss_only_extract(PHY_VARS_NR_UE *phy_vars_ue,
int32_t pss_ext[4][72],
uint8_t subframe);
/*! \brief Extract only SSS resource elements
@param phy_vars_ue Pointer to UE variables
@param[out] sss_ext contain the SSS signals after the extraction
@returns 0 on success
*/
int sss_only_extract(PHY_VARS_NR_UE *phy_vars_ue,
int32_t sss_ext[4][72],
uint8_t subframe);
/*! \brief Performs detection of SSS to find cell ID and other framing parameters (FDD/TDD, normal/extended prefix)
@param phy_vars_ue Pointer to UE variables
@param tot_metric Pointer to variable containing maximum metric under framing hypothesis (to be compared to other hypotheses
@param flip_max Pointer to variable indicating if start of frame is in second have of RX buffer (i.e. PSS/SSS is flipped)
@param phase_max Pointer to variable (0 ... 6) containing rought phase offset between PSS and SSS (can be used for carrier
frequency adjustment. 0 means -pi/3, 6 means pi/3.
@returns 0 on success
*/
int rx_sss(PHY_VARS_NR_UE *phy_vars_ue,int32_t *tot_metric,uint8_t *flip_max,uint8_t *phase_max);
/*! \brief receiver for the PBCH
\returns number of tx antennas or -1 if error
*/
int nr_rx_pbch( PHY_VARS_NR_UE *ue,
UE_nr_rxtx_proc_t *proc,
NR_UE_PBCH *nr_ue_pbch_vars,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t eNB_id,
uint8_t i_ssb,
MIMO_mode_t mimo_mode,
uint32_t high_speed_flag);
int nr_pbch_detection(UE_nr_rxtx_proc_t *proc,
PHY_VARS_NR_UE *ue,
int pbch_initial_symbol,
runmode_t mode);
uint16_t rx_pbch_emul(PHY_VARS_NR_UE *phy_vars_ue,
uint8_t eNB_id,
uint8_t pbch_phase);
/*! \brief PBCH unscrambling
This is similar to pbch_scrabling with the difference that inputs are signed s16s (llr values) and instead of flipping bits we change signs.
\param frame_parms Pointer to frame descriptor
\param llr Output of the demodulator
\param length Length of the sequence
\param frame_mod4 Frame number modulo 4*/
void pbch_unscrambling(NR_DL_FRAME_PARMS *frame_parms,
int8_t* llr,
uint32_t length,
uint8_t frame_mod4);
void generate_64qam_table(void);
void generate_16qam_table(void);
void generate_qpsk_table(void);
uint16_t extract_crc(uint8_t *dci,uint8_t DCI_LENGTH);
/*! \brief LLR from two streams. This function takes two streams (qpsk modulated) and calculates the LLR, considering one stream as interference.
\param stream0_in pointer to first stream0
\param stream1_in pointer to first stream1
\param stream0_out pointer to output stream
\param rho01 pointer to correlation matrix
\param length*/
void qpsk_qpsk_TM3456(short *stream0_in,
short *stream1_in,
short *stream0_out,
short *rho01,
int length
);
/** \brief Attempt decoding of a particular DCI with given length and format.
@param DCI_LENGTH length of DCI in bits
@param DCI_FMT Format of DCI
@param e e-sequence (soft bits)
@param decoded_output Output of Viterbi decoder
*/
void dci_decoding(uint8_t DCI_LENGTH,
uint8_t DCI_FMT,
int8_t *e,
uint8_t *decoded_output);
/** \brief Do 36.213 DCI decoding procedure by searching different RNTI options and aggregation levels. Currently does
not employ the complexity reducing procedure based on RNTI.
@param phy_vars_ue UE variables
@param dci_alloc Pointer to DCI_ALLOC_t array to store results for DLSCH/ULSCH programming
@param do_common If 1 perform search in common search-space else ue-specific search-space
@param eNB_id eNB Index on which to act
@param subframe Index of subframe
@returns bitmap of occupied CCE positions (i.e. those detected)
*/
uint16_t dci_decoding_procedure(PHY_VARS_NR_UE *phy_vars_ue,
DCI_ALLOC_t *dci_alloc,
int do_common,
int16_t eNB_id,
uint8_t subframe);
uint16_t dci_CRNTI_decoding_procedure(PHY_VARS_NR_UE *ue,
DCI_ALLOC_t *dci_alloc,
uint8_t DCIFormat,
uint8_t agregationLevel,
int16_t eNB_id,
uint8_t subframe);
uint16_t dci_decoding_procedure_emul(NR_UE_PDCCH **lte_ue_pdcch_vars,
uint8_t num_ue_spec_dci,
uint8_t num_common_dci,
DCI_ALLOC_t *dci_alloc_tx,
DCI_ALLOC_t *dci_alloc_rx,
int16_t eNB_id);
/** \brief Compute Q (modulation order) based on I_MCS PDSCH. Implements table 7.1.7.1-1 from 36.213.
@param I_MCS */
uint8_t get_Qm(uint8_t I_MCS);
/** \brief Compute Q (modulation order) based on I_MCS for PUSCH. Implements table 8.6.1-1 from 36.213.
@param I_MCS */
uint8_t get_Qm_ul(uint8_t I_MCS);
/** \brief Compute I_TBS (transport-block size) based on I_MCS for PDSCH. Implements table 7.1.7.1-1 from 36.213.
@param I_MCS */
uint8_t get_I_TBS(uint8_t I_MCS);
/** \brief Compute I_TBS (transport-block size) based on I_MCS for PUSCH. Implements table 8.6.1-1 from 36.213.
@param I_MCS */
unsigned char get_I_TBS_UL(unsigned char I_MCS);
/** \brief Compute Q (modulation order) based on downlink I_MCS. Implements table 7.1.7.1-1 from 36.213.
@param I_MCS
@param nb_rb
@return Transport block size */
uint32_t get_TBS_DL(uint8_t mcs, uint16_t nb_rb);
/** \brief Compute Q (modulation order) based on uplink I_MCS. Implements table 7.1.7.1-1 from 36.213.
@param I_MCS
@param nb_rb
@return Transport block size */
uint32_t get_TBS_UL(uint8_t mcs, uint16_t nb_rb);
/* \brief Return bit-map of resource allocation for a given DCI rballoc (RIV format) and vrb type
@param N_RB_DL number of PRB on DL
@param indicator for even/odd slot
@param vrb vrb index
@param Ngap Gap indicator
*/
uint32_t get_prb(int N_RB_DL,int odd_slot,int vrb,int Ngap);
/* \brief Return prb for a given vrb index
@param vrb_type VRB type (0=localized,1=distributed)
@param rb_alloc_dci rballoc field from DCI
*/
uint32_t get_rballoc(vrb_t vrb_type,uint16_t rb_alloc_dci);
/* \brief Return bit-map of resource allocation for a given DCI rballoc (RIV format) and vrb type
@returns Transmission mode (1-7)
*/
uint8_t get_transmission_mode(module_id_t Mod_id, uint8_t CC_id, rnti_t rnti);
/* \brief
@param ra_header Header of resource allocation (0,1) (See sections 7.1.6.1/7.1.6.2 of 36.213 Rel8.6)
@param rb_alloc Bitmap allocation from DCI (format 1,2)
@returns number of physical resource blocks
*/
uint32_t conv_nprb(uint8_t ra_header,uint32_t rb_alloc,int N_RB_DL);
int get_G(NR_DL_FRAME_PARMS *frame_parms,uint16_t nb_rb,uint32_t *rb_alloc,uint8_t mod_order,uint8_t Nl,uint8_t num_pdcch_symbols,int frame,uint8_t subframe, uint8_t beamforming_mode);
int adjust_G(NR_DL_FRAME_PARMS *frame_parms,uint32_t *rb_alloc,uint8_t mod_order,uint8_t subframe);
int adjust_G2(NR_DL_FRAME_PARMS *frame_parms,uint32_t *rb_alloc,uint8_t mod_order,uint8_t subframe,uint8_t symbol);
#ifndef modOrder
#define modOrder(I_MCS,I_TBS) ((I_MCS-I_TBS)*2+2) // Find modulation order from I_TBS and I_MCS
#endif
/** \fn uint8_t I_TBS2I_MCS(uint8_t I_TBS);
\brief This function maps I_tbs to I_mcs according to Table 7.1.7.1-1 in 3GPP TS 36.213 V8.6.0. Where there is two supported modulation orders for the same I_TBS then either high or low modulation is chosen by changing the equality of the two first comparisons in the if-else statement.
\param I_TBS Index of Transport Block Size
\return I_MCS given I_TBS
*/
uint8_t I_TBS2I_MCS(uint8_t I_TBS);
/** \fn uint8_t SE2I_TBS(float SE,
uint8_t N_PRB,
uint8_t symbPerRB);
\brief This function maps a requested throughput in number of bits to I_tbs. The throughput is calculated as a function of modulation order, RB allocation and number of symbols per RB. The mapping orginates in the "Transport block size table" (Table 7.1.7.2.1-1 in 3GPP TS 36.213 V8.6.0)
\param SE Spectral Efficiency (before casting to integer, multiply by 1024, remember to divide result by 1024!)
\param N_PRB Number of PhysicalResourceBlocks allocated \sa lte_frame_parms->N_RB_DL
\param symbPerRB Number of symbols per resource block allocated to this channel
\return I_TBS given an SE and an N_PRB
*/
uint8_t SE2I_TBS(float SE,
uint8_t N_PRB,
uint8_t symbPerRB);
/** \brief This function generates the sounding reference symbol (SRS) for the uplink according to 36.211 v8.6.0. If IFFT_FPGA is defined, the SRS is quantized to a QPSK sequence.
@param frame_parms LTE DL Frame Parameters
@param soundingrs_ul_config_dedicated Dynamic configuration from RRC during Connection Establishment
@param txdataF pointer to the frequency domain TX signal
@returns 0 on success*/
int generate_srs(NR_DL_FRAME_PARMS *frame_parms,
SOUNDINGRS_UL_CONFIG_DEDICATED *soundingrs_ul_config_dedicated,
int *txdataF,
int16_t amp,
uint32_t subframe);
/*!
\brief This function is similar to generate_srs_tx but generates a conjugate sequence for channel estimation. If IFFT_FPGA is defined, the SRS is quantized to a QPSK sequence.
@param phy_vars_ue Pointer to PHY_VARS structure
@param eNB_id Index of destination eNB for this SRS
@param amp Linear amplitude of SRS
@param subframe Index of subframe on which to act
@returns 0 on success, -1 on error with message
*/
int32_t generate_srs_tx(PHY_VARS_NR_UE *phy_vars_ue,
uint8_t eNB_id,
int16_t amp,
uint32_t subframe);
/*!
\brief This function generates the downlink reference signal for the PUSCH according to 36.211 v8.6.0. The DRS occuies the RS defined by rb_alloc and the symbols 2 and 8 for extended CP and 3 and 10 for normal CP.
*/
int32_t generate_drs_pusch(PHY_VARS_NR_UE *phy_vars_ue,
UE_nr_rxtx_proc_t *proc,
uint8_t eNB_id,
int16_t amp,
uint32_t subframe,
uint32_t first_rb,
uint32_t nb_rb,
uint8_t ant);
/*!
\brief This function initializes the Group Hopping, Sequence Hopping and nPRS sequences for PUCCH/PUSCH according to 36.211 v8.6.0. It should be called after configuration of UE (reception of SIB2/3) and initial configuration of eNB (or after reconfiguration of cell-specific parameters).
@param frame_parms Pointer to a NR_DL_FRAME_PARMS structure (eNB or UE)*/
void init_ul_hopping(NR_DL_FRAME_PARMS *frame_parms);
/*!
\brief This function implements the initialization of paging parameters for UE (See Section 7, 36.304).It must be called after setting IMSImod1024 during UE startup and after receiving SIB2
@param ue Pointer to UE context
@param defaultPagingCycle T from 36.304 (0=32,1=64,2=128,3=256)
@param nB nB from 36.304 (0=4T,1=2T,2=T,3=T/2,4=T/4,5=T/8,6=T/16,7=T/32*/
int init_ue_paging_info(PHY_VARS_NR_UE *ue, long defaultPagingCycle, long nB);
int32_t compareints (const void * a, const void * b);
void ulsch_modulation(int32_t **txdataF,
int16_t amp,
frame_t frame,
uint32_t subframe,
NR_DL_FRAME_PARMS *frame_parms,
NR_UE_ULSCH_t *ulsch);
int generate_ue_dlsch_params_from_dci(int frame,
uint8_t subframe,
void *dci_pdu,
rnti_t rnti,
DCI_format_t dci_format,
NR_UE_PDCCH *pdcch_vars,
NR_UE_PDSCH *pdsch_vars,
NR_UE_DLSCH_t **dlsch,
NR_DL_FRAME_PARMS *frame_parms,
PDSCH_CONFIG_DEDICATED *pdsch_config_dedicated,
uint16_t si_rnti,
uint16_t ra_rnti,
uint16_t p_rnti,
uint8_t beamforming_mode,
uint16_t tc_rnti);
int generate_ue_ulsch_params_from_dci(void *dci_pdu,
rnti_t rnti,
uint8_t subframe,
DCI_format_t dci_format,
PHY_VARS_NR_UE *phy_vars_ue,
UE_nr_rxtx_proc_t *proc,
uint16_t si_rnti,
uint16_t ra_rnti,
uint16_t p_rnti,
uint16_t cba_rnti,
uint8_t eNB_id,
uint8_t use_srs);
int32_t generate_ue_ulsch_params_from_rar(PHY_VARS_NR_UE *phy_vars_ue,
UE_nr_rxtx_proc_t *proc,
uint8_t eNB_id);
double sinr_eff_cqi_calc(PHY_VARS_NR_UE *phy_vars_ue,
uint8_t eNB_id,
uint8_t subframe);
uint8_t sinr2cqi(double sinr,uint8_t trans_mode);
int dump_dci(NR_DL_FRAME_PARMS *frame_parms, DCI_ALLOC_t *dci);
int dump_ue_stats(PHY_VARS_NR_UE *phy_vars_ue, UE_nr_rxtx_proc_t *proc, char* buffer, int length, runmode_t mode, int input_level_dBm);
void init_transport_channels(uint8_t);
void generate_RIV_tables(void);
/*!
\brief This function performs the initial cell search procedure - PSS detection, SSS detection and PBCH detection. At the
end, the basic frame parameters are known (Frame configuration - TDD/FDD and cyclic prefix length,
N_RB_DL, PHICH_CONFIG and Nid_cell) and the UE can begin decoding PDCCH and DLSCH SI to retrieve the rest. Once these
parameters are know, the routine calls some basic initialization routines (cell-specific reference signals, etc.)
@param phy_vars_ue Pointer to UE variables
@param mode current running mode
*/
int nr_initial_sync(UE_nr_rxtx_proc_t *proc,
PHY_VARS_NR_UE *phy_vars_ue, runmode_t mode);
/*!
\brief Encoding of PUSCH/ACK/RI/ACK from 36-212.
@param a Pointer to ulsch SDU
@param frame_parms Pointer to Frame parameters
@param ulsch Pointer to ulsch descriptor
@param harq_pid HARQ process ID
@param tmode Transmission mode (1-7)
@param control_only_flag Generate PUSCH with control information only
@param Nbundled Parameter for ACK/NAK bundling (36.213 Section 7.3)
*/
uint32_t ulsch_encoding(uint8_t *a,
PHY_VARS_NR_UE *phy_vars_ue,
uint8_t harq_pid,
uint8_t eNB_id,
uint8_t subframe_rx,
uint8_t tmode,
uint8_t control_only_flag,
uint8_t Nbundled);
void print_CQI(void *o,UCI_format_t uci_format,uint8_t eNB_id,int N_RB_DL);
void fill_CQI(NR_UE_ULSCH_t *ulsch,PHY_NR_MEASUREMENTS *meas,uint8_t eNB_id, uint8_t harq_pid,int N_RB_DL, rnti_t rnti, uint8_t trans_mode,double sinr_eff);
void reset_cba_uci(void *o);
/** \brief This routine computes the subband PMI bitmap based on measurements (0,1,2,3 for rank 0 and 0,1 for rank 1) in the format needed for UCI
@param meas pointer to measurements
@param eNB_id eNB_id
@param nb_subbands number of subbands
@returns subband PMI bitmap
*/
uint16_t quantize_subband_pmi(PHY_NR_MEASUREMENTS *meas,uint8_t eNB_id,int nb_subbands);
int32_t pmi_convert_rank1_from_rank2(uint16_t pmi_alloc, int tpmi, int nb_rb);
uint16_t quantize_subband_pmi2(PHY_NR_MEASUREMENTS *meas,uint8_t eNB_id,uint8_t a_id,int nb_subbands);
uint64_t cqi2hex(uint32_t cqi);
uint16_t computeRIV(uint16_t N_RB_DL,uint16_t RBstart,uint16_t Lcrbs);
/** \brief This routine extracts a single subband PMI from a bitmap coming from UCI or the pmi_extend function
@param N_RB_DL number of resource blocks
@param mimo_mode
@param pmi_alloc subband PMI bitmap
@param rb resource block for which to extract PMI
@returns subband PMI
*/
uint8_t get_pmi(uint8_t N_RB_DL,MIMO_mode_t mode, uint32_t pmi_alloc,uint16_t rb);
int get_nCCE_offset_l1(int *CCE_table,
const unsigned char L,
const int nCCE,
const int common_dci,
const unsigned short rnti,
const unsigned char subframe);
uint16_t get_nCCE(uint8_t num_pdcch_symbols,NR_DL_FRAME_PARMS *frame_parms,uint8_t mi);
uint16_t get_nquad(uint8_t num_pdcch_symbols,NR_DL_FRAME_PARMS *frame_parms,uint8_t mi);
uint8_t get_mi(NR_DL_FRAME_PARMS *frame,uint8_t subframe);
uint16_t get_nCCE_mac(uint8_t Mod_id,uint8_t CC_id,int num_pdcch_symbols,int subframe);
uint8_t get_num_pdcch_symbols(uint8_t num_dci,DCI_ALLOC_t *dci_alloc,NR_DL_FRAME_PARMS *frame_parms,uint8_t subframe);
void pdcch_interleaving(NR_DL_FRAME_PARMS *frame_parms,int32_t **z, int32_t **wbar,uint8_t n_symbols_pdcch,uint8_t mi);
void pdcch_unscrambling(NR_DL_FRAME_PARMS *frame_parms,
uint8_t subframe,
int8_t* llr,
uint32_t length);
void dlsch_unscrambling(NR_DL_FRAME_PARMS *frame_parms,
int mbsfn_flag,
NR_UE_DLSCH_t *dlsch,
int G,
int16_t* llr,
uint8_t q,
uint8_t Ns);
void init_ncs_cell(NR_DL_FRAME_PARMS *frame_parms,uint8_t ncs_cell[20][7]);
void generate_pucch1x(int32_t **txdataF,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t ncs_cell[20][7],
PUCCH_FMT_t fmt,
PUCCH_CONFIG_DEDICATED *pucch_config_dedicated,
uint16_t n1_pucch,
uint8_t shortened_format,
uint8_t *payload,
int16_t amp,
uint8_t subframe);
void generate_pucch2x(int32_t **txdataF,
NR_DL_FRAME_PARMS *fp,
uint8_t ncs_cell[20][7],
PUCCH_FMT_t fmt,
PUCCH_CONFIG_DEDICATED *pucch_config_dedicated,
uint16_t n2_pucch,
uint8_t *payload,
int A,
int B2,
int16_t amp,
uint8_t subframe,
uint16_t rnti);
void generate_pucch3x(int32_t **txdataF,
NR_DL_FRAME_PARMS *frame_parms,
uint8_t ncs_cell[20][7],
PUCCH_FMT_t fmt,
PUCCH_CONFIG_DEDICATED *pucch_config_dedicated,
uint16_t n3_pucch,
uint8_t shortened_format,
uint8_t *payload,
int16_t amp,
uint8_t subframe,
uint16_t rnti);
void init_ulsch_power_LUT(void);
/*!
\brief Check for PRACH TXop in subframe
@param frame_parms Pointer to NR_DL_FRAME_PARMS
@param frame frame index to check
@param subframe subframe index to check
@returns 0 on success
*/
int is_prach_subframe(NR_DL_FRAME_PARMS *frame_parms,frame_t frame, uint8_t subframe);
/*!
\brief Generate PRACH waveform
@param phy_vars_ue Pointer to ue top-level descriptor
@param eNB_id Index of destination eNB
@param subframe subframe index to operate on
@param index of preamble (0-63)
@param Nf System frame number
@returns 0 on success
*/
int32_t generate_prach(PHY_VARS_NR_UE *phy_vars_ue,uint8_t eNB_id,uint8_t subframe,uint16_t Nf);
/*!
\brief Helper for MAC, returns number of available PRACH in TDD for a particular configuration index
@param frame_parms Pointer to NR_DL_FRAME_PARMS structure
@returns 0-5 depending on number of available prach
*/
uint8_t get_num_prach_tdd(module_id_t Mod_id);
/*!
\brief Return the PRACH format as a function of the Configuration Index and Frame type.
@param prach_ConfigIndex PRACH Configuration Index
@param frame_type 0-FDD, 1-TDD
@returns 0-1 accordingly
*/
uint8_t get_prach_fmt(uint8_t prach_ConfigIndex,lte_frame_type_t frame_type);
/*!
\brief Helper for MAC, returns frequency index of PRACH resource in TDD for a particular configuration index
@param frame_parms Pointer to NR_DL_FRAME_PARMS structure
@returns 0-5 depending on number of available prach
*/
uint8_t get_fid_prach_tdd(module_id_t Mod_id,uint8_t tdd_map_index);
/*!
\brief Comp ute DFT of PRACH ZC sequences. Used for generation of prach in UE and reception of PRACH in eNB.
@param rootSequenceIndex PRACH root sequence
#param prach_ConfigIndex PRACH Configuration Index
@param zeroCorrelationZoneConfig PRACH ncs_config
@param highSpeedFlat PRACH High-Speed Flag
@param frame_type TDD/FDD flag
@param Xu DFT output
*/
void compute_prach_seq(uint16_t rootSequenceIndex,
uint8_t prach_ConfigIndex,
uint8_t zeroCorrelationZoneConfig,
uint8_t highSpeedFlag,
lte_frame_type_t frame_type,
uint32_t X_u[64][839]);
void init_prach_tables(int N_ZC);
void init_unscrambling_lut(void);
void init_scrambling_lut(void);
/*!
\brief Return the status of MBSFN in this frame/subframe
@param frame Frame index
@param subframe Subframe index
@param frame_parms Pointer to frame parameters
@returns 1 if subframe is for MBSFN
*/
int is_pmch_subframe(frame_t frame, int subframe, NR_DL_FRAME_PARMS *frame_parms);
uint8_t is_not_pilot(uint8_t pilots, uint8_t re, uint8_t nushift, uint8_t use2ndpilots);
uint8_t is_not_UEspecRS(int8_t lprime, uint8_t re, uint8_t nushift, uint8_t Ncp, uint8_t beamforming_mode);
uint32_t dlsch_decoding_abstraction(double *dlsch_MIPB,
NR_DL_FRAME_PARMS *lte_frame_parms,
NR_UE_DLSCH_t *dlsch,
uint8_t subframe,
uint8_t num_pdcch_symbols);
// DL power control functions
double get_pa_dB(uint8_t pa);
double computeRhoA_UE(PDSCH_CONFIG_DEDICATED *pdsch_config_dedicated,
NR_UE_DLSCH_t *dlsch_ue,
uint8_t dl_power_off,
uint8_t n_antenna_port);
double computeRhoB_UE(PDSCH_CONFIG_DEDICATED *pdsch_config_dedicated,
PDSCH_CONFIG_COMMON *pdsch_config_common,
uint8_t n_antenna_port,
NR_UE_DLSCH_t *dlsch_ue,
uint8_t dl_power_off);
/*void compute_sqrt_RhoAoRhoB(PDSCH_CONFIG_DEDICATED *pdsch_config_dedicated,
PDSCH_CONFIG_COMMON *pdsch_config_common,
uint8_t n_antenna_port,
NR_UE_DLSCH_t *dlsch_ue);
*/
uint8_t get_prach_prb_offset(NR_DL_FRAME_PARMS *frame_parms,
uint8_t prach_ConfigIndex,
uint8_t n_ra_prboffset,
uint8_t tdd_mapindex, uint16_t Nf);
void nr_pdcch_unscrambling(uint16_t crnti, NR_DL_FRAME_PARMS *frame_parms, uint8_t nr_tti_rx,
int16_t *z, uint32_t length, uint16_t pdcch_DMRS_scrambling_id, int do_common);
uint32_t lte_gold_generic(uint32_t *x1, uint32_t *x2, uint8_t reset);
uint8_t nr_dci_decoding_procedure(int s,
int p,
PHY_VARS_NR_UE *ue,
NR_DCI_ALLOC_t *dci_alloc,
NR_SEARCHSPACE_TYPE_t searchSpacetype,
int16_t eNB_id,
uint8_t nr_tti_rx,
uint8_t dci_fields_sizes_cnt[MAX_NR_DCI_DECODED_SLOT][NBR_NR_DCI_FIELDS][NBR_NR_FORMATS],
uint16_t n_RB_ULBWP,
uint16_t n_RB_DLBWP,
crc_scrambled_t *crc_scrambled,
format_found_t *format_found,
uint16_t crc_scrambled_values[TOTAL_NBR_SCRAMBLED_VALUES]);
int nr_generate_ue_ul_dlsch_params_from_dci(PHY_VARS_NR_UE *ue,
uint8_t eNB_id,
int frame,
uint8_t nr_tti_rx,
uint64_t dci_pdu[2],
uint16_t rnti,
uint8_t dci_length,
NR_DCI_format_t dci_format,
NR_UE_PDCCH *pdcch_vars,
NR_UE_PDSCH *pdsch_vars,
NR_UE_DLSCH_t **dlsch,
NR_UE_ULSCH_t *ulsch,
NR_DL_FRAME_PARMS *frame_parms,
PDSCH_CONFIG_DEDICATED *pdsch_config_dedicated,
uint8_t beamforming_mode,
uint8_t dci_fields_sizes[NBR_NR_DCI_FIELDS][NBR_NR_FORMATS],
uint16_t n_RB_ULBWP,
uint16_t n_RB_DLBWP,
uint16_t crc_scrambled_values[TOTAL_NBR_SCRAMBLED_VALUES],
NR_DCI_INFO_EXTRACTED_t *nr_dci_info_extracted);
int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
PDSCH_t type,
unsigned char eNB_id,
unsigned char eNB_id_i, //if this == ue->n_connected_eNB, we assume MU interference
uint32_t frame,
uint8_t nr_tti_rx,
unsigned char symbol,
unsigned char first_symbol_flag,
RX_type_t rx_type,
unsigned char i_mod,
unsigned char harq_pid);
uint32_t nr_get_G(uint16_t nb_rb, uint16_t nb_symb_sch,uint8_t nb_re_dmrs,uint16_t length_dmrs, uint8_t Qm, uint8_t Nl) ;
uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
short *dlsch_llr,
NR_DL_FRAME_PARMS *frame_parms,
NR_UE_DLSCH_t *dlsch,
NR_DL_UE_HARQ_t *harq_process,
uint32_t frame,
uint16_t nb_symb_sch,
uint8_t nr_tti_rx,
uint8_t harq_pid,
uint8_t is_crnti,
uint8_t llr8_flag);
int nr_extract_dci_info(PHY_VARS_NR_UE *ue,
uint8_t eNB_id,
lte_frame_type_t frame_type,
uint8_t dci_length,
uint16_t rnti,
uint64_t dci_pdu[2],
fapi_nr_dci_pdu_rel15_t *nr_pdci_info_extracted,
uint8_t dci_fields_sizes[NBR_NR_DCI_FIELDS][NBR_NR_FORMATS],
NR_DCI_format_t dci_format,
uint8_t nr_tti_rx,
uint16_t n_RB_ULBWP,
uint16_t n_RB_DLBWP,
uint16_t crc_scrambled_values[TOTAL_NBR_SCRAMBLED_VALUES]);
/**@}*/
#endif
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "PHY/defs_nr_UE.h"
int16_t get_nr_PL(PHY_VARS_NR_UE *ue,uint8_t gNB_index)
{
LOG_D(PHY,"get_PL : rsrp %f dBm/RE (%f), eNB power %d dBm/RE\n",
(1.0*dB_fixed_times10(ue->measurements.rsrp[gNB_index])-(10.0*ue->rx_total_gain_dB))/10.0,
10*log10((double)ue->measurements.rsrp[gNB_index]),
ue->frame_parms.ss_PBCH_BlockPower);
return((int16_t)(((10*ue->rx_total_gain_dB) -
dB_fixed_times10(ue->measurements.rsrp[gNB_index])+
// dB_fixed_times10(RSoffset*12*ue_g[Mod_id][CC_id]->frame_parms.N_RB_DL) +
(ue->frame_parms.ss_PBCH_BlockPower*10))/10));
}
......@@ -1085,79 +1085,13 @@ uint8_t nr_subframe2harq_pid(NR_DL_FRAME_PARMS *frame_parms,uint32_t frame,uint8
uint8_t ret = 255;
uint8_t subframe = nr_tti_rx>>((int)(log2 (frame_parms->ttis_per_subframe)));
AssertFatal(1==0,"Not ready for this ...\n");
if (frame_parms->frame_type == FDD) {
ret = (((frame<<1)+nr_tti_rx)&7);
} else {
switch (frame_parms->tdd_config) {
case 1:
if ((subframe==2) ||
(subframe==3) ||
(subframe==7) ||
(subframe==8))
switch (subframe) {
case 2:
case 3:
ret = (subframe-2);
break;
case 7:
case 8:
ret = (subframe-5);
break;
default:
LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,frame_parms->tdd_config);
ret = (255);
break;
}
break;
case 2:
if ((subframe!=2) && (subframe!=7)) {
LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,frame_parms->tdd_config);
//mac_xface->macphy_exit("subframe2_harq_pid, Illegal subframe");
ret = (255);
}
ret = (subframe/7);
break;
case 3:
if ((subframe<2) || (subframe>4)) {
LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,frame_parms->tdd_config);
ret = (255);
}
ret = (subframe-2);
break;
case 4:
if ((subframe<2) || (subframe>3)) {
LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,frame_parms->tdd_config);
ret = (255);
}
ret = (subframe-2);
break;
case 5:
if (subframe!=2) {
LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,frame_parms->tdd_config);
ret = (255);
}
ret = (subframe-2);
break;
} else {
default:
LOG_E(PHY,"subframe2_harq_pid, Unsupported TDD mode %d\n",frame_parms->tdd_config);
ret = (255);
}
}
if (ret == 255) {
LOG_E(PHY, "invalid harq_pid(%d) at SFN/SF = %d/%d\n", ret, frame, subframe);
......@@ -1171,46 +1105,16 @@ uint8_t nr_pdcch_alloc2ul_subframe(NR_DL_FRAME_PARMS *frame_parms,uint8_t n)
{
uint8_t ul_subframe = 255;
if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 1) &&
((n==1)||(n==6))) // tdd_config 0,1 SF 1,5
ul_subframe = ((n+6)%10);
else if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 6) &&
((n==0)||(n==1)||(n==5)||(n==6)))
ul_subframe = ((n+7)%10);
else if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 6) &&
(n==9)) // tdd_config 6 SF 9
ul_subframe = ((n+5)%10);
else
ul_subframe = ((n+4)%10);
AssertFatal(1==0,"Not ready for this\n");
LOG_D(PHY, "subframe %d: PUSCH subframe = %d\n", n, ul_subframe);
return ul_subframe;
}
uint32_t nr_pdcch_alloc2ul_frame(NR_DL_FRAME_PARMS *frame_parms,uint32_t frame, uint8_t n)
{
uint32_t ul_frame = 255;
if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 1) &&
((n==1)||(n==6))) // tdd_config 0,1 SF 1,5
ul_frame = (frame + (n==1 ? 0 : 1));
else if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 6) &&
((n==0)||(n==1)||(n==5)||(n==6)))
ul_frame = (frame + (n>=5 ? 1 : 0));
else if ((frame_parms->frame_type == TDD) &&
(frame_parms->tdd_config == 6) &&
(n==9)) // tdd_config 6 SF 9
ul_frame = (frame+1);
else
ul_frame = (frame+(n>=6 ? 1 : 0));
AssertFatal(1==0,"Not ready for this\n");
LOG_D(PHY, "frame %d subframe %d: PUSCH frame = %d\n", frame, n, ul_frame);
return ul_frame;
}
......@@ -30,20 +30,11 @@
* \warning
*/
#include "PHY/sse_intrin.h"
//#include "PHY/defs_common.h"
//#include "PHY/phy_extern.h"
//#include "PHY/phy_extern_ue.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
//#include "PHY/defs.h"
#include "PHY/impl_defs_nr.h"
//#include "PHY/defs_nr_common.h"
#include "PHY/defs_nr_UE.h"
#include "PHY/NR_UE_TRANSPORT/nr_prach.h"
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
//#include "PHY/extern.h"
//#include "LAYER2/MAC/extern.h"
//#include "PHY/NR_UE_TRANSPORT/pucch_nr.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
......@@ -54,514 +45,25 @@
#define NR_PRACH_DEBUG 1
extern uint16_t NCS_unrestricted_delta_f_RA_125[16];
extern uint16_t NCS_restricted_TypeA_delta_f_RA_125[15];
extern uint16_t NCS_restricted_TypeB_delta_f_RA_125[13];
extern uint16_t NCS_unrestricted_delta_f_RA_5[16];
extern uint16_t NCS_restricted_TypeA_delta_f_RA_5[16];
extern uint16_t NCS_restricted_TypeB_delta_f_RA_5[14];
extern uint16_t NCS_unrestricted_delta_f_RA_15[16];
extern uint16_t prach_root_sequence_map_0_3[838];
extern uint16_t prach_root_sequence_map_abc[138];
extern int64_t table_6_3_3_2_2_prachConfig_Index [256][9];
extern int64_t table_6_3_3_2_3_prachConfig_Index [256][9];
extern int64_t table_6_3_3_2_4_prachConfig_Index [256][10];
extern uint16_t nr_du[838];
extern int16_t nr_ru[2*839];
void dump_nr_prach_config(NR_DL_FRAME_PARMS *frame_parms,uint8_t subframe)
{
FILE *fd;
fd = fopen("prach_config.txt","w");
fprintf(fd,"prach_config: subframe = %d\n",subframe);
fprintf(fd,"prach_config: N_RB_UL = %d\n",frame_parms->N_RB_UL);
fprintf(fd,"prach_config: frame_type = %s\n",(frame_parms->frame_type==1) ? "TDD":"FDD");
if(frame_parms->frame_type==1) fprintf(fd,"prach_config: tdd_config = %d\n",frame_parms->tdd_config);
fprintf(fd,"prach_config: rootSequenceIndex = %d\n",frame_parms->prach_config_common.rootSequenceIndex);
fprintf(fd,"prach_config: prach_ConfigIndex = %d\n",frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex);
fprintf(fd,"prach_config: Ncs_config = %d\n",frame_parms->prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig);
fprintf(fd,"prach_config: highSpeedFlag = %d\n",frame_parms->prach_config_common.prach_ConfigInfo.highSpeedFlag);
fprintf(fd,"prach_config: n_ra_prboffset = %d\n",frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset);
fclose(fd);
}
// This function computes the du
void nr_fill_du(uint8_t prach_fmt)
{
uint16_t iu,u,p;
uint16_t N_ZC;
uint16_t *prach_root_sequence_map;
if (prach_fmt<4) {
N_ZC = 839;
prach_root_sequence_map = prach_root_sequence_map_0_3;
} else {
N_ZC = 139;
prach_root_sequence_map = prach_root_sequence_map_abc;
}
for (iu=0; iu<(N_ZC-1); iu++) {
u=prach_root_sequence_map[iu];
p=1;
while (((u*p)%N_ZC)!=1)
p++;
nr_du[u] = ((p<(N_ZC>>1)) ? p : (N_ZC-p));
}
}
#if 0
uint8_t get_num_prach_tdd(module_id_t Mod_id)
{
NR_DL_FRAME_PARMS *fp = &PHY_vars_UE_g[Mod_id][0]->frame_parms;
return(tdd_preamble_map[fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex][fp->tdd_config].num_prach);
}
uint8_t get_fid_prach_tdd(module_id_t Mod_id,uint8_t tdd_map_index)
{
NR_DL_FRAME_PARMS *fp = &PHY_vars_UE_g[Mod_id][0]->frame_parms;
return(tdd_preamble_map[fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex][fp->tdd_config].map[tdd_map_index].f_ra);
}
#endif
uint16_t get_nr_prach_fmt(uint8_t prach_ConfigIndex)
{
//return (table_6_3_3_2_2_prachConfig_Index[prach_ConfigIndex][0]); // if using table 6.3.3.2-2: Random access configurations for FR1 and paired spectrum/supplementary uplink
return (table_6_3_3_2_3_prachConfig_Index[prach_ConfigIndex][0]); // if using table 6.3.3.2-3: Random access configurations for FR1 and unpaired spectrum
// For FR2 not implemented. FIXME
}
#if 0
uint8_t get_prach_fmt(uint8_t prach_ConfigIndex,lte_frame_type_t frame_type)
{
if (frame_type == FDD) // FDD
return(prach_ConfigIndex>>4);
else {
if (prach_ConfigIndex < 20)
return (0);
if (prach_ConfigIndex < 30)
return (1);
if (prach_ConfigIndex < 40)
return (2);
if (prach_ConfigIndex < 48)
return (3);
else
return (4);
}
}
uint8_t get_prach_prb_offset(NR_DL_FRAME_PARMS *frame_parms,
uint8_t prach_ConfigIndex,
uint8_t n_ra_prboffset,
uint8_t tdd_mapindex, uint16_t Nf, uint16_t prach_fmt)
{
lte_frame_type_t frame_type = frame_parms->frame_type;
uint8_t tdd_config = frame_parms->tdd_config;
uint8_t n_ra_prb;
uint8_t f_ra,t1_ra;
uint8_t Nsp=2;
if (frame_type == TDD) { // TDD
if (tdd_preamble_map[prach_ConfigIndex][tdd_config].num_prach==0) {
LOG_E(PHY, "Illegal prach_ConfigIndex %"PRIu8"", prach_ConfigIndex);
return(-1);
}
// adjust n_ra_prboffset for frequency multiplexing (p.36 36.211)
f_ra = tdd_preamble_map[prach_ConfigIndex][tdd_config].map[tdd_mapindex].f_ra;
if (prach_fmt < 4) {
if ((f_ra&1) == 0) {
n_ra_prb = n_ra_prboffset + 6*(f_ra>>1);
} else {
n_ra_prb = frame_parms->N_RB_UL - 6 - n_ra_prboffset + 6*(f_ra>>1);
}
} else {
if ((tdd_config >2) && (tdd_config<6))
Nsp = 2;
t1_ra = tdd_preamble_map[prach_ConfigIndex][tdd_config].map[0].t1_ra;
if ((((Nf&1)*(2-Nsp)+t1_ra)&1) == 0) {
n_ra_prb = 6*f_ra;
} else {
n_ra_prb = frame_parms->N_RB_UL - 6*(f_ra+1);
}
}
}
else { //FDD
n_ra_prb = n_ra_prboffset;
}
return(n_ra_prb);
}
#endif //0
int is_nr_prach_subframe(NR_DL_FRAME_PARMS *frame_parms,uint32_t frame, uint8_t subframe) {
uint8_t prach_ConfigIndex = frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
/*
// For FR1 paired
if (((frame%table_6_3_3_2_2_prachConfig_Index[prach_ConfigIndex][2]) == table_6_3_3_2_2_prachConfig_Index[prach_ConfigIndex][3]) &&
((table_6_3_3_2_2_prachConfig_Index[prach_ConfigIndex][4]&(1<<subframe)) == 1)) {
// using table 6.3.3.2-2: Random access configurations for FR1 and paired spectrum/supplementary uplink
return(1);
} else {
return(0);
}
*/
// For FR1 unpaired
if (((frame%table_6_3_3_2_3_prachConfig_Index[prach_ConfigIndex][2]) == table_6_3_3_2_3_prachConfig_Index[prach_ConfigIndex][3]) &&
((table_6_3_3_2_3_prachConfig_Index[prach_ConfigIndex][4]&(1<<subframe)) == 1)) {
// using table 6.3.3.2-2: Random access configurations for FR1 and unpaired
return(1);
} else {
return(0);
}
/*
// For FR2: FIXME
if ((((frame%table_6_3_3_2_4_prachConfig_Index[prach_ConfigIndex][2]) == table_6_3_3_2_4_prachConfig_Index[prach_ConfigIndex][3]) ||
((frame%table_6_3_3_2_4_prachConfig_Index[prach_ConfigIndex][2]) == table_6_3_3_2_4_prachConfig_Index[prach_ConfigIndex][4]))
&&
((table_6_3_3_2_4_prachConfig_Index[prach_ConfigIndex][5]&(1<<subframe)) == 1)) {
// using table 6.3.3.2-2: Random access configurations for FR1 and unpaired
return(1);
} else {
return(0);
}
*/
}
#if 0
int is_prach_subframe0(NR_DL_FRAME_PARMS *frame_parms,uint8_t prach_ConfigIndex,uint32_t frame, uint8_t subframe)
{
// uint8_t prach_ConfigIndex = frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
uint8_t tdd_config = frame_parms->tdd_config;
uint8_t t0_ra;
uint8_t t1_ra;
uint8_t t2_ra;
int prach_mask = 0;
if (frame_parms->frame_type == FDD) { //FDD
//implement Table 5.7.1-2 from 36.211 (Rel-10, p.41)
if ((((frame&1) == 1) && (subframe < 9)) ||
(((frame&1) == 0) && (subframe == 9))) // This is an odd frame, ignore even-only PRACH frames
if (((prach_ConfigIndex&0xf)<3) || // 0,1,2,16,17,18,32,33,34,48,49,50
((prach_ConfigIndex&0x1f)==18) || // 18,50
((prach_ConfigIndex&0xf)==15)) // 15,47
return(0);
switch (prach_ConfigIndex&0x1f) {
case 0:
case 3:
if (subframe==1) prach_mask = 1;
break;
case 1:
case 4:
if (subframe==4) prach_mask = 1;
break;
case 2:
case 5:
if (subframe==7) prach_mask = 1;
break;
case 6:
if ((subframe==1) || (subframe==6)) prach_mask=1;
break;
case 7:
if ((subframe==2) || (subframe==7)) prach_mask=1;
break;
case 8:
if ((subframe==3) || (subframe==8)) prach_mask=1;
break;
case 9:
if ((subframe==1) || (subframe==4) || (subframe==7)) prach_mask=1;
break;
case 10:
if ((subframe==2) || (subframe==5) || (subframe==8)) prach_mask=1;
break;
case 11:
if ((subframe==3) || (subframe==6) || (subframe==9)) prach_mask=1;
break;
case 12:
if ((subframe&1)==0) prach_mask=1;
break;
case 13:
if ((subframe&1)==1) prach_mask=1;
break;
case 14:
prach_mask=1;
break;
case 15:
if (subframe==9) prach_mask=1;
break;
}
} else { // TDD
AssertFatal(prach_ConfigIndex<64,
"Illegal prach_ConfigIndex %d for ",prach_ConfigIndex);
AssertFatal(tdd_preamble_map[prach_ConfigIndex][tdd_config].num_prach>0,
"Illegal prach_ConfigIndex %d for ",prach_ConfigIndex);
t0_ra = tdd_preamble_map[prach_ConfigIndex][tdd_config].map[0].t0_ra;
t1_ra = tdd_preamble_map[prach_ConfigIndex][tdd_config].map[0].t1_ra;
t2_ra = tdd_preamble_map[prach_ConfigIndex][tdd_config].map[0].t2_ra;
#ifdef PRACH_DEBUG
LOG_I(PHY,"[PRACH] Checking for PRACH format (ConfigIndex %d) in TDD subframe %d (%d,%d,%d)\n",
prach_ConfigIndex,
subframe,
t0_ra,t1_ra,t2_ra);
#endif
if ((((t0_ra == 1) && ((frame &1)==0))|| // frame is even and PRACH is in even frames
((t0_ra == 2) && ((frame &1)==1))|| // frame is odd and PRACH is in odd frames
(t0_ra == 0)) && // PRACH is in all frames
(((subframe<5)&&(t1_ra==0)) || // PRACH is in 1st half-frame
(((subframe>4)&&(t1_ra==1))))) { // PRACH is in 2nd half-frame
if ((prach_ConfigIndex<48) && // PRACH only in normal UL subframe
(((subframe%5)-2)==t2_ra)) prach_mask=1;
else if ((prach_ConfigIndex>47) && (((subframe%5)-1)==t2_ra)) prach_mask=1; // PRACH can be in UpPTS
}
}
return(prach_mask);
}
int is_prach_subframe(NR_DL_FRAME_PARMS *frame_parms,uint32_t frame, uint8_t subframe) {
uint8_t prach_ConfigIndex = frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
int prach_mask = is_prach_subframe0(frame_parms,prach_ConfigIndex,frame,subframe);
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
int i;
for (i=0;i<4;i++) {
if (frame_parms->prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[i] == 1)
prach_mask|=(is_prach_subframe0(frame_parms,frame_parms->prach_emtc_config_common.prach_ConfigInfo.prach_ConfigIndex[i],frame,subframe)<<(i+1));
}
#endif
return(prach_mask);
}
#endif //0
void compute_nr_prach_seq(uint16_t rootSequenceIndex,
uint8_t prach_ConfigIndex,
uint8_t zeroCorrelationZoneConfig,
uint8_t highSpeedFlag,
lte_frame_type_t frame_type,
uint32_t X_u[64][839])
{
// Compute DFT of x_u => X_u[k] = x_u(inv(u)*k)^* X_u[k] = exp(j\pi u*inv(u)*k*(inv(u)*k+1)/N_ZC)
unsigned int k,inv_u,i,NCS=0,num_preambles;
int N_ZC;
uint8_t prach_fmt = get_prach_fmt(prach_ConfigIndex,frame_type);
uint16_t *prach_root_sequence_map;
uint16_t u, preamble_offset;
uint16_t n_shift_ra,n_shift_ra_bar, d_start,numshift;
uint8_t not_found;
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_IN);
#ifdef PRACH_DEBUG
LOG_I(PHY,"compute_prach_seq: NCS_config %d, prach_fmt %d\n",zeroCorrelationZoneConfig, prach_fmt);
#endif
AssertFatal(prach_fmt<4,
"PRACH sequence is only precomputed for prach_fmt<4 (have %"PRIu8")\n", prach_fmt );
N_ZC = (prach_fmt < 4) ? 839 : 139;
//init_prach_tables(N_ZC); //moved to phy_init_lte_ue/eNB, since it takes to long in real-time
if (prach_fmt < 4) {
prach_root_sequence_map = prach_root_sequence_map_0_3;
} else {
// FIXME cannot be reached
prach_root_sequence_map = prach_root_sequence_map_abc;
}
#ifdef PRACH_DEBUG
LOG_I( PHY, "compute_prach_seq: done init prach_tables\n" );
#endif
int restricted_Type = 0; //this is hardcoded ('0' for restricted_TypeA; and '1' for restricted_TypeB). FIXME
if (highSpeedFlag== 0) {
#ifdef PRACH_DEBUG
LOG_I(PHY,"Low speed prach : NCS_config %d\n",zeroCorrelationZoneConfig);
#endif
AssertFatal(zeroCorrelationZoneConfig<=15,
"FATAL, Illegal Ncs_config for unrestricted format %"PRIu8"\n", zeroCorrelationZoneConfig );
if (prach_fmt<3) NCS = NCS_unrestricted_delta_f_RA_125[zeroCorrelationZoneConfig];
if (prach_fmt==3) NCS = NCS_unrestricted_delta_f_RA_5[zeroCorrelationZoneConfig];
if (prach_fmt>3) NCS = NCS_unrestricted_delta_f_RA_15[zeroCorrelationZoneConfig];
num_preambles = (NCS==0) ? 64 : ((64*NCS)/N_ZC);
if (NCS>0) num_preambles++;
preamble_offset = 0;
} else {
#ifdef PRACH_DEBUG
LOG_I( PHY, "high speed prach : NCS_config %"PRIu8"\n", zeroCorrelationZoneConfig );
#endif
AssertFatal(zeroCorrelationZoneConfig<=14,
"FATAL, Illegal Ncs_config for restricted format %"PRIu8"\n", zeroCorrelationZoneConfig );
if (prach_fmt<3){
if (restricted_Type == 0) NCS = NCS_restricted_TypeA_delta_f_RA_125[zeroCorrelationZoneConfig]; // for TypeA, this is hardcoded. FIXME
if (restricted_Type == 1) NCS = NCS_restricted_TypeB_delta_f_RA_125[zeroCorrelationZoneConfig]; // for TypeB, this is hardcoded. FIXME
}
if (prach_fmt==3){
if (restricted_Type == 0) NCS = NCS_restricted_TypeA_delta_f_RA_5[zeroCorrelationZoneConfig]; // for TypeA, this is hardcoded. FIXME
if (restricted_Type == 1) NCS = NCS_restricted_TypeB_delta_f_RA_5[zeroCorrelationZoneConfig]; // for TypeB, this is hardcoded. FIXME
}
if (prach_fmt>3){
}
//NCS = NCS_restricted[zeroCorrelationZoneConfig];
nr_fill_du(prach_fmt);
num_preambles = 64; // compute ZC sequence for 64 possible roots
// find first non-zero shift root (stored in preamble_offset)
not_found = 1;
preamble_offset = 0;
while (not_found == 1) {
// current root depending on rootSequenceIndex
int index = (rootSequenceIndex + preamble_offset) % N_ZC;
if (prach_fmt<4) {
// prach_root_sequence_map points to prach_root_sequence_map0_3
DevAssert( index < sizeof(prach_root_sequence_map_0_3) / sizeof(prach_root_sequence_map_0_3[0]) );
} else {
// prach_root_sequence_map points to prach_root_sequence_map4
DevAssert( index < sizeof(prach_root_sequence_map_abc) / sizeof(prach_root_sequence_map_abc[0]) );
}
u = prach_root_sequence_map[index];
uint16_t n_group_ra = 0;
if ( (nr_du[u]<(N_ZC/3)) && (nr_du[u]>=NCS) ) {
n_shift_ra = nr_du[u]/NCS;
d_start = (nr_du[u]<<1) + (n_shift_ra * NCS);
n_group_ra = N_ZC/d_start;
n_shift_ra_bar = max(0,(N_ZC-(nr_du[u]<<1)-(n_group_ra*d_start))/N_ZC);
} else if ( (nr_du[u]>=(N_ZC/3)) && (nr_du[u]<=((N_ZC - NCS)>>1)) ) {
n_shift_ra = (N_ZC - (nr_du[u]<<1))/NCS;
d_start = N_ZC - (nr_du[u]<<1) + (n_shift_ra * NCS);
n_group_ra = nr_du[u]/d_start;
n_shift_ra_bar = min(n_shift_ra,max(0,(nr_du[u]- (n_group_ra*d_start))/NCS));
} else {
n_shift_ra = 0;
n_shift_ra_bar = 0;
}
// This is the number of cyclic shifts for the current root u
numshift = (n_shift_ra*n_group_ra) + n_shift_ra_bar;
// skip to next root and recompute parameters if numshift==0
if (numshift>0)
not_found = 0;
else
preamble_offset++;
}
}
#ifdef PRACH_DEBUG
if (NCS>0)
LOG_I( PHY, "Initializing %u preambles for PRACH (NCS_config %"PRIu8", NCS %u, N_ZC/NCS %u)\n",
num_preambles, zeroCorrelationZoneConfig, NCS, N_ZC/NCS );
#endif
for (i=0; i<num_preambles; i++) {
int index = (rootSequenceIndex+i+preamble_offset) % N_ZC;
if (prach_fmt<4) {
// prach_root_sequence_map points to prach_root_sequence_map0_3
DevAssert( index < sizeof(prach_root_sequence_map_0_3) / sizeof(prach_root_sequence_map_0_3[0]) );
} else {
// prach_root_sequence_map points to prach_root_sequence_map4
DevAssert( index < sizeof(prach_root_sequence_map_abc) / sizeof(prach_root_sequence_map_abc[0]) );
}
u = prach_root_sequence_map[index];
inv_u = nr_ZC_inv[u]; // multiplicative inverse of u
// X_u[0] stores the first ZC sequence where the root u has a non-zero number of shifts
// for the unrestricted case X_u[0] is the first root indicated by the rootSequenceIndex
for (k=0; k<N_ZC; k++) {
// 420 is the multiplicative inverse of 2 (required since ru is exp[j 2\pi n])
X_u[i][k] = ((uint32_t*)nr_ru)[(((k*(1+(inv_u*k)))%N_ZC)*420)%N_ZC];
}
}
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_OUT);
}
void init_nr_prach_tables(int N_ZC)
{
int i,m;
// Compute the modular multiplicative inverse 'iu' of u s.t. iu*u = 1 mod N_ZC
nr_ZC_inv[0] = 0;
nr_ZC_inv[1] = 1;
for (i=2; i<N_ZC; i++) {
for (m=2; m<N_ZC; m++)
if (((i*m)%N_ZC) == 1) {
nr_ZC_inv[i] = m;
break;
}
#ifdef PRACH_DEBUG
if (i<16)
printf("i %d : inv %d\n",i,nr_ZC_inv[i]);
#endif
}
// Compute quantized roots of unity
for (i=0; i<N_ZC; i++) {
nr_ru[i<<1] = (int16_t)(floor(32767.0*cos(2*M_PI*(double)i/N_ZC)));
nr_ru[1+(i<<1)] = (int16_t)(floor(32767.0*sin(2*M_PI*(double)i/N_ZC)));
#ifdef PRACH_DEBUG
if (i<16)
printf("i %d : runity %d,%d\n",i,nr_ru[i<<1],nr_ru[1+(i<<1)]);
#endif
}
}
int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe, uint16_t Nf )
......@@ -569,15 +71,16 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
//lte_frame_type_t frame_type = ue->frame_parms.frame_type;
//uint8_t tdd_config = ue->frame_parms.tdd_config;
uint16_t rootSequenceIndex = ue->frame_parms.prach_config_common.rootSequenceIndex;
uint8_t prach_ConfigIndex = ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
uint8_t Ncs_config = ue->frame_parms.prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig;
uint8_t restricted_set = ue->frame_parms.prach_config_common.prach_ConfigInfo.highSpeedFlag;
//uint8_t n_ra_prboffset = ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_FreqOffset;
NR_DL_FRAME_PARMS *fp=&ue->frame_parms;
uint16_t rootSequenceIndex = fp->prach_config_common.rootSequenceIndex;
uint8_t prach_ConfigIndex = fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex;
uint8_t Ncs_config = fp->prach_config_common.prach_ConfigInfo.zeroCorrelationZoneConfig;
uint8_t restricted_set = fp->prach_config_common.prach_ConfigInfo.highSpeedFlag;
//uint8_t n_ra_prboffset = fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart;
uint8_t preamble_index = ue->prach_resources[eNB_id]->ra_PreambleIndex;
//uint8_t tdd_mapindex = ue->prach_resources[eNB_id]->ra_TDD_map_index;
int16_t *prachF = ue->prach_vars[eNB_id]->prachF;
static int16_t prach_tmp[45600*4] __attribute__((aligned(32)));
int16_t prach_tmp[98304*2*4] __attribute__((aligned(32)));
int16_t *prach = prach_tmp;
int16_t *prach2;
int16_t amp = ue->prach_vars[eNB_id]->amp;
......@@ -589,7 +92,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
uint16_t preamble_index0,n_shift_ra,n_shift_ra_bar;
uint16_t d_start,numshift;
uint16_t prach_fmt = get_nr_prach_fmt(prach_ConfigIndex);
uint16_t prach_fmt = get_nr_prach_fmt(prach_ConfigIndex,fp->frame_type,fp->freq_range);
//uint8_t Nsp=2;
//uint8_t f_ra,t1_ra;
uint16_t N_ZC = (prach_fmt<4)?839:139;
......@@ -603,9 +106,9 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
int i, prach_len=0;
uint16_t first_nonzero_root_idx=0;
#if defined(EXMIMO) || defined(OAI_USRP)
prach_start = (ue->rx_offset+subframe*ue->frame_parms.samples_per_tti-ue->hw_timing_advance-ue->N_TA_offset);
#ifdef PRACH_DEBUG
#if defined(OAI_USRP)
prach_start = (ue->rx_offset+subframe*(fp->samples_per_tti<<1)-ue->hw_timing_advance-ue->N_TA_offset);
#ifdef NR_PRACH_DEBUG
LOG_I(PHY,"[UE %d] prach_start %d, rx_offset %d, hw_timing_advance %d, N_TA_offset %d\n", ue->Mod_id,
prach_start,
ue->rx_offset,
......@@ -614,13 +117,13 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
#endif
if (prach_start<0)
prach_start+=(ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME);
prach_start+=((fp->samples_per_tti<<1)*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME);
if (prach_start>=(ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME))
prach_start-=(ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME);
if (prach_start>=((fp->samples_per_tti<<1)*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME))
prach_start-=((fp->samples_per_tti<<1)*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME);
#else //normal case (simulation)
prach_start = subframe*ue->frame_parms.samples_per_tti-ue->N_TA_offset;
prach_start = subframe*(fp->samples_per_tti<<1)-ue->N_TA_offset;
LOG_I(PHY,"[UE %d] prach_start %d, rx_offset %d, hw_timing_advance %d, N_TA_offset %d\n", ue->Mod_id,
prach_start,
ue->rx_offset,
......@@ -659,10 +162,10 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
NCS = NCS_unrestricted_delta_f_RA_15[Ncs_config];
}
n_ra_prb = ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_FreqOffset;
n_ra_prb = fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart;
// n_ra_prb = get_nr_prach_prb_offset(&(ue->frame_parms),
// ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_ConfigIndex,
// ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_FreqOffset,
// fp->prach_config_common.prach_ConfigInfo.prach_ConfigIndex,
// fp->prach_config_common.prach_ConfigInfo.msg1_frequencystart,
// tdd_mapindex,
// Nf,
// prach_fmt);
......@@ -683,7 +186,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
if ((f_ra&1) == 0) {
n_ra_prb = n_ra_prboffset + 6*(f_ra>>1);
} else {
n_ra_prb = ue->frame_parms.N_RB_UL - 6 - n_ra_prboffset + 6*(f_ra>>1);
n_ra_prb = fp->N_RB_UL - 6 - n_ra_prboffset + 6*(f_ra>>1);
}
} else {
if ((tdd_config >2) && (tdd_config<6))
......@@ -694,7 +197,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
if ((((Nf&1)*(2-Nsp)+t1_ra)&1) == 0) {
n_ra_prb = 6*f_ra;
} else {
n_ra_prb = ue->frame_parms.N_RB_UL - 6*(f_ra+1);
n_ra_prb = fp->N_RB_UL - 6*(f_ra+1);
}
}
}
......@@ -709,7 +212,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
preamble_shift *= NCS;
} else { // This is the high-speed case
#ifdef PRACH_DEBUG
#ifdef NR_PRACH_DEBUG
LOG_I(PHY,"[UE %d] High-speed mode, NCS_config %d\n",ue->Mod_id,Ncs_config);
#endif
......@@ -768,11 +271,11 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
}
// now generate PRACH signal
#ifdef PRACH_DEBUG
#ifdef NR_PRACH_DEBUG
if (NCS>0)
LOG_I(PHY,"Generate PRACH for RootSeqIndex %d, Preamble Index %d, NCS %d (NCS_config %d, N_ZC/NCS %d) n_ra_prb %d: Preamble_offset %d, Preamble_shift %d\n",
rootSequenceIndex,preamble_index,NCS,Ncs_config,N_ZC/NCS,n_ra_prb,
LOG_I(PHY,"Generate PRACH for RootSeqIndex %d, Preamble Index %d, PRACH Format %x, prach_ConfigIndex %d, NCS %d (NCS_config %d, N_ZC/NCS %d) n_ra_prb %d: Preamble_offset %d, Preamble_shift %d\n",
rootSequenceIndex,preamble_index,prach_fmt,prach_ConfigIndex,NCS,Ncs_config,N_ZC/NCS,n_ra_prb,
preamble_offset,preamble_shift);
#endif
......@@ -780,10 +283,10 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
// nsymb = (frame_parms->Ncp==0) ? 14:12;
// subframe_offset = (unsigned int)frame_parms->ofdm_symbol_size*subframe*nsymb;
k = (12*n_ra_prb) - 6*ue->frame_parms.N_RB_UL;
k = (12*n_ra_prb) - 6*fp->N_RB_UL;
if (k<0)
k+=ue->frame_parms.ofdm_symbol_size;
k+=fp->ofdm_symbol_size;
k*=12;
k+=13;
......@@ -791,13 +294,13 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
Xu = (int16_t*)ue->X_u[preamble_offset-first_nonzero_root_idx];
/*
k+=(12*ue->frame_parms.first_carrier_offset);
if (k>(12*ue->frame_parms.ofdm_symbol_size))
k-=(12*ue->frame_parms.ofdm_symbol_size);
k+=(12*fp->first_carrier_offset);
if (k>(12*fp->ofdm_symbol_size))
k-=(12*fp->ofdm_symbol_size);
*/
k*=2;
switch (ue->frame_parms.N_RB_UL) {
switch (fp->N_RB_UL) {
case 6:
memset((void*)prachF,0,4*1536);
break;
......@@ -819,7 +322,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
break;
case 100:
if (ue->frame_parms.threequarter_fs == 0)
if (fp->threequarter_fs == 0)
memset((void*)prachF,0,4*24576);
else
memset((void*)prachF,0,4*18432);
......@@ -854,7 +357,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
prachF[k++]= ((Xu_re*nr_ru[offset2<<1]) - (Xu_im*nr_ru[1+(offset2<<1)]))>>15;
prachF[k++]= ((Xu_im*nr_ru[offset2<<1]) + (Xu_re*nr_ru[1+(offset2<<1)]))>>15;
if (k==(12*2*ue->frame_parms.ofdm_symbol_size))
if (k==(12*2*fp->ofdm_symbol_size))
k=0;
}
......@@ -865,7 +368,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
break;
case 1:
Ncp = 21024;
Ncp = 2*21024;
break;
case 2:
......@@ -916,254 +419,294 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
Ncp = 3168;
break;
}
#if 0 // code for LTE
switch (prach_fmt) {
case 0:
Ncp = 3168;
break;
case 1:
case 3:
Ncp = 21024;
break;
case 2:
Ncp = 6240;
break;
case 4:
Ncp = 448;
break;
default:
Ncp = 3168;
break;
}
switch (ue->frame_parms.N_RB_UL) {
case 6:
Ncp>>=4;
prach+=4; // makes prach2 aligned to 128-bit
break;
case 15:
Ncp>>=3;
break;
case 25:
Ncp>>=2;
break;
case 50:
Ncp>>=1;
break;
case 75:
Ncp=(Ncp*3)>>2;
break;
}
#endif
if (ue->frame_parms.threequarter_fs == 1)
Ncp=(Ncp*3)>>2;
if (fp->N_RB_UL <= 100)
AssertFatal(1==0,"N_RB_UL %d not supported for NR PRACH yet\n",fp->N_RB_UL);
else if (fp->N_RB_UL < 137) { // 46.08 or 61.44 Ms/s
if (fp->threequarter_fs==0) { //61.44 Ms/s
// This is after cyclic prefix (Ncp<<1 samples for 30.72 Ms/s, Ncp<<2 samples for 61.44 Ms/s
prach2 = prach+(Ncp<<1);
// do IDFT
switch (ue->frame_parms.N_RB_UL) {
case 6:
if (prach_fmt == 4) {
idft256(prachF,prach2,1);
memmove( prach, prach+512, Ncp<<2 );
prach_len = 256+Ncp;
} else {
idft1536(prachF,prach2,1);
memmove( prach, prach+3072, Ncp<<2 );
prach_len = 1536+Ncp;
if (prach_fmt>1) {
memmove( prach2+3072, prach2, 6144 );
prach_len = 2*1536+Ncp;
}
}
break;
case 15:
if (prach_fmt == 4) {
idft512(prachF,prach2,1);
//TODO: account for repeated format in dft output
memmove( prach, prach+1024, Ncp<<2 );
prach_len = 512+Ncp;
} else {
idft3072(prachF,prach2,1);
memmove( prach, prach+6144, Ncp<<2 );
prach_len = 3072+Ncp;
if (prach_fmt>1) {
memmove( prach2+6144, prach2, 12288 );
prach_len = 2*3072+Ncp;
}
}
break;
case 25:
default:
if (prach_fmt == 4) {
idft1024(prachF,prach2,1);
memmove( prach, prach+2048, Ncp<<2 );
prach_len = 1024+Ncp;
} else {
idft6144(prachF,prach2,1);
/*for (i=0;i<6144*2;i++)
prach2[i]<<=1;*/
memmove( prach, prach+12288, Ncp<<2 );
prach_len = 6144+Ncp;
if (prach_fmt>1) {
memmove( prach2+12288, prach2, 24576 );
prach_len = 2*6144+Ncp;
}
}
break;
case 50:
if (prach_fmt == 4) {
idft2048(prachF,prach2,1);
memmove( prach, prach+4096, Ncp<<2 );
prach_len = 2048+Ncp;
} else {
idft12288(prachF,prach2,1);
memmove( prach, prach+24576, Ncp<<2 );
prach_len = 12288+Ncp;
if (prach_fmt>1) {
memmove( prach2+24576, prach2, 49152 );
prach_len = 2*12288+Ncp;
}
}
break;
case 75:
if (prach_fmt == 4) {
idft3072(prachF,prach2,1);
//TODO: account for repeated format in dft output
memmove( prach, prach+6144, Ncp<<2 );
prach_len = 3072+Ncp;
} else {
idft18432(prachF,prach2,1);
memmove( prach, prach+36864, Ncp<<2 );
prach_len = 18432+Ncp;
if (prach_fmt>1) {
memmove( prach2+36834, prach2, 73728 );
prach_len = 2*18432+Ncp;
}
}
break;
case 100:
if (ue->frame_parms.threequarter_fs == 0) {
if (prach_fmt == 4) {
idft4096(prachF,prach2,1);
memmove( prach, prach+8192, Ncp<<2 );
prach_len = 4096+Ncp;
} else {
idft24576(prachF,prach2,1);
memmove( prach, prach+49152, Ncp<<2 );
prach_len = 24576+Ncp;
if (prach_fmt>1) {
memmove( prach2+49152, prach2, 98304 );
prach_len = 2* 24576+Ncp;
}
}
}
else {
if (prach_fmt == 4) {
idft3072(prachF,prach2,1);
//TODO: account for repeated format in dft output
memmove( prach, prach+6144, Ncp<<2 );
prach_len = 3072+Ncp;
} else {
idft18432(prachF,prach2,1);
memmove( prach, prach+36864, Ncp<<2 );
prach_len = 18432+Ncp;
printf("Generated prach for 100 PRB, 3/4 sampling\n");
if (prach_fmt>1) {
memmove( prach2+36834, prach2, 73728 );
prach_len = 2*18432+Ncp;
}
}
}
break;
case 106:
if (prach_fmt == 0) { //24576 samples @ 30.72 Ms/s, 49152 samples @ 61.44 Ms/s
dft49152(prachF,prach2,1);
// here we have |empty | Prach49152|
memmove(prach,prach+(49152<<1),(Ncp<<3));
// here we have |Prefix | Prach49152|
}
else if (prach_fmt == 1) { //24576 samples @ 30.72 Ms/s, 49152 samples @ 61.44 Ms/s
dft49152(prachF,prach2,1);
memmove(prach2+(49152<<1),prach2,(49152<<2));
// here we have |empty | Prach49152 | Prach49152|
memmove(prach,prach+(49152<<2),(Ncp<<3));
// here we have |Prefix | Prach49152 | Prach49152|
}
else if (prach_fmt == 2) { //24576 samples @ 30.72 Ms/s, 49152 samples @ 61.44 Ms/s
dft49152(prachF,prach2,1);
memmove(prach2+(49152<<1),prach2,(49152<<2));
// here we have |empty | Prach49152 | Prach49152| empty49152 | empty49152
memmove(prach2+(49152<<2),prach2,(49152<<3));
// here we have |empty | Prach49152 | Prach49152| Prach49152 | Prach49152
memmove(prach,prach+(49152<<3),(Ncp<<3));
// here we have |Prefix | Prach49152 | Prach49152| Prach49152 | Prach49152
}
else if (prach_fmt == 3) { // //6144 samples @ 30.72 Ms/s, 12288 samples @ 61.44 Ms/s
dft12288(prachF,prach2,1);
memmove(prach2+(12288<<1),prach2,(12288<<2));
// here we have |empty | Prach12288 | Prach12288| empty12288 | empty12288
memmove(prach2+(12288<<2),prach2,(12288<<3));
// here we have |empty | Prach12288 | Prach12288| Prach12288 | Prach12288
memmove(prach,prach+(12288<<3),(Ncp<<3));
// here we have |Prefix | Prach12288 | Prach12288| Prach12288 | Prach12288
}
else if (prach_fmt == 0xa1 || prach_fmt == 0xb1 || prach_fmt == 0xc0) {
dft2048(prachF,prach2,1);
// here we have |empty | Prach2048 |
if (prach_fmt != 0xc0)
memmove(prach2+(2048<<1),prach2,(2048<<2));
memmove(prach,prach+(2048<<1),(Ncp<<2));
// here we have |Prefix | Prach2048 | Prach2048 (if ! 0xc0) |
}
else if (prach_fmt == 0xa3 || prach_fmt == 0xb3) { // 6x2048
dft2048(prachF,prach2,1);
// here we have |empty | Prach2048 |
memmove(prach2+(2048<<1),prach2,(2048<<2));
// here we have |empty | Prach2048 | Prach2048| empty2048 | empty2048 | empty2048 | empty2048
memmove(prach2+(2048<<2),prach2,(2048<<3));
// here we have |empty | Prach2048 | Prach2048| Prach2048 | Prach2048 | empty2048 | empty2048
memmove(prach2+((2048<<1)*3),prach2,(2048<<3));
// here we have |empty | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048
memmove(prach,prach+(2048<<1),(Ncp<<2));
// here we have |Prefix | Prach2048 |
}
else if (prach_fmt == 0xb4) { // 12x2048
dft2048(prachF,prach2,1);
// here we have |empty | Prach2048 |
memmove(prach2+(2048<<1),prach2,(2048<<2));
// here we have |empty | Prach2048 | Prach2048| empty2048 | empty2048 | empty2048 | empty2048
memmove(prach2+(2048<<2),prach2,(2048<<3));
// here we have |empty | Prach2048 | Prach2048| Prach2048 | Prach2048 | empty2048 | empty2048
memmove(prach2+(2048<<3),prach2,(2048<<3));
// here we have |empty | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048
memmove(prach2+(2048<<1)*6,prach2,(2048<<2)*6);
// here we have |empty | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048 | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048|
memmove(prach,prach+(2048<<1),(Ncp<<2));
// here we have |Prefix | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048 | Prach2048 | Prach2048| Prach2048 | Prach2048 | Prach2048 | Prach2048|
}
}
else { // 46.08 Ms/s
Ncp = (Ncp*3)/4;
if (prach_fmt == 0) {
idft24576(prachF,prach2,1);
memmove(prach, prach+49152, Ncp<<2);
prach_len = 24576+Ncp;
}
if (prach_fmt == 1) {
idft24576(prachF,prach2,1);
memmove(prach2+49152, prach2, 98304);
memmove(prach, prach+49152, Ncp<<2);
prach_len = 2 * 24576 + Ncp;
dft36864(prachF,prach2,1);
// here we have |empty | Prach73728|
memmove(prach,prach+(36864<<1),(Ncp<<2));
// here we have |Prefix | Prach73728|
}
else if (prach_fmt == 1) {
dft36864(prachF,prach2,1);
memmove(prach2+(36864<<1),prach2,(36864<<2));
// here we have |empty | Prach73728 | Prach73728|
memmove(prach,prach+(36864<<2),(Ncp<<3));
// here we have |Prefix | Prach73728 | Prach73728|
}
if (prach_fmt == 2) {
idft24576(prachF,prach2,1);
memmove(prach2+49152, prach2, 98304);
memmove(prach2+98304, prach2, 98304);
memmove(prach, prach+49152, Ncp<<2);
prach_len = 4 * 24576 + Ncp;
}
if (prach_fmt == 3) {
idft6144(prachF,prach2,1);
memmove(prach2+6144, prach2, 12288);
memmove(prach2+12288, prach2, 12288);
memmove(prach, prach+12288, Ncp<<2);
prach_len = 4 * 6144 + Ncp;
}
// For FR2
if (prach_fmt == 0xa1) { // we consider numderology mu = 1
idft1024(prachF,prach2,1);
memmove(prach2+2048, prach2, 4096);
memmove(prach, prach+2048, Ncp<<2);
prach_len = 2 * 1024 + Ncp;
}
#if 0
if (prach_fmt == 0xa2) {
}
if (prach_fmt == 0xa3) {
}
if (prach_fmt == 0xb1) {
}
if (prach_fmt == 0xb2) {
}
if (prach_fmt == 0xb3) {
}
if (prach_fmt == 0xb4) {
}
if (prach_fmt == 0xc0) {
}
if (prach_fmt == 0xc2) {
}
#endif
break;
dft36864(prachF,prach2,1);
memmove(prach2+(36864<<1),prach2,(36864<<2));
// here we have |empty | Prach73728 | Prach73728| empty73728 | empty73728
memmove(prach2+(36864<<2),prach2,(36864<<3));
// here we have |empty | Prach73728 | Prach73728| Prach73728 | Prach73728
memmove(prach,prach+(36864<<3),(Ncp<<3));
// here we have |Prefix | Prach73728 | Prach73728| Prach73728 | Prach73728
}
else if (prach_fmt == 3) {
dft9216(prachF,prach2,1);
memmove(prach2+(9216<<1),prach2,(9216<<2));
// here we have |empty | Prach9216 | Prach9216| empty9216 | empty9216
memmove(prach2+(9216<<2),prach2,(9216<<3));
// here we have |empty | Prach9216 | Prach9216| Prach9216 | Prach9216
memmove(prach,prach+(9216<<3),(Ncp<<3));
// here we have |Prefix | Prach9216 | Prach9216| Prach9216 | Prach9216
}
else if (prach_fmt == 0xa1 || prach_fmt == 0xb1 || prach_fmt == 0xc0) {
dft1536(prachF,prach2,1);
// here we have |empty | Prach1536 |
if (prach_fmt != 0xc0)
memmove(prach2+(1536<<1),prach2,(1536<<2));
memmove(prach,prach+(1536<<1),(Ncp<<2));
// here we have |Prefix | Prach1536 | Prach1536 (if ! 0xc0) |
}
else if (prach_fmt == 0xa3 || prach_fmt == 0xb3) { // 6x1536
dft1536(prachF,prach2,1);
// here we have |empty | Prach1536 |
memmove(prach2+(1536<<1),prach2,(1536<<2));
// here we have |empty | Prach1536 | Prach1536| empty1536 | empty1536 | empty1536 | empty1536
memmove(prach2+(1536<<2),prach2,(1536<<3));
// here we have |empty | Prach1536 | Prach1536| Prach1536 | Prach1536 | empty1536 | empty1536
memmove(prach2+((1536<<1)*3),prach2,(1536<<3));
// here we have |empty | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536
memmove(prach,prach+(1536<<1),(Ncp<<2));
// here we have |Prefix | Prach1536 |
}
else if (prach_fmt == 0xb4) { // 12x1536
dft1536(prachF,prach2,1);
// here we have |empty | Prach1536 |
memmove(prach2+(1536<<1),prach2,(1536<<2));
// here we have |empty | Prach1536 | Prach1536| empty1536 | empty1536 | empty1536 | empty1536
memmove(prach2+(1536<<2),prach2,(1536<<3));
// here we have |empty | Prach1536 | Prach1536| Prach1536 | Prach1536 | empty1536 | empty1536
memmove(prach2+(1536<<3),prach2,(1536<<3));
// here we have |empty | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536
memmove(prach2+(1536<<1)*6,prach2,(1536<<2)*6);
// here we have |empty | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536 | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536|
memmove(prach,prach+(1536<<1),(Ncp<<2));
// here we have |Prefix | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536 | Prach1536 | Prach1536| Prach1536 | Prach1536 | Prach1536 | Prach1536|
}
}
}
else if (fp->N_RB_UL <= 273) {// 92.16 or 122.88 Ms/s
if (fp->threequarter_fs==0) { //122.88 Ms/s
if (prach_fmt == 0) { //24576 samples @ 30.72 Ms/s, 98304 samples @ 122.88 Ms/s
dft98304(prachF,prach2,1);
// here we have |empty | Prach98304|
memmove(prach,prach+(98304<<1),(Ncp<<4));
// here we have |Prefix | Prach98304|
}
else if (prach_fmt == 1) {
dft98304(prachF,prach2,1);
memmove(prach2+(98304<<1),prach2,(98304<<2));
// here we have |empty | Prach98304 | Prach98304|
memmove(prach,prach+(98304<<2),(Ncp<<4));
// here we have |Prefix | Prach98304 | Prach98304|
}
else if (prach_fmt == 2) {
dft98304(prachF,prach2,1);
memmove(prach2+(98304<<1),prach2,(98304<<2));
// here we have |empty | Prach98304 | Prach98304| empty98304 | empty98304
memmove(prach2+(98304<<2),prach2,(98304<<3));
// here we have |empty | Prach98304 | Prach98304| Prach98304 | Prach98304
memmove(prach,prach+(98304<<3),(Ncp<<4));
// here we have |Prefix | Prach98304 | Prach98304| Prach98304 | Prach98304
}
else if (prach_fmt == 3) { // 4x6144, Ncp 3168
dft24576(prachF,prach2,1);
memmove(prach2+(24576<<1),prach2,(24576<<2));
// here we have |empty | Prach24576 | Prach24576| empty24576 | empty24576
memmove(prach2+(24576<<2),prach2,(24576<<3));
// here we have |empty | Prach24576 | Prach24576| Prach24576 | Prach24576
memmove(prach,prach+(24576<<3),(Ncp<<4));
// here we have |Prefix | Prach24576 | Prach24576| Prach24576 | Prach24576
}
else if (prach_fmt == 0xa1 || prach_fmt == 0xb1 || prach_fmt == 0xc0) {
dft4096(prachF,prach2,1);
// here we have |empty | Prach4096 |
if (prach_fmt != 0xc0)
memmove(prach2+(4096<<1),prach2,(4096<<2));
memmove(prach,prach+(4096<<1),(Ncp<<2));
// here we have |Prefix | Prach4096 | Prach4096 (if ! 0xc0) |
}
else if (prach_fmt == 0xa3 || prach_fmt == 0xb3) { // 6x4096
dft4096(prachF,prach2,1);
// here we have |empty | Prach4096 |
memmove(prach2+(4096<<1),prach2,(4096<<2));
// here we have |empty | Prach4096 | Prach4096| empty4096 | empty4096 | empty4096 | empty4096
memmove(prach2+(4096<<2),prach2,(4096<<3));
// here we have |empty | Prach4096 | Prach4096| Prach4096 | Prach4096 | empty4096 | empty4096
memmove(prach2+((4096<<1)*3),prach2,(4096<<3));
// here we have |empty | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096
memmove(prach,prach+(4096<<1),(Ncp<<2));
// here we have |Prefix | Prach4096 |
}
else if (prach_fmt == 0xb4) { // 12x4096
dft4096(prachF,prach2,1);
// here we have |empty | Prach4096 |
memmove(prach2+(4096<<1),prach2,(4096<<2));
// here we have |empty | Prach4096 | Prach4096| empty4096 | empty4096 | empty4096 | empty4096
memmove(prach2+(4096<<2),prach2,(4096<<3));
// here we have |empty | Prach4096 | Prach4096| Prach4096 | Prach4096 | empty4096 | empty4096
memmove(prach2+(4096<<3),prach2,(4096<<3));
// here we have |empty | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096
memmove(prach2+(4096<<1)*6,prach2,(4096<<2)*6);
// here we have |empty | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096 | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096|
memmove(prach,prach+(4096<<1),(Ncp<<2));
// here we have |Prefix | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096 | Prach4096 | Prach4096| Prach4096 | Prach4096 | Prach4096 | Prach4096|
}
}
else { // 92.16 Ms/s
Ncp = (Ncp*3)/4;
if (prach_fmt == 0) {
dft73728(prachF,prach2,1);
// here we have |empty | Prach73728|
memmove(prach,prach+(73728<<1),(Ncp<<4));
// here we have |Prefix | Prach73728|
}
else if (prach_fmt == 1) {
dft73728(prachF,prach2,1);
memmove(prach2+(73728<<1),prach2,(73728<<2));
// here we have |empty | Prach73728 | Prach73728|
memmove(prach,prach+(73728<<2),(Ncp<<4));
// here we have |Prefix | Prach73728 | Prach73728|
}
//LOG_I(PHY,"prach_len=%d\n",prach_len);
// AssertFatal(prach_fmt<4,
// "prach_fmt4 not fully implemented" );
#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
if (prach_fmt == 2) {
dft73728(prachF,prach2,1);
memmove(prach2+(73728<<1),prach2,(73728<<2));
// here we have |empty | Prach73728 | Prach73728| empty73728 | empty73728
memmove(prach2+(73728<<2),prach2,(73728<<3));
// here we have |empty | Prach73728 | Prach73728| Prach73728 | Prach73728
memmove(prach,prach+(73728<<3),(Ncp<<4));
// here we have |Prefix | Prach73728 | Prach73728| Prach73728 | Prach73728
}
else if (prach_fmt == 3) {
dft18432(prachF,prach2,1);
memmove(prach2+(18432<<1),prach2,(18432<<2));
// here we have |empty | Prach18432 | Prach18432| empty18432 | empty18432
memmove(prach2+(18432<<2),prach2,(18432<<3));
// here we have |empty | Prach18432 | Prach18432| Prach18432 | Prach18432
memmove(prach,prach+(18432<<3),(Ncp<<4));
// here we have |Prefix | Prach18432 | Prach18432| Prach18432 | Prach18432
}
else if (prach_fmt == 0xa1 || prach_fmt == 0xb1 || prach_fmt == 0xc0) {
dft3072(prachF,prach2,1);
// here we have |empty | Prach3072 |
if (prach_fmt != 0xc0)
memmove(prach2+(3072<<1),prach2,(3072<<2));
memmove(prach,prach+(3072<<1),(Ncp<<2));
// here we have |Prefix | Prach3072 | Prach3072 (if ! 0xc0) |
}
else if (prach_fmt == 0xa3 || prach_fmt == 0xb3) { // 6x3072
dft3072(prachF,prach2,1);
// here we have |empty | Prach3072 |
memmove(prach2+(3072<<1),prach2,(3072<<2));
// here we have |empty | Prach3072 | Prach3072| empty3072 | empty3072 | empty3072 | empty3072
memmove(prach2+(3072<<2),prach2,(3072<<3));
// here we have |empty | Prach3072 | Prach3072| Prach3072 | Prach3072 | empty3072 | empty3072
memmove(prach2+((3072<<1)*3),prach2,(3072<<3));
// here we have |empty | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072
memmove(prach,prach+(3072<<1),(Ncp<<2));
// here we have |Prefix | Prach3072 |
}
else if (prach_fmt == 0xb4) { // 12x3072
dft3072(prachF,prach2,1);
// here we have |empty | Prach3072 |
memmove(prach2+(3072<<1),prach2,(3072<<2));
// here we have |empty | Prach3072 | Prach3072| empty3072 | empty3072 | empty3072 | empty3072
memmove(prach2+(3072<<2),prach2,(3072<<3));
// here we have |empty | Prach3072 | Prach3072| Prach3072 | Prach3072 | empty3072 | empty3072
memmove(prach2+(3072<<3),prach2,(3072<<3));
// here we have |empty | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072
memmove(prach2+(3072<<1)*6,prach2,(3072<<2)*6);
// here we have |empty | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072 | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072|
memmove(prach,prach+(3072<<1),(Ncp<<2));
// here we have |Prefix | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072 | Prach3072 | Prach3072| Prach3072 | Prach3072 | Prach3072 | Prach3072|
}
}
}
#if defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
int j;
int overflow = prach_start + prach_len - LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*ue->frame_parms.samples_per_tti;
int overflow = prach_start + prach_len - LTE_NUMBER_OF_SUBFRAMES_PER_FRAME*(fp->samples_per_tti<<1);
LOG_I( PHY, "prach_start=%d, overflow=%d\n", prach_start, overflow );
for (i=prach_start,j=0; i<min(ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME,prach_start+prach_len); i++,j++) {
for (i=prach_start,j=0; i<min((fp->samples_per_tti<<1)*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME,prach_start+prach_len); i++,j++) {
((int16_t*)ue->common_vars.txdata[0])[2*i] = prach[2*j];
((int16_t*)ue->common_vars.txdata[0])[2*i+1] = prach[2*j+1];
}
......@@ -1172,24 +715,11 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
((int16_t*)ue->common_vars.txdata[0])[2*i] = prach[2*j];
((int16_t*)ue->common_vars.txdata[0])[2*i+1] = prach[2*j+1];
}
#if defined(EXMIMO)
// handle switch before 1st TX subframe, guarantee that the slot prior to transmission is switch on
for (k=prach_start - (ue->frame_parms.samples_per_tti>>1) ; k<prach_start ; k++) {
if (k<0)
ue->common_vars.txdata[0][k+ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME] &= 0xFFFEFFFE;
else if (k>(ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME))
ue->common_vars.txdata[0][k-ue->frame_parms.samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME] &= 0xFFFEFFFE;
else
ue->common_vars.txdata[0][k] &= 0xFFFEFFFE;
}
#endif
#else
for (i=0; i<prach_len; i++) {
((int16_t*)(&ue->common_vars.txdata[0][prach_start]))[2*i] = prach[2*i];
((int16_t*)(&ue->common_vars.txdata[0][prach_start]))[2*i+1] = prach[2*i+1];
}
#endif
......@@ -1197,7 +727,7 @@ int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe,
#if defined(PRACH_WRITE_OUTPUT_DEBUG)
LOG_M("prach_txF0.m","prachtxF0",prachF,prach_len-Ncp,1,1);
LOG_M("prach_tx0.m","prachtx0",prach+(Ncp<<1),prach_len-Ncp,1,1);
LOG_M("txsig.m","txs",(int16_t*)(&ue->common_vars.txdata[0][0]),2*ue->frame_parms.samples_per_tti,1,1);
LOG_M("txsig.m","txs",(int16_t*)(&ue->common_vars.txdata[0][0]),4*fp->samples_per_tti,1,1);
exit(-1);
#endif
......
......@@ -1804,5 +1804,7 @@ int nr_extract_dci_info(PHY_VARS_NR_UE *ue,
uint16_t crc_scrambled_values[TOTAL_NBR_SCRAMBLED_VALUES]);
int32_t generate_nr_prach( PHY_VARS_NR_UE *ue, uint8_t eNB_id, uint8_t subframe, uint16_t Nf );
/**@}*/
#endif
......@@ -5692,6 +5692,18 @@ void dft6144(int16_t *input, int16_t *output,int scale)
}
int16_t twa9216[6144] __attribute__((aligned(32)));
int16_t twb9216[6144] __attribute__((aligned(32)));
// 3072 x 3
void dft9216(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
void idft9216(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
int16_t twa12288[8192] __attribute__((aligned(32)));
int16_t twb12288[8192] __attribute__((aligned(32)));
......@@ -6042,6 +6054,59 @@ void idft24576(int16_t *input, int16_t *output,int scale)
}
}
int16_t twa36864[24576] __attribute__((aligned(32)));
int16_t twb36884[24576] __attribute__((aligned(32)));
// 12288 x 3
void dft36864(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
void idft36864(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
int16_t twa49152[32768] __attribute__((aligned(32)));
int16_t twb49152[32768] __attribute__((aligned(32)));
// 16384 x 3
void dft49152(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
void idft49152(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
int16_t twa73728[49152] __attribute__((aligned(32)));
int16_t twb73728[49152] __attribute__((aligned(32)));
// 24576 x 3
void dft73728(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
void idft73728(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
int16_t twa98304[49152] __attribute__((aligned(32)));
int16_t twb98304[49152] __attribute__((aligned(32)));
// 32768 x 3
void dft98304(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
void idft98304(int16_t *input, int16_t *output,int scale) {
AssertFatal(1==0,"Need to do this ..\n");
}
/// THIS SECTION IS FOR ALL PUSCH DFTS (i.e. radix 2^a * 3^b * 4^c * 5^d)
/// They use twiddles for 4-way parallel DFTS (i.e. 4 DFTS with interleaved input/output)
......
......@@ -199,6 +199,21 @@ void dft3072(int16_t *sigF,int16_t *sig,int scale);
void dft24576(int16_t *sigF,int16_t *sig,int scale);
void dft49152(int16_t *sigF,int16_t *sig,int scale);
void idft49152(int16_t *sigF,int16_t *sig,int scale);
void dft9216(int16_t *sigF,int16_t *sig,int scale);
void idft9216(int16_t *sigF,int16_t *sig,int scale);
void dft36864(int16_t *sigF,int16_t *sig,int scale);
void idft36864(int16_t *sigF,int16_t *sig,int scale);
void dft98304(int16_t *sigF,int16_t *sig,int scale);
void idft98304(int16_t *sigF,int16_t *sig,int scale);
void dft73728(int16_t *sigF,int16_t *sig,int scale);
void idft73728(int16_t *sigF,int16_t *sig,int scale);
/*!\fn int32_t rotate_cpx_vector(int16_t *x,int16_t *alpha,int16_t *y,uint32_t N,uint16_t output_shift)
This function performs componentwise multiplication of a vector with a complex scalar.
......
......@@ -171,6 +171,17 @@ typedef struct {
int16_t sqrt_rho_b;
} NR_gNB_DLSCH_t;
typedef struct {
/// \brief ?.
/// first index: ? [0..1023] (hard coded)
int16_t *prachF;
/// \brief ?.
/// second index: rx antenna [0..63] (hard coded) \note Hard coded array size indexed by \c nb_antennas_rx.
/// third index: frequency-domain sample [0..ofdm_symbol_size*12[
int16_t **rxsigF;
/// \brief local buffer to compute prach_ifft
int32_t *prach_ifft;
} NR_gNB_PRACH;
typedef struct {
/// Nfapi ULSCH PDU
......@@ -573,7 +584,7 @@ typedef struct PHY_VARS_gNB_s {
LTE_eNB_UCI uci_vars[NUMBER_OF_UE_MAX];
LTE_eNB_SRS srs_vars[NUMBER_OF_UE_MAX];
LTE_eNB_PUSCH *pusch_vars[NUMBER_OF_UE_MAX];
LTE_eNB_PRACH prach_vars;
NR_gNB_PRACH prach_vars;
NR_gNB_DLSCH_t *dlsch[NUMBER_OF_UE_MAX][2]; // Nusers times two spatial streams
NR_gNB_ULSCH_t *ulsch[NUMBER_OF_UE_MAX+1][2]; // [Nusers times + number of RA][2 codewords], index 0 in [NUMBER_OF_UE_MAX+1] is for RA
// LTE_eNB_ULSCH_t *ulsch[NUMBER_OF_UE_MAX+1]; // Nusers + number of RA
......@@ -606,6 +617,9 @@ typedef struct PHY_VARS_gNB_s {
/// Indicator set to 0 after first SR
uint8_t first_sr[NUMBER_OF_UE_MAX];
/// PRACH root sequence
uint32_t X_u[64][839];
uint32_t max_peak_val;
/// \brief sinr for all subcarriers of the current link (used only for abstraction).
......
......@@ -93,6 +93,15 @@
#define NR_MAX_PDSCH_ENCODED_LENGTH NR_MAX_NB_RB*NR_SYMBOLS_PER_SLOT*NR_NB_SC_PER_RB*8*NR_MAX_NB_LAYERS // 8 is the maximum modulation order (it was 950984 before !!)
#define NR_MAX_PDSCH_TBS 3824
#define MAX_NUM_NR_DLSCH_SEGMENTS 16
#define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056)
#define MAX_NUM_NR_ULSCH_SEGMENTS MAX_NUM_NR_DLSCH_SEGMENTS
#define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056)
#define MAX_NUM_NR_CHANNEL_BITS (14*273*12*6) // 14 symbols, 273 RB
#define MAX_NUM_NR_RE (14*273*12)
typedef enum {
NR_MU_0=0,
NR_MU_1,
......@@ -181,23 +190,25 @@ typedef struct {
} nr_cce_t;
/// PRACH-ConfigInfo from 36.331 RRC spec
/// PRACH-ConfigInfo from 38.331 RRC spec
typedef struct {
/// Parameter: prach-ConfigurationIndex, see TS 36.211 (5.7.1). \vr{[0..63]}
/// Parameter: prach-ConfigurationIndex, see TS 38.211 (6.3.3.2).
uint8_t prach_ConfigIndex;
/// Parameter: High-speed-flag, see TS 36.211 (5.7.2). \vr{[0..1]} 1 corresponds to Restricted set and 0 to Unrestricted set.
/// Parameter: High-speed-flag, see TS 38.211 (6.3.3.1). 1 corresponds to Restricted set and 0 to Unrestricted set.
uint8_t highSpeedFlag;
/// Parameter: \f$N_\text{CS}\f$, see TS 36.211 (5.7.2). \vr{[0..15]}\n Refer to table 5.7.2-2 for preamble format 0..3 and to table 5.7.2-3 for preamble format 4.
/// Restricted Set Config (type A=0 , type B=1) TS 38.211 (6.3.3.1)
uint8_t restrictedSetConfig;
/// 38.211 (NCS 38.211 6.3.3.1).
uint8_t zeroCorrelationZoneConfig;
/// Parameter: prach-FrequencyOffset, see TS 36.211 (5.7.1). \vr{[0..94]}\n For TDD the value range is dependent on the value of \ref prach_ConfigIndex.
uint8_t prach_FreqOffset;
/// see TS 38.211 (6.3.3.2).
uint8_t msg1_frequencystart;
} NR_PRACH_CONFIG_INFO;
/// PRACH-ConfigSIB or PRACH-Config
typedef struct {
/// Parameter: RACH_ROOT_SEQUENCE, see TS 36.211 (5.7.1). \vr{[0..837]}
/// Parameter: prach-rootSequenceIndex, see TS 38.211 (6.3.3.2).
uint16_t rootSequenceIndex;
/// prach_Config_enabled=1 means enabled. \vr{[0..1]}
/// prach_Config_enabled=1 means enabled.}
uint8_t prach_Config_enabled;
/// PRACH Configuration Information
NR_PRACH_CONFIG_INFO prach_ConfigInfo;
......@@ -231,11 +242,9 @@ typedef struct NR_DL_FRAME_PARMS {
/// total Number of Resource Block Groups: this is ceil(N_PRB/P)
/// Frame type (0 FDD, 1 TDD)
lte_frame_type_t frame_type;
/// TDD subframe assignment (0-7) (default = 3) (254=RX only, 255=TX only)
uint8_t tdd_config;
/// TDD S-subframe configuration (0-9)
/// Cell ID
uint16_t Nid_cell;
/// subcarrier spacing (15,30,60,120)
uint32_t subcarrier_spacing;
/// 3/4 sampling
uint8_t threequarter_fs;
......@@ -290,7 +299,8 @@ typedef struct NR_DL_FRAME_PARMS {
uint8_t nushift;
/// SRS configuration from TS 38.331 RRC
SRS_NR srs_nr;
/// Power used by SSB in order to estimate signal strength and path loss
int ss_PBCH_BlockPower;
/// for NR TDD management
TDD_UL_DL_configCommon_t *p_tdd_UL_DL_Configuration;
......
......@@ -66,33 +66,10 @@ EXTERN const uint8_t N_slot_subframe[MU_NUMBER]
#endif
;
//#define RX_NB_TH_MAX 3
//#define RX_NB_TH 3
#if 0
#define LTE_SLOTS_PER_SUBFRAME (2)
#define LTE_NUMBER_OF_SUBFRAMES_PER_FRAME (10)
#define LTE_SLOTS_PER_FRAME (20)
#define LTE_CE_FILTER_LENGTH (5)
#define LTE_CE_OFFSET (LTE_CE_FILTER_LENGTH)
#define TX_RX_SWITCH_SYMBOL (NUMBER_OF_SYMBOLS_PER_FRAME>>1)
#define PBCH_PDU_SIZE (3) //bytes
#define PRACH_SYMBOL (3) //position of the UL PSS wrt 2nd slot of special subframe
#define NUMBER_OF_FREQUENCY_GROUPS (lte_frame_parms->N_RB_DL)
#define SSS_AMP (1148)
#define MAX_NUM_PHICH_GROUPS (56) //110 RBs Ng=2, p.60 36-212, Sec. 6.9
#define MAX_MBSFN_AREA (8)
#endif
#define NB_DL_DATA_TO_UL_ACK (8) /* size of table TS 38.213 Table 9.2.3-1 */
/***********************************************************************
*
* FUNCTIONALITY : System information type 1
......@@ -123,11 +100,6 @@ SystemInformationBlockType1_nr_t;
#define FRAME_DURATION_MICRO_SEC (10000) /* frame duration in microsecond */
typedef enum {
SLOT_DL = 0,
SLOT_UL = 1,
} nr_slot_t;
typedef enum {
ms0p5 = 500, /* duration is given in microsecond */
ms0p625 = 625,
......@@ -139,7 +111,7 @@ typedef enum {
ms10 = 10000,
} dl_UL_TransmissionPeriodicity_t;
typedef struct {
typedef struct TDD_UL_DL_configCommon_s {
/// Reference SCS used to determine the time domain boundaries in the UL-DL pattern which must be common across all subcarrier specific
/// virtual carriers, i.e., independent of the actual subcarrier spacing using for data transmission.
/// Only the values 15 or 30 kHz (<6GHz), 60 or 120 kHz (>6GHz) are applicable.
......@@ -163,7 +135,7 @@ typedef struct {
/// Corresponds to L1 parameter 'number-of-UL-symbols-common' (see 38.211, section FFS_Section)
uint8_t nrofUplinkSymbols;
/// \ for setting a sequence
struct TDD_UL_DL_configCommon_t *p_next_TDD_UL_DL_configCommon_t;
struct TDD_UL_DL_configCommon_s *p_next;
} TDD_UL_DL_configCommon_t;
typedef struct {
......@@ -362,9 +334,9 @@ typedef struct {
/***********************************************************************
*
* FUNCTIONALITY : Packed Downlink Shared Channel PDSCH
* FUNCTIONALITY : Physical Downlink Shared Channel PDSCH
*
* DESCRIPTION : interface description for PSCH configuration
* DESCRIPTION : interface description for PSDCH configuration
*
************************************************************************/
......@@ -1107,6 +1079,7 @@ typedef struct {
SchedulingRequestResourceConfig_t *sr_ResourceConfig[MAX_NR_OF_SR_CONFIG_PER_CELL_GROUP];
} scheduling_request_config_t;
#undef EXTERN
#undef INIT_VARIABLES_PHY_IMPLEMENTATION_DEFS_NR_H
#endif /* PHY_IMPL_DEFS_NR_H */
......@@ -149,10 +149,10 @@
#define OFDM_SYMBOL_SIZE_BYTES0 (OFDM_SYMBOL_SIZE_SAMPLES0*2)
#define OFDM_SYMBOL_SIZE_BYTES_NO_PREFIX (OFDM_SYMBOL_SIZE_SAMPLES_NO_PREFIX*2)
#define SLOT_LENGTH_BYTES (frame_parms->samples_per_tti<<1) // 4 bytes * samples_per_tti/2
#define SLOT_LENGTH_BYTES (frame_parms->samples_per_slot) // 4 bytes * samples_per_tti/2
#define SLOT_LENGTH_BYTES_NO_PREFIX (OFDM_SYMBOL_SIZE_BYTES_NO_PREFIX * NUMBER_OF_OFDM_SYMBOLS_PER_SLOT)
#define FRAME_LENGTH_COMPLEX_SAMPLES (frame_parms->samples_per_tti*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME)
#define FRAME_LENGTH_COMPLEX_SAMPLES (frame_parms->samples_per_subframe*LTE_NUMBER_OF_SUBFRAMES_PER_FRAME)
#define FRAME_LENGTH_SAMPLES (FRAME_LENGTH_COMPLEX_SAMPLES*2)
#define FRAME_LENGTH_SAMPLES_NO_PREFIX (NUMBER_OF_SYMBOLS_PER_FRAME*OFDM_SYMBOL_SIZE_SAMPLES_NO_PREFIX)
#define FRAME_LENGTH_COMPLEX_SAMPLES_NO_PREFIX (FRAME_LENGTH_SAMPLES_NO_PREFIX/2)
......
......@@ -78,12 +78,12 @@ int set_tdd_config_nr(NR_DL_FRAME_PARMS *frame_parms, int dl_UL_TransmissionPeri
int nb_periods_per_frame = (FRAME_DURATION_MICRO_SEC/dl_UL_TransmissionPeriodicity);
int nb_slots_per_period = (frame_parms->ttis_per_subframe * LTE_NUMBER_OF_SUBFRAMES_PER_FRAME)/nb_periods_per_frame;
int nb_slots_per_period = ((1<<frame_parms->numerology_index) * LTE_NUMBER_OF_SUBFRAMES_PER_FRAME)/nb_periods_per_frame;
if (nb_slots_per_period != (nrofDownlinkSlots + nrofUplinkSlots)) {
LOG_E(PHY,"set_tdd_configuration_nr: given period is inconsistent with current tdd configuration \n");
return (-1);
}
AssertFatal(nb_slots_per_period == (nrofDownlinkSlots + nrofUplinkSlots + 1),
"set_tdd_configuration_nr: given period is inconsistent with current tdd configuration, nrofDownlinkSlots %d, nrofUplinkSlots %d, nb_slots_per_period %d \n",
nrofDownlinkSlots,nrofUplinkSlots,nb_slots_per_period);
AssertFatal(nrofDownlinkSymbols + nrofUplinkSymbols < 14,"illegal symbol configuration DL %d, UL %d\n",nrofDownlinkSymbols,nrofUplinkSymbols);
while(slot_number != nb_slots_to_set) {
......
......@@ -1474,7 +1474,7 @@ if (ue->prach_resources[eNB_id]!=NULL) {
LOG_I(PHY,"mode %d\n",mode);
if ((ue->mac_enabled==1) && (mode != calib_prach_tx)) {
ue->tx_power_dBm[nr_tti_tx] = ue->prach_resources[eNB_id]->ra_PREAMBLE_RECEIVED_TARGET_POWER+get_PL(ue->Mod_id,ue->CC_id,eNB_id);
ue->tx_power_dBm[nr_tti_tx] = ue->prach_resources[eNB_id]->ra_PREAMBLE_RECEIVED_TARGET_POWER+get_nr_PL(ue,eNB_id);
}
else {
ue->tx_power_dBm[nr_tti_tx] = ue->tx_power_max_dBm;
......@@ -1486,7 +1486,7 @@ if (ue->prach_resources[eNB_id]!=NULL) {
frame_tx,
nr_tti_tx,
ue->prach_resources[eNB_id]->ra_PreambleIndex,
get_PL(ue->Mod_id,ue->CC_id,eNB_id),
get_nr_PL(ue,eNB_id),
ue->tx_power_dBm[nr_tti_tx],
ue->prach_resources[eNB_id]->ra_PREAMBLE_RECEIVED_TARGET_POWER,
ue->prach_resources[eNB_id]->ra_TDD_map_index,
......@@ -1518,7 +1518,7 @@ if (ue->prach_resources[eNB_id]!=NULL) {
// stop_meas(&ue->tx_prach);
LOG_D(PHY,"[UE %d][RAPROC] PRACH PL %d dB, power %d dBm, digital power %d dB (amp %d)\n",
ue->Mod_id,
get_PL(ue->Mod_id,ue->CC_id,eNB_id),
get_nr_PL(ue,eNB_id),
ue->tx_power_dBm[nr_tti_tx],
dB_fixed(prach_power),
ue->prach_vars[eNB_id]->amp);
......@@ -1537,8 +1537,8 @@ if (ue->prach_resources[eNB_id]!=NULL) {
LOG_I(PHY,"[UE %d][RAPROC] Frame %d, nr_tti_rx %d: Generating PRACH (eNB %d) preamble index %d for UL, TX power %d dBm (PL %d dB), l3msg \n",
ue->Mod_id,frame_tx,nr_tti_tx,eNB_id,
ue->prach_resources[eNB_id]->ra_PreambleIndex,
ue->prach_resources[eNB_id]->ra_PREAMBLE_RECEIVED_TARGET_POWER+get_PL(ue->Mod_id,ue->CC_id,eNB_id),
get_PL(ue->Mod_id,ue->CC_id,eNB_id));
ue->prach_resources[eNB_id]->ra_PREAMBLE_RECEIVED_TARGET_POWER+get_nr_PL(ue,eNB_id),
get_nr_PL(ue,eNB_id));
}
......@@ -1932,7 +1932,7 @@ if (abstraction_flag == 0) {
#endif
#if T_TRACER
T(T_UE_PHY_PUSCH_TX_POWER, T_INT(eNB_id),T_INT(Mod_id), T_INT(frame_tx%1024), T_INT(nr_tti_tx),T_INT(ue->tx_power_dBm[nr_tti_tx]),
T_INT(tx_amp),T_INT(ue->ulsch[eNB_id]->f_pusch),T_INT(get_PL(Mod_id,0,eNB_id)),T_INT(nb_rb));
T_INT(tx_amp),T_INT(ue->ulsch[eNB_id]->f_pusch),T_INT(get_nr_PL(ue,eNB_id)),T_INT(nb_rb));
#endif
#ifdef UE_DEBUG_TRACE
......@@ -2306,7 +2306,7 @@ void ue_pucch_procedures(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_
#endif
#if T_TRACER
T(T_UE_PHY_PUCCH_TX_POWER, T_INT(eNB_id),T_INT(Mod_id), T_INT(frame_tx%1024), T_INT(nr_tti_tx),T_INT(ue->tx_power_dBm[nr_tti_tx]),
T_INT(tx_amp),T_INT(ue->dlsch[ue->current_thread_id[proc->nr_tti_rx]][eNB_id][0]->g_pucch),T_INT(get_PL(ue->Mod_id,ue->CC_id,eNB_id)));
T_INT(tx_amp),T_INT(ue->dlsch[ue->current_thread_id[proc->nr_tti_rx]][eNB_id][0]->g_pucch),T_INT(get_nr_PL(ue,eNB_id)));
#endif
#ifdef UE_DEBUG_TRACE
......@@ -2414,7 +2414,7 @@ void ue_pucch_procedures(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_
#endif
#if T_TRACER
T(T_UE_PHY_PUCCH_TX_POWER, T_INT(eNB_id),T_INT(Mod_id), T_INT(frame_tx%1024), T_INT(nr_tti_tx),T_INT(ue->tx_power_dBm[nr_tti_tx]),
T_INT(tx_amp),T_INT(ue->dlsch[ue->current_thread_id[proc->nr_tti_rx]][eNB_id][0]->g_pucch),T_INT(get_PL(ue->Mod_id,ue->CC_id,eNB_id)));
T_INT(tx_amp),T_INT(ue->dlsch[ue->current_thread_id[proc->nr_tti_rx]][eNB_id][0]->g_pucch),T_INT(get_nr_PL(ue,eNB_id)));
#endif
#ifdef UE_DEBUG_TRACE
LOG_I(PHY,"[UE %d][RNTI %x] AbsSubFrame %d.%d Generating PUCCH 2 (RI or CQI), Po_PUCCH %d, isShortenPucch %d, amp %d\n",
......
......@@ -103,7 +103,7 @@ int16_t get_pucch_tx_power_ue(PHY_VARS_NR_UE *ue,
int P_O_PUCCH = P_O_NOMINAL_PUCCH + P_O_UE_PUCCH;
int16_t PL = get_PL(ue->Mod_id, ue->CC_id, gNB_id); /* LTE function because NR path loss not yet implemented FFS TODO NR */
int16_t PL = get_nr_PL(ue,gNB_id); /* LTE function because NR path loss not yet implemented FFS TODO NR */
int16_t delta_F_PUCCH = power_config->deltaF_PUCCH_f[pucch_format];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment