From 6cb04bb43a6068c66e982e090f43c8deaf4c7b52 Mon Sep 17 00:00:00 2001 From: Cedric Roux <cedric.roux@eurecom.fr> Date: Wed, 2 May 2018 11:27:41 +0200 Subject: [PATCH] basic simulator: initial release This commit introduces a 'basic simulator'. This basic simulator is made of: - the standard eNB code using a special driver that mimics the USRP driver - the standard UE code using a special driver that mimics the USRP driver - no channel simulation - some special code to deal with faster-than-realtime behaviour of this basic simulator It connects one UE to one eNB. It requires an EPC, populated with the correct configuration for the UE. This is the initial release and may contain bugs (most probably race conditions due to the faster-than-realtime behaviour). To use it, see the documentation at: targets/ARCH/tcp_bridge/README.tcp_bridge_oai. It has been tested with 25, 50 and 100 RBs, FDD mode. (No check at all has been done to know if it could work in TDD mode.) --- cmake_targets/CMakeLists.txt | 21 +- cmake_targets/build_oai | 98 ++++++ common/utils/T/T.h | 11 + common/utils/T/T_defs.h | 14 +- common/utils/T/local_tracer.c | 14 + openair1/PHY/LTE_ESTIMATION/lte_adjust_sync.c | 5 + openair1/PHY/LTE_TRANSPORT/pss.c | 5 + targets/ARCH/tcp_bridge/README.tcp_bridge_oai | 41 +++ targets/ARCH/tcp_bridge/tcp_bridge_oai.c | 320 ++++++++++++++++++ targets/RT/USER/lte-ue.c | 20 ++ 10 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 targets/ARCH/tcp_bridge/README.tcp_bridge_oai create mode 100644 targets/ARCH/tcp_bridge/tcp_bridge_oai.c diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 7bfb0ab611c..2e3b0bf7430 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 04bd45e48e2..2ba63a0ce40 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 37668a83097..cab246a6fff 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 16d35210f2a..9961c1919e5 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 6d0ecddd459..67657634c4e 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 18444076f31..ec54931f8b0 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 c746a331db2..31c60d1207b 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 00000000000..ef0588ddee4 --- /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 00000000000..ba6d96db933 --- /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 2c83ea9a04e..87c2dcf058a 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++; -- GitLab