diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 0bc19f419507d41d54fd13d59c45535290c4a92b..e061da3ef287eb4934e45fcb535eb4532bc0bc5e 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -362,14 +362,14 @@ elseif (${RF_BOARD} STREQUAL "OAI_BLADERF")
     ${OPENAIR_TARGETS}/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c
     )
   LINK_DIRECTORIES("/usr/lib/x86_64-linux-gnu/")
-  set(option_HW_lib "bladeRF")
-
+  
 elseif (${RF_BOARD} STREQUAL "ETHERNET")
   include_directories ("${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB")
   set(HW_SOURCE ${HW_SOURCE}
-     ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c
-  )
-
+    ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c
+    )
+  set(LOWLATENCY True)
+  
 elseif (${RF_BOARD} STREQUAL "CPRIGW")
   set(HW_SOURCE ${HW_SOURCE}
     ${OPENAIR_TARGETS}/ARCH/CPRIGW/USERSPACE/LIB/cprigw_lib.c
@@ -393,7 +393,7 @@ add_boolean_option(OAI_NW_DRIVER_TYPE_ETHERNET False "????")
 add_boolean_option(DISABLE_USE_NAS False "???")
 add_boolean_option(ENABLE_STANDALONE_EPC True "Compile MME, SGW and PGW in a single executable")
 add_boolean_option(EPC_BUILD False "???")
-add_boolean_option(LOWLATENCY  True "Use the Linux scheduler SCHED_DEADLINE: kernel >= 3.14")
+add_boolean_option(LOWLATENCY True "Use the Linux scheduler SCHED_DEADLINE: kernel >= 3.14")
 add_boolean_option(NAS_ADDRESS_FIX False "specific to oaisim: for nasmesh driver")
 add_boolean_option(NAS_NETLINK False "???? Must be True to compile nasmesh driver without rtai")
 add_boolean_option(OAISIM False "specific to oaisim")
@@ -1567,6 +1567,8 @@ add_executable(lte-softmodem-nos1
   ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c
   ${OPENAIR_TARGETS}/SIMU/USER/init_lte.c
   ${OPENAIR_TARGETS}/COMMON/create_tasks.c
+  #${OPENAIR2_DIR}/RRC/NAS/nas_config.c # enable if you want rrc to mount ip interface
+  #${OPENAIR2_DIR}/RRC/NAS/rb_config.c
   ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c
   ${HW_SOURCE}
   ${RTAI_SOURCE}
@@ -1711,6 +1713,36 @@ target_link_libraries (oai_sgw
   )
 endif(MESSAGE_CHART_GENERATOR)
 
+
+# rrh
+################################
+set(DRIVER2013)
+
+#Note: only on RF type is currently supported for RRH
+add_executable(rrh_gw
+  ${OPENAIR_TARGETS}/RT/USER/rrh_gw.c
+  ${OPENAIR_TARGETS}/RT/USER/eNB_transport_IQ.c
+  ${OPENAIR_TARGETS}/RT/USER/UE_transport_IQ.c
+  ${OPENAIR_TARGETS}/RT/USER/rt_wrapper.c	
+  ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c 
+  ${HW_SOURCE}
+  )
+# assert and common_lib.h
+target_include_directories(rrh_gw PRIVATE  ${OPENAIR_DIR}/common/utils/itti ${OPENAIR_TARGETS}/ARCH/COMMON ${OPENAIR_TARGETS}/ARCH/ETHERNET/USERSPACE/LIB/ )
+
+target_link_libraries(rrh_gw
+  -Wl,--start-group
+  UTIL LFDS  
+  -Wl,--end-group )
+target_link_libraries (rrh_gw rt pthread m )
+target_link_libraries (rrh_gw ${option_HW_lib} ${LIBBOOST_LIBRARIES} )
+
+#Message("-- default_HW_lib=ETHERNET") # only in case of RRH
+Message("-- option_HW_lib=${option_HW_lib}")
+Message("-- HW_SOURCE=${HW_SOURCE}")
+
+
+
 # USIM process
 #################
 #add_executable(usim
diff --git a/cmake_targets/autotests/run_compilation_autotests.bash b/cmake_targets/autotests/run_compilation_autotests.bash
index 50ec7ee21c44c4e717c1265665a7387bd24b238f..228331ea8b30974163bad1b6a7d5ead75ded8f0a 100755
--- a/cmake_targets/autotests/run_compilation_autotests.bash
+++ b/cmake_targets/autotests/run_compilation_autotests.bash
@@ -34,7 +34,7 @@ mkdir -p $tdir/bin $tdir/log
 
 updated=$(svn st -q $OPENAIR_DIR)
 if [ "$updated" != "" ] ; then
-	echo_warning "some files are not in svn: $updated"
+	echo_warning "some files are not in svn:\n $updated"
 fi
 
 cd $tdir 
@@ -83,5 +83,9 @@ test_compile \
     test.0120 nasmesh \
     CMakeFiles/nasmesh/nasmesh.ko $tdir/bin/nasmesh.ko 
 
+test_compile \
+    test.0130 rrh_gw \
+    rrh_gw $tdir/bin/rrh_gw
+
 # write the test results into a file
 xUnit_write "$tdir/log/compilation_autotests.xml"
diff --git a/cmake_targets/autotests/test.0130/CMakeLists.txt b/cmake_targets/autotests/test.0130/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..45c99b01ab5c684a16661316a6fe9c69c1ec480a
--- /dev/null
+++ b/cmake_targets/autotests/test.0130/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8)
+set(ENABLE_VCD_FIFO False )
+set(ENABLE_ITTI False )
+set(RF_BOARD "ETHERNET")
+set(PACKAGE_NAME "\"rrh_gw\"")
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt)
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index ae5de617b1549becb2ebe82c6c806f1a0e8e8dd2..73924cee2a3a7a2796fcb9f7a43642bb891ef9bf 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -75,11 +75,13 @@ Options
    Makes the UE specific parts (ue_ip, usim, nvram) 
 --EPC
    Makes the EPC (MME-SPGW, HSS)
+--RRH
+   Makes the RRH
 -r | --3gpp-release
    default is Rel10,
    Rel8 limits the implementation to 3GPP Release 8 version
 -w | --hardware
-   EXMIMO (Default), USRP, BLADERF, None
+   EXMIMO (Default), USRP, BLADERF, ETHERNET, None
    Adds this RF board support (in external packages installation and in compilation)
 --oaisim
    Makes the oaisim simulator. Hardware will be defaulted to "NONE".
@@ -147,6 +149,10 @@ function main() {
             EPC=1
             echo_info "Will compile EPC"
             shift;;
+       --RRH)
+            RRH=1
+            echo_info "Will compile RRH"
+            shift;;
        -r | --3gpp-release)
             REL=$2
             echo_info "setting release to: $REL"
@@ -326,7 +332,7 @@ function main() {
 #    compilations \
 #      at_commands at_nas_ue \
 #      at_nas_ue $dbin/at_nas_ue
-      
+    
     [ "$CLEAN" = "1" ] && rm -rf $DIR/nas_sim_tools/build
     mkdir -p $DIR/nas_sim_tools/build
     cd $DIR/nas_sim_tools/build
@@ -497,6 +503,32 @@ function main() {
 	#  oaisim_mme $dbin/oaisim_mme.$REL
   fi
 
+  # RRH compilation
+  ##################
+  if [ "$RRH" = "1" ] ; then
+    echo_info "Compiling RRH"
+    if [  $HW == "ETHERNET" ] ; then 
+	echo_info "RF frontend for RRH is not defined. This mode is used for testing (loopback)."
+    elif [ $HW != "EXMIMO" -a $HW != "OAI_USRP" -a $HW != "OAI_BLADERF" ] ; then 
+	echo_fatal "Hardware not defined ($HW)"
+    fi
+    cmake_file=$DIR/rrh_gw/CMakeLists.txt
+    echo "cmake_minimum_required(VERSION 2.8)"   >   $cmake_file
+    echo "set(ENABLE_VCD_FIFO $VCD_TIMING )"     >>  $cmake_file
+    echo "set(ENABLE_ITTI False )"     					 >>  $cmake_file
+    echo "set(RF_BOARD \"${HW}\")"               >>  $cmake_file
+    echo 'set(PACKAGE_NAME "\"rrh_gw\"")' >>  $cmake_file
+    echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file
+
+    [ "$CLEAN" = "1" ] && rm -rf $DIR/rrh_gw/build
+    mkdir -p $DIR/rrh_gw/build
+    cd $DIR/rrh_gw/build
+    cmake ..
+    compilations \
+      rrh_gw rrh_gw \
+      rrh_gw $dbin/rrh_gw
+  fi
+
   # EPC compilation
   ##################
   if [ "$EPC" = "1" ] ; then
diff --git a/cmake_targets/rrh_gw/CMakeLists.txt b/cmake_targets/rrh_gw/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9ff1437f0c58db559a3a9386489a18cc1e2a596e
--- /dev/null
+++ b/cmake_targets/rrh_gw/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8)
+set(ENABLE_VCD_FIFO False )
+set(ENABLE_ITTI False )
+set(RF_BOARD "ETHERNET")
+set(PACKAGE_NAME "\"rrh_gw\"")
+include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)
diff --git a/openair1/PHY/TOOLS/time_meas.c b/openair1/PHY/TOOLS/time_meas.c
index c734db09af8f17d36cdfa811ba6b51cc898e5b85..675de01d31e7b1de429d5984995856cc032a1af2 100644
--- a/openair1/PHY/TOOLS/time_meas.c
+++ b/openair1/PHY/TOOLS/time_meas.c
@@ -39,10 +39,12 @@ double get_cpu_freq_GHz(void) {
 
   time_stats_t ts = {0};
   reset_meas(&ts);
-  start_meas(&ts);
+  ts.trials++;
+  ts.in = rdtsc_oai();
   sleep(1);
-  stop_meas(&ts);
+  ts.diff = (rdtsc_oai()-ts.in);
   cpu_freq_GHz = (double)ts.diff/1000000000;
+  printf("CPU Freq is %f \n", cpu_freq_GHz);
   return cpu_freq_GHz; 
 }
 
diff --git a/openair1/PHY/TOOLS/time_meas.h b/openair1/PHY/TOOLS/time_meas.h
index 2bd6dc489deb07e6924debcc4c3ce7aebc6115fc..1bb5212b55f32263f2d12431c50349b492e923a4 100644
--- a/openair1/PHY/TOOLS/time_meas.h
+++ b/openair1/PHY/TOOLS/time_meas.h
@@ -43,8 +43,8 @@ double cpu_freq_GHz;
 typedef struct {
 
   long long in;
-  long long diff_now;
   long long diff;
+  long long diff_now;
   long long p_time; /*!< \brief absolute process duration */
   long long diff_square; /*!< \brief process duration square */
   long long max;
@@ -113,38 +113,31 @@ static inline void stop_meas(time_stats_t *ts)
 
   if (opp_enabled) {
     long long out = rdtsc_oai();
-
-      ts->diff_now = (out-ts->in);
-      
-      ts->diff += (out-ts->in);
-      /// process duration is the difference between two clock points
-      ts->p_time = (out-ts->in);
-      ts->diff_square += (out-ts->in)*(out-ts->in);
-
-      if ((out-ts->in) > ts->max)
-        ts->max = out-ts->in;
-
+    
+    ts->diff_now = (out-ts->in);
+    
+    ts->diff_now = (out-ts->in);
+    ts->diff += (out-ts->in);
+    /// process duration is the difference between two clock points
+    ts->p_time = (out-ts->in);
+    ts->diff_square += (out-ts->in)*(out-ts->in);
+    
+    if ((out-ts->in) > ts->max)
+      ts->max = out-ts->in;
+    
   }
 }
 
-static inline void reset_meas(time_stats_t *ts)
-{
+static inline void reset_meas(time_stats_t *ts) {
+
   static int cpu_freq_set=0;
   
-  if (opp_enabled) {
-    ts->trials=0;
-    ts->diff_now=0;
-    ts->diff=0;
-    ts->p_time=0;
-    ts->diff_square=0;
-    ts->max=0;
-
-    if (cpu_freq_set == 0) {
-      cpu_freq_set = 1;
-      get_cpu_freq_GHz();
-      printf("CPU Freq is %f \n", cpu_freq_GHz);
-    } 
-  }
+  ts->trials=0;
+  ts->diff=0;
+  ts->diff_now=0;
+  ts->p_time=0;
+  ts->diff_square=0;
+  ts->max=0;
   
 }
 
diff --git a/openair2/UTIL/LOG/log.c b/openair2/UTIL/LOG/log.c
index 1ae3b720f2691cd9ce4b34cffe76373b7e8552d4..283a906b5885ec72a5750f054cb0ba882765ed5e 100755
--- a/openair2/UTIL/LOG/log.c
+++ b/openair2/UTIL/LOG/log.c
@@ -398,7 +398,15 @@ int logInit (void)
   g_log->log_component[SCTP].fd = 0;
   g_log->log_component[SCTP].filelog = 0;
   g_log->log_component[SCTP].filelog_name = "";
-
+ 
+  g_log->log_component[RRH].name = "RRH";
+  g_log->log_component[RRH].level = LOG_EMERG;
+  g_log->log_component[RRH].flag = LOG_MED;
+  g_log->log_component[RRH].interval = 1;
+  g_log->log_component[RRH].fd = 0;
+  g_log->log_component[RRH].filelog = 0;
+  g_log->log_component[RRH].filelog_name = "";
+  
   g_log->level2string[LOG_EMERG]         = "G"; //EMERG
   g_log->level2string[LOG_ALERT]         = "A"; // ALERT
   g_log->level2string[LOG_CRIT]          = "C"; // CRITIC
diff --git a/openair2/UTIL/LOG/log.h b/openair2/UTIL/LOG/log.h
index 9de693fa92fbaf70414f4b41750e628284534b43..724504656f9a6570c5138cd65573f6c977e8937e 100755
--- a/openair2/UTIL/LOG/log.h
+++ b/openair2/UTIL/LOG/log.h
@@ -267,6 +267,7 @@ typedef enum {
   TMR,
   USIM,
   LOCALIZE,
+  RRH,
   MAX_LOG_COMPONENTS,
 }
 comp_name_t;
diff --git a/openair2/UTIL/LOG/vcd_signal_dumper.c b/openair2/UTIL/LOG/vcd_signal_dumper.c
index 3775b7e1dc06f939c018ac7e582cec0e2bfc8ce1..62b380fddbd41f0b3db1b062940d6db6ec76f9bf 100644
--- a/openair2/UTIL/LOG/vcd_signal_dumper.c
+++ b/openair2/UTIL/LOG/vcd_signal_dumper.c
@@ -77,6 +77,8 @@ struct vcd_module_s {
 const char* eurecomVariablesNames[] = {
   "frame_number_TX_eNB",
   "frame_number_RX_eNB",
+  "runtime_TX_eNB",
+  "runtime_RX_eNB",
   "frame_number_TX_UE",
   "frame_number_RX_UE",
   "slot_number_TX_UE",
@@ -94,6 +96,13 @@ const char* eurecomVariablesNames[] = {
   "rxcnt",
   "trx_ts",
   "trx_tst",
+  "tx_ts",
+  "rx_ts",
+  "hw_cnt_rx",
+  "lhw_cnt_rx",
+  "hw_cnt_tx",
+  "lhw_cnt_tx",
+  "pck_rx",
   "dummy_dump",
   "itti_send_msg",
   "itti_poll_msg",
@@ -133,6 +142,15 @@ const char* eurecomFunctionsNames[] = {
   "ue_thread_tx",
   "ue_thread_rx",
 
+ /* RRH signals  */ 
+  "eNB_tx",
+  "eNB_rx",
+  "eNB_trx",
+  "eNB_tm",
+  "eNB_rx_sleep",
+  "eNB_tx_sleep",
+  "eNB_proc_sleep",
+
   /* PHY signals  */
   "ue_synch",
   "ue_slot_fep",
diff --git a/openair2/UTIL/LOG/vcd_signal_dumper.h b/openair2/UTIL/LOG/vcd_signal_dumper.h
index 2d8560d615e642bd494cb20cb815daf691c141a5..825b05d53576f30a03bfa9353e84a5c453a037f6 100644
--- a/openair2/UTIL/LOG/vcd_signal_dumper.h
+++ b/openair2/UTIL/LOG/vcd_signal_dumper.h
@@ -49,6 +49,8 @@
 typedef enum {
   VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_ENB = 0,
   VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX_ENB,
+  VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB,
+  VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RX_ENB,
   VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX_UE,
   VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX_UE,
   VCD_SIGNAL_DUMPER_VARIABLES_SLOT_NUMBER_TX_UE,
@@ -66,6 +68,13 @@ typedef enum {
   VCD_SIGNAL_DUMPER_VARIABLES_RXCNT,
   VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS,
   VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST,
+  VCD_SIGNAL_DUMPER_VARIABLES_TX_TS,
+  VCD_SIGNAL_DUMPER_VARIABLES_RX_TS,
+  VCD_SIGNAL_DUMPER_VARIABLES_RX_HWCNT,
+  VCD_SIGNAL_DUMPER_VARIABLES_RX_LHWCNT,
+  VCD_SIGNAL_DUMPER_VARIABLES_TX_HWCNT,
+  VCD_SIGNAL_DUMPER_VARIABLES_TX_LHWCNT,
+  VCD_SIGNAL_DUMPER_VARIABLES_RX_PCK,
   VCD_SIGNAL_DUMPER_VARIABLES_DUMMY_DUMP,
   VCD_SIGNAL_DUMPER_VARIABLE_ITTI_SEND_MSG,
   VCD_SIGNAL_DUMPER_VARIABLE_ITTI_POLL_MSG,
@@ -107,6 +116,16 @@ typedef enum {
   VCD_SIGNAL_DUMPER_FUNCTIONS_UE_THREAD_TX,
   VCD_SIGNAL_DUMPER_FUNCTIONS_UE_THREAD_RX,
 
+  /* RRH signals  */
+ 
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP,
+  VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_SLEEP,
+
   /* PHY signals  */
   VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SYNCH,
   VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP,
diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c
index bbb8463714c7696124114636feb1bf098e5a537b..72cc76984b8212cbf5bcf2ffc581bc48cb715144 100644
--- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c
+++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c
@@ -132,7 +132,7 @@ int trx_brf_end(openair0_device *device) {
   return 0;
 }
 
-//int openair0_device_brf_init(openair0_device *device, openair0_config_t *openair0_cfg) {
+//int  openair0_dev_init_bladerf(openair0_device *device, openair0_config_t *openair0_cfg) {
 int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
 
   int status;
@@ -263,15 +263,27 @@ int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cf
   return 0;
 }
 
-void brf_error(int status) {
+int brf_error(int status) {
   
   exit(-1);
+  //return 1; // or status error code
 }
 
 int openair0_stop(int card) {
 
   return(0);
 
+}
+
+int openair0_print_stats(openair0_device* device) {
+
+  return(0);
+
+}
+int openair0_reset_stats(openair0_device* device) {
+
+  return(0);
+
 }
 int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int dummy) {
   return 0;
diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h
index 7a73352e71a1f1a8ba278d52692e05cffd60fa5b..52b5ca84537cc6b0689b387211b5963df53bc3a5 100644
--- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h
+++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h
@@ -78,4 +78,4 @@ typedef struct {
  * func prototypes 
  */
 
-void brf_error(int status);
+int brf_error(int status);
diff --git a/targets/ARCH/COMMON/common_lib.h b/targets/ARCH/COMMON/common_lib.h
index dc8e508c18befe023c25e1dae7c8d9b385059b69..9560117b2ea5fc4faa10ca7ec5ae29da4a163ad3 100644
--- a/targets/ARCH/COMMON/common_lib.h
+++ b/targets/ARCH/COMMON/common_lib.h
@@ -26,16 +26,23 @@
    Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
 
  *******************************************************************************/
+/*! \file common_lib.h 
+ * \brief common APIs for different RF frontend device 
+ * \author HongliangXU, Navid Nikaein
+ * \date 2015
+ * \version 0.2
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning
+ */
 
-/** common_lib.h
-*
-* Author: HongliangXU : hong-liang-xu@agilent.com
-*/
 #ifndef COMMON_LIB_H
 #define COMMON_LIB_H
 #include <stdint.h>
 
 typedef int64_t openair0_timestamp;
+typedef volatile int64_t openair0_vtimestamp;
 typedef struct openair0_device_t openair0_device;
 /* structrue holds the parameters to configure USRP devices
  */
@@ -56,10 +63,18 @@ typedef struct {
   int Mod_id;
   // device log level
   int log_level;
+  //! number of downlink resource blocks
+  int num_rb_dl;
+  //! number of samples per frame 
+  unsigned int  samples_per_frame;
   //! the sample rate for both transmit and receive.
   double sample_rate;
   //! number of samples per RX/TX packet (USRP + Ethernet)
   int samples_per_packet;
+  // delay in sending samples (write)  due to hardware access, softmodem processing and fronthaul delay if exist
+  int   	tx_delay;
+  //! adjust the position of the samples after delay when sending   
+  unsigned int	tx_forward_nsamps;
   //! number of RX channels (=RX antennas)
   int rx_num_channels;
   //! number of TX channels (=TX antennas)
@@ -84,9 +99,14 @@ typedef struct {
   //! Auto calibration flag
   int autocal[4];
   //! RRH IP addr for Ethernet interface
-  char *rrh_ip;
+  char *remote_ip;
   //! RRH port number for Ethernet interface
-  int rrh_port;
+  int remote_port;
+  //! my IP addr for Ethernet interface (eNB/BBU, UE)
+  char *my_ip;
+  //! my port number for Ethernet interface (eNB/BBU, UE)
+  int my_port;
+
 } openair0_config_t;
 
 typedef struct {
@@ -98,10 +118,42 @@ typedef struct {
 
 
 
+/*!\brief device type */
+typedef enum {
+  MIN_DEV_TYPE = 0,
+  /*!\brief device is ETH */
+  ETH_IF,
+  /*!\brief device is ExpressMIMO */
+  EXMIMO_IF,
+  /*!\brief device is USRP*/
+  USRP_IF,
+  /*!\brief device is BLADE RF*/
+  BLADERF_IF,
+  /*!\brief device is NONE*/
+  NONE_IF,
+  MAX_DEV_TYPE
+
+} dev_type_t;
+
+/*!\brief  type */
+typedef enum {
+  MIN_FUNC_TYPE = 0,
+  BBU_FUNC,
+  RRH_FUNC,
+  MAX_FUNC_TYPE
+
+}func_type_t;
+
 struct openair0_device_t {
   /* Module ID of this device */
   int Mod_id;
 
+  /* Type of this device */
+  func_type_t func_type;
+  
+  /* Type of this device */
+  dev_type_t type;
+
   /* RF frontend parameters set by application */
   openair0_config_t openair0_cfg;
 
@@ -113,11 +165,20 @@ struct openair0_device_t {
   /* Called to start the transceiver. Return 0 if OK, < 0 if error */
   int (*trx_start_func)(openair0_device *device);
 
+  /* Called to initiate transceiver threads */
+  void (*trx_thread_func)(openair0_device *device, unsigned int rt_period, uint8_t RT_flag,uint8_t NRT_flag);
+    
+  /* Called to request connection from the transceiver/RRH. Return 0 if OK, < 0 if error */
+  int (*trx_request_func)(openair0_device *device);
+
+  /* Called to reply back to connection state to eNB/BBU. Return 0 if OK, < 0 if error */
+  int (*trx_reply_func)(openair0_device *openair0);
+
   /* Write 'nsamps' samples on each channel from buffers. buff[0] is the array for
    * the first channel. timestamp if the time (in samples) at which the first sample
    * MUST be sent
    * use flags = 1 to send as timestamp specfied*/
-  void (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags);
+  int (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id, int flags);
 
   /*! \brief Receive samples from hardware.
    * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
@@ -130,7 +191,19 @@ struct openair0_device_t {
    * \param cc Number of channels. If cc == 1, only buff[0] is filled with samples.
    * \returns the number of sample read
    */
-  int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int cc);
+  int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int antenna_id);
+
+  /*! \brief print the device statistics  
+   * \param device the hardware to use
+   * \returns  0 on success
+   */
+  int (*trx_get_stats_func)(openair0_device *device);
+
+  /*! \brief Reset device statistics  
+   * \param device the hardware to use
+   * \returns 0 in success 
+   */
+  int (*trx_reset_stats_func)(openair0_device *device);
 
   /* Terminate operation of the transceiver -- free all associated resources */
   void (*trx_end_func)(openair0_device *device);
@@ -142,15 +215,33 @@ extern "C"
 {
 #endif
 
-/* return 0 if OK, < 0 if error */
-  int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg);
-  openair0_timestamp get_usrp_time(openair0_device *device);
-  int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config);
-  int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg);
-  
-  int openair0_set_gains(openair0_device* device, openair0_config_t *openair0_cfg);
-  
-  int openair0_stop(int card);
+/* return 0 if OK */
+
+int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg);
+openair0_timestamp get_usrp_time(openair0_device *device);
+
+//EXMIMO
+//int openair0_dev_init_exmimo(openair0_device *device, openair0_config_t *openair0_cfg);
+//USPRP
+//int openair0_dev_init_usrp(openair0_device* device, openair0_config_t *openair0_cfg);
+//BLADERF
+//int openair0_dev_init_bladerf(openair0_device* device, openair0_config_t *openair0_cfg);
+
+//ETHERNET
+int openair0_dev_init_eth(openair0_device *device, openair0_config_t *openair0_cfg);
+
+int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config);
+
+int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg);
+
+int openair0_set_gains(openair0_device* device, openair0_config_t *openair0_cfg);
+
+int openair0_print_stats(openair0_device* device);
+
+int openair0_reset_stats(openair0_device* device);
+
+int openair0_stop(int card);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c
index df8b150502b16d4298c0362a607f6ac7ca5d4d0b..ae5c540dc4efe2f9c4f0c0cce97d883d3a67511b 100644
--- a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c
+++ b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.c
@@ -26,16 +26,16 @@
    Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
 
  *******************************************************************************/
-
-/** ethernet_lib : API to stream I/Q samples over standard ethernet
-*
-*  Authors: Pedro Dinis    <pedrodinis20@gmail.com>
-*           Lucio Ferreira <lucio.ferreira@inov.pt>
-*           Raymond Knopp  <raymond.knopp@eurecom.fr>
-*
-*  Changelog:
-*  06.10.2014: Initial version
-*/
+/*! \fileethernet_lib.c 
+ * \brief API to stream I/Q samples over standard ethernet
+ * \author Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp
+ * \date 2015
+ * \version 0.2
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning 
+ */
 
 #include <arpa/inet.h>
 #include <linux/if_packet.h>
@@ -50,203 +50,361 @@
 #include <errno.h>
 
 #include "common_lib.h"
+#include "ethernet_lib.h"
 
 #define DEFAULT_IF  "eth0"
-#define BUF_SIZ   8960 /*Jumbo frame size*/
+#define BUF_SIZ      8960 /*Jumbo frame size*/
 #define MAX_INST        4
 
-int sockfd[MAX_INST];
-struct sockaddr_in dest_addr[MAX_INST];
+//int sockfd[MAX_INST];
+int num_devices = 0;
 int dest_addr_len[MAX_INST];
-
-int i;
-int tx_len = 0;
 char sendbuf[MAX_INST][BUF_SIZ]; /*TODO*/
 
 
-/**PDF: Initialization of UDP Socket to communicate with one DEST */
-int ethernet_socket_init(int Mod_id, char *dest_ip,int dest_port)
-{
+/* Initialization of UDP Socket to communicate with one destination */
+int ethernet_socket_init(openair0_device *device) {
 
-  /**PDF: To be passed by input argument*/
-  //  DEST_port = 32000;
-  struct sockaddr_in *dest = &dest_addr[Mod_id];
+  int i = 0;
+  eth_state_t *eth = (eth_state_t*)device->priv;
+  int Mod_id = device->Mod_id;
+  
+//  struct sockaddr_in *dest = &dest_addr[Mod_id];
   char str[INET_ADDRSTRLEN];
-
+  const char *dest_ip;
+  int dest_port;
+  
+  if (device->func_type == RRH_FUNC ){
+    dest_ip   = device->openair0_cfg.my_ip;   
+    dest_port = device->openair0_cfg.my_port;
+    printf("[RRH] ip addr %s port %d\n",dest_ip, dest_port);
+  } else {
+    dest_ip   = device->openair0_cfg.remote_ip;
+    dest_port = device->openair0_cfg.remote_port;
+    printf("[BBU] ip addr %s port %d\n",dest_ip, dest_port);
+  }
+    
   /* Open RAW socket to send on */
-
-  if ((sockfd[Mod_id] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+  if ((eth->sockfd[Mod_id] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
     perror("ETHERNET: Error opening socket");
     exit(0);
   }
 
-  bzero((void *)dest,sizeof(struct sockaddr));
-  dest->sin_family = AF_INET;
-  dest->sin_addr.s_addr=inet_addr(dest_ip);
-  dest->sin_port=htons(dest_port);
-  dest_addr_len[Mod_id] = sizeof(struct sockaddr);
+  /* initialize destination address */
+  for (i=0; i< MAX_INST; i++)  
+    bzero((void *)&(eth->dest_addr[i]), sizeof(eth->dest_addr[i]));
+
+ // bzero((void *)dest,sizeof(struct sockaddr_in));
+  eth->dest_addr[Mod_id].sin_family = AF_INET;
+  inet_pton(AF_INET,dest_ip,&(eth->dest_addr[Mod_id].sin_addr.s_addr));
+  eth->dest_addr[Mod_id].sin_port=htons(dest_port);
+  dest_addr_len[Mod_id] = sizeof(struct sockaddr_in);
+  inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
+  
+  /* if RRH, then I am the server, so bind */
+  if (device->func_type == RRH_FUNC ){    
+    if (bind(eth->sockfd[Mod_id],(struct sockaddr *)&eth->dest_addr[Mod_id], dest_addr_len[Mod_id])<0) {
+      perror("ETHERNET: Cannot bind to socket");
+      exit(0);
+    }else {
+      printf("[RRH] binding mod_%d to %s:%d\n",Mod_id,str,ntohs(eth->dest_addr[Mod_id].sin_port));
+    }
+    
+  }else {
+    printf("[BBU] Connecting to %s:%d\n",str,ntohs(eth->dest_addr[Mod_id].sin_port));
+  }
+  
+  return 0;
+}
 
-  inet_ntop(AF_INET, &(dest->sin_addr), str, INET_ADDRSTRLEN);
-  printf("Connecting to %s:%d\n",str,ntohs(dest->sin_port));
+void ethernet_socket_opt (openair0_device *device){
+
+  eth_state_t *eth = (eth_state_t*)device->priv;
+  int Mod_id=device->Mod_id;
+  
+  int sndbuf_size=0, rcvbuf_size=0;      
+  socklen_t optlen;    
+  /* chang the MTU of the eth interface */ 
+  struct ifreq ifr; 
+  
+  optlen = sizeof(int);  
+  
+  if (getsockopt(eth->sockfd[Mod_id],  
+                 SOL_SOCKET,  
+                 SO_SNDBUF,  
+                 &sndbuf_size,&optlen))   
+    printf("error:getsockopt()\n");  
+  
+  if (getsockopt(eth->sockfd[Mod_id],  
+                 SOL_SOCKET,  
+                 SO_RCVBUF,  
+                 &rcvbuf_size,&optlen))
+    printf("error:getsockopt()\n"); 
+
+  printf( "sndbuf_size= %d bytes rcvbuf_size= %d bytes\n", sndbuf_size,
+	  rcvbuf_size); 
+
+  ifr.ifr_addr.sa_family = AF_INET;
+  //iap->ifa_name is bond1:xx
+  strncpy(ifr.ifr_name, DEFAULT_IF, sizeof(ifr.ifr_name));
+  ifr.ifr_mtu = device->openair0_cfg.samples_per_packet*5; 
+  if (ioctl(eth->sockfd[Mod_id], SIOCSIFMTU, (caddr_t)&ifr)  < 0 )
+    perror ("Can't set the MTU");
+  else 
+    printf("[ETHERNET] %s MTU size has changed to %d\n",DEFAULT_IF,ifr.ifr_mtu);
+  
+}
 
 
+int trx_eth_request(openair0_device *device) {
+
+  int 	       Mod_id = device->Mod_id;
+  eth_state_t *eth = (eth_state_t*)device->priv;
+  void 	      *msg;
+  ssize_t     msg_len;
+
+  
+  /* send to RRH (server) required configuration parameters:
+     -number of downlink RBs (so that frame/packet size can be set accordingly)
+     -rx_num_0channels
+     -tx_num_channels
+     -rx_freq
+     -tx_freq
+     -rxg_mode[4]	
+     -rx_gain
+     -tx_gain
+     -rx_bw
+     -tx_bw
+     -autocal */ 
+  
+  msg=malloc(sizeof(openair0_config_t));
+  msg_len=sizeof(openair0_config_t);
+  memcpy(msg,(void*)&device->openair0_cfg,msg_len);	
+  
+  if (sendto(eth->sockfd[Mod_id],msg,msg_len,0,(struct sockaddr *)&eth->dest_addr[Mod_id],dest_addr_len[Mod_id])==-1){
+    perror("ETHERNET: ");
+    exit(0);
+  }
+     
+  return 0;
 }
 
 
 
-int ethernet_write_data(int Mod_id, openair0_timestamp timestamp, const void **buff, int antenna_id, int nsamps)
-{
+int trx_eth_reply(openair0_device *device) {
 
-  void *buff2 = (void*)buff[antenna_id]-sizeof(openair0_timestamp)-(sizeof(int16_t)*2);
-  int32_t temp0 = *(int32_t *)buff2;
-  openair0_timestamp temp1 = *(openair0_timestamp *)(buff2+(sizeof(int16_t)*2));
+  eth_state_t   *eth = (eth_state_t*)device->priv;
+  int 		Mod_id = device->Mod_id;
+  char 		str[INET_ADDRSTRLEN];
+  void 		*msg;
+  ssize_t	msg_len;
+  
+  msg=malloc(sizeof(openair0_config_t));
+  msg_len=sizeof(openair0_config_t);
 
-  int n_written;
+  /* receive from client (lte-softmodem)  */
+  if (recvfrom(eth->sockfd[Mod_id],
+	       msg,
+	       msg_len,
+	       0,
+	       (struct sockaddr *)&eth->dest_addr[Mod_id],
+	       (socklen_t *)&dest_addr_len[Mod_id])==-1){
+    perror("ETHERNET: ");
+    exit(0);
+  }
+		
+   memcpy((void*)&device->openair0_cfg,msg,msg_len);	
+   inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
+   device->openair0_cfg.remote_port =ntohs(eth->dest_addr[Mod_id].sin_port);
+   device->openair0_cfg.remote_ip=str;
+   //ethernet_socket_opt (device);
+   printf("[RRH] write mod_%d %d to %s:%d\n",Mod_id,eth->sockfd[Mod_id],str,ntohs(eth->dest_addr[Mod_id].sin_port));
+
+   return 1;
+}
 
-  n_written = 0;
-  ((int16_t *)buff2)[0] = 1+(antenna_id<<1);
-  ((int16_t *)buff2)[1] = nsamps;
-  *((openair0_timestamp *)(buff2+(sizeof(int16_t)*2))) = timestamp;
-  //  printf("Timestamp TX sent : %d\n",timestamp);
 
-  //  printf("buffer head : %d %d %d %d \n",((int16_t *)buff2)[0],((int16_t *)buff2)[1],((int16_t *)buff2)[2],((int16_t *)buff2)[3]);
-  while(n_written < nsamps) {
+int ethernet_write_data(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id) {	
+	
+  int n_written;
+  uint16_t header_size=sizeof(int32_t) + sizeof(openair0_timestamp);
+  eth_state_t *eth = (eth_state_t*)device->priv;
+  int Mod_id = device->Mod_id;
+  int sendto_flag =0;
+  sendto_flag|=MSG_DONTWAIT;
+	
+  /* buff[antenna_id] points to the position in tx buffer where the payload to be sent is
+     buff2 points to the position in tx buffer where the packet header will be placed */
+  void *buff2 = (void*)(buff[antenna_id]-header_size); // (void*)((unsigned char *)buff[antenna_id]-header_size);
+
+  /* we don't want to ovewrite with the header info the previous tx buffer data so we store it*/
+  int32_t temp0 = *(int32_t *)buff2;
+  openair0_timestamp  temp1 = *(openair0_timestamp *)(buff2 + sizeof(int32_t));
+  //printf("temp0=%d temp=%d\n",temp0,temp1);
+  //char str[INET_ADDRSTRLEN];
+  
+  n_written = 0;
+  
+  *(int16_t *)(buff2 + sizeof(int16_t))=1+(antenna_id<<1);
+  *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = timestamp;
+	
+  //inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
+  //printf("[RRH]write mod_%d %d to %s:%d, len %d, buff %p antenna %d\n",
+  //Mod_id,eth->sockfd[Mod_id],str,ntohs(eth->dest_addr[Mod_id].sin_port),(nsamps<<2)+header_size, buff2, antenna_id);
+	
+while(n_written < nsamps) {
     /* Send packet */
-    if ((n_written += sendto(sockfd[Mod_id],
-                             buff2,
-                             (nsamps<<2)+sizeof(openair0_timestamp)+(2*sizeof(int16_t)),
-                             0,
-                             (struct sockaddr*)&dest_addr[Mod_id],
-                             dest_addr_len[Mod_id])) < 0) {
-      printf("Send failed for Mod_id %d\n",Mod_id);
-      perror("ETHERNET:");
-      exit(-1);
+  if ((n_written += sendto(eth->sockfd[Mod_id],
+			   buff2, 
+                           (nsamps<<2)+header_size,
+                           0,
+                           (struct sockaddr*)&eth->dest_addr[Mod_id],
+                           dest_addr_len[Mod_id])) < 0) {
+    perror("ETHERNET WRITE");
+    exit(-1);
     }
   }
 
+/* printf("Buffer head TX: nu=%d an_id=%d ts%d byte_send=%d \n",		*(int16_t *)buff2,
+  *(int16_t *)(buff2 + sizeof(int16_t)),
+  *(openair0_timestamp *)(buff2 + sizeof(int32_t)),
+  n_written>>2);*/
+  
+  /* tx buffer values restored */  
   *(int32_t *)buff2 = temp0;
-  *(openair0_timestamp *)(buff2+2*sizeof(int16_t)) = temp1;
+  *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1;
+  //printf("-----------------temp0=%d temp=%d\n",*(int32_t *)buff2,*(openair0_timestamp *)(buff2 + sizeof(int32_t)));
   return n_written;
 
 }
 
 
-int ethernet_read_data(int Mod_id,openair0_timestamp *timestamp,void **buff, int antenna_id, int nsamps)
-{
+int ethernet_read_data(openair0_device *device,openair0_timestamp *timestamp,void **buff, int nsamps,int antenna_id) {
 
-  void *buff2 = (void*)buff[antenna_id]-sizeof(openair0_timestamp);
   int bytes_received;
   int block_cnt;
   int ret;
-  openair0_timestamp temp = *(openair0_timestamp*)(buff2);
-  int16_t mesg[2];
   char str[INET_ADDRSTRLEN];
-
-  //mesg[0] = 0+(antenna_id<<1);
-  //mesg[1] = nsamps;
-
-  inet_ntop(AF_INET, &(dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
+  uint16_t  header_size=sizeof(int32_t) + sizeof(openair0_timestamp);
+
+  
+  /* buff[antenna_id] points to the position in rx buffer where the payload to be received will be placed
+     buff2 points to the position in rx buffer where the packet header will be placed */
+  void *buff2 = (void*)(buff[antenna_id]-header_size);
+  
+  /* we don't want to ovewrite with the header info the previous rx buffer data so we store it*/
+  int32_t temp0 = *(int32_t *)buff2;
+  openair0_timestamp temp1 = *(openair0_timestamp *)(buff2 + sizeof(int32_t));
+  
+  eth_state_t *eth = (eth_state_t*)device->priv;
+  int Mod_id = device->Mod_id;
+  
+  inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
   // send command RX for nsamps samples
-  //  printf("requesting %d samples from (%s:%d)\n",nsamps,str,ntohs(dest_addr[Mod_id].sin_port));
-
-  //sendto(sockfd[Mod_id],mesg,4,0,(struct sockaddr *)&dest_addr[Mod_id],dest_addr_len[Mod_id]);
+  // printf("requesting %d samples from (%s:%d)\n",nsamps,str,ntohs(eth->dest_addr[Mod_id].sin_port));
 
   bytes_received=0;
   block_cnt=0;
 
   while(bytes_received < (int)((nsamps<<2))) {
     //printf("requesting %d bytes\n",(nsamps<<2));
-    ret=recvfrom(sockfd[Mod_id],
+    ret=recvfrom(eth->sockfd[Mod_id],
                  buff2+bytes_received,
-                 (nsamps<<2)+sizeof(openair0_timestamp)-bytes_received,
+                 (nsamps<<2)+header_size-bytes_received,
                  0,//MSG_DONTWAIT,
-                 (struct sockaddr *)&dest_addr[Mod_id],
-                 &dest_addr_len[Mod_id]);
+                 (struct sockaddr *)&eth->dest_addr[Mod_id],
+                 (socklen_t *)&dest_addr_len[Mod_id]);
 
     //printf("bytes_received %d (ret %d)\n",bytes_received+ret,ret);
     if (ret==-1) {
       if (errno == EAGAIN) {
-        perror("ETHERNET: ");
-        return((nsamps<<2)+sizeof(openair0_timestamp));
+        perror("ETHERNET READ: ");
+        return((nsamps<<2) + header_size);
       } else if (errno == EWOULDBLOCK) {
         block_cnt++;
         usleep(10);
-
+	
         if (block_cnt == 100) return(-1);
       }
     } else {
       bytes_received+=ret;
     }
   }
-
-
-  //printf("buffer head : %x %x %x %x \n",((int32_t *)buff2)[0],((int32_t *)buff2)[1],((int32_t *)buff2)[2],((int32_t *)buff2)[3]);
-  *timestamp =  *(openair0_timestamp *)(buff2);
-  //  printf("Received %d samples, timestamp = %d\n",bytes_received>>2,*(int32_t*)timestamp);
-  *(openair0_timestamp *)(buff2) = temp;
+  /*
+    printf("Buffer head RX: nu=%d an_id=%d ts%d byte_recv=%d\n",      *(int16_t *)buff2,
+    *(int16_t *)(buff2 + sizeof(int16_t)),
+    *(openair0_timestamp *)(buff2 + sizeof(int32_t)),
+    ret>>2);*/
+  
+  /* store the timestamp value from packet's header */
+  *timestamp =  *(openair0_timestamp *)(buff2 + sizeof(int32_t));
+  
+  /* tx buffer values restored */  
+  *(int32_t *)buff2 = temp0;
+  *(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1;
+  // printf("Received %d samples, timestamp = %d\n",bytes_received>>2,*(int32_t*)timestamp);
+  
   return nsamps;
-
+  
 }
 
 
-int trx_eth_start(openair0_device *openair0)
-{
-
-  int16_t mesg[2];
-  int Mod_id;
-
-  Mod_id = openair0->openair0_cfg.Mod_id;
-  ethernet_socket_init(openair0->openair0_cfg.Mod_id, openair0->openair0_cfg.rrh_ip,openair0->openair0_cfg.rrh_port);
-
-  mesg[0] = 1 + 0;  //antenna index is 0, to be replaced by the number of active antennas
-  mesg[1] = openair0->openair0_cfg.samples_per_packet;
-
-  sendto(sockfd[Mod_id],mesg,4,0,(struct sockaddr *)&dest_addr[Mod_id],dest_addr_len[Mod_id]);
-
-
-  return(0);
+int trx_eth_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int antenna_id, int flags) {
+  
+  return ethernet_write_data(device,timestamp,buff,nsamps,antenna_id);
 }
 
-void trx_eth_write(openair0_device *device, openair0_timestamp timestamp, const void **buff, int nsamps, int cc, int flags)
-{
-  int i;
-
-  for (i=0; i<cc; i++)
-    ethernet_write_data(device->Mod_id,timestamp,buff,i,nsamps);
+int trx_eth_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int antenna_id) {
+  
+  return(ethernet_read_data(device,ptimestamp,buff,nsamps,antenna_id));
+  
 }
-int trx_eth_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int cc)
-{
 
-  int i;
+int openair0_stop(int card) {
 
-  for (i=0; i<cc; i++)
-    return(ethernet_read_data(device->Mod_id,ptimestamp,buff,i,nsamps));
+  return(0);
 
 }
 
-void trx_eth_end(openair0_device *device)
-{
+int openair0_print_stats(openair0_device* device) {
 
+  return(0);
 
+}
+int openair0_reset_stats(openair0_device* device) {
 
+  return(0);
 
 }
+void trx_eth_end(openair0_device *device) {
+  // close all the sockets
 
-int openair0_stop(int dummy) {
-
-   return 0;
+  
 }
 
-int num_devices = 0;
 
-int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg)
-{
+int openair0_dev_init_eth(openair0_device *device, openair0_config_t *openair0_cfg){
+
+  eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
+  int card = 0;
+  memset(eth, 0, sizeof(eth_state_t));
+  eth->buffer_size = (unsigned int)openair0_cfg[card].samples_per_packet*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024
+  eth->sample_rate = (unsigned int)openair0_cfg[card].sample_rate;
+  device->priv = eth; 	
 
-  printf("ETHERNET: Initializing openair0_device\n");
-  device->Mod_id         = num_devices++;
-  device->trx_start_func = trx_eth_start;
-  device->trx_end_func   = trx_eth_end;
-  device->trx_read_func  = trx_eth_read;
-  device->trx_write_func = trx_eth_write;
+  printf("ETHERNET: Initializing openair0_device for %s ...\n", ((device->func_type == BBU_FUNC) ? "BBU": "RRH"));
+  device->Mod_id           = num_devices++;
+  device->trx_request_func = trx_eth_request;
+  device->trx_reply_func   = trx_eth_reply;
+  device->trx_end_func     = trx_eth_end;
+  device->trx_read_func    = trx_eth_read;
+  device->trx_write_func   = trx_eth_write;
+  
   memcpy((void*)&device->openair0_cfg,(void*)openair0_cfg,sizeof(openair0_config_t));
+
+  if (ethernet_socket_init(device)!=0){
+    return -1;
+  }
+  
+  return 0;
 }
diff --git a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h
index d00e6ad94a6eff1e3d296675f8c5624cd2ff7b98..727f06738af6a5c37f3a94d17fb3b735b66f7474 100644
--- a/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h
+++ b/targets/ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h
@@ -1,3 +1,42 @@
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+/*! \file ethernet_lib.h
+ * \brief API to stream I/Q samples over standard ethernet
+ * \author Katerina Trilyraki, Navid Nikaein
+ * \date 2015
+ * \version 0.2
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning 
+ */
+
 #include <arpa/inet.h>
 #include <linux/if_packet.h>
 #include <stdio.h>
@@ -8,8 +47,75 @@
 #include <net/if.h>
 #include <netinet/ether.h>
 
-int ethernet_socket_init(int Mod_id, char *dest_ip,int dest_port);
 
-int ethernet_write_data(int Mod_id, const void *buff, int nsamps,int cc);
+typedef struct {
+
+  // opaque eth data struct
+  //struct eth_if *dev;
+  // An empty ("") or NULL device identifier will result in the first encountered device being opened (using the first discovered backend)
+
+  int sockfd[4];
+  struct sockaddr_in dest_addr[4];
+
+
+  unsigned int buffer_size;
+  unsigned int timeout_ns;
+
+  //struct eth_metadata meta_rx;
+  //struct eth_metadata meta_tx;
+
+  unsigned int sample_rate;
+  // time offset between transmiter timestamp and receiver timestamp;
+  double tdiff;
+  // use brf_time_offset to get this value
+  int tx_forward_nsamps; //166 for 20Mhz
+
+
+  // --------------------------------
+  // Debug and output control
+  // --------------------------------
+  int num_underflows;
+  int num_overflows;
+  int num_seq_errors;
+  int num_rx_errors;
+  int num_tx_errors;
+
+  uint64_t tx_actual_nsamps; // actual number of samples transmitted
+  uint64_t rx_actual_nsamps;
+  uint64_t tx_nsamps; // number of planned samples
+  uint64_t rx_nsamps;
+  uint64_t tx_count; // number pf packets
+  uint64_t rx_count;
+  //openair0_timestamp rx_timestamp;
+
+} eth_state_t;
+
+#define 	ETH_META_STATUS_OVERRUN   (1 << 0)
+#define 	ETH_META_STATUS_UNDERRUN  (1 << 1)
+
+struct eth_meta_data{
+	uint64_t 	timestamp;
+	uint32_t 	flags;	 
+	uint32_t 	status;
+ 	unsigned int 	actual_count;
+};
+
+
+typedef struct {
+	/* packet's timestamp */
+  openair0_timestamp timestamp;
+  /* variable declared for alignment purposes (sample size=32 bit)  */
+  int16_t 	not_used;
+  /* antenna port used to resynchronize*/
+  int16_t 	antenna_id;
+} header_t;
+
+
+
+int ethernet_socket_init(openair0_device *device);
+
+int ethernet_write_data(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id);
+
+int ethernet_read_data(openair0_device *device,openair0_timestamp *timestamp,void **buff, int nsamps,int antenna_id);
 
-int ethernet_read_data(int Mod_id,void *buff, int nsamps,int cc);
+void ethernet_socket_opt (openair0_device *device);
diff --git a/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c b/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c
index 5e11a4c2f4eea97a0276d3528fc99eb3b180d320..6c8f4f16f92df91b812cdae3399a2f0b17aa7153 100644
--- a/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c
+++ b/targets/ARCH/EXMIMO/USERSPACE/LIB/openair0_lib.c
@@ -247,6 +247,7 @@ int openair0_stop_without_reset(int card)
 #define MY_RF_MODE      (RXEN + TXEN + TXLPFNORM + TXLPFEN + TXLPF25 + RXLPFNORM + RXLPFEN + RXLPF25 + LNA1ON +LNAMax + RFBBNORM + DMAMODE_RX + DMAMODE_TX)
 #define RF_MODE_BASE    (LNA1ON + RFBBNORM)
 
+//int openair0_dev_init_exmimo(openair0_device *device, openair0_config_t *openair0_cfg) {
 int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
 
   // Initialize card
diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
index 66e2b9ed9ece20af98dbcef4cf0eabc8f0faab61..cf15321c8d94f7399db2adcdea6ecf874c527472 100644
--- a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
+++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
@@ -44,8 +44,10 @@
 #include <complex>
 #include <fstream>
 #include <cmath>
+
 #include "common_lib.h"
 
+
 typedef struct
 {
 
@@ -121,7 +123,7 @@ static void trx_usrp_end(openair0_device *device)
 	s->tx_stream->send("", 0, s->tx_md);
 	s->tx_md.end_of_burst = false;
 }
-static void trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
+static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
 {
   usrp_state_t *s = (usrp_state_t*)device->priv;
   s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
@@ -138,6 +140,8 @@ static void trx_usrp_write(openair0_device *device, openair0_timestamp timestamp
   else
     s->tx_stream->send(buff[0], nsamps, s->tx_md);
   s->tx_md.start_of_burst = false;
+
+  return 0;
 }
 
 static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc)
@@ -195,7 +199,7 @@ static bool is_equal(double a, double b)
   return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
 }
 
-int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int dummy) {
+int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg, int dummy) {
 
   usrp_state_t *s = (usrp_state_t*)device->priv;
 
@@ -237,7 +241,17 @@ int openair0_stop(int card) {
   return(0);
 
 }
+int openair0_print_stats(openair0_device* device) {
 
+  return(0);
+
+}
+int openair0_reset_stats(openair0_device* device) {
+
+  return(0);
+
+}
+//int openair0_dev_init_usrp(openair0_device* device, openair0_config_t *openair0_cfg)
 int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cfg)
 {
   uhd::set_thread_priority_safe(1.0);
@@ -376,7 +390,7 @@ int openair0_device_init(openair0_device* device, openair0_config_t *openair0_cf
   device->trx_end_func   = trx_usrp_end;
   device->trx_read_func  = trx_usrp_read;
   device->trx_write_func = trx_usrp_write;
-
+  
   s->sample_rate = openair0_cfg[0].sample_rate;
   // TODO:
   // init tx_forward_nsamps based usrp_time_offset ex
diff --git a/targets/RT/USER/UE_transport_IQ.c b/targets/RT/USER/UE_transport_IQ.c
new file mode 100644
index 0000000000000000000000000000000000000000..74623e1ba81b88ed442909264c44477f36f055e4
--- /dev/null
+++ b/targets/RT/USER/UE_transport_IQ.c
@@ -0,0 +1,562 @@
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+/*! \file UE_transport_IQ.c
+ * \brief UE transport IQ sampels 
+ * \author  Katerina Trilyraki, Navid Nikaein, Raymond Knopp
+ * \date 2015
+ * \version 0.1
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning very experimental 
+ */
+
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include "common_lib.h"
+#include "PHY/defs.h"
+#include "rrh_gw.h"
+#include "rrh_gw_externs.h"
+
+#define START_CMD 1
+#define RRH_UE_PORT 51000
+#define RRH_UE_DEST_IP "127.0.0.1"
+#define PRINTF_PERIOD 3750
+
+/******************************************************************************
+ **                               FUNCTION PROTOTYPES                        **
+ ******************************************************************************/
+void *rrh_proc_UE_thread(void *);
+void *rrh_UE_thread(void *);
+void *rrh_UE_rx_thread(void *);
+void *rrh_UE_tx_thread(void *);
+
+
+
+openair0_timestamp	timestamp_UE_tx[4]= {0,0,0,0},timestamp_UE_rx[4]= {0,0,0,0};
+openair0_timestamp 	nrt_UE_counter[4]= {0,0,0,0};
+
+pthread_t		main_rrh_UE_thread,main_rrh_proc_UE_thread;
+pthread_attr_t 		attr, attr_proc;
+struct sched_param 	sched_param_rrh, sched_param_rrh_proc;
+pthread_cond_t 		sync_UE_cond[4];
+pthread_mutex_t 	sync_UE_mutex[4];
+
+int32_t 		overflow_rx_buffer_UE[4]= {0,0,0,0};
+int32_t 		nsamps_UE[4]= {0,0,0,0};
+int32_t 		UE_tx_started=0,UE_rx_started=0;
+int32_t 		RT_flag_UE=0, NRT_flag_UE=1;
+int32_t 		counter_UE_rx[4]= {0,0,0,0};
+int32_t 		counter_UE_tx[4]= {0,0,0,0};
+int32_t			**tx_buffer_UE, **rx_buffer_UE;
+
+int 			sync_UE_rx[4]= {-1,-1,-1,-1};
+void 			*rrh_UE_thread_status;
+
+
+void *rx_ue[2]; // FIXME hard coded array size; indexed by lte_frame_parms.nb_antennas_rx
+void *tx_ue[2]; // FIXME hard coded array size; indexed by lte_frame_parms.nb_antennas_tx
+
+/*! \fn void create_UE_trx_threads( openair0_device *dev_ue, uint8_t RT_flag,uint8_t NRT_flag)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void create_UE_trx_threads( rrh_module_t *dev_ue, uint8_t RT_flag,uint8_t NRT_flag) {
+
+  int 	  i;
+  int 	  error_code_UE, error_code_proc_UE;
+  void    *tmp;
+  
+  RT_flag_UE=RT_flag;
+  NRT_flag_UE=NRT_flag;
+  
+  pthread_attr_init(&attr);
+  sched_param_rrh.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  pthread_attr_init(&attr_proc);
+  sched_param_rrh_proc.sched_priority = sched_get_priority_max(SCHED_FIFO-1);
+  
+  pthread_attr_setschedparam(&attr,&sched_param_rrh);
+  pthread_attr_setschedpolicy(&attr,SCHED_FIFO);
+  pthread_attr_setschedparam(&attr_proc,&sched_param_rrh_proc);
+  pthread_attr_setschedpolicy(&attr_proc,SCHED_FIFO-1);
+  
+  
+  for (i=0; i<4; i++) {
+    pthread_mutex_init(&sync_UE_mutex[i],NULL);
+    pthread_cond_init(&sync_UE_cond[i],NULL);
+  }
+  
+  error_code_UE = pthread_create(&main_rrh_UE_thread, &attr, rrh_UE_thread, (void *)dev_ue);
+  error_code_proc_UE = pthread_create(&main_rrh_proc_UE_thread, &attr_proc, rrh_proc_UE_thread, (void *)dev_ue);
+  
+  if (error_code_UE) {
+    printf("Error while creating UE thread\n");
+    exit(-1);
+  }
+  
+  if (error_code_proc_UE) {
+    printf("Error while creating UE proc thread\n");
+    exit(-1);
+  }
+  
+}
+
+
+/*! \fn void *rrh_proc_UE_thread((void *)dev_ue)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void *rrh_proc_UE_thread(void * arg) {
+  
+  int				antenna_index,i;
+  openair0_timestamp 		truncated_timestamp, truncated_timestamp_final, last_hw_counter=0;
+  struct			timespec time_req, time_rem;
+  int16_t       		*txp,*rxp;
+  unsigned int                  samples_per_frame=0;
+  rrh_module_t 	*dev=(rrh_module_t *)arg;
+  
+  samples_per_frame= dev->eth_dev.openair0_cfg.samples_per_frame;
+  AssertFatal(samples_per_frame <=0, "invalide samples_per_frame !%u\n",samples_per_frame);
+
+  time_req.tv_sec = 0;
+  time_req.tv_nsec = 1000;
+  
+  while (rrh_exit==0) {
+    //wait until some data has been copied
+    for (antenna_index=0; antenna_index<4; antenna_index++) {
+      if (sync_UE_rx[antenna_index]==0) {
+	if (!UE_tx_started) {
+	  UE_tx_started=1;  //Set this flag to 1 to indicate that a UE started retrieving data
+	  if (RT_flag_UE==1) {
+	    last_hw_counter=hw_counter;
+	  }
+	} else {
+	  if (RT_flag_UE==1) {
+	    if (hw_counter > last_hw_counter+1) {
+	      printf("L1");
+	      //              goto end_copy_UE;
+	    } else {
+	      while (hw_counter < last_hw_counter+1)
+		nanosleep(&time_req,&time_rem);
+	    }
+	  }
+	}
+	
+	truncated_timestamp = timestamp_UE_tx[antenna_index]%(samples_per_frame);
+	truncated_timestamp_final =  (timestamp_UE_tx[antenna_index]+nsamps_UE[antenna_index])%samples_per_frame;
+	
+	if ((truncated_timestamp + nsamps_UE[antenna_index]) > samples_per_frame) {
+	  if ((timestamp_eNB_rx[antenna_index]%samples_per_frame < nsamps_UE[antenna_index]) && (eNB_rx_started==1)) {
+	    overflow_rx_buffer_eNB[antenna_index]++;
+	    printf("eNB Overflow[%d] : %d, timestamp : %d\n",antenna_index,overflow_rx_buffer_eNB[antenna_index],(int)truncated_timestamp);
+	    
+	    if (NRT_flag_UE==1) {
+	      while ((timestamp_eNB_rx[antenna_index]%samples_per_frame) < nsamps_UE[antenna_index])
+		nanosleep(&time_req,&time_rem);
+	    }
+	  }
+	  
+	  rxp = (int16_t*)&rx_buffer_eNB[antenna_index][truncated_timestamp];
+	  txp = (int16_t*)&tx_buffer_UE[antenna_index][truncated_timestamp];
+	  
+	  for (i=0; i<(samples_per_frame<<1)-(truncated_timestamp<<1); i++) {
+	    rxp[i] = txp[i]>>6;
+	  }
+	  
+	  rxp = (int16_t*)&rx_buffer_eNB[antenna_index][0];
+	  txp = (int16_t*)&tx_buffer_UE[antenna_index][0];
+	  
+	  for (i=0; i<nsamps_eNB[antenna_index]-(samples_per_frame)+(truncated_timestamp); i++) {
+	    rxp[i] = txp[i]>>6;
+	  }
+	  
+	} else {
+	  if (((truncated_timestamp < (timestamp_eNB_rx[antenna_index]%samples_per_frame)) && 
+	       (truncated_timestamp_final >  (timestamp_eNB_rx[antenna_index]%samples_per_frame))) && 
+	      (eNB_rx_started==1)) {
+	    overflow_rx_buffer_eNB[antenna_index]++;
+	    printf("eNB Overflow[%d] : %d, timestamp : %d\n",antenna_index,overflow_rx_buffer_eNB[antenna_index],(int)truncated_timestamp);
+	    
+	    if (NRT_flag_UE==1) {
+	      while (truncated_timestamp_final >  timestamp_eNB_rx[antenna_index]%samples_per_frame)
+		nanosleep(&time_req,&time_rem);
+	    }
+	  }
+	  
+	  rxp = (int16_t*)&rx_buffer_eNB[antenna_index][truncated_timestamp];
+	  txp = (int16_t*)&tx_buffer_UE[antenna_index][truncated_timestamp];
+	  
+	  for (i=0; i<(nsamps_eNB[antenna_index]); i++) {
+	    rxp[i] =txp[i]>>6;
+	  }
+		  
+	}
+	//end_copy_UE :
+	last_hw_counter=hw_counter;
+	pthread_mutex_lock(&sync_UE_mutex[antenna_index]);
+	sync_UE_rx[antenna_index]--;
+	pthread_mutex_unlock(&sync_UE_mutex[antenna_index]);
+	
+      }
+    }
+
+  }
+  
+  return(0);
+}
+
+/*! \fn void *rrh_UE_thread(void *arg)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void *rrh_UE_thread(void *arg) {
+  
+  rrh_module_t 	*dev=(rrh_module_t *)arg;
+
+  struct sched_param 	sched_param_UE_rx, sched_param_UE_tx;
+  int16_t		i,cmd;   //,nsamps,antenna_index;
+  ssize_t 	        bytes_received;
+  struct timespec 	time_req_1us, time_rem_1us;
+  pthread_t 		UE_rx_thread, UE_tx_thread;
+  pthread_attr_t 	attr_UE_rx, attr_UE_tx;
+  int 			error_code_UE_rx, error_code_UE_tx;
+  void 			*tmp;
+  unsigned int          samples_per_frame=0;
+  
+  samples_per_frame= dev->eth_dev.openair0_cfg.samples_per_frame;
+  time_req_1us.tv_sec = 0;
+  time_req_1us.tv_nsec = 1000;
+  
+  while (rrh_exit==0) {
+    
+    cmd=dev->eth_dev.trx_reply_func(&dev->eth_dev);
+    
+    /* allocate memory for TX/RX buffers */
+    rx_buffer_UE = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.rx_num_channels*sizeof(int32_t*));
+    tx_buffer_UE = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.tx_num_channels*sizeof(int32_t*));
+    
+    for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++) {
+      tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame+4));
+      memset(tmp,0,sizeof(int32_t)*(samples_per_frame+4));
+      rx_buffer_UE[i]=(tmp+4*sizeof(int32_t));  
+    }
+    
+    for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) {
+      tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame+4));
+      memset(tmp,0,sizeof(int32_t)*(samples_per_frame+4));
+      tx_buffer_UE[i]=(tmp+4*sizeof(int32_t));  
+    }
+    
+    printf("Client %s:%d is connected (DL_RB=%d) rt=%d|%d. \n" ,   dev->eth_dev.openair0_cfg.remote_ip,
+	   dev->eth_dev.openair0_cfg.remote_port,
+	   dev->eth_dev.openair0_cfg.num_rb_dl,
+	   dev->eth_dev.openair0_cfg.rx_num_channels,
+	   dev->eth_dev.openair0_cfg.tx_num_channels);
+    
+    if (cmd==START_CMD) {
+      
+      pthread_attr_init(&attr_UE_rx);
+      pthread_attr_init(&attr_UE_tx);
+      sched_param_UE_rx.sched_priority = sched_get_priority_max(SCHED_FIFO);
+      sched_param_UE_tx.sched_priority = sched_get_priority_max(SCHED_FIFO);
+      pthread_attr_setschedparam(&attr_UE_rx,&sched_param_UE_rx);
+      pthread_attr_setschedparam(&attr_UE_tx,&sched_param_UE_tx);
+      pthread_attr_setschedpolicy(&attr_UE_rx,SCHED_FIFO);
+      pthread_attr_setschedpolicy(&attr_UE_tx,SCHED_FIFO);
+      
+      error_code_UE_rx = pthread_create(&UE_rx_thread, &attr_UE_rx, rrh_UE_rx_thread, (void *)&dev);
+      error_code_UE_tx = pthread_create(&UE_tx_thread, &attr_UE_tx, rrh_UE_tx_thread, (void *)&dev);
+      
+      if (error_code_UE_rx) {
+	printf("Error while creating UE RX thread\n");
+	exit(-1);
+      }
+
+      if (error_code_UE_tx) {
+	printf("Error while creating UE TX thread\n");
+	exit(-1);
+      }
+      
+      while (rrh_exit==0)
+	sleep(1);
+    }
+  }
+  
+  
+  rrh_UE_thread_status = 0;
+  pthread_exit(&rrh_UE_thread_status);
+  
+  return(0);
+}
+
+
+/*! \fn void *rrh_UE_rx_thread(void *arg)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void *rrh_UE_rx_thread(void *arg) {
+
+  rrh_module_t        *dev = (rrh_module_t *)arg;
+  struct 	      timespec time0,time1,time2;
+  struct 	      timespec time_req_1us, time_rem_1us;
+  ssize_t	      bytes_sent;
+  int 		      antenna_index, nsamps;
+  int 		      trace_cnt=0;
+  unsigned long long  max_rx_time=0, min_rx_time=133333, total_rx_time=0, average_rx_time=133333, s_period=0, trial=0;
+  unsigned int        samples_per_frame=0;
+  openair0_timestamp  temp, last_hw_counter=0;
+  
+  antenna_index     = 0;
+  nsamps	    = dev->eth_dev.openair0_cfg.samples_per_packet;
+  samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame;
+  
+  while (rrh_exit == 0) {
+    if (!UE_rx_started) {
+      UE_rx_started=1;  //Set this flag to 1 to indicate that a UE started retrieving data
+      
+      if (RT_flag_UE==1) {
+	last_hw_counter=hw_counter;
+      }
+    } else {
+      if (RT_flag_UE==1) {
+	if (hw_counter > last_hw_counter+1) {
+	  printf("L1");
+	  //goto end_copy_UE;
+	} else {
+	  while (hw_counter < last_hw_counter+1)
+	    nanosleep(&time_req_1us,&time_rem_1us);
+	}
+      }
+    }
+    
+    clock_gettime(CLOCK_MONOTONIC,&time1);
+    
+    // send return
+    
+    if ((timestamp_UE_rx[antenna_index]%(samples_per_frame)+nsamps) > samples_per_frame) { // Wrap around if nsamps exceeds the buffer limit
+      if (((timestamp_eNB_tx[antenna_index]%(samples_per_frame)) < ((timestamp_UE_rx[antenna_index]+nsamps)%(samples_per_frame))) && (eNB_tx_started==1)) {
+	printf("UE underflow wraparound timestamp_UE_rx : %d, timestamp_eNB_tx : %d\n",
+	       (int)(timestamp_UE_rx[antenna_index]%(samples_per_frame)),
+	       (int)(timestamp_eNB_tx[antenna_index]%samples_per_frame));
+	
+	if (NRT_flag_UE==1) {
+	  while ((timestamp_eNB_tx[antenna_index]%samples_per_frame) < ((timestamp_UE_rx[antenna_index]+nsamps)%(samples_per_frame)))
+	    nanosleep(&time_req_1us,&time_rem_1us);
+	}
+      }
+      
+      rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][timestamp_UE_rx[antenna_index]%(samples_per_frame)];
+      if ((bytes_sent =dev->eth_dev.trx_write_func (dev,
+						    timestamp_UE_rx[antenna_index],
+						    rx_ue,
+						    ( samples_per_frame- (timestamp_eNB_rx[antenna_index]%(samples_per_frame)) ),
+					antenna_index,
+						    0))<0)
+	perror("RRH UE : sendto for RX");
+      
+      rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][3];
+      if ((bytes_sent  =dev->eth_dev.trx_write_func (dev,
+						     timestamp_UE_rx[antenna_index],
+						     rx_ue,
+						     (nsamps - samples_per_frame + (timestamp_eNB_rx[antenna_index]%(samples_per_frame))),
+						     antenna_index,
+						     0))<0)
+	perror("RRH UE : sendto for RX");
+      
+    } else {
+      if (((timestamp_UE_rx[antenna_index]%samples_per_frame)< timestamp_eNB_tx[antenna_index]%samples_per_frame) && 
+	  (((timestamp_UE_rx[antenna_index]+nsamps)%samples_per_frame) > (timestamp_eNB_tx[antenna_index]%samples_per_frame)) && 
+	  (eNB_tx_started==1) ) {
+	printf("UE underflow timestamp_UE_rx : %d, timestamp_eNB_tx : %d\n",
+	       (int)(timestamp_UE_rx[antenna_index]%samples_per_frame),
+	       (int)(timestamp_eNB_tx[antenna_index]%samples_per_frame));
+	
+	if (NRT_flag_UE==1) {
+	  while (((timestamp_UE_rx[antenna_index]+nsamps)%samples_per_frame) > (timestamp_eNB_tx[antenna_index]%samples_per_frame)) {
+	    nanosleep(&time_req_1us,&time_rem_1us);
+	  }
+	}
+      }
+      
+      rx_ue[antenna_index]=(void*)&rx_buffer_UE[antenna_index][timestamp_UE_rx[antenna_index]%(samples_per_frame)];
+      if ((bytes_sent = dev->eth_dev.trx_write_func (dev,
+						     timestamp_UE_rx[antenna_index],
+						     rx_ue,
+						     nsamps,
+						     antenna_index,
+						     0))<0)
+	perror("RRH UE thread: sendto for RX");
+    }
+    
+    timestamp_UE_rx[antenna_index]+=nsamps;
+    last_hw_counter=hw_counter;
+    
+    clock_gettime(CLOCK_MONOTONIC,&time2);
+    
+    if (trace_cnt++ > 10) {
+      total_rx_time = (unsigned int)(time2.tv_nsec - time0.tv_nsec);
+      
+      if (total_rx_time < 0) total_rx_time=1000000000-total_rx_time;
+      
+      if ((total_rx_time > 0) && (total_rx_time < 1000000000)) {
+	trial++;
+	
+	if (total_rx_time < min_rx_time)
+	  min_rx_time = total_rx_time;
+	
+	if (total_rx_time > max_rx_time)
+	  max_rx_time = total_rx_time;
+	
+	average_rx_time = (long long unsigned int)((average_rx_time*trial)+total_rx_time)/(trial+1);
+      }
+      
+      if (s_period++ == PRINTF_PERIOD) {
+	s_period=0;
+	printf("Average UE RX time : %lu\tMax RX time : %lu\tMin RX time : %lu\n",average_rx_time,max_rx_time,min_rx_time);
+	
+      }
+      
+      //printf("RX: t1 %llu (time from last send), t2 %llu (sendto of %lu bytes) total time : %llu\n",(long long unsigned int)(time1.tv_nsec  - time0.tv_nsec), (long long unsigned int)(time2.tv_nsec - time1.tv_nsec),
+      //   (nsamps<<2)+sizeof(openair0_timestamp),(long long unsigned int)(time2.tv_nsec - time0.tv_nsec));
+      
+    }
+    
+    memcpy(&time0,&time2,sizeof(struct timespec));
+    
+  }  //while (UE_exit==0)
+  
+  return(0);
+  
+}
+
+
+/*! \fn void *rrh_UE_tx_thread(void *arg)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void *rrh_UE_tx_thread(void *arg) {
+  
+  struct timespec 	time0a,time0,time1,time2;
+  struct timespec 	time_req_1us, time_rem_1us;
+  
+  
+  rrh_module_t    	*dev = (rrh_module_t *)arg;
+  ssize_t 		bytes_received;
+  int 			antenna_index, nsamps;
+  int 			trace_cnt=0;
+  unsigned int          samples_per_frame=0;
+  
+  antenna_index    = 0;
+  nsamps 	   = dev->eth_dev.openair0_cfg.samples_per_packet;
+  samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame;
+  
+  
+  while (rrh_exit == 0) {
+    
+    clock_gettime(CLOCK_MONOTONIC,&time0a);
+    
+    bytes_received = dev->eth_dev.trx_read_func(dev,
+						&timestamp_UE_tx[antenna_index],
+						tx_ue,
+						nsamps,
+						antenna_index);
+    
+    clock_gettime(CLOCK_MONOTONIC,&time1);
+    
+    if (NRT_flag_UE==1) {
+      nrt_UE_counter[antenna_index]++;
+    }
+    printf(" first part size: %d   second part size: %d idx=%d \n",
+	   (int32_t)(((samples_per_frame)<<2)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2)),
+	   (nsamps<<2)-((samples_per_frame)<<2)+((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2),
+	   timestamp_UE_tx[antenna_index]%(samples_per_frame));
+    if ((timestamp_UE_tx[antenna_index]%(samples_per_frame)+nsamps) > samples_per_frame) { // Wrap around if nsamps exceeds the buffer limit
+      memcpy(&tx_buffer_UE[antenna_index][timestamp_UE_tx[antenna_index]%(samples_per_frame)],(void*)(tx_ue[antenna_index]),
+	     (samples_per_frame<<2)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2));
+      
+      memcpy(&tx_buffer_UE[antenna_index][0], (void*)(tx_ue[antenna_index]+(samples_per_frame*4)-((timestamp_UE_tx[antenna_index]%(samples_per_frame))<<2)),
+             (nsamps<<2)-((samples_per_frame-(timestamp_UE_tx[antenna_index]%(samples_per_frame)))<<2));
+      //printf("Received UE TX samples for antenna %d, nsamps %d (%d)\n",antenna_index,nsamps,(int)(bytes_received>>2));
+    } else {
+      memcpy(&tx_buffer_UE[antenna_index][timestamp_UE_tx[antenna_index]%(samples_per_frame)], (void*)(tx_ue[antenna_index]),(nsamps<<2));
+    }
+    
+    while (sync_UE_rx[antenna_index]==0)
+      nanosleep(&time_req_1us,&time_rem_1us);
+    
+    pthread_mutex_lock(&sync_UE_mutex[antenna_index]);
+    sync_UE_rx[antenna_index]++;
+    
+    if (!sync_UE_rx[antenna_index]) {
+      counter_UE_tx[antenna_index]=(counter_UE_tx[antenna_index]+nsamps)%samples_per_frame;
+      nsamps_UE[antenna_index]=nsamps;
+    } else {
+      printf("rrh_eNB_proc thread is busy, will exit\n");
+      exit(-1);
+    }
+    
+    pthread_mutex_unlock(&sync_UE_mutex[antenna_index]);
+    
+    clock_gettime(CLOCK_MONOTONIC,&time2);
+    
+    memcpy(&time0,&time2,sizeof(struct timespec));
+    
+  }
+  
+  return(0);
+  
+}
+
+
diff --git a/targets/RT/USER/eNB_transport_IQ.c b/targets/RT/USER/eNB_transport_IQ.c
new file mode 100644
index 0000000000000000000000000000000000000000..19b03f76928aa6b5183a235f1372d53727c6c4d2
--- /dev/null
+++ b/targets/RT/USER/eNB_transport_IQ.c
@@ -0,0 +1,588 @@
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+
+/*! \file eNB_transport_IQ.c
+ * \brief eNB transport IQ sampels 
+ * \author  Katerina Trilyraki, Navid Nikaein, Raymond Knopp
+ * \date 2015
+ * \version 0.1
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning very experimental 
+ */
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include "common_lib.h"
+#include "PHY/defs.h"
+#include "rrh_gw.h"
+#include "rrh_gw_externs.h"
+#include "rt_wrapper.h"
+
+#define RRH_eNB_PORT     50000
+#define RRH_eNB_DEST_IP  "192.168.12.31"
+#define PRINTF_PERIOD    3750
+#define HEADER_SIZE      ((sizeof(int32_t) + sizeof(openair0_timestamp))>>2)
+//#undef LOWLATENCY
+/******************************************************************************
+ **                               FUNCTION PROTOTYPES                        **
+ ******************************************************************************/
+void *rrh_eNB_rx_thread(void *);
+void *rrh_eNB_tx_thread(void *);
+void *rrh_proc_eNB_thread(void *);
+void *rrh_eNB_thread(void *);
+void set_rt_period( openair0_config_t openair0_cfg);
+void print_dev_config(openair0_device device);
+
+openair0_timestamp	timestamp_eNB_tx[4]= {0,0,0,0},timestamp_eNB_rx[4]= {0,0,0,0};
+openair0_timestamp 	nrt_eNB_counter[4]= {0,0,0,0};
+
+pthread_t	   main_rrh_eNB_thread;
+pthread_attr_t     attr, attr_proc;
+struct sched_param sched_param_rrh;
+pthread_cond_t     sync_eNB_cond[4];
+pthread_mutex_t    sync_eNB_mutex[4];
+
+int32_t 	overflow_rx_buffer_eNB[4]= {0,0,0,0};
+int32_t 	nsamps_eNB[4]= {0,0,0,0};
+int32_t 	eNB_tx_started=0,eNB_rx_started=0;
+int32_t 	counter_eNB_rx[4]= {0,0,0,0};
+int32_t 	counter_eNB_tx[4]= {0,0,0,0};
+uint8_t		RT_flag_eNB,NRT_flag_eNB;
+int32_t		**tx_buffer_eNB, **rx_buffer_eNB;
+
+void 		*rrh_eNB_thread_status;
+void 		*rx_eNB[2]; //to be fixed
+void 		*tx_eNB[2]; //to be fixed
+int 		sync_eNB_rx[4]= {-1,-1,-1,-1};
+
+unsigned int  rx_pos=0, next_rx_pos=0;
+unsigned int  tx_pos=0, prev_tx_pos=0;
+
+
+/*! \fn void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag,uint8_t NRT_flag)
+ * \brief this function
+ * \param[in]
+ * \param[out]
+ * \return
+ * \note
+ * @ingroup  _oai
+ */
+void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag, uint8_t NRT_flag){
+
+  //int 	i;
+  int 	error_code_eNB;//, error_code_proc_eNB;
+
+  RT_flag_eNB=RT_flag;
+  NRT_flag_eNB=NRT_flag;
+
+  pthread_attr_init(&attr);
+  sched_param_rrh.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  pthread_attr_setschedparam(&attr,&sched_param_rrh);
+  pthread_attr_setschedpolicy(&attr,SCHED_FIFO);
+/*	
+	for (i=0; i<4; i++) {
+		pthread_mutex_init(&sync_eNB_mutex[i],NULL);
+		pthread_cond_init(&sync_eNB_cond[i],NULL);
+	}*/
+
+  error_code_eNB = pthread_create(&main_rrh_eNB_thread, &attr, rrh_eNB_thread, (void *)dev_enb);
+  if (error_code_eNB) {
+    printf("Error while creating eNB thread\n");
+    exit(-1);
+  }
+
+}
+
+/*! \fn void *rrh_eNB_thread(void *arg)
+ * \brief this function
+ * \param[in]
+ * \param[out]
+ * \return
+ * \note
+ * @ingroup  _oai
+ */
+void *rrh_eNB_thread(void *arg)
+{
+  rrh_module_t 	       	*dev=(rrh_module_t *)arg;
+  pthread_t  	      	eNB_rx_thread, eNB_tx_thread;  
+  int 	      		error_code_eNB_rx, error_code_eNB_tx;
+  int16_t	        i,j;   		
+  void 			*tmp;
+  unsigned int          samples_per_frame=0;
+  
+
+  while (rrh_exit==0) {
+    
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX, 1 );
+   
+    /* handshake with client to exchange parameters */
+    dev->eth_dev.trx_reply_func(&dev->eth_dev);//change port  make it plus_id
+    /* prints received configuration parameters */
+    print_dev_config(dev->eth_dev);
+    set_rt_period(dev->eth_dev.openair0_cfg);
+    samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame;
+    
+    /* allocate memory for TX/RX buffers
+       each antenna port has a TX and a RX buffer
+       each TX and RX buffer is of (samples_per_frame + HEADER_SIZE) samples(int32_t) */
+    rx_buffer_eNB = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.rx_num_channels*sizeof(int32_t*));
+    tx_buffer_eNB = (int32_t**)malloc16(dev->eth_dev.openair0_cfg.tx_num_channels*sizeof(int32_t*));
+    
+    LOG_D(RRH,"rx ch %d %p and tx ch %d %p\n", 
+	  dev->eth_dev.openair0_cfg.rx_num_channels,
+	  rx_buffer_eNB,
+	  dev->eth_dev.openair0_cfg.tx_num_channels,
+	  tx_buffer_eNB);
+    /* rx_buffer_eNB points to the beginning of data */
+    for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++) {
+      tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE));
+      memset(tmp,0,sizeof(int32_t)*(samples_per_frame + HEADER_SIZE));
+      rx_buffer_eNB[i]=( tmp + (HEADER_SIZE*sizeof(int32_t)) );  
+      LOG_D(RRH," rx ch %d %p |%p header=%d size=%d sample=%d spf=%d\n", 
+	    i,
+	    rx_buffer_eNB[i],tmp,HEADER_SIZE,(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE)),sizeof(int32_t),samples_per_frame);
+    }
+    /* tx_buffer_eNB points to the beginning of data */
+    for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) {
+      tmp=(void *)malloc(sizeof(int32_t)*(samples_per_frame + HEADER_SIZE));
+      memset(tmp,0,sizeof(int32_t)*(samples_per_frame + HEADER_SIZE));
+      tx_buffer_eNB[i]=( tmp + (HEADER_SIZE*sizeof(int32_t)) );  
+      LOG_D(RRH," tx ch %d %p| %p \n", i,tx_buffer_eNB[i],tmp);
+    }
+    /* dummy initialization for TX/RX buffers */
+    for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++) {
+      for (j=0; j<samples_per_frame; j++) {
+	rx_buffer_eNB[i][j]=32+i; 
+	tx_buffer_eNB[i][j]=13+i; 
+      } 
+    }
+
+    
+#ifdef LOWLATENCY
+    error_code_eNB_rx = pthread_create(&eNB_rx_thread, NULL, rrh_eNB_rx_thread, (void *)dev);
+    error_code_eNB_tx = pthread_create(&eNB_tx_thread, NULL, rrh_eNB_tx_thread, (void *)dev);	
+    LOG_D(RRH,"[eNB][SCHED] deadline scheduling applied to TX/RX threads\n");	
+#else
+    pthread_attr_t	attr_eNB_rx, attr_eNB_tx;
+    struct sched_param 	sched_param_eNB_rx, sched_param_eNB_tx;
+    pthread_attr_init(&attr_eNB_rx);
+    pthread_attr_init(&attr_eNB_tx);	
+    sched_param_eNB_rx.sched_priority = sched_get_priority_max(SCHED_FIFO);
+    sched_param_eNB_tx.sched_priority = sched_get_priority_max(SCHED_FIFO);
+    pthread_attr_setschedparam(&attr_eNB_rx,&sched_param_eNB_rx);
+    pthread_attr_setschedparam(&attr_eNB_tx,&sched_param_eNB_tx);
+    pthread_attr_setschedpolicy(&attr_eNB_rx,SCHED_FIFO);
+    pthread_attr_setschedpolicy(&attr_eNB_tx,SCHED_FIFO);
+    
+    error_code_eNB_rx = pthread_create(&eNB_rx_thread, &attr_eNB_rx, rrh_eNB_rx_thread, (void *)dev);
+    error_code_eNB_tx = pthread_create(&eNB_tx_thread, &attr_eNB_tx, rrh_eNB_tx_thread, (void *)dev);
+    LOG_D(RRH,"[eNB][SCHED] FIFO scheduling applied to TX/RX threads\n");		
+#endif
+
+    if (error_code_eNB_rx) {
+      LOG_E(RRH,"[eNB] Error while creating eNB RX thread\n");
+      exit(-1);
+    }
+    if (error_code_eNB_tx) {
+      LOG_E(RRH,"[eNB] Error while creating eNB TX thread\n");
+      exit(-1);
+    }
+
+    while (rrh_exit==0)
+      sleep(1);
+    
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TRX,0 );
+  }  //while (eNB_exit==0)
+
+  rrh_eNB_thread_status = 0;
+  pthread_exit(&rrh_eNB_thread_status);
+  return(0);
+}
+
+
+
+/*! \fn void *rrh_eNB_rx_thread(void *arg)
+ * \brief this function
+ * \param[in]
+ * \param[out]
+ * \return
+ * \note
+ * @ingroup  _oai
+ */
+void *rrh_eNB_rx_thread(void *arg){
+
+  /* measuremnt related vars */
+  struct timespec time0,time1,time2;
+  unsigned long long max_rx_time=0, min_rx_time=rt_period, total_rx_time=0, average_rx_time=rt_period, s_period=0, trial=0;
+  int trace_cnt=0;
+
+  struct timespec time_req_1us, time_rem_1us;
+  rrh_module_t *dev = (rrh_module_t *)arg;
+  ssize_t bytes_sent;
+  int i, spp ,pck_rx=0;
+  openair0_vtimestamp last_hw_counter=0;  //volatile int64_t	last_hw_counter=0;---> read 
+  unsigned int samples_per_frame=0;
+  uint8_t loopback=0,measurements=0;
+
+  //RTIME sleep_ns=1000;
+  time_req_1us.tv_sec = 0;
+  time_req_1us.tv_nsec =1000;  //time_req_1us.tv_nsec = (int)rt_period/2;--->granularity issue
+  spp =  dev->eth_dev.openair0_cfg.samples_per_packet;
+  samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame;
+  loopback = dev->loopback;
+  measurements = dev->measurements;
+  next_rx_pos = spp;
+
+  LOG_I(RRH,"rt period is set to %d\n",rt_period);
+#ifdef LOWLATENCY
+  struct sched_attr attr;
+  unsigned int flags = 0;
+
+  attr.size = sizeof(attr);
+  attr.sched_flags = 0;
+  attr.sched_nice = 0;
+  attr.sched_priority = 0;
+
+  attr.sched_policy   = SCHED_DEADLINE;
+  attr.sched_runtime  = (0.1   *  100) * 10000; // 
+  attr.sched_deadline = rt_period;// 0.1   *  1000000; // 
+  attr.sched_period   = rt_period; //0.1   *  1000000; // each TX/RX thread has 
+
+  if (sched_setattr(0, &attr, flags) < 0 ) {
+    perror("[SCHED] eNB RX thread: sched_setattr failed (run with sudo)\n");
+    exit(-1);
+  }
+#endif
+
+  while (rrh_exit == 0) {
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX, 1 );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_HWCNT, hw_counter );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_LHWCNT, last_hw_counter );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_PCK, pck_rx );
+
+    for (i=0; i<dev->eth_dev.openair0_cfg.rx_num_channels; i++){
+      if (!eNB_rx_started) {
+	eNB_rx_started=1; // set this flag to 1 to indicate that eNB started
+	if (RT_flag_eNB==1) {
+	  last_hw_counter=hw_counter;
+	}
+      } else {
+	if (RT_flag_eNB==1) {
+	  if (hw_counter > last_hw_counter+1) {
+	    printf("LR");
+	  } else {
+	    while (hw_counter < last_hw_counter+1){
+	      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP, 1 );
+	      nanosleep(&time_req_1us,&time_rem_1us);
+	      //rt_sleep_ns(sleep_ns);
+	      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX_SLEEP, 0 );
+	    } 
+	  }
+	}
+      }
+
+      if (measurements == 1 )
+	clock_gettime(CLOCK_MONOTONIC,&time1);
+      
+      /* LOG_I(RRH,"send for%d at %d with %d |%d|%d| \n",i,rx_pos,timestamp_eNB_rx[i],((timestamp_eNB_rx[i]+spp)%samples_per_frame),next_rx_pos );
+			
+      if ((timestamp_UE_tx[i]%samples_per_frame < next_rx_pos) && (UE_tx_started==1)) {
+	printf("eNB underflow\n");
+	if (NRT_flag_eNB==1) {
+	  while ((timestamp_UE_tx[i]%samples_per_frame) < spp)
+          nanosleep(&time_req_1us,&time_rem_1us);
+	}
+      }	
+      if (((rx_pos)< timestamp_UE_tx[i]%samples_per_frame) && (next_rx_pos > (timestamp_UE_tx[i]%samples_per_frame)) && (UE_tx_started==1)) {
+	printf("eNB underflow\n");
+	if (NRT_flag_eNB==1) {
+	  while (next_rx_pos > (timestamp_UE_tx[i]%samples_per_frame))
+          nanosleep(&time_req_1us,&time_rem_1us);
+	}
+	}*/
+
+      if (loopback == 1 ) {
+	if (sync_eNB_rx[i]==0) {
+	  rx_eNB[i] = (void*)&tx_buffer_eNB[i][tx_pos];
+	  LOG_I(RRH,"tx_buffer_eNB[i][tx_pos]=%d ,tx_pos=%d\n",tx_buffer_eNB[i][tx_pos],tx_pos);			
+	}
+	else{
+	  rx_eNB[i] = (void*)&rx_buffer_eNB[i][rx_pos];
+	  LOG_I(RRH,"rx_buffer_eNB[i][rx_pos]=%d ,rx_pos=%d\n",rx_buffer_eNB[i][rx_pos],rx_pos);	
+	}
+      }
+
+      rx_eNB[i] = (void*)&rx_buffer_eNB[i][rx_pos];
+
+      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RXCNT, rx_pos );
+      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RX_TS, timestamp_eNB_rx[i]&0xffffffff );
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 ); 
+      
+      //LOG_D(RRH,"rx_buffer_eNB[i][rx_pos]=%p ,rx_pos=%d, i=%d ts=%d\n",&rx_buffer_eNB[i][rx_pos],rx_pos,i,timestamp_eNB_rx[i]);	 
+      if ((bytes_sent = dev->eth_dev.trx_write_func (&dev->eth_dev,
+						     timestamp_eNB_rx[i],
+						     rx_eNB,
+						     spp,
+						     i,
+						     0))<0){
+	perror("RRH eNB : sendto for RX");
+      }
+    
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0 );
+
+      timestamp_eNB_rx[i]+=spp;
+      last_hw_counter=hw_counter;
+
+      if (measurements == 1 ) {
+
+	clock_gettime(CLOCK_MONOTONIC,&time2);
+	
+	if (trace_cnt++ > 10) {
+	  total_rx_time = (unsigned int)(time2.tv_nsec - time0.tv_nsec);
+	  if (total_rx_time < 0)
+	    total_rx_time=1000000000-total_rx_time;
+	  
+	  if ((total_rx_time > 0) && (total_rx_time < 1000000000)) {
+	    trial++;
+	    if (total_rx_time < min_rx_time)
+	      min_rx_time = total_rx_time;
+	    if (total_rx_time > max_rx_time){
+	      max_rx_time = total_rx_time;
+	      LOG_D(RRH,"Max value %d update at rx_position %d \n",timestamp_eNB_rx[i], rx_pos);
+	    }
+	    average_rx_time = (long long unsigned int)((average_rx_time*trial)+total_rx_time)/(trial+1);
+	  }
+	  if (s_period++ == PRINTF_PERIOD) {
+	    s_period=0;
+	    LOG_I(RRH,"Average eNB RX time : %lu\tMax RX time : %lu\tMin RX time : %lu\n",average_rx_time,max_rx_time,min_rx_time);
+	  }
+	}
+	
+	memcpy(&time0,&time2,sizeof(struct timespec));
+      }
+
+      if (loopback == 1 ){
+	pthread_mutex_lock(&sync_eNB_mutex[i]);
+	sync_eNB_rx[i]--;
+	pthread_mutex_unlock(&sync_eNB_mutex[i]);
+      }
+      
+    }//for each antenna
+    
+    rx_pos += spp;    
+    pck_rx++;
+    next_rx_pos=(rx_pos+spp);
+
+    if (next_rx_pos >= samples_per_frame)
+      next_rx_pos -= samples_per_frame;  
+    if (rx_pos >= samples_per_frame)
+      rx_pos -= samples_per_frame;
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_RX, 0 );
+  }  //while (eNB_exit==0)
+  return(0);
+}
+
+
+/*! \fn void *rrh_eNB_tx_thread(void *arg)
+ * \brief this function
+ * \param[in]
+ * \param[out]
+ * \return
+ * \note
+ * @ingroup  _oai
+ */
+void *rrh_eNB_tx_thread(void *arg){
+
+  struct timespec time0a,time0,time1,time2;
+
+  rrh_module_t *dev = (rrh_module_t *)arg;
+  struct timespec time_req_1us, time_rem_1us;
+  ssize_t bytes_received;
+  int spp,i;
+  openair0_timestamp last_hw_counter=0;
+  unsigned int samples_per_frame=0;
+  uint8_t loopback=0,measurements=0;
+
+#ifdef LOWLATENCY
+  struct sched_attr attr;
+  unsigned int flags = 0;
+
+  attr.size = sizeof(attr);
+  attr.sched_flags = 0;
+  attr.sched_nice = 0;
+  attr.sched_priority = 0;
+
+  attr.sched_policy   = SCHED_DEADLINE;
+  attr.sched_runtime  = (0.1  *  100) * 10000; // 
+  attr.sched_deadline = rt_period;//0.1  *  1000000; // 
+  attr.sched_period   = rt_period;//0.1  *  1000000; // each TX/RX thread has 
+
+  if (sched_setattr(0, &attr, flags) < 0 ) {
+    perror("[SCHED] eNB TX thread: sched_setattr failed\n");
+    exit(-1);
+  }
+#endif	
+
+  tx_pos=0;
+  time_req_1us.tv_sec = 0;
+  time_req_1us.tv_nsec = 1000;
+  spp = dev->eth_dev.openair0_cfg.samples_per_packet;
+  samples_per_frame = dev->eth_dev.openair0_cfg.samples_per_frame;
+  loopback = dev->loopback;
+  measurements = dev->measurements;
+  
+  while (rrh_exit == 0) {
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX, 1 );		
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_HWCNT, hw_counter );
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_LHWCNT, last_hw_counter );
+
+    if (measurements == 1 )
+      clock_gettime(CLOCK_MONOTONIC,&time0a);
+
+    for (i=0; i<dev->eth_dev.openair0_cfg.tx_num_channels; i++){
+      if (!eNB_tx_started) {
+	eNB_tx_started=1; // set this flag to 1 to indicate that eNB started
+	if (RT_flag_eNB==1) {
+	last_hw_counter=hw_counter;
+	}
+      } else {
+	if (RT_flag_eNB==1) {
+	  if (hw_counter > last_hw_counter+1) {
+	    printf("LT");
+	  } else {
+	    while (hw_counter < last_hw_counter+1){
+	      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP, 1 );
+	      nanosleep(&time_req_1us,&time_rem_1us);
+	      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX_SLEEP, 0 );
+	    }
+	  }
+	}
+      }
+
+      if (measurements == 1 ) 
+	clock_gettime(CLOCK_MONOTONIC,&time1);
+
+      tx_eNB[i] = (void*)&tx_buffer_eNB[i][tx_pos];			
+      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TXCNT, tx_pos );
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 );
+      bytes_received = dev->eth_dev.trx_read_func(&dev->eth_dev,
+						&timestamp_eNB_tx[i],
+						tx_eNB,
+						spp,
+						i);		
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 );	
+      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TX_TS, timestamp_eNB_tx[i]&0xffffffff );
+      if (NRT_flag_eNB==1) {
+	nrt_eNB_counter[i]++;
+      }
+      prev_tx_pos=tx_pos;
+      tx_pos += spp;   
+
+      if (tx_pos >= samples_per_frame)
+	tx_pos -= samples_per_frame;
+      
+      last_hw_counter=hw_counter;
+	
+      if (loopback ==1 ) { 
+	while (sync_eNB_rx[i]==0)
+	  nanosleep(&time_req_1us,&time_rem_1us);
+	
+	pthread_mutex_lock(&sync_eNB_mutex[i]);
+	sync_eNB_rx[i]++;
+	pthread_mutex_unlock(&sync_eNB_mutex[i]);
+      }
+      
+    }
+    if (measurements == 1 ) {
+      clock_gettime(CLOCK_MONOTONIC,&time2);
+      memcpy(&time0,&time2,sizeof(struct timespec));
+    }
+
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TX, 0 );
+  }
+  return(0);
+}
+
+//needs to be fixed 
+void set_rt_period( openair0_config_t openair0_cfg){
+
+  AssertFatal(openair0_cfg.samples_per_frame > 0, "invalide samples per frame !%u\n",openair0_cfg.samples_per_frame);
+  AssertFatal(openair0_cfg.samples_per_packet > 0, "invalide samples per packets !%u\n",openair0_cfg.samples_per_packet);
+  rt_period= (double)(openair0_cfg.samples_per_packet/(openair0_cfg.samples_per_frame/10.0)*1000000);
+  AssertFatal(rt_period > 0, "invalide rt period !%u\n", rt_period);
+  LOG_I(RRH,"Real time period is set to %d ns.\n",rt_period);
+  // set the calibration parameters for ETH later: check with RK	
+  openair0_cfg.tx_delay=0; 
+  openair0_cfg.tx_forward_nsamps=0;
+
+ //only in case of  NRT with emulated UE
+  create_timer_thread();  
+  // usleep(rt_period*1000);
+
+}
+
+
+void print_dev_config(openair0_device device){
+
+  printf("\tMod_id=%d\n \tlog level=%d\n \tDL_RB=%d\n \tspf=%d\n \tsr=%f\n \tspp=%d\n \ttx_delay=%d\n \ttx_forwd_samps=%d\n \trx_ch=%d\n \ttx_ch=%d\n \trx_freq=%f\n \ttx_freq=%f\n \trxg_mode=%d\n \trx_gain=%f\n \ttx_gain=%f\n \trx_bw=%f\n \ttx_bw=%f\n \tautocal=%d\n \trem_addr %s:%d\n \tmy_addr %s:%d\n",	
+	 device.openair0_cfg.Mod_id,
+	 device.openair0_cfg.log_level,
+	 device.openair0_cfg.num_rb_dl,
+	 device.openair0_cfg.samples_per_frame,
+	 device.openair0_cfg.sample_rate,
+	 device.openair0_cfg.samples_per_packet,
+	 device.openair0_cfg.tx_delay,
+	 device.openair0_cfg.tx_forward_nsamps,
+	 device.openair0_cfg.rx_num_channels,
+	 device.openair0_cfg.tx_num_channels,
+	 device.openair0_cfg.rx_freq[0],
+	 device.openair0_cfg.tx_freq[0],
+	 device.openair0_cfg.rxg_mode[0],
+	 device.openair0_cfg.rx_gain[0],
+	 device.openair0_cfg.tx_gain[0],
+	 device.openair0_cfg.rx_bw,
+	 device.openair0_cfg.tx_bw,
+	 device.openair0_cfg.autocal[0],
+	 device.openair0_cfg.remote_ip,
+	 device.openair0_cfg.remote_port,
+	 device.openair0_cfg.my_ip,
+	 device.openair0_cfg.my_port  
+	 );
+
+}
diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c
index 84ff023e69ec5618b11193068247d61c08e431ea..44e806c82d1f9efedcd5be8008d7beb486b633c7 100644
--- a/targets/RT/USER/lte-softmodem.c
+++ b/targets/RT/USER/lte-softmodem.c
@@ -45,15 +45,11 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/mman.h>
-#include <sys/stat.h>
-#include <fcntl.h>
 #include <sched.h>
 #include <linux/sched.h>
 #include <signal.h>
 #include <execinfo.h>
 #include <getopt.h>
-#include <syscall.h>
-#include <pthread.h>  //  for gettid
 
 #include "rt_wrapper.h"
 #undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
@@ -156,6 +152,10 @@ int32_t **txdata;
 int setup_ue_buffers(PHY_VARS_UE **phy_vars_ue, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]);
 int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_cfg, openair0_rf_map rf_map[MAX_NUM_CCs]);
 
+uint16_t runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100]
+uint16_t runtime_phy_tx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100]
+double cpuf;
+
 void fill_ue_band_info(void);
 #ifdef XFORMS
 // current status is that every UE has a DL scope for a SINGLE eNB (eNB_id=0)
@@ -293,9 +293,9 @@ static int                      tx_max_power[MAX_NUM_CCs]; /* =  {0,0}*/;
 char ref[128] = "internal";
 char channels[128] = "0";
 
-unsigned int samples_per_frame = 307200;
-unsigned int tx_forward_nsamps=0;
-int tx_delay;
+//unsigned int samples_per_frame = 307200;
+//unsigned int tx_forward_nsamps=0;
+//int tx_delay;
 
 #endif
 
@@ -380,20 +380,6 @@ unsigned int build_rfdc(int dcoff_i_rxfe, int dcoff_q_rxfe)
   return (dcoff_i_rxfe + (dcoff_q_rxfe<<8));
 }
 
-#ifdef LOWLATENCY
-int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
-{
-
-  return syscall(__NR_sched_setattr, pid, attr, flags);
-}
-
-
-int sched_getattr(pid_t pid,struct sched_attr *attr,unsigned int size, unsigned int flags)
-{
-
-  return syscall(__NR_sched_getattr, pid, attr, size, flags);
-}
-#endif
 #if !defined(ENABLE_ITTI)
 void signal_handler(int sig)
 {
@@ -474,40 +460,6 @@ void exit_fun(const char* s)
   //exit (-1);
 }
 
-static int latency_target_fd = -1;
-static int32_t latency_target_value = 0;
-/* Latency trick - taken from cyclictest.c
- * if the file /dev/cpu_dma_latency exists,
- * open it and write a zero into it. This will tell
- * the power management system not to transition to
- * a high cstate (in fact, the system acts like idle=poll)
- * When the fd to /dev/cpu_dma_latency is closed, the behavior
- * goes back to the system default.
- *
- * Documentation/power/pm_qos_interface.txt
- */
-static void set_latency_target(void)
-{
-  struct stat s;
-  int ret;
-
-  if (stat("/dev/cpu_dma_latency", &s) == 0) {
-    latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
-
-    if (latency_target_fd == -1)
-      return;
-
-    ret = write(latency_target_fd, &latency_target_value, 4);
-
-    if (ret == 0) {
-      printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno));
-      close(latency_target_fd);
-      return;
-    }
-
-    printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
-  }
-}
 
 #ifdef XFORMS
 
@@ -1082,7 +1034,8 @@ static void* eNB_thread_tx( void* param )
 
   // This creates a 1ms reservation every 10ms period
   attr.sched_policy   = SCHED_DEADLINE;
-  attr.sched_runtime  = 0.9 *  1000000; // each tx thread requires 1ms to finish its job
+   //attr.sched_runtime  = 0.9 *  1000000; // each tx thread requires 1ms to finish its job
+  attr.sched_runtime  = (uint64_t) (get_runtime_tx(proc->subframe, runtime_phy_tx, target_dl_mcs,frame_parms[0]->N_RB_DL,cpuf,PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx) *  1000000); // each tx thread requires 1ms to finish its job
   attr.sched_deadline = 1   *  1000000; // each tx thread will finish within 1ms
   attr.sched_period   = 1   * 10000000; // each tx thread has a period of 10ms from the starting point
 
@@ -1196,13 +1149,20 @@ static void* eNB_thread_tx( void* param )
 
     if (proc->frame_tx==1024)
       proc->frame_tx=0;
-
     stop_meas( &softmodem_stats_tx_sf[proc->subframe] );
+#ifdef LOWLATENCY
+    if (opp_enabled){
+      if(softmodem_stats_tx_sf[proc->subframe].diff_now/(cpuf) > attr.sched_runtime){
+	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_TX_ENB, (softmodem_stats_tx_sf[proc->subframe].diff_now/cpuf - attr.sched_runtime)/1000000.0);
+      }
+    }
+#endif 
     print_meas_now(&softmodem_stats_tx_sf[proc->subframe],"eNB_TX_SF",proc->subframe, tx_time_file);
 
   }
 
-  stop_meas( &softmodem_stats_tx_sf[proc->subframe] );
+
+
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_TX0+(2*proc->subframe), 0 );
 
 #ifdef HARD_RT
@@ -1276,7 +1236,7 @@ static void* eNB_thread_rx( void* param )
 
   /* This creates a 2ms reservation every 10ms period*/
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime  = 1   *  1000000; // each rx thread must finish its job in the worst case in 2ms
+  attr.sched_runtime  = (uint64_t)(get_runtime_rx(proc->subframe, runtime_phy_rx, target_ul_mcs,frame_parms[0]->N_RB_DL,cpuf,PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx) *  1000000); // each tx thread requires 1ms to finish its job
   attr.sched_deadline = 1   *  1000000; // each rx thread will finish within 2ms
   attr.sched_period   = 1   * 10000000; // each rx thread has a period of 10ms from the starting point
 
@@ -1355,11 +1315,17 @@ static void* eNB_thread_rx( void* param )
       proc->frame_rx=0;
 
     stop_meas( &softmodem_stats_rx_sf[proc->subframe] );
+#ifdef LOWLATENCY
+    if (opp_enabled){
+      if(softmodem_stats_rx_sf[proc->subframe].diff_now/(cpuf) > attr.sched_runtime){
+	VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_RUNTIME_RX_ENB, (softmodem_stats_rx_sf[proc->subframe].diff_now/cpuf - attr.sched_runtime)/1000000.0);
+      }
+    }
+#endif  
     print_meas_now(&softmodem_stats_rx_sf[proc->subframe],"eNB_RX_SF",proc->subframe, rx_time_file);
-
   }
 
-  stop_meas( &softmodem_stats_rx_sf[proc->subframe] );
+  //stop_meas( &softmodem_stats_rx_sf[proc->subframe] );
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_PROC_RX0+(2*proc->subframe), 0 );
 
 #ifdef HARD_RT
@@ -1409,7 +1375,6 @@ void init_eNB_proc(void)
       pthread_attr_setschedpolicy (&attr_eNB_proc_rx[CC_id][i], SCHED_FIFO);
       printf("Setting OS scheduler to SCHED_FIFO for eNB [cc%d][thread%d] \n",CC_id, i);
 #endif
-
       PHY_vars_eNB_g[0][CC_id]->proc[i].instance_cnt_tx = -1;
       PHY_vars_eNB_g[0][CC_id]->proc[i].instance_cnt_rx = -1;
       PHY_vars_eNB_g[0][CC_id]->proc[i].subframe = i;
@@ -1418,8 +1383,13 @@ void init_eNB_proc(void)
       pthread_mutex_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].mutex_rx, NULL);
       pthread_cond_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].cond_tx, NULL);
       pthread_cond_init( &PHY_vars_eNB_g[0][CC_id]->proc[i].cond_rx, NULL);
+#ifndef LOWLATENCY
+      pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, &attr_eNB_proc_tx[CC_id][i], eNB_thread_tx, &PHY_vars_eNB_g[0][CC_id]->proc[i] );
+      pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_rx, &attr_eNB_proc_rx[CC_id][i], eNB_thread_rx, &PHY_vars_eNB_g[0][CC_id]->proc[i] );
+#else 
       pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, NULL, eNB_thread_tx, &PHY_vars_eNB_g[0][CC_id]->proc[i] );
       pthread_create( &PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_rx, NULL, eNB_thread_rx, &PHY_vars_eNB_g[0][CC_id]->proc[i] );
+#endif
       char name[16];
       snprintf( name, sizeof(name), "TX %d", i );
       pthread_setname_np( PHY_vars_eNB_g[0][CC_id]->proc[i].pthread_tx, name );
@@ -1574,7 +1544,7 @@ static void* eNB_thread( void* arg )
   int hw_subframe = 0; // 0..NUM_ENB_THREADS-1 => 0..9
   spp = openair0_cfg[0].samples_per_packet;
   unsigned int rx_pos = 0;
-  unsigned int tx_pos = spp*tx_delay;
+  unsigned int tx_pos = 0; //spp*tx_delay;
 #endif
   int CC_id=0;	
   struct timespec trx_time0, trx_time1, trx_time2;
@@ -1591,11 +1561,11 @@ static void* eNB_thread( void* arg )
   attr.sched_nice = 0;
   attr.sched_priority = 0;
 
-  /* This creates a .5 ms  reservation */
+  /* This creates a .2 ms  reservation */
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime  = 0.2 * 1000000;
-  attr.sched_deadline = 0.9 * 1000000;
-  attr.sched_period   = 1.0 * 1000000;
+  attr.sched_runtime  = (0.2 * 100) * 10000;
+  attr.sched_deadline = (0.9 * 100) * 10000;
+  attr.sched_period   = 1 * 1000000;
 
 
   /* pin the eNB main thread to CPU0*/
@@ -1674,7 +1644,7 @@ static void* eNB_thread( void* arg )
       first_run=0;
 
       if (diff<0)
-        diff = diff +150;
+        diff = diff + 150;
 
       LOG_I(HW,"eNB Frame %d, time %llu: slot %d, hw_slot %d, diff %d\n",frame, rt_get_time_ns(), slot, hw_slot, diff);
     }
@@ -1774,18 +1744,21 @@ static void* eNB_thread( void* arg )
 
       clock_gettime( CLOCK_MONOTONIC, &trx_time0 );
 
-      // prepare rx buffer pointers
-      for (int i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx; i++)
-        rxp[i] = (void*)&rxdata[i][rx_pos];
-
-      start_meas( &softmodem_stats_hw );
+       start_meas( &softmodem_stats_hw );
 
       openair0_timestamp timestamp;
-      rxs = openair0.trx_read_func(&openair0,
-                                   &timestamp,
-                                   rxp,
-                                   spp,
-                                   PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx);
+      int i=0;
+      // prepare rx buffer pointers
+      for (i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_rx; i++){
+        rxp[i] = (void*)&rxdata[i][rx_pos];
+	// check if nsymb_read == spp
+	// map antenna port i to the cc_id. Now use the 1:1 mapping
+	rxs = openair0.trx_read_func(&openair0,
+				     &timestamp,
+				     rxp,
+				     spp,
+				     i);
+      }
       stop_meas( &softmodem_stats_hw );
       clock_gettime( CLOCK_MONOTONIC, &trx_time1 );
 
@@ -1796,23 +1769,26 @@ static void* eNB_thread( void* arg )
       VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 );
 
       // Transmit TX buffer based on timestamp from RX
-      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 );
 
-      // prepare tx buffer pointers
-      for (int i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx; i++)
-        txp[i] = (void*)&txdata[i][tx_pos];
 
-      if (frame > 50) {
-        openair0.trx_write_func(&openair0,
-                                (timestamp+(tx_delay*spp)-tx_forward_nsamps),
+if (frame > 50) {
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1 );
+      // prepare tx buffer pointers
+      for (i=0; i<PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx; i++){
+        txp[i] = (void*)&rxdata[i][tx_pos];
+		//printf("tx_pos %d ts %d, ts_offset %d txp[i] %p, ap %d\n", tx_pos,  timestamp, (timestamp+(tx_delay*spp)-tx_forward_nsamps),txp[i], i);
+	  // if symb_written < spp ==> error 
+	  openair0.trx_write_func(&openair0,
+                                (timestamp+(openair0_cfg[card].tx_delay*spp)-openair0_cfg[card].tx_forward_nsamps),
                                 txp,
                                 spp,
-                                PHY_vars_eNB_g[0][0]->lte_frame_parms.nb_antennas_tx,
+                                i,
                                 1);
-      }
+        }
+	}
 
       VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, timestamp&0xffffffff );
-      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp+(tx_delay*spp)-tx_forward_nsamps)&0xffffffff );
+      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp+(openair0_cfg[card].tx_delay*spp)-openair0_cfg[card].tx_forward_nsamps)&0xffffffff );
 
       stop_meas( &softmodem_stats_mt );
       clock_gettime( CLOCK_MONOTONIC, &trx_time2 );
@@ -1856,12 +1832,12 @@ static void* eNB_thread( void* arg )
       rx_pos += spp;
       tx_pos += spp;
 
-      if (tx_pos >= samples_per_frame)
-        tx_pos -= samples_per_frame;
+      if (tx_pos >= openair0_cfg[card].samples_per_frame)
+        tx_pos -= openair0_cfg[card].samples_per_frame;
     }
 
-    if (rx_pos >= samples_per_frame)
-      rx_pos -= samples_per_frame;
+    if (rx_pos >= openair0_cfg[card].samples_per_frame)
+      rx_pos -= openair0_cfg[card].samples_per_frame;
 
 
 #endif // USRP
@@ -2573,8 +2549,10 @@ int main( int argc, char **argv )
       VCD_SIGNAL_DUMPER_INIT("/tmp/openair_dump_eNB.vcd");
   }
 
-  if (opp_enabled ==1)
+  if (opp_enabled ==1){
     reset_opp_meas();
+  }
+  cpuf=get_cpu_freq_GHz();
 
 #if defined(ENABLE_ITTI)
 
@@ -2585,10 +2563,11 @@ int main( int argc, char **argv )
   }
 
   itti_init(TASK_MAX, THREAD_MAX, MESSAGES_ID_MAX, tasks_info, messages_info, messages_definition_xml, itti_dump_file);
-#endif
+ 
   // initialize mscgen log after ITTI
   MSC_INIT(MSC_E_UTRAN, THREAD_MAX+TASK_MAX);
-
+#endif
+ 
 #ifdef OPENAIR2
 
   if (opt_type != OPT_NONE) {
@@ -2834,7 +2813,10 @@ int main( int argc, char **argv )
     openair_daq_vars.target_ue_ul_mcs=target_ul_mcs;
 
   }
-
+#ifndef RTAI
+  fill_modeled_runtime_table(runtime_phy_rx,runtime_phy_tx);
+  cpuf=get_cpu_freq_GHz();
+#endif 
 
   dump_frame_parms(frame_parms[0]);
 
@@ -2842,56 +2824,64 @@ int main( int argc, char **argv )
     sample_rate = 30.72e6;
     bw          = 10.0e6;
 #ifndef EXMIMO
-    openair0_cfg[0].samples_per_packet = 2048;
-    samples_per_frame = 307200; 
+    openair0_cfg[card].sample_rate=30.72e6;
+    openair0_cfg[card].samples_per_packet = 2048;
+    openair0_cfg[card].samples_per_frame = 307200; 
     openair0_cfg[card].tx_bw = 10e6;
     openair0_cfg[card].rx_bw = 10e6;
     // from usrp_time_offset
-    tx_forward_nsamps = 175;
-    tx_delay = 8;
+    openair0_cfg[card].tx_forward_nsamps = 175;
+    openair0_cfg[card].tx_delay = 8;
 #endif
   } else if(frame_parms[0]->N_RB_DL == 50) {
     sample_rate = 15.36e6;
     bw          = 5.0e6;
 #ifndef EXMIMO
-    openair0_cfg[0].samples_per_packet = 2048;
-    samples_per_frame = 153600;
+    openair0_cfg[card].sample_rate=15.36e6;
+    openair0_cfg[card].samples_per_packet = 2048;
+    openair0_cfg[card].samples_per_frame = 153600;
     openair0_cfg[card].tx_bw = 5e6;
     openair0_cfg[card].rx_bw = 5e6;
-    tx_forward_nsamps = 95;
-    tx_delay = 5;
+    openair0_cfg[card].tx_forward_nsamps = 95;
+    openair0_cfg[card].tx_delay = 5;
 #endif
   } else if (frame_parms[0]->N_RB_DL == 25) {
     sample_rate = 7.68e6;
     bw          = 2.5e6;
 #ifndef EXMIMO
-    samples_per_frame = 76800;
+    openair0_cfg[card].sample_rate=7.68e6;
+    openair0_cfg[card].samples_per_frame = 76800;
     openair0_cfg[card].tx_bw = 2.5e6;
     openair0_cfg[card].rx_bw = 2.5e6;
-    openair0_cfg[0].samples_per_packet = 1024;
+    openair0_cfg[card].samples_per_packet = 1024;
 #ifdef OAI_USRP
-    tx_forward_nsamps = 70;
-    tx_delay = 6;
+    openair0_cfg[card].tx_forward_nsamps = 70;
+    openair0_cfg[card].tx_delay = 6;
 #elif OAI_BLADERF
-    tx_forward_nsamps = 70;
-    tx_delay = 6;
+    openair0_cfg[card].tx_forward_nsamps = 70;
+    openair0_cfg[card].tx_delay = 6;
 #endif 
 #endif
   } else if (frame_parms[0]->N_RB_DL == 6) {
     sample_rate = 1.92e6;
     bw          = 0.96e6;
 #ifndef EXMIMO
-    openair0_cfg[0].samples_per_packet = 256;
-    samples_per_frame = 19200;
+    openair0_cfg[card].sample_rate=1.92e6;
+    openair0_cfg[card].samples_per_packet = 256;
+    openair0_cfg[card].samples_per_frame = 19200;
     openair0_cfg[card].tx_bw = 1.5e6;
     openair0_cfg[card].rx_bw = 1.5e6;
-    tx_forward_nsamps = 40;
-    tx_delay = 8;
+    openair0_cfg[card].tx_forward_nsamps = 40;
+    openair0_cfg[card].tx_delay = 8;
 #endif
   }
 
 #ifdef ETHERNET
 
+//calib needed
+  openair0_cfg[card].tx_delay = 0;
+  openair0_cfg[card].tx_forward_nsamps = 0;
+
   if (frame_parms[0]->N_RB_DL == 6) openair0_cfg[0].samples_per_packet = 256;
   else openair0_cfg[0].samples_per_packet = 1024;
 
@@ -2911,14 +2901,14 @@ int main( int argc, char **argv )
 
     if (UE_flag) {
       printf("ETHERNET: Configuring UE ETH for %s:%d\n",rrh_UE_ip,rrh_UE_port);
-      openair0_cfg[card].rrh_ip   = &rrh_UE_ip[0];
-      openair0_cfg[card].rrh_port = rrh_UE_port;
+      openair0_cfg[card].remote_ip   = &rrh_UE_ip[0];
+      openair0_cfg[card].remote_port = rrh_UE_port;
     } else {
       printf("ETHERNET: Configuring eNB ETH for %s:%d\n",rrh_eNB_ip,rrh_eNB_port);
-      openair0_cfg[card].rrh_ip   = &rrh_eNB_ip[0];
-      openair0_cfg[card].rrh_port = rrh_eNB_port;
+      openair0_cfg[card].remote_ip   = &rrh_eNB_ip[0];
+      openair0_cfg[card].remote_port = rrh_eNB_port;
     }
-
+openair0_cfg[card].num_rb_dl=frame_parms[0]->N_RB_DL;
 #endif
     openair0_cfg[card].sample_rate = sample_rate;
     openair0_cfg[card].tx_bw = bw;
@@ -2967,7 +2957,22 @@ int main( int argc, char **argv )
 #endif
   }
 
-  printf("Initializing openair0 ...");
+
+#ifdef ETHERNET 
+  openair0.type=ETH_IF; // not used for the moment
+  openair0_dev_init_eth(&openair0, &openair0_cfg[0]);
+#else 
+#ifdef EXMIMO
+  openair0.type=EXMIMO_IF;
+  printf("Setting the HW to EXMIMO and initializing openair0 ...\n");
+#elif OAI_USRP
+  openair0.type=USRP_IF;
+  printf("Setting the HW to USRP and initializing openair0 ...\n");
+#elif OAI_BLADERF  
+  openair0.type=BLADERF_IF;	
+  printf("Setting the HW to BLADERF and initializing openair0 ...\n");   
+#endif 
+  openair0.func_type = BBU_FUNC;
   openair0_cfg[0].log_level = glog_level;
 
 
@@ -2979,6 +2984,7 @@ int main( int argc, char **argv )
   else if (mode==loop_through_memory) {
     
   }
+#endif 
 
   printf("Done\n");
 
@@ -3296,8 +3302,12 @@ int main( int argc, char **argv )
 
 #ifndef EXMIMO
 #ifndef USRP_DEBUG
+<<<<<<< .mine
+  openair0.trx_request_func(&openair0);
+=======
   if (mode!=loop_through_memory)
     openair0.trx_start_func(&openair0);
+>>>>>>> .r7708
   //  printf("returning from usrp start streaming: %llu\n",get_usrp_time(&openair0));
 #endif
 #endif
@@ -3543,17 +3553,18 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c
 
     for (i=0; i<frame_parms->nb_antennas_rx; i++) {
       free(phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i]);
-      rxdata[i] = (int32_t*)(16 + malloc16(16+samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation
+      rxdata[i] = (int32_t*)(16 + malloc16(16+openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation
       phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i] = rxdata[i]-N_TA_offset; // N_TA offset for TDD         FIXME! N_TA_offset > 16 => access of unallocated memory
-      memset(rxdata[i], 0, samples_per_frame*sizeof(int32_t));
+      memset(rxdata[i], 0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t));
       printf("rxdata[%d] @ %p (%p) (N_TA_OFFSET %d)\n", i, phy_vars_eNB[CC_id]->lte_eNB_common_vars.rxdata[0][i],rxdata[i],N_TA_offset);
+      
     }
 
     for (i=0; i<frame_parms->nb_antennas_tx; i++) {
       free(phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i]);
-      txdata[i] = (int32_t*)(16 + malloc16(16 + samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation
+      txdata[i] = (int32_t*)(16 + malloc16(16 + openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t))); // FIXME broken memory allocation
       phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i] = txdata[i];
-      memset(txdata[i], 0, samples_per_frame*sizeof(int32_t));
+      memset(txdata[i],0, openair0_cfg[rf_map[CC_id].card].samples_per_frame*sizeof(int32_t));
       printf("txdata[%d] @ %p\n", i, phy_vars_eNB[CC_id]->lte_eNB_common_vars.txdata[0][i]);
 
     }
@@ -3564,26 +3575,27 @@ int setup_eNB_buffers(PHY_VARS_eNB **phy_vars_eNB, openair0_config_t *openair0_c
   return(0);
 }
 
-void reset_opp_meas(void)
-{
+void reset_opp_meas(void) {
   int sfn;
   reset_meas(&softmodem_stats_mt);
   reset_meas(&softmodem_stats_hw);
-
+  
   for (sfn=0; sfn < 10; sfn++) {
     reset_meas(&softmodem_stats_tx_sf[sfn]);
     reset_meas(&softmodem_stats_rx_sf[sfn]);
   }
 }
 
-void print_opp_meas(void)
-{
+void print_opp_meas(void) {
+
   int sfn=0;
   print_meas(&softmodem_stats_mt, "Main ENB Thread", NULL, NULL);
   print_meas(&softmodem_stats_hw, "HW Acquisation", NULL, NULL);
-
+  
   for (sfn=0; sfn < 10; sfn++) {
     print_meas(&softmodem_stats_tx_sf[sfn],"[eNB][total_phy_proc_tx]",NULL, NULL);
     print_meas(&softmodem_stats_rx_sf[sfn],"[eNB][total_phy_proc_rx]",NULL,NULL);
   }
 }
+
+
diff --git a/targets/RT/USER/lte-ue.c b/targets/RT/USER/lte-ue.c
index 5c9bf13504f8a81d718528c2a169b4f2af76d71a..dc624b6050a7e6cabad086c04f5481d0a3005e38 100644
--- a/targets/RT/USER/lte-ue.c
+++ b/targets/RT/USER/lte-ue.c
@@ -119,8 +119,8 @@ extern int oai_exit;
 extern int32_t **rxdata;
 extern int32_t **txdata;
 
-extern unsigned int tx_forward_nsamps;
-extern int tx_delay;
+//extern unsigned int tx_forward_nsamps;
+//extern int tx_delay;
 
 extern int rx_input_level_dBm;
 extern uint8_t exit_missed_slots;
@@ -583,9 +583,9 @@ static void *UE_thread_tx(void *arg)
 
   /* This creates a 1ms reservation every 10ms period*/
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime = 1 * 900000;  // each tx thread requires .5ms to finish its job
-  attr.sched_deadline =1 * 1000000; // each tx thread will finish within 1ms
-  attr.sched_period = 1 * 1000000; // each tx thread has a period of 1ms from the starting point
+  attr.sched_runtime  = (0.9 * 100) * 10000;  // each tx thread requires .5ms to finish its job
+  attr.sched_deadline = (1   * 100) * 10000; // each tx thread will finish within 1ms
+  attr.sched_period   = (1   * 100) * 10000; // each tx thread has a period of 1ms from the starting point
 
 
   if (sched_setattr(0, &attr, flags) < 0 ) {
@@ -732,9 +732,9 @@ static void *UE_thread_rx(void *arg)
 
   // This creates a .5ms reservation every 1ms period
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime = 1 * 900000;  // each rx thread requires 1ms to finish its job
-  attr.sched_deadline =1 * 1000000; // each rx thread will finish within 1ms
-  attr.sched_period = 1 * 1000000; // each rx thread has a period of 1ms from the starting point
+  attr.sched_runtime   = (0.9 * 100) * 10000;//900000;  // each rx thread requires 1ms to finish its job
+  attr.sched_deadline  = (1   * 100) * 10000; // each rx thread will finish within 1ms
+  attr.sched_period    = (1   * 100) * 10000; // each rx thread has a period of 1ms from the starting point
 
   if (sched_setattr(0, &attr, flags) < 0 ) {
     perror("[SCHED] UE_thread_rx : sched_setattr failed\n");
@@ -938,7 +938,7 @@ void *UE_thread(void *arg)
   static int UE_thread_retval;
   PHY_VARS_UE *UE = PHY_vars_UE_g[0][0];
   int spp = openair0_cfg[0].samples_per_packet;
-  int slot=1, frame=0, hw_subframe=0, rxpos=0, txpos=spp*tx_delay;
+  int slot=1, frame=0, hw_subframe=0, rxpos=0, txpos=spp*openair0_cfg[0].tx_delay;
   int dummy[2][spp];
   int dummy_dump = 0;
   int tx_enabled = 0;
@@ -972,9 +972,9 @@ void *UE_thread(void *arg)
 
   // This creates a .5 ms  reservation
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime  = 0.25 * 1000000;
-  attr.sched_deadline = 0.25 * 1000000;
-  attr.sched_period   = 0.5 * 1000000;
+  attr.sched_runtime  = (0.25 * 100) * 10000;
+  attr.sched_deadline = (0.25 * 100) * 10000;
+  attr.sched_period   = (0.5 * 100)  * 10000;
 
   if (sched_setattr(0, &attr, flags) < 0 ) {
     perror("[SCHED] main eNB thread: sched_setattr failed\n");
@@ -1056,7 +1056,7 @@ void *UE_thread(void *arg)
           txp[i] = (void*)&txdata[i][txpos];
 
         openair0.trx_write_func(&openair0,
-                                (timestamp+spp*tx_delay-tx_forward_nsamps),
+                                (timestamp+spp*openair0_cfg[0].tx_delay-openair0_cfg[0].tx_forward_nsamps),
                                 txp,
 				spp - ((first_rx==1) ? rx_off_diff : 0),
                                 UE->lte_frame_parms.nb_antennas_tx,
@@ -1353,9 +1353,9 @@ void *UE_thread(void *arg)
 
   // This creates a .25 ms  reservation
   attr.sched_policy = SCHED_DEADLINE;
-  attr.sched_runtime  = 0.1 * 1000000;
-  attr.sched_deadline = 0.25 * 1000000;
-  attr.sched_period   = 0.5 * 1000000;
+  attr.sched_runtime  = (0.1  * 100) * 10000;
+  attr.sched_deadline = (0.25 * 100) * 10000;
+  attr.sched_period   = (0.5  * 100) * 10000;
 
   // pin the UE main thread to CPU0
   // if (pthread_setaffinity_np(pthread_self(), sizeof(mask),&mask) <0) {
diff --git a/targets/RT/USER/rrh.gtkw b/targets/RT/USER/rrh.gtkw
new file mode 100644
index 0000000000000000000000000000000000000000..36ebadf9ca5fbe847b1a1d2914469f54eab9c46c
--- /dev/null
+++ b/targets/RT/USER/rrh.gtkw
@@ -0,0 +1,111 @@
+[*]
+[*] GTKWave Analyzer v3.3.58 (w)1999-2014 BSI
+[*] Thu Jul  9 09:53:25 2015
+[*]
+[dumpfile] "/tmp/openair_dump_eNB.vcd"
+[dumpfile_mtime] "Thu Jul  9 09:52:29 2015"
+[dumpfile_size] 170586112
+[savefile] "/home/sud/openair4G/targets/RT/USER/eNB2.gtkw"
+[timestart] 16177999000
+[size] 1535 876
+[pos] -1 -1
+*-17.749426 16178576148 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+[sst_width] 224
+[signals_width] 230
+[sst_expanded] 1
+[sst_vpaned_height] 230
+@28
+[color] 1
+functions.eNB_rx
+functions.eNB_rx_sleep
+[color] 7
+functions.trx_write
+@c00024
+variables.rxcnt[63:0]
+@28
+(0)variables.rxcnt[63:0]
+(1)variables.rxcnt[63:0]
+(2)variables.rxcnt[63:0]
+(3)variables.rxcnt[63:0]
+(4)variables.rxcnt[63:0]
+(5)variables.rxcnt[63:0]
+(6)variables.rxcnt[63:0]
+(7)variables.rxcnt[63:0]
+(8)variables.rxcnt[63:0]
+(9)variables.rxcnt[63:0]
+(10)variables.rxcnt[63:0]
+(11)variables.rxcnt[63:0]
+(12)variables.rxcnt[63:0]
+(13)variables.rxcnt[63:0]
+(14)variables.rxcnt[63:0]
+(15)variables.rxcnt[63:0]
+(16)variables.rxcnt[63:0]
+(17)variables.rxcnt[63:0]
+(18)variables.rxcnt[63:0]
+(19)variables.rxcnt[63:0]
+(20)variables.rxcnt[63:0]
+(21)variables.rxcnt[63:0]
+(22)variables.rxcnt[63:0]
+(23)variables.rxcnt[63:0]
+(24)variables.rxcnt[63:0]
+(25)variables.rxcnt[63:0]
+(26)variables.rxcnt[63:0]
+(27)variables.rxcnt[63:0]
+(28)variables.rxcnt[63:0]
+(29)variables.rxcnt[63:0]
+(30)variables.rxcnt[63:0]
+(31)variables.rxcnt[63:0]
+(32)variables.rxcnt[63:0]
+(33)variables.rxcnt[63:0]
+(34)variables.rxcnt[63:0]
+(35)variables.rxcnt[63:0]
+(36)variables.rxcnt[63:0]
+(37)variables.rxcnt[63:0]
+(38)variables.rxcnt[63:0]
+(39)variables.rxcnt[63:0]
+(40)variables.rxcnt[63:0]
+(41)variables.rxcnt[63:0]
+(42)variables.rxcnt[63:0]
+(43)variables.rxcnt[63:0]
+(44)variables.rxcnt[63:0]
+(45)variables.rxcnt[63:0]
+(46)variables.rxcnt[63:0]
+(47)variables.rxcnt[63:0]
+(48)variables.rxcnt[63:0]
+(49)variables.rxcnt[63:0]
+(50)variables.rxcnt[63:0]
+(51)variables.rxcnt[63:0]
+(52)variables.rxcnt[63:0]
+(53)variables.rxcnt[63:0]
+(54)variables.rxcnt[63:0]
+(55)variables.rxcnt[63:0]
+(56)variables.rxcnt[63:0]
+(57)variables.rxcnt[63:0]
+(58)variables.rxcnt[63:0]
+(59)variables.rxcnt[63:0]
+(60)variables.rxcnt[63:0]
+(61)variables.rxcnt[63:0]
+(62)variables.rxcnt[63:0]
+(63)variables.rxcnt[63:0]
+@1401200
+-group_end
+@28
+[color] 1
+functions.eNB_tx
+functions.eNB_tx_sleep
+[color] 7
+functions.trx_read
+@24
+variables.txcnt[63:0]
+variables.rx_ts[63:0]
+variables.tx_ts[63:0]
+variables.pck_rx[63:0]
+variables.hw_cnt_rx[63:0]
+variables.lhw_cnt_rx[63:0]
+variables.hw_frame[63:0]
+@28
+functions.eNB_tm
+@29
+functions.eNB_trx
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/targets/RT/USER/rrh_gw.c b/targets/RT/USER/rrh_gw.c
new file mode 100644
index 0000000000000000000000000000000000000000..8042be78858da09c2660efd8afe4f5a075cffde2
--- /dev/null
+++ b/targets/RT/USER/rrh_gw.c
@@ -0,0 +1,540 @@
+
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+
+/*! \file rrh_gw.h
+ * \brief top-level for the remote radio head gateway (RRH_gw) module reusing the ethernet library 
+ * \author Navid Nikaein,  Katerina Trilyraki, Raymond Knopp
+ * \date 2015
+ * \version 0.1
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning very experimental 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <time.h>
+
+#include "common_lib.h"
+#include "rrh_gw.h" // change to rrh_new.h, put externs in rrh_extern.h
+#include "rt_wrapper.h"
+
+
+#include "log_if.h"
+#include "log_extern.h"
+#include "vcd_signal_dumper.h"
+
+//#undef LOWLATENCY
+
+/******************************************************************************
+ **                               FUNCTION PROTOTYPES                        **
+ ******************************************************************************/
+static void debug_init(void);
+static void get_options(int argc, char *argv[]);
+static void print_help(void);
+static void get_RFinterfaces(void);
+static rrh_module_t new_module(unsigned int id, dev_type_t type);
+
+
+char rrh_ip[20] = "192.168.12.31"; // there is code to detect the my ip address
+int  rrh_port = 50000; // has to be an option
+
+/* log */
+int16_t           glog_level          = LOG_DEBUG;
+int16_t           glog_verbosity      = LOG_MED;
+int16_t           rrh_log_level       = LOG_INFO;
+int16_t           rrh_log_verbosity   = LOG_MED;
+int16_t           enb_log_level       = LOG_INFO;
+int16_t           enb_log_verbosity   = LOG_MED;
+int16_t           ue_log_level        = LOG_INFO;
+int16_t           ue_log_verbosity    = LOG_MED;
+
+
+/* flags definitions */
+uint8_t 		eNB_flag=0;
+uint8_t 		UE_flag=0;
+uint8_t 		EXMIMO_flag=0;
+uint8_t 		USRP_flag=0;
+uint8_t 		RT_flag=0, NRT_flag=1;
+uint8_t 		rrh_exit=0;
+
+/* Default operation as RRH:
+   - there are no eNB nor UE modules
+   - no RF device is asscociated with RRH */
+uint8_t 	    num_eNB_mod=0;
+uint8_t 	    num_UE_mod=0;
+uint8_t 	    num_EXMIMO_mod=0;
+uint8_t 	    num_USRP_mod=0;
+uint8_t             loopback_flag=0;
+uint8_t             measurements_flag=0;
+uint8_t             hardware_target=NONE_IF;
+
+unsigned int    	RT_period=0;
+rrh_module_t 	        *enb_array,*ue_array;
+pthread_mutex_t         timer_mutex;
+openair0_vtimestamp 	hw_counter=0;
+
+unsigned int		rt_period;
+
+struct itimerspec     timerspec;
+
+
+int main(int argc, char **argv) {
+
+  unsigned int i;
+  
+  /* parse input arguments */
+  get_options(argc, argv);
+  /* initialize logger and signal analyzer */
+  debug_init();
+  
+  set_latency_target();
+  /*make a graceful exit when ctrl-c is pressed */
+  signal(SIGSEGV, signal_handler);
+  signal(SIGINT, signal_handler);
+  //probe RF front end devices interfaced to RRH
+  //get_RFinterfaces();
+  
+  /* create modules based on input arguments */
+  if (eNB_flag==1){
+    
+    enb_array=(rrh_module_t*)malloc(num_eNB_mod*sizeof(rrh_module_t));	
+    // create num of modules
+    for(i=0;i<num_eNB_mod;i++){
+      /*
+	enb_array[i]=new_module(i, get_RF_interfaces(&hardware_target));
+      */
+      enb_array[i]=new_module(i, NONE_IF);		
+      create_eNB_trx_threads(&enb_array[i],RT_flag,NRT_flag);
+      LOG_I(RRH,"[eNB %d] module(s) created (%u) \n",i,num_eNB_mod);		
+    }
+    
+  }
+  if (UE_flag==1){
+    
+    ue_array=(rrh_module_t*)malloc(num_UE_mod*sizeof(rrh_module_t));	
+    // create num of modules
+    for(i=0;i<num_UE_mod;i++){
+      ue_array[i]=new_module(i, NONE_IF);
+      create_UE_trx_threads(&ue_array[i],RT_flag,NRT_flag);			
+      LOG_I(RRH,"[UE %d] module(s) created (%u)\n",i, num_UE_mod);
+    }
+  }
+
+  printf("TYPE <CTRL-C> TO TERMINATE\n");
+  
+  while (rrh_exit==0)
+    sleep(1);
+
+  //close sockets 
+  
+  return EXIT_SUCCESS;
+}
+
+
+
+
+/*!\fn openair0_device new_module (unsigned int id, dev_type_t type)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+static rrh_module_t new_module (unsigned int id, dev_type_t type) {
+
+  rrh_module_t 	  	rrh_mod;
+  openair0_config_t 	openair0_cfg;
+  
+  rrh_mod.id=id;
+  rrh_mod.loopback=loopback_flag;
+  rrh_mod.measurements=measurements_flag;
+  rrh_mod.eth_dev.func_type=RRH_FUNC;
+  /* each module is associated with an ethernet device */
+  rrh_mod.eth_dev.type=ETH_IF;
+  openair0_cfg.my_ip=&rrh_ip[0];
+  openair0_cfg.my_port=rrh_port;
+
+  //printf("rrh ip %s\n", rrh_mod.eth_dev.openair0_cfg.my_ip);
+  /* ethernet device initialization*/
+  if (openair0_dev_init_eth(&rrh_mod.eth_dev, &openair0_cfg)<0){
+    LOG_E(RRH,"Exiting, cannot initialize ethernet interface.\n");
+    exit(-1);
+  }
+  
+  /* initialize another HW device */
+  switch(type){
+  case ETH_IF:
+    LOG_E(RRH,"Error: one module is associated with one ETHERNET iterface!\n");
+    break;
+  case EXMIMO_IF:
+    rrh_mod.devs=malloc(sizeof(openair0_device));	
+    rrh_mod.devs->type=EXMIMO_IF;
+    LOG_I(RRH,"Setting RF interface to EXMIMO_IF and initializing device ...\n");	
+    /*if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){
+      printf("Exiting, cannot initialize device.\n");
+      exit(-1);
+      }*/
+    break;
+  case USRP_IF:
+    rrh_mod.devs=malloc(sizeof(openair0_device));	
+    rrh_mod.devs->type=USRP_IF;
+    LOG_I(RRH,"Setting RF interface to USRP_IF and initializing device ...\n");
+    /*if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){
+    printf("Exiting, cannot initialize device.\n");
+    exit(-1);
+    }	*/	 
+    break;
+  case BLADERF_IF:
+    rrh_mod.devs=malloc(sizeof(openair0_device));	
+    rrh_mod.devs->type=BLADERF_IF;
+    LOG_I(RRH,"Setting RF interface to BLADERF_IF and initializing device ...\n");
+    /*	if (openair0_device_init(rrh_mod.devs, &openair0_cfg)<0){
+   	printf("Exiting, cannot initialize device.\n");
+    	exit(-1);
+  	}	*/
+    break;
+  case NONE_IF:
+    rrh_mod.devs=NULL;
+    LOG_I(RRH,"Setting RF interface to NONE_IF... \n");
+    break;
+  default:
+    break;
+  }
+  return rrh_mod;
+}
+
+
+
+/*! \fn static void get_RFinterfaces(void)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+static void get_RFinterfaces(void) {
+
+  //tbd
+  EXMIMO_flag=1;
+  USRP_flag=1;
+  num_EXMIMO_mod=1;
+  num_USRP_mod=1;
+
+}
+
+
+/*!\fn void create_timer_thread(void)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void create_timer_thread() {
+  
+  int 			error_code_timer;
+  pthread_t 		main_timer_proc_thread;
+
+  LOG_I(RRH,"Creating timer thread with rt period  %d ns.\n",rt_period);
+ 
+  /* setup the timer to generate an interrupt:
+     -for the first time in (sample_per_packet/sample_rate) ns
+     -and then every (sample_per_packet/sample_rate) ns */
+  timerspec.it_value.tv_sec =     rt_period/1000000000;
+  timerspec.it_value.tv_nsec =    rt_period%1000000000;
+  timerspec.it_interval.tv_sec =  rt_period/1000000000;
+  timerspec.it_interval.tv_nsec = rt_period%1000000000;
+  
+  pthread_mutex_init(&timer_mutex,NULL);
+  
+#ifndef LOWLATENCY
+  pthread_attr_t 		attr_timer;
+  struct sched_param 	sched_param_timer;
+
+  pthread_attr_init(&attr_timer);
+  sched_param_timer.sched_priority = sched_get_priority_max(SCHED_FIFO-1);
+  pthread_attr_setschedparam(&attr_timer,&sched_param_timer);
+  pthread_attr_setschedpolicy(&attr_timer,SCHED_FIFO-1);
+  error_code_timer = pthread_create(&main_timer_proc_thread, &attr_timer, timer_proc, (void *)&timerspec);
+  LOG_D(RRH,"[SCHED] FIFO scheduling \n");		
+#else 
+  error_code_timer = pthread_create(&main_timer_proc_thread, NULL, timer_proc, (void *)&timerspec);
+  LOG_D(RRH,"[SCHED] LOWLATENCY scheduling \n");		
+#endif	
+  
+  if (error_code_timer) {
+    LOG_E(RRH,"Error while creating timer proc thread\n");
+    exit(-1);
+  }
+}
+
+
+/*! \fn void *timer_proc(void *arg)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void *timer_proc(void *arg) {
+
+  timer_t             timerid;    // timer ID for timer
+  struct itimerspec   *timer = (struct itimerspec*)arg; // the timer data structure
+  struct itimerspec   *old_value;
+
+  LOG_I(RRH,"timer thread created\n");
+    
+  
+#ifdef LOWLATENCY
+  struct sched_attr attr;
+  unsigned int flags = 0;
+  
+  attr.size = sizeof(attr);
+  attr.sched_flags = 0;
+  attr.sched_nice = 0;
+  attr.sched_priority = 0;
+  
+  attr.sched_policy   = SCHED_DEADLINE;
+  attr.sched_runtime  =  (0.1  *  100) * 10000; // 
+  attr.sched_deadline =  rt_period;//(0.1  *  100) * 10000; // 
+  attr.sched_period   =  rt_period;//(0.1  *  100) * 10000; // each TX/RX thread has, as a function of RT PERIOD ?? 
+  
+  if (sched_setattr(0, &attr, flags) < 0 ) {
+    perror("[SCHED] timer thread: sched_setattr failed\n");
+    exit(-1);
+  }
+#endif
+    //printf("Starting the timer\n");
+  if (timer_create (CLOCK_REALTIME, NULL, &timerid) == -1) {
+    fprintf (stderr, "couldn't create a timer\n");
+    perror (NULL);
+    exit (EXIT_FAILURE);
+  }
+  
+  signal(SIGALRM, timer_signal_handler);
+  // and start it!
+  timer_settime (timerid, 0, timer, old_value);
+  //timer_settime (timerid, TIMER_ABSTIME, timer, old_value);
+  
+  while (!rrh_exit) {
+    //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM, 1 );
+    sleep(1);
+    //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_eNB_TM, 0 );
+  }
+  
+  timer_delete(timerid);
+  
+  return (0);
+}
+
+
+/*! \fn void timer_signal_handler(int sig)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void timer_signal_handler(int sig) {
+  
+  if (sig == SIGALRM) {
+    pthread_mutex_lock(&timer_mutex);
+    hw_counter ++;
+    pthread_mutex_unlock(&timer_mutex);
+    VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_HW_FRAME, hw_counter);
+  }
+}
+
+
+/*! \fn void signal_handler(int sig)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+void signal_handler(int sig) {
+  
+  void *array[10];
+  size_t size;
+  
+  if (sig==SIGSEGV) {
+    // get void*'s for all entries on the stack
+    size = backtrace(array, 10);
+    
+    // print out all the frames to stderr
+    fprintf(stderr, "Error: signal %d:\n", sig);
+    backtrace_symbols_fd(array, size, 2);
+    exit(-1);
+  } else {
+    printf("trying to exit gracefully...\n");
+    rrh_exit = 1;
+  }
+}
+
+
+/*! \fn void debug_init(void)
+ * \brief this function
+ * \param[in]
+ * \param[out]
+ * \return
+ * \note
+ * @ingroup  _oai
+ */
+static void debug_init(void) {
+	
+  // log initialization
+  logInit();
+  set_glog(glog_level,  glog_verbosity);
+  
+  set_comp_log(RRH,     rrh_log_level,   rrh_log_verbosity, 1);
+  //set_comp_log(ENB_LOG, enb_log_level,   enb_log_verbosity, 1);
+  //set_comp_log(UE_LOG,  ue_log_level,    ue_log_verbosity,  1);
+  
+  // vcd initialization
+  if (ouput_vcd) {
+    
+    if (eNB_flag==1)
+      vcd_signal_dumper_init("/tmp/openair_dump_eNB.vcd");
+    if  (UE_flag==1)
+      vcd_signal_dumper_init("/tmp/openair_dump_UE.vcd");
+    
+  }
+  
+}
+
+
+/*!\fn void get_options(int argc, char *argv[])
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+static void get_options(int argc, char *argv[]) {
+
+  int 	opt;
+
+  while ((opt = getopt(argc, argv, "xvhlte:n:u:g:r:w:")) != -1) {
+    
+    switch (opt) {
+    case 'n':
+      eNB_flag=1;
+      num_eNB_mod=atoi(optarg);
+      break;
+    case 'u':
+      UE_flag=1;
+      num_UE_mod=atoi(optarg);
+      break;
+    case 'g':
+      glog_level=atoi(optarg);
+      break;
+    case 'r':
+      //rrh_log_level=atoi(optarg);
+      break;
+    case 'e':
+      //enb_log_level=atoi(optarg);
+      break;
+    case 'x':
+      rt_period = DEFAULT_PERIOD_NS;
+      RT_flag=1;
+      NRT_flag=0;
+      break;
+    case 'v':
+      /* extern from vcd */
+      ouput_vcd=1;  
+      break;
+    case 'l':
+      /*In loopback mode rrh sends back to bbu what it receives*/
+      loopback_flag=1; 
+      break;
+    case 't':
+      /*When measurements are enabled statistics related to TX/RX time are printed*/
+      measurements_flag=1; 
+      break;
+    case 'w':
+      /* force to use this target*/ 
+      //hardware_target=
+      break;
+    case 'h':
+      print_help();
+      exit(-1);
+    default: /* '?' */
+      //fprintf(stderr, "Usage: \n", argv[0]);
+      exit(-1);
+    }
+  }
+  
+}
+
+
+/*!\fn void print_help(void)
+* \brief this function
+* \param[in]
+* \param[out]
+* \return
+* \note
+* @ingroup  _oai
+*/
+static void print_help(void) {
+
+  puts("Usage: \n");
+  puts("     sudo -E chrt 99 ./rrh -n1 -g6 -v -t");
+  puts("Options:\n");
+  puts("\t -n create eNB module\n");
+  puts("\t -u create UE module\n");
+  puts("\t -g define global log level\n");
+  puts("\t -r define rrh log level\n");
+  puts("\t -e define eNB log level\n");
+  puts("\t -x enable real time bahaviour\n");
+  puts("\t -v enable vcd dump\n");
+  puts("\t -l enable loopback mode\n");
+  puts("\t -t enable measurements\n");
+  puts("\t -w force to use specified HW\n");
+  puts("\t -h display info\n");
+
+}
+
diff --git a/targets/RT/USER/rrh_gw.h b/targets/RT/USER/rrh_gw.h
new file mode 100644
index 0000000000000000000000000000000000000000..de941f1b375a6ff5580bf43b9dfdcf41c4a6f120
--- /dev/null
+++ b/targets/RT/USER/rrh_gw.h
@@ -0,0 +1,85 @@
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+
+/*! \file rrh_gw.h
+ * \brief header file for remote radio head gateway (RRH_gw) module 
+ * \author Navid Nikaein,  Katerina Trilyraki, Raymond Knopp
+ * \date 2015
+ * \version 0.1
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning very experimental 
+ */
+
+#ifndef RRH_GW_H_
+#define RRH_GW_H_
+
+#include "ethernet_lib.h"
+#include "vcd_signal_dumper.h"
+#include "assertions.h"
+
+#define DEFAULT_PERIOD_NS 200000
+#define RRH_UE_PORT 51000
+#define RRH_UE_DEST_IP "127.0.0.1"
+
+/*! \brief RRH supports two types of modules: eNB and UE
+	 each module is associated a device of type ETH_IF 
+	 and optionally with another device (USRP/BLADERF/EXMIMO) */
+typedef struct {
+  //! module id
+  uint8_t id;
+  //! loopback flag
+  uint8_t loopback;
+  //! measurement flag
+  uint8_t measurements;
+  //! module's ethernet device
+  openair0_device eth_dev;
+  //! pointer to RF module's device (pointer->since its optional)
+  openair0_device *devs;
+  
+}rrh_module_t;
+
+
+/******************************************************************************
+ **                               FUNCTION PROTOTYPES                        **
+ ******************************************************************************/
+void signal_handler(int sig);
+void timer_signal_handler(int);
+void *timer_proc(void *);
+void create_timer_thread(void);
+
+
+/******************************************************************************
+ **                               FUNCTION PROTOTYPES                        **
+ ******************************************************************************/
+void create_UE_trx_threads( rrh_module_t *dev_ue, uint8_t RT_flag, uint8_t NRT_flag);
+void create_eNB_trx_threads( rrh_module_t *dev_enb, uint8_t RT_flag, uint8_t NRT_flag);
+
+#endif
diff --git a/targets/RT/USER/rrh_gw_externs.h b/targets/RT/USER/rrh_gw_externs.h
new file mode 100644
index 0000000000000000000000000000000000000000..317af1bf4868600a1d2bab76ed8be436fc93cb98
--- /dev/null
+++ b/targets/RT/USER/rrh_gw_externs.h
@@ -0,0 +1,53 @@
+/*******************************************************************************
+    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@eurecom.fr
+
+   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
+
+ *******************************************************************************/
+/*! \file rrh_gw_extern.h
+ * \brief rrh gatewy external vars
+ * \author Navid Nikaein,  Katerina Trilyraki, Raymond Knopp
+ * \date 2015
+ * \version 0.1
+ * \company Eurecom
+ * \maintainer:  navid.nikaein@eurecom.fr
+ * \note
+ * \warning very experimental 
+ */
+
+#ifndef RRH_GW_EXTERNS_H_
+#define RRH_GW_EXTERNS_H_
+
+extern openair0_timestamp 	timestamp_UE_tx[4] ,timestamp_UE_rx[4] ,timestamp_eNB_rx[4],timestamp_eNB_tx[4];
+extern openair0_vtimestamp 	hw_counter;
+extern int32_t			UE_tx_started,UE_rx_started,eNB_rx_started ,eNB_tx_started;
+extern int32_t			nsamps_UE[4],nsamps_eNB[4];
+extern int32_t			overflow_rx_buffer_eNB[4],overflow_rx_buffer_UE[4];
+extern uint8_t			rrh_exit;
+extern int32_t			**rx_buffer_eNB, **rx_buffer_UE;
+
+extern unsigned int			rt_period;
+
+#endif 
diff --git a/targets/RT/USER/rt_wrapper.c b/targets/RT/USER/rt_wrapper.c
index f6221400b7e44d341a7d54ccb24376ed9b3950a4..62329d0f1b098ee464ed1e16c1183c7ad7ec6fb2 100644
--- a/targets/RT/USER/rt_wrapper.c
+++ b/targets/RT/USER/rt_wrapper.c
@@ -28,17 +28,51 @@
  *******************************************************************************/
 
 /*! \file rt_wrapper.h
-* \brief provides a wrapper for the timing function for real-time opeartions depending on weather RTAI is used or not
-* \author F. Kaltenberger
+* \brief provides a wrapper for the timing function, runtime calculations for real-time opeartions depending on weather RTAI or LOWLATENCY kernels are used or not
+* \author F. Kaltenberger and Navid Nikaein
 * \date 2013
 * \version 0.1
 * \company Eurecom
-* \email: florian.kaltenberger@eurecom.fr
+* \email: florian.kaltenberger@eurecom.fr, navid.nikaein@eurecom.fr
 * \note
 * \warning
 */
 
 #include "rt_wrapper.h"
+static int latency_target_fd = -1;
+static int32_t latency_target_value = 0;
+/* Latency trick - taken from cyclictest.c
+ * if the file /dev/cpu_dma_latency exists,
+ * open it and write a zero into it. This will tell
+ * the power management system not to transition to
+ * a high cstate (in fact, the system acts like idle=poll)
+ * When the fd to /dev/cpu_dma_latency is closed, the behavior
+ * goes back to the system default.
+ *
+ * Documentation/power/pm_qos_interface.txt
+ */
+void set_latency_target(void) {
+  struct stat s;
+  int ret;
+
+  if (stat("/dev/cpu_dma_latency", &s) == 0) {
+    latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
+
+    if (latency_target_fd == -1)
+      return;
+
+    ret = write(latency_target_fd, &latency_target_value, 4);
+
+    if (ret == 0) {
+      printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno));
+      close(latency_target_fd);
+      return;
+    }
+
+    printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
+  }
+}
+
 
 #ifndef RTAI
 
@@ -89,6 +123,135 @@ void check_clock(void)
   }
 }
 
+uint16_t cell_processing_dl[6]={10,15,24,42,80,112};
+uint16_t platform_processing_dl=20; // upperbound for GPP, LXC, DOCKER and KVM
+uint16_t user_processing_dl_a[6]={2,4,5,7,10,12};
+uint16_t user_processing_dl_b[6]={10, 15, 25, 70, 110, 150};
+uint16_t user_processing_dl_err[6]={20, 40, 60, 90, 120, 160};
+uint16_t protocol_processing_dl[6]={150, 250, 350, 450, 650, 800}; // assumption: max MCS 27 --> gives an upper bound for the transport block size : to be measured 
+
+uint16_t cell_processing_ul[6]={10,15,24,42,80,112};
+uint16_t platform_processing_ul=30; // upperbound for GPP, LXC, DOCKER and KVM
+uint16_t user_processing_ul_a[6]={5, 9, 12, 24, 33, 42};
+uint16_t user_processing_ul_b[6]={20, 30, 40, 76, 140, 200};
+uint16_t user_processing_ul_err[6]={15, 25, 32, 60, 80, 95};
+uint16_t protocol_processing_ul[6]={100, 200, 300, 400, 550, 700}; // assumption: max MCS 16 --> gives an upper bound for the transport block size 
+
+int fill_modeled_runtime_table(uint16_t runtime_phy_rx[29][6],
+			       uint16_t runtime_phy_tx[29][6]){
+  //double cpu_freq;
+  //cpu_freq = get_cpu_freq_GHz();
+  // target_dl_mcs
+  // target_ul_mcs
+  // frame_parms[0]->N_RB_DL
+  int i,j;
+  memset(runtime_phy_rx,0,sizeof(uint16_t)*29*6);
+  memset(runtime_phy_tx,0,sizeof(uint16_t)*29*6);
+  /* only the BBU/PHY procesing time */ 
+  for (i=0;i<29;i++){
+    for (j=0;j<6;j++){
+      runtime_phy_rx[i][j] = cell_processing_ul[j] + platform_processing_ul + user_processing_ul_err[j] + user_processing_ul_a[j]*i+  user_processing_ul_b[j];
+      runtime_phy_tx[i][j] = cell_processing_dl[j] + platform_processing_dl + user_processing_dl_err[j] + user_processing_dl_a[j]*i+  user_processing_dl_b[j];
+    }
+  }
+}
+ 
+// int runtime_upper_layers[6]; // values for different RBs
+// int runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100]
+// int runtime_phy_tx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100]
+
+// target_dl_mcs
+  // target_ul_mcs
+  // frame_parms[0]->N_RB_DL
+  //runtime_upper_layers[6]; // values for different RBs
+  // int runtime_phy_rx[29][6]; // SISO [MCS 0-28][RBs 0-5 : 6, 15, 25, 50, 75, 100] 
+  // int runtime_phy_tx[29][6]
+
+double get_runtime_tx(int tx_subframe, uint16_t runtime_phy_tx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_tx_antenna){
+   int i;	
+   double runtime;
+   //printf("cpuf =%lf  \n",cpuf);
+   switch(N_RB_DL){
+   case 6:
+     i = 0;
+     break;
+   case 15:
+     i = 1;
+     break;
+   case 25:
+     i = 2;
+     break;
+   case 50:
+     i = 3;
+     break;
+   case 75:
+     i = 4;
+     break;
+   case 100:
+     i = 5;
+     break;
+   default:
+     i = 3;
+     break;
+   }			
+   
+   runtime = ( (3.2/cpuf)*(double)runtime_phy_tx[mcs][i] + (3.2/cpuf)*(double)protocol_processing_dl[i])/1000 ;
+   printf("Setting tx %d runtime value (ms) = %lf\n",tx_subframe,runtime);
+   
+   return runtime;	
+ }
+ 
+double get_runtime_rx(int rx_subframe, uint16_t runtime_phy_rx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_rx_antenna){
+   int i;	
+   double runtime;
+   
+   //printf("N_RB_DL=%d  cpuf =%lf  \n",N_RB_DL, cpuf);
+   switch(N_RB_DL){
+   case 6:
+     i = 0;
+     break;
+   case 15:
+     i = 1;
+     break;
+   case 25:
+     i = 2;
+     break;
+   case 50:
+     i = 3;
+     break;
+   case 75:
+     i = 4;
+     break;
+   case 100:
+     i = 5;
+     break;
+   default:
+     i = 3;
+     break;
+   }			
+   
+   runtime = ((3.2/cpuf)*(double)runtime_phy_rx[mcs][i] + (3.2/cpuf)*(double)protocol_processing_ul[i])/1000 ;
+   printf("Setting rx %d runtime value (ms) = %lf \n",rx_subframe, runtime);
+   
+   return runtime;	
+}
+
+#ifdef LOWLATENCY
+int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
+{
+
+  return syscall(__NR_sched_setattr, pid, attr, flags);
+}
+
+
+int sched_getattr(pid_t pid,struct sched_attr *attr,unsigned int size, unsigned int flags)
+{
+
+  return syscall(__NR_sched_getattr, pid, attr, size, flags);
+}
+
+#endif
+
 #else
 
 int rt_sleep_ns(RTIME x)
diff --git a/targets/RT/USER/rt_wrapper.h b/targets/RT/USER/rt_wrapper.h
index e917ceaca63353f7f93a33e6027d19d776bf1ac1..abe6e79a9116c89c773f658710bef115ac698382 100644
--- a/targets/RT/USER/rt_wrapper.h
+++ b/targets/RT/USER/rt_wrapper.h
@@ -38,13 +38,20 @@
 * \warning This code will be removed when a legacy libc API becomes available.
 */
 
+
+void set_latency_target(void);
+
 #ifndef RTAI
 #include <time.h>
 #include <errno.h>
-#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <pthread.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <syscall.h>
+#include <math.h>
 
 #define RTIME long long int
 
@@ -56,6 +63,11 @@ int rt_sleep_ns (RTIME x);
 
 void check_clock(void);
 
+int fill_modeled_runtime_table(uint16_t runtime_phy_rx[29][6],uint16_t runtime_phy_tx[29][6]);
+
+double get_runtime_tx(int tx_subframe, uint16_t runtime_phy_tx[29][6],uint32_t mcs, int N_RB_DL,double cpuf,int nb_tx_antenna);
+
+double get_runtime_rx(int rx_subframe, uint16_t runtime_phy_rx[29][6], uint32_t mcs, int N_RB_DL,double cpuf,int nb_rx_antenna);
 /**
  * see https://www.kernel.org/doc/Documentation/scheduler/sched-deadline.txt  or
  * http://www.blaess.fr/christophe/2014/04/05/utiliser-un-appel-systeme-inconnu-de-la-libc/