/*
 * 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.0  (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.h"
#include "PHY/extern.h"
#include "SCHED/defs.h"
#include "SCHED/extern.h"
#include "nfapi_interface.h"

#include "PHY/LTE_TRANSPORT/if4_tools.h"
#include "PHY/LTE_TRANSPORT/if5_tools.h"

//#define DEBUG_PHY_PROC (Already defined in cmake)
//#define DEBUG_ULSCH

#include "LAYER2/MAC/extern.h"
#include "LAYER2/MAC/defs.h"

#include "UTIL/LOG/log.h"
#include "UTIL/LOG/vcd_signal_dumper.h"

#include "T.h"

#include "assertions.h"
#include "msc.h"

#include <time.h>

#if defined(ENABLE_ITTI)
#   include "intertask_interface.h"
#endif


#if defined(FLEXRAN_AGENT_SB_IF)
//Agent-related headers
#include "ENB_APP/flexran_agent_extern.h"
#include "ENB_APP/CONTROL_MODULES/MAC/flexran_agent_mac.h"
#include "LAYER2/MAC/flexran_agent_mac_proto.h"
#endif


#define NS_PER_SLOT 500000

#define PUCCH 1

void exit_fun(const char* s);

extern int exit_openair;
struct timespec start_fh, start_fh_prev;
int start_fh_sf, start_fh_prev_sf;
// Fix per CC openair rf/if device update
// extern openair0_device openair0;

unsigned char dlsch_input_buffer[2700] __attribute__ ((aligned(32)));
int eNB_sync_buffer0[640*6] __attribute__ ((aligned(32)));
int eNB_sync_buffer1[640*6] __attribute__ ((aligned(32)));
int *eNB_sync_buffer[2] = {eNB_sync_buffer0, eNB_sync_buffer1};

extern uint16_t hundred_times_log10_NPRB[100];

unsigned int max_peak_val;
int max_sync_pos;

int harq_pid_updated[NUMBER_OF_UE_MAX][8] = {{0}};
int harq_pid_round[NUMBER_OF_UE_MAX][8] = {{0}};

//DCI_ALLOC_t dci_alloc[8];

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);
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);
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);
void fill_sr_indication(PHY_VARS_eNB *eNB,uint16_t rnti,int frame,int subframe);
void fill_rx_indication(PHY_VARS_eNB *eNB,int UE_id,int frame,int subframe);
void fill_crc_indication(PHY_VARS_eNB *eNB,int UE_id,int frame,int subframe,uint8_t crc_flag);



#define AMP_OVER_SQRT2 ((AMP*ONE_OVER_SQRT2_Q15)>>15)
#define AMP_OVER_2 (AMP>>1)
int QPSK[4]= {AMP_OVER_SQRT2|(AMP_OVER_SQRT2<<16),AMP_OVER_SQRT2|((65536-AMP_OVER_SQRT2)<<16),((65536-AMP_OVER_SQRT2)<<16)|AMP_OVER_SQRT2,((65536-AMP_OVER_SQRT2)<<16)|(65536-AMP_OVER_SQRT2)};
int QPSK2[4]= {AMP_OVER_2|(AMP_OVER_2<<16),AMP_OVER_2|((65536-AMP_OVER_2)<<16),((65536-AMP_OVER_2)<<16)|AMP_OVER_2,((65536-AMP_OVER_2)<<16)|(65536-AMP_OVER_2)};



unsigned int taus(void);


void pmch_procedures(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,PHY_VARS_RN *rn,relaying_type_t r_type) {


#if defined(Rel10) || defined(Rel14)
  MCH_PDU *mch_pduP;
  MCH_PDU  mch_pdu;
  //  uint8_t sync_area=255;
#endif

  int subframe = proc->subframe_tx;

  // This is DL-Cell spec pilots in Control region
  generate_pilots_slot(eNB,
		       eNB->common_vars.txdataF,
		       AMP,
		       subframe<<1,1);

  
#if defined(Rel10) || defined(Rel14)
  // 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);
  */
  switch (r_type) {
  case no_relay:
    if ((mch_pduP->Pdu_size > 0) && (mch_pduP->sync_area == 0)) // TEST: only transmit mcch for sync area 0
      LOG_I(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;
    }
    
    break;
    
  case multicast_relay:
    if ((mch_pduP->Pdu_size > 0) && ((mch_pduP->mcch_active == 1) || mch_pduP->msi_active==1)) {
      LOG_I(PHY,"[RN %"PRIu8"] Frame %d subframe %d: Got the MCH PDU for MBSFN  sync area %"PRIu8" (MCS %"PRIu8", TBS %"PRIu16")\n",
	    rn->Mod_id,rn->frame, subframe,
	    mch_pduP->sync_area,mch_pduP->mcs,mch_pduP->Pdu_size);
    } else if (rn->mch_avtive[subframe%5] == 1) { // SF2 -> SF7, SF3 -> SF8
      mch_pduP= &mch_pdu;
      memcpy(&mch_pduP->payload, // could be a simple copy
	     rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->b,
	     rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->TBS>>3);
      mch_pduP->Pdu_size = (uint16_t) (rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->TBS>>3);
      mch_pduP->mcs = rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->mcs;
      LOG_I(PHY,"[RN %"PRIu8"] Frame %d subframe %d: Forward the MCH PDU for MBSFN received on SF %d sync area %"PRIu8" (MCS %"PRIu8", TBS %"PRIu16")\n",
	    rn->Mod_id,rn->frame, subframe,subframe%5,
	    rn->sync_area[subframe%5],mch_pduP->mcs,mch_pduP->Pdu_size);
    } else {
      mch_pduP=NULL;
    }
    
    rn->mch_avtive[subframe]=0;
    break;
    
  default:
    LOG_W(PHY,"[eNB %"PRIu8"] Frame %d subframe %d: unknown relaying type %d \n",
	  eNB->Mod_id,proc->frame_tx,subframe,r_type);
    mch_pduP=NULL;
    break;
  }// switch
  
  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,eNB_rxtx_proc_t *proc) {

  LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms;
  int **txdataF = eNB->common_vars.txdataF;
  uint8_t *pbch_pdu=&eNB->pbch_pdu[0];
  int subframe = proc->subframe_tx;
  int frame = proc->frame_tx;

  LOG_D(PHY,"common_signal_procedures: frame %d, subframe %d\n",frame,subframe); 

  // generate Cell-Specific Reference Signals for both slots
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_RS_TX,1);
  generate_pilots_slot(eNB,
		       txdataF,
		       AMP,
		       subframe<<1,0);
  // 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");
      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];
  int input_buffer_length = dlsch_harq->TBS/8;
  LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms;

  if (frame < 20) {

    LOG_I(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, input_buffer_length, 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);
  }    
#if defined(MESSAGE_CHART_GENERATOR_PHY)
  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,
		     input_buffer_length,
		     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);
#endif
  
  
  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 %d\n",ra_flag);
  // 36-212 
  start_meas(&eNB->dlsch_encoding_stats);
  AssertFatal(dlsch_harq->pdu!=NULL,"dlsch_harq->pdu == NULL (rnti %x)\n",dlsch->rnti);
  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_interleaving_stats);
  stop_meas(&eNB->dlsch_encoding_stats);
  // 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,
		   subframe,
		   dlsch_harq->pdsch_start,
		   dlsch,
		   dlsch1);
  
  stop_meas(&eNB->dlsch_modulation_stats);

  dlsch->active = 0;
}


void handle_nfapi_dci_dl_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,nfapi_dl_config_request_pdu_t *dl_config_pdu);
void handle_nfapi_dci_dl_pdu(PHY_VARS_eNB *eNB,  
			     eNB_rxtx_proc_t *proc,
			     nfapi_dl_config_request_pdu_t *dl_config_pdu) {

  int idx                         = proc->subframe_tx&1;  
  LTE_eNB_PDCCH *pdcch_vars       = &eNB->pdcch_vars[idx];
  nfapi_dl_config_dci_dl_pdu *pdu = &dl_config_pdu->dci_dl_pdu;

  LOG_D(PHY,"Frame %d, Subframe %d: DCI processing\n",proc->frame_tx,proc->subframe_tx);

  // copy dci configuration into eNB structure
  fill_dci_and_dlsch(eNB,proc,&pdcch_vars->dci_alloc[pdcch_vars->num_dci],pdu);
}

void handle_nfapi_mpdcch_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,nfapi_dl_config_request_pdu_t *dl_config_pdu);
void handle_nfapi_mpdcch_pdu(PHY_VARS_eNB *eNB,  
			     eNB_rxtx_proc_t *proc,
			     nfapi_dl_config_request_pdu_t *dl_config_pdu) {

  int idx                         = proc->subframe_tx&1;  
  LTE_eNB_MPDCCH *mpdcch_vars     = &eNB->mpdcch_vars[idx];
  nfapi_dl_config_mpdcch_pdu *pdu = &dl_config_pdu->mpdcch_pdu;

  LOG_D(PHY,"Frame %d, Subframe %d: MDCI processing\n",proc->frame_tx,proc->subframe_tx);

  // copy dci configuration into eNB structure
  fill_mdci_and_dlsch(eNB,proc,&mpdcch_vars->mdci_alloc[mpdcch_vars->num_dci],pdu);
}

void handle_nfapi_hi_dci0_dci_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,
				  nfapi_hi_dci0_request_pdu_t *hi_dci0_config_pdu);

void handle_nfapi_hi_dci0_dci_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,  
				  nfapi_hi_dci0_request_pdu_t *hi_dci0_config_pdu){
  
  int idx                         = proc->subframe_tx&1;
  LTE_eNB_PDCCH *pdcch_vars       = &eNB->pdcch_vars[idx];
  // copy dci configuration in to eNB structure
  fill_dci0(eNB,proc,&pdcch_vars->dci_alloc[pdcch_vars->num_dci], &hi_dci0_config_pdu->dci_pdu);
}

void handle_nfapi_hi_dci0_hi_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,
				 nfapi_hi_dci0_request_pdu_t *hi_dci0_config_pdu);


void handle_nfapi_hi_dci0_hi_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,  
				 nfapi_hi_dci0_request_pdu_t *hi_dci0_config_pdu) {
  
  nfapi_hi_dci0_hi_pdu *pdu      = &hi_dci0_config_pdu->hi_pdu;
  LTE_eNB_PHICH *phich           = &eNB->phich_vars[proc->subframe_tx&1];

  // copy dci configuration in to eNB structure
  LOG_D(PHY,"Received HI PDU which value %d (rbstart %d,cshift %d)\n",
	hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.hi_value,
	hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.resource_block_start,
	hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.cyclic_shift_2_for_drms);

  phich->config[phich->num_hi].hi       = hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.hi_value;
  phich->config[phich->num_hi].first_rb = hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.resource_block_start;
  phich->config[phich->num_hi].n_DMRS   = hi_dci0_config_pdu->hi_pdu.hi_pdu_rel8.cyclic_shift_2_for_drms;
  phich->num_hi++;
  AssertFatal(phich->num_hi<32,"Maximum number of phich reached in subframe\n");
}

void handle_nfapi_bch_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,  
			  nfapi_dl_config_request_pdu_t *dl_config_pdu,
			  uint8_t *sdu) {
 
  nfapi_dl_config_bch_pdu_rel8_t *rel8 = &dl_config_pdu->bch_pdu.bch_pdu_rel8;
  
  AssertFatal(rel8->length == 3, "BCH PDU has length %d != 3\n",rel8->length);

  LOG_D(PHY,"bch_pdu: %x,%x,%x\n",sdu[0],sdu[1],sdu[2]);
  eNB->pbch_pdu[0] = sdu[2];
  eNB->pbch_pdu[1] = sdu[1];
  eNB->pbch_pdu[2] = sdu[0];

  // adjust transmit amplitude here based on NFAPI info

}

#ifdef Rel14
extern uint32_t localRIV2alloc_LUT6[32];
extern uint32_t localRIV2alloc_LUT25[512];
extern uint32_t localRIV2alloc_LUT50_0[1600];
extern uint32_t localRIV2alloc_LUT50_1[1600];
extern uint32_t localRIV2alloc_LUT100_0[6000];
extern uint32_t localRIV2alloc_LUT100_1[6000];
extern uint32_t localRIV2alloc_LUT100_2[6000];
extern uint32_t localRIV2alloc_LUT100_3[6000];
#endif

void handle_nfapi_dlsch_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,  
			    nfapi_dl_config_request_pdu_t *dl_config_pdu,
			    uint8_t codeword_index,
			    uint8_t *sdu) {

  nfapi_dl_config_dlsch_pdu_rel8_t *rel8 = &dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8;
#ifndef Rel8
  nfapi_dl_config_dlsch_pdu_rel10_t *rel10 = &dl_config_pdu->dlsch_pdu.dlsch_pdu_rel10;
#endif
#ifdef Rel14
  nfapi_dl_config_dlsch_pdu_rel13_t *rel13 = &dl_config_pdu->dlsch_pdu.dlsch_pdu_rel13;
#endif
  LTE_eNB_DLSCH_t *dlsch0=NULL,*dlsch1=NULL;
  LTE_DL_eNB_HARQ_t *dlsch0_harq=NULL,*dlsch1_harq=NULL;
  int UE_id;
  int harq_pid;
   
  
  UE_id = find_dlsch(rel8->rnti,eNB,SEARCH_EXIST_OR_FREE);
  AssertFatal(UE_id!=-1,"no free or exiting 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];

#ifdef Rel14
  if ((rel13->pdsch_payload_type < 2) && (rel13->ue_type>0)) dlsch0->harq_ids[proc->subframe_tx] = 0;
#endif

  harq_pid        = dlsch0->harq_ids[proc->subframe_tx];
  AssertFatal((harq_pid>=0) && (harq_pid<8),"harq_pid %d not in 0...7\n",harq_pid);
  dlsch0_harq     = dlsch0->harq_processes[harq_pid];
  dlsch1_harq     = dlsch1->harq_processes[harq_pid];
  AssertFatal(dlsch0_harq!=NULL,"dlsch_harq is null\n");

  
  dlsch0_harq->pdsch_start = eNB->pdcch_vars[proc->subframe_tx & 1].num_pdcch_symbols;

  if (dlsch0_harq->round==0) {  //get pointer to SDU if this a new SDU
    LOG_I(PHY,"NFAPI: frame %d, subframe %d: programming dlsch, rnti %x, UE_id %d, harq_pid %d\n",
	  proc->frame_tx,proc->subframe_tx,rel8->rnti,UE_id,harq_pid);
    if (codeword_index == 0) dlsch0_harq->pdu                    = sdu;
    else                     dlsch1_harq->pdu                    = sdu;
  }

#ifdef Rel14
  dlsch0->sib1_br_flag=0;

  if ((rel13->pdsch_payload_type <2) && (rel13->ue_type>0)) { // this is a BR/CE UE and SIB1-BR/SI-BR
    dlsch0->rnti             = 0xFFFF;
    dlsch0->Kmimo            = 1;
    dlsch0->Mdlharq          = 4; 
    dlsch0->Nsoft            = 25344;
    dlsch0->i0               = rel13->initial_transmission_sf_io;
    dlsch0_harq->pdsch_start = rel10->pdsch_start;

    if (rel13->pdsch_payload_type == 0) dlsch0->sib1_br_flag=1;

    // configure PDSCH
    switch (eNB->frame_parms.N_RB_DL) {
    case 6:
      dlsch0_harq->rb_alloc[0]      = localRIV2alloc_LUT6[rel8->resource_block_coding];
      break;
    case 15:
      AssertFatal(1==0,"15 PRBs not supported for now\n");
      break;
    case 25:
      dlsch0_harq->rb_alloc[0]      = localRIV2alloc_LUT25[rel8->resource_block_coding];
      break;
    case 50:  
      dlsch0_harq->rb_alloc[0]      = localRIV2alloc_LUT50_0[rel8->resource_block_coding];
      dlsch0_harq->rb_alloc[1]      = localRIV2alloc_LUT50_1[rel8->resource_block_coding];
      break;
    case 75:
      AssertFatal(1==0,"75 PRBs not supported for now\n");
      break;
    case 100:
      dlsch0_harq->rb_alloc[0]      = localRIV2alloc_LUT100_0[rel8->resource_block_coding];
      dlsch0_harq->rb_alloc[1]      = localRIV2alloc_LUT100_1[rel8->resource_block_coding];
      dlsch0_harq->rb_alloc[2]      = localRIV2alloc_LUT100_2[rel8->resource_block_coding];
      dlsch0_harq->rb_alloc[3]      = localRIV2alloc_LUT100_3[rel8->resource_block_coding];
    }

    dlsch0->active                  = 1;
  
    dlsch0_harq->nb_rb              = 6; 
    dlsch0_harq->vrb_type           = LOCALIZED;
    dlsch0_harq->rvidx              = rel8->redundancy_version;
    dlsch0_harq->Nl                 = 1;
    dlsch0_harq->mimo_mode          = (eNB->frame_parms.nb_antenna_ports_eNB == 1) ? SISO : ALAMOUTI;
    dlsch0_harq->dl_power_off       = 1;
    dlsch0_harq->round              = 0;
    dlsch0_harq->status             = ACTIVE;
    dlsch0_harq->TBS                = rel8->length<<3;
    dlsch0_harq->Qm                 = rel8->modulation;
    dlsch0_harq->codeword           = 0;
  }
  else {
    dlsch0->i0               = 0xFFFF;
  }
#endif
}

uint16_t to_beta_offset_harqack[16]={16,20,25,32,40,50,64,80,101,127,160,248,400,640,1008,8};

void handle_ulsch_harq_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe) {

  nfapi_ul_config_ulsch_pdu_rel8_t *rel8 = &ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8;

  LTE_eNB_ULSCH_t *ulsch=eNB->ulsch[UE_id];
  LTE_UL_eNB_HARQ_t *ulsch_harq;
  nfapi_ul_config_ulsch_harq_information *harq_information = &ul_config_pdu->ulsch_harq_pdu.harq_information;

  int harq_pid = rel8->harq_process_number;
  ulsch_harq = ulsch->harq_processes[harq_pid];
  ulsch_harq->frame                      = frame;
  ulsch_harq->subframe                   = subframe;
  ulsch_harq->O_ACK                      = harq_information->harq_information_rel10.harq_size;
  ulsch->beta_offset_harqack_times8      = to_beta_offset_harqack[harq_information->harq_information_rel10.delta_offset_harq];
}

uint16_t to_beta_offset_ri[16]={9,13,16,20,25,32,40,50,64,80,101,127,160,0,0,0};
uint16_t to_beta_offset_cqi[16]={0,0,9,10,11,13,14,16,18,20,23,25,28,32,40,50};

void handle_ulsch_cqi_ri_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe) {

  nfapi_ul_config_cqi_ri_information_rel9_t *rel9 = &ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9;

  LTE_eNB_ULSCH_t *ulsch        = eNB->ulsch[UE_id];
  int harq_pid = ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.harq_process_number;
  LTE_UL_eNB_HARQ_t *ulsch_harq = ulsch->harq_processes[harq_pid];

  ulsch_harq->frame                       = frame;
  ulsch_harq->subframe                    = subframe;  
  ulsch_harq->O_RI                        = rel9->aperiodic_cqi_pmi_ri_report.cc[0].ri_size;
  ulsch_harq->Or1                         = rel9->aperiodic_cqi_pmi_ri_report.cc[0].dl_cqi_pmi_size[0];
  if (ulsch_harq->O_RI>1) ulsch_harq->Or2 = rel9->aperiodic_cqi_pmi_ri_report.cc[0].dl_cqi_pmi_size[1];
  ulsch->beta_offset_ri_times8            = to_beta_offset_ri[rel9->delta_offset_ri];
  ulsch->beta_offset_cqi_times8           = to_beta_offset_cqi[rel9->delta_offset_cqi];
}

void handle_ulsch_cqi_harq_ri_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe) {

  nfapi_ul_config_cqi_ri_information_rel9_t *rel9 = &ul_config_pdu->ulsch_cqi_harq_ri_pdu.cqi_ri_information.cqi_ri_information_rel9;

  LTE_eNB_ULSCH_t *ulsch        = eNB->ulsch[UE_id];
  int harq_pid = ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.harq_process_number;
  LTE_UL_eNB_HARQ_t *ulsch_harq = ulsch->harq_processes[harq_pid];
  nfapi_ul_config_ulsch_harq_information *harq_information = &ul_config_pdu->ulsch_cqi_harq_ri_pdu.harq_information;

  ulsch_harq->frame                       = frame;
  ulsch_harq->subframe                    = subframe;  
  ulsch_harq->O_RI                        = rel9->aperiodic_cqi_pmi_ri_report.cc[0].ri_size;
  ulsch_harq->Or1                         = rel9->aperiodic_cqi_pmi_ri_report.cc[0].dl_cqi_pmi_size[0];
  ulsch_harq->O_ACK                       = harq_information->harq_information_rel10.harq_size;

  if (ulsch_harq->O_RI>1) ulsch_harq->Or2 = rel9->aperiodic_cqi_pmi_ri_report.cc[0].dl_cqi_pmi_size[1];

  ulsch->beta_offset_harqack_times8       = to_beta_offset_harqack[harq_information->harq_information_rel10.delta_offset_harq];
  ulsch->beta_offset_ri_times8            = to_beta_offset_ri[rel9->delta_offset_ri];
  ulsch->beta_offset_cqi_times8           = to_beta_offset_cqi[rel9->delta_offset_cqi];

}

void handle_uci_harq_information(PHY_VARS_eNB *eNB, LTE_eNB_UCI *uci,nfapi_ul_config_harq_information *harq_information) {


  if (eNB->frame_parms.frame_type == FDD) {
    uci->num_pucch_resources = harq_information->harq_information_rel9_fdd.number_of_pucch_resources; 

    LOG_I(PHY,"Programming UCI HARQ mode %d : size %d in (%d,%d)\n",
	  harq_information->harq_information_rel9_fdd.ack_nack_mode,
	  harq_information->harq_information_rel9_fdd.harq_size,
	  uci->frame,uci->subframe);

    if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 0) &&
	(harq_information->harq_information_rel9_fdd.harq_size == 1)) {
      uci->pucch_fmt =  pucch_format1a;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 0) &&
	     (harq_information->harq_information_rel9_fdd.harq_size == 2)) {
      uci->pucch_fmt =  pucch_format1b;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 1) &&
	     (harq_information->harq_information_rel9_fdd.harq_size == 2)) {
      uci->pucch_fmt =  pucch_format1b_csA2;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
      uci->n_pucch_1[1][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_1;
      uci->n_pucch_1[1][1] = harq_information->harq_information_rel11.n_pucch_2_1;
    }
    else if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 1) &&
	     (harq_information->harq_information_rel9_fdd.harq_size == 3)) {
      uci->pucch_fmt =  pucch_format1b_csA3;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
      uci->n_pucch_1[1][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_1;
      uci->n_pucch_1[1][1] = harq_information->harq_information_rel11.n_pucch_2_1;
      uci->n_pucch_1[2][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_2;
      uci->n_pucch_1[2][1] = harq_information->harq_information_rel11.n_pucch_2_2;
    }
    else if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 1) &&
	     (harq_information->harq_information_rel9_fdd.harq_size == 4)) {
      uci->pucch_fmt =  pucch_format1b_csA4;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
      uci->n_pucch_1[1][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_1;
      uci->n_pucch_1[1][1] = harq_information->harq_information_rel11.n_pucch_2_1;
      uci->n_pucch_1[2][0] = harq_information->harq_information_rel9_fdd.n_pucch_1_2;
      uci->n_pucch_1[2][1] = harq_information->harq_information_rel11.n_pucch_2_2;
    }
    else if (harq_information->harq_information_rel9_fdd.ack_nack_mode == 2) {
      uci->pucch_fmt =  pucch_format3;
      uci->n_pucch_3[0] = harq_information->harq_information_rel9_fdd.n_pucch_1_0;
      uci->n_pucch_3[1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else AssertFatal(1==0,"unsupported HARQ mode %d\n",harq_information->harq_information_rel9_fdd.ack_nack_mode);
  }
  else { // TDD
    uci->num_pucch_resources = harq_information->harq_information_rel10_tdd.number_of_pucch_resources; 

    if (harq_information->harq_information_rel10_tdd.ack_nack_mode == 0) {//bundling

      uci->pucch_fmt =  harq_information->harq_information_rel10_tdd.harq_size==1 ? pucch_format1a : pucch_format1b;
      uci->tdd_bundling = 1;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else if ((harq_information->harq_information_rel10_tdd.ack_nack_mode == 1) && //multiplexing 
	     (uci->num_pucch_resources == 1)) {
      uci->pucch_fmt = harq_information->harq_information_rel10_tdd.harq_size==1 ? pucch_format1a : pucch_format1b;
      uci->tdd_bundling = 0;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else if ((harq_information->harq_information_rel10_tdd.ack_nack_mode == 1) && //multiplexing M>1
	     (uci->num_pucch_resources > 1)) {
      uci->pucch_fmt = pucch_format1b;
      uci->tdd_bundling = 0;
      uci->n_pucch_1[0][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_0;
      uci->n_pucch_1[0][1] = harq_information->harq_information_rel11.n_pucch_2_0;
      uci->n_pucch_1[1][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_1;
      uci->n_pucch_1[1][1] = harq_information->harq_information_rel11.n_pucch_2_1;
      uci->n_pucch_1[2][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_2;
      uci->n_pucch_1[2][1] = harq_information->harq_information_rel11.n_pucch_2_2;
      uci->n_pucch_1[3][0] = harq_information->harq_information_rel10_tdd.n_pucch_1_3;
      uci->n_pucch_1[3][1] = harq_information->harq_information_rel11.n_pucch_2_3;
    }
    else if (harq_information->harq_information_rel10_tdd.ack_nack_mode == 2) {
      uci->pucch_fmt =  pucch_format3;
      uci->n_pucch_3[0] = harq_information->harq_information_rel10_tdd.n_pucch_1_0;
      uci->n_pucch_3[1] = harq_information->harq_information_rel11.n_pucch_2_0;
    }
    else AssertFatal(1==0,"unsupported HARQ mode %d\n",harq_information->harq_information_rel10_tdd.ack_nack_mode);
  }
}

void handle_uci_sr_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe,uint8_t srs_active) {
  LTE_eNB_UCI *uci = &eNB->uci_vars[UE_id];

  uci->frame               = frame;
  uci->subframe            = subframe;
  uci->rnti                = ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti;
  uci->type                = SR;
  uci->pucch_fmt           = pucch_format1;
  uci->num_antenna_ports   = 1;
  uci->num_pucch_resources = 1;
  uci->n_pucch_1_0_sr[0]   = ul_config_pdu->uci_sr_pdu.sr_information.sr_information_rel8.pucch_index;
  uci->srs_active          = srs_active;
  uci->active              = 1;

  LOG_I(PHY,"Programming UCI SR rnti %x, pucch1_0 %d for (%d,%d)\n",
	uci->rnti,uci->n_pucch_1_0_sr[0],frame,subframe);
  

}

void handle_uci_sr_harq_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe,uint8_t srs_active) {

  LTE_eNB_UCI *uci = &eNB->uci_vars[UE_id];

  uci->frame               = frame;
  uci->subframe            = subframe;
  uci->rnti                = ul_config_pdu->uci_sr_harq_pdu.ue_information.ue_information_rel8.rnti;
  uci->type                = HARQ_SR;
  uci->num_antenna_ports   = 1;
  uci->num_pucch_resources = 1;
  uci->n_pucch_1_0_sr[0]    = ul_config_pdu->uci_sr_harq_pdu.sr_information.sr_information_rel8.pucch_index;
  uci->srs_active          = srs_active;
  uci->active              = 1;


  handle_uci_harq_information(eNB,uci,&ul_config_pdu->uci_sr_harq_pdu.harq_information);
}

void handle_uci_harq_pdu(PHY_VARS_eNB *eNB,int UE_id,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe,uint8_t srs_active) {

  LTE_eNB_UCI *uci = &eNB->uci_vars[UE_id];

  uci->frame             = frame;
  uci->subframe          = subframe;
  uci->rnti              = ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti;
  uci->type              = HARQ;
  uci->srs_active        = srs_active;
  uci->num_antenna_ports = ul_config_pdu->uci_harq_pdu.harq_information.harq_information_rel11.num_ant_ports;

  handle_uci_harq_information(eNB,uci,&ul_config_pdu->uci_harq_pdu.harq_information);

  uci->active=1;
  
}


void handle_srs_pdu(PHY_VARS_eNB *eNB,nfapi_ul_config_request_pdu_t *ul_config_pdu,uint16_t frame,uint8_t subframe) {

  int i;

  for (i=0;i<NUMBER_OF_UE_MAX;i++) {
    
    if (eNB->soundingrs_ul_config_dedicated[i].active==1) continue;
    
    eNB->soundingrs_ul_config_dedicated[i].active               = 1;
    eNB->soundingrs_ul_config_dedicated[i].frame                = frame;
    eNB->soundingrs_ul_config_dedicated[i].subframe             = subframe;
    eNB->soundingrs_ul_config_dedicated[i].rnti                 = ul_config_pdu->srs_pdu.srs_pdu_rel8.rnti;
    eNB->soundingrs_ul_config_dedicated[i].srs_Bandwidth        = ul_config_pdu->srs_pdu.srs_pdu_rel8.srs_bandwidth;
    eNB->soundingrs_ul_config_dedicated[i].srs_HoppingBandwidth = ul_config_pdu->srs_pdu.srs_pdu_rel8.srs_hopping_bandwidth;
    eNB->soundingrs_ul_config_dedicated[i].freqDomainPosition   = ul_config_pdu->srs_pdu.srs_pdu_rel8.frequency_domain_position;
    eNB->soundingrs_ul_config_dedicated[i].transmissionComb     = ul_config_pdu->srs_pdu.srs_pdu_rel8.transmission_comb;
    eNB->soundingrs_ul_config_dedicated[i].srs_ConfigIndex      = ul_config_pdu->srs_pdu.srs_pdu_rel8.i_srs;
    eNB->soundingrs_ul_config_dedicated[i].cyclicShift          = ul_config_pdu->srs_pdu.srs_pdu_rel8.sounding_reference_cyclic_shift;
    break; 
  }
  AssertFatal(i<NUMBER_OF_UE_MAX,"No room for SRS processing\n");
}

void handle_nfapi_ul_pdu(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,
			 nfapi_ul_config_request_pdu_t *ul_config_pdu,
			 uint16_t frame,uint8_t subframe,uint8_t srs_present) {

  nfapi_ul_config_ulsch_pdu_rel8_t *rel8 = &ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8;

  int8_t UE_id;

  // check if we have received a dci for this ue and ulsch descriptor is configured

  if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_PDU_TYPE) {
    AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.rnti,eNB,SEARCH_EXIST))>=0,
		"No existing UE ULSCH for rnti %x\n",rel8->rnti);
    AssertFatal(eNB->ulsch[UE_id]->harq_mask > 0,
		"ulsch for UE_id %d is not active\n",UE_id);
    LOG_I(PHY,"Applying UL config for UE %d, rnti %x for frame %d, subframe %d\n",
	  UE_id,rel8->rnti,frame,subframe);

    fill_ulsch(eNB,&ul_config_pdu->ulsch_pdu,frame,subframe);

  }
 
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE) {
    AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
    AssertFatal(eNB->ulsch[UE_id]->harq_mask > 0,
		"ulsch for UE_id %d is not active\n",UE_id);

    fill_ulsch(eNB,&ul_config_pdu->ulsch_harq_pdu.ulsch_pdu,frame,subframe);
    handle_ulsch_harq_pdu(eNB,UE_id,ul_config_pdu,frame,subframe);

  } 
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE) {
    AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,
				    eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
    AssertFatal(eNB->ulsch[UE_id]->harq_mask > 0,
		"ulsch for UE_id %d is not active\n",UE_id);
    fill_ulsch(eNB,&ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu,frame,subframe);
    handle_ulsch_cqi_ri_pdu(eNB,UE_id,ul_config_pdu,frame,subframe);

  } 
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE) {
    AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,
				    eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
    AssertFatal(eNB->ulsch[UE_id]->harq_mask > 0,
		"ulsch for UE_id %d is not active\n",UE_id);
    fill_ulsch(eNB,&ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu,frame,subframe);
    handle_ulsch_cqi_harq_ri_pdu(eNB,UE_id,ul_config_pdu,frame,subframe);

  } 
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE) {
    AssertFatal((UE_id = find_uci(ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti,
				  proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE UCI for rnti %x\n",ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti);
    handle_uci_harq_pdu(eNB,UE_id,ul_config_pdu,frame,subframe,srs_present);
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE) {
    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE not handled yet\n");
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE) {
    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE not handled yet\n");
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE) {
    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE not handled yet\n");
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE) {
    AssertFatal((UE_id = find_uci(ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti,
				  proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE UCI for rnti %x\n",ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti);
    handle_uci_sr_pdu(eNB,UE_id,ul_config_pdu,frame,subframe,srs_present);
    
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE) {
    AssertFatal((UE_id = find_uci(rel8->rnti,proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
		"No available UE UCI for rnti %x\n",ul_config_pdu->uci_sr_harq_pdu.ue_information.ue_information_rel8.rnti);
    handle_uci_sr_harq_pdu(eNB,UE_id,ul_config_pdu,frame,subframe,srs_present);
  }
  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_SRS_PDU_TYPE) {
    handle_srs_pdu(eNB,ul_config_pdu,frame,subframe);
  }
}

void schedule_response(Sched_Rsp_t *Sched_INFO) {

  PHY_VARS_eNB *eNB;
  eNB_rxtx_proc_t *proc;
  // copy data from L2 interface into L1 structures
  module_id_t               Mod_id       = Sched_INFO->module_id;
  uint8_t                   CC_id        = Sched_INFO->CC_id;
  nfapi_dl_config_request_t *DL_req      = Sched_INFO->DL_req;
  nfapi_hi_dci0_request_t   *HI_DCI0_req = Sched_INFO->HI_DCI0_req;
  nfapi_ul_config_request_t *UL_req      = Sched_INFO->UL_req;
  nfapi_tx_request_t        *TX_req      = Sched_INFO->TX_req;
  frame_t                   frame        = Sched_INFO->frame;
  sub_frame_t               subframe     = Sched_INFO->subframe;
  LTE_DL_FRAME_PARMS        *fp;
  int                       ul_subframe;
  int                       ul_frame;
  int                       harq_pid;
  LTE_UL_eNB_HARQ_t         *ulsch_harq;

  AssertFatal(RC.eNB!=NULL,"RC.eNB is null\n");
  AssertFatal(RC.eNB[Mod_id]!=NULL,"RC.eNB[%d] is null\n",Mod_id);
  AssertFatal(RC.eNB[Mod_id][CC_id]!=NULL,"RC.eNB[%d][%d] is null\n",Mod_id,CC_id);

  eNB         = RC.eNB[Mod_id][CC_id];
  fp          = &eNB->frame_parms;
  proc        = &eNB->proc.proc_rxtx[0];
  ul_subframe = pdcch_alloc2ul_subframe(fp,subframe);
  ul_frame    = pdcch_alloc2ul_frame(fp,frame,subframe);

  AssertFatal(proc->subframe_tx == subframe, "Current subframe %d != NFAPI subframe %d\n",proc->subframe_tx,subframe);
  AssertFatal(proc->subframe_tx == subframe, "Current frame %d != NFAPI frame %d\n",proc->frame_tx,frame);

  uint8_t number_dl_pdu             = DL_req->dl_config_request_body.number_pdu;
  uint8_t number_hi_dci0_pdu        = HI_DCI0_req->hi_dci0_request_body.number_of_dci+HI_DCI0_req->hi_dci0_request_body.number_of_hi;
  uint8_t number_ul_pdu             = UL_req->ul_config_request_body.number_of_pdus;


  nfapi_dl_config_request_pdu_t *dl_config_pdu;
  nfapi_hi_dci0_request_pdu_t   *hi_dci0_req_pdu;
  nfapi_ul_config_request_pdu_t *ul_config_pdu;

  int i;

  eNB->pdcch_vars[subframe&1].num_pdcch_symbols = DL_req->dl_config_request_body.number_pdcch_ofdm_symbols;
  eNB->pdcch_vars[subframe&1].num_dci           = 0;
  eNB->phich_vars[subframe&1].num_hi            = 0;

  LOG_I(PHY,"NFAPI: Frame %d, Subframe %d: received %d dl_pdu, %d tx_req, %d hi_dci0_config_req, %d UL_config \n",
	frame,subframe,number_dl_pdu,TX_req->tx_request_body.number_of_pdus,number_hi_dci0_pdu,number_ul_pdu);

  
  if ((subframe_select(fp,ul_subframe)==SF_UL) ||
      (fp->frame_type == FDD)) {
    harq_pid = subframe2harq_pid(fp,ul_frame,ul_subframe);

    // clear DCI allocation maps for new subframe
    
    for (i=0; i<NUMBER_OF_UE_MAX; i++) {
      if (eNB->ulsch[i]) {
	ulsch_harq = eNB->ulsch[i]->harq_processes[harq_pid];
        ulsch_harq->dci_alloc=0;
        ulsch_harq->rar_alloc=0;
      }
    }
  }
  for (i=0;i<number_dl_pdu;i++) {
    dl_config_pdu = &DL_req->dl_config_request_body.dl_config_pdu_list[i];
    LOG_I(PHY,"NFAPI: dl_pdu %d : type %d\n",i,dl_config_pdu->pdu_type);
    switch (dl_config_pdu->pdu_type) {
    case NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE:
      handle_nfapi_dci_dl_pdu(eNB,proc,dl_config_pdu);
      eNB->pdcch_vars[subframe&1].num_dci++;
      break;
    case NFAPI_DL_CONFIG_BCH_PDU_TYPE:
      AssertFatal(dl_config_pdu->bch_pdu.bch_pdu_rel8.pdu_index<TX_req->tx_request_body.number_of_pdus,
		  "bch_pdu_rel8.pdu_index>=TX_req->number_of_pdus (%d>%d)\n",
		  dl_config_pdu->bch_pdu.bch_pdu_rel8.pdu_index,
		  TX_req->tx_request_body.number_of_pdus);
      eNB->pbch_configured=1;
      handle_nfapi_bch_pdu(eNB,proc,dl_config_pdu,
			   TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->bch_pdu.bch_pdu_rel8.pdu_index].segments[0].segment_data);
      break;
    case NFAPI_DL_CONFIG_MCH_PDU_TYPE:
      //      handle_nfapi_mch_dl_pdu(eNB,dl_config_pdu);
      break;
    case NFAPI_DL_CONFIG_DLSCH_PDU_TYPE:
      AssertFatal(dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index<TX_req->tx_request_body.number_of_pdus,
		  "dlsch_pdu_rel8.pdu_index>=TX_req->number_of_pdus (%d>%d)\n",
		  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index,
		  TX_req->tx_request_body.number_of_pdus);
      AssertFatal((dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks<3) &&
		  (dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks>0),
		  "dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks = %d not in [1,2]\n",
		  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks);
      handle_nfapi_dlsch_pdu(eNB,proc,dl_config_pdu,
			     dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks-1,
			     TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index].segments[0].segment_data);
      if (dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.rnti == eNB->preamble_list[0].preamble_rel8.rnti) {// is RAR pdu
	LOG_I(PHY,"Frame %d, Subframe %d: Received LTE RAR pdu, programming based on UL Grant\n",frame,subframe); 
	generate_eNB_ulsch_params_from_rar(eNB,
					   TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index].segments[0].segment_data,
					   frame,
					   subframe);
					   
      }					   
      break;
    case NFAPI_DL_CONFIG_PCH_PDU_TYPE:
      //      handle_nfapi_pch_pdu(eNB,dl_config_pdu);
      break;
    case NFAPI_DL_CONFIG_PRS_PDU_TYPE:
      //      handle_nfapi_prs_pdu(eNB,dl_config_pdu);
      break;
    case NFAPI_DL_CONFIG_CSI_RS_PDU_TYPE:
      //      handle_nfapi_csi_rs_pdu(eNB,dl_config_pdu);
      break;
    case NFAPI_DL_CONFIG_EPDCCH_DL_PDU_TYPE:
      //      handle_nfapi_epdcch_pdu(eNB,dl_config_pdu);
      break;
    case NFAPI_DL_CONFIG_MPDCCH_PDU_TYPE:
      handle_nfapi_mpdcch_pdu(eNB,proc,dl_config_pdu);
      eNB->mpdcch_vars[subframe&1].num_dci++; 
      break;
    }
  }
  
  for (i=0;i<number_hi_dci0_pdu;i++) {

    hi_dci0_req_pdu = &HI_DCI0_req->hi_dci0_request_body.hi_dci0_pdu_list[i];

    LOG_I(PHY,"NFAPI: hi_dci0_pdu %d : type %d\n",i,hi_dci0_req_pdu->pdu_type);

    switch (hi_dci0_req_pdu->pdu_type) {


    case NFAPI_HI_DCI0_DCI_PDU_TYPE:
      handle_nfapi_hi_dci0_dci_pdu(eNB,proc,hi_dci0_req_pdu);
      eNB->pdcch_vars[subframe&1].num_dci++; 
      break;

    case NFAPI_HI_DCI0_HI_PDU_TYPE:
      handle_nfapi_hi_dci0_hi_pdu(eNB,proc,hi_dci0_req_pdu);

      break;
    }
  } 

  for (i=0;i<number_ul_pdu;i++) {
    ul_config_pdu = &UL_req->ul_config_request_body.ul_config_pdu_list[i];
    LOG_I(PHY,"NFAPI: ul_pdu %d : type %d\n",i,ul_config_pdu->pdu_type);
    AssertFatal(ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE ||
		ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE
		,
		"Optional UL_PDU type %d not supported\n",ul_config_pdu->pdu_type);
    handle_nfapi_ul_pdu(eNB,proc,ul_config_pdu,UL_req->sfn_sf>>4,UL_req->sfn_sf&0xf,UL_req->ul_config_request_body.srs_present);
  }
}



void phy_procedures_eNB_TX(PHY_VARS_eNB *eNB,
			   eNB_rxtx_proc_t *proc,
                           relaying_type_t r_type,
			   PHY_VARS_RN *rn,
			   int do_meas)
{
  UNUSED(rn);
  int frame=proc->frame_tx;
  int subframe=proc->subframe_tx;
  uint32_t i,aa;
  uint8_t harq_pid;
  int8_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;

  int offset = eNB->CC_id;//proc == &eNB->proc.proc_rxtx[0] ? 0 : 1;

  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+offset,1);
  if (do_meas==1) start_meas(&eNB->phy_proc_tx);

  T(T_ENB_PHY_DL_TICK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe));

  // 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 (is_pmch_subframe(frame,subframe,fp)) {
    pmch_procedures(eNB,proc,rn,r_type);
  }
  else {
    // this is not a pmch subframe, so generate PSS/SSS/PBCH
    common_signal_procedures(eNB,proc);
  }

  // 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 */
  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.
       */
      if ((subframe_select(fp,ul_subframe)==SF_UL) ||
          (fp->frame_type == FDD)) {
        ulsch_harq->previous_first_rb = ulsch_harq->first_rb;
        ulsch_harq->previous_n_DMRS   = ulsch_harq->n_DMRS;
      }
    }
  }


  //  num_pdcch_symbols = DCI_pdu->num_pdcch_symbols;
  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",(dci common %"PRIu8", dci uespec %"PRIu8"\n",num_pdcch_symbols,
  //        DCI_pdu->Num_common_dci,DCI_pdu->Num_ue_spec_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_I(PHY,"[eNB %"PRIu8"] Frame %d, subframe %d: Calling generate_dci_top (pdcch) (num_dci %"PRIu8")\n",eNB->Mod_id,frame, subframe,
	  num_dci);
    
  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);

  // 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)&&
	  (dlsch0->active == 1)) {

	// get harq_pid
	harq_pid = dlsch0->harq_ids[subframe];
	AssertFatal(harq_pid>=0,"harq_pid is negative\n");
	// generate pdsch
	pdsch_procedures(eNB,
			 proc,
			 harq_pid,
			 dlsch0,
			 dlsch1,
			 &eNB->UE_stats[(uint32_t)UE_id],
			 0);


      }

      else if ((dlsch0)&&
	       (dlsch0->rnti>0)&&
	       (dlsch0->active == 0)) {

	// 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;
      }
    }



  generate_phich_top(eNB,
		     proc,
		     AMP);

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX+offset,0);
  if (do_meas==1) stop_meas(&eNB->phy_proc_tx);
  
}


void process_HARQ_feedback(uint8_t UE_id,
			   PHY_VARS_eNB *eNB,
			   eNB_rxtx_proc_t *proc,
			   uint8_t pusch_flag,
			   uint8_t *pucch_payload,
			   uint8_t pucch_sel,
			   uint8_t SR_payload)
{

  LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms;
  uint8_t dl_harq_pid[8],dlsch_ACK[8],dl_subframe;
  LTE_eNB_DLSCH_t *dlsch             =  eNB->dlsch[(uint32_t)UE_id][0];
  LTE_eNB_UE_stats *ue_stats         =  &eNB->UE_stats[(uint32_t)UE_id];
  LTE_DL_eNB_HARQ_t *dlsch_harq_proc;
  uint8_t subframe_m4,M,m;
  int mp;
  int all_ACKed=1,nb_alloc=0,nb_ACK=0;
  int frame = proc->frame_rx;
  int subframe = proc->subframe_rx;
  int harq_pid = subframe2harq_pid( fp,frame,subframe);

  nfapi_harq_indication_pdu_t *pdu;

  if (fp->frame_type == FDD) { //FDD
    subframe_m4 = (subframe<4) ? subframe+6 : subframe-4;

    dl_harq_pid[0] = dlsch->harq_ids[subframe_m4];
    M=1;

    if (pusch_flag == 1) {
      dlsch_ACK[0] = eNB->ulsch[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[0];
      if (dlsch->subframe_tx[subframe_m4]==1)
	LOG_D(PHY,"[eNB %d] Frame %d: Received ACK/NAK %d on PUSCH for subframe %d\n",eNB->Mod_id,
	      frame,dlsch_ACK[0],subframe_m4);
    }
    else {
      dlsch_ACK[0] = pucch_payload[0];
      LOG_D(PHY,"[eNB %d] Frame %d: Received ACK/NAK %d on PUCCH for subframe %d\n",eNB->Mod_id,
	    frame,dlsch_ACK[0],subframe_m4);
      /*
	if (dlsch_ACK[0]==0)
	AssertFatal(0,"Exiting on NAK on PUCCH\n");
      */
    }

    // fill ACK/NAK Indication
    pdu = &eNB->UL_INFO.harq_ind.harq_pdu_list[eNB->UL_INFO.harq_ind.number_of_harqs];
    eNB->UL_INFO.harq_ind.number_of_harqs++;    

#if defined(MESSAGE_CHART_GENERATOR_PHY)
    MSC_LOG_RX_MESSAGE(
		       MSC_PHY_ENB,MSC_PHY_UE,
		       NULL,0,
		       "%05u:%02u %s received %s  rnti %x harq id %u  tx SF %u",
		       frame,subframe,
		       (pusch_flag == 1)?"PUSCH":"PUCCH",
		       (dlsch_ACK[0])?"ACK":"NACK",
		       dlsch->rnti,
		       dl_harq_pid[0],
		       subframe_m4
		       );
#endif
  } else { // TDD Handle M=1,2 cases only

    M=ul_ACK_subframe2_M(fp,
                         subframe);

    // Now derive ACK information for TDD
    if (pusch_flag == 1) { // Do PUSCH ACK/NAK first
      // detect missing DAI
      //FK: this code is just a guess
      //RK: not exactly, yes if scheduled from PHICH (i.e. no DCI format 0)
      //    otherwise, it depends on how many of the PDSCH in the set are scheduled, we can leave it like this,
      //    but we have to adapt the code below.  For example, if only one out of 2 are scheduled, only 1 bit o_ACK is used

      dlsch_ACK[0] = eNB->ulsch[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[0];
      dlsch_ACK[1] = (eNB->pucch_config_dedicated[UE_id].tdd_AckNackFeedbackMode == bundling)
	?eNB->ulsch[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[0]:eNB->ulsch[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[1];
    }


    else {  // PUCCH ACK/NAK
      if ((SR_payload == 1)&&(pucch_sel!=2)) {  // decode Table 7.3 if multiplexing and SR=1
        nb_ACK = 0;

        if (M == 2) {
          if ((pucch_payload[0] == 1) && (pucch_payload[1] == 1)) // b[0],b[1]
            nb_ACK = 1;
          else if ((pucch_payload[0] == 1) && (pucch_payload[1] == 0))
            nb_ACK = 2;
        } else if (M == 3) {
          if ((pucch_payload[0] == 1) && (pucch_payload[1] == 1))
            nb_ACK = 1;
          else if ((pucch_payload[0] == 1) && (pucch_payload[1] == 0))
            nb_ACK = 2;
          else if ((pucch_payload[0] == 0) && (pucch_payload[1] == 1))
            nb_ACK = 3;
        }
      } else if (pucch_sel == 2) { // bundling or M=1
        dlsch_ACK[0] = pucch_payload[0];
        dlsch_ACK[1] = pucch_payload[0];
      } else { // multiplexing with no SR, this is table 10.1
        if (M==1)
          dlsch_ACK[0] = pucch_payload[0];
        else if (M==2) {
          if (((pucch_sel == 1) && (pucch_payload[0] == 1) && (pucch_payload[1] == 1)) ||
              ((pucch_sel == 0) && (pucch_payload[0] == 0) && (pucch_payload[1] == 1)))
            dlsch_ACK[0] = 1;
          else
            dlsch_ACK[0] = 0;

          if (((pucch_sel == 1) && (pucch_payload[0] == 1) && (pucch_payload[1] == 1)) ||
              ((pucch_sel == 1) && (pucch_payload[0] == 0) && (pucch_payload[1] == 0)))
            dlsch_ACK[1] = 1;
          else
            dlsch_ACK[1] = 0;
        }
      }
    }
  }

  // handle case where positive SR was transmitted with multiplexing
  if ((SR_payload == 1)&&(pucch_sel!=2)&&(pusch_flag == 0)) {
    nb_alloc = 0;

    for (m=0; m<M; m++) {
      dl_subframe = ul_ACK_subframe2_dl_subframe(fp,
						 subframe,
						 m);

      if (dlsch->subframe_tx[dl_subframe]==1)
        nb_alloc++;
    }

    if (nb_alloc == nb_ACK)
      all_ACKed = 1;
    else
      all_ACKed = 0;
  }


  for (m=0,mp=-1; m<M; m++) {

    dl_subframe = ul_ACK_subframe2_dl_subframe(fp,
					       subframe,
					       m);
    if (dlsch->subframe_tx[dl_subframe]==1) {
      if (pusch_flag == 1)
        mp++;
      else
        mp = m;

      dl_harq_pid[m]     = dlsch->harq_ids[dl_subframe];
      harq_pid_updated[UE_id][dl_harq_pid[m]] = 1;

      if ((pucch_sel != 2)&&(pusch_flag == 0)) { // multiplexing
        if ((SR_payload == 1)&&(all_ACKed == 1))
          dlsch_ACK[m] = 1;
        else
          dlsch_ACK[m] = 0;
      }

      if (dl_harq_pid[m]<dlsch->Mdlharq) {
        dlsch_harq_proc = dlsch->harq_processes[dl_harq_pid[m]];
#ifdef DEBUG_PHY_PROC
        LOG_D(PHY,"[eNB %d][PDSCH %x/%d] subframe %d, status %d, round %d (rv %d, TBS %d)\n",eNB->Mod_id,
              dlsch->rnti,dl_harq_pid[m],dl_subframe,
              dlsch_harq_proc->status,dlsch_harq_proc->round,
              dlsch->harq_processes[dl_harq_pid[m]]->rvidx,
              dlsch->harq_processes[dl_harq_pid[m]]->TBS);

        if (dlsch_harq_proc->status==DISABLED)
          LOG_E(PHY,"dlsch_harq_proc is disabled? \n");

#endif

        if ((dl_harq_pid[m]<dlsch->Mdlharq) &&
            (dlsch_harq_proc->status == ACTIVE)) {
          // dl_harq_pid of DLSCH is still active

          if ( dlsch_ACK[mp]==0) {
            // Received NAK
#ifdef DEBUG_PHY_PROC
            LOG_D(PHY,"[eNB %d][PDSCH %x/%d] M = %d, m= %d, mp=%d NAK Received in round %d, requesting retransmission\n",eNB->Mod_id,
                  dlsch->rnti,dl_harq_pid[m],M,m,mp,dlsch_harq_proc->round);
#endif

            T(T_ENB_PHY_DLSCH_UE_NACK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id), T_INT(dlsch->rnti),
              T_INT(dl_harq_pid[m]));

            if (dlsch_harq_proc->round == 0)
              ue_stats->dlsch_NAK_round0++;

            ue_stats->dlsch_NAK[dl_harq_pid[m]][dlsch_harq_proc->round]++;


            // then Increment DLSCH round index
            dlsch_harq_proc->round++;


            if (dlsch_harq_proc->round == dlsch->Mlimit) {
              // This was the last round for DLSCH so reset round and increment l2_error counter
#ifdef DEBUG_PHY_PROC
              LOG_W(PHY,"[eNB %d][PDSCH %x/%d] DLSCH retransmissions exhausted, dropping packet\n",eNB->Mod_id,
                    dlsch->rnti,dl_harq_pid[m]);
#endif
#if defined(MESSAGE_CHART_GENERATOR_PHY)
              MSC_LOG_EVENT(MSC_PHY_ENB, "0 HARQ DLSCH Failed RNTI %"PRIx16" round %u",
                            dlsch->rnti,
                            dlsch_harq_proc->round);
#endif

              dlsch_harq_proc->round = 0;
              ue_stats->dlsch_l2_errors[dl_harq_pid[m]]++;
              dlsch_harq_proc->status = SCH_IDLE;
              dlsch->harq_ids[dl_subframe] = dlsch->Mdlharq;
            }
          } else {
#ifdef DEBUG_PHY_PROC
            LOG_D(PHY,"[eNB %d][PDSCH %x/%d] ACK Received in round %d, resetting process\n",eNB->Mod_id,
                  dlsch->rnti,dl_harq_pid[m],dlsch_harq_proc->round);
#endif

            T(T_ENB_PHY_DLSCH_UE_ACK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id), T_INT(dlsch->rnti),
              T_INT(dl_harq_pid[m]));

            ue_stats->dlsch_ACK[dl_harq_pid[m]][dlsch_harq_proc->round]++;

            // Received ACK so set round to 0 and set dlsch_harq_pid IDLE
            dlsch_harq_proc->round  = 0;
            dlsch_harq_proc->status = SCH_IDLE;
            dlsch->harq_ids[dl_subframe] = dlsch->Mdlharq;

            ue_stats->total_TBS = ue_stats->total_TBS +
	      eNB->dlsch[(uint8_t)UE_id][0]->harq_processes[dl_harq_pid[m]]->TBS;
            /*
              ue_stats->total_transmitted_bits = ue_stats->total_transmitted_bits +
              eNB->dlsch[(uint8_t)UE_id][0]->harq_processes[dl_harq_pid[m]]->TBS;
            */
          }
	 
          // Do fine-grain rate-adaptation for DLSCH
          if (ue_stats->dlsch_NAK_round0 > dlsch->error_threshold) {
            if (ue_stats->dlsch_mcs_offset == 1)
              ue_stats->dlsch_mcs_offset=0;
            else
              ue_stats->dlsch_mcs_offset=-1;
          }

#ifdef DEBUG_PHY_PROC
          LOG_D(PHY,"[process_HARQ_feedback] Frame %d Setting round to %d for pid %d (subframe %d)\n",frame,
                dlsch_harq_proc->round,dl_harq_pid[m],subframe);
#endif
	  harq_pid_round[UE_id][dl_harq_pid[m]] = dlsch_harq_proc->round;
          // Clear NAK stats and adjust mcs offset
          // after measurement window timer expires
          if (ue_stats->dlsch_sliding_cnt == dlsch->ra_window_size) {
            if ((ue_stats->dlsch_mcs_offset == 0) && (ue_stats->dlsch_NAK_round0 < 2))
              ue_stats->dlsch_mcs_offset = 1;

            if ((ue_stats->dlsch_mcs_offset == 1) && (ue_stats->dlsch_NAK_round0 > 2))
              ue_stats->dlsch_mcs_offset = 0;

            if ((ue_stats->dlsch_mcs_offset == 0) && (ue_stats->dlsch_NAK_round0 > 2))
              ue_stats->dlsch_mcs_offset = -1;

            if ((ue_stats->dlsch_mcs_offset == -1) && (ue_stats->dlsch_NAK_round0 < 2))
              ue_stats->dlsch_mcs_offset = 0;

            ue_stats->dlsch_NAK_round0 = 0;
            ue_stats->dlsch_sliding_cnt = 0;
          }
        }
      }
    }
  }
}


void prach_procedures(PHY_VARS_eNB *eNB,
#ifdef Rel14
		      int br_flag
#endif
		      ) {

  LTE_DL_FRAME_PARMS *fp=&eNB->frame_parms;
  uint16_t max_preamble[4],max_preamble_energy[4],max_preamble_delay[4];
  uint16_t i;
  int frame,subframe;
#ifdef Rel14
  if (br_flag==1) {
    subframe = eNB->proc.subframe_prach_br;
    frame = eNB->proc.frame_prach_br;
    pthread_mutex_lock(&eNB->UL_INFO_mutex);
    eNB->UL_INFO.rach_ind_br.number_of_preambles=0;
    pthread_mutex_unlock(&eNB->UL_INFO_mutex);
  }
  else
#endif
    {
      pthread_mutex_lock(&eNB->UL_INFO_mutex);
      eNB->UL_INFO.rach_ind.number_of_preambles=0;
      pthread_mutex_unlock(&eNB->UL_INFO_mutex);
      subframe = eNB->proc.subframe_prach;
      frame = eNB->proc.frame_prach;
    }
  uint8_t CC_id = eNB->CC_id;
  RU_t *ru;
  int aa=0;
  int ru_aa;
  LTE_eNB_PRACH *prach_vars;

 
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1);



  for (i=0;i<eNB->num_RU;i++) {
    ru=eNB->RU_list[i];
    for (ru_aa=0,aa=0;ru_aa<ru->nb_rx;ru_aa++,aa++) {
      eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[ru_aa];
#ifdef Rel14
      int ce_level;

      if (br_flag==1)
	for (ce_level=0;ce_level<4;ce_level++) eNB->prach_vars_br.rxsigF[ce_level][aa] = eNB->RU_list[i]->prach_rxsigF_br[ce_level][ru_aa];
#endif
    }
  }

  rx_prach(eNB,
	   eNB->RU_list[0],
	   &max_preamble[0],
	   &max_preamble_energy[0],
	   &max_preamble_delay[0],
	   frame,
	   0
#ifdef Rel14
	   ,br_flag
#endif
	   );

  //#ifdef DEBUG_PHY_PROC
  LOG_I(PHY,"[RAPROC] Frame %d, subframe %d : Most likely preamble %d, energy %d dB delay %d\n",
        frame,subframe,
	max_preamble[0],
        max_preamble_energy[0]/10,
        max_preamble_delay[0]);
  //q#endif

#ifdef Rel14
  if (br_flag==1) {

    prach_vars = &eNB->prach_vars_br;
    int prach_mask;
      
    prach_mask = is_prach_subframe(&eNB->frame_parms,eNB->proc.frame_prach_br,eNB->proc.subframe_prach_br);
    
    eNB->UL_INFO.rach_ind_br.preamble_list                              = eNB->preamble_list_br;
    int ind=0;
    int ce_level=0;
    /* Save for later, it doesn't work    
    for (int ind=0,ce_level=0;ce_level<4;ce_level++) {
      
      if ((eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[ce_level]==1)&&
	  (prach_mask&(1<<(1+ce_level)) > 0) && // prach is active and CE level has finished its repetitions
	  (eNB->prach_vars_br.repetition_number[ce_level]==
	   eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_numRepetitionPerPreambleAttempt[ce_level])) {
    */ 
    if (eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[0]==1){ 
      if (max_preamble_energy[0] > 350) {
	eNB->UL_INFO.rach_ind_br.number_of_preambles++;
	
	eNB->preamble_list_br[ind].preamble_rel8.timing_advance        = max_preamble_delay[ind];//
	eNB->preamble_list_br[ind].preamble_rel8.preamble              = max_preamble[ind];
	// note: fid is implicitly 0 here, this is the rule for eMTC RA-RNTI from 36.321, Section 5.1.4
	eNB->preamble_list_br[ind].preamble_rel8.rnti                  = 1+subframe+(eNB->prach_vars_br.first_frame[ce_level]%40);  
	eNB->preamble_list_br[ind].instance_length                     = 0; //don't know exactly what this is
	eNB->preamble_list_br[ind].preamble_rel13.rach_resource_type   = 1+ce_level;  // CE Level
	LOG_I(PHY,"Filling NFAPI indication for RACH %d CELevel %d (mask %x) : TA %d, Preamble %d, rnti %x, rach_resource_type %d\n",
	      ind,
	      ce_level,
	      prach_mask,
	      eNB->preamble_list_br[ind].preamble_rel8.timing_advance,
	      eNB->preamble_list_br[ind].preamble_rel8.preamble,
	      eNB->preamble_list_br[ind].preamble_rel8.rnti,
	      eNB->preamble_list_br[ind].preamble_rel13.rach_resource_type);
      }
      /*
	ind++;
      }
      } */// ce_level
    }
  }
  else
#endif

    {
      if (max_preamble_energy[0] > 350) {

	LOG_D(PHY,"[eNB %d/%d][RAPROC] Frame %d, subframe %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d\n",
	      eNB->Mod_id,
	      eNB->CC_id,
	      frame,
	      subframe,
	      max_preamble[0],
	      max_preamble_energy[0]/10,
	      max_preamble_energy[0]%10,
	      max_preamble_delay[0]);
	
	    T(T_ENB_PHY_INITIATE_RA_PROCEDURE, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), 0,
	      T_INT(max_preamble[0]), T_INT(max_preamble_energy[0]), T_INT(max_preamble_delay[0]));
	    
	    prach_vars = &eNB->prach_vars;
	    
	    
	    pthread_mutex_lock(&eNB->UL_INFO_mutex);
	    
	    eNB->UL_INFO.rach_ind.number_of_preambles                 = 1;
	    eNB->UL_INFO.rach_ind.preamble_list                       = eNB->preamble_list;
	    
	    eNB->preamble_list[0].preamble_rel8.timing_advance        = max_preamble_delay[0];
	    eNB->preamble_list[0].preamble_rel8.preamble              = max_preamble[0];
	    eNB->preamble_list[0].preamble_rel8.rnti                  = 1+subframe;  // note: fid is implicitly 0 here
	    eNB->preamble_list[0].preamble_rel13.rach_resource_type   = 0;
	    eNB->preamble_list[0].instance_length                     = 0; //don't know exactly what this is
	    
	    LOG_I(PHY,"Filling NFAPI indication for RACH : TA %d, Preamble %d, rnti %x, rach_resource_type %d\n",
		  eNB->preamble_list[0].preamble_rel8.timing_advance,
		  eNB->preamble_list[0].preamble_rel8.preamble,
		  eNB->preamble_list[0].preamble_rel8.rnti,
		  eNB->preamble_list[0].preamble_rel13.rach_resource_type);	    
	    pthread_mutex_unlock(&eNB->UL_INFO_mutex);
      
      } // max_preamble_energy > 350
    } // else br_flag
      /*
	mac_xface->initiate_ra_proc(eNB->Mod_id,
	eNB->CC_id,
	frame,
	preamble_max,
	preamble_delay_list[preamble_max]*update_TA/update_TA2,
	0,subframe,0);*/
      
    
    /*  } else {
    MSC_LOG_EVENT(MSC_PHY_ENB, "0 RA Failed add user, too many");
    LOG_I(PHY,"[eNB %d][RAPROC] frame %d, subframe %d: Unable to add user, max user count reached\n",
	  eNB->Mod_id,frame, subframe);
	  }*/

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0);
}

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) {

  pthread_mutex_lock(&eNB->UL_INFO_mutex);
  nfapi_sr_indication_pdu_t *pdu =   &eNB->UL_INFO.sr_ind.sr_pdu_list[eNB->UL_INFO.sr_ind.number_of_srs];

  pdu->instance_length                                = 0; // don't know what to do with this
  //  pdu->rx_ue_information.handle                       = handle;
  pdu->rx_ue_information.rnti                         = rnti;
  pthread_mutex_unlock(&eNB->UL_INFO_mutex);

  eNB->UL_INFO.sr_ind.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};
  uint8_t do_SR = 0;
  uint8_t pucch_sel = 0;
  int32_t metric[4]={0,0,0,0},metric_SR=0,max_metric;
  ANFBmode_t bundling_flag;
  PUCCH_FMT_t format;
  const int subframe = proc->subframe_rx;
  const int frame = proc->frame_rx;
  int i;
  LTE_eNB_UCI *uci;
  uint16_t tdd_multiplexing_mask=0;
  int res;

  for (i=0;i<NUMBER_OF_UE_MAX;i++) {

    uci = &eNB->uci_vars[i];
    if ((uci->active == 1) &&
	(uci->frame == frame) &&
	(uci->subframe == subframe)) {

      // 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;
      }

      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_I(PHY,"[eNB %d][SR %x] Frame %d subframe %d Checking SR is %d (SR n1pucch is %d)\n",
	      eNB->Mod_id,
	      uci->rnti,
	      frame,
	      subframe,
	      SR_payload,
	      uci->n_pucch_1_0_sr[0]);
	if (uci->type == SR) {
	  if (SR_payload == 1) {
	    fill_sr_indication(eNB,uci->rnti,frame,subframe);
	    return;
	  }
	  else {
	    return;
	  }
	}
      case HARQ:
	if (fp->frame_type == FDD) {
	  LOG_I(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);
 
	  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);
	  }
	  
#ifdef DEBUG_PHY_PROC
	  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],metric0);
#endif
	  
	  fill_uci_harq_indication(eNB,uci,frame,subframe,pucch_b0b1[0],0,0xffff);

	}
	else { // frame_type == TDD
	  

	  // 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);
	  	  
	    

	  }

	  
	  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[1][0] == 1 && pucch_b0b1[1][1] == 1) { // 1/4/7 ACKs
	      harq_ack[0] = 1;
	    }
	    else if (pucch_b0b1[1][0] == 1 && pucch_b0b1[1][1] != 1) { // 2/5/8 ACKs
	      harq_ack[0] = 2;
	    }
	    else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] == 1) { // 3/6/9 ACKs
	      harq_ack[0] = 3;
	    }
	    else if (pucch_b0b1[1][0] != 1 && pucch_b0b1[1][1] != 1) { // 0 ACKs, or at least one DL assignment missed
	      harq_ack[0] = 0;
	    }
	    fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,2,0xffff); // special_bundling mode
	  } 
	  else if ((bundling_flag == 0) && (res==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
		}
	      }
	    }
	    fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode
	  } //else if ((bundling_flag == 0) && (res==2))
	  else if ((bundling_flag == 0) && (res==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
	      
	    } 
	    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;
		}
	      }
	    }
	    fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode
	  } //else if ((bundling_flag == 0) && (res==3)) 
	  else if ((bundling_flag == 0) && (res==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
		
	    } 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;
		}
	      }
	    }
	    fill_uci_harq_indication(eNB,uci,frame,subframe,harq_ack,1,tdd_multiplexing_mask); // multiplexing mode
	  } // else if ((bundling_flag == 0) && (res==4))
	  else { // bundling
	    harq_ack[0] = pucch_b0b1[0][0];
	    harq_ack[1] = pucch_b0b1[0][1];
	    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, sel %d, (%d,%d)\n",eNB->Mod_id,
		eNB->dlsch[UE_id][0]->rnti,
		frame,subframe,
		metric0,metric1,pucch_sel,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,j,k;
  uint32_t harq_pid, harq_idx, round;
  uint8_t nPRS;
  int sync_pos;
  uint16_t rnti=0;
  uint8_t access_mode;
  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;
  int offset         = eNB->CC_id;//(proc == &eNB->proc.proc_rxtx[0]) ? 0 : 1;
  
  if (fp->frame_type == FDD) harq_pid = ((10*frame) + subframe)&7;
  else                       harq_pid = subframe%10;



  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_I(PHY,"Frame %d, subframe %d: PUSCH procedures, harq_pid %d, UE %d/%x\n",
			     frame,subframe,harq_pid,i,ulsch->rnti);
    
    if ((ulsch) &&
        (ulsch->rnti>0) &&
        (ulsch_harq->status == ACTIVE) &&
	(ulsch_harq->frame == frame) &&
	(ulsch_harq->subframe == subframe)) {
      
      
      // UE is has ULSCH scheduling
      round = ulsch_harq->round;
 
      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_I(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;

      LOG_I(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, nprs %d), O_ACK %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,
            nPRS,
            ulsch_harq->O_ACK);

      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_I(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\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]),
            20,//eNB->measurements.n0_power_dB[0],
            20,//eNB->measurements.n0_power_dB[1],
            ulsch_harq->o_ACK[0],
            ulsch_harq->o_ACK[1],
            ret);


      //compute the expected ULSCH RX power (for the stats)
      eNB->ulsch[(uint32_t)i]->harq_processes[harq_pid]->delta_TF =
        get_hundred_times_delta_IF_eNB(eNB,i,harq_pid, 0); // 0 means bw_factor is not considered


      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);
      
      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(i), T_INT(ulsch->rnti),
          T_INT(harq_pid));

        ulsch_harq->round++;

	fill_crc_indication(eNB,i,frame,subframe,1); // indicate NAK to MAC

        LOG_I(PHY,"[eNB][PUSCH %d] Increasing to round %d\n",harq_pid,ulsch_harq->round);

	LOG_I(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-1,
	      ulsch->Mlimit,
	      ulsch_harq->o_ACK[0],
	      ulsch_harq->o_ACK[1]);
	
#if defined(MESSAGE_CHART_GENERATOR_PHY)
	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
				     );
#endif

      }  // 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
        T(T_ENB_PHY_ULSCH_UE_ACK, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(i), T_INT(ulsch->rnti),
          T_INT(harq_pid));

#if defined(MESSAGE_CHART_GENERATOR_PHY)
        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
			   );
#endif

#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);

#ifdef DEBUG_PHY_PROC
      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]);
#endif
      
    } //     if ((ulsch) &&
      //         (ulsch->rnti>0) &&
      //         (ulsch_harq->status == ACTIVE))
  }   //   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,pthread_attr_t *attr_td) {

  eNB_proc_t *proc = &eNB->proc;

  proc->tdp.eNB = eNB;
  proc->instance_cnt_td         = -1;
    
  pthread_mutex_init( &proc->mutex_td, NULL);
  pthread_cond_init( &proc->cond_td, NULL);

  pthread_create(&proc->pthread_td, attr_td, td_thread, (void*)&proc->tdp);

}

extern void *te_thread(void*);

void init_te_thread(PHY_VARS_eNB *eNB,pthread_attr_t *attr_te) {

  eNB_proc_t *proc = &eNB->proc;

  proc->tep.eNB = eNB;
  proc->instance_cnt_te         = -1;
    
  pthread_mutex_init( &proc->mutex_te, NULL);
  pthread_cond_init( &proc->cond_te, NULL);

  printf("Creating te_thread\n");
  pthread_create(&proc->pthread_te, attr_te, te_thread, (void*)&proc->tep);

}





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);
  pdu                                    = &eNB->UL_INFO.rx_ind.rx_pdu_list[eNB->UL_INFO.rx_ind.number_of_pdus];

  //  pdu->rx_ue_information.handle          = eNB->ulsch[UE_id]->handle;
  pdu->rx_ue_information.rnti            = eNB->ulsch[UE_id]->rnti;
  pdu->rx_indication_rel8.length         = eNB->ulsch[UE_id]->harq_processes[harq_pid]->TBS>>3;
  pdu->rx_indication_rel8.offset         = 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
  switch (eNB->frame_parms.N_RB_DL) {
  case 6:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update;  
    break;
  case 15:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update/2;  
    break;
  case 25:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update/4;  
    break;
  case 50:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update/8;  
    break;
  case 75:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update/12;  
    break;
  case 100:
    pdu->rx_indication_rel8.timing_advance = timing_advance_update/16;  
    break;
  }
  // put timing advance command in 0..63 range
  pdu->rx_indication_rel8.timing_advance += 31;
  if (pdu->rx_indication_rel8.timing_advance < 0)  pdu->rx_indication_rel8.timing_advance = 0;
  if (pdu->rx_indication_rel8.timing_advance > 63) pdu->rx_indication_rel8.timing_advance = 63;

  // estimate UL_CQI for MAC (from antenna port 0 only)
  int SNRtimes10 = dB_fixed_times10(eNB->pusch_vars[UE_id]->ulsch_power[0]) - 200;//(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;

  eNB->UL_INFO.rx_ind.number_of_pdus++;
  pthread_mutex_unlock(&eNB->UL_INFO_mutex);

}

void release_harq(PHY_VARS_eNB *eNB,int UE_id,int tb,uint16_t frame,uint8_t subframe,uint16_t mask) {

  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;
  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;
    harq_pid = dlsch0->harq_ids[subframe_tx];
    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");

    dlsch0_harq->status = SCH_IDLE;
    if ((dlsch1_harq == NULL)||
	((dlsch1_harq!=NULL)&&
	 (dlsch1_harq->status == SCH_IDLE)))
      dlsch0->harq_mask   &= ~(1<<harq_pid);
  }
  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);
      if (((1<<m)&mask) > 0) {
	harq_pid = dlsch0->harq_ids[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");
      
	  dlsch0_harq->status = SCH_IDLE;
	  if ((dlsch1_harq == NULL)||
	      ((dlsch1_harq!=NULL)&&
	       (dlsch1_harq->status == SCH_IDLE)))
	    dlsch0->harq_mask   &= ~(1<<harq_pid);
	}
      }
    }
  }
}

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;
  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);
    harq_pid = dlsch0->harq_ids[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];
  uint8_t O;

  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.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++;
  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;


  pthread_mutex_lock(&eNB->UL_INFO_mutex);
  nfapi_harq_indication_pdu_t *pdu =   &eNB->UL_INFO.harq_ind.harq_pdu_list[eNB->UL_INFO.harq_ind.number_of_harqs];
  int M;
  int i;

  pdu->instance_length                                = 0; // don't know what to do with this
  //  pdu->rx_ue_information.handle                       = handle;
  pdu->rx_ue_information.rnti                         = rnti;

  if (eNB->frame_parms.frame_type == FDD) {
    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
      if (ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
   
    }
  }
  else { // TDD
    M=ul_ACK_subframe2_M(&eNB->frame_parms,
			 subframe);

    pdu->harq_indication_fdd_rel13.mode = 1-bundling;  
    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_tdd_rel13.harq_data[0].multiplex.value_0 = 2-ulsch_harq->o_ACK[i];
      // release DLSCH if needed
      if (ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
      if      (M==1 && ulsch_harq->O_ACK==1 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
      else if (M==1 && ulsch_harq->O_ACK==2 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
      else if (M>1 && ulsch_harq->o_ACK[i] == 1) {
	// spatial bundling
	release_harq(eNB,UE_id,0,frame,subframe,1<<i);
	release_harq(eNB,UE_id,1,frame,subframe,1<<i);
      }
    }	
  }
  eNB->UL_INFO.harq_ind.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,i;


  pthread_mutex_lock(&eNB->UL_INFO_mutex);
  nfapi_harq_indication_pdu_t *pdu =   &eNB->UL_INFO.harq_ind.harq_pdu_list[eNB->UL_INFO.harq_ind.number_of_harqs];

  pdu->instance_length                                = 0; // don't know what to do with this
  //  pdu->rx_ue_information.handle                       = handle;
  pdu->rx_ue_information.rnti                         = uci->rnti;


  if (eNB->frame_parms.frame_type == FDD) {
    if (uci->pucch_fmt == pucch_format1a) {
      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
      if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
    }
    else if (uci->pucch_fmt == pucch_format1b) {
      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
      if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
      if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
    }
    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.mode = tdd_mapping_mode;  

    switch (tdd_mapping_mode) {
    case 0: // bundling

      if (uci->pucch_fmt == pucch_format1a) {
	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].bundling.value_0 = harq_ack[0];
	// release all bundled DLSCH if needed
	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
      }
      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.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
	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
	if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
      }
      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.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
	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
      }
      else if (uci->num_pucch_resources == 1 && 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.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
	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
	if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
      }
      else { // num_pucch_resources (M) > 1
	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);
	release_harq(eNB,UE_id,1,frame,subframe,tdd_multiplexing_mask);
      }
      break;
    case 2: // special bundling (SR collision)
      pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1;
      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:
	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) {
	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
	}
	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) {
	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
	}
	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) {
	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
	}
	break;
      }
      break;

    }
  } //TDD


  eNB->UL_INFO.harq_ind.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_pdu_list[eNB->UL_INFO.crc_ind.number_of_crcs];

  pdu->instance_length                                = 0; // don't know what to do with this
  //  pdu->rx_ue_information.handle                       = handle;
  pdu->rx_ue_information.rnti                         = eNB->ulsch[UE_id]->rnti;
  pdu->crc_indication_rel8.crc_flag                   = crc_flag;

  eNB->UL_INFO.crc_ind.number_of_crcs++;
  pthread_mutex_unlock(&eNB->UL_INFO_mutex);
}

void phy_procedures_eNB_uespec_RX(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc,const relaying_type_t r_type)
{
  //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;


  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));

  T(T_ENB_PHY_INPUT_SIGNAL, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(0),
    T_BUFFER(&eNB->common_vars.rxdata[0][0][subframe*eNB->frame_parms.samples_per_tti],
             eNB->frame_parms.samples_per_tti * 4));



  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_UESPEC, 1 );

  LOG_I(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;

  eNB->UL_INFO.rx_ind.number_of_pdus  = 0;
  eNB->UL_INFO.crc_ind.number_of_crcs = 0;

  srs_procedures(eNB,proc);

  uci_procedures(eNB,proc);

  pusch_procedures(eNB,proc);

  lte_eNB_I0_measurements(eNB,
			  subframe,
			  0,
			  eNB->first_run_I0_measurements);
  eNB->first_run_I0_measurements = 0;
  
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX_UESPEC, 0 );

  stop_meas(&eNB->phy_proc_rx);

}