diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 7bfb0ab611cf39710e5c934f5b89411c7173ba21..2e3b0bf743088da0e524fb10925e54aeab840145 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -243,6 +243,7 @@ add_boolean_option(UE_TIMING_TRACE     False "Activate UE timing trace")
 add_boolean_option(DISABLE_LOG_X       False "Deactivate all LOG_* macros")
 add_boolean_option(USRP_REC_PLAY       False "Enable USRP record playback mode")
 add_boolean_option(UE_NAS_USE_TUN      False "Enable UE NAS TUN device instead of ue_ip.ko")
+add_boolean_option(BASIC_SIMULATOR     False "Has to be True when building the basic simulator, False otherwise")
 
 add_boolean_option(DEBUG_CONSOLE False "makes debugging easier, disables stdout/stderr buffering")
 
@@ -563,14 +564,26 @@ add_library(oai_mobipass MODULE ${TPLIB_MOBIPASS_SOURCE} )
 get_target_property(mobipas_cflags oai_mobipass COMPILE_FLAGS)
 set_target_properties(oai_mobipass PROPERTIES COMPILE_FLAGS "${mobipass_cflags} -fvisibility=hidden")
 
+# TCP bridge libraries
+######################################################################
+
+# this one is for internal use at Eurecom and is not documented
 set(HWLIB_TCP_BRIDGE_SOURCE
   ${OPENAIR_TARGETS}/ARCH/tcp_bridge/tcp_bridge.c
   )
-add_library(oai_tcp_bridge MODULE ${HWLIB_TCP_BRIDGE_SOURCE} )
+add_library(tcp_bridge MODULE ${HWLIB_TCP_BRIDGE_SOURCE} )
 
-#get_target_property(tcp_bridge_cflags oai_tcp_bridge COMPILE_FLAGS)
-#set_target_properties(oai_tcp_bridge PROPERTIES COMPILE_FLAGS "${tcp_bridge_cflags} -fvisibility=hidden")
-set_target_properties(oai_tcp_bridge PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
+#get_target_property(tcp_bridge_cflags tcp_bridge COMPILE_FLAGS)
+#set_target_properties(tcp_bridge PROPERTIES COMPILE_FLAGS "${tcp_bridge_cflags} -fvisibility=hidden")
+set_target_properties(tcp_bridge PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
+
+# this one is to connect OAI eNB and OAI UE in the basic simulator
+# see targets/ARCH/tcp_bridge/README.tcp_bridge_oai for usage
+set(HWLIB_TCP_BRIDGE_OAI_SOURCE
+  ${OPENAIR_TARGETS}/ARCH/tcp_bridge/tcp_bridge_oai.c
+  )
+add_library(tcp_bridge_oai MODULE ${HWLIB_TCP_BRIDGE_OAI_SOURCE} )
+set_target_properties(tcp_bridge_oai PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
 
 ##########################################################
 
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index 04bd45e48e2ac4c5fa68c82e5aa7e638039c5040..2ba63a0ce40049abe035b58c5e3d7949bfb08817 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -68,6 +68,7 @@ DISABLE_LOG_X="False"
 USRP_REC_PLAY="False"
 BUILD_ECLIPSE=0
 UE_NAS_USE_TUN="False"
+BASIC_SIMULATOR=0
 trap handle_ctrl_c INT
 
 function print_help() {
@@ -159,6 +160,9 @@ Options
    Build for I/Q record-playback modes
 --ue-nas-use-tun
    Use TUN devices for the UEs instead of ue_ip.ko
+--basic-simulator
+   Generates a basic [1 UE + 1 eNB + no channel] simulator.
+   See targets/ARCH/tcp_bridge/README.tcp_bridge_oai for documentation.
 Usage (first build):
  oaisim (eNB + UE): ./build_oai -I  --oaisim -x --install-system-files
  Eurecom EXMIMO + COTS UE : ./build_oai -I  --eNB -x --install-system-files
@@ -354,6 +358,10 @@ function main() {
             UE_NAS_USE_TUN="True"
             echo_info "Enabling UE NAS TUN device usage instead of ue_ip.ko"
             shift 1;;
+        --basic-simulator)
+            BASIC_SIMULATOR=1
+            echo_info "Compiling the basic simulator"
+            shift 1;;
         -h | --help)
             print_help
             exit 1;;
@@ -919,6 +927,96 @@ fi
   else
     echo_info "10. Bypassing the Tests ..."
   fi
+
+  # basic simulator
+  #####################
+  if [ "$BASIC_SIMULATOR" = "1" ]; then
+    echo_info "Build basic simulator"
+    [ "$CLEAN" = "1" ] && rm -rf $OPENAIR_DIR/cmake_targets/basic_simulator
+    mkdir -p $OPENAIR_DIR/cmake_targets/basic_simulator
+    mkdir -p $OPENAIR_DIR/cmake_targets/basic_simulator/enb
+    mkdir -p $OPENAIR_DIR/cmake_targets/basic_simulator/ue
+
+    # enb
+
+    cmake_file=$OPENAIR_DIR/cmake_targets/basic_simulator/enb/CMakeLists.txt
+    echo "cmake_minimum_required(VERSION 2.8)"         >  $cmake_file
+    echo "set ( CMAKE_BUILD_TYPE $CMAKE_BUILD_TYPE )"  >> $cmake_file
+    echo "set ( CFLAGS_PROCESSOR_USER \"$CFLAGS_PROCESSOR_USER\" )" >>  $cmake_file
+    echo "set ( RRC_ASN1_VERSION \"${REL}\")"          >>  $cmake_file
+    echo "set ( ENABLE_VCD_FIFO $VCD_TIMING )"         >>  $cmake_file
+    echo "set ( XFORMS $XFORMS )"                      >>  $cmake_file
+    echo "set ( RF_BOARD \"OAI_USRP\")"                >>  $cmake_file
+    echo "set ( TRANSP_PRO \"None\")"                  >>  $cmake_file
+    echo "set(PACKAGE_NAME \"simulator_enb\")"         >>  $cmake_file
+    echo "set (DEADLINE_SCHEDULER \"False\" )"         >>  $cmake_file
+    echo "set (CPU_AFFINITY \"False\" )"               >>  $cmake_file
+    echo "set ( T_TRACER \"True\" )"                   >>  $cmake_file
+    echo "set (UE_AUTOTEST_TRACE $UE_AUTOTEST_TRACE)"  >>  $cmake_file
+    echo "set (UE_DEBUG_TRACE $UE_DEBUG_TRACE)"        >>  $cmake_file
+    echo "set (UE_TIMING_TRACE $UE_TIMING_TRACE)"      >>  $cmake_file
+    echo "set (DISABLE_LOG_X $DISABLE_LOG_X)"          >>  $cmake_file
+    echo "set (USRP_REC_PLAY $USRP_REC_PLAY)"          >>  $cmake_file
+    echo "set (BASIC_SIMULATOR \"True\" )"             >>  $cmake_file
+    echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt)' >> $cmake_file
+
+    echo_info "Build eNB"
+    echo_info "logs are in $dlog/basic_simulator_enb.txt"
+    {
+      cd $OPENAIR_DIR/cmake_targets/basic_simulator/enb
+      cmake .
+      make -j`nproc` coding params_libconfig tcp_bridge_oai lte-softmodem
+      ln -sf libtcp_bridge_oai.so liboai_device.so
+      cd ../..
+    } > $dlog/basic_simulator_enb.txt 2>&1
+    check_warnings "$dlog/basic_simulator_enb.txt"
+
+    # ue
+
+    echo_info "Compile conf2uedata"
+    compilations \
+        nas_sim_tools conf2uedata \
+        conf2uedata $dbin/conf2uedata
+
+    cmake_file=$OPENAIR_DIR/cmake_targets/basic_simulator/ue/CMakeLists.txt
+    echo "cmake_minimum_required(VERSION 2.8)"        >  $cmake_file
+    echo "set ( CMAKE_BUILD_TYPE $CMAKE_BUILD_TYPE )" >> $cmake_file
+    echo "set ( CFLAGS_PROCESSOR_USER \"$CFLAGS_PROCESSOR_USER\" )" >>  $cmake_file
+    echo "set ( RRC_ASN1_VERSION \"${REL}\")"         >>  $cmake_file
+    echo "set ( ENABLE_VCD_FIFO $VCD_TIMING )"        >>  $cmake_file
+    echo "set ( XFORMS $XFORMS )"                     >>  $cmake_file
+    echo "set ( RF_BOARD \"OAI_USRP\")"               >>  $cmake_file
+    echo "set ( TRANSP_PRO \"None\")"                 >>  $cmake_file
+    echo "set(PACKAGE_NAME \"simulator_ue\")"         >>  $cmake_file
+    echo "set (DEADLINE_SCHEDULER \"False\" )"        >>  $cmake_file
+    echo "set (CPU_AFFINITY \"False\" )"              >>  $cmake_file
+    echo "set ( T_TRACER \"False\" )"                 >>  $cmake_file
+    echo "set (UE_AUTOTEST_TRACE $UE_AUTOTEST_TRACE)" >>  $cmake_file
+    echo "set (UE_DEBUG_TRACE $UE_DEBUG_TRACE)"       >>  $cmake_file
+    echo "set (UE_TIMING_TRACE $UE_TIMING_TRACE)"     >>  $cmake_file
+    echo "set (DISABLE_LOG_X $DISABLE_LOG_X)"         >>  $cmake_file
+    echo "set (USRP_REC_PLAY $USRP_REC_PLAY)"         >>  $cmake_file
+    echo "set (LINUX True )"                          >>  $cmake_file
+    echo "set (PDCP_USE_NETLINK True )"               >>  $cmake_file
+    echo "set (BASIC_SIMULATOR \"True\" )"            >>  $cmake_file
+    echo "set (UE_NAS_USE_TUN \"True\" )"             >>  $cmake_file
+    echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt)' >> $cmake_file
+
+    echo_info "Build UE"
+    echo_info "logs are in $dlog/basic_simulator_ue.txt"
+    {
+      cd $OPENAIR_DIR/cmake_targets/basic_simulator/ue
+      cmake .
+      make -j`nproc` coding params_libconfig tcp_bridge_oai lte-uesoftmodem
+      ln -sf libtcp_bridge_oai.so liboai_device.so
+      cd ../..
+    } > $dlog/basic_simulator_ue.txt 2>&1
+    check_warnings "$dlog/basic_simulator_ue.txt"
+
+    echo_info "Generate UE SIM data"
+    $OPENAIR_DIR/targets/bin/conf2uedata -c $OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf -o $OPENAIR_DIR/cmake_targets/basic_simulator/ue
+
+  fi
 }
 
 main "$@"
diff --git a/common/utils/T/T.h b/common/utils/T/T.h
index 37668a830973dc668530ec685acabd0cf3ea1de5..cab246a6fff78017ab7e62df8c101f53212d8f8b 100644
--- a/common/utils/T/T.h
+++ b/common/utils/T/T.h
@@ -110,6 +110,16 @@ typedef struct {
 extern volatile int *T_freelist_head;
 extern T_cache_t *T_cache;
 
+/* When running the basic simulator, we may fill the T cache too fast.
+ * Let's not crash if it's full, just wait.
+ */
+#if BASIC_SIMULATOR
+#  define T_BASIC_SIMULATOR_WAIT \
+     while (T_cache[T_LOCAL_slot].busy) usleep(100)
+#else
+#  define T_BASIC_SIMULATOR_WAIT /* */
+#endif
+
 /* used at header of Tn, allocates buffer */
 #define T_LOCAL_DATA \
   char *T_LOCAL_buf; \
@@ -118,6 +128,7 @@ extern T_cache_t *T_cache;
   T_LOCAL_slot = __sync_fetch_and_add(T_freelist_head, 1) \
                  & (T_CACHE_SIZE - 1); \
   (void)__sync_fetch_and_and(T_freelist_head, T_CACHE_SIZE - 1); \
+  T_BASIC_SIMULATOR_WAIT; \
   if (T_cache[T_LOCAL_slot].busy) { \
     printf("%s:%d:%s: T cache is full - consider increasing its size\n", \
            __FILE__, __LINE__, __FUNCTION__); \
diff --git a/common/utils/T/T_defs.h b/common/utils/T/T_defs.h
index 16d35210f2aabf60358910dca52e7a02a1fe57c9..9961c1919e5d29cd40f120a5abedbc5f98363df6 100644
--- a/common/utils/T/T_defs.h
+++ b/common/utils/T/T_defs.h
@@ -8,10 +8,20 @@
 #define T_MAX_ARGS 16
 
 /* maximum size of a message - increase if needed */
-#define T_BUFFER_MAX (1024*64)
+#if BASIC_SIMULATOR
+   /* let's have 100 RBs functional for the basic simulator */
+#  define T_BUFFER_MAX (1024*64*2)
+#else
+#  define T_BUFFER_MAX (1024*64)
+#endif
 
 /* size of the local cache for messages (must be pow(2,something)) */
-#define T_CACHE_SIZE (8192 * 2)
+#if BASIC_SIMULATOR
+   /* we don't need much space for the basic simulator */
+#  define T_CACHE_SIZE 1024
+#else
+#  define T_CACHE_SIZE (8192 * 2)
+#endif
 
 /* maximum number of bytes a message can contain */
 #ifdef T_SEND_TIME
diff --git a/common/utils/T/local_tracer.c b/common/utils/T/local_tracer.c
index 6d0ecddd45913d1c58a813008091feaf0a2913f9..67657634c4ed86a0146f4ab0a93ea50d68510f39 100644
--- a/common/utils/T/local_tracer.c
+++ b/common/utils/T/local_tracer.c
@@ -300,6 +300,20 @@ static void forward(void *_forwarder, char *buf, int size)
   if (f->tail != NULL) f->tail->next = new;
   f->tail = new;
 
+#if BASIC_SIMULATOR
+  /* When runnng the basic simulator, the tracer may be too slow.
+   * Let's not take too much memory in the tracee and
+   * wait if there is too much data to send. 200MB is
+   * arbitrary.
+   */
+  while (f->memusage > 200 * 1024 * 1024) {
+    if (pthread_cond_signal(&f->cond)) abort();
+    if (pthread_mutex_unlock(&f->lock)) abort();
+    usleep(1000);
+    if (pthread_mutex_lock(&f->lock)) abort();
+  }
+#endif /* BASIC_SIMULATOR */
+
   f->memusage += size+4;
   /* warn every 100MB */
   if (f->memusage > f->last_warning_memusage &&
diff --git a/openair1/PHY/LTE_ESTIMATION/lte_adjust_sync.c b/openair1/PHY/LTE_ESTIMATION/lte_adjust_sync.c
index 18444076f315de40ee4ef925e31ccd93043b1d37..ec54931f8b037653833dd0b7ee3dd5ef8c5c4fd8 100644
--- a/openair1/PHY/LTE_ESTIMATION/lte_adjust_sync.c
+++ b/openair1/PHY/LTE_ESTIMATION/lte_adjust_sync.c
@@ -84,6 +84,11 @@ void lte_adjust_synch(LTE_DL_FRAME_PARMS *frame_parms,
   {
       diff = max_pos_fil - (frame_parms->nb_prefix_samples>>3);
 
+#if BASIC_SIMULATOR
+      /* a hack without which the UE does not connect (to be fixed somehow) */
+      diff = 0;
+#endif
+
       if ( abs(diff) < SYNCH_HYST )
           ue->rx_offset = 0;
       else
diff --git a/openair1/PHY/LTE_TRANSPORT/pss.c b/openair1/PHY/LTE_TRANSPORT/pss.c
index c746a331db246e2e46139d4386120389d3549557..31c60d1207bd5e6099334bdf24032f54f70b9f09 100644
--- a/openair1/PHY/LTE_TRANSPORT/pss.c
+++ b/openair1/PHY/LTE_TRANSPORT/pss.c
@@ -75,6 +75,11 @@ int generate_pss(int32_t **txdataF,
   a = (frame_parms->nb_antenna_ports_eNB == 1) ? amp: (amp*ONE_OVER_SQRT2_Q15)>>15;
   //printf("[PSS] amp=%d, a=%d\n",amp,a);
 
+#if BASIC_SIMULATOR
+  /* a hack to remove at some point (the UE doesn't synch with 100 RBs) */
+  a = (frame_parms->nb_antenna_ports_eNB == 1) ? 4*amp: (amp*ONE_OVER_SQRT2_Q15)>>15;
+#endif
+
   Nsymb = (frame_parms->Ncp==NORMAL)?14:12;
 
   for (aa=0; aa<frame_parms->nb_antenna_ports_eNB; aa++) {
diff --git a/targets/ARCH/tcp_bridge/README.tcp_bridge_oai b/targets/ARCH/tcp_bridge/README.tcp_bridge_oai
new file mode 100644
index 0000000000000000000000000000000000000000..ef0588ddee4fecf4a765a2b42b18fb047c2df96a
--- /dev/null
+++ b/targets/ARCH/tcp_bridge/README.tcp_bridge_oai
@@ -0,0 +1,41 @@
+The driver tcp_bridge_oai.c is to be used with the basic simulator.
+
+To build the basic simulator:
+    cd [openair top directory]
+    . oaienv
+    cd cmake_targets
+    ./build_oai --basic-simulator
+    cd ../common/utils/T/tracer
+    make
+
+To use it, you need to run the eNB and the UE.
+
+The eNB requires the T tracer.
+Open two terminals.
+In one terminal, run:
+    cd [openair top directory]
+    cd common/utils/T/tracer
+    ./enb -d ../T_messages.txt
+In the other terminal, run:
+    cd [openair top directory]
+    cd cmake_targets/basic_simulator/enb
+    export ENODEB=1
+    sudo -E ./lte-softmodem -O [configuration file]
+[configuration file] is just a regular configuration file.
+The eNB needs an EPC.
+
+To run the UE, open a terminal and run:
+    cd [openair top directory]
+    cd cmake_targets/basic_simulator/ue
+    sudo ./lte-uesoftmodem -C 2680000000 -r 25
+Adapt the value of -r, it has to match the value N_RB_DL in the configuration
+file of the eNB. (Same for -C which should match the value downlink_frequency
+in the configuration file.)
+
+The UE configuration (security keys) is generated from the file
+openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf. You need to configure
+your EPC to know about the UE. If you change the file
+openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf then you need to
+regenerate the configuration files using the program targets/bin/conf2uedata.
+You run it as:
+    $OPENAIR_DIR/targets/bin/conf2uedata -c $OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf -o $OPENAIR_DIR/cmake_targets/basic_simulator/ue
diff --git a/targets/ARCH/tcp_bridge/tcp_bridge_oai.c b/targets/ARCH/tcp_bridge/tcp_bridge_oai.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba6d96db93372c3407d0d1c41ff666e3a2a85606
--- /dev/null
+++ b/targets/ARCH/tcp_bridge/tcp_bridge_oai.c
@@ -0,0 +1,320 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+int fullread(int fd, void *_buf, int count)
+{
+  char *buf = _buf;
+  int ret = 0;
+  int l;
+  while (count) {
+    l = read(fd, buf, count);
+    if (l <= 0) return -1;
+    count -= l;
+    buf += l;
+    ret += l;
+  }
+  return ret;
+}
+
+int fullwrite(int fd, void *_buf, int count)
+{
+  char *buf = _buf;
+  int ret = 0;
+  int l;
+  while (count) {
+    l = write(fd, buf, count);
+    if (l <= 0) return -1;
+    count -= l;
+    buf += l;
+    ret += l;
+  }
+  return ret;
+}
+
+#include "common_lib.h"
+
+typedef struct {
+  int sock;
+  int samples_per_subframe;
+  uint64_t timestamp;
+  int is_enb;
+} tcp_bridge_state_t;
+
+void verify_connection(int fd, int is_enb)
+{
+  char c = is_enb;
+  if (fullwrite(fd, &c, 1) != 1) exit(1);
+  if (fullread(fd, &c, 1) != 1) exit(1);
+  if (c == is_enb) {
+    printf("\x1b[31mtcp_bridge: error: you have to run one UE and one eNB"
+           " (did you run 'export ENODEB=1' in the eNB terminal?)\x1b[m\n");
+    exit(1);
+  }
+}
+
+int tcp_bridge_start(openair0_device *device)
+{
+  int port = 4043;
+  tcp_bridge_state_t *tcp_bridge = device->priv;
+  int try;
+  int max_try = 5;
+
+  int sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock == -1) { perror("tcp_bridge: socket"); exit(1); }
+
+  int enable = 1;
+  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)))
+    { perror("tcp_bridge: SO_REUSEADDR"); exit(1); }
+
+  struct sockaddr_in addr = {
+    sin_family: AF_INET,
+    sin_port: htons(port),
+    sin_addr: { s_addr: INADDR_ANY }
+  };
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
+    if (errno == EADDRINUSE) goto client_mode;
+    { perror("tcp_bridge: bind"); exit(1); }
+  }
+
+  if (listen(sock, 5))
+    { perror("tcp_bridge: listen"); exit(1); }
+
+  printf("tcp_bridge: wait for connection on port %d\n", port);
+
+  socklen_t len = sizeof(addr);
+  int sock2 = accept(sock, (struct sockaddr *)&addr, &len);
+  if (sock2 == -1)
+    { perror("tcp_bridge: accept"); exit(1); }
+
+  close(sock);
+
+  tcp_bridge->sock = sock2;
+
+  printf("tcp_bridge: connection established\n");
+
+  verify_connection(sock2, tcp_bridge->is_enb);
+
+  return 0;
+
+client_mode:
+  addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+  for (try = 0; try < max_try; try++) {
+    if (try != 0) sleep(1);
+
+    printf("tcp_bridge: trying to connect to 127.0.0.1:%d (attempt %d/%d)\n",
+           port, try+1, max_try);
+
+    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+      printf("tcp_bridge: connection established\n");
+      tcp_bridge->sock = sock;
+      verify_connection(sock, tcp_bridge->is_enb);
+      return 0;
+    }
+
+    perror("tcp_bridge");
+  }
+
+  printf("tcp_bridge: connection failed\n");
+
+  exit(1);
+}
+
+int tcp_bridge_request(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
+int tcp_bridge_reply(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
+int tcp_bridge_get_stats(openair0_device* device) { return 0; }
+int tcp_bridge_reset_stats(openair0_device* device) { return 0; }
+void tcp_bridge_end(openair0_device *device) {}
+int tcp_bridge_stop(openair0_device *device) { return 0; }
+int tcp_bridge_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) { return 0; }
+int tcp_bridge_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) { return 0; }
+
+int tcp_bridge_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
+{
+  if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
+  tcp_bridge_state_t *t = device->priv;
+  int n = fullwrite(t->sock, buff[0], nsamps * 4);
+  if (n != nsamps * 4) {
+    printf("tcp_bridge: write error ret %d (wanted %d) error %s\n", n, nsamps*4, strerror(errno));
+    abort();
+  }
+  return nsamps;
+}
+
+int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
+{
+  if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
+  tcp_bridge_state_t *t = device->priv;
+  int n = fullread(t->sock, buff[0], nsamps * 4);
+  if (n != nsamps * 4) {
+    printf("tcp_bridge: read error ret %d nsamps*4 %d error %s\n", n, nsamps * 4, strerror(errno));
+    abort();
+  }
+  *timestamp = t->timestamp;
+  t->timestamp += nsamps;
+  return nsamps;
+}
+
+int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
+{
+  if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
+  tcp_bridge_state_t *t = device->priv;
+  int n;
+
+  /* In synch mode, UE does not write, but we need to
+   * send something to the eNodeB.
+   * We know that UE is in synch mode when it reads
+   * 10 subframes at a time.
+   */
+  if (nsamps == t->samples_per_subframe * 10) {
+    uint32_t b[nsamps];
+    memset(b, 0, nsamps * 4);
+    n = fullwrite(t->sock, b, nsamps * 4);
+    if (n != nsamps * 4) {
+      printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
+      abort();
+    }
+  }
+
+  return tcp_bridge_read(device, timestamp, buff, nsamps, cc);
+}
+
+/* To startup proper communcation between eNB and UE,
+ * we need to understand that:
+ * - eNodeB starts reading subframe 0
+ * - then eNodeB starts sending subframe 4
+ * and then repeats read/write for each subframe.
+ * The UE:
+ * - reads 10 subframes at a time until it is synchronized
+ * - then reads subframe n and writes subframe n+2
+ * We also want to enforce that the subframe 0 is read
+ * at the beginning of the UE RX buffer, not in the middle
+ * of it.
+ * So it means:
+ * - for the eNodeB: let it run as in normal mode (as with a B210)
+ * - for the UE, on its very first read:
+ *   - we want this read to get data from subframe 0
+ *     but the first write of eNodeB is subframe 4
+ *     so we first need to read and ignore 6 subframes
+ *   - the UE will start its TX only at the subframe 2
+ *     corresponding to the subframe 0 it just read,
+ *     so we need to write 12 subframes before anything
+ *     (the function tcp_bridge_read_ue takes care to
+ *     insert dummy TX data during the synch phase)
+ *
+ * Here is a drawing of the beginning of things to make
+ * this logic clearer.
+ *
+ * We see that eNB starts RX at subframe 0, starts TX at subfram 4,
+ * and that UE starts RX at subframe 10 and TX at subframe 12.
+ *
+ * We understand that the UE has to transmit 12 empty
+ * subframes for the eNodeB to start its processing.
+ *
+ * And because the eNodeB starts its TX at subframe 4 and we
+ * want the UE to start its RX at subframe 10, we need to
+ * read and ignore 6 subframes in the UE.
+ *
+ *          -------------------------------------------------------------------------
+ * eNB RX:  | *0* | 1 | 2 | 3 |  4  | 5 | 6 | 7 | 8 | 9 |  10  | 11 |  12  | 13 | 14 ...
+ *          -------------------------------------------------------------------------
+ *
+ *          -------------------------------------------------------------------------
+ * eNB TX:  |  0  | 1 | 2 | 3 | *4* | 5 | 6 | 7 | 8 | 9 |  10  | 11 |  12  | 13 | 14 ...
+ *          -------------------------------------------------------------------------
+ *
+ *          -------------------------------------------------------------------------
+ * UE RX:   |  0  | 1 | 2 | 3 |  4  | 5 | 6 | 7 | 8 | 9 | *10* | 11 |  12  | 13 | 14 ...
+ *          -------------------------------------------------------------------------
+ *
+ *          -------------------------------------------------------------------------
+ * UE TX:   |  0  | 1 | 2 | 3 |  4  | 5 | 6 | 7 | 8 | 9 |  10  | 11 | *12* | 13 | 14 ...
+ *          -------------------------------------------------------------------------
+ *
+ * As a final note, we do TX before RX to ensure that the eNB will
+ * get some data and send us something so there is no deadlock
+ * at the beginning of things. Hopefully the kernel buffers for
+ * the sockets are big enough so that the first (big) TX can
+ * return to user mode before the buffers are full. If this
+ * is wrong in some environment, we will need to work by smaller
+ * units of data at a time.
+ */
+int tcp_bridge_ue_first_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
+{
+  if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
+  tcp_bridge_state_t *t = device->priv;
+
+  uint32_t b[t->samples_per_subframe * 12];
+  memset(b, 0, nsamps * 4);
+  int n = fullwrite(t->sock, b, t->samples_per_subframe * 12 * 4);
+  if (n != t->samples_per_subframe * 12 * 4) {
+    printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
+    abort();
+  }
+  n = fullread(t->sock, b, t->samples_per_subframe * 6 * 4);
+  if (n != t->samples_per_subframe * 6 * 4) {
+    printf("tcp_bridge: read error ret %d error %s\n", n, strerror(errno));
+    abort();
+  }
+
+  device->trx_read_func = tcp_bridge_read_ue;
+
+  return tcp_bridge_read_ue(device, timestamp, buff, nsamps, cc);
+}
+
+__attribute__((__visibility__("default")))
+int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
+{
+  tcp_bridge_state_t *tcp_bridge = (tcp_bridge_state_t*)malloc(sizeof(tcp_bridge_state_t));
+  memset(tcp_bridge, 0, sizeof(tcp_bridge_state_t));
+
+  tcp_bridge->is_enb = getenv("ENODEB") != NULL;
+
+  printf("tcp_bridge: running as %s\n", tcp_bridge->is_enb ? "eNB" : "UE");
+
+  /* only 25, 50 or 100 PRBs handled for the moment */
+  if (openair0_cfg[0].sample_rate != 30720000 &&
+      openair0_cfg[0].sample_rate != 15360000 &&
+      openair0_cfg[0].sample_rate !=  7680000) {
+    printf("tcp_bridge: ERROR: only 25, 50 or 100 PRBs supported\n");
+    exit(1);
+  }
+
+  device->trx_start_func       = tcp_bridge_start;
+  device->trx_get_stats_func   = tcp_bridge_get_stats;
+  device->trx_reset_stats_func = tcp_bridge_reset_stats;
+  device->trx_end_func         = tcp_bridge_end;
+  device->trx_stop_func        = tcp_bridge_stop;
+  device->trx_set_freq_func    = tcp_bridge_set_freq;
+  device->trx_set_gains_func   = tcp_bridge_set_gains;
+  device->trx_write_func       = tcp_bridge_write;
+
+  if (tcp_bridge->is_enb) {
+    device->trx_read_func      = tcp_bridge_read;
+  } else {
+    device->trx_read_func      = tcp_bridge_ue_first_read;
+  }
+
+  device->priv = tcp_bridge;
+
+  switch ((int)openair0_cfg[0].sample_rate) {
+  case 30720000: tcp_bridge->samples_per_subframe = 30720; break;
+  case 15360000: tcp_bridge->samples_per_subframe = 15360; break;
+  case 7680000:  tcp_bridge->samples_per_subframe = 7680; break;
+  }
+
+  /* let's pretend to be a b2x0 */
+  device->type = USRP_B200_DEV;
+
+  device->openair0_cfg=&openair0_cfg[0];
+
+  return 0;
+}
diff --git a/targets/RT/USER/lte-ue.c b/targets/RT/USER/lte-ue.c
index 2c83ea9a04e22671ba45fd6ced7b1ddbbc0656fe..87c2dcf058af1b569086bca6edf5122a584d7b93 100644
--- a/targets/RT/USER/lte-ue.c
+++ b/targets/RT/USER/lte-ue.c
@@ -692,6 +692,9 @@ static void *UE_thread_rxn_txnp4(void *arg) {
           exit_fun("noting to add");
         }
         proc->instance_cnt_rxtx--;
+#if BASIC_SIMULATOR
+        if (pthread_cond_signal(&proc->cond_rxtx) != 0) abort();
+#endif
         if (pthread_mutex_unlock(&proc->mutex_rxtx) != 0) {
           LOG_E( PHY, "[SCHED][UE] error unlocking mutex for UE RXTX\n" );
           exit_fun("noting to add");
@@ -751,6 +754,12 @@ void *UE_thread(void *arg) {
         AssertFatal ( 0== pthread_mutex_unlock(&UE->proc.mutex_synch), "");
 
         if (is_synchronized == 0) {
+#if BASIC_SIMULATOR
+            while (!((instance_cnt_synch = UE->proc.instance_cnt_synch) < 0)) {
+              printf("ue sync not ready\n");
+              usleep(500*1000);
+            }
+#endif
             if (instance_cnt_synch < 0) {  // we can invoke the synch
                 // grab 10 ms of signal and wakeup synch thread
                 for (int i=0; i<UE->frame_parms.nb_antennas_rx; i++)
@@ -833,6 +842,17 @@ void *UE_thread(void *arg) {
                 // update thread index for received subframe
                 UE->current_thread_id[sub_frame] = thread_idx;
 
+#if BASIC_SIMULATOR
+                {
+                  int t;
+                  for (t = 0; t < 2; t++) {
+                        UE_rxtx_proc_t *proc = &UE->proc.proc_rxtx[t];
+                        pthread_mutex_lock(&proc->mutex_rxtx);
+                        while (proc->instance_cnt_rxtx >= 0) pthread_cond_wait( &proc->cond_rxtx, &proc->mutex_rxtx );
+                        pthread_mutex_unlock(&proc->mutex_rxtx);
+                  }
+                }
+#endif
                 LOG_D(PHY,"Process Subframe %d thread Idx %d \n", sub_frame, UE->current_thread_id[sub_frame]);
 
                 thread_idx++;