/* * 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_TRANSPORT/nr_prach.c * \brief Top-level routines for generating and decoding the PRACH physical channel V15.4 2018-12 * \author R. Knopp * \date 2019 * \version 0.1 * \company Eurecom * \email: knopp@eurecom.fr * \note * \warning */ #include "PHY/defs_gNB.h" #include "PHY/NR_TRANSPORT/nr_transport.h" #include "PHY/NR_TRANSPORT/nr_transport_proto_common.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 uint16_t nr_du[838]; extern int16_t nr_ru[2*839]; void rx_nr_prach_ru(RU_t *ru, nfapi_nr_prach_pdu_t *prach_pdu, int frame, int slot) { AssertFatal(ru!=NULL,"ru is null\n"); int16_t **rxsigF=NULL; NR_DL_FRAME_PARMS *fp=&ru->frame_parms; int frame_type = ru->config.cell_config.frame_duplex_type.value; int16_t *prach[ru->nb_rx]; uint8_t prach_fmt = prach_pdu->prach_format; int prach_sequence_length = ru->config.prach_config.prach_sequence_length.value; int msg1_frequencystart = ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value; rxsigF = ru->prach_rxsigF; AssertFatal(ru->if_south == LOCAL_RF,"we shouldn't call this if if_south != LOCAL_RF\n"); for (int aa=0; aanb_rx; aa++) prach[aa] = (int16_t*)&ru->common.rxdata[aa][(slot*fp->samples_per_slot)-ru->N_TA_offset]; int mu = fp->numerology_index; int Ncp; int16_t *prach2; if (prach_sequence_length == 0) { switch (prach_fmt) { case 0: Ncp = 3168; break; case 1: Ncp = 21024; break; case 2: Ncp = 4688; break; case 3: Ncp = 3168; break; } } else { switch (prach_fmt) { case 0: //A1 Ncp = 288/(1<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; if (prach_sequence_length == 0 && prach_fmt == 3) { K=4; kbar=10; } else if (prach_sequence_length == 1) { // Note: Assumes that PRACH SCS is same as PUSCH SCS K=1; kbar=2; } int n_ra_prb = msg1_frequencystart; int k = (12*n_ra_prb) - 6*fp->N_RB_UL; int N_ZC = (prach_fmt<4)?839:139; if (k<0) k+=(fp->ofdm_symbol_size); k*=K; k+=kbar; int reps=1; int dftlen=0; for (int aa=0; aanb_rx; aa++) { AssertFatal(prach[aa]!=NULL,"prach[%d] is null\n",aa); // do DFT if (fp->N_RB_UL <= 100) AssertFatal(1==0,"N_RB_UL %d not support for NR PRACH yet\n",fp->N_RB_UL); 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_sequence_length == 0) { if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2) dft49152(prach2,rxsigF[aa],1); if (prach_fmt == 1 || prach_fmt == 2) { dft49152(prach2+98304,rxsigF[aa]+98304,1); reps++; } if (prach_fmt == 2) { dft49152(prach2+(98304*2),rxsigF[aa]+(98304*2),1); dft49152(prach2+(98304*3),rxsigF[aa]+(98304*3),1); reps+=2; } if (prach_fmt == 3) { for (int i=0;i<4;i++) dft12288(prach2+(i*12288*2),rxsigF[aa]+(i*12288*2),1); reps=4; } } else { dft2048(prach2,rxsigF[aa],1); if (prach_fmt != 7) { // !=C0 dft2048(prach2+4096,rxsigF[aa]+4096,1); reps++; } if (prach_fmt == 1 || prach_fmt == 2 || prach_fmt == 4 || prach_fmt == 5 || prach_fmt == 6 || prach_fmt == 8) { dft2048(prach2+4096*2,rxsigF[aa]+4096*2,1); dft2048(prach2+4096*3,rxsigF[aa]+4096*3,1); reps+=2; } if (prach_fmt == 2 || prach_fmt == 5 || prach_fmt == 6) { dft2048(prach2+4096*4,rxsigF[aa]+4096*4,1); dft2048(prach2+4096*5,rxsigF[aa]+4096*5,1); reps+=2; } if (prach_fmt == 6) { for (int i=6;i<11;i++) dft2048(prach2+(4096*i),rxsigF[aa]+(4096*i),1); reps+=6; } } } else { // 40 MHz @ 46.08 Ms/s if (prach_sequence_length == 0) { 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) { dft36864(prach2,rxsigF[aa],1); reps++; } if (prach_fmt == 1 || prach_fmt == 2) { dft36864(prach2+73728,rxsigF[aa]+73728,1); reps++; } if (prach_fmt == 2) { dft36864(prach2+(73728*2),rxsigF[aa]+(73728*2),1); dft36864(prach2+(73728*3),rxsigF[aa]+(73728*3),1); reps+=2; } if (prach_fmt == 3) { for (int i=0;i<4;i++) dft9216(prach2+(i*9216*2),rxsigF[aa]+(i*9216*2),1); reps=4; } } else { dft1536(prach2,rxsigF[aa],1); if (prach_fmt != 0xc0) { dft1536(prach2+3072,rxsigF[aa]+3072,1); reps++; } if (prach_fmt == 0xa2 || prach_fmt == 0xa3 || prach_fmt == 0xb2 || prach_fmt == 0xb3 || prach_fmt == 0xb4 || prach_fmt == 0xc2) { dft1536(prach2+3072*2,rxsigF[aa]+3072*2,1); dft1536(prach2+3072*3,rxsigF[aa]+3072*3,1); reps+=2; } if (prach_fmt == 0xa3 || prach_fmt == 0xb3 || prach_fmt == 0xB4) { dft1536(prach2+3072*4,rxsigF[aa]+3072*4,1); dft1536(prach2+3072*5,rxsigF[aa]+3072*5,1); reps+=2; } if (prach_fmt == 0xB4) { for (int i=6;i<11;i++) dft1536(prach2+(3072*i),rxsigF[aa]+(3072*i),1); reps+=6; } } // short format } // 3/4 sampling } // <=50 MHz BW else if (fp->N_RB_UL <= 273) { if (fp->threequarter_fs==0) { prach2 = prach[aa] + (Ncp<<3); dftlen=98304; //80,90,100 MHz @ 122.88 Ms/s if (prach_sequence_length == 0) { if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2) dft98304(prach2,rxsigF[aa],1); if (prach_fmt == 1 || prach_fmt == 2) { dft98304(prach2+196608,rxsigF[aa]+196608,1); reps++; } if (prach_fmt == 2) { dft98304(prach2+196608*2,rxsigF[aa]+196608*2,1); dft98304(prach2+(196608*3),rxsigF[aa]+(196608*3),1); reps+=2; } if (prach_fmt == 3) { for (int i=0;i<4;i++) dft24576(prach2+(i*2*24576),rxsigF[aa]+(i*2*24576),1); reps=4; dftlen=24576; } } else { dftlen=4096; dft4096(prach2,rxsigF[aa],1); if (prach_fmt != 7) { //!=C0 dft4096(prach2+8192,rxsigF[aa]+8192,1); reps++; } if (prach_fmt == 1 || prach_fmt == 2 || prach_fmt == 4 || prach_fmt == 5 || prach_fmt == 6 || prach_fmt == 8) { dft4096(prach2+8192*2,rxsigF[aa]+8192*2,1); dft4096(prach2+8192*3,rxsigF[aa]+8192*3,1); reps+=2; } if (prach_fmt == 2 || prach_fmt == 5 || prach_fmt == 6) { dft4096(prach2+8192*4,rxsigF[aa]+8192*4,1); dft4096(prach2+8192*5,rxsigF[aa]+8192*5,1); reps+=2; } if (prach_fmt == 6) { for (int i=6;i<11;i++) dft4096(prach2+(8192*i),rxsigF[aa]+(8192*i),1); reps+=6; } } } else { AssertFatal(fp->N_RB_UL <= 217,"cannot do more than 217 PRBs with 3/4 sampling\n"); prach2 = prach[aa] + (6*Ncp); // 80 MHz @ 92.16 Ms/s dftlen=73728; if (prach_sequence_length == 0) { if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2) { dft73728(prach2,rxsigF[aa],1); reps++; } if (prach_fmt == 1 || prach_fmt == 2) { dft73728(prach2+(2*73728),rxsigF[aa]+(2*73728),1); reps++; } if (prach_fmt == 3) { for (int i=0;i<4;i++) dft18432(prach2+(i*2*18432),rxsigF[aa]+(i*2*18432),1); reps=4; dftlen=18432; } } else { dftlen=3072; dft3072(prach2,rxsigF[aa],1); if (prach_fmt != 7) { //!=C0 dft3072(prach2+6144,rxsigF[aa]+6144,1); reps++; } if (prach_fmt == 1 || prach_fmt == 2 || prach_fmt == 4 || prach_fmt == 5 || prach_fmt == 6 || prach_fmt == 8) { dft3072(prach2+6144*2,rxsigF[aa]+6144*2,1); dft3072(prach2+6144*3,rxsigF[aa]+6144*3,1); reps+=2; } if (prach_fmt == 2 || prach_fmt == 5 || prach_fmt == 6) { dft3072(prach2+6144*4,rxsigF[aa]+6144*4,1); dft3072(prach2+6144*5,rxsigF[aa]+6144*5,1); reps+=2; } if (prach_fmt == 6) { for (int i=6;i<11;i++) dft3072(prach2+(6144*i),rxsigF[aa]+(6144*i),1); reps+=6; } } } } //Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH) int16_t rxsigF_tmp[N_ZC<<1]; // if (k+N_ZC > dftlen) { // PRACH signal is split around DC int16_t *rxsigF2=rxsigF[aa]; int k2=k<<1; for (int j=0;jgNB_config.prach_config; NR_DL_FRAME_PARMS *fp; lte_frame_type_t frame_type; uint16_t rootSequenceIndex; int numrootSequenceIndex; 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=99,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; uint16_t u; int16_t *Xu=0; uint16_t offset; uint16_t first_nonzero_root_idx=0; uint8_t new_dft=0; uint8_t aa; int32_t lev; int16_t levdB; int log2_ifft_size=10; int16_t prach_ifft_tmp[2048*2] __attribute__((aligned(32))); int32_t *prach_ifft=(int32_t*)NULL; AssertFatal(gNB!=NULL,"gNB is null\n"); fp = &gNB->frame_parms; nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; frame_type = gNB->gNB_config.cell_config.frame_duplex_type.value; rootSequenceIndex = cfg->num_prach_fd_occasions_list[0].prach_root_sequence_index.value; numrootSequenceIndex = cfg->num_prach_fd_occasions_list[0].num_root_sequences.value; NCS = cfg->num_prach_fd_occasions_list[0].prach_zero_corr_conf.value; int prach_sequence_length = cfg->prach_sequence_length.value; int msg1_frequencystart = cfg->num_prach_fd_occasions_list[0].k1.value; // int num_unused_root_sequences = cfg->num_prach_fd_occasions_list[0].num_unused_root_sequences.value; // cfg->num_prach_fd_occasions_list[0].unused_root_sequences_list restricted_set = cfg->restricted_set_config.value; AssertFatal(prach_sequence_length == 1, "no support yet for long prachSequenceLength\n"); uint8_t prach_fmt = prach_pdu->prach_format; uint16_t N_ZC = (prach_sequence_length==0)?839:139; prach_ifft = gNB->prach_vars.prach_ifft; prachF = gNB->prach_vars.prachF; if (LOG_DEBUGFLAG(PRACH)){ if ((frame&1023) < 20) LOG_D(PHY,"PRACH (gNB) : running rx_prach for subframe %d, msg1_frequencystart %d, rootSequenceIndex %d\n", subframe,msg1_frequencystart,rootSequenceIndex); } start_meas(&gNB->rx_prach); prach_root_sequence_map = (cfg->prach_sequence_length.value==0) ? 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; preamble_offset_old = 99; *max_preamble_energy=0; for (preamble_index=0 ; preamble_index<64 ; preamble_index++) { if (LOG_DEBUGFLAG(PRACH)){ int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],(N_ZC==839) ? 840: 140)); 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 preamble_offset = ((NCS==0)? preamble_index : (preamble_index/(N_ZC/NCS))); if (preamble_offset != preamble_offset_old) { preamble_offset_old = preamble_offset; new_dft = 1; // This is the \nu corresponding to the preamble index preamble_shift = 0; } else { preamble_shift -= NCS; if (preamble_shift < 0) preamble_shift+=N_ZC; } } else { // This is the high-speed case new_dft = 0; // set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this // preamble index and find the corresponding cyclic shift // Check if all shifts for that root have been processed if (preamble_index0 == numshift) { not_found = 1; new_dft = 1; preamble_index0 -= numshift; (preamble_offset==0 && numshift==0) ? (preamble_offset) : (preamble_offset++); 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 (numshift>0) ? (not_found = 0) : (preamble_offset++); } } 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 preamble_shift = 0; if (preamble_shift < 0) preamble_shift+=N_ZC; preamble_index0++; if (preamble_index == 0) first_nonzero_root_idx = preamble_offset; } // 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, NCS %d, N_ZC/NCS %d: offset %d, preamble shift %d , en %d)\n", frame,subframe,preamble_index,NCS,N_ZC/NCS,preamble_offset,preamble_shift,en); } if (new_dft == 1) { new_dft = 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)) { LOG_M("prach_rxF0.m","prach_rxF0",rxsigF[0],N_ZC,1,1); LOG_M("prach_rxF1.m","prach_rxF1",rxsigF[1],6144,1,1); } for (aa=0;aa>15); prachF[offset+1] = (int16_t)(((int32_t)Xu[offset]*rxsigF[aa][offset+1] - (int32_t)Xu[offset+1]*rxsigF[aa][offset])>>15); } // Now do IFFT of size 1024 (N_ZC=839) or 256 (N_ZC=139) if (N_ZC == 839) { log2_ifft_size = 10; idft1024(prachF,prach_ifft_tmp,1); // 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 { idft256(prachF,prach_ifft_tmp,1); log2_ifft_size = 8; // compute energy and accumulate over receive antennas and repetitions for BR for (i=0;i<256;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; } if (LOG_DUMPFLAG(PRACH)) { if (aa==0) LOG_M("prach_rxF_comp0.m","prach_rxF_comp0",prachF,1024,1,1); if (aa==1) LOG_M("prach_rxF_comp1.m","prach_rxF_comp1",prachF,1024,1,1); } }// antennas_rx } // new dft // check energy in nth time shift, for preamble_shift2 = ((preamble_shift==0) ? 0 : ((preamble_shift<*max_preamble_energy) { *max_preamble_energy = levdB; *max_preamble_delay = i; // Note: This has to be normalized to the 30.72 Ms/s sampling rate *max_preamble = preamble_index; } } }// preamble_index // The conversion from *max_preamble_delay from TA value is done here. // It is normalized to the 30.72 Ms/s, considering the numerology, N_RB and the sampling rate // See table 6.3.3.1 -1 and -2 in 38211. // Format 0, 1, 2: 24576 samples @ 30.72 Ms/s, 98304 samples @ 122.88 Ms/s // By solving: // max_preamble_delay * ( (24576*(fs/30.72M)) / 1024 ) / fs = TA * 16 * 64 / 2^mu * Tc // Format 3: 6144 samples @ 30.72 Ms/s, 24576 samples @ 122.88 Ms/s // By solving: // max_preamble_delay * ( (6144*(fs/30.72M)) / 1024 ) / fs = TA * 16 * 64 / 2^mu * Tc // Format >3: 2048/2^mu samples @ 30.72 Ms/s, 2048/2^mu * 4 samples @ 122.88 Ms/s // By solving: // max_preamble_delay * ( (2048/2^mu*(fs/30.72M)) / 256 ) / fs = TA * 16 * 64 / 2^mu * Tc uint16_t *TA = max_preamble_delay; int mu = fp->numerology_index; if (cfg->prach_sequence_length.value==0) { if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2) *TA = *TA*3*(1<60) { int k = (12*n_ra_prb) - 6*fp->N_RB_UL; if (k<0) k+=fp->ofdm_symbol_size; k*=12; k+=13; k*=2; 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); } } /* LOG_DUMPFLAG(PRACH) */ stop_meas(&gNB->rx_prach); }