/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/

/*! \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"

#ifdef EMOS
#include "SCHED/phy_procedures_emos.h"
#endif

//#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"

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


//Agent-related headers
#include "ENB_APP/enb_agent_extern.h"
#include "ENB_APP/enb_agent_mac.h"
#include "LAYER2/MAC/enb_agent_mac_proto.h"

//#define DIAG_PHY

#define NS_PER_SLOT 500000

#define PUCCH 1

extern int exit_openair;


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;

//DCI_ALLOC_t dci_alloc[8];

#ifdef EMOS
fifo_dump_emos_eNB emos_dump_eNB;
#endif

#if defined(SMBV) && !defined(EXMIMO)
extern const char smbv_fname[];
extern unsigned short config_frames[4];
extern uint8_t smbv_frame_cnt;
#endif

#ifdef DIAG_PHY
extern int rx_sig_fifo;
#endif

uint8_t is_SR_subframe(PHY_VARS_eNB *phy_vars_eNB,uint8_t UE_id,uint8_t sched_subframe)
{

  const int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  const int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;

  LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d Checking for SR TXOp(sr_ConfigIndex %d)\n",
        phy_vars_eNB->Mod_id,phy_vars_eNB->ulsch_eNB[UE_id]->rnti,frame,subframe,
        phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex);

  if (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex <= 4) {        // 5 ms SR period
    if ((subframe%5) == phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex)
      return(1);
  } else if (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex <= 14) { // 10 ms SR period
    if (subframe==(phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex-5))
      return(1);
  } else if (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex <= 34) { // 20 ms SR period
    if ((10*(frame&1)+subframe) == (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex-15))
      return(1);
  } else if (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex <= 74) { // 40 ms SR period
    if ((10*(frame&3)+subframe) == (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex-35))
      return(1);
  } else if (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex <= 154) { // 80 ms SR period
    if ((10*(frame&7)+subframe) == (phy_vars_eNB->scheduling_request_config[UE_id].sr_ConfigIndex-75))
      return(1);
  }

  return(0);
}

void put_harq_pid_in_freelist(LTE_eNB_DLSCH_t *DLSCH_ptr, int harq_pid)
{
  if (harq_pid >=0 && harq_pid < 8) {
    DLSCH_ptr->harq_pid_freelist[harq_pid] = 1;
  } else {
    LOG_E(PHY, "%s:%d: critical error, get in touch with the authors\n", __FILE__, __LINE__);
    abort();
  }
}

void remove_harq_pid_from_freelist(LTE_eNB_DLSCH_t *DLSCH_ptr, int harq_pid)
{

  if (harq_pid >=0 && harq_pid < 8) {
    DLSCH_ptr->harq_pid_freelist[harq_pid] = 0;
  } else {
    LOG_E(PHY, "%s:%d: critical error, get in touch with the authors\n", __FILE__, __LINE__);
    abort();
  }
}

int32_t add_ue(int16_t rnti, PHY_VARS_eNB *phy_vars_eNB)
{
  uint8_t i;

#ifdef DEBUG_PHY_PROC
  LOG_I(PHY,"[eNB %d/%d] Adding UE with rnti %x\n",
        phy_vars_eNB->Mod_id,
        phy_vars_eNB->CC_id,
        (uint16_t)rnti);
#endif

  for (i=0; i<NUMBER_OF_UE_MAX; i++) {
    if ((phy_vars_eNB->dlsch_eNB[i]==NULL) || (phy_vars_eNB->ulsch_eNB[i]==NULL)) {
      MSC_LOG_EVENT(MSC_PHY_ENB, "0 Failed add ue %"PRIx16" (ENOMEM)", rnti);
      LOG_E(PHY,"Can't add UE, not enough memory allocated\n");
      return(-1);
    } else {
      if (phy_vars_eNB->eNB_UE_stats[i].crnti==0) {
        MSC_LOG_EVENT(MSC_PHY_ENB, "0 Add ue %"PRIx16" ", rnti);
        LOG_D(PHY,"UE_id %d associated with rnti %x\n",i, (uint16_t)rnti);
        phy_vars_eNB->dlsch_eNB[i][0]->rnti = rnti;
        phy_vars_eNB->ulsch_eNB[i]->rnti = rnti;
        phy_vars_eNB->eNB_UE_stats[i].crnti = rnti;

	phy_vars_eNB->eNB_UE_stats[i].Po_PUCCH1_below = 0;
	phy_vars_eNB->eNB_UE_stats[i].Po_PUCCH1_above = (int32_t)pow(10.0,.1*(phy_vars_eNB->lte_frame_parms.ul_power_control_config_common.p0_NominalPUCCH+phy_vars_eNB->rx_total_gain_eNB_dB));
	phy_vars_eNB->eNB_UE_stats[i].Po_PUCCH        = (int32_t)pow(10.0,.1*(phy_vars_eNB->lte_frame_parms.ul_power_control_config_common.p0_NominalPUCCH+phy_vars_eNB->rx_total_gain_eNB_dB));
	LOG_D(PHY,"Initializing Po_PUCCH: p0_NominalPUCCH %d, gain %d => %d\n",
	      phy_vars_eNB->lte_frame_parms.ul_power_control_config_common.p0_NominalPUCCH,
	      phy_vars_eNB->rx_total_gain_eNB_dB,
	      phy_vars_eNB->eNB_UE_stats[i].Po_PUCCH);
  
        return(i);
      }
    }
  }
  return(-1);
}

int mac_phy_remove_ue(module_id_t Mod_idP,rnti_t rntiP) {
  uint8_t i;
  int j,CC_id;
  PHY_VARS_eNB *phy_vars_eNB;

  for (CC_id=0;CC_id<MAX_NUM_CCs;CC_id++) {
    phy_vars_eNB = PHY_vars_eNB_g[Mod_idP][CC_id];
    for (i=0; i<NUMBER_OF_UE_MAX; i++) {
      if ((phy_vars_eNB->dlsch_eNB[i]==NULL) || (phy_vars_eNB->ulsch_eNB[i]==NULL)) {
	MSC_LOG_EVENT(MSC_PHY_ENB, "0 Failed remove ue %"PRIx16" (ENOMEM)", rnti);
	LOG_E(PHY,"Can't remove UE, not enough memory allocated\n");
	return(-1);
      } else {
	if (phy_vars_eNB->eNB_UE_stats[i].crnti==rntiP) {
	  MSC_LOG_EVENT(MSC_PHY_ENB, "0 Removed ue %"PRIx16" ", rntiP);
#ifdef DEBUG_PHY_PROC
	  LOG_I(PHY,"eNB %d removing UE %d with rnti %x\n",phy_vars_eNB->Mod_id,i,rnti);
#endif
	  //msg("[PHY] UE_id %d\n",i);
	  clean_eNb_dlsch(phy_vars_eNB->dlsch_eNB[i][0]);
	  clean_eNb_ulsch(phy_vars_eNB->ulsch_eNB[i]);
	  //phy_vars_eNB->eNB_UE_stats[i].crnti = 0;
	  memset(&phy_vars_eNB->eNB_UE_stats[i],0,sizeof(LTE_eNB_UE_stats));
	  //  mac_exit_wrapper("Removing UE");
	  
	  /* clear the harq pid freelist */
	  phy_vars_eNB->dlsch_eNB[i][0]->head_freelist = 0;
	  phy_vars_eNB->dlsch_eNB[i][0]->tail_freelist = 0;
	  for (j = 0; j < 8; j++)
	    put_harq_pid_in_freelist(phy_vars_eNB->dlsch_eNB[i][0], j);
	  
	  return(i);
	}
      }
    }
  }
  MSC_LOG_EVENT(MSC_PHY_ENB, "0 Failed remove ue %"PRIx16" (not found)", rntiP);
  return(-1);
}

int8_t find_next_ue_index(PHY_VARS_eNB *phy_vars_eNB)
{
  uint8_t i;

  for (i=0; i<NUMBER_OF_UE_MAX; i++) {
    if (phy_vars_eNB->eNB_UE_stats[i].crnti==0) {
      /*if ((phy_vars_eNB->dlsch_eNB[i]) &&
      (phy_vars_eNB->dlsch_eNB[i][0]) &&
      (phy_vars_eNB->dlsch_eNB[i][0]->rnti==0))*/
      LOG_D(PHY,"Next free UE id is %d\n",i);
      return(i);
    }
  }

  return(-1);
}

int prev_frame = -1;
int prev_subframe = -1;
int prev_harq_pid = 0;
int prev_harq_round = 0;

int get_ue_active_harq_pid(const uint8_t Mod_id,const uint8_t CC_id,const uint16_t rnti, const int frame, const uint8_t subframe,uint8_t *harq_pid,uint8_t *round,const uint8_t harq_flag)
{
  LTE_eNB_DLSCH_t *DLSCH_ptr;
  LTE_eNB_ULSCH_t *ULSCH_ptr;
  uint8_t ulsch_subframe,ulsch_frame;
  uint8_t i, j;
  int8_t UE_id = find_ue(rnti,PHY_vars_eNB_g[Mod_id][CC_id]);
  int sf1=(10*frame)+subframe,sf2,sfdiff,sfdiff_max=7;

  if (UE_id==-1) {
    LOG_D(PHY,"Cannot find UE with rnti %x (Mod_id %d, CC_id %d)\n",rnti, Mod_id, CC_id);
    *round=0;
    return(-1);
  }

  if ((harq_flag == openair_harq_DL) || (harq_flag == openair_harq_RA))  {// this is a DL request

    DLSCH_ptr = PHY_vars_eNB_g[Mod_id][CC_id]->dlsch_eNB[(uint32_t)UE_id][0];

    if (harq_flag == openair_harq_RA) {
      if (DLSCH_ptr->harq_processes[0] != NULL) {
	*harq_pid = 0;
	*round = DLSCH_ptr->harq_processes[0]->round;
	return 0;
      } else {
	return -1;
      }
    }

    if (prev_frame == frame && prev_subframe == subframe) {
      *harq_pid = prev_harq_pid;
      *round = prev_harq_round;
      return (0);
    }

    // set to no available process first
    *harq_pid = -1;

    j = prev_harq_pid;

    for (i = 0; i < 8; i++) {
      j = (j + 1) % 8;
      if (DLSCH_ptr->harq_processes[j] != NULL) {
	if (DLSCH_ptr->harq_processes[j]->status == ACTIVE) {
	  sf2 = (DLSCH_ptr->harq_processes[j]->frame*10) + DLSCH_ptr->harq_processes[j]->subframe;
	  if (sf2<=sf1) {
	    sfdiff = sf1-sf2;
	  } else { // this happens when wrapping around 1024 frame barrier
	    sfdiff = 10240 + sf1-sf2;
	  }
	  LOG_D(PHY,"process %d is active, round %d (waiting %d)\n",i,DLSCH_ptr->harq_processes[j]->round,sfdiff);
	  if (sfdiff > sfdiff_max) {
	    prev_harq_pid = j;
	    prev_harq_round = DLSCH_ptr->harq_processes[j]->round;
	    prev_frame = frame;
	    prev_subframe = subframe;
	    *harq_pid = j;
	    *round = DLSCH_ptr->harq_processes[j]->round;
	    LOG_D(PHY,"process %d, %d is the allocated process\n", *harq_pid, *round);
	    return 0;
	  }
	} else { // Send from the freelist
	  if (DLSCH_ptr->harq_pid_freelist[j] == 1) {
	    *harq_pid = j;
	    *round = 0;
	    prev_harq_pid = *harq_pid;
	    prev_harq_round = *round;
	    prev_frame = frame;
	    prev_subframe = subframe;
	    LOG_D(PHY,"process %d is first free process\n", *harq_pid);
	    return 0;
	  }
	}
      } else {
	LOG_E(PHY,"[eNB %d] DLSCH process %d for rnti %x (UE_id %d) not allocated\n",Mod_id,i,rnti,UE_id);
	return(-1);
      }
    }

    if (*harq_pid == 255) {
      *round = 0;
      LOG_I(PHY, "TEST, no process found\n");
      return -1;
    }

    LOG_D(PHY,"get_ue_active_harq_pid DL => Frame %d, Subframe %d : harq_pid %d\n",
	  frame,subframe,*harq_pid);
  } else { // This is a UL request

    ULSCH_ptr = PHY_vars_eNB_g[Mod_id][CC_id]->ulsch_eNB[(uint32_t)UE_id];
    ulsch_subframe = pdcch_alloc2ul_subframe(&PHY_vars_eNB_g[Mod_id][CC_id]->lte_frame_parms,subframe);
    ulsch_frame    = pdcch_alloc2ul_frame(&PHY_vars_eNB_g[Mod_id][CC_id]->lte_frame_parms,frame,subframe);
    // Note this is for TDD configuration 3,4,5 only
    *harq_pid = subframe2harq_pid(&PHY_vars_eNB_g[Mod_id][CC_id]->lte_frame_parms,
                                  ulsch_frame,
                                  ulsch_subframe);
    *round    = ULSCH_ptr->harq_processes[*harq_pid]->round;
    LOG_T(PHY,"[eNB %d][PUSCH %d] Frame %d subframe %d Checking HARQ, round %d\n",Mod_id,*harq_pid,frame,subframe,*round);
  }

  return(0);
}

int16_t get_target_pusch_rx_power(const module_id_t module_idP, const uint8_t CC_id)
{
  //return PHY_vars_eNB_g[module_idP][CC_id]->PHY_measurements_eNB[0].n0_power_tot_dBm;
  return PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.ul_power_control_config_common.p0_NominalPUSCH;
}

int16_t get_target_pucch_rx_power(const module_id_t module_idP, const uint8_t CC_id)
{
  //return PHY_vars_eNB_g[module_idP][CC_id]->PHY_measurements_eNB[0].n0_power_tot_dBm;
  return PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.ul_power_control_config_common.p0_NominalPUCCH;
}

#ifdef EMOS
void phy_procedures_emos_eNB_TX(unsigned char subframe, PHY_VARS_eNB *phy_vars_eNB)
{

}
#endif



void phy_procedures_eNB_S_RX(unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_eNB,uint8_t abstraction_flag,relaying_type_t r_type)
{
  UNUSED(r_type);

  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;

#ifdef DEBUG_PHY_PROC
  LOG_D(PHY,"[eNB %d] Frame %d: Doing phy_procedures_eNB_S_RX(%d)\n", phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_rx, subframe);
#endif


  if (abstraction_flag == 0) {
    lte_eNB_I0_measurements(phy_vars_eNB,
			    subframe,
                            0,
                            phy_vars_eNB->first_run_I0_measurements);
  }

#ifdef PHY_ABSTRACTION
  else {
    lte_eNB_I0_measurements_emul(phy_vars_eNB,
                                 0);
  }

#endif


}



#ifdef EMOS
void phy_procedures_emos_eNB_RX(unsigned char subframe,PHY_VARS_eNB *phy_vars_eNB)
{

  uint8_t aa;
  uint16_t last_subframe_emos;
  uint16_t pilot_pos1 = 3 - phy_vars_eNB->lte_frame_parms.Ncp, pilot_pos2 = 10 - 2*phy_vars_eNB->lte_frame_parms.Ncp;
  uint32_t bytes;

  last_subframe_emos=0;




#ifdef EMOS_CHANNEL

  //if (last_slot%2==1) // this is for all UL subframes
  if (subframe==3)
    for (aa=0; aa<phy_vars_eNB->lte_frame_parms.nb_antennas_rx; aa++) {
      memcpy(&emos_dump_eNB.channel[aa][last_subframe_emos*2*phy_vars_eNB->lte_frame_parms.N_RB_UL*12],
             &phy_vars_eNB->lte_eNB_pusch_vars[0]->drs_ch_estimates[0][aa][phy_vars_eNB->lte_frame_parms.N_RB_UL*12*pilot_pos1],
             phy_vars_eNB->lte_frame_parms.N_RB_UL*12*sizeof(int));
      memcpy(&emos_dump_eNB.channel[aa][(last_subframe_emos*2+1)*phy_vars_eNB->lte_frame_parms.N_RB_UL*12],
             &phy_vars_eNB->lte_eNB_pusch_vars[0]->drs_ch_estimates[0][aa][phy_vars_eNB->lte_frame_parms.N_RB_UL*12*pilot_pos2],
             phy_vars_eNB->lte_frame_parms.N_RB_UL*12*sizeof(int));
    }

#endif

  if (subframe==4) {
    emos_dump_eNB.timestamp = rt_get_time_ns();
    emos_dump_eNB.frame_tx = phy_vars_eNB->proc[subframe].frame_rx;
    emos_dump_eNB.rx_total_gain_dB = phy_vars_eNB->rx_total_gain_eNB_dB;
    emos_dump_eNB.mimo_mode = phy_vars_eNB->transmission_mode[0];
    memcpy(&emos_dump_eNB.PHY_measurements_eNB,
           &phy_vars_eNB->PHY_measurements_eNB[0],
           sizeof(PHY_MEASUREMENTS_eNB));
    memcpy(&emos_dump_eNB.eNB_UE_stats[0],&phy_vars_eNB->eNB_UE_stats[0],NUMBER_OF_UE_MAX*sizeof(LTE_eNB_UE_stats));

    bytes = rtf_put(CHANSOUNDER_FIFO_MINOR, &emos_dump_eNB, sizeof(fifo_dump_emos_eNB));

    //bytes = rtf_put(CHANSOUNDER_FIFO_MINOR, "test", sizeof("test"));
    if (bytes!=sizeof(fifo_dump_emos_eNB)) {
      LOG_W(PHY,"[eNB %d] Frame %d, subframe %d, Problem writing EMOS data to FIFO (bytes=%d, size=%d)\n",
            phy_vars_eNB->Mod_id,phy_vars_eNB->proc[(subframe+1)%10].frame_rx, subframe,bytes,sizeof(fifo_dump_emos_eNB));
    } else {
      if (phy_vars_eNB->proc[(subframe+1)%10].frame_tx%100==0) {
        LOG_I(PHY,"[eNB %d] Frame %d (%d), subframe %d, Writing %d bytes EMOS data to FIFO\n",
              phy_vars_eNB->Mod_id,phy_vars_eNB->proc[(subframe+1)%10].frame_rx, ((fifo_dump_emos_eNB*)&emos_dump_eNB)->frame_tx, subframe, bytes);
      }
    }
  }
}
#endif


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


#if defined(ENABLE_ITTI)
#   if ENABLE_RAL
extern PHY_MEASUREMENTS PHY_measurements;

void phy_eNB_lte_measurement_thresholds_test_and_report(instance_t instanceP, ral_threshold_phy_t* threshold_phy_pP, uint16_t valP)
{
  MessageDef *message_p = NULL;

  if (
    (
      ((threshold_phy_pP->threshold.threshold_val <  valP) && (threshold_phy_pP->threshold.threshold_xdir == RAL_ABOVE_THRESHOLD)) ||
      ((threshold_phy_pP->threshold.threshold_val >  valP) && (threshold_phy_pP->threshold.threshold_xdir == RAL_BELOW_THRESHOLD))
    )  ||
    (threshold_phy_pP->threshold.threshold_xdir == RAL_NO_THRESHOLD)
  ) {
    message_p = itti_alloc_new_message(TASK_PHY_ENB , PHY_MEAS_REPORT_IND);
    memset(&PHY_MEAS_REPORT_IND(message_p), 0, sizeof(PHY_MEAS_REPORT_IND(message_p)));

    memcpy(&PHY_MEAS_REPORT_IND (message_p).threshold,
           &threshold_phy_pP->threshold,
           sizeof(PHY_MEAS_REPORT_IND (message_p).threshold));

    memcpy(&PHY_MEAS_REPORT_IND (message_p).link_param,
           &threshold_phy_pP->link_param,
           sizeof(PHY_MEAS_REPORT_IND (message_p).link_param));
    \

    switch (threshold_phy_pP->link_param.choice) {
    case RAL_LINK_PARAM_CHOICE_LINK_PARAM_VAL:
      PHY_MEAS_REPORT_IND (message_p).link_param._union.link_param_val = valP;
      break;

    case RAL_LINK_PARAM_CHOICE_QOS_PARAM_VAL:
      //PHY_MEAS_REPORT_IND (message_p).link_param._union.qos_param_val.
      AssertFatal (1 == 0, "TO DO RAL_LINK_PARAM_CHOICE_QOS_PARAM_VAL\n");
      break;
    }

    itti_send_msg_to_task(TASK_RRC_ENB, instanceP, message_p);
  }
}

void phy_eNB_lte_check_measurement_thresholds(instance_t instanceP, ral_threshold_phy_t* threshold_phy_pP)
{
  unsigned int  mod_id;

  mod_id = instanceP;

  switch (threshold_phy_pP->link_param.link_param_type.choice) {

  case RAL_LINK_PARAM_TYPE_CHOICE_GEN:
    switch (threshold_phy_pP->link_param.link_param_type._union.link_param_gen) {
    case RAL_LINK_PARAM_GEN_DATA_RATE:
      phy_eNB_lte_measurement_thresholds_test_and_report(instanceP, threshold_phy_pP, 0);
      break;

    case RAL_LINK_PARAM_GEN_SIGNAL_STRENGTH:
      phy_eNB_lte_measurement_thresholds_test_and_report(instanceP, threshold_phy_pP, 0);
      break;

    case RAL_LINK_PARAM_GEN_SINR:
      phy_eNB_lte_measurement_thresholds_test_and_report(instanceP, threshold_phy_pP, 0);
      break;

    case RAL_LINK_PARAM_GEN_THROUGHPUT:
      break;

    case RAL_LINK_PARAM_GEN_PACKET_ERROR_RATE:
      break;

    default:
      ;
    }

    break;

  case RAL_LINK_PARAM_TYPE_CHOICE_LTE:
    switch (threshold_phy_pP->link_param.link_param_type._union.link_param_gen) {
    case RAL_LINK_PARAM_LTE_UE_RSRP:
      break;

    case RAL_LINK_PARAM_LTE_UE_RSRQ:
      break;

    case RAL_LINK_PARAM_LTE_UE_CQI:
      break;

    case RAL_LINK_PARAM_LTE_AVAILABLE_BW:
      break;

    case RAL_LINK_PARAM_LTE_PACKET_DELAY:
      break;

    case RAL_LINK_PARAM_LTE_PACKET_LOSS_RATE:
      break;

    case RAL_LINK_PARAM_LTE_L2_BUFFER_STATUS:
      break;

    case RAL_LINK_PARAM_LTE_MOBILE_NODE_CAPABILITIES:
      break;

    case RAL_LINK_PARAM_LTE_EMBMS_CAPABILITY:
      break;

    case RAL_LINK_PARAM_LTE_JUMBO_FEASIBILITY:
      break;

    case RAL_LINK_PARAM_LTE_JUMBO_SETUP_STATUS:
      break;

    case RAL_LINK_PARAM_LTE_NUM_ACTIVE_EMBMS_RECEIVERS_PER_FLOW:
      break;

    default:
      ;
    }

    break;

  default:
    ;
  }
}
#   endif
#endif


unsigned int taus(void);
DCI_PDU DCI_pdu_tmp;

void phy_procedures_eNB_TX(unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_eNB,uint8_t abstraction_flag,
                           relaying_type_t r_type,PHY_VARS_RN *phy_vars_rn)
{
  UNUSED(phy_vars_rn);
  uint8_t *pbch_pdu=&phy_vars_eNB->pbch_pdu[0];
  uint16_t input_buffer_length, re_allocated=0;
  uint32_t i,aa;
  uint8_t harq_pid;
  DCI_PDU *DCI_pdu;
  uint8_t *DLSCH_pdu=NULL;
  //DCI_PDU DCI_pdu_tmp;
  uint8_t DLSCH_pdu_tmp[768*8];
  int8_t UE_id;
  uint8_t num_pdcch_symbols=0;
  uint8_t ul_subframe;
  uint32_t ul_frame;
#ifdef Rel10
  MCH_PDU *mch_pduP;
  MCH_PDU  mch_pdu;
  //  uint8_t sync_area=255;
#endif
#if defined(SMBV) && !defined(EXMIMO)
  // counts number of allocations in subframe
  // there is at least one allocation for PDCCH
  uint8_t smbv_alloc_cnt = 1;
#endif
  int frame = phy_vars_eNB->proc[sched_subframe].frame_tx;
  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_tx;

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX,1);
  start_meas(&phy_vars_eNB->phy_proc_tx);

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

  for (i=0; i<NUMBER_OF_UE_MAX; i++) {
    // If we've dropped the UE, go back to PRACH mode for this UE

    if (phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors == ULSCH_max_consecutive_errors) {
      LOG_W(PHY,"[eNB %d, CC %d] frame %d, subframe %d, UE %d: ULSCH consecutive error count reached %u, triggering UL Failure\n",
            phy_vars_eNB->Mod_id,phy_vars_eNB->CC_id,frame,subframe, i, phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors);
      phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors=0;
      mac_xface->UL_failure_indication(phy_vars_eNB->Mod_id,
				       phy_vars_eNB->CC_id,
				       frame,
				       phy_vars_eNB->eNB_UE_stats[i].crnti,
				       subframe);
				       
    }
	

  }


  // Get scheduling info for next subframe
  if (phy_vars_eNB->mac_enabled==1) {
    if (phy_vars_eNB->CC_id == 0) {
      mac_xface->eNB_dlsch_ulsch_scheduler(phy_vars_eNB->Mod_id,0,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);//,1);
    }
  }

  if (abstraction_flag==0) {
    // clear the transmit data array for the current subframe
    for (aa=0; aa<phy_vars_eNB->lte_frame_parms.nb_antennas_tx_eNB; aa++) {

      memset(&phy_vars_eNB->lte_eNB_common_vars.txdataF[0][aa][subframe*phy_vars_eNB->lte_frame_parms.ofdm_symbol_size*(phy_vars_eNB->lte_frame_parms.symbols_per_tti)],
             0,phy_vars_eNB->lte_frame_parms.ofdm_symbol_size*(phy_vars_eNB->lte_frame_parms.symbols_per_tti)*sizeof(int32_t));
    }
  }


  if (is_pmch_subframe(phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,&phy_vars_eNB->lte_frame_parms)) {

    if (abstraction_flag==0) {
      // This is DL-Cell spec pilots in Control region
      generate_pilots_slot(phy_vars_eNB,
                           phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                           AMP,
                           subframe<<1,1);
    }

#ifdef Rel10
    // 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(phy_vars_eNB->Mod_id,
                                      phy_vars_eNB->CC_id,
                                      phy_vars_eNB->proc[sched_subframe].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",
              phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,mch_pduP->mcs,
              phy_vars_eNB->dlsch_eNB_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",
              phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].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",
              phy_vars_rn->Mod_id,phy_vars_rn->frame, subframe,
              mch_pduP->sync_area,mch_pduP->mcs,mch_pduP->Pdu_size);
      } else if (phy_vars_rn->mch_avtive[subframe%5] == 1) { // SF2 -> SF7, SF3 -> SF8
        mch_pduP= &mch_pdu;
        memcpy(&mch_pduP->payload, // could be a simple copy
               phy_vars_rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->b,
               phy_vars_rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->TBS>>3);
        mch_pduP->Pdu_size = (uint16_t) (phy_vars_rn->dlsch_rn_MCH[subframe%5]->harq_processes[0]->TBS>>3);
        mch_pduP->mcs = phy_vars_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",
              phy_vars_rn->Mod_id,phy_vars_rn->frame, subframe,subframe%5,
              phy_vars_rn->sync_area[subframe%5],mch_pduP->mcs,mch_pduP->Pdu_size);
      } else {
        mch_pduP=NULL;
      }

      phy_vars_rn->mch_avtive[subframe]=0;
      break;

    default:
      LOG_W(PHY,"[eNB %"PRIu8"] Frame %d subframe %d: unknown relaying type %d \n",
            phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,r_type);
      mch_pduP=NULL;
      break;
    }// switch

    if (mch_pduP) {
      fill_eNB_dlsch_MCH(phy_vars_eNB,mch_pduP->mcs,1,0, abstraction_flag);
      // Generate PMCH
      generate_mch(phy_vars_eNB,sched_subframe,(uint8_t*)mch_pduP->payload,abstraction_flag);
#ifdef DEBUG_PHY

      for (i=0; i<mch_pduP->Pdu_size; i++)
        msg("%2"PRIx8".",(uint8_t)mch_pduP->payload[i]);

      msg("\n");
#endif
    } else {
      LOG_D(PHY,"[eNB/RN] Frame %d subframe %d: MCH not generated \n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);
    }

#endif
  }

  else {
    // this is not a pmch subframe

    if (abstraction_flag==0) {
      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_RS_TX,1);
      generate_pilots_slot(phy_vars_eNB,
                           phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                           AMP,
                           subframe<<1,0);
      if (subframe_select(&phy_vars_eNB->lte_frame_parms,subframe) == SF_DL)
	generate_pilots_slot(phy_vars_eNB,
			     phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
			     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)
      if (subframe == 0) {
        if (phy_vars_eNB->lte_frame_parms.frame_type == FDD) {
          generate_pss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                       AMP,
                       &phy_vars_eNB->lte_frame_parms,
                       (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 6 : 5,
                       0);
          generate_sss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                       AMP,
                       &phy_vars_eNB->lte_frame_parms,
                       (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 5 : 4,
                       0);

        }
      }
    }
  }

  if (subframe == 0) {
    // generate PBCH (Physical Broadcast CHannel) info
    if ((phy_vars_eNB->proc[sched_subframe].frame_tx&3) == 0) {
      pbch_pdu[2] = 0;

      // FIXME setting pbch_pdu[2] to zero makes the switch statement easier: remove all the or-operators
      switch (phy_vars_eNB->lte_frame_parms.N_RB_DL) {
      case 6:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (0<<5);
        break;

      case 15:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (1<<5);
        break;

      case 25:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (2<<5);
        break;

      case 50:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (3<<5);
        break;

      case 75:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (4<<5);
        break;

      case 100:
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (5<<5);
        break;

      default:
        // FIXME if we get here, this should be flagged as an error, right?
        pbch_pdu[2] = (pbch_pdu[2]&0x1f) | (2<<5);
        break;
      }

      pbch_pdu[2] = (pbch_pdu[2]&0xef) |
                    ((phy_vars_eNB->lte_frame_parms.phich_config_common.phich_duration << 4)&0x10);

      switch (phy_vars_eNB->lte_frame_parms.phich_config_common.phich_resource) {
      case oneSixth:
        pbch_pdu[2] = (pbch_pdu[2]&0xf3) | (0<<2);
        break;

      case half:
        pbch_pdu[2] = (pbch_pdu[2]&0xf3) | (1<<2);
        break;

      case one:
        pbch_pdu[2] = (pbch_pdu[2]&0xf3) | (2<<2);
        break;

      case two:
        pbch_pdu[2] = (pbch_pdu[2]&0xf3) | (3<<2);
        break;

      default:
        // unreachable
        break;
      }

      pbch_pdu[2] = (pbch_pdu[2]&0xfc) | ((phy_vars_eNB->proc[sched_subframe].frame_tx>>8)&0x3);
      pbch_pdu[1] = phy_vars_eNB->proc[sched_subframe].frame_tx&0xfc;
      pbch_pdu[0] = 0;
    }

    /// First half of SSS (TDD)
    if (abstraction_flag==0) {

      if (phy_vars_eNB->lte_frame_parms.frame_type == TDD) {
        generate_sss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 6 : 5,
                     1);
      }
    }




    if (abstraction_flag==0) {

      generate_pbch(&phy_vars_eNB->lte_eNB_pbch,
                    phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                    AMP,
                    &phy_vars_eNB->lte_frame_parms,
                    pbch_pdu,
                    phy_vars_eNB->proc[sched_subframe].frame_tx&3);

    }

#ifdef PHY_ABSTRACTION
    else {
      generate_pbch_emul(phy_vars_eNB,pbch_pdu);
    }

#endif


  }

  if (subframe == 1) {

    if (abstraction_flag==0) {

      if (phy_vars_eNB->lte_frame_parms.frame_type == TDD) {
        generate_pss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     2,
                     2);
      }
    }
  }

  // Second half of PSS/SSS (FDD)
  if (subframe == 5) {

    if (abstraction_flag==0) {

      if (phy_vars_eNB->lte_frame_parms.frame_type == FDD) {
        generate_pss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 6 : 5,
                     10);
        generate_sss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 5 : 4,
                     10);

      }
    }
  }

  //  Second-half of SSS (TDD)
  if (subframe == 5) {
    if (abstraction_flag==0) {

      if (phy_vars_eNB->lte_frame_parms.frame_type == TDD) {
        generate_sss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     (phy_vars_eNB->lte_frame_parms.Ncp==NORMAL) ? 6 : 5,
                     11);
      }
    }
  }

  // Second half of PSS (TDD)
  if (subframe == 6) {

    if (abstraction_flag==0) {

      if (phy_vars_eNB->lte_frame_parms.frame_type == TDD) {
        generate_pss(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                     AMP,
                     &phy_vars_eNB->lte_frame_parms,
                     2,
                     12);
      }
    }
  }



#if defined(SMBV) && !defined(EXMIMO)

  // PBCH takes one allocation
  if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
    if (subframe==0)
      smbv_alloc_cnt++;
  }

#endif

  if (phy_vars_eNB->mac_enabled==1) {
    // Parse DCI received from MAC
    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PDCCH_TX,1);
    DCI_pdu = mac_xface->get_dci_sdu(phy_vars_eNB->Mod_id,
				     phy_vars_eNB->CC_id,
				     phy_vars_eNB->proc[sched_subframe].frame_tx,
				     subframe);
  }
  else {
    DCI_pdu = &DCI_pdu_tmp;
#ifdef EMOS_CHANNEL
    fill_dci_emos(DCI_pdu,sched_subframe,phy_vars_eNB);
#else
    fill_dci(DCI_pdu,sched_subframe,phy_vars_eNB);
#endif
  }

  // clear existing ulsch dci allocations before applying info from MAC  (this is table
  ul_subframe = pdcch_alloc2ul_subframe(&phy_vars_eNB->lte_frame_parms,subframe);
  ul_frame = pdcch_alloc2ul_frame(&phy_vars_eNB->lte_frame_parms,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);

  if ((subframe_select(&phy_vars_eNB->lte_frame_parms,ul_subframe)==SF_UL) ||
      (phy_vars_eNB->lte_frame_parms.frame_type == FDD)) {
    harq_pid = subframe2harq_pid(&phy_vars_eNB->lte_frame_parms,ul_frame,ul_subframe);

    for (i=0; i<NUMBER_OF_UE_MAX; i++)
      if (phy_vars_eNB->ulsch_eNB[i]) {
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->dci_alloc=0;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->rar_alloc=0;
      }
  }

  // clear previous allocation information for all UEs
  for (i=0; i<NUMBER_OF_UE_MAX; i++) {
    phy_vars_eNB->dlsch_eNB[i][0]->subframe_tx[subframe] = 0;
  }


  num_pdcch_symbols = DCI_pdu->num_pdcch_symbols;

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

#if defined(SMBV) && !defined(EXMIMO)

  // Sets up PDCCH and DCI table
  if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4) && ((DCI_pdu->Num_common_dci+DCI_pdu->Num_ue_spec_dci)>0)) {
    msg("[SMBV] Frame %3d, SF %d PDCCH, number of DCIs %d\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,DCI_pdu->Num_common_dci+DCI_pdu->Num_ue_spec_dci);
    dump_dci(&phy_vars_eNB->lte_frame_parms,&DCI_pdu->dci_alloc[0]);
    smbv_configure_pdcch(smbv_fname,(smbv_frame_cnt*10) + (subframe),num_pdcch_symbols,DCI_pdu->Num_common_dci+DCI_pdu->Num_ue_spec_dci);
  }

#endif



  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,DCI_pdu->num_pdcch_symbols);

  for (i=0; i<DCI_pdu->Num_common_dci + DCI_pdu->Num_ue_spec_dci ; i++) {
    LOG_D(PHY,"[eNB] Subframe %d: DCI %d/%d : rnti %x, CCEind %d\n",subframe,i,DCI_pdu->Num_common_dci+DCI_pdu->Num_ue_spec_dci,DCI_pdu->dci_alloc[i].rnti,DCI_pdu->dci_alloc[i].firstCCE);
    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,DCI_pdu->dci_alloc[i].rnti);
    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,DCI_pdu->dci_alloc[i].format);
    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,DCI_pdu->dci_alloc[i].firstCCE);

    if (DCI_pdu->dci_alloc[i].rnti == SI_RNTI) {

      generate_eNB_dlsch_params_from_dci(frame,
					 subframe,
                                         &DCI_pdu->dci_alloc[i].dci_pdu[0],
                                         DCI_pdu->dci_alloc[i].rnti,
                                         DCI_pdu->dci_alloc[i].format,
                                         &phy_vars_eNB->dlsch_eNB_SI,
                                         &phy_vars_eNB->lte_frame_parms,
                                         phy_vars_eNB->pdsch_config_dedicated,
                                         SI_RNTI,
                                         0,
                                         P_RNTI,
                                         phy_vars_eNB->eNB_UE_stats[0].DL_pmi_single);


      phy_vars_eNB->dlsch_eNB_SI->nCCE[subframe] = DCI_pdu->dci_alloc[i].firstCCE;

      LOG_T(PHY,"[eNB %"PRIu8"] Frame %d subframe %d : CCE resource for common DCI (SI)  => %"PRIu8"/%u\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
	    phy_vars_eNB->dlsch_eNB_SI->nCCE[subframe],DCI_pdu->dci_alloc[i].firstCCE);
      
#if defined(SMBV) && !defined(EXMIMO)

      // configure SI DCI
      if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
	msg("[SMBV] Frame %3d, SI in SF %d DCI %"PRIu32"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,i);
	smbv_configure_common_dci(smbv_fname,(smbv_frame_cnt*10) + (subframe), "SI", &DCI_pdu->dci_alloc[i], i);
      }
      
#endif
      

    } else if (DCI_pdu->dci_alloc[i].ra_flag == 1) {

      generate_eNB_dlsch_params_from_dci(frame,
					 subframe,
                                         &DCI_pdu->dci_alloc[i].dci_pdu[0],
                                         DCI_pdu->dci_alloc[i].rnti,
                                         DCI_pdu->dci_alloc[i].format,
                                         &phy_vars_eNB->dlsch_eNB_ra,
                                         &phy_vars_eNB->lte_frame_parms,
                                         phy_vars_eNB->pdsch_config_dedicated,
                                         SI_RNTI,
                                         DCI_pdu->dci_alloc[i].rnti,
                                         P_RNTI,
                                         phy_vars_eNB->eNB_UE_stats[0].DL_pmi_single);


      phy_vars_eNB->dlsch_eNB_ra->nCCE[subframe] = DCI_pdu->dci_alloc[i].firstCCE;

      LOG_D(PHY,"[eNB %"PRIu8"] Frame %d subframe %d : CCE resource for common DCI (RA)  => %"PRIu8"/%u (num_pdcch_symbols %d)\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
	    phy_vars_eNB->dlsch_eNB_ra->nCCE[subframe],get_nCCE_mac(phy_vars_eNB->Mod_id,phy_vars_eNB->CC_id,num_pdcch_symbols,subframe),num_pdcch_symbols);
#if defined(SMBV) && !defined(EXMIMO)

      // configure RA DCI
      if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
	msg("[SMBV] Frame %3d, RA in SF %d DCI %"PRIu32"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,i);
	smbv_configure_common_dci(smbv_fname,(smbv_frame_cnt*10) + (subframe), "RA", &DCI_pdu->dci_alloc[i], i);
      }

#endif

    }

    else if (DCI_pdu->dci_alloc[i].format != format0) { // this is a normal DLSCH allocation

      if (phy_vars_eNB->mac_enabled==1)
	UE_id = find_ue((int16_t)DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB);
      else
	UE_id = i;

      if (UE_id>=0) {
	if ((frame%100)==0) {
	  LOG_D(PHY,"Frame %3d, SF %d \n",frame,subframe); 
	  dump_dci(&phy_vars_eNB->lte_frame_parms,&DCI_pdu->dci_alloc[i]);
	}
#if defined(SMBV) && !defined(EXMIMO)
        // Configure this user
        if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
          msg("[SMBV] Frame %3d, SF %d (SMBV SF %d) Configuring user %d with RNTI %"PRIu16" in TM%"PRIu8"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,(smbv_frame_cnt*10) + (subframe),UE_id+1,
              DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB->transmission_mode[(uint8_t)UE_id]);
          smbv_configure_user(smbv_fname,UE_id+1,phy_vars_eNB->transmission_mode[(uint8_t)UE_id],DCI_pdu->dci_alloc[i].rnti);
        }

#endif

        generate_eNB_dlsch_params_from_dci(frame,
					   subframe,
                                           &DCI_pdu->dci_alloc[i].dci_pdu[0],
                                           DCI_pdu->dci_alloc[i].rnti,
                                           DCI_pdu->dci_alloc[i].format,
                                           phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id],
                                           &phy_vars_eNB->lte_frame_parms,
                                           phy_vars_eNB->pdsch_config_dedicated,
                                           SI_RNTI,
                                           0,
                                           P_RNTI,
                                           phy_vars_eNB->eNB_UE_stats[(uint8_t)UE_id].DL_pmi_single);
        LOG_D(PHY,"[eNB %"PRIu8"][PDSCH %"PRIx16"/%"PRIu8"] Frame %d subframe %d: Generated dlsch params\n",
              phy_vars_eNB->Mod_id,DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->current_harq_pid,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);


        T(T_ENB_PHY_DLSCH_UE_DCI, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id),
          T_INT(DCI_pdu->dci_alloc[i].rnti), T_INT(DCI_pdu->dci_alloc[i].format),
          T_INT(phy_vars_eNB->dlsch_eNB[(int)UE_id][0]->current_harq_pid));

        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->nCCE[subframe] = DCI_pdu->dci_alloc[i].firstCCE;

	LOG_D(PHY,"[eNB %"PRIu8"] Frame %d subframe %d : CCE resource for ue DCI (PDSCH %"PRIx16")  => %"PRIu8"/%u\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
	      DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->nCCE[subframe],DCI_pdu->dci_alloc[i].firstCCE);

#if defined(SMBV) && !defined(EXMIMO)
	
	// configure UE-spec DCI
	if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
	  msg("[SMBV] Frame %3d, PDSCH in SF %d DCI %"PRIu32"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,i);
	  smbv_configure_ue_spec_dci(smbv_fname,(smbv_frame_cnt*10) + (subframe), UE_id+1, &DCI_pdu->dci_alloc[i], i);
	}

#endif

        LOG_D(PHY,"[eNB %"PRIu8"][DCI][PDSCH %"PRIx16"] Frame %d subframe %d UE_id %"PRId8" Generated DCI format %d, aggregation %d\n",
              phy_vars_eNB->Mod_id, DCI_pdu->dci_alloc[i].rnti,
              phy_vars_eNB->proc[sched_subframe].frame_tx, subframe,UE_id,
              DCI_pdu->dci_alloc[i].format,
              1<<DCI_pdu->dci_alloc[i].L);
      } else {
        LOG_D(PHY,"[eNB %"PRIu8"][PDSCH] Frame %d : No UE_id with corresponding rnti %"PRIx16", dropping DLSCH\n",
              phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,DCI_pdu->dci_alloc[i].rnti);
      }
    }

  }

  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_DCI_INFO,(frame*10)+subframe);

  // Apply physicalConfigDedicated if needed
  phy_config_dedicated_eNB_step2(phy_vars_eNB);

  for (i=0; i<DCI_pdu->Num_common_dci + DCI_pdu->Num_ue_spec_dci ; i++) {
    if (DCI_pdu->dci_alloc[i].format == format0) {  // this is a ULSCH allocation

      harq_pid = subframe2harq_pid(&phy_vars_eNB->lte_frame_parms,
                                   pdcch_alloc2ul_frame(&phy_vars_eNB->lte_frame_parms,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
                                   pdcch_alloc2ul_subframe(&phy_vars_eNB->lte_frame_parms,subframe));

      if (harq_pid==255) {
        LOG_E(PHY,"[eNB %"PRIu8"] Frame %d: Bad harq_pid for ULSCH allocation\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx);
        return; 
      }

      if (phy_vars_eNB->mac_enabled==1)
	UE_id = find_ue((int16_t)DCI_pdu->dci_alloc[i].rnti,phy_vars_eNB);
      else
	UE_id = i;

      T(T_ENB_PHY_ULSCH_UE_DCI, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(UE_id),
        T_INT(DCI_pdu->dci_alloc[i].rnti), T_INT(harq_pid));

      if (UE_id<0) {
        LOG_E(PHY,"[eNB %"PRIu8"] Frame %d: Unknown UE_id for rnti %"PRIx16"\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,DCI_pdu->dci_alloc[i].rnti);
        mac_exit_wrapper("Invalid UE id (< 0) detected");
        return; // not reached
      }

      LOG_D(PHY,
            "[eNB %"PRIu8"][PUSCH %"PRIu8"] Frame %d subframe %d UL Frame %"PRIu32", UL Subframe %"PRIu8", Generated ULSCH (format0) DCI (rnti %"PRIx16", dci %"PRIx8") (DCI pos %"PRIu32"/%d), aggregation %d\n",
            phy_vars_eNB->Mod_id,
            subframe2harq_pid(&phy_vars_eNB->lte_frame_parms,
                              pdcch_alloc2ul_frame(&phy_vars_eNB->lte_frame_parms,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
                              pdcch_alloc2ul_subframe(&phy_vars_eNB->lte_frame_parms,subframe)),
            phy_vars_eNB->proc[sched_subframe].frame_tx,
            subframe,
            pdcch_alloc2ul_frame(&phy_vars_eNB->lte_frame_parms,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
            pdcch_alloc2ul_subframe(&phy_vars_eNB->lte_frame_parms,subframe),
            DCI_pdu->dci_alloc[i].rnti,
            DCI_pdu->dci_alloc[i].dci_pdu[0],
            i,
            DCI_pdu->Num_common_dci + DCI_pdu->Num_ue_spec_dci,
            1<<DCI_pdu->dci_alloc[i].L);

      generate_eNB_ulsch_params_from_dci(&DCI_pdu->dci_alloc[i].dci_pdu[0],
                                         DCI_pdu->dci_alloc[i].rnti,
                                         sched_subframe,
                                         format0,
                                         UE_id,
                                         phy_vars_eNB,
                                         SI_RNTI,
                                         0,
                                         P_RNTI,
                                         CBA_RNTI,
                                         0);  // do_srs

      LOG_T(PHY,"[eNB %"PRIu8"] Frame %d subframe %d : CCE resources for UE spec DCI (PUSCH %"PRIx16") => %d/%u\n",
	    phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,DCI_pdu->dci_alloc[i].rnti,
	    DCI_pdu->dci_alloc[i].firstCCE,DCI_pdu->dci_alloc[i].firstCCE);
      
#if defined(SMBV) && !defined(EXMIMO)

        // configure UE-spec DCI for UL Grant
      if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
	msg("[SMBV] Frame %3d, SF %d UL DCI %"PRIu32"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,i);
	smbv_configure_ue_spec_dci(smbv_fname,(smbv_frame_cnt*10) + (subframe), UE_id+1, &DCI_pdu->dci_alloc[i], i);
      }
      
#endif

      

      if ((DCI_pdu->dci_alloc[i].rnti  >= CBA_RNTI) && (DCI_pdu->dci_alloc[i].rnti < P_RNTI))
        phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->harq_processes[harq_pid]->subframe_cba_scheduling_flag = 1;
      else
        phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->harq_processes[harq_pid]->subframe_scheduling_flag = 1;

    }
  }





  // if we have DCI to generate do it now
  if ((DCI_pdu->Num_common_dci + DCI_pdu->Num_ue_spec_dci)>0) {


  } else { // for emulation!!
    phy_vars_eNB->num_ue_spec_dci[(subframe)&1]=0;
    phy_vars_eNB->num_common_dci[(subframe)&1]=0;
  }

  if (abstraction_flag == 0) {

    if (DCI_pdu->Num_ue_spec_dci+DCI_pdu->Num_common_dci > 0)
      LOG_D(PHY,"[eNB %"PRIu8"] Frame %d, subframe %d: Calling generate_dci_top (pdcch) (common %"PRIu8",ue_spec %"PRIu8")\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx, subframe,
            DCI_pdu->Num_common_dci,DCI_pdu->Num_ue_spec_dci);

    num_pdcch_symbols = generate_dci_top(DCI_pdu->Num_ue_spec_dci,
                                         DCI_pdu->Num_common_dci,
                                         DCI_pdu->dci_alloc,
                                         0,
                                         AMP,
                                         &phy_vars_eNB->lte_frame_parms,
                                         phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                                         subframe);

  }

#ifdef PHY_ABSTRACTION // FIXME this ifdef seems suspicious
  else {
    LOG_D(PHY,"[eNB %"PRIu8"] Frame %d, subframe %d: Calling generate_dci_top_emul\n",phy_vars_eNB->Mod_id,phy_vars_eNB->proc[sched_subframe].frame_tx, subframe);
    num_pdcch_symbols = generate_dci_top_emul(phy_vars_eNB,DCI_pdu->Num_ue_spec_dci,DCI_pdu->Num_common_dci,DCI_pdu->dci_alloc,subframe);
  }

#endif

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PDCCH_TX,0);

  // Check for SI activity

  if (phy_vars_eNB->dlsch_eNB_SI->active == 1) {
    input_buffer_length = phy_vars_eNB->dlsch_eNB_SI->harq_processes[0]->TBS/8;


    if (phy_vars_eNB->mac_enabled==1) {
      DLSCH_pdu = mac_xface->get_dlsch_sdu(phy_vars_eNB->Mod_id,
					   phy_vars_eNB->CC_id,
					   phy_vars_eNB->proc[sched_subframe].frame_tx,
					   SI_RNTI,
					   0);
    }
    else {
      DLSCH_pdu = DLSCH_pdu_tmp;

      for (i=0; i<input_buffer_length; i++)
	DLSCH_pdu[i] = (unsigned char)(taus()&0xff);
    }

#if defined(SMBV) && !defined(EXMIMO)

    // Configures the data source of allocation (allocation is configured by DCI)
    if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
      msg("[SMBV] Frame %3d, Configuring SI payload in SF %d alloc %"PRIu8"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,(smbv_frame_cnt*10) + (subframe),smbv_alloc_cnt);
      smbv_configure_datalist_for_alloc(smbv_fname, smbv_alloc_cnt++, (smbv_frame_cnt*10) + (subframe), DLSCH_pdu, input_buffer_length);
    }

#endif

    if (abstraction_flag == 0) {

      start_meas(&phy_vars_eNB->dlsch_encoding_stats);
      dlsch_encoding(DLSCH_pdu,
                     &phy_vars_eNB->lte_frame_parms,
                     num_pdcch_symbols,
                     phy_vars_eNB->dlsch_eNB_SI,
                     phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
                     &phy_vars_eNB->dlsch_rate_matching_stats,
                     &phy_vars_eNB->dlsch_turbo_encoding_stats,
                     &phy_vars_eNB->dlsch_interleaving_stats);
      stop_meas(&phy_vars_eNB->dlsch_encoding_stats);

      start_meas(&phy_vars_eNB->dlsch_scrambling_stats);
      dlsch_scrambling(&phy_vars_eNB->lte_frame_parms,
                       0,
                       phy_vars_eNB->dlsch_eNB_SI,
                       get_G(&phy_vars_eNB->lte_frame_parms,
                             phy_vars_eNB->dlsch_eNB_SI->harq_processes[0]->nb_rb,
                             phy_vars_eNB->dlsch_eNB_SI->harq_processes[0]->rb_alloc,
                             get_Qm(phy_vars_eNB->dlsch_eNB_SI->harq_processes[0]->mcs),
                             1,
                             num_pdcch_symbols,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
                       0,
                       subframe<<1);

      stop_meas(&phy_vars_eNB->dlsch_scrambling_stats);

      start_meas(&phy_vars_eNB->dlsch_modulation_stats);

      re_allocated = dlsch_modulation(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                                      AMP,
                                      subframe,
                                      &phy_vars_eNB->lte_frame_parms,
                                      num_pdcch_symbols,
                                      phy_vars_eNB->dlsch_eNB_SI,
                                      (LTE_eNB_DLSCH_t *)NULL);
      stop_meas(&phy_vars_eNB->dlsch_modulation_stats);
    }

#ifdef PHY_ABSTRACTION
    else {
      start_meas(&phy_vars_eNB->dlsch_encoding_stats);
      dlsch_encoding_emul(phy_vars_eNB,
                          DLSCH_pdu,
                          phy_vars_eNB->dlsch_eNB_SI);
      stop_meas(&phy_vars_eNB->dlsch_encoding_stats);
    }

#endif
    phy_vars_eNB->dlsch_eNB_SI->active = 0;

  }

  // Check for RA activity
  if (phy_vars_eNB->dlsch_eNB_ra->active == 1) {

    input_buffer_length = phy_vars_eNB->dlsch_eNB_ra->harq_processes[0]->TBS/8;

    int16_t crnti = mac_xface->fill_rar(phy_vars_eNB->Mod_id,
                                        phy_vars_eNB->CC_id,
                                        phy_vars_eNB->proc[sched_subframe].frame_tx,
                                        dlsch_input_buffer,
                                        phy_vars_eNB->lte_frame_parms.N_RB_UL,
                                        input_buffer_length);
    if (crnti!=0) 
      UE_id = add_ue(crnti,phy_vars_eNB);
    else 
      UE_id = -1;

    if (UE_id==-1) {
      LOG_W(PHY,"[eNB] Max user count reached.\n");
      mac_xface->cancel_ra_proc(phy_vars_eNB->Mod_id,
                                phy_vars_eNB->CC_id,
                                phy_vars_eNB->proc[sched_subframe].frame_tx,
                                crnti);
    } else {
      phy_vars_eNB->eNB_UE_stats[(uint32_t)UE_id].mode = RA_RESPONSE;
      // Initialize indicator for first SR (to be cleared after ConnectionSetup is acknowledged)
      phy_vars_eNB->first_sr[(uint32_t)UE_id] = 1;

      generate_eNB_ulsch_params_from_rar(dlsch_input_buffer,
                                         phy_vars_eNB->proc[sched_subframe].frame_tx,
                                         (subframe),
                                         phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id],
                                         &phy_vars_eNB->lte_frame_parms);

      phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_active = 1;

      get_Msg3_alloc(&phy_vars_eNB->lte_frame_parms,
                     subframe,
                     phy_vars_eNB->proc[sched_subframe].frame_tx,
                     &phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_frame,
                     &phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_subframe);
      LOG_D(PHY,"[eNB][RAPROC] Frame %d subframe %d, Activated Msg3 demodulation for UE %"PRId8" in frame %"PRIu32", subframe %"PRIu8"\n",
            phy_vars_eNB->proc[sched_subframe].frame_tx,
            subframe,
            UE_id,
            phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_frame,
            phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_subframe);

#if defined(SMBV) && !defined(EXMIMO)

      // Configures the data source of allocation (allocation is configured by DCI)
      if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
        msg("[SMBV] Frame %3d, Configuring RA payload in SF %d alloc %"PRIu8"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,(smbv_frame_cnt*10) + (subframe),smbv_alloc_cnt);
        smbv_configure_datalist_for_alloc(smbv_fname, smbv_alloc_cnt++, (smbv_frame_cnt*10) + (subframe), dlsch_input_buffer, input_buffer_length);
      }

#endif


      LOG_D(PHY,"[eNB %"PRIu8"][RAPROC] Frame %d, subframe %d: Calling generate_dlsch (RA) with input size = %"PRIu16",Msg3 frame %"PRIu32", Msg3 subframe %"PRIu8"\n",
            phy_vars_eNB->Mod_id,
            phy_vars_eNB->proc[sched_subframe].frame_tx, subframe,input_buffer_length,
            phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_frame,
            phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_subframe);


      if (abstraction_flag == 0) {

        dlsch_encoding(dlsch_input_buffer,
                       &phy_vars_eNB->lte_frame_parms,
                       num_pdcch_symbols,
                       phy_vars_eNB->dlsch_eNB_ra,
                       phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
                       &phy_vars_eNB->dlsch_rate_matching_stats,
                       &phy_vars_eNB->dlsch_turbo_encoding_stats,
                       &phy_vars_eNB->dlsch_interleaving_stats);

        //  phy_vars_eNB->dlsch_eNB_ra->rnti = RA_RNTI;
        dlsch_scrambling(&phy_vars_eNB->lte_frame_parms,
                         0,
                         phy_vars_eNB->dlsch_eNB_ra,
                         get_G(&phy_vars_eNB->lte_frame_parms,
                               phy_vars_eNB->dlsch_eNB_ra->harq_processes[0]->nb_rb,
                               phy_vars_eNB->dlsch_eNB_ra->harq_processes[0]->rb_alloc,
                               get_Qm(phy_vars_eNB->dlsch_eNB_ra->harq_processes[0]->mcs),
                               1,
                               num_pdcch_symbols,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
                         0,
                         subframe<<1);

        re_allocated = dlsch_modulation(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                                        AMP,
                                        subframe,
                                        &phy_vars_eNB->lte_frame_parms,
                                        num_pdcch_symbols,
                                        phy_vars_eNB->dlsch_eNB_ra,
                                        (LTE_eNB_DLSCH_t *)NULL);
      }

#ifdef PHY_ABSTRACTION
      else {
        dlsch_encoding_emul(phy_vars_eNB,
                            dlsch_input_buffer,
                            phy_vars_eNB->dlsch_eNB_ra);
      }

#endif
      LOG_D(PHY,"[eNB %"PRIu8"][RAPROC] Frame %d subframe %d Deactivating DLSCH RA\n",phy_vars_eNB->Mod_id,
            phy_vars_eNB->proc[sched_subframe].frame_tx,subframe);

    } //max user count

    phy_vars_eNB->dlsch_eNB_ra->active = 0;
  }

#ifndef DISABLE_SF_TRIGGER
  //Send subframe trigger to the controller
  if (mac_agent_registered[phy_vars_eNB->Mod_id]) {
    agent_mac_xface[phy_vars_eNB->Mod_id]->enb_agent_send_sf_trigger(phy_vars_eNB->Mod_id);
  }
#endif

  // Now scan UE specific DLSCH
  for (UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++)
  {
    if ((phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0])&&
        (phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->rnti>0)&&
        (phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->active == 1)) {
      harq_pid = phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->current_harq_pid;
      input_buffer_length = phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->TBS/8;



      LOG_D(PHY,
            "[eNB %"PRIu8"][PDSCH %"PRIx16"/%"PRIu8"] Frame %d, subframe %d: Generating PDSCH/DLSCH with input size = %"PRIu16", G %d, nb_rb %"PRIu16", mcs %"PRIu8", pmi_alloc %"PRIx16", rv %"PRIu8" (round %"PRIu8")\n",
            phy_vars_eNB->Mod_id, phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->rnti,harq_pid,
            phy_vars_eNB->proc[sched_subframe].frame_tx, subframe, input_buffer_length,
            get_G(&phy_vars_eNB->lte_frame_parms,
                  phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->nb_rb,
                  phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->rb_alloc,
                  get_Qm(phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->mcs),
                  phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->Nl,
                  num_pdcch_symbols,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
            phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->nb_rb,
            phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->mcs,
            pmi2hex_2Ar1(phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->pmi_alloc),
            phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->rvidx,
            phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->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", mcs %"PRIu8", pmi_alloc %"PRIx16", rv %"PRIu8" (round %"PRIu8")",
        phy_vars_eNB->proc[sched_subframe].frame_tx, subframe,
        input_buffer_length,
        get_G(&phy_vars_eNB->lte_frame_parms,
        		phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->nb_rb,
        		phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->rb_alloc,
        		get_Qm(phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->mcs),
        		phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->Nl,
        		num_pdcch_symbols,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->nb_rb,
        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->mcs,
        pmi2hex_2Ar1(phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->pmi_alloc),
        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->rvidx,
        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->round);
#endif

      phy_vars_eNB->eNB_UE_stats[(uint8_t)UE_id].dlsch_sliding_cnt++;

      if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->harq_processes[harq_pid]->round == 0) {

        phy_vars_eNB->eNB_UE_stats[(uint32_t)UE_id].dlsch_trials[harq_pid][0]++;

	if (phy_vars_eNB->mac_enabled==1) {
	  DLSCH_pdu = mac_xface->get_dlsch_sdu(phy_vars_eNB->Mod_id,
					       phy_vars_eNB->CC_id,
					       phy_vars_eNB->proc[sched_subframe].frame_tx,
					       phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->rnti,
					       0);
	  phy_vars_eNB->eNB_UE_stats[UE_id].total_TBS_MAC += phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->TBS;
	}
	else {
	  DLSCH_pdu = DLSCH_pdu_tmp;
	  
	  for (i=0; i<input_buffer_length; i++)
	    DLSCH_pdu[i] = (unsigned char)(taus()&0xff);
	}
	
#if defined(SMBV) && !defined(EXMIMO)

        // Configures the data source of allocation (allocation is configured by DCI)
        if (smbv_is_config_frame(phy_vars_eNB->proc[sched_subframe].frame_tx) && (smbv_frame_cnt < 4)) {
          msg("[SMBV] Frame %3d, Configuring PDSCH payload in SF %d alloc %"PRIu8"\n",phy_vars_eNB->proc[sched_subframe].frame_tx,(smbv_frame_cnt*10) + (subframe),smbv_alloc_cnt);
          smbv_configure_datalist_for_user(smbv_fname, UE_id+1, DLSCH_pdu, input_buffer_length);
        }

#endif


#ifdef DEBUG_PHY_PROC
#ifdef DEBUG_DLSCH
        LOG_T(PHY,"eNB DLSCH SDU: \n");

        for (i=0; i<phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->TBS>>3; i++)
          LOG_T(PHY,"%"PRIx8".",DLSCH_pdu[i]);

        LOG_T(PHY,"\n");
#endif
#endif
      } else {
        phy_vars_eNB->eNB_UE_stats[(uint32_t)UE_id].dlsch_trials[harq_pid][phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->round]++;
#ifdef DEBUG_PHY_PROC
#ifdef DEBUG_DLSCH
        LOG_D(PHY,"[eNB] This DLSCH is a retransmission\n");
#endif
#endif
      }

      if (abstraction_flag==0) {

        // 36-212
        start_meas(&phy_vars_eNB->dlsch_encoding_stats);
        dlsch_encoding(DLSCH_pdu,
                       &phy_vars_eNB->lte_frame_parms,
                       num_pdcch_symbols,
                       phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0],
                       phy_vars_eNB->proc[sched_subframe].frame_tx,subframe,
                       &phy_vars_eNB->dlsch_rate_matching_stats,
                       &phy_vars_eNB->dlsch_turbo_encoding_stats,
                       &phy_vars_eNB->dlsch_interleaving_stats);
        stop_meas(&phy_vars_eNB->dlsch_encoding_stats);
        // 36-211
        start_meas(&phy_vars_eNB->dlsch_scrambling_stats);
        dlsch_scrambling(&phy_vars_eNB->lte_frame_parms,
                         0,
                         phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0],
                         get_G(&phy_vars_eNB->lte_frame_parms,
                               phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->nb_rb,
                               phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->rb_alloc,
                               get_Qm(phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->mcs),
                               phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[harq_pid]->Nl,
                               num_pdcch_symbols,phy_vars_eNB->proc[sched_subframe].frame_tx,subframe),
                         0,
                         subframe<<1);
        stop_meas(&phy_vars_eNB->dlsch_scrambling_stats);
        start_meas(&phy_vars_eNB->dlsch_modulation_stats);


        re_allocated = dlsch_modulation(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                                        AMP,
                                        subframe,
                                        &phy_vars_eNB->lte_frame_parms,
                                        num_pdcch_symbols,
                                        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0],
                                        phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][1]);

        stop_meas(&phy_vars_eNB->dlsch_modulation_stats);
      }

#ifdef PHY_ABSTRACTION
      else {
        start_meas(&phy_vars_eNB->dlsch_encoding_stats);
        dlsch_encoding_emul(phy_vars_eNB,
                            DLSCH_pdu,
                            phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]);
        stop_meas(&phy_vars_eNB->dlsch_encoding_stats);
      }

#endif
      phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->active = 0;

    }

    else if ((phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0])&&
             (phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->rnti>0)&&
             (phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->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)
      phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->subframe_tx[subframe]=0;
    }
  }



  // if we have PHICH to generate

  if (is_phich_subframe(&phy_vars_eNB->lte_frame_parms,subframe))
  {
    generate_phich_top(phy_vars_eNB,
                       sched_subframe,
                       AMP,
                       0,
                       abstraction_flag);
  }



#ifdef EMOS
  phy_procedures_emos_eNB_TX(subframe, phy_vars_eNB);
#endif

#if !(defined(EXMIMO) || defined(OAI_USRP) || defined (CPRIGW))

  if (abstraction_flag==0)
  {
    start_meas(&phy_vars_eNB->ofdm_mod_stats);
    do_OFDM_mod(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                phy_vars_eNB->lte_eNB_common_vars.txdata[0],
                phy_vars_eNB->proc[sched_subframe].frame_tx,subframe<<1,
                &phy_vars_eNB->lte_frame_parms);
    do_OFDM_mod(phy_vars_eNB->lte_eNB_common_vars.txdataF[0],
                phy_vars_eNB->lte_eNB_common_vars.txdata[0],
                phy_vars_eNB->proc[sched_subframe].frame_tx,1+(subframe<<1),
                &phy_vars_eNB->lte_frame_parms);
    stop_meas(&phy_vars_eNB->ofdm_mod_stats);
  }

#endif

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_TX,0);
  stop_meas(&phy_vars_eNB->phy_proc_tx);


  (void)re_allocated; /* remove gcc warning "set but not used" */
}

void process_Msg3(PHY_VARS_eNB *phy_vars_eNB,uint8_t sched_subframe,uint8_t UE_id, uint8_t harq_pid)
{
  // this prepares the demodulation of the first PUSCH of a new user, containing Msg3

  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;

  LOG_D(PHY,"[eNB %d][RAPROC] frame %d : subframe %d : process_Msg3 UE_id %d (active %d, subframe %d, frame %d)\n",
        phy_vars_eNB->Mod_id,
        frame,subframe,
        UE_id,phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_active,
        phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_subframe,
        phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_frame);
  phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_flag = 0;

  if ((phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_active == 1) &&
      (phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_subframe == subframe) &&
      (phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_frame == (uint32_t)frame))   {

    //    harq_pid = 0;

    phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_active = 0;
    phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->Msg3_flag = 1;
    phy_vars_eNB->ulsch_eNB[(uint32_t)UE_id]->harq_processes[harq_pid]->subframe_scheduling_flag=1;
    LOG_D(PHY,"[eNB %d][RAPROC] frame %d, subframe %d: Setting subframe_scheduling_flag (Msg3) for UE %d\n",
          phy_vars_eNB->Mod_id,
          frame,subframe,UE_id);
  }
}


// This function retrieves the harq_pid of the corresponding DLSCH process
// and updates the error statistics of the DLSCH based on the received ACK
// info from UE along with the round index.  It also performs the fine-grain
// rate-adaptation based on the error statistics derived from the ACK/NAK process

void process_HARQ_feedback(uint8_t UE_id,
                           uint8_t sched_subframe,
                           PHY_VARS_eNB *phy_vars_eNB,
                           uint8_t pusch_flag,
                           uint8_t *pucch_payload,
                           uint8_t pucch_sel,
                           uint8_t SR_payload)
{

  uint8_t dl_harq_pid[8],dlsch_ACK[8],dl_subframe;
  LTE_eNB_DLSCH_t *dlsch             =  phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0];
  LTE_eNB_UE_stats *ue_stats         =  &phy_vars_eNB->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 = phy_vars_eNB->proc[sched_subframe].frame_rx;
  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  int harq_pid = subframe2harq_pid( &phy_vars_eNB->lte_frame_parms,frame,subframe);

  if (phy_vars_eNB->lte_frame_parms.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] = phy_vars_eNB->ulsch_eNB[(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",phy_vars_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",phy_vars_eNB->Mod_id,
	    frame,dlsch_ACK[0],subframe_m4);
      /*
      if (dlsch_ACK[0]==0)
	AssertFatal(0,"Exiting on NAK on PUCCH\n");
      */
    }


#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(&phy_vars_eNB->lte_frame_parms,
                         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] = phy_vars_eNB->ulsch_eNB[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[0];
      dlsch_ACK[1] = (phy_vars_eNB->pucch_config_dedicated[UE_id].tdd_AckNackFeedbackMode == bundling)
                     ?phy_vars_eNB->ulsch_eNB[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[0]:phy_vars_eNB->ulsch_eNB[(uint8_t)UE_id]->harq_processes[harq_pid]->o_ACK[1];
      //      printf("UE %d: ACK %d,%d\n",UE_id,dlsch_ACK[0],dlsch_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
        //  printf("*** (%d,%d)\n",pucch_payload[0],pucch_payload[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(&phy_vars_eNB->lte_frame_parms,
                    subframe,
                    m);

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

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

    //    printf("nb_alloc %d, all_ACKed %d\n",nb_alloc,all_ACKed);
  }


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

    dl_subframe = ul_ACK_subframe2_dl_subframe(&phy_vars_eNB->lte_frame_parms,
                  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];

      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 (mcs %d, rv %d, TBS %d)\n",phy_vars_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]]->mcs,
              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

          //    msg("[PHY] eNB %d Process %d is active (%d)\n",phy_vars_eNB->Mod_id,dl_harq_pid[m],dlsch_ACK[m]);
          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",phy_vars_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(phy_vars_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",phy_vars_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;
              put_harq_pid_in_freelist(dlsch, dl_harq_pid[m]);
              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",phy_vars_eNB->Mod_id,
                  dlsch->rnti,dl_harq_pid[m],dlsch_harq_proc->round);
#endif

            T(T_ENB_PHY_DLSCH_UE_ACK, T_INT(phy_vars_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;
            put_harq_pid_in_freelist(dlsch, dl_harq_pid[m]);
            dlsch->harq_ids[dl_subframe] = dlsch->Mdlharq;

            ue_stats->total_TBS = ue_stats->total_TBS +
                                  phy_vars_eNB->dlsch_eNB[(uint8_t)UE_id][0]->harq_processes[dl_harq_pid[m]]->TBS;
            /*
              ue_stats->total_transmitted_bits = ue_stats->total_transmitted_bits +
              phy_vars_eNB->dlsch_eNB[(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

          // 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 get_n1_pucch_eNB(PHY_VARS_eNB *phy_vars_eNB,
                      uint8_t UE_id,
                      uint8_t sched_subframe,
                      int16_t *n1_pucch0,
                      int16_t *n1_pucch1,
                      int16_t *n1_pucch2,
                      int16_t *n1_pucch3)
{

  LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->lte_frame_parms;
  uint8_t nCCE0,nCCE1;
  int sf;
  int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;
  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;

  if (frame_parms->frame_type == FDD ) {
    sf = (subframe<4) ? (subframe+6) : (subframe-4);
    //    printf("n1_pucch_eNB: subframe %d, nCCE %d\n",sf,phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[sf]);

    if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[sf]>0) {
      *n1_pucch0 = frame_parms->pucch_config_common.n1PUCCH_AN + phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[sf];
      *n1_pucch1 = -1;
    } else {
      *n1_pucch0 = -1;
      *n1_pucch1 = -1;
    }
  } else {

    switch (frame_parms->tdd_config) {
    case 1:  // DL:S:UL:UL:DL:DL:S:UL:UL:DL
      if (subframe == 2) {  // ACK subframes 5 and 6
        /*  if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[6]>0) {
          nCCE1 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[6];
          *n1_pucch1 = get_Np(frame_parms->N_RB_DL,nCCE1,1) + nCCE1 + frame_parms->pucch_config_common.n1PUCCH_AN;
          }
          else
          *n1_pucch1 = -1;*/

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[5]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[5];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0+ frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;

        *n1_pucch1 = -1;
      } else if (subframe == 3) { // ACK subframe 9

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[9]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[9];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0 +frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;

        *n1_pucch1 = -1;

      } else if (subframe == 7) { // ACK subframes 0 and 1
        //harq_ack[0].nCCE;
        //harq_ack[1].nCCE;
        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[0]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[0];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0 + frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;

        *n1_pucch1 = -1;
      } else if (subframe == 8) { // ACK subframes 4
        //harq_ack[4].nCCE;
        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[4]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[4];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0 + frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;

        *n1_pucch1 = -1;
      } else {
        LOG_D(PHY,"[eNB %d] frame %d: phy_procedures_lte.c: get_n1pucch, illegal subframe %d for tdd_config %d\n",
              phy_vars_eNB->Mod_id,
              frame,
              subframe,frame_parms->tdd_config);
        return;
      }

      break;

    case 3:  // DL:S:UL:UL:UL:DL:DL:DL:DL:DL
      if (subframe == 2) {  // ACK subframes 5,6 and 1 (S in frame-2), forget about n-11 for the moment (S-subframe)
        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[6]>0) {
          nCCE1 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[6];
          *n1_pucch1 = get_Np(frame_parms->N_RB_DL,nCCE1,1) + nCCE1 + frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch1 = -1;

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[5]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[5];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0+ frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;
      } else if (subframe == 3) { // ACK subframes 7 and 8
        LOG_D(PHY,"get_n1_pucch_eNB : subframe 3, subframe_tx[7] %d, subframe_tx[8] %d\n",
              phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[7],phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[8]);

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[8]>0) {
          nCCE1 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[8];
          *n1_pucch1 = get_Np(frame_parms->N_RB_DL,nCCE1,1) + nCCE1 + frame_parms->pucch_config_common.n1PUCCH_AN;
          LOG_D(PHY,"nCCE1 %d, n1_pucch1 %d\n",nCCE1,*n1_pucch1);
        } else
          *n1_pucch1 = -1;

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[7]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[7];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0 +frame_parms->pucch_config_common.n1PUCCH_AN;
          LOG_D(PHY,"nCCE0 %d, n1_pucch0 %d\n",nCCE0,*n1_pucch0);
        } else
          *n1_pucch0 = -1;
      } else if (subframe == 4) { // ACK subframes 9 and 0
        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[0]>0) {
          nCCE1 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[0];
          *n1_pucch1 = get_Np(frame_parms->N_RB_DL,nCCE1,1) + nCCE1 + frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch1 = -1;

        if (phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->subframe_tx[9]>0) {
          nCCE0 = phy_vars_eNB->dlsch_eNB[(uint32_t)UE_id][0]->nCCE[9];
          *n1_pucch0 = get_Np(frame_parms->N_RB_DL,nCCE0,0) + nCCE0 +frame_parms->pucch_config_common.n1PUCCH_AN;
        } else
          *n1_pucch0 = -1;
      } else {
        LOG_D(PHY,"[eNB %d] Frame %d: phy_procedures_lte.c: get_n1pucch, illegal subframe %d for tdd_config %d\n",
              phy_vars_eNB->Mod_id,frame,subframe,frame_parms->tdd_config);
        return;
      }

      break;
    }  // switch tdd_config

    // Don't handle the case M>2
    *n1_pucch2 = -1;
    *n1_pucch3 = -1;
  }
}

void prach_procedures(PHY_VARS_eNB *phy_vars_eNB,uint8_t sched_subframe,uint8_t abstraction_flag)
{

  uint16_t preamble_energy_list[64],preamble_delay_list[64];
  uint16_t preamble_max,preamble_energy_max;
  uint16_t i;
  int8_t UE_id;
  int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;
  uint8_t CC_id = phy_vars_eNB->CC_id;

  memset(&preamble_energy_list[0],0,64*sizeof(uint16_t));
  memset(&preamble_delay_list[0],0,64*sizeof(uint16_t));

  if (abstraction_flag == 0) {
    LOG_D(PHY,"[eNB %d][RAPROC] Frame %d, Subframe %d : PRACH RX Signal Power : %d dBm\n",phy_vars_eNB->Mod_id, 
          frame,subframe,dB_fixed(signal_energy(&phy_vars_eNB->lte_eNB_common_vars.rxdata[0][0][subframe*phy_vars_eNB->lte_frame_parms.samples_per_tti],512)) - phy_vars_eNB->rx_total_gain_eNB_dB);


    rx_prach(phy_vars_eNB,
             subframe,
             preamble_energy_list,
             preamble_delay_list,
             frame,
             0);
  } else {
    for (UE_id=0; UE_id<NB_UE_INST; UE_id++) {

      LOG_D(PHY,"[RAPROC] UE_id %d (%p), generate_prach %d, UE RSI %d, eNB RSI %d preamble index %d\n",
            UE_id,PHY_vars_UE_g[UE_id][CC_id],PHY_vars_UE_g[UE_id][CC_id]->generate_prach,
            PHY_vars_UE_g[UE_id][CC_id]->lte_frame_parms.prach_config_common.rootSequenceIndex,
            phy_vars_eNB->lte_frame_parms.prach_config_common.rootSequenceIndex,
            PHY_vars_UE_g[UE_id][CC_id]->prach_PreambleIndex);

      if ((PHY_vars_UE_g[UE_id][CC_id]->generate_prach==1) &&
          (PHY_vars_UE_g[UE_id][CC_id]->lte_frame_parms.prach_config_common.rootSequenceIndex ==
           phy_vars_eNB->lte_frame_parms.prach_config_common.rootSequenceIndex) ) {
        preamble_energy_list[PHY_vars_UE_g[UE_id][CC_id]->prach_PreambleIndex] = 800;
        preamble_delay_list[PHY_vars_UE_g[UE_id][CC_id]->prach_PreambleIndex] = 5;

      }
    }
  }

  preamble_energy_max = preamble_energy_list[0];
  preamble_max = 0;

  for (i=1; i<64; i++) {
    if (preamble_energy_max < preamble_energy_list[i]) {
      preamble_energy_max = preamble_energy_list[i];
      preamble_max = i;
    }
  }

#ifdef DEBUG_PHY_PROC
  LOG_D(PHY,"[RAPROC] Most likely preamble %d, energy %d dB delay %d\n",
        preamble_max,
        preamble_energy_list[preamble_max],
        preamble_delay_list[preamble_max]);
#endif

  if (preamble_energy_list[preamble_max] > 580) {

    UE_id = find_next_ue_index(phy_vars_eNB);
 
    if (UE_id>=0) {
      phy_vars_eNB->eNB_UE_stats[(uint32_t)UE_id].UE_timing_offset = preamble_delay_list[preamble_max]&0x1FFF; //limit to 13 (=11+2) bits

      phy_vars_eNB->eNB_UE_stats[(uint32_t)UE_id].sector = 0;
      LOG_D(PHY,"[eNB %d/%d][RAPROC] Frame %d, subframe %d Initiating RA procedure (UE_id %d) with preamble %d, energy %d.%d dB, delay %d\n",
            phy_vars_eNB->Mod_id,
            phy_vars_eNB->CC_id,
            frame,
            subframe,
	    UE_id,
            preamble_max,
            preamble_energy_max/10,
            preamble_energy_max%10,
            preamble_delay_list[preamble_max]);

      if (phy_vars_eNB->mac_enabled==1) {
        uint8_t update_TA=4;

        switch (phy_vars_eNB->lte_frame_parms.N_RB_DL) {
        case 6:
          update_TA = 16;
          break;

        case 25:
          update_TA = 4;
          break;

        case 50:
          update_TA = 2;
          break;

        case 100:
          update_TA = 1;
          break;
        }

      mac_xface->initiate_ra_proc(phy_vars_eNB->Mod_id,
                                  phy_vars_eNB->CC_id,
                                  frame,
                                  preamble_max,
                                  preamble_delay_list[preamble_max]*update_TA,
				  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",
            phy_vars_eNB->Mod_id,frame, subframe);
    }
  }
}

void ulsch_decoding_procedures(unsigned char subframe, unsigned int i, PHY_VARS_eNB *phy_vars_eNB, unsigned char abstraction_flag)
{
  UNUSED(subframe);
  UNUSED(i);
  UNUSED(phy_vars_eNB);
  UNUSED(abstraction_flag);
  LOG_D(PHY,"ulsch_decoding_procedures not yet implemented. should not be called");
}


void pucch_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_eNB,int UE_id,int harq_pid,const uint8_t abstraction_flag) {

  LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->lte_frame_parms;
  uint8_t SR_payload = 0,*pucch_payload=NULL,pucch_payload0[2]= {0,0},pucch_payload1[2]= {0,0};
  int16_t n1_pucch0,n1_pucch1,n1_pucch2,n1_pucch3;
  uint8_t do_SR = 0;
  uint8_t pucch_sel = 0;
  int32_t metric0=0,metric1=0,metric0_SR=0;
  ANFBmode_t bundling_flag;
  PUCCH_FMT_t format;

  const int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  const int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;

  if ((phy_vars_eNB->dlsch_eNB[UE_id][0]) &&
      (phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti>0) &&
      (phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->subframe_scheduling_flag==0)) { 

      // check SR availability
      do_SR = is_SR_subframe(phy_vars_eNB,UE_id,sched_subframe);
      //      do_SR = 0;

      // Now ACK/NAK
      // First check subframe_tx flag for earlier subframes
      get_n1_pucch_eNB(phy_vars_eNB,
                       UE_id,
                       sched_subframe,
                       &n1_pucch0,
                       &n1_pucch1,
                       &n1_pucch2,
                       &n1_pucch3);

      LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d, subframe %d Checking for PUCCH (%d,%d,%d,%d) SR %d\n",
            phy_vars_eNB->Mod_id,phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,
            frame,subframe,
            n1_pucch0,n1_pucch1,n1_pucch2,n1_pucch3,do_SR);

      if ((n1_pucch0==-1) && (n1_pucch1==-1) && (do_SR==0)) {  // no TX PDSCH that have to be checked and no SR for this UE_id
      } else {
        // otherwise we have some PUCCH detection to do

	// Null out PUCCH PRBs for noise measurement
	switch(phy_vars_eNB->lte_frame_parms.N_RB_UL) {
	case 6:
	  phy_vars_eNB->rb_mask_ul[0] |= (0x1 | (1<<5)); //position 5
	  break;
	case 15:
	  phy_vars_eNB->rb_mask_ul[0] |= (0x1 | (1<<14)); // position 14
	  break;
	case 25:
	  phy_vars_eNB->rb_mask_ul[0] |= (0x1 | (1<<24)); // position 24
	  break;
	case 50:
	  phy_vars_eNB->rb_mask_ul[0] |= 0x1;
	  phy_vars_eNB->rb_mask_ul[1] |= (1<<17); // position 49 (49-32)
	  break;
	case 75:
	  phy_vars_eNB->rb_mask_ul[0] |= 0x1;
	  phy_vars_eNB->rb_mask_ul[2] |= (1<<10); // position 74 (74-64)
	  break;
	case 100:
	  phy_vars_eNB->rb_mask_ul[0] |= 0x1;
	  phy_vars_eNB->rb_mask_ul[3] |= (1<<3); // position 99 (99-96)
	  break;
	default:
	  LOG_E(PHY,"Unknown number for N_RB_UL %d\n",phy_vars_eNB->lte_frame_parms.N_RB_UL);
	  break;
	}

        if (do_SR == 1) {
          phy_vars_eNB->eNB_UE_stats[UE_id].sr_total++;

          if (abstraction_flag == 0)
            metric0_SR = rx_pucch(phy_vars_eNB,
				  pucch_format1,
				  UE_id,
				  phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex,
				  0, // n2_pucch
				  0, // shortened format, should be use_srs flag, later
				  &SR_payload,
                                  frame,
				  subframe,
				  PUCCH1_THRES);

#ifdef PHY_ABSTRACTION
          else {
            metric0_SR = rx_pucch_emul(phy_vars_eNB,
				       UE_id,
				       pucch_format1,
				       0,
				       &SR_payload,
				       sched_subframe);
            LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d Checking SR (UE SR %d/%d)\n",phy_vars_eNB->Mod_id,
                  phy_vars_eNB->ulsch_eNB[UE_id]->rnti,frame,subframe,SR_payload,phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex);
          }

#endif

          if (SR_payload == 1) {
            LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d Got SR for PUSCH, transmitting to MAC\n",phy_vars_eNB->Mod_id,
                  phy_vars_eNB->ulsch_eNB[UE_id]->rnti,frame,subframe);
            phy_vars_eNB->eNB_UE_stats[UE_id].sr_received++;

            if (phy_vars_eNB->first_sr[UE_id] == 1) { // this is the first request for uplink after Connection Setup, so clear HARQ process 0 use for Msg4
              /* is this test necessary? */
              if (phy_vars_eNB->dlsch_eNB[UE_id][0]->harq_processes[0]->status != SCH_IDLE)
                put_harq_pid_in_freelist(phy_vars_eNB->dlsch_eNB[UE_id][0], 0);
              phy_vars_eNB->first_sr[UE_id] = 0;
              phy_vars_eNB->dlsch_eNB[UE_id][0]->harq_processes[0]->round=0;
              phy_vars_eNB->dlsch_eNB[UE_id][0]->harq_processes[0]->status=SCH_IDLE;
              LOG_D(PHY,"[eNB %d][SR %x] Frame %d subframe %d First SR\n",
                    phy_vars_eNB->Mod_id,
                    phy_vars_eNB->ulsch_eNB[UE_id]->rnti,frame,subframe);
            }

	    if (phy_vars_eNB->mac_enabled==1) {
	      mac_xface->SR_indication(phy_vars_eNB->Mod_id,
				       phy_vars_eNB->CC_id,
				       frame,
				       phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,subframe);
	    }
          }
        }// do_SR==1

        if ((n1_pucch0==-1) && (n1_pucch1==-1)) { // just check for SR
        } else if (phy_vars_eNB->lte_frame_parms.frame_type==FDD) { // FDD
          // if SR was detected, use the n1_pucch from SR, else use n1_pucch0
	  //          n1_pucch0 = (SR_payload==1) ? phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex:n1_pucch0;

	  LOG_D(PHY,"Demodulating PUCCH for ACK/NAK: n1_pucch0 %d (%d), SR_payload %d\n",n1_pucch0,phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex,SR_payload);

          if (abstraction_flag == 0) {



            metric0 = rx_pucch(phy_vars_eNB,
                               pucch_format1a,
                               UE_id,
                               (uint16_t)n1_pucch0,
                               0, //n2_pucch
                               0, // shortened format
                               pucch_payload0,
                               frame,
                               subframe,
                               PUCCH1a_THRES);

            if (metric0 < metric0_SR)
	      metric0=rx_pucch(phy_vars_eNB,
			       pucch_format1a,
			       UE_id,
			       phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex,
			       0, //n2_pucch
			       0, // shortened format
			       pucch_payload0,
                               frame,
			       subframe,
			       PUCCH1a_THRES);
	  }
          else {
#ifdef PHY_ABSTRACTION
            metric0 = rx_pucch_emul(phy_vars_eNB,UE_id,
                                    pucch_format1a,
                                    0,
                                    pucch_payload0,
                                    subframe);
#endif
          }

#ifdef DEBUG_PHY_PROC
          LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d pucch1a (FDD) payload %d (metric %d)\n",
                phy_vars_eNB->Mod_id,
                phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,
                frame,subframe,
                pucch_payload0[0],metric0);
#endif

          process_HARQ_feedback(UE_id,sched_subframe,phy_vars_eNB,
                                0,// pusch_flag
                                pucch_payload0,
                                2,
                                SR_payload);

        } // FDD
        else {  //TDD

          bundling_flag = phy_vars_eNB->pucch_config_dedicated[UE_id].tdd_AckNackFeedbackMode;

          // fix later for 2 TB case and format1b

          if ((frame_parms->frame_type==FDD) ||
              (bundling_flag==bundling)    ||
              ((frame_parms->frame_type==TDD)&&(frame_parms->tdd_config==1)&&((subframe!=2)||(subframe!=7)))) {
            format = pucch_format1a;
            //      msg("PUCCH 1a\n");
          } else {
            format = pucch_format1b;
            //      msg("PUCCH 1b\n");
          }

          // 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",phy_vars_eNB->Mod_id,
                  phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,
                  frame,subframe,
                  n1_pucch0,n1_pucch1,n1_pucch2,n1_pucch3,format);
#endif

            if (abstraction_flag == 0)
              metric0_SR = rx_pucch(phy_vars_eNB,
				    format,
				    UE_id,
				    phy_vars_eNB->scheduling_request_config[UE_id].sr_PUCCH_ResourceIndex,
				    0, //n2_pucch
				    0, // shortened format
				    pucch_payload0,
                                    frame,
				    subframe,
				    PUCCH1a_THRES);
            else {
#ifdef PHY_ABSTRACTION
              metric0 = rx_pucch_emul(phy_vars_eNB,UE_id,
                                      format,
                                      0,
                                      pucch_payload0,
                                      subframe);
#endif
            }
          } else { //using n1_pucch0/n1_pucch1 resources
#ifdef DEBUG_PHY_PROC
            LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d Checking ACK/NAK (%d,%d,%d,%d) format %d\n",phy_vars_eNB->Mod_id,
                  phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,
                  frame,subframe,
                  n1_pucch0,n1_pucch1,n1_pucch2,n1_pucch3,format);
#endif
            metric0=0;
            metric1=0;

            // Check n1_pucch0 metric
            if (n1_pucch0 != -1) {
              if (abstraction_flag == 0)
                metric0 = rx_pucch(phy_vars_eNB,
                                   format,
                                   UE_id,
                                   (uint16_t)n1_pucch0,
                                   0, // n2_pucch
                                   0, // shortened format
                                   pucch_payload0,
                                   frame,
                                   subframe,
                                   PUCCH1a_THRES);
              else {
#ifdef PHY_ABSTRACTION
                metric0 = rx_pucch_emul(phy_vars_eNB,UE_id,
                                        format,
                                        0,
                                        pucch_payload0,
                                        subframe);
#endif
              }
            }

            // Check n1_pucch1 metric
            if (n1_pucch1 != -1) {
              if (abstraction_flag == 0)
                metric1 = rx_pucch(phy_vars_eNB,
                                   format,
                                   UE_id,
                                   (uint16_t)n1_pucch1,
                                   0, //n2_pucch
                                   0, // shortened format
                                   pucch_payload1,
                                   frame,
                                   subframe,
                                   PUCCH1a_THRES);
              else {
#ifdef PHY_ABSTRACTION
                metric1 = rx_pucch_emul(phy_vars_eNB,UE_id,
                                        format,
                                        1,
                                        pucch_payload1,
                                        subframe);


#endif
              }
            }
          }

          if (SR_payload == 1) {
            pucch_payload = pucch_payload0;

            if (bundling_flag == bundling)
              pucch_sel = 2;
          } else if (bundling_flag == multiplexing) { // multiplexing + no SR
            pucch_payload = (metric1>metric0) ? pucch_payload1 : pucch_payload0;
            pucch_sel     = (metric1>metric0) ? 1 : 0;
          } else { // bundling + no SR
            if (n1_pucch1 != -1)
              pucch_payload = pucch_payload1;
            else if (n1_pucch0 != -1)
              pucch_payload = pucch_payload0;

            pucch_sel = 2;  // indicate that this is a bundled ACK/NAK
          }

#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",phy_vars_eNB->Mod_id,
                phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,
                frame,subframe,
                metric0,metric1,pucch_sel,pucch_payload[0],pucch_payload[1]);
#endif
          process_HARQ_feedback(UE_id,sched_subframe,phy_vars_eNB,
                                0,// pusch_flag
                                pucch_payload,
                                pucch_sel,
                                SR_payload);
        }
      }

    }
}

void cba_procedures(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_eNB,int UE_id,int harq_pid,const uint8_t abstraction_flag) {

  uint8_t access_mode;
  int num_active_cba_groups;
  const int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  const int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;
  uint16_t rnti=0;
  int ret=0;

  num_active_cba_groups = phy_vars_eNB->ulsch_eNB[UE_id]->num_active_cba_groups;
  
  if ((phy_vars_eNB->ulsch_eNB[UE_id]) &&
      (num_active_cba_groups > 0) &&
      (phy_vars_eNB->ulsch_eNB[UE_id]->cba_rnti[UE_id%num_active_cba_groups]>0) &&
      (phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->subframe_cba_scheduling_flag==1)) {
    rnti=0;
    
#ifdef DEBUG_PHY_PROC
    LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d Checking PUSCH/ULSCH CBA Reception for UE %d with cba rnti %x mode %s\n",
	  phy_vars_eNB->Mod_id,harq_pid,
	  frame,subframe,
	  UE_id, (uint16_t)phy_vars_eNB->ulsch_eNB[UE_id]->cba_rnti[UE_id%num_active_cba_groups],mode_string[phy_vars_eNB->eNB_UE_stats[UE_id].mode]);
#endif
    
    if (abstraction_flag==0) {
      rx_ulsch(phy_vars_eNB,
	       sched_subframe,
	       phy_vars_eNB->eNB_UE_stats[UE_id].sector,  // this is the effective sector id
	       UE_id,
	       phy_vars_eNB->ulsch_eNB,
	       0);
    }
    
#ifdef PHY_ABSTRACTION
    else {
      rx_ulsch_emul(phy_vars_eNB,
		    subframe,
		    phy_vars_eNB->eNB_UE_stats[UE_id].sector,  // this is the effective sector id
		    UE_id);
    }
    
#endif
    
    if (abstraction_flag == 0) {
      ret = ulsch_decoding(phy_vars_eNB,
			   UE_id,
			   sched_subframe,
			   0, // control_only_flag
			   phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->V_UL_DAI,
			   phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->nb_rb>20 ? 1 : 0);
    }
    
#ifdef PHY_ABSTRACTION
    else {
      ret = ulsch_decoding_emul(phy_vars_eNB,
				sched_subframe,
				UE_id,
				&rnti);
    }
    
#endif
    
    if (phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->cqi_crc_status == 1) {
#ifdef DEBUG_PHY_PROC
      
      print_CQI(phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->o,phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->uci_format,0,phy_vars_eNB->lte_frame_parms.N_RB_DL);
#endif
      access_mode = UNKNOWN_ACCESS;
      extract_CQI(phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->o,
		  phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->uci_format,
		  &phy_vars_eNB->eNB_UE_stats[UE_id],
		  phy_vars_eNB->lte_frame_parms.N_RB_DL,
		  &rnti, &access_mode);
      phy_vars_eNB->eNB_UE_stats[UE_id].rank = phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->o_RI[0];
    }
    
      phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->subframe_cba_scheduling_flag=0;
      phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->status= SCH_IDLE;
      
      if ((num_active_cba_groups > 0) &&
          (UE_id + num_active_cba_groups < NUMBER_OF_UE_MAX) &&
          (phy_vars_eNB->ulsch_eNB[UE_id+num_active_cba_groups]->cba_rnti[UE_id%num_active_cba_groups] > 0 ) &&
          (phy_vars_eNB->ulsch_eNB[UE_id+num_active_cba_groups]->num_active_cba_groups> 0)) {
#ifdef DEBUG_PHY_PROC
        LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d UE %d harq_pid %d resetting the subframe_scheduling_flag for Ue %d cba groups %d members\n",
              phy_vars_eNB->Mod_id,harq_pid,frame,subframe,UE_id,harq_pid,
              UE_id+num_active_cba_groups, UE_id%phy_vars_eNB->ulsch_eNB[UE_id]->num_active_cba_groups);
#endif
        phy_vars_eNB->ulsch_eNB[UE_id+num_active_cba_groups]->harq_processes[harq_pid]->subframe_cba_scheduling_flag=1;
        phy_vars_eNB->ulsch_eNB[UE_id+num_active_cba_groups]->harq_processes[harq_pid]->status= CBA_ACTIVE;
        phy_vars_eNB->ulsch_eNB[UE_id+num_active_cba_groups]->harq_processes[harq_pid]->TBS=phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->TBS;
      }

      if (ret == (1+MAX_TURBO_ITERATIONS)) {
        phy_vars_eNB->eNB_UE_stats[UE_id].ulsch_round_errors[harq_pid][phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->round]++;
        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->phich_active = 1;
        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->phich_ACK = 0;
        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->round++;
      } // ulsch in error
      else {
        LOG_D(PHY,"[eNB %d][PUSCH %d] Frame %d subframe %d ULSCH received, setting round to 0, PHICH ACK\n",
              phy_vars_eNB->Mod_id,harq_pid,
              frame,subframe);

        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->phich_active = 1;
        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->phich_ACK = 1;
        phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->round = 0;
        phy_vars_eNB->eNB_UE_stats[UE_id].ulsch_consecutive_errors = 0;
#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,phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->TBS>>3);

        for (j=0; j<phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->TBS>>3; j++)
          LOG_T(PHY,"%x.",phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->b[j]);

        LOG_T(PHY,"\n");
#endif
#endif

        if (access_mode > UNKNOWN_ACCESS) {
          LOG_D(PHY,"[eNB %d] Frame %d, Subframe %d : received ULSCH SDU from CBA transmission, UE (%d,%x), CBA (group %d, rnti %x)\n",
                phy_vars_eNB->Mod_id, frame,subframe,
                UE_id, phy_vars_eNB->ulsch_eNB[UE_id]->rnti,
                UE_id % phy_vars_eNB->ulsch_eNB[UE_id]->num_active_cba_groups, phy_vars_eNB->ulsch_eNB[UE_id]->cba_rnti[UE_id%num_active_cba_groups]);

          // detect if there is a CBA collision
          if (phy_vars_eNB->cba_last_reception[UE_id%num_active_cba_groups] == 0 ) {
            mac_xface->rx_sdu(phy_vars_eNB->Mod_id,
                              phy_vars_eNB->CC_id,
                              frame,subframe,
                              phy_vars_eNB->ulsch_eNB[UE_id]->rnti,
                              phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->b,
                              phy_vars_eNB->ulsch_eNB[UE_id]->harq_processes[harq_pid]->TBS>>3,
                              harq_pid,
                              NULL);

            phy_vars_eNB->cba_last_reception[UE_id%num_active_cba_groups]+=1;//(subframe);
          } else {
            if (phy_vars_eNB->cba_last_reception[UE_id%num_active_cba_groups] == 1 )
              LOG_N(PHY,"[eNB%d] Frame %d subframe %d : first CBA collision detected \n ",
                    phy_vars_eNB->Mod_id,frame,subframe);

            LOG_N(PHY,"[eNB%d] Frame %d subframe %d : CBA collision set SR for UE %d in group %d \n ",
                  phy_vars_eNB->Mod_id,frame,subframe,
                  phy_vars_eNB->cba_last_reception[UE_id%num_active_cba_groups],UE_id%num_active_cba_groups );

            phy_vars_eNB->cba_last_reception[UE_id%num_active_cba_groups]+=1;

            mac_xface->SR_indication(phy_vars_eNB->Mod_id,
                                     phy_vars_eNB->CC_id,
                                     frame,
                                     phy_vars_eNB->dlsch_eNB[UE_id][0]->rnti,subframe);
          }
        } // UNKNOWN_ACCESS
      } // ULSCH CBA not in error
  }

}

void phy_procedures_eNB_RX(const unsigned char sched_subframe,PHY_VARS_eNB *phy_vars_eNB,const uint8_t abstraction_flag,const relaying_type_t r_type)
{
  //RX processing
  UNUSED(r_type);
  uint32_t l, ret=0,i,j,k;
  uint32_t harq_pid, harq_idx, round;
  uint8_t nPRS;
  LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_eNB->lte_frame_parms;
  int sync_pos;
  uint16_t rnti=0;
  uint8_t access_mode;

  const int subframe = phy_vars_eNB->proc[sched_subframe].subframe_rx;
  const int frame = phy_vars_eNB->proc[sched_subframe].frame_rx;

  AssertFatal(sched_subframe < NUM_ENB_THREADS, "Bad sched_subframe %d", sched_subframe);

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX,1);
  start_meas(&phy_vars_eNB->phy_proc_rx);
#ifdef DEBUG_PHY_PROC
  LOG_D(PHY,"[eNB %d] Frame %d: Doing phy_procedures_eNB_RX(%d)\n",phy_vars_eNB->Mod_id,frame, subframe);
#endif

  T(T_ENB_PHY_UL_TICK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe));

  T(T_ENB_PHY_INPUT_SIGNAL, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(0),
    T_BUFFER(&phy_vars_eNB->lte_eNB_common_vars.rxdata[0][0][subframe*phy_vars_eNB->lte_frame_parms.samples_per_tti],
             phy_vars_eNB->lte_frame_parms.samples_per_tti * 4));

  phy_vars_eNB->rb_mask_ul[0]=0;
  phy_vars_eNB->rb_mask_ul[1]=0;
  phy_vars_eNB->rb_mask_ul[2]=0;
  phy_vars_eNB->rb_mask_ul[3]=0;

  if (abstraction_flag == 0) {
    remove_7_5_kHz(phy_vars_eNB,subframe<<1);
    remove_7_5_kHz(phy_vars_eNB,(subframe<<1)+1);
  }

  // check if we have to detect PRACH first
  if (is_prach_subframe(&phy_vars_eNB->lte_frame_parms,frame,subframe)>0) {
    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1);
    prach_procedures(phy_vars_eNB,sched_subframe,abstraction_flag);
    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0);
  }

  if (abstraction_flag == 0) {
    start_meas(&phy_vars_eNB->ofdm_demod_stats);

    for (l=0; l<phy_vars_eNB->lte_frame_parms.symbols_per_tti/2; l++) {

      slot_fep_ul(&phy_vars_eNB->lte_frame_parms,
                  &phy_vars_eNB->lte_eNB_common_vars,
                  l,
                  subframe<<1,
                  0,
                  0
                 );
      slot_fep_ul(&phy_vars_eNB->lte_frame_parms,
                  &phy_vars_eNB->lte_eNB_common_vars,
                  l,
                  (subframe<<1)+1,
                  0,
                  0
                 );
    }

    stop_meas(&phy_vars_eNB->ofdm_demod_stats);
  }

  // Check for active processes in current subframe
  harq_pid = subframe2harq_pid(&phy_vars_eNB->lte_frame_parms,
                               frame,subframe);

  // reset the cba flag used for collision detection
  for (i=0; i < NUM_MAX_CBA_GROUP; i++) {
    phy_vars_eNB->cba_last_reception[i]=0;
  }

  // Do PUCCH processing first

  for (i=0; i<NUMBER_OF_UE_MAX; i++) {
    pucch_procedures(sched_subframe,phy_vars_eNB,i,harq_pid,abstraction_flag);
  }

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

    // check for Msg3
    if (phy_vars_eNB->mac_enabled==1) {
      if (phy_vars_eNB->eNB_UE_stats[i].mode == RA_RESPONSE) {
	process_Msg3(phy_vars_eNB,sched_subframe,i,harq_pid);
      }
    }


    phy_vars_eNB->pusch_stats_rb[i][(frame*10)+subframe] = -63;
    phy_vars_eNB->pusch_stats_round[i][(frame*10)+subframe] = 0;
    phy_vars_eNB->pusch_stats_mcs[i][(frame*10)+subframe] = -63;

    if ((phy_vars_eNB->ulsch_eNB[i]) &&
        (phy_vars_eNB->ulsch_eNB[i]->rnti>0) &&
        (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->subframe_scheduling_flag==1)) {
      // UE is has ULSCH scheduling
      round = phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round;

      for (int rb=0;
           rb<=phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb;
	   rb++) {
	int rb2 = rb+phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->first_rb;
	phy_vars_eNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31));
      }
#ifdef DEBUG_PHY_PROC
      LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d Scheduling PUSCH/ULSCH Reception for rnti %x (UE_id %d)\n",
            phy_vars_eNB->Mod_id,harq_pid,
            frame,subframe,phy_vars_eNB->ulsch_eNB[i]->rnti,i);
#endif

      if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1) {
        LOG_D(PHY,"[eNB %d] frame %d, subframe %d: Scheduling ULSCH Reception for Msg3 in Sector %d\n",
              phy_vars_eNB->Mod_id,
              frame,
              subframe,
              phy_vars_eNB->eNB_UE_stats[i].sector);
	VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_ULSCH_MSG3,1);
      } else {
        LOG_D(PHY,"[eNB %d] frame %d, subframe %d: Scheduling ULSCH Reception for UE %d Mode %s\n",
              phy_vars_eNB->Mod_id,
              frame,
              subframe,
              i,
              mode_string[phy_vars_eNB->eNB_UE_stats[i].mode]);
      }


      nPRS = phy_vars_eNB->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.nPRS[subframe<<1];

      phy_vars_eNB->ulsch_eNB[i]->cyclicShift = (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->n_DMRS2 + phy_vars_eNB->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift +
          nPRS)%12;

      if (frame_parms->frame_type == FDD ) {
        int sf = (subframe<4) ? (subframe+6) : (subframe-4);

        if (phy_vars_eNB->dlsch_eNB[i][0]->subframe_tx[sf]>0) { // we have downlink transmission
          phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->O_ACK = 1;
        } else {
          phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->O_ACK = 0;
        }
      }

      LOG_D(PHY,
            "[eNB %d][PUSCH %d] Frame %d Subframe %d Demodulating PUSCH: dci_alloc %d, rar_alloc %d, round %d, first_rb %d, nb_rb %d, mcs %d, TBS %d, rv %d, cyclic_shift %d (n_DMRS2 %d, cyclicShift_common %d, nprs %d), O_ACK %d \n",
            phy_vars_eNB->Mod_id,harq_pid,frame,subframe,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->dci_alloc,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->rar_alloc,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->first_rb,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->mcs,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->TBS,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->rvidx,
            phy_vars_eNB->ulsch_eNB[i]->cyclicShift,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->n_DMRS2,
            phy_vars_eNB->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift,
            nPRS,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->O_ACK);
      phy_vars_eNB->pusch_stats_rb[i][(frame*10)+subframe] = phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb;
      phy_vars_eNB->pusch_stats_round[i][(frame*10)+subframe] = phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round;
      phy_vars_eNB->pusch_stats_mcs[i][(frame*10)+subframe] = phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->mcs;
      start_meas(&phy_vars_eNB->ulsch_demodulation_stats);

      if (abstraction_flag==0) {
        rx_ulsch(phy_vars_eNB,
                 sched_subframe,
                 phy_vars_eNB->eNB_UE_stats[i].sector,  // this is the effective sector id
                 i,
                 phy_vars_eNB->ulsch_eNB,
                 0);
      }

#ifdef PHY_ABSTRACTION
      else {
        rx_ulsch_emul(phy_vars_eNB,
                      subframe,
                      phy_vars_eNB->eNB_UE_stats[i].sector,  // this is the effective sector id
                      i);
      }

#endif
      stop_meas(&phy_vars_eNB->ulsch_demodulation_stats);


      start_meas(&phy_vars_eNB->ulsch_decoding_stats);

      if (abstraction_flag == 0) {
        ret = ulsch_decoding(phy_vars_eNB,
                             i,
                             sched_subframe,
                             0, // control_only_flag
                             phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->V_UL_DAI,
			     phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb>20 ? 1 : 0);
      }

#ifdef PHY_ABSTRACTION
      else {
        ret = ulsch_decoding_emul(phy_vars_eNB,
                                  sched_subframe,
                                  i,
                                  &rnti);
      }

#endif
      stop_meas(&phy_vars_eNB->ulsch_decoding_stats);

      LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d RNTI %x RX power (%d,%d) RSSI (%d,%d) N0 (%d,%d) dB ACK (%d,%d), decoding iter %d\n",
            phy_vars_eNB->Mod_id,harq_pid,
            frame,subframe,
            phy_vars_eNB->ulsch_eNB[i]->rnti,
            dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[0]),
            dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[1]),
            phy_vars_eNB->eNB_UE_stats[i].UL_rssi[0],
            phy_vars_eNB->eNB_UE_stats[i].UL_rssi[1],
            phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[0],
            phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[1],
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[0],
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[1],
            ret);


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

      //dump_ulsch(phy_vars_eNB, sched_subframe, i);

      phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[harq_pid][phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round]++;
#ifdef DEBUG_PHY_PROC
      LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d UE %d harq_pid %d Clearing subframe_scheduling_flag\n",
            phy_vars_eNB->Mod_id,harq_pid,frame,subframe,i,harq_pid);
#endif
      phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->subframe_scheduling_flag=0;

      if (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->cqi_crc_status == 1) {
#ifdef DEBUG_PHY_PROC
        //if (((phy_vars_eNB->proc[sched_subframe].frame_tx%10) == 0) || (phy_vars_eNB->proc[sched_subframe].frame_tx < 50))
        print_CQI(phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o,phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->uci_format,0,phy_vars_eNB->lte_frame_parms.N_RB_DL);
#endif
        extract_CQI(phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o,
                    phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->uci_format,
                    &phy_vars_eNB->eNB_UE_stats[i],
                    phy_vars_eNB->lte_frame_parms.N_RB_DL,
                    &rnti, &access_mode);
        phy_vars_eNB->eNB_UE_stats[i].rank = phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_RI[0];

      }

      if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1)
	VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_ULSCH_MSG3,0);

      if (ret == (1+MAX_TURBO_ITERATIONS)) {
        T(T_ENB_PHY_ULSCH_UE_NACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(i), T_INT(phy_vars_eNB->ulsch_eNB[i]->rnti),
          T_INT(harq_pid));

        phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors[harq_pid][phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round]++;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_active = 1;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_ACK = 0;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round++;

        LOG_D(PHY,"[eNB][PUSCH %d] Increasing to round %d\n",harq_pid,phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round);

        if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1) {
          LOG_D(PHY,"[eNB %d/%d][RAPROC] frame %d, subframe %d, UE %d: Error receiving ULSCH (Msg3), round %d/%d\n",
                phy_vars_eNB->Mod_id,
                phy_vars_eNB->CC_id,
                frame,subframe, i,
                phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round-1,
                phy_vars_eNB->lte_frame_parms.maxHARQ_Msg3Tx-1);

	  LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d RNTI %x RX power (%d,%d) RSSI (%d,%d) N0 (%d,%d) dB ACK (%d,%d), decoding iter %d\n",
		phy_vars_eNB->Mod_id,harq_pid,
		frame,subframe,
		phy_vars_eNB->ulsch_eNB[i]->rnti,
		dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[0]),
		dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[1]),
		phy_vars_eNB->eNB_UE_stats[i].UL_rssi[0],
		phy_vars_eNB->eNB_UE_stats[i].UL_rssi[1],
		phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[0],
		phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[1],
		phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[0],
		phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[1],
		ret);

          if (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round ==
              phy_vars_eNB->lte_frame_parms.maxHARQ_Msg3Tx) {
            LOG_D(PHY,"[eNB %d][RAPROC] maxHARQ_Msg3Tx reached, abandoning RA procedure for UE %d\n",
                  phy_vars_eNB->Mod_id, i);
            phy_vars_eNB->eNB_UE_stats[i].mode = PRACH;
	    if (phy_vars_eNB->mac_enabled==1) {
	      mac_xface->cancel_ra_proc(phy_vars_eNB->Mod_id,
					phy_vars_eNB->CC_id,
					frame,
					phy_vars_eNB->eNB_UE_stats[i].crnti);
	    }
            mac_phy_remove_ue(phy_vars_eNB->Mod_id,phy_vars_eNB->eNB_UE_stats[i].crnti);

            phy_vars_eNB->ulsch_eNB[(uint32_t)i]->Msg3_active = 0;
            //phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_active = 0;

          } else {
            // activate retransmission for Msg3 (signalled to UE PHY by PHICH (not MAC/DCI)
            phy_vars_eNB->ulsch_eNB[(uint32_t)i]->Msg3_active = 1;

            get_Msg3_alloc_ret(&phy_vars_eNB->lte_frame_parms,
                               subframe,
                               frame,
                               &phy_vars_eNB->ulsch_eNB[i]->Msg3_frame,
                               &phy_vars_eNB->ulsch_eNB[i]->Msg3_subframe);
          }
          LOG_D(PHY,"[eNB] Frame %d, Subframe %d: Msg3 in error, i = %d \n", frame,subframe,i);
        } // This is Msg3 error

        else { //normal ULSCH
          LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d UE %d Error receiving ULSCH, round %d/%d (ACK %d,%d)\n",
                phy_vars_eNB->Mod_id,harq_pid,
                frame,subframe, i,
                phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round-1,
                phy_vars_eNB->ulsch_eNB[i]->Mlimit,
                phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[0],
                phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->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,
            phy_vars_eNB->ulsch_eNB[i]->rnti,harq_pid,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round-1
            );
#endif

          if (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round== phy_vars_eNB->ulsch_eNB[i]->Mlimit) {
            LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d UE %d ULSCH Mlimit %d reached\n",
                  phy_vars_eNB->Mod_id,harq_pid,
                  frame,subframe, i,
                  phy_vars_eNB->ulsch_eNB[i]->Mlimit);

            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round=0;
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_active=0;
            phy_vars_eNB->eNB_UE_stats[i].ulsch_errors[harq_pid]++;
            phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors++;

	    // indicate error to MAC
	    mac_xface->rx_sdu(phy_vars_eNB->Mod_id,
			      phy_vars_eNB->CC_id,
			      frame,subframe,
			      phy_vars_eNB->ulsch_eNB[i]->rnti,
			      NULL,
			      0,
			      harq_pid,
			      &phy_vars_eNB->ulsch_eNB[i]->Msg3_flag);
          }
        }
      }  // ulsch in error
      else {
        T(T_ENB_PHY_ULSCH_UE_ACK, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(i), T_INT(phy_vars_eNB->ulsch_eNB[i]->rnti),
          T_INT(harq_pid));

        if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1) {
	  LOG_D(PHY,"[eNB %d][PUSCH %d] Frame %d subframe %d ULSCH received, setting round to 0, PHICH ACK\n",
		phy_vars_eNB->Mod_id,harq_pid,
		frame,subframe);
	  LOG_D(PHY,"[eNB %d][PUSCH %d] frame %d subframe %d RNTI %x RX power (%d,%d) RSSI (%d,%d) N0 (%d,%d) dB ACK (%d,%d), decoding iter %d\n",
		phy_vars_eNB->Mod_id,harq_pid,
		frame,subframe,
		phy_vars_eNB->ulsch_eNB[i]->rnti,
		dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[0]),
		dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[1]),
		phy_vars_eNB->eNB_UE_stats[i].UL_rssi[0],
		phy_vars_eNB->eNB_UE_stats[i].UL_rssi[1],
		phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[0],
		phy_vars_eNB->PHY_measurements_eNB->n0_power_dB[1],
		phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[0],
		phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[1],
		ret);
	}
#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,
          phy_vars_eNB->ulsch_eNB[i]->rnti,harq_pid
          );
#endif
        for (j=0; j<phy_vars_eNB->lte_frame_parms.nb_antennas_rx; j++)
          //this is the RSSI per RB
          phy_vars_eNB->eNB_UE_stats[i].UL_rssi[j] =
	    
            dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[i]->ulsch_power[j]*
                     (phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb*12)/
                     phy_vars_eNB->lte_frame_parms.ofdm_symbol_size) -
            phy_vars_eNB->rx_total_gain_eNB_dB -
            hundred_times_log10_NPRB[phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->nb_rb-1]/100 -
            get_hundred_times_delta_IF_eNB(phy_vars_eNB,i,harq_pid, 0)/100;
	    
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_active = 1;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->phich_ACK = 1;
        phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->round = 0;
        phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors = 0;

        if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 1) {
	  if (phy_vars_eNB->mac_enabled==1) {

	    LOG_I(PHY,"[eNB %d][RAPROC] Frame %d Terminating ra_proc for harq %d, UE %d\n",
		  phy_vars_eNB->Mod_id,
		  frame,harq_pid,i);
	    
	    mac_xface->rx_sdu(phy_vars_eNB->Mod_id,
			      phy_vars_eNB->CC_id,
			      frame,subframe,
			      phy_vars_eNB->ulsch_eNB[i]->rnti,
			      phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->b,
			      phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->TBS>>3,
			      harq_pid,
			      &phy_vars_eNB->ulsch_eNB[i]->Msg3_flag);
	    
	    // one-shot msg3 detection by MAC: empty PDU (e.g. CRNTI)
	    if (phy_vars_eNB->ulsch_eNB[i]->Msg3_flag == 0 ) {
	      phy_vars_eNB->eNB_UE_stats[i].mode = PRACH;
	      mac_xface->cancel_ra_proc(phy_vars_eNB->Mod_id,
					phy_vars_eNB->CC_id,
					frame,
					phy_vars_eNB->eNB_UE_stats[i].crnti);
	      mac_phy_remove_ue(phy_vars_eNB->Mod_id,phy_vars_eNB->eNB_UE_stats[i].crnti);
	      phy_vars_eNB->ulsch_eNB[(uint32_t)i]->Msg3_active = 0;
	    } // Msg3_flag == 0
	    
	  } // mac_enabled==1

          phy_vars_eNB->eNB_UE_stats[i].mode = PUSCH;
          phy_vars_eNB->ulsch_eNB[i]->Msg3_flag = 0;

	  LOG_D(PHY,"[eNB %d][RAPROC] Frame %d : RX Subframe %d Setting UE %d mode to PUSCH\n",phy_vars_eNB->Mod_id,frame,subframe,i);

          for (k=0; k<8; k++) { //harq_processes
            for (j=0; j<phy_vars_eNB->dlsch_eNB[i][0]->Mlimit; j++) {
              phy_vars_eNB->eNB_UE_stats[i].dlsch_NAK[k][j]=0;
              phy_vars_eNB->eNB_UE_stats[i].dlsch_ACK[k][j]=0;
              phy_vars_eNB->eNB_UE_stats[i].dlsch_trials[k][j]=0;
            }

            phy_vars_eNB->eNB_UE_stats[i].dlsch_l2_errors[k]=0;
            phy_vars_eNB->eNB_UE_stats[i].ulsch_errors[k]=0;
            phy_vars_eNB->eNB_UE_stats[i].ulsch_consecutive_errors=0;

            for (j=0; j<phy_vars_eNB->ulsch_eNB[i]->Mlimit; j++) {
              phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[k][j]=0;
              phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts_last[k][j]=0;
              phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors[k][j]=0;
              phy_vars_eNB->eNB_UE_stats[i].ulsch_round_fer[k][j]=0;
            }
          }

          phy_vars_eNB->eNB_UE_stats[i].dlsch_sliding_cnt=0;
          phy_vars_eNB->eNB_UE_stats[i].dlsch_NAK_round0=0;
          phy_vars_eNB->eNB_UE_stats[i].dlsch_mcs_offset=0;
        } // Msg3_flag==1
	else {  // Msg3_flag == 0

#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,phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->TBS>>3);

          for (j=0; j<phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->TBS>>3; j++)
            LOG_T(PHY,"%x.",phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->b[j]);

          LOG_T(PHY,"\n");
#endif
#endif

	  if (phy_vars_eNB->mac_enabled==1) {

	    mac_xface->rx_sdu(phy_vars_eNB->Mod_id,
			      phy_vars_eNB->CC_id,
			      frame,subframe,
			      phy_vars_eNB->ulsch_eNB[i]->rnti,
			      phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->b,
			      phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->TBS>>3,
			      harq_pid,
			      NULL);

#ifdef LOCALIZATION
	    start_meas(&phy_vars_eNB->localization_stats);
	    aggregate_eNB_UE_localization_stats(phy_vars_eNB,
						i,
						frame,
						subframe,
						get_hundred_times_delta_IF_eNB(phy_vars_eNB,i,harq_pid, 1)/100);
	    stop_meas(&phy_vars_eNB->localization_stats);
#endif
	    
	  } // mac_enabled==1
        } // Msg3_flag == 0

        // estimate timing advance for MAC
        if (abstraction_flag == 0) {
          sync_pos = lte_est_timing_advance_pusch(phy_vars_eNB,i,sched_subframe);
          phy_vars_eNB->eNB_UE_stats[i].timing_advance_update = sync_pos - phy_vars_eNB->lte_frame_parms.nb_prefix_samples/4; //to check
        }

#ifdef DEBUG_PHY_PROC
        LOG_D(PHY,"[eNB %d] frame %d, subframe %d: user %d: timing advance = %d\n",
              phy_vars_eNB->Mod_id,
              frame, subframe,
              i,
              phy_vars_eNB->eNB_UE_stats[i].timing_advance_update);
#endif


      }  // ulsch not in error

      // process HARQ feedback
#ifdef DEBUG_PHY_PROC
      LOG_D(PHY,"[eNB %d][PDSCH %x] Frame %d subframe %d, Processing HARQ feedback for UE %d (after PUSCH)\n",phy_vars_eNB->Mod_id,
            phy_vars_eNB->dlsch_eNB[i][0]->rnti,
            frame,subframe,
            i);
#endif
      process_HARQ_feedback(i,
                            sched_subframe,
                            phy_vars_eNB,
                            1, // pusch_flag
                            0,
                            0,
                            0);

#ifdef DEBUG_PHY_PROC
      LOG_D(PHY,"[eNB %d] Frame %d subframe %d, sect %d: received ULSCH harq_pid %d for UE %d, ret = %d, CQI CRC Status %d, ACK %d,%d, ulsch_errors %d/%d\n",
            phy_vars_eNB->Mod_id,frame,subframe,
            phy_vars_eNB->eNB_UE_stats[i].sector,
            harq_pid,
            i,
            ret,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->cqi_crc_status,
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[0],
            phy_vars_eNB->ulsch_eNB[i]->harq_processes[harq_pid]->o_ACK[1],
            phy_vars_eNB->eNB_UE_stats[i].ulsch_errors[harq_pid],
            phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[harq_pid][0]);
#endif
      
      // dump stats to VCD
      if (i==0) {
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_MCS0+harq_pid,phy_vars_eNB->pusch_stats_mcs[0][(frame*10)+subframe]);
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_RB0+harq_pid,phy_vars_eNB->pusch_stats_rb[0][(frame*10)+subframe]);
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_ROUND0+harq_pid,phy_vars_eNB->pusch_stats_round[0][(frame*10)+subframe]);
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_RSSI0+harq_pid,dB_fixed(phy_vars_eNB->lte_eNB_pusch_vars[0]->ulsch_power[0]));
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_RES0+harq_pid,ret);
	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_SFN0+harq_pid,(frame*10)+subframe);
      }
    } // ulsch_eNB[0] && ulsch_eNB[0]->rnti>0 && ulsch_eNB[0]->subframe_scheduling_flag == 1


    // update ULSCH statistics for tracing
    if ((frame % 100 == 0) && (subframe == 4)) {
      for (harq_idx=0; harq_idx<8; harq_idx++) {
        for (round=0; round<phy_vars_eNB->ulsch_eNB[i]->Mlimit; round++) {
          if ((phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[harq_idx][round] -
               phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts_last[harq_idx][round]) != 0) {
            phy_vars_eNB->eNB_UE_stats[i].ulsch_round_fer[harq_idx][round] =
              (100*(phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors[harq_idx][round] -
                    phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors_last[harq_idx][round]))/
              (phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[harq_idx][round] -
               phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts_last[harq_idx][round]);
          } else {
            phy_vars_eNB->eNB_UE_stats[i].ulsch_round_fer[harq_idx][round] = 0;
          }

          phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts_last[harq_idx][round] =
            phy_vars_eNB->eNB_UE_stats[i].ulsch_decoding_attempts[harq_idx][round];
          phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors_last[harq_idx][round] =
            phy_vars_eNB->eNB_UE_stats[i].ulsch_round_errors[harq_idx][round];
        }
      }
    }

    if ((frame % 100 == 0) && (subframe==4)) {
      phy_vars_eNB->eNB_UE_stats[i].dlsch_bitrate = (phy_vars_eNB->eNB_UE_stats[i].total_TBS -
          phy_vars_eNB->eNB_UE_stats[i].total_TBS_last);

      phy_vars_eNB->eNB_UE_stats[i].total_TBS_last = phy_vars_eNB->eNB_UE_stats[i].total_TBS;
    }

    // CBA (non-LTE)
    cba_procedures(sched_subframe,phy_vars_eNB,i,harq_pid,abstraction_flag);
  } // loop i=0 ... NUMBER_OF_UE_MAX-1

  if (abstraction_flag == 0) {
    lte_eNB_I0_measurements(phy_vars_eNB,
			    subframe,
			    0,
			    phy_vars_eNB->first_run_I0_measurements);
    phy_vars_eNB->first_run_I0_measurements = 0;
  }

#ifdef PHY_ABSTRACTION
    else {
      lte_eNB_I0_measurements_emul(phy_vars_eNB,
                                   0);
    }

#endif


    //}

#ifdef EMOS
  phy_procedures_emos_eNB_RX(subframe,phy_vars_eNB);
#endif

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_RX,0);
  stop_meas(&phy_vars_eNB->phy_proc_rx);

}

#undef DEBUG_PHY_PROC

#ifdef Rel10
int phy_procedures_RN_eNB_TX(unsigned char last_slot, unsigned char next_slot, relaying_type_t r_type)
{

  int do_proc=0;// do nothing

  switch(r_type) {
  case no_relay:
    do_proc= no_relay; // perform the normal eNB operation
    break;

  case multicast_relay:
    if (((next_slot >>1) < 6) || ((next_slot >>1) > 8))
      do_proc = 0; // do nothing
    else // SF#6, SF#7 and SF#8
      do_proc = multicast_relay; // do PHY procedures eNB TX

    break;

  default: // should'not be here
    LOG_W(PHY,"Not supported relay type %d, do nothing\n", r_type);
    do_proc=0;
    break;
  }

  return do_proc;
}
#endif
void phy_procedures_eNB_lte(unsigned char subframe,PHY_VARS_eNB **phy_vars_eNB,uint8_t abstraction_flag,
                            relaying_type_t r_type, PHY_VARS_RN *phy_vars_rn)
{
#if defined(ENABLE_ITTI)
  MessageDef   *msg_p;
  const char   *msg_name;
  instance_t    instance;
  unsigned int  Mod_id;
  int           result;
#endif


  int CC_id=0;

  /*
    if (phy_vars_eNB->proc[sched_subframe].frame_tx >= 1000)
    mac_xface->macphy_exit("Exiting after 1000 Frames\n");
  */
  VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_ENB, phy_vars_eNB[0]->proc[subframe].frame_tx);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_LTE,1);
  start_meas(&phy_vars_eNB[0]->phy_proc);

#if defined(ENABLE_ITTI)

  do {
    // Checks if a message has been sent to PHY sub-task
    itti_poll_msg (TASK_PHY_ENB, &msg_p);

    if (msg_p != NULL) {
      msg_name = ITTI_MSG_NAME (msg_p);
      instance = ITTI_MSG_INSTANCE (msg_p);
      Mod_id = instance;

      switch (ITTI_MSG_ID(msg_p)) {
#   if ENABLE_RAL

      case TIMER_HAS_EXPIRED:
        // check if it is a measurement timer
      {
        hashtable_rc_t       hashtable_rc;

        hashtable_rc = hashtable_is_key_exists(PHY_vars_eNB_g[Mod_id][0]->ral_thresholds_timed, (uint64_t)(TIMER_HAS_EXPIRED(msg_p).timer_id));

        if (hashtable_rc == HASH_TABLE_OK) {
          phy_eNB_lte_check_measurement_thresholds(instance, (ral_threshold_phy_t*)TIMER_HAS_EXPIRED(msg_p).arg);
        }
      }
      break;


      case PHY_MEAS_THRESHOLD_REQ:
#warning "TO DO LIST OF THRESHOLDS"
        LOG_D(PHY, "[ENB %d] Received %s\n", Mod_id, msg_name);
        {
          ral_threshold_phy_t* threshold_phy_p  = NULL;
          int                  index, res;
          long                 timer_id;
          hashtable_rc_t       hashtable_rc;

          switch (PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.th_action) {

          case RAL_TH_ACTION_CANCEL_THRESHOLD:
            break;

          case RAL_TH_ACTION_SET_NORMAL_THRESHOLD:
          case RAL_TH_ACTION_SET_ONE_SHOT_THRESHOLD:
            for (index = 0; index < PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.num_thresholds; index++) {
              threshold_phy_p                  = calloc(1, sizeof(ral_threshold_phy_t));
              threshold_phy_p->th_action       = PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.th_action;
              memcpy(&threshold_phy_p->link_param.link_param_type,
                     &PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.link_param_type,
                     sizeof(ral_link_param_type_t));

              memcpy(&threshold_phy_p->threshold,
                     &PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.thresholds[index],
                     sizeof(ral_threshold_t));

              switch (PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.union_choice) {

              case RAL_LINK_CFG_PARAM_CHOICE_TIMER_NULL:
                switch (PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.link_param_type.choice) {
                case RAL_LINK_PARAM_TYPE_CHOICE_GEN:
                  SLIST_INSERT_HEAD(
                    &PHY_vars_eNB_g[Mod_id][0]->ral_thresholds_gen_polled[PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.link_param_type._union.link_param_gen],
                    threshold_phy_p,
                    ral_thresholds);
                  break;

                case RAL_LINK_PARAM_TYPE_CHOICE_LTE:
                  SLIST_INSERT_HEAD(
                    &PHY_vars_eNB_g[Mod_id][0]->ral_thresholds_lte_polled[PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.link_param_type._union.link_param_lte],
                    threshold_phy_p,
                    ral_thresholds);
                  break;

                default:
                  LOG_E(PHY, "[ENB %d] BAD PARAMETER cfg_param.link_param_type.choice %d in %s\n",
                        Mod_id, PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.link_param_type.choice, msg_name);
                }

                break;

              case RAL_LINK_CFG_PARAM_CHOICE_TIMER:
                res = timer_setup(
                        (uint32_t)(PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param._union.timer_interval/1000),//uint32_t      interval_sec,
                        (uint32_t)(PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param._union.timer_interval%1000),//uint32_t      interval_us,
                        TASK_PHY_ENB,
                        instance,
                        TIMER_PERIODIC,
                        threshold_phy_p,
                        &timer_id);

                if (res == 0) {
                  hashtable_rc = hashtable_insert(PHY_vars_eNB_g[Mod_id][0]->ral_thresholds_timed, (uint64_t )timer_id, (void*)threshold_phy_p);

                  if (hashtable_rc == HASH_TABLE_OK) {
                    threshold_phy_p->timer_id = timer_id;
                  } else {
                    LOG_E(PHY, "[ENB %d]  %s: Error in hashtable. Could not configure threshold index %d \n",
                          Mod_id, msg_name, index);
                  }

                } else {
                  LOG_E(PHY, "[ENB %d]  %s: Could not configure threshold index %d because of timer initialization failure\n",
                        Mod_id, msg_name, index);
                }

                break;

              default: // already checked in RRC, should not happen here
                LOG_E(PHY, "[ENB %d] BAD PARAMETER cfg_param.union_choice %d in %s\n",
                      Mod_id, PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.union_choice, msg_name);
              }
            }

            break;

          default:
            LOG_E(PHY, "[ENB %d] BAD PARAMETER th_action value %d in %s\n",
                  Mod_id, PHY_MEAS_THRESHOLD_REQ(msg_p).cfg_param.th_action, msg_name);
          }

        }
        break;
#   endif

        /* Messages from eNB app */
      case PHY_CONFIGURATION_REQ:
        LOG_I(PHY, "[eNB %d] Received %s\n", instance, msg_name);
        /* TODO */

        break;

      default:
        LOG_E(PHY, "[ENB %d] Received unexpected message %s\n", Mod_id, msg_name);
        break;
      }

      result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
      AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
    }
  } while(msg_p != NULL);

#endif


  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    if ((((phy_vars_eNB[CC_id]->lte_frame_parms.frame_type == TDD)&&
          (subframe_select(&phy_vars_eNB[CC_id]->lte_frame_parms,phy_vars_eNB[CC_id]->proc[subframe].subframe_tx)==SF_DL))||
         (phy_vars_eNB[CC_id]->lte_frame_parms.frame_type == FDD))) {
#ifdef Rel10

      if (phy_procedures_RN_eNB_TX(phy_vars_eNB[CC_id]->proc[subframe].subframe_rx, phy_vars_eNB[CC_id]->proc[subframe].subframe_tx, r_type) != 0 )
#endif
        phy_procedures_eNB_TX(subframe,phy_vars_eNB[CC_id],abstraction_flag,r_type,phy_vars_rn);
    }

    if ((((phy_vars_eNB[CC_id]->lte_frame_parms.frame_type == TDD )&&
          (subframe_select(&phy_vars_eNB[CC_id]->lte_frame_parms,phy_vars_eNB[CC_id]->proc[subframe].subframe_rx)==SF_UL)) ||
         (phy_vars_eNB[CC_id]->lte_frame_parms.frame_type == FDD))) {
      phy_procedures_eNB_RX(subframe,phy_vars_eNB[CC_id],abstraction_flag,r_type);
    }

    if (subframe_select(&phy_vars_eNB[CC_id]->lte_frame_parms,phy_vars_eNB[CC_id]->proc[subframe].subframe_tx)==SF_S) {
#ifdef Rel10

      if (phy_procedures_RN_eNB_TX(subframe, subframe, r_type) != 0 )
#endif
        phy_procedures_eNB_TX(subframe,phy_vars_eNB[CC_id],abstraction_flag,r_type,phy_vars_rn);
    }

    if ((subframe_select(&phy_vars_eNB[CC_id]->lte_frame_parms,phy_vars_eNB[CC_id]->proc[subframe].subframe_rx)==SF_S)) {
      phy_procedures_eNB_S_RX(subframe,phy_vars_eNB[CC_id],abstraction_flag,r_type);
    }

    phy_vars_eNB[CC_id]->proc[subframe].frame_tx++;
    phy_vars_eNB[CC_id]->proc[subframe].frame_rx++;

    if (phy_vars_eNB[CC_id]->proc[subframe].frame_tx>=MAX_FRAME_NUMBER) // defined in impl_defs_top.h
      phy_vars_eNB[CC_id]->proc[subframe].frame_tx-=MAX_FRAME_NUMBER;

    if (phy_vars_eNB[CC_id]->proc[subframe].frame_rx>=MAX_FRAME_NUMBER)
      phy_vars_eNB[CC_id]->proc[subframe].frame_rx-=MAX_FRAME_NUMBER;
  }

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_ENB_LTE,0);
  stop_meas(&phy_vars_eNB[0]->phy_proc);
}