/* * 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_procedures_lte_eNB.c * \brief Implementation of eNB procedures from 36.213 LTE specifications * \author R. Knopp, F. Kaltenberger, N. Nikaein, X. Foukas * \date 2011 * \version 0.1 * \company Eurecom * \email: knopp@eurecom.fr,florian.kaltenberger@eurecom.fr,navid.nikaein@eurecom.fr, x.foukas@sms.ed.ac.uk * \note * \warning */ #include "PHY/defs_eNB.h" #include "PHY/phy_extern.h" #include "SCHED/sched_eNB.h" #include "SCHED/sched_common_extern.h" #include "PHY/LTE_ESTIMATION/lte_estimation.h" #include "nfapi_interface.h" #include "fapi_l1.h" #include "common/utils/LOG/log.h" #include <common/utils/system.h> #include "common/utils/LOG/vcd_signal_dumper.h" #include "assertions.h" #include "msc.h" #include <time.h> #if defined(ENABLE_ITTI) # include "intertask_interface.h" #endif extern uint8_t nfapi_mode; int16_t get_hundred_times_delta_IF_eNB(PHY_VARS_eNB *eNB,uint16_t UE_id,uint8_t harq_pid, uint8_t bw_factor) { uint32_t Nre,sumKr,MPR_x100,Kr,r; uint16_t beta_offset_pusch; DevAssert( UE_id < NUMBER_OF_UE_MAX+1 ); DevAssert( harq_pid < 8 ); Nre = eNB->ulsch[UE_id]->harq_processes[harq_pid]->Nsymb_initial * eNB->ulsch[UE_id]->harq_processes[harq_pid]->nb_rb*12; sumKr = 0; for (r=0; r<eNB->ulsch[UE_id]->harq_processes[harq_pid]->C; r++) { if (r<eNB->ulsch[UE_id]->harq_processes[harq_pid]->Cminus) Kr = eNB->ulsch[UE_id]->harq_processes[harq_pid]->Kminus; else Kr = eNB->ulsch[UE_id]->harq_processes[harq_pid]->Kplus; sumKr += Kr; } if (Nre==0) return(0); MPR_x100 = 100*sumKr/Nre; // Note: MPR=is the effective spectral efficiency of the PUSCH // FK 20140908 sumKr is only set after the ulsch_encoding beta_offset_pusch = 8; //(eNB->ulsch[UE_id]->harq_processes[harq_pid]->control_only == 1) ? eNB->ulsch[UE_id]->beta_offset_cqi_times8:8; DevAssert( UE_id < NUMBER_OF_UE_MAX ); //#warning "This condition happens sometimes. Need more investigation" // navid //DevAssert( MPR_x100/6 < 100 ); if (1==1) { //eNB->ul_power_control_dedicated[UE_id].deltaMCS_Enabled == 1) { // This is the formula from Section 5.1.1.1 in 36.213 10*log10(deltaIF_PUSCH = (2^(MPR*Ks)-1)*beta_offset_pusch) if (bw_factor == 1) { uint8_t nb_rb = eNB->ulsch[UE_id]->harq_processes[harq_pid]->nb_rb; return(hundred_times_delta_TF[MPR_x100/6]+10*dB_fixed_times10((beta_offset_pusch)>>3)) + hundred_times_log10_NPRB[nb_rb-1]; } else return(hundred_times_delta_TF[MPR_x100/6]+10*dB_fixed_times10((beta_offset_pusch)>>3)); } else { return(0); } } int16_t get_hundred_times_delta_IF_mac(module_id_t module_idP, uint8_t CC_id, rnti_t rnti, uint8_t harq_pid) { int8_t UE_id; if ((RC.eNB == NULL) || (module_idP > RC.nb_inst) || (CC_id > RC.nb_CC[module_idP])) { LOG_E(PHY,"get_UE_stats: No eNB found (or not allocated) for Mod_id %d,CC_id %d\n",module_idP,CC_id); return -1; } UE_id = find_ulsch( rnti, RC.eNB[module_idP][CC_id],SEARCH_EXIST); if (UE_id == -1) { // not found return 0; } return get_hundred_times_delta_IF_eNB( RC.eNB[module_idP][CC_id], UE_id, harq_pid, 0 ); } int oai_nfapi_rach_ind(nfapi_rach_indication_t *rach_ind); lte_subframe_t get_subframe_direction(uint8_t Mod_id,uint8_t CC_id,uint8_t subframe) { return(subframe_select(&RC.eNB[Mod_id][CC_id]->frame_parms,subframe)); } void pmch_procedures(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc) { #if (RRC_VERSION >= MAKE_VERSION(10, 0, 0)) MCH_PDU *mch_pduP=NULL; // uint8_t sync_area=255; #endif int subframe = proc->subframe_tx; AssertFatal(1==0,"pmch not tested for the moment, exiting\n"); // This is DL-Cell spec pilots in Control region generate_pilots_slot(eNB, eNB->common_vars.txdataF, AMP, subframe<<1,1); #if (RRC_VERSION >= MAKE_VERSION(10, 0, 0)) // if mcch is active, send regardless of the node type: eNB or RN // when mcch is active, MAC sched does not allow MCCH and MTCH multiplexing /* mch_pduP = mac_xface->get_mch_sdu(eNB->Mod_id, eNB->CC_id, proc->frame_tx, subframe); */ if ((mch_pduP->Pdu_size > 0) && (mch_pduP->sync_area == 0)) // TEST: only transmit mcch for sync area 0 LOG_D(PHY,"[eNB%"PRIu8"] Frame %d subframe %d : Got MCH pdu for MBSFN (MCS %"PRIu8", TBS %d) \n", eNB->Mod_id,proc->frame_tx,subframe,mch_pduP->mcs, eNB->dlsch_MCH->harq_processes[0]->TBS>>3); else { LOG_D(PHY,"[DeNB %"PRIu8"] Frame %d subframe %d : Do not transmit MCH pdu for MBSFN sync area %"PRIu8" (%s)\n", eNB->Mod_id,proc->frame_tx,subframe,mch_pduP->sync_area, (mch_pduP->Pdu_size == 0)? "Empty MCH PDU":"Let RN transmit for the moment"); mch_pduP = NULL; } if (mch_pduP) { fill_eNB_dlsch_MCH(eNB,mch_pduP->mcs,1,0); // Generate PMCH generate_mch(eNB,proc,(uint8_t*)mch_pduP->payload); } else { LOG_D(PHY,"[eNB/RN] Frame %d subframe %d: MCH not generated \n",proc->frame_tx,subframe); } #endif } void common_signal_procedures (PHY_VARS_eNB *eNB,int frame, int subframe) { LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; int **txdataF = eNB->common_vars.txdataF; uint8_t *pbch_pdu=&eNB->pbch_pdu[0]; //LOG_D(PHY,"common_signal_procedures: frame %d, subframe %d fdd:%s dir:%s\n",frame,subframe,fp->frame_type == FDD?"FDD":"TDD", subframe_select(fp,subframe) == SF_DL?"DL":"UL?"); // generate Cell-Specific Reference Signals for both slots VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_RS_TX,1); if(subframe_select(fp,subframe) == SF_S) generate_pilots_slot(eNB, txdataF, AMP, subframe<<1,1); else generate_pilots_slot(eNB, txdataF, AMP, subframe<<1,0); // check that 2nd slot is for DL // check that 2nd slot is for DL if (subframe_select(fp,subframe) == SF_DL) generate_pilots_slot(eNB, txdataF, AMP, (subframe<<1)+1,0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_RS_TX,0); // First half of PSS/SSS (FDD, slot 0) if (subframe == 0) { if (fp->frame_type == FDD) { generate_pss(txdataF, AMP, fp, (fp->Ncp==NORMAL) ? 6 : 5, 0); generate_sss(txdataF, AMP, fp, (fp->Ncp==NORMAL) ? 5 : 4, 0); } /// First half of SSS (TDD, slot 1) if (fp->frame_type == TDD) { generate_sss(txdataF, AMP, fp, (fp->Ncp==NORMAL) ? 6 : 5, 1); } // generate PBCH (Physical Broadcast CHannel) info /// generate PBCH if ((frame&3)==0) { //AssertFatal(eNB->pbch_configured==1,"PBCH was not configured by MAC\n"); if (eNB->pbch_configured!=1) return; eNB->pbch_configured=0; } generate_pbch(&eNB->pbch, txdataF, AMP, fp, pbch_pdu, frame&3); } else if ((subframe == 1) && (fp->frame_type == TDD)){ generate_pss(txdataF, AMP, fp, 2, 2); } // Second half of PSS/SSS (FDD, slot 10) else if ((subframe == 5) && (fp->frame_type == FDD)) { generate_pss(txdataF, AMP, &eNB->frame_parms, (fp->Ncp==NORMAL) ? 6 : 5, 10); generate_sss(txdataF, AMP, &eNB->frame_parms, (fp->Ncp==NORMAL) ? 5 : 4, 10); } // Second-half of SSS (TDD, slot 11) else if ((subframe == 5) && (fp->frame_type == TDD)) { generate_sss(txdataF, AMP, fp, (fp->Ncp==NORMAL) ? 6 : 5, 11); } // Second half of PSS (TDD, slot 12) else if ((subframe == 6) && (fp->frame_type == TDD)) { generate_pss(txdataF, AMP, fp, 2, 12); } } void pdsch_procedures(PHY_VARS_eNB *eNB, eNB_rxtx_proc_t *proc, int harq_pid, LTE_eNB_DLSCH_t *dlsch, LTE_eNB_DLSCH_t *dlsch1, LTE_eNB_UE_stats *ue_stats, int ra_flag) { int frame=proc->frame_tx; int subframe=proc->subframe_tx; LTE_DL_eNB_HARQ_t *dlsch_harq=dlsch->harq_processes[harq_pid]; LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; if (dlsch->rnti == 0x02) {//frame < 200) { LOG_D(PHY, "[eNB %"PRIu8"][PDSCH %"PRIx16"/%"PRIu8"] Frame %d, subframe %d: Generating PDSCH/DLSCH with input size = %"PRIu16", pdsch_start %d, G %d, nb_rb %"PRIu16", rb0 %x, rb1 %x, TBS %"PRIu16", pmi_alloc %"PRIx64", rv %"PRIu8" (round %"PRIu8")\n", eNB->Mod_id, dlsch->rnti,harq_pid, frame, subframe, dlsch_harq->TBS/8, dlsch_harq->pdsch_start, get_G(fp, dlsch_harq->nb_rb, dlsch_harq->rb_alloc, dlsch_harq->Qm, dlsch_harq->Nl, dlsch_harq->pdsch_start, frame, subframe, dlsch_harq->mimo_mode==TM7?7:0), dlsch_harq->nb_rb, dlsch_harq->rb_alloc[0], dlsch_harq->rb_alloc[1], dlsch_harq->TBS, pmi2hex_2Ar1(dlsch_harq->pmi_alloc), dlsch_harq->rvidx, dlsch_harq->round); } MSC_LOG_TX_MESSAGE( MSC_PHY_ENB,MSC_PHY_UE, NULL,0, "%05u:%02u PDSCH/DLSCH input size = %"PRIu16", G %d, nb_rb %"PRIu16", TBS %"PRIu16", pmi_alloc %"PRIx16", rv %"PRIu8" (round %"PRIu8")", frame, subframe, dlsch_harq->TBS/8, get_G(fp, dlsch_harq->nb_rb, dlsch_harq->rb_alloc, dlsch_harq->Qm, dlsch_harq->Nl, dlsch_harq->pdsch_start, frame, subframe, dlsch_harq->mimo_mode==TM7?7:0), dlsch_harq->nb_rb, dlsch_harq->TBS, pmi2hex_2Ar1(dlsch_harq->pmi_alloc), dlsch_harq->rvidx, dlsch_harq->round); if (ue_stats) ue_stats->dlsch_sliding_cnt++; if (dlsch_harq->round == 0) { if (ue_stats) ue_stats->dlsch_trials[harq_pid][0]++; } else { ue_stats->dlsch_trials[harq_pid][dlsch_harq->round]++; #ifdef DEBUG_PHY_PROC #ifdef DEBUG_DLSCH LOG_D(PHY,"[eNB] This DLSCH is a retransmission\n"); #endif #endif } LOG_D(PHY,"Generating DLSCH/PDSCH pdu:%p pdsch_start:%d frame:%d subframe:%d nb_rb:%d rb_alloc:%d Qm:%d Nl:%d round:%d\n", dlsch_harq->pdu,dlsch_harq->pdsch_start,frame,subframe,dlsch_harq->nb_rb,dlsch_harq->rb_alloc[0],dlsch_harq->Qm,dlsch_harq->Nl,dlsch_harq->round); // 36-212 if (nfapi_mode == 0 || nfapi_mode == 1) { // monolthic OR PNF - do not need turbo encoding on VNF if (dlsch_harq->pdu==NULL){ LOG_E(PHY,"dlsch_harq->pdu == NULL SFN/SF:%04d%d dlsch[rnti:%x] dlsch_harq[pdu:%p pdsch_start:%d Qm:%d Nl:%d round:%d nb_rb:%d rb_alloc[0]:%d]\n", frame,subframe,dlsch->rnti, dlsch_harq->pdu,dlsch_harq->pdsch_start,dlsch_harq->Qm,dlsch_harq->Nl,dlsch_harq->round,dlsch_harq->nb_rb,dlsch_harq->rb_alloc[0]); return; } start_meas(&eNB->dlsch_encoding_stats); eNB->te(eNB, dlsch_harq->pdu, dlsch_harq->pdsch_start, dlsch, frame, subframe, &eNB->dlsch_rate_matching_stats, &eNB->dlsch_turbo_encoding_stats, &eNB->dlsch_turbo_encoding_waiting_stats, &eNB->dlsch_turbo_encoding_main_stats, &eNB->dlsch_turbo_encoding_wakeup_stats0, &eNB->dlsch_turbo_encoding_wakeup_stats1, &eNB->dlsch_interleaving_stats); stop_meas(&eNB->dlsch_encoding_stats); if(eNB->dlsch_encoding_stats.p_time>500*3000 && opp_enabled == 1) { print_meas_now(&eNB->dlsch_encoding_stats,"total coding",stderr); } // 36-211 start_meas(&eNB->dlsch_scrambling_stats); dlsch_scrambling(fp, 0, dlsch, harq_pid, get_G(fp, dlsch_harq->nb_rb, dlsch_harq->rb_alloc, dlsch_harq->Qm, dlsch_harq->Nl, dlsch_harq->pdsch_start, frame,subframe, 0), 0, frame, subframe<<1); stop_meas(&eNB->dlsch_scrambling_stats); start_meas(&eNB->dlsch_modulation_stats); dlsch_modulation(eNB, eNB->common_vars.txdataF, AMP, frame, subframe, dlsch_harq->pdsch_start, dlsch, dlsch1); stop_meas(&eNB->dlsch_modulation_stats); } #ifdef PHY_TX_THREAD dlsch->active[subframe] = 0; #else dlsch->active = 0; #endif dlsch_harq->round++; LOG_D(PHY,"Generating DLSCH/PDSCH dlsch_harq[round:%d]\n",dlsch_harq->round); } void phy_procedures_eNB_TX(PHY_VARS_eNB *eNB, eNB_rxtx_proc_t *proc, int do_meas) { int frame=proc->frame_tx; int subframe=proc->subframe_tx; uint32_t i,aa; uint8_t harq_pid; int16_t UE_id=0; uint8_t num_pdcch_symbols=0; uint8_t num_dci=0; uint8_t ul_subframe; uint32_t ul_frame; LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; LTE_UL_eNB_HARQ_t *ulsch_harq; if ((fp->frame_type == TDD) && (subframe_select(fp,subframe)==SF_UL)) return; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX+(eNB->CC_id),1); if (do_meas==1) start_meas(&eNB->phy_proc_tx); // clear the transmit data array for the current subframe for (aa=0; aa<fp->nb_antenna_ports_eNB; aa++) { memset(&eNB->common_vars.txdataF[aa][subframe*fp->ofdm_symbol_size*(fp->symbols_per_tti)], 0,fp->ofdm_symbol_size*(fp->symbols_per_tti)*sizeof(int32_t)); } if (nfapi_mode == 0 || nfapi_mode == 1) { if (is_pmch_subframe(frame,subframe,fp)) { pmch_procedures(eNB,proc); } else { // this is not a pmch subframe, so generate PSS/SSS/PBCH common_signal_procedures(eNB,proc->frame_tx, proc->subframe_tx); } } // clear existing ulsch dci allocations before applying info from MAC (this is table ul_subframe = pdcch_alloc2ul_subframe(fp,subframe); ul_frame = pdcch_alloc2ul_frame(fp,frame,subframe); // clear previous allocation information for all UEs for (i=0; i<NUMBER_OF_UE_MAX; i++) { if (eNB->dlsch[i][0]) eNB->dlsch[i][0]->subframe_tx[subframe] = 0; } /* save old HARQ information needed for PHICH generation */ /* TODO: check the following test - in the meantime it is put back as it was before */ //if ((ul_subframe < 10)&& // (subframe_select(fp,ul_subframe)==SF_UL)) { // This means that there is a potential UL subframe that will be scheduled here if (ul_subframe < 10) { // This means that there is a potential UL subframe that will be scheduled here for (i=0; i<NUMBER_OF_UE_MAX; i++) { harq_pid = subframe2harq_pid(fp,ul_frame,ul_subframe); if (eNB->ulsch[i]) { ulsch_harq = eNB->ulsch[i]->harq_processes[harq_pid]; /* Store first_rb and n_DMRS for correct PHICH generation below. * For PHICH generation we need "old" values of last scheduling * for this HARQ process. 'generate_eNB_dlsch_params' below will * overwrite first_rb and n_DMRS and 'generate_phich_top', done * after 'generate_eNB_dlsch_params', would use the "new" values * instead of the "old" ones. * * This has been tested for FDD only, may be wrong for TDD. * * TODO: maybe we should restructure the code to be sure it * is done correctly. The main concern is if the code * changes and first_rb and n_DMRS are modified before * we reach here, then the PHICH processing will be wrong, * using wrong first_rb and n_DMRS values to compute * ngroup_PHICH and nseq_PHICH. * * TODO: check if that works with TDD. */ ulsch_harq->previous_first_rb = ulsch_harq->first_rb; ulsch_harq->previous_n_DMRS = ulsch_harq->n_DMRS; } } } num_pdcch_symbols = eNB->pdcch_vars[subframe&1].num_pdcch_symbols; num_dci = eNB->pdcch_vars[subframe&1].num_dci; LOG_D(PHY,"num_pdcch_symbols %"PRIu8",number dci %"PRIu8"\n",num_pdcch_symbols, num_dci); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,num_pdcch_symbols); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,(frame*10)+subframe); if (num_dci > 0) LOG_D(PHY,"[eNB %"PRIu8"] Frame %d, subframe %d: Calling generate_dci_top (pdcch) (num_dci %"PRIu8") num_pdcch_symbols:%d\n",eNB->Mod_id,frame, subframe, num_dci, num_pdcch_symbols); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PDCCH_TX,1); if (nfapi_mode == 0 || nfapi_mode == 1) { generate_dci_top(num_pdcch_symbols, num_dci, &eNB->pdcch_vars[subframe&1].dci_alloc[0], 0, AMP, fp, eNB->common_vars.txdataF, subframe); } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PDCCH_TX,0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1); // Now scan UE specific DLSCH LTE_eNB_DLSCH_t *dlsch0,*dlsch1; for (UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++) { dlsch0 = eNB->dlsch[(uint8_t)UE_id][0]; dlsch1 = eNB->dlsch[(uint8_t)UE_id][1]; if ((dlsch0)&& (dlsch0->rnti>0)&& #ifdef PHY_TX_THREAD (dlsch0->active[subframe] == 1)) { #else (dlsch0->active == 1)) { #endif // get harq_pid harq_pid = dlsch0->harq_ids[frame%2][subframe]; AssertFatal(harq_pid>=0,"harq_pid is negative\n"); if (harq_pid>=8) { LOG_E(PHY,"harq_pid:%d corrupt must be 0-7 UE_id:%d frame:%d subframe:%d rnti:%x\n", harq_pid,UE_id,frame,subframe,dlsch0->rnti); } else { // generate pdsch pdsch_procedures(eNB, proc, harq_pid, dlsch0, dlsch1, &eNB->UE_stats[(uint32_t)UE_id], 0); } } else if ((dlsch0)&& (dlsch0->rnti>0)&& #ifdef PHY_TX_THREAD (dlsch0->active[subframe] == 0)) { #else (dlsch0->active == 0)) { #endif // clear subframe TX flag since UE is not scheduled for PDSCH in this subframe (so that we don't look for PUCCH later) dlsch0->subframe_tx[subframe]=0; } } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_PHICH,1); generate_phich_top(eNB, proc, AMP); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_PHICH,0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX+(eNB->CC_id),0); if (do_meas==1) stop_meas(&eNB->phy_proc_tx); } void srs_procedures(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc) { LTE_DL_FRAME_PARMS *fp = &eNB->frame_parms; const int subframe = proc->subframe_rx; const int frame = proc->frame_rx; int i; if (is_srs_occasion_common(fp,frame,subframe)) { // Do SRS processing // check if there is SRS and we have to use shortened format // TODO: check for exceptions in transmission of SRS together with ACK/NACK for (i=0;i<NUMBER_OF_UE_MAX;i++) { if (eNB->soundingrs_ul_config_dedicated[i].active==1) { if (lte_srs_channel_estimation(fp, &eNB->common_vars, &eNB->srs_vars[i], &eNB->soundingrs_ul_config_dedicated[i], subframe, 0/*eNB_id*/)) { LOG_E(PHY,"problem processing SRS\n"); } eNB->soundingrs_ul_config_dedicated[i].active=0; } } } } void fill_sr_indication(PHY_VARS_eNB *eNB,uint16_t rnti,int frame,int subframe,uint32_t stat) { pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_sr_indication_t *sr_ind = &eNB->UL_INFO.sr_ind; nfapi_sr_indication_body_t *sr_ind_body = &sr_ind->sr_indication_body; nfapi_sr_indication_pdu_t *pdu = &sr_ind_body->sr_pdu_list[sr_ind_body->number_of_srs]; sr_ind->sfn_sf = frame<<4|subframe; sr_ind->header.message_id = NFAPI_RX_SR_INDICATION; sr_ind_body->tl.tag = NFAPI_SR_INDICATION_BODY_TAG; pdu->instance_length = 0; // don't know what to do with this // pdu->rx_ue_information.handle = handle; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = rnti; int SNRtimes10 = dB_fixed_times10(stat) - 300;//(10*eNB->measurements.n0_power_dB[0]); pdu->ul_cqi_information.tl.tag = NFAPI_UL_CQI_INFORMATION_TAG; if (SNRtimes10 < -640) pdu->ul_cqi_information.ul_cqi=0; else if (SNRtimes10 > 635) pdu->ul_cqi_information.ul_cqi=255; else pdu->ul_cqi_information.ul_cqi=(640+SNRtimes10)/5; pdu->ul_cqi_information.channel = 0; sr_ind_body->number_of_srs++; pthread_mutex_unlock(&eNB->UL_INFO_mutex); } void uci_procedures(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc) { LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; uint8_t SR_payload = 0,pucch_b0b1[4][2]= {{0,0},{0,0},{0,0},{0,0}},harq_ack[4]={0,0,0,0}; int32_t metric[4]={0,0,0,0},metric_SR=0,max_metric=0; const int subframe = proc->subframe_rx; const int frame = proc->frame_rx; int i; LTE_eNB_UCI *uci; uint16_t tdd_multiplexing_mask=0; for (i=0;i<NUMBER_OF_UE_MAX;i++) { uci = &eNB->uci_vars[i]; if ((uci->active == 1) && (uci->frame == frame) && (uci->subframe == subframe)) { LOG_D(PHY,"Frame %d, subframe %d: Running uci procedures (type %d) for %d \n",frame,subframe,uci->type,i); uci->active=0; // Null out PUCCH PRBs for noise measurement switch(fp->N_RB_UL) { case 6: eNB->rb_mask_ul[0] |= (0x1 | (1<<5)); //position 5 break; case 15: eNB->rb_mask_ul[0] |= (0x1 | (1<<14)); // position 14 break; case 25: eNB->rb_mask_ul[0] |= (0x1 | (1<<24)); // position 24 break; case 50: eNB->rb_mask_ul[0] |= 0x1; eNB->rb_mask_ul[1] |= (1<<17); // position 49 (49-32) break; case 75: eNB->rb_mask_ul[0] |= 0x1; eNB->rb_mask_ul[2] |= (1<<10); // position 74 (74-64) break; case 100: eNB->rb_mask_ul[0] |= 0x1; eNB->rb_mask_ul[3] |= (1<<3); // position 99 (99-96) break; default: LOG_E(PHY,"Unknown number for N_RB_UL %d\n",fp->N_RB_UL); break; } SR_payload = 0; switch (uci->type) { case SR: case HARQ_SR: metric_SR = rx_pucch(eNB, uci->pucch_fmt, i, uci->n_pucch_1_0_sr[0], 0, // n2_pucch uci->srs_active, // shortened format &SR_payload, frame, subframe, PUCCH1_THRES); LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d Checking SR is %d (uci.type %d SR n1pucch is %d)\n", eNB->Mod_id, uci->rnti, frame, subframe, SR_payload, uci->type, uci->n_pucch_1_0_sr[0]); if (uci->type == SR) { if (SR_payload == 1) { fill_sr_indication(eNB,uci->rnti,frame,subframe,metric_SR); break; } else { break; } } case HARQ: if (fp->frame_type == FDD) { LOG_D(PHY,"Frame %d Subframe %d Demodulating PUCCH (UCI %d) for ACK/NAK (uci->pucch_fmt %d,uci->type %d.uci->frame %d, uci->subframe %d): n1_pucch0 %d SR_payload %d\n", frame,subframe,i, uci->pucch_fmt,uci->type, uci->frame,uci->subframe,uci->n_pucch_1[0][0], SR_payload); metric[0] = rx_pucch(eNB, uci->pucch_fmt, i, uci->n_pucch_1[0][0], 0, //n2_pucch uci->srs_active, // shortened format pucch_b0b1[0], frame, subframe, PUCCH1a_THRES); /* cancel SR detection if reception on n1_pucch0 is better than on SR PUCCH resource index, otherwise send it up to MAC */ if (uci->type==HARQ_SR && metric[0] > metric_SR) SR_payload = 0; else if (SR_payload == 1) fill_sr_indication(eNB,uci->rnti,frame,subframe,metric_SR); if (uci->type==HARQ_SR && metric[0] <= metric_SR) { /* when transmitting ACK/NACK on SR PUCCH resource index, SR payload is always 1 */ SR_payload = 1; metric[0]=rx_pucch(eNB, uci->pucch_fmt, i, uci->n_pucch_1_0_sr[0], 0, //n2_pucch uci->srs_active, // shortened format pucch_b0b1[0], frame, subframe, PUCCH1a_THRES); } LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d pucch1a (FDD) payload %d (metric %d)\n", eNB->Mod_id, uci->rnti, frame,subframe, pucch_b0b1[0][0],metric[0]); uci->stat = metric[0]; fill_uci_harq_indication(eNB,uci,frame,subframe,pucch_b0b1[0],0,0xffff); } else { // frame_type == TDD LOG_D(PHY,"Frame %d Subframe %d Demodulating PUCCH (UCI %d) for ACK/NAK (uci->pucch_fmt %d,uci->type %d.uci->frame %d, uci->subframe %d): n1_pucch0 %d SR_payload %d\n", frame,subframe,i, uci->pucch_fmt,uci->type, uci->frame,uci->subframe,uci->n_pucch_1[0][0], SR_payload); #if 1 metric[0] = rx_pucch(eNB, uci->pucch_fmt, i, uci->n_pucch_1[0][0], 0, //n2_pucch uci->srs_active, // shortened format pucch_b0b1[0], frame, subframe, PUCCH1a_THRES); if (uci->type==HARQ_SR && metric[0] > metric_SR) SR_payload = 0; else if (SR_payload == 1) fill_sr_indication(eNB,uci->rnti,frame,subframe,metric_SR); if (uci->type==HARQ_SR && metric[0] <= metric_SR) { SR_payload = 1; metric[0] = rx_pucch(eNB, pucch_format1b, i, uci->n_pucch_1_0_sr[0], 0, //n2_pucch uci->srs_active, // shortened format pucch_b0b1[0], frame, subframe, PUCCH1a_THRES); } #else // if SR was detected, use the n1_pucch from SR if (SR_payload==1) { #ifdef DEBUG_PHY_PROC LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d Checking ACK/NAK (%d,%d,%d,%d) format %d with SR\n",eNB->Mod_id, eNB->dlsch[UE_id][0]->rnti, frame,subframe, n1_pucch0,n1_pucch1,n1_pucch2,n1_pucch3,format); #endif metric[0] = rx_pucch(eNB, pucch_format1b, i, uci->n_pucch_1_0_sr[0], 0, //n2_pucch uci->srs_active, // shortened format pucch_b0b1[0], frame, subframe, PUCCH1a_THRES); } else { //using assigned pucch resources #ifdef DEBUG_PHY_PROC LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d Checking ACK/NAK M=%d (%d,%d,%d,%d) format %d\n",eNB->Mod_id, eNB->dlsch[UE_id][0]->rnti, frame,subframe, uci->num_pucch_resources, uci->n_pucch_1[res][0], uci->n_pucch_1[res][1], uci->n_pucch_1[res][2], uci->n_pucch_1[res][3], uci->pucch_fmt); #endif for (res=0;res<uci->num_pucch_resources;res++) metric[res] = rx_pucch(eNB, uci->pucch_fmt, i, uci->n_pucch_1[res][0], 0, // n2_pucch uci->srs_active, // shortened format pucch_b0b1[res], frame, subframe, PUCCH1a_THRES); } #ifdef DEBUG_PHY_PROC LOG_D(PHY,"RNTI %x type %d SR_payload %d Frame %d Subframe %d pucch_b0b1[0][0] %d pucch_b0b1[0][1] %d pucch_b0b1[1][0] %d pucch_b0b1[1][1] %d \n", uci->rnti,uci->type,SR_payload,frame,subframe,pucch_b0b1[0][0],pucch_b0b1[0][1],pucch_b0b1[1][0],pucch_b0b1[1][1]); #endif #endif if (SR_payload == 1) { // this implements Table 7.3.1 from 36.213 if (pucch_b0b1[0][0] == 4) { // there isn't a likely transmission harq_ack[0] = 4; // DTX } else if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] == 1) { // 1/4/7 ACKs harq_ack[0] = 1; } else if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] != 1) { // 2/5/8 ACKs harq_ack[0] = 2; } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] == 1) { // 3/6/9 ACKs harq_ack[0] = 3; } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] != 1) { // 0 ACKs, or at least one DL assignment missed harq_ack[0] = 0; } uci->stat = metric[0]; fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,2,0xffff); // special_bundling mode } else if ((uci->tdd_bundling == 0) && (uci->num_pucch_resources==2)){ // multiplexing + no SR, implement Table 10.1.3-5 (Rel14) for multiplexing with M=2 if (pucch_b0b1[0][0] == 4 || pucch_b0b1[1][0] == 4) { // there isn't a likely transmission harq_ack[0] = 4; // DTX harq_ack[1] = 6; // NACK/DTX } else { if (metric[1]>metric[0]) { if (pucch_b0b1[1][0] == 1 && pucch_b0b1[1][1] != 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK tdd_multiplexing_mask = 0x3; } else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] == 1){ harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK tdd_multiplexing_mask = 0x2; } else { harq_ack[0] = 4; // DTX harq_ack[1] = 4; // DTX } } else { if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX tdd_multiplexing_mask = 0x1; } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] != 1){ harq_ack[0] = 2; // NACK harq_ack[1] = 6; // NACK/DTX } else { harq_ack[0] = 4; // DTX harq_ack[1] = 4; // DTX } } } uci->stat = max(metric[0],metric[1]); fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode } //else if ((uci->tdd_bundling == 0) && (res==2)) else if ((uci->tdd_bundling == 0) && (uci->num_pucch_resources==3)){ // multiplexing + no SR, implement Table 10.1.3-6 (Rel14) for multiplexing with M=3 if (harq_ack[0] == 4 || harq_ack[1] == 4 || harq_ack[2] == 4) { // there isn't a likely transmission harq_ack[0] = 4; // DTX harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX max_metric = 0; } else { max_metric = max(metric[0],max(metric[1],metric[2])); if (metric[0]==max_metric) { if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX tdd_multiplexing_mask = 0x1; } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] != 1){ harq_ack[0] = 2; // NACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX } else { harq_ack[0] = 4; // DTX harq_ack[1] = 4; // DTX harq_ack[2] = 4; // DTX } } // if (metric[0]==max_metric) { else if (metric[1]==max_metric) { if (pucch_b0b1[1][0] == 1 && pucch_b0b1[1][1] != 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK harq_ack[2] = 6; // NACK/DTX tdd_multiplexing_mask = 0x3; } else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] == 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 6; // NACK/DTX tdd_multiplexing_mask = 0x2; } else { harq_ack[0] = 4; // DTX harq_ack[1] = 4; // DTX harq_ack[2] = 4; // DTX } } // if (metric[1]==max_metric) { else { if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK tdd_multiplexing_mask = 0x7; } else if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 1; // ACK tdd_multiplexing_mask = 0x5; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] == 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK tdd_multiplexing_mask = 0x6; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 1; // ACK tdd_multiplexing_mask = 0x4; } } uci->stat = max_metric; fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode } } //else if ((uci->tdd_bundling == 0) && (res==3)) else if ((uci->tdd_bundling == 0) && (uci->num_pucch_resources==4)){ // multiplexing + no SR, implement Table 10.1.3-7 (Rel14) for multiplexing with M=4 if (pucch_b0b1[0][0] == 4 || pucch_b0b1[1][0] == 4 || pucch_b0b1[2][0] == 4 || pucch_b0b1[3][0] == 4) { // there isn't a likely transmission harq_ack[0] = 4; // DTX harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 6; // NACK/DTX max_metric = 0; } else { max_metric = max(metric[0],max(metric[1],max(metric[2],metric[3]))); if (metric[0]==max_metric) { if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] != 1){ harq_ack[0] = 2; // NACK harq_ack[1] = 4; // DTX harq_ack[2] = 4; // DTX harq_ack[3] = 4; // DTX } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0x9; } else if (pucch_b0b1[0][0] == 1 && pucch_b0b1[0][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 6; // NACK/DTX tdd_multiplexing_mask = 0x1; } else if (pucch_b0b1[0][0] != 1 && pucch_b0b1[0][1] != 1){ harq_ack[0] = 2; // NACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 6; // NACK/DTX } } else if (metric[1]==max_metric) { if (pucch_b0b1[1][0] == 1 && pucch_b0b1[1][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0xF; } else if (pucch_b0b1[1][0] == 1 && pucch_b0b1[1][1] != 1 ) { harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 6; // NACK/DTX tdd_multiplexing_mask = 0x3; } else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] != 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0xE; } else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] == 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 6; // NACK/DTX tdd_multiplexing_mask = 0x2; } } else if (metric[2]==max_metric) { if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK harq_ack[3] = 6; // NACK/DTX tdd_multiplexing_mask = 0x7; } else if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 1; // ACK harq_ack[3] = 6; // NACK/DTX tdd_multiplexing_mask = 0x5; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] == 1 ) { harq_ack[0] = 4; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 1; // ACK harq_ack[3] = 4; // NACK/DTX tdd_multiplexing_mask = 0x6; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 4; // NACK/DTX harq_ack[1] = 4; // NACK/DTX harq_ack[2] = 1; // ACK harq_ack[3] = 4; // NACK/DTX tdd_multiplexing_mask = 0x4; } } else { // max_metric[3]=max_metric if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] == 1){ harq_ack[0] = 1; // ACK harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 1; // ACK harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0xD; } else if (pucch_b0b1[2][0] == 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 1; // ACK harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0xA; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] == 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 1; // ACK harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0xC; } else if (pucch_b0b1[2][0] != 1 && pucch_b0b1[2][1] != 1 ) { harq_ack[0] = 6; // NACK/DTX harq_ack[1] = 6; // NACK/DTX harq_ack[2] = 6; // NACK/DTX harq_ack[3] = 1; // ACK tdd_multiplexing_mask = 0x8; } } } uci->stat = max_metric; fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode } // else if ((uci->tdd_bundling == 0) && (res==4)) else { // bundling harq_ack[0] = pucch_b0b1[0][0]; harq_ack[1] = pucch_b0b1[0][1]; uci->stat = metric[0]; LOG_D(PHY,"bundling: (%d,%d), metric %d\n",harq_ack[0],harq_ack[1],uci->stat); fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,0,0xffff); // special_bundling mode } #ifdef DEBUG_PHY_PROC LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d ACK/NAK metric 0 %d, metric 1 %d, (%d,%d)\n",eNB->Mod_id, eNB->dlsch[UE_id][0]->rnti, frame,subframe, metric0,metric1,pucch_b0b1[0],pucch_b0b1[1]); #endif } break; default: AssertFatal(1==0,"Unsupported UCI type %d\n",uci->type); break; } if (SR_payload == 1) { LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d Got SR for PUSCH, transmitting to MAC\n",eNB->Mod_id, uci->rnti,frame,subframe); if (eNB->first_sr[i] == 1) { // this is the first request for uplink after Connection Setup, so clear HARQ process 0 use for Msg4 eNB->first_sr[i] = 0; eNB->dlsch[i][0]->harq_processes[0]->round=0; eNB->dlsch[i][0]->harq_processes[0]->status=SCH_IDLE; LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d First SR\n", eNB->Mod_id, eNB->ulsch[i]->rnti,frame,subframe); } } } } } void pusch_procedures(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc) { uint32_t ret=0,i; uint32_t harq_pid; uint8_t nPRS; LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; LTE_eNB_ULSCH_t *ulsch; LTE_UL_eNB_HARQ_t *ulsch_harq; const int subframe = proc->subframe_rx; const int frame = proc->frame_rx; harq_pid = subframe2harq_pid(&eNB->frame_parms,frame,subframe); for (i=0; i<NUMBER_OF_UE_MAX; i++) { ulsch = eNB->ulsch[i]; ulsch_harq = ulsch->harq_processes[harq_pid]; if (ulsch->rnti>0) LOG_D(PHY,"eNB->ulsch[%d]->harq_processes[harq_pid:%d] SFN/SF:%04d%d: PUSCH procedures, UE %d/%x ulsch_harq[status:%d SFN/SF:%04d%d handled:%d]\n", i, harq_pid, frame,subframe,i,ulsch->rnti, ulsch_harq->status, ulsch_harq->frame, ulsch_harq->subframe, ulsch_harq->handled); if ((ulsch) && (ulsch->rnti>0) && (ulsch_harq->status == ACTIVE) && (ulsch_harq->frame == frame) && (ulsch_harq->subframe == subframe) && (ulsch_harq->handled == 0)) { // UE has ULSCH scheduling for (int rb=0; rb<=ulsch_harq->nb_rb; rb++) { int rb2 = rb+ulsch_harq->first_rb; eNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31)); } LOG_D(PHY,"[eNB %d] frame %d, subframe %d: Scheduling ULSCH Reception for UE %d \n", eNB->Mod_id, frame, subframe, i); nPRS = fp->pusch_config_common.ul_ReferenceSignalsPUSCH.nPRS[subframe<<1]; ulsch->cyclicShift = (ulsch_harq->n_DMRS2 + fp->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift + nPRS)%12; AssertFatal(ulsch_harq->TBS>0,"illegal TBS %d\n",ulsch_harq->TBS); LOG_D(PHY, "[eNB %d][PUSCH %d] Frame %d Subframe %d Demodulating PUSCH: dci_alloc %d, rar_alloc %d, round %d, first_rb %d, nb_rb %d, Qm %d, TBS %d, rv %d, cyclic_shift %d (n_DMRS2 %d, cyclicShift_common %d, ), O_ACK %d, beta_cqi %d \n", eNB->Mod_id,harq_pid,frame,subframe, ulsch_harq->dci_alloc, ulsch_harq->rar_alloc, ulsch_harq->round, ulsch_harq->first_rb, ulsch_harq->nb_rb, ulsch_harq->Qm, ulsch_harq->TBS, ulsch_harq->rvidx, ulsch->cyclicShift, ulsch_harq->n_DMRS2, fp->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift, ulsch_harq->O_ACK, ulsch->beta_offset_cqi_times8); start_meas(&eNB->ulsch_demodulation_stats); rx_ulsch(eNB,proc, i); stop_meas(&eNB->ulsch_demodulation_stats); start_meas(&eNB->ulsch_decoding_stats); ret = ulsch_decoding(eNB,proc, i, 0, // control_only_flag ulsch_harq->V_UL_DAI, ulsch_harq->nb_rb>20 ? 1 : 0); stop_meas(&eNB->ulsch_decoding_stats); LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d RNTI %x RX power (%d,%d) N0 (%d,%d) dB ACK (%d,%d), decoding iter %d ulsch_harq->cqi_crc_status:%d ackBits:%d ulsch_decoding_stats[t:%lld max:%lld]\n", eNB->Mod_id,harq_pid, frame,subframe, ulsch->rnti, dB_fixed(eNB->pusch_vars[i]->ulsch_power[0]), dB_fixed(eNB->pusch_vars[i]->ulsch_power[1]), 30,//eNB->measurements.n0_power_dB[0], 30,//eNB->measurements.n0_power_dB[1], ulsch_harq->o_ACK[0], ulsch_harq->o_ACK[1], ret, ulsch_harq->cqi_crc_status, ulsch_harq->O_ACK, eNB->ulsch_decoding_stats.p_time, eNB->ulsch_decoding_stats.max); //compute the expected ULSCH RX power (for the stats) ulsch_harq->delta_TF = get_hundred_times_delta_IF_eNB(eNB,i,harq_pid, 0); // 0 means bw_factor is not considered if (RC.mac != NULL) { /* ulsim dose not use RC.mac context. */ if (ulsch_harq->cqi_crc_status == 1) { #ifdef DEBUG_PHY_PROC //if (((frame%10) == 0) || (frame < 50)) print_CQI(ulsch_harq->o,ulsch_harq->uci_format,0,fp->N_RB_DL); #endif fill_ulsch_cqi_indication(eNB,frame,subframe,ulsch_harq,ulsch->rnti); RC.mac[eNB->Mod_id]->UE_list.UE_sched_ctrl[i].cqi_req_flag &= (~(1 << subframe)); } else { if(RC.mac[eNB->Mod_id]->UE_list.UE_sched_ctrl[i].cqi_req_flag & (1 << subframe) ){ RC.mac[eNB->Mod_id]->UE_list.UE_sched_ctrl[i].cqi_req_flag &= (~(1 << subframe)); RC.mac[eNB->Mod_id]->UE_list.UE_sched_ctrl[i].cqi_req_timer=30; LOG_D(PHY,"Frame %d,Subframe %d, We're supposed to get a cqi here. Set cqi_req_timer to 30.\n",frame,subframe); } } } if (ret == (1+MAX_TURBO_ITERATIONS)) { T(T_ENB_PHY_ULSCH_UE_NACK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(ulsch->rnti), T_INT(harq_pid)); fill_crc_indication(eNB,i,frame,subframe,1); // indicate NAK to MAC fill_rx_indication(eNB,i,frame,subframe); // indicate SDU to MAC LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d UE %d Error receiving ULSCH, round %d/%d (ACK %d,%d)\n", eNB->Mod_id,harq_pid, frame,subframe, i, ulsch_harq->round, ulsch->Mlimit, ulsch_harq->o_ACK[0], ulsch_harq->o_ACK[1]); if (ulsch_harq->round >= 3) { ulsch_harq->status = SCH_IDLE; ulsch_harq->handled = 0; ulsch->harq_mask &= ~(1 << harq_pid); ulsch_harq->round = 0; } MSC_LOG_RX_DISCARDED_MESSAGE( MSC_PHY_ENB,MSC_PHY_UE, NULL,0, "%05u:%02u ULSCH received rnti %x harq id %u round %d", frame,subframe, ulsch->rnti,harq_pid, ulsch_harq->round-1 ); /* Mark the HARQ process to release it later if max transmission reached * (see below). * MAC does not send the max transmission count, we have to deal with it * locally in PHY. */ ulsch_harq->handled = 1; } // ulsch in error else { fill_crc_indication(eNB,i,frame,subframe,0); // indicate ACK to MAC fill_rx_indication(eNB,i,frame,subframe); // indicate SDU to MAC ulsch_harq->status = SCH_IDLE; ulsch->harq_mask &= ~(1 << harq_pid); T(T_ENB_PHY_ULSCH_UE_ACK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(ulsch->rnti), T_INT(harq_pid)); MSC_LOG_RX_MESSAGE( MSC_PHY_ENB,MSC_PHY_UE, NULL,0, "%05u:%02u ULSCH received rnti %x harq id %u", frame,subframe, ulsch->rnti,harq_pid ); #ifdef DEBUG_PHY_PROC #ifdef DEBUG_ULSCH LOG_D(PHY,"[eNB] Frame %d, Subframe %d : ULSCH SDU (RX harq_pid %d) %d bytes:",frame,subframe, harq_pid,ulsch_harq->TBS>>3); for (j=0; j<ulsch_harq->TBS>>3; j++) LOG_T(PHY,"%x.",ulsch->harq_processes[harq_pid]->b[j]); LOG_T(PHY,"\n"); #endif #endif } // ulsch not in error if (ulsch_harq->O_ACK>0) fill_ulsch_harq_indication(eNB,ulsch_harq,ulsch->rnti,frame,subframe,ulsch->bundling); LOG_D(PHY,"[eNB %d] Frame %d subframe %d: received ULSCH harq_pid %d for UE %d, ret = %d, CQI CRC Status %d, ACK %d,%d, ulsch_errors %d/%d\n", eNB->Mod_id,frame,subframe, harq_pid, i, ret, ulsch_harq->cqi_crc_status, ulsch_harq->o_ACK[0], ulsch_harq->o_ACK[1], eNB->UE_stats[i].ulsch_errors[harq_pid], eNB->UE_stats[i].ulsch_decoding_attempts[harq_pid][0]); } // if ((ulsch) && // (ulsch->rnti>0) && // (ulsch_harq->status == ACTIVE)) else if ((ulsch) && (ulsch->rnti>0) && (ulsch_harq->status == ACTIVE) && (ulsch_harq->frame == frame) && (ulsch_harq->subframe == subframe) && (ulsch_harq->handled == 1)) { // this harq process is stale, kill it, this 1024 frames later (10s), consider reducing that ulsch_harq->status = SCH_IDLE; ulsch_harq->handled = 0; ulsch->harq_mask &= ~(1 << harq_pid); LOG_W(PHY,"Removing stale ULSCH config for UE %x harq_pid %d (harq_mask is now 0x%2.2x)\n", ulsch->rnti, harq_pid, ulsch->harq_mask); } } // for (i=0; i<NUMBER_OF_UE_MAX; i++) } extern int oai_exit; extern void *td_thread(void*); void init_td_thread(PHY_VARS_eNB *eNB) { eNB_proc_t *proc = &eNB->proc; proc->tdp.eNB = eNB; proc->instance_cnt_td = -1; threadCreate(&proc->pthread_td, td_thread, (void*)&proc->tdp, "TD", -1, OAI_PRIORITY_RT); } void kill_td_thread(PHY_VARS_eNB *eNB) { eNB_proc_t *proc = &eNB->proc; proc->instance_cnt_td = 0; pthread_cond_signal(&proc->cond_td); pthread_join(proc->pthread_td, NULL); pthread_mutex_destroy( &proc->mutex_td ); pthread_cond_destroy( &proc->cond_td ); } extern void *te_thread(void*); void init_te_thread(PHY_VARS_eNB *eNB) { eNB_proc_t *proc = &eNB->proc; for(int i=0; i<3 ;i++){ proc->tep[i].eNB = eNB; proc->tep[i].instance_cnt_te = -1; LOG_I(PHY,"Creating te_thread %d\n",i); char txt[128]; sprintf(txt,"TE_%d", i); threadCreate(&proc->tep[i].pthread_te, te_thread, (void*)&proc->tep[i], txt, -1, OAI_PRIORITY_RT); } } void kill_te_thread(PHY_VARS_eNB *eNB) { eNB_proc_t *proc = &eNB->proc; for(int i=0; i<3 ;i++){ proc->tep[i].instance_cnt_te = 0; pthread_cond_signal(&proc->tep[i].cond_te); pthread_join(proc->tep[i].pthread_te, NULL); pthread_mutex_destroy( &proc->tep[i].mutex_te); pthread_cond_destroy( &proc->tep[i].cond_te); } } void fill_rx_indication(PHY_VARS_eNB *eNB,int UE_id,int frame,int subframe) { nfapi_rx_indication_pdu_t *pdu; int timing_advance_update; int sync_pos; uint32_t harq_pid = subframe2harq_pid(&eNB->frame_parms, frame,subframe); pthread_mutex_lock(&eNB->UL_INFO_mutex); eNB->UL_INFO.rx_ind.sfn_sf = frame<<4| subframe; eNB->UL_INFO.rx_ind.rx_indication_body.tl.tag = NFAPI_RX_INDICATION_BODY_TAG; pdu = &eNB->UL_INFO.rx_ind.rx_indication_body.rx_pdu_list[eNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus]; // pdu->rx_ue_information.handle = eNB->ulsch[UE_id]->handle; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = eNB->ulsch[UE_id]->rnti; pdu->rx_indication_rel8.tl.tag = NFAPI_RX_INDICATION_REL8_TAG; pdu->rx_indication_rel8.length = eNB->ulsch[UE_id]->harq_processes[harq_pid]->TBS>>3; pdu->rx_indication_rel8.offset = 1; // DJP - I dont understand - but broken unless 1 ???? 0; // filled in at the end of the UL_INFO formation pdu->data = eNB->ulsch[UE_id]->harq_processes[harq_pid]->b; // estimate timing advance for MAC sync_pos = lte_est_timing_advance_pusch(eNB,UE_id); timing_advance_update = sync_pos; // - eNB->frame_parms.nb_prefix_samples/4; //to check // if (timing_advance_update > 10) { dump_ulsch(eNB,frame,subframe,UE_id); exit(-1);} // if (timing_advance_update < -10) { dump_ulsch(eNB,frame,subframe,UE_id); exit(-1);} switch (eNB->frame_parms.N_RB_DL) { case 6: /* nothing to do */ break; case 15: timing_advance_update /= 2; break; case 25: timing_advance_update /= 4; break; case 50: timing_advance_update /= 8; break; case 75: timing_advance_update /= 12; break; case 100: timing_advance_update /= 16; break; default: abort(); } // put timing advance command in 0..63 range timing_advance_update += 31; if (timing_advance_update < 0) timing_advance_update = 0; if (timing_advance_update > 63) timing_advance_update = 63; pdu->rx_indication_rel8.timing_advance = timing_advance_update; // estimate UL_CQI for MAC (from antenna port 0 only) int SNRtimes10 = dB_fixed_times10(eNB->pusch_vars[UE_id]->ulsch_power[0]) - 300;//(10*eNB->measurements.n0_power_dB[0]); if (SNRtimes10 < -640) pdu->rx_indication_rel8.ul_cqi=0; else if (SNRtimes10 > 635) pdu->rx_indication_rel8.ul_cqi=255; else pdu->rx_indication_rel8.ul_cqi=(640+SNRtimes10)/5; LOG_D(PHY,"[PUSCH %d] Frame %d Subframe %d Filling RX_indication with SNR %d (%d), timing_advance %d (update %d)\n", harq_pid,frame,subframe,SNRtimes10,pdu->rx_indication_rel8.ul_cqi,pdu->rx_indication_rel8.timing_advance, timing_advance_update); eNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus++; eNB->UL_INFO.rx_ind.sfn_sf = frame<<4 | subframe; pthread_mutex_unlock(&eNB->UL_INFO_mutex); } /* release the harq if its round is >= 'after_rounds' */ static void do_release_harq(PHY_VARS_eNB *eNB,int UE_id,int tb,uint16_t frame,uint8_t subframe,uint16_t mask, int after_rounds) { LTE_eNB_DLSCH_t *dlsch0=NULL,*dlsch1=NULL; LTE_DL_eNB_HARQ_t *dlsch0_harq=NULL,*dlsch1_harq=NULL; int harq_pid; int subframe_tx,frame_tx; int M,m; AssertFatal(UE_id!=-1,"no existing dlsch context\n"); AssertFatal(UE_id<NUMBER_OF_UE_MAX,"returned UE_id %d >= %d(NUMBER_OF_UE_MAX)\n",UE_id,NUMBER_OF_UE_MAX); dlsch0 = eNB->dlsch[UE_id][0]; dlsch1 = eNB->dlsch[UE_id][1]; if (eNB->frame_parms.frame_type == FDD) { subframe_tx = (subframe+6)%10; frame_tx = ul_ACK_subframe2_dl_frame(&eNB->frame_parms,frame,subframe,subframe_tx); harq_pid = dlsch0->harq_ids[frame_tx%2][subframe_tx]; // or just use 0 for fdd? AssertFatal((harq_pid>=0) && (harq_pid<10),"harq_pid %d not in 0...9\n",harq_pid); dlsch0_harq = dlsch0->harq_processes[harq_pid]; dlsch1_harq = dlsch1->harq_processes[harq_pid]; AssertFatal(dlsch0_harq!=NULL,"dlsch0_harq is null\n"); #if T_TRACER if (after_rounds != -1) { T(T_ENB_PHY_DLSCH_UE_NACK, T_INT(0), T_INT(frame), T_INT(subframe), T_INT(dlsch0->rnti), T_INT(harq_pid)); } else { T(T_ENB_PHY_DLSCH_UE_ACK, T_INT(0), T_INT(frame), T_INT(subframe), T_INT(dlsch0->rnti), T_INT(harq_pid)); } #endif if (dlsch0_harq->round >= after_rounds) { dlsch0_harq->status = SCH_IDLE; /*if ((dlsch1_harq == NULL)|| ((dlsch1_harq!=NULL)&& (dlsch1_harq->status == SCH_IDLE)))*/ dlsch0->harq_mask &= ~(1<<harq_pid); } LOG_D(PHY,"Frame %d, subframe %d: Releasing harq %d for UE %x\n",frame,subframe,harq_pid,dlsch0->rnti); } else { // release all processes in the bundle that was acked, based on mask // This is at most 4 for multiplexing and 9 for bundling/special bundling M=ul_ACK_subframe2_M(&eNB->frame_parms, subframe); for (m=0; m<M; m++) { subframe_tx = ul_ACK_subframe2_dl_subframe(&eNB->frame_parms, subframe, m); frame_tx = ul_ACK_subframe2_dl_frame(&eNB->frame_parms,frame,subframe,subframe_tx); if (((1<<m)&mask) > 0) { harq_pid = dlsch0->harq_ids[frame_tx%2][subframe_tx]; if ((harq_pid>=0) && (harq_pid<dlsch0->Mdlharq)) { dlsch0_harq = dlsch0->harq_processes[harq_pid]; dlsch1_harq = dlsch1->harq_processes[harq_pid]; AssertFatal(dlsch0_harq!=NULL,"dlsch0_harq is null\n"); #if T_TRACER if (after_rounds != -1) { T(T_ENB_PHY_DLSCH_UE_NACK, T_INT(0), T_INT(frame), T_INT(subframe), T_INT(dlsch0->rnti), T_INT(harq_pid)); } else { T(T_ENB_PHY_DLSCH_UE_ACK, T_INT(0), T_INT(frame), T_INT(subframe), T_INT(dlsch0->rnti), T_INT(harq_pid)); } #endif if (dlsch0_harq->round >= after_rounds) { dlsch0_harq->status = SCH_IDLE; if ((dlsch1_harq == NULL)|| ((dlsch1_harq!=NULL)&& (dlsch1_harq->status == SCH_IDLE))) dlsch0->harq_mask &= ~(1<<harq_pid); } } } } } } static void release_harq(PHY_VARS_eNB *eNB,int UE_id,int tb,uint16_t frame,uint8_t subframe,uint16_t mask, int is_ack) { /* Maximum number of DL transmissions = 4. * TODO: get the value from configuration. * If is_ack is true then we release immediately. The value -1 can be used for that. */ do_release_harq(eNB, UE_id, tb, frame, subframe, mask, is_ack ? -1 : 4); } int getM(PHY_VARS_eNB *eNB,int frame,int subframe) { int M,Mtx=0; LTE_eNB_DLSCH_t *dlsch0=NULL,*dlsch1=NULL; LTE_DL_eNB_HARQ_t *dlsch0_harq=NULL,*dlsch1_harq=NULL; int harq_pid; int subframe_tx,frame_tx; int m; M=ul_ACK_subframe2_M(&eNB->frame_parms, subframe); for (m=0; m<M; m++) { subframe_tx = ul_ACK_subframe2_dl_subframe(&eNB->frame_parms, subframe, m); frame_tx = ul_ACK_subframe2_dl_frame(&eNB->frame_parms,frame, subframe,subframe_tx); harq_pid = dlsch0->harq_ids[frame_tx%2][subframe_tx]; if (harq_pid>=0 && harq_pid<10) { dlsch0_harq = dlsch0->harq_processes[harq_pid]; dlsch1_harq = dlsch1->harq_processes[harq_pid]; AssertFatal(dlsch0_harq!=NULL,"dlsch0_harq is null\n"); if (dlsch0_harq->status == ACTIVE|| (dlsch1_harq!=NULL && dlsch1_harq->status == ACTIVE)) Mtx ++; } } return(Mtx); } void fill_ulsch_cqi_indication(PHY_VARS_eNB *eNB,uint16_t frame,uint8_t subframe,LTE_UL_eNB_HARQ_t *ulsch_harq,uint16_t rnti) { pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_cqi_indication_pdu_t *pdu = &eNB->UL_INFO.cqi_ind.cqi_pdu_list[eNB->UL_INFO.cqi_ind.number_of_cqis]; nfapi_cqi_indication_raw_pdu_t *raw_pdu = &eNB->UL_INFO.cqi_ind.cqi_raw_pdu_list[eNB->UL_INFO.cqi_ind.number_of_cqis]; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = rnti; if (ulsch_harq->cqi_crc_status != 1) pdu->cqi_indication_rel9.data_offset = 0; else pdu->cqi_indication_rel9.data_offset = 1; // fill in after all cqi_indications have been generated when non-zero // by default set O to rank 1 value pdu->cqi_indication_rel9.tl.tag = NFAPI_CQI_INDICATION_REL9_TAG; pdu->cqi_indication_rel9.length = (ulsch_harq->Or1>>3) + ((ulsch_harq->Or1&7) > 0 ? 1 : 0); pdu->cqi_indication_rel9.ri[0] = 0; // if we have RI bits, set them and if rank2 overwrite O if (ulsch_harq->O_RI>0) { pdu->cqi_indication_rel9.ri[0] = ulsch_harq->o_RI[0]; if (ulsch_harq->o_RI[0] == 2) pdu->cqi_indication_rel9.length = (ulsch_harq->Or2>>3) + ((ulsch_harq->Or2&7) > 0 ? 1 : 0); pdu->cqi_indication_rel9.timing_advance = 0; } pdu->cqi_indication_rel9.number_of_cc_reported = 1; pdu->ul_cqi_information.channel = 1; // PUSCH memcpy((void*)raw_pdu->pdu,ulsch_harq->o,pdu->cqi_indication_rel9.length); eNB->UL_INFO.cqi_ind.number_of_cqis++; LOG_D(PHY,"eNB->UL_INFO.cqi_ind.number_of_cqis:%d\n", eNB->UL_INFO.cqi_ind.number_of_cqis); pthread_mutex_unlock(&eNB->UL_INFO_mutex); } void fill_ulsch_harq_indication(PHY_VARS_eNB *eNB,LTE_UL_eNB_HARQ_t *ulsch_harq,uint16_t rnti, int frame,int subframe,int bundling) { int UE_id = find_dlsch(rnti,eNB,SEARCH_EXIST); if( (UE_id<0) || (UE_id>=NUMBER_OF_UE_MAX) ){ LOG_E(PHY,"illegal UE_id found!!! rnti %04x UE_id %d\n",rnti,UE_id); return; } //AssertFatal(UE_id>=0,"UE_id doesn't exist\n"); pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_harq_indication_pdu_t *pdu = &eNB->UL_INFO.harq_ind.harq_indication_body.harq_pdu_list[eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs]; int M; int i; eNB->UL_INFO.harq_ind.header.message_id = NFAPI_HARQ_INDICATION; eNB->UL_INFO.harq_ind.sfn_sf = frame<<4|subframe; eNB->UL_INFO.harq_ind.harq_indication_body.tl.tag = NFAPI_HARQ_INDICATION_BODY_TAG; pdu->instance_length = 0; // don't know what to do with this // pdu->rx_ue_information.handle = handle; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = rnti; if (eNB->frame_parms.frame_type == FDD) { pdu->harq_indication_fdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_FDD_REL13_TAG; pdu->harq_indication_fdd_rel13.mode = 0; pdu->harq_indication_fdd_rel13.number_of_ack_nack = ulsch_harq->O_ACK; for (i=0;i<ulsch_harq->O_ACK;i++) { AssertFatal(ulsch_harq->o_ACK[i] == 0 || ulsch_harq->o_ACK[i] == 1, "harq_ack[%d] is %d, should be 1,2 or 4\n",i,ulsch_harq->o_ACK[i]); pdu->harq_indication_fdd_rel13.harq_tb_n[i] = 2-ulsch_harq->o_ACK[i]; // release DLSCH if needed release_harq(eNB,UE_id,i,frame,subframe,0xffff, ulsch_harq->o_ACK[i] == 1); } } else { // TDD M=ul_ACK_subframe2_M(&eNB->frame_parms, subframe); pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.mode = 1-bundling; pdu->harq_indication_tdd_rel13.number_of_ack_nack = ulsch_harq->O_ACK; for (i=0;i<ulsch_harq->O_ACK;i++) { AssertFatal(ulsch_harq->o_ACK[i] == 0 || ulsch_harq->o_ACK[i] == 1, "harq_ack[%d] is %d, should be 1,2 or 4\n",i,ulsch_harq->o_ACK[i]); pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 2-ulsch_harq->o_ACK[i]; // release DLSCH if needed /* TODO: review this code, it's most certainly wrong. * We have to release the proper HARQ in case of ACK or NACK if max retransmission reached. * Basically, call release_harq with 1 as last argument when ACK and 0 when NACK. */ release_harq(eNB,UE_id,i,frame,subframe,0xffff, ulsch_harq->o_ACK[i] == 1); if (M==1 && ulsch_harq->O_ACK==1 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff, ulsch_harq->o_ACK[i] == 1); else if (M==1 && ulsch_harq->O_ACK==2 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff, ulsch_harq->o_ACK[i] == 1); else if (M>1 && ulsch_harq->o_ACK[i] == 1) { // spatial bundling release_harq(eNB,UE_id,0,frame,subframe,1<<i, ulsch_harq->o_ACK[i] == 1); release_harq(eNB,UE_id,1,frame,subframe,1<<i, ulsch_harq->o_ACK[i] == 1); } } } //LOG_E(PHY,"eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs:%d\n", eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs); eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs++; pthread_mutex_unlock(&eNB->UL_INFO_mutex); } void fill_uci_harq_indication(PHY_VARS_eNB *eNB, LTE_eNB_UCI *uci, int frame, int subframe, uint8_t *harq_ack, uint8_t tdd_mapping_mode, uint16_t tdd_multiplexing_mask) { int UE_id=find_dlsch(uci->rnti,eNB,SEARCH_EXIST); //AssertFatal(UE_id>=0,"UE_id doesn't exist rnti:%x\n", uci->rnti); if (UE_id < 0) { LOG_E(PHY,"SFN/SF:%04d%d Unable to find rnti:%x do not send HARQ\n", frame, subframe, uci->rnti); return; } pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_harq_indication_t *ind = &eNB->UL_INFO.harq_ind; nfapi_harq_indication_body_t *body = &ind->harq_indication_body; nfapi_harq_indication_pdu_t *pdu = &body->harq_pdu_list[eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs]; ind->sfn_sf = frame<<4|subframe; ind->header.message_id = NFAPI_HARQ_INDICATION; body->tl.tag = NFAPI_HARQ_INDICATION_BODY_TAG; pdu->instance_length = 0; // don't know what to do with this // pdu->rx_ue_information.handle = handle; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = uci->rnti; // estimate UL_CQI for MAC (from antenna port 0 only) pdu->ul_cqi_information.tl.tag = NFAPI_UL_CQI_INFORMATION_TAG; int SNRtimes10 = dB_fixed_times10(uci->stat) - 300;//(10*eNB->measurements.n0_power_dB[0]); if (SNRtimes10 < -100) LOG_I(PHY,"uci->stat %d \n",uci->stat); if (SNRtimes10 < -640) pdu->ul_cqi_information.ul_cqi=0; else if (SNRtimes10 > 635) pdu->ul_cqi_information.ul_cqi=255; else pdu->ul_cqi_information.ul_cqi=(640+SNRtimes10)/5; pdu->ul_cqi_information.channel = 0; if (eNB->frame_parms.frame_type == FDD) { if (uci->pucch_fmt == pucch_format1a) { pdu->harq_indication_fdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_FDD_REL13_TAG; pdu->harq_indication_fdd_rel13.mode = 0; pdu->harq_indication_fdd_rel13.number_of_ack_nack = 1; AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]); pdu->harq_indication_fdd_rel13.harq_tb_n[0] = harq_ack[0]; // release DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); } else if (uci->pucch_fmt == pucch_format1b) { pdu->harq_indication_fdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_FDD_REL13_TAG; pdu->harq_indication_fdd_rel13.mode = 0; pdu->harq_indication_fdd_rel13.number_of_ack_nack = 2; AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]); AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]); pdu->harq_indication_fdd_rel13.harq_tb_n[0] = harq_ack[0]; pdu->harq_indication_fdd_rel13.harq_tb_n[1] = harq_ack[1]; // release DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, harq_ack[1] == 1); } else AssertFatal(1==0,"only format 1a/b for now, received %d\n",uci->pucch_fmt); } else { // TDD AssertFatal(tdd_mapping_mode==0 || tdd_mapping_mode==1 || tdd_mapping_mode==2, "Illegal tdd_mapping_mode %d\n",tdd_mapping_mode); pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.mode = tdd_mapping_mode; LOG_D(PHY,"%s(eNB, uci_harq format %d, rnti:%04x, frame:%d, subframe:%d, tdd_mapping_mode:%d) harq_ack[0]:%d harq_ack[1]:%d\n", __FUNCTION__, uci->pucch_fmt,uci->rnti, frame, subframe, tdd_mapping_mode,harq_ack[0],harq_ack[1]); switch (tdd_mapping_mode) { case 0: // bundling if (uci->pucch_fmt == pucch_format1a) { pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1; LOG_D(PHY,"bundling, pucch1a, number of ack nack %d\n",pdu->harq_indication_tdd_rel13.number_of_ack_nack); AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]); pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = harq_ack[0]; // release all bundled DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); } else if (uci->pucch_fmt == pucch_format1b) { pdu->harq_indication_tdd_rel13.number_of_ack_nack = 2; AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]); AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]); pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = harq_ack[0]; pdu->harq_indication_tdd_rel13.harq_data[1].bundling.value_0 = harq_ack[1]; // release all DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, harq_ack[1] == 1); } break; case 1: // multiplexing AssertFatal(uci->pucch_fmt == pucch_format1b,"uci->pucch_format %d is not format1b\n",uci->pucch_fmt); if (uci->num_pucch_resources == 1 && uci->pucch_fmt == pucch_format1a) { pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1; AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]); pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0]; // release all DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); } else if (uci->num_pucch_resources == 1 && uci->pucch_fmt == pucch_format1b) { pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.number_of_ack_nack = 2; AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]); AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]); pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0]; pdu->harq_indication_tdd_rel13.harq_data[1].multiplex.value_0 = harq_ack[1]; // release all DLSCH if needed release_harq(eNB,UE_id,0,frame,subframe,0xffff, harq_ack[0] == 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, harq_ack[1] == 1); } else { // num_pucch_resources (M) > 1 pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.number_of_ack_nack = uci->num_pucch_resources; pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0]; pdu->harq_indication_tdd_rel13.harq_data[1].multiplex.value_0 = harq_ack[1]; if (uci->num_pucch_resources == 3) pdu->harq_indication_tdd_rel13.harq_data[2].multiplex.value_0 = harq_ack[2]; if (uci->num_pucch_resources == 4) pdu->harq_indication_tdd_rel13.harq_data[3].multiplex.value_0 = harq_ack[3]; // spatial-bundling in this case so release both HARQ if necessary release_harq(eNB,UE_id,0,frame,subframe,tdd_multiplexing_mask, 1 /* force release? previous code was unconditional */); release_harq(eNB,UE_id,1,frame,subframe,tdd_multiplexing_mask, 1 /* force release? previous code was unconditional */); } break; case 2: // special bundling (SR collision) pdu->harq_indication_tdd_rel13.tl.tag = NFAPI_HARQ_INDICATION_TDD_REL13_TAG; pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1; pdu->harq_indication_tdd_rel13.mode = 0; int tdd_config5_sf2scheds=0; if (eNB->frame_parms.tdd_config==5) tdd_config5_sf2scheds = getM(eNB,frame,subframe); switch (harq_ack[0]) { case 0: case 4: pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 0; /* TODO: release_harq here? this whole code looks suspicious */ break; case 1: // check if M=1,4,7 if (uci->num_pucch_resources == 1 || uci->num_pucch_resources == 4 || tdd_config5_sf2scheds == 1 || tdd_config5_sf2scheds == 4 || tdd_config5_sf2scheds == 7) { pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 1; release_harq(eNB,UE_id,0,frame,subframe,0xffff, 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, 1); }else{ pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 0; } break; case 2: // check if M=2,5,8 if (uci->num_pucch_resources == 2 || tdd_config5_sf2scheds == 2 || tdd_config5_sf2scheds == 5 || tdd_config5_sf2scheds == 8) { pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 1; release_harq(eNB,UE_id,0,frame,subframe,0xffff, 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, 1); }else{ pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 0; } break; case 3: // check if M=3,6,9 if (uci->num_pucch_resources == 3 || tdd_config5_sf2scheds == 3 || tdd_config5_sf2scheds == 6 || tdd_config5_sf2scheds == 9) { pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 1; release_harq(eNB,UE_id,0,frame,subframe,0xffff, 1); release_harq(eNB,UE_id,1,frame,subframe,0xffff, 1); }else{ pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = 0; } break; } break; } } //TDD eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs++; LOG_D(PHY,"Incremented eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs:%d\n", eNB->UL_INFO.harq_ind.harq_indication_body.number_of_harqs); pthread_mutex_unlock(&eNB->UL_INFO_mutex); } void fill_crc_indication(PHY_VARS_eNB *eNB,int UE_id,int frame,int subframe,uint8_t crc_flag) { pthread_mutex_lock(&eNB->UL_INFO_mutex); nfapi_crc_indication_pdu_t *pdu = &eNB->UL_INFO.crc_ind.crc_indication_body.crc_pdu_list[eNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs]; eNB->UL_INFO.crc_ind.sfn_sf = frame<<4 | subframe; eNB->UL_INFO.crc_ind.header.message_id = NFAPI_CRC_INDICATION; eNB->UL_INFO.crc_ind.crc_indication_body.tl.tag = NFAPI_CRC_INDICATION_BODY_TAG; pdu->instance_length = 0; // don't know what to do with this // pdu->rx_ue_information.handle = handle; pdu->rx_ue_information.tl.tag = NFAPI_RX_UE_INFORMATION_TAG; pdu->rx_ue_information.rnti = eNB->ulsch[UE_id]->rnti; pdu->crc_indication_rel8.tl.tag = NFAPI_CRC_INDICATION_REL8_TAG; pdu->crc_indication_rel8.crc_flag = crc_flag; eNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs++; //LOG_D(PHY, "%s() rnti:%04x crcs:%d crc_flag:%d\n", __FUNCTION__, pdu->rx_ue_information.rnti, eNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs, crc_flag); pthread_mutex_unlock(&eNB->UL_INFO_mutex); } void phy_procedures_eNB_uespec_RX(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc) { //RX processing for ue-specific resources (i LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms; const int subframe = proc->subframe_rx; const int frame = proc->frame_rx; /* TODO: use correct rxdata */ T(T_ENB_PHY_INPUT_SIGNAL, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(0), T_BUFFER(&eNB->RU_list[0]->common.rxdata[0][subframe*eNB->frame_parms.samples_per_tti], eNB->frame_parms.samples_per_tti * 4)); if ((fp->frame_type == TDD) && (subframe_select(fp,subframe)!=SF_UL)) return; T(T_ENB_PHY_UL_TICK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe)); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_UESPEC, 1 ); LOG_D(PHY,"[eNB %d] Frame %d: Doing phy_procedures_eNB_uespec_RX(%d)\n",eNB->Mod_id,frame, subframe); eNB->rb_mask_ul[0]=0; eNB->rb_mask_ul[1]=0; eNB->rb_mask_ul[2]=0; eNB->rb_mask_ul[3]=0; // Fix me here, these should be locked eNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus = 0; eNB->UL_INFO.crc_ind.crc_indication_body.number_of_crcs = 0; // Call SRS first since all others depend on presence of SRS or lack thereof srs_procedures(eNB,proc); eNB->first_run_I0_measurements = 0; uci_procedures(eNB,proc); if (nfapi_mode == 0 || nfapi_mode == 1) { // If PNF or monolithic pusch_procedures(eNB,proc); } lte_eNB_I0_measurements(eNB, subframe, 0, eNB->first_run_I0_measurements); int min_I0=1000,max_I0=0; if ((frame==0) && (subframe==4)) { for (int i=0;i<eNB->frame_parms.N_RB_UL;i++) { if (i==(eNB->frame_parms.N_RB_UL>>1) - 1) i+=2; if (eNB->measurements.n0_subband_power_tot_dB[i]<min_I0) min_I0 = eNB->measurements.n0_subband_power_tot_dB[i]; if (eNB->measurements.n0_subband_power_tot_dB[i]>max_I0) max_I0 = eNB->measurements.n0_subband_power_tot_dB[i]; } LOG_I(PHY,"max_I0 %d, min_I0 %d\n",max_I0,min_I0); } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_UESPEC, 0 ); }