diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 7477ef819fbcd6c0c458dc2a43091270676e921f..a95994a8b12ccbb24e61776ffde926038f299af6 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -1484,6 +1484,13 @@ endif (${XFORMS})
 
 set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
 
+#add the T tracer
+add_boolean_option(T_TRACER True "Activate the T tracer" )
+include_directories("${OPENAIR_DIR}/common/utils/T")
+add_boolean_option(T_USE_SHARED_MEMORY True "Use shared memory to communicate with the T tracer" )
+set(T_SOURCE
+    ${OPENAIR_DIR}/common/utils/T/T.c)
+set (T_LIB "-lrt")
 
 # Hack on a test of asn1c version (already dirty)
 add_definitions(-DASN1_MINIMUM_VERSION=924)
@@ -1516,6 +1523,7 @@ add_executable(lte-softmodem
   ${RTAI_SOURCE}
   ${XFORMS_SOURCE}
   ${XFORMS_SOURCE_SOFTMODEM}
+  ${T_SOURCE}
   )
 
 target_link_libraries (lte-softmodem
@@ -1529,6 +1537,8 @@ target_link_libraries (lte-softmodem pthread m ${CONFIG_LIBRARIES} rt crypt ${CR
 target_link_libraries (lte-softmodem ${LIBBOOST_LIBRARIES})
 target_link_libraries (lte-softmodem ${LIB_LMS_LIBRARIES})
 
+target_link_libraries (lte-softmodem ${T_LIB})
+
 # lte-softmodem-nos1 is both eNB and UE implementation
 ###################################################
 add_executable(lte-softmodem-nos1
@@ -1552,6 +1562,7 @@ add_executable(lte-softmodem-nos1
   ${RTAI_SOURCE}
   ${XFORMS_SOURCE}
   ${XFORMS_SOURCE_SOFTMODEM}
+  ${T_SOURCE}
   )
 target_link_libraries (lte-softmodem-nos1
   -Wl,--start-group
@@ -1563,6 +1574,8 @@ target_link_libraries (lte-softmodem-nos1 pthread m ${CONFIG_LIBRARIES} rt crypt
 target_link_libraries (lte-softmodem-nos1 ${LIBBOOST_LIBRARIES})
 target_link_libraries (lte-softmodem-nos1  ${LIB_LMS_LIBRARIES})
 
+target_link_libraries (lte-softmodem ${T_LIB})
+
 # rrh
 ################################
 #Note: only one RF type (USRP) is currently supported for RRH
@@ -1687,6 +1700,7 @@ add_executable(oaisim_nos1
   ${HW_SOURCE}
   ${TRANSPORT_SOURCE}  
   ${XFORMS_SOURCE}
+  ${T_SOURCE}
 )
 target_include_directories(oaisim_nos1 PUBLIC  ${OPENAIR_TARGETS}/SIMU/USER)
 target_link_libraries (oaisim_nos1
@@ -1700,6 +1714,7 @@ target_link_libraries (oaisim_nos1 pthread m ${CONFIG_LIBRARIES} rt crypt ${CRYP
 #Force link with forms, regardless XFORMS option
 target_link_libraries (oaisim_nos1 forms)
 
+target_link_libraries (lte-softmodem ${T_LIB})
 
 # Unitary tests for each piece of L1: example, mbmssim is MBMS L1 simulator
 #####################################
diff --git a/common/utils/T/T.c b/common/utils/T/T.c
new file mode 100644
index 0000000000000000000000000000000000000000..13bf25b324019a8de5d69395448f7d69721e9fb3
--- /dev/null
+++ b/common/utils/T/T.c
@@ -0,0 +1,147 @@
+#include "T.h"
+#include <string.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* array used to activate/disactivate a log */
+static int T_IDs[T_NUMBER_OF_IDS];
+int *T_active = T_IDs;
+
+static int T_socket;
+
+/* T_cache
+ * - the T macro picks up the head of freelist and marks it busy
+ * - the T sender thread periodically wakes up and sends what's to be sent
+ */
+volatile int _T_freelist_head;
+volatile int *T_freelist_head = &_T_freelist_head;
+int T_busylist_head;
+T_cache_t _T_cache[T_CACHE_SIZE];
+T_cache_t *T_cache = _T_cache;
+
+static void get_message(int s)
+{
+  char t;
+  int l;
+  int id;
+
+  if (read(s, &t, 1) != 1) abort();
+printf("got mess %d\n", t);
+  switch (t) {
+  case 0:
+    /* toggle all those IDs */
+    /* optimze? (too much syscalls) */
+    if (read(s, &l, sizeof(int)) != sizeof(int)) abort();
+    while (l) {
+      if (read(s, &id, sizeof(int)) != sizeof(int)) abort();
+      T_IDs[id] = 1 - T_IDs[id];
+      l--;
+    }
+    break;
+  }
+}
+
+#ifndef T_USE_SHARED_MEMORY
+
+static void *T_send_thread(void *_)
+{
+  while (1) {
+    usleep(5000);
+    __sync_synchronize();
+    while (T_cache[T_busylist_head].busy) {
+      char *b;
+      int l;
+      /* TODO: be sure about those memory barriers - in doubt one is
+       * put here too
+       */
+      __sync_synchronize();
+      b = T_cache[T_busylist_head].buffer;
+      l = T_cache[T_busylist_head].length;
+      while (l) {
+        int done = write(T_socket, b, l);
+        if (done <= 0) {
+          printf("%s:%d:%s: error sending to socket\n",
+                 __FILE__, __LINE__, __FUNCTION__);
+          abort();
+        }
+        b += done;
+        l -= done;
+      }
+      T_cache[T_busylist_head].busy = 0;
+      T_busylist_head++;
+      T_busylist_head &= T_CACHE_SIZE - 1;
+    }
+  }
+  return NULL;
+}
+
+#endif /* T_USE_SHARED_MEMORY */
+
+static void *T_receive_thread(void *_)
+{
+  while (1) get_message(T_socket);
+  return NULL;
+}
+
+static void new_thread(void *(*f)(void *), void *data)
+{
+  pthread_t t;
+  pthread_attr_t att;
+
+  if (pthread_attr_init(&att))
+    { fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
+  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
+    { fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
+  if (pthread_create(&t, &att, f, data))
+    { fprintf(stderr, "pthread_create err\n"); exit(1); }
+  if (pthread_attr_destroy(&att))
+    { fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
+}
+
+void T_connect_to_tracer(char *addr, int port)
+{
+  struct sockaddr_in a;
+  int s;
+#ifdef T_USE_SHARED_MEMORY
+  int T_shm_fd;
+#endif
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s == -1) { perror("socket"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(addr);
+
+  if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1)
+    { perror("connect"); exit(1); }
+
+  /* wait for first message - initial list of active T events */
+  get_message(s);
+
+  T_socket = s;
+
+#ifdef T_USE_SHARED_MEMORY
+  /* setup shared memory */
+  T_shm_fd = shm_open(T_SHM_FILENAME, O_RDWR /*| O_SYNC*/, 0666);
+  shm_unlink(T_SHM_FILENAME);
+  if (T_shm_fd == -1) { perror(T_SHM_FILENAME); abort(); }
+  T_cache = mmap(NULL, T_CACHE_SIZE * sizeof(T_cache_t),
+                 PROT_READ | PROT_WRITE, MAP_SHARED, T_shm_fd, 0);
+  if (T_cache == NULL)
+    { perror(T_SHM_FILENAME); abort(); }
+  close(T_shm_fd);
+#endif
+
+#ifndef T_USE_SHARED_MEMORY
+  new_thread(T_send_thread, NULL);
+#endif
+  new_thread(T_receive_thread, NULL);
+}
diff --git a/common/utils/T/T.h b/common/utils/T/T.h
new file mode 100644
index 0000000000000000000000000000000000000000..6797213d311818097ef7db008797c8f772687906
--- /dev/null
+++ b/common/utils/T/T.h
@@ -0,0 +1,544 @@
+#ifndef _T_T_T_
+#define _T_T_T_
+
+#if T_TRACER
+
+#include <stdint.h>
+
+#include "T_defs.h"
+
+/* T message IDs */
+#include "T_IDs.h"
+
+/* known type - this is where you add new types */
+
+#define T_INT(x) int, (x)
+#define T_FLOAT(x) float, (x)
+#define T_BUFFER(x, len) buffer, ((T_buffer){addr:(x), length:(len)})
+#define T_STRING(x) string, (x)
+#define T_PRINTF(...) printf, (__VA_ARGS__)
+
+/* for each known type a T_PUT_XX macro is defined */
+
+#define T_PUT_int(argnum, val) \
+  do { \
+    int T_PUT_var = (val); \
+    T_CHECK_SIZE(sizeof(int), argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &T_PUT_var, sizeof(int)); \
+    T_LOCAL_size += sizeof(int); \
+  } while (0)
+
+#define T_PUT_float(argnum, val) \
+  do { \
+    float T_PUT_var = (val); \
+    T_CHECK_SIZE(sizeof(float), argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &T_PUT_var, sizeof(float)); \
+    T_LOCAL_size += sizeof(float); \
+  } while (0)
+
+#define T_PUT_buffer(argnum, val) \
+  do { \
+    T_buffer T_PUT_buffer_var = (val); \
+    T_PUT_int(argnum, T_PUT_buffer_var.length); \
+    T_CHECK_SIZE(T_PUT_buffer_var.length, argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, T_PUT_buffer_var.addr, \
+           T_PUT_buffer_var.length); \
+    T_LOCAL_size += T_PUT_buffer_var.length; \
+  } while (0)
+
+#define T_PUT_string(argnum, val) \
+  do { \
+    char *T_PUT_var = (val); \
+    int T_PUT_len = strlen(T_PUT_var) + 1; \
+    T_CHECK_SIZE(T_PUT_len, argnum); \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, T_PUT_var, T_PUT_len); \
+    T_LOCAL_size += T_PUT_len; \
+  } while (0)
+
+#define T_PUT_printf_deref(...) __VA_ARGS__
+
+#define T_PUT_printf(argnum, x) \
+  do { \
+    int T_PUT_len = snprintf(T_LOCAL_buf + T_LOCAL_size, \
+                             T_BUFFER_MAX - T_LOCAL_size, T_PUT_printf_deref x); \
+    if (T_PUT_len < 0) { \
+      printf("%s:%d:%s: you can't read this, or can you?", \
+               __FILE__, __LINE__, __FUNCTION__); \
+      abort(); \
+    } \
+    if (T_PUT_len >= T_BUFFER_MAX - T_LOCAL_size) { \
+      printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+               ", consider increasing T_BUFFER_MAX (%d)\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+      abort(); \
+    } \
+    T_LOCAL_size += T_PUT_len + 1; \
+  } while (0)
+
+/* structure type to detect that you pass a known type as first arg of T */
+struct T_header;
+
+/* to define message ID */
+#define T_ID(x) ((struct T_header *)(uintptr_t)(x))
+
+/* T macro tricks */
+
+#define TN(...) TN_N(__VA_ARGS__,33,32,31,30,29,28,27,26,25,24,23,22,21,\
+        20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__)
+#define TN_N(n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17,\
+        n18,n19,n20,n21,n22,n23,n24,n25,n26,n27,n28,n29,n30,n31,n32,n,...) T##n
+#define T(...) TN(__VA_ARGS__)
+
+/* type used to send arbitrary buffer data */
+typedef struct {
+  void *addr;
+  int length;
+} T_buffer;
+
+extern volatile int *T_freelist_head;
+extern T_cache_t *T_cache;
+
+/* used at header of Tn, allocates buffer */
+#define T_LOCAL_DATA \
+  char *T_LOCAL_buf; \
+  int T_LOCAL_size = 0; \
+  int T_LOCAL_slot; \
+  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); \
+  if (T_cache[T_LOCAL_slot].busy) { \
+    printf("%s:%d:%s: T cache is full - consider increasing its size\n", \
+           __FILE__, __LINE__, __FUNCTION__); \
+    abort(); \
+  } \
+  T_LOCAL_buf = T_cache[T_LOCAL_slot].buffer;
+
+#define T_ACTIVE(x) T_active[(intptr_t)x]
+
+#ifdef T_USE_SHARED_MEMORY
+
+#define T_SEND() \
+  T_cache[T_LOCAL_slot].length = T_LOCAL_size; \
+  __sync_synchronize(); \
+  T_cache[T_LOCAL_slot].busy = 1; \
+  T_send(T_LOCAL_buf, T_LOCAL_size)
+
+#else /* T_USE_SHARED_MEMORY */
+
+/* when not using shared memory, wait for send to finish */
+#define T_SEND() \
+  T_cache[T_LOCAL_slot].length = T_LOCAL_size; \
+  __sync_synchronize(); \
+  T_cache[T_LOCAL_slot].busy = 1; \
+  T_send(T_LOCAL_buf, T_LOCAL_size); \
+  while (T_cache[T_LOCAL_slot].busy) usleep(1*1000)
+
+#endif /* T_USE_SHARED_MEMORY */
+
+#define T_CHECK_SIZE(len, argnum) \
+  if (T_LOCAL_size + (len) > T_BUFFER_MAX) { \
+    printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+             ", consider increasing T_BUFFER_MAX (%d)\n", \
+             __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+    abort(); \
+  }
+
+#if 0
+#define T_PUT(type, var, argnum) \
+  do { \
+    if (T_LOCAL_size + sizeof(var) > T_BUFFER_MAX) { \
+      printf("%s:%d:%s: cannot put argument %d in T macro, not enough space" \
+               ", consider increasing T_BUFFER_MAX (%d)\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum, T_BUFFER_MAX); \
+      abort(); \
+    } \
+    memcpy(T_LOCAL_buf + T_LOCAL_size, &var, sizeof(var)); \
+    T_LOCAL_size += sizeof(var); \
+  } while (0)
+#endif
+
+#if 0
+#define T_PROCESS(x, argnum) \
+  do { \
+    T_PUT(typeof(x), x, argnum); \
+  } while (0)
+#endif
+
+#if 0
+#define T_PROCESS(x, argnum) \
+  do { \
+    if (__builtin_types_compatible_p(typeof(x), int)) \
+      { T_PUT(int, (intptr_t)(x), argnum); printf("int\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), short)) \
+      { T_PUT(short, (intptr_t)(x), argnum); printf("short\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), float)) \
+      { T_PUT(float, (x), argnum); printf("float\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), char *)) \
+      { T_PUT(char *, (char *)(intptr_t)(x), argnum); printf("char *\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), float *)) \
+      { T_PUT(float *, (float *)(intptr_t)(x), argnum); printf("float *\n"); } \
+    else if (__builtin_types_compatible_p(typeof(x), void *)) \
+      { T_PUT(void *, (void *)(intptr_t)(x), argnum); printf("void *\n"); } \
+    else { \
+      printf("%s:%d:%s: unsupported type for argument %d in T macro\n", \
+               __FILE__, __LINE__, __FUNCTION__, argnum); \
+      abort(); \
+    } \
+  } while (0)
+#endif
+
+#define T_HEADER(x) \
+  do { \
+    if (!__builtin_types_compatible_p(typeof(x), struct T_header *)) { \
+      printf("%s:%d:%s: " \
+             "bad use of T, pass a message ID as first parameter\n", \
+             __FILE__, __LINE__, __FUNCTION__); \
+      abort(); \
+    } \
+    T_PUT_int(1, (int)(uintptr_t)(x)); \
+  } while (0)
+
+#define T1(t) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T3(t,t0,x0) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T5(t,t0,x0,t1,x1) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T7(t,t0,x0,t1,x1,t2,x2) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T9(t,t0,x0,t1,x1,t2,x2,t3,x3) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T11(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T13(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T15(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T17(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T19(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T21(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T23(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T25(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T27(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T29(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T31(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13,t14,x14) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_PUT_##t14(16, x14); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T33(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15) \
+  do { \
+    if (T_ACTIVE(t)) { \
+      T_LOCAL_DATA \
+      T_HEADER(t); \
+      T_PUT_##t0(2, x0); \
+      T_PUT_##t1(3, x1); \
+      T_PUT_##t2(4, x2); \
+      T_PUT_##t3(5, x3); \
+      T_PUT_##t4(6, x4); \
+      T_PUT_##t5(7, x5); \
+      T_PUT_##t6(8, x6); \
+      T_PUT_##t7(9, x7); \
+      T_PUT_##t8(10, x8); \
+      T_PUT_##t9(11, x9); \
+      T_PUT_##t10(12, x10); \
+      T_PUT_##t11(13, x11); \
+      T_PUT_##t12(14, x12); \
+      T_PUT_##t13(15, x13); \
+      T_PUT_##t14(16, x14); \
+      T_PUT_##t15(17, x15); \
+      T_SEND(); \
+    } \
+  } while (0)
+
+#define T_CALL_ERROR \
+  do { \
+    printf("%s:%d:%s: error calling T, you have to use T_INT() or T_XX()\n", \
+           __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define T2(...) T_CALL_ERROR
+#define T4(...) T_CALL_ERROR
+#define T6(...) T_CALL_ERROR
+#define T8(...) T_CALL_ERROR
+#define T10(...) T_CALL_ERROR
+#define T12(...) T_CALL_ERROR
+#define T14(...) T_CALL_ERROR
+#define T16(...) T_CALL_ERROR
+#define T18(...) T_CALL_ERROR
+#define T20(...) T_CALL_ERROR
+#define T22(...) T_CALL_ERROR
+#define T24(...) T_CALL_ERROR
+#define T26(...) T_CALL_ERROR
+#define T28(...) T_CALL_ERROR
+#define T30(...) T_CALL_ERROR
+#define T32(...) T_CALL_ERROR
+
+#ifndef T_USE_SHARED_MEMORY
+
+#include <stdio.h>
+
+static inline void T_send(char *buf, int size)
+{
+  int i;
+return;
+  printf("sending %d bytes", size);
+  for (i = 0; i < size; i++)
+    printf("%s%2.2x", i?" ":"\n", (unsigned char)buf[i]);
+  printf("\n");
+}
+
+#else /* T_USE_SHARED_MEMORY */
+
+#define T_send(...) /**/
+
+#endif /* T_USE_SHARED_MEMORY */
+
+extern int *T_active;
+
+void T_connect_to_tracer(char *addr, int port);
+
+#else /* T_TRACER */
+
+/* if T_TRACER is not defined or is 0, the T is deactivated */
+#define T(...) /**/
+
+#endif /* T_TRACER */
+
+#endif /* _T_T_T_ */
diff --git a/common/utils/T/T_IDs.h b/common/utils/T/T_IDs.h
new file mode 100644
index 0000000000000000000000000000000000000000..eb5377186c9b92f85721fda49fddfc7dced79e9b
--- /dev/null
+++ b/common/utils/T/T_IDs.h
@@ -0,0 +1,135 @@
+/* generated file, do not edit by hand */
+
+#define T_ENB_INPUT_SIGNAL T_ID(0)
+#define T_ENB_UL_CHANNEL_ESTIMATE T_ID(1)
+#define T_PUSCH_IQ T_ID(2)
+#define T_PUCCH_1AB_IQ T_ID(3)
+#define T_PUCCH_1_ENERGY T_ID(4)
+#define T_LEGACY_MAC_INFO T_ID(5)
+#define T_LEGACY_MAC_ERROR T_ID(6)
+#define T_LEGACY_MAC_WARNING T_ID(7)
+#define T_LEGACY_MAC_DEBUG T_ID(8)
+#define T_LEGACY_MAC_TRACE T_ID(9)
+#define T_LEGACY_PHY_INFO T_ID(10)
+#define T_LEGACY_PHY_ERROR T_ID(11)
+#define T_LEGACY_PHY_WARNING T_ID(12)
+#define T_LEGACY_PHY_DEBUG T_ID(13)
+#define T_LEGACY_PHY_TRACE T_ID(14)
+#define T_LEGACY_S1AP_INFO T_ID(15)
+#define T_LEGACY_S1AP_ERROR T_ID(16)
+#define T_LEGACY_S1AP_WARNING T_ID(17)
+#define T_LEGACY_S1AP_DEBUG T_ID(18)
+#define T_LEGACY_S1AP_TRACE T_ID(19)
+#define T_LEGACY_X2AP_INFO T_ID(20)
+#define T_LEGACY_X2AP_ERROR T_ID(21)
+#define T_LEGACY_X2AP_WARNING T_ID(22)
+#define T_LEGACY_X2AP_DEBUG T_ID(23)
+#define T_LEGACY_X2AP_TRACE T_ID(24)
+#define T_LEGACY_RRC_INFO T_ID(25)
+#define T_LEGACY_RRC_ERROR T_ID(26)
+#define T_LEGACY_RRC_WARNING T_ID(27)
+#define T_LEGACY_RRC_DEBUG T_ID(28)
+#define T_LEGACY_RRC_TRACE T_ID(29)
+#define T_LEGACY_RLC_INFO T_ID(30)
+#define T_LEGACY_RLC_ERROR T_ID(31)
+#define T_LEGACY_RLC_WARNING T_ID(32)
+#define T_LEGACY_RLC_DEBUG T_ID(33)
+#define T_LEGACY_RLC_TRACE T_ID(34)
+#define T_LEGACY_PDCP_INFO T_ID(35)
+#define T_LEGACY_PDCP_ERROR T_ID(36)
+#define T_LEGACY_PDCP_WARNING T_ID(37)
+#define T_LEGACY_PDCP_DEBUG T_ID(38)
+#define T_LEGACY_PDCP_TRACE T_ID(39)
+#define T_LEGACY_ENB_APP_INFO T_ID(40)
+#define T_LEGACY_ENB_APP_ERROR T_ID(41)
+#define T_LEGACY_ENB_APP_WARNING T_ID(42)
+#define T_LEGACY_ENB_APP_DEBUG T_ID(43)
+#define T_LEGACY_ENB_APP_TRACE T_ID(44)
+#define T_LEGACY_SCTP_INFO T_ID(45)
+#define T_LEGACY_SCTP_ERROR T_ID(46)
+#define T_LEGACY_SCTP_WARNING T_ID(47)
+#define T_LEGACY_SCTP_DEBUG T_ID(48)
+#define T_LEGACY_SCTP_TRACE T_ID(49)
+#define T_LEGACY_UDP__INFO T_ID(50)
+#define T_LEGACY_UDP__ERROR T_ID(51)
+#define T_LEGACY_UDP__WARNING T_ID(52)
+#define T_LEGACY_UDP__DEBUG T_ID(53)
+#define T_LEGACY_UDP__TRACE T_ID(54)
+#define T_LEGACY_NAS_INFO T_ID(55)
+#define T_LEGACY_NAS_ERROR T_ID(56)
+#define T_LEGACY_NAS_WARNING T_ID(57)
+#define T_LEGACY_NAS_DEBUG T_ID(58)
+#define T_LEGACY_NAS_TRACE T_ID(59)
+#define T_LEGACY_HW_INFO T_ID(60)
+#define T_LEGACY_HW_ERROR T_ID(61)
+#define T_LEGACY_HW_WARNING T_ID(62)
+#define T_LEGACY_HW_DEBUG T_ID(63)
+#define T_LEGACY_HW_TRACE T_ID(64)
+#define T_LEGACY_EMU_INFO T_ID(65)
+#define T_LEGACY_EMU_ERROR T_ID(66)
+#define T_LEGACY_EMU_WARNING T_ID(67)
+#define T_LEGACY_EMU_DEBUG T_ID(68)
+#define T_LEGACY_EMU_TRACE T_ID(69)
+#define T_LEGACY_OTG_INFO T_ID(70)
+#define T_LEGACY_OTG_ERROR T_ID(71)
+#define T_LEGACY_OTG_WARNING T_ID(72)
+#define T_LEGACY_OTG_DEBUG T_ID(73)
+#define T_LEGACY_OTG_TRACE T_ID(74)
+#define T_LEGACY_OCG_INFO T_ID(75)
+#define T_LEGACY_OCG_ERROR T_ID(76)
+#define T_LEGACY_OCG_WARNING T_ID(77)
+#define T_LEGACY_OCG_DEBUG T_ID(78)
+#define T_LEGACY_OCG_TRACE T_ID(79)
+#define T_LEGACY_OCM_INFO T_ID(80)
+#define T_LEGACY_OCM_ERROR T_ID(81)
+#define T_LEGACY_OCM_WARNING T_ID(82)
+#define T_LEGACY_OCM_DEBUG T_ID(83)
+#define T_LEGACY_OCM_TRACE T_ID(84)
+#define T_LEGACY_OIP_INFO T_ID(85)
+#define T_LEGACY_OIP_ERROR T_ID(86)
+#define T_LEGACY_OIP_WARNING T_ID(87)
+#define T_LEGACY_OIP_DEBUG T_ID(88)
+#define T_LEGACY_OIP_TRACE T_ID(89)
+#define T_LEGACY_OMG_INFO T_ID(90)
+#define T_LEGACY_OMG_ERROR T_ID(91)
+#define T_LEGACY_OMG_WARNING T_ID(92)
+#define T_LEGACY_OMG_DEBUG T_ID(93)
+#define T_LEGACY_OMG_TRACE T_ID(94)
+#define T_LEGACY_OPT_INFO T_ID(95)
+#define T_LEGACY_OPT_ERROR T_ID(96)
+#define T_LEGACY_OPT_WARNING T_ID(97)
+#define T_LEGACY_OPT_DEBUG T_ID(98)
+#define T_LEGACY_OPT_TRACE T_ID(99)
+#define T_LEGACY_GTPU_INFO T_ID(100)
+#define T_LEGACY_GTPU_ERROR T_ID(101)
+#define T_LEGACY_GTPU_WARNING T_ID(102)
+#define T_LEGACY_GTPU_DEBUG T_ID(103)
+#define T_LEGACY_GTPU_TRACE T_ID(104)
+#define T_LEGACY_TMR_INFO T_ID(105)
+#define T_LEGACY_TMR_ERROR T_ID(106)
+#define T_LEGACY_TMR_WARNING T_ID(107)
+#define T_LEGACY_TMR_DEBUG T_ID(108)
+#define T_LEGACY_TMR_TRACE T_ID(109)
+#define T_LEGACY_OSA_INFO T_ID(110)
+#define T_LEGACY_OSA_ERROR T_ID(111)
+#define T_LEGACY_OSA_WARNING T_ID(112)
+#define T_LEGACY_OSA_DEBUG T_ID(113)
+#define T_LEGACY_OSA_TRACE T_ID(114)
+#define T_LEGACY_component_INFO T_ID(115)
+#define T_LEGACY_component_ERROR T_ID(116)
+#define T_LEGACY_component_WARNING T_ID(117)
+#define T_LEGACY_component_DEBUG T_ID(118)
+#define T_LEGACY_component_TRACE T_ID(119)
+#define T_LEGACY_componentP_INFO T_ID(120)
+#define T_LEGACY_componentP_ERROR T_ID(121)
+#define T_LEGACY_componentP_WARNING T_ID(122)
+#define T_LEGACY_componentP_DEBUG T_ID(123)
+#define T_LEGACY_componentP_TRACE T_ID(124)
+#define T_LEGACY_CLI_INFO T_ID(125)
+#define T_LEGACY_CLI_ERROR T_ID(126)
+#define T_LEGACY_CLI_WARNING T_ID(127)
+#define T_LEGACY_CLI_DEBUG T_ID(128)
+#define T_LEGACY_CLI_TRACE T_ID(129)
+#define T_first T_ID(130)
+#define T_buf_test T_ID(131)
+#define T_NUMBER_OF_IDS 132
diff --git a/common/utils/T/T_defs.h b/common/utils/T/T_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..5aca54e8d621fc30d62cc28ee3c360c6ba01bf42
--- /dev/null
+++ b/common/utils/T/T_defs.h
@@ -0,0 +1,18 @@
+#ifndef _T_defs_H_
+#define _T_defs_H_
+
+/* maximum size of a message - increase if needed */
+#define T_BUFFER_MAX (1024*64)
+
+/* size of the local cache for messages (must be pow(2,something)) */
+#define T_CACHE_SIZE (8192 * 2)
+
+typedef struct {
+  volatile int busy;
+  char buffer[T_BUFFER_MAX];
+  int length;
+} T_cache_t;
+
+#define T_SHM_FILENAME "/T_shm_segment"
+
+#endif /* _T_defs_H_ */
diff --git a/common/utils/T/database.c b/common/utils/T/database.c
new file mode 100644
index 0000000000000000000000000000000000000000..59d8cbdc852cca264fe2222bb8792954cd920c0d
--- /dev/null
+++ b/common/utils/T/database.c
@@ -0,0 +1,325 @@
+#include "defs.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+typedef struct {
+  char *name;
+  char *desc;
+  char **groups;
+  int size;
+  int id;
+} id;
+
+typedef struct {
+  char *name;
+  char **ids;
+  int size;
+} group;
+
+typedef struct {
+  char *name;
+  id *i;
+  int isize;
+  group *g;
+  int gsize;
+} database;
+
+typedef struct {
+  char *data;
+  int size;
+  int maxsize;
+} buffer;
+
+typedef struct {
+  buffer name;
+  buffer value;
+} parser;
+
+void put(buffer *b, int c)
+{
+  if (b->size == b->maxsize) {
+    b->maxsize += 256;
+    b->data = realloc(b->data, b->maxsize);
+    if (b->data == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  b->data[b->size] = c;
+  b->size++;
+}
+
+void smash_spaces(FILE *f)
+{
+  int c;
+  while (1) {
+    c = fgetc(f);
+    if (isspace(c)) continue;
+    if (c == ' ') continue;
+    if (c == '\t') continue;
+    if (c == '\n') continue;
+    if (c == 10 || c == 13) continue;
+    if (c == '#') {
+      while (1) {
+        c = fgetc(f);
+        if (c == '\n' || c == EOF) break;
+      }
+      continue;
+    }
+    break;
+  }
+  if (c != EOF) ungetc(c, f);
+}
+
+void get_line(parser *p, FILE *f, char **name, char **value)
+{
+  int c;
+  p->name.size = 0;
+  p->value.size = 0;
+  *name = NULL;
+  *value = NULL;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == '=' || isspace(c) || c == EOF))
+    { put(&p->name, c); c = fgetc(f); }
+  if (c == EOF) return;
+  put(&p->name, 0);
+  while (!(c == EOF || c == '=')) c = fgetc(f);
+  if (c == EOF) return;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == 10 || c == 13 || c == EOF))
+    { put(&p->value, c); c = fgetc(f); }
+  put(&p->value, 0);
+  if (p->name.size <= 1) return;
+  if (p->value.size <= 1) return;
+  *name = p->name.data;
+  *value = p->value.data;
+}
+
+int group_cmp(const void *_p1, const void *_p2)
+{
+  const group *p1 = _p1;
+  const group *p2 = _p2;
+  return strcmp(p1->name, p2->name);
+}
+
+int id_cmp(const void *_p1, const void *_p2)
+{
+  const id *p1 = _p1;
+  const id *p2 = _p2;
+  return strcmp(p1->name, p2->name);
+}
+
+int string_cmp(const void *_p1, const void *_p2)
+{
+  char * const *p1 = _p1;
+  char * const *p2 = _p2;
+  return strcmp(*p1, *p2);
+}
+
+id *add_id(database *r, char *idname, int i)
+{
+  if (bsearch(&(id){name:idname}, r->i, r->isize, sizeof(id), id_cmp) != NULL)
+    { printf("ERROR: ID '%s' declared more than once\n", idname); exit(1); }
+  if ((r->isize & 1023) == 0) {
+    r->i = realloc(r->i, (r->isize + 1024) * sizeof(id));
+    if (r->i == NULL) { printf("out of memory\n"); exit(1); }
+  }
+  r->i[r->isize].name = strdup(idname);
+  if (r->i[r->isize].name == NULL) { printf("out of memory\n"); exit(1); }
+  r->i[r->isize].desc = NULL;
+  r->i[r->isize].groups = NULL;
+  r->i[r->isize].size = 0;
+  r->i[r->isize].id = i;
+  r->isize++;
+  qsort(r->i, r->isize, sizeof(id), id_cmp);
+  return (id*)bsearch(&(id){name:idname}, r->i, r->isize, sizeof(id), id_cmp);
+}
+
+group *get_group(database *r, char *group_name)
+{
+  group *ret;
+
+  ret = bsearch(&(group){name:group_name},
+                r->g, r->gsize, sizeof(group), group_cmp);
+  if (ret != NULL) return ret;
+
+  if ((r->gsize & 1023) == 0) {
+    r->g = realloc(r->g, (r->gsize + 1024) * sizeof(group));
+    if (r->g == NULL) abort();
+  }
+  r->g[r->gsize].name = strdup(group_name);
+  if (r->g[r->gsize].name == NULL) abort();
+  r->g[r->gsize].ids = NULL;
+  r->g[r->gsize].size = 0;
+  r->gsize++;
+
+  qsort(r->g, r->gsize, sizeof(group), group_cmp);
+
+  return bsearch(&(group){name:group_name},
+                 r->g, r->gsize, sizeof(group), group_cmp);
+}
+
+void group_add_id(group *g, char *id)
+{
+  if ((g->size & 1023) == 0) {
+    g->ids = realloc(g->ids, (g->size + 1024) * sizeof(char *));
+    if (g->ids == NULL) abort();
+  }
+  g->ids[g->size] = id;
+  g->size++;
+}
+
+void id_add_group(id *i, char *group)
+{
+  char *g = bsearch(&group, i->groups, i->size, sizeof(char *), string_cmp);
+  if (g != NULL) return;
+
+  if ((i->size & 1023) == 0) {
+    i->groups = realloc(i->groups, (i->size+1024) * sizeof(char *));
+    if (i->groups == NULL) abort();
+  }
+  i->groups[i->size] = group;
+  i->size++;
+  qsort(i->groups, i->size, sizeof(char *), string_cmp);
+}
+
+void add_groups(database *r, id *i, char *groups)
+{
+  group *g;
+  if (i == NULL) {printf("ERROR: GROUP line before ID line\n");exit(1);}
+  while (1) {
+    char *start = groups;
+    char *end = start;
+    while (!isspace(*end) && *end != ':' && *end != 0) end++;
+    if (end == start) {
+      printf("bad group line: groups are seperated by ':'\n");
+      abort();
+    }
+    if (*end == 0) end = NULL; else *end = 0;
+
+    g = get_group(r, start);
+    group_add_id(g, i->name);
+    id_add_group(i, g->name);
+
+    if (end == NULL) break;
+    end++;
+    while ((isspace(*end) || *end == ':') && *end != 0) end++;
+    if (*end == 0) break;
+    groups = end;
+  }
+}
+
+void add_desc(id *i, char *desc)
+{
+  if (i == NULL) {printf("ERROR: DESC line before ID line\n");exit(1);}
+  i->desc = strdup(desc); if (i->desc == NULL) abort();
+}
+
+void *parse_database(char *filename)
+{
+  FILE *in;
+  parser p;
+  database *r;
+  char *name, *value;
+  id *last_id = NULL;
+  int i;
+
+  r = calloc(1, sizeof(*r)); if (r == NULL) abort();
+  memset(&p, 0, sizeof(p));
+
+  r->name = strdup(filename); if (r->name == NULL) abort();
+
+  in = fopen(filename, "r"); if (in == NULL) { perror(filename); abort(); }
+
+  i = 0;
+
+  while (1) {
+    get_line(&p, in, &name, &value);
+    if (name == NULL) break;
+//printf("%s %s\n", name, value);
+    if (!strcmp(name, "ID")) { last_id = add_id(r, value, i); i++; }
+    if (!strcmp(name, "GROUP")) add_groups(r, last_id, value);
+    if (!strcmp(name, "DESC")) add_desc(last_id, value);
+  }
+
+  fclose(in);
+  free(p.name.data);
+  free(p.value.data);
+
+  return r;
+}
+
+void dump_database(void *_d)
+{
+  database *d = _d;
+  int i;
+
+  printf("database %s: %d IDs, %d GROUPs\n", d->name, d->isize, d->gsize);
+  for (i = 0; i < d->isize; i++) {
+    int j;
+    printf("ID %s [%s] [in %d group%s]\n",
+           d->i[i].name, d->i[i].desc ? d->i[i].desc : "",
+           d->i[i].size, d->i[i].size > 1 ? "s" : "");
+    for (j = 0; j < d->i[i].size; j++)
+      printf("    in GROUP: %s\n", d->i[i].groups[j]);
+  }
+  for (i = 0; i < d->gsize; i++) {
+    int j;
+    printf("GROUP %s [size %d]\n", d->g[i].name, d->g[i].size);
+    for (j = 0; j < d->g[i].size; j++)
+      printf("  contains ID: %s\n", d->g[i].ids[j]);
+  }
+}
+
+void list_ids(void *_d)
+{
+  database *d = _d;
+  int i;
+  for (i = 0; i < d->isize; i++) printf("%s\n", d->i[i].name);
+}
+
+void list_groups(void *_d)
+{
+  database *d = _d;
+  int i;
+  for (i = 0; i < d->gsize; i++) printf("%s\n", d->g[i].name);
+}
+
+static int onoff_id(database *d, char *name, int *a, int onoff)
+{
+  id *i;
+  i = bsearch(&(id){name:name}, d->i, d->isize, sizeof(id), id_cmp);
+  if (i == NULL) return 0;
+  a[i->id] = onoff;
+  printf("turning %s %s\n", onoff ? "ON" : "OFF", name);
+  return 1;
+}
+
+static int onoff_group(database *d, char *name, int *a, int onoff)
+{
+  group *g;
+  int i;
+  g = bsearch(&(group){name:name}, d->g, d->gsize, sizeof(group), group_cmp);
+  if (g == NULL) return 0;
+  for (i = 0; i < g->size; i++) onoff_id(d, g->ids[i], a, onoff);
+  return 1;
+}
+
+void on_off(void *_d, char *item, int *a, int onoff)
+{
+  int done;
+  database *d = _d;
+  int i;
+  if (item == NULL) {
+    for (i = 0; i < d->isize; i++) a[i] = onoff;
+    printf("turning %s all traces\n", onoff ? "ON" : "OFF");
+    return;
+  }
+  done = onoff_group(d, item, a, onoff);
+  done += onoff_id(d, item, a, onoff);
+  if (done == 0) {
+    printf("ERROR: ID/group '%s' not found in database\n", item);
+    exit(1);
+  }
+}
diff --git a/common/utils/T/defs.h b/common/utils/T/defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..69b379ce0b3c3963475622ec3ed6d66b472ed97b
--- /dev/null
+++ b/common/utils/T/defs.h
@@ -0,0 +1,30 @@
+#ifndef _TRACER_DEFS_H_
+#define _TRACER_DEFS_H_
+
+/* types of plots */
+#define PLOT_VS_TIME   0
+#define PLOT_IQ_POINTS 1
+#define PLOT_MINMAX    2
+
+void new_thread(void *(*f)(void *), void *data);
+
+/* ... is { int count; int type; char *color; } for 'nplots' plots */
+void *make_plot(int width, int height, char *title, int nplots, ...);
+void plot_set(void *plot, float *data, int len, int pos, int pp);
+void iq_plot_set(void *plot, short *data, int len, int pos, int pp);
+void iq_plot_set_sized(void *_plot, short *data, int len, int pp);
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp);
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp);
+
+/* returns an opaque pointer - truly a 'database *', see t_data.c */
+void *parse_database(char *filename);
+void dump_database(void *database);
+void list_ids(void *database);
+void list_groups(void *database);
+void on_off(void *d, char *item, int *a, int onoff);
+
+void *forwarder(char *ip, int port);
+void forward(void *forwarder, char *buf, int size);
+void forward_start_client(void *forwarder, int socket);
+
+#endif /* _TRACER_DEFS_H_ */
diff --git a/common/utils/T/forward.c b/common/utils/T/forward.c
new file mode 100644
index 0000000000000000000000000000000000000000..3177e22b2c362b261d7a1791345af7a2b78aa7f4
--- /dev/null
+++ b/common/utils/T/forward.c
@@ -0,0 +1,162 @@
+#include "defs.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+
+typedef struct databuf {
+  char *d;
+  int l;
+  struct databuf *next;
+} databuf;
+
+typedef struct {
+  int s;
+  int sc;
+  pthread_mutex_t lock;
+  pthread_mutex_t datalock;
+  pthread_cond_t datacond;
+  databuf * volatile head, *tail;
+} forward_data;
+
+static void *data_sender(void *_f)
+{
+  forward_data *f = _f;
+  databuf *cur;
+  char *buf, *b;
+  int size;
+
+wait:
+  if (pthread_mutex_lock(&f->datalock)) abort();
+  while (f->head == NULL)
+    if (pthread_cond_wait(&f->datacond, &f->datalock)) abort();
+  cur = f->head;
+  buf = cur->d;
+  size = cur->l;
+  f->head = cur->next;
+  if (f->head == NULL) f->tail = NULL;
+  if (pthread_mutex_unlock(&f->datalock)) abort();
+  free(cur);
+  goto process;
+
+process:
+  if (pthread_mutex_lock(&f->lock)) abort();
+
+int *x = (int*)buf;
+if (*x < 0 || *x >= 132) printf("bad id %d\n", *x);
+printf("DATA_SENDER sends type %d\n", *x);
+
+  b = buf;
+  while (size) {
+    int l = write(f->s, b, size);
+printf("DATA_SENDER write buffer %p len %d returns %d\n", b, size, l);
+    if (l <= 0) { printf("forward error\n"); exit(1); }
+    size -= l;
+    b += l;
+  }
+
+  if (pthread_mutex_unlock(&f->lock)) abort();
+
+  free(buf);
+
+  goto wait;
+}
+
+static void do_forward(forward_data *f, int from, int to, int lock)
+{
+  int l, len;
+  char *b;
+  char buf[1024];
+  while (1) {
+    len = read(from, buf, 1024);
+    if (len <= 0) break;
+    b = buf;
+
+    if (lock) if (pthread_mutex_lock(&f->lock)) abort();
+
+    while (len) {
+      l = write(to, b, len);
+printf("DO_FORWARD write buffer %p len %d returns %d\n", b, len, l);
+      if (l <= 0) break;
+      len -= l;
+      b += l;
+    }
+
+    if (lock) if (pthread_mutex_unlock(&f->lock)) abort();
+  }
+}
+
+static void *forward_s_to_sc(void *_f)
+{
+  forward_data *f = _f;
+  do_forward(f, f->s, f->sc, 0);
+  return NULL;
+}
+
+static void *forward_sc_to_s(void *_f)
+{
+  forward_data *f = _f;
+  do_forward(f, f->sc, f->s, 1);
+  printf("INFO: forwarder exits\n");
+  return NULL;
+}
+
+void forward_start_client(void *_f, int s)
+{
+  forward_data *f = _f;
+  f->sc = s;
+  new_thread(forward_s_to_sc, f);
+  new_thread(forward_sc_to_s, f);
+}
+
+void *forwarder(char *ip, int port)
+{
+  forward_data *f;
+  struct sockaddr_in a;
+
+  f = malloc(sizeof(*f)); if (f == NULL) abort();
+
+  pthread_mutex_init(&f->lock, NULL);
+  pthread_mutex_init(&f->datalock, NULL);
+  pthread_cond_init(&f->datacond, NULL);
+
+  f->sc = -1;
+  f->head = f->tail = NULL;
+
+  f->s = socket(AF_INET, SOCK_STREAM, 0);
+  if (f->s == -1) { perror("socket"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(ip);
+
+  if (connect(f->s, (struct sockaddr *)&a, sizeof(a)) == -1)
+    { perror("connect"); exit(1); }
+
+  new_thread(data_sender, f);
+
+  return f;
+}
+
+void forward(void *_forwarder, char *buf, int size)
+{
+  forward_data *f = _forwarder;
+  databuf *new;
+
+  new = malloc(sizeof(*new)); if (new == NULL) abort();
+
+  if (pthread_mutex_lock(&f->datalock)) abort();
+
+  new->d = malloc(size); if (new->d == NULL) abort();
+  memcpy(new->d, buf, size);
+  new->l = size;
+  new->next = NULL;
+  if (f->head == NULL) f->head = new;
+  f->tail = new;
+
+  if (pthread_cond_signal(&f->datacond)) abort();
+  if (pthread_mutex_unlock(&f->datalock)) abort();
+}
diff --git a/common/utils/T/generate_Txx.c b/common/utils/T/generate_Txx.c
new file mode 100644
index 0000000000000000000000000000000000000000..da6282adc13db3b5baae112b54b9dc80c30deb84
--- /dev/null
+++ b/common/utils/T/generate_Txx.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+void print(int n)
+{
+  int i;
+  printf("#define T%d(t", n);
+  for(i=0; i<(n-1)/2; i++) printf(",t%d,x%d", i, i);
+  printf(") \\\n");
+  printf("  do { \\\n");
+  printf("    if (T_ACTIVE(t)) { \\\n");
+  printf("      T_LOCAL_DATA \\\n");
+  printf("      T_HEADER(t); \\\n");
+  for(i=0; i<(n-1)/2; i++) printf("      T_PUT_##t%d(%d, x%d); \\\n", i, i+2, i);
+  printf("      T_SEND(); \\\n");
+  printf("    } \\\n");
+  printf("  } while (0)\n");
+  printf("\n");
+}
+
+int main(void)
+{
+  int i;
+  for (i = 11; i <= 33; i+=2) print(i);
+  return 0;
+}
diff --git a/common/utils/T/genids.c b/common/utils/T/genids.c
new file mode 100644
index 0000000000000000000000000000000000000000..734c506368ac348ba9db9ae2eae8048fcec929d6
--- /dev/null
+++ b/common/utils/T/genids.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char **unique_ids;
+int unique_ids_size;
+int unique_ids_maxsize;
+
+int cmp(const void *p1, const void *p2)
+{
+  return strcmp(*(char * const *)p1, *(char * const *)p2);
+}
+
+/* return 1 if s was not already known, 0 if it was */
+int new_unique_id(char *s)
+{
+  if (unique_ids_size)
+  if (bsearch(&s, unique_ids, unique_ids_size, sizeof(char *), cmp) != NULL) {
+    printf("FATAL: ID %s is not unique\n", s);
+    return 0;
+  }
+  if (unique_ids_size == unique_ids_maxsize) {
+    unique_ids_maxsize += 256;
+    unique_ids = realloc(unique_ids, unique_ids_maxsize * sizeof(char *));
+    if (unique_ids == NULL) { printf("out of memory\n"); abort(); }
+  }
+  unique_ids[unique_ids_size] = strdup(s);
+  if (unique_ids[unique_ids_size] == NULL)
+    { printf("out of memory\n"); abort(); }
+  unique_ids_size++;
+  qsort(unique_ids, unique_ids_size, sizeof(char *), cmp);
+  return 1;
+}
+
+char *bufname;
+int bufname_size;
+int bufname_maxsize;
+
+void putname(int c)
+{
+  if (bufname_size == bufname_maxsize) {
+    bufname_maxsize += 256;
+    bufname = realloc(bufname, bufname_maxsize);
+    if (bufname == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  bufname[bufname_size] = c;
+  bufname_size++;
+}
+
+char *bufvalue;
+int bufvalue_size;
+int bufvalue_maxsize;
+
+void putvalue(int c)
+{
+  if (bufvalue_size == bufvalue_maxsize) {
+    bufvalue_maxsize += 256;
+    bufvalue = realloc(bufvalue, bufvalue_maxsize);
+    if (bufvalue == NULL) { printf("memory allocation error\n"); exit(1); }
+  }
+  bufvalue[bufvalue_size] = c;
+  bufvalue_size++;
+}
+
+void smash_spaces(FILE *f)
+{
+  int c;
+  while (1) {
+    c = fgetc(f);
+    if (isspace(c)) continue;
+    if (c == ' ') continue;
+    if (c == '\t') continue;
+    if (c == '\n') continue;
+    if (c == 10 || c == 13) continue;
+    if (c == '#') {
+      while (1) {
+        c = fgetc(f);
+        if (c == '\n' || c == EOF) break;
+      }
+      continue;
+    }
+    break;
+  }
+  if (c != EOF) ungetc(c, f);
+}
+
+void get_line(FILE *f, char **name, char **value)
+{
+  int c;
+  bufname_size = 0;
+  bufvalue_size = 0;
+  *name = NULL;
+  *value = NULL;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == '=' || isspace(c) || c == EOF)) { putname(c); c = fgetc(f); }
+  if (c == EOF) return;
+  putname(0);
+  while (!(c == EOF || c == '=')) c = fgetc(f);
+  if (c == EOF) return;
+  smash_spaces(f);
+  c = fgetc(f);
+  while (!(c == 10 || c == 13 || c == EOF)) { putvalue(c); c = fgetc(f); }
+  putvalue(0);
+  if (bufname_size <= 1) return;
+  if (bufvalue_size <= 1) return;
+  *name = bufname;
+  *value = bufvalue;
+}
+
+int main(int n, char **v)
+{
+  FILE *in;
+  FILE *out;
+  char *name;
+  char *value;
+  char *in_name;
+  char *out_name;
+
+  if (n != 3) { printf("gimme <source> <dest>\n"); exit(1); }
+
+  n = 0;
+
+  in_name = v[1];
+  out_name = v[2];
+
+  in = fopen(in_name, "r"); if (in == NULL) { perror(in_name); exit(1); }
+  out = fopen(out_name, "w"); if (out == NULL) { perror(out_name); exit(1); }
+
+  fprintf(out, "/* generated file, do not edit by hand */\n\n");
+
+  while (1) {
+    get_line(in, &name, &value);
+    if (name == NULL) break;
+    printf("name '%s' value '%s'\n", name, value);
+    if (isspace(value[strlen(value)-1])) {
+      printf("bad value '%s' (no space at the end please!)\n", value);
+      unlink(out_name);
+      exit(1);
+    }
+    if (!strcmp(name, "ID")) {
+      if (!new_unique_id(value)) { unlink(out_name); exit(1); }
+      fprintf(out, "#define T_%s T_ID(%d)\n", value, n);
+      n++;
+    }
+  }
+  fprintf(out, "#define T_NUMBER_OF_IDS %d\n", n);
+
+  fclose(in);
+  fclose(out);
+
+  return 0;
+}
diff --git a/common/utils/T/main.c b/common/utils/T/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..3552f19df52d0c16652e85afb49f0ffee4258bb5
--- /dev/null
+++ b/common/utils/T/main.c
@@ -0,0 +1,21 @@
+#include "T.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(void)
+{
+  T_connect_to_tracer("127.0.0.1", 2020);
+  printf("after connect\n");
+  while (1) {
+    char *buf = "hello world %s!\n";
+    //T(T_first, T_PRINTF("hello world %s!\n", "yo"));
+    T(T_buf_test, T_BUFFER("hello world %s!\n", strlen(buf)+1));
+    usleep(1);
+  }
+  while (1) pause();
+
+  return 0;
+}
diff --git a/common/utils/T/plot.c b/common/utils/T/plot.c
new file mode 100644
index 0000000000000000000000000000000000000000..74473842883c7e968ecf3c84079c4645e18f2467
--- /dev/null
+++ b/common/utils/T/plot.c
@@ -0,0 +1,291 @@
+#include "defs.h"
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <stdarg.h>
+
+typedef struct {
+  float *buf;
+  short *iqbuf;
+  int count;
+  int type;
+  volatile int iq_count;  /* for ULSCH IQ data */
+  int iq_insert_pos;
+  GC g;
+} data;
+
+typedef struct {
+  Display *d;
+  Window w;
+  Pixmap px;
+  GC bg;
+  int width;
+  int height;
+  pthread_mutex_t lock;
+  float zoom;
+  int timer_pipe[2];
+  data *p;             /* list of plots */
+  int nplots;
+} plot;
+
+static void *timer_thread(void *_p)
+{
+  plot *p = _p;
+  char c;
+
+  while (1) {
+    /* more or less 10Hz */
+    usleep(100*1000);
+    c = 1;
+    if (write(p->timer_pipe[1], &c, 1) != 1) abort();
+  }
+
+  return NULL;
+}
+
+static void *plot_thread(void *_p)
+{
+  float v;
+  float *s;
+  int i, j;
+  plot *p = _p;
+  int redraw = 0;
+  int replot = 0;
+  fd_set rset;
+  int xfd = ConnectionNumber(p->d);
+  int maxfd = xfd > p->timer_pipe[0] ? xfd : p->timer_pipe[0];
+  int pp;
+
+  while (1) {
+    while (XPending(p->d)) {
+      XEvent e;
+      XNextEvent(p->d, &e);
+      switch (e.type) {
+      case ButtonPress:
+        /* button 4: zoom out */
+        if (e.xbutton.button == 4) { p->zoom = p->zoom * 1.25; replot = 1; }
+        /* button 5: zoom in */
+        if (e.xbutton.button == 5) { p->zoom = p->zoom * 0.8; replot = 1; }
+        printf("zoom: %f\n", p->zoom);
+        break;
+      case Expose: redraw = 1; break;
+      }
+    }
+
+    if (replot == 1) {
+      replot = 0;
+      redraw = 1;
+
+      if (pthread_mutex_lock(&p->lock)) abort();
+
+      XFillRectangle(p->d, p->px, p->bg, 0, 0, p->width, p->height);
+
+      for (pp = 0; pp < p->nplots; pp++) {
+        if (p->p[pp].type == PLOT_MINMAX) {
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            int min = *s;
+            int max = *s;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) {
+              if (*s < min) min = *s;
+              if (*s > max) max = *s;
+            }
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100-min, i, 100-max);
+          }
+        } else if (p->p[pp].type == PLOT_VS_TIME) {
+          for (i = 0; i < p->p[pp].count; i++)
+            p->p[pp].buf[i] =
+                10*log10(1.0+(float)(p->p[pp].iqbuf[2*i]*p->p[pp].iqbuf[2*i]+
+                p->p[pp].iqbuf[2*i+1]*p->p[pp].iqbuf[2*i+1]));
+          s = p->p[pp].buf;
+          for (i = 0; i < 512; i++) {
+            v = 0;
+            for (j = 0; j < p->p[pp].count/512; j++, s++) v += *s;
+            v /= p->p[pp].count/512;
+            XDrawLine(p->d, p->px, p->p[pp].g, i, 100, i, 100-v);
+          }
+        } else if (p->p[pp].type == PLOT_IQ_POINTS) {
+          XPoint pts[p->p[pp].iq_count];
+          int count = p->p[pp].iq_count;
+          for (i = 0; i < count; i++) {
+            pts[i].x = p->p[pp].iqbuf[2*i]*p->zoom/20+50;
+            pts[i].y = -p->p[pp].iqbuf[2*i+1]*p->zoom/20+50;
+          }
+          XDrawPoints(p->d, p->px, p->p[pp].g, pts, count, CoordModeOrigin);
+        }
+      }
+
+      if (pthread_mutex_unlock(&p->lock)) abort();
+    }
+
+    if (redraw) {
+      redraw = 0;
+      XCopyArea(p->d, p->px, p->w, DefaultGC(p->d, DefaultScreen(p->d)),
+                0, 0, p->width, p->height, 0, 0);
+    }
+
+    XFlush(p->d);
+
+    FD_ZERO(&rset);
+    FD_SET(p->timer_pipe[0], &rset);
+    FD_SET(xfd, &rset);
+    if (select(maxfd+1, &rset, NULL, NULL, NULL) == -1) abort();
+    if (FD_ISSET(p->timer_pipe[0], &rset)) {
+      char b[512];
+      if (read(p->timer_pipe[0], b, 512) <= 0) abort();
+      replot = 1;
+    }
+  }
+
+  return NULL;
+}
+
+void *make_plot(int width, int height, char *title, int nplots, ...)
+{
+  plot *p;
+  Display *d;
+  Window w;
+  Pixmap pm;
+  int i;
+  va_list ap;
+  XGCValues gcv;
+
+  p = malloc(sizeof(*p)); if (p == NULL) abort();
+
+  d = XOpenDisplay(0); if (d == NULL) abort();
+  w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, width, height,
+        0, WhitePixel(d, DefaultScreen(d)), WhitePixel(d, DefaultScreen(d)));
+  XSelectInput(d, w, ExposureMask | ButtonPressMask);
+  XMapWindow(d, w);
+
+  {
+    XSetWindowAttributes att;
+    att.backing_store = Always;
+    XChangeWindowAttributes(d, w, CWBackingStore, &att);
+  }
+
+  XStoreName(d, w, title);
+
+  p->bg = XCreateGC(d, w, 0, NULL);
+  XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->bg);
+  gcv.foreground = WhitePixel(d, DefaultScreen(d));
+  XChangeGC(d, p->bg, GCForeground, &gcv);
+
+  pm = XCreatePixmap(d, w, width, height, DefaultDepth(d, DefaultScreen(d)));
+
+  p->width = width;
+  p->height = height;
+  p->p = malloc(nplots * sizeof(data)); if (p->p == NULL) abort();
+
+  va_start(ap, nplots);
+  for (i = 0; i < nplots; i++) {
+    int count;
+    int type;
+    char *color;
+    XColor rcol, scol;
+
+    count = va_arg(ap, int);
+    type = va_arg(ap, int);
+    color = va_arg(ap, char *);
+
+    p->p[i].g = XCreateGC(d, w, 0, NULL);
+    XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, p->p[i].g);
+    if (XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)),
+                         color, &scol, &rcol)) {
+      gcv.foreground = scol.pixel;
+      XChangeGC(d, p->p[i].g, GCForeground, &gcv);
+    } else {
+      printf("could not allocate color '%s'\n", color);
+      abort();
+    }
+
+    if (type == PLOT_VS_TIME) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    } else if (type == PLOT_MINMAX) {
+      p->p[i].buf = malloc(sizeof(float) * count);
+      if (p->p[i].buf == NULL) abort();
+      p->p[i].iqbuf = NULL;
+    } else {
+      p->p[i].buf = NULL;
+      p->p[i].iqbuf = malloc(sizeof(short) * count * 2);
+      if(p->p[i].iqbuf==NULL)abort();
+    }
+    p->p[i].count = count;
+    p->p[i].type = type;
+    p->p[i].iq_count = 0;
+    p->p[i].iq_insert_pos = 0;
+  }
+  va_end(ap);
+
+  p->d = d;
+  p->w = w;
+  p->px = pm;
+
+  p->zoom = 1;
+  p->nplots = nplots;
+
+  pthread_mutex_init(&p->lock, NULL);
+
+  if (pipe(p->timer_pipe)) abort();
+
+  new_thread(plot_thread, p);
+  new_thread(timer_thread, p);
+
+  return p;
+}
+
+void plot_set(void *_plot, float *data, int len, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].buf + pos, data, len * sizeof(float));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set(void *_plot, short *data, int count, int pos, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf + pos * 2, data, count * 2 * sizeof(short));
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_set_sized(void *_plot, short *data, int count, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  memcpy(p->p[pp].iqbuf, data, count * 2 * sizeof(short));
+  p->p[pp].iq_count = count;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_iq_point_loop(void *_plot, short i, short q, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2] = i;
+  p->p[pp].iqbuf[p->p[pp].iq_insert_pos*2+1] = q;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
+
+void iq_plot_add_energy_point_loop(void *_plot, int e, int pp)
+{
+  plot *p = _plot;
+  if (pthread_mutex_lock(&p->lock)) abort();
+  p->p[pp].buf[p->p[pp].iq_insert_pos] = e;
+  if (p->p[pp].iq_count != p->p[pp].count) p->p[pp].iq_count++;
+  p->p[pp].iq_insert_pos++;
+  if (p->p[pp].iq_insert_pos == p->p[pp].count) p->p[pp].iq_insert_pos = 0;
+  if (pthread_mutex_unlock(&p->lock)) abort();
+}
diff --git a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
index 1eb663ec89e844a2adc171ecdc818e912a97d81c..f17c69c5f3886392396a3806a0066c90b262a59a 100644
--- a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
+++ b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c
@@ -331,6 +331,11 @@ int32_t lte_ul_channel_estimation(PHY_VARS_eNB *phy_vars_eNB,
 	break;
       }
 
+      if (aa == 0)
+        T(T_ENB_UL_CHANNEL_ESTIMATE, T_INT(eNB_id), T_INT(UE_id),
+          T_INT(phy_vars_eNB->proc[sched_subframe].frame_rx), T_INT(subframe),
+          T_INT(0), T_BUFFER(ul_ch_estimates_time[0], 512  * 4));
+
 #ifdef DEBUG_CH
 
       if (aa==0) {
diff --git a/openair1/PHY/LTE_TRANSPORT/pucch.c b/openair1/PHY/LTE_TRANSPORT/pucch.c
index 837c99b07371110fc5a2c89c6b3e2fc6dadff4f9..07789e48e1d4589d8c7cb5cd692d7f969c0de64b 100644
--- a/openair1/PHY/LTE_TRANSPORT/pucch.c
+++ b/openair1/PHY/LTE_TRANSPORT/pucch.c
@@ -793,6 +793,9 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
     phy_vars_eNB->pucch1_stats_thres[UE_id][(subframe<<10)+phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe]] = sigma2_dB+pucch1_thres;
     phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe] = (phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe]+1)&1023;
 
+    T(T_PUCCH_1_ENERGY, T_INT(phy_vars_eNB->Mod_id), T_INT(UE_id), T_INT(-1), T_INT(subframe),
+      T_INT(stat_max), T_INT(sigma2_dB+pucch1_thres));
+
     /*
     if (phy_vars_eNB->pucch1_stats_cnt[UE_id][subframe] == 0) {
       write_output("pucch_debug.m","pucch_energy",
@@ -1031,6 +1034,9 @@ uint32_t rx_pucch(PHY_VARS_eNB *phy_vars_eNB,
 	phy_vars_eNB->pucch1ab_stats[UE_id][(subframe<<11) + 1+2*(phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe])] = (stat_im);
 	phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe] = (phy_vars_eNB->pucch1ab_stats_cnt[UE_id][subframe]+1)&1023;
 
+      /* frame not available here - set to -1 for the moment */
+printf("sending %d %d\n", stat_re, stat_im);
+      T(T_PUCCH_1AB_IQ, T_INT(phy_vars_eNB->Mod_id), T_INT(UE_id), T_INT(-1), T_INT(subframe), T_INT(stat_re), T_INT(stat_im));
 
 	  
       *payload = (stat_re<0) ? 1 : 0;
diff --git a/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c b/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
index f2e4dea58aa75c2264ef7d502e7a169d1ec3e7e1..5a0115a24b61545a76bec8ff8cd3e6063d770fc4 100644
--- a/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
+++ b/openair1/PHY/LTE_TRANSPORT/ulsch_demodulation.c
@@ -1617,7 +1617,7 @@ void rx_ulsch(PHY_VARS_eNB *phy_vars_eNB,
   rx_power_correction = 1;
 
   if (ulsch[UE_id]->harq_processes[harq_pid]->nb_rb == 0) {
-    LOG_E(PHY,"PUSCH (%d/%x) nb_rb=0!\n", harq_pid,ulsch[UE_id]->rnti,harq_pid);
+    LOG_E(PHY,"PUSCH (%d/%x) nb_rb=0!\n", harq_pid,ulsch[UE_id]->rnti);
     return;
   }
 
@@ -1840,6 +1840,11 @@ void rx_ulsch(PHY_VARS_eNB *phy_vars_eNB,
 #endif
 
 
+  T(T_PUSCH_IQ, T_INT(eNB_id), T_INT(UE_id), T_INT(phy_vars_eNB->proc[sched_subframe].frame_rx),
+    T_INT(subframe), T_INT(ulsch[UE_id]->harq_processes[harq_pid]->nb_rb),
+    T_BUFFER(eNB_pusch_vars->rxdataF_comp[eNB_id][0],
+             2 * /* ulsch[UE_id]->harq_processes[harq_pid]->nb_rb */ frame_parms->N_RB_UL *12*frame_parms->symbols_per_tti*2));
+
   llrp = (int16_t*)&eNB_pusch_vars->llr[0];
 
   for (l=0; l<frame_parms->symbols_per_tti-ulsch[UE_id]->harq_processes[harq_pid]->srs_active; l++) {
diff --git a/openair1/PHY/TOOLS/lte_phy_scope.c b/openair1/PHY/TOOLS/lte_phy_scope.c
index 843a77fe62a3a060c816c6e009e9699a71700045..c8029ee62bdbdea7298d519f341895fc517581fb 100644
--- a/openair1/PHY/TOOLS/lte_phy_scope.c
+++ b/openair1/PHY/TOOLS/lte_phy_scope.c
@@ -352,8 +352,8 @@ void phy_scope_eNB(FD_lte_phy_scope_enb *form,
     fl_set_xyplot_data(form->pucch_comp,I_pucch,Q_pucch,10240,"","","");
     fl_set_xyplot_data(form->pucch_comp1,B_pucch,A_pucch,1024,"","","");
     fl_add_xyplot_overlay(form->pucch_comp1,1,B_pucch,C_pucch,1024,FL_RED);
-    fl_set_xyplot_ybounds(form->pucch_comp,-5000,5000);
-    fl_set_xyplot_xbounds(form->pucch_comp,-5000,5000);
+    fl_set_xyplot_ybounds(form->pucch_comp,-100,100);
+    fl_set_xyplot_xbounds(form->pucch_comp,-100,100);
 
     fl_set_xyplot_ybounds(form->pucch_comp1,20,80);
   }
diff --git a/openair1/SCHED/phy_procedures_lte_eNb.c b/openair1/SCHED/phy_procedures_lte_eNb.c
index 375c0dcd1f165cf712e39e03e983c83da8a5fbc3..d16ac720eb53b444be79333f8ad38e3a22c80afa 100755
--- a/openair1/SCHED/phy_procedures_lte_eNb.c
+++ b/openair1/SCHED/phy_procedures_lte_eNb.c
@@ -2443,6 +2443,10 @@ void phy_procedures_eNB_RX(const unsigned char sched_subframe,PHY_VARS_eNB *phy_
   LOG_D(PHY,"[eNB %d] Frame %d: Doing phy_procedures_eNB_RX(%d)\n",phy_vars_eNB->Mod_id,frame, subframe);
 #endif
 
+  T(T_ENB_INPUT_SIGNAL, T_INT(phy_vars_eNB->Mod_id), T_INT(frame), T_INT(subframe), T_INT(0),
+    T_BUFFER(&phy_vars_eNB->lte_eNB_common_vars.rxdata[0][0][subframe*phy_vars_eNB->lte_frame_parms.samples_per_tti],
+             phy_vars_eNB->lte_frame_parms.samples_per_tti * 4));
+
   /*
 #ifdef OAI_USRP
   for (aa=0;aa<phy_vars_eNB->lte_frame_parms.nb_antennas_rx;aa++)
diff --git a/openair2/UTIL/LOG/log.h b/openair2/UTIL/LOG/log.h
index c6abbfa001033107c8e1c57671f6e000d8514c0e..dce5a190e4d454b3fe8f60ed4fc1a9ada4e6ebdc 100755
--- a/openair2/UTIL/LOG/log.h
+++ b/openair2/UTIL/LOG/log.h
@@ -147,26 +147,40 @@ extern "C" {
 
 // debugging macros
 #ifdef USER_MODE
-#define LOG_G(c, x...) logIt(c, LOG_EMERG, x)
-#define LOG_A(c, x...) logIt(c, LOG_ALERT, x)
-#define LOG_C(c, x...) logIt(c, LOG_CRIT,  x)
-#define LOG_E(c, x...) logIt(c, LOG_ERR, x)
-#define LOG_W(c, x...) logIt(c, LOG_WARNING, x)
-#define LOG_N(c, x...) logIt(c, LOG_NOTICE, x)
-#define LOG_I(c, x...) logIt(c, LOG_INFO, x)
-#define LOG_D(c, x...) logIt(c, LOG_DEBUG, x)
-#define LOG_F(c, x...) logIt(c, LOG_FILE, x)  // log to a file, useful for the MSC chart generation
-#define LOG_T(c, x...) logIt(c, LOG_TRACE, x)
-#else
-#define LOG_G(c, x...) printk(x)
-#define LOG_A(c, x...) printk(x)
-#define LOG_C(c, x...) printk(x)
-#define LOG_E(c, x...) printk(x)
-#define LOG_W(c, x...) printk(x)
-#define LOG_N(c, x...) printk(x)
-#define LOG_I(c, x...) printk(x)
-#define LOG_D(c, x...) printk(x)
-#define LOG_T(c, x...) printk(x)
+#  if T_TRACER
+#    include "T.h"
+#    define LOG_I(c, x...) T(T_LEGACY_ ## c ## _INFO, T_PRINTF(x))
+#    define LOG_W(c, x...) T(T_LEGACY_ ## c ## _WARNING, T_PRINTF(x))
+#    define LOG_E(c, x...) T(T_LEGACY_ ## c ## _ERROR, T_PRINTF(x))
+#    define LOG_D(c, x...) T(T_LEGACY_ ## c ## _DEBUG, T_PRINTF(x))
+#    define LOG_T(c, x...) T(T_LEGACY_ ## c ## _TRACE, T_PRINTF(x))
+#    define LOG_G(c, x...) /* */
+#    define LOG_A(c, x...) /* */
+#    define LOG_C(c, x...) /* */
+#    define LOG_N(c, x...) /* */
+#    define LOG_F(c, x...) /* */
+#  else /* T_TRACER */
+#    define LOG_G(c, x...) logIt(c, LOG_EMERG, x)
+#    define LOG_A(c, x...) logIt(c, LOG_ALERT, x)
+#    define LOG_C(c, x...) logIt(c, LOG_CRIT,  x)
+#    define LOG_E(c, x...) logIt(c, LOG_ERR, x)
+#    define LOG_W(c, x...) logIt(c, LOG_WARNING, x)
+#    define LOG_N(c, x...) logIt(c, LOG_NOTICE, x)
+#    define LOG_I(c, x...) logIt(c, LOG_INFO, x)
+#    define LOG_D(c, x...) logIt(c, LOG_DEBUG, x)
+#    define LOG_F(c, x...) logIt(c, LOG_FILE, x)  // log to a file, useful for the MSC chart generation
+#    define LOG_T(c, x...) logIt(c, LOG_TRACE, x)
+#  endif /* T_TRACER */
+#else /* USER_MODE */
+#  define LOG_G(c, x...) printk(x)
+#  define LOG_A(c, x...) printk(x)
+#  define LOG_C(c, x...) printk(x)
+#  define LOG_E(c, x...) printk(x)
+#  define LOG_W(c, x...) printk(x)
+#  define LOG_N(c, x...) printk(x)
+#  define LOG_I(c, x...) printk(x)
+#  define LOG_D(c, x...) printk(x)
+#  define LOG_T(c, x...) printk(x)
 #endif
 /* @}*/
 
diff --git a/openair2/UTIL/OTG/otg_kpi.c b/openair2/UTIL/OTG/otg_kpi.c
index 3aa55df05faddbd28535377d3de180c8f58d79d6..877150d892205cff1beeebfa3464c6f9b2dde7ad 100644
--- a/openair2/UTIL/OTG/otg_kpi.c
+++ b/openair2/UTIL/OTG/otg_kpi.c
@@ -61,7 +61,7 @@ void tx_throughput(int src, int dst, int application)
     else if (g_otg->flow_start[src][dst][application] < get_ctime() )
       otg_info->tx_throughput[src][dst][application]=((double)otg_info->tx_num_bytes[src][dst][application] *1000*8)/ ((get_ctime() - g_otg->flow_start[src][dst][application])*1024);
     else 
-      LOG_W("[src %d][dst %d][app %d] flow start time less than the simu time (start %d, duration %d, ctime %d)\n",
+      LOG_W(OTG, "[src %d][dst %d][app %d] flow start time less than the simu time (start %d, duration %d, ctime %d)\n",
 	    src, dst, application,
 	    g_otg->flow_start[src][dst][application],
 	    g_otg->flow_duration[src][dst][application],
@@ -94,7 +94,7 @@ if ((g_otg->flow_start[src][dst][application]+g_otg->flow_duration[src][dst][app
     else if (g_otg->flow_start[src][dst][application] < get_ctime() )
       otg_info->rx_goodput[src][dst][application]=((double)otg_info->rx_num_bytes[src][dst][application] *1000*8)/ ((get_ctime() - g_otg->flow_start[src][dst][application])*1024);
     else 
-      LOG_W("[src %d][dst %d][app %d] flow start time less than the simu time (start %d, duration %d, ctime %d)\n",
+      LOG_W(OTG, "[src %d][dst %d][app %d] flow start time less than the simu time (start %d, duration %d, ctime %d)\n",
 	    src, dst, application,
 	    g_otg->flow_start[src][dst][application],
 	    g_otg->flow_duration[src][dst][application],
diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c
index 7df20f05c81212c64aa8159d032978f472aad98e..67ee981100e6d0ddcf45b49c1045cbd0654b026a 100644
--- a/targets/RT/USER/lte-softmodem.c
+++ b/targets/RT/USER/lte-softmodem.c
@@ -51,6 +51,10 @@
 #include <execinfo.h>
 #include <getopt.h>
 
+#if T_TRACER
+#include "T.h"
+#endif
+
 #include "rt_wrapper.h"
 #undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
 
@@ -2615,6 +2619,13 @@ int main( int argc, char **argv )
 
   PHY_VARS_UE *UE[MAX_NUM_CCs];
 
+#if T_TRACER
+  char *T_ip = "127.0.0.1";
+  int T_port = 2020;
+  printf("connecting to T tracer IP %s port %d\n", T_ip, T_port);
+  T_connect_to_tracer(T_ip, T_port);
+#endif
+
   mode = normal_txrx;
   memset(&openair0_cfg[0],0,sizeof(openair0_config_t)*MAX_CARDS);
 
diff --git a/targets/SIMU/USER/oaisim.c b/targets/SIMU/USER/oaisim.c
index 9e4880906d35b2a9d41162999d09afce48874b7a..c3700c9da911fe41fea7215281d414bfc5e2bb9c 100644
--- a/targets/SIMU/USER/oaisim.c
+++ b/targets/SIMU/USER/oaisim.c
@@ -114,6 +114,10 @@ char smbv_ip[16];
 # include "create_tasks.h"
 #endif
 
+#if T_TRACER
+#include "T.h"
+#endif
+
 /*
  DCI0_5MHz_TDD0_t          UL_alloc_pdu;
  DCI1A_5MHz_TDD_1_6_t      CCCH_alloc_pdu;
@@ -1264,6 +1268,14 @@ main (int argc, char **argv)
   int node_id;
   int port,Process_Flag=0,wgt,Channel_Flag=0,temp;
 #endif
+
+#if T_TRACER
+  char *T_ip = "127.0.0.1";
+  int T_port = 2020;
+  printf("connecting to T tracer IP %s port %d\n", T_ip, T_port);
+  T_connect_to_tracer(T_ip, T_port);
+#endif
+
   //default parameters
   oai_emulation.info.n_frames = 0xffff; //1024;          //10;
   oai_emulation.info.n_frames_flag = 0; //fixme