diff --git a/CMakeLists.txt b/CMakeLists.txt index c8f92b065..8abc0c063 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ PROJECT (LIBLTE) LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") -INCLUDE(libLTEPackage) #setup cpack +INCLUDE(libLTEPackage) #setup cpack include(CTest) set( CTEST_MEMORYCHECK_COMMAND valgrind ) @@ -78,6 +78,9 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) IF(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE") + # IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wno-error=implicit-function-declaration -Wno-error=unused-but-set-variable") + # ENDIF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") IF(NOT WIN32) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) ENDIF(NOT WIN32) diff --git a/README.md b/README.md index 1b816498b..f783db483 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,15 @@ Current Status =============== The following parts are available: - * Physical Broadcast channel (PBCH) eNodeB and UE part. The UE supports multi-antenna transmitters + * Physical Channels: PBCH, PCFICH, PDCCH, PHICH, PDSCH eNodeB and UE side * Synchronization and CFO estimation/correction * Equalization - * UE receiver verified with live LTE signals + * UE receiver (MIB + SIB1 decoding) verified with live LTE signals Hardware ======== -The library currently uses Ettus Universal Hardware Driver (UHD). Thus, any hardware supported by UHD can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. +The library currently uses Ettus Universal Hardware Driver (UHD). Thus, any hardware supported by UHD can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. We are using the B210 USRP. Download & Install Instructions @@ -50,12 +50,12 @@ PHY Examples Setup one or two computers connected to two USRP or UHD-compatible hardware. From the eNodeB, type ``` -lte/phy/examples/pbch_enodeb -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] +lte/phy/examples/pdsch_enodeb -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] ``` From the UE, type ``` -lte/phy/examples/pbch_ue -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] +lte/phy/examples/pdsch_ue -f [frequency_in_Hz] [-a [UHD args]] [-h for more commands] ``` And the output should look something like the following video. In this example, we removed the transmitter and receiver antennas in the middle of the demonstration, showing how reception is still possible (despite with some erros). @@ -65,17 +65,26 @@ https://www.dropbox.com/s/txh1nuzdb0igq5n/demo_pbch.ogv ![Screenshopt of the PBCH example output](pbch_capture.png "Screenshopt of the PBCH example output") +The SIB1 message is decoded and shown on the console, for example: + +``` +Decoded SIB1 Message: [40 48 50 03 02 0b 14 4a 30 18 28 20 90 81 84 79 a0 00 ]; +``` + +Then, you can use any ASN.1 SIB decoder to read the message. This site http://www.marben-products.com/asn.1/services/decoder-asn1-lte.html is a good example. + + If you don't have a pair of USRP, you can also test the demo by writing the samples to a file and then reading them: From the eNodeB, type ``` -lte/phy/examples/pbch_enodeb -o [output_file] -c [cell_id] [-h for more commands] +lte/phy/examples/pdsch_enodeb -o [output_file] -c [cell_id] [-h for more commands] ``` From the UE, type ``` -lte/phy/examples/pbch_ue -i [input_file] -c [cell_id] [-h for more commands] +lte/phy/examples/pdsch_ue -i [input_file] -c [cell_id] [-h for more commands] ``` diff --git a/cmake/modules/FindVolk.cmake b/cmake/modules/FindVolk.cmake index 7a9cb5349..d3a5ceba1 100644 --- a/cmake/modules/FindVolk.cmake +++ b/cmake/modules/FindVolk.cmake @@ -5,7 +5,7 @@ FIND_PATH( VOLK_INCLUDE_DIRS NAMES volk.h HINTS $ENV{VOLK_DIR}/include/volk - ${CMAKE_INSTALL_PREFIX}/include/volk + ${CMAKE_INSTALL_PREFIX}/include/volk ${PC_VOLK_INCLUDE_DIR} PATHS /usr/local/include/volk /usr/include/volk @@ -15,9 +15,9 @@ FIND_LIBRARY( VOLK_LIBRARIES NAMES volk HINTS $ENV{VOLK_DIR}/lib - ${CMAKE_INSTALL_PREFIX}/lib - ${CMAKE_INSTALL_PREFIX}/lib64 - ${PC_VOLK_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + ${PC_VOLK_LIBDIR} PATHS /usr/local/lib /usr/local/lib64 /usr/lib @@ -25,35 +25,91 @@ FIND_LIBRARY( ) # Some functions are not defined in old volk versions -SET(CMAKE_REQUIRED_LIBRARIES volk m) +SET(CMAKE_REQUIRED_LIBRARIES volk m) CHECK_FUNCTION_EXISTS_MATH(volk_32f_index_max_16u HAVE_VOLK_MAX_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32f_accumulator_s32f HAVE_VOLK_ACC_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32fc_multiply_32fc HAVE_VOLK_MULT_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_conjugate_32fc HAVE_VOLK_CONJ_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_multiply_32fc HAVE_VOLK_MULT2_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_multiply_conjugate_32fc HAVE_VOLK_MULT2_CONJ_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_32f_multiply_32fc HAVE_VOLK_MULT_REAL_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_multiply_32f HAVE_VOLK_MULT_FLOAT_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_magnitude_32f HAVE_VOLK_MAG_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_divide_32f HAVE_VOLK_DIVIDE_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_32f_dot_prod_32fc HAVE_VOLK_DOTPROD_FC_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_conjugate_dot_prod_32fc HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_dot_prod_32f HAVE_VOLK_DOTPROD_F_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32f_atan2_32f HAVE_VOLK_ATAN_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_convert_16i HAVE_VOLK_CONVERT_FI_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_32f_x2 HAVE_VOLK_DEINTERLEAVE_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_subtract_32f HAVE_VOLK_SUB_FLOAT_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_square_dist_32f HAVE_VOLK_SQUARE_DIST_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_real_32f HAVE_VOLK_DEINTERLEAVE_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_index_max_16u HAVE_VOLK_MAX_ABS_FUNCTION) + SET(VOLK_DEFINITIONS "HAVE_VOLK") +IF(${HAVE_VOLK_MAX_ABS_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_ABS_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_SQUARE_DIST_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SQUARE_DIST_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DEINTERLEAVE_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DEINTERLEAVE_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_MULT2_CONJ_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_CONJ_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DEINTERLEAVE_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DEINTERLEAVE_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_CONVERT_FI_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_CONVERT_FI_FUNCTION") +ENDIF() IF(${HAVE_VOLK_MAX_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_FUNCTION") ENDIF() IF(${HAVE_VOLK_ACC_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_ACC_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_ACC_FUNCTION") ENDIF() IF(${HAVE_VOLK_MULT_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_FUNCTION") ENDIF() IF(${HAVE_VOLK_CONJ_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_CONJ_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_CONJ_FUNCTION") ENDIF() IF(${HAVE_VOLK_MULT2_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_MULT_FLOAT_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_FLOAT_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_MULT_REAL_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_REAL_FUNCTION") ENDIF() IF(${HAVE_VOLK_MAG_FUNCTION}) - SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAG_FUNCTION") + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAG_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DIVIDE_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DIVIDE_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DOTPROD_FC_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_FC_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_DOTPROD_F_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_F_FUNCTION") +ENDIF() +IF(${HAVE_VOLK_ATAN_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_ATAN_FUNCTION") ENDIF() - INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(VOLK DEFAULT_MSG VOLK_LIBRARIES VOLK_INCLUDE_DIRS) MARK_AS_ADVANCED(VOLK_LIBRARIES VOLK_INCLUDE_DIRS VOLK_DEFINITIONS) diff --git a/cuhd/include/liblte/cuhd/cuhd.h b/cuhd/include/liblte/cuhd/cuhd.h index d67c2eba1..e1604b93d 100644 --- a/cuhd/include/liblte/cuhd/cuhd.h +++ b/cuhd/include/liblte/cuhd/cuhd.h @@ -31,26 +31,65 @@ extern "C" { #endif #include - +#include + #include "liblte/config.h" #include "liblte/cuhd/cuhd_utils.h" -LIBLTE_API int cuhd_open(char *args, void **handler); +LIBLTE_API int cuhd_open(char *args, + void **handler); + LIBLTE_API int cuhd_close(void *h); LIBLTE_API int cuhd_start_rx_stream(void *h); -LIBLTE_API int cuhd_start_rx_stream_nsamples(void *h, int nsamples); + +LIBLTE_API int cuhd_start_rx_stream_nsamples(void *h, + uint32_t nsamples); + LIBLTE_API int cuhd_stop_rx_stream(void *h); + +LIBLTE_API void cuhd_flush_buffer(void *h); + LIBLTE_API bool cuhd_rx_wait_lo_locked(void *h); -LIBLTE_API double cuhd_set_rx_srate(void *h, double freq); -LIBLTE_API double cuhd_set_rx_gain(void *h, double gain); -LIBLTE_API double cuhd_set_rx_freq(void *h, double freq); -LIBLTE_API int cuhd_recv(void *h, void *data, int nsamples, int blocking); - -LIBLTE_API double cuhd_set_tx_srate(void *h, double freq); -LIBLTE_API double cuhd_set_tx_gain(void *h, double gain); -LIBLTE_API double cuhd_set_tx_freq(void *h, double freq); -LIBLTE_API int cuhd_send(void *h, void *data, int nsamples, int blocking); + +LIBLTE_API double cuhd_set_rx_srate(void *h, + double freq); + +LIBLTE_API double cuhd_set_rx_gain(void *h, + double gain); + +LIBLTE_API double cuhd_set_rx_freq(void *h, + double freq); + +LIBLTE_API double cuhd_set_rx_freq_offset(void *h, + double freq, + double off); + +LIBLTE_API int cuhd_recv(void *h, + void *data, + uint32_t nsamples, + bool blocking); + +LIBLTE_API int cuhd_recv_timed(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +LIBLTE_API double cuhd_set_tx_srate(void *h, + double freq); + +LIBLTE_API double cuhd_set_tx_gain(void *h, + double gain); + +LIBLTE_API double cuhd_set_tx_freq(void *h, + double freq); + +LIBLTE_API int cuhd_send(void *h, + void *data, + uint32_t nsamples, + bool blocking); #ifdef __cplusplus diff --git a/cuhd/lib/cuhd_handler.hpp b/cuhd/lib/cuhd_handler.hpp index 349117a88..8e3ecedd6 100644 --- a/cuhd/lib/cuhd_handler.hpp +++ b/cuhd/lib/cuhd_handler.hpp @@ -30,9 +30,9 @@ class cuhd_handler { public: - uhd::usrp::multi_usrp::sptr usrp; - uhd::rx_streamer::sptr rx_stream; - bool rx_stream_enable; - uhd::tx_streamer::sptr tx_stream; + uhd::usrp::multi_usrp::sptr usrp; + uhd::rx_streamer::sptr rx_stream; + bool rx_stream_enable; + uhd::tx_streamer::sptr tx_stream; }; diff --git a/cuhd/lib/cuhd_imp.cpp b/cuhd/lib/cuhd_imp.cpp index 626415f7e..9608c94a3 100644 --- a/cuhd/lib/cuhd_imp.cpp +++ b/cuhd/lib/cuhd_imp.cpp @@ -34,9 +34,11 @@ #include "cuhd_handler.hpp" #include "liblte/cuhd/cuhd.h" +//#define METADATA_VERBOSE -void my_handler(uhd::msg::type_t type, const std::string &msg){ - //handle the message... +void my_handler(uhd::msg::type_t type, const std::string & msg) +{ + //handle the message... } typedef _Complex float complex_t; @@ -45,161 +47,228 @@ typedef _Complex float complex_t; bool isLocked(void *h) { - cuhd_handler* handler = static_cast(h); - std::vector mb_sensors = handler->usrp->get_mboard_sensor_names(); - std::vector rx_sensors = handler->usrp->get_rx_sensor_names(0); - if(std::find(rx_sensors.begin(), rx_sensors.end(), "lo_locked") != rx_sensors.end()) { - return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool(); - } - else if(std::find(mb_sensors.begin(), mb_sensors.end(), "ref_locked") != mb_sensors.end()) { - return handler->usrp->get_mboard_sensor("ref_locked", 0).to_bool(); - } - else { - usleep(500); - return true; - } + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + std::vector < std::string > mb_sensors = + handler->usrp->get_mboard_sensor_names(); + std::vector < std::string > rx_sensors = + handler->usrp->get_rx_sensor_names(0); + if (std::find(rx_sensors.begin(), rx_sensors.end(), "lo_locked") != + rx_sensors.end()) { + return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool(); + } else if (std::find(mb_sensors.begin(), mb_sensors.end(), "ref_locked") != + mb_sensors.end()) { + return handler->usrp->get_mboard_sensor("ref_locked", 0).to_bool(); + } else { + usleep(500); + return true; + } } bool cuhd_rx_wait_lo_locked(void *h) { - double report = 0.0; - while(isLocked(h) && report < 3.0) - { - report += 0.1; - usleep(1000); - } - return isLocked(h); + double report = 0.0; + while (isLocked(h) && report < 3.0) { + report += 0.1; + usleep(1000); + } + return isLocked(h); +} + +int cuhd_start_rx_stream(void *h) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + handler->usrp->issue_stream_cmd(cmd); + return 0; } -int cuhd_start_rx_stream(void *h) { - cuhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - handler->usrp->issue_stream_cmd(cmd); - return 0; +int cuhd_stop_rx_stream(void *h) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + handler->usrp->issue_stream_cmd(cmd); + return 0; } -int cuhd_stop_rx_stream(void *h) { - cuhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - handler->usrp->issue_stream_cmd(cmd); - return 0; +void cuhd_flush_buffer(void *h) +{ + int n; + _Complex float tmp[1024]; + do { + n = cuhd_recv(h, tmp, 1024, 0); + } while (n > 0); } -int cuhd_start_rx_stream_nsamples(void *h, int nsamples) { - cuhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - cmd.num_samps = nsamples; - handler->usrp->issue_stream_cmd(cmd); - return 0; +int cuhd_start_rx_stream_nsamples(void *h, uint32_t nsamples) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + cmd.num_samps = nsamples; + handler->usrp->issue_stream_cmd(cmd); + return 0; } -int cuhd_open(char *args, void **h) { - cuhd_handler* handler = new cuhd_handler(); - std::string _args=std::string(args); - handler->usrp = uhd::usrp::multi_usrp::make(_args); - - // Try to set LTE clock - handler->usrp->set_master_clock_rate(30720000); - - handler->usrp->set_clock_source("internal"); - - std::string otw, cpu; - otw="sc16"; - cpu="fc32"; - uhd::stream_args_t stream_args(cpu, otw); - handler->rx_stream = handler->usrp->get_rx_stream(stream_args); - handler->tx_stream = handler->usrp->get_tx_stream(stream_args); - - *h = handler; - - return 0; -} - -int cuhd_close(void *h) { - cuhd_stop_rx_stream(h); - /** Something else to close the USRP?? */ - return 0; -} - - -double cuhd_set_rx_srate(void *h, double freq) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_rx_rate(freq); - double ret = handler->usrp->get_rx_rate(); - return ret; -} - -double cuhd_set_rx_gain(void *h, double gain) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_rx_gain(gain); - return handler->usrp->get_rx_gain(); -} - -double cuhd_set_rx_freq(void *h, double freq) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_rx_freq(freq); - return handler->usrp->get_rx_freq(); -} - -int cuhd_recv(void *h, void *data, int nsamples, int blocking) { - cuhd_handler* handler = static_cast(h); - uhd::rx_metadata_t md; - if (blocking) { - int n=0,p; - complex_t *data_c = (complex_t*) data; - do { - p=handler->rx_stream->recv(&data_c[n], nsamples-n, md); - if (p == -1) { - return -1; - } - n+=p; - } while(nrx_stream->recv(data, nsamples, md, 0.0); - } -} - -double cuhd_set_tx_gain(void *h, double gain) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_tx_gain(gain); - return handler->usrp->get_tx_gain(); -} - -double cuhd_set_tx_srate(void *h, double freq) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_tx_rate(freq); - return handler->usrp->get_tx_rate(); -} - -double cuhd_set_tx_freq(void *h, double freq) { - cuhd_handler* handler = static_cast(h); - handler->usrp->set_tx_freq(freq); - return handler->usrp->get_tx_freq(); -} - -int cuhd_send(void *h, void *data, int nsamples, int blocking) { - cuhd_handler* handler = static_cast(h); - uhd::tx_metadata_t md; - if (blocking) { - int n=0,p; - complex_t *data_c = (complex_t*) data; - do { - p=handler->tx_stream->send(&data_c[n], nsamples-n, md); - if (p == -1) { - return -1; - } - n+=p; - } while(ntx_stream->send(data, nsamples, md, 0.0); - } +int cuhd_open(char *args, void **h) +{ + cuhd_handler *handler = new cuhd_handler(); + std::string _args = std::string(args); + handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=30720000" + ", num_recv_frames=512"); + +// handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=50000000" + ", num_recv_frames=512"); + handler->usrp->set_clock_source("internal"); + + std::string otw, cpu; + otw = "sc16"; + cpu = "fc32"; + uhd::stream_args_t stream_args(cpu, otw); + handler->rx_stream = handler->usrp->get_rx_stream(stream_args); + handler->tx_stream = handler->usrp->get_tx_stream(stream_args); + + *h = handler; + + return 0; +} + +int cuhd_close(void *h) +{ + cuhd_stop_rx_stream(h); + /** Something else to close the USRP?? */ + return 0; +} + + +double cuhd_set_rx_srate(void *h, double freq) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_rx_rate(freq); + double ret = handler->usrp->get_rx_rate(); + return ret; +} + +double cuhd_set_rx_gain(void *h, double gain) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_rx_gain(gain); + return handler->usrp->get_rx_gain(); +} + +double cuhd_set_rx_freq(void *h, double freq) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_rx_freq(freq); + return handler->usrp->get_rx_freq(); +} + +double cuhd_set_rx_freq_offset(void *h, double freq, double off) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_rx_freq(uhd::tune_request_t(freq,off)); + return handler->usrp->get_rx_freq(); +} + +int cuhd_recv(void *h, void *data, uint32_t nsamples, bool blocking) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + uhd::rx_metadata_t md; + if (blocking) { + int n = 0, p; + complex_t *data_c = (complex_t *) data; + do { + p = handler->rx_stream->recv(&data_c[n], nsamples - n, md); + if (p == -1) { + return -1; + } + n += p; +#ifdef METADATA_VERBOSE + if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) { + std::cout << "\nError code: " << md.to_pp_string() << "\n\n"; + } +#endif + } while (n < nsamples); + return nsamples; + } else { + return handler->rx_stream->recv(data, nsamples, md, 0.0); + } +} + +int cuhd_recv_timed(void *h, + void *data, + uint32_t nsamples, + int blocking, + time_t *secs, + double *frac_secs) { + cuhd_handler* handler = static_cast(h); + uhd::rx_metadata_t md; + *secs = -1; + *frac_secs = -1; + int p; + if (blocking) { + int n=0; + complex_t *data_c = (complex_t*) data; + do { + p=handler->rx_stream->recv(&data_c[n], nsamples-n, md); + if (p == -1) { + return -1; + } + if(*secs < 0){ + *secs = md.time_spec.get_full_secs(); + *frac_secs = md.time_spec.get_frac_secs(); + } + n+=p; + } while(nrx_stream->recv(data, nsamples, md, 0.0); + *secs = md.time_spec.get_full_secs(); + *frac_secs = md.time_spec.get_frac_secs(); + return p; + } +} + + +double cuhd_set_tx_gain(void *h, double gain) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_tx_gain(gain); + return handler->usrp->get_tx_gain(); +} + +double cuhd_set_tx_srate(void *h, double freq) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_tx_rate(freq); + return handler->usrp->get_tx_rate(); +} + +double cuhd_set_tx_freq(void *h, double freq) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + handler->usrp->set_tx_freq(freq); + return handler->usrp->get_tx_freq(); +} + +int cuhd_send(void *h, void *data, uint32_t nsamples, bool blocking) +{ + cuhd_handler *handler = static_cast < cuhd_handler * >(h); + uhd::tx_metadata_t md; + if (blocking) { + int n = 0, p; + complex_t *data_c = (complex_t *) data; + do { + p = handler->tx_stream->send(&data_c[n], nsamples - n, md); + if (p == -1) { + return -1; + } + n += p; + } while (n < nsamples); + return nsamples; + } else { + return handler->tx_stream->send(data, nsamples, md, 0.0); + } } diff --git a/cuhd/lib/cuhd_utils.c b/cuhd/lib/cuhd_utils.c index 9c4b1e9ac..b6dfcfa24 100644 --- a/cuhd/lib/cuhd_utils.c +++ b/cuhd/lib/cuhd_utils.c @@ -46,7 +46,7 @@ int cuhd_rssi_scan(void *uhd, float *freqs, float *rssi, int nof_bands, double f goto free_and_exit; } - cuhd_set_rx_gain(uhd, 0.0); + cuhd_set_rx_gain(uhd, 20.0); cuhd_set_rx_srate(uhd, fs); for (i=0;i +#include +#include +#include +#include +#include +#include + +#include + +#include "liblte/phy/phy.h" + +#include "cell_search_utils.h" + + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" +#endif + +#define MHZ 1000000 +#define SAMP_FREQ 1920000 +#define FLEN 9600 +#define FLEN_PERIOD 0.005 + +#define MAX_EARFCN 1000 + + +int band = -1; +int earfcn_start=-1, earfcn_end = -1; +int nof_frames_total = 50; +int nof_frames_detected = 10; +float threshold = CS_FIND_THRESHOLD; + + +float uhd_gain = 60.0; +char *uhd_args=""; + +void usage(char *prog) { + printf("Usage: %s [agsendtvb] -b band\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD gain [Default %.2f dB]\n", uhd_gain); + printf("\t-s earfcn_start [Default All]\n"); + printf("\t-e earfcn_end [Default All]\n"); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-d nof_frames_detected [Default 10]\n"); + printf("\t-t threshold [Default %.2f]\n",threshold); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agsendtvb")) != -1) { + switch(opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'b': + band = atoi(argv[optind]); + break; + case 's': + earfcn_start = atoi(argv[optind]); + break; + case 'e': + earfcn_end = atoi(argv[optind]); + break; + case 'n': + nof_frames_total = atoi(argv[optind]); + break; + case 'd': + nof_frames_detected = atoi(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (band == -1) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int n; + void *uhd; + ue_celldetect_t s; + ue_celldetect_result_t found_cells[3]; + cf_t *buffer; + int nof_freqs; + lte_earfcn_t channels[MAX_EARFCN]; + uint32_t freq; + pbch_mib_t mib; + + parse_args(argc, argv); + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + cuhd_set_rx_gain(uhd, uhd_gain); + + nof_freqs = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); + if (nof_freqs < 0) { + fprintf(stderr, "Error getting EARFCN list\n"); + exit(-1); + } + + buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + + if (ue_celldetect_init(&s)) { + fprintf(stderr, "Error initiating UE sync module\n"); + exit(-1); + } + if (threshold > 0) { + ue_celldetect_set_threshold(&s, threshold); + } + + if (nof_frames_total > 0) { + ue_celldetect_set_nof_frames_total(&s, nof_frames_total); + } + if (nof_frames_detected > 0) { + ue_celldetect_set_nof_frames_detected(&s, nof_frames_detected); + } + + for (freq=0;freq threshold/2) { + if (decode_pbch(uhd, buffer, &found_cells[i], nof_frames_total, &mib)) { + fprintf(stderr, "Error decoding PBCH\n"); + exit(-1); + } + } + } + } + } + + ue_celldetect_free(&s); + cuhd_close(uhd); + exit(0); +} + + diff --git a/lte/phy/examples/cell_search_utils.c b/lte/phy/examples/cell_search_utils.c new file mode 100644 index 000000000..a5bb30adc --- /dev/null +++ b/lte/phy/examples/cell_search_utils.c @@ -0,0 +1,157 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "liblte/phy/phy.h" + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" + +int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell, uint32_t nof_frames_total, pbch_mib_t *mib) +{ + ue_mib_t uemib; + int n; + + bzero(mib, sizeof(pbch_mib_t)); + + uint32_t nof_frames = 0; + uint32_t flen = MIB_FRAME_SIZE; + + if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) { + fprintf(stderr, "Error initiating PBCH decoder\n"); + return LIBLTE_ERROR; + } + + INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0); + cuhd_set_rx_srate(uhd, 1920000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + do { + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total); + + n = ue_mib_decode(&uemib, buffer, flen, mib); + if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling ue_mib_decode()\n"); + return LIBLTE_ERROR; + } + if (n == MIB_FRAME_UNALIGNED) { + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + nof_frames++; + } while (n != MIB_FOUND && nof_frames < 2*nof_frames_total); + if (n == MIB_FOUND) { + printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames); + pbch_mib_fprint(stdout, mib, found_cell->cell_id); + } else { + printf("\nCould not decode MIB\n"); + } + + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + ue_mib_free(&uemib); + + return LIBLTE_SUCCESS; +} + +int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t found_cell[3]) +{ + int n; + + INFO("Setting sampling frequency 960 KHz for PSS search\n", 0); + cuhd_set_rx_srate(uhd, 960000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + uint32_t nof_scanned_cells = 0; + uint32_t flen = 4800; + int nof_detected_cells = 0; + + do { + + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + n = ue_celldetect_scan(s, buffer, flen, &found_cell[nof_scanned_cells]); + switch(n) { + case CS_FRAME_UNALIGNED: + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + return LIBLTE_ERROR; + case CS_CELL_DETECTED: + nof_detected_cells++; + if (found_cell[nof_scanned_cells].peak > 0) { + printf("\n\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n", + found_cell[nof_scanned_cells].cell_id, + lte_cp_string(found_cell[nof_scanned_cells].cp), + found_cell[nof_scanned_cells].peak, found_cell[nof_scanned_cells].mode, + s->nof_frames_detected); + } + + nof_scanned_cells++; + break; + case CS_CELL_NOT_DETECTED: + nof_scanned_cells++; + break; + case LIBLTE_ERROR: + case LIBLTE_ERROR_INVALID_INPUTS: + fprintf(stderr, "Error calling cellsearch_scan()\n"); + return LIBLTE_ERROR; + } + } while(nof_scanned_cells < 3); + + INFO("Stopping receiver...\n", 0); + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + return nof_detected_cells; +} +#endif diff --git a/lte/phy/examples/cell_search_utils.h b/lte/phy/examples/cell_search_utils.h new file mode 100644 index 000000000..2fefed105 --- /dev/null +++ b/lte/phy/examples/cell_search_utils.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "liblte/phy/phy.h" + +int decode_pbch(void *uhd, + cf_t *buffer, + ue_celldetect_result_t *found_cell, + uint32_t nof_frames_total, + pbch_mib_t *mib); + +int find_cell(void *uhd, + ue_celldetect_t *s, + cf_t *buffer, + ue_celldetect_result_t found_cell[3]); \ No newline at end of file diff --git a/lte/phy/examples/iodev.c b/lte/phy/examples/iodev.c new file mode 100644 index 000000000..ce55b3a3a --- /dev/null +++ b/lte/phy/examples/iodev.c @@ -0,0 +1,254 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include +#include + +#include "iodev.h" + +#include "liblte/phy/io/filesource.h" +#include "liblte/phy/ue/ue_sync.h" +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" +#endif + +#include "cell_search_utils.h" + + +int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + return cuhd_recv(h, data, nsamples, 1); +} + +/* Setup USRP or input file */ +int iodev_init(iodev_t *q, iodev_cfg_t *config, lte_cell_t *cell, pbch_mib_t *mib) { + + if (config->input_file_name) { + + mib->phich_resources = R_1; + mib->phich_length = PHICH_NORM; + + cell->id = config->cell_id_file; + cell->cp = CPNORM; + cell->nof_ports = config->nof_ports_file; + cell->nof_prb = config->nof_prb_file; + + if (filesource_init(&q->fsrc, config->input_file_name, COMPLEX_FLOAT_BIN)) { + return LIBLTE_ERROR; + } + q->mode = FILESOURCE; + int symbol_sz = lte_symbol_sz(cell->nof_prb); + if (symbol_sz > 0) { + q->sf_len = SF_LEN(symbol_sz); + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb); + return LIBLTE_ERROR; + } + + q->input_buffer_file = vec_malloc(q->sf_len * sizeof(cf_t)); + if (!q->input_buffer_file) { + perror("malloc"); + return LIBLTE_ERROR; + } + q->sf_idx = 9; + + } else { +#ifndef DISABLE_UHD + printf("Opening UHD device...\n"); + if (cuhd_open(config->uhd_args, &q->uhd)) { + fprintf(stderr, "Error opening uhd\n"); + return LIBLTE_ERROR; + } + + cuhd_set_rx_gain(q->uhd, config->uhd_gain); + + /* set receiver frequency */ + cuhd_set_rx_freq(q->uhd, (double) config->uhd_freq); + + cuhd_rx_wait_lo_locked(q->uhd); + DEBUG("Set uhd_freq to %.3f MHz\n", (double ) config->uhd_freq); + + int n; + ue_celldetect_t cd; + ue_celldetect_result_t found_cells[3]; + + cf_t *buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + if (ue_celldetect_init(&cd)) { + fprintf(stderr, "Error initiating UE cell detect\n"); + exit(-1); + } + n = find_cell(q->uhd, &cd, buffer, found_cells); + if (n < 0) { + fprintf(stderr, "Error searching cell\n"); + exit(-1); + } + + int max_peak_cell = 0; + float max_peak_value = -1.0; + if (n > 0) { + for (int i=0;i<3;i++) { + if (found_cells[i].peak > max_peak_value) { + max_peak_value = found_cells[i].peak; + max_peak_cell = i; + } + } + if (decode_pbch(q->uhd, buffer, &found_cells[max_peak_cell], 400, mib)) { + fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", found_cells[max_peak_cell].cell_id); + return LIBLTE_ERROR; + } + } else { + fprintf(stderr, "Could not find any cell in this frequency\n"); + return LIBLTE_ERROR; + } + + free(buffer); + cell->cp = found_cells[max_peak_cell].cp; + cell->id = found_cells[max_peak_cell].cell_id; + cell->nof_prb = mib->nof_prb; + cell->nof_ports = mib->nof_ports; + + /* set sampling frequency */ + int srate = lte_sampling_freq_hz(cell->nof_prb); + if (srate != -1) { + cuhd_set_rx_srate(q->uhd, (double) srate); + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb); + return LIBLTE_ERROR; + } + + DEBUG("Starting receiver...\n", 0); + cuhd_start_rx_stream(q->uhd); + + if (ue_sync_init(&q->sframe, *cell, cuhd_recv_wrapper, q->uhd)) { + fprintf(stderr, "Error initiating ue_sync\n"); + return LIBLTE_ERROR; + } + + /* Decodes the SSS signal during the tracking phase. Extra overhead, but makes sure we are in the correct subframe */ + ue_sync_decode_sss_on_track(&q->sframe, true); + + // Here, the subframe length and input buffer is managed by ue_sync + q->mode = UHD; + +#else + printf("Error UHD not available. Select an input file\n"); + return LIBLTE_ERROR; +#endif + } + + memcpy(&q->config, config, sizeof(iodev_cfg_t)); + + return LIBLTE_SUCCESS; +} + + + +void iodev_free(iodev_t *q) { + + if (q->mode == FILESOURCE) { + filesource_free(&q->fsrc); + } else { +#ifndef DISABLE_UHD + cuhd_close(q->uhd); +#endif + } +} +/* Receive samples from the USRP or read from file */ +int iodev_receive(iodev_t *q, cf_t **buffer) { + int n; + if (q->mode == FILESOURCE) { + INFO(" ----- READING %d SAMPLES ---- \n", q->sf_len); + n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len); + *buffer = q->input_buffer_file; + if (n == -1) { + fprintf(stderr, "Error reading file\n"); + /* wrap file if arrive to end */ + } else if (n < q->sf_len) { + DEBUG("Read %d from file. Seeking to 0\n",n); + filesource_seek(&q->fsrc, 0); + n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len); + if (n == -1) { + fprintf(stderr, "Error reading file\n"); + /* wrap file if arrive to end */ + } else { + n = 1; + } + } else { + n = 1; + } + q->sf_idx++; + if (q->sf_idx == 10) { + q->sf_idx = 0; + } + usleep(5000); + } else { + /* Use ue_sync_work which returns a synchronized buffer of subframe samples */ +#ifndef DISABLE_UHD + n = ue_sync_get_buffer(&q->sframe, buffer); + if (n < 0) { + fprintf(stderr, "Error calling ue_sync_work()\n"); + } +#endif + } + return n; +} + +void* iodev_get_cuhd(iodev_t *q) { + if (q->mode == UHD) { + return q->uhd; + } else { + return NULL; + } +} + +bool iodev_isfile(iodev_t *q) { + return q->mode == FILESOURCE; +} + +bool iodev_isUSRP(iodev_t *q) { + return q->mode == UHD; +} + +uint32_t iodev_get_sfidx(iodev_t *q) { + if (iodev_isfile(q)) { + return q->sf_idx; + } else { + return ue_sync_get_sfidx(&q->sframe); + } +} + + diff --git a/lte/phy/examples/iodev.h b/lte/phy/examples/iodev.h new file mode 100644 index 000000000..bc020222f --- /dev/null +++ b/lte/phy/examples/iodev.h @@ -0,0 +1,99 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef IODEF_H +#define IODEF_H + +#include "liblte/config.h" + +#include "liblte/phy/ue/ue_sync.h" +#include "liblte/phy/io/filesource.h" + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" +#endif + +/********* + * + * This component is a wrapper to the cuhd or filesource modules. It uses + * sync_frame_t to read aligned subframes from the USRP or filesource to read + * subframes from a file. + * + * When created, it starts receiving/reading at 1.92 MHz. The sampling frequency + * can then be changed using iodev_set_srate() + */ + + +typedef enum LIBLTE_API {FILESOURCE, UHD} iodev_mode_t; + +typedef _Complex float cf_t; + +typedef struct LIBLTE_API { + char *input_file_name; + uint32_t cell_id_file; + uint32_t nof_prb_file; + uint32_t nof_ports_file; + + float uhd_freq; + float uhd_gain; + char *uhd_args; + float find_threshold; +} iodev_cfg_t; + +typedef struct LIBLTE_API { + #ifndef DISABLE_UHD + void *uhd; + ue_sync_t sframe; + #endif + uint32_t sf_len; + uint32_t sf_idx; + cf_t *input_buffer_file; // for UHD mode, the input buffer is managed by sync_frame_t + filesource_t fsrc; + iodev_cfg_t config; + iodev_mode_t mode; +} iodev_t; + + +LIBLTE_API int iodev_init(iodev_t *q, + iodev_cfg_t *config, + lte_cell_t *cell, + pbch_mib_t *mib); + +LIBLTE_API void iodev_free(iodev_t *q); + +LIBLTE_API int iodev_receive(iodev_t *q, + cf_t **buffer); + +LIBLTE_API void* iodev_get_cuhd(iodev_t *q); + +LIBLTE_API uint32_t iodev_get_sfidx(iodev_t *q); + +LIBLTE_API bool iodev_isfile(iodev_t *q); + +LIBLTE_API bool iodev_isUSRP(iodev_t *q); + +#endif \ No newline at end of file diff --git a/lte/phy/examples/pbch_enodeb.c b/lte/phy/examples/pbch_enodeb.c deleted file mode 100644 index 5a0f9c583..000000000 --- a/lte/phy/examples/pbch_enodeb.c +++ /dev/null @@ -1,283 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2014 The libLTE Developers. See the - * COPYRIGHT file at the top-level directory of this distribution. - * - * \section LICENSE - * - * This file is part of the libLTE library. - * - * libLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * libLTE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * A copy of the GNU Lesser General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include - -#include "liblte/phy/phy.h" - -#ifndef DISABLE_UHD - #include "liblte/cuhd/cuhd.h" - void *uhd; -#endif - -char *output_file_name = NULL; -int nof_frames=-1; -int cell_id = 1; -int nof_prb = 6; -char *uhd_args = ""; - -float uhd_amp=0.25, uhd_gain=10.0, uhd_freq=2400000000; - -filesink_t fsink; -lte_fft_t ifft; -pbch_t pbch; - -cf_t *slot_buffer = NULL, *output_buffer = NULL; -int slot_n_re, slot_n_samples; - -#define UHD_SAMP_FREQ 1920000 - -void usage(char *prog) { - printf("Usage: %s [agmfoncvp]\n", prog); -#ifndef DISABLE_UHD - printf("\t-a UHD args [Default %s]\n", uhd_args); - printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain); - printf("\t-m UHD signal amplitude [Default %.2f]\n", uhd_amp); - printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq/1000000); -#else - printf("\t UHD is disabled. CUHD library not available\n"); -#endif - printf("\t-o output_file [Default USRP]\n"); - printf("\t-n number of frames [Default %d]\n", nof_frames); - printf("\t-c cell id [Default %d]\n", cell_id); - printf("\t-p nof_prb [Default %d]\n", nof_prb); - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "agfmoncpv")) != -1) { - switch(opt) { - case 'a': - uhd_args = argv[optind]; - break; - case 'g': - uhd_gain = atof(argv[optind]); - break; - case 'm': - uhd_amp = atof(argv[optind]); - break; - case 'f': - uhd_freq = atof(argv[optind]); - break; - case 'o': - output_file_name = argv[optind]; - break; - case 'n': - nof_frames = atoi(argv[optind]); - break; - case 'p': - nof_prb = atoi(argv[optind]); - break; - case 'c': - cell_id = atoi(argv[optind]); - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - exit(-1); - } - } -#ifdef DISABLE_UHD - if (!output_file_name) { - usage(argv[0]); - exit(-1); - } -#endif -} - -void base_init() { - /* init memory */ - slot_buffer = malloc(sizeof(cf_t) * slot_n_re); - if (!slot_buffer) { - perror("malloc"); - exit(-1); - } - output_buffer = malloc(sizeof(cf_t) * slot_n_samples); - if (!output_buffer) { - perror("malloc"); - exit(-1); - } - /* open file or USRP */ - if (output_file_name) { - if (filesink_init(&fsink, output_file_name, COMPLEX_FLOAT_BIN)) { - fprintf(stderr, "Error opening file %s\n", output_file_name); - exit(-1); - } - } else { -#ifndef DISABLE_UHD - printf("Opening UHD device...\n"); - if (cuhd_open(uhd_args,&uhd)) { - fprintf(stderr, "Error opening uhd\n"); - exit(-1); - } -#else - printf("Error UHD not available. Select an output file\n"); - exit(-1); -#endif - } - - /* create ifft object */ - if (lte_ifft_init(&ifft, CPNORM, nof_prb)) { - fprintf(stderr, "Error creating iFFT object\n"); - exit(-1); - } - if (pbch_init(&pbch, 6, cell_id, CPNORM)) { - fprintf(stderr, "Error creating PBCH object\n"); - exit(-1); - } -} - -void base_free() { - - pbch_free(&pbch); - - lte_ifft_free(&ifft); - - if (slot_buffer) { - free(slot_buffer); - } - if (output_buffer) { - free(output_buffer); - } - if (output_file_name) { - filesink_free(&fsink); - } else { -#ifndef DISABLE_UHD - cuhd_close(&uhd); -#endif - } -} - -int main(int argc, char **argv) { - int nf, ns, N_id_2; - cf_t pss_signal[PSS_LEN]; - float sss_signal0[SSS_LEN]; // for subframe 0 - float sss_signal5[SSS_LEN]; // for subframe 5 - pbch_mib_t mib; - refsignal_t refs[NSLOTS_X_FRAME]; - int i; - cf_t *slot1_symbols[MAX_PORTS_CTRL]; - - -#ifdef DISABLE_UHD - if (argc < 3) { - usage(argv[0]); - exit(-1); - } -#endif - - parse_args(argc,argv); - - N_id_2 = cell_id%3; - slot_n_re = CPNORM_NSYMB * nof_prb * RE_X_RB; - slot_n_samples = SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb)); - - /* this *must* be called after setting slot_len_* */ - base_init(); - - /* Generate PSS/SSS signals */ - pss_generate(pss_signal, N_id_2); - sss_generate(sss_signal0, sss_signal5, cell_id); - - /* Generate CRS signals */ - for (i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "liblte/phy/phy.h" - -#ifndef DISABLE_UHD - #include "liblte/cuhd/cuhd.h" - void *uhd; -#endif - -#ifndef DISABLE_GRAPHICS - #include "liblte/graphics/plot.h" - plot_real_t poutfft; - plot_complex_t pce; - plot_scatter_t pscatrecv, pscatequal; -#endif - -#define MHZ 1000000 -#define SAMP_FREQ 1920000 -#define FLEN 9600 -#define FLEN_PERIOD 0.005 - -#define NOF_PORTS 2 - -float find_threshold = 20.0, track_threshold = 10.0; -int max_track_lost = 20, nof_frames = -1; -int track_len=300; -char *input_file_name = NULL; -int disable_plots = 0; - -int go_exit=0; - -float uhd_freq = 2600000000.0, uhd_gain = 20.0; -char *uhd_args = ""; - -filesource_t fsrc; -cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; -pbch_t pbch; -lte_fft_t fft; -chest_t chest; -sync_t sfind, strack; -cfo_t cfocorr; - - -enum sync_state {FIND, TRACK}; - -void usage(char *prog) { - printf("Usage: %s [iagfndvp]\n", prog); - printf("\t-i input_file [Default use USRP]\n"); -#ifndef DISABLE_UHD - printf("\t-a UHD args [Default %s]\n", uhd_args); - printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); - printf("\t-f UHD RX frequency [Default %.1f MHz]\n", uhd_freq/1000000); -#else - printf("\t UHD is disabled. CUHD library not available\n"); -#endif - printf("\t-n nof_frames [Default %d]\n", nof_frames); - printf("\t-p PSS threshold [Default %f]\n", find_threshold); -#ifndef DISABLE_GRAPHICS - printf("\t-d disable plots [Default enabled]\n"); -#else - printf("\t plots are disabled. Graphics library not available\n"); -#endif - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "iagfndvp")) != -1) { - switch(opt) { - case 'i': - input_file_name = argv[optind]; - break; - case 'a': - uhd_args = argv[optind]; - break; - case 'g': - uhd_gain = atof(argv[optind]); - break; - case 'f': - uhd_freq = atof(argv[optind]); - break; - case 'p': - find_threshold = atof(argv[optind]); - break; - case 'n': - nof_frames = atoi(argv[optind]); - break; - case 'd': - disable_plots = 1; - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - exit(-1); - } - } -} - -#ifndef DISABLE_GRAPHICS - -void init_plots() { - plot_init(); - plot_real_init(&poutfft); - plot_real_setTitle(&poutfft, "Output FFT - Magnitude"); - plot_real_setLabels(&poutfft, "Index", "dB"); - plot_real_setYAxisScale(&poutfft, -60, 0); - plot_real_setXAxisScale(&poutfft, 1, 504); - - plot_complex_init(&pce); - plot_complex_setTitle(&pce, "Channel Estimates"); - plot_complex_setYAxisScale(&pce, Ip, -0.01, 0.01); - plot_complex_setYAxisScale(&pce, Q, -0.01, 0.01); - plot_complex_setYAxisScale(&pce, Magnitude, 0, 0.01); - plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI); - - plot_scatter_init(&pscatrecv); - plot_scatter_setTitle(&pscatrecv, "Received Symbols"); - plot_scatter_setXAxisScale(&pscatrecv, -0.01, 0.01); - plot_scatter_setYAxisScale(&pscatrecv, -0.01, 0.01); - - plot_scatter_init(&pscatequal); - plot_scatter_setTitle(&pscatequal, "Equalized Symbols"); - plot_scatter_setXAxisScale(&pscatequal, -1, 1); - plot_scatter_setYAxisScale(&pscatequal, -1, 1); -} - -#endif - -int base_init(int frame_length) { - int i; - -#ifndef DISABLE_GRAPHICS - if (!disable_plots) { - init_plots(); - } -#else - printf("-- PLOTS are disabled. Graphics library not available --\n\n"); -#endif - - if (input_file_name) { - if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { - return -1; - } - } else { - /* open UHD device */ - #ifndef DISABLE_UHD - printf("Opening UHD device...\n"); - if (cuhd_open(uhd_args,&uhd)) { - fprintf(stderr, "Error opening uhd\n"); - return -1; - } - #else - printf("Error UHD not available. Select an input file\n"); - return -1; - #endif - } - - input_buffer = (cf_t*) malloc(frame_length * sizeof(cf_t)); - if (!input_buffer) { - perror("malloc"); - return -1; - } - - fft_buffer = (cf_t*) malloc(CPNORM_NSYMB * 72 * sizeof(cf_t)); - if (!fft_buffer) { - perror("malloc"); - return -1; - } - - for (i=0;i max_track_lost) { - INFO("%d frames lost. Going back to FIND", frame_cnt - last_found); - printf("\r\n"); - fflush(stdout); - printf("\r\n"); - state = FIND; - } - - // Correct CFO - INFO("Correcting CFO=%.4f\n", cfo); - - cfo_correct(&cfocorr, input_buffer, -cfo/128); - - if (nslot == 0 && find_idx + 960 < FLEN) { - INFO("Finding MIB at idx %d\n", find_idx); - if (mib_decoder_run(&input_buffer[find_idx], &mib)) { - INFO("MIB detected attempt=%d\n", frame_cnt); - if (verbose == VERBOSE_NONE) { - if (!nof_found_mib) { - printf("\r\n"); - fflush(stdout); - printf("\r\n"); - printf(" - Phy. CellId:\t%d\n", cell_id); - pbch_mib_fprint(stdout, &mib); - } - } - nof_found_mib++; - } else { - INFO("MIB not found attempt %d\n",frame_cnt); - } - if (frame_cnt) { - printf("SFN: %4d, CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Errors: %4d/%4d, ErrorRate: %.1e\r", mib.sfn, - cfo*15, timeoffset/5, find_idx, frame_cnt-2*(nof_found_mib-1), frame_cnt, - (float) (frame_cnt-2*(nof_found_mib-1))/frame_cnt); - fflush(stdout); - } - } - if (input_file_name) { - usleep(5000); - } - nslot = (nslot+10)%20; - break; - } - frame_cnt++; - } - - base_free(); - - printf("\nBye\n"); - exit(0); -} - diff --git a/lte/phy/examples/pdsch_enodeb.c b/lte/phy/examples/pdsch_enodeb.c new file mode 100644 index 000000000..b0b9ffc95 --- /dev/null +++ b/lte/phy/examples/pdsch_enodeb.c @@ -0,0 +1,393 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" +void *uhd; +#endif + +char *output_file_name = NULL; + +lte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 1, // cell_id + CPNORM // cyclic prefix +}; + +uint32_t cfi=1; +uint32_t mcs_idx = 12; +int nof_frames = -1; + +char *uhd_args = ""; +float uhd_amp = 0.01, uhd_gain = 10.0, uhd_freq = 2400000000; + +filesink_t fsink; +lte_fft_t ifft; +pbch_t pbch; +pcfich_t pcfich; +pdcch_t pdcch; +pdsch_t pdsch; +pdsch_harq_t harq_process; +regs_t regs; + +cf_t *sf_buffer = NULL, *output_buffer = NULL; +int sf_n_re, sf_n_samples; + +void usage(char *prog) { + printf("Usage: %s [agmfoncvp]\n", prog); +#ifndef DISABLE_UHD + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-l UHD amplitude [Default %.2f]\n", uhd_amp); + printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq / 1000000); +#else + printf("\t UHD is disabled. CUHD library not available\n"); +#endif + printf("\t-o output_file [Default USRP]\n"); + printf("\t-m MCS index [Default %d]\n", mcs_idx); + printf("\t-n number of frames [Default %d]\n", nof_frames); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-p nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "aglfmoncpv")) != -1) { + switch (opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'l': + uhd_amp = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 'o': + output_file_name = argv[optind]; + break; + case 'm': + mcs_idx = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'p': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +#ifdef DISABLE_UHD + if (!output_file_name) { + usage(argv[0]); + exit(-1); + } +#endif +} + +void base_init() { + + /* init memory */ + sf_buffer = malloc(sizeof(cf_t) * sf_n_re); + if (!sf_buffer) { + perror("malloc"); + exit(-1); + } + output_buffer = malloc(sizeof(cf_t) * sf_n_samples); + if (!output_buffer) { + perror("malloc"); + exit(-1); + } + /* open file or USRP */ + if (output_file_name) { + if (filesink_init(&fsink, output_file_name, COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", output_file_name); + exit(-1); + } + } else { +#ifndef DISABLE_UHD + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } +#else + printf("Error UHD not available. Select an output file\n"); + exit(-1); +#endif + } + + /* create ifft object */ + if (lte_ifft_init(&ifft, CPNORM, cell.nof_prb)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + if (pbch_init(&pbch, cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + + if (regs_init(®s, R_1, PHICH_NORM, cell)) { + fprintf(stderr, "Error initiating regs\n"); + exit(-1); + } + + if (pcfich_init(&pcfich, ®s, cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + + if (regs_set_cfi(®s, cfi)) { + fprintf(stderr, "Error setting CFI\n"); + exit(-1); + } + + if (pdcch_init(&pdcch, ®s, cell)) { + fprintf(stderr, "Error creating PDCCH object\n"); + exit(-1); + } + + if (pdsch_init(&pdsch, cell)) { + fprintf(stderr, "Error creating PDSCH object\n"); + exit(-1); + } + + pdsch_set_rnti(&pdsch, 1234); + + if (pdsch_harq_init(&harq_process, &pdsch)) { + fprintf(stderr, "Error initiating HARQ process\n"); + exit(-1); + } +} + +void base_free() { + + pdsch_harq_free(&harq_process); + pdsch_free(&pdsch); + pdcch_free(&pdcch); + regs_free(®s); + pbch_free(&pbch); + + lte_ifft_free(&ifft); + + if (sf_buffer) { + free(sf_buffer); + } + if (output_buffer) { + free(output_buffer); + } + if (output_file_name) { + filesink_free(&fsink); + } else { +#ifndef DISABLE_UHD + cuhd_close(&uhd); +#endif + } +} + +int main(int argc, char **argv) { + int nf, sf_idx, N_id_2; + cf_t pss_signal[PSS_LEN]; + float sss_signal0[SSS_LEN]; // for subframe 0 + float sss_signal5[SSS_LEN]; // for subframe 5 + pbch_mib_t mib; + ra_pdsch_t ra_dl; + ra_prb_t prb_alloc; + refsignal_t refs[NSLOTS_X_FRAME]; + int i, n; + char *data; + cf_t *sf_symbols[MAX_PORTS]; + dci_msg_t dci_msg; + dci_location_t locations[NSUBFRAMES_X_FRAME][10]; + +#ifdef DISABLE_UHD + if (argc < 3) { + usage(argv[0]); + exit(-1); + } +#endif + + parse_args(argc, argv); + + N_id_2 = cell.id % 3; + sf_n_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB; + sf_n_samples = 2 * SLOT_LEN(lte_symbol_sz(cell.nof_prb)); + + /* this *must* be called after setting slot_len_* */ + base_init(); + + /* Generate PSS/SSS signals */ + pss_generate(pss_signal, N_id_2); + sss_generate(sss_signal0, sss_signal5, cell.id); + + /* Generate CRS signals */ + for (i = 0; i < NSLOTS_X_FRAME; i++) { + if (refsignal_init_LTEDL(&refs[i], 0, i, cell)) { + fprintf(stderr, "Error initiating CRS slot=%d\n", i); + return -1; + } + } + + mib.nof_ports = cell.nof_ports; + mib.nof_prb = cell.nof_prb; + mib.phich_length = PHICH_NORM; + mib.phich_resources = R_1; + mib.sfn = 0; + + for (i = 0; i < MAX_PORTS; i++) { // now there's only 1 port + sf_symbols[i] = sf_buffer; + } + +#ifndef DISABLE_UHD + if (!output_file_name) { + printf("Set TX rate: %.2f MHz\n", + cuhd_set_tx_srate(uhd, lte_sampling_freq_hz(cell.nof_prb)) / 1000000); + printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, uhd_gain)); + printf("Set TX freq: %.2f MHz\n", + cuhd_set_tx_freq(uhd, uhd_freq) / 1000000); + } +#endif + + bzero(&ra_dl, sizeof(ra_pdsch_t)); + ra_dl.harq_process = 0; + ra_dl.mcs_idx = mcs_idx; + ra_dl.ndi = 0; + ra_dl.rv_idx = 0; + ra_dl.alloc_type = alloc_type0; + ra_dl.type0_alloc.rbg_bitmask = 0xffffffff; + + dci_msg_pack_pdsch(&ra_dl, &dci_msg, Format1, cell.nof_prb, false); + + ra_prb_get_dl(&prb_alloc, &ra_dl, cell.nof_prb); + ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, 1, cell.nof_prb<10?(cfi+1):cfi, CPNORM); + ra_mcs_from_idx_dl(mcs_idx, cell.nof_prb, &ra_dl.mcs); + + ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb); + + /* Initiate valid DCI locations */ + for (i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" +#include "iodev.h" + +#ifndef DISABLE_GRAPHICS +void init_plots(); +void do_plots(ue_dl_t *q, uint32_t sf_idx); +#endif + +int go_exit = 0; + +/* Local function definitions */ +void init_plots(); + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + uint16_t rnti; + int nof_subframes; + bool disable_plots; + iodev_cfg_t io_config; +}prog_args_t; + +void args_default(prog_args_t *args) { + args->io_config.cell_id_file = 195; + args->io_config.nof_prb_file = 50; + args->io_config.nof_ports_file = 2; + args->rnti = SIRNTI; + args->nof_subframes = -1; + args->disable_plots = false; + args->io_config.find_threshold = -1.0; + args->io_config.input_file_name = NULL; + args->io_config.uhd_args = ""; + args->io_config.uhd_freq = -1.0; + args->io_config.uhd_gain = 60.0; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [cargfndvtb] [-i input_file | -f rx_frequency (in Hz)]\n", prog); + printf("\t-c cell_id if reading from file [Default %d]\n", args->io_config.cell_id_file); + printf("\t-p nof_prb if reading from file [Default %d]\n", args->io_config.nof_prb_file); + printf("\t-o nof_ports if reading from file [Default %d]\n", args->io_config.nof_ports_file); + printf("\t-r RNTI to look for [Default 0x%x]\n", args->rnti); +#ifndef DISABLE_UHD + printf("\t-a UHD args [Default %s]\n", args->io_config.uhd_args); + printf("\t-g UHD RX gain [Default %.2f dB]\n", args->io_config.uhd_gain); +#else + printf("\t UHD is disabled. CUHD library not available\n"); +#endif + printf("\t-b Decode PBCH only [Default All channels]\n"); + printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes); + printf("\t-t PSS threshold [Default %f]\n", args->io_config.find_threshold); +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "icagfndvtbpro")) != -1) { + switch (opt) { + case 'i': + args->io_config.input_file_name = argv[optind]; + break; + case 'c': + args->io_config.cell_id_file = atoi(argv[optind]); + break; + case 'p': + args->io_config.nof_prb_file = atoi(argv[optind]); + break; + case 'o': + args->io_config.nof_ports_file = atoi(argv[optind]); + break; + case 'a': + args->io_config.uhd_args = argv[optind]; + break; + case 'g': + args->io_config.uhd_gain = atof(argv[optind]); + break; + case 'f': + args->io_config.uhd_freq = atof(argv[optind]); + break; + case 't': + args->io_config.find_threshold = atof(argv[optind]); + break; + case 'n': + args->nof_subframes = atoi(argv[optind]); + break; + case 'r': + args->rnti= atoi(argv[optind]); + break; + case 'd': + args->disable_plots = true; + break; + case 'v': + verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->io_config.uhd_freq < 0 && args->io_config.input_file_name == NULL) { + usage(args, argv[0]); + } +} +/**********************************************************************/ + +void sigintHandler(int x) { + go_exit = 1; +} + +/* TODO: Do something with the output data */ +char data[10000]; + +extern float mean_exec_time; + +int main(int argc, char **argv) { + int ret; + cf_t *sf_buffer; + iodev_t iodev; + prog_args_t prog_args; + lte_cell_t cell; + ue_dl_t ue_dl; + int64_t sf_cnt; + pbch_mib_t mib; + bool printed_sib = false; + int rlen; + + parse_args(&prog_args, argc, argv); + +#ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots) { + init_plots(); + } +#endif + + /* Setup SIGINT handler */ + printf("\n --- Press Ctrl+C to exit --- \n"); + signal(SIGINT, sigintHandler); + + /* Initialize subframe counter */ + sf_cnt = 0; + + if (iodev_init(&iodev, &prog_args.io_config, &cell, &mib)) { + exit(-1); + } + + if (ue_dl_init(&ue_dl, cell, mib.phich_resources, mib.phich_length, 1234)) { + fprintf(stderr, "Error initiating UE downlink processing module\n"); + exit(-1); + } + pdsch_set_rnti(&ue_dl.pdsch, prog_args.rnti); + + /* Main loop */ + while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) { + + ret = iodev_receive(&iodev, &sf_buffer); + if (ret < 0) { + fprintf(stderr, "Error reading from input device (%d)\n", ret); + break; + } + + /* iodev_receive returns 1 if successfully read 1 aligned subframe */ + if (ret == 1) { + rlen = ue_dl_decode(&ue_dl, sf_buffer, data, iodev_get_sfidx(&iodev), prog_args.rnti); + if (rlen < 0) { + fprintf(stderr, "\nError running receiver\n");fflush(stdout); + exit(-1); + } + if (prog_args.rnti == SIRNTI && !printed_sib && rlen > 0) { + printf("\n\nDecoded SIB1 Message: "); + vec_fprint_hex(stdout, data, rlen); + printf("\n");fflush(stdout); + printed_sib = true; + } + + // Plot and Printf + if (!(sf_cnt % 10)) { + printf("CFO: %+.4f KHz, SFO: %+.4f Khz, NOI: %.2f Errors: %4d/%4d/%4d, BLER: %.1e, Texec: %.2f\r", + ue_sync_get_cfo(&iodev.sframe)/1000, ue_sync_get_sfo(&iodev.sframe)/1000, + pdsch_average_noi(&ue_dl.pdsch), + (int) ue_dl.pkt_errors, (int) ue_dl.pkts_total, (int) ue_dl.nof_trials, + (float) ue_dl.pkt_errors / ue_dl.pkts_total, + mean_exec_time); + } + #ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots && iodev_get_sfidx(&iodev) == 5) { + do_plots(&ue_dl, 5); + } + #endif + } else if (ret == 0) { + printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r", + sync_get_peak_value(&iodev.sframe.sfind), + iodev.sframe.frame_total_cnt, iodev.sframe.state); + } + sf_cnt++; + } // Main loop + + ue_dl_free(&ue_dl); + iodev_free(&iodev); + + printf("\nBye\n"); + exit(0); +} + + + + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + + +#include "liblte/graphics/plot.h" +plot_real_t poutfft; +plot_complex_t pce; +plot_scatter_t pscatrecv, pscatequal; + +float tmp_plot[SLOT_LEN_RE(MAX_PRB, CPNORM)]; + +void init_plots() { + plot_init(); + plot_real_init(&poutfft); + plot_real_setTitle(&poutfft, "Output FFT - Magnitude"); + plot_real_setLabels(&poutfft, "Index", "dB"); + plot_real_setYAxisScale(&poutfft, -30, 20); + + plot_complex_init(&pce); + plot_complex_setTitle(&pce, "Channel Estimates"); + plot_complex_setYAxisScale(&pce, Ip, -3, 3); + plot_complex_setYAxisScale(&pce, Q, -3, 3); + plot_complex_setYAxisScale(&pce, Magnitude, 0, 4); + plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI); + + plot_scatter_init(&pscatrecv); + plot_scatter_setTitle(&pscatrecv, "Received Symbols"); + plot_scatter_setXAxisScale(&pscatrecv, -4, 4); + plot_scatter_setYAxisScale(&pscatrecv, -4, 4); + + plot_scatter_init(&pscatequal); + plot_scatter_setTitle(&pscatequal, "Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal, -2, 2); + plot_scatter_setYAxisScale(&pscatequal, -2, 2); +} + +void do_plots(ue_dl_t *q, uint32_t sf_idx) { + int i; + uint32_t nof_re = SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp); + uint32_t nof_symbols = q->harq_process[0].prb_alloc.re_sf[sf_idx]; + for (i = 0; i < nof_re; i++) { + tmp_plot[i] = 10 * log10f(cabsf(q->sf_symbols[i])); + if (isinf(tmp_plot[i])) { + tmp_plot[i] = -80; + } + } + plot_real_setNewData(&poutfft, tmp_plot, nof_re); + plot_complex_setNewData(&pce, q->ce[0], nof_re); + plot_scatter_setNewData(&pscatrecv, q->pdsch.pdsch_symbols[0], nof_symbols); + plot_scatter_setNewData(&pscatequal, q->pdsch.pdsch_d, nof_symbols); +} + +#endif diff --git a/lte/phy/examples/scan_mib.c b/lte/phy/examples/scan_mib.c deleted file mode 100644 index 9bdf883e6..000000000 --- a/lte/phy/examples/scan_mib.c +++ /dev/null @@ -1,559 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2014 The libLTE Developers. See the - * COPYRIGHT file at the top-level directory of this distribution. - * - * \section LICENSE - * - * This file is part of the libLTE library. - * - * libLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * libLTE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * A copy of the GNU Lesser General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "liblte/phy/phy.h" - -//#define DISABLE_UHD - -#ifndef DISABLE_UHD -#include "liblte/cuhd/cuhd.h" -#endif - -#define MHZ 1000000 -#define SAMP_FREQ 1920000 -#define RSSI_FS 1000000 -#define FLEN 9600 -#define FLEN_PERIOD 0.005 - -#define RSSI_DECIM 20 - -#define IS_SIGNAL(i) (10*log10f(rssi[i]) + 30 > rssi_threshold) - -int band, earfcn=-1; -float find_threshold = 10.0, track_threshold = 8.0; -int earfcn_start=-1, earfcn_end = -1; -float rssi_threshold = -45.0; -int max_track_lost=9; -int nof_frames_find=20, nof_frames_track=100, nof_samples_rssi=50000; -int track_len=500; - -cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; -pbch_t pbch; -lte_fft_t fft; -chest_t chest; -sync_t sfind, strack; -cfo_t cfocorr; - -float *cfo_v; -int *idx_v, *idx_valid, *t; -float *p2a_v; -void *uhd; -int nof_bands; -float uhd_gain = 20.0; - -#define MAX_EARFCN 1000 -lte_earfcn_t channels[MAX_EARFCN]; -float rssi[MAX_EARFCN]; -float rssi_d[MAX_EARFCN/RSSI_DECIM]; -float freqs[MAX_EARFCN]; -float cfo[MAX_EARFCN]; -float p2a[MAX_EARFCN]; - -enum sync_state {INIT, FIND, TRACK, MIB, DONE}; - - -void usage(char *prog) { - printf("Usage: %s [seRrFfTtgv] -b band\n", prog); - printf("\t-s earfcn_start [Default All]\n"); - printf("\t-e earfcn_end [Default All]\n"); - printf("\t-R rssi_nof_samples [Default %d]\n", nof_samples_rssi); - printf("\t-r rssi_threshold [Default %.2f dBm]\n", rssi_threshold); - printf("\t-F pss_find_nof_frames [Default %d]\n", nof_frames_find); - printf("\t-f pss_find_threshold [Default %.2f]\n", find_threshold); - printf("\t-T pss_track_nof_frames [Default %d]\n", nof_frames_track); - printf("\t-t pss_track_threshold [Default %.2f]\n", track_threshold); - printf("\t-l pss_track_len [Default %d]\n", track_len); - printf("\t-g gain [Default %.2f dB]\n", uhd_gain); - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "bseRrFfTtgv")) != -1) { - switch(opt) { - case 'b': - band = atoi(argv[optind]); - break; - case 's': - earfcn_start = atoi(argv[optind]); - break; - case 'e': - earfcn_end = atoi(argv[optind]); - break; - case 'R': - nof_samples_rssi = atoi(argv[optind]); - break; - case 'r': - rssi_threshold = -atof(argv[optind]); - break; - case 'F': - nof_frames_find = atoi(argv[optind]); - break; - case 'f': - find_threshold = atof(argv[optind]); - break; - case 'T': - nof_frames_track = atoi(argv[optind]); - break; - case 't': - track_threshold = atof(argv[optind]); - break; - case 'g': - uhd_gain = atof(argv[optind]); - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - exit(-1); - } - } -} - -int base_init(int frame_length) { - int i; - - input_buffer = malloc(2 * frame_length * sizeof(cf_t)); - if (!input_buffer) { - perror("malloc"); - return -1; - } - - fft_buffer = malloc(CPNORM_NSYMB * 72 * sizeof(cf_t)); - if (!fft_buffer) { - perror("malloc"); - return -1; - } - - for (i=0;i 0) { - return mean/n; - } else { - return 0.0; - } -} - -int preprocess_idx(int *in, int *out, int *period, int len) { - int i, n; - n=0; - for (i=0;i 100) { - /* scan every Mhz, that is 10 freqs */ - for (i=0;i= nof_frames_find) { - state = INIT; - freq++; - } - } - break; - case TRACK: - INFO("Tracking PSS find_idx %d offset %d\n", find_idx, find_idx - track_len); - - track_idx = sync_run(&strack, &input_buffer[FLEN + find_idx - track_len]); - p2a_v[frame_cnt] = sync_get_peak_to_avg(&strack); - - /* save cell id for the best peak-to-avg */ - if (p2a_v[frame_cnt] > max_peak_to_avg) { - max_peak_to_avg = p2a_v[frame_cnt]; - cell_id = sync_get_cell_id(&strack); - } - if (track_idx != -1) { - cfo_v[frame_cnt] = sync_get_cfo(&strack); - last_found = frame_cnt; - find_idx += track_idx - track_len; - idx_v[frame_cnt] = find_idx; - nslot = sync_get_slot_id(&strack); - } else { - idx_v[frame_cnt] = -1; - cfo_v[frame_cnt] = 0.0; - } - /* if we missed to many PSS it is not a cell, next freq */ - if (frame_cnt - last_found > max_track_lost) { - INFO("\n[%3d/%d]: EARFCN %d Freq. %.2f MHz %d frames lost\n", freq, nof_bands, - channels[freq].id, channels[freq].fd, frame_cnt - last_found); - - state = INIT; - freq++; - } else if (frame_cnt >= nof_frames_track) { - mib_decoder_init(cell_id); - - cfo[freq] = mean_valid(idx_v, cfo_v, frame_cnt); - p2a[freq] = mean_valid(idx_v, p2a_v, frame_cnt); - valid_frames = preprocess_idx(idx_v, idx_valid, t, frame_cnt); - sfo = sfo_estimate_period(idx_valid, t, valid_frames, FLEN_PERIOD); - - state = MIB; - nslot=(nslot+10)%20; - } - break; - case MIB: - INFO("Finding MIB at freq %.2f Mhz offset=%d, cell_id=%d, slot_idx=%d\n", channels[freq].fd, find_idx, cell_id, nslot); - - // TODO: Correct SFO - - // Correct CFO - INFO("Correcting CFO=%.4f\n", cfo[freq]); - cfo_correct(&cfocorr, &input_buffer[FLEN], (-cfo[freq])/128); - - if (nslot == 0) { - if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) { - INFO("MIB detected attempt=%d\n", mib_attempts); - state = DONE; - } else { - INFO("MIB not detected attempt=%d\n", mib_attempts); - if (mib_attempts == 0) { - freq++; - state = INIT; - } - } - mib_attempts++; - } - nslot = (nslot+10)%20; - - - break; - case DONE: - printf("\n[%3d/%d]: FOUND EARFCN %d Freq. %.2f MHz. " - "PAR %2.2f dB, CFO=%+.2f KHz, SFO=%+2.3f KHz, CELL_ID=%3d\n", freq, nof_bands, - channels[freq].id, channels[freq].fd, - 10*log10f(p2a[freq]), cfo[freq] * 15, sfo / 1000, cell_id); - pbch_mib_fprint(stdout, &mib); - state = INIT; - freq++; - break; - } - - /** FIXME: This is not necessary at all */ - if (state == TRACK || state == FIND) { - memcpy(input_buffer, &input_buffer[FLEN], FLEN * sizeof(cf_t)); - } - frame_cnt++; - } - } - - base_free(); - - printf("\n\nDone\n"); - exit(0); -} - diff --git a/lte/phy/examples/scan_pss.c b/lte/phy/examples/scan_pss.c deleted file mode 100644 index 7f86b9f30..000000000 --- a/lte/phy/examples/scan_pss.c +++ /dev/null @@ -1,509 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2014 The libLTE Developers. See the - * COPYRIGHT file at the top-level directory of this distribution. - * - * \section LICENSE - * - * This file is part of the libLTE library. - * - * libLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * libLTE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * A copy of the GNU Lesser General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "liblte/phy/phy.h" -#include "liblte/cuhd/cuhd.h" - -#define MHZ 1000000 -#define SAMP_FREQ 1920000 -#define RSSI_FS 1000000 -#define FLEN 9600 -#define FLEN_PERIOD 0.005 - -#define RSSI_DECIM 20 - -#define IS_SIGNAL(i) (10*log10f(rssi[i]) + 30 > rssi_threshold) - - -int band, earfcn=-1; -float find_threshold = 10.0, track_threshold = 8.0; -int earfcn_start=-1, earfcn_end = -1; -float rssi_threshold = -45.0; -int max_track_lost=9; -int nof_frames_find=20, nof_frames_track=100, nof_samples_rssi=50000; -int track_len=500; - -cf_t *input_buffer; -float *cfo_v; -int *idx_v, *idx_valid, *t; -float *p2a_v; -void *uhd; -int nof_bands; -float uhd_gain = 20.0; - -#define MAX_EARFCN 1000 -lte_earfcn_t channels[MAX_EARFCN]; -float rssi[MAX_EARFCN]; -float rssi_d[MAX_EARFCN/RSSI_DECIM]; -float freqs[MAX_EARFCN]; -float cfo[MAX_EARFCN]; -float p2a[MAX_EARFCN]; - -enum sync_state {INIT, FIND, TRACK, DONE}; - -void print_to_matlab(); - -void usage(char *prog) { - printf("Usage: %s [seRrFfTtgv] -b band\n", prog); - printf("\t-s earfcn_start [Default All]\n"); - printf("\t-e earfcn_end [Default All]\n"); - printf("\t-R rssi_nof_samples [Default %d]\n", nof_samples_rssi); - printf("\t-r rssi_threshold [Default %.2f dBm]\n", rssi_threshold); - printf("\t-F pss_find_nof_frames [Default %d]\n", nof_frames_find); - printf("\t-f pss_find_threshold [Default %.2f]\n", find_threshold); - printf("\t-T pss_track_nof_frames [Default %d]\n", nof_frames_track); - printf("\t-t pss_track_threshold [Default %.2f]\n", track_threshold); - printf("\t-l pss_track_len [Default %d]\n", track_len); - printf("\t-g gain [Default %.2f dB]\n", uhd_gain); - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "bseRrFfTtgv")) != -1) { - switch(opt) { - case 'b': - band = atoi(argv[optind]); - break; - case 's': - earfcn_start = atoi(argv[optind]); - break; - case 'e': - earfcn_end = atoi(argv[optind]); - break; - case 'R': - nof_samples_rssi = atoi(argv[optind]); - break; - case 'r': - rssi_threshold = -atof(argv[optind]); - break; - case 'F': - nof_frames_find = atoi(argv[optind]); - break; - case 'f': - find_threshold = atof(argv[optind]); - break; - case 'T': - nof_frames_track = atoi(argv[optind]); - break; - case 't': - track_threshold = atof(argv[optind]); - break; - case 'g': - uhd_gain = atof(argv[optind]); - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - exit(-1); - } - } -} - -int base_init(int frame_length) { - - input_buffer = malloc(2 * frame_length * sizeof(cf_t)); - if (!input_buffer) { - perror("malloc"); - exit(-1); - } - - idx_v = malloc(nof_frames_track * sizeof(int)); - if (!idx_v) { - perror("malloc"); - exit(-1); - } - idx_valid = malloc(nof_frames_track * sizeof(int)); - if (!idx_valid) { - perror("malloc"); - exit(-1); - } - t = malloc(nof_frames_track * sizeof(int)); - if (!t) { - perror("malloc"); - exit(-1); - } - cfo_v = malloc(nof_frames_track * sizeof(float)); - if (!cfo_v) { - perror("malloc"); - exit(-1); - } - p2a_v = malloc(nof_frames_track * sizeof(float)); - if (!p2a_v) { - perror("malloc"); - exit(-1); - } - - bzero(cfo, sizeof(float) * MAX_EARFCN); - bzero(p2a, sizeof(float) * MAX_EARFCN); - - /* open UHD device */ - printf("Opening UHD device...\n"); - if (cuhd_open("",&uhd)) { - fprintf(stderr, "Error opening uhd\n"); - exit(-1); - } - - return 0; -} - -void base_free() { - - cuhd_close(uhd); - free(input_buffer); - free(idx_v); - free(idx_valid); - free(t); - free(cfo_v); - free(p2a_v); -} - -float mean_valid(int *idx_v, float *x, int nof_frames) { - int i; - float mean = 0; - int n = 0; - for (i=0;i 0) { - return mean/n; - } else { - return 0.0; - } -} - -int preprocess_idx(int *in, int *out, int *period, int len) { - int i, n; - n=0; - for (i=0;i 100) { - /* scan every Mhz, that is 10 freqs */ - for (i=0;i= nof_frames_find) { - state = INIT; - printf("[%3d/%d]: EARFCN %d Freq. %.2f MHz No PSS found\r", freq, nof_bands, - channels[freq].id, channels[freq].fd, frame_cnt - last_found); - if (VERBOSE_ISINFO()) { - printf("\n"); - } - freq++; - } - } - break; - case TRACK: - INFO("Tracking PSS find_idx %d offset %d\n", find_idx, find_idx + track_len); - - filesink_write(&fs, &input_buffer[FLEN+find_idx+track_len], track_len); - - track_idx = sync_run(&strack, &input_buffer[FLEN + find_idx - track_len]); - p2a_v[frame_cnt] = sync_get_peak_to_avg(&strack); - - /* save cell id for the best peak-to-avg */ - if (p2a_v[frame_cnt] > max_peak_to_avg) { - max_peak_to_avg = p2a_v[frame_cnt]; - cell_id = sync_get_cell_id(&strack); - } - if (track_idx != -1) { - cfo_v[frame_cnt] = sync_get_cfo(&strack); - last_found = frame_cnt; - find_idx += track_idx - track_len; - idx_v[frame_cnt] = find_idx; - } else { - idx_v[frame_cnt] = -1; - cfo_v[frame_cnt] = 0.0; - } - /* if we missed to many PSS it is not a cell, next freq */ - if (frame_cnt - last_found > max_track_lost) { - INFO("\n[%3d/%d]: EARFCN %d Freq. %.2f MHz %d frames lost\n", freq, nof_bands, - channels[freq].id, channels[freq].fd, frame_cnt - last_found); - - state = INIT; - freq++; - } else if (frame_cnt >= nof_frames_track) { - state = DONE; - } - break; - case DONE: - - cfo[freq] = mean_valid(idx_v, cfo_v, frame_cnt); - p2a[freq] = mean_valid(idx_v, p2a_v, frame_cnt); - valid_frames = preprocess_idx(idx_v, idx_valid, t, frame_cnt); - sfo = sfo_estimate_period(idx_valid, t, valid_frames, FLEN_PERIOD); - - printf("\n[%3d/%d]: FOUND EARFCN %d Freq. %.2f MHz. " - "PAR %2.2f dB, CFO=%+.2f KHz, SFO=%+2.3f KHz, CELL_ID=%3d\n", freq, nof_bands, - channels[freq].id, channels[freq].fd, - 10*log10f(p2a[freq]), cfo[freq] * 15, sfo / 1000, cell_id); - state = INIT; - freq++; - break; - } - if (state == TRACK || (state == FIND && frame_cnt)) { - memcpy(input_buffer, &input_buffer[FLEN], FLEN * sizeof(cf_t)); - } - frame_cnt++; - } - } - - print_to_matlab(); - - sync_free(&sfind); - base_free(); - - printf("\n\nDone\n"); - exit(0); -} - -void print_to_matlab() { - int i; - - FILE *f = fopen("output.m", "w"); - if (!f) { - perror("fopen"); - exit(-1); - } - fprintf(f, "fd=["); - for (i=0;i -#include -#include -#include -#include -#include - -#include "liblte/phy/phy.h" -#include "liblte/cuhd/cuhd.h" - -int nof_frames=1000; -int band; - -cf_t *input_buffer, *fft_buffer; -void *uhd; -int earfcn_start = -1, earfcn_end = -1; - -#define MAX_EARFCN 1000 -lte_earfcn_t channels[MAX_EARFCN]; - -#define MHZ 1000000 -#define SAMP_FREQ 1920000 - -void usage(char *prog) { - printf("Usage: %s [nvse] -b band\n", prog); - printf("\t-s earfcn_start [Default All]\n"); - printf("\t-e earfcn_end [Default All]\n"); - printf("\t-n number of frames [Default %d]\n", nof_frames); - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "sebnv")) != -1) { - switch(opt) { - case 'b': - band = atoi(argv[optind]); - break; - case 's': - earfcn_start = atoi(argv[optind]); - break; - case 'e': - earfcn_end = atoi(argv[optind]); - break; - case 'n': - nof_frames = atoi(argv[optind]); - break; - case 'v': - verbose++; - break; - default: - usage(argv[0]); - exit(-1); - } - } -} - -int base_init() { - - input_buffer = malloc(4 * 960 * sizeof(cf_t)); - if (!input_buffer) { - perror("malloc"); - exit(-1); - } - - /* open UHD device */ - printf("Opening UHD device...\n"); - if (cuhd_open("",&uhd)) { - fprintf(stderr, "Error opening uhd\n"); - exit(-1); - } - - printf("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); - cuhd_set_rx_srate(uhd, SAMP_FREQ); - - printf("Starting receiver...\n"); - cuhd_start_rx_stream(uhd); - return 0; -} - - - -int main(int argc, char **argv) { - int frame_cnt; - int i; - int nsamples; - float rssi[MAX_EARFCN]; - - if (argc < 3) { - usage(argv[0]); - exit(-1); - } - - parse_args(argc,argv); - - if (base_init()) { - fprintf(stderr, "Error initializing memory\n"); - exit(-1); - } - - int nof_bands = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); - printf("Scanning %d freqs in band %d\n", nof_bands, band); - for (i=0;i corr_peak_threshold) { + if (peak_value[N_id_2] > corr_peak_threshold) { sss_idx = peak_pos[N_id_2]-2*(symbol_sz+CP(symbol_sz,CPNORM_LEN)); if (sss_idx >= 0) { @@ -228,7 +227,7 @@ int main(int argc, char **argv) { cfo[frame_cnt] = pss_synch_cfo_compute(&pss[N_id_2], &input[peak_pos[N_id_2]-128]); printf("\t%d\t%d\t%d\t%d\t%.3f\t\t%3d\t%d\t%d\t%.3f\n", frame_cnt,N_id_2, sss_synch_N_id_1(&sss[N_id_2], m0, m1), - sss_synch_subframe(m0, m1), peak_value[N_id_2]/mean_value[N_id_2], + sss_synch_subframe(m0, m1), peak_value[N_id_2], peak_pos[N_id_2], m0, m1, cfo[frame_cnt]); } diff --git a/lte/phy/include/liblte/phy/agc/agc.h b/lte/phy/include/liblte/phy/agc/agc.h new file mode 100644 index 000000000..90a02a512 --- /dev/null +++ b/lte/phy/include/liblte/phy/agc/agc.h @@ -0,0 +1,77 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#ifndef AGC_ +#define AGC_ + +#include +#include +#include + +#include "liblte/config.h" + +/* Automatic Gain Control + * + */ +typedef _Complex float cf_t; + +#define AGC_DEFAULT_BW (5e-2) + +typedef struct LIBLTE_API{ + float bandwidth; + float gain; + float y_out; + bool lock; + bool isfirst; +} agc_t; + +LIBLTE_API int agc_init (agc_t *q); + +LIBLTE_API void agc_free(agc_t *q); + +LIBLTE_API void agc_reset(agc_t *q); + +LIBLTE_API void agc_set_bandwidth(agc_t *q, + float bandwidth); + +LIBLTE_API float agc_get_rssi(agc_t *q); + +LIBLTE_API float agc_get_output_level(agc_t *q); + +LIBLTE_API float agc_get_gain(agc_t *q); + +LIBLTE_API void agc_lock(agc_t *q, + bool enable); + +LIBLTE_API void agc_process(agc_t *q, + cf_t *input, + cf_t *output, + uint32_t len); + +#endif // AGC_ diff --git a/lte/phy/include/liblte/phy/ch_estimation/chest.h b/lte/phy/include/liblte/phy/ch_estimation/chest.h index b1f1e59a7..2e4434fab 100644 --- a/lte/phy/include/liblte/phy/ch_estimation/chest.h +++ b/lte/phy/include/liblte/phy/ch_estimation/chest.h @@ -33,14 +33,19 @@ #include #include "liblte/config.h" + +#include "liblte/phy/resampling/interp.h" #include "liblte/phy/ch_estimation/refsignal.h" -#include "liblte/phy/filter/filter2d.h" #include "liblte/phy/common/phy_common.h" typedef _Complex float cf_t; /* this is only a shortcut */ -typedef enum {LINEAR} chest_interp_t; -typedef void (*interpolate_fnc_t) (cf_t *input, cf_t *output, int M, int len, int off_st, int off_end); +typedef void (*interpolate_fnc_t) (cf_t *input, + cf_t *output, + uint32_t M, + uint32_t len, + uint32_t off_st, + uint32_t off_end); /** This is an OFDM channel estimator. * It works with any reference signal pattern, provided by the object @@ -50,31 +55,94 @@ typedef void (*interpolate_fnc_t) (cf_t *input, cf_t *output, int M, int len, in */ /* Low-level API */ -typedef struct LIBLTE_API{ - int nof_ports; - int nof_symbols; - int nof_prb; - lte_cp_t cp; +typedef struct LIBLTE_API { + uint32_t nof_ports; + uint32_t nof_re; + uint32_t nof_symbols; + refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME]; - interpolate_fnc_t interp; + interp_t interp_time[MAX_PORTS]; + interp_t interp_freq[MAX_PORTS]; + }chest_t; -LIBLTE_API int chest_init(chest_t *q, chest_interp_t interp, lte_cp_t cp, int nof_prb, int nof_ports); -LIBLTE_API void chest_free(chest_t *q); - -LIBLTE_API int chest_ref_LTEDL_slot_port(chest_t *q, int port, int nslot, int cell_id); -LIBLTE_API int chest_ref_LTEDL_slot(chest_t *q, int nslot, int cell_id); -LIBLTE_API int chest_ref_LTEDL(chest_t *q, int cell_id); +LIBLTE_API int chest_init(chest_t *q, + uint32_t nof_re, + uint32_t nof_symbols, + uint32_t nof_ports); -LIBLTE_API void chest_ce_ref(chest_t *q, cf_t *input, int nslot, int port_id, int nref); -LIBLTE_API void chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, int nslot, int port_id); -LIBLTE_API void chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, int nslot); +LIBLTE_API void chest_free(chest_t *q); -LIBLTE_API void chest_fprint(chest_t *q, FILE *stream, int nslot, int port_id); -LIBLTE_API void chest_ref_fprint(chest_t *q, FILE *stream, int nslot, int port_id); -LIBLTE_API void chest_recvsig_fprint(chest_t *q, FILE *stream, int nslot, int port_id); -LIBLTE_API void chest_ce_fprint(chest_t *q, FILE *stream, int nslot, int port_id); -LIBLTE_API int chest_ref_symbols(chest_t *q, int port_id, int nslot, int l[2]); +LIBLTE_API int chest_set_nof_ports(chest_t *q, + uint32_t nof_ports); + +LIBLTE_API int chest_init_LTEDL(chest_t *q, + lte_cell_t cell); + +LIBLTE_API int chest_ref_LTEDL_slot_port(chest_t *q, + uint32_t nslot, + uint32_t port_id, + lte_cell_t cell); + +LIBLTE_API int chest_ref_LTEDL_slot(chest_t *q, + uint32_t nslot, + lte_cell_t cell); + +LIBLTE_API int chest_ref_LTEDL(chest_t *q, + lte_cell_t cell); + +LIBLTE_API int chest_ce_ref(chest_t *q, + cf_t *input, + uint32_t nslot, + uint32_t port_id, + uint32_t nref); + +LIBLTE_API int chest_ce_slot_port(chest_t *q, + cf_t *input, + cf_t *ce, + uint32_t nslot, + uint32_t port_id); + +LIBLTE_API int chest_ce_sf_port(chest_t *q, + cf_t *input, + cf_t *ce, + uint32_t sf_idx, + uint32_t port_id); + +LIBLTE_API int chest_ce_slot(chest_t *q, + cf_t *input, + cf_t *ce[MAX_PORTS], + uint32_t nslot); + +LIBLTE_API int chest_ce_sf(chest_t *q, + cf_t *input, + cf_t *ce[MAX_PORTS], + uint32_t sf_idx); + +LIBLTE_API void chest_fprint(chest_t *q, + FILE *stream, + uint32_t nslot, + uint32_t port_id); + +LIBLTE_API void chest_ref_fprint(chest_t *q, + FILE *stream, + uint32_t nslot, + uint32_t port_id); + +LIBLTE_API void chest_recvsig_fprint(chest_t *q, + FILE *stream, + uint32_t nslot, + uint32_t port_id); + +LIBLTE_API void chest_ce_fprint(chest_t *q, + FILE *stream, + uint32_t nslot, + uint32_t port_id); + +LIBLTE_API int chest_ref_symbols(chest_t *q, + uint32_t port_id, + uint32_t nslot, + uint32_t l[2]); /* High-level API */ @@ -91,8 +159,7 @@ typedef struct LIBLTE_API{ cf_t *input; int in_len; struct chest_ctrl_in { - int slot_id; // slot id in the 10ms frame - int cell_id; + int sf_idx; // subframe id in the 10ms frame } ctrl_in; cf_t *output[MAX_PORTS]; int out_len[MAX_PORTS]; @@ -105,3 +172,11 @@ LIBLTE_API int chest_work(chest_hl* hl); LIBLTE_API int chest_stop(chest_hl* hl); #endif + + + + + + + + diff --git a/lte/phy/include/liblte/phy/ch_estimation/refsignal.h b/lte/phy/include/liblte/phy/ch_estimation/refsignal.h index 7b1f6518a..51ad505ff 100644 --- a/lte/phy/include/liblte/phy/ch_estimation/refsignal.h +++ b/lte/phy/include/liblte/phy/ch_estimation/refsignal.h @@ -43,26 +43,30 @@ typedef _Complex float cf_t; typedef struct LIBLTE_API{ - int time_idx; - int freq_idx; + uint32_t time_idx; + uint32_t freq_idx; cf_t simbol; cf_t recv_simbol; }ref_t; typedef struct LIBLTE_API{ - int nof_refs; // number of reference signals - int *symbols_ref; // symbols with at least one reference - int nsymbols; // number of symbols with at least one reference - int voffset; // offset of the first reference in the freq domain - int nof_prb; + uint32_t nof_refs; // number of reference signals + uint32_t *symbols_ref; // symbols with at least one reference + uint32_t nsymbols; // number of symbols with at least one reference + uint32_t voffset; // offset of the first reference in the freq domain + uint32_t nof_prb; ref_t *refs; cf_t *ch_est; } refsignal_t; -LIBLTE_API int refsignal_init_LTEDL(refsignal_t *q, int port_id, int nslot, - int cell_id, lte_cp_t cp, int nof_prb); +LIBLTE_API int refsignal_init_LTEDL(refsignal_t *q, + uint32_t port_id, + uint32_t nslot, + lte_cell_t cell); + LIBLTE_API void refsignal_free(refsignal_t *q); -LIBLTE_API void refsignal_put(refsignal_t *q, cf_t *slot_symbols); +LIBLTE_API int refsignal_put(refsignal_t *q, + cf_t *slot_symbols); #endif diff --git a/lte/phy/include/liblte/phy/channel/ch_awgn.h b/lte/phy/include/liblte/phy/channel/ch_awgn.h index 19a452cb1..940ad332d 100644 --- a/lte/phy/include/liblte/phy/channel/ch_awgn.h +++ b/lte/phy/include/liblte/phy/channel/ch_awgn.h @@ -27,6 +27,8 @@ #include +#include + #include "liblte/config.h" #ifndef CH_AWGN_ @@ -34,8 +36,18 @@ typedef _Complex float cf_t; -LIBLTE_API void ch_awgn_c(const cf_t* input, cf_t* output, float variance, int buff_sz); -LIBLTE_API void ch_awgn_f(const float* x, float* y, float variance, int buff_sz); +LIBLTE_API void ch_awgn_c(const cf_t* input, + cf_t* output, + float variance, + uint32_t len); + +LIBLTE_API void ch_awgn_f(const float* x, + float* y, + float variance, + uint32_t len); + +LIBLTE_API float ch_awgn_get_variance(float ebno_db, + float rate); /* High-level API */ diff --git a/lte/phy/include/liblte/phy/common/fft.h b/lte/phy/include/liblte/phy/common/fft.h index 0b0f1dd0b..3e20c475f 100644 --- a/lte/phy/include/liblte/phy/common/fft.h +++ b/lte/phy/include/liblte/phy/common/fft.h @@ -42,20 +42,41 @@ typedef _Complex float cf_t; /* this is only a shortcut */ /* This is common for both directions */ typedef struct LIBLTE_API{ dft_plan_t fft_plan; - int nof_symbols; - int symbol_sz; - int nof_guards; - int nof_re; - lte_cp_t cp_type; + uint32_t nof_symbols; + uint32_t symbol_sz; + uint32_t nof_guards; + uint32_t nof_re; + uint32_t slot_sz; + lte_cp_t cp; cf_t *tmp; // for removing zero padding }lte_fft_t; -LIBLTE_API int lte_fft_init(lte_fft_t *q, lte_cp_t cp_type, int nof_prb); +LIBLTE_API int lte_fft_init(lte_fft_t *q, + lte_cp_t cp_type, + uint32_t nof_prb); + LIBLTE_API void lte_fft_free(lte_fft_t *q); -LIBLTE_API void lte_fft_run(lte_fft_t *q, cf_t *input, cf_t *output); -LIBLTE_API int lte_ifft_init(lte_fft_t *q, lte_cp_t cp_type, int nof_prb); +LIBLTE_API void lte_fft_run_slot(lte_fft_t *q, + cf_t *input, + cf_t *output); + +LIBLTE_API void lte_fft_run_sf(lte_fft_t *q, + cf_t *input, + cf_t *output); + +LIBLTE_API int lte_ifft_init(lte_fft_t *q, + lte_cp_t cp_type, + uint32_t nof_prb); + LIBLTE_API void lte_ifft_free(lte_fft_t *q); -LIBLTE_API void lte_ifft_run(lte_fft_t *q, cf_t *input, cf_t *output); + +LIBLTE_API void lte_ifft_run_slot(lte_fft_t *q, + cf_t *input, + cf_t *output); + +LIBLTE_API void lte_ifft_run_sf(lte_fft_t *q, + cf_t *input, + cf_t *output); #endif diff --git a/lte/phy/include/liblte/phy/common/phy_common.h b/lte/phy/include/liblte/phy/common/phy_common.h index 3449449c8..4f7efc742 100644 --- a/lte/phy/include/liblte/phy/common/phy_common.h +++ b/lte/phy/include/liblte/phy/common/phy_common.h @@ -29,82 +29,90 @@ #ifndef _LTEBASE_ #define _LTEBASE_ +#include +#include #include "liblte/config.h" #define NSUBFRAMES_X_FRAME 10 -#define NSLOTS_X_FRAME (2*NSUBFRAMES_X_FRAME) +#define NSLOTS_X_FRAME (2*NSUBFRAMES_X_FRAME) #define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE -#define LTE_NULL_BIT 0 +#define LTE_NULL_BIT 0 #define LTE_NULL_SYMBOL 2 #define LTE_NIL_SYMBOL 2 -#define MAX_PORTS 4 -#define MAX_PORTS_CTRL 4 +#define MAX_PORTS 4 #define MAX_LAYERS 8 #define MAX_CODEWORDS 2 #define LTE_CRC24A 0x1864CFB #define LTE_CRC24B 0X1800063 -#define LTE_CRC16 0x11021 -#define LTE_CRC8 0x19B +#define LTE_CRC16 0x11021 +#define LTE_CRC8 0x19B typedef enum {CPNORM, CPEXT} lte_cp_t; -#define SIRNTI 0xFFFF +#define SIRNTI 0xFFFF #define PRNTI 0xFFFE #define MRNTI 0xFFFD -#define MAX_NSYMB 7 +#define MAX_NSYMB 7 -#define CPNORM_NSYMB 7 -#define CPNORM_SF_NSYMB 2*CPNORM_NSYMB -#define CPNORM_0_LEN 160 -#define CPNORM_LEN 144 +#define MAX_PRB 110 +#define RE_X_RB 12 + +#define SYMBOL_SZ_MAX 2048 + +#define CPNORM_NSYMB 7 +#define CPNORM_SF_NSYMB (2*CPNORM_NSYMB) +#define CPNORM_0_LEN 160 +#define CPNORM_LEN 144 -#define CPEXT_NSYMB 6 -#define CPEXT_SF_NSYMB 2*CPEXT_NSYMB -#define CPEXT_LEN 512 -#define CPEXT_7_5_LEN 1024 +#define CPEXT_NSYMB 6 +#define CPEXT_SF_NSYMB (2*CPEXT_NSYMB) +#define CPEXT_LEN 512 +#define CPEXT_7_5_LEN 1024 #define CP_ISNORM(cp) (cp==CPNORM) #define CP_ISEXT(cp) (cp==CPEXT) #define CP_NSYMB(cp) (CP_ISNORM(cp)?CPNORM_NSYMB:CPEXT_NSYMB) -#define CP(symbol_sz, c) (c*symbol_sz/2048) -#define CP_NORM(symbol, symbol_sz) (symbol==0)?CP(symbol_sz,CPNORM_0_LEN):CP(symbol_sz,CPNORM_LEN) -#define CP_EXT(symbol_sz) CP(symbol_sz,CPEXT_LEN) +#define CP(symbol_sz, c) ((c*symbol_sz)/2048) +#define CP_NORM(symbol, symbol_sz) ((symbol==0)?CP((symbol_sz),CPNORM_0_LEN):CP((symbol_sz),CPNORM_LEN)) +#define CP_EXT(symbol_sz) (CP((symbol_sz),CPEXT_LEN)) -#define SLOT_LEN_CPNORM(symbol_sz) (symbol_sz+CP(symbol_sz,CPNORM_0_LEN)+(CPNORM_NSYMB-1)*(symbol_sz+CP(symbol_sz,CPNORM_LEN))) -#define SLOT_LEN_CPEXT(symbol_sz) (CPEXT_NSYMB*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) -#define SLOT_LEN(symbol_sz, cp) CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz) +#define SLOT_LEN(symbol_sz) (480*((symbol_sz)/64)) +#define SF_LEN(symbol_sz) (2*SLOT_LEN(symbol_sz)) +#define SF_LEN_MAX (SF_LEN(SYMBOL_SZ_MAX)) -#define SF_LEN_CPNORM(symbol_sz) 2*SLOT_LEN_CPNORM(symbol_sz) -#define SF_LEN_CPEXT(symbol_sz) 2*SLOT_LEN_CPEXT(symbol_sz) -#define SF_LEN(symbol_sz, cp) 2*SLOT_LEN(cp, symbol_sz) +#define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp)) +#define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp)) #define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) #define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) #define SAMPLE_IDX(nof_prb, symbol_idx, sample_idx) (symbol_idx*nof_prb*RE_X_RB + sample_idx) -#define MAX_PRB 110 -#define RE_X_RB 12 - #define RS_VSHIFT(cell_id) (cell_id%6) #define GUARD_RE(nof_prb) ((lte_symbol_sz(nof_prb)-nof_prb*RE_X_RB)/2) +#define SYMBOL_HAS_REF(l, cp, nof_ports) ((l == 1 && nof_ports == 4) \ + || l == 0 \ + || l == CP_NSYMB(cp) - 3) -LIBLTE_API const int lte_symbol_sz(int nof_prb); -LIBLTE_API int lte_re_x_prb(int ns, int symbol, int nof_ports, int nof_symbols); -LIBLTE_API int lte_voffset(int symbol_id, int cell_id, int nof_ports); #define NOF_LTE_BANDS 29 #define NOF_TC_CB_SIZES 188 +typedef struct LIBLTE_API { + uint32_t nof_prb; + uint32_t nof_ports; + uint32_t id; + lte_cp_t cp; +}lte_cell_t; typedef enum LIBLTE_API { SINGLE_ANTENNA,TX_DIVERSITY, SPATIAL_MULTIPLEX @@ -113,6 +121,10 @@ typedef enum LIBLTE_API { typedef enum LIBLTE_API { PHICH_NORM, PHICH_EXT} phich_length_t; typedef enum LIBLTE_API { R_1_6, R_1_2, R_1, R_2} phich_resources_t; +typedef enum LIBLTE_API { + LTE_BPSK = 1, LTE_QPSK = 2, LTE_QAM16 = 4, LTE_QAM64 = 6 +} lte_mod_t; + typedef struct LIBLTE_API { int id; @@ -123,16 +135,59 @@ LIBLTE_API enum band_geographical_area { ALL, NAR, APAC, EMEA, JAPAN, CALA, NA }; -LIBLTE_API int lte_cb_size(int index); -LIBLTE_API int lte_find_cb_index(int long_cb); +LIBLTE_API bool lte_cell_isvalid(lte_cell_t *cell); + +LIBLTE_API bool lte_N_id_2_isvalid(uint32_t N_id_2); + +LIBLTE_API bool lte_N_id_1_isvalid(uint32_t N_id_1); + +LIBLTE_API bool lte_symbol_sz_isvalid(uint32_t symbol_sz); + +LIBLTE_API int lte_symbol_sz(uint32_t nof_prb); -LIBLTE_API float lte_band_fd(int earfcn); -LIBLTE_API int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int earfcn_start, int earfcn_end, int max_elems); -LIBLTE_API int lte_band_get_fd_band_all(int band, lte_earfcn_t *earfcn, int max_nelems); -LIBLTE_API int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, int max_elems); -LIBLTE_API int lte_str2mimotype(char *mimo_type_str, lte_mimo_type_t *type); +LIBLTE_API int lte_sampling_freq_hz(uint32_t nof_prb); + +LIBLTE_API uint32_t lte_re_x_prb(uint32_t ns, + uint32_t symbol, + uint32_t nof_ports, + uint32_t nof_symbols); + +LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id, + uint32_t cell_id, + uint32_t nof_ports); + +LIBLTE_API int lte_cb_size(uint32_t index); + +LIBLTE_API char *lte_cp_string(lte_cp_t cp); + +LIBLTE_API char *lte_mod_string(lte_mod_t mod); + +LIBLTE_API uint32_t lte_mod_bits_x_symbol(lte_mod_t mod); + +LIBLTE_API int lte_find_cb_index(uint32_t long_cb); + +LIBLTE_API float lte_band_fd(uint32_t earfcn); + +LIBLTE_API int lte_band_get_fd_band(uint32_t band, + lte_earfcn_t *earfcn, + int earfcn_start, + int earfcn_end, + uint32_t max_elems); + +LIBLTE_API int lte_band_get_fd_band_all(uint32_t band, + lte_earfcn_t *earfcn, + uint32_t max_nelems); + +LIBLTE_API int lte_band_get_fd_region(enum band_geographical_area region, + lte_earfcn_t *earfcn, + uint32_t max_elems); + +LIBLTE_API int lte_str2mimotype(char *mimo_type_str, + lte_mimo_type_t *type); + LIBLTE_API char *lte_mimotype2str(lte_mimo_type_t type); + #endif diff --git a/lte/phy/include/liblte/phy/common/sequence.h b/lte/phy/include/liblte/phy/common/sequence.h index 0fb52df2f..972a31ebf 100644 --- a/lte/phy/include/liblte/phy/common/sequence.h +++ b/lte/phy/include/liblte/phy/common/sequence.h @@ -33,19 +33,39 @@ typedef struct LIBLTE_API { char *c; - int len; + uint32_t len; } sequence_t; -LIBLTE_API int sequence_init(sequence_t *q, int len); +LIBLTE_API int sequence_init(sequence_t *q, uint32_t len); + LIBLTE_API void sequence_free(sequence_t *q); -LIBLTE_API int sequence_LTEPRS(sequence_t *q, int len, int seed); +LIBLTE_API int sequence_LTEPRS(sequence_t *q, + uint32_t len, + uint32_t seed); + +LIBLTE_API int sequence_pbch(sequence_t *seq, + lte_cp_t cp, + uint32_t cell_id); + +LIBLTE_API int sequence_pcfich(sequence_t *seq, + uint32_t nslot, + uint32_t cell_id); + +LIBLTE_API int sequence_phich(sequence_t *seq, + uint32_t nslot, + uint32_t cell_id); + +LIBLTE_API int sequence_pdcch(sequence_t *seq, + uint32_t nslot, + uint32_t cell_id, + uint32_t len); -LIBLTE_API int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id); -LIBLTE_API int sequence_pcfich(sequence_t *seq, int nslot, int cell_id); -LIBLTE_API int sequence_phich(sequence_t *seq, int nslot, int cell_id); -LIBLTE_API int sequence_pdcch(sequence_t *seq, int nslot, int cell_id, int len); -LIBLTE_API int sequence_pdsch(sequence_t *seq, unsigned short rnti, int q, - int nslot, int cell_id, int len); +LIBLTE_API int sequence_pdsch(sequence_t *seq, + unsigned short rnti, + int q, + uint32_t nslot, + uint32_t cell_id, + uint32_t len); #endif diff --git a/lte/phy/include/liblte/phy/fec/convcoder.h b/lte/phy/include/liblte/phy/fec/convcoder.h index 79416289f..38dd5857d 100644 --- a/lte/phy/include/liblte/phy/fec/convcoder.h +++ b/lte/phy/include/liblte/phy/fec/convcoder.h @@ -34,13 +34,13 @@ #include "liblte/config.h" typedef struct LIBLTE_API { - int R; - int K; - int poly[3]; + uint32_t R; + uint32_t K; + uint32_t poly[3]; bool tail_biting; }convcoder_t; -LIBLTE_API int convcoder_encode(convcoder_t *q, char *input, char *output, int frame_length); +LIBLTE_API int convcoder_encode(convcoder_t *q, char *input, char *output, uint32_t frame_length); /* High-level API */ diff --git a/lte/phy/include/liblte/phy/fec/crc.h b/lte/phy/include/liblte/phy/fec/crc.h index e071f3398..e9d148a52 100644 --- a/lte/phy/include/liblte/phy/fec/crc.h +++ b/lte/phy/include/liblte/phy/fec/crc.h @@ -30,6 +30,7 @@ #define CRC_ #include "liblte/config.h" +#include typedef struct LIBLTE_API { unsigned long table[256]; @@ -45,6 +46,6 @@ typedef struct LIBLTE_API { LIBLTE_API int crc_init(crc_t *h, unsigned int crc_poly, int crc_order); LIBLTE_API int crc_set_init(crc_t *h, unsigned long crc_init_value); LIBLTE_API void crc_attach(crc_t *h, char *data, int len); -LIBLTE_API unsigned int crc_checksum(crc_t *h, char *data, int len); +LIBLTE_API uint32_t crc_checksum(crc_t *h, char *data, int len); #endif diff --git a/lte/phy/include/liblte/phy/fec/rm_conv.h b/lte/phy/include/liblte/phy/fec/rm_conv.h index e161fe47d..6a37177b0 100644 --- a/lte/phy/include/liblte/phy/fec/rm_conv.h +++ b/lte/phy/include/liblte/phy/fec/rm_conv.h @@ -33,8 +33,15 @@ #define RX_NULL 10000 #define TX_NULL 80 -LIBLTE_API int rm_conv_tx(char *input, int in_len, char *output, int out_len); -LIBLTE_API int rm_conv_rx(float *input, int in_len, float *output, int out_len); +LIBLTE_API int rm_conv_tx(char *input, + uint32_t in_len, + char *output, + uint32_t out_len); + +LIBLTE_API int rm_conv_rx(float *input, + uint32_t in_len, + float *output, + uint32_t out_len); /* High-level API */ typedef struct diff --git a/lte/phy/include/liblte/phy/fec/rm_turbo.h b/lte/phy/include/liblte/phy/fec/rm_turbo.h index 7f1c45db3..0b5fad70e 100644 --- a/lte/phy/include/liblte/phy/fec/rm_turbo.h +++ b/lte/phy/include/liblte/phy/fec/rm_turbo.h @@ -40,25 +40,30 @@ #include "liblte/config.h" -typedef struct LIBLTE_API { - int buffer_len; - char *buffer; -} rm_turbo_t; -LIBLTE_API int rm_turbo_init(rm_turbo_t *q, int max_codeblock_len); -LIBLTE_API void rm_turbo_free(rm_turbo_t *q); -LIBLTE_API int rm_turbo_tx(rm_turbo_t *q, char *input, int in_len, char *output, - int out_len, int rv_idx); -LIBLTE_API int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, - float *output, int out_len, int rv_idx); +LIBLTE_API int rm_turbo_tx(char *w_buff, + uint32_t buff_len, + char *input, + uint32_t in_len, + char *output, + uint32_t out_len, + uint32_t rv_idx); + +LIBLTE_API int rm_turbo_rx(float *w_buff, + uint32_t buff_len, + float *input, + uint32_t in_len, + float *output, + uint32_t out_len, + uint32_t rv_idx); /* High-level API */ typedef struct LIBLTE_API { - rm_turbo_t q; + struct rm_turbo_init { int direction; } init; - void *input; // input type may be char or float depending on hard + void *input; // input type may be char or float depending on hard int in_len; struct rm_turbo_ctrl_in { int E; diff --git a/lte/phy/include/liblte/phy/fec/tc_interl.h b/lte/phy/include/liblte/phy/fec/tc_interl.h index 03a9eb824..ec9eb747f 100644 --- a/lte/phy/include/liblte/phy/fec/tc_interl.h +++ b/lte/phy/include/liblte/phy/fec/tc_interl.h @@ -31,15 +31,15 @@ #include "liblte/config.h" typedef struct LIBLTE_API { - int *forward; - int *reverse; - int max_long_cb; + uint32_t *forward; + uint32_t *reverse; + uint32_t max_long_cb; } tc_interl_t; -LIBLTE_API int tc_interl_LTE_gen(tc_interl_t *h, int long_cb); -LIBLTE_API int tc_interl_UMTS_gen(tc_interl_t *h, int long_cb); +LIBLTE_API int tc_interl_LTE_gen(tc_interl_t *h, uint32_t long_cb); +LIBLTE_API int tc_interl_UMTS_gen(tc_interl_t *h, uint32_t long_cb); -LIBLTE_API int tc_interl_init(tc_interl_t *h, int max_long_cb); +LIBLTE_API int tc_interl_init(tc_interl_t *h, uint32_t max_long_cb); LIBLTE_API void tc_interl_free(tc_interl_t *h); #endif diff --git a/lte/phy/include/liblte/phy/fec/turbocoder.h b/lte/phy/include/liblte/phy/fec/turbocoder.h index f150446bf..af272e223 100644 --- a/lte/phy/include/liblte/phy/fec/turbocoder.h +++ b/lte/phy/include/liblte/phy/fec/turbocoder.h @@ -37,13 +37,13 @@ #define TOTALTAIL 12 typedef struct LIBLTE_API { - int max_long_cb; + uint32_t max_long_cb; tc_interl_t interl; } tcod_t; -LIBLTE_API int tcod_init(tcod_t *h, int max_long_cb); +LIBLTE_API int tcod_init(tcod_t *h, uint32_t max_long_cb); LIBLTE_API void tcod_free(tcod_t *h); -LIBLTE_API int tcod_encode(tcod_t *h, char *input, char *output, int long_cb); +LIBLTE_API int tcod_encode(tcod_t *h, char *input, char *output, uint32_t long_cb); #endif diff --git a/lte/phy/include/liblte/phy/fec/turbodecoder.h b/lte/phy/include/liblte/phy/fec/turbodecoder.h index da2e60b5f..178f51956 100644 --- a/lte/phy/include/liblte/phy/fec/turbodecoder.h +++ b/lte/phy/include/liblte/phy/fec/turbodecoder.h @@ -68,13 +68,25 @@ typedef struct LIBLTE_API { tc_interl_t interleaver; } tdec_t; -LIBLTE_API int tdec_init(tdec_t *h, int max_long_cb); -LIBLTE_API void tdec_free(tdec_t *h); - -LIBLTE_API int tdec_reset(tdec_t *h, int long_cb); -LIBLTE_API void tdec_iteration(tdec_t *h, llr_t *input, int long_cb); -LIBLTE_API void tdec_decision(tdec_t *h, char *output, int long_cb); -LIBLTE_API void tdec_run_all(tdec_t *h, llr_t *input, char *output, int nof_iterations, - int long_cb); +LIBLTE_API int tdec_init(tdec_t * h, + uint32_t max_long_cb); + +LIBLTE_API void tdec_free(tdec_t * h); + +LIBLTE_API int tdec_reset(tdec_t * h, uint32_t long_cb); + +LIBLTE_API void tdec_iteration(tdec_t * h, + llr_t * input, + uint32_t long_cb); + +LIBLTE_API void tdec_decision(tdec_t * h, + char *output, + uint32_t long_cb); + +LIBLTE_API void tdec_run_all(tdec_t * h, + llr_t * input, + char *output, + uint32_t nof_iterations, + uint32_t long_cb); #endif diff --git a/lte/phy/include/liblte/phy/fec/viterbi.h b/lte/phy/include/liblte/phy/fec/viterbi.h index 0d955656d..a94c58e19 100644 --- a/lte/phy/include/liblte/phy/fec/viterbi.h +++ b/lte/phy/include/liblte/phy/fec/viterbi.h @@ -38,21 +38,34 @@ typedef enum { typedef struct LIBLTE_API{ void *ptr; - int R; - int K; + uint32_t R; + uint32_t K; unsigned int framebits; bool tail_biting; - int poly[3]; - int (*decode) (void*, unsigned char*, char*, int); + uint32_t poly[3]; + int (*decode) (void*, uint8_t*, char*, uint32_t); void (*free) (void*); unsigned char *tmp; unsigned char *symbols_uc; }viterbi_t; -LIBLTE_API int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], int max_frame_length, bool tail_bitting); +LIBLTE_API int viterbi_init(viterbi_t *q, + viterbi_type_t type, + uint32_t poly[3], + uint32_t max_frame_length, + bool tail_bitting); + LIBLTE_API void viterbi_free(viterbi_t *q); -LIBLTE_API int viterbi_decode_f(viterbi_t *q, float *symbols, char *data, int frame_length); -LIBLTE_API int viterbi_decode_uc(viterbi_t *q, unsigned char *symbols, char *data, int frame_length); + +LIBLTE_API int viterbi_decode_f(viterbi_t *q, + float *symbols, + char *data, + uint32_t frame_length); + +LIBLTE_API int viterbi_decode_uc(viterbi_t *q, + uint8_t *symbols, + char *data, + uint32_t frame_length); /* High-level API */ diff --git a/lte/phy/include/liblte/phy/modem/demod_hard.h b/lte/phy/include/liblte/phy/modem/demod_hard.h index a3b621819..51f5b6c53 100644 --- a/lte/phy/include/liblte/phy/modem/demod_hard.h +++ b/lte/phy/include/liblte/phy/modem/demod_hard.h @@ -38,13 +38,13 @@ typedef _Complex float cf_t; typedef struct LIBLTE_API { - enum modem_std table; /* In this implementation, mapping table is hard-coded */ + lte_mod_t mod; /* In this implementation, mapping table is hard-coded */ }demod_hard_t; LIBLTE_API void demod_hard_init(demod_hard_t* q); -LIBLTE_API void demod_hard_table_set(demod_hard_t* q, enum modem_std table); -LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols); +LIBLTE_API void demod_hard_table_set(demod_hard_t* q, lte_mod_t mod); +LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, uint32_t nsymbols); @@ -52,7 +52,7 @@ LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, typedef struct LIBLTE_API { demod_hard_t obj; struct demod_hard_init { - enum modem_std std; // Symbol mapping standard (see modem_table.h) + lte_mod_t std; // Symbol mapping standard (see modem_table.h) } init; cf_t* input; diff --git a/lte/phy/include/liblte/phy/modem/demod_soft.h b/lte/phy/include/liblte/phy/modem/demod_soft.h index bb2c4368b..2a2e40b55 100644 --- a/lte/phy/include/liblte/phy/modem/demod_soft.h +++ b/lte/phy/include/liblte/phy/modem/demod_soft.h @@ -56,7 +56,7 @@ typedef struct LIBLTE_API { modem_table_t table; struct demod_soft_init{ - enum modem_std std; // symbol mapping standard (see modem_table.h) + lte_mod_t std; // symbol mapping standard (see modem_table.h) } init; const cf_t* input; diff --git a/lte/phy/include/liblte/phy/modem/mod.h b/lte/phy/include/liblte/phy/modem/mod.h index 5467f13d0..8ee5defc8 100644 --- a/lte/phy/include/liblte/phy/modem/mod.h +++ b/lte/phy/include/liblte/phy/modem/mod.h @@ -37,13 +37,13 @@ typedef _Complex float cf_t; -LIBLTE_API int mod_modulate(modem_table_t* table, const char *bits, cf_t* symbols, int nbits); +LIBLTE_API int mod_modulate(modem_table_t* table, const char *bits, cf_t* symbols, uint32_t nbits); /* High-level API */ typedef struct LIBLTE_API { modem_table_t obj; struct mod_init { - enum modem_std std; // symbol mapping standard (see modem_table.h) + lte_mod_t std; // symbol mapping standard (see modem_table.h) } init; const char* input; diff --git a/lte/phy/include/liblte/phy/modem/modem_table.h b/lte/phy/include/liblte/phy/modem/modem_table.h index f94c03f86..794cd0bc4 100644 --- a/lte/phy/include/liblte/phy/modem/modem_table.h +++ b/lte/phy/include/liblte/phy/modem/modem_table.h @@ -34,30 +34,39 @@ #include #include +#include "liblte/phy/common/phy_common.h" #include "liblte/config.h" typedef _Complex float cf_t; typedef struct LIBLTE_API { - int idx[2][6][32]; + uint32_t idx[2][6][32]; + uint32_t min_idx[2][64][6]; /* NEW: for each constellation point zone (2, 4, 16, 64 for BPSK, QPSK, 16QAM, 64QAM) the 2x(1, 2, 4, and 6 closest constellation points) for each bit, respectively. */ + uint32_t d_idx[64][7]; /* NEW: for each constellation point zone (2, 4, 16, 64 for BPSK, QPSK, 16QAM, 64QAM) the 2, 3, 5 and 7 indices to constellation points that need to be computed for any recevied symbol modulated as BPSK, QPSK, 16QAM, and 64QAM, respectively. */ + }soft_table_t; typedef struct LIBLTE_API { - cf_t* symbol_table; // bit-to-symbol mapping - soft_table_t soft_table; // symbol-to-bit mapping (used in soft demodulating) - int nsymbols; // number of modulation symbols - int nbits_x_symbol; // number of bits per symbol + cf_t* symbol_table; // bit-to-symbol mapping + soft_table_t soft_table; // symbol-to-bit mapping (used in soft demodulating) + uint32_t nsymbols; // number of modulation symbols + uint32_t nbits_x_symbol; // number of bits per symbol }modem_table_t; -// Modulation standards -enum modem_std { - LTE_BPSK = 1, LTE_QPSK = 2, LTE_QAM16 = 4, LTE_QAM64 = 6 -}; - LIBLTE_API void modem_table_init(modem_table_t* q); + LIBLTE_API void modem_table_free(modem_table_t* q); + LIBLTE_API void modem_table_reset(modem_table_t* q); -LIBLTE_API int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol); -LIBLTE_API int modem_table_std(modem_table_t* q, enum modem_std table, bool compute_soft_demod); + +LIBLTE_API int modem_table_set(modem_table_t* q, + cf_t* table, + soft_table_t *soft_table, + uint32_t nsymbols, + uint32_t nbits_x_symbol); + +LIBLTE_API int modem_table_lte(modem_table_t* q, + lte_mod_t modulation, + bool compute_soft_demod); #endif // MODEM_TABLE_ diff --git a/lte/phy/include/liblte/phy/phch/dci.h b/lte/phy/include/liblte/phy/phch/dci.h index eb6b8afd3..be57914c0 100644 --- a/lte/phy/include/liblte/phy/phch/dci.h +++ b/lte/phy/include/liblte/phy/phch/dci.h @@ -62,41 +62,72 @@ typedef enum { } dci_spec_t; typedef struct LIBLTE_API { - unsigned char nof_bits; - unsigned char L; // Aggregation level - unsigned char ncce; // Position of first CCE of the dci - unsigned short rnti; -} dci_candidate_t; + uint32_t L; // Aggregation level + uint32_t ncce; // Position of first CCE of the dci +} dci_location_t; typedef struct LIBLTE_API { char data[DCI_MAX_BITS]; - dci_candidate_t location; + uint32_t nof_bits; } dci_msg_t; -typedef struct LIBLTE_API { - dci_msg_t *msg; - int nof_dcis; - int max_dcis; -} dci_t; - -LIBLTE_API int dci_init(dci_t *q, int max_dci); -LIBLTE_API void dci_free(dci_t *q); +/* Converts a received PDSCH DL scheduling DCI message + * to ra structures ready to be passed to the harq setup function + */ +LIBLTE_API int dci_msg_to_ra_dl(dci_msg_t *msg, + uint16_t msg_rnti, + uint16_t c_rnti, + lte_cell_t cell, + uint32_t cfi, + ra_pdsch_t *ra_dl); + +/* TODO +LIBLTE_API int dci_msg_to_ra_ul(dci_msg_t *msg, + uint16_t msg_rnti, + uint16_t c_rnti, + lte_cell_t cell, + uint32_t cfi, + ra_pusch_t *ra_ul); +*/ LIBLTE_API char* dci_format_string(dci_format_t format); -LIBLTE_API int dci_msg_candidate_set(dci_msg_t *msg, int L, int nCCE, unsigned short rnti); -LIBLTE_API void dci_candidate_fprint(FILE *f, dci_candidate_t *q); +LIBLTE_API int dci_location_set(dci_location_t *c, + uint32_t L, + uint32_t nCCE); + +LIBLTE_API bool dci_location_isvalid(dci_location_t *c); -LIBLTE_API int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, int nof_prb, unsigned short crnti); -LIBLTE_API void dci_msg_type_fprint(FILE *f, dci_msg_type_t type); +LIBLTE_API int dci_msg_get_type(dci_msg_t *msg, + dci_msg_type_t *type, + uint32_t nof_prb, + uint16_t msg_rnti, + uint16_t crnti); + +LIBLTE_API void dci_msg_type_fprint(FILE *f, + dci_msg_type_t type); // For dci_msg_type_t = PUSCH_SCHED -LIBLTE_API int dci_msg_pack_pusch(ra_pusch_t *data, dci_msg_t *msg, int nof_prb); -LIBLTE_API int dci_msg_unpack_pusch(dci_msg_t *msg, ra_pusch_t *data, int nof_prb); +LIBLTE_API int dci_msg_pack_pusch(ra_pusch_t *data, + dci_msg_t *msg, + uint32_t nof_prb); -// For dci_msg_type_t = PDSCH_SCHED -LIBLTE_API int dci_msg_pack_pdsch(ra_pdsch_t *data, dci_msg_t *msg, dci_format_t format, int nof_prb, bool crc_is_crnti); -LIBLTE_API int dci_msg_unpack_pdsch(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, bool crc_is_crnti); +LIBLTE_API int dci_msg_unpack_pusch(dci_msg_t *msg, + ra_pusch_t *data, + uint32_t nof_prb); -LIBLTE_API int dci_format_sizeof(dci_format_t format, int nof_prb); +// For dci_msg_type_t = PDSCH_SCHED +LIBLTE_API int dci_msg_pack_pdsch(ra_pdsch_t *data, + dci_msg_t *msg, + dci_format_t format, + uint32_t nof_prb, + bool crc_is_crnti); + +LIBLTE_API int dci_msg_unpack_pdsch(dci_msg_t *msg, + ra_pdsch_t *data, + uint32_t nof_prb, + bool crc_is_crnti); + +LIBLTE_API uint32_t dci_format_sizeof(dci_format_t format, + uint32_t nof_prb); #endif // DCI_ diff --git a/lte/phy/include/liblte/phy/phch/pbch.h b/lte/phy/include/liblte/phy/phch/pbch.h index 65c11f9e0..e8a79b513 100644 --- a/lte/phy/include/liblte/phy/phch/pbch.h +++ b/lte/phy/include/liblte/phy/phch/pbch.h @@ -47,24 +47,23 @@ typedef _Complex float cf_t; typedef struct LIBLTE_API { - int nof_ports; - int nof_prb; - int sfn; + uint32_t nof_ports; + uint32_t nof_prb; + uint32_t sfn; phich_length_t phich_length; phich_resources_t phich_resources; }pbch_mib_t; /* PBCH object */ typedef struct LIBLTE_API { - int cell_id; - lte_cp_t cp; - int nof_prb; - int nof_symbols; + lte_cell_t cell; + + uint32_t nof_symbols; /* buffers */ - cf_t *ce[MAX_PORTS_CTRL]; - cf_t *pbch_symbols[MAX_PORTS_CTRL]; - cf_t *pbch_x[MAX_PORTS_CTRL]; + cf_t *ce[MAX_PORTS]; + cf_t *pbch_symbols[MAX_PORTS]; + cf_t *pbch_x[MAX_PORTS]; cf_t *pbch_d; float *pbch_llr; float *temp; @@ -73,7 +72,7 @@ typedef struct LIBLTE_API { char *data; char *data_enc; - int frame_idx; + uint32_t frame_idx; /* tx & rx objects */ modem_table_t mod; @@ -83,17 +82,25 @@ typedef struct LIBLTE_API { crc_t crc; convcoder_t encoder; -}pbch_t; +} pbch_t; + +LIBLTE_API int pbch_init(pbch_t *q, + lte_cell_t cell); -LIBLTE_API int pbch_init(pbch_t *q, int nof_prb, int cell_id, lte_cp_t cp); LIBLTE_API void pbch_free(pbch_t *q); -LIBLTE_API int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], float ebno, pbch_mib_t *mib); -LIBLTE_API void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL], int nof_ports); +LIBLTE_API int pbch_decode(pbch_t *q, + cf_t *slot1_symbols, + cf_t *ce_slot1[MAX_PORTS], + pbch_mib_t *mib); + +LIBLTE_API int pbch_encode(pbch_t *q, + pbch_mib_t *mib, + cf_t *slot1_symbols[MAX_PORTS]); + LIBLTE_API void pbch_decode_reset(pbch_t *q); -LIBLTE_API void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib); -LIBLTE_API bool pbch_exists(int nframe, int nslot); -LIBLTE_API int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); -LIBLTE_API int pbch_get(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); +LIBLTE_API void pbch_mib_fprint(FILE *stream, + pbch_mib_t *mib, + uint32_t cell_id); #endif // PBCH_ diff --git a/lte/phy/include/liblte/phy/phch/pcfich.h b/lte/phy/include/liblte/phy/phch/pcfich.h index e092ce4e5..80ad465f9 100644 --- a/lte/phy/include/liblte/phy/phch/pcfich.h +++ b/lte/phy/include/liblte/phy/phch/pcfich.h @@ -45,19 +45,16 @@ typedef _Complex float cf_t; /* PCFICH object */ typedef struct LIBLTE_API { - int cell_id; - lte_cp_t cp; + lte_cell_t cell; int nof_symbols; - int nof_prb; - int nof_ports; /* handler to REGs resource mapper */ regs_t *regs; /* buffers */ - cf_t ce[MAX_PORTS_CTRL][PCFICH_RE]; - cf_t pcfich_symbols[MAX_PORTS_CTRL][PCFICH_RE]; - cf_t pcfich_x[MAX_PORTS_CTRL][PCFICH_RE]; + cf_t ce[MAX_PORTS][PCFICH_RE]; + cf_t pcfich_symbols[MAX_PORTS][PCFICH_RE]; + cf_t pcfich_x[MAX_PORTS][PCFICH_RE]; cf_t pcfich_d[PCFICH_RE]; /* bit message */ @@ -70,16 +67,22 @@ typedef struct LIBLTE_API { } pcfich_t; -LIBLTE_API int pcfich_init(pcfich_t *q, regs_t *regs, int cell_id, int nof_prb, - int nof_tx_ports, lte_cp_t cp); +LIBLTE_API int pcfich_init(pcfich_t *q, + regs_t *regs, + lte_cell_t cell); + LIBLTE_API void pcfich_free(pcfich_t *q); -LIBLTE_API int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - int nsubframe, int *cfi, int *distance); -LIBLTE_API int pcfich_encode(pcfich_t *q, int cfi, cf_t *slot_symbols[MAX_PORTS_CTRL], - int nsubframe); -LIBLTE_API bool pcfich_exists(int nframe, int nslot); -LIBLTE_API int pcfich_put(regs_t *h, cf_t *pcfich, cf_t *slot_data); -LIBLTE_API int pcfich_get(regs_t *h, cf_t *pcfich, cf_t *slot_data); +LIBLTE_API int pcfich_decode(pcfich_t *q, + cf_t *sf_symbols, + cf_t *ce[MAX_PORTS], + uint32_t subframe, + uint32_t *cfi, + uint32_t *distance); + +LIBLTE_API int pcfich_encode(pcfich_t *q, + uint32_t cfi, + cf_t *sf_symbols[MAX_PORTS], + uint32_t subframe); #endif diff --git a/lte/phy/include/liblte/phy/phch/pdcch.h b/lte/phy/include/liblte/phy/phch/pdcch.h index 7ae5f6918..f15dd1519 100644 --- a/lte/phy/include/liblte/phy/phch/pdcch.h +++ b/lte/phy/include/liblte/phy/phch/pdcch.h @@ -44,41 +44,26 @@ typedef _Complex float cf_t; -#define PDCCH_NOF_SEARCH_MODES 3 typedef enum LIBLTE_API { - SEARCH_NONE = 3, SEARCH_SI = 0, SEARCH_RA = 1, SEARCH_UE = 2 + SEARCH_UE, SEARCH_COMMON } pdcch_search_mode_t; -/* - * A search mode is indicated by higher layers to look for SI/C/RA-RNTI - * DCI messages as defined in Section 7.1 of 36.213 - */ -typedef struct LIBLTE_API { - int nof_candidates; - dci_candidate_t *candidates[NSUBFRAMES_X_FRAME]; -} pdcch_search_t; /* PDCCH object */ typedef struct LIBLTE_API { - int cell_id; - lte_cp_t cp; - int nof_prb; - int nof_bits; - int nof_symbols; - int nof_ports; - int nof_regs; - int nof_cce; - - pdcch_search_t search_mode[PDCCH_NOF_SEARCH_MODES]; - pdcch_search_mode_t current_search_mode; + lte_cell_t cell; + uint32_t e_bits; + uint32_t nof_regs; + uint32_t nof_cce; + uint32_t max_bits; regs_t *regs; /* buffers */ - cf_t *ce[MAX_PORTS_CTRL]; - cf_t *pdcch_symbols[MAX_PORTS_CTRL]; - cf_t *pdcch_x[MAX_PORTS_CTRL]; + cf_t *ce[MAX_PORTS]; + cf_t *pdcch_symbols[MAX_PORTS]; + cf_t *pdcch_x[MAX_PORTS]; cf_t *pdcch_d; char *pdcch_e; float *pdcch_llr; @@ -91,36 +76,48 @@ typedef struct LIBLTE_API { crc_t crc; } pdcch_t; -LIBLTE_API int pdcch_init(pdcch_t *q, regs_t *regs, int nof_prb, int nof_ports, - int cell_id, lte_cp_t cp); -LIBLTE_API void pdcch_free(pdcch_t *q); - -/* Encoding functions */ -LIBLTE_API int pdcch_encode(pdcch_t *q, dci_t *dci, cf_t *slot_symbols[MAX_PORTS_CTRL], - int nsubframe); - -/* Decoding functions */ +LIBLTE_API int pdcch_init(pdcch_t *q, + regs_t *regs, + lte_cell_t cell); -/* There are two ways to decode the DCI messages: - * a) call pdcch_set_search_si/ue/ra and then call pdcch_decode() - * b) call pdcch_extract_llr() and then call pdcch_decode_si/ue/ra - */ - -LIBLTE_API int pdcch_decode(pdcch_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - dci_t *dci, int nsubframe, float ebno); -LIBLTE_API int pdcch_extract_llr(pdcch_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - float *llr, int nsubframe, float ebno); - -LIBLTE_API void pdcch_init_search_si(pdcch_t *q); -LIBLTE_API void pdcch_set_search_si(pdcch_t *q); -LIBLTE_API int pdcch_decode_si(pdcch_t *q, float *llr, dci_t *dci); +LIBLTE_API void pdcch_free(pdcch_t *q); -LIBLTE_API void pdcch_init_search_ue(pdcch_t *q, unsigned short c_rnti); -LIBLTE_API void pdcch_set_search_ue(pdcch_t *q); -LIBLTE_API int pdcch_decode_ue(pdcch_t *q, float *llr, dci_t *dci, int nsubframe); -LIBLTE_API void pdcch_init_search_ra(pdcch_t *q, unsigned short ra_rnti); -LIBLTE_API void pdcch_set_search_ra(pdcch_t *q); -LIBLTE_API int pdcch_decode_ra(pdcch_t *q, float *llr, dci_t *dci); +/* Encoding function */ +LIBLTE_API int pdcch_encode(pdcch_t *q, + dci_msg_t *msg, + dci_location_t location, + uint16_t rnti, + cf_t *sf_symbols[MAX_PORTS], + uint32_t nsubframe, + uint32_t cfi); + +/* Decoding functions: Extract the LLRs and save them in the pdcch_t object */ +LIBLTE_API int pdcch_extract_llr(pdcch_t *q, + cf_t *sf_symbols, + cf_t *ce[MAX_PORTS], + dci_location_t location, + uint32_t nsubframe, + uint32_t cfi); + +/* Decoding functions: Try to decode a DCI message after calling pdcch_extract_llr */ +LIBLTE_API int pdcch_decode_msg(pdcch_t *q, + dci_msg_t *msg, + dci_format_t format, + uint16_t *crc_rem); + +/* Function for generation of UE-specific search space DCI locations */ +LIBLTE_API uint32_t pdcch_ue_locations(pdcch_t *q, + dci_location_t *locations, + uint32_t max_locations, + uint32_t nsubframe, + uint32_t cfi, + uint16_t rnti); + +/* Function for generation of common search space DCI locations */ +LIBLTE_API uint32_t pdcch_common_locations(pdcch_t *q, + dci_location_t *locations, + uint32_t max_locations, + uint32_t cfi); #endif diff --git a/lte/phy/include/liblte/phy/phch/pdsch.h b/lte/phy/include/liblte/phy/phch/pdsch.h index 207a5bf7a..173837b98 100644 --- a/lte/phy/include/liblte/phy/phch/pdsch.h +++ b/lte/phy/include/liblte/phy/phch/pdsch.h @@ -43,49 +43,102 @@ #include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/regs.h" -#define TDEC_ITERATIONS 1 +#define TDEC_MAX_ITERATIONS 6 typedef _Complex float cf_t; +typedef struct LIBLTE_API { + ra_mcs_t mcs; + ra_prb_t prb_alloc; + lte_cell_t cell; + + uint32_t max_cb; + uint32_t w_buff_size; + float **pdsch_w_buff_f; + char **pdsch_w_buff_c; + + struct cb_segm { + uint32_t F; + uint32_t C; + uint32_t K1; + uint32_t K2; + uint32_t C1; + uint32_t C2; + } cb_segm; + +} pdsch_harq_t; + /* PDSCH object */ typedef struct LIBLTE_API { - int cell_id; - lte_cp_t cp; - int nof_prb; - int nof_ports; - int max_symbols; - unsigned short rnti; - - /* buffers */ - cf_t *ce[MAX_PORTS]; - cf_t *pdsch_symbols[MAX_PORTS]; - cf_t *pdsch_x[MAX_PORTS]; - cf_t *pdsch_d; - char *pdsch_e_bits; - char *cb_in_b; - char *cb_out_b; - float *pdsch_llr; - float *pdsch_rm_f; - - /* tx & rx objects */ - modem_table_t mod[4]; - demod_soft_t demod; - sequence_t seq_pdsch[NSUBFRAMES_X_FRAME]; - tcod_t encoder; - tdec_t decoder; - rm_turbo_t rm_turbo; - crc_t crc_tb; - crc_t crc_cb; + lte_cell_t cell; + + uint32_t max_symbols; + bool rnti_is_set; + uint16_t rnti; + uint32_t nof_iterations; + uint64_t average_nof_iterations_n; + float average_nof_iterations; + + /* buffers */ + // void buffers are shared for tx and rx + cf_t *ce[MAX_PORTS]; + cf_t *pdsch_symbols[MAX_PORTS]; + cf_t *pdsch_x[MAX_PORTS]; + cf_t *pdsch_d; + char *cb_in; + void *cb_out; + void *pdsch_e; + + /* tx & rx objects */ + modem_table_t mod[4]; + demod_soft_t demod; + sequence_t seq_pdsch[NSUBFRAMES_X_FRAME]; + tcod_t encoder; + tdec_t decoder; + crc_t crc_tb; + crc_t crc_cb; }pdsch_t; -LIBLTE_API int pdsch_init(pdsch_t *q, unsigned short user_rnti, int nof_prb, - int nof_ports, int cell_id, lte_cp_t cp); +LIBLTE_API int pdsch_init(pdsch_t *q, + lte_cell_t cell); + LIBLTE_API void pdsch_free(pdsch_t *q); -LIBLTE_API int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], - int nsubframe, ra_mcs_t mcs, ra_prb_t *prb_alloc); -LIBLTE_API int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], - char *data, int nsubframe, ra_mcs_t mcs, ra_prb_t *prb_alloc); +LIBLTE_API int pdsch_set_rnti(pdsch_t *q, + uint16_t rnti); + +LIBLTE_API int pdsch_harq_init(pdsch_harq_t *p, + pdsch_t *pdsch); + +LIBLTE_API int pdsch_harq_setup(pdsch_harq_t *p, + ra_mcs_t mcs, + ra_prb_t *prb_alloc); + +LIBLTE_API void pdsch_harq_free(pdsch_harq_t *p); + +LIBLTE_API int pdsch_encode(pdsch_t *q, + char *data, + cf_t *sf_symbols[MAX_PORTS], + uint32_t nsubframe, + pdsch_harq_t *harq_process, + uint32_t rv_idx); + +LIBLTE_API int pdsch_decode(pdsch_t *q, + cf_t *sf_symbols, + cf_t *ce[MAX_PORTS], + char *data, + uint32_t nsubframe, + pdsch_harq_t *harq_process, + uint32_t rv_idx); + +LIBLTE_API float pdsch_average_noi(pdsch_t *q); + +LIBLTE_API uint32_t pdsch_last_noi(pdsch_t *q); +LIBLTE_API int pdsch_get(pdsch_t *q, + cf_t *sf_symbols, + cf_t *pdsch_symbols, + ra_prb_t *prb_alloc, + uint32_t subframe); #endif diff --git a/lte/phy/include/liblte/phy/phch/phich.h b/lte/phy/include/liblte/phy/phch/phich.h index d01b10fc7..dbe68dfea 100644 --- a/lte/phy/include/liblte/phy/phch/phich.h +++ b/lte/phy/include/liblte/phy/phch/phich.h @@ -55,17 +55,15 @@ typedef _Complex float cf_t; /* phich object */ typedef struct LIBLTE_API { - lte_cp_t cp; - int nof_prb; - int nof_tx_ports; - + lte_cell_t cell; + /* handler to REGs resource mapper */ regs_t *regs; /* buffers */ - cf_t ce[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; - cf_t phich_symbols[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; - cf_t phich_x[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; + cf_t ce[MAX_PORTS][PHICH_MAX_NSYMB]; + cf_t phich_symbols[MAX_PORTS][PHICH_MAX_NSYMB]; + cf_t phich_x[MAX_PORTS][PHICH_MAX_NSYMB]; cf_t phich_d[PHICH_MAX_NSYMB]; cf_t phich_d0[PHICH_MAX_NSYMB]; cf_t phich_z[PHICH_NBITS]; @@ -80,18 +78,31 @@ typedef struct LIBLTE_API { }phich_t; -LIBLTE_API int phich_init(phich_t *q, regs_t *regs, int cell_id, int nof_prb, int nof_tx_ports, lte_cp_t cp); -LIBLTE_API void phich_free(phich_t *q); -LIBLTE_API int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - int ngroup, int nseq, int nsubframe, char *ack, int *distance); -LIBLTE_API int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, - cf_t *slot_symbols[MAX_PORTS_CTRL]); +LIBLTE_API int phich_init(phich_t *q, + regs_t *regs, + lte_cell_t cell); +LIBLTE_API void phich_free(phich_t *q); -LIBLTE_API void phich_reset(phich_t *q, cf_t *slot_symbols[MAX_PORTS_CTRL]); -LIBLTE_API int phich_ngroups(phich_t *q); -LIBLTE_API bool phich_exists(int nframe, int nslot); -LIBLTE_API int phich_put(regs_t *h, cf_t *phich, cf_t *slot_data); -LIBLTE_API int phich_get(regs_t *h, cf_t *phich, cf_t *slot_data); +LIBLTE_API int phich_decode(phich_t *q, + cf_t *slot_symbols, + cf_t *ce[MAX_PORTS], + uint32_t ngroup, + uint32_t nseq, + uint32_t nsubframe, + char *ack, + uint32_t *distance); + +LIBLTE_API int phich_encode(phich_t *q, + char ack, + uint32_t ngroup, + uint32_t nseq, + uint32_t nsubframe, + cf_t *slot_symbols[MAX_PORTS]); + +LIBLTE_API void phich_reset(phich_t *q, + cf_t *slot_symbols[MAX_PORTS]); + +LIBLTE_API uint32_t phich_ngroups(phich_t *q); #endif // PHICH_ diff --git a/lte/phy/include/liblte/phy/phch/prach.h b/lte/phy/include/liblte/phy/phch/prach.h index b50e78b0d..4bdd08778 100644 --- a/lte/phy/include/liblte/phy/phch/prach.h +++ b/lte/phy/include/liblte/phy/phch/prach.h @@ -46,7 +46,7 @@ typedef _Complex float cf_t; * Based on 3GPP TS 36.211 version 10.7.0 Release 10. */ -typedef struct LIBLTE_API{ +typedef struct LIBLTE_API { // Parameters from higher layers (extracted from SIB2) uint32_t f; // preamble format uint32_t rsi; // rootSequenceIndex diff --git a/lte/phy/include/liblte/phy/phch/ra.h b/lte/phy/include/liblte/phy/phch/ra.h index 4e94f277a..0d519c031 100644 --- a/lte/phy/include/liblte/phy/phch/ra.h +++ b/lte/phy/include/liblte/phy/phch/ra.h @@ -37,17 +37,9 @@ * allocation. */ -typedef enum LIBLTE_API { - MOD_NULL = 0, BPSK = 1, QPSK = 2, QAM16 = 3, QAM64 = 4 -} ra_mod_t; - typedef struct LIBLTE_API { - ra_mod_t mod; // By default, mod = MOD_NULL and the mcs_idx value is taken by the packing functions - // otherwise mod + tbs values are used to generate the mcs_idx automatically. - uint8_t tbs_idx; - uint8_t mcs_idx; - int tbs;// If tbs<=0, the tbs_idx value is taken by the packing functions to generate the DCI - // message. Otherwise the tbs_idx corresponding to the lower nearest TBS is taken. + lte_mod_t mod; + uint32_t tbs; } ra_mcs_t; typedef enum LIBLTE_API { @@ -60,13 +52,14 @@ typedef struct LIBLTE_API { typedef struct LIBLTE_API { uint32_t vrb_bitmask; - uint8_t rbg_subset;bool shift; + uint32_t rbg_subset; + bool shift; } ra_type1_t; typedef struct LIBLTE_API { uint32_t riv; // if L_crb==0, DCI message packer will take this value directly - uint16_t L_crb; - uint16_t RB_start; + uint32_t L_crb; + uint32_t RB_start; enum { nprb1a_2 = 0, nprb1a_3 = 1 } n_prb1a; @@ -79,20 +72,34 @@ typedef struct LIBLTE_API { } ra_type2_t; typedef struct LIBLTE_API { - unsigned short rnti; + uint32_t prb_idx[MAX_PRB]; + uint32_t nof_prb; +} ra_prb_slot_t; + +typedef struct LIBLTE_API { + ra_prb_slot_t slot[2]; + uint32_t lstart; + uint32_t re_sf[NSUBFRAMES_X_FRAME]; +} ra_prb_t; + +typedef struct LIBLTE_API { + uint16_t rnti; ra_type_t alloc_type; union { ra_type0_t type0_alloc; ra_type1_t type1_alloc; ra_type2_t type2_alloc; }; + ra_prb_t prb_alloc; + uint32_t mcs_idx; ra_mcs_t mcs; - uint8_t harq_process; - uint8_t rv_idx;bool ndi; + uint32_t harq_process; + uint32_t rv_idx; + bool ndi; } ra_pdsch_t; typedef struct LIBLTE_API { - /* 36.213 Table 8.4-2: hop_half is 0 for < 10 Mhz and 10 for > 10 Mh. + /* 36.213 Table 8.4-2: hop_half is 0 for < 10 Mhz and 10 for > 10 Mhz. * hop_quart is 00 for > 10 Mhz and hop_quart_neg is 01 for > 10 Mhz. */ enum { @@ -103,64 +110,85 @@ typedef struct LIBLTE_API { hop_type_2 = 3 } freq_hop_fl; + ra_prb_t prb_alloc; + ra_type2_t type2_alloc; + uint32_t mcs_idx; ra_mcs_t mcs; - uint8_t rv_idx; // If set to non-zero, a retransmission is requested with the same modulation + uint32_t rv_idx; // If set to non-zero, a retransmission is requested with the same modulation // than before (Format0 message, see also 8.6.1 in 36.2313). - bool ndi;bool cqi_request; + bool ndi; + bool cqi_request; } ra_pusch_t; -typedef struct LIBLTE_API { - uint8_t prb_idx[110]; - int nof_prb; -} ra_prb_slot_t; +LIBLTE_API void ra_prb_fprint(FILE *f, + ra_prb_slot_t *prb); -typedef struct LIBLTE_API { - ra_prb_slot_t slot[2]; - int lstart; - int re_sf[NSUBFRAMES_X_FRAME]; -} ra_prb_t; +LIBLTE_API int ra_prb_get_dl(ra_prb_t *prb, + ra_pdsch_t *ra, + uint32_t nof_prb); + +LIBLTE_API int ra_prb_get_ul(ra_prb_slot_t *prb, + ra_pusch_t *ra, + uint32_t nof_prb); + +LIBLTE_API void ra_prb_get_re_dl(ra_prb_t *prb_dist, + uint32_t nof_prb, + uint32_t nof_ports, + uint32_t nof_ctrl_symbols, + lte_cp_t cp); + +LIBLTE_API uint32_t ra_nprb_dl(ra_pdsch_t *ra, + uint32_t nof_prb); + +LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra, + uint32_t nof_prb); + +LIBLTE_API int ra_mcs_from_idx_dl(uint32_t mcs_idx, + uint32_t nof_prb, + ra_mcs_t *mcs); + +LIBLTE_API int ra_mcs_from_idx_ul(uint32_t mcs_idx, + uint32_t nof_prb, + ra_mcs_t *mcs); + +LIBLTE_API int ra_tbs_from_idx_format1c(uint32_t tbs_idx); + +LIBLTE_API int ra_tbs_from_idx(uint32_t tbs_idx, + uint32_t n_prb); + +LIBLTE_API int ra_tbs_to_table_idx(uint32_t tbs, + uint32_t n_prb); + +LIBLTE_API uint32_t ra_type0_P(uint32_t nof_prb); + +LIBLTE_API uint32_t ra_type2_to_riv(uint32_t L_crb, + uint32_t RB_start, + uint32_t nof_prb); + +LIBLTE_API void ra_type2_from_riv(uint32_t riv, + uint32_t *L_crb, + uint32_t *RB_start, + uint32_t nof_prb, + uint32_t nof_vrb); + +LIBLTE_API uint32_t ra_type2_n_vrb_dl(uint32_t nof_prb, + bool ngap_is_1); + +LIBLTE_API uint32_t ra_type2_n_rb_step(uint32_t nof_prb); + +LIBLTE_API uint32_t ra_type2_ngap(uint32_t nof_prb, + bool ngap_is_1); + +LIBLTE_API uint32_t ra_type1_N_rb(uint32_t nof_prb); + +LIBLTE_API void ra_pdsch_fprint(FILE *f, + ra_pdsch_t *ra, + uint32_t nof_prb); -LIBLTE_API void ra_prb_fprint(FILE *f, ra_prb_slot_t *prb); - -LIBLTE_API int ra_prb_get_dl(ra_prb_t *prb, ra_pdsch_t *ra, int nof_prb); -LIBLTE_API int ra_prb_get_ul(ra_prb_slot_t *prb, ra_pusch_t *ra, int nof_prb); -LIBLTE_API void ra_prb_get_re(ra_prb_t *prb_dist, int nof_prb, int nof_ports, - int nof_ctrl_symbols, lte_cp_t cp); - -LIBLTE_API int ra_nprb_dl(ra_pdsch_t *ra, int nof_prb); -LIBLTE_API int ra_nprb_ul(ra_pusch_t *ra, int nof_prb); -LIBLTE_API int ra_re_x_prb(int nsubframe, int nslot, int prb_idx, int nof_prb, - int nof_ports, int nof_ctrl_symbols, lte_cp_t cp); - -LIBLTE_API uint8_t ra_mcs_to_table_idx(ra_mcs_t *mcs); -LIBLTE_API int ra_mcs_from_idx_dl(uint8_t idx, ra_mcs_t *mcs); -LIBLTE_API int ra_mcs_from_idx_ul(uint8_t idx, ra_mcs_t *mcs); -LIBLTE_API int ra_tbs_from_idx_format1c(uint8_t tbs_idx); -LIBLTE_API int ra_tbs_to_table_idx_format1c(int tbs); -LIBLTE_API int ra_tbs_from_idx(uint8_t tbs_idx, int n_prb); -LIBLTE_API int ra_tbs_to_table_idx(int tbs, int n_prb); - -LIBLTE_API uint8_t ra_mcs_to_table_idx(ra_mcs_t *mcs); -LIBLTE_API int ra_mcs_from_idx_dl(uint8_t idx, ra_mcs_t *mcs); -LIBLTE_API int ra_mcs_from_idx_ul(uint8_t idx, ra_mcs_t *mcs); - -LIBLTE_API char *ra_mod_string(ra_mod_t mod); - -LIBLTE_API int ra_type0_P(int nof_prb); - -LIBLTE_API uint32_t ra_type2_to_riv(uint16_t L_crb, uint16_t RB_start, int nof_prb); -LIBLTE_API void ra_type2_from_riv(uint32_t riv, uint16_t *L_crb, uint16_t *RB_start, - int nof_prb, int nof_vrb); -LIBLTE_API int ra_type2_n_vrb_dl(int nof_prb, bool ngap_is_1); -LIBLTE_API int ra_type2_n_rb_step(int nof_prb); -LIBLTE_API int ra_type2_ngap(int nof_prb, bool ngap_is_1); -LIBLTE_API int ra_type1_N_rb(int nof_prb); - -LIBLTE_API void ra_pdsch_set_mcs_index(ra_pdsch_t *ra, uint8_t mcs_idx); -LIBLTE_API void ra_pdsch_set_mcs(ra_pdsch_t *ra, ra_mod_t mod, uint8_t tbs_idx); -LIBLTE_API void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, int nof_prb); -LIBLTE_API void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, int nof_prb); +LIBLTE_API void ra_pusch_fprint(FILE *f, + ra_pusch_t *ra, + uint32_t nof_prb); #endif /* RB_ALLOC_H_ */ diff --git a/lte/phy/include/liblte/phy/phch/regs.h b/lte/phy/include/liblte/phy/phch/regs.h index 79d90d615..1b2f470d6 100644 --- a/lte/phy/include/liblte/phy/phch/regs.h +++ b/lte/phy/include/liblte/phy/phch/regs.h @@ -45,57 +45,91 @@ typedef _Complex float cf_t; typedef struct LIBLTE_API { - int k[4]; - int k0; - int l; + uint32_t k[4]; + uint32_t k0; + uint32_t l; bool assigned; }regs_reg_t; typedef struct LIBLTE_API { - int nof_regs; + uint32_t nof_regs; regs_reg_t **regs; }regs_ch_t; typedef struct LIBLTE_API { - int cell_id; - int nof_prb; - int max_ctrl_symbols; - int cfi; - int ngroups_phich; - int nof_ports; - lte_cp_t cp; + lte_cell_t cell; + uint32_t max_ctrl_symbols; + uint32_t cfi; + bool cfi_initiated; + uint32_t ngroups_phich; + phich_resources_t phich_res; phich_length_t phich_len; + regs_ch_t pcfich; regs_ch_t *phich; // there are several phich regs_ch_t pdcch[3]; /* PDCCH indexing, permutation and interleaving is computed for the three possible CFI value */ - int nof_regs; + + uint32_t nof_regs; regs_reg_t *regs; }regs_t; -LIBLTE_API int regs_init(regs_t *h, int cell_id, int nof_prb, int nof_ports, - phich_resources_t phich_res, phich_length_t phich_len, lte_cp_t cp); -LIBLTE_API void regs_free(regs_t *h); -LIBLTE_API int regs_set_cfi(regs_t *h, int nof_ctrl_symbols); - -LIBLTE_API int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb); -LIBLTE_API int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb); -LIBLTE_API int regs_get_reg(regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, int nof_prb); -LIBLTE_API int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, int nof_prb); +LIBLTE_API int regs_init(regs_t *h, + phich_resources_t phich_res, + phich_length_t phich_len, + lte_cell_t cell); -LIBLTE_API int regs_pcfich_nregs(regs_t *h); -LIBLTE_API int regs_pcfich_put(regs_t *h, cf_t pcfich_symbols[REGS_PCFICH_NSYM], cf_t *slot_symbols); -LIBLTE_API int regs_pcfich_get(regs_t *h, cf_t *slot_symbols, cf_t pcfich_symbols[REGS_PCFICH_NSYM]); +LIBLTE_API void regs_free(regs_t *h); +LIBLTE_API int regs_set_cfi(regs_t *h, + uint32_t nof_ctrl_symbols); + + +LIBLTE_API uint32_t regs_pcfich_nregs(regs_t *h); +LIBLTE_API int regs_pcfich_put(regs_t *h, + cf_t pcfich_symbols[REGS_PCFICH_NSYM], + cf_t *slot_symbols); + +LIBLTE_API int regs_pcfich_get(regs_t *h, + cf_t *slot_symbols, + cf_t pcfich_symbols[REGS_PCFICH_NSYM]); + +LIBLTE_API uint32_t regs_phich_nregs(regs_t *h); +LIBLTE_API int regs_phich_add(regs_t *h, + cf_t phich_symbols[REGS_PHICH_NSYM], + uint32_t ngroup, + cf_t *slot_symbols); + +LIBLTE_API int regs_phich_get(regs_t *h, + cf_t *slot_symbols, + cf_t phich_symbols[REGS_PHICH_NSYM], + uint32_t ngroup); + +LIBLTE_API uint32_t regs_phich_ngroups(regs_t *h); +LIBLTE_API int regs_phich_reset(regs_t *h, + cf_t *slot_symbols); + +LIBLTE_API int regs_pdcch_nregs(regs_t *h, uint32_t cfi); +LIBLTE_API int regs_pdcch_put(regs_t *h, + cf_t *pdcch_symbols, + cf_t *slot_symbols); + +LIBLTE_API int regs_pdcch_put_offset(regs_t *h, + cf_t *pdcch_symbols, + cf_t *slot_symbols, + uint32_t start_reg, + uint32_t nof_regs); + +LIBLTE_API int regs_pdcch_get(regs_t *h, + cf_t *slot_symbols, + cf_t *pdcch_symbols); + +LIBLTE_API int regs_pdcch_get_offset(regs_t *h, + cf_t *slot_symbols, + cf_t *pdcch_symbols, + uint32_t start_reg, + uint32_t nof_regs); -LIBLTE_API int regs_phich_nregs(regs_t *h); -LIBLTE_API int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup, cf_t *slot_symbols); -LIBLTE_API int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup); -LIBLTE_API int regs_phich_ngroups(regs_t *h); -LIBLTE_API int regs_phich_reset(regs_t *h, cf_t *slot_symbols); +#endif // REGS_H_ -LIBLTE_API int regs_pdcch_nregs(regs_t *h); -LIBLTE_API int regs_pdcch_put(regs_t *h, cf_t *pdcch_symbols, cf_t *slot_symbols); -LIBLTE_API int regs_pdcch_get(regs_t *h, cf_t *slot_symbols, cf_t *pdcch_symbols); -#endif // REGS_H_ diff --git a/lte/phy/include/liblte/phy/phy.h b/lte/phy/include/liblte/phy/phy.h index bf1d67626..eef9eccc1 100644 --- a/lte/phy/include/liblte/phy/phy.h +++ b/lte/phy/include/liblte/phy/phy.h @@ -50,11 +50,14 @@ #include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/fft.h" -#include "liblte/phy/common/sequence.h" - + #include "liblte/phy/ch_estimation/chest.h" #include "liblte/phy/ch_estimation/refsignal.h" +#include "liblte/phy/resampling/interp.h" +#include "liblte/phy/resampling/decim.h" +#include "liblte/phy/resampling/resample_arb.h" + #include "liblte/phy/channel/ch_awgn.h" #include "liblte/phy/fec/viterbi.h" @@ -89,11 +92,14 @@ #include "liblte/phy/phch/pbch.h" #include "liblte/phy/phch/pcfich.h" #include "liblte/phy/phch/phich.h" + +#include "liblte/phy/ue/ue_sync.h" +#include "liblte/phy/ue/ue_mib.h" +#include "liblte/phy/ue/ue_celldetect.h" +#include "liblte/phy/ue/ue_dl.h" #include "liblte/phy/scrambling/scrambling.h" -#include "liblte/phy/resampling/interp.h" - #include "liblte/phy/sync/pss.h" #include "liblte/phy/sync/sfo.h" #include "liblte/phy/sync/sss.h" diff --git a/lte/phy/include/liblte/phy/resampling/decim.h b/lte/phy/include/liblte/phy/resampling/decim.h new file mode 100644 index 000000000..4d3036784 --- /dev/null +++ b/lte/phy/include/liblte/phy/resampling/decim.h @@ -0,0 +1,39 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef DECIM_H +#define DECIM_H_ + +#include "liblte/config.h" + +typedef _Complex float cf_t; + + +LIBLTE_API void decim_c(cf_t *input, cf_t *output, int M, int len); +LIBLTE_API void decim_f(float *input, float *output, int M, int len); + +#endif // DECIM_H diff --git a/lte/phy/include/liblte/phy/resampling/interp.h b/lte/phy/include/liblte/phy/resampling/interp.h index ef7e87205..c39c83602 100644 --- a/lte/phy/include/liblte/phy/resampling/interp.h +++ b/lte/phy/include/liblte/phy/resampling/interp.h @@ -26,15 +26,73 @@ */ #ifndef INTERP_H -#define INTERP_H_ +#define INTERP_H + +#include #include "liblte/config.h" typedef _Complex float cf_t; +typedef enum LIBLTE_API {LINEAR} interp_type_t; + +typedef struct LIBLTE_API { + interp_type_t type; + + float *in_mag; + float *in_arg; + float *in_mag0; + float *in_arg0; + float *in_mag1; + float *in_arg1; + + float *out_mag; + float *out_arg; + float *out_arg2; + int16_t *table_idx; + + cf_t *out_cexp; + cf_t *out_prod; + + cf_t *cexptable; + + uint32_t len; + uint32_t M; + +}interp_t; + +LIBLTE_API int interp_init(interp_t *q, + interp_type_t type, + uint32_t len, + uint32_t M); + +LIBLTE_API void interp_free(interp_t *q); + +LIBLTE_API void interp_run(interp_t *q, + cf_t *input, + cf_t *output); + +LIBLTE_API void interp_run_offset(interp_t *q, + cf_t *input, + cf_t *output, + uint32_t off_st, + uint32_t off_end); + +LIBLTE_API void interp_linear_offset(cf_t *input, + cf_t *output, + uint32_t M, + uint32_t len, + uint32_t off_st, + uint32_t off_end); + +LIBLTE_API void interp_linear_c(cf_t *input, + cf_t *output, + uint32_t M, + uint32_t len); -LIBLTE_API void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end); -LIBLTE_API void interp_linear(cf_t *input, cf_t *output, int M, int len); -LIBLTE_API void interp_linear_f(float *input, float *output, int M, int len); +LIBLTE_API void interp_linear_f(float *input, + float *output, + uint32_t M, + uint32_t len); #endif // INTERP_H diff --git a/lte/phy/include/liblte/phy/sync/cfo.h b/lte/phy/include/liblte/phy/sync/cfo.h index c2adb9a04..b88683ad1 100644 --- a/lte/phy/include/liblte/phy/sync/cfo.h +++ b/lte/phy/include/liblte/phy/sync/cfo.h @@ -32,6 +32,7 @@ #include #include "liblte/config.h" +#include "liblte/phy/utils/cexptab.h" typedef _Complex float cf_t; @@ -48,10 +49,20 @@ typedef struct LIBLTE_API { cf_t *cur_cexp; }cfo_t; -LIBLTE_API int cfo_init(cfo_t *h, int nsamples); +LIBLTE_API int cfo_init(cfo_t *h, + uint32_t nsamples); + LIBLTE_API void cfo_free(cfo_t *h); -LIBLTE_API void cfo_set_tol(cfo_t *h, float tol); -LIBLTE_API void cfo_correct(cfo_t *h, cf_t *x, float freq); +LIBLTE_API int cfo_realloc(cfo_t *h, + uint32_t samples); + +LIBLTE_API void cfo_set_tol(cfo_t *h, + float tol); + +LIBLTE_API void cfo_correct(cfo_t *h, + cf_t *input, + cf_t *output, + float freq); #endif // CFO_ diff --git a/lte/phy/include/liblte/phy/sync/pss.h b/lte/phy/include/liblte/phy/sync/pss.h index 62ad0cc7a..22828240e 100644 --- a/lte/phy/include/liblte/phy/sync/pss.h +++ b/lte/phy/include/liblte/phy/sync/pss.h @@ -42,8 +42,7 @@ typedef _Complex float cf_t; /* this is only a shortcut */ #define DEFAULT_CORRELATION_TH 10000 #define DEFAULT_NOSYNC_TIMEOUT 5 -#define PSS_LEN_FREQ 129 // FFT-based convolution removes 1 leaving it in 128 -#define PSS_LEN 62 +#define PSS_LEN 62 #define PSS_RE 6*12 @@ -67,45 +66,45 @@ typedef struct LIBLTE_API { conv_fft_cc_t conv_fft; #endif - int frame_size; - int N_id_2; - float current_cfo; - bool cfo_auto; // default true - int nof_nosync_frames; - int nosync_timeout_frames; // default 5 - float correlation_threshold; // default 10000 - int frame_start_idx; - int fb_wp; - - cf_t *pss_signal_freq; + uint32_t frame_size; + uint32_t N_id_2; + uint32_t fft_size; + + cf_t *pss_signal_freq[3]; // One sequence for each N_id_2 cf_t *tmp_input; - float *conv_abs; - cf_t *frame_buffer; cf_t *conv_output; - cf_t *tmp_nco; + }pss_synch_t; typedef enum { PSS_TX, PSS_RX } pss_direction_t; /* Basic functionality */ -LIBLTE_API int pss_synch_init(pss_synch_t *q, int frame_size); +LIBLTE_API int pss_synch_init_fft(pss_synch_t *q, + uint32_t frame_size, + uint32_t fft_size); + +LIBLTE_API int pss_synch_init(pss_synch_t *q, + uint32_t frame_size); + LIBLTE_API void pss_synch_free(pss_synch_t *q); -LIBLTE_API int pss_generate(cf_t *signal, int N_id_2); -LIBLTE_API void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp); -LIBLTE_API int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2); -LIBLTE_API int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, float *corr_mean_value); -LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv); +LIBLTE_API int pss_generate(cf_t *signal, + uint32_t N_id_2); + +LIBLTE_API void pss_put_slot(cf_t *pss_signal, + cf_t *slot, + uint32_t nof_prb, + lte_cp_t cp); +LIBLTE_API int pss_synch_set_N_id_2(pss_synch_t *q, + uint32_t N_id_2); -/* Automatic frame management functions (for periodic calling) */ -LIBLTE_API int pss_synch_periodic(pss_synch_t *q, cf_t *input, cf_t *output, int nsamples); -LIBLTE_API void pss_synch_set_timeout(pss_synch_t *q, int nof_frames); -LIBLTE_API void pss_synch_set_threshold(pss_synch_t *q, float threshold); -LIBLTE_API void pss_synch_set_cfo_mode(pss_synch_t *q, bool cfo_auto); -LIBLTE_API float pss_synch_get_cfo(pss_synch_t *q); -LIBLTE_API int pss_synch_get_frame_start_idx(pss_synch_t *q); +LIBLTE_API int pss_synch_find_pss(pss_synch_t *q, + cf_t *input, + float *corr_peak_value); +LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q, + cf_t *pss_recv); /* High-level API */ diff --git a/lte/phy/include/liblte/phy/sync/sss.h b/lte/phy/include/liblte/phy/sync/sss.h index 0426e1502..bdc219542 100644 --- a/lte/phy/include/liblte/phy/sync/sss.h +++ b/lte/phy/include/liblte/phy/sync/sss.h @@ -38,26 +38,20 @@ typedef _Complex float cf_t; /* this is only a shortcut */ -/** gives the beginning of the SSS symbol (to be passed to sss_synch_m0m1). - * subframe_sz is the length of the subframe, e.g. 1920 for the 1.9 MHz - * symbol_sz is the OFDM symbol size (including CP), e.g. 137 for the 1.9 MHz - */ -#define SSS_SYMBOL_ST(subframe_sz, symbol_sz) (subframe_sz/2-2*symbol_sz) -#define SSS_POS_SYMBOL 33 -#define SSS_DFT_LEN 128 -#define N_SSS 31 +#define N_SSS 31 #define SSS_LEN 2*N_SSS +#define SSS_MAX_FFT_LEN 2048 + struct sss_tables{ int z1[N_SSS][N_SSS]; int c[2][N_SSS]; int s[N_SSS][N_SSS]; - int N_id_2; }; /* Allocate 32 complex to make it multiple of 32-byte AVX instructions alignment requirement. - * Should use vect_malloc() to make it platform agnostic. + * Should use vec_malloc() to make it platform agnostic. */ struct fc_tables{ cf_t z1[N_SSS+1][N_SSS+1]; @@ -70,34 +64,68 @@ struct fc_tables{ typedef struct LIBLTE_API { dft_plan_t dftp_input; + + uint32_t fft_size; float corr_peak_threshold; - int symbol_sz; - int subframe_sz; - - int N_id_1_table[30][30]; - struct fc_tables fc_tables; + uint32_t symbol_sz; + uint32_t subframe_sz; + uint32_t N_id_2; + + uint32_t N_id_1_table[30][30]; + struct fc_tables fc_tables[3]; // one for each N_id_2 }sss_synch_t; /* Basic functionality */ -LIBLTE_API int sss_synch_init(sss_synch_t *q); +LIBLTE_API int sss_synch_init(sss_synch_t *q, + uint32_t fft_size); + +LIBLTE_API int sss_synch_realloc(sss_synch_t *q, + uint32_t fft_size); + LIBLTE_API void sss_synch_free(sss_synch_t *q); -LIBLTE_API void sss_generate(float *signal0, float *signal5, int cell_id); -LIBLTE_API void sss_put_slot(float *sss, cf_t *symbol, int nof_prb, lte_cp_t cp); -LIBLTE_API int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2); +LIBLTE_API void sss_generate(float *signal0, + float *signal5, + uint32_t cell_id); + +LIBLTE_API void sss_put_slot(float *sss, + cf_t *symbol, + uint32_t nof_prb, + lte_cp_t cp); + +LIBLTE_API int sss_synch_set_N_id_2(sss_synch_t *q, + uint32_t N_id_2); + +LIBLTE_API int sss_synch_m0m1(sss_synch_t *q, + cf_t *input, + uint32_t *m0, + float *m0_value, + uint32_t *m1, + float *m1_value); + +LIBLTE_API uint32_t sss_synch_subframe(uint32_t m0, + uint32_t m1); + +LIBLTE_API int sss_synch_N_id_1(sss_synch_t *q, + uint32_t m0, + uint32_t m1); + +LIBLTE_API int sss_synch_frame(sss_synch_t *q, + cf_t *input, + uint32_t *subframe_idx, + uint32_t *N_id_1); + +LIBLTE_API void sss_synch_set_threshold(sss_synch_t *q, + float threshold); -LIBLTE_API void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, - int *m1, float *m1_value); -LIBLTE_API int sss_synch_subframe(int m0, int m1); -LIBLTE_API int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1); +LIBLTE_API void sss_synch_set_symbol_sz(sss_synch_t *q, + uint32_t symbol_sz); -LIBLTE_API int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, int *N_id_1); -LIBLTE_API void sss_synch_set_threshold(sss_synch_t *q, float threshold); -LIBLTE_API void sss_synch_set_symbol_sz(sss_synch_t *q, int symbol_sz); -LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz); +LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q, + uint32_t subframe_sz); /* High-level API */ @@ -105,18 +133,18 @@ LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz); typedef struct LIBLTE_API { sss_synch_t obj; struct sss_synch_init { - int N_id_2; + uint32_t N_id_2; } init; cf_t *input; - int in_len; + uint32_t in_len; struct sss_synch_ctrl_in { - int symbol_sz; - int subframe_sz; - int correlation_threshold; + uint32_t symbol_sz; + uint32_t subframe_sz; + uint32_t correlation_threshold; } ctrl_in; struct sss_synch_ctrl_out { - int subframe_idx; - int N_id_1; + uint32_t subframe_idx; + uint32_t N_id_1; } ctrl_out; }sss_synch_hl; diff --git a/lte/phy/include/liblte/phy/sync/sync.h b/lte/phy/include/liblte/phy/sync/sync.h index b1602a117..0cf86b7c9 100644 --- a/lte/phy/include/liblte/phy/sync/sync.h +++ b/lte/phy/include/liblte/phy/sync/sync.h @@ -30,15 +30,19 @@ #define SYNC_ #include +#include #include "liblte/config.h" -#include "pss.h" -#include "sss.h" -#include "sfo.h" +#include "liblte/phy/sync/pss.h" +#include "liblte/phy/sync/sss.h" + +#define FFT_SIZE_MIN 64 +#define FFT_SIZE_MAX 2048 /** * * This object performs time and frequency synchronization using the PSS and SSS signals. + * * The object is designed to work with signals sampled at 1.92 Mhz centered at the carrier frequency. * Thus, downsampling is required if the signal is sampled at higher frequencies. * @@ -47,60 +51,83 @@ * functions sync_pss_det_absolute() and sync_pss_det_peakmean(). */ -enum sync_pss_det { ABSOLUTE, PEAK_MEAN}; - typedef struct LIBLTE_API { - pss_synch_t pss[3]; // One for each N_id_2 - sss_synch_t sss[3]; // One for each N_id_2 - enum sync_pss_det pss_mode; + pss_synch_t pss; + sss_synch_t sss; float threshold; - float peak_to_avg; - int force_N_id_2; - int N_id_2; - int N_id_1; - int slot_id; + float mean_energy; + float peak_value; + float mean_peak_value; + uint32_t N_id_2; + uint32_t N_id_1; + uint32_t sf_idx; + uint32_t fft_size; + uint32_t frame_size; + uint64_t frame_cnt; float cfo; - lte_cp_t cp; bool detect_cp; bool sss_en; + bool normalize_en; + lte_cp_t cp; }sync_t; -LIBLTE_API int sync_init(sync_t *q, int frame_size); +LIBLTE_API int sync_init(sync_t *q, + uint32_t frame_size, + uint32_t fft_size); + LIBLTE_API void sync_free(sync_t *q); -/* Runs the synchronization algorithm. input signal must be sampled at 1.92 MHz and should be frame_size long at least */ -LIBLTE_API int sync_run(sync_t *q, cf_t *input); +LIBLTE_API void sync_reset(sync_t *q); + +/* Finds a correlation peak in the input signal around position find_offset */ +LIBLTE_API int sync_find(sync_t *q, + cf_t *input, + uint32_t find_offset, + uint32_t *peak_position); /* Sets the threshold for peak comparison */ -LIBLTE_API void sync_set_threshold(sync_t *q, float threshold); -/* Set peak comparison to absolute value */ -LIBLTE_API void sync_pss_det_absolute(sync_t *q); -/* Set peak comparison to relative to the mean */ -LIBLTE_API void sync_pss_det_peak_to_avg(sync_t *q); - -/* Forces the synchronizer to check one N_id_2 PSS sequence only (useful for tracking mode) */ -LIBLTE_API void sync_force_N_id_2(sync_t *q, int force_N_id_2); -/* Forces the synchronizer to skip CP detection (useful for tracking mode) */ -LIBLTE_API void sync_force_cp(sync_t *q, lte_cp_t cp); -/* Enables/Disables SSS detection (useful for tracking mode) */ -LIBLTE_API void sync_sss_en(sync_t *q, bool enabled); - - -/* Gets the slot id (0 or 10) */ -LIBLTE_API int sync_get_slot_id(sync_t *q); -/* Gets the last peak-to-average ratio */ -LIBLTE_API float sync_get_peak_to_avg(sync_t *q); -/* Gets the N_id_2 from the last call to synch_run() */ -LIBLTE_API int sync_get_N_id_2(sync_t *q); -/* Gets the N_id_1 from the last call to synch_run() */ -LIBLTE_API int sync_get_N_id_1(sync_t *q); +LIBLTE_API void sync_set_threshold(sync_t *q, + float threshold); + +/* Gets the subframe idx (0 or 5) */ +LIBLTE_API uint32_t sync_get_sf_idx(sync_t *q); + +/* Gets the last peak value */ +LIBLTE_API float sync_get_last_peak_value(sync_t *q); + +/* Gets the mean peak value */ +LIBLTE_API float sync_get_peak_value(sync_t *q); + +/* Gets the last input signal energy estimation value */ +LIBLTE_API float sync_get_input_energy(sync_t *q); + +/* Sets the N_id_2 to search for */ +LIBLTE_API int sync_set_N_id_2(sync_t *q, + uint32_t N_id_2); + /* Gets the Physical CellId from the last call to synch_run() */ LIBLTE_API int sync_get_cell_id(sync_t *q); + /* Gets the CFO estimation from the last call to synch_run() */ LIBLTE_API float sync_get_cfo(sync_t *q); + /* Gets the CP length estimation from the last call to synch_run() */ LIBLTE_API lte_cp_t sync_get_cp(sync_t *q); +/* Enables/Disables energy normalization every frame. If disabled, uses the mean */ +LIBLTE_API void sync_normalize_en(sync_t *q, + bool enable); + +/* Enables/Disables SSS detection */ +LIBLTE_API void sync_sss_en(sync_t *q, + bool enabled); + +LIBLTE_API bool sync_sss_detected(sync_t *q); + +/* Enables/Disables CP detection */ +LIBLTE_API void sync_cp_en(sync_t *q, + bool enabled); + #endif // SYNC_ diff --git a/lte/phy/include/liblte/phy/ue/ue_celldetect.h b/lte/phy/include/liblte/phy/ue/ue_celldetect.h new file mode 100644 index 000000000..778e17508 --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_celldetect.h @@ -0,0 +1,131 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_CELLSEARCH_ +#define UE_CELLSEARCH_ + +#include + +#include "liblte/config.h" +#include "liblte/phy/sync/sync.h" +#include "liblte/phy/sync/cfo.h" +#include "liblte/phy/ch_estimation/chest.h" +#include "liblte/phy/phch/pbch.h" +#include "liblte/phy/common/fft.h" + +/************************************************************ + * + * This object scans a signal for LTE cells using the known PSS + * and SSS sequences. + * + * The function ue_celldetect_scan() shall be called multiple times, + * each passing a number of samples multiple of 4800, sampled at 960 KHz + * (that is, 5 ms of samples). + * + * The function returns 0 until a signal is found nof_frames_detected times or + * after nof_frames_total with no signal detected. + * + * See ue_cell_detect.c for an example. + * + ************************************************************/ + +/** + * TODO: Check also peak offset + */ + +#define CS_DEFAULT_MAXFRAMES_TOTAL 300 +#define CS_DEFAULT_MAXFRAMES_DETECTED 30 + +#define CS_DEFAULT_NOFFRAMES_TOTAL 100 +#define CS_DEFAULT_NOFFRAMES_DETECTED 10 + +#define CS_FIND_THRESHOLD 0.6 + +#define CS_FRAME_UNALIGNED -3 +#define CS_CELL_DETECTED 2 +#define CS_CELL_NOT_DETECTED 3 + +typedef struct LIBLTE_API { + uint32_t cell_id; + lte_cp_t cp; + float peak; + uint32_t mode; +} ue_celldetect_result_t; + + +typedef struct LIBLTE_API { + sync_t sfind; + uint32_t max_frames_total; + uint32_t max_frames_detected; + uint32_t nof_frames_total; + uint32_t nof_frames_detected; + + uint32_t current_nof_detected; + uint32_t current_nof_total; + + uint32_t current_N_id_2; + + uint32_t *mode_ntimes; + char *mode_counted; + + ue_celldetect_result_t *candidates; +} ue_celldetect_t; + + +LIBLTE_API int ue_celldetect_init(ue_celldetect_t *q); + +LIBLTE_API int ue_celldetect_init_max(ue_celldetect_t *q, + uint32_t max_frames_total, + uint32_t max_frames_detected); + +LIBLTE_API void ue_celldetect_free(ue_celldetect_t *q); + +LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q); + +LIBLTE_API int ue_celldetect_scan(ue_celldetect_t *q, + cf_t *signal, + uint32_t nsamples, + ue_celldetect_result_t *found_cell); + +LIBLTE_API int ue_celldetect_set_nof_frames_total(ue_celldetect_t *q, + uint32_t nof_frames); + +LIBLTE_API int ue_celldetect_set_nof_frames_detected(ue_celldetect_t *q, + uint32_t nof_frames); + +LIBLTE_API void ue_celldetect_set_threshold(ue_celldetect_t *q, + float threshold); + +LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q); + + + + + + +#endif // SYNC_FRAME_ + diff --git a/lte/phy/include/liblte/phy/ue/ue_dl.h b/lte/phy/include/liblte/phy/ue/ue_dl.h new file mode 100644 index 000000000..e38d0ebf0 --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_dl.h @@ -0,0 +1,99 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEDL_H +#define UEDL_H + +/******************************************************* + * + * This module is a frontend to all the data and control channels processing + * modules. + ********************************************************/ + + + +#include "liblte/phy/ch_estimation/chest.h" +#include "liblte/phy/common/fft.h" +#include "liblte/phy/common/phy_common.h" + +#include "liblte/phy/phch/dci.h" +#include "liblte/phy/phch/pbch.h" +#include "liblte/phy/phch/pcfich.h" +#include "liblte/phy/phch/pdcch.h" +#include "liblte/phy/phch/pdsch.h" +#include "liblte/phy/phch/phich.h" +#include "liblte/phy/phch/ra.h" +#include "liblte/phy/phch/regs.h" + +#include "liblte/phy/utils/vector.h" +#include "liblte/phy/utils/debug.h" + +#include "liblte/config.h" + +#define NOF_HARQ_PROCESSES 8 + +typedef struct LIBLTE_API { + pbch_t pbch; + pcfich_t pcfich; + pdcch_t pdcch; + pdsch_t pdsch; + pdsch_harq_t harq_process[NOF_HARQ_PROCESSES]; + regs_t regs; + lte_fft_t fft; + chest_t chest; + + lte_cell_t cell; + + cf_t *sf_symbols; + cf_t *ce[MAX_PORTS]; + + uint64_t pkt_errors; + uint64_t pkts_total; + uint64_t nof_trials; + + uint32_t sfn; + bool pbch_decoded; + + uint16_t user_rnti; +}ue_dl_t; + +/* This function shall be called just after the initial synchronization */ +LIBLTE_API int ue_dl_init(ue_dl_t *q, + lte_cell_t cell, + phich_resources_t phich_resources, + phich_length_t phich_length, + uint16_t user_rnti); + +LIBLTE_API void ue_dl_free(ue_dl_t *q); + +LIBLTE_API int ue_dl_decode(ue_dl_t *q, + cf_t *sf_buffer, + char *data, + uint32_t sf_idx, + uint16_t rnti); + +#endif \ No newline at end of file diff --git a/lte/phy/include/liblte/phy/ue/ue_mib.h b/lte/phy/include/liblte/phy/ue/ue_mib.h new file mode 100644 index 000000000..31624d6f9 --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_mib.h @@ -0,0 +1,109 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_MIB_ +#define UE_MIB_ + + +/************************************************************ + * + * This object decodes the MIB from the PBCH of an LTE signal. + * + * The function ue_mib_decode() shall be called multiple times, + * each passing a number of samples multiple of 19200, sampled at 1.92 MHz + * (that is, 10 ms of samples). + * + * The function uses the sync_t object to find the PSS sequence and + * decode the PBCH to obtain the MIB. + * + * The function returns 0 until the MIB is decoded. + * + * See ue_cell_detect.c for an example. + * + ************************************************************/ + +#include + +#include "liblte/config.h" +#include "liblte/phy/sync/sync.h" +#include "liblte/phy/sync/cfo.h" +#include "liblte/phy/ch_estimation/chest.h" +#include "liblte/phy/phch/pbch.h" +#include "liblte/phy/common/fft.h" + +#define MIB_FIND_THRESHOLD 0.6 + +#define MIB_NOF_PORTS 2 + +#define MIB_FRAME_SIZE 9600 + +#define MIB_FRAME_UNALIGNED -3 +#define MIB_FOUND 1 +#define MIB_NOTFOUND 0 + +typedef struct LIBLTE_API { + sync_t sfind; + + uint32_t cell_id; + + cf_t *slot1_symbols; + cf_t *ce[MIB_NOF_PORTS]; + + lte_fft_t fft; + chest_t chest; + pbch_t pbch; + + uint32_t frame_cnt; + uint32_t last_frame_trial; +} ue_mib_t; + + +LIBLTE_API int ue_mib_init(ue_mib_t *q, + uint32_t cell_id, + lte_cp_t cp); + +LIBLTE_API void ue_mib_free(ue_mib_t *q); + +LIBLTE_API void ue_mib_reset(ue_mib_t *q); + +LIBLTE_API int ue_mib_decode(ue_mib_t *q, + cf_t *signal, + uint32_t nsamples, + pbch_mib_t *mib); + +LIBLTE_API void ue_mib_set_threshold(ue_mib_t *q, + float threshold); + +LIBLTE_API void ue_mib_reset(ue_mib_t *q); + + + + + + +#endif // SYNC_FRAME_ + diff --git a/lte/phy/include/liblte/phy/ue/ue_sync.h b/lte/phy/include/liblte/phy/ue/ue_sync.h new file mode 100644 index 000000000..1e608ebbf --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_sync.h @@ -0,0 +1,124 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_SYNC_ +#define UE_SYNC_ + +#include + +#include "liblte/config.h" +#include "liblte/phy/sync/sync.h" +#include "liblte/phy/sync/cfo.h" +#include "liblte/phy/ch_estimation/chest.h" +#include "liblte/phy/phch/pbch.h" +#include "liblte/phy/common/fft.h" + +/************************************************************** + * + * This object automatically manages the cell synchronization procedure. + * + * The main function is ue_sync_get_buffer(), which returns a pointer + * to the aligned subframe of samples (before FFT). This function + * should be called regularly, returning every 1 ms. It reads from the + * USRP, aligns the samples to the subframe and performs time/freq synch. + * + * The function returns 1 when the signal is correctly acquired and the + * returned buffer is aligned with the subframe. + * + *************************************************************/ + +typedef enum LIBLTE_API { SF_FIND, SF_TRACK} ue_sync_state_t; + +#define TRACK_MAX_LOST 10 +#define MEASURE_EXEC_TIME + +typedef struct LIBLTE_API { + sync_t sfind; + sync_t strack; + + void *stream; + int (*recv_callback)(void*, void*, uint32_t); + + ue_sync_state_t state; + + cf_t *input_buffer; + + /* These count half frames (5ms) */ + uint64_t frame_ok_cnt; + uint32_t frame_no_cnt; + uint32_t frame_total_cnt; + + /* this is the system frame number (SFN) */ + uint32_t frame_number; + + lte_cell_t cell; + uint32_t sf_idx; + + cfo_t cfocorr; + float cur_cfo; + + bool decode_sss_on_track; + + uint32_t peak_idx; + int time_offset; + float mean_time_offset; + #ifdef MEASURE_EXEC_TIME + float mean_exec_time; + #endif +} ue_sync_t; + + +LIBLTE_API int ue_sync_init(ue_sync_t *q, + lte_cell_t cell, + int (recv_callback)(void*, void*, uint32_t), + void *stream_handler); + +LIBLTE_API void ue_sync_free(ue_sync_t *q); + +LIBLTE_API int ue_sync_get_buffer(ue_sync_t *q, + cf_t **sf_symbols); + +LIBLTE_API void ue_sync_reset(ue_sync_t *q); + +LIBLTE_API void ue_sync_decode_sss_on_track(ue_sync_t *q, + bool enabled); + +LIBLTE_API ue_sync_state_t ue_sync_get_state(ue_sync_t *q); + +LIBLTE_API uint32_t ue_sync_get_sfidx(ue_sync_t *q); + +LIBLTE_API float ue_sync_get_cfo(ue_sync_t *q); + +LIBLTE_API float ue_sync_get_sfo(ue_sync_t *q); + + + + + + +#endif // SYNC_FRAME_ + diff --git a/lte/phy/include/liblte/phy/utils/bit.h b/lte/phy/include/liblte/phy/utils/bit.h index 7256b6d09..acfdbce3c 100644 --- a/lte/phy/include/liblte/phy/utils/bit.h +++ b/lte/phy/include/liblte/phy/utils/bit.h @@ -38,7 +38,7 @@ LIBLTE_API uint32_t bit_unpack(char **bits, int nof_bits); LIBLTE_API void bit_pack(uint32_t value, char **bits, int nof_bits); LIBLTE_API void bit_fprint(FILE *stream, char *bits, int nof_bits); LIBLTE_API unsigned int bit_diff(char *x, char *y, int nbits); -LIBLTE_API int bit_count(unsigned int n); +LIBLTE_API uint32_t bit_count(uint32_t n); #endif // BIT_ diff --git a/lte/phy/include/liblte/phy/utils/cexptab.h b/lte/phy/include/liblte/phy/utils/cexptab.h index 058ea81c4..6922eff32 100644 --- a/lte/phy/include/liblte/phy/utils/cexptab.h +++ b/lte/phy/include/liblte/phy/utils/cexptab.h @@ -30,19 +30,28 @@ #define CEXPTAB_ #include +#include #include "liblte/config.h" typedef _Complex float cf_t; typedef struct LIBLTE_API { - int size; + uint32_t size; cf_t *tab; }cexptab_t; -LIBLTE_API int cexptab_init(cexptab_t *nco, int size); +LIBLTE_API int cexptab_init(cexptab_t *nco, + uint32_t size); + LIBLTE_API void cexptab_free(cexptab_t *nco); -LIBLTE_API void cexptab_gen(cexptab_t *nco, cf_t *x, float freq, int len); -LIBLTE_API void cexptab_gen_direct(cf_t *x, float freq, int len); +LIBLTE_API void cexptab_gen(cexptab_t *nco, + cf_t *x, + float freq, + uint32_t len); + +LIBLTE_API void cexptab_gen_direct(cf_t *x, + float freq, + uint32_t len); #endif // CEXPTAB_ diff --git a/lte/phy/include/liblte/phy/utils/convolution.h b/lte/phy/include/liblte/phy/utils/convolution.h index 99c058f02..ed9404738 100644 --- a/lte/phy/include/liblte/phy/utils/convolution.h +++ b/lte/phy/include/liblte/phy/utils/convolution.h @@ -32,23 +32,36 @@ #include "liblte/config.h" #include "liblte/phy/utils/dft.h" +typedef _Complex float cf_t; + typedef struct LIBLTE_API { - _Complex float *input_fft; - _Complex float *filter_fft; - _Complex float *output_fft; - _Complex float *output_fft2; - int input_len; - int filter_len; - int output_len; + cf_t *input_fft; + cf_t *filter_fft; + cf_t *output_fft; + cf_t *output_fft2; + uint32_t input_len; + uint32_t filter_len; + uint32_t output_len; dft_plan_t input_plan; dft_plan_t filter_plan; dft_plan_t output_plan; }conv_fft_cc_t; -LIBLTE_API int conv_fft_cc_init(conv_fft_cc_t *state, int input_len, int filter_len); -LIBLTE_API void conv_fft_cc_free(conv_fft_cc_t *state); -LIBLTE_API int conv_fft_cc_run(conv_fft_cc_t *state, _Complex float *input, _Complex float *filter, _Complex float *output); +LIBLTE_API int conv_fft_cc_init(conv_fft_cc_t *q, + uint32_t input_len, + uint32_t filter_len); + +LIBLTE_API void conv_fft_cc_free(conv_fft_cc_t *q); + +LIBLTE_API uint32_t conv_fft_cc_run(conv_fft_cc_t *q, + cf_t *input, + cf_t *filter, + cf_t *output); -LIBLTE_API int conv_cc(_Complex float *input, _Complex float *filter, _Complex float *output, int input_len, int filter_len); +LIBLTE_API uint32_t conv_cc(cf_t *input, + cf_t *filter, + cf_t *output, + uint32_t input_len, + uint32_t filter_len); #endif // CONVOLUTION_H_ diff --git a/lte/phy/include/liblte/phy/utils/debug.h b/lte/phy/include/liblte/phy/utils/debug.h index 06b03079c..78bba353a 100644 --- a/lte/phy/include/liblte/phy/utils/debug.h +++ b/lte/phy/include/liblte/phy/utils/debug.h @@ -44,6 +44,7 @@ LIBLTE_API extern int verbose; #define VERBOSE_ISINFO() (verbose>=VERBOSE_INFO) #define VERBOSE_ISDEBUG() (verbose>=VERBOSE_DEBUG) +#define VERBOSE_ISNONE() (verbose==VERBOSE_NONE) #define PRINT_DEBUG verbose=VERBOSE_DEBUG #define PRINT_INFO verbose=VERBOSE_INFO diff --git a/lte/phy/include/liblte/phy/utils/vector.h b/lte/phy/include/liblte/phy/utils/vector.h index 4456f8036..3989dd69d 100644 --- a/lte/phy/include/liblte/phy/utils/vector.h +++ b/lte/phy/include/liblte/phy/utils/vector.h @@ -30,51 +30,87 @@ #define VECTOR_ #include +#include #include "liblte/config.h" typedef _Complex float cf_t; +#define EXPAVERAGE(data, average, nframes) (((data) + (average) * (nframes)) / ((nframes) + 1)) + + /** Return the sum of all the elements */ -LIBLTE_API int vec_acc_ii(int *x, int len); -LIBLTE_API float vec_acc_ff(float *x, int len); -LIBLTE_API cf_t vec_acc_cc(cf_t *x, int len); +LIBLTE_API int vec_acc_ii(int *x, uint32_t len); +LIBLTE_API float vec_acc_ff(float *x, uint32_t len); +LIBLTE_API cf_t vec_acc_cc(cf_t *x, uint32_t len); -LIBLTE_API void *vec_malloc(int size); +LIBLTE_API void *vec_malloc(uint32_t size); + +LIBLTE_API void *vec_realloc(void *ptr, uint32_t old_size, uint32_t new_size); /* print vectors */ -LIBLTE_API void vec_fprint_c(FILE *stream, cf_t *x, int len); -LIBLTE_API void vec_fprint_f(FILE *stream, float *x, int len); -LIBLTE_API void vec_fprint_b(FILE *stream, char *x, int len); -LIBLTE_API void vec_fprint_i(FILE *stream, int *x, int len); +LIBLTE_API void vec_fprint_c(FILE *stream, cf_t *x, uint32_t len); +LIBLTE_API void vec_fprint_f(FILE *stream, float *x, uint32_t len); +LIBLTE_API void vec_fprint_b(FILE *stream, char *x, uint32_t len); +LIBLTE_API void vec_fprint_i(FILE *stream, int *x, uint32_t len); +LIBLTE_API void vec_fprint_hex(FILE *stream, char *x, uint32_t len); + +/* Saves a vector to a file */ +LIBLTE_API void vec_save_file(char *filename, void *buffer, uint32_t len); /* sum two vectors */ -LIBLTE_API void vec_sum_ch(char *z, char *x, char *y, int len); -LIBLTE_API void vec_sum_ccc(cf_t *z, cf_t *x, cf_t *y, int len); +LIBLTE_API void vec_sum_ch(char *x, char *y, char *z, uint32_t len); +LIBLTE_API void vec_sum_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len); + +/* substract two vectors z=x-y */ +LIBLTE_API void vec_sub_fff(float *x, float *y, float *z, uint32_t len); + +/* Square distance */ +LIBLTE_API void vec_square_dist(cf_t symbol, cf_t *points, float *distance, uint32_t npoints); /* scalar product */ -LIBLTE_API void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len); -LIBLTE_API void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len); +LIBLTE_API void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, uint32_t len); +LIBLTE_API void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, uint32_t len); +LIBLTE_API void vec_sc_prod_fff(float *x, float h, float *z, uint32_t len); + +LIBLTE_API void vec_convert_fi(float *x, int16_t *z, float scale, uint32_t len); + +LIBLTE_API void vec_deinterleave_cf(cf_t *x, float *real, float *imag, uint32_t len); +LIBLTE_API void vec_deinterleave_real_cf(cf_t *x, float *real, uint32_t len); + +/* vector product (element-wise) */ +LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len); /* vector product (element-wise) */ -LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, int len); -LIBLTE_API void vec_prod_ccc_unalign(cf_t *x, cf_t *y, cf_t *z, int len); +LIBLTE_API void vec_prod_cfc(cf_t *x, float *y, cf_t *z, uint32_t len); + +/* conjugate vector product (element-wise) */ +LIBLTE_API void vec_prod_conj_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len); + +/* Dot-product */ +LIBLTE_API cf_t vec_dot_prod_ccc(cf_t *x, cf_t *y, uint32_t len); +LIBLTE_API cf_t vec_dot_prod_conj_ccc(cf_t *x, cf_t *y, uint32_t len); +LIBLTE_API float vec_dot_prod_fff(float *x, float *y, uint32_t len); /* z=x/y vector division (element-wise) */ -LIBLTE_API void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, int len); +LIBLTE_API void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len); /* conjugate */ -LIBLTE_API void vec_conj_cc(cf_t *x, cf_t *y, int len); +LIBLTE_API void vec_conj_cc(cf_t *x, cf_t *y, uint32_t len); /* average vector power */ -LIBLTE_API float vec_avg_power_cf(cf_t *x, int len); +LIBLTE_API float vec_avg_power_cf(cf_t *x, uint32_t len); /* return the index of the maximum value in the vector */ -LIBLTE_API int vec_max_fi(float *x, int len); +LIBLTE_API uint32_t vec_max_fi(float *x, uint32_t len); +LIBLTE_API uint32_t vec_max_abs_ci(cf_t *x, uint32_t len); /* quantify vector of floats and convert to unsigned char */ -LIBLTE_API void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, int len); +LIBLTE_API void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len); /* magnitude of each vector element */ -LIBLTE_API void vec_abs_cf(cf_t *x, float *abs, int len); +LIBLTE_API void vec_abs_cf(cf_t *x, float *abs, uint32_t len); + +/* argument of each vector element */ +LIBLTE_API void vec_arg_cf(cf_t *x, float *arg, uint32_t len); #endif // VECTOR_ diff --git a/lte/phy/lib/CMakeLists.txt b/lte/phy/lib/CMakeLists.txt index ec06db805..bbb80299c 100644 --- a/lte/phy/lib/CMakeLists.txt +++ b/lte/phy/lib/CMakeLists.txt @@ -29,13 +29,13 @@ FIND_PACKAGE(FFTW3F REQUIRED) # TODO: distribute kissfft instead INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) IF(${DISABLE_VOLK}) - IF(${DISABLE_VOLK} EQUAL 0) - FIND_PACKAGE(Volk) - ELSE(${DISABLE_VOLK} EQUAL 0) - MESSAGE(STATUS "VOLK library disabled (DISABLE_VOLK=1)") - ENDIF(${DISABLE_VOLK} EQUAL 0) + IF(${DISABLE_VOLK} EQUAL 0) + FIND_PACKAGE(Volk) + ELSE(${DISABLE_VOLK} EQUAL 0) + MESSAGE(STATUS "VOLK library disabled (DISABLE_VOLK=1)") + ENDIF(${DISABLE_VOLK} EQUAL 0) ELSE(${DISABLE_VOLK}) - FIND_PACKAGE(Volk) + FIND_PACKAGE(Volk) ENDIF(${DISABLE_VOLK}) ######################################################################## @@ -44,10 +44,10 @@ ENDIF(${DISABLE_VOLK}) FILE(GLOB modules *) SET(SOURCES_ALL "") FOREACH (_module ${modules}) - IF(IS_DIRECTORY ${_module}) - FILE(GLOB_RECURSE tmp "${_module}/src/*.c") - LIST(APPEND SOURCES_ALL ${tmp}) - ENDIF(IS_DIRECTORY ${_module}) + IF(IS_DIRECTORY ${_module}) + FILE(GLOB_RECURSE tmp "${_module}/src/*.c") + LIST(APPEND SOURCES_ALL ${tmp}) + ENDIF(IS_DIRECTORY ${_module}) ENDFOREACH() ADD_LIBRARY(lte_phy SHARED ${SOURCES_ALL}) @@ -56,12 +56,12 @@ INSTALL(TARGETS lte_phy DESTINATION ${LIBRARY_DIR}) LIBLTE_SET_PIC(lte_phy) IF(VOLK_FOUND) - INCLUDE_DIRECTORIES(${VOLK_INCLUDE_DIRS}) - SET_TARGET_PROPERTIES(lte_phy PROPERTIES COMPILE_DEFINITIONS "${VOLK_DEFINITIONS}") - TARGET_LINK_LIBRARIES(lte_phy ${VOLK_LIBRARIES}) - MESSAGE(STATUS " Compiling with VOLK SIMD library.") + INCLUDE_DIRECTORIES(${VOLK_INCLUDE_DIRS}) + SET_TARGET_PROPERTIES(lte_phy PROPERTIES COMPILE_DEFINITIONS "${VOLK_DEFINITIONS}") + TARGET_LINK_LIBRARIES(lte_phy ${VOLK_LIBRARIES}) + MESSAGE(STATUS " Compiling with VOLK SIMD library.") ELSE(VOLK_FOUND) - MESSAGE(STATUS " VOLK SIMD library NOT found. Using generic implementation.") + MESSAGE(STATUS " VOLK SIMD library NOT found. Using generic implementation.") ENDIF(VOLK_FOUND) @@ -70,10 +70,10 @@ ENDIF(VOLK_FOUND) ######################################################################## FILE(GLOB_RECURSE cmakefiles CMakeLists.txt) FOREACH (_file ${cmakefiles}) - GET_FILENAME_COMPONENT(dir ${_file} PATH) - IF (NOT ${dir} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) - ADD_SUBDIRECTORY(${dir}) - ENDIF () + GET_FILENAME_COMPONENT(dir ${_file} PATH) + IF (NOT ${dir} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + ADD_SUBDIRECTORY(${dir}) + ENDIF () ENDFOREACH() diff --git a/lte/phy/lib/agc/src/agc.c b/lte/phy/lib/agc/src/agc.c new file mode 100644 index 000000000..5d1add9ff --- /dev/null +++ b/lte/phy/lib/agc/src/agc.c @@ -0,0 +1,100 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include +#include +#include + +#include "liblte/phy/utils/debug.h" + +#include "liblte/phy/agc/agc.h" +#include "liblte/phy/utils/vector.h" +#include "liblte/phy/utils/debug.h" + + +int agc_init (agc_t *q) { + bzero(q, sizeof(agc_t)); + agc_reset(q); + return LIBLTE_SUCCESS; +} + +void agc_free(agc_t *q) { + bzero(q, sizeof(agc_t)); +} + +void agc_reset(agc_t *q) { + q->bandwidth = AGC_DEFAULT_BW; + q->lock = false; + q->gain = 1.0; + q->y_out = 1.0; + q->isfirst = true; +} + +void agc_set_bandwidth(agc_t *q, float bandwidth) { + q->bandwidth = bandwidth; +} + +float agc_get_rssi(agc_t *q) { + return 1.0/q->gain; +} + +float agc_get_output_level(agc_t *q) { + return q->y_out; +} + +float agc_get_gain(agc_t *q) { + return q->gain; +} + + +void agc_lock(agc_t *q, bool enable) { + q->lock = enable; +} + +void agc_process(agc_t *q, cf_t *input, cf_t *output, uint32_t len) { + + // Apply current gain to input signal + vec_sc_prod_cfc(input, q->gain, output, len); + + // compute output energy estimate + float y = sqrtf(crealf(vec_dot_prod_conj_ccc(output, output, len))/len); + + if (q->isfirst) { + q->y_out = y; + q->gain = 1/y; + q->isfirst = false; + } else { + q->y_out = (1-q->bandwidth) * q->y_out + q->bandwidth * y; + if (!q->lock) { + q->gain *= expf(-0.5*q->bandwidth*logf(q->y_out)); + } + } + DEBUG("AGC gain: %.3f y_out=%.3f, y=%.3f\n", q->gain, q->y_out, y); +} diff --git a/lte/phy/lib/ch_estimation/src/chest.c b/lte/phy/lib/ch_estimation/src/chest.c index a953153d9..293d3fe2b 100644 --- a/lte/phy/lib/ch_estimation/src/chest.c +++ b/lte/phy/lib/ch_estimation/src/chest.c @@ -29,24 +29,36 @@ #include #include #include -#include #include #include "liblte/phy/ch_estimation/chest.h" -#include "liblte/phy/resampling/interp.h" #include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/debug.h" #define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz) #define SF_SZ(q) (2 * SLOT_SZ(q)) -void chest_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { +#define VOLK_INTERP + +void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) { chest_ref_fprint(q, stream, nslot, port_id); chest_recvsig_fprint(q, stream, nslot, port_id); chest_ce_fprint(q, stream, nslot, port_id); } -void chest_ref_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { +/* Sets the number of ports to estimate. nof_ports must be smaler than nof_ports + * used during the call to chest_init(). + */ +int chest_set_nof_ports(chest_t *q, uint32_t nof_ports) { + if (nof_ports < q->nof_ports) { + q->nof_ports = nof_ports; + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; + } +} + +void chest_ref_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) { int i; fprintf(stream, "refs%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { @@ -56,7 +68,7 @@ void chest_ref_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { fprintf(stream, "];\n"); } -void chest_recvsig_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { +void chest_recvsig_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) { int i; fprintf(stream, "recvsig%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { @@ -66,7 +78,7 @@ void chest_recvsig_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { fprintf(stream, "];\n"); } -void chest_ce_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { +void chest_ce_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) { int i; fprintf(stream, "mag%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { @@ -80,143 +92,227 @@ void chest_ce_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { fprintf(stream, "];\n"); } -void chest_ce_ref(chest_t *q, cf_t *input, int nslot, int port_id, int nref) { +int chest_ce_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint32_t nref) { int fidx, tidx; cf_t known_ref, channel_ref; - - fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index - tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index - known_ref = q->refsignal[port_id][nslot].refs[nref].simbol; - channel_ref = input[SAMPLE_IDX(q->nof_prb, tidx, fidx)]; - q->refsignal[port_id][nslot].refs[nref].recv_simbol = channel_ref; - - DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, SAMPLE_IDX(q->nof_prb, tidx, fidx), - 10*log10f(cabsf(channel_ref/known_ref)), - cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,cargf(channel_ref/known_ref)/M_PI); - - /* FIXME: compare with threshold */ - if (channel_ref != 0) { - q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref; - } else { - q->refsignal[port_id][nslot].ch_est[nref] = 0; + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL && + nslot < NSLOTS_X_FRAME && + port_id < q->nof_ports) + { + if (nref < q->refsignal[port_id][nslot].nof_refs) { + + fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index + tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index + + known_ref = q->refsignal[port_id][nslot].refs[nref].simbol; + channel_ref = input[tidx * q->nof_re + fidx]; + q->refsignal[port_id][nslot].refs[nref].recv_simbol = channel_ref; + + + DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, tidx * q->nof_re + fidx, + 10*log10f(cabsf(channel_ref/known_ref)), + cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI, + cargf(channel_ref/known_ref)/M_PI); + + + /* FIXME: compare with threshold */ + if (channel_ref != 0) { + q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref; + } else { + q->refsignal[port_id][nslot].ch_est[nref] = 1e-6; + } + ret = LIBLTE_SUCCESS; + } } + return ret; } /* Computes channel estimates for each reference in a slot and port. * Saves the nof_prb * 12 * nof_symbols channel estimates in the array ce */ -void chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, int nslot, int port_id) { - int i, j; +int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32_t port_id) { + uint32_t i, j; cf_t x[2], y[MAX_NSYMB]; - assert(nslot >= 0 && nslot < NSLOTS_X_FRAME); - assert(port_id >= 0 && port_id < q->nof_ports); - assert(q->refsignal[port_id][nslot].nsymbols <= 2); - - refsignal_t *r = &q->refsignal[port_id][nslot]; - - INFO("Estimating channel slot=%d port=%d using %d reference signals\n", - nslot, port_id, r->nof_refs); - - for (i=0;inof_refs;i++) { - chest_ce_ref(q, input, nslot, port_id, i); - } - - /* interpolate the symbols with references - * in the freq domain */ - for (i=0;insymbols;i++) { - interp_linear_offset(&r->ch_est[i * r->nof_refs/2], - &ce[r->symbols_ref[i] * q->nof_prb * RE_X_RB], RE_X_RB/2, - r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset); + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL && + nslot < NSLOTS_X_FRAME && + port_id < q->nof_ports) + { + if (q->refsignal[port_id][nslot].nsymbols <= 2) { + refsignal_t *r = &q->refsignal[port_id][nslot]; + + DEBUG("Estimating channel slot=%d port=%d using %d reference signals\n", + nslot, port_id, r->nof_refs); + + for (i=0;inof_refs;i++) { + chest_ce_ref(q, input, nslot, port_id, i); + } - } - /* now interpolate in the time domain */ - for (i=0;inof_prb * RE_X_RB; i++) { - if (r->nsymbols > 1) { - for (j=0;jnsymbols;j++) { - x[j] = ce[r->symbols_ref[j] * q->nof_prb * RE_X_RB + i]; + /* interpolate the symbols with references + * in the freq domain */ + for (i=0;insymbols;i++) { +#ifdef VOLK_INTERP + interp_run_offset(&q->interp_freq[port_id], + &r->ch_est[i * r->nof_refs/2], &ce[r->symbols_ref[i] * q->nof_re], + r->voffset, RE_X_RB/2-r->voffset); +#else + interp_linear_offset(&r->ch_est[i * r->nof_refs/2], + &ce[r->symbols_ref[i] * q->nof_re], RE_X_RB/2, + r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset); +#endif } - interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0], - 2, r->symbols_ref[0], 3); - } else { - for (j=0;jsymbols_ref[0] * q->nof_prb * RE_X_RB + i]; + /* now interpolate in the time domain */ + for (i=0;inof_re; i++) { + if (r->nsymbols > 1) { + for (j=0;jnsymbols;j++) { + x[j] = ce[r->symbols_ref[j] * q->nof_re + i]; + } +#ifdef VOLK_INTERP + interp_run_offset(&q->interp_time[port_id], x, y, + r->symbols_ref[0], 3); +#else + interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0], + 2, r->symbols_ref[0], 3); +#endif + } else { + for (j=0;jsymbols_ref[0] * q->nof_re + i]; + } + } + for (j=0;jnof_symbols;j++) { + ce[j * q->nof_re + i] = y[j]; + } } + ret = LIBLTE_SUCCESS; } - for (j=0;jnof_symbols;j++) { - ce[j * q->nof_prb * RE_X_RB + i] = y[j]; + } + return ret; +} + + +/* Computes channel estimates for each reference in a subframe and port id. + */ +int chest_ce_sf_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id) { + int n, slotsz, ret; + slotsz = q->nof_symbols*q->nof_re; + for (n=0;n<2;n++) { + ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[n*slotsz], 2*sf_idx+n, port_id); + if (ret != LIBLTE_SUCCESS) { + return ret; } } + return LIBLTE_SUCCESS; } -/* Computes channel estimates for each reference in a slot. - * Saves the result for the p-th port to the pointer ce[p] +/* Computes channel estimates for each reference in a slot for all ports. */ -void chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, int nslot) { - int p; +int chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, uint32_t nslot) { + int p, ret; for (p=0;pnof_ports;p++) { - chest_ce_slot_port(q, input, ce[p], nslot, p); + ret = chest_ce_slot_port(q, input, ce[p], nslot, p); + if (ret != LIBLTE_SUCCESS) { + return ret; + } } + return LIBLTE_SUCCESS; } -int chest_init(chest_t *q, chest_interp_t interp, lte_cp_t cp, int nof_prb, int nof_ports) { - - if (nof_ports > MAX_PORTS) { - fprintf(stderr, "Error: Maximum ports %d\n", MAX_PORTS); - return -1; +/* Computes channel estimates for each reference in a subframe for all ports. + */ +int chest_ce_sf(chest_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx) { + int p, n, slotsz, ret; + slotsz = q->nof_symbols*q->nof_re; + for (p=0;pnof_ports;p++) { + for (n=0;n<2;n++) { + ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[p][n*slotsz], 2*sf_idx+n, p); + if (ret != LIBLTE_SUCCESS) { + return ret; + } + } } - bzero(q, sizeof(chest_t)); - - q->nof_ports = nof_ports; - q->nof_symbols = CP_NSYMB(cp); - q->cp = cp; - q->nof_prb = nof_prb; + return LIBLTE_SUCCESS; +} - switch(interp) { - case LINEAR: - q->interp = interp_linear_offset; +int chest_init(chest_t *q, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + nof_ports <= MAX_PORTS) + { + bzero(q, sizeof(chest_t)); + + q->nof_ports = nof_ports; + q->nof_symbols = nof_symbols; + q->nof_re = nof_re; + + INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n", + q->nof_symbols, q->nof_re, nof_ports); + + ret = LIBLTE_SUCCESS; } - - INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n", - q->nof_symbols, nof_prb, nof_ports); - - return 0; + return ret; } -int chest_ref_LTEDL_slot_port(chest_t *q, int port, int nslot, int cell_id) { - if (port < 0 || port > q->nof_ports) { - return -1; - } - if (nslot < 0 || nslot > NSLOTS_X_FRAME) { - return -1; +int chest_init_LTEDL(chest_t *q, lte_cell_t cell) { + int ret; + ret = chest_init(q, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports); + if (ret != LIBLTE_SUCCESS) { + return ret; + } else { + return chest_ref_LTEDL(q, cell); } +} - if (refsignal_init_LTEDL(&q->refsignal[port][nslot], port, nslot, cell_id, q->cp, q->nof_prb)) { - fprintf(stderr, "Error initiating CRS port=%d, slot=%d\n", port, nslot); - return -1; +int chest_ref_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + port_id < MAX_PORTS && + nslot < NSLOTS_X_FRAME) + { + ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell); + +#ifdef VOLK_INTERP + if (ret == LIBLTE_SUCCESS) { + if (nslot == 0) { + ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2); + if (ret == LIBLTE_SUCCESS) { + ret = interp_init(&q->interp_time[port_id], LINEAR, 2, + q->refsignal[port_id][nslot].symbols_ref[1] - q->refsignal[port_id][nslot].symbols_ref[0]); + } + } + } +#endif } - - return 0; + return ret; } -int chest_ref_LTEDL_slot(chest_t *q, int nslot, int cell_id) { - int p; +int chest_ref_LTEDL_slot(chest_t *q, uint32_t nslot, lte_cell_t cell) { + int p, ret; for (p=0;pnof_ports;p++) { - if (chest_ref_LTEDL_slot_port(q, p, nslot, cell_id)) { - return -1; + ret = chest_ref_LTEDL_slot_port(q, nslot, p, cell); + if (ret != LIBLTE_SUCCESS) { + return ret; } } - return 0; + return LIBLTE_SUCCESS; } -int chest_ref_LTEDL(chest_t *q, int cell_id) { - int n; +int chest_ref_LTEDL(chest_t *q, lte_cell_t cell) { + int n, ret; for (n=0;nrefsignal[p][n]); } } +#ifdef VOLK_INTERP + for (p=0;pinterp_freq[p]); + interp_free(&q->interp_time[p]); + } +#endif bzero(q, sizeof(chest_t)); } /* Fills l[2] with the symbols in the slot nslot that contain references. * returns the number of symbols with references (in the slot) */ -int chest_ref_symbols(chest_t *q, int port_id, int nslot, int l[2]) { - if (nslot < 0 || nslot > NSLOTS_X_FRAME) { - return -1; +int chest_ref_symbols(chest_t *q, uint32_t port_id, uint32_t nslot, uint32_t l[2]) { + + if (q != NULL && + port_id < MAX_PORTS && + nslot < NSLOTS_X_FRAME) + { + memcpy(l, q->refsignal[port_id][nslot].symbols_ref, sizeof(uint32_t) * q->refsignal[port_id][nslot].nsymbols); + return q->refsignal[port_id][nslot].nsymbols; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } - memcpy(l, q->refsignal[port_id][nslot].symbols_ref, sizeof(int) * q->refsignal[port_id][nslot].nsymbols); - return q->refsignal[port_id][nslot].nsymbols; } @@ -245,6 +352,8 @@ int chest_ref_symbols(chest_t *q, int port_id, int nslot, int l[2]) { */ int chest_initialize(chest_hl* h) { + lte_cell_t cell; + if (!h->init.nof_symbols) { h->init.nof_symbols = CPNORM_NSYMB; // Normal CP } @@ -252,37 +361,23 @@ int chest_initialize(chest_hl* h) { h->init.nof_prb = 6; } - if (chest_init(&h->obj, LINEAR, (h->init.nof_symbols==CPNORM_NSYMB)?CPNORM:CPEXT, - h->init.nof_prb, h->init.nof_ports)) { + cell.id = h->init.cell_id; + cell.nof_ports = h->init.nof_ports; + cell.nof_prb = h->init.nof_prb; + cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT; + + if (chest_init_LTEDL(&h->obj, cell)) { fprintf(stderr, "Error initializing equalizer\n"); return -1; } - if (h->init.cell_id != -1) { - if (chest_ref_LTEDL(&h->obj, h->init.cell_id)) { - fprintf(stderr, "Error initializing reference signal\n"); - return -1; - } - } return 0; } -/** This function must be called in an slot basis (0.5ms) for LTE */ +/** This function must be called in an subframe basis (1ms) for LTE */ int chest_work(chest_hl* hl) { - int i; chest_t *q = &hl->obj; - - if (hl->init.cell_id != hl->ctrl_in.cell_id) { - if (chest_ref_LTEDL(q, hl->init.cell_id)) { - fprintf(stderr, "Error initializing reference signal\n"); - return -1; - } - } - - for (i=0;iinit.nof_ports;i++) { - chest_ce_slot_port(q, hl->input, hl->output[i], 1, 0); - hl->out_len[i] = hl->in_len; - } + chest_ce_sf(q, hl->input, hl->output, hl->ctrl_in.sf_idx); return 0; } diff --git a/lte/phy/lib/ch_estimation/src/refsignal.c b/lte/phy/lib/ch_estimation/src/refsignal.c index 3303899a9..0ecdb59c1 100644 --- a/lte/phy/lib/ch_estimation/src/refsignal.c +++ b/lte/phy/lib/ch_estimation/src/refsignal.c @@ -40,7 +40,7 @@ #define idx(x, y) (l*nof_refs_x_symbol+i) -int refsignal_v(int port_id, int ns, int symbol_id) { +int refsignal_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id) { int v=-1; switch(port_id) { case 0: @@ -67,108 +67,121 @@ int refsignal_v(int port_id, int ns, int symbol_id) { return v; } -int refsignal_k(int m, int v, int cell_id) { +uint32_t refsignal_k(uint32_t m, uint32_t v, uint32_t cell_id) { return 6*m+((v+(cell_id%6))%6); } -void refsignal_put(refsignal_t *q, cf_t *slot_symbols) { - int i; - int fidx, tidx; - for (i=0;inof_refs;i++) { - fidx = q->refs[i].freq_idx; // reference frequency index - tidx = q->refs[i].time_idx; // reference time index - slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].simbol; +int refsignal_put(refsignal_t *q, cf_t *slot_symbols) { + uint32_t i; + uint32_t fidx, tidx; + if (q != NULL && + slot_symbols != NULL) + { + for (i=0;inof_refs;i++) { + fidx = q->refs[i].freq_idx; // reference frequency index + tidx = q->refs[i].time_idx; // reference time index + slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].simbol; + } + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } } /** Initializes refsignal_t object according to 3GPP 36.211 6.10.1 * */ -int refsignal_init_LTEDL(refsignal_t *q, int port_id, int nslot, - int cell_id, lte_cp_t cp, int nof_prb) { - - unsigned int c_init; - int ns, l, lp[2]; - int N_cp; - int i; - int ret = -1; +int refsignal_init_LTEDL(refsignal_t *q, uint32_t port_id, uint32_t nslot, + lte_cell_t cell) { + + uint32_t c_init; + uint32_t ns, l, lp[2]; + uint32_t N_cp; + uint32_t i; + int ret = LIBLTE_ERROR_INVALID_INPUTS; sequence_t seq; int v; - int mp; - int nof_refs_x_symbol, nof_ref_symbols; - - bzero(q, sizeof(refsignal_t)); - bzero(&seq, sizeof(sequence_t)); - - if (CP_ISNORM(cp)) { - N_cp = 1; - } else { - N_cp = 0; - } - - if (port_id < 0 || port_id > (MAX_PORTS - 1)) { - fprintf(stderr, "Invalid port id %d\n", port_id); - return -1; - } + uint32_t mp; + uint32_t nof_refs_x_symbol, nof_ref_symbols; + + if (q != NULL && + port_id < MAX_PORTS && + nslot < NSLOTS_X_FRAME && + lte_cell_isvalid(&cell)) + { + + bzero(q, sizeof(refsignal_t)); + bzero(&seq, sizeof(sequence_t)); + + if (CP_ISNORM(cell.cp)) { + N_cp = 1; + } else { + N_cp = 0; + } - if (port_id < 2) { - nof_ref_symbols = 2; - lp[0] = 0; - lp[1] = CP_NSYMB(cp) - 3; - } else { - nof_ref_symbols = 1; - lp[0] = 1; - } - nof_refs_x_symbol = 2 * nof_prb; - - q->nof_refs = nof_refs_x_symbol * nof_ref_symbols; - q->nsymbols = nof_ref_symbols; - q->symbols_ref = malloc(sizeof(int) * nof_ref_symbols); - q->voffset = cell_id%6; - q->nof_prb = nof_prb; - if (!q->symbols_ref) { - return -1; - } + if (port_id < 2) { + nof_ref_symbols = 2; + lp[0] = 0; + lp[1] = CP_NSYMB(cell.cp) - 3; + } else { + nof_ref_symbols = 1; + lp[0] = 1; + } + nof_refs_x_symbol = 2 * cell.nof_prb; - memcpy(q->symbols_ref, lp, sizeof(int) * nof_ref_symbols); + q->nof_refs = nof_refs_x_symbol * nof_ref_symbols; + q->nsymbols = nof_ref_symbols; + q->voffset = cell.id%6; + q->nof_prb = cell.nof_prb; - q->refs = vec_malloc(q->nof_refs * sizeof(ref_t)); - if (!q->refs) { - goto free_and_exit; - } - q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t)); - if (!q->ch_est) { - goto free_and_exit; - } + q->symbols_ref = malloc(sizeof(uint32_t) * nof_ref_symbols); + if (!q->symbols_ref) { + perror("malloc"); + goto free_and_exit; + } - ns = nslot; - for (l = 0; l < nof_ref_symbols; l++) { + memcpy(q->symbols_ref, lp, sizeof(uint32_t) * nof_ref_symbols); - c_init = 1024 * (7 * (ns + 1) + lp[l] + 1) * (2 * cell_id + 1) - + 2 * cell_id + N_cp; - if (sequence_LTEPRS(&seq, 2 * 2 * MAX_PRB, c_init)) { + q->refs = vec_malloc(q->nof_refs * sizeof(ref_t)); + if (!q->refs) { goto free_and_exit; } + q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t)); + if (!q->ch_est) { + goto free_and_exit; + } + + ns = nslot; + for (l = 0; l < nof_ref_symbols; l++) { - v = refsignal_v(port_id, ns, lp[l]); + c_init = 1024 * (7 * (ns + 1) + lp[l] + 1) * (2 * cell.id + 1) + + 2 * cell.id + N_cp; + ret = sequence_LTEPRS(&seq, 2 * 2 * MAX_PRB, c_init); + if (ret != LIBLTE_SUCCESS) { + goto free_and_exit; + } - for (i = 0; i < nof_refs_x_symbol; i++) { - mp = i + MAX_PRB - nof_prb; + v = refsignal_v(port_id, ns, lp[l]); - /* generate signal */ - __real__ q->refs[idx(l,i)].simbol = (1 - 2 * (float) seq.c[2 * mp]) / sqrt(2); - __imag__ q->refs[idx(l,i)].simbol = (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2); + for (i = 0; i < nof_refs_x_symbol; i++) { + mp = i + MAX_PRB - cell.nof_prb; - /* mapping to resource elements */ - q->refs[idx(l,i)].freq_idx = refsignal_k(i, v, cell_id); - q->refs[idx(l,i)].time_idx = lp[l]; + /* generate signal */ + __real__ q->refs[idx(l,i)].simbol = (1 - 2 * (float) seq.c[2 * mp]) / sqrt(2); + __imag__ q->refs[idx(l,i)].simbol = (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2); + + /* mapping to resource elements */ + q->refs[idx(l,i)].freq_idx = refsignal_k(i, (uint32_t) v, cell.id); + q->refs[idx(l,i)].time_idx = lp[l]; + } } + ret = LIBLTE_SUCCESS; } - - ret = 0; free_and_exit: - sequence_free(&seq); - if (ret == -1) { + if (ret != LIBLTE_ERROR_INVALID_INPUTS) { + sequence_free(&seq); + } + if (ret == LIBLTE_ERROR) { refsignal_free(q); } return ret; diff --git a/lte/phy/lib/ch_estimation/test/chest_test.c b/lte/phy/lib/ch_estimation/test/chest_test.c index 5f5d6647e..b0328b4ba 100644 --- a/lte/phy/lib/ch_estimation/test/chest_test.c +++ b/lte/phy/lib/ch_estimation/test/chest_test.c @@ -33,19 +33,22 @@ #include "liblte/phy/phy.h" -int cell_id = -1; -int nof_prb = 6; -lte_cp_t cp = CPNORM; +lte_cell_t cell = { + 6, // nof_prb + MAX_PORTS, // nof_ports + 1000, // cell_id + CPNORM // cyclic prefix +}; char *output_matlab = NULL; void usage(char *prog) { printf("Usage: %s [recov]\n", prog); - printf("\t-r nof_prb [Default %d]\n", nof_prb); + printf("\t-r nof_prb [Default %d]\n", cell.nof_prb); printf("\t-e extended cyclic prefix [Default normal]\n"); - printf("\t-c cell_id (-1 tests all). [Default %d]\n", cell_id); + printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id); printf("\t-o output matlab file [Default %s]\n",output_matlab?output_matlab:"None"); printf("\t-v increase verbosity\n"); @@ -56,13 +59,13 @@ void parse_args(int argc, char **argv) { while ((opt = getopt(argc, argv, "recov")) != -1) { switch(opt) { case 'r': - nof_prb = atoi(argv[optind]); + cell.nof_prb = atoi(argv[optind]); break; case 'e': - cp = CPEXT; + cell.cp = CPEXT; break; case 'c': - cell_id = atoi(argv[optind]); + cell.id = atoi(argv[optind]); break; case 'o': output_matlab = argv[optind]; @@ -120,7 +123,7 @@ int main(int argc, char **argv) { int max_cid; FILE *fmatlab = NULL; float mse_mag, mse_phase; - + parse_args(argc,argv); if (output_matlab) { @@ -131,7 +134,7 @@ int main(int argc, char **argv) { } } - num_re = nof_prb * RE_X_RB * CP_NSYMB(cp); + num_re = cell.nof_prb * RE_X_RB * CP_NSYMB(cell.cp); input = malloc(num_re * sizeof(cf_t)); if (!input) { @@ -149,28 +152,25 @@ int main(int argc, char **argv) { goto do_exit; } - if (cell_id == -1) { + if (cell.id == 1000) { cid = 0; max_cid = 504; } else { - cid = cell_id; - max_cid = cell_id; + cid = cell.id; + max_cid = cell.id; } + while(cid <= max_cid) { - if (chest_init(&eq, LINEAR, cp, nof_prb, MAX_PORTS)) { + cell.id = cid; + if (chest_init_LTEDL(&eq, cell)) { fprintf(stderr, "Error initializing equalizer\n"); goto do_exit; } - if (chest_ref_LTEDL(&eq, cid)) { - fprintf(stderr, "Error initializing reference signal\n"); - goto do_exit; - } - for (n_slot=0;n_slot #include #include +#include #include "gauss.h" #include "liblte/phy/channel/ch_awgn.h" -void ch_awgn_c(const cf_t* x, cf_t* y, float variance, int buff_sz) { - _Complex float tmp; - int i; +float ch_awgn_get_variance(float ebno_db, float rate) { + float esno_db = ebno_db + 10 * log10f(rate); + return sqrtf(1 / (powf(10, esno_db / 10))); +} + +void ch_awgn_c(const cf_t* x, cf_t* y, float variance, uint32_t len) { + cf_t tmp; + uint32_t i; - for (i=0;itmp = malloc(symbol_sz * sizeof(cf_t)); + q->tmp = malloc((uint32_t) symbol_sz * sizeof(cf_t)); if (!q->tmp) { perror("malloc"); return -1; @@ -56,15 +56,18 @@ int lte_fft_init_(lte_fft_t *q, lte_cp_t cp_type, int nof_prb, dft_dir_t dir) { dft_plan_set_norm(&q->fft_plan, true); dft_plan_set_dc(&q->fft_plan, true); - q->symbol_sz = symbol_sz; - q->nof_symbols = CP_NSYMB(cp_type); - q->cp_type = cp_type; + q->symbol_sz = (uint32_t) symbol_sz; + q->nof_symbols = CP_NSYMB(cp); + q->cp = cp; q->nof_re = nof_prb * RE_X_RB; q->nof_guards = ((symbol_sz - q->nof_re) / 2); - DEBUG("Init %s symbol_sz=%d, nof_symbols=%d, cp_type=%s, nof_re=%d, nof_guards=%d\n", + q->slot_sz = SLOT_LEN(symbol_sz); + + DEBUG("Init %s symbol_sz=%d, nof_symbols=%d, cp=%s, nof_re=%d, nof_guards=%d\n", dir==FORWARD?"FFT":"iFFT", q->symbol_sz, q->nof_symbols, - q->cp_type==CPNORM?"Normal":"Extended", q->nof_re, q->nof_guards); - return 0; + q->cp==CPNORM?"Normal":"Extended", q->nof_re, q->nof_guards); + + return LIBLTE_SUCCESS; } void lte_fft_free_(lte_fft_t *q) { @@ -75,25 +78,28 @@ void lte_fft_free_(lte_fft_t *q) { bzero(q, sizeof(lte_fft_t)); } -int lte_fft_init(lte_fft_t *q, lte_cp_t cp_type, int nof_prb) { - return lte_fft_init_(q, cp_type, nof_prb, FORWARD); +int lte_fft_init(lte_fft_t *q, lte_cp_t cp, uint32_t nof_prb) { + return lte_fft_init_(q, cp, nof_prb, FORWARD); } void lte_fft_free(lte_fft_t *q) { lte_fft_free_(q); } -int lte_ifft_init(lte_fft_t *q, lte_cp_t cp_type, int nof_prb) { - int i; - if (lte_fft_init_(q, cp_type, nof_prb, BACKWARD)) { - return -1; +int lte_ifft_init(lte_fft_t *q, lte_cp_t cp, uint32_t nof_prb) { + uint32_t i; + int ret; + + ret = lte_fft_init_(q, cp, nof_prb, BACKWARD); + + if (ret == LIBLTE_SUCCESS) { + /* set now zeros at CP */ + for (i=0;inof_symbols;i++) { + bzero(q->tmp, q->nof_guards * sizeof(cf_t)); + bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); + } } - /* set now zeros at CP */ - for (i=0;inof_symbols;i++) { - bzero(q->tmp, q->nof_guards * sizeof(cf_t)); - bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); - } - return 0; + return ret; } void lte_ifft_free(lte_fft_t *q) { @@ -103,10 +109,10 @@ void lte_ifft_free(lte_fft_t *q) { /* Transforms input samples into output OFDM symbols. * Performs FFT on a each symbol and removes CP. */ -void lte_fft_run(lte_fft_t *q, cf_t *input, cf_t *output) { - int i; +void lte_fft_run_slot(lte_fft_t *q, cf_t *input, cf_t *output) { + uint32_t i; for (i=0;inof_symbols;i++) { - input += CP_ISNORM(q->cp_type)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz); + input += CP_ISNORM(q->cp)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz); dft_run_c(&q->fft_plan, input, q->tmp); memcpy(output, &q->tmp[q->nof_guards], q->nof_re * sizeof(cf_t)); input += q->symbol_sz; @@ -114,13 +120,20 @@ void lte_fft_run(lte_fft_t *q, cf_t *input, cf_t *output) { } } +void lte_fft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) { + uint32_t n; + for (n=0;n<2;n++) { + lte_fft_run_slot(q, &input[n*q->slot_sz], &output[n*q->nof_re*q->nof_symbols]); + } +} + /* Transforms input OFDM symbols into output samples. * Performs FFT on a each symbol and adds CP. */ -void lte_ifft_run(lte_fft_t *q, cf_t *input, cf_t *output) { - int i, cp_len; +void lte_ifft_run_slot(lte_fft_t *q, cf_t *input, cf_t *output) { + uint32_t i, cp_len; for (i=0;inof_symbols;i++) { - cp_len = CP_ISNORM(q->cp_type)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz); + cp_len = CP_ISNORM(q->cp)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz); memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); dft_run_c(&q->fft_plan, q->tmp, &output[cp_len]); input += q->nof_re; @@ -130,3 +143,9 @@ void lte_ifft_run(lte_fft_t *q, cf_t *input, cf_t *output) { } } +void lte_ifft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) { + uint32_t n; + for (n=0;n<2;n++) { + lte_ifft_run_slot(q, &input[n*q->nof_re*q->nof_symbols], &output[n*q->slot_sz]); + } +} diff --git a/lte/phy/lib/common/src/phy_common.c b/lte/phy/lib/common/src/phy_common.c index c87f6eecf..6b16215a6 100644 --- a/lte/phy/lib/common/src/phy_common.c +++ b/lte/phy/lib/common/src/phy_common.c @@ -50,36 +50,115 @@ const int tc_cb_sizes[NOF_TC_CB_SIZES] = { 40, 48, 56, 64, 72, 80, 88, 96, 104, 4800, 4864, 4928, 4992, 5056, 5120, 5184, 5248, 5312, 5376, 5440, 5504, 5568, 5632, 5696, 5760, 5824, 5888, 5952, 6016, 6080, 6144 }; +/* Returns true if the structure pointed by cell has valid parameters + */ +bool lte_cell_isvalid(lte_cell_t *cell) { + if (cell->id < 504 && + cell->nof_ports > 0 && + cell->nof_ports < MAX_PORTS+1 && + cell->nof_prb > 5 && + cell->nof_prb < MAX_PRB+1 + ) { + return true; + } else { + return false; + } +} + +bool lte_N_id_2_isvalid(uint32_t N_id_2) { + if (N_id_2 < 3) { + return true; + } else { + return false; + } +} + +bool lte_N_id_1_isvalid(uint32_t N_id_1) { + if (N_id_1 < 169) { + return true; + } else { + return false; + } +} + + /* * Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index */ -int lte_cb_size(int index) { - if (index >= 0 && index < NOF_TC_CB_SIZES) { +int lte_cb_size(uint32_t index) { + if (index < NOF_TC_CB_SIZES) { return tc_cb_sizes[index]; } else { - return -1; + return LIBLTE_ERROR; + } +} + +char *lte_mod_string(lte_mod_t mod) { + switch (mod) { + case LTE_BPSK: + return "BPSK"; + case LTE_QPSK: + return "QPSK"; + case LTE_QAM16: + return "QAM16"; + case LTE_QAM64: + return "QAM64"; + default: + return "N/A"; + } +} + +uint32_t lte_mod_bits_x_symbol(lte_mod_t mod) { + switch (mod) { + case LTE_BPSK: + return 1; + case LTE_QPSK: + return 2; + case LTE_QAM16: + return 4; + case LTE_QAM64: + return 6; + default: + return 0; + } +} + +char *lte_cp_string(lte_cp_t cp) { + if (cp == CPNORM) { + return "Normal"; + } else { + return "Extended"; } } /* * Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212 */ -int lte_find_cb_index(int long_cb) { +int lte_find_cb_index(uint32_t long_cb) { int j = 0; while (j < NOF_TC_CB_SIZES && tc_cb_sizes[j] < long_cb) { j++; } if (j == NOF_TC_CB_SIZES) { - return -1; + return LIBLTE_ERROR; } else { return j; } } -const int lte_symbol_sz(int nof_prb) { +int lte_sampling_freq_hz(uint32_t nof_prb) { + int n = lte_symbol_sz(nof_prb); + if (n == -1) { + return LIBLTE_ERROR; + } else { + return 15000 * n; + } +} + +int lte_symbol_sz(uint32_t nof_prb) { if (nof_prb<=0) { - return -1; + return LIBLTE_ERROR; } if (nof_prb<=6) { return 128; @@ -94,10 +173,22 @@ const int lte_symbol_sz(int nof_prb) { } else if (nof_prb<=100) { return 2048; } - return -1; + return LIBLTE_ERROR; +} + +bool lte_symbol_sz_isvalid(uint32_t symbol_sz) { + if (symbol_sz == 128 || + symbol_sz == 256 || + symbol_sz == 512 || + symbol_sz == 1024 || + symbol_sz == 2048) { + return true; + } else { + return false; + } } -int lte_voffset(int symbol_id, int cell_id, int nof_ports) { +uint32_t lte_voffset(uint32_t symbol_id, uint32_t cell_id, uint32_t nof_ports) { if (nof_ports == 1 && symbol_id==0) { return (cell_id+3) % 6; } else { @@ -106,7 +197,7 @@ int lte_voffset(int symbol_id, int cell_id, int nof_ports) { } /* Returns the number of available RE per PRB */ -int lte_re_x_prb(int ns, int symbol, int nof_ports, int nof_symbols) { +uint32_t lte_re_x_prb(uint32_t ns, uint32_t symbol, uint32_t nof_ports, uint32_t nof_symbols) { if (symbol == 0) { if (((ns % 2) == 0) || (ns == 1)) { return RE_X_RB - 4; @@ -138,10 +229,10 @@ int lte_re_x_prb(int ns, int symbol, int nof_ports, int nof_symbols) { struct lte_band { - int band; + uint32_t band; float fd_low_mhz; - int earfcn_offset; - int earfcn_max; + uint32_t earfcn_offset; + uint32_t earfcn_max; enum band_geographical_area area; }; @@ -186,9 +277,9 @@ int lte_str2mimotype(char *mimo_type_str, lte_mimo_type_t *type) { } else if (!strcmp(mimo_type_str, "multiplex")) { *type = SPATIAL_MULTIPLEX; } else { - return -1; + return LIBLTE_ERROR; } - return 0; + return LIBLTE_SUCCESS; } char *lte_mimotype2str(lte_mimo_type_t type) { @@ -203,12 +294,16 @@ char *lte_mimotype2str(lte_mimo_type_t type) { return NULL; } -float get_fd(struct lte_band *band, int earfcn) { - return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset); +float get_fd(struct lte_band *band, uint32_t earfcn) { + if (earfcn >= band->earfcn_offset) { + return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset); + } else { + return 0.0; + } } -float lte_band_fd(int earfcn) { - int i; +float lte_band_fd(uint32_t earfcn) { + uint32_t i; i=0; while(i < NOF_LTE_BANDS && lte_bands[i].earfcn_offset lte_bands[i].earfcn_max) { fprintf(stderr, "Error: Invalid end earfcn %d. Max is %d\n", end_earfcn, lte_bands[i].earfcn_max); - return -1; + return LIBLTE_ERROR; } } if (start_earfcn == -1) { @@ -248,7 +343,7 @@ int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int start_earfcn, int e } else { if (start_earfcn < lte_bands[i].earfcn_offset) { fprintf(stderr, "Error: Invalid start earfcn %d. Min is %d\n", start_earfcn, lte_bands[i].earfcn_offset); - return -1; + return LIBLTE_ERROR; } } nof_earfcn = end_earfcn - start_earfcn; @@ -260,11 +355,11 @@ int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int start_earfcn, int e earfcn[j].id = j + start_earfcn; earfcn[j].fd = get_fd(<e_bands[i], earfcn[j].id); } - return j; + return (int) j; } -int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, int max_elems) { - int i; +int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, uint32_t max_elems) { + uint32_t i; int n; int nof_fd = 0; for (i=0;i 0;i++) { @@ -274,7 +369,7 @@ int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *ear nof_fd += n; max_elems -= n; } else { - return -1; + return LIBLTE_ERROR; } } } diff --git a/lte/phy/lib/common/src/sequence.c b/lte/phy/lib/common/src/sequence.c index a659b0562..f4325ccd7 100644 --- a/lte/phy/lib/common/src/sequence.c +++ b/lte/phy/lib/common/src/sequence.c @@ -41,17 +41,16 @@ * It follows the 3GPP Release 8 (LTE) 36.211 * Section 7.2 */ -void generate_prs_c(sequence_t *q, unsigned int seed) { +void generate_prs_c(sequence_t *q, uint32_t seed) { int n; - unsigned int *x1; - unsigned int *x2; + uint32_t *x1, *x2; - x1 = calloc(Nc + q->len + 31, sizeof(unsigned int)); + x1 = calloc(Nc + q->len + 31, sizeof(uint32_t)); if (!x1) { perror("calloc"); return; } - x2 = calloc(Nc + q->len + 31, sizeof(unsigned int)); + x2 = calloc(Nc + q->len + 31, sizeof(uint32_t)); if (!x2) { free(x1); perror("calloc"); @@ -76,26 +75,26 @@ void generate_prs_c(sequence_t *q, unsigned int seed) { free(x2); } -int sequence_LTEPRS(sequence_t *q, int len, int seed) { +int sequence_LTEPRS(sequence_t *q, uint32_t len, uint32_t seed) { if (sequence_init(q, len)) { - return -1; + return LIBLTE_ERROR; } q->len = len; generate_prs_c(q, seed); - return 0; + return LIBLTE_SUCCESS; } -int sequence_init(sequence_t *q, int len) { +int sequence_init(sequence_t *q, uint32_t len) { if (q->c && (q->len != len)) { free(q->c); } if (!q->c) { q->c = malloc(len * sizeof(char)); if (!q->c) { - return -1; + return LIBLTE_ERROR; } } - return 0; + return LIBLTE_SUCCESS; } void sequence_free(sequence_t *q) { diff --git a/lte/phy/lib/common/test/fft_test.c b/lte/phy/lib/common/test/fft_test.c index a5e1e3662..74f14f311 100644 --- a/lte/phy/lib/common/test/fft_test.c +++ b/lte/phy/lib/common/test/fft_test.c @@ -87,7 +87,7 @@ int main(int argc, char **argv) { perror("malloc"); exit(-1); } - outfft = malloc(sizeof(cf_t) * SLOT_LEN_CPNORM(lte_symbol_sz(n_prb))); + outfft = malloc(sizeof(cf_t) * SLOT_LEN(lte_symbol_sz(n_prb))); if (!outfft) { perror("malloc"); exit(-1); @@ -111,8 +111,8 @@ int main(int argc, char **argv) { input[i] = 100 * ((float) rand()/RAND_MAX + (float) I*rand()/RAND_MAX); } - lte_ifft_run(&ifft, input, outfft); - lte_fft_run(&fft, outfft, outifft); + lte_ifft_run_slot(&ifft, input, outfft); + lte_fft_run_slot(&fft, outfft, outifft); /* compute MSE */ diff --git a/lte/phy/lib/fec/src/convcoder.c b/lte/phy/lib/fec/src/convcoder.c index c20121d5c..2153327ac 100644 --- a/lte/phy/lib/fec/src/convcoder.c +++ b/lte/phy/lib/fec/src/convcoder.c @@ -28,33 +28,41 @@ #include #include +#include #include #include "liblte/phy/fec/convcoder.h" #include "parity.h" -int convcoder_encode(convcoder_t *q, char *input, char *output, int frame_length) { - unsigned int sr; - int i,j; - int len = q->tail_biting ? frame_length : (frame_length + q->K - 1); +int convcoder_encode(convcoder_t *q, char *input, char *output, uint32_t frame_length) { + uint32_t sr; + uint32_t i,j; + uint32_t len = q->tail_biting ? frame_length : (frame_length + q->K - 1); - if (q->tail_biting) { - sr = 0; - for (i=frame_length - q->K + 1; i q->K + 1) + { + if (q->tail_biting) { + sr = 0; + for (i=frame_length - q->K + 1; iR;j++) { - output[q->R * i + j] = parity(sr & q->poly[j]); + for (i = 0; i < len; i++) { + char bit = (i < frame_length) ? (input[i] & 1) : 0; + sr = (sr << 1) | bit; + for (j=0;jR;j++) { + output[q->R * i + j] = parity(sr & q->poly[j]); + } } + return q->R*len; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } - - return q->R*len; } diff --git a/lte/phy/lib/fec/src/crc.c b/lte/phy/lib/fec/src/crc.c index 00f74785c..c286c2f63 100644 --- a/lte/phy/lib/fec/src/crc.c +++ b/lte/phy/lib/fec/src/crc.c @@ -114,7 +114,7 @@ int crc_init(crc_t *h, unsigned int crc_poly, int crc_order) { return 0; } -unsigned int crc_checksum(crc_t *h, char *data, int len) { +uint32_t crc_checksum(crc_t *h, char *data, int len) { int i, k, len8, res8, a = 0; unsigned int crc = 0; char *pter; diff --git a/lte/phy/lib/fec/src/parity.c b/lte/phy/lib/fec/src/parity.c index 568994888..b12edfaee 100644 --- a/lte/phy/lib/fec/src/parity.c +++ b/lte/phy/lib/fec/src/parity.c @@ -4,26 +4,27 @@ */ #include +#include -unsigned char Partab[256]; -int P_init; +uint8_t Partab[256]; +uint32_t P_init; /* Create 256-entry odd-parity lookup table * Needed only on non-ia32 machines */ void partab_init(void) { - int i, cnt, ti; + uint32_t i, cnt, ti; - /* Initialize parity lookup table */ - for (i = 0; i < 256; i++) { - cnt = 0; - ti = i; - while (ti) { - if (ti & 1) - cnt++; - ti >>= 1; - } - Partab[i] = cnt & 1; - } - P_init = 1; + /* Initialize parity lookup table */ + for (i = 0; i < 256; i++) { + cnt = 0; + ti = i; + while (ti) { + if (ti & 1) + cnt++; + ti >>= 1; + } + Partab[i] = cnt & 1; + } + P_init = 1; } diff --git a/lte/phy/lib/fec/src/parity.h b/lte/phy/lib/fec/src/parity.h index dfda24f57..5ad4b1b5c 100644 --- a/lte/phy/lib/fec/src/parity.h +++ b/lte/phy/lib/fec/src/parity.h @@ -10,16 +10,16 @@ /* Determine parity of argument: 1 = odd, 0 = even */ #ifdef __i386__ -static inline int parityb(unsigned char x){ +static inline uint32_t parityb(uint8_t x){ __asm__ __volatile__ ("test %1,%1;setpo %0" : "=qhm" (x) : "qh" (x)); return x; } #else void partab_init(); -static inline int parityb(unsigned char x){ - extern unsigned char Partab[256]; - extern int P_init; +static inline uint32_t parityb(uint8_t x){ + extern uint8_t Partab[256]; + extern uint32_t P_init; if(!P_init){ partab_init(); } @@ -28,7 +28,7 @@ static inline int parityb(unsigned char x){ #endif -static inline int parity(int x){ +static inline uint32_t parity(int x){ /* Fold down to one byte */ x ^= (x >> 16); x ^= (x >> 8); diff --git a/lte/phy/lib/fec/src/rm_conv.c b/lte/phy/lib/fec/src/rm_conv.c index 3b4254c5f..0c10c9a0d 100644 --- a/lte/phy/lib/fec/src/rm_conv.c +++ b/lte/phy/lib/fec/src/rm_conv.c @@ -27,25 +27,27 @@ #include #include +#include + #include "liblte/phy/fec/rm_conv.h" #define NCOLS 32 #define NROWS_MAX NCOLS -unsigned char RM_PERM_CC[NCOLS] = { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, +uint8_t RM_PERM_CC[NCOLS] = { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31, 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30 }; -unsigned char RM_PERM_CC_INV[NCOLS] = +uint8_t RM_PERM_CC_INV[NCOLS] = { 16, 0, 24, 8, 20, 4, 28, 12, 18, 2, 26, 10, 22, 6, 30, 14, 17, 1, 25, 9, 21, 5, 29, 13, 19, 3, 27, 11, 23, 7, 31, 15 }; -int rm_conv_tx(char *input, int in_len, char *output, int out_len) { +int rm_conv_tx(char *input, uint32_t in_len, char *output, uint32_t out_len) { char tmp[3 * NCOLS * NROWS_MAX]; int nrows, ndummy, K_p; int i, j, k, s; - nrows = (int) (in_len / 3 - 1) / NCOLS + 1; + nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1; if (nrows > NROWS_MAX) { fprintf(stderr, "Input too large. Max input length is %d\n", 3 * NCOLS * NROWS_MAX); @@ -89,7 +91,7 @@ int rm_conv_tx(char *input, int in_len, char *output, int out_len) { /* Undoes Convolutional Code Rate Matching. * 3GPP TS 36.212 v10.1.0 section 5.1.4.2 */ -int rm_conv_rx(float *input, int in_len, float *output, int out_len) { +int rm_conv_rx(float *input, uint32_t in_len, float *output, uint32_t out_len) { int nrows, ndummy, K_p; int i, j, k; @@ -97,7 +99,7 @@ int rm_conv_rx(float *input, int in_len, float *output, int out_len) { float tmp[3 * NCOLS * NROWS_MAX]; - nrows = (int) (out_len / 3 - 1) / NCOLS + 1; + nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1; if (nrows > NROWS_MAX) { fprintf(stderr, "Output too large. Max output length is %d\n", 3 * NCOLS * NROWS_MAX); diff --git a/lte/phy/lib/fec/src/rm_turbo.c b/lte/phy/lib/fec/src/rm_turbo.c index f678a7685..ae52d9593 100644 --- a/lte/phy/lib/fec/src/rm_turbo.c +++ b/lte/phy/lib/fec/src/rm_turbo.c @@ -30,50 +30,43 @@ #include #include #include +#include #include "liblte/phy/fec/rm_turbo.h" #define NCOLS 32 #define NROWS_MAX NCOLS -unsigned char RM_PERM_TC[NCOLS] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, +uint8_t RM_PERM_TC[NCOLS] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 }; -int rm_turbo_init(rm_turbo_t *q, int buffer_len) { - q->buffer_len = buffer_len; - q->buffer = malloc(buffer_len * sizeof(float)); - if (!q->buffer) { - perror("malloc"); - return -1; - } - return 0; -} - -void rm_turbo_free(rm_turbo_t *q) { - if (q->buffer) { - free(q->buffer); - } -} /* Turbo Code Rate Matching. * 3GPP TS 36.212 v10.1.0 section 5.1.4.1 * + * If rv_idx==0, the circular buffer w_buff is filled with all redundancy versions and + * the corresponding version of length out_len is saved in the output buffer. + * Otherwise, the corresponding version is directly obtained from w_buff and saved into output. + * + * Note that calling this function with rv_idx!=0 without having called it first with rv_idx=0 + * will produce unwanted results. + * * TODO: Soft buffer size limitation according to UE category */ -int rm_turbo_tx(rm_turbo_t *q, char *input, int in_len, char *output, - int out_len, int rv_idx) { +int rm_turbo_tx(char *w_buff, uint32_t w_buff_len, char *input, uint32_t in_len, char *output, + uint32_t out_len, uint32_t rv_idx) { - char *tmp = (char*) q->buffer; - int nrows, ndummy, K_p; + int ndummy, kidx; + int nrows, K_p; - int i, j, k, s, kidx, N_cb, k0; + int i, j, k, s, N_cb, k0; - nrows = (int) (in_len / 3 - 1) / NCOLS + 1; + nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1; K_p = nrows * NCOLS; - if (3 * K_p > q->buffer_len) { + if (3 * K_p > w_buff_len) { fprintf(stderr, "Input too large. Max input length including dummy bits is %d (3x%dx32, in_len %d)\n", - q->buffer_len, nrows, in_len); + w_buff_len, nrows, in_len); return -1; } @@ -82,47 +75,49 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, int in_len, char *output, ndummy = 0; } - /* Sub-block interleaver (5.1.4.1.1) and bit collection */ - k = 0; - for (s = 0; s < 2; s++) { - for (j = 0; j < NCOLS; j++) { - for (i = 0; i < nrows; i++) { - if (s == 0) { - kidx = k % K_p; - } else { - kidx = K_p + 2 * (k % K_p); - } - if (i * NCOLS + RM_PERM_TC[j] < ndummy) { - tmp[kidx] = TX_NULL; - } else { - tmp[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s]; + if (rv_idx == 0) { + /* Sub-block interleaver (5.1.4.1.1) and bit collection */ + k = 0; + for (s = 0; s < 2; s++) { + for (j = 0; j < NCOLS; j++) { + for (i = 0; i < nrows; i++) { + if (s == 0) { + kidx = k % K_p; + } else { + kidx = K_p + 2 * (k % K_p); + } + if (i * NCOLS + RM_PERM_TC[j] < ndummy) { + w_buff[kidx] = TX_NULL; + } else { + w_buff[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s]; + } + k++; } - k++; } } - } - // d_k^(2) goes through special permutation - for (k = 0; k < K_p; k++) { - kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p; - if ((kidx - ndummy) < 0) { - tmp[K_p + 2 * k + 1] = TX_NULL; - } else { - tmp[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2]; + // d_k^(2) goes through special permutation + for (k = 0; k < K_p; k++) { + kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p; + if ((kidx - ndummy) < 0) { + w_buff[K_p + 2 * k + 1] = TX_NULL; + } else { + w_buff[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2]; + } } } /* Bit selection and transmission 5.1.4.1.2 */ - N_cb = 3 * K_p; // TODO: Soft buffer size limitation + N_cb = 3 * K_p; // TODO: Soft buffer size limitation k0 = nrows - * (2 * (int) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); + * (2 * (uint32_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); k = 0; j = 0; while (k < out_len) { - if (tmp[(k0 + j) % N_cb] != TX_NULL) { - output[k] = tmp[(k0 + j) % N_cb]; + if (w_buff[(k0 + j) % N_cb] != TX_NULL) { + output[k] = w_buff[(k0 + j) % N_cb]; k++; } j++; @@ -132,23 +127,24 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, int in_len, char *output, /* Undoes Turbo Code Rate Matching. * 3GPP TS 36.212 v10.1.0 section 5.1.4.1 + * + * If rv_idx==0, the w_buff circular buffer is initialized. Every subsequent call + * with rv_idx!=0 will soft-combine the LLRs from input with w_buff */ -int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, - int out_len, int rv_idx) { +int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_len, float *output, + uint32_t out_len, uint32_t rv_idx) { int nrows, ndummy, K_p, k0, N_cb, jp, kidx; int i, j, k; int d_i, d_j; bool isdummy; - float *tmp = (float*) q->buffer; - - nrows = (int) (out_len / 3 - 1) / NCOLS + 1; + nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1; K_p = nrows * NCOLS; - if (3 * K_p > q->buffer_len) { + if (3 * K_p > w_buff_len) { fprintf(stderr, "Input too large. Max output length including dummy bits is %d (3x%dx32, in_len %d)\n", - q->buffer_len, nrows, out_len); + w_buff_len, nrows, out_len); return -1; } @@ -157,14 +153,16 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, ndummy = 0; } - for (i = 0; i < 3 * K_p; i++) { - tmp[i] = RX_NULL; + if (rv_idx == 0) { + for (i = 0; i < 3 * K_p; i++) { + w_buff[i] = RX_NULL; + } } /* Undo bit collection. Account for dummy bits */ - N_cb = 3 * K_p; // TODO: Soft buffer size limitation + N_cb = 3 * K_p; // TODO: Soft buffer size limitation k0 = nrows - * (2 * (int) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); + * (2 * (uint32_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); k = 0; j = 0; @@ -185,7 +183,7 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, isdummy = true; } } else { - int jpp = (jp - K_p - 1) / 2; + uint32_t jpp = (jp - K_p - 1) / 2; kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; if ((kidx - ndummy) < 0) { isdummy = true; @@ -195,10 +193,10 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, } if (!isdummy) { - if (tmp[jp] == RX_NULL) { - tmp[jp] = input[k]; + if (w_buff[jp] == RX_NULL) { + w_buff[jp] = input[k]; } else if (input[k] != RX_NULL) { - tmp[jp] += input[k]; /* soft combine LLRs */ + w_buff[jp] += input[k]; /* soft combine LLRs */ } k++; } @@ -213,15 +211,14 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, if (j != 2) { kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); } else { - k = (i + ndummy - 1) % K_p; if (k < 0) k += K_p; kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; kidx = 2 * kidx + K_p + 1; } - if (tmp[kidx] != RX_NULL) { - output[i * 3 + j] = tmp[kidx]; + if (w_buff[kidx] != RX_NULL) { + output[i * 3 + j] = w_buff[kidx]; } else { output[i * 3 + j] = 0; } @@ -233,25 +230,15 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, int in_len, float *output, /** High-level API */ int rm_turbo_initialize(rm_turbo_hl* h) { - return rm_turbo_init(&h->q, 7000); + return 0; } /** This function can be called in a subframe (1ms) basis */ int rm_turbo_work(rm_turbo_hl* hl) { - if (hl->init.direction) { - rm_turbo_tx(&hl->q, hl->input, hl->in_len, hl->output, hl->ctrl_in.E, - hl->ctrl_in.rv_idx); - hl->out_len = hl->ctrl_in.E; - } else { - rm_turbo_rx(&hl->q, hl->input, hl->in_len, hl->output, hl->ctrl_in.S, - hl->ctrl_in.rv_idx); - hl->out_len = hl->ctrl_in.S; - } return 0; } int rm_turbo_stop(rm_turbo_hl* hl) { - rm_turbo_free(&hl->q); return 0; } diff --git a/lte/phy/lib/fec/src/tc_interl_lte.c b/lte/phy/lib/fec/src/tc_interl_lte.c index 227d4d9f9..b77c04779 100644 --- a/lte/phy/lib/fec/src/tc_interl_lte.c +++ b/lte/phy/lib/fec/src/tc_interl_lte.c @@ -27,6 +27,7 @@ #include #include +#include #include "liblte/phy/common/phy_common.h" #include "liblte/phy/fec/tc_interl.h" @@ -39,7 +40,7 @@ * ************************************************/ -const int f1_list[NOF_TC_CB_SIZES] = { 3, 7, 19, 7, 7, 11, 5, 11, 7, 41, 103, +const uint32_t f1_list[NOF_TC_CB_SIZES] = { 3, 7, 19, 7, 7, 11, 5, 11, 7, 41, 103, 15, 9, 17, 9, 21, 101, 21, 57, 23, 13, 27, 11, 27, 85, 29, 33, 15, 17, 33, 103, 19, 19, 37, 19, 21, 21, 115, 193, 21, 133, 81, 45, 23, 243, 151, 155, 25, 51, 47, 91, 29, 29, 247, 29, 89, 91, 157, 55, 31, 17, 35, 227, 65, 19, @@ -52,7 +53,7 @@ const int f1_list[NOF_TC_CB_SIZES] = { 3, 7, 19, 7, 7, 11, 5, 11, 7, 41, 103, 39, 127, 39, 39, 31, 113, 41, 251, 43, 21, 43, 45, 45, 161, 89, 323, 47, 23, 47, 263 }; -const int f2_list[NOF_TC_CB_SIZES] = { 10, 12, 42, 16, 18, 20, 22, 24, 26, 84, +const uint32_t f2_list[NOF_TC_CB_SIZES] = { 10, 12, 42, 16, 18, 20, 22, 24, 26, 84, 90, 32, 34, 108, 38, 120, 84, 44, 46, 48, 50, 52, 36, 56, 58, 60, 62, 32, 198, 68, 210, 36, 74, 76, 78, 120, 82, 84, 86, 44, 90, 46, 94, 48, 98, 40, 102, 52, 106, 72, 110, 168, 114, 58, 118, 180, 122, 62, 84, 64, 66, 68, 420, @@ -65,9 +66,9 @@ const int f2_list[NOF_TC_CB_SIZES] = { 10, 12, 42, 16, 18, 20, 22, 24, 26, 84, 280, 142, 480, 146, 444, 120, 152, 462, 234, 158, 80, 96, 902, 166, 336, 170, 86, 174, 176, 178, 120, 182, 184, 186, 94, 190, 480 }; -int tc_interl_LTE_gen(tc_interl_t *h, int long_cb) { - int cb_table_idx, f1, f2; - unsigned long long i, j; +int tc_interl_LTE_gen(tc_interl_t *h, uint32_t long_cb) { + uint32_t cb_table_idx, f1, f2; + uint64_t i, j; if (long_cb > h->max_long_cb) { fprintf(stderr, "Interleaver initiated for max_long_cb=%d\n", @@ -90,8 +91,8 @@ int tc_interl_LTE_gen(tc_interl_t *h, int long_cb) { h->reverse[0] = 0; for (i = 1; i < long_cb; i++) { j = (f1 * i + f2 * i * i) % (long_cb); - h->forward[i] = j; - h->reverse[j] = i; + h->forward[i] = (uint32_t) j; + h->reverse[j] = (uint32_t) i; } return 0; diff --git a/lte/phy/lib/fec/src/tc_interl_umts.c b/lte/phy/lib/fec/src/tc_interl_umts.c index 5c2e3213d..931749347 100644 --- a/lte/phy/lib/fec/src/tc_interl_umts.c +++ b/lte/phy/lib/fec/src/tc_interl_umts.c @@ -28,13 +28,14 @@ #include #include #include +#include #include "liblte/phy/fec/tc_interl.h" #include "liblte/phy/fec/turbocoder.h" #define TURBO_RATE 3 -int mcd(int x, int y); +uint32_t mcd(uint32_t x, uint32_t y); /************************************************ * @@ -53,14 +54,14 @@ const unsigned char table_v[52] = { 3, 2, 2, 3, 2, 5, 2, 3, 2, 6, 3, 5, 2, 2, 2, 2, 7, 5, 3, 2, 3, 5, 2, 5, 2, 6, 3, 3, 2, 3, 2, 2, 6, 5, 2, 5, 2, 2, 2, 19, 5, 2, 3, 2, 3, 2, 6, 3, 7, 7, 6, 3 }; -int tc_interl_init(tc_interl_t *h, int max_long_cb) { +int tc_interl_init(tc_interl_t *h, uint32_t max_long_cb) { int ret = -1; - h->forward = malloc(sizeof(int) * max_long_cb); + h->forward = malloc(sizeof(uint32_t) * max_long_cb); if (!h->forward) { perror("malloc"); goto clean_exit; } - h->reverse = malloc(sizeof(int) * max_long_cb); + h->reverse = malloc(sizeof(uint32_t) * max_long_cb); if (!h->reverse) { perror("malloc"); goto clean_exit; @@ -83,17 +84,17 @@ void tc_interl_free(tc_interl_t *h) { bzero(h, sizeof(tc_interl_t)); } -int tc_interl_UMTS_gen(tc_interl_t *h, int long_cb) { +int tc_interl_UMTS_gen(tc_interl_t *h, uint32_t long_cb) { - int i, j; - int res, prim, aux; - int kp, k; - int *per, *desper; - unsigned char v; - unsigned short p; - unsigned short s[MAX_COLS], q[MAX_ROWS], r[MAX_ROWS], T[MAX_ROWS]; - unsigned short U[MAX_COLS * MAX_ROWS]; - int M_Rows, M_Cols, M_long; + uint32_t i, j; + uint32_t res, prim, aux; + uint32_t kp, k; + uint32_t *per, *desper; + uint8_t v; + uint16_t p; + uint16_t s[MAX_COLS], q[MAX_ROWS], r[MAX_ROWS], T[MAX_ROWS]; + uint16_t U[MAX_COLS * MAX_ROWS]; + uint32_t M_Rows, M_Cols, M_long; M_long = long_cb; @@ -260,8 +261,8 @@ int tc_interl_UMTS_gen(tc_interl_t *h, int long_cb) { } -int mcd(int x, int y) { - int r = 1; +uint32_t mcd(uint32_t x, uint32_t y) { + uint32_t r = 1; while (r) { r = x % y; diff --git a/lte/phy/lib/fec/src/turbocoder.c b/lte/phy/lib/fec/src/turbocoder.c index 75aebb6e9..7483601d9 100644 --- a/lte/phy/lib/fec/src/turbocoder.c +++ b/lte/phy/lib/fec/src/turbocoder.c @@ -26,12 +26,14 @@ */ -#include "liblte/phy/fec/turbocoder.h" #include +#include + +#include "liblte/phy/fec/turbocoder.h" #define NOF_REGS 3 -int tcod_init(tcod_t *h, int max_long_cb) { +int tcod_init(tcod_t *h, uint32_t max_long_cb) { if (tc_interl_init(&h->interl, max_long_cb)) { return -1; @@ -45,13 +47,13 @@ void tcod_free(tcod_t *h) { h->max_long_cb = 0; } -int tcod_encode(tcod_t *h, char *input, char *output, int long_cb) { +int tcod_encode(tcod_t *h, char *input, char *output, uint32_t long_cb) { char reg1_0, reg1_1, reg1_2, reg2_0, reg2_1, reg2_2; - int i, k = 0, j; + uint32_t i, k = 0, j; char bit; char in, out; - int *per; + uint32_t *per; if (long_cb > h->max_long_cb) { fprintf(stderr, "Turbo coder initiated for max_long_cb=%d\n", diff --git a/lte/phy/lib/fec/src/turbodecoder.c b/lte/phy/lib/fec/src/turbodecoder.c index 2fbb61787..171eefde3 100644 --- a/lte/phy/lib/fec/src/turbodecoder.c +++ b/lte/phy/lib/fec/src/turbodecoder.c @@ -27,8 +27,10 @@ #include #include +#include #include #include +#include #include "liblte/phy/fec/turbodecoder.h" @@ -38,13 +40,15 @@ * Decoder * ************************************************/ -void map_gen_beta(map_gen_t *s, llr_t *input, llr_t *parity, int long_cb) { +void map_gen_beta(map_gen_t * s, llr_t * input, llr_t * parity, + uint32_t long_cb) +{ llr_t m_b[8], new[8], old[8]; llr_t x, y, xy; int k; - int end = long_cb + RATE; + uint32_t end = long_cb + RATE; llr_t *beta = s->beta; - int i; + uint32_t i; for (i = 0; i < 8; i++) { old[i] = beta[8 * (end) + i]; @@ -83,16 +87,17 @@ void map_gen_beta(map_gen_t *s, llr_t *input, llr_t *parity, int long_cb) { } } -void map_gen_alpha(map_gen_t *s, llr_t *input, llr_t *parity, llr_t *output, - int long_cb) { +void map_gen_alpha(map_gen_t * s, llr_t * input, llr_t * parity, llr_t * output, + uint32_t long_cb) +{ llr_t m_b[8], new[8], old[8], max1[8], max0[8]; llr_t m1, m0; llr_t x, y, xy; llr_t out; - int k; - int end = long_cb; + uint32_t k; + uint32_t end = long_cb; llr_t *beta = s->beta; - int i; + uint32_t i; old[0] = 0; for (i = 1; i < 8; i++) { @@ -149,7 +154,8 @@ void map_gen_alpha(map_gen_t *s, llr_t *input, llr_t *parity, llr_t *output, } } -int map_gen_init(map_gen_t *h, int max_long_cb) { +int map_gen_init(map_gen_t * h, int max_long_cb) +{ bzero(h, sizeof(map_gen_t)); h->beta = malloc(sizeof(llr_t) * (max_long_cb + TOTALTAIL + 1) * NUMSTATES); if (!h->beta) { @@ -160,16 +166,18 @@ int map_gen_init(map_gen_t *h, int max_long_cb) { return 0; } -void map_gen_free(map_gen_t *h) { +void map_gen_free(map_gen_t * h) +{ if (h->beta) { free(h->beta); } bzero(h, sizeof(map_gen_t)); } -void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output, - int long_cb) { - int k; +void map_gen_dec(map_gen_t * h, llr_t * input, llr_t * parity, llr_t * output, + uint32_t long_cb) +{ + uint32_t k; h->beta[(long_cb + TAIL) * NUMSTATES] = 0; for (k = 1; k < NUMSTATES; k++) @@ -184,10 +192,11 @@ void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output, * TURBO DECODER INTERFACE * ************************************************/ -int tdec_init(tdec_t *h, int max_long_cb) { +int tdec_init(tdec_t * h, uint32_t max_long_cb) +{ int ret = -1; bzero(h, sizeof(tdec_t)); - int len = max_long_cb + TOTALTAIL; + uint32_t len = max_long_cb + TOTALTAIL; h->max_long_cb = max_long_cb; @@ -226,13 +235,14 @@ int tdec_init(tdec_t *h, int max_long_cb) { } ret = 0; - clean_and_exit: if (ret == -1) { +clean_and_exit:if (ret == -1) { tdec_free(h); } return ret; } -void tdec_free(tdec_t *h) { +void tdec_free(tdec_t * h) +{ if (h->llr1) { free(h->llr1); } @@ -256,8 +266,9 @@ void tdec_free(tdec_t *h) { bzero(h, sizeof(tdec_t)); } -void tdec_iteration(tdec_t *h, llr_t *input, int long_cb) { - int i; +void tdec_iteration(tdec_t * h, llr_t * input, uint32_t long_cb) +{ + uint32_t i; // Prepare systematic and parity bits for MAP DEC #1 for (i = 0; i < long_cb; i++) { @@ -275,19 +286,19 @@ void tdec_iteration(tdec_t *h, llr_t *input, int long_cb) { // Prepare systematic and parity bits for MAP DEC #1 for (i = 0; i < long_cb; i++) { h->syst[i] = h->llr1[h->interleaver.forward[i]] - - h->w[h->interleaver.forward[i]]; + - h->w[h->interleaver.forward[i]]; h->parity[i] = input[RATE * i + 2]; } for (i = long_cb; i < long_cb + RATE; i++) { h->syst[i] = - input[RATE * long_cb + NINPUTS * RATE + NINPUTS * (i - long_cb)]; + input[RATE * long_cb + NINPUTS * RATE + NINPUTS * (i - long_cb)]; h->parity[i] = input[RATE * long_cb + NINPUTS * RATE - + NINPUTS * (i - long_cb) + 1]; + + NINPUTS * (i - long_cb) + 1]; } // Run MAP DEC #1 map_gen_dec(&h->dec, h->syst, h->parity, h->llr2, long_cb); - + // Update a-priori LLR from the last iteration for (i = 0; i < long_cb; i++) { h->w[i] += h->llr2[h->interleaver.reverse[i]] - h->llr1[i]; @@ -295,26 +306,29 @@ void tdec_iteration(tdec_t *h, llr_t *input, int long_cb) { } -int tdec_reset(tdec_t *h, int long_cb) { - memset(h->w, 0, sizeof(llr_t) * long_cb); +int tdec_reset(tdec_t * h, uint32_t long_cb) +{ if (long_cb > h->max_long_cb) { fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n", - h->max_long_cb); + h->max_long_cb); return -1; } + memset(h->w, 0, sizeof(llr_t) * long_cb); return tc_interl_LTE_gen(&h->interleaver, long_cb); } -void tdec_decision(tdec_t *h, char *output, int long_cb) { - int i; +void tdec_decision(tdec_t * h, char *output, uint32_t long_cb) +{ + uint32_t i; for (i = 0; i < long_cb; i++) { - output[i] = (h->llr2[h->interleaver.reverse[i]] > 0) ? 1 : 0; + output[i] = (h->llr2[h->interleaver.reverse[i]] > 0) ? 1 : 0; } } -void tdec_run_all(tdec_t *h, llr_t *input, char *output, int nof_iterations, - int long_cb) { - int iter = 0; +void tdec_run_all(tdec_t * h, llr_t * input, char *output, + uint32_t nof_iterations, uint32_t long_cb) +{ + uint32_t iter = 0; tdec_reset(h, long_cb); @@ -325,4 +339,3 @@ void tdec_run_all(tdec_t *h, llr_t *input, char *output, int nof_iterations, tdec_decision(h, output, long_cb); } - diff --git a/lte/phy/lib/fec/src/viterbi.c b/lte/phy/lib/fec/src/viterbi.c index 4b79ea983..b9fb2b0ec 100644 --- a/lte/phy/lib/fec/src/viterbi.c +++ b/lte/phy/lib/fec/src/viterbi.c @@ -27,6 +27,8 @@ #include #include +#include + #include #include @@ -38,11 +40,11 @@ #define DEB 0 -int decode37(void *o, unsigned char *symbols, char *data, int frame_length) { +int decode37(void *o, uint8_t *symbols, char *data, uint32_t frame_length) { viterbi_t *q = o; - int i; + uint32_t i; - int best_state; + uint32_t best_state; if (frame_length > q->framebits) { fprintf(stderr, "Initialized decoder for max frame length %d bits\n", @@ -73,7 +75,7 @@ int decode37(void *o, unsigned char *symbols, char *data, int frame_length) { return q->framebits; } -int decode39(void *o, unsigned char *symbols, char *data, int frame_length) { +int decode39(void *o, uint8_t *symbols, char *data, uint32_t frame_length) { viterbi_t *q = o; if (frame_length > q->framebits) { @@ -113,7 +115,7 @@ void free39(void *o) { delete_viterbi39_port(q->ptr); } -int init37(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { +int init37(viterbi_t *q, uint32_t poly[3], uint32_t framebits, bool tail_biting) { q->K = 7; q->R = 3; q->framebits = framebits; @@ -145,7 +147,7 @@ int init37(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { } } -int init39(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { +int init39(viterbi_t *q, uint32_t poly[3], uint32_t framebits, bool tail_biting) { q->K = 9; q->R = 3; q->framebits = framebits; @@ -171,8 +173,8 @@ int init39(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { } } -int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], - int max_frame_length, bool tail_bitting) { +int viterbi_init(viterbi_t *q, viterbi_type_t type, uint32_t poly[3], + uint32_t max_frame_length, bool tail_bitting) { switch (type) { case viterbi_37: return init37(q, poly, max_frame_length, tail_bitting); @@ -185,12 +187,14 @@ int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], } void viterbi_free(viterbi_t *q) { - q->free(q); + if (q->free) { + q->free(q); + } } /* symbols are real-valued */ -int viterbi_decode_f(viterbi_t *q, float *symbols, char *data, int frame_length) { - int len; +int viterbi_decode_f(viterbi_t *q, float *symbols, char *data, uint32_t frame_length) { + uint32_t len; if (frame_length > q->framebits) { fprintf(stderr, "Initialized decoder for max frame length %d bits\n", q->framebits); @@ -205,13 +209,13 @@ int viterbi_decode_f(viterbi_t *q, float *symbols, char *data, int frame_length) return q->decode(q, q->symbols_uc, data, frame_length); } -int viterbi_decode_uc(viterbi_t *q, unsigned char *symbols, char *data, - int frame_length) { +int viterbi_decode_uc(viterbi_t *q, uint8_t *symbols, char *data, + uint32_t frame_length) { return q->decode(q, symbols, data, frame_length); } int viterbi_initialize(viterbi_hl* h) { - int poly[3]; + uint32_t poly[3]; viterbi_type_t type; if (h->init.rate == 2) { if (h->init.constraint_length == 7) { @@ -241,7 +245,7 @@ int viterbi_initialize(viterbi_hl* h) { poly[0] = h->init.generator_0; poly[1] = h->init.generator_1; poly[2] = h->init.generator_2; - return viterbi_init(&h->obj, type, poly, h->init.frame_length, + return viterbi_init(&h->obj, type, poly, (uint32_t) h->init.frame_length, h->init.tail_bitting ? true : false); } diff --git a/lte/phy/lib/fec/src/viterbi37.h b/lte/phy/lib/fec/src/viterbi37.h index 6afc563e8..7bacca2c8 100644 --- a/lte/phy/lib/fec/src/viterbi37.h +++ b/lte/phy/lib/fec/src/viterbi37.h @@ -27,8 +27,20 @@ #include -void *create_viterbi37_port(int polys[3], int len); -int init_viterbi37_port(void *p, int starting_state); -int chainback_viterbi37_port(void *p, char *data, unsigned int nbits, unsigned int endstate); +void *create_viterbi37_port(uint32_t polys[3], + uint32_t len); + +int init_viterbi37_port(void *p, + uint32_t starting_state); + +int chainback_viterbi37_port(void *p, + char *data, + uint32_t nbits, + uint32_t endstate); + void delete_viterbi37_port(void *p); -int update_viterbi37_blk_port(void *p, unsigned char *syms, int nbits, int *best_state); + +int update_viterbi37_blk_port(void *p, + unsigned char *syms, + uint32_t nbits, + uint32_t *best_state); diff --git a/lte/phy/lib/fec/src/viterbi37_port.c b/lte/phy/lib/fec/src/viterbi37_port.c index 7eb311324..09dfa4bdc 100644 --- a/lte/phy/lib/fec/src/viterbi37_port.c +++ b/lte/phy/lib/fec/src/viterbi37_port.c @@ -4,6 +4,8 @@ */ #include #include +#include + #include #include "viterbi37.h" #include "parity.h" @@ -30,9 +32,9 @@ struct v37 { }; /* Initialize Viterbi decoder for start of new frame */ -int init_viterbi37_port(void *p, int starting_state) { +int init_viterbi37_port(void *p, uint32_t starting_state) { struct v37 *vp = p; - int i; + uint32_t i; if (p == NULL) return -1; @@ -48,8 +50,8 @@ int init_viterbi37_port(void *p, int starting_state) { return 0; } -void set_viterbi37_polynomial_port(int polys[3]) { - int state; +void set_viterbi37_polynomial_port(uint32_t polys[3]) { + uint32_t state; for (state = 0; state < 32; state++) { Branchtab37[0].c[state] = @@ -62,7 +64,7 @@ void set_viterbi37_polynomial_port(int polys[3]) { } /* Create a new instance of a Viterbi decoder */ -void *create_viterbi37_port(int polys[3], int len) { +void *create_viterbi37_port(uint32_t polys[3], uint32_t len) { struct v37 *vp; set_viterbi37_polynomial_port(polys); @@ -82,8 +84,8 @@ void *create_viterbi37_port(int polys[3], int len) { /* Viterbi chainback */ int chainback_viterbi37_port(void *p, char *data, /* Decoded output data */ - unsigned int nbits, /* Number of data bits */ - unsigned int endstate) { /* Terminal encoder state */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ struct v37 *vp = p; decision_t *d; @@ -145,18 +147,18 @@ unsigned int metric,m0,m1,decision;\ * of symbols! */ -int update_viterbi37_blk_port(void *p, unsigned char *syms, int nbits, int *best_state) { +int update_viterbi37_blk_port(void *p, uint8_t *syms, uint32_t nbits, uint32_t *best_state) { struct v37 *vp = p; decision_t *d; if (p == NULL) return -1; - int k=0; + uint32_t k=0; d = (decision_t *) vp->dp; while (nbits--) { void *tmp; - unsigned char sym0, sym1, sym2; - int i; + uint8_t sym0, sym1, sym2; + uint32_t i; d->w[0] = d->w[1] = 0; @@ -174,8 +176,8 @@ int update_viterbi37_blk_port(void *p, unsigned char *syms, int nbits, int *best vp->new_metrics = tmp; } if (best_state) { - int i, bst=0; - unsigned int minmetric=UINT_MAX; + uint32_t i, bst=0; + uint32_t minmetric=UINT_MAX; for (i=0;i<64;i++) { if (vp->old_metrics->w[i] < minmetric) { bst = i; diff --git a/lte/phy/lib/fec/src/viterbi39.h b/lte/phy/lib/fec/src/viterbi39.h index a5830d145..09a3280cf 100644 --- a/lte/phy/lib/fec/src/viterbi39.h +++ b/lte/phy/lib/fec/src/viterbi39.h @@ -27,10 +27,19 @@ #include -void *create_viterbi39_port(int polys[3], int len); -int init_viterbi39_port(void *p, int starting_state); -int chainback_viterbi39_port(void *p, char *data, /* Decoded output data */ - unsigned int nbits, /* Number of data bits */ - unsigned int endstate); +void *create_viterbi39_port(uint32_t polys[3], + uint32_t len); + +int init_viterbi39_port(void *p, + uint32_t starting_state); + +int chainback_viterbi39_port(void *p, + char *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate); + void delete_viterbi39_port(void *p); -int update_viterbi39_blk_port(void *p, unsigned char *syms, int nbits); + +int update_viterbi39_blk_port(void *p, + uint8_t *syms, + uint32_t nbits); diff --git a/lte/phy/lib/fec/src/viterbi39_port.c b/lte/phy/lib/fec/src/viterbi39_port.c index fe57297c7..1d87b2c4a 100644 --- a/lte/phy/lib/fec/src/viterbi39_port.c +++ b/lte/phy/lib/fec/src/viterbi39_port.c @@ -4,6 +4,7 @@ */ #include #include +#include #include #include "viterbi39.h" #include "parity.h" @@ -29,9 +30,9 @@ struct v39 { }; /* Initialize Viterbi decoder for start of new frame */ -int init_viterbi39_port(void *p, int starting_state) { +int init_viterbi39_port(void *p, uint32_t starting_state) { struct v39 *vp = p; - int i; + uint32_t i; if (p == NULL) return -1; @@ -45,8 +46,8 @@ int init_viterbi39_port(void *p, int starting_state) { return 0; } -void set_viterbi39_polynomial_port(int polys[3]) { - int state; +void set_viterbi39_polynomial_port(uint32_t polys[3]) { + uint32_t state; for (state = 0; state < 128; state++) { Branchtab39[0].c[state] = @@ -59,7 +60,7 @@ void set_viterbi39_polynomial_port(int polys[3]) { } /* Create a new instance of a Viterbi decoder */ -void *create_viterbi39_port(int polys[3], int len) { +void *create_viterbi39_port(uint32_t polys[3], uint32_t len) { struct v39 *vp; set_viterbi39_polynomial_port(polys); @@ -79,8 +80,8 @@ void *create_viterbi39_port(int polys[3], int len) { /* Viterbi chainback */ int chainback_viterbi39_port(void *p, char *data, /* Decoded output data */ - unsigned int nbits, /* Number of data bits */ - unsigned int endstate) { /* Terminal encoder state */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ struct v39 *vp = p; decision_t *d; @@ -140,7 +141,7 @@ unsigned int metric,m0,m1,decision;\ * of symbols! */ -int update_viterbi39_blk_port(void *p, unsigned char *syms, int nbits) { +int update_viterbi39_blk_port(void *p, uint8_t *syms, uint32_t nbits) { struct v39 *vp = p; decision_t *d; @@ -150,8 +151,8 @@ int update_viterbi39_blk_port(void *p, unsigned char *syms, int nbits) { d = (decision_t *) vp->dp; while (nbits--) { void *tmp; - unsigned char sym0, sym1, sym2; - int i; + uint8_t sym0, sym1, sym2; + uint32_t i; for (i = 0; i < 8; i++) d->w[i] = 0; diff --git a/lte/phy/lib/fec/test/rm_turbo_test.c b/lte/phy/lib/fec/test/rm_turbo_test.c index aa42ca832..42db72696 100644 --- a/lte/phy/lib/fec/test/rm_turbo_test.c +++ b/lte/phy/lib/fec/test/rm_turbo_test.c @@ -73,10 +73,9 @@ void parse_args(int argc, char **argv) { int main(int argc, char **argv) { int i; - char *bits, *rm_bits; - float *rm_symbols, *unrm_symbols; + char *bits, *rm_bits, *w_buff_c; + float *rm_symbols, *unrm_symbols, *w_buff_f; int nof_errors; - rm_turbo_t rm_turbo; parse_args(argc, argv); @@ -85,6 +84,11 @@ int main(int argc, char **argv) { perror("malloc"); exit(-1); } + w_buff_c = malloc(sizeof(char) * nof_tx_bits * 10); + if (!w_buff_c) { + perror("malloc"); + exit(-1); + } rm_bits = malloc(sizeof(char) * nof_rx_bits); if (!rm_bits) { perror("malloc"); @@ -95,6 +99,11 @@ int main(int argc, char **argv) { perror("malloc"); exit(-1); } + w_buff_f = malloc(sizeof(float) * nof_rx_bits * 10); + if (!w_buff_c) { + perror("malloc"); + exit(-1); + } unrm_symbols = malloc(sizeof(float) * nof_tx_bits); if (!unrm_symbols) { perror("malloc"); @@ -105,15 +114,13 @@ int main(int argc, char **argv) { bits[i] = rand() % 2; } - rm_turbo_init(&rm_turbo, 2000); - - rm_turbo_tx(&rm_turbo, bits, nof_tx_bits, rm_bits, nof_rx_bits, rv_idx); + rm_turbo_tx(w_buff_c, nof_tx_bits * 10, bits, nof_tx_bits, rm_bits, nof_rx_bits, rv_idx); for (i = 0; i < nof_rx_bits; i++) { rm_symbols[i] = (float) rm_bits[i] ? 1 : -1; } - rm_turbo_rx(&rm_turbo, rm_symbols, nof_rx_bits, unrm_symbols, nof_tx_bits, + rm_turbo_rx(w_buff_f, nof_rx_bits * 10, rm_symbols, nof_rx_bits, unrm_symbols, nof_tx_bits, rv_idx); nof_errors = 0; @@ -123,8 +130,6 @@ int main(int argc, char **argv) { } } - rm_turbo_free(&rm_turbo); - free(bits); free(rm_bits); free(rm_symbols); diff --git a/lte/phy/lib/fec/test/turbocoder_test.c b/lte/phy/lib/fec/test/turbocoder_test.c index a222b9750..fb0fabc85 100644 --- a/lte/phy/lib/fec/test/turbocoder_test.c +++ b/lte/phy/lib/fec/test/turbocoder_test.c @@ -41,9 +41,9 @@ typedef _Complex float cf_t; -int frame_length = 1000, nof_frames = 100; +uint32_t frame_length = 1000, nof_frames = 100; float ebno_db = 100.0; -unsigned int seed = 0; +uint32_t seed = 0; int K = -1; #define MAX_ITERATIONS 4 @@ -51,9 +51,9 @@ int nof_iterations = MAX_ITERATIONS; int test_known_data = 0; int test_errors = 0; -#define SNR_POINTS 8 -#define SNR_MIN 0.0 -#define SNR_MAX 4.0 +#define SNR_POINTS 8 +#define SNR_MIN 0.0 +#define SNR_MAX 4.0 void usage(char *prog) { printf("Usage: %s [nlesv]\n", prog); @@ -127,16 +127,16 @@ void output_matlab(float ber[MAX_ITERATIONS][SNR_POINTS], int snr_points) { } int main(int argc, char **argv) { - int frame_cnt; + uint32_t frame_cnt; float *llr; unsigned char *llr_c; char *data_tx, *data_rx, *symbols; - int i, j; + uint32_t i, j; float var[SNR_POINTS]; - int snr_points; + uint32_t snr_points; float ber[MAX_ITERATIONS][SNR_POINTS]; - unsigned int errors[100]; - int coded_length; + uint32_t errors[100]; + uint32_t coded_length; struct timeval tdata[3]; float mean_usec; tdec_t tdec; @@ -247,7 +247,7 @@ int main(int argc, char **argv) { /* decoder */ tdec_reset(&tdec, frame_length); - int t; + uint32_t t; if (nof_iterations == -1) { t = MAX_ITERATIONS; } else { diff --git a/lte/phy/lib/mimo/src/precoding.c b/lte/phy/lib/mimo/src/precoding.c index 036de038d..6c21ff21e 100644 --- a/lte/phy/lib/mimo/src/precoding.c +++ b/lte/phy/lib/mimo/src/precoding.c @@ -124,6 +124,11 @@ int precoding_type(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_layers, /* ZF detector */ int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols) { + for (int i=0;itable = table; +void demod_hard_table_set(demod_hard_t* q, lte_mod_t mod) { + q->mod = mod; } -int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols) { +int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, uint32_t nsymbols) { int nbits=-1; - switch(q->table) { + switch(q->mod) { case LTE_BPSK: hard_bpsk_demod(symbols,bits,nsymbols); nbits=nsymbols; diff --git a/lte/phy/lib/modem/src/demod_soft.c b/lte/phy/lib/modem/src/demod_soft.c index 2c874a1af..aa9884e71 100644 --- a/lte/phy/lib/modem/src/demod_soft.c +++ b/lte/phy/lib/modem/src/demod_soft.c @@ -36,6 +36,7 @@ void demod_soft_init(demod_soft_t *q) { bzero((void*)q,sizeof(demod_soft_t)); + q->sigma = 1.0; } void demod_soft_table_set(demod_soft_t *q, modem_table_t *table) { @@ -57,8 +58,10 @@ int demod_soft_demodulate(demod_soft_t *q, const cf_t* symbols, float* llr, int q->table->symbol_table, q->table->soft_table.idx, q->sigma); break; case APPROX: - llr_approx(symbols, llr, nsymbols, q->table->nsymbols, q->table->nbits_x_symbol, +/* llr_approx(symbols, llr, nsymbols, q->table->nsymbols, q->table->nbits_x_symbol, q->table->symbol_table, q->table->soft_table.idx, q->sigma); +*/ llr_approx(symbols, llr, nsymbols, q->table->nsymbols, q->table->nbits_x_symbol, + q->table->symbol_table, q->table->soft_table.idx, q->table->soft_table.d_idx, q->table->soft_table.min_idx, q->sigma); break; } return nsymbols*q->table->nbits_x_symbol; @@ -69,7 +72,7 @@ int demod_soft_demodulate(demod_soft_t *q, const cf_t* symbols, float* llr, int /* High-Level API */ int demod_soft_initialize(demod_soft_hl* hl) { modem_table_init(&hl->table); - if (modem_table_std(&hl->table,hl->init.std,true)) { + if (modem_table_lte(&hl->table,hl->init.std,true)) { return -1; } demod_soft_init(&hl->obj); diff --git a/lte/phy/lib/modem/src/hard_demod_lte.c b/lte/phy/lib/modem/src/hard_demod_lte.c index 761c72140..f841f3ac7 100644 --- a/lte/phy/lib/modem/src/hard_demod_lte.c +++ b/lte/phy/lib/modem/src/hard_demod_lte.c @@ -33,6 +33,7 @@ #include "liblte/phy/modem/demod_hard.h" #include "hard_demod_lte.h" + /** * @ingroup Hard BPSK demodulator * @@ -46,9 +47,9 @@ * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_bpsk_demod(const cf_t* in, char* out, int N) +inline void hard_bpsk_demod(const cf_t* in, char* out, uint32_t N) { - int s; + uint32_t s; for (s=0; s 0) { @@ -81,9 +82,9 @@ inline void hard_bpsk_demod(const cf_t* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qpsk_demod(const cf_t* in, char* out, int N) +inline void hard_qpsk_demod(const cf_t* in, char* out, uint32_t N) { - int s; + uint32_t s; for (s=0; s 0) { @@ -115,9 +116,9 @@ inline void hard_qpsk_demod(const cf_t* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qam16_demod(const cf_t* in, char* out, int N) +inline void hard_qam16_demod(const cf_t* in, char* out, uint32_t N) { - int s; + uint32_t s; for (s=0; s 0) { @@ -157,9 +158,9 @@ inline void hard_qam16_demod(const cf_t* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qam64_demod(const cf_t* in, char* out, int N) +inline void hard_qam64_demod(const cf_t* in, char* out, uint32_t N) { - int s; + uint32_t s; for (s=0; sidx[0][0][0] = 0; soft_table->idx[1][0][0] = 1; + + /* set two matrices for LLR approx. calculation */ + soft_table->min_idx[0][0][0] = 0; + soft_table->min_idx[0][1][0] = 0; + soft_table->min_idx[1][0][0] = 1; + soft_table->min_idx[1][1][0] = 1; + + soft_table->d_idx[0][0] = 0; + soft_table->d_idx[0][1] = 1; + soft_table->d_idx[1][0] = 0; + soft_table->d_idx[1][1] = 1; + } /** * Set the QPSK modulation table */ void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { - int i,j; + uint32_t i,j; // LTE-QPSK constellation: // Q @@ -91,13 +105,15 @@ void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demo soft_table->idx[1][0][1] = 3; soft_table->idx[1][1][0] = 1; soft_table->idx[1][1][1] = 3; + + LLR_approx_params(table, soft_table, 2); /* last param indicating B (bits per symbol) */ } /** * Set the 16QAM modulation table */ void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { - int i,j; + uint32_t i,j; // LTE-16QAM constellation: // Q // 1011 1001 | 0001 0011 @@ -156,13 +172,15 @@ void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_dem soft_table->idx[0][3][i] = 2*i; soft_table->idx[1][3][i] = 2*i+1; } + + LLR_approx_params(table, soft_table, 4); /* last param indication B (bits per symbol) */ } /** * Set the 64QAM modulation table */ void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { - int i,j; + uint32_t i,j; // LTE-64QAM constellation: // see [3GPP TS 36.211 version 10.5.0 Release 10, Section 7.1.4] table[0] = QAM64_LEVEL_2 + QAM64_LEVEL_2*_Complex_I; @@ -277,4 +295,96 @@ void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_dem soft_table->idx[0][5][i] = 2*i; soft_table->idx[1][5][i] = 2*i+1; } + + LLR_approx_params(table, soft_table, 6); /* last param indication modulation */ +} + +/* Precompute two tables for calculating the distances based on the received symbol location relative to the constellation points */ +void LLR_approx_params(const cf_t* table, soft_table_t *soft_table, int B) { + + int i, j, b, k; + float x, y, d0, d1, min_d0, min_d1; + int M, D; + uint32_t min_idx0[64][6], min_idx1[64][6]; + uint32_t count; + int flag; + + + D = B+1; /* number of different distances to be computed */ + //M = pow(2,B); /* number of constellation points */ + switch (B) { + case 1: {M = 2; break;} /* BPSK */ + case 2: {M = 4; break;} /* QPSK */ + case 4: {M = 16; break;} /* 16QAM */ + case 6: {M = 64; break;} /* 64QAM */ + default: {M = 4; break;} /* QPSK */ + } + + for (i=0;iidx[0][b][j]]; + y = __imag__ table[i] - __imag__ table[soft_table->idx[0][b][j]]; + d0 = x*x + y*y; + if (d0 < min_d0) { + min_d0 = d0; + min_idx0[i][b] = soft_table->idx[0][b][j]; + } + + x = __real__ table[i] - __real__ table[soft_table->idx[1][b][j]]; + y = __imag__ table[i] - __imag__ table[soft_table->idx[1][b][j]]; + d1 = x*x + y*y; + if (d1 < min_d1) { + min_d1 = d1; + min_idx1[i][b] = soft_table->idx[1][b][j]; + } + } + } + } + + for (i=0;id_idx[i][j] = -1; /* intialization */ + } + } + + for (i=0;id_idx[i][k]) { + soft_table->min_idx[0][i][b] = k; + flag = 1; /* no new entry to idxdx */ + break; + } + } + + if (flag == 0) { /* new entry to min and d_idx */ + soft_table->d_idx[i][count] = min_idx0[i][b]; + soft_table->min_idx[0][i][b] = count; + count++; + } + } + for (b=0;bd_idx[i][k]) { + soft_table->min_idx[1][i][b] = k; + flag = 1; /* no new entry to d_idx */ + break; + } + } + + if (flag == 0) { /* new entry to min and d_idx */ + soft_table->d_idx[i][count] = min_idx1[i][b]; + soft_table->min_idx[1][i][b] = count; + count++; + } + } + } } + diff --git a/lte/phy/lib/modem/src/lte_tables.h b/lte/phy/lib/modem/src/lte_tables.h index 77544a62a..3698945fc 100644 --- a/lte/phy/lib/modem/src/lte_tables.h +++ b/lte/phy/lib/modem/src/lte_tables.h @@ -38,6 +38,14 @@ #define QAM64_LEVEL_3 5/sqrt(42) #define QAM64_LEVEL_4 7/sqrt(42) +//////////////// NUEVO ////////////////////// +/* HARD DEMODULATION Thresholds, necessary for obtaining the zone of received symbol for optimized LLR approx implementation */ +#define QAM16_THRESHOLD 2/sqrt(10) +#define QAM64_THRESHOLD_1 2/sqrt(42) +#define QAM64_THRESHOLD_2 4/sqrt(42) +#define QAM64_THRESHOLD_3 6/sqrt(42) +//=========================================// + #define QAM64_LEVEL_x 2/sqrt(42) /* this is not an QAM64 level, but, rather, an auxiliary value that can be used for computing the * symbol from the bit sequence */ @@ -45,7 +53,18 @@ -void set_BPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); +void set_BPSKtable(cf_t* table, + soft_table_t *soft_table, + bool compute_soft_demod); + +void set_QPSKtable(cf_t* table, + soft_table_t *soft_table, + bool compute_soft_demod); + +void set_16QAMtable(cf_t* table, + soft_table_t *soft_table, + bool compute_soft_demod); + +void set_64QAMtable(cf_t* table, + soft_table_t *soft_table, + bool compute_soft_demod); diff --git a/lte/phy/lib/modem/src/mod.c b/lte/phy/lib/modem/src/mod.c index 382d86d10..ef6a600e7 100644 --- a/lte/phy/lib/modem/src/mod.c +++ b/lte/phy/lib/modem/src/mod.c @@ -35,14 +35,17 @@ /** Low-level API */ -int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, int nbits) { - int i,j,idx; +int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, uint32_t nbits) { + uint32_t i,j,idx; char *b_ptr=(char*) bits; j=0; for (i=0;inbits_x_symbol) { idx = bit_unpack(&b_ptr,q->nbits_x_symbol); - assert(idx >= 0 && idx < q->nsymbols); - symbols[j] = q->symbol_table[idx]; + if (idx < q->nsymbols) { + symbols[j] = q->symbol_table[idx]; + } else { + return LIBLTE_ERROR; + } j++; } return j; @@ -52,7 +55,7 @@ int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, int nbits) { /* High-Level API */ int mod_initialize(mod_hl* hl) { modem_table_init(&hl->obj); - if (modem_table_std(&hl->obj,hl->init.std,false)) { + if (modem_table_lte(&hl->obj,hl->init.std,false)) { return -1; } diff --git a/lte/phy/lib/modem/src/modem_table.c b/lte/phy/lib/modem/src/modem_table.c index 578b6dc98..e62ca981e 100644 --- a/lte/phy/lib/modem/src/modem_table.c +++ b/lte/phy/lib/modem/src/modem_table.c @@ -33,6 +33,7 @@ #include #include +#include "liblte/phy/common/phy_common.h" #include "liblte/phy/modem/modem_table.h" #include "lte_tables.h" @@ -56,27 +57,27 @@ void modem_table_reset(modem_table_t* q) { modem_table_init(q); } -int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol) { +int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, uint32_t nsymbols, uint32_t nbits_x_symbol) { if (q->nsymbols) { - return -1; + return LIBLTE_ERROR; } q->nsymbols = nsymbols; if (table_create(q)) { - return -1; + return LIBLTE_ERROR; } memcpy(q->symbol_table,table,q->nsymbols*sizeof(cf_t)); memcpy(&q->soft_table,soft_table,sizeof(soft_table_t)); q->nbits_x_symbol = nbits_x_symbol; - return 0; + return LIBLTE_SUCCESS; } -int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_demod) { - switch(std) { +int modem_table_lte(modem_table_t* q, lte_mod_t modulation, bool compute_soft_demod) { + switch(modulation) { case LTE_BPSK: q->nbits_x_symbol = 1; q->nsymbols = 2; if (table_create(q)) { - return -1; + return LIBLTE_ERROR; } set_BPSKtable(q->symbol_table, &q->soft_table, compute_soft_demod); break; @@ -84,7 +85,7 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem q->nbits_x_symbol = 2; q->nsymbols = 4; if (table_create(q)) { - return -1; + return LIBLTE_ERROR; } set_QPSKtable(q->symbol_table, &q->soft_table, compute_soft_demod); break; @@ -92,7 +93,7 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem q->nbits_x_symbol = 4; q->nsymbols = 16; if (table_create(q)) { - return -1; + return LIBLTE_ERROR; } set_16QAMtable(q->symbol_table, &q->soft_table, compute_soft_demod); break; @@ -100,10 +101,10 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem q->nbits_x_symbol = 6; q->nsymbols = 64; if (table_create(q)) { - return -1; + return LIBLTE_ERROR; } set_64QAMtable(q->symbol_table, &q->soft_table, compute_soft_demod); break; } - return 0; + return LIBLTE_SUCCESS; } diff --git a/lte/phy/lib/modem/src/soft_algs.c b/lte/phy/lib/modem/src/soft_algs.c index 7268d2a31..8c79c5a9c 100644 --- a/lte/phy/lib/modem/src/soft_algs.c +++ b/lte/phy/lib/modem/src/soft_algs.c @@ -31,8 +31,560 @@ #include #include #include +#include #include "soft_algs.h" +#include "liblte/phy/utils/vector.h" + +#define QAM16_THRESHOLD 2/sqrt(10) +#define QAM64_THRESHOLD_1 2/sqrt(42) +#define QAM64_THRESHOLD_2 4/sqrt(42) +#define QAM64_THRESHOLD_3 6/sqrt(42) + + +typedef _Complex float cf_t; + +// There are 3 implemenations: 1 - based on zones; 2 - using volk, 3 - straightforward C +#define LLR_APPROX_IMPLEMENTATION 1 + +#if LLR_APPROX_IMPLEMENTATION == 1 + +float dd[10000][7]; // 7 distances that are needed to compute LLR approx for 64QAM +uint32_t zone[10000]; // Zone of received symbol with respect to grid of QAM constellation diagram + + +/** + * @ingroup Received modulation symbol zone + * Determine location of received modulation symbol + * + * \param in input symbol (_Complex float) + * \param z associated zone in constellation diagram (int) + * \param N number of symbols + */ +static void zone_QPSK(const cf_t * in, uint32_t * z, int N) +{ + + int s; + float re, im; + + for (s = 0; s < N; s++) { + + re = __real__ in[s]; + im = __imag__ in[s]; + + if (re > 0) { + if (im > 0) { /* 1st Quadrand (upper-right) */ + z[s] = 0; + } else { /* 4th Quadrand (lower-right) */ + z[s] = 1; + } + } else { + if (im > 0) { /* 2nd Quadrand (upper-left) */ + z[s] = 2; + } else { /* 3rd Quadrand (lower-left) */ + z[s] = 3; + } + } + } +} + +/** + * @ingroup Received modulation symbol zone + * Determine location of received modulation symbol + * + * \param in input symbol (_Complex float) + * \param z associated zone in constellation diagram (int) + * \param N number of symbols + */ +static void zone_QAM16(const cf_t * in, uint32_t * z, int N) +{ + + int s; + float re, im; + + for (s = 0; s < N; s++) { + + re = __real__ in[s]; + im = __imag__ in[s]; + + if (re > 0) { + if (im > 0) { /* 1st Quadrand (upper-right) */ + if (re > QAM16_THRESHOLD) { + if (im > QAM16_THRESHOLD) { + z[s] = 3; + } else { + z[s] = 2; + } + } else { + if (im > QAM16_THRESHOLD) { + z[s] = 1; + } else { + z[s] = 0; + } + } + } else { /* 4th Quadrand (lower-right) */ + if (re > QAM16_THRESHOLD) { + if (im < -QAM16_THRESHOLD) { + z[s] = 7; + } else { + z[s] = 6; + } + } else { + if (im < -QAM16_THRESHOLD) { + z[s] = 5; + } else { + z[s] = 4; + } + } + } + } else { + if (im > 0) { /* 2nd Quadrand (upper-left) */ + if (re < -QAM16_THRESHOLD) { + if (im > QAM16_THRESHOLD) { + z[s] = 11; + } else { + z[s] = 10; + } + } else { + if (im > QAM16_THRESHOLD) { + z[s] = 9; + } else { + z[s] = 8; + } + } + } else { /* 3rd Quadrand (lower-left) */ + if (re < -QAM16_THRESHOLD) { + if (im < -QAM16_THRESHOLD) { + z[s] = 15; + } else { + z[s] = 14; + } + } else { + if (im < -QAM16_THRESHOLD) { + z[s] = 13; + } else { + z[s] = 12; + } + } + } + } + } +} + +/** + * @ingroup Received modulation symbol zone + * Determine location of received modulation symbol + * + * \param in input symbol (_Complex float) + * \param z associated zone in constellation diagram (int) + * \param N number of symbols + */ + +static void zone_QAM64(const cf_t * in, uint32_t * z, int N) +{ + + int s; + float re, im; + + for (s = 0; s < N; s++) { + + re = __real__ in[s]; + im = __imag__ in[s]; + + if (re > 0) { + + if (im > 0) { + + if (re > QAM64_THRESHOLD_2) { + + if (re > QAM64_THRESHOLD_3) { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 15; + } else { + z[s] = 14; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 10; + } else { + z[s] = 11; + } + + } else { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 13; + } else { + z[s] = 12; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 8; + } else { + z[s] = 9; + } + } + + } else if (re > QAM64_THRESHOLD_1) { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 5; + } else { + z[s] = 4; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 0; + } else { + z[s] = 1; + } + + } else { + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 7; + } else { + z[s] = 6; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 2; + } else { + z[s] = 3; + } + } + + } else { /* forth quadrant (lower-right) */ + + if (re > QAM64_THRESHOLD_2) { + + if (re > QAM64_THRESHOLD_3) { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 31; + } else { + z[s] = 30; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 26; + } else { + z[s] = 27; + } + + } else { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 29; + } else { + z[s] = 28; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 24; + } else { + z[s] = 25; + } + } + + } else if (re > QAM64_THRESHOLD_1) { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 21; + } else { + z[s] = 20; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 16; + } else { + z[s] = 17; + } + + } else { + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 23; + } else { + z[s] = 22; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 18; + } else { + z[s] = 19; + } + } + } + + } else { /* re < 0 */ + + if (im > 0) { /* second quadrant (upper-left) */ + + if (re < -QAM64_THRESHOLD_2) { + + if (re < -QAM64_THRESHOLD_3) { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 47; + } else { + z[s] = 46; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 42; + } else { + z[s] = 43; + } + + } else { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 45; + } else { + z[s] = 44; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 40; + } else { + z[s] = 41; + } + } + + } else if (re < -QAM64_THRESHOLD_1) { + + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 37; + } else { + z[s] = 36; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 32; + } else { + z[s] = 33; + } + + } else { + if (im > QAM64_THRESHOLD_2) { + if (im > QAM64_THRESHOLD_3) { + z[s] = 39; + } else { + z[s] = 38; + } + } else if (im > QAM64_THRESHOLD_1) { + z[s] = 34; + } else { + z[s] = 35; + } + } + } else { /* third quadrant (lower-left) */ + if (re < -QAM64_THRESHOLD_2) { + + if (re < -QAM64_THRESHOLD_3) { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 63; + } else { + z[s] = 62; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 58; + } else { + z[s] = 59; + } + + } else { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 61; + } else { + z[s] = 60; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 56; + } else { + z[s] = 57; + } + } + + } else if (re < -QAM64_THRESHOLD_1) { + + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 53; + } else { + z[s] = 52; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 48; + } else { + z[s] = 49; + } + + } else { + if (im < -QAM64_THRESHOLD_2) { + if (im < -QAM64_THRESHOLD_3) { + z[s] = 55; + } else { + z[s] = 54; + } + } else if (im < -QAM64_THRESHOLD_1) { + z[s] = 50; + } else { + z[s] = 51; + } + } + + } + } + } +} + +static void compute_zone(const cf_t * in, uint32_t * z, int N, int B) +{ + switch (B) { + case 1:{ + memset(zone, 0, N * sizeof(int)); + break; + } /* BPSK */ + case 2:{ + zone_QPSK(in, z, N); + break; + } /* QPSK */ + case 4:{ + zone_QAM16(in, z, N); + break; + } /* 16QAM */ + case 6:{ + zone_QAM64(in, z, N); + break; + } /* 64QAM */ + } +} + +static void compute_square_dist(const cf_t * in, cf_t * symbols, + uint32_t(*idx)[7], int N, int B) +{ + int s, b; + float *d_ptr; + cf_t symbols_extract[7]; + + for (s = 0; s < N; s++) { /* N: number of received symbols */ + d_ptr = dd[s]; + for (b = 0; b < B + 1; b++) { + symbols_extract[b] = symbols[idx[zone[s]][b]]; /* only subset of distances to constellation points needed for LLR approx */ + //x = __real__ in[s] - __real__ symbols[idx[zone[s]][b]]; + //y = __imag__ in[s] - __imag__ symbols[idx[zone[s]][b]]; + //dd[s][b] = x*x + y*y; + //printf("\n%f + j %f", __real__ symbols_extract[b], __imag__ symbols_extract[b]); + } + vec_square_dist(in[s], symbols_extract, d_ptr, B + 1); /* B+1 distances to be computed */ + } +} + +static void compute_llr(int N, int B, uint32_t(*min)[64][6], float sigma2, + float *out) +{ + int s, b; + for (s = 0; s < N; s++) { + for (b = 0; b < B; b++) { /* bits per symbol */ + out[s * B + b] = + (dd[s][min[0][zone[s]][b]] - dd[s][min[1][zone[s]][b]]) / sigma2; + } + } +} + +void llr_approx(const _Complex float *in, float *out, int N, int M, int B, + _Complex float *symbols, uint32_t(*S)[6][32], uint32_t(*idx)[7], + uint32_t(*min)[64][6], float sigma2) +{ + if ((M == 2) || (M == 4) || (M == 16) || (M == 64)) { + compute_zone(in, zone, N, B); + compute_square_dist(in, symbols, idx, N, B); + compute_llr(N, B, min, sigma2, out); + } +} + +#elif LLR_APPROX_IMPLEMENTATION == 2 + +float d[10000][64]; +float num[10000], den[10000]; + +static void compute_square_dist(const cf_t * in, cf_t * symbols, int N, int M) +{ + int s; + float *d_ptr; + for (s = 0; s < N; s++) { + d_ptr = d[s]; + vec_square_dist(in[s], symbols, d_ptr, M); + } +} + +static void compute_min_dist(uint32_t(*S)[6][32], int N, int B, int M) +{ + int s, b, i; + for (s = 0; s < N; s++) { + for (b = 0; b < B; b++) { /* bits per symbol */ + /* initiate num[b] and den[b] */ + num[s * B + b] = 1e10; + den[s * B + b] = 1e10; + + for (i = 0; i < M / 2; i++) { + if (d[s][S[0][b][i]] < num[s * B + b]) { + num[s * B + b] = d[s][S[0][b][i]]; + } + if (d[s][S[1][b][i]] < den[s * B + b]) { + den[s * B + b] = d[s][S[1][b][i]]; + } + } + } + } +} + +static void compute_llr(int N, int B, float sigma2, float *out) +{ + int s, b; + for (s = 0; s < N; s++) { + for (b = 0; b < B; b++) { /* bits per symbol */ + out[s * B + b] = (num[s * B + b] - den[s * B + b]) / sigma2; + } + } +} + +void llr_approx(const _Complex float *in, float *out, int N, int M, int B, + _Complex float *symbols, uint32_t(*S)[6][32], float sigma2) +{ + + if (M <= 64) { + compute_square_dist(in, symbols, N, M); + + compute_min_dist(S, N, B, M); + + compute_llr(N, B, sigma2, out); + } + for (b = 0; b < B; b++) { /* bits per symbol */ + out[s * B + b] = (num[s * B + b] - den[s * B + b]) / sigma2; + } +} + +void llr_approx(const _Complex float *in, float *out, int N, int M, int B, + _Complex float *symbols, uint32_t(*S)[6][32], uint32_t(*idx)[7], + uint32_t(*min)[64][6], float sigma2) +{ + + if (M <= 64) { + compute_square_dist(in, symbols, N, M); + + compute_min_dist(S, N, B, M); + + compute_llr(N, B, sigma2, out); + } +} + +#else /** * @ingroup Soft Modulation Demapping based on the approximate @@ -51,50 +603,53 @@ * \param sigma2 Noise vatiance */ void llr_approx(const _Complex float *in, float *out, int N, int M, int B, - _Complex float *symbols, int (*S)[6][32], float sigma2) { + _Complex float *symbols, uint32_t(*S)[6][32], uint32_t(*idx)[7], + uint32_t(*min)[64][6], float sigma2) +{ int i, s, b; float num, den; - float new_num, new_den; - float idiff0, qdiff0, idiff1, qdiff1; int change_sign = -1; + float x, y, d[64]; - for (s=0; stv_usec; +// fin = y->tv_usec; + parse_args(argc, argv); /* initialize objects */ - if (modem_table_std(&mod, modulation, soft_output)) { + if (modem_table_lte(&mod, modulation, soft_output)) { fprintf(stderr, "Error initializing modem table\n"); exit(-1); } @@ -156,7 +163,12 @@ int main(int argc, char **argv) { /* demodulate */ if (soft_output) { + + gettimeofday(&x, NULL); demod_soft_demodulate(&demod_soft, symbols, llr, num_bits / mod.nbits_x_symbol); + gettimeofday(&y, NULL); + printf("\nElapsed time [ns]: %d\n", (int) y.tv_usec - (int) x.tv_usec); + for (i=0;i=0 ? 1 : 0; } diff --git a/lte/phy/lib/modem/test/soft_demod_test.c b/lte/phy/lib/modem/test/soft_demod_test.c new file mode 100644 index 000000000..96b76c5b0 --- /dev/null +++ b/lte/phy/lib/modem/test/soft_demod_test.c @@ -0,0 +1,232 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +int nof_frames = 10; +int num_bits = 1000; +lte_mod_t modulation = 0; + +void usage(char *prog) { + printf("Usage: %s [nfv] -m modulation (1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-f nof_frames [Default %d]\n", nof_frames); + printf("\t-v verbose [Default None]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nmvf")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 'f': + nof_frames = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + case 'm': + switch(atoi(argv[optind])) { + case 1: + modulation = LTE_BPSK; + break; + case 2: + modulation = LTE_QPSK; + break; + case 4: + modulation = LTE_QAM16; + break; + case 6: + modulation = LTE_QAM64; + break; + default: + fprintf(stderr, "Invalid modulation %d. Possible values: " + "(1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", atoi(argv[optind])); + break; + } + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (modulation == 0) { + usage(argv[0]); + exit(-1); + } +} + +float mse_threshold() { + switch(modulation) { + case LTE_BPSK: + return 1.0e-6; + case LTE_QPSK: + return 1.0e-6; + case LTE_QAM16: + return 0.11; + case LTE_QAM64: + return 0.19; + default: + return -1.0; + } +} + +int main(int argc, char **argv) { + int i; + modem_table_t mod; + demod_soft_t demod_soft; + char *input, *output; + cf_t *symbols; + float *llr_exact, *llr_approx; + + parse_args(argc, argv); + + /* initialize objects */ + if (modem_table_lte(&mod, modulation, true)) { + fprintf(stderr, "Error initializing modem table\n"); + exit(-1); + } + + /* check that num_bits is multiple of num_bits x symbol */ + num_bits = mod.nbits_x_symbol * (num_bits / mod.nbits_x_symbol); + + demod_soft_init(&demod_soft); + demod_soft_table_set(&demod_soft, &mod); + demod_soft_sigma_set(&demod_soft, 2.0 / mod.nbits_x_symbol); + + + /* allocate buffers */ + input = malloc(sizeof(char) * num_bits); + if (!input) { + perror("malloc"); + exit(-1); + } + output = malloc(sizeof(char) * num_bits); + if (!output) { + perror("malloc"); + exit(-1); + } + symbols = malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol); + if (!symbols) { + perror("malloc"); + exit(-1); + } + + llr_exact = malloc(sizeof(float) * num_bits); + if (!llr_exact) { + perror("malloc"); + exit(-1); + } + + llr_approx = malloc(sizeof(float) * num_bits); + if (!llr_approx) { + perror("malloc"); + exit(-1); + } + + /* generate random data */ + srand(0); + + int ret = -1; + double mse; + struct timeval t[3]; + float mean_texec = 0.0; + for (int n=0;n 0) { + mean_texec = EXPAVERAGE((float) t[0].tv_usec, mean_texec, n-1); + } + + /* check MSE */ + mse = 0.0; + for (i=0;i %f)\n", mse, mse_threshold()); + } + exit(ret); +} diff --git a/lte/phy/lib/phch/src/dci.c b/lte/phy/lib/phch/src/dci.c index b2745d607..5d57a28ab 100644 --- a/lte/phy/lib/phch/src/dci.c +++ b/lte/phy/lib/phch/src/dci.c @@ -40,52 +40,86 @@ #include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/debug.h" -int dci_init(dci_t *q, int max_dcis) { - q->msg = calloc(sizeof(dci_msg_t), max_dcis); - if (!q->msg) { - perror("malloc"); - return -1; - } - q->nof_dcis = 0; - q->max_dcis = max_dcis; - return 0; -} -void dci_free(dci_t *q) { - if (q->msg) { - free(q->msg); +int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti, + lte_cell_t cell, uint32_t cfi, + ra_pdsch_t *ra_dl) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && + ra_dl != NULL && + lte_cell_isvalid(&cell) && + cfi > 0 && + cfi < 4) + { + ret = LIBLTE_ERROR; + + dci_msg_type_t type; + if (dci_msg_get_type(msg, &type, cell.nof_prb, msg_rnti, c_rnti)) { + fprintf(stderr, "Can't get DCI message type\n"); + return ret; + } + + if (VERBOSE_ISDEBUG()) { + dci_msg_type_fprint(stdout, type); + } + if (type.type == PDSCH_SCHED) { + bzero(ra_dl, sizeof(ra_pdsch_t)); + + if (dci_msg_unpack_pdsch(msg, ra_dl, cell.nof_prb, msg_rnti != SIRNTI)) { + fprintf(stderr, "Can't unpack PDSCH message\n"); + return ret; + } + + if (VERBOSE_ISDEBUG()) { + ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb); + } + + if (ra_prb_get_dl(&ra_dl->prb_alloc, ra_dl, cell.nof_prb)) { + fprintf(stderr, "Error computing resource allocation\n"); + return ret; + } + + ra_prb_get_re_dl(&ra_dl->prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp); + + ret = LIBLTE_SUCCESS; + } } + return ret; } -void dci_candidate_fprint(FILE *f, dci_candidate_t *q) { - fprintf(f, "L: %d, nCCE: %d, RNTI: 0x%x, nBits: %d\n", q->L, q->ncce, q->rnti, - q->nof_bits); -} - -int dci_msg_candidate_set(dci_msg_t *msg, int L, int nCCE, unsigned short rnti) { - if (L >= 0 && L <= 3) { - msg->location.L = (unsigned char) L; +int dci_location_set(dci_location_t *c, uint32_t L, uint32_t nCCE) { + if (L <= 3) { + c->L = L; } else { fprintf(stderr, "Invalid L %d\n", L); - return -1; + return LIBLTE_ERROR; } - if (nCCE >= 0 && nCCE <= 87) { - msg->location.ncce = (unsigned char) nCCE; + if (nCCE <= 87) { + c->ncce = nCCE; } else { fprintf(stderr, "Invalid nCCE %d\n", nCCE); - return -1; + return LIBLTE_ERROR; + } + return LIBLTE_SUCCESS; +} + +bool dci_location_isvalid(dci_location_t *c) { + if (c->L <= 3 && c->ncce <= 87) { + return true; + } else { + return false; } - msg->location.rnti = rnti; - return 0; } -int riv_nbits(int nof_prb) { - return (int) ceilf(log2f((float) nof_prb * ((float) nof_prb + 1) / 2)); +uint32_t riv_nbits(uint32_t nof_prb) { + return (uint32_t) ceilf(log2f((float) nof_prb * ((float) nof_prb + 1) / 2)); } -const int ambiguous_sizes[10] = { 12, 14, 16, 20, 24, 26, 32, 40, 44, 56 }; +const uint32_t ambiguous_sizes[10] = { 12, 14, 16, 20, 24, 26, 32, 40, 44, 56 }; -bool is_ambiguous_size(int size) { +bool is_ambiguous_size(uint32_t size) { int i; for (i = 0; i < 10; i++) { if (size == ambiguous_sizes[i]) { @@ -98,12 +132,12 @@ bool is_ambiguous_size(int size) { /********************************** * PAYLOAD sizeof functions * ********************************/ -int dci_format0_sizeof_(int nof_prb) { +uint32_t dci_format0_sizeof_(uint32_t nof_prb) { return 1 + 1 + riv_nbits(nof_prb) + 5 + 1 + 2 + 3 + 1; } -int dci_format1A_sizeof(int nof_prb) { - int n; +uint32_t dci_format1A_sizeof(uint32_t nof_prb) { + uint32_t n; n = 1 + 1 + riv_nbits(nof_prb) + 5 + 3 + 1 + 2 + 2; while (n < dci_format0_sizeof_(nof_prb)) { n++; @@ -114,17 +148,17 @@ int dci_format1A_sizeof(int nof_prb) { return n; } -int dci_format0_sizeof(int nof_prb) { - int n = dci_format0_sizeof_(nof_prb); +uint32_t dci_format0_sizeof(uint32_t nof_prb) { + uint32_t n = dci_format0_sizeof_(nof_prb); while (n < dci_format1A_sizeof(nof_prb)) { n++; } return n; } -int dci_format1_sizeof(int nof_prb) { +uint32_t dci_format1_sizeof(uint32_t nof_prb) { - int n = (int) ceilf((float) nof_prb / ra_type0_P(nof_prb)) + 5 + 3 + 1 + 2 + uint32_t n = (uint32_t) ceilf((float) nof_prb / ra_type0_P(nof_prb)) + 5 + 3 + 1 + 2 + 2; if (nof_prb > 10) { n++; @@ -136,17 +170,17 @@ int dci_format1_sizeof(int nof_prb) { return n; } -int dci_format1C_sizeof(int nof_prb) { - int n_vrb_dl_gap1 = ra_type2_n_vrb_dl(nof_prb, true); - int n_step = ra_type2_n_rb_step(nof_prb); - int n = +riv_nbits((int) n_vrb_dl_gap1 / n_step) + 5; +uint32_t dci_format1C_sizeof(uint32_t nof_prb) { + uint32_t n_vrb_dl_gap1 = ra_type2_n_vrb_dl(nof_prb, true); + uint32_t n_step = ra_type2_n_rb_step(nof_prb); + uint32_t n = riv_nbits((uint32_t) n_vrb_dl_gap1 / n_step) + 5; if (nof_prb >= 50) { n++; } return n; } -int dci_format_sizeof(dci_format_t format, int nof_prb) { +uint32_t dci_format_sizeof(dci_format_t format, uint32_t nof_prb) { switch (format) { case Format0: return dci_format0_sizeof(nof_prb); @@ -157,7 +191,7 @@ int dci_format_sizeof(dci_format_t format, int nof_prb) { case Format1C: return dci_format1C_sizeof(nof_prb); default: - return -1; + return LIBLTE_ERROR; } } @@ -170,11 +204,11 @@ int dci_format_sizeof(dci_format_t format, int nof_prb) { * * TODO: TPC and cyclic shift for DM RS not implemented */ -int dci_format0_pack(ra_pusch_t *data, dci_msg_t *msg, int nof_prb) { +int dci_format0_pack(ra_pusch_t *data, dci_msg_t *msg, uint32_t nof_prb) { /* pack bits */ char *y = msg->data; - int n_ul_hop; + uint32_t n_ul_hop; *y++ = 0; // format differentiation if (data->freq_hop_fl == hop_disabled) { // frequency hopping @@ -203,28 +237,7 @@ int dci_format0_pack(ra_pusch_t *data, dci_msg_t *msg, int nof_prb) { bit_pack(riv, &y, riv_nbits(nof_prb) - n_ul_hop); /* pack MCS according to 8.6.1 of 36.213 */ - uint32_t mcs; - if (data->cqi_request) { - mcs = 29; - } else { - if (data->rv_idx) { - mcs = 28 + data->rv_idx; - } else { - if (data->mcs.mod == MOD_NULL) { - mcs = data->mcs.mcs_idx; - } else { - if (data->mcs.tbs) { - if (data->mcs.tbs) { - data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs, - ra_nprb_ul(data, nof_prb)); - } - } - mcs = ra_mcs_to_table_idx(&data->mcs); - } - } - } - - bit_pack(mcs, &y, 5); + bit_pack(data->mcs_idx, &y, 5); *y++ = data->ndi; @@ -241,33 +254,33 @@ int dci_format0_pack(ra_pusch_t *data, dci_msg_t *msg, int nof_prb) { *y++ = data->cqi_request; // Padding with zeros - int n = dci_format0_sizeof(nof_prb); + uint32_t n = dci_format0_sizeof(nof_prb); while (y - msg->data < n) { *y++ = 0; } - msg->location.nof_bits = (y - msg->data); - return 0; + msg->nof_bits = (y - msg->data); + return LIBLTE_SUCCESS; } /* Unpacks DCI format 0 data and store result in msg according * to 36.212 5.3.3.1.1 * * TODO: TPC and cyclic shift for DM RS not implemented */ -int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, int nof_prb) { +int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, uint32_t nof_prb) { /* pack bits */ char *y = msg->data; - int n_ul_hop; + uint32_t n_ul_hop; /* Make sure it's a Format0 message */ - if (msg->location.nof_bits != dci_format_sizeof(Format0, nof_prb)) { + if (msg->nof_bits != dci_format_sizeof(Format0, nof_prb)) { fprintf(stderr, "Invalid message length for format 0\n"); - return -1; + return LIBLTE_ERROR; } if (*y++ != 0) { fprintf(stderr, "Invalid format differentiation field value. This is Format1A\n"); - return -1; + return LIBLTE_ERROR; } if (*y++ == 0) { data->freq_hop_fl = hop_disabled; @@ -286,11 +299,11 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, int nof_prb) { uint32_t riv = bit_unpack(&y, riv_nbits(nof_prb) - n_ul_hop); ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, nof_prb, nof_prb); - bit_pack(riv, &y, riv_nbits(nof_prb) - n_ul_hop); + bit_pack((uint32_t) riv, &y, riv_nbits(nof_prb) - n_ul_hop); data->type2_alloc.riv = riv; /* unpack MCS according to 8.6 of 36.213 */ - uint32_t mcs = bit_unpack(&y, 5); + data->mcs_idx = bit_unpack(&y, 5); data->ndi = *y++ ? true : false; @@ -301,23 +314,19 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, int nof_prb) { data->cqi_request = *y++ ? true : false; // 8.6.2 First paragraph - if (mcs <= 28) { - ra_mcs_from_idx_ul(mcs, &data->mcs); - data->mcs.tbs = ra_tbs_from_idx(data->mcs.tbs_idx, - ra_nprb_ul(data, nof_prb)); - } - - // 8.6.1 and 8.6.2 36.213 second paragraph - if (mcs == 29 && data->cqi_request && ra_nprb_ul(data, nof_prb) <= 4) { - data->mcs.mod = QPSK; - } - if (mcs > 29) { - // Else leave MOD_NULL and use the previously used PUSCH modulation - data->mcs.mod = MOD_NULL; - data->rv_idx = mcs - 28; - } - - return 0; + if (data->mcs_idx <= 28) { + ra_mcs_from_idx_ul(data->mcs_idx, ra_nprb_ul(data, nof_prb), &data->mcs); + } else if (data->mcs_idx == 29 && data->cqi_request && ra_nprb_ul(data, nof_prb) <= 4) { + // 8.6.1 and 8.6.2 36.213 second paragraph + data->mcs.mod = LTE_QPSK; + data->mcs.tbs = 0; + } else if (data->mcs_idx >= 29) { + // Else leave TBS and use the previously used PUSCH modulation + data->mcs.tbs = 0; + data->rv_idx = data->mcs_idx - 28; + } + + return LIBLTE_SUCCESS; } /* Packs DCI format 1 data to a sequence of bits and store them in msg according @@ -326,7 +335,7 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, int nof_prb) { * TODO: TPC commands */ -int dci_format1_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { +int dci_format1_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb) { /* pack bits */ char *y = msg->data; @@ -336,36 +345,26 @@ int dci_format1_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { } /* Resource allocation: type0 or type 1 */ - int P = ra_type0_P(nof_prb); - int alloc_size = (int) ceilf((float) nof_prb / P); + uint32_t P = ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); switch (data->alloc_type) { case alloc_type0: - bit_pack(data->type0_alloc.rbg_bitmask, &y, alloc_size); + bit_pack((uint32_t) data->type0_alloc.rbg_bitmask, &y, alloc_size); break; case alloc_type1: - bit_pack(data->type1_alloc.rbg_subset, &y, (int) ceilf(log2f(P))); + bit_pack((uint32_t) data->type1_alloc.rbg_subset, &y, (int) ceilf(log2f(P))); *y++ = data->type1_alloc.shift ? 1 : 0; - bit_pack(data->type1_alloc.vrb_bitmask, &y, + bit_pack((uint32_t) data->type1_alloc.vrb_bitmask, &y, alloc_size - (int) ceilf(log2f(P)) - 1); break; default: fprintf(stderr, "Format 1 accepts type0 or type1 resource allocation only\n"); - return -1; + return LIBLTE_ERROR; } - /* pack MCS according to 7.1.7 of 36.213 */ - uint32_t mcs; - if (data->mcs.mod == MOD_NULL) { - mcs = data->mcs.mcs_idx; - } else { - if (data->mcs.tbs) { - data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs, - ra_nprb_dl(data, nof_prb)); - } - mcs = ra_mcs_to_table_idx(&data->mcs); - } - bit_pack(mcs, &y, 5); + /* pack MCS */ + bit_pack(data->mcs_idx, &y, 5); /* harq process number */ bit_pack(data->harq_process, &y, 3); @@ -380,24 +379,24 @@ int dci_format1_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { *y++ = 0; // Padding with zeros - int n = dci_format1_sizeof(nof_prb); + uint32_t n = dci_format1_sizeof(nof_prb); while (y - msg->data < n) { *y++ = 0; } - msg->location.nof_bits = (y - msg->data); + msg->nof_bits = (y - msg->data); - return 0; + return LIBLTE_SUCCESS; } -int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb) { +int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb) { /* pack bits */ char *y = msg->data; /* Make sure it's a Format1 message */ - if (msg->location.nof_bits != dci_format_sizeof(Format1, nof_prb)) { + if (msg->nof_bits != dci_format_sizeof(Format1, nof_prb)) { fprintf(stderr, "Invalid message length for format 1\n"); - return -1; + return LIBLTE_ERROR; } if (nof_prb > 10) { @@ -407,8 +406,8 @@ int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb) { } /* Resource allocation: type0 or type 1 */ - int P = ra_type0_P(nof_prb); - int alloc_size = (int) ceilf((float) nof_prb / P); + uint32_t P = ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); switch (data->alloc_type) { case alloc_type0: data->type0_alloc.rbg_bitmask = bit_unpack(&y, alloc_size); @@ -420,17 +419,17 @@ int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb) { alloc_size - (int) ceilf(log2f(P)) - 1); break; default: - fprintf(stderr, - "Format 1 accepts type0 or type1 resource allocation only\n"); - return -1; + fprintf(stderr, "Format 1 accepts type0 or type1 resource allocation only\n"); + return LIBLTE_ERROR; } - /* pack MCS according to 7.1.7 of 36.213 */ - uint32_t mcs = bit_unpack(&y, 5); - data->mcs.mcs_idx = mcs; - ra_mcs_from_idx_dl(mcs, &data->mcs); - data->mcs.tbs = ra_tbs_from_idx(data->mcs.tbs_idx, ra_nprb_dl(data, nof_prb)); - + /* unpack MCS according to 7.1.7 of 36.213 */ + data->mcs_idx = bit_unpack(&y, 5); + if (ra_mcs_from_idx_dl(data->mcs_idx, ra_nprb_dl(data, nof_prb), &data->mcs)) { + fprintf(stderr, "Error getting MCS\n"); + return LIBLTE_ERROR; + } + /* harq process number */ data->harq_process = bit_unpack(&y, 3); @@ -441,14 +440,14 @@ int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb) { // TPC not implemented - return 0; + return LIBLTE_SUCCESS; } /* Packs DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3 * * TODO: RA procedure initiated by PDCCH, TPC commands */ -int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, +int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb, bool crc_is_crnti) { /* pack bits */ @@ -458,7 +457,7 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, if (data->alloc_type != alloc_type2) { fprintf(stderr, "Format 1A accepts type2 resource allocation only\n"); - return -1; + return LIBLTE_ERROR; } *y++ = data->type2_alloc.mode; // localized or distributed VRB assignment @@ -467,10 +466,10 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, if (data->type2_alloc.L_crb > nof_prb) { fprintf(stderr, "L_CRB=%d can not exceed system BW for localized type2\n", data->type2_alloc.L_crb); - return -1; + return LIBLTE_ERROR; } } else { - int n_vrb_dl; + uint32_t n_vrb_dl; if (crc_is_crnti && nof_prb > 50) { n_vrb_dl = 16; } else { @@ -480,7 +479,7 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, fprintf(stderr, "L_CRB=%d can not exceed N_vrb_dl=%d for distributed type2\n", data->type2_alloc.L_crb, n_vrb_dl); - return -1; + return LIBLTE_ERROR; } } /* pack RIV according to 7.1.6.3 of 36.213 */ @@ -491,7 +490,7 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, } else { riv = data->type2_alloc.riv; } - int nb_gap = 0; + uint32_t nb_gap = 0; if (crc_is_crnti && data->type2_alloc.mode == t2_dist && nof_prb >= 50) { nb_gap = 1; *y++ = data->type2_alloc.n_gap; @@ -499,23 +498,7 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, bit_pack(riv, &y, riv_nbits(nof_prb) - nb_gap); // in format1A, MCS = TBS according to 7.1.7.2 of 36.213 - uint32_t mcs; - if (data->mcs.mod == MOD_NULL) { - mcs = data->mcs.mcs_idx; - } else { - if (data->mcs.tbs) { - // In format 1A, n_prb_1a is 2 or 3 if crc is not scrambled with C-RNTI - int n_prb; - if (!crc_is_crnti) { - n_prb = ra_nprb_dl(data, nof_prb); - } else { - n_prb = data->type2_alloc.n_prb1a == nprb1a_2 ? 2 : 3; - } - data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs, n_prb); - } - mcs = data->mcs.tbs_idx; - } - bit_pack(mcs, &y, 5); + bit_pack(data->mcs_idx, &y, 5); bit_pack(data->harq_process, &y, 3); @@ -538,34 +521,33 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb, } // Padding with zeros - int n = dci_format1A_sizeof(nof_prb); + uint32_t n = dci_format1A_sizeof(nof_prb); while (y - msg->data < n) { *y++ = 0; } - msg->location.nof_bits = (y - msg->data); + msg->nof_bits = (y - msg->data); - return 0; + return LIBLTE_SUCCESS; } /* Unpacks DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3 * */ -int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, +int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb, bool crc_is_crnti) { /* pack bits */ char *y = msg->data; /* Make sure it's a Format0 message */ - if (msg->location.nof_bits != dci_format_sizeof(Format1A, nof_prb)) { + if (msg->nof_bits != dci_format_sizeof(Format1A, nof_prb)) { fprintf(stderr, "Invalid message length for format 1A\n"); - return -1; + return LIBLTE_ERROR; } if (*y++ != 1) { - fprintf(stderr, - "Invalid format differentiation field value. This is Format0\n"); - return -1; + fprintf(stderr, "Invalid format differentiation field value. This is Format0\n"); + return LIBLTE_ERROR; } data->alloc_type = alloc_type2; @@ -575,12 +557,12 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, data->type2_alloc.n_gap = t2_ng1; /* unpack RIV according to 7.1.6.3 of 36.213 */ - int nb_gap = 0; + uint32_t nb_gap = 0; if (crc_is_crnti && data->type2_alloc.mode == t2_dist && nof_prb >= 50) { nb_gap = 1; data->type2_alloc.n_gap = *y++; } - int nof_vrb; + uint32_t nof_vrb; if (data->type2_alloc.mode == t2_loc) { nof_vrb = nof_prb; } else { @@ -592,7 +574,7 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, data->type2_alloc.riv = riv; // unpack MCS - data->mcs.mcs_idx = bit_unpack(&y, 5); + data->mcs_idx = bit_unpack(&y, 5); data->harq_process = bit_unpack(&y, 3); @@ -613,23 +595,23 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, y++; // MSB of TPC is reserved data->type2_alloc.n_prb1a = *y++; // LSB indicates N_prb_1a for TBS } - data->mcs.tbs_idx = data->mcs.mcs_idx; - int n_prb; + + uint32_t n_prb; if (crc_is_crnti) { n_prb = ra_nprb_dl(data, nof_prb); } else { n_prb = data->type2_alloc.n_prb1a == nprb1a_2 ? 2 : 3; } - data->mcs.tbs = ra_tbs_from_idx(data->mcs.tbs_idx, n_prb); - data->mcs.mod = QPSK; + data->mcs.tbs = ra_tbs_from_idx(data->mcs_idx, n_prb); + data->mcs.mod = LTE_QPSK; - return 0; + return LIBLTE_SUCCESS; } /* Format 1C for compact scheduling of PDSCH words * */ -int dci_format1Cs_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { +int dci_format1Cs_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb) { /* pack bits */ char *y = msg->data; @@ -637,32 +619,31 @@ int dci_format1Cs_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { if (data->alloc_type != alloc_type2 || data->type2_alloc.mode != t2_dist) { fprintf(stderr, "Format 1C accepts distributed type2 resource allocation only\n"); - return -1; + return LIBLTE_ERROR; } if (nof_prb >= 50) { *y++ = data->type2_alloc.n_gap; } - int n_step = ra_type2_n_rb_step(nof_prb); - int n_vrb_dl = ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == t2_ng1); + uint32_t n_step = ra_type2_n_rb_step(nof_prb); + uint32_t n_vrb_dl = ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == t2_ng1); - if (data->type2_alloc.L_crb > ((int) n_vrb_dl / n_step) * n_step) { - fprintf(stderr, - "L_CRB=%d can not exceed N_vrb_dl=%d for distributed type2\n", - data->type2_alloc.L_crb, ((int) n_vrb_dl / n_step) * n_step); - return -1; + if (data->type2_alloc.L_crb > ((uint32_t) n_vrb_dl / n_step) * n_step) { + fprintf(stderr, "L_CRB=%d can not exceed N_vrb_dl=%d for distributed type2\n", + data->type2_alloc.L_crb, ((uint32_t) n_vrb_dl / n_step) * n_step); + return LIBLTE_ERROR; } if (data->type2_alloc.L_crb % n_step) { fprintf(stderr, "L_crb must be multiple of n_step\n"); - return -1; + return LIBLTE_ERROR; } if (data->type2_alloc.RB_start % n_step) { fprintf(stderr, "RB_start must be multiple of n_step\n"); - return -1; + return LIBLTE_ERROR; } - int L_p = data->type2_alloc.L_crb / n_step; - int RB_p = data->type2_alloc.RB_start / n_step; - int n_vrb_p = (int) n_vrb_dl / n_step; + uint32_t L_p = data->type2_alloc.L_crb / n_step; + uint32_t RB_p = data->type2_alloc.RB_start / n_step; + uint32_t n_vrb_p = (int) n_vrb_dl / n_step; uint32_t riv; if (data->type2_alloc.L_crb) { @@ -673,60 +654,50 @@ int dci_format1Cs_pack(ra_pdsch_t *data, dci_msg_t *msg, int nof_prb) { bit_pack(riv, &y, riv_nbits((int) n_vrb_dl / n_step)); // in format1C, MCS = TBS according to 7.1.7.2 of 36.213 - uint32_t mcs; - if (data->mcs.mod == MOD_NULL) { - mcs = data->mcs.mcs_idx; - } else { - if (data->mcs.tbs) { - data->mcs.tbs_idx = ra_tbs_to_table_idx_format1c(data->mcs.tbs); - } - mcs = data->mcs.tbs_idx; - } - bit_pack(mcs, &y, 5); + bit_pack(data->mcs_idx, &y, 5); - msg->location.nof_bits = (y - msg->data); + msg->nof_bits = (y - msg->data); - return 0; + return LIBLTE_SUCCESS; } -int dci_format1Cs_unpack(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb) { - uint16_t L_p, RB_p; +int dci_format1Cs_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb) { + uint32_t L_p, RB_p; /* pack bits */ char *y = msg->data; - if (msg->location.nof_bits != dci_format_sizeof(Format1C, nof_prb)) { + if (msg->nof_bits != dci_format_sizeof(Format1C, nof_prb)) { fprintf(stderr, "Invalid message length for format 1C\n"); - return -1; + return LIBLTE_ERROR; } data->alloc_type = alloc_type2; data->type2_alloc.mode = t2_dist; if (nof_prb >= 50) { data->type2_alloc.n_gap = *y++; } - int n_step = ra_type2_n_rb_step(nof_prb); - int n_vrb_dl = ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == t2_ng1); + uint32_t n_step = ra_type2_n_rb_step(nof_prb); + uint32_t n_vrb_dl = ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == t2_ng1); uint32_t riv = bit_unpack(&y, riv_nbits((int) n_vrb_dl / n_step)); - int n_vrb_p = (int) n_vrb_dl / n_step; + uint32_t n_vrb_p = (uint32_t) n_vrb_dl / n_step; ra_type2_from_riv(riv, &L_p, &RB_p, n_vrb_p, n_vrb_p); data->type2_alloc.L_crb = L_p * n_step; data->type2_alloc.RB_start = RB_p * n_step; data->type2_alloc.riv = riv; - data->mcs.mcs_idx = bit_unpack(&y, 5); - data->mcs.tbs_idx = data->mcs.mcs_idx; - data->mcs.tbs = ra_tbs_from_idx_format1c(data->mcs.tbs_idx); - data->mcs.mod = QPSK; + data->mcs_idx = bit_unpack(&y, 5); + data->mcs.tbs = ra_tbs_from_idx_format1c(data->mcs_idx); + data->mcs.mod = LTE_QPSK; - msg->location.nof_bits = (y - msg->data); + msg->nof_bits = (y - msg->data); - return 0; + return LIBLTE_SUCCESS; } int dci_msg_pack_pdsch(ra_pdsch_t *data, dci_msg_t *msg, dci_format_t format, - int nof_prb, bool crc_is_crnti) { + uint32_t nof_prb, bool crc_is_crnti) { switch (format) { case Format1: return dci_format1_pack(data, msg, nof_prb); @@ -737,28 +708,28 @@ int dci_msg_pack_pdsch(ra_pdsch_t *data, dci_msg_t *msg, dci_format_t format, default: fprintf(stderr, "Invalid DCI format %s for PDSCH resource allocation\n", dci_format_string(format)); - return -1; + return LIBLTE_ERROR; } } -int dci_msg_unpack_pdsch(dci_msg_t *msg, ra_pdsch_t *data, int nof_prb, +int dci_msg_unpack_pdsch(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb, bool crc_is_crnti) { - if (msg->location.nof_bits == dci_format_sizeof(Format1, nof_prb)) { + if (msg->nof_bits == dci_format_sizeof(Format1, nof_prb)) { return dci_format1_unpack(msg, data, nof_prb); - } else if (msg->location.nof_bits == dci_format_sizeof(Format1A, nof_prb)) { + } else if (msg->nof_bits == dci_format_sizeof(Format1A, nof_prb)) { return dci_format1As_unpack(msg, data, nof_prb, crc_is_crnti); - } else if (msg->location.nof_bits == dci_format_sizeof(Format1C, nof_prb)) { + } else if (msg->nof_bits == dci_format_sizeof(Format1C, nof_prb)) { return dci_format1Cs_unpack(msg, data, nof_prb); } else { - return -1; + return LIBLTE_ERROR; } } -int dci_msg_pack_pusch(ra_pusch_t *data, dci_msg_t *msg, int nof_prb) { +int dci_msg_pack_pusch(ra_pusch_t *data, dci_msg_t *msg, uint32_t nof_prb) { return dci_format0_pack(data, msg, nof_prb); } -int dci_msg_unpack_pusch(dci_msg_t *msg, ra_pusch_t *data, int nof_prb) { +int dci_msg_unpack_pusch(dci_msg_t *msg, ra_pusch_t *data, uint32_t nof_prb) { return dci_format0_unpack(msg, data, nof_prb); } @@ -798,36 +769,38 @@ void dci_msg_type_fprint(FILE *f, dci_msg_type_t type) { } } -int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, int nof_prb, - unsigned short crnti) { - if (msg->location.nof_bits == dci_format_sizeof(Format0, nof_prb) +int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, uint32_t nof_prb, + uint16_t msg_rnti, uint16_t crnti) +{ + DEBUG("Get message type: nof_bits=%d, msg_rnti=0x%x, crnti=0x%x\n", msg->nof_bits, msg_rnti, crnti); + if (msg->nof_bits == dci_format_sizeof(Format0, nof_prb) && !msg->data[0]) { type->type = PUSCH_SCHED; type->format = Format0; - return 0; - } else if (msg->location.nof_bits == dci_format_sizeof(Format1, nof_prb)) { + return LIBLTE_SUCCESS; + } else if (msg->nof_bits == dci_format_sizeof(Format1, nof_prb)) { type->type = PDSCH_SCHED; // only these 2 types supported type->format = Format1; - return 0; - } else if (msg->location.nof_bits == dci_format_sizeof(Format1A, nof_prb)) { - if (msg->location.rnti == crnti) { + return LIBLTE_SUCCESS; + } else if (msg->nof_bits == dci_format_sizeof(Format1A, nof_prb)) { + if (msg_rnti == crnti) { type->type = RA_PROC_PDCCH; type->format = Format1A; } else { type->type = PDSCH_SCHED; // only these 2 types supported type->format = Format1A; } - return 0; - } else if (msg->location.nof_bits == dci_format_sizeof(Format1C, nof_prb)) { - if (msg->location.rnti == MRNTI) { + return LIBLTE_SUCCESS; + } else if (msg->nof_bits == dci_format_sizeof(Format1C, nof_prb)) { + if (msg_rnti == MRNTI) { type->type = MCCH_CHANGE; type->format = Format1C; } else { type->type = PDSCH_SCHED; // only these 2 types supported type->format = Format1C; } - return 0; + return LIBLTE_SUCCESS; } - return -1; + return LIBLTE_ERROR; } diff --git a/lte/phy/lib/phch/src/pbch.c b/lte/phy/lib/phch/src/pbch.c index 6113e5342..d2f5ca450 100644 --- a/lte/phy/lib/phch/src/pbch.c +++ b/lte/phy/lib/phch/src/pbch.c @@ -50,31 +50,45 @@ bool pbch_exists(int nframe, int nslot) { return (!(nframe % 5) && nslot == 1); } -int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, - bool put) { +int pbch_cp(cf_t *input, cf_t *output, lte_cell_t cell, bool put) { int i; cf_t *ptr; - assert(cell_id >= 0); + if (put) { ptr = input; - output += nof_prb * RE_X_RB / 2 - 36; + output += cell.nof_prb * RE_X_RB / 2 - 36; } else { ptr = output; - input += nof_prb * RE_X_RB / 2 - 36; + input += cell.nof_prb * RE_X_RB / 2 - 36; } - + /* symbol 0 & 1 */ for (i = 0; i < 2; i++) { - prb_cp_ref(&input, &output, cell_id % 3, 4, 6, put); + prb_cp_ref(&input, &output, cell.id % 3, 4, 4*6, put); + if (put) { + output += cell.nof_prb * RE_X_RB - 2*36; + } else { + input += cell.nof_prb * RE_X_RB - 2*36; + } } /* symbols 2 & 3 */ - if (CP_ISNORM(cp)) { + if (CP_ISNORM(cell.cp)) { for (i = 0; i < 2; i++) { prb_cp(&input, &output, 6); + if (put) { + output += cell.nof_prb * RE_X_RB - 2*36; + } else { + input += cell.nof_prb * RE_X_RB - 2*36; + } } } else { prb_cp(&input, &output, 6); - prb_cp_ref(&input, &output, cell_id % 3, 4, 6, put); + if (put) { + output += cell.nof_prb * RE_X_RB - 2*36; + } else { + input += cell.nof_prb * RE_X_RB - 2*36; + } + prb_cp_ref(&input, &output, cell.id % 3, 4, 4*6, put); } if (put) { return input - ptr; @@ -90,9 +104,8 @@ int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, * * 36.211 10.3 section 6.6.4 */ -int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, - int cell_id) { - return pbch_cp(pbch, slot1_data, nof_prb, cp, cell_id, true); +int pbch_put(cf_t *pbch, cf_t *slot1_data, lte_cell_t cell) { + return pbch_cp(pbch, slot1_data, cell, true); } /** @@ -102,92 +115,96 @@ int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, * * 36.211 10.3 section 6.6.4 */ -int pbch_get(cf_t *slot1_data, cf_t *pbch, int nof_prb, lte_cp_t cp, - int cell_id) { - return pbch_cp(slot1_data, pbch, nof_prb, cp, cell_id, false); +int pbch_get(cf_t *slot1_data, cf_t *pbch, lte_cell_t cell) { + return pbch_cp(slot1_data, pbch, cell, false); } -/** Initializes the PBCH transmitter and receiver */ -int pbch_init(pbch_t *q, int nof_prb, int cell_id, lte_cp_t cp) { - int ret = -1; - if (cell_id < 0) { - return -1; - } - bzero(q, sizeof(pbch_t)); - q->cell_id = cell_id; - q->cp = cp; - q->nof_prb = nof_prb; +/** Initializes the PBCH transmitter and receiver. + * At the receiver, the field nof_ports in the cell structure indicates the + * maximum number of BS transmitter ports to look for. + */ +int pbch_init(pbch_t *q, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; - if (modem_table_std(&q->mod, LTE_QPSK, true)) { - goto clean; - } - demod_soft_init(&q->demod); - demod_soft_table_set(&q->demod, &q->mod); - demod_soft_alg_set(&q->demod, APPROX); - if (sequence_pbch(&q->seq_pbch, q->cp, q->cell_id)) { - goto clean; - } + if (q != NULL && + lte_cell_isvalid(&cell)) + { + ret = LIBLTE_ERROR; - int poly[3] = { 0x6D, 0x4F, 0x57 }; - if (viterbi_init(&q->decoder, viterbi_37, poly, 40, true)) { - goto clean; - } - if (crc_init(&q->crc, LTE_CRC16, 16)) { - goto clean; - } - q->encoder.K = 7; - q->encoder.R = 3; - q->encoder.tail_biting = true; - memcpy(q->encoder.poly, poly, 3 * sizeof(int)); + bzero(q, sizeof(pbch_t)); + q->cell = cell; - q->nof_symbols = (CP_ISNORM(q->cp)) ? PBCH_RE_CPNORM : PBCH_RE_CPEXT; + if (modem_table_lte(&q->mod, LTE_QPSK, true)) { + goto clean; + } + demod_soft_init(&q->demod); + demod_soft_table_set(&q->demod, &q->mod); + demod_soft_alg_set(&q->demod, APPROX); + if (sequence_pbch(&q->seq_pbch, q->cell.cp, q->cell.id)) { + goto clean; + } - q->pbch_d = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pbch_d) { - goto clean; - } - int i; - for (i = 0; i < MAX_PORTS_CTRL; i++) { - q->ce[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->ce[i]) { + uint32_t poly[3] = { 0x6D, 0x4F, 0x57 }; + if (viterbi_init(&q->decoder, viterbi_37, poly, 40, true)) { goto clean; } - q->pbch_x[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pbch_x[i]) { + if (crc_init(&q->crc, LTE_CRC16, 16)) { goto clean; } - q->pbch_symbols[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pbch_symbols[i]) { + q->encoder.K = 7; + q->encoder.R = 3; + q->encoder.tail_biting = true; + memcpy(q->encoder.poly, poly, 3 * sizeof(int)); + + q->nof_symbols = (CP_ISNORM(q->cell.cp)) ? PBCH_RE_CPNORM : PBCH_RE_CPEXT; + + q->pbch_d = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_d) { goto clean; } + int i; + for (i = 0; i < q->cell.nof_ports; i++) { + q->ce[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->ce[i]) { + goto clean; + } + q->pbch_x[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_x[i]) { + goto clean; + } + q->pbch_symbols[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_symbols[i]) { + goto clean; + } + } + q->pbch_llr = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->pbch_llr) { + goto clean; + } + q->temp = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->temp) { + goto clean; + } + q->pbch_rm_f = malloc(sizeof(float) * 120); + if (!q->pbch_rm_f) { + goto clean; + } + q->pbch_rm_b = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->pbch_rm_b) { + goto clean; + } + q->data = malloc(sizeof(char) * 40); + if (!q->data) { + goto clean; + } + q->data_enc = malloc(sizeof(char) * 120); + if (!q->data_enc) { + goto clean; + } + ret = LIBLTE_SUCCESS; } - q->pbch_llr = malloc(sizeof(float) * q->nof_symbols * 4 * 2); - if (!q->pbch_llr) { - goto clean; - } - q->temp = malloc(sizeof(float) * q->nof_symbols * 4 * 2); - if (!q->temp) { - goto clean; - } - q->pbch_rm_f = malloc(sizeof(float) * 120); - if (!q->pbch_rm_f) { - goto clean; - } - q->pbch_rm_b = malloc(sizeof(float) * q->nof_symbols * 4 * 2); - if (!q->pbch_rm_b) { - goto clean; - } - q->data = malloc(sizeof(char) * 40); - if (!q->data) { - goto clean; - } - q->data_enc = malloc(sizeof(char) * 120); - if (!q->data_enc) { - goto clean; - } - - ret = 0; - clean: if (ret == -1) { +clean: + if (ret == LIBLTE_ERROR) { pbch_free(q); } return ret; @@ -198,7 +215,7 @@ void pbch_free(pbch_t *q) { free(q->pbch_d); } int i; - for (i = 0; i < MAX_PORTS_CTRL; i++) { + for (i = 0; i < q->cell.nof_ports; i++) { if (q->ce[i]) { free(q->ce[i]); } @@ -313,7 +330,8 @@ void pbch_mib_pack(pbch_mib_t *mib, char *msg) { bit_pack(mib->sfn >> 2, &msg, 8); } -void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib) { +void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib, uint32_t cell_id) { + printf(" - Cell ID: %d\n", cell_id); printf(" - Nof ports: %d\n", mib->nof_ports); printf(" - PRB: %d\n", mib->nof_prb); printf(" - PHICH Length: %s\n", @@ -355,15 +373,28 @@ void crc_set_mask(char *data, int nof_ports) { * * Returns 0 if the data is correct, -1 otherwise */ -int pbch_crc_check(pbch_t *q, char *bits, int nof_ports) { +uint32_t pbch_crc_check(pbch_t *q, char *bits, uint32_t nof_ports) { char data[40]; memcpy(data, bits, 40 * sizeof(char)); crc_set_mask(data, nof_ports); - return crc_checksum(&q->crc, data, 40); + int ret = crc_checksum(&q->crc, data, 40); + if (ret == 0) { + uint32_t chkzeros=0; + for (int i=0;i<24 && !chkzeros;i++) { + chkzeros += data[i]; + } + if (chkzeros) { + return 0; + } else { + return -1; + } + } else { + return ret; + } } -int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, - int nof_bits, int nof_ports) { +int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, uint32_t src, uint32_t dst, uint32_t n, + uint32_t nof_bits, uint32_t nof_ports) { int j; memcpy(&q->temp[dst * nof_bits], &q->pbch_llr[src * nof_bits], @@ -417,150 +448,168 @@ int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, /* Decodes the PBCH channel * * The PBCH spans in 40 ms. This function is called every 10 ms. It tries to decode the MIB - * given the symbols of the slot #1 of each radio frame. Successive calls will use more frames + * given the symbols of a subframe (1 ms). Successive calls will use more subframes * to help the decoding process. * * Returns 1 if successfully decoded MIB, 0 if not and -1 on error */ -int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], - float ebno, pbch_mib_t *mib) { - int src, dst, res, nb; - int nant_[3] = { 1, 2, 4 }; - int na, nant; - - /* Set pointers for layermapping & precoding */ +int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[MAX_PORTS], pbch_mib_t *mib) { + uint32_t src, dst, nb; + uint32_t nant_[3] = { 1, 2, 4 }; + uint32_t na, nant; int i; - int nof_bits = 2 * q->nof_symbols; + int nof_bits; cf_t *x[MAX_LAYERS]; + + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + slot1_symbols != NULL && + mib != NULL) + { + for (i=0;icell.nof_ports;i++) { + if (ce_slot1[i] == NULL) { + return LIBLTE_ERROR_INVALID_INPUTS; + } + } - /* number of layers equals number of ports */ - for (i = 0; i < MAX_PORTS_CTRL; i++) { - x[i] = q->pbch_x[i]; - } - memset(&x[MAX_PORTS_CTRL], 0, sizeof(cf_t*) * (MAX_LAYERS - MAX_PORTS_CTRL)); - - /* extract symbols */ - if (q->nof_symbols - != pbch_get(slot1_symbols, q->pbch_symbols[0], q->nof_prb, q->cp, - q->cell_id)) { - fprintf(stderr, "There was an error getting the PBCH symbols\n"); - return -1; - } + /* Set pointers for layermapping & precoding */ + nof_bits = 2 * q->nof_symbols; - /* extract channel estimates */ - for (i = 0; i < MAX_PORTS_CTRL; i++) { - if (q->nof_symbols - != pbch_get(ce[i], q->ce[i], q->nof_prb, q->cp, q->cell_id)) { + /* number of layers equals number of ports */ + for (i = 0; i < MAX_PORTS; i++) { + x[i] = q->pbch_x[i]; + } + memset(&x[MAX_PORTS], 0, sizeof(cf_t*) * (MAX_LAYERS - MAX_PORTS)); + + /* extract symbols */ + if (q->nof_symbols != pbch_get(slot1_symbols, q->pbch_symbols[0], q->cell)) { fprintf(stderr, "There was an error getting the PBCH symbols\n"); - return -1; + return LIBLTE_ERROR; } - } - q->frame_idx++; - res = 0; + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (q->nof_symbols != pbch_get(ce_slot1[i], q->ce[i], q->cell)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return LIBLTE_ERROR; + } + } - /* Try decoding for 1 to 4 antennas */ - for (na = 0; na < 3 && !res; na++) { - nant = nant_[na]; + q->frame_idx++; + ret = 0; - INFO("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); + /* Try decoding for 1 to cell.nof_ports antennas */ + for (na = 0; na < q->cell.nof_ports && !ret; na++) { + nant = nant_[na]; - /* in conctrol channels, only diversity is supported */ - if (nant == 1) { - /* no need for layer demapping */ - predecoding_single_zf(q->pbch_symbols[0], q->ce[0], q->pbch_d, - q->nof_symbols); - } else { - predecoding_diversity_zf(q->pbch_symbols[0], q->ce, x, nant, - q->nof_symbols); - layerdemap_diversity(x, q->pbch_d, nant, q->nof_symbols / nant); - } - - /* demodulate symbols */ - demod_soft_sigma_set(&q->demod, ebno); - demod_soft_demodulate(&q->demod, q->pbch_d, - &q->pbch_llr[nof_bits * (q->frame_idx - 1)], q->nof_symbols); - - /* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received - * 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234. - * We know they are ordered. - * - * FIXME: There are unnecessary checks because 2,3,4 have already been processed in the previous - * calls. - */ - for (nb = 0; nb < q->frame_idx && !res; nb++) { - for (dst = 0; (dst < 4 - nb) && !res; dst++) { - for (src = 0; src < q->frame_idx - nb && !res; src++) { - DEBUG("Trying %d blocks at offset %d as subframe mod4 number %d\n", - nb + 1, src, dst); - res = pbch_decode_frame(q, mib, src, dst, nb + 1, nof_bits, nant); + DEBUG("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); + + /* in conctrol channels, only diversity is supported */ + if (nant == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pbch_symbols[0], q->ce[0], q->pbch_d, + q->nof_symbols); + } else { + predecoding_diversity_zf(q->pbch_symbols[0], q->ce, x, nant, + q->nof_symbols); + layerdemap_diversity(x, q->pbch_d, nant, q->nof_symbols / nant); + } + + /* demodulate symbols */ + demod_soft_sigma_set(&q->demod, 1.0); + demod_soft_demodulate(&q->demod, q->pbch_d, + &q->pbch_llr[nof_bits * (q->frame_idx - 1)], q->nof_symbols); + + /* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received + * 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234. + * We know they are ordered. + * + * FIXME: There are unnecessary checks because 2,3,4 have already been processed in the previous + * calls. + */ + for (nb = 0; nb < q->frame_idx && !ret; nb++) { + for (dst = 0; (dst < 4 - nb) && !ret; dst++) { + for (src = 0; src < q->frame_idx - nb && !ret; src++) { + ret = pbch_decode_frame(q, mib, src, dst, nb + 1, nof_bits, nant); + + } } } } - } - /* If not found, make room for the next packet of radio frame symbols */ - if (q->frame_idx == 4) { - memmove(q->pbch_llr, &q->pbch_llr[nof_bits], nof_bits * 3 * sizeof(float)); - q->frame_idx = 3; + /* If not found, make room for the next packet of radio frame symbols */ + if (q->frame_idx == 4) { + memmove(q->pbch_llr, &q->pbch_llr[nof_bits], nof_bits * 3 * sizeof(float)); + q->frame_idx = 3; + } } - return res; + return ret; } /** Converts the MIB message to symbols mapped to SLOT #1 ready for transmission */ -void pbch_encode(pbch_t *q, pbch_mib_t *mib, - cf_t *slot1_symbols[MAX_PORTS_CTRL], int nof_ports) { +int pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS]) { int i; - int nof_bits = 2 * q->nof_symbols; - - assert(nof_ports <= MAX_PORTS_CTRL); - - /* Set pointers for layermapping & precoding */ + int nof_bits; cf_t *x[MAX_LAYERS]; + + if (q != NULL && + mib != NULL) + { + for (i=0;icell.nof_ports;i++) { + if (slot1_symbols[i] == NULL) { + return LIBLTE_ERROR_INVALID_INPUTS; + } + } + /* Set pointers for layermapping & precoding */ + nof_bits = 2 * q->nof_symbols; - /* number of layers equals number of ports */ - for (i = 0; i < nof_ports; i++) { - x[i] = q->pbch_x[i]; - } - memset(&x[nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - nof_ports)); + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pbch_x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); + + if (q->frame_idx == 0) { + /* pack MIB */ + pbch_mib_pack(mib, q->data); - if (q->frame_idx == 0) { - /* pack MIB */ - pbch_mib_pack(mib, q->data); + /* encode & modulate */ + crc_attach(&q->crc, q->data, 24); + crc_set_mask(q->data, q->cell.nof_ports); - /* encode & modulate */ - crc_attach(&q->crc, q->data, 24); - crc_set_mask(q->data, nof_ports); + convcoder_encode(&q->encoder, q->data, q->data_enc, 40); - convcoder_encode(&q->encoder, q->data, q->data_enc, 40); + rm_conv_tx(q->data_enc, 120, q->pbch_rm_b, 4 * nof_bits); - rm_conv_tx(q->data_enc, 120, q->pbch_rm_b, 4 * nof_bits); + } - } + scrambling_b_offset(&q->seq_pbch, &q->pbch_rm_b[q->frame_idx * nof_bits], + q->frame_idx * nof_bits, nof_bits); + mod_modulate(&q->mod, &q->pbch_rm_b[q->frame_idx * nof_bits], q->pbch_d, + nof_bits); - scrambling_b_offset(&q->seq_pbch, &q->pbch_rm_b[q->frame_idx * nof_bits], - q->frame_idx * nof_bits, nof_bits); - mod_modulate(&q->mod, &q->pbch_rm_b[q->frame_idx * nof_bits], q->pbch_d, - nof_bits); + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + layermap_diversity(q->pbch_d, x, q->cell.nof_ports, q->nof_symbols); + precoding_diversity(x, q->pbch_symbols, q->cell.nof_ports, + q->nof_symbols / q->cell.nof_ports); + } else { + memcpy(q->pbch_symbols[0], q->pbch_d, q->nof_symbols * sizeof(cf_t)); + } - /* layer mapping & precoding */ - if (nof_ports > 1) { - layermap_diversity(q->pbch_d, x, nof_ports, q->nof_symbols); - precoding_diversity(x, q->pbch_symbols, nof_ports, - q->nof_symbols / nof_ports); + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + pbch_put(q->pbch_symbols[i], slot1_symbols[i], q->cell); + } + q->frame_idx++; + if (q->frame_idx == 4) { + q->frame_idx = 0; + } + return LIBLTE_SUCCESS; } else { - memcpy(q->pbch_symbols[0], q->pbch_d, q->nof_symbols * sizeof(cf_t)); - } - - /* mapping to resource elements */ - for (i = 0; i < nof_ports; i++) { - pbch_put(q->pbch_symbols[i], slot1_symbols[i], q->nof_prb, q->cp, - q->cell_id); - } - q->frame_idx++; - if (q->frame_idx == 4) { - q->frame_idx = 0; + return LIBLTE_ERROR_INVALID_INPUTS; } } diff --git a/lte/phy/lib/phch/src/pcfich.c b/lte/phy/lib/phch/src/pcfich.c index e2fd52c5b..f65d3394e 100644 --- a/lte/phy/lib/phch/src/pcfich.c +++ b/lte/phy/lib/phch/src/pcfich.c @@ -54,37 +54,42 @@ bool pcfich_exists(int nframe, int nslot) { return true; } -/** Initializes the pcfich channel receiver */ -int pcfich_init(pcfich_t *q, regs_t *regs, int cell_id, int nof_prb, - int nof_ports, lte_cp_t cp) { - int ret = -1; - if (cell_id < 0) { - return -1; - } - bzero(q, sizeof(pcfich_t)); - q->cell_id = cell_id; - q->cp = cp; - q->regs = regs; - q->nof_prb = nof_prb; - q->nof_ports = nof_ports; - - if (modem_table_std(&q->mod, LTE_QPSK, false)) { - goto clean; - } +/** Initializes the pcfich channel receiver. + * On error, returns -1 and frees the structrure + */ +int pcfich_init(pcfich_t *q, regs_t *regs, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + regs != NULL && + lte_cell_isvalid(&cell)) + { + ret = LIBLTE_ERROR; + + bzero(q, sizeof(pcfich_t)); + q->cell = cell; + q->regs = regs; + + if (modem_table_lte(&q->mod, LTE_QPSK, false)) { + goto clean; + } - demod_hard_init(&q->demod); - demod_hard_table_set(&q->demod, LTE_QPSK); + demod_hard_init(&q->demod); + demod_hard_table_set(&q->demod, LTE_QPSK); - for (int nsf = 0; nsf < NSUBFRAMES_X_FRAME; nsf++) { - if (sequence_pcfich(&q->seq_pcfich[nsf], 2 * nsf, q->cell_id)) { - goto clean; + for (int nsf = 0; nsf < NSUBFRAMES_X_FRAME; nsf++) { + if (sequence_pcfich(&q->seq_pcfich[nsf], 2 * nsf, q->cell.id)) { + goto clean; + } } - } - q->nof_symbols = PCFICH_RE; + q->nof_symbols = PCFICH_RE; - ret = 0; - clean: if (ret == -1) { + ret = LIBLTE_SUCCESS; + } + + clean: + if (ret == LIBLTE_ERROR) { pcfich_free(q); } return ret; @@ -100,7 +105,7 @@ void pcfich_free(pcfich_t *q) { /** Finds the CFI with minimum distance with the vector of received 32 bits. * Saves the CFI value in the cfi pointer and returns the distance. */ -int pcfich_cfi_decode(char bits[PCFICH_CFI_LEN], int *cfi) { +int pcfich_cfi_decode(char bits[PCFICH_CFI_LEN], uint32_t *cfi) { int i, j; int distance, index = -1; int min = 32; @@ -120,7 +125,6 @@ int pcfich_cfi_decode(char bits[PCFICH_CFI_LEN], int *cfi) { *cfi = index + 1; } return min; - } /** Encodes the CFI producing a vector of 32 bits. @@ -128,19 +132,19 @@ int pcfich_cfi_decode(char bits[PCFICH_CFI_LEN], int *cfi) { */ int pcfich_cfi_encode(int cfi, char bits[PCFICH_CFI_LEN]) { if (cfi < 1 || cfi > 3) { - fprintf(stderr, "Invalid CFI %d\n", cfi); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; + } else{ + memcpy(bits, cfi_table[cfi - 1], PCFICH_CFI_LEN * sizeof(char)); + return LIBLTE_SUCCESS; } - memcpy(bits, cfi_table[cfi - 1], PCFICH_CFI_LEN * sizeof(char)); - return 0; } /* Decodes the PCFICH channel and saves the CFI in the cfi pointer. * * Returns 1 if successfully decoded the CFI, 0 if not and -1 on error */ -int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - int nsubframe, int *cfi, int *distance) { +int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS], + uint32_t nsubframe, uint32_t *cfi, uint32_t *distance) { int dist; /* Set pointers for layermapping & precoding */ @@ -148,112 +152,120 @@ int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], cf_t *x[MAX_LAYERS]; cf_t *ce_precoding[MAX_PORTS]; - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid nslot %d\n", nsubframe); - return -1; - } + if (q != NULL && + slot_symbols != NULL && + nsubframe < NSUBFRAMES_X_FRAME) + { - /* number of layers equals number of ports */ - for (i = 0; i < MAX_PORTS_CTRL; i++) { - x[i] = q->pcfich_x[i]; - } - for (i = 0; i < MAX_PORTS; i++) { - ce_precoding[i] = q->ce[i]; - } - - /* extract symbols */ - if (q->nof_symbols - != regs_pcfich_get(q->regs, slot_symbols, q->pcfich_symbols[0])) { - fprintf(stderr, "There was an error getting the PCFICH symbols\n"); - return -1; - } + /* number of layers equals number of ports */ + for (i = 0; i < MAX_PORTS; i++) { + x[i] = q->pcfich_x[i]; + } + for (i = 0; i < MAX_PORTS; i++) { + ce_precoding[i] = q->ce[i]; + } - /* extract channel estimates */ - for (i = 0; i < q->nof_ports; i++) { - if (q->nof_symbols != regs_pcfich_get(q->regs, ce[i], q->ce[i])) { + /* extract symbols */ + if (q->nof_symbols + != regs_pcfich_get(q->regs, slot_symbols, q->pcfich_symbols[0])) { fprintf(stderr, "There was an error getting the PCFICH symbols\n"); - return -1; + return LIBLTE_ERROR; } - } - /* in control channels, only diversity is supported */ - if (q->nof_ports == 1) { - /* no need for layer demapping */ - predecoding_single_zf(q->pcfich_symbols[0], q->ce[0], q->pcfich_d, - q->nof_symbols); - } else { - predecoding_diversity_zf(q->pcfich_symbols[0], ce_precoding, x, - q->nof_ports, q->nof_symbols); - layerdemap_diversity(x, q->pcfich_d, q->nof_ports, - q->nof_symbols / q->nof_ports); - } + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (q->nof_symbols != regs_pcfich_get(q->regs, ce[i], q->ce[i])) { + fprintf(stderr, "There was an error getting the PCFICH symbols\n"); + return LIBLTE_ERROR; + } + } - /* demodulate symbols */ - demod_hard_demodulate(&q->demod, q->pcfich_d, q->data, q->nof_symbols); + /* in control channels, only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pcfich_symbols[0], q->ce[0], q->pcfich_d, + q->nof_symbols); + } else { + predecoding_diversity_zf(q->pcfich_symbols[0], ce_precoding, x, + q->cell.nof_ports, q->nof_symbols); + layerdemap_diversity(x, q->pcfich_d, q->cell.nof_ports, + q->nof_symbols / q->cell.nof_ports); + } - /* Scramble with the sequence for slot nslot */ - scrambling_b(&q->seq_pcfich[nsubframe], q->data); + /* demodulate symbols */ + demod_hard_demodulate(&q->demod, q->pcfich_d, q->data, q->nof_symbols); - /* decode CFI */ - dist = pcfich_cfi_decode(q->data, cfi); - if (distance) { - *distance = dist; - } - if (dist < PCFICH_MAX_DISTANCE) { - return 1; + /* Scramble with the sequence for slot nslot */ + scrambling_b(&q->seq_pcfich[nsubframe], q->data); + + /* decode CFI */ + dist = pcfich_cfi_decode(q->data, cfi); + if (distance) { + *distance = dist; + } + if (dist < PCFICH_MAX_DISTANCE) { + return 1; + } else { + return 0; + } } else { - return 0; + return LIBLTE_ERROR_INVALID_INPUTS; } + } /** Encodes CFI and maps symbols to the slot */ -int pcfich_encode(pcfich_t *q, int cfi, cf_t *slot_symbols[MAX_PORTS_CTRL], - int nsubframe) { +int pcfich_encode(pcfich_t *q, uint32_t cfi, cf_t *slot_symbols[MAX_PORTS], + uint32_t subframe) { int i; - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid nslot %d\n", nsubframe); - return -1; - } + if (q != NULL && + cfi < 3 && + slot_symbols != NULL && + subframe < NSUBFRAMES_X_FRAME) + { - /* Set pointers for layermapping & precoding */ - cf_t *x[MAX_LAYERS]; - cf_t *symbols_precoding[MAX_PORTS]; + /* Set pointers for layermapping & precoding */ + cf_t *x[MAX_LAYERS]; + cf_t *symbols_precoding[MAX_PORTS]; - /* number of layers equals number of ports */ - for (i = 0; i < q->nof_ports; i++) { - x[i] = q->pcfich_x[i]; - } - for (i = 0; i < MAX_PORTS; i++) { - symbols_precoding[i] = q->pcfich_symbols[i]; - } + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pcfich_x[i]; + } + for (i = 0; i < MAX_PORTS; i++) { + symbols_precoding[i] = q->pcfich_symbols[i]; + } - /* pack MIB */ - pcfich_cfi_encode(cfi, q->data); + /* pack CFI */ + pcfich_cfi_encode(cfi, q->data); - /* scramble for slot sequence nslot */ - scrambling_b(&q->seq_pcfich[nsubframe], q->data); + /* scramble for slot sequence nslot */ + scrambling_b(&q->seq_pcfich[subframe], q->data); - mod_modulate(&q->mod, q->data, q->pcfich_d, PCFICH_CFI_LEN); + mod_modulate(&q->mod, q->data, q->pcfich_d, PCFICH_CFI_LEN); - /* layer mapping & precoding */ - if (q->nof_ports > 1) { - layermap_diversity(q->pcfich_d, x, q->nof_ports, q->nof_symbols); - precoding_diversity(x, symbols_precoding, q->nof_ports, - q->nof_symbols / q->nof_ports); - } else { - memcpy(q->pcfich_symbols[0], q->pcfich_d, q->nof_symbols * sizeof(cf_t)); - } + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + layermap_diversity(q->pcfich_d, x, q->cell.nof_ports, q->nof_symbols); + precoding_diversity(x, symbols_precoding, q->cell.nof_ports, + q->nof_symbols / q->cell.nof_ports); + } else { + memcpy(q->pcfich_symbols[0], q->pcfich_d, q->nof_symbols * sizeof(cf_t)); + } - /* mapping to resource elements */ - for (i = 0; i < q->nof_ports; i++) { - if (regs_pcfich_put(q->regs, q->pcfich_symbols[i], slot_symbols[i]) < 0) { - fprintf(stderr, "Error putting PCHICH resource elements\n"); - return -1; + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (regs_pcfich_put(q->regs, q->pcfich_symbols[i], slot_symbols[i]) < 0) { + fprintf(stderr, "Error putting PCHICH resource elements\n"); + return LIBLTE_ERROR; + } } + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } - - return 0; } + diff --git a/lte/phy/lib/phch/src/pdcch.c b/lte/phy/lib/phch/src/pdcch.c index d0522322c..99d49b4aa 100644 --- a/lte/phy/lib/phch/src/pdcch.c +++ b/lte/phy/lib/phch/src/pdcch.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include "liblte/phy/phch/dci.h" @@ -42,257 +41,115 @@ #include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/debug.h" -#define PDCCH_NOF_FORMATS 4 -#define PDCCH_FORMAT_NOF_CCE(i) (1<b)?b:a) -/** - * 36.213 9.1 - */ -int gen_common_search(dci_candidate_t *c, int nof_cce, int nof_bits, - unsigned short rnti) { - int i, l, L, k; - k = 0; - for (l = 3; l > 1; l--) { - L = (1 << l); - for (i = 0; i < MIN(nof_cce,16) / (L); i++) { - c[k].L = l; - c[k].nof_bits = nof_bits; - c[k].rnti = rnti; - c[k].ncce = (L) * (i % (nof_cce / (L))); - INFO("Common SS Candidate %d: RNTI: 0x%x, nCCE: %d, Nbits: %d, L: %d\n", - k, c[k].rnti, c[k].ncce, c[k].nof_bits, c[k].L); - k++; - } - } - return k; -} -/** - * 36.213 9.1 - */ -int gen_ue_search(dci_candidate_t *c, int nof_cce, int nof_bits, - unsigned short rnti, int subframe) { - int i, l, L, k, m; - unsigned int Yk; - const int S[4] = { 6, 12, 8, 16 }; - k = 0; - if (!subframe) { - INFO("UE-specific candidates for RNTI: 0x%x, NofBits: %d, NofCCE: %d\n", - rnti, nof_bits, nof_cce); - if (VERBOSE_ISINFO()) - printf("[INFO]: "); - } - for (l = 3; l >= 0; l--) { - L = (1 << l); - for (i = 0; i < MIN(nof_cce / L, 16 / S[l]); i++) { - c[k].L = l; - c[k].nof_bits = nof_bits; - c[k].rnti = rnti; - Yk = rnti; - for (m = 0; m < subframe; m++) { - Yk = (39827 * Yk) % 65537; - } - c[k].ncce = L * ((Yk + i) % (nof_cce / L)); - if (!subframe) { - if (VERBOSE_ISINFO()) { - printf("(%d, %d), ", c[k].ncce, c[k].L); - } - } - k++; - } - } - if (!subframe) { - if (VERBOSE_ISINFO()) - printf("\n"); - } - return k; -} +#define NOF_COMMON_FORMATS 2 +const dci_format_t common_formats[NOF_COMMON_FORMATS] = { Format1A, Format1C }; -void pdcch_init_common(pdcch_t *q, pdcch_search_t *s, unsigned short rnti) { - int k, i; - s->nof_candidates = NOF_COMMON_FORMATS - * (MIN(q->nof_cce,16) / 4 + MIN(q->nof_cce,16) / 8); - if (s->nof_candidates) { - s->candidates[0] = malloc(sizeof(dci_candidate_t) * s->nof_candidates); - dci_candidate_t *c = s->candidates[0]; - s->nof_candidates = 0; - if (c) { - // Format 1A and 1C L=4 and L=8, 4 and 2 candidates, only if nof_cce > 16 - k = 0; - for (i = 0; i < NOF_COMMON_FORMATS; i++) { - k += gen_common_search(&c[k], q->nof_cce, - dci_format_sizeof(common_formats[i], q->nof_prb), SIRNTI); - s->nof_candidates++; - } - } - } -} +#define NOF_UE_FORMATS 2 +const dci_format_t ue_formats[NOF_UE_FORMATS] = { Format0, Format1 }; // 1A has the same payload as 0 -/** 36.213 v9.3 Table 7.1-1: System Information DCI messages - * Expect DCI formats 1C and 1A in the common search space - */ -void pdcch_init_search_si(pdcch_t *q) { - pdcch_init_common(q, &q->search_mode[SEARCH_SI], SIRNTI); - q->current_search_mode = SEARCH_SI; -} -/** 36.213 v9.3 Table 7.1-5 - * user-specific search space. Currently supported transmission Mode 1: - * DCI Format 1A and 1 + PUSCH scheduling format 0 - */ -void pdcch_init_search_ue(pdcch_t *q, unsigned short c_rnti) { - int l, n, k, i; - pdcch_search_t *s = &q->search_mode[SEARCH_UE]; - s->nof_candidates = 0; - for (l = 0; l < 3; l++) { - s->nof_candidates += NOF_UE_FORMATS * (MIN(q->nof_cce,16) / (1 << l)); - } - INFO( - "Initiating %d candidate(s) in the UE-specific search space for C-RNTI: 0x%x\n", - s->nof_candidates, c_rnti); - if (s->nof_candidates) { - for (n = 0; n < NSUBFRAMES_X_FRAME; n++) { - s->candidates[n] = malloc(sizeof(dci_candidate_t) * s->nof_candidates); - dci_candidate_t *c = s->candidates[n]; - - if (c) { - // Expect Formats 1, 1A, 0 - k = 0; - for (i = 0; i < NOF_UE_FORMATS; i++) { - k += gen_ue_search(&c[k], q->nof_cce, - dci_format_sizeof(ue_formats[i], q->nof_prb), c_rnti, n); - } - } - } - } - q->current_search_mode = SEARCH_UE; +static void set_cfi(pdcch_t *q, uint32_t cfi) { + if (cfi > 0 && cfi < 4) { + q->nof_regs = (regs_pdcch_nregs(q->regs, cfi) / 9) * 9; + q->nof_cce = q->nof_regs / 9; + } } -/** 36.213 v9.3 Table 7.1-3 - * Expect DCI formats 1C and 1A in the common search space - */ -void pdcch_init_search_ra(pdcch_t *q, unsigned short ra_rnti) { - pdcch_init_common(q, &q->search_mode[SEARCH_RA], ra_rnti); - q->current_search_mode = SEARCH_RA; -} - -void pdcch_set_search_si(pdcch_t *q) { - q->current_search_mode = SEARCH_SI; -} -void pdcch_set_search_ue(pdcch_t *q) { - q->current_search_mode = SEARCH_UE; -} -void pdcch_set_search_ra(pdcch_t *q) { - q->current_search_mode = SEARCH_RA; -} /** Initializes the PDCCH transmitter and receiver */ -int pdcch_init(pdcch_t *q, regs_t *regs, int nof_prb, int nof_ports, - int cell_id, lte_cp_t cp) { - int ret = -1; - int i; +int pdcch_init(pdcch_t *q, regs_t *regs, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t i; - if (cell_id < 0) { - return -1; - } - if (nof_ports > MAX_PORTS_CTRL) { - fprintf(stderr, "Invalid number of ports %d\n", nof_ports); - return -1; - } - bzero(q, sizeof(pdcch_t)); - q->cell_id = cell_id; - q->cp = cp; - q->regs = regs; - q->nof_ports = nof_ports; - q->nof_prb = nof_prb; - q->current_search_mode = SEARCH_NONE; - - q->nof_regs = (regs_pdcch_nregs(q->regs) / 9) * 9; - q->nof_cce = q->nof_regs / 9; - q->nof_symbols = 4 * q->nof_regs; - q->nof_bits = 2 * q->nof_symbols; - - INFO("Init PDCCH: %d CCEs (%d REGs), %d bits, %d symbols, %d ports\n", - q->nof_cce, q->nof_regs, q->nof_bits, q->nof_symbols, q->nof_ports); - - if (modem_table_std(&q->mod, LTE_QPSK, true)) { - goto clean; - } - if (crc_init(&q->crc, LTE_CRC16, 16)) { - goto clean; - } + if (q != NULL && + regs != NULL && + lte_cell_isvalid(&cell)) + { + ret = LIBLTE_ERROR; + bzero(q, sizeof(pdcch_t)); + q->cell = cell; + q->regs = regs; - demod_soft_init(&q->demod); - demod_soft_table_set(&q->demod, &q->mod); - demod_soft_alg_set(&q->demod, APPROX); + /* Allocate memory for the largest aggregation level L=3 */ + q->max_bits = PDCCH_FORMAT_NOF_BITS(3); - for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { - if (sequence_pdcch(&q->seq_pdcch[i], 2 * i, q->cell_id, q->nof_bits)) { + INFO("Init PDCCH: %d bits, %d symbols, %d ports\n", q->max_bits, q->max_bits/2, q->cell.nof_ports); + + if (modem_table_lte(&q->mod, LTE_QPSK, true)) { + goto clean; + } + if (crc_init(&q->crc, LTE_CRC16, 16)) { goto clean; } - } - - int poly[3] = { 0x6D, 0x4F, 0x57 }; - if (viterbi_init(&q->decoder, viterbi_37, poly, DCI_MAX_BITS + 16, true)) { - goto clean; - } - q->pdcch_e = malloc(sizeof(char) * q->nof_bits); - if (!q->pdcch_e) { - goto clean; - } + demod_soft_init(&q->demod); + demod_soft_table_set(&q->demod, &q->mod); + demod_soft_alg_set(&q->demod, APPROX); - q->pdcch_llr = malloc(sizeof(float) * q->nof_bits); - if (!q->pdcch_llr) { - goto clean; - } + for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { + // we need to pregenerate the sequence for the maximum number of bits, which is 8 times + // the maximum number of REGs (for CFI=3) + if (sequence_pdcch(&q->seq_pdcch[i], 2 * i, q->cell.id, 8*regs_pdcch_nregs(q->regs, 3))) { + goto clean; + } + } - q->pdcch_d = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pdcch_d) { - goto clean; - } + uint32_t poly[3] = { 0x6D, 0x4F, 0x57 }; + if (viterbi_init(&q->decoder, viterbi_37, poly, DCI_MAX_BITS + 16, true)) { + goto clean; + } - for (i = 0; i < MAX_PORTS_CTRL; i++) { - q->ce[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->ce[i]) { + q->pdcch_e = malloc(sizeof(char) * q->max_bits); + if (!q->pdcch_e) { goto clean; } - q->pdcch_x[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pdcch_x[i]) { + + q->pdcch_llr = malloc(sizeof(float) * q->max_bits); + if (!q->pdcch_llr) { goto clean; } - q->pdcch_symbols[i] = malloc(sizeof(cf_t) * q->nof_symbols); - if (!q->pdcch_symbols[i]) { + + q->pdcch_d = malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->pdcch_d) { goto clean; } + + for (i = 0; i < MAX_PORTS; i++) { + q->ce[i] = malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->ce[i]) { + goto clean; + } + q->pdcch_x[i] = malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->pdcch_x[i]) { + goto clean; + } + q->pdcch_symbols[i] = malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->pdcch_symbols[i]) { + goto clean; + } + } + + ret = LIBLTE_SUCCESS; } - ret = 0; - clean: if (ret == -1) { + clean: + if (ret == LIBLTE_ERROR) { pdcch_free(q); } return ret; } void pdcch_free(pdcch_t *q) { - int i, j; + int i; - for (i = 0; i < PDCCH_NOF_SEARCH_MODES; i++) { - for (j = 0; j < NSUBFRAMES_X_FRAME; j++) { - if (q->search_mode[i].candidates[j]) { - free(q->search_mode[i].candidates[j]); - } - } - } if (q->pdcch_e) { free(q->pdcch_e); } @@ -302,7 +159,7 @@ void pdcch_free(pdcch_t *q) { if (q->pdcch_d) { free(q->pdcch_d); } - for (i = 0; i < MAX_PORTS_CTRL; i++) { + for (i = 0; i < MAX_PORTS; i++) { if (q->ce[i]) { free(q->ce[i]); } @@ -322,189 +179,257 @@ void pdcch_free(pdcch_t *q) { viterbi_free(&q->decoder); } -/** 36.212 5.3.3.2 to 5.3.3.4 - * - * Returns XOR between parity and remainder bits - * - * TODO: UE transmit antenna selection CRC mask +/** 36.213 v9.1.1 + * Computes up to max_candidates UE-specific candidates for DCI messages and saves them + * in the structure pointed by c. + * Returns the number of candidates saved in the array c. */ -unsigned short dci_decode(pdcch_t *q, float *e, char *data, int E, int nof_bits) { - - float tmp[3 * (DCI_MAX_BITS + 16)]; - unsigned short p_bits, crc_res; - char *x; - - assert(nof_bits < DCI_MAX_BITS); +uint32_t pdcch_ue_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidates, + uint32_t nsubframe, uint32_t cfi, uint16_t rnti) { + + int l; // this must be int because of the for(;;--) loop + uint32_t i, k, L, m; + uint32_t Yk, ncce; + const int S[4] = { 6, 12, 8, 16 }; - /* unrate matching */ - rm_conv_rx(e, E, tmp, 3 * (nof_bits + 16)); + set_cfi(q, cfi); - DEBUG("Viterbi input: ", 0); - if (VERBOSE_ISDEBUG()) { - vec_fprint_f(stdout, tmp, 3 * (nof_bits + 16)); + // Compute Yk for this subframe + Yk = rnti; + for (m = 0; m < nsubframe; m++) { + Yk = (39827 * Yk) % 65537; } - /* viterbi decoder */ - viterbi_decode_f(&q->decoder, tmp, data, nof_bits + 16); - - if (VERBOSE_ISDEBUG()) { - bit_fprint(stdout, data, nof_bits + 16); + k = 0; + // All aggregation levels from 8 to 1 + for (l = 3; l >= 0; l--) { + L = (1 << l); + // For all possible ncce offset + for (i = 0; i < MIN(q->nof_cce / L, 16 / S[l]); i++) { + ncce = L * ((Yk + i) % (q->nof_cce / L)); + if (k < max_candidates && + ncce + PDCCH_FORMAT_NOF_CCE(l) <= q->nof_cce) + { + c[k].L = l; + c[k].ncce = ncce; + + DEBUG("UE-specific SS Candidate %d: nCCE: %d, L: %d\n", + k, c[k].ncce, c[k].L); + + k++; + } + } } - x = &data[nof_bits]; - p_bits = (unsigned short) bit_unpack(&x, 16); - crc_res = ((unsigned short) crc_checksum(&q->crc, data, nof_bits) & 0xffff); - DEBUG("p_bits: 0x%x, crc_res: 0x%x, tot: 0x%x\n", p_bits, crc_res, - p_bits ^ crc_res); - return (p_bits ^ crc_res); + INFO("Initiated %d candidate(s) in the UE-specific search space for C-RNTI: 0x%x\n", k, rnti); + + return k; } -int pdcch_decode_candidate(pdcch_t *q, float *llr, dci_candidate_t *c, - dci_msg_t *msg) { - unsigned short crc_res; - DEBUG("Trying Candidate: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x\n", - c->nof_bits, PDCCH_FORMAT_NOF_BITS(c->L), c->ncce, c->L, c->rnti); - crc_res = dci_decode(q, &llr[72 * c->ncce], msg->data, - PDCCH_FORMAT_NOF_BITS(c->L), c->nof_bits); - - if (c->rnti == crc_res) { - memcpy(&msg->location, c, sizeof(dci_candidate_t)); - INFO("FOUND Candidate: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x\n", - c->nof_bits, PDCCH_FORMAT_NOF_BITS(c->L), c->ncce, c->L, c->rnti); - return 1; - } - return 0; -} -int pdcch_extract_llr(pdcch_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - float *llr, int nsubframe, float ebno) { - /* Set pointers for layermapping & precoding */ - int i; - cf_t *x[MAX_LAYERS]; +/** + * 36.213 9.1.1 + * Computes up to max_candidates candidates in the common search space + * for DCI messages and saves them in the structure pointed by c. + * Returns the number of candidates saved in the array c. + */ +uint32_t pdcch_common_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidates, + uint32_t cfi) +{ + uint32_t i, l, L, k; - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid subframe %d\n", nsubframe); - return -1; - } + set_cfi(q, cfi); - if (ebno == 0.0) { - fprintf(stderr, "EbNo is Zero\n"); - return -1; + k = 0; + for (l = 3; l > 1; l--) { + L = (1 << l); + for (i = 0; i < MIN(q->nof_cce, 16) / (L); i++) { + if (k < max_candidates) { + c[k].L = l; + c[k].ncce = (L) * (i % (q->nof_cce / (L))); + DEBUG("Common SS Candidate %d: nCCE: %d, L: %d\n", + k, c[k].ncce, c[k].L); + k++; + } + } } + + INFO("Initiated %d candidate(s) in the Common search space\n", k); + + return k; +} - /* number of layers equals number of ports */ - for (i = 0; i < q->nof_ports; i++) { - x[i] = q->pdcch_x[i]; - } - memset(&x[q->nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->nof_ports)); - - /* extract symbols */ - int n = regs_pdcch_get(q->regs, slot_symbols, q->pdcch_symbols[0]); - if (q->nof_symbols != n) { - fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", - q->nof_symbols, n); - return -1; - } - /* extract channel estimates */ - for (i = 0; i < q->nof_ports; i++) { - n = regs_pdcch_get(q->regs, ce[i], q->ce[i]); - if (q->nof_symbols != n) { - fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", - q->nof_symbols, n); - return -1; - } - } - /* in control channels, only diversity is supported */ - if (q->nof_ports == 1) { - /* no need for layer demapping */ - predecoding_single_zf(q->pdcch_symbols[0], q->ce[0], q->pdcch_d, - q->nof_symbols); - } else { - predecoding_diversity_zf(q->pdcch_symbols[0], q->ce, x, q->nof_ports, - q->nof_symbols); - layerdemap_diversity(x, q->pdcch_d, q->nof_ports, - q->nof_symbols / q->nof_ports); - } - DEBUG("pdcch d symbols: ", 0); - if (VERBOSE_ISDEBUG()) { - vec_fprint_c(stdout, q->pdcch_d, q->nof_symbols); - } - /* demodulate symbols */ - demod_soft_sigma_set(&q->demod, ebno); - demod_soft_demodulate(&q->demod, q->pdcch_d, q->pdcch_llr, q->nof_symbols); - DEBUG("llr: ", 0); - if (VERBOSE_ISDEBUG()) { - vec_fprint_f(stdout, q->pdcch_llr, q->nof_bits); - } +/** 36.212 5.3.3.2 to 5.3.3.4 + * + * Returns XOR between parity and remainder bits + * + * TODO: UE transmit antenna selection CRC mask + */ +static int dci_decode(pdcch_t *q, float *e, char *data, uint32_t E, uint32_t nof_bits, uint16_t *crc) { + + float tmp[3 * (DCI_MAX_BITS + 16)]; + uint16_t p_bits, crc_res; + char *x; - /* descramble */ - scrambling_f_offset(&q->seq_pdcch[nsubframe], llr, 0, q->nof_bits); + if (q != NULL && + data != NULL && + E <= q->max_bits && + nof_bits <= DCI_MAX_BITS) + { - return 0; -} + /* unrate matching */ + rm_conv_rx(e, E, tmp, 3 * (nof_bits + 16)); -int pdcch_decode_current_mode(pdcch_t *q, float *llr, dci_t *dci, int subframe) { - int k, i; + DEBUG("Viterbi input: ", 0); + if (VERBOSE_ISDEBUG()) { + vec_fprint_f(stdout, tmp, 3 * (nof_bits + 16)); + } - if (q->current_search_mode == SEARCH_UE) { - k = subframe; - } else { - k = 0; - } + /* viterbi decoder */ + viterbi_decode_f(&q->decoder, tmp, data, nof_bits + 16); - for (i = 0; - i < q->search_mode[q->current_search_mode].nof_candidates - && dci->nof_dcis < dci->max_dcis; i++) { - if (pdcch_decode_candidate(q, q->pdcch_llr, - &q->search_mode[q->current_search_mode].candidates[k][i], - &dci->msg[dci->nof_dcis])) { - dci->nof_dcis++; + if (VERBOSE_ISDEBUG()) { + bit_fprint(stdout, data, nof_bits + 16); } + + x = &data[nof_bits]; + p_bits = (uint16_t) bit_unpack(&x, 16); + crc_res = ((uint16_t) crc_checksum(&q->crc, data, nof_bits) & 0xffff); + DEBUG("p_bits: 0x%x, crc_checksum: 0x%x, crc_rem: 0x%x\n", p_bits, crc_res, + p_bits ^ crc_res); + + if (crc) { + *crc = p_bits ^ crc_res; + } + return LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters: E: %d, max_bits: %d, nof_bits: %d\n", E, q->max_bits, nof_bits); + return LIBLTE_ERROR_INVALID_INPUTS; } - return dci->nof_dcis; } -int pdcch_decode_si(pdcch_t *q, float *llr, dci_t *dci) { - pdcch_set_search_si(q); - return pdcch_decode_current_mode(q, llr, dci, 0); -} -int pdcch_decode_ra(pdcch_t *q, float *llr, dci_t *dci) { - pdcch_set_search_ra(q); - return pdcch_decode_current_mode(q, llr, dci, 0); -} -int pdcch_decode_ue(pdcch_t *q, float *llr, dci_t *dci, int nsubframe) { - pdcch_set_search_ue(q); - return pdcch_decode_current_mode(q, llr, dci, nsubframe); +/** Tries to decode a DCI message from the LLRs stored in the pdcch_t structure by the function + * pdcch_extract_llr(). This function can be called multiple times. + * The decoded message is stored in msg and the CRC remainder in crc_rem pointer + * + */ +int pdcch_decode_msg(pdcch_t *q, dci_msg_t *msg, dci_format_t format, uint16_t *crc_rem) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + msg != NULL && + crc_rem != NULL) + { + uint32_t nof_bits = dci_format_sizeof(format, q->cell.nof_prb); + + ret = dci_decode(q, q->pdcch_llr, msg->data, q->e_bits, nof_bits, crc_rem); + if (ret == LIBLTE_SUCCESS) { + msg->nof_bits = nof_bits; + } + } + return ret; } -/* Decodes PDCCH channels - * - * dci->nof_dcis is the size of the dci->msg buffer (ie max number of messages) - * - * Returns number of messages stored in dci +/** Extracts the LLRs from dci_location_t location of the subframe and stores them in the pdcch_t structure. + * DCI messages can be extracted from this location calling the function pdcch_decode_msg(). + * Every time this function is called (with a different location), the last demodulated symbols are overwritten and + * new messages from other locations can be decoded */ -int pdcch_decode(pdcch_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - dci_t *dci, int nsubframe, float ebno) { +int pdcch_extract_llr(pdcch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], + dci_location_t location, uint32_t nsubframe, uint32_t cfi) { - if (pdcch_extract_llr(q, slot_symbols, ce, q->pdcch_llr, nsubframe, ebno)) { - return -1; - } + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + /* Set pointers for layermapping & precoding */ + uint32_t i, nof_symbols; + cf_t *x[MAX_LAYERS]; - if (q->current_search_mode != SEARCH_NONE) { - return pdcch_decode_current_mode(q, q->pdcch_llr, dci, nsubframe); - } + if (q != NULL && + nsubframe < 10 && + cfi > 0 && + cfi < 4 && + dci_location_isvalid(&location)) + { + set_cfi(q, cfi); + + q->e_bits = PDCCH_FORMAT_NOF_BITS(location.L); + nof_symbols = q->e_bits/2; + ret = LIBLTE_ERROR; + + if (location.ncce + PDCCH_FORMAT_NOF_CCE(location.L) <= q->nof_cce) { + + INFO("Extracting LLRs: E: %d, nCCE: %d, L: %d, SF: %d, CFI: %d\n", + q->e_bits, location.ncce, location.L, nsubframe, cfi); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pdcch_x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); + + /* extract symbols */ + int n = regs_pdcch_get_offset(q->regs, sf_symbols, q->pdcch_symbols[0], + location.ncce * 9, PDCCH_FORMAT_NOF_REGS(location.L)); + if (nof_symbols != n) { + fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", nof_symbols, n); + return ret; + } + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + n = regs_pdcch_get_offset(q->regs, ce[i], q->ce[i], + location.ncce * 9, PDCCH_FORMAT_NOF_REGS(location.L)); + if (nof_symbols != n) { + fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", nof_symbols, n); + return ret; + } + } + + /* in control channels, only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pdcch_symbols[0], q->ce[0], q->pdcch_d, nof_symbols); + } else { + predecoding_diversity_zf(q->pdcch_symbols[0], q->ce, x, q->cell.nof_ports, nof_symbols); + layerdemap_diversity(x, q->pdcch_d, q->cell.nof_ports, nof_symbols / q->cell.nof_ports); + } + + DEBUG("pdcch d symbols: ", 0); + if (VERBOSE_ISDEBUG()) { + vec_fprint_c(stdout, q->pdcch_d, nof_symbols); + } - return 0; + /* demodulate symbols */ + demod_soft_sigma_set(&q->demod, 1.0); + demod_soft_demodulate(&q->demod, q->pdcch_d, q->pdcch_llr, nof_symbols); + + DEBUG("llr: ", 0); + if (VERBOSE_ISDEBUG()) { + vec_fprint_f(stdout, q->pdcch_llr, q->e_bits); + } + + /* descramble */ + scrambling_f_offset(&q->seq_pdcch[nsubframe], q->pdcch_llr, 72 * location.ncce, q->e_bits); + + ret = LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Illegal DCI message nCCE: %d, L: %d, nof_cce: %d\n", location.ncce, location.L, q->nof_cce); + } + } + return ret; } -void crc_set_mask_rnti(char *crc, unsigned short rnti) { - int i; + + + +static void crc_set_mask_rnti(char *crc, uint16_t rnti) { + uint32_t i; char mask[16]; char *r = mask; @@ -519,92 +444,115 @@ void crc_set_mask_rnti(char *crc, unsigned short rnti) { /** 36.212 5.3.3.2 to 5.3.3.4 * TODO: UE transmit antenna selection CRC mask */ -void dci_encode(pdcch_t *q, char *data, char *e, int nof_bits, int E, - unsigned short rnti) { +static int dci_encode(pdcch_t *q, char *data, char *e, uint32_t nof_bits, uint32_t E, + uint16_t rnti) { convcoder_t encoder; char tmp[3 * (DCI_MAX_BITS + 16)]; + + if (q != NULL && + data != NULL && + e != NULL && + nof_bits < DCI_MAX_BITS && + E < q->max_bits) + { + + int poly[3] = { 0x6D, 0x4F, 0x57 }; + encoder.K = 7; + encoder.R = 3; + encoder.tail_biting = true; + memcpy(encoder.poly, poly, 3 * sizeof(int)); + + crc_attach(&q->crc, data, nof_bits); + crc_set_mask_rnti(&data[nof_bits], rnti); + + convcoder_encode(&encoder, data, tmp, nof_bits + 16); + + DEBUG("CConv output: ", 0); + if (VERBOSE_ISDEBUG()) { + vec_fprint_b(stdout, tmp, 3 * (nof_bits + 16)); + } - assert(nof_bits < DCI_MAX_BITS); - - int poly[3] = { 0x6D, 0x4F, 0x57 }; - encoder.K = 7; - encoder.R = 3; - encoder.tail_biting = true; - memcpy(encoder.poly, poly, 3 * sizeof(int)); - - crc_attach(&q->crc, data, nof_bits); - crc_set_mask_rnti(&data[nof_bits], rnti); - - convcoder_encode(&encoder, data, tmp, nof_bits + 16); - - DEBUG("CConv output: ", 0); - if (VERBOSE_ISDEBUG()) { - vec_fprint_b(stdout, tmp, 3 * (nof_bits + 16)); + rm_conv_tx(tmp, 3 * (nof_bits + 16), e, E); + + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } - - rm_conv_tx(tmp, 3 * (nof_bits + 16), e, E); } -/** Converts the set of DCI messages to symbols mapped to the slot ready for transmission +/** Encodes ONE DCI message and allocates the encoded bits to the dci_location_t indicated by + * the parameter location. The CRC is scrambled with the RNTI parameter. + * This function can be called multiple times and encoded DCI messages will be allocated to the + * sf_symbols buffer ready for transmission. + * If the same location is provided in multiple messages, the encoded bits will be overwritten. + * + * @TODO: Use a bitmask and CFI to ensure message locations are valid and old messages are not overwritten. */ -int pdcch_encode(pdcch_t *q, dci_t *dci, cf_t *slot_symbols[MAX_PORTS_CTRL], - int nsubframe) { - int i; - /* Set pointers for layermapping & precoding */ - cf_t *x[MAX_LAYERS]; +int pdcch_encode(pdcch_t *q, dci_msg_t *msg, dci_location_t location, uint16_t rnti, + cf_t *sf_symbols[MAX_PORTS], uint32_t nsubframe, uint32_t cfi) { - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid subframe %d\n", nsubframe); - return -1; - } + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t i; + cf_t *x[MAX_LAYERS]; + uint32_t nof_symbols; + + if (q != NULL && + sf_symbols != NULL && + nsubframe < 10 && + cfi > 0 && + cfi < 4 && + dci_location_isvalid(&location)) + { + + set_cfi(q, cfi); + + q->e_bits = PDCCH_FORMAT_NOF_BITS(location.L); + nof_symbols = q->e_bits/2; + ret = LIBLTE_ERROR; + + if (location.ncce + PDCCH_FORMAT_NOF_CCE(location.L) <= q->nof_cce && + msg->nof_bits < DCI_MAX_BITS) + { + INFO("Encoding DCI: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x\n", + msg->nof_bits, q->e_bits, location.ncce, location.L, rnti); + + dci_encode(q, msg->data, q->pdcch_e, msg->nof_bits, q->e_bits, rnti); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pdcch_x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); - /* number of layers equals number of ports */ - for (i = 0; i < q->nof_ports; i++) { - x[i] = q->pdcch_x[i]; - } - memset(&x[q->nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->nof_ports)); - - /* should add elements? Or maybe random bits to facilitate power estimation */ - bzero(q->pdcch_e, q->nof_bits); - - /* Encode DCIs */ - for (i = 0; i < dci->nof_dcis; i++) { - /* do some sanity checks */ - if (dci->msg[i].location.ncce + PDCCH_FORMAT_NOF_CCE(dci->msg[i].location.L) - > q->nof_cce || dci->msg[i].location.L > 3 - || dci->msg[i].location.nof_bits > DCI_MAX_BITS) { - fprintf(stderr, "Illegal DCI message %d\n", i); - return -1; + scrambling_b_offset(&q->seq_pdcch[nsubframe], q->pdcch_e, 72 * location.ncce, q->e_bits); + + DEBUG("Scrambling output: ", 0); + if (VERBOSE_ISDEBUG()) { + vec_fprint_b(stdout, q->pdcch_e, q->e_bits); + } + + mod_modulate(&q->mod, q->pdcch_e, q->pdcch_d, q->e_bits); + + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + layermap_diversity(q->pdcch_d, x, q->cell.nof_ports, nof_symbols); + precoding_diversity(x, q->pdcch_symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports); + } else { + memcpy(q->pdcch_symbols[0], q->pdcch_d, nof_symbols * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + regs_pdcch_put_offset(q->regs, q->pdcch_symbols[i], sf_symbols[i], + location.ncce * 9, PDCCH_FORMAT_NOF_REGS(location.L)); + } + + ret = LIBLTE_SUCCESS; + + } else { + fprintf(stderr, "Illegal DCI message nCCE: %d, L: %d, nof_cce: %d\n", location.ncce, location.L, q->nof_cce); } - INFO("Encoding DCI %d: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x\n", i, - dci->msg[i].location.nof_bits, - PDCCH_FORMAT_NOF_BITS(dci->msg[i].location.L), - dci->msg[i].location.ncce, dci->msg[i].location.L, - dci->msg[i].location.rnti); - - dci_encode(q, dci->msg[i].data, &q->pdcch_e[72 * dci->msg[i].location.ncce], - dci->msg[i].location.nof_bits, - PDCCH_FORMAT_NOF_BITS(dci->msg[i].location.L), - dci->msg[i].location.rnti); - } - - scrambling_b_offset(&q->seq_pdcch[nsubframe], q->pdcch_e, 0, q->nof_bits); - - mod_modulate(&q->mod, q->pdcch_e, q->pdcch_d, q->nof_bits); - - /* layer mapping & precoding */ - if (q->nof_ports > 1) { - layermap_diversity(q->pdcch_d, x, q->nof_ports, q->nof_symbols); - precoding_diversity(x, q->pdcch_symbols, q->nof_ports, - q->nof_symbols / q->nof_ports); - } else { - memcpy(q->pdcch_symbols[0], q->pdcch_d, q->nof_symbols * sizeof(cf_t)); - } - - /* mapping to resource elements */ - for (i = 0; i < q->nof_ports; i++) { - regs_pdcch_put(q->regs, q->pdcch_symbols[i], slot_symbols[i]); - } - return 0; + } + return ret; } diff --git a/lte/phy/lib/phch/src/pdsch.c b/lte/phy/lib/phch/src/pdsch.c index 0314cd95b..2c21b7dd6 100644 --- a/lte/phy/lib/phch/src/pdsch.c +++ b/lte/phy/lib/phch/src/pdsch.c @@ -41,93 +41,98 @@ #include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/vector.h" -const enum modem_std modulations[4] = + +#define MAX_PDSCH_RE(cp) (2 * CP_NSYMB(cp) * 12) + + + +const lte_mod_t modulations[4] = { LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 }; + + -#define MAX_PDSCH_RE(cp) (2 * (CP_NSYMB(cp) - 1) * 12 - 6) -#define HAS_REF(l, cp, nof_ports) ((l == 1 && nof_ports == 4) \ - || l == 0 \ - || l == CP_NSYMB(cp) - 3) int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc, - int nsubframe, bool put) { - int s, n, l, lp, lstart, lend, nof_refs; + uint32_t nsubframe, bool put) { + uint32_t s, n, l, lp, lstart, lend, nof_refs; bool is_pbch, is_sss; cf_t *in_ptr = input, *out_ptr = output; - int offset; + uint32_t offset = 0; -assert(q->cell_id >= 0); - - INFO("%s %d RE from %d PRB\n", put ? "Putting" : "Getting", + INFO("%s %d RE from %d PRB\n", put ? "Putting" : "Getting", prb_alloc->re_sf[nsubframe], prb_alloc->slot[0].nof_prb); - if (q->nof_ports == 1) { + if (q->cell.nof_ports == 1) { nof_refs = 2; } else { nof_refs = 4; } for (s = 0; s < 2; s++) { - if (s == 0) { - lstart = prb_alloc->lstart; - } else { - lstart = 0; - } - - for (l = lstart; l < CP_NSYMB(q->cp); l++) { + for (l = 0; l < CP_NSYMB(q->cell.cp); l++) { for (n = 0; n < prb_alloc->slot[s].nof_prb; n++) { - lend = CP_NSYMB(q->cp); + if (s == 0) { + lstart = prb_alloc->lstart; + } else { + lstart = 0; + } + lend = CP_NSYMB(q->cell.cp); is_pbch = is_sss = false; // Skip PSS/SSS signals if (s == 0 && (nsubframe == 0 || nsubframe == 5)) { - if (prb_alloc->slot[s].prb_idx[n] >= q->nof_prb / 2 - 3 - && prb_alloc->slot[s].prb_idx[n] <= q->nof_prb / 2 + 3) { - lend = CP_NSYMB(q->cp) - 2; + if (prb_alloc->slot[s].prb_idx[n] >= q->cell.nof_prb / 2 - 3 + && prb_alloc->slot[s].prb_idx[n] <= q->cell.nof_prb / 2 + 3) { + lend = CP_NSYMB(q->cell.cp) - 2; is_sss = true; } } // Skip PBCH if (s == 1 && nsubframe == 0) { - if (prb_alloc->slot[s].prb_idx[n] >= q->nof_prb / 2 - 3 - && prb_alloc->slot[s].prb_idx[n] <= q->nof_prb / 2 + 3) { + if (prb_alloc->slot[s].prb_idx[n] >= q->cell.nof_prb / 2 - 3 + && prb_alloc->slot[s].prb_idx[n] <= q->cell.nof_prb / 2 + 3) { lstart = 4; is_pbch = true; } } - lp = l + s * CP_NSYMB(q->cp); + lp = l + s * CP_NSYMB(q->cell.cp); if (put) { - out_ptr = &output[(lp * q->nof_prb + prb_alloc->slot[s].prb_idx[n]) + out_ptr = &output[(lp * q->cell.nof_prb + prb_alloc->slot[s].prb_idx[n]) * RE_X_RB]; } else { - in_ptr = &input[(lp * q->nof_prb + prb_alloc->slot[s].prb_idx[n]) + in_ptr = &input[(lp * q->cell.nof_prb + prb_alloc->slot[s].prb_idx[n]) * RE_X_RB]; } - - if (is_pbch && (q->nof_prb % 2) - && (prb_alloc->slot[s].prb_idx[n] == q->nof_prb / 2 - 3 - && prb_alloc->slot[s].prb_idx[n] == q->nof_prb / 2 + 3)) { - if (l < lstart) { - prb_cp_half(&in_ptr, &out_ptr, 1); - } - } if (l >= lstart && l < lend) { - if (HAS_REF(l, q->cp, q->nof_ports)) { + if (SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { if (nof_refs == 2 && l != 0) { - offset = q->cell_id % 3 + 3; + offset = q->cell.id % 3 + 3; } else { - offset = q->cell_id % 3; + offset = q->cell.id % 3; } - prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, 1, put); + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs, put); } else { prb_cp(&in_ptr, &out_ptr, 1); } } - if (is_sss && (q->nof_prb % 2) - && (prb_alloc->slot[s].prb_idx[n] == q->nof_prb / 2 - 3 - && prb_alloc->slot[s].prb_idx[n] == q->nof_prb / 2 + 3)) { - if (l >= lend) { - prb_cp_half(&in_ptr, &out_ptr, 1); + if ((q->cell.nof_prb % 2) && ((is_pbch && l < lstart) || (is_sss && l >= lend))) { + if (prb_alloc->slot[s].prb_idx[n] == q->cell.nof_prb / 2 - 3) { + if (SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put); + } else { + prb_cp_half(&in_ptr, &out_ptr, 1); + } + } else if (prb_alloc->slot[s].prb_idx[n] == q->cell.nof_prb / 2 + 3) { + if (put) { + out_ptr += 6; + } else { + in_ptr += 6; + } + if (SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put); + } else { + prb_cp_half(&in_ptr, &out_ptr, 1); + } } } } @@ -135,9 +140,9 @@ assert(q->cell_id >= 0); } if (put) { - return (int) (input - in_ptr); + return abs((int) (input - in_ptr)); } else { - return (int) (output - out_ptr); + return abs((int) (output - out_ptr)); } } @@ -149,8 +154,8 @@ assert(q->cell_id >= 0); * 36.211 10.3 section 6.3.5 */ int pdsch_put(pdsch_t *q, cf_t *pdsch_symbols, cf_t *sf_symbols, - ra_prb_t *prb_alloc, int nsubframe) { - return pdsch_cp(q, pdsch_symbols, sf_symbols, prb_alloc, nsubframe, true); + ra_prb_t *prb_alloc, uint32_t subframe) { + return pdsch_cp(q, pdsch_symbols, sf_symbols, prb_alloc, subframe, true); } /** @@ -161,117 +166,94 @@ int pdsch_put(pdsch_t *q, cf_t *pdsch_symbols, cf_t *sf_symbols, * 36.211 10.3 section 6.3.5 */ int pdsch_get(pdsch_t *q, cf_t *sf_symbols, cf_t *pdsch_symbols, - ra_prb_t *prb_alloc, int nsubframe) { - return pdsch_cp(q, sf_symbols, pdsch_symbols, prb_alloc, nsubframe, false); + ra_prb_t *prb_alloc, uint32_t subframe) { + return pdsch_cp(q, sf_symbols, pdsch_symbols, prb_alloc, subframe, false); } /** Initializes the PDCCH transmitter and receiver */ -int pdsch_init(pdsch_t *q, unsigned short user_rnti, int nof_prb, int nof_ports, - int cell_id, lte_cp_t cp) { - int ret = -1; +int pdsch_init(pdsch_t *q, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; int i; - if (cell_id < 0) { - return -1; - } - - if (nof_ports > MAX_PORTS) { - fprintf(stderr, "Invalid number of ports %d\n", nof_ports); - return -1; - } - - bzero(q, sizeof(pdsch_t)); - q->cell_id = cell_id; - q->cp = cp; - q->nof_ports = nof_ports; - q->nof_prb = nof_prb; - q->rnti = user_rnti; - - q->max_symbols = nof_prb * MAX_PDSCH_RE(cp); - - INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->nof_ports, - q->nof_prb, q->max_symbols); - - for (i = 0; i < 4; i++) { - if (modem_table_std(&q->mod[i], modulations[i], true)) { + if (q != NULL && + lte_cell_isvalid(&cell)) + { + + bzero(q, sizeof(pdsch_t)); + ret = LIBLTE_ERROR; + + q->cell = cell; + q->average_nof_iterations_n = 0; + q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp); + + INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports, + q->cell.nof_prb, q->max_symbols); + + for (i = 0; i < 4; i++) { + if (modem_table_lte(&q->mod[i], modulations[i], true)) { + goto clean; + } + } + if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) { goto clean; } - } - if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) { - goto clean; - } - if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) { - goto clean; - } - - demod_soft_init(&q->demod); - demod_soft_alg_set(&q->demod, APPROX); - - for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { - if (sequence_pdsch(&q->seq_pdsch[i], q->rnti, 0, 2 * i, q->cell_id, - q->max_symbols * q->mod[3].nbits_x_symbol)) { + if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) { goto clean; } - } - if (tcod_init(&q->encoder, MAX_LONG_CB)) { - goto clean; - } - if (tdec_init(&q->decoder, MAX_LONG_CB)) { - goto clean; - } - if (rm_turbo_init(&q->rm_turbo, 3 * MAX_LONG_CB)) { - goto clean; - } + demod_soft_init(&q->demod); + demod_soft_alg_set(&q->demod, APPROX); + + q->rnti_is_set = false; - q->cb_in_b = malloc(sizeof(char) * MAX_LONG_CB); - if (!q->cb_in_b) { - goto clean; - } - q->cb_out_b = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12)); - if (!q->cb_out_b) { - goto clean; - } - - q->pdsch_rm_f = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12)); - if (!q->pdsch_rm_f) { - goto clean; - } - - q->pdsch_e_bits = malloc( - sizeof(char) * q->max_symbols * q->mod[3].nbits_x_symbol); - if (!q->pdsch_e_bits) { - goto clean; - } - - q->pdsch_llr = malloc( - sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol); - if (!q->pdsch_llr) { - goto clean; - } - - q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols); - if (!q->pdsch_d) { - goto clean; - } + if (tcod_init(&q->encoder, MAX_LONG_CB)) { + goto clean; + } + if (tdec_init(&q->decoder, MAX_LONG_CB)) { + goto clean; + } - for (i = 0; i < nof_ports; i++) { - q->ce[i] = malloc(sizeof(cf_t) * q->max_symbols); - if (!q->ce[i]) { + // Allocate floats for reception (LLRs) + q->cb_in = malloc(sizeof(char) * MAX_LONG_CB); + if (!q->cb_in) { goto clean; } - q->pdsch_x[i] = malloc(sizeof(cf_t) * q->max_symbols); - if (!q->pdsch_x[i]) { + + q->cb_out = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12)); + if (!q->cb_out) { goto clean; } - q->pdsch_symbols[i] = malloc(sizeof(cf_t) * q->max_symbols); - if (!q->pdsch_symbols[i]) { + + // Allocate floats for reception (LLRs) + q->pdsch_e = malloc(sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol); + if (!q->pdsch_e) { + goto clean; + } + + q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols); + if (!q->pdsch_d) { goto clean; } - } - ret = 0; - clean: if (ret == -1) { + for (i = 0; i < q->cell.nof_ports; i++) { + q->ce[i] = malloc(sizeof(cf_t) * q->max_symbols); + if (!q->ce[i]) { + goto clean; + } + q->pdsch_x[i] = malloc(sizeof(cf_t) * q->max_symbols); + if (!q->pdsch_x[i]) { + goto clean; + } + q->pdsch_symbols[i] = malloc(sizeof(cf_t) * q->max_symbols); + if (!q->pdsch_symbols[i]) { + goto clean; + } + } + + ret = LIBLTE_SUCCESS; + } + clean: + if (ret == LIBLTE_ERROR) { pdsch_free(q); } return ret; @@ -280,25 +262,19 @@ int pdsch_init(pdsch_t *q, unsigned short user_rnti, int nof_prb, int nof_ports, void pdsch_free(pdsch_t *q) { int i; - if (q->cb_in_b) { - free(q->cb_in_b); - } - if (q->cb_out_b) { - free(q->cb_out_b); - } - if (q->pdsch_e_bits) { - free(q->pdsch_e_bits); + if (q->cb_in) { + free(q->cb_in); } - if (q->pdsch_rm_f) { - free(q->pdsch_rm_f); + if (q->cb_out) { + free(q->cb_out); } - if (q->pdsch_llr) { - free(q->pdsch_llr); + if (q->pdsch_e) { + free(q->pdsch_e); } if (q->pdsch_d) { free(q->pdsch_d); } - for (i = 0; i < q->nof_ports; i++) { + for (i = 0; i < q->cell.nof_ports; i++) { if (q->ce[i]) { free(q->ce[i]); } @@ -319,23 +295,26 @@ void pdsch_free(pdsch_t *q) { } tdec_free(&q->decoder); tcod_free(&q->encoder); - rm_turbo_free(&q->rm_turbo); } -struct cb_segm { - int F; - int C; - int K1; - int K2; - int C1; - int C2; -}; - +int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) { + uint32_t i; + for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { + if (sequence_pdsch(&q->seq_pdsch[i], rnti, 0, 2 * i, q->cell.id, + q->max_symbols * q->mod[3].nbits_x_symbol)) { + return LIBLTE_ERROR; + } + } + q->rnti_is_set = true; + q->rnti = rnti; + return LIBLTE_SUCCESS; +} /* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */ -void codeblock_segmentation(struct cb_segm *s, int tbs) { - int Bp, B, idx1; - +static int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) { + uint32_t Bp, B, idx1; + int ret; + B = tbs + 24; /* Calculate CB sizes */ @@ -343,356 +322,577 @@ void codeblock_segmentation(struct cb_segm *s, int tbs) { s->C = 1; Bp = B; } else { - s->C = (int) ceilf((float) B / (6114 - 24)); + s->C = (uint32_t) ceilf((float) B / (6114 - 24)); Bp = B + 24 * s->C; } - idx1 = lte_find_cb_index(Bp / s->C); - s->K1 = lte_cb_size(idx1); - if (s->C == 1) { - s->K2 = 0; - s->C2 = 0; - s->C1 = 1; - } else { - s->K2 = lte_cb_size(idx1 - 1); - s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2); - s->C1 = s->C - s->C2; + ret = lte_find_cb_index(Bp / s->C); + if (ret != LIBLTE_ERROR) { + idx1 = (uint32_t) ret; + ret = lte_cb_size(idx1); + if (ret != LIBLTE_ERROR) { + s->K1 = (uint32_t) ret; + ret = lte_cb_size(idx1 - 1); + if (ret != LIBLTE_ERROR) { + if (s->C == 1) { + s->K2 = 0; + s->C2 = 0; + s->C1 = 1; + } else { + s->K2 = (uint32_t) ret; + s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2); + s->C1 = s->C - s->C2; + } + s->F = s->C1 * s->K1 + s->C2 * s->K2 - Bp; + INFO("CB Segmentation: TBS: %d, C=%d, C+=%d K+=%d, C-=%d, K-=%d, F=%d, Bp=%d\n", + tbs, s->C, s->C1, s->K1, s->C2, s->K2, s->F, Bp); + } + } + } + return ret; +} + +int pdsch_harq_init(pdsch_harq_t *p, pdsch_t *pdsch) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (p != NULL) { + uint32_t i; + bzero(p, sizeof(pdsch_harq_t)); + + p->cell = pdsch->cell; + ret = ra_tbs_from_idx(26, p->cell.nof_prb); + if (ret != LIBLTE_ERROR) { + p->max_cb = (uint32_t) ret / (6114 - 24) + 1; + + p->pdsch_w_buff_f = malloc(sizeof(float*) * p->max_cb); + if (!p->pdsch_w_buff_f) { + perror("malloc"); + return LIBLTE_ERROR; + } + + p->pdsch_w_buff_c = malloc(sizeof(char*) * p->max_cb); + if (!p->pdsch_w_buff_c) { + perror("malloc"); + return LIBLTE_ERROR; + } + + // We add 50 % larger buffer to the maximum expected bits per subframe + // FIXME: Use HARQ buffer limitation based on UE category + p->w_buff_size = p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp) * 6 * 2 / p->max_cb; + for (i=0;imax_cb;i++) { + p->pdsch_w_buff_f[i] = malloc(sizeof(float) * p->w_buff_size); + if (!p->pdsch_w_buff_f[i]) { + perror("malloc"); + return LIBLTE_ERROR; + } + p->pdsch_w_buff_c[i] = malloc(sizeof(char) * p->w_buff_size); + if (!p->pdsch_w_buff_c[i]) { + perror("malloc"); + return LIBLTE_ERROR; + } + } + ret = LIBLTE_SUCCESS; + } } - s->F = s->C1 * s->K1 + s->C2 * s->K2 - Bp; - INFO( - "CB Segmentation: TBS: %d, C=%d, C+=%d K+=%d, C-=%d, K-=%d, F=%d, Bp=%d\n", - tbs, s->C, s->C1, s->K1, s->C2, s->K2, s->F, Bp); + return ret; } +void pdsch_harq_free(pdsch_harq_t *p) { + if (p) { + uint32_t i; + if (p->pdsch_w_buff_f) { + for (i=0;imax_cb;i++) { + if (p->pdsch_w_buff_f[i]) { + free(p->pdsch_w_buff_f[i]); + } + } + free(p->pdsch_w_buff_f); + } + if (p->pdsch_w_buff_c) { + for (i=0;imax_cb;i++) { + if (p->pdsch_w_buff_c[i]) { + free(p->pdsch_w_buff_c[i]); + } + } + free(p->pdsch_w_buff_c); + } + bzero(p, sizeof(pdsch_harq_t)); + } +} + +int pdsch_harq_setup(pdsch_harq_t *p, ra_mcs_t mcs, ra_prb_t *prb_alloc) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (p != NULL && + mcs.tbs > 0) + { + uint32_t nof_bits, nof_bits_e, nof_symbols; + + p->mcs = mcs; + memcpy(&p->prb_alloc, prb_alloc, sizeof(ra_prb_t)); + + codeblock_segmentation(&p->cb_segm, mcs.tbs); + + nof_bits = mcs.tbs; + nof_symbols = prb_alloc->re_sf[1]; // Any subframe except 0 and 5 has maximum RE + nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(mcs.mod); + + if (nof_bits > nof_bits_e) { + fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); + return LIBLTE_ERROR; + } + + if (nof_symbols > p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp)) { + fprintf(stderr, + "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n", + nof_symbols, p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp), p->cell.nof_prb); + return LIBLTE_ERROR; + } + + if (p->cb_segm.C > p->max_cb) { + fprintf(stderr, "Codeblock segmentation returned more CBs (%d) than allocated (%d)\n", + p->cb_segm.C, p->max_cb); + return LIBLTE_ERROR; + } + ret = LIBLTE_SUCCESS; + } + return ret; +} + + +float pdsch_average_noi(pdsch_t *q) { + return q->average_nof_iterations; +} + +uint32_t pdsch_last_noi(pdsch_t *q) { + return q->nof_iterations; +} + + /* Decode a transport block according to 36.212 5.3.2 * */ -int pdsch_decode_tb(pdsch_t *q, char *data, int tbs, int nb_e, int rv_idx) { +int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, + pdsch_harq_t *harq_process, uint32_t rv_idx) +{ char parity[24]; char *p_parity = parity; - unsigned int par_rx, par_tx; - int i; - int cb_len, rp, wp, rlen, F, n_e; - struct cb_segm cbs; + uint32_t par_rx, par_tx; + uint32_t i; + uint32_t cb_len, rp, wp, rlen, F, n_e; + float *e_bits = q->pdsch_e; + + if (q != NULL && + data != NULL && + nb_e < q->max_symbols * q->mod[3].nbits_x_symbol) + { + + rp = 0; + rp = 0; + wp = 0; + for (i = 0; i < harq_process->cb_segm.C; i++) { + + /* Get read/write lengths */ + if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) { + cb_len = harq_process->cb_segm.K1; + } else { + cb_len = harq_process->cb_segm.K2; + } + if (harq_process->cb_segm.C == 1) { + rlen = cb_len; + } else { + rlen = cb_len - 24; + } + if (i == 0) { + F = harq_process->cb_segm.F; + } else { + F = 0; + } - /* Compute CB segmentation for this TBS */ - codeblock_segmentation(&cbs, tbs); + if (i < harq_process->cb_segm.C - 1) { + n_e = nb_e / harq_process->cb_segm.C; + } else { + n_e = nb_e - rp; + } - rp = 0; - rp = 0; - wp = 0; - for (i = 0; i < cbs.C; i++) { + DEBUG("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, + cb_len, rlen - F, wp, rp, F, n_e); - /* Get read/write lengths */ - if (i < cbs.C - cbs.C2) { - cb_len = cbs.K1; - } else { - cb_len = cbs.K2; - } - if (cbs.C == 1) { - rlen = cb_len; - } else { - rlen = cb_len - 24; - } - if (i == 0) { - F = cbs.F; - } else { - F = 0; - } + + /* Rate Unmatching */ + if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size, + &e_bits[rp], n_e, + (float*) q->cb_out, 3 * cb_len + 12, rv_idx)) { + fprintf(stderr, "Error in rate matching\n"); + return LIBLTE_ERROR; + } - if (i < cbs.C - 1) { - n_e = nb_e / cbs.C; - } else { - n_e = nb_e - rp; - } + /* Turbo Decoding with CRC-based early stopping */ + q->nof_iterations = 0; + bool early_stop = false; + uint32_t len_crc; + char *cb_in_ptr; + crc_t *crc_ptr; + tdec_reset(&q->decoder, cb_len); + + do { + + tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len); + q->nof_iterations++; + + if (harq_process->cb_segm.C > 1) { + len_crc = cb_len; + cb_in_ptr = q->cb_in; + crc_ptr = &q->crc_cb; + } else { + len_crc = tbs+24; + bzero(q->cb_in, F*sizeof(char)); + cb_in_ptr = &q->cb_in[F]; + crc_ptr = &q->crc_tb; + } - INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, - cb_len, rlen - F, wp, rp, F, n_e); + tdec_decision(&q->decoder, q->cb_in, cb_len); + + /* Check Codeblock CRC and stop early if incorrect */ + if (!crc_checksum(crc_ptr, cb_in_ptr, len_crc)) { + early_stop = true; + } + + } while (q->nof_iterations < TDEC_MAX_ITERATIONS && !early_stop); + + q->average_nof_iterations = EXPAVERAGE((float) q->nof_iterations, + q->average_nof_iterations, + q->average_nof_iterations_n); + q->average_nof_iterations_n++; + + /* Copy data to another buffer, removing the Codeblock CRC */ + if (i < harq_process->cb_segm.C - 1) { + memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(char)); + } else { + DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n", + rlen - F - 24, wp, F, rlen - 24); + + /* Append Transport Block parity bits to the last CB */ + memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char)); + memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(char)); + } - /* Rate Unmatching */ - rm_turbo_rx(&q->rm_turbo, &q->pdsch_llr[rp], n_e, q->pdsch_rm_f, - 3 * cb_len + 12, rv_idx); + /* Set read/write pointers */ + wp += (rlen - F); + rp += n_e; + } - /* Turbo Decoding */ - tdec_run_all(&q->decoder, q->pdsch_rm_f, q->cb_in_b, TDEC_ITERATIONS, - cb_len); + DEBUG("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); - if (cbs.C > 1) { - /* Check Codeblock CRC */ - //crc_attach(&q->crc_cb, q->pdsch_b[wp], cb_len); - } + // Compute transport block CRC + par_rx = crc_checksum(&q->crc_tb, data, tbs); + + // check parity bits + par_tx = bit_unpack(&p_parity, 24); - if (VERBOSE_ISDEBUG()) { - DEBUG("CB#%d Len=%d: ", i, cb_len); - vec_fprint_b(stdout, q->cb_in_b, cb_len); + if (!par_rx) { + INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0); } - /* Copy data to another buffer, removing the Codeblock CRC */ - if (i < cbs.C - 1) { - memcpy(&data[wp], &q->cb_in_b[F], (rlen - F) * sizeof(char)); + if (par_rx == par_tx) { + INFO("TB decoded OK\n",i); + return LIBLTE_SUCCESS; } else { - INFO("Last CB, appending parity: %d to %d from %d and 24 from %d\n", - rlen - F - 24, wp, F, rlen - 24); - /* Append Transport Block parity bits to the last CB */ - memcpy(&data[wp], &q->cb_in_b[F], (rlen - F - 24) * sizeof(char)); - memcpy(parity, &q->cb_in_b[rlen - 24], 24 * sizeof(char)); + INFO("Error in TB parity\n",i); + return LIBLTE_ERROR; } - - /* Set read/write pointers */ - wp += (rlen - F); - rp += n_e; - } - - INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); - - // Compute transport block CRC - par_rx = crc_checksum(&q->crc_tb, data, tbs); - - // check parity bits - par_tx = bit_unpack(&p_parity, 24); - - if (VERBOSE_ISDEBUG()) { - DEBUG("DATA: ", 0); - vec_fprint_b(stdout, data, tbs); - DEBUG("PARITY: ", 0); - vec_fprint_b(stdout, parity, 24); - } - - if (!par_rx) { - printf("\n\tCAUTION!! Received all-zero transport block\n\n"); + } else { + return LIBLTE_ERROR_INVALID_INPUTS; } - - return (par_rx != par_tx); } /** Decodes the PDSCH from the received symbols */ -int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, - int nsubframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) { +int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, uint32_t subframe, + pdsch_harq_t *harq_process, uint32_t rv_idx) +{ /* Set pointers for layermapping & precoding */ - int i; + uint32_t i, n; cf_t *x[MAX_LAYERS]; - int nof_symbols, nof_bits, nof_bits_e; - - nof_bits = mcs.tbs; - nof_symbols = prb_alloc->re_sf[nsubframe]; - nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol; - - if (nof_bits > nof_bits_e) { - fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); - return -1; - } - - if (nof_symbols > q->max_symbols) { - fprintf(stderr, - "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n", - nof_symbols, q->max_symbols, q->nof_prb); - return -1; - } - - INFO( - "Decoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n", - nsubframe, mcs.mod, nof_bits, nof_symbols, nof_bits_e); - - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid subframe %d\n", nsubframe); - return -1; - } - - /* number of layers equals number of ports */ - for (i = 0; i < q->nof_ports; i++) { - x[i] = q->pdsch_x[i]; - } - memset(&x[q->nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->nof_ports)); - - /* extract symbols */ - pdsch_get(q, sf_symbols, q->pdsch_symbols[0], prb_alloc, nsubframe); + uint32_t nof_symbols, nof_bits, nof_bits_e; + + if (q != NULL && + sf_symbols != NULL && + data != NULL && + subframe < 10 && + harq_process != NULL && + harq_process->mcs.mod > 0) + { + + nof_bits = harq_process->mcs.tbs; + nof_symbols = harq_process->prb_alloc.re_sf[subframe]; + nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol; + + + INFO("Decoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", + subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e, rv_idx); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pdsch_x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); + + /* extract symbols */ + n = pdsch_get(q, sf_symbols, q->pdsch_symbols[0], &harq_process->prb_alloc, subframe); + if (n != nof_symbols) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); + return LIBLTE_ERROR; + } - /* extract channel estimates */ - for (i = 0; i < q->nof_ports; i++) { - pdsch_get(q, ce[i], q->ce[i], prb_alloc, nsubframe); - } + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + n = pdsch_get(q, ce[i], q->ce[i], &harq_process->prb_alloc, subframe); + if (n != nof_symbols) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); + return LIBLTE_ERROR; + } + } + + /* TODO: only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pdsch_symbols[0], q->ce[0], q->pdsch_d, + nof_symbols); + } else { + predecoding_diversity_zf(q->pdsch_symbols[0], q->ce, x, q->cell.nof_ports, + nof_symbols); + layerdemap_diversity(x, q->pdsch_d, q->cell.nof_ports, + nof_symbols / q->cell.nof_ports); + } + + /* demodulate symbols + * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation, + * thus we don't need tot set it in the LLRs normalization + */ + demod_soft_sigma_set(&q->demod, 2.0 / q->mod[harq_process->mcs.mod - 1].nbits_x_symbol); + demod_soft_table_set(&q->demod, &q->mod[harq_process->mcs.mod - 1]); + demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols); + + /* + for (int j=0;jpdsch_d[j])) || isnan(cimagf(q->pdsch_d[j]))) { + printf("\nerror in d[%d]=%f+%f symbols:%f+%f ce0:%f+%f ce1:%f+%f\n",j, + crealf(q->pdsch_d[j]), cimagf(q->pdsch_d[j]), + crealf(q->pdsch_symbols[0][j]), cimagf(q->pdsch_symbols[0][j]), + crealf(q->ce[0][j]), cimagf(q->ce[0][j]), + crealf(q->ce[1][j]), cimagf(q->ce[1][j]) + ); + } + } + */ - /* TODO: only diversity is supported */ - if (q->nof_ports == 1) { - /* no need for layer demapping */ - predecoding_single_zf(q->pdsch_symbols[0], q->ce[0], q->pdsch_d, - nof_symbols); + /* descramble */ + scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 0, nof_bits_e); + + return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx); } else { - predecoding_diversity_zf(q->pdsch_symbols[0], q->ce, x, q->nof_ports, - nof_symbols); - layerdemap_diversity(x, q->pdsch_d, q->nof_ports, - nof_symbols / q->nof_ports); + return LIBLTE_ERROR_INVALID_INPUTS; } - - /* demodulate symbols */ - demod_soft_sigma_set(&q->demod, 2.0 / q->mod[mcs.mod - 1].nbits_x_symbol); - demod_soft_table_set(&q->demod, &q->mod[mcs.mod - 1]); - demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_llr, nof_symbols); - - /* descramble */ - scrambling_f_offset(&q->seq_pdsch[nsubframe], q->pdsch_llr, 0, nof_bits_e); - - return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, 0); } /* Encode a transport block according to 36.212 5.3.2 * */ -void pdsch_encode_tb(pdsch_t *q, char *data, int tbs, int nb_e, int rv_idx) { +int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, + pdsch_harq_t *harq_process, uint32_t rv_idx) +{ char parity[24]; char *p_parity = parity; - unsigned int par; - int i; - int cb_len, rp, wp, rlen, F, n_e; - struct cb_segm cbs; - - /* Compute CB segmentation */ - codeblock_segmentation(&cbs, tbs); - - /* Compute transport block CRC */ - par = crc_checksum(&q->crc_tb, data, tbs); - - /* parity bits will be appended later */ - bit_pack(par, &p_parity, 24); - - if (VERBOSE_ISDEBUG()) { - DEBUG("DATA: ", 0); - vec_fprint_b(stdout, data, tbs); - DEBUG("PARITY: ", 0); - vec_fprint_b(stdout, parity, 24); - } + uint32_t par; + uint32_t i; + uint32_t cb_len, rp, wp, rlen, F, n_e; + char *e_bits = q->pdsch_e; + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + data != NULL && + nb_e < q->max_symbols * q->mod[3].nbits_x_symbol) + { + + if (q->rnti_is_set) { + if (rv_idx == 0) { + /* Compute transport block CRC */ + par = crc_checksum(&q->crc_tb, data, tbs); + + /* parity bits will be appended later */ + bit_pack(par, &p_parity, 24); + + if (VERBOSE_ISDEBUG()) { + DEBUG("DATA: ", 0); + vec_fprint_b(stdout, data, tbs); + DEBUG("PARITY: ", 0); + vec_fprint_b(stdout, parity, 24); + } - /* Add filler bits to the new data buffer */ - for (i = 0; i < cbs.F; i++) { - q->cb_in_b[i] = LTE_NULL_BIT; - } + /* Add filler bits to the new data buffer */ + for (i = 0; i < harq_process->cb_segm.F; i++) { + q->cb_in[i] = LTE_NULL_BIT; + } + } + + wp = 0; + rp = 0; + for (i = 0; i < harq_process->cb_segm.C; i++) { + + /* Get read lengths */ + if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) { + cb_len = harq_process->cb_segm.K1; + } else { + cb_len = harq_process->cb_segm.K2; + } + if (harq_process->cb_segm.C > 1) { + rlen = cb_len - 24; + } else { + rlen = cb_len; + } + if (i == 0) { + F = harq_process->cb_segm.F; + } else { + F = 0; + } - wp = 0; - rp = 0; - for (i = 0; i < cbs.C; i++) { + if (i < harq_process->cb_segm.C - 1) { + n_e = nb_e / harq_process->cb_segm.C; + } else { + n_e = nb_e - wp; + } - /* Get read lengths */ - if (i < cbs.C - cbs.C2) { - cb_len = cbs.K1; - } else { - cb_len = cbs.K2; - } - if (cbs.C > 1) { - rlen = cb_len - 24; - } else { - rlen = cb_len; - } - if (i == 0) { - F = cbs.F; - } else { - F = 0; - } + INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, + cb_len, rlen - F, wp, rp, F, n_e); - if (i < cbs.C - 1) { - n_e = nb_e / cbs.C; - } else { - n_e = nb_e - wp; - } + if (rv_idx == 0) { + /* Copy data to another buffer, making space for the Codeblock CRC */ + if (i < harq_process->cb_segm.C - 1) { + memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(char)); + } else { + INFO("Last CB, appending parity: %d from %d and 24 to %d\n", + rlen - F - 24, rp, rlen - 24); + /* Append Transport Block parity bits to the last CB */ + memcpy(&q->cb_in[F], &data[rp], (rlen - F - 24) * sizeof(char)); + memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(char)); + } + if (harq_process->cb_segm.C > 1) { + /* Attach Codeblock CRC */ + crc_attach(&q->crc_cb, q->cb_in, rlen); + } + if (VERBOSE_ISDEBUG()) { + DEBUG("CB#%d Len=%d: ", i, cb_len); + vec_fprint_b(stdout, q->cb_in, cb_len); + } + /* Turbo Encoding */ + tcod_encode(&q->encoder, q->cb_in, (char*) q->cb_out, cb_len); + } + + /* Rate matching */ + if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size, + (char*) q->cb_out, 3 * cb_len + 12, + &e_bits[wp], n_e, rv_idx)) + { + fprintf(stderr, "Error in rate matching\n"); + return LIBLTE_ERROR; + } - INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, - cb_len, rlen - F, wp, rp, F, n_e); + /* Set read/write pointers */ + rp += (rlen - F); + wp += n_e; + } - /* Copy data to another buffer, making space for the Codeblock CRC */ - if (i < cbs.C - 1) { - memcpy(&q->cb_in_b[F], &data[rp], (rlen - F) * sizeof(char)); + INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); + + ret = LIBLTE_SUCCESS; } else { - INFO("Last CB, appending parity: %d from %d and 24 to %d\n", - rlen - F - 24, rp, rlen - 24); - /* Append Transport Block parity bits to the last CB */ - memcpy(&q->cb_in_b[F], &data[rp], (rlen - F - 24) * sizeof(char)); - memcpy(&q->cb_in_b[rlen - 24], parity, 24 * sizeof(char)); - } - - if (cbs.C > 1) { - /* Attach Codeblock CRC */ - crc_attach(&q->crc_cb, q->cb_in_b, rlen); + fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n"); } - - if (VERBOSE_ISDEBUG()) { - DEBUG("CB#%d Len=%d: ", i, cb_len); - vec_fprint_b(stdout, q->cb_in_b, cb_len); - } - - /* Turbo Encoding */ - tcod_encode(&q->encoder, q->cb_in_b, q->cb_out_b, cb_len); - - /* Rate matching */ - rm_turbo_tx(&q->rm_turbo, q->cb_out_b, 3 * cb_len + 12, - &q->pdsch_e_bits[wp], n_e, rv_idx); - - /* Set read/write pointers */ - rp += (rlen - F); - wp += n_e; - } - - INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); + } + return ret; } /** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission */ -int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], - int nsubframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) { +int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe, + pdsch_harq_t *harq_process, uint32_t rv_idx) +{ int i; - int nof_symbols, nof_bits, nof_bits_e; + uint32_t nof_symbols, nof_bits, nof_bits_e; /* Set pointers for layermapping & precoding */ cf_t *x[MAX_LAYERS]; + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + data != NULL && + subframe < 10 && + harq_process != NULL) + { + + if (q->rnti_is_set) { + for (i=0;icell.nof_ports;i++) { + if (sf_symbols[i] == NULL) { + return LIBLTE_ERROR_INVALID_INPUTS; + } + } + + nof_bits = harq_process->mcs.tbs; + nof_symbols = harq_process->prb_alloc.re_sf[subframe]; + nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol; - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid subframe %d\n", nsubframe); - return -1; - } - - nof_bits = mcs.tbs; - nof_symbols = prb_alloc->re_sf[nsubframe]; - nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol; - - if (nof_bits > nof_bits_e) { - fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); - return -1; - } - - if (nof_symbols > q->max_symbols) { - fprintf(stderr, - "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n", - nof_symbols, q->max_symbols, q->nof_prb); - return -1; - } - - INFO( - "Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n", - nsubframe, mcs.mod, nof_bits, nof_symbols, nof_bits_e); - - /* number of layers equals number of ports */ - for (i = 0; i < q->nof_ports; i++) { - x[i] = q->pdsch_x[i]; - } - memset(&x[q->nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->nof_ports)); + if (harq_process->mcs.tbs == 0) { + return LIBLTE_ERROR_INVALID_INPUTS; + } + + if (nof_bits > nof_bits_e) { + fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); + return LIBLTE_ERROR_INVALID_INPUTS; + } - pdsch_encode_tb(q, data, nof_bits, nof_bits_e, 0); + if (nof_symbols > q->max_symbols) { + fprintf(stderr, + "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n", + nof_symbols, q->max_symbols, q->cell.nof_prb); + return LIBLTE_ERROR_INVALID_INPUTS; + } - scrambling_b_offset(&q->seq_pdsch[nsubframe], q->pdsch_e_bits, 0, nof_bits_e); + INFO("Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", + subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e, rv_idx); - mod_modulate(&q->mod[mcs.mod - 1], q->pdsch_e_bits, q->pdsch_d, nof_bits_e); + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->pdsch_x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); - /* TODO: only diversity supported */ - if (q->nof_ports > 1) { - layermap_diversity(q->pdsch_d, x, q->nof_ports, nof_symbols); - precoding_diversity(x, q->pdsch_symbols, q->nof_ports, - nof_symbols / q->nof_ports); - } else { - memcpy(q->pdsch_symbols[0], q->pdsch_d, nof_symbols * sizeof(cf_t)); - } + if (pdsch_encode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx)) { + fprintf(stderr, "Error encoding TB\n"); + return LIBLTE_ERROR; + } + + scrambling_b_offset(&q->seq_pdsch[subframe], (char*) q->pdsch_e, 0, nof_bits_e); + + mod_modulate(&q->mod[harq_process->mcs.mod - 1], (char*) q->pdsch_e, q->pdsch_d, nof_bits_e); + + /* TODO: only diversity supported */ + if (q->cell.nof_ports > 1) { + layermap_diversity(q->pdsch_d, x, q->cell.nof_ports, nof_symbols); + precoding_diversity(x, q->pdsch_symbols, q->cell.nof_ports, + nof_symbols / q->cell.nof_ports); + } else { + memcpy(q->pdsch_symbols[0], q->pdsch_d, nof_symbols * sizeof(cf_t)); + } - /* mapping to resource elements */ - for (i = 0; i < q->nof_ports; i++) { - pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], prb_alloc, nsubframe); - } - return 0; + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe); + } + ret = LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n"); + } + } + return ret; } - + \ No newline at end of file diff --git a/lte/phy/lib/phch/src/phich.c b/lte/phy/lib/phch/src/phich.c index 31f282611..d27765269 100644 --- a/lte/phy/lib/phch/src/phich.c +++ b/lte/phy/lib/phch/src/phich.c @@ -49,46 +49,49 @@ const cf_t w_normal[PHICH_NORM_NSEQUENCES][4] = { { 1, 1, 1, 1 }, const cf_t w_ext[PHICH_EXT_NSEQUENCES][2] = { { 1, 1 }, { 1, -1 }, { I, I }, { I, -I } }; -bool phich_exists(int nframe, int nslot) { - return true; -} -int phich_ngroups(phich_t *q) { +uint32_t phich_ngroups(phich_t *q) { return regs_phich_ngroups(q->regs); } -void phich_reset(phich_t *q, cf_t *slot_symbols[MAX_PORTS_CTRL]) { +void phich_reset(phich_t *q, cf_t *slot_symbols[MAX_PORTS]) { int i; - for (i = 0; i < MAX_PORTS_CTRL; i++) { + for (i = 0; i < MAX_PORTS; i++) { regs_phich_reset(q->regs, slot_symbols[i]); } } /** Initializes the phich channel receiver */ -int phich_init(phich_t *q, regs_t *regs, int cell_id, int nof_prb, - int nof_tx_ports, lte_cp_t cp) { - int ret = -1; - bzero(q, sizeof(phich_t)); - q->cp = cp; - q->regs = regs; - q->nof_prb = nof_prb; - q->nof_tx_ports = nof_tx_ports; - - if (modem_table_std(&q->mod, LTE_BPSK, false)) { - goto clean; - } +int phich_init(phich_t *q, regs_t *regs, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + regs != NULL && + lte_cell_isvalid(&cell)) + { + + bzero(q, sizeof(phich_t)); + ret = LIBLTE_ERROR; + + q->cell = cell; + q->regs = regs; + + if (modem_table_lte(&q->mod, LTE_BPSK, false)) { + goto clean; + } - demod_hard_init(&q->demod); - demod_hard_table_set(&q->demod, LTE_BPSK); + demod_hard_init(&q->demod); + demod_hard_table_set(&q->demod, LTE_BPSK); - for (int nsf = 0; nsf < NSUBFRAMES_X_FRAME; nsf++) { - if (sequence_phich(&q->seq_phich[nsf], 2 * nsf, cell_id)) { - goto clean; + for (int nsf = 0; nsf < NSUBFRAMES_X_FRAME; nsf++) { + if (sequence_phich(&q->seq_phich[nsf], 2 * nsf, q->cell.id)) { + goto clean; + } } + ret = LIBLTE_SUCCESS; } - - ret = 0; - clean: if (ret == -1) { + clean: + if (ret == LIBLTE_ERROR) { phich_free(q); } return ret; @@ -104,7 +107,7 @@ void phich_free(phich_t *q) { /* Decodes ACK * */ -char phich_ack_decode(char bits[PHICH_NBITS], int *distance) { +char phich_ack_decode(char bits[PHICH_NBITS], uint32_t *distance) { int i, n; n = 0; @@ -136,39 +139,43 @@ void phich_ack_encode(char ack, char bits[PHICH_NBITS]) { * * Returns 1 if successfully decoded the CFI, 0 if not and -1 on error */ -int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], - int ngroup, int nseq, int nsubframe, char *ack, int *distance) { +int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS], + uint32_t ngroup, uint32_t nseq, uint32_t subframe, char *ack, uint32_t *distance) { /* Set pointers for layermapping & precoding */ int i, j; cf_t *x[MAX_LAYERS]; cf_t *ce_precoding[MAX_PORTS]; + + if (q == NULL || slot_symbols == NULL) { + return LIBLTE_ERROR_INVALID_INPUTS; + } - DEBUG("Decoding PHICH Ngroup: %d, Nseq: %d\n", ngroup, nseq); - - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid nslot %d\n", nsubframe); - return -1; + if (subframe >= NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", subframe); + return LIBLTE_ERROR_INVALID_INPUTS; } - if (CP_ISEXT(q->cp)) { - if (nseq < 0 || nseq > PHICH_EXT_NSEQUENCES) { + if (CP_ISEXT(q->cell.cp)) { + if (nseq >= PHICH_EXT_NSEQUENCES) { fprintf(stderr, "Invalid nseq %d\n", nseq); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } } else { - if (nseq < 0 || nseq > PHICH_NORM_NSEQUENCES) { + if (nseq >= PHICH_NORM_NSEQUENCES) { fprintf(stderr, "Invalid nseq %d\n", nseq); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } } if (ngroup >= regs_phich_ngroups(q->regs)) { fprintf(stderr, "Invalid ngroup %d\n", ngroup); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } + DEBUG("Decoding PHICH Ngroup: %d, Nseq: %d\n", ngroup, nseq); + /* number of layers equals number of ports */ - for (i = 0; i < MAX_PORTS_CTRL; i++) { + for (i = 0; i < MAX_PORTS; i++) { x[i] = q->phich_x[i]; } for (i = 0; i < MAX_PORTS; i++) { @@ -179,34 +186,34 @@ int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], if (PHICH_MAX_NSYMB != regs_phich_get(q->regs, slot_symbols, q->phich_symbols[0], ngroup)) { fprintf(stderr, "There was an error getting the phich symbols\n"); - return -1; + return LIBLTE_ERROR; } /* extract channel estimates */ - for (i = 0; i < q->nof_tx_ports; i++) { + for (i = 0; i < q->cell.nof_ports; i++) { if (PHICH_MAX_NSYMB != regs_phich_get(q->regs, ce[i], q->ce[i], ngroup)) { fprintf(stderr, "There was an error getting the phich symbols\n"); - return -1; + return LIBLTE_ERROR; } } /* in control channels, only diversity is supported */ - if (q->nof_tx_ports == 1) { + if (q->cell.nof_ports == 1) { /* no need for layer demapping */ predecoding_single_zf(q->phich_symbols[0], q->ce[0], q->phich_d0, PHICH_MAX_NSYMB); } else { predecoding_diversity_zf(q->phich_symbols[0], ce_precoding, x, - q->nof_tx_ports, PHICH_MAX_NSYMB); - layerdemap_diversity(x, q->phich_d0, q->nof_tx_ports, - PHICH_MAX_NSYMB / q->nof_tx_ports); + q->cell.nof_ports, PHICH_MAX_NSYMB); + layerdemap_diversity(x, q->phich_d0, q->cell.nof_ports, + PHICH_MAX_NSYMB / q->cell.nof_ports); } DEBUG("Recv!!: \n", 0); DEBUG("d0: ", 0); if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d0, PHICH_MAX_NSYMB); - if (CP_ISEXT(q->cp)) { + if (CP_ISEXT(q->cell.cp)) { if (ngroup % 2) { for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { q->phich_d[2 * i + 0] = q->phich_d0[4 * i + 2]; @@ -226,10 +233,10 @@ int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d, PHICH_EXT_MSYMB); - scrambling_c(&q->seq_phich[nsubframe], q->phich_d); + scrambling_c(&q->seq_phich[subframe], q->phich_d); /* De-spreading */ - if (CP_ISEXT(q->cp)) { + if (CP_ISEXT(q->cell.cp)) { for (i = 0; i < PHICH_NBITS; i++) { q->phich_z[i] = 0; for (j = 0; j < PHICH_EXT_NSF; j++) { @@ -257,43 +264,48 @@ int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], *ack = phich_ack_decode(q->data, distance); } - return 0; + return LIBLTE_SUCCESS; } /** Encodes ACK/NACK bits, modulates and inserts into resource. * The parameter ack is an array of phich_ngroups() pointers to buffers of nof_sequences chars */ -int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, - cf_t *slot_symbols[MAX_PORTS_CTRL]) { +int phich_encode(phich_t *q, char ack, uint32_t ngroup, uint32_t nseq, uint32_t subframe, + cf_t *slot_symbols[MAX_PORTS]) { int i; - if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { - fprintf(stderr, "Invalid nslot %d\n", nsubframe); - return -1; + if (q == NULL || slot_symbols == NULL) { + return LIBLTE_ERROR_INVALID_INPUTS; + } + + if (subframe >= NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", subframe); + return LIBLTE_ERROR_INVALID_INPUTS; } - if (CP_ISEXT(q->cp)) { - if (nseq < 0 || nseq > PHICH_EXT_NSEQUENCES) { + if (CP_ISEXT(q->cell.cp)) { + if (nseq >= PHICH_EXT_NSEQUENCES) { fprintf(stderr, "Invalid nseq %d\n", nseq); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } } else { - if (nseq < 0 || nseq > PHICH_NORM_NSEQUENCES) { + if (nseq >= PHICH_NORM_NSEQUENCES) { fprintf(stderr, "Invalid nseq %d\n", nseq); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } } if (ngroup >= regs_phich_ngroups(q->regs)) { fprintf(stderr, "Invalid ngroup %d\n", ngroup); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } + /* Set pointers for layermapping & precoding */ cf_t *x[MAX_LAYERS]; cf_t *symbols_precoding[MAX_PORTS]; /* number of layers equals number of ports */ - for (i = 0; i < q->nof_tx_ports; i++) { + for (i = 0; i < q->cell.nof_ports; i++) { x[i] = q->phich_x[i]; } for (i = 0; i < MAX_PORTS; i++) { @@ -310,7 +322,7 @@ int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, vec_fprint_c(stdout, q->phich_z, PHICH_NBITS); /* Spread with w */ - if (CP_ISEXT(q->cp)) { + if (CP_ISEXT(q->cell.cp)) { for (i = 0; i < PHICH_EXT_MSYMB; i++) { q->phich_d[i] = w_ext[nseq][i % PHICH_EXT_NSF] * q->phich_z[i / PHICH_EXT_NSF]; @@ -326,10 +338,10 @@ int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d, PHICH_EXT_MSYMB); - scrambling_c(&q->seq_phich[nsubframe], q->phich_d); + scrambling_c(&q->seq_phich[subframe], q->phich_d); /* align to REG */ - if (CP_ISEXT(q->cp)) { + if (CP_ISEXT(q->cell.cp)) { if (ngroup % 2) { for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { q->phich_d0[4 * i + 0] = 0; @@ -354,24 +366,24 @@ int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, vec_fprint_c(stdout, q->phich_d0, PHICH_MAX_NSYMB); /* layer mapping & precoding */ - if (q->nof_tx_ports > 1) { - layermap_diversity(q->phich_d0, x, q->nof_tx_ports, PHICH_MAX_NSYMB); - precoding_diversity(x, symbols_precoding, q->nof_tx_ports, - PHICH_MAX_NSYMB / q->nof_tx_ports); + if (q->cell.nof_ports > 1) { + layermap_diversity(q->phich_d0, x, q->cell.nof_ports, PHICH_MAX_NSYMB); + precoding_diversity(x, symbols_precoding, q->cell.nof_ports, + PHICH_MAX_NSYMB / q->cell.nof_ports); /**FIXME: According to 6.9.2, Precoding for 4 tx ports is different! */ } else { memcpy(q->phich_symbols[0], q->phich_d0, PHICH_MAX_NSYMB * sizeof(cf_t)); } /* mapping to resource elements */ - for (i = 0; i < q->nof_tx_ports; i++) { + for (i = 0; i < q->cell.nof_ports; i++) { if (regs_phich_add(q->regs, q->phich_symbols[i], ngroup, slot_symbols[i]) < 0) { fprintf(stderr, "Error putting PCHICH resource elements\n"); - return -1; + return LIBLTE_ERROR; } } - return 0; + return LIBLTE_SUCCESS; } diff --git a/lte/phy/lib/phch/src/prb.c b/lte/phy/lib/phch/src/prb.c index 527fb0716..3bd597af6 100644 --- a/lte/phy/lib/phch/src/prb.c +++ b/lte/phy/lib/phch/src/prb.c @@ -33,14 +33,14 @@ #include "liblte/phy/common/phy_common.h" void prb_cp_ref(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb, bool advance_output) { + int nof_intervals, bool advance_output) { int i; int ref_interval = ((RE_X_RB / nof_refs) - 1); memcpy(*output, *input, offset * sizeof(cf_t)); *input += offset; *output += offset; - for (i = 0; i < nof_refs * nof_prb - 1; i++) { + for (i = 0; i < nof_intervals - 1; i++) { if (advance_output) { (*output)++; } else { @@ -70,18 +70,18 @@ void prb_cp(cf_t **input, cf_t **output, int nof_prb) { void prb_cp_half(cf_t **input, cf_t **output, int nof_prb) { - memcpy(*output, *input, sizeof(cf_t) * RE_X_RB * nof_prb / 2); - *input += nof_prb * RE_X_RB / 2; - *output += nof_prb * RE_X_RB / 2; + memcpy(*output, *input, sizeof(cf_t) * RE_X_RB * nof_prb / 2); + *input += nof_prb * RE_X_RB / 2; + *output += nof_prb * RE_X_RB / 2; } void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb) { - prb_cp_ref(input, output, offset, nof_refs, nof_prb, false); + int nof_intervals) { + prb_cp_ref(input, output, offset, nof_refs, nof_intervals, false); } void prb_get_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb) { - prb_cp_ref(input, output, offset, nof_refs, nof_prb, true); + int nof_intervals) { + prb_cp_ref(input, output, offset, nof_refs, nof_intervals, true); } diff --git a/lte/phy/lib/phch/src/prb.h b/lte/phy/lib/phch/src/prb.h index 9df8f1bf5..0ca10fd5a 100644 --- a/lte/phy/lib/phch/src/prb.h +++ b/lte/phy/lib/phch/src/prb.h @@ -29,10 +29,10 @@ typedef _Complex float cf_t; void prb_cp_ref(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb, bool advance_input); + int nof_intervals, bool advance_input); void prb_cp(cf_t **input, cf_t **output, int nof_prb); void prb_cp_half(cf_t **input, cf_t **output, int nof_prb); void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb); + int nof_intervals); void phch_get_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, - int nof_prb); + int nof_intervals); diff --git a/lte/phy/lib/phch/src/ra.c b/lte/phy/lib/phch/src/ra.c index db9d4df8c..f48ff6324 100644 --- a/lte/phy/lib/phch/src/ra.c +++ b/lte/phy/lib/phch/src/ra.c @@ -41,23 +41,23 @@ #define min(a,b) (a= nof_prb / 2 - 3 && prb_idx <= nof_prb / 2 + 3)) { - if (nsubframe == 0) { - if (nslot == 0) { + if (subframe == 0) { + if (slot == 0) { re = (CP_NSYMB(cp) - nof_ctrl_symbols - 2) * RE_X_RB; } else { if (CP_ISEXT(cp)) { @@ -67,16 +67,16 @@ int ra_re_x_prb(int nsubframe, int nslot, int prb_idx, int nof_prb, re = (CP_NSYMB(cp) - 4) * RE_X_RB + 2 * nof_ports; } } - } else if (nsubframe == 5) { - if (nslot == 0) { + } else if (subframe == 5) { + if (slot == 0) { re = (CP_NSYMB(cp) - nof_ctrl_symbols - 2) * RE_X_RB; } } if ((nof_prb % 2) && (prb_idx == nof_prb / 2 - 3 || prb_idx == nof_prb / 2 + 3)) { - if (nslot == 0) { + if (slot == 0) { re += 2 * RE_X_RB / 2; - } else if (nsubframe == 0) { + } else if (subframe == 0) { re += 4 * RE_X_RB / 2 - nof_ports; if (CP_ISEXT(cp)) { re -= nof_ports > 2 ? 2 : nof_ports; @@ -90,10 +90,10 @@ int ra_re_x_prb(int nsubframe, int nslot, int prb_idx, int nof_prb, switch (nof_ports) { case 1: case 2: - re -= 2 * (nslot + 1) * nof_ports; + re -= 2 * (slot + 1) * nof_ports; break; case 4: - if (nslot == 1) { + if (slot == 1) { re -= 12; } else { re -= 4; @@ -109,14 +109,15 @@ int ra_re_x_prb(int nsubframe, int nslot, int prb_idx, int nof_prb, } /* Computes the number of RE for each PRB in the prb_dist structure */ -void ra_prb_get_re(ra_prb_t *prb_dist, int nof_prb, int nof_ports, - int nof_ctrl_symbols, lte_cp_t cp) { - int i, j, s; +void ra_prb_get_re_dl(ra_prb_t *prb_dist, uint32_t nof_prb, uint32_t nof_ports, + uint32_t nof_ctrl_symbols, lte_cp_t cp) { + uint32_t i, j, s; /* Set start symbol according to Section 7.1.6.4 in 36.213 */ prb_dist->lstart = nof_ctrl_symbols; // Compute number of RE per subframe for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { + prb_dist->re_sf[i] = 0; for (s = 0; s < 2; s++) { for (j = 0; j < prb_dist->slot[s].nof_prb; j++) { prb_dist->re_sf[i] += ra_re_x_prb(i, s, prb_dist->slot[s].prb_idx[j], @@ -138,25 +139,25 @@ void ra_prb_fprint(FILE *f, ra_prb_slot_t *prb) { } /** Compute PRB allocation for Downlink as defined in 8.1 of 36.213 */ -int ra_prb_get_ul(ra_prb_slot_t *prb, ra_pusch_t *ra, int nof_prb) { +int ra_prb_get_ul(ra_prb_slot_t *prb, ra_pusch_t *ra, uint32_t nof_prb) { int i; if (ra->type2_alloc.mode != t2_loc) { fprintf(stderr, "Uplink only accepts type2 localized scheduling\n"); - return -1; + return LIBLTE_ERROR; } for (i = 0; i < ra->type2_alloc.L_crb; i++) { prb->prb_idx[i] = i + ra->type2_alloc.RB_start; prb->nof_prb++; } - return 0; + return LIBLTE_SUCCESS; } /** Compute PRB allocation for Downlink as defined in 7.1.6 of 36.213 */ -int ra_prb_get_dl(ra_prb_t *prb_dist, ra_pdsch_t *ra, int nof_prb) { +int ra_prb_get_dl(ra_prb_t *prb_dist, ra_pdsch_t *ra, uint32_t nof_prb) { int i, j; uint32_t bitmask; - int P = ra_type0_P(nof_prb); - int n_rb_rbg_subset, n_rb_type1; + uint32_t P = ra_type0_P(nof_prb); + uint32_t n_rb_rbg_subset, n_rb_type1; bzero(prb_dist, sizeof(ra_prb_t)); switch (ra->alloc_type) { @@ -166,8 +167,10 @@ int ra_prb_get_dl(ra_prb_t *prb_dist, ra_pdsch_t *ra, int nof_prb) { for (i = 0; i < nb; i++) { if (bitmask & (1 << (nb - i - 1))) { for (j = 0; j < P; j++) { - prb_dist->slot[0].prb_idx[prb_dist->slot[0].nof_prb] = i * P + j; - prb_dist->slot[0].nof_prb++; + if (i*P+j < nof_prb) { + prb_dist->slot[0].prb_idx[prb_dist->slot[0].nof_prb] = i * P + j; + prb_dist->slot[0].nof_prb++; + } } } } @@ -257,35 +260,33 @@ int ra_prb_get_dl(ra_prb_t *prb_dist, ra_pdsch_t *ra, int nof_prb) { } break; default: - return -1; + return LIBLTE_ERROR; } - return 0; + return LIBLTE_SUCCESS; } /* Returns the number of allocated PRB for Uplink */ -int ra_nprb_ul(ra_pusch_t *ra, int nof_prb) { +uint32_t ra_nprb_ul(ra_pusch_t *ra, uint32_t nof_prb) { return ra->type2_alloc.L_crb; } /* Returns the number of allocated PRB for Downlink */ -int ra_nprb_dl(ra_pdsch_t *ra, int nof_prb) { - int nprb; - int nof_rbg, P; +uint32_t ra_nprb_dl(ra_pdsch_t *ra, uint32_t nof_prb) { + uint32_t nprb; + uint32_t nof_rbg, P; switch (ra->alloc_type) { case alloc_type0: // Get the number of allocated RBG except the last RBG nof_rbg = bit_count(ra->type0_alloc.rbg_bitmask & 0xFFFFFFFE); P = ra_type0_P(nof_prb); - if (nof_rbg > (int) ceilf((float) nof_prb / P)) { - fprintf(stderr, "Number of RGB (%d) can not exceed %d\n", nof_prb, - (int) ceilf((float) nof_prb / P)); - return -1; + if (nof_rbg > (uint32_t) ceilf((float) nof_prb / P)) { + nof_rbg = (uint32_t) ceilf((float) nof_prb / P) - 1; } nprb = nof_rbg * P; // last RBG may have smaller size. Add if set - int P_last = (nof_prb % P); + uint32_t P_last = (nof_prb % P); if (!P_last) P_last = P; nprb += P_last * (ra->type0_alloc.rbg_bitmask & 1); @@ -295,20 +296,20 @@ int ra_nprb_dl(ra_pdsch_t *ra, int nof_prb) { if (nprb > ra_type1_N_rb(nof_prb)) { fprintf(stderr, "Number of RB (%d) can not exceed %d\n", nprb, ra_type1_N_rb(nof_prb)); - return -1; + return LIBLTE_ERROR; } break; case alloc_type2: nprb = ra->type2_alloc.L_crb; break; default: - return -1; + return LIBLTE_ERROR; } return nprb; } /* RBG size for type0 scheduling as in table 7.1.6.1-1 of 36.213 */ -int ra_type0_P(int nof_prb) { +uint32_t ra_type0_P(uint32_t nof_prb) { if (nof_prb <= 10) { return 1; } else if (nof_prb <= 26) { @@ -321,15 +322,15 @@ int ra_type0_P(int nof_prb) { } /* Returns N_rb_type1 according to section 7.1.6.2 */ -int ra_type1_N_rb(int nof_prb) { - int P = ra_type0_P(nof_prb); - return (int) ceilf((float) nof_prb / P) - (int) ceilf(log2f((float) P)) - 1; +uint32_t ra_type1_N_rb(uint32_t nof_prb) { + uint32_t P = ra_type0_P(nof_prb); + return (uint32_t) ceilf((float) nof_prb / P) - (uint32_t) ceilf(log2f((float) P)) - 1; } /* Convert Type2 scheduling L_crb and RB_start to RIV value */ -uint32_t ra_type2_to_riv(uint16_t L_crb, uint16_t RB_start, int nof_prb) { +uint32_t ra_type2_to_riv(uint32_t L_crb, uint32_t RB_start, uint32_t nof_prb) { uint32_t riv; - if (L_crb <= (int) nof_prb / 2) { + if (L_crb <= nof_prb / 2) { riv = nof_prb * (L_crb - 1) + RB_start; } else { riv = nof_prb * (nof_prb - L_crb + 1) + nof_prb - 1 - RB_start; @@ -338,10 +339,10 @@ uint32_t ra_type2_to_riv(uint16_t L_crb, uint16_t RB_start, int nof_prb) { } /* Convert Type2 scheduling RIV value to L_crb and RB_start values */ -void ra_type2_from_riv(uint32_t riv, uint16_t *L_crb, uint16_t *RB_start, - int nof_prb, int nof_vrb) { - *L_crb = (int) (riv / nof_prb) + 1; - *RB_start = riv % nof_prb; +void ra_type2_from_riv(uint32_t riv, uint32_t *L_crb, uint32_t *RB_start, + uint32_t nof_prb, uint32_t nof_vrb) { + *L_crb = (uint32_t) (riv / nof_prb) + 1; + *RB_start = (uint32_t) (riv % nof_prb); if (*L_crb > nof_vrb - *RB_start) { *L_crb = nof_prb - (int) (riv / nof_prb) + 1; *RB_start = nof_prb - riv % nof_prb - 1; @@ -349,7 +350,7 @@ void ra_type2_from_riv(uint32_t riv, uint16_t *L_crb, uint16_t *RB_start, } /* Table 6.2.3.2-1 in 36.211 */ -int ra_type2_ngap(int nof_prb, bool ngap_is_1) { +uint32_t ra_type2_ngap(uint32_t nof_prb, bool ngap_is_1) { if (nof_prb <= 10) { return nof_prb / 2; } else if (nof_prb == 11) { @@ -372,7 +373,7 @@ int ra_type2_ngap(int nof_prb, bool ngap_is_1) { } /* Table 7.1.6.3-1 in 36.213 */ -int ra_type2_n_rb_step(int nof_prb) { +uint32_t ra_type2_n_rb_step(uint32_t nof_prb) { if (nof_prb < 50) { return 2; } else { @@ -381,143 +382,96 @@ int ra_type2_n_rb_step(int nof_prb) { } /* as defined in 6.2.3.2 of 36.211 */ -int ra_type2_n_vrb_dl(int nof_prb, bool ngap_is_1) { - int ngap = ra_type2_ngap(nof_prb, ngap_is_1); +uint32_t ra_type2_n_vrb_dl(uint32_t nof_prb, bool ngap_is_1) { + uint32_t ngap = ra_type2_ngap(nof_prb, ngap_is_1); if (ngap_is_1) { return 2 * (ngap < (nof_prb - ngap) ? ngap : nof_prb - ngap); } else { - return ((int) nof_prb / ngap) * 2 * ngap; - } -} - -/* Converts ra_mcs_t structure to MCS index for both Uplink and Downlink */ -uint8_t ra_mcs_to_table_idx(ra_mcs_t *mcs) { - switch (mcs->mod) { - case QPSK: - return mcs->tbs_idx; - case QAM16: - return mcs->tbs_idx + 1; - case QAM64: - return mcs->tbs_idx + 2; - default: - return 0; + return ((uint32_t) nof_prb / ngap) * 2 * ngap; } } /* Converts MCS index to ra_mcs_t structure for Downlink as defined inTable 7.1.7.1-1 on 36.213 */ -int ra_mcs_from_idx_dl(uint8_t idx, ra_mcs_t *mcs) { - if (idx < 10) { - mcs->mod = QPSK; - mcs->tbs_idx = idx; - } else if (idx < 17) { - mcs->mod = QAM16; - mcs->tbs_idx = idx - 1; - } else if (idx < 29) { - mcs->mod = QAM64; - mcs->tbs_idx = idx - 2; - } else if (idx == 29) { - mcs->mod = QPSK; - mcs->tbs_idx = 0; - } else if (idx == 30) { - mcs->mod = QAM16; - mcs->tbs_idx = 0; - } else if (idx == 31) { - mcs->mod = QAM64; - mcs->tbs_idx = 0; +int ra_mcs_from_idx_dl(uint32_t mcs_idx, uint32_t nof_prb, ra_mcs_t *mcs) { + if (mcs_idx < 10) { + mcs->mod = LTE_QPSK; + mcs->tbs = ra_tbs_from_idx(mcs_idx, nof_prb); + } else if (mcs_idx < 17) { + mcs->mod = LTE_QAM16; + mcs->tbs = ra_tbs_from_idx(mcs_idx - 1, nof_prb); + } else if (mcs_idx < 29) { + mcs->mod = LTE_QAM64; + mcs->tbs = ra_tbs_from_idx(mcs_idx - 2, nof_prb); + } else if (mcs_idx == 29) { + mcs->mod = LTE_QPSK; + mcs->tbs = 0; + } else if (mcs_idx == 30) { + mcs->mod = LTE_QAM16; + mcs->tbs = 0; + } else if (mcs_idx == 31) { + mcs->mod = LTE_QAM64; + mcs->tbs = 0; } else { - mcs->mod = MOD_NULL; - mcs->tbs_idx = 0; - return -1; + return LIBLTE_ERROR; } - return 0; + return LIBLTE_SUCCESS; } /* Converts MCS index to ra_mcs_t structure for Uplink as defined in Table 8.6.1-1 on 36.213 */ -int ra_mcs_from_idx_ul(uint8_t idx, ra_mcs_t *mcs) { - if (idx < 11) { - mcs->mod = QPSK; - mcs->tbs_idx = idx; - } else if (idx < 21) { - mcs->mod = QAM16; - mcs->tbs_idx = idx - 1; - } else if (idx < 29) { - mcs->mod = QAM64; - mcs->tbs_idx = idx - 2; +int ra_mcs_from_idx_ul(uint32_t mcs_idx, uint32_t nof_prb, ra_mcs_t *mcs) { + if (mcs_idx < 11) { + mcs->mod = LTE_QPSK; + mcs->tbs = ra_tbs_from_idx(mcs_idx, nof_prb); + } else if (mcs_idx < 21) { + mcs->mod = LTE_QAM16; + mcs->tbs = ra_tbs_from_idx(mcs_idx - 1, nof_prb); + } else if (mcs_idx < 29) { + mcs->mod = LTE_QAM64; + mcs->tbs = ra_tbs_from_idx(mcs_idx - 2, nof_prb); } else { - mcs->mod = MOD_NULL; - mcs->tbs_idx = 0; - return -1; + return LIBLTE_ERROR; } - return 0; + return LIBLTE_SUCCESS; } /* Downlink Transport Block size for Format 1C as defined in 7.1.7.2.2-1 on 36.213 */ -int ra_tbs_from_idx_format1c(uint8_t tbs_idx) { +int ra_tbs_from_idx_format1c(uint32_t tbs_idx) { if (tbs_idx < 32) { return tbs_format1c_table[tbs_idx]; } else { - return -1; + return LIBLTE_ERROR; } } -/* Returns lowest nearest index of TBS value in table 7.1.7.2.2-1 on 36.213 - * or -1 if the TBS value is not within the valid TBS values - */ -int ra_tbs_to_table_idx_format1c(int tbs) { - int idx; - if (tbs < tbs_format1c_table[0]) { - return -1; - } - for (idx = 1; idx < 32; idx++) { - if (tbs_format1c_table[idx - 1] <= tbs && tbs_format1c_table[idx] >= tbs) { - return idx; - } - } - return -1; -} - /* Downlink Transport Block size determination as defined in 7.1.7.2 on 36.213 */ -int ra_tbs_from_idx(uint8_t tbs_idx, int n_prb) { - if (tbs_idx < 27 && n_prb > 0 && n_prb <= 110) { +int ra_tbs_from_idx(uint32_t tbs_idx, uint32_t n_prb) { + if (tbs_idx < 27 && n_prb > 0 && n_prb <= MAX_PRB) { return tbs_table[tbs_idx][n_prb - 1]; } else { - return -1; + return LIBLTE_ERROR; } } /* Returns lowest nearest index of TBS value in table 7.1.7.2 on 36.213 * or -1 if the TBS value is not within the valid TBS values */ -int ra_tbs_to_table_idx(int tbs, int n_prb) { - int idx; - if (n_prb > 0 && n_prb <= 110) { - return -1; +int ra_tbs_to_table_idx(uint32_t tbs, uint32_t n_prb) { + uint32_t idx; + if (n_prb > 0 && n_prb <= MAX_PRB) { + return LIBLTE_ERROR; } if (tbs < tbs_table[0][n_prb]) { - return -1; + return LIBLTE_ERROR; } for (idx = 1; idx < 28; idx++) { if (tbs_table[idx - 1][n_prb] <= tbs && tbs_table[idx][n_prb] >= tbs) { return idx; } } - return -1; + return LIBLTE_ERROR; } -char *ra_mod_string(ra_mod_t mod) { - switch (mod) { - case QPSK: - return "QPSK"; - case QAM16: - return "QAM16"; - case QAM64: - return "QAM64"; - default: - return "N/A"; - } -} - -void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, int nof_prb) { +void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, uint32_t nof_prb) { fprintf(f, "Frequency Hopping:\t"); if (ra->freq_hop_fl == hop_disabled) { fprintf(f, "No"); @@ -540,16 +494,8 @@ char *ra_type_string(ra_type_t alloc_type) { } } -void ra_pdsch_set_mcs_index(ra_pdsch_t *ra, uint8_t mcs_idx) { - ra->mcs.mod = MOD_NULL; - ra->mcs.mcs_idx = mcs_idx; -} -void ra_pdsch_set_mcs(ra_pdsch_t *ra, ra_mod_t mod, uint8_t tbs_idx) { - ra->mcs.mod = mod; - ra->mcs.tbs_idx = tbs_idx; -} -void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, int nof_prb) { +void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, uint32_t nof_prb) { fprintf(f, " - Resource Allocation Type:\t\t%s\n", ra_type_string(ra->alloc_type)); switch (ra->alloc_type) { @@ -590,8 +536,8 @@ void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, int nof_prb) { } fprintf(f, " - Number of PRBs:\t\t\t%d\n", ra_nprb_dl(ra, nof_prb)); - fprintf(f, " - Modulation and coding scheme index:\t%d\n", ra->mcs.mcs_idx); - fprintf(f, " - Modulation type:\t\t\t%s\n", ra_mod_string(ra->mcs.mod)); + fprintf(f, " - Modulation and coding scheme index:\t%d\n", ra->mcs_idx); + fprintf(f, " - Modulation type:\t\t\t%s\n", lte_mod_string(ra->mcs.mod)); fprintf(f, " - Transport block size:\t\t%d\n", ra->mcs.tbs); fprintf(f, " - HARQ process:\t\t\t%d\n", ra->harq_process); fprintf(f, " - New data indicator:\t\t\t%s\n", ra->ndi ? "Yes" : "No"); diff --git a/lte/phy/lib/phch/src/regs.c b/lte/phy/lib/phch/src/regs.c index 014f0dce1..0c0bf5405 100644 --- a/lte/phy/lib/phch/src/regs.c +++ b/lte/phy/lib/phch/src/regs.c @@ -34,7 +34,26 @@ #include "liblte/phy/phch/regs.h" #include "liblte/phy/utils/debug.h" -regs_reg_t *regs_find_reg(regs_t *h, int k, int l); +#define REG_IDX(r, i, n) r->k[i]+r->l*n*RE_X_RB + + +regs_reg_t *regs_find_reg(regs_t *h, uint32_t k, uint32_t l); +int regs_put_reg(regs_reg_t *reg, + cf_t *reg_data, + cf_t *slot_symbols, + uint32_t nof_prb); + +int regs_add_reg(regs_reg_t *reg, + cf_t *reg_data, + cf_t *slot_symbols, + uint32_t nof_prb); + +int regs_get_reg(regs_reg_t *reg, + cf_t *slot_symbols, + cf_t *reg_data, + uint32_t nof_prb); + +int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, uint32_t nof_prb); /*************************************************************** @@ -62,14 +81,15 @@ const unsigned char PDCCH_PERM[PDCCH_NCOLS] = */ int regs_pdcch_init(regs_t *h) { int i, m, cfi, nof_ctrl_symbols; - int ret = -1; - int nrows, ndummy, j, k, kp; + int ret = LIBLTE_ERROR; + int nrows, ndummy, j; + uint32_t k, kp; regs_reg_t **tmp = NULL; bzero(&h->pdcch, sizeof(regs_ch_t)); for (cfi=0;cfi<3;cfi++) { - if (h->nof_prb < 10) { + if (h->cell.nof_prb < 10) { nof_ctrl_symbols = cfi+2; } else { nof_ctrl_symbols = cfi+1; @@ -110,9 +130,10 @@ int regs_pdcch_init(regs_t *h) { for (i = 0; i < nrows; i++) { if (i*PDCCH_NCOLS + PDCCH_PERM[j] >= ndummy) { m = i*PDCCH_NCOLS + PDCCH_PERM[j]-ndummy; - kp = (k-h->cell_id)%h->pdcch[cfi].nof_regs; - if (kp < 0) { - kp += h->pdcch[cfi].nof_regs; + if (k < h->cell.id) { + kp = (h->pdcch[cfi].nof_regs + k-h->cell.id)%h->pdcch[cfi].nof_regs; + } else { + kp = (k-h->cell.id)%h->pdcch[cfi].nof_regs; } h->pdcch[cfi].regs[m] = tmp[kp]; k++; @@ -122,59 +143,79 @@ int regs_pdcch_init(regs_t *h) { h->pdcch[cfi].nof_regs = (h->pdcch[cfi].nof_regs/9)*9; free(tmp); tmp = NULL; - if (VERBOSE_ISINFO() && cfi == 1) { - for (i=0;ipdcch[cfi].nof_regs;i++) { - INFO("Logical PDCCH REG#%d:%d (%d,%d)\n", i%9,i/9, - h->pdcch[cfi].regs[i]->k0, h->pdcch[cfi].regs[i]->l); - } - } } - ret = 0; + ret = LIBLTE_SUCCESS; clean_and_exit: if (tmp) { free(tmp); } - if (ret == -1) { + if (ret == LIBLTE_ERROR) { regs_pdcch_free(h); } return ret; } -int regs_pdcch_nregs(regs_t *h) { - if (h->cfi == -1) { - fprintf(stderr, "Must call regs_set_cfi() first\n"); - return -1; +int regs_pdcch_nregs(regs_t *h, uint32_t cfi) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return LIBLTE_ERROR; } else { - return h->pdcch[h->cfi].nof_regs; + return (int) h->pdcch[cfi-1].nof_regs; } } /** Copy quadruplets to REGs and cyclic shift them, according to the * second part of 6.8.5 in 36.211 */ -int regs_pdcch_put(regs_t *h, cf_t *pdcch_symbols, cf_t *slot_symbols) { - if (h->cfi == -1) { + +int regs_pdcch_put_offset(regs_t *h, cf_t *pdcch_symbols, cf_t *slot_symbols, uint32_t start_reg, uint32_t nof_regs) { + if (h->cfi_initiated) { + if (start_reg + nof_regs <= h->pdcch[h->cfi].nof_regs) { + uint32_t i, k; + k = 0; + for (i=start_reg;ipdcch[h->cfi].regs[i], &pdcch_symbols[k], slot_symbols, h->cell.nof_prb); + k += 4; + } + return k; + } else { + fprintf(stderr, "Out of range: start_reg + nof_reg must be lower than %d\n", h->pdcch[h->cfi].nof_regs); + return LIBLTE_ERROR; + } + } else { fprintf(stderr, "Must call regs_set_cfi() first\n"); - return -1; - } - int i; - for (i=0;ipdcch[h->cfi].nof_regs;i++) { - regs_put_reg(h->pdcch[h->cfi].regs[i], &pdcch_symbols[i*4], slot_symbols, h->nof_prb); + return LIBLTE_ERROR; } - return h->pdcch[h->cfi].nof_regs*4; } -int regs_pdcch_get(regs_t *h, cf_t *slot_symbols, cf_t *pdcch_symbols) { - if (h->cfi == -1) { +int regs_pdcch_put(regs_t *h, cf_t *pdcch_symbols, cf_t *slot_symbols) { + return regs_pdcch_put_offset(h, pdcch_symbols, slot_symbols, 0, h->pdcch[h->cfi].nof_regs); +} + +int regs_pdcch_get_offset(regs_t *h, cf_t *slot_symbols, cf_t *pdcch_symbols, uint32_t start_reg, uint32_t nof_regs) { + if (h->cfi_initiated) { + if (start_reg + nof_regs <= h->pdcch[h->cfi].nof_regs) { + uint32_t i, k; + k = 0; + for (i=start_reg;ipdcch[h->cfi].regs[i], slot_symbols, &pdcch_symbols[k], h->cell.nof_prb); + k += 4; + } + return k; + } else { + fprintf(stderr, "Out of range: start_reg + nof_reg must be lower than %d\n", h->pdcch[h->cfi].nof_regs); + return LIBLTE_ERROR; + } + } else { fprintf(stderr, "Must call regs_set_cfi() first\n"); - return -1; - } - int i; - for (i=0;ipdcch[h->cfi].nof_regs;i++) { - regs_get_reg(h->pdcch[h->cfi].regs[i], slot_symbols, &pdcch_symbols[i*4], h->nof_prb); + return LIBLTE_ERROR; } - return h->pdcch[h->cfi].nof_regs*4; +} + + +int regs_pdcch_get(regs_t *h, cf_t *slot_symbols, cf_t *pdcch_symbols) { + return regs_pdcch_get_offset(h, slot_symbols, pdcch_symbols, 0, h->pdcch[h->cfi].nof_regs); } @@ -191,9 +232,9 @@ int regs_pdcch_get(regs_t *h, cf_t *slot_symbols, cf_t *pdcch_symbols) { */ int regs_phich_init(regs_t *h) { float ng; - int i,ni,li,n[3],nreg,mi; + uint32_t i, ni, li, n[3], nreg, mi; regs_reg_t **regs_phich[3]; - int ret = -1; + int ret = LIBLTE_ERROR; switch(h->phich_res) { case R_1_6: @@ -212,7 +253,7 @@ int regs_phich_init(regs_t *h) { ng = 0; break; } - h->ngroups_phich = (int) ceilf(ng * ((float) h->nof_prb/8)); + h->ngroups_phich = (int) ceilf(ng * ((float) h->cell.nof_prb/8)); h->phich = malloc(sizeof(regs_ch_t) * h->ngroups_phich); if (!h->phich) { perror("malloc"); @@ -261,7 +302,7 @@ int regs_phich_init(regs_t *h) { for (mi=0;mingroups_phich;mi++) { // here ngroups is the number of mapping units for (i=0;i<3;i++) { li=h->phich_len==PHICH_EXT?i:0; // Step 7 - ni=((h->cell_id*n[li]/n[0])+mi+i*n[li]/3) % n[li]; // Step 8 + ni=((h->cell.id*n[li]/n[0])+mi+i*n[li]/3) % n[li]; // Step 8 h->phich[mi].regs[i] = regs_phich[li][ni]; h->phich[mi].regs[i]->assigned = true; INFO("Assigned PHICH REG#%d (%d,%d)\n",nreg,h->phich[mi].regs[i]->k0,li); @@ -271,13 +312,13 @@ int regs_phich_init(regs_t *h) { // now the number of mapping units = number of groups for normal cp. For extended cp // ngroups = 2 * number mapping units - if (CP_ISEXT(h->cp)) { + if (CP_ISEXT(h->cell.cp)) { h->ngroups_phich *= 2; } - ret = 0; + ret = LIBLTE_SUCCESS; clean_and_exit: - if (ret == -1) { + if (ret == LIBLTE_ERROR) { if (h->phich) { for (i=0;ingroups_phich;i++) { if (h->phich[i].regs) { @@ -296,9 +337,9 @@ clean_and_exit: } void regs_phich_free(regs_t *h) { - int i; + uint32_t i; if (h->phich) { - if (CP_ISEXT(h->cp)) { + if (CP_ISEXT(h->cell.cp)) { h->ngroups_phich /= 2; } for (i=0;ingroups_phich;i++) { @@ -310,8 +351,9 @@ void regs_phich_free(regs_t *h) { } } -int regs_phich_nregs(regs_t *h) { - int i, n; +uint32_t regs_phich_nregs(regs_t *h) { + uint32_t i; + uint32_t n; n=0; for (i=0;ingroups_phich;i++) { n += h->phich[i].nof_regs; @@ -320,7 +362,7 @@ int regs_phich_nregs(regs_t *h) { } -int regs_phich_ngroups(regs_t *h) { +uint32_t regs_phich_ngroups(regs_t *h) { return h->ngroups_phich; } @@ -331,18 +373,18 @@ int regs_phich_ngroups(regs_t *h) { * * Returns the number of written symbols, or -1 on error */ -int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup, cf_t *slot_symbols) { - int i; - if (ngroup < 0 || ngroup > h->ngroups_phich) { +int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], uint32_t ngroup, cf_t *slot_symbols) { + uint32_t i; + if (ngroup >= h->ngroups_phich) { fprintf(stderr, "Error invalid ngroup %d\n", ngroup); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } - if (CP_ISEXT(h->cp)) { + if (CP_ISEXT(h->cell.cp)) { ngroup /= 2; } regs_ch_t *rch = &h->phich[ngroup]; for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { - regs_add_reg(rch->regs[i], &phich_symbols[i*REGS_RE_X_REG], slot_symbols, h->nof_prb); + regs_add_reg(rch->regs[i], &phich_symbols[i*REGS_RE_X_REG], slot_symbols, h->cell.nof_prb); } return i*REGS_RE_X_REG; } @@ -353,20 +395,20 @@ int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup, c * Returns the number of written symbols, or -1 on error */ int regs_phich_reset(regs_t *h, cf_t *slot_symbols) { - int i; - int ngroup, ng; - for (ngroup = 0;ngroup < h->ngroups_phich;CP_ISEXT(h->cp)?ngroup+=2:ngroup++) { - if (CP_ISEXT(h->cp)) { + uint32_t i; + uint32_t ngroup, ng; + for (ngroup = 0;ngroup < h->ngroups_phich;CP_ISEXT(h->cell.cp)?ngroup+=2:ngroup++) { + if (CP_ISEXT(h->cell.cp)) { ng = ngroup/2; } else { ng = ngroup; } regs_ch_t *rch = &h->phich[ng]; for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { - regs_reset_reg(rch->regs[i], slot_symbols, h->nof_prb); + regs_reset_reg(rch->regs[i], slot_symbols, h->cell.nof_prb); } } - return 0; + return LIBLTE_SUCCESS; } /** @@ -374,18 +416,18 @@ int regs_phich_reset(regs_t *h, cf_t *slot_symbols) { * * Returns the number of written symbols, or -1 on error */ -int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup) { - int i; - if (ngroup < 0 || ngroup > h->ngroups_phich) { +int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_NSYM], uint32_t ngroup) { + uint32_t i; + if (ngroup >= h->ngroups_phich) { fprintf(stderr, "Error invalid ngroup %d\n", ngroup); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } - if (CP_ISEXT(h->cp)) { + if (CP_ISEXT(h->cell.cp)) { ngroup /= 2; } regs_ch_t *rch = &h->phich[ngroup]; for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { - regs_get_reg(rch->regs[i], slot_symbols, &phich_symbols[i*REGS_RE_X_REG], h->nof_prb); + regs_get_reg(rch->regs[i], slot_symbols, &phich_symbols[i*REGS_RE_X_REG], h->cell.nof_prb); } return i*REGS_RE_X_REG; } @@ -410,39 +452,40 @@ int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_ * 36.211 10.3 section 6.7.4 */ int regs_pcfich_init(regs_t *h) { - int i, k_hat, k; + uint32_t i; + uint32_t k_hat, k; regs_ch_t *ch = &h->pcfich; ch->regs = malloc(sizeof(regs_reg_t*) * REGS_PCFICH_NREGS); if (!ch->regs) { perror("malloc"); - return -1; + return LIBLTE_ERROR; } ch->nof_regs = REGS_PCFICH_NREGS; - INFO("PCFICH allocating %d regs. CellID: %d, PRB: %d\n", ch->nof_regs, h->cell_id, h->nof_prb); + INFO("PCFICH allocating %d regs. CellID: %d, PRB: %d\n", ch->nof_regs, h->cell.id, h->cell.nof_prb); - k_hat = (RE_X_RB / 2) * (h->cell_id % (2 * h->nof_prb)); + k_hat = (RE_X_RB / 2) * (h->cell.id % (2 * h->cell.nof_prb)); for (i = 0; i < REGS_PCFICH_NREGS; i++) { - k = (k_hat + (i * h->nof_prb / 2) * (RE_X_RB / 2)) - % (h->nof_prb * RE_X_RB); + k = (k_hat + (i * h->cell.nof_prb / 2) * (RE_X_RB / 2)) + % (h->cell.nof_prb * RE_X_RB); ch->regs[i] = regs_find_reg(h, k, 0); if (!ch->regs[i]) { fprintf(stderr, "Error allocating PCFICH: REG (%d,0) not found\n", k); - return -1; + return LIBLTE_ERROR; } else if (ch->regs[i]->assigned) { fprintf(stderr, "Error allocating PCFICH: REG (%d,0) already allocated\n", k); - return -1; + return LIBLTE_ERROR; } else { ch->regs[i]->assigned = true; INFO("Assigned PCFICH REG#%d (%d,0)\n", i, k); } } - return 0; + return LIBLTE_SUCCESS; } void regs_pcfich_free(regs_t *h) { @@ -451,7 +494,7 @@ void regs_pcfich_free(regs_t *h) { } } -int regs_pcfich_nregs(regs_t *h) { +uint32_t regs_pcfich_nregs(regs_t *h) { return h->pcfich.nof_regs; } @@ -463,9 +506,9 @@ int regs_pcfich_nregs(regs_t *h) { int regs_pcfich_put(regs_t *h, cf_t pcfich_symbols[REGS_PCFICH_NSYM], cf_t *slot_symbols) { regs_ch_t *rch = &h->pcfich; - int i; + uint32_t i; for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { - regs_put_reg(rch->regs[i], &pcfich_symbols[i*REGS_RE_X_REG], slot_symbols, h->nof_prb); + regs_put_reg(rch->regs[i], &pcfich_symbols[i*REGS_RE_X_REG], slot_symbols, h->cell.nof_prb); } return i*REGS_RE_X_REG; } @@ -477,9 +520,9 @@ int regs_pcfich_put(regs_t *h, cf_t pcfich_symbols[REGS_PCFICH_NSYM], cf_t *slot */ int regs_pcfich_get(regs_t *h, cf_t *slot_symbols, cf_t ch_data[REGS_PCFICH_NSYM]) { regs_ch_t *rch = &h->pcfich; - int i; + uint32_t i; for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { - regs_get_reg(rch->regs[i], slot_symbols, &ch_data[i*REGS_RE_X_REG], h->nof_prb); + regs_get_reg(rch->regs[i], slot_symbols, &ch_data[i*REGS_RE_X_REG], h->cell.nof_prb); } return i*REGS_RE_X_REG; } @@ -503,8 +546,8 @@ int regs_pcfich_get(regs_t *h, cf_t *slot_symbols, cf_t ch_data[REGS_PCFICH_NSYM * ***************************************************************/ -regs_reg_t *regs_find_reg(regs_t *h, int k, int l) { - int i; +regs_reg_t *regs_find_reg(regs_t *h, uint32_t k, uint32_t l) { + uint32_t i; for (i=0;inof_regs;i++) { if (h->regs[i].l == l && h->regs[i].k0 == k) { return &h->regs[i]; @@ -517,7 +560,7 @@ regs_reg_t *regs_find_reg(regs_t *h, int k, int l) { * Returns the number of REGs in a PRB * 36.211 Section 6.2.4 */ -int regs_num_x_symbol(int symbol, int nof_port, lte_cp_t cp) { +int regs_num_x_symbol(uint32_t symbol, uint32_t nof_port, lte_cp_t cp) { switch (symbol) { case 0: @@ -530,7 +573,7 @@ int regs_num_x_symbol(int symbol, int nof_port, lte_cp_t cp) { case 4: return 2; default: - return -1; + return LIBLTE_ERROR; } break; case 2: @@ -542,7 +585,7 @@ int regs_num_x_symbol(int symbol, int nof_port, lte_cp_t cp) { return 2; } default: - return -1; + return LIBLTE_ERROR; } } @@ -550,8 +593,8 @@ int regs_num_x_symbol(int symbol, int nof_port, lte_cp_t cp) { * Initializes the indices of a REG * 36.211 Section 6.2.4 */ -int regs_reg_init(regs_reg_t *reg, int symbol, int nreg, int k0, int maxreg, int vo) { - int i, j, z; +int regs_reg_init(regs_reg_t *reg, uint32_t symbol, uint32_t nreg, uint32_t k0, uint32_t maxreg, uint32_t vo) { + uint32_t i, j, z; reg->l = symbol; reg->assigned = false; @@ -576,7 +619,7 @@ int regs_reg_init(regs_reg_t *reg, int symbol, int nreg, int k0, int maxreg, int } if (j != 4) { fprintf(stderr, "Something went wrong: expected 2 references\n"); - return -1; + return LIBLTE_ERROR; } break; @@ -589,9 +632,9 @@ int regs_reg_init(regs_reg_t *reg, int symbol, int nreg, int k0, int maxreg, int break; default: fprintf(stderr, "Invalid number of REGs per PRB: %d\n", maxreg); - return -1; + return LIBLTE_ERROR; } - return 0; + return LIBLTE_SUCCESS; } void regs_free(regs_t *h) { @@ -607,19 +650,20 @@ void regs_free(regs_t *h) { /** Sets the CFI value for this subframe (CFI must be in the range 1..3). */ -int regs_set_cfi(regs_t *h, int cfi) { +int regs_set_cfi(regs_t *h, uint32_t cfi) { if (cfi > 0 && cfi <= 3) { if (h->phich_len == PHICH_EXT && - ((h->nof_prb < 10 && cfi < 2) || (h->nof_prb >= 10 && cfi < 3))) { + ((h->cell.nof_prb < 10 && cfi < 2) || (h->cell.nof_prb >= 10 && cfi < 3))) { fprintf(stderr, "PHICH length is extended. The number of control symbols should be at least 3.\n"); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } else { + h->cfi_initiated = true; h->cfi = cfi - 1; - return 0; + return LIBLTE_SUCCESS; } } else { fprintf(stderr, "Invalid CFI %d\n", cfi); - return -1; + return LIBLTE_ERROR_INVALID_INPUTS; } } @@ -628,97 +672,98 @@ int regs_set_cfi(regs_t *h, int cfi) { * Sets all REG indices and initializes PCFICH, PHICH and PDCCH REGs * Returns 0 if OK, -1 on error */ -int regs_init(regs_t *h, int cell_id, int nof_prb, int nof_ports, - phich_resources_t phich_res, phich_length_t phich_len, lte_cp_t cp) { - int ret = -1; - int i, j[4], jmax, n[4], prb, k; - int vo = cell_id % 3; - int max_ctrl_symbols = nof_prb<10?4:3; - - bzero(h, sizeof(regs_t)); - - h->cell_id = cell_id; - h->nof_prb = nof_prb; - h->max_ctrl_symbols = max_ctrl_symbols; - h->cfi = -1; // not yet initialized - h->phich_res = phich_res; - h->phich_len = phich_len; - h->cp = cp; - h->nof_ports = nof_ports; - - h->nof_regs = 0; - for (i = 0; i < max_ctrl_symbols; i++) { - n[i] = regs_num_x_symbol(i, nof_ports, cp); - if (n[i] == -1) { - return -1; - } - h->nof_regs += nof_prb * n[i]; - } - INFO("Indexing %d REGs. CellId: %d, %d PRB, CP: %s\n", h->nof_regs, h->cell_id, h->nof_prb, - CP_ISNORM(cp)?"Normal":"Extended"); - h->regs = malloc(sizeof(regs_reg_t) * h->nof_regs); - if (!h->regs) { - perror("malloc"); - goto clean_and_exit; - } +int regs_init(regs_t *h, phich_resources_t phich_res, phich_length_t phich_len, lte_cell_t cell) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t i, k; + uint32_t j[4], jmax, prb; + uint32_t n[4], vo; + uint32_t max_ctrl_symbols; + + if (h != NULL && + lte_cell_isvalid(&cell)) + { + bzero(h, sizeof(regs_t)); + ret = LIBLTE_ERROR; + + max_ctrl_symbols = cell.nof_prb<10?4:3; + vo = cell.id % 3; + h->cell = cell; + h->max_ctrl_symbols = max_ctrl_symbols; + h->cfi_initiated = false; + h->phich_res = phich_res; + h->phich_len = phich_len; + + h->nof_regs = 0; + for (i = 0; i < max_ctrl_symbols; i++) { + n[i] = regs_num_x_symbol(i, h->cell.nof_ports, h->cell.cp); + if (n[i] == -1) { + return -1; + } + h->nof_regs += h->cell.nof_prb * n[i]; + } + INFO("Indexing %d REGs. CellId: %d, %d PRB, CP: %s\n", h->nof_regs, h->cell.id, h->cell.nof_prb, + CP_ISNORM(h->cell.cp)?"Normal":"Extended"); + h->regs = malloc(sizeof(regs_reg_t) * h->nof_regs); + if (!h->regs) { + perror("malloc"); + goto clean_and_exit; + } - /* Sort REGs according to PDCCH mapping, beggining from the lowest l index then k */ - bzero(j, sizeof(int) * 4); - k = i = prb = jmax = 0; - while (k < h->nof_regs) { - if (n[i] == 3 || (n[i] == 2 && jmax != 1)) { - if (regs_reg_init(&h->regs[k], i, j[i], prb * RE_X_RB, n[i], vo)) { - fprintf(stderr, "Error initializing REGs\n"); - goto clean_and_exit; + /* Sort REGs according to PDCCH mapping, beggining from the lowest l index then k */ + bzero(j, sizeof(int) * 4); + k = i = prb = jmax = 0; + while (k < h->nof_regs) { + if (n[i] == 3 || (n[i] == 2 && jmax != 1)) { + if (regs_reg_init(&h->regs[k], i, j[i], prb * RE_X_RB, n[i], vo)) { + fprintf(stderr, "Error initializing REGs\n"); + goto clean_and_exit; + } + /*DEBUG("Available REG #%3d: l=%d, prb=%d, nreg=%d (k0=%d)\n", k, i, prb, j[i], + h->regs[k].k0); + */ + j[i]++; + k++; + } + i++; + if (i == max_ctrl_symbols) { + i = 0; + jmax++; + } + if (jmax == 3) { + prb++; + bzero(j, sizeof(int) * 4); + jmax = 0; } - DEBUG("Available REG #%3d: l=%d, prb=%d, nreg=%d (k0=%d)\n", k, i, prb, j[i], - h->regs[k].k0); - j[i]++; - k++; } - i++; - if (i == max_ctrl_symbols) { - i = 0; - jmax++; + if (regs_pcfich_init(h)) { + fprintf(stderr, "Error initializing PCFICH REGs\n"); + goto clean_and_exit; + } + + if (regs_phich_init(h)) { + fprintf(stderr, "Error initializing PHICH REGs\n"); + goto clean_and_exit; } - if (jmax == 3) { - prb++; - bzero(j, sizeof(int) * 4); - jmax = 0; + if (regs_pdcch_init(h)) { + fprintf(stderr, "Error initializing PDCCH REGs\n"); + goto clean_and_exit; } - } - if (regs_pcfich_init(h)) { - fprintf(stderr, "Error initializing PCFICH REGs\n"); - goto clean_and_exit; - } - if (regs_phich_init(h)) { - fprintf(stderr, "Error initializing PHICH REGs\n"); - goto clean_and_exit; - } - if (regs_pdcch_init(h)) { - fprintf(stderr, "Error initializing PDCCH REGs\n"); - goto clean_and_exit; + ret = LIBLTE_SUCCESS; } - - ret = 0; - clean_and_exit: - if (ret == -1) { + if (ret == LIBLTE_ERROR) { regs_free(h); } return ret; } -#define REG_IDX(r, i, n) r->k[i]+r->l*n*RE_X_RB - /** * Puts one REG data (4 symbols) in the slot symbols array */ -int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb) { - int i; +int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; for (i = 0; i < REGS_RE_X_REG; i++) { - DEBUG("PUT REG: i=%d, (k=%d,l=%d)\n", i, REG_IDX(reg, i, nof_prb),reg->l); slot_symbols[REG_IDX(reg, i, nof_prb)] = reg_data[i]; } return REGS_RE_X_REG; @@ -728,13 +773,10 @@ int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_pr * Adds one REG data (4 symbols) in the slot symbols array * Used by PHICH */ -int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb) { - int i; +int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; for (i = 0; i < REGS_RE_X_REG; i++) { slot_symbols[REG_IDX(reg, i, nof_prb)] += reg_data[i]; - DEBUG("ADD REG: i=%d, (k=%d,l=%d): %.1f+%.1fi\n", i, REG_IDX(reg, i, nof_prb),reg->l, - __real__ slot_symbols[REG_IDX(reg, i, nof_prb)], - __imag__ slot_symbols[REG_IDX(reg, i, nof_prb)]); } return REGS_RE_X_REG; } @@ -743,10 +785,9 @@ int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_pr /** * Reset REG data (4 symbols) in the slot symbols array */ -int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, int nof_prb) { - int i; +int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; for (i = 0; i < REGS_RE_X_REG; i++) { - DEBUG("RESET REG: i=%d, (k=%d,l=%d)\n", i, REG_IDX(reg, i, nof_prb),reg->l); slot_symbols[REG_IDX(reg, i, nof_prb)] = 0; } return REGS_RE_X_REG; @@ -755,13 +796,12 @@ int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, int nof_prb) { /** * Gets one REG data (4 symbols) from the slot symbols array */ -int regs_get_reg(regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, int nof_prb) { - int i; +int regs_get_reg(regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, uint32_t nof_prb) { + uint32_t i; for (i = 0; i < REGS_RE_X_REG; i++) { reg_data[i] = slot_symbols[REG_IDX(reg, i, nof_prb)]; - //DEBUG("GET REG: i=%d, (k=%d,l=%d): %.1f+%.1fi\n", i, REG_IDX(reg, i, nof_prb),reg->l, - // __real__ reg_data[i], __imag__ reg_data[i]); } return REGS_RE_X_REG; } + diff --git a/lte/phy/lib/phch/src/sequences.c b/lte/phy/lib/phch/src/sequences.c index 372179926..499eb1cd0 100644 --- a/lte/phy/lib/phch/src/sequences.c +++ b/lte/phy/lib/phch/src/sequences.c @@ -33,7 +33,7 @@ /** * 36.211 6.6.1 */ -int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { +int sequence_pbch(sequence_t *seq, lte_cp_t cp, uint32_t cell_id) { bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, CP_ISNORM(cp)?1920:1728, cell_id); } @@ -41,7 +41,7 @@ int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { /** * 36.211 6.7.1 */ -int sequence_pcfich(sequence_t *seq, int nslot, int cell_id) { +int sequence_pcfich(sequence_t *seq, uint32_t nslot, uint32_t cell_id) { bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, 32, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); } @@ -50,7 +50,7 @@ int sequence_pcfich(sequence_t *seq, int nslot, int cell_id) { /** * 36.211 6.9.1 */ -int sequence_phich(sequence_t *seq, int nslot, int cell_id) { +int sequence_phich(sequence_t *seq, uint32_t nslot, uint32_t cell_id) { bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, 12, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); } @@ -58,7 +58,7 @@ int sequence_phich(sequence_t *seq, int nslot, int cell_id) { /** * 36.211 6.8.2 */ -int sequence_pdcch(sequence_t *seq, int nslot, int cell_id, int len) { +int sequence_pdcch(sequence_t *seq, uint32_t nslot, uint32_t cell_id, uint32_t len) { bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, len, (nslot/2) * 512 + cell_id); } @@ -66,7 +66,7 @@ int sequence_pdcch(sequence_t *seq, int nslot, int cell_id, int len) { /** * 36.211 6.3.1 */ -int sequence_pdsch(sequence_t *seq, unsigned short rnti, int q, int nslot, int cell_id, int len) { - bzero(seq, sizeof(sequence_t)); - return sequence_LTEPRS(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id); +int sequence_pdsch(sequence_t *seq, unsigned short rnti, int q, uint32_t nslot, uint32_t cell_id, uint32_t len) { + bzero(seq, sizeof(sequence_t)); + return sequence_LTEPRS(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id); } diff --git a/lte/phy/lib/phch/test/CMakeLists.txt b/lte/phy/lib/phch/test/CMakeLists.txt index 36ed64f2a..328d8128a 100644 --- a/lte/phy/lib/phch/test/CMakeLists.txt +++ b/lte/phy/lib/phch/test/CMakeLists.txt @@ -92,6 +92,7 @@ TARGET_LINK_LIBRARIES(pdsch_re_test lte_phy) ADD_TEST(pdsch_re_test pdsch_re_test) ADD_TEST(pdsch_test pdsch_test -l 50000 -m 4 -n 110) +ADD_TEST(pdsch_test pdsch_test -l 500 -m 2 -n 50 -r 2) ######################################################################## # FILE TEST diff --git a/lte/phy/lib/phch/test/dci_unpacking.c b/lte/phy/lib/phch/test/dci_unpacking.c index b2e377b2e..6a1087d11 100644 --- a/lte/phy/lib/phch/test/dci_unpacking.c +++ b/lte/phy/lib/phch/test/dci_unpacking.c @@ -81,9 +81,8 @@ int main(int argc, char **argv) { printf("\n"); dci_msg_type_t dci_type; - msg.location.rnti = SIRNTI; - msg.location.nof_bits = len; - if (dci_msg_get_type(&msg, &dci_type, nof_prb, 1234)) { + msg.nof_bits = len; + if (dci_msg_get_type(&msg, &dci_type, nof_prb, SIRNTI, 1234)) { fprintf(stderr, "Can't obtain DCI message type\n"); exit(-1); } diff --git a/lte/phy/lib/phch/test/pbch_file_test.c b/lte/phy/lib/phch/test/pbch_file_test.c index c356620cf..78e2b982a 100644 --- a/lte/phy/lib/phch/test/pbch_file_test.c +++ b/lte/phy/lib/phch/test/pbch_file_test.c @@ -35,17 +35,20 @@ char *input_file_name = NULL; char *matlab_file_name = NULL; -int cell_id = 150; -lte_cp_t cp = CPNORM; -int nof_prb = 6; FILE *fmatlab = NULL; -#define NOF_PORTS 2 +lte_cell_t cell = { + 6, // nof_prb + 2, // nof_ports + 150, // cell_id + CPNORM // cyclic prefix +}; + #define FLEN 9600 filesource_t fsrc; -cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; +cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; pbch_t pbch; lte_fft_t fft; chest_t chest; @@ -53,20 +56,22 @@ chest_t chest; void usage(char *prog) { printf("Usage: %s [vcoe] -i input_file\n", prog); printf("\t-o output matlab file name [Default Disabled]\n"); - printf("\t-c cell_id [Default %d]\n", cell_id); + printf("\t-c cell_id [Default %d]\n", cell.id); + printf("\t-n nof_prb [Default %d]\n", cell.nof_prb); printf("\t-e Set extended prefix [Default Normal]\n"); printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; + while ((opt = getopt(argc, argv, "iovce")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; break; case 'c': - cell_id = atoi(argv[optind]); + cell.id = atoi(argv[optind]); break; case 'o': matlab_file_name = argv[optind]; @@ -75,7 +80,7 @@ void parse_args(int argc, char **argv) { verbose++; break; case 'e': - cp = CPEXT; + cell.cp = CPEXT; break; default: usage(argv[0]); @@ -112,36 +117,36 @@ int base_init() { exit(-1); } - fft_buffer = malloc(CP_NSYMB(cp) * nof_prb * RE_X_RB * sizeof(cf_t)); + fft_buffer = malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); if (!fft_buffer) { perror("malloc"); return -1; } - for (i=0;i= 1234 && crc_rem < 1234 + nof_dcis) { + crc_rem -= 1234; + memcpy(&dci_rx[crc_rem], &dci_tmp, sizeof(dci_msg_t)); + } else { + printf("Received invalid DCI CRC 0x%x\n", crc_rem); + goto quit; + } + } + for (i = 0; i < nof_dcis; i++) { + if (memcmp(dci_tx[i].data, dci_rx[i].data, dci_tx[i].nof_bits)) { + printf("Error in DCI %d: Received data does not match\n", i); + goto quit; } - } else { - printf("Transmitted %d DCIs but got %d\n", dci_tx.nof_dcis, nof_dcis); - goto quit; } ret = 0; - quit: pdcch_free(&pdcch); + +quit: + pdcch_free(&pdcch); regs_free(®s); - dci_free(&dci_tx); - dci_free(&dci_rx); - for (i = 0; i < MAX_PORTS_CTRL; i++) { + for (i = 0; i < MAX_PORTS; i++) { free(ce[i]); free(slot_symbols[i]); } diff --git a/lte/phy/lib/phch/test/pdsch_file_test.c b/lte/phy/lib/phch/test/pdsch_file_test.c index 882b612fb..6cd8bd0e5 100644 --- a/lte/phy/lib/phch/test/pdsch_file_test.c +++ b/lte/phy/lib/phch/test/pdsch_file_test.c @@ -35,299 +35,294 @@ char *input_file_name = NULL; char *matlab_file_name = NULL; -int cell_id = 0; -int cfi = 2; -lte_cp_t cp = CPNORM; -int nof_prb = 6; -int nof_ports = 1; + +lte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 0, // cell_id + CPNORM // cyclic prefix +}; + int flen; -unsigned short rnti = SIRNTI; + +uint32_t cfi = 2; +uint16_t rnti = SIRNTI; + int max_frames = 10; FILE *fmatlab = NULL; filesource_t fsrc; pdcch_t pdcch; pdsch_t pdsch; -cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; +pdsch_harq_t harq_process; +cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; regs_t regs; lte_fft_t fft; chest_t chest; -dci_t dci_rx; void usage(char *prog) { - printf("Usage: %s [vcfoe] -i input_file\n", prog); - printf("\t-o output matlab file name [Default Disabled]\n"); - printf("\t-c cell_id [Default %d]\n", cell_id); - printf("\t-f cfi [Default %d]\n", cfi); - printf("\t-r rnti [Default SI-RNTI]\n"); - printf("\t-p nof_ports [Default %d]\n", nof_ports); - printf("\t-n nof_prb [Default %d]\n", nof_prb); - printf("\t-m max_frames [Default %d]\n", max_frames); - printf("\t-e Set extended prefix [Default Normal]\n"); - printf("\t-v [set verbose to debug, default none]\n"); + printf("Usage: %s [vcfoe] -i input_file\n", prog); + printf("\t-o output matlab file name [Default Disabled]\n"); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-r rnti [Default SI-RNTI]\n"); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-m max_frames [Default %d]\n", max_frames); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "irovfcenmp")) != -1) { - switch(opt) { - case 'i': - input_file_name = argv[optind]; - break; - case 'c': - cell_id = atoi(argv[optind]); - break; - case 'r': - rnti = strtoul(argv[optind], NULL, 0); - break; - case 'm': - max_frames = strtoul(argv[optind], NULL, 0); - break; - case 'f': - cfi = atoi(argv[optind]); - break; - case 'n': - nof_prb = atoi(argv[optind]); - break; - case 'p': - nof_ports = atoi(argv[optind]); - break; - case 'o': - matlab_file_name = argv[optind]; - break; - case 'v': - verbose++; - break; - case 'e': - cp = CPEXT; - break; - default: - usage(argv[0]); - exit(-1); - } - } - if (!input_file_name) { - usage(argv[0]); - exit(-1); - } + int opt; + while ((opt = getopt(argc, argv, "irovfcenmp")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'r': + rnti = strtoul(argv[optind], NULL, 0); + break; + case 'm': + max_frames = strtoul(argv[optind], NULL, 0); + break; + case 'f': + cfi = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'o': + matlab_file_name = argv[optind]; + break; + case 'v': + verbose++; + break; + case 'e': + cell.cp = CPEXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } } int base_init() { - int i; - - if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { - fprintf(stderr, "Error opening file %s\n", input_file_name); - exit(-1); - } - - if (matlab_file_name) { - fmatlab = fopen(matlab_file_name, "w"); - if (!fmatlab) { - perror("fopen"); - return -1; - } - } else { - fmatlab = NULL; - } - - flen = 2 * (SLOT_LEN(lte_symbol_sz(nof_prb), cp)); - - input_buffer = malloc(flen * sizeof(cf_t)); - if (!input_buffer) { - perror("malloc"); - exit(-1); - } - - fft_buffer = malloc(2 * CP_NSYMB(cp) * nof_prb * RE_X_RB * sizeof(cf_t)); - if (!fft_buffer) { - perror("malloc"); - return -1; - } - - for (i=0;i 0) { - slot_symbols[0][j] += slot_symbols[i][j]; - } - ce[i][j] = 1; - } - } - - gettimeofday(&t[1], NULL); - int r = pdsch_decode(&pdsch, slot_symbols[0], ce, data, subframe, mcs, &prb_alloc); - gettimeofday(&t[2], NULL); - get_time_interval(t); - if (r) { - printf("Error decoding\n"); - ret = -1; - } else { - printf("DECODED OK in %d:%d (%.2f Mbps)\n", (int) t[0].tv_sec, (int) t[0].tv_usec, (float) mcs.tbs/t[0].tv_usec); - } - ret = 0; + pdsch_t pdsch; + uint32_t i, j; + char *data = NULL; + cf_t *ce[MAX_PORTS]; + uint32_t nof_re; + cf_t *slot_symbols[MAX_PORTS]; + int ret = -1; + struct timeval t[3]; + ra_mcs_t mcs; + ra_prb_t prb_alloc; + pdsch_harq_t harq_process; + uint32_t rv; + + parse_args(argc,argv); + + nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB; + + mcs.tbs = tbs; + mcs.mod = modulation; + prb_alloc.slot[0].nof_prb = cell.nof_prb; + for (i=0;i 0) { + slot_symbols[0][j] += slot_symbols[i][j]; + } + ce[i][j] = 1; + } + } + + gettimeofday(&t[1], NULL); + int r = pdsch_decode(&pdsch, slot_symbols[0], ce, data, subframe, &harq_process, rv); + gettimeofday(&t[2], NULL); + get_time_interval(t); + if (r) { + printf("Error decoding\n"); + ret = -1; + goto quit; + } else { + printf("DECODED OK in %d:%d (%.2f Mbps)\n", (int) t[0].tv_sec, (int) t[0].tv_usec, (float) mcs.tbs/t[0].tv_usec); + } + + } + ret = 0; quit: - pdsch_free(&pdsch); - - for (i=0;i +#include +#include "liblte/phy/resampling/decim.h" +#include "liblte/phy/utils/debug.h" + + +/* Performs integer linear decimation by a factor of M */ +void decim_c(cf_t *input, cf_t *output, int M, int len) { + int i; + for (i=0;i #include +#include + #include "liblte/phy/resampling/interp.h" +#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/debug.h" +#define TABLE_SIZE 1024 + + +#ifdef TABLE_SIZE + #define ARG2IDX(arg) ((uint32_t) ((1+(arg)/M_PI)*TABLE_SIZE/2)) + #define MYCEXP(arg) q->cexptable[ARG2IDX(arg)] +#else + #define MYCEXP(arg) (cosf(arg) + I*sinf(arg)) +#endif + +#define MAX_OFFSET 64 + +int interp_init(interp_t *q, interp_type_t type, uint32_t len, uint32_t M) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = LIBLTE_ERROR; + + q->in_arg = vec_malloc(len * sizeof(float)); + if (!q->in_arg) { + goto clean_and_exit; + } + q->in_mag = vec_malloc(len * sizeof(float)); + if (!q->in_mag) { + goto clean_and_exit; + } + q->out_arg = vec_malloc((MAX_OFFSET + M * len) * sizeof(float)); + if (!q->out_arg) { + goto clean_and_exit; + } + q->out_arg2 = vec_malloc((MAX_OFFSET + M * len) * sizeof(float)); + if (!q->out_arg2) { + goto clean_and_exit; + } + q->table_idx = vec_malloc((MAX_OFFSET + M * len) * sizeof(int16_t)); + if (!q->table_idx) { + goto clean_and_exit; + } + q->out_mag = vec_malloc((MAX_OFFSET + M * len) * sizeof(float)); + if (!q->out_mag) { + goto clean_and_exit; + } + q->out_cexp = vec_malloc((MAX_OFFSET + M * len) * sizeof(cf_t)); + if (!q->out_cexp) { + goto clean_and_exit; + } + q->out_prod = vec_malloc((MAX_OFFSET + M * len) * sizeof(cf_t)); + if (!q->out_prod) { + goto clean_and_exit; + } +#ifdef TABLE_SIZE + q->cexptable = vec_malloc(TABLE_SIZE * sizeof(cf_t)); + uint32_t i; + for (i=0;icexptable[i] = cexpf(I*M_PI*(2*((float) i/TABLE_SIZE) - 1)); + } +#endif + q->M = M; + q->len = len; + ret = LIBLTE_SUCCESS; + } + +clean_and_exit: + if (ret == LIBLTE_ERROR) { + interp_free(q); + } + return ret; +} + +void interp_free(interp_t *q) { + if (q) { + if (q->in_arg) { + free(q->in_arg); + } + if (q->in_mag) { + free(q->in_mag); + } + if (q->out_arg) { + free(q->out_arg); + } + if (q->out_cexp) { + free(q->out_cexp); + } + if (q->out_mag) { + free(q->out_mag); + } + + if (q->out_prod) { + free(q->out_prod); + } +#ifdef TABLE_SIZE + if (q->cexptable) { + free(q->cexptable); + } +#endif + } +} + +void interp_run_offset(interp_t *q, cf_t *input, cf_t *output, uint32_t off_st, uint32_t off_end) { + uint32_t i, j, n; + float mag0=0, mag1=0, arg0=0, arg1=0; + float dmag, darg; + uint32_t M = q->M; + uint32_t len1 = q->len-1; + + if (off_st + off_end < MAX_OFFSET) { + vec_abs_cf(input, q->in_mag, q->len); + vec_arg_cf(input, q->in_arg, q->len); + + mag0 = q->in_mag[0]; + mag1 = q->in_mag[1]; + arg0 = q->in_arg[0]; + arg1 = q->in_arg[1]; + dmag=(mag1-mag0)/M; + darg=(arg1-arg0)/M; + for (j=0;jout_mag[j] = mag0 - (j+1)*dmag; + q->out_arg[j] = arg0 - (j+1)*darg; + } + + for (i=0;iin_mag[i]; + mag1 = q->in_mag[i+1]; + arg0 = q->in_arg[i]; + arg1 = q->in_arg[i+1]; + dmag=(mag1-mag0)/M; + darg=(arg1-arg0)/M; + for (j=0;jout_mag[i*M+j+off_st] = mag0 + j*dmag; + q->out_arg[i*M+j+off_st] = arg0 + j*darg; + } + } + if (q->len > 1) { + for (j=0;jout_mag[i*M+j+off_st] = mag1 + j*dmag; + q->out_arg[i*M+j+off_st] = arg1 + j*darg; + } + } + uint32_t len=i*M+j+off_st; +#ifdef TABLE_SIZE + vec_convert_fi(q->out_arg, q->table_idx, (float) TABLE_SIZE/2/M_PI, len); + for (n=0;nout_cexp[n] = q->cexptable[q->table_idx[n]+TABLE_SIZE/2]; + } +#else + for (n=0;nout_cexp[n] = MYCEXP(q->out_arg[n]); + } +#endif + vec_prod_cfc(q->out_cexp, q->out_mag, output, len); + } + +} + +void interp_run(interp_t *q, cf_t *input, cf_t *output) { + interp_run_offset(q, input, output, 0, 1); +} + /* Performs 1st order linear interpolation with out-of-bound interpolation */ -void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end) { - int i, j; +void interp_linear_offset(cf_t *input, cf_t *output, uint32_t M, uint32_t len, uint32_t off_st, uint32_t off_end) { + uint32_t i, j; float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0; for (i=0;i +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +typedef _Complex float cf_t; + +int main(int argc, char **argv) { + uint32_t N = 1000; // Number of sinwave samples + interp_t interp; + struct timeval t[3]; + + if (argc < 3) { + printf("usage: %s upsampling_rate nof_trials\n", argv[0]); + exit(-1); + } + + uint32_t M = atoi(argv[1]); + uint32_t nof_trials = atoi(argv[2]); + + if (interp_init(&interp, LINEAR, N, M)) { + exit(-1); + } + + cf_t *in = vec_malloc(N*sizeof(cf_t)); + if(!in) { + perror("malloc"); + exit(-1); + } + cf_t *out = vec_malloc(M * N*sizeof(cf_t)); + if(!out) { + perror("malloc"); + exit(-1); + } + cf_t *out_volk = vec_malloc(M * N*sizeof(cf_t)); + if(!out_volk) { + perror("malloc"); + exit(-1); + } + + srand(time(NULL)); + for(uint32_t i=0;ilen); for (i = 0; i < len; i++) { diff --git a/lte/phy/lib/sync/src/cfo.c b/lte/phy/lib/sync/src/cfo.c index 2f3c7dac5..204fb9d43 100644 --- a/lte/phy/lib/sync/src/cfo.c +++ b/lte/phy/lib/sync/src/cfo.c @@ -34,14 +34,14 @@ #include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/debug.h" -int cfo_init(cfo_t *h, int nsamples) { - int ret = -1; +int cfo_init(cfo_t *h, uint32_t nsamples) { + int ret = LIBLTE_ERROR; bzero(h, sizeof(cfo_t)); if (cexptab_init(&h->tab, CFO_CEXPTAB_SIZE)) { goto clean; } - h->cur_cexp = malloc(sizeof(cf_t) * nsamples); + h->cur_cexp = vec_malloc(sizeof(cf_t) * nsamples); if (!h->cur_cexp) { goto clean; } @@ -50,9 +50,9 @@ int cfo_init(cfo_t *h, int nsamples) { h->nsamples = nsamples; cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); - ret = 0; + ret = LIBLTE_SUCCESS; clean: - if (ret == -1) { + if (ret == LIBLTE_ERROR) { cfo_free(h); } return ret; @@ -70,11 +70,23 @@ void cfo_set_tol(cfo_t *h, float tol) { h->tol = tol; } -void cfo_correct(cfo_t *h, cf_t *x, float freq) { +int cfo_realloc(cfo_t *h, uint32_t samples) { + h->cur_cexp = realloc(h->cur_cexp, sizeof(cf_t) * samples); + if (!h->cur_cexp) { + perror("realloc"); + return LIBLTE_ERROR; + } + cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, samples); + h->nsamples = samples; + + return LIBLTE_SUCCESS; +} + +void cfo_correct(cfo_t *h, cf_t *input, cf_t *output, float freq) { if (fabs(h->last_freq - freq) > h->tol) { h->last_freq = freq; cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); - INFO("CFO generating new table for frequency %.4f\n", freq); + DEBUG("CFO generating new table for frequency %.4f\n", freq); } - vec_prod_ccc(h->cur_cexp, x, x, h->nsamples); + vec_prod_ccc(h->cur_cexp, input, output, h->nsamples); } diff --git a/lte/phy/lib/sync/src/find_sss.c b/lte/phy/lib/sync/src/find_sss.c index 33c4e015c..7bc52d558 100644 --- a/lte/phy/lib/sync/src/find_sss.c +++ b/lte/phy/lib/sync/src/find_sss.c @@ -31,18 +31,10 @@ #include "liblte/phy/utils/vector.h" #include "liblte/phy/sync/sss.h" -cf_t corr_sz(cf_t *z, cf_t *s) { - cf_t sum; - cf_t zsprod[32]; - vec_prod_ccc(z, s, zsprod, N_SSS - 1); - sum = vec_acc_cc(zsprod, N_SSS - 1); - - return sum; -} -void corr_all_zs(cf_t *z, cf_t s[32][32], cf_t *output) { - int m; +void corr_all_zs(cf_t *z, cf_t s[N_SSS+1][N_SSS+1], cf_t *output) { + uint32_t m; for (m = 0; m < N_SSS; m++) { - output[m] = corr_sz(z, s[m]); + output[m] = vec_dot_prod_ccc(z, s[m], N_SSS - 1); } } @@ -58,54 +50,64 @@ void corr_all_zs(cf_t *z, cf_t s[32][32], cf_t *output) { * */ -void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, - int *m1, float *m1_value) { - - /* This is aprox 3-4 kbytes of stack. Consider moving to sss_synch_t?? */ - cf_t zdelay[N_SSS+1],zconj[N_SSS+1],zprod[N_SSS+1]; - cf_t y[2][N_SSS+1], z[N_SSS+1], tmp[N_SSS+1]; - float tmp_real[N_SSS+1]; - cf_t input_fft[SSS_DFT_LEN]; - - int i; - - dft_run_c(&q->dftp_input, input, input_fft); - - for (i = 0; i < N_SSS; i++) { - y[0][i] = input_fft[SSS_POS_SYMBOL + 2 * i]; - y[1][i] = input_fft[SSS_POS_SYMBOL + 2 * i + 1]; - } - - vec_prod_ccc(y[0], q->fc_tables.c[0], z, N_SSS); - memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); - vec_conj_cc(z, zconj, N_SSS - 1); - vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); - - corr_all_zs(zprod, q->fc_tables.s, tmp); - vec_abs_cf(tmp, tmp_real, N_SSS); - *m0 = vec_max_fi(tmp_real, N_SSS); - if (m0_value) { - *m0_value = tmp_real[*m0]; - } - - vec_prod_ccc(y[1], q->fc_tables.c[1], tmp, N_SSS); - vec_prod_ccc(tmp, q->fc_tables.z1[*m0], z, N_SSS); - memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); - vec_conj_cc(z, zconj, N_SSS - 1); - vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); - - corr_all_zs(zprod, q->fc_tables.s, tmp); - vec_abs_cf(tmp, tmp_real, N_SSS); - *m1 = vec_max_fi(tmp_real, N_SSS); - if (m1_value) { - *m1_value = tmp_real[*m1]; - } - +int sss_synch_m0m1(sss_synch_t *q, cf_t *input, uint32_t *m0, float *m0_value, + uint32_t *m1, float *m1_value) +{ + + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL && + m0 != NULL && + m1 != NULL) + { + + /* Consider moving to sss_synch_t?? */ + cf_t zdelay[N_SSS+1],zconj[N_SSS+1],zprod[N_SSS+1]; + cf_t y[2][N_SSS+1], z[N_SSS+1], tmp[N_SSS+1]; + float tmp_real[N_SSS+1]; + cf_t input_fft[SSS_MAX_FFT_LEN]; + uint32_t i; + + dft_run_c(&q->dftp_input, input, input_fft); + + for (i = 0; i < N_SSS; i++) { + y[0][i] = input_fft[q->fft_size/2-N_SSS + 2 * i]; + y[1][i] = input_fft[q->fft_size/2-N_SSS + 2 * i + 1]; + } + + vec_prod_ccc(y[0], q->fc_tables[q->N_id_2].c[0], z, N_SSS); + memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); + vec_conj_cc(z, zconj, N_SSS - 1); + vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); + + corr_all_zs(zprod, q->fc_tables[q->N_id_2].s, tmp); + vec_abs_cf(tmp, tmp_real, N_SSS); + *m0 = vec_max_fi(tmp_real, N_SSS); + if (m0_value) { + *m0_value = tmp_real[*m0]; + } + + vec_prod_ccc(y[1], q->fc_tables[q->N_id_2].c[1], tmp, N_SSS); + vec_prod_ccc(tmp, q->fc_tables[q->N_id_2].z1[*m0], z, N_SSS); + memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); + vec_conj_cc(z, zconj, N_SSS - 1); + vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); + + corr_all_zs(zprod, q->fc_tables[q->N_id_2].s, tmp); + vec_abs_cf(tmp, tmp_real, N_SSS); + *m1 = vec_max_fi(tmp_real, N_SSS); + if (m1_value) { + *m1_value = tmp_real[*m1]; + } + ret = LIBLTE_SUCCESS; + } + return ret; } void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in) { - int i, j; - bzero(fc_tables, sizeof(struct fc_tables)); + uint32_t i, j; + for (i = 0; i < N_SSS; i++) { for (j = 0; j < N_SSS; j++) { __real__ fc_tables->z1[i][j] = (float) in->z1[i][j]; diff --git a/lte/phy/lib/sync/src/gen_sss.c b/lte/phy/lib/sync/src/gen_sss.c index 89279cda3..da3e895b2 100644 --- a/lte/phy/lib/sync/src/gen_sss.c +++ b/lte/phy/lib/sync/src/gen_sss.c @@ -58,19 +58,19 @@ void generate_zsc_tilde(int *z_tilde, int *s_tilde, int *c_tilde) { z_tilde[i] = 1 - 2 * x[i]; } -void generate_m0m1(int N_id_1, int *m0, int *m1) { - int q_prime = N_id_1 / (N_SSS - 1); - int q = (N_id_1 + (q_prime * (q_prime + 1) / 2)) / (N_SSS - 1); - int m_prime = N_id_1 + (q * (q + 1) / 2); +void generate_m0m1(uint32_t N_id_1, uint32_t *m0, uint32_t *m1) { + uint32_t q_prime = N_id_1 / (N_SSS - 1); + uint32_t q = (N_id_1 + (q_prime * (q_prime + 1) / 2)) / (N_SSS - 1); + uint32_t m_prime = N_id_1 + (q * (q + 1) / 2); *m0 = m_prime % N_SSS; *m1 = (*m0 + m_prime / N_SSS + 1) % N_SSS; } /* table[m0][m1-1]=N_id_1 */ -void generate_N_id_1_table(int table[30][30]) { - int m0, m1; - int N_id_1; +void generate_N_id_1_table(uint32_t table[30][30]) { + uint32_t m0, m1; + uint32_t N_id_1; for (N_id_1=0;N_id_1<168;N_id_1++) { generate_m0m1(N_id_1, &m0, &m1); table[m0][m1-1] = N_id_1; @@ -78,61 +78,60 @@ void generate_N_id_1_table(int table[30][30]) { } -void generate_s(int *s, int *s_tilde, int m0_m1) { - int i; +void generate_s(int *s, int *s_tilde, uint32_t m0_m1) { + uint32_t i; for (i = 0; i < N_SSS; i++) { s[i] = s_tilde[(i + m0_m1) % N_SSS]; } } void generate_s_all(int s[N_SSS][N_SSS], int *s_tilde) { - int i; + uint32_t i; for (i = 0; i < N_SSS; i++) { generate_s(s[i], s_tilde, i); } } -void generate_c(int *c, int *c_tilde, int N_id_2, int is_c0) { - int i; +void generate_c(int *c, int *c_tilde, uint32_t N_id_2, bool is_c0) { + uint32_t i; for (i = 0; i < N_SSS; i++) { - c[i] = c_tilde[(i + N_id_2 + (is_c0 > 0 ? 3 : 0)) % N_SSS]; + c[i] = c_tilde[(i + N_id_2 + (is_c0 ? 3 : 0)) % N_SSS]; } } -void generate_z(int *z, int *z_tilde, int m0_m1) { - int i; +void generate_z(int *z, int *z_tilde, uint32_t m0_m1) { + uint32_t i; for (i = 0; i < N_SSS; i++) { z[i] = z_tilde[(i + (m0_m1 % 8)) % N_SSS]; } } void generate_z_all(int z[N_SSS][N_SSS], int *z_tilde) { - int i; + uint32_t i; for (i = 0; i < N_SSS; i++) { generate_z(z[i], z_tilde, i); } } -void generate_sss_all_tables(struct sss_tables *tables, int N_id_2) { - int i; +void generate_sss_all_tables(struct sss_tables *tables, uint32_t N_id_2) { + uint32_t i; int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS]; generate_zsc_tilde(z_t, s_t, c_t); generate_s_all(tables->s, s_t); generate_z_all(tables->z1, z_t); for (i = 0; i < 2; i++) { - generate_c(tables->c[i], c_t, N_id_2, i); + generate_c(tables->c[i], c_t, N_id_2, i > 0); } - tables->N_id_2 = N_id_2; } -void sss_generate(float *signal0, float *signal5, int cell_id) { +void sss_generate(float *signal0, float *signal5, uint32_t cell_id) { - int i; - int id1 = cell_id / 3; - int id2 = cell_id % 3; - int m0; - int m1; + uint32_t i; + uint32_t id1 = cell_id / 3; + uint32_t id2 = cell_id % 3; + uint32_t m0; + uint32_t m1; int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS]; int s0[N_SSS], s1[N_SSS], c0[N_SSS], c1[N_SSS], z1_0[N_SSS], z1_1[N_SSS]; diff --git a/lte/phy/lib/sync/src/pss.c b/lte/phy/lib/sync/src/pss.c index 6d17a93ff..9cb7b53fa 100644 --- a/lte/phy/lib/sync/src/pss.c +++ b/lte/phy/lib/sync/src/pss.c @@ -36,93 +36,137 @@ #include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/convolution.h" +#include "liblte/phy/utils/debug.h" -#define NOT_SYNC 0xF0F0F0F0 -/* Initializes the object. subframe_size is the size, in samples, of the 1ms subframe - * - */ -int pss_synch_init(pss_synch_t *q, int frame_size) { - int ret = -1; - bzero(q, sizeof(pss_synch_t)); - - q->pss_signal_freq = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); - if (!q->pss_signal_freq) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; - } - q->conv_abs = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(float)); - if (!q->conv_abs) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; - } - q->tmp_input = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); - if (!q->tmp_input) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; - } - q->frame_buffer = vec_malloc(4 * frame_size * sizeof(cf_t)); - if (!q->frame_buffer) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; - } - q->conv_output = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); - if (!q->conv_output) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; +int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_size) { + dft_plan_t plan; + cf_t pss_signal_pad[2048]; + cf_t pss_signal_time[PSS_LEN]; + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (lte_N_id_2_isvalid(N_id_2) && + fft_size <= 2048) + { + + pss_generate(pss_signal_time, N_id_2); + + bzero(pss_signal_pad, fft_size * sizeof(cf_t)); + bzero(pss_signal_freq, fft_size * sizeof(cf_t)); + memcpy(&pss_signal_pad[(fft_size-PSS_LEN)/2], pss_signal_time, PSS_LEN * sizeof(cf_t)); + + if (dft_plan(&plan, fft_size, BACKWARD, COMPLEX)) { + return LIBLTE_ERROR; + } + + dft_plan_set_mirror(&plan, true); + dft_plan_set_dc(&plan, true); + dft_plan_set_norm(&plan, true); + dft_run_c(&plan, pss_signal_pad, pss_signal_freq); + + vec_conj_cc(pss_signal_freq, pss_signal_freq, fft_size); + vec_sc_prod_cfc(pss_signal_freq, 1.0/62.0, pss_signal_freq, fft_size); + + dft_plan_free(&plan); + + ret = LIBLTE_SUCCESS; } + return ret; +} -#ifdef CONVOLUTION_FFT - if (conv_fft_cc_init(&q->conv_fft, frame_size, PSS_LEN_FREQ)) { - fprintf(stderr, "Error initiating convolution FFT\n"); - goto clean_and_exit; +/* Initializes the PSS synchronization object with fft_size=128 + */ +int pss_synch_init(pss_synch_t *q, uint32_t frame_size) { + return pss_synch_init_fft(q, frame_size, 128); +} +/* Initializes the PSS synchronization object. + * + * It correlates a signal of frame_size samples with the PSS sequence in the frequency + * domain. The PSS sequence is transformed using fft_size samples. + */ +int pss_synch_init_fft(pss_synch_t *q, uint32_t frame_size, uint32_t fft_size) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + + uint32_t N_id_2; + uint32_t buffer_size; + bzero(q, sizeof(pss_synch_t)); + + q->N_id_2 = 10; + q->fft_size = fft_size; + q->frame_size = frame_size; + + buffer_size = fft_size + frame_size + 1; + + q->tmp_input = vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->tmp_input) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + q->conv_output = vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->conv_output) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + for (N_id_2=0;N_id_2<3;N_id_2++) { + q->pss_signal_freq[N_id_2] = vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->pss_signal_freq[N_id_2]) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + /* The PSS is translated into the frequency domain for each N_id_2 */ + if (pss_synch_init_N_id_2(q->pss_signal_freq[N_id_2], N_id_2, fft_size)) { + fprintf(stderr, "Error initiating PSS detector for N_id_2=%d fft_size=%d\n", N_id_2, fft_size); + goto clean_and_exit; + } + } + #ifdef CONVOLUTION_FFT + if (conv_fft_cc_init(&q->conv_fft, frame_size, fft_size)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + goto clean_and_exit; + } + #endif + + ret = LIBLTE_SUCCESS; } -#endif - q->correlation_threshold = DEFAULT_CORRELATION_TH; - q->nosync_timeout_frames = DEFAULT_NOSYNC_TIMEOUT; - q->cfo_auto = true; - q->N_id_2 = -1; - q->frame_size = frame_size; - q->frame_start_idx = NOT_SYNC; - q->fb_wp = 0; - - ret = 0; - clean_and_exit: if (ret == -1) { +clean_and_exit: + if (ret == LIBLTE_ERROR) { pss_synch_free(q); } return ret; } void pss_synch_free(pss_synch_t *q) { - if (q->pss_signal_freq) { - free(q->pss_signal_freq); - } - if (q->conv_abs) { - free(q->conv_abs); - } - if (q->tmp_input) { - free(q->tmp_input); - } - if (q->frame_buffer) { - free(q->frame_buffer); - } - if (q->conv_output) { - free(q->conv_output); - } + uint32_t i; -#ifdef CONVOLUTION_FFT - conv_fft_cc_free(&q->conv_fft); -#endif + if (q) { + for (i=0;i<3;i++) { + if (q->pss_signal_freq[i]) { + free(q->pss_signal_freq[i]); + } + } + #ifdef CONVOLUTION_FFT + conv_fft_cc_free(&q->conv_fft); + + #endif + if (q->tmp_input) { + free(q->tmp_input); + } + if (q->conv_output) { + free(q->conv_output); + } - bzero(q, sizeof(pss_synch_t)); + bzero(q, sizeof(pss_synch_t)); + } } /** * This function calculates the Zadoff-Chu sequence. * @param signal Output array. */ -int pss_generate(cf_t *signal, int N_id_2) { +int pss_generate(cf_t *signal, uint32_t N_id_2) { int i; float arg; const float root_value[] = { 25.0, 29.0, 34.0 }; @@ -130,7 +174,7 @@ int pss_generate(cf_t *signal, int N_id_2) { int sign = -1; - if (N_id_2 < 0 || N_id_2 > 2) { + if (N_id_2 > 2) { fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); return -1; } @@ -154,7 +198,7 @@ int pss_generate(cf_t *signal, int N_id_2) { /** 36.211 10.3 section 6.11.1.2 */ -void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp) { +void pss_put_slot(cf_t *pss_signal, cf_t *slot, uint32_t nof_prb, lte_cp_t cp) { int k; k = (CP_NSYMB(cp) - 1) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); @@ -162,45 +206,17 @@ void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp) { memset(&slot[k + PSS_LEN], 0, 5 * sizeof(cf_t)); } -/** Sets the current N_id_2 value. Initializes the object for this PSS sequence - * Returns -1 on error, 0 otherwise - */ -int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) { - q->N_id_2 = N_id_2; - - dft_plan_t plan; - cf_t pss_signal_pad[PSS_LEN_FREQ]; - cf_t pss_signal_time[PSS_LEN]; - if (N_id_2 < 0 || N_id_2 > 2) { +/** Sets the current N_id_2 value. Returns -1 on error, 0 otherwise + */ +int pss_synch_set_N_id_2(pss_synch_t *q, uint32_t N_id_2) { + if (!lte_N_id_2_isvalid((N_id_2))) { fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); return -1; + } else { + q->N_id_2 = N_id_2; + return 0; } - - pss_generate(pss_signal_time, N_id_2); - - memset(pss_signal_pad, 0, PSS_LEN_FREQ * sizeof(cf_t)); - memset(q->pss_signal_freq, 0, PSS_LEN_FREQ * sizeof(cf_t)); - memcpy(&pss_signal_pad[33], pss_signal_time, PSS_LEN * sizeof(cf_t)); - - if (dft_plan(&plan, PSS_LEN_FREQ - 1, BACKWARD, COMPLEX)) { - return -1; - } - dft_plan_set_mirror(&plan, true); - dft_plan_set_dc(&plan, true); - - dft_run_c(&plan, pss_signal_pad, q->pss_signal_freq); - - vec_sc_prod_cfc(q->pss_signal_freq, (float) 1 / (PSS_LEN_FREQ - 1), - pss_signal_pad, PSS_LEN_FREQ); - - vec_conj_cc(pss_signal_pad, q->pss_signal_freq, PSS_LEN_FREQ); - - q->N_id_2 = N_id_2; - - dft_plan_free(&plan); - - return 0; } /** Returns the index of the PSS correlation peak in a subframe. @@ -209,33 +225,42 @@ int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) { * * Input buffer must be subframe_size long. */ -int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, - float *corr_mean_value) { - int corr_peak_pos; - int conv_output_len; - - memset(&q->pss_signal_freq[PSS_LEN_FREQ], 0, q->frame_size * sizeof(cf_t)); - memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); - memset(&q->tmp_input[q->frame_size], 0, PSS_LEN_FREQ * sizeof(cf_t)); - -#ifdef CONVOLUTION_FFT - conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, - q->pss_signal_freq, q->conv_output); -#else - conv_output_len = conv_cc(input, q->pss_signal_freq, q->conv_output, q->frame_size, PSS_LEN_FREQ); -#endif - - vec_abs_cf(q->conv_output, q->conv_abs, conv_output_len); - corr_peak_pos = vec_max_fi(q->conv_abs, conv_output_len); - if (corr_peak_value) { - *corr_peak_value = q->conv_abs[corr_peak_pos]; - } - if (corr_mean_value) { - *corr_mean_value = vec_acc_ff(q->conv_abs, conv_output_len) - / conv_output_len; - } - - return (int) corr_peak_pos; +int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL) + { + + uint32_t corr_peak_pos; + uint32_t conv_output_len; + + if (!lte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Error finding PSS peak, N_id_2 not set\n"); + return LIBLTE_ERROR; + } + + bzero(&q->pss_signal_freq[q->N_id_2][q->fft_size], q->frame_size * sizeof(cf_t)); + memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); + bzero(&q->tmp_input[q->frame_size], q->fft_size * sizeof(cf_t)); + + /* Correlate input with PSS sequence */ + #ifdef CONVOLUTION_FFT + conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, + q->pss_signal_freq[q->N_id_2], q->conv_output); + #else + conv_output_len = conv_cc(input, q->pss_signal_freq[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); + #endif + + /* Find maximum of the absolute value of the correlation */ + corr_peak_pos = vec_max_abs_ci(q->conv_output, conv_output_len); + if (corr_peak_value) { + *corr_peak_value = cabsf(q->conv_output[corr_peak_pos]); + } + ret = (int) corr_peak_pos; + } + return ret; } /* Returns the CFO estimation given a PSS received sequence @@ -245,155 +270,12 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, */ float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv) { cf_t y0, y1, yr; - cf_t y[PSS_LEN_FREQ - 1]; - - vec_prod_ccc_unalign(q->pss_signal_freq, pss_recv, y, PSS_LEN_FREQ - 1); - y0 = vec_acc_cc(y, (PSS_LEN_FREQ - 1) / 2); - y1 = vec_acc_cc(&y[(PSS_LEN_FREQ - 1) / 2], (PSS_LEN_FREQ - 1) / 2); + y0 = vec_dot_prod_ccc(q->pss_signal_freq[q->N_id_2], pss_recv, q->fft_size/2); + y1 = vec_dot_prod_ccc(&q->pss_signal_freq[q->N_id_2][q->fft_size/2], &pss_recv[q->fft_size/2], q->fft_size/2); + yr = conjf(y0) * y1; return atan2f(__imag__ yr, __real__ yr) / M_PI; } -/** This function is designed to be called periodically on a subframe basis. - * The function finds the PSS correlation peak and computes (does not adjust) CFO automatically as defined by - * pss_synch_set_cfo_mode(). - * - * If the PSS sequence is not found, returns 0 writes nothing to the output buffer. - * If the PSS sequence is found, aligns the beginning of the subframe to the output buffer and returns the number of samples - * written to the output buffer. - * If synchronized, subsequent calls to this function align the input buffer to the subframe beginning. - */ -int pss_synch_frame(pss_synch_t *q, cf_t *input, cf_t *output, int nsamples) { - int max_idx, tmp_start_idx; - int retval; - float max_value; - - if (nsamples != q->frame_size) { - fprintf(stderr, "Configured for frame size %d but got %d samples\n", - q->frame_size, nsamples); - return -1; - } - - if (q->N_id_2 < 0) { - fprintf(stderr, - "N_id_2 must be configured before calling pss_synch()\n"); - return -1; - } - - max_idx = pss_synch_find_pss(q, input, &max_value, NULL); - if (max_value > q->correlation_threshold) { - tmp_start_idx = max_idx - nsamples / 2; - if (q->frame_start_idx != tmp_start_idx) { - printf("Re-synchronizing: new index is %d, old was %d\n", - tmp_start_idx, q->frame_start_idx); - } - q->frame_start_idx = tmp_start_idx; - } else { - if (q->nosync_timeout_frames > 0) { - q->nof_nosync_frames++; - if (q->nof_nosync_frames >= q->nosync_timeout_frames) { - q->frame_start_idx = NOT_SYNC; - } - } - } - - if (q->frame_start_idx == NOT_SYNC) { - - memcpy(q->frame_buffer, input, nsamples * sizeof(cf_t)); - retval = 0; - - } else if (q->frame_start_idx > 0) { - - if (q->fb_wp) { - memcpy(&q->frame_buffer[(nsamples - q->frame_start_idx)], input, - q->frame_start_idx * sizeof(cf_t)); - memcpy(output, q->frame_buffer, nsamples * sizeof(cf_t)); - retval = nsamples; - } else { - retval = 0; - } - memcpy(q->frame_buffer, &input[q->frame_start_idx], - (nsamples - q->frame_start_idx) * sizeof(cf_t)); - q->fb_wp = 1; - - } else { - - memcpy(output, &q->frame_buffer[nsamples + q->frame_start_idx], - (-q->frame_start_idx) * sizeof(cf_t)); - memcpy(&output[-q->frame_start_idx], input, - (nsamples + q->frame_start_idx) * sizeof(cf_t)); - memcpy(&q->frame_buffer[nsamples + q->frame_start_idx], - &input[nsamples + q->frame_start_idx], - (-q->frame_start_idx) * sizeof(cf_t)); - retval = nsamples; - } - - if (q->frame_start_idx != NOT_SYNC && q->cfo_auto && retval) { - q->current_cfo = pss_synch_cfo_compute(q, - &output[q->frame_size / 2 - PSS_LEN_FREQ + 1]); - } - - return retval; -} - -void pss_synch_set_timeout(pss_synch_t *q, int nof_frames) { - q->nosync_timeout_frames = nof_frames; -} - -void pss_synch_set_threshold(pss_synch_t *q, float threshold) { - q->correlation_threshold = threshold; -} - -void pss_synch_set_cfo_mode(pss_synch_t *q, bool cfo_auto) { - q->cfo_auto = cfo_auto; -} - -float pss_synch_get_cfo(pss_synch_t *q) { - return q->current_cfo; -} - -int pss_synch_get_frame_start_idx(pss_synch_t *q) { - return q->frame_start_idx; -} - -/** High-level API */ - -int pss_synch_initialize(pss_synch_hl* h) { - int fs = h->init.frame_size; - if (!fs) { - fs = DEFAULT_FRAME_SIZE; - } - if (pss_synch_init(&h->obj, fs)) { - return -1; - } - if (h->init.unsync_nof_pkts) { - pss_synch_set_timeout(&h->obj, h->init.unsync_nof_pkts); - } - - pss_synch_set_N_id_2(&h->obj, h->init.N_id_2); - if (h->init.do_cfo) { - pss_synch_set_cfo_mode(&h->obj, true); - } else { - pss_synch_set_cfo_mode(&h->obj, false); - } - return 0; -} - -int pss_synch_work(pss_synch_hl* hl) { - - if (hl->ctrl_in.correlation_threshold) { - pss_synch_set_threshold(&hl->obj, hl->ctrl_in.correlation_threshold); - } - - hl->out_len = pss_synch_frame(&hl->obj, hl->input, hl->output, hl->in_len); - - return 0; -} - -int pss_synch_stop(pss_synch_hl* hl) { - pss_synch_free(&hl->obj); - return 0; -} - diff --git a/lte/phy/lib/sync/src/sss.c b/lte/phy/lib/sync/src/sss.c index f3b4bb62a..5755ea18f 100644 --- a/lte/phy/lib/sync/src/sss.c +++ b/lte/phy/lib/sync/src/sss.c @@ -35,21 +35,58 @@ #include "liblte/phy/sync/sss.h" #include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/convolution.h" +#include "liblte/phy/utils/vector.h" -void generate_sss_all_tables(struct sss_tables *tables, int N_id_2); +void generate_sss_all_tables(struct sss_tables *tables, uint32_t N_id_2); void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in); -void generate_N_id_1_table(int table[30][30]); - -int sss_synch_init(sss_synch_t *q) { - bzero(q, sizeof(sss_synch_t)); +void generate_N_id_1_table(uint32_t table[30][30]); + +int sss_synch_init(sss_synch_t *q, uint32_t fft_size) { + + if (q != NULL && + fft_size <= 2048) + { + uint32_t N_id_2; + struct sss_tables sss_tables; + + bzero(q, sizeof(sss_synch_t)); + + if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) { + sss_synch_free(q); + return LIBLTE_ERROR; + } + q->fft_size = fft_size; + + generate_N_id_1_table(q->N_id_1_table); + dft_plan_set_mirror(&q->dftp_input, true); + dft_plan_set_dc(&q->dftp_input, true); + + for (N_id_2=0;N_id_2<3;N_id_2++) { + generate_sss_all_tables(&sss_tables, N_id_2); + convert_tables(&q->fc_tables[N_id_2], &sss_tables); + } + q->N_id_2 = 0; + return LIBLTE_SUCCESS; + } + return LIBLTE_ERROR_INVALID_INPUTS; +} - if (dft_plan(&q->dftp_input, SSS_DFT_LEN, FORWARD, COMPLEX)) { - return -1; +int sss_synch_realloc(sss_synch_t *q, uint32_t fft_size) { + if (q != NULL && + fft_size <= 2048) + { + dft_plan_free(&q->dftp_input); + if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) { + sss_synch_free(q); + return LIBLTE_ERROR; + } + dft_plan_set_mirror(&q->dftp_input, true); + dft_plan_set_dc(&q->dftp_input, true); + + q->fft_size = fft_size; + return LIBLTE_SUCCESS; } - generate_N_id_1_table(q->N_id_1_table); - dft_plan_set_mirror(&q->dftp_input, true); - dft_plan_set_dc(&q->dftp_input, true); - return 0; + return LIBLTE_ERROR_INVALID_INPUTS; } void sss_synch_free(sss_synch_t *q) { @@ -57,84 +94,41 @@ void sss_synch_free(sss_synch_t *q) { bzero(q, sizeof(sss_synch_t)); } -/** Initializes the SSS sequences for the given N_id_2 */ -int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2) { - if (N_id_2 < 0 || N_id_2 > 2) { +/** Sets the N_id_2 to search for */ +int sss_synch_set_N_id_2(sss_synch_t *q, uint32_t N_id_2) { + if (!lte_N_id_2_isvalid(N_id_2)) { fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); - return -1; + return LIBLTE_ERROR; + } else { + q->N_id_2 = N_id_2; + return LIBLTE_SUCCESS; } - - struct sss_tables sss_tables; - generate_sss_all_tables(&sss_tables, N_id_2); - convert_tables(&q->fc_tables, &sss_tables); - - return 0; } /** 36.211 10.3 section 6.11.2.2 */ -void sss_put_slot(float *sss, cf_t *slot, int nof_prb, lte_cp_t cp) { - int i, k; +void sss_put_slot(float *sss, cf_t *slot, uint32_t nof_prb, lte_cp_t cp) { + uint32_t i, k; k = (CP_NSYMB(cp) - 2) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; - memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); - for (i = 0; i < SSS_LEN; i++) { - __real__ slot[k + i] = sss[i]; - __imag__ slot[k + i] = 0; - } - memset(&slot[k + SSS_LEN], 0, 5 * sizeof(cf_t)); -} - -/* In this function, input points to the beginning of the subframe. Saves result in subframe_idx and N_id_1 - * Return 1 if the sequence was found, 0 if the peak is not found, -1 if the subframe_sz or symbol_sz are - * invalid or not configured. - * Before calling this function, the correlation threshold and symbol size duration need to be set - * using sss_synch_set_threshold() and sss_synch_set_symbol_sz(). - */ -int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, int *N_id_1) { - int m0, m1; - float m0_value, m1_value; - - if (q->subframe_sz <= 0 || q->symbol_sz <= 0) { - return -1; - } - - sss_synch_m0m1(q, &input[SSS_SYMBOL_ST(q->subframe_sz, q->symbol_sz)], &m0, - &m0_value, &m1, &m1_value); - - if (m0_value > q->corr_peak_threshold - && m1_value > q->corr_peak_threshold) { - if (subframe_idx) { - *subframe_idx = sss_synch_subframe(m0, m1); + + if (k > 5) { + memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); + for (i = 0; i < SSS_LEN; i++) { + __real__ slot[k + i] = sss[i]; + __imag__ slot[k + i] = 0; } - if (N_id_1) { - *N_id_1 = sss_synch_N_id_1(q, m0, m1); - } - return 1; - } else { - return 0; + memset(&slot[k + SSS_LEN], 0, 5 * sizeof(cf_t)); } } -/** Used by sss_synch_frame() to compute the beginning of the SSS symbol - * symbol_sz MUST INCLUDE THE CYCLIC PREFIX SIZE - */ -void sss_synch_set_symbol_sz(sss_synch_t *q, int symbol_sz) { - q->symbol_sz = symbol_sz; -} - -/** Used by sss_synch_frame() to compute the beginning of the SSS symbol */ -void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz) { - q->subframe_sz = subframe_sz; -} - /** Sets the SSS correlation peak detection threshold */ void sss_synch_set_threshold(sss_synch_t *q, float threshold) { q->corr_peak_threshold = threshold; } /** Returns the subframe index based on the m0 and m1 values */ -int sss_synch_subframe(int m0, int m1) { +uint32_t sss_synch_subframe(uint32_t m0, uint32_t m1) { if (m1 > m0) { return 0; } else { @@ -143,27 +137,27 @@ int sss_synch_subframe(int m0, int m1) { } /** Returns the N_id_1 value based on the m0 and m1 values */ -int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1) { - if (m0 < 0 || m0 > 29 || m1 < 0 || m1 > 29) { - return -1; +int sss_synch_N_id_1(sss_synch_t *q, uint32_t m0, uint32_t m1) { + if (m0==m1 || m0 > 29 || m1 > 29) { + return LIBLTE_ERROR; } if (m1 > m0) { return q->N_id_1_table[m0][m1 - 1]; } else { return q->N_id_1_table[m1][m0 - 1]; - } + } } /** High-level API */ int sss_synch_initialize(sss_synch_hl* h) { - if (sss_synch_init(&h->obj)) { - return -1; + if (sss_synch_init(&h->obj, 128)) { + return LIBLTE_ERROR; } sss_synch_set_N_id_2(&h->obj, h->init.N_id_2); - return 0; + return LIBLTE_SUCCESS; } int sss_synch_work(sss_synch_hl* hl) { @@ -171,20 +165,12 @@ int sss_synch_work(sss_synch_hl* hl) { if (hl->ctrl_in.correlation_threshold) { sss_synch_set_threshold(&hl->obj, hl->ctrl_in.correlation_threshold); } - if (hl->ctrl_in.subframe_sz) { - sss_synch_set_subframe_sz(&hl->obj, hl->ctrl_in.subframe_sz); - } - if (hl->ctrl_in.symbol_sz) { - sss_synch_set_symbol_sz(&hl->obj, hl->ctrl_in.symbol_sz); - } - sss_synch_frame(&hl->obj, hl->input, &hl->ctrl_out.subframe_idx, - &hl->ctrl_out.N_id_1); - - return 0; + + return LIBLTE_SUCCESS; } int sss_synch_stop(sss_synch_hl* hl) { sss_synch_free(&hl->obj); - return 0; + return LIBLTE_SUCCESS; } diff --git a/lte/phy/lib/sync/src/sync.c b/lte/phy/lib/sync/src/sync.c index aad208eae..34e4bd28e 100644 --- a/lte/phy/lib/sync/src/sync.c +++ b/lte/phy/lib/sync/src/sync.c @@ -25,232 +25,272 @@ * */ - - #include +#include +#include #include "liblte/phy/utils/debug.h" #include "liblte/phy/common/phy_common.h" #include "liblte/phy/sync/sync.h" +#include "liblte/phy/utils/vector.h" -int sync_init(sync_t *q, int frame_size) { - int N_id_2; - bzero(q, sizeof(sync_t)); - q->force_N_id_2 = -1; - q->threshold = 1.5; - q->pss_mode = PEAK_MEAN; - q->detect_cp = true; - q->sss_en = true; +static bool fft_size_isvalid(uint32_t fft_size) { + if (fft_size >= FFT_SIZE_MIN && fft_size <= FFT_SIZE_MAX && (fft_size%64) == 0) { + return true; + } else { + return false; + } +} - for (N_id_2=0;N_id_2<3;N_id_2++) { - if (pss_synch_init(&q->pss[N_id_2], frame_size)) { +int sync_init(sync_t *q, uint32_t frame_size, uint32_t fft_size) { + + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + frame_size >= fft_size && + frame_size <= 307200 && + fft_size_isvalid(fft_size)) + { + bzero(q, sizeof(sync_t)); + q->detect_cp = true; + q->normalize_en = true; + q->sss_en = true; + q->N_id_2 = 1000; + q->N_id_1 = 1000; + q->fft_size = fft_size; + q->frame_size = frame_size; + + if (pss_synch_init_fft(&q->pss, frame_size, fft_size)) { fprintf(stderr, "Error initializing PSS object\n"); - return -1; - } - if (pss_synch_set_N_id_2(&q->pss[N_id_2], N_id_2)) { - fprintf(stderr, "Error initializing N_id_2\n"); - return -1; + return LIBLTE_ERROR; } - if (sss_synch_init(&q->sss[N_id_2])) { + if (sss_synch_init(&q->sss, fft_size)) { fprintf(stderr, "Error initializing SSS object\n"); - return -1; - } - if (sss_synch_set_N_id_2(&q->sss[N_id_2], N_id_2)) { - fprintf(stderr, "Error initializing N_id_2\n"); - return -1; + return LIBLTE_ERROR; } - DEBUG("PSS and SSS initiated N_id_2=%d\n", N_id_2); - } - return 0; + DEBUG("SYNC init with frame_size=%d and fft_size=%d\n", frame_size, fft_size); + + ret = LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters frame_size: %d, fft_size: %d\n", frame_size, fft_size); + } + return ret; } void sync_free(sync_t *q) { - int N_id_2; - - for (N_id_2=0;N_id_2<3;N_id_2++) { - pss_synch_free(&q->pss[N_id_2]); - sss_synch_free(&q->sss[N_id_2]); + if (q) { + pss_synch_free(&q->pss); + sss_synch_free(&q->sss); } } -void sync_pss_det_absolute(sync_t *q) { - q->pss_mode = ABSOLUTE; -} -void sync_pss_det_peak_to_avg(sync_t *q) { - q->pss_mode = PEAK_MEAN; -} - void sync_set_threshold(sync_t *q, float threshold) { q->threshold = threshold; } -void sync_force_N_id_2(sync_t *q, int force_N_id_2) { - q->force_N_id_2 = force_N_id_2; +void sync_sss_en(sync_t *q, bool enabled) { + q->sss_en = enabled; } -void sync_force_cp(sync_t *q, lte_cp_t cp) { - q->cp = cp; - q->detect_cp = false; +void sync_normalize_en(sync_t *q, bool enable) { + q->normalize_en = enable; } -void sync_sss_en(sync_t *q, bool enabled) { - q->sss_en = enabled; +bool sync_sss_detected(sync_t *q) { + return lte_N_id_1_isvalid(q->N_id_1); } int sync_get_cell_id(sync_t *q) { - if (q->N_id_1 >=0 && q->N_id_2 >= 0) { - return q->N_id_1*3 + q->N_id_2; + if (lte_N_id_2_isvalid(q->N_id_2) && lte_N_id_1_isvalid(q->N_id_1)) { + return q->N_id_1*3 + q->N_id_2; } else { - return -1; + fprintf(stderr, "Error getting cell_id, invalid N_id_1 or N_id_2\n"); + return LIBLTE_ERROR; } } -int sync_get_N_id_1(sync_t *q) { - return q->N_id_1; -} - -int sync_get_N_id_2(sync_t *q) { - return q->N_id_2; +int sync_set_N_id_2(sync_t *q, uint32_t N_id_2) { + if (lte_N_id_2_isvalid(N_id_2)) { + q->N_id_2 = N_id_2; + return LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid N_id_2=%d\n", N_id_2); + return LIBLTE_ERROR_INVALID_INPUTS; + } } -int sync_get_slot_id(sync_t *q) { - return q->slot_id; +uint32_t sync_get_sf_idx(sync_t *q) { + return q->sf_idx; } float sync_get_cfo(sync_t *q) { return q->cfo; } -float sync_get_peak_to_avg(sync_t *q) { - return q->peak_to_avg; +float sync_get_last_peak_value(sync_t *q) { + return q->peak_value; +} + +float sync_get_peak_value(sync_t *q) { + return q->mean_peak_value; +} + +void sync_cp_en(sync_t *q, bool enabled) { + q->detect_cp = enabled; } lte_cp_t sync_get_cp(sync_t *q) { return q->cp; } -int sync_run(sync_t *q, cf_t *input) { - int N_id_2, peak_pos[3], sss_idx_n, sss_idx_e; - int m0, m1; - float m0_value_e, m1_value_e,m0_value_n, m1_value_n; - int slot_id_e, N_id_1_e, slot_id_n, N_id_1_n; - float peak_value[3]; - float mean_value[3]; - float max=-999; - int i; - int peak_detected; - - if (q->force_N_id_2 == -1) { - for (N_id_2=0;N_id_2<3;N_id_2++) { - peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], input, - &peak_value[N_id_2], &mean_value[N_id_2]); - } - for (i=0;i<3;i++) { - if (peak_value[i] > max) { - max = peak_value[i]; - N_id_2 = i; - } - } +/* CP detection algorithm taken from: + * "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" + * by Jung-In Kim et al. + */ +static lte_cp_t detect_cp(sync_t *q, cf_t *input, uint32_t peak_pos) +{ + float R_norm, R_ext, C_norm, C_ext; + float M_norm, M_ext; + + R_norm = crealf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + &input[peak_pos-CP_NORM(7, q->fft_size)], + CP_NORM(7, q->fft_size))); + C_norm = cabsf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + &input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + CP_NORM(7, q->fft_size))); + R_ext = crealf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + &input[peak_pos-CP_EXT(q->fft_size)], + CP_EXT(q->fft_size))); + C_ext = cabsf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + &input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + CP_EXT(q->fft_size))); + M_norm = R_norm/C_norm; + M_ext = R_ext/C_ext; + + if (M_norm > M_ext) { + return CPNORM; + } else if (M_norm < M_ext) { + return CPEXT; } else { - N_id_2 = q->force_N_id_2; - peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], input, - &peak_value[N_id_2], &mean_value[N_id_2]); - } - - q->peak_to_avg = peak_value[N_id_2] / mean_value[N_id_2]; - - DEBUG("PSS possible peak N_id_2=%d, pos=%d peak=%.2f par=%.2f threshold=%.2f\n", - N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold); - - /* If peak detected */ - peak_detected = 0; - if (peak_pos[N_id_2] - 128 >= 0) { - if (q->pss_mode == ABSOLUTE) { - if (peak_value[N_id_2] > q->threshold) { - peak_detected = 1; - } + if (R_norm > R_ext) { + return CPNORM; } else { - if (q->peak_to_avg > q->threshold) { - peak_detected = 1; - } + return CPEXT; } } - if (peak_detected) { +} - q->cfo = pss_synch_cfo_compute(&q->pss[N_id_2], &input[peak_pos[N_id_2]-128]); +int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos) { + uint32_t m0, m1; + int sss_idx, ret; + float m0_value, m1_value; - INFO("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2, - peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold, q->cfo); + sss_synch_set_N_id_2(&q->sss, q->N_id_2); - if (q->sss_en) { + if (q->detect_cp) { + q->cp = detect_cp(q, input, peak_pos); + } + + /* Make sure we have enough room to find SSS sequence */ + sss_idx = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, q->cp)); - /* Make sure we have enough room to find SSS sequence */ - sss_idx_n = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); - sss_idx_e = peak_pos[N_id_2]-2*(128+CP(128,CPEXT_LEN)); + if (sss_idx < 0) { + INFO("Not enough room to decode CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx, peak_pos); + return LIBLTE_SUCCESS; + } + + /* try Normal CP length */ + sss_synch_m0m1(&q->sss, &input[sss_idx], &m0, &m0_value, &m1, &m1_value); + + q->sf_idx = sss_synch_subframe(m0, m1); + ret = sss_synch_N_id_1(&q->sss, m0, m1); + if (ret >= 0) { + q->N_id_1 = (uint32_t) ret; + } else { + q->N_id_1 = 1000; + } + + DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", + q->N_id_1, q->sf_idx, CP_ISNORM(q->cp)?"Normal":"Extended"); - if (q->detect_cp) { - if (sss_idx_n < 0 || sss_idx_e < 0) { - INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e); - return -1; - } - } else { - if (CP_ISNORM(q->cp)) { - if (sss_idx_n < 0) { - INFO("Not enough room to decode SSS (%d)\n", sss_idx_n); - return -1; - } - } else { - if (sss_idx_e < 0) { - INFO("Not enough room to decode SSS (%d)\n", sss_idx_e); - return -1; - } - } - } - N_id_1_e = -1; - N_id_1_n = -1; - slot_id_e = -1; - slot_id_n = -1; - /* try Normal CP length */ - if (q->detect_cp || CP_ISNORM(q->cp)) { - sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_n], - &m0, &m0_value_n, &m1, &m1_value_n); - - slot_id_n = 2 * sss_synch_subframe(m0, m1); - N_id_1_n = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); - } + return 1; +} - if (q->detect_cp || CP_ISEXT(q->cp)) { - /* Now try Extended CP length */ - sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_e], - &m0, &m0_value_e, &m1, &m1_value_e); +int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position) +{ + + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + float peak_unnormalized, energy; + + if (q != NULL && + input != NULL && + lte_N_id_2_isvalid(q->N_id_2) && + fft_size_isvalid(q->fft_size)) + { + uint32_t peak_pos; + + if (peak_position) { + *peak_position = 0; + } - slot_id_e = 2 * sss_synch_subframe(m0, m1); - N_id_1_e = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + pss_synch_set_N_id_2(&q->pss, q->N_id_2); + + peak_pos = pss_synch_find_pss(&q->pss, &input[find_offset], &peak_unnormalized); + + if (q->normalize_en && + peak_pos + find_offset >= q->fft_size) + { + /* Compute the energy of the received PSS sequence to normalize */ + cf_t *pss_ptr = &input[find_offset+peak_pos-q->fft_size]; + energy = sqrtf(crealf(vec_dot_prod_conj_ccc(pss_ptr, pss_ptr, q->fft_size)) / (q->fft_size)); + q->mean_energy = EXPAVERAGE(energy, q->mean_energy, q->frame_cnt); + } else { + if (q->mean_energy == 0.0) { + q->mean_energy = 1.0; } + energy = q->mean_energy; + } - /* Correlation with extended CP hypoteshis is greater than with normal? */ - if ((q->detect_cp && m0_value_e * m1_value_e > m0_value_n * m1_value_n) - || CP_ISEXT(q->cp)) { - q->cp = CPEXT; - q->slot_id = slot_id_e; - q->N_id_1 = N_id_1_e; - /* then is normal CP */ + /* Normalize and compute mean peak value */ + q->peak_value = peak_unnormalized/energy; + q->mean_peak_value = EXPAVERAGE(q->peak_value, q->mean_peak_value, q->frame_cnt); + q->frame_cnt++; + + /* If peak is over threshold, compute CFO and SSS */ + if (q->peak_value >= q->threshold) { + if (find_offset + peak_pos >= q->fft_size) { + q->cfo = pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]); + if (q->sss_en) { + if (sync_sss(q, input, find_offset + peak_pos) < 0) { + fprintf(stderr, "Error synchronizing with SSS\n"); + return LIBLTE_ERROR; + } + } } else { - q->cp = CPNORM; - q->slot_id = slot_id_n; - q->N_id_1 = N_id_1_n; + INFO("Warning: no space for CFO computation\n",0); } - q->N_id_2 = N_id_2; - - INFO("SSS detected N_id_1=%d, slot_idx=%d, %s CP\n", - q->N_id_1, q->slot_id, CP_ISNORM(q->cp)?"Normal":"Extended"); + + if (peak_position) { + *peak_position = peak_pos; + } + ret = 1; + } else { + ret = LIBLTE_SUCCESS; } - return peak_pos[N_id_2]; + INFO("SYNC ret=%d N_id_2=%d pos=%d peak=%.2f energy=%.3f threshold=%.2f sf_idx=%d\n", + ret, q->N_id_2, peak_pos, q->peak_value, energy, q->threshold, q->sf_idx); - } else { - return -1; + } else if (lte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Must call sync_set_N_id_2() first!\n"); } + + return ret; +} + +void sync_reset(sync_t *q) { + q->frame_cnt = 0; } diff --git a/lte/phy/lib/sync/test/CMakeLists.txt b/lte/phy/lib/sync/test/CMakeLists.txt index 2c5b29c3b..d9f8c49b3 100644 --- a/lte/phy/lib/sync/test/CMakeLists.txt +++ b/lte/phy/lib/sync/test/CMakeLists.txt @@ -19,6 +19,16 @@ # and at http://www.gnu.org/licenses/. # +######################################################################## +# PROGRAM TO DEBUG PSS FROM USRP +######################################################################## + +LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND) +IF(${CUHD_FIND} GREATER -1) + ADD_EXECUTABLE(pss_usrp pss_usrp.c) + TARGET_LINK_LIBRARIES(pss_usrp lte_phy cuhd) +ENDIF(${CUHD_FIND} GREATER -1) + ######################################################################## # SYNC TEST ######################################################################## @@ -26,11 +36,16 @@ ADD_EXECUTABLE(sync_test sync_test.c) TARGET_LINK_LIBRARIES(sync_test lte_phy) -ADD_TEST(sync_test_100 sync_test -o 100) -ADD_TEST(sync_test_400 sync_test -o 400) -ADD_TEST(sync_test_100_e sync_test -o 100 -e) -ADD_TEST(sync_test_400_e sync_test -o 400 -e) - +ADD_TEST(sync_test_100 sync_test -o 100 -c 501) +ADD_TEST(sync_test_400 sync_test -o 400 -c 2) +ADD_TEST(sync_test_100_e sync_test -o 100 -e -c 150) +ADD_TEST(sync_test_400_e sync_test -o 400 -e -c 151) + +ADD_TEST(sync_test_100 sync_test -o 100 -p 50 -c 501) +ADD_TEST(sync_test_400 sync_test -o 400 -p 50 -c 500) +ADD_TEST(sync_test_100_e sync_test -o 100 -e -p 50 -c 133) +ADD_TEST(sync_test_400_e sync_test -o 400 -e -p 50 -c 123) + ######################################################################## # CFO TEST ######################################################################## diff --git a/lte/phy/lib/sync/test/cfo_test.c b/lte/phy/lib/sync/test/cfo_test.c index 1ccb17cc2..5c6086c15 100644 --- a/lte/phy/lib/sync/test/cfo_test.c +++ b/lte/phy/lib/sync/test/cfo_test.c @@ -96,8 +96,8 @@ int main(int argc, char **argv) { return -1; } - cfo_correct(&cfocorr, output, freq); - cfo_correct(&cfocorr, output, -freq); + cfo_correct(&cfocorr, output, output, freq); + cfo_correct(&cfocorr, output, output, -freq); mse = 0; for (i=0;i +#include +#include +#include +#include +#include +#include + +#include + +#include "liblte/phy/phy.h" +#include "liblte/cuhd/cuhd.h" + +uint32_t N_id_2 = 100; +char *uhd_args=""; +float uhd_gain=40.0, uhd_freq=-1.0; +int nof_frames = -1; +uint32_t fft_size=64; +float threshold = 0.4; + +void usage(char *prog) { + printf("Usage: %s [agtvnp] -f rx_frequency_hz -i N_id_2\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD Gain [Default %.2f dB]\n", uhd_gain); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); + printf("\t-v verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agtvsfi")) != -1) { + switch (opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'i': + N_id_2 = atoi(argv[optind]); + break; + case 's': + fft_size = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (N_id_2 > 2 || uhd_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + cf_t *buffer; + int frame_cnt, n; + void *uhd; + pss_synch_t pss; + int32_t flen; + int peak_idx, last_peak; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + + parse_args(argc, argv); + + flen = 4800*(fft_size/64); + + buffer = malloc(sizeof(cf_t) * flen); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (pss_synch_init_fft(&pss, flen, fft_size)) { + fprintf(stderr, "Error initiating PSS\n"); + exit(-1); + } + if (pss_synch_set_N_id_2(&pss, N_id_2)) { + fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2); + exit(-1); + } + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + printf("Set RX rate: %.2f MHz\n", cuhd_set_rx_srate(uhd, flen*2*100) / 1000000); + printf("Set RX gain: %.1f dB\n", cuhd_set_rx_gain(uhd, uhd_gain)); + printf("Set RX freq: %.2f MHz\n", cuhd_set_rx_freq(uhd, uhd_freq) / 1000000); + cuhd_rx_wait_lo_locked(uhd); + cuhd_start_rx_stream(uhd); + + printf("Frame length %d samples\n", flen); + printf("PSS detection threshold: %.2f\n", threshold); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + last_peak = 0; + mean_peak = 0; + while(frame_cnt < nof_frames || nof_frames == -1) { + n = cuhd_recv(uhd, buffer, flen, 1); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + peak_idx = pss_synch_find_pss(&pss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding PSS peak\n"); + exit(-1); + } + + float y = sqrtf(crealf(vec_dot_prod_conj_ccc(&buffer[peak_idx-fft_size], + &buffer[peak_idx-fft_size], + fft_size)) / + fft_size); + float x = peak_value/y; + + mean_peak = EXPAVERAGE(x, mean_peak, frame_cnt); + + if (x >= threshold) { + nof_det++; + } else { + nof_nodet++; + } + + if (frame_cnt > 100) { + if (abs(last_peak-peak_idx) > 10) { + if (x >= threshold) { + nof_nopeakdet++; + } else { + if (nof_nodet > 0) { + nof_nodet--; + } + } + nof_nopeak++; + } + } + + frame_cnt++; + + printf("[%5d]: Pos: %5d, En: %.4f Val: %.3f MeanVal: %.3f, Det: %.3f, No-Det: %.3f, NoPeak: %.3f, NoPeakDet: %.3f\r", + frame_cnt, + peak_idx, y, x, mean_peak, + (float) nof_det/frame_cnt, (float) nof_nodet/frame_cnt, + (float) nof_nopeak/frame_cnt, (float) nof_nopeakdet/nof_nopeak); + + if (VERBOSE_ISINFO()) { + printf("\n"); + } + + last_peak = peak_idx; + + } + + pss_synch_free(&pss); + free(buffer); + cuhd_close(uhd); + + printf("Ok\n"); + exit(0); +} + diff --git a/lte/phy/lib/sync/test/sync_test.c b/lte/phy/lib/sync/test/sync_test.c index f1641c9eb..b3f3133ae 100644 --- a/lte/phy/lib/sync/test/sync_test.c +++ b/lte/phy/lib/sync/test/sync_test.c @@ -32,18 +32,21 @@ #include #include #include + #include #include "liblte/phy/phy.h" int cell_id = -1, offset = 0; lte_cp_t cp = CPNORM; +uint32_t nof_prb=6; -#define FLEN 9600 +#define FLEN SF_LEN(fft_size) void usage(char *prog) { - printf("Usage: %s [coev]\n", prog); + printf("Usage: %s [cpoev]\n", prog); printf("\t-c cell_id [Default check for all]\n"); + printf("\t-p nof_prb [Default %d]\n", nof_prb); printf("\t-o offset [Default %d]\n", offset); printf("\t-e extended CP [Default normal]\n"); printf("\t-v verbose\n"); @@ -51,11 +54,14 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "coev")) != -1) { + while ((opt = getopt(argc, argv, "cpoev")) != -1) { switch (opt) { case 'c': cell_id = atoi(argv[optind]); break; + case 'p': + nof_prb = atoi(argv[optind]); + break; case 'o': offset = atoi(argv[optind]); break; @@ -78,40 +84,48 @@ int main(int argc, char **argv) { cf_t pss_signal[PSS_LEN]; float sss_signal0[SSS_LEN]; // for subframe 0 float sss_signal5[SSS_LEN]; // for subframe 5 - int cid, max_cid, find_idx; + int cid, max_cid; + uint32_t find_idx; sync_t sync; lte_fft_t ifft; - + int fft_size; + parse_args(argc, argv); + fft_size = lte_symbol_sz(nof_prb); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", nof_prb); + exit(-1); + } + buffer = malloc(sizeof(cf_t) * FLEN); if (!buffer) { perror("malloc"); exit(-1); } - fft_buffer = malloc(sizeof(cf_t) * 2 * FLEN); + fft_buffer = malloc(sizeof(cf_t) * FLEN); if (!fft_buffer) { perror("malloc"); exit(-1); } - - if (lte_ifft_init(&ifft, cp, 6)) { + + if (lte_ifft_init(&ifft, cp, nof_prb)) { fprintf(stderr, "Error creating iFFT object\n"); exit(-1); } - if (sync_init(&sync, FLEN)) { + if (sync_init(&sync, FLEN, fft_size)) { fprintf(stderr, "Error initiating PSS/SSS\n"); return -1; } - sync_set_threshold(&sync, 20); - sync_force_N_id_2(&sync, -1); + /* Set a very high threshold to make sure the correlation is ok */ + sync_set_threshold(&sync, 1.4); if (cell_id == -1) { cid = 0; - max_cid = 149; + max_cid = 49; } else { cid = cell_id; max_cid = cell_id; @@ -123,21 +137,28 @@ int main(int argc, char **argv) { pss_generate(pss_signal, N_id_2); sss_generate(sss_signal0, sss_signal5, cid); + sync_set_N_id_2(&sync, N_id_2); + for (ns=0;ns<2;ns++) { memset(buffer, 0, sizeof(cf_t) * FLEN); - pss_put_slot(pss_signal, buffer, 6, cp); - sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, cp); + pss_put_slot(pss_signal, buffer, nof_prb, cp); + sss_put_slot(ns?sss_signal5:sss_signal0, buffer, nof_prb, cp); /* Transform to OFDM symbols */ - memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN); - lte_ifft_run(&ifft, buffer, &fft_buffer[offset]); + memset(fft_buffer, 0, sizeof(cf_t) * FLEN); + lte_ifft_run_slot(&ifft, buffer, &fft_buffer[offset]); + + vec_save_file("input", fft_buffer, sizeof(cf_t) * FLEN); - find_idx = sync_run(&sync, fft_buffer); - find_ns = sync_get_slot_id(&sync); + if (sync_find(&sync, fft_buffer, 0, &find_idx) < 0) { + fprintf(stderr, "Error running sync_find\n"); + exit(-1); + } + find_ns = 2*sync_get_sf_idx(&sync); printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset, ns, find_ns); - if (find_idx != offset + 960) { - printf("offset != find_offset: %d != %d\n", find_idx, offset + 960); + if (find_idx != offset + FLEN/2) { + printf("offset != find_offset: %d != %d\n", find_idx, offset + FLEN/2); exit(-1); } if (ns*10 != find_ns) { diff --git a/lte/phy/lib/ue/src/ue_celldetect.c b/lte/phy/lib/ue/src/ue_celldetect.c new file mode 100644 index 000000000..fa4c75e82 --- /dev/null +++ b/lte/phy/lib/ue/src/ue_celldetect.c @@ -0,0 +1,268 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "liblte/phy/ue/ue_celldetect.h" + +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#define FIND_FFTSIZE 64 +#define FIND_SFLEN 5*SF_LEN(FIND_FFTSIZE) + +int ue_celldetect_init(ue_celldetect_t * q) { + return ue_celldetect_init_max(q, CS_DEFAULT_MAXFRAMES_TOTAL, CS_DEFAULT_MAXFRAMES_DETECTED); +} + +int ue_celldetect_init_max(ue_celldetect_t * q, uint32_t max_frames_total, uint32_t max_frames_detected) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = LIBLTE_ERROR; + + bzero(q, sizeof(ue_celldetect_t)); + + q->candidates = malloc(sizeof(ue_celldetect_result_t) * max_frames_detected); + if (!q->candidates) { + perror("malloc"); + goto clean_exit; + } + if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) { + goto clean_exit; + } + q->mode_ntimes = malloc(sizeof(uint32_t) * max_frames_detected); + if (!q->mode_ntimes) { + perror("malloc"); + goto clean_exit; + } + q->mode_counted = malloc(sizeof(char) * max_frames_detected); + if (!q->mode_counted) { + perror("malloc"); + goto clean_exit; + } + + sync_set_threshold(&q->sfind, CS_FIND_THRESHOLD); + sync_sss_en(&q->sfind, true); + + q->max_frames_total = max_frames_total; + q->max_frames_detected = max_frames_detected; + q->nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL; + q->nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED; + + ue_celldetect_reset(q); + + ret = LIBLTE_SUCCESS; + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_celldetect_free(q); + } + return ret; +} + +void ue_celldetect_free(ue_celldetect_t * q) +{ + if (q->candidates) { + free(q->candidates); + } + if (q->mode_counted) { + free(q->mode_counted); + } + if (q->mode_ntimes) { + free(q->mode_ntimes); + } + sync_free(&q->sfind); +} + + +void ue_celldetect_reset(ue_celldetect_t * q) +{ + q->current_nof_detected = 0; + q->current_nof_total = 0; + q->current_N_id_2 = 0; +} + +void ue_celldetect_set_threshold(ue_celldetect_t * q, float threshold) +{ + sync_set_threshold(&q->sfind, threshold); +} + +int ue_celldetect_set_nof_frames_total(ue_celldetect_t * q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames_total) { + q->nof_frames_total = nof_frames; + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR; + } +} + +int ue_celldetect_set_nof_frames_detected(ue_celldetect_t * q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames_detected) { + q->nof_frames_detected = nof_frames; + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR; + } +} + +/* Decide the most likely cell based on the mode */ +void decide_cell(ue_celldetect_t * q, ue_celldetect_result_t *found_cell) +{ + uint32_t i, j; + + bzero(q->mode_counted, q->nof_frames_detected); + bzero(q->mode_ntimes, sizeof(uint32_t) * q->nof_frames_detected); + + /* First find mode of CELL IDs */ + for (i = 0; i < q->nof_frames_detected; i++) { + uint32_t cnt = 1; + for (j=i+1;jnof_frames_detected;j++) { + if (q->candidates[j].cell_id == q->candidates[i].cell_id && !q->mode_counted[j]) { + q->mode_counted[j]=1; + cnt++; + } + } + q->mode_ntimes[i] = cnt; + } + uint32_t max_times=0, mode_pos=0; + for (i=0;inof_frames_detected;i++) { + if (q->mode_ntimes[i] > 0) { + DEBUG("ntimes[%d]=%d (CID: %d)\n",i,q->mode_ntimes[i],q->candidates[i].cell_id); + } + if (q->mode_ntimes[i] > max_times) { + max_times = q->mode_ntimes[i]; + mode_pos = i; + } + } + found_cell->cell_id = q->candidates[mode_pos].cell_id; + /* Now in all these cell IDs, find most frequent CP */ + uint32_t nof_normal = 0; + found_cell->peak = 0; + for (i=0;inof_frames_detected;i++) { + if (q->candidates[i].cell_id == found_cell->cell_id) { + if (CP_ISNORM(q->candidates[i].cp)) { + nof_normal++; + } + found_cell->peak += q->candidates[i].peak/q->mode_ntimes[mode_pos]; + } + } + if (nof_normal > q->mode_ntimes[mode_pos]/2) { + found_cell->cp = CPNORM; + } else { + found_cell->cp = CPEXT; + } + found_cell->mode = q->mode_ntimes[mode_pos]; +} + +int ue_celldetect_scan(ue_celldetect_t * q, + cf_t *signal, + uint32_t nsamples, + ue_celldetect_result_t *found_cell) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t peak_idx; + uint32_t nof_input_frames; + + + if (q != NULL && + signal != NULL && + nsamples >= 4800) + { + ret = LIBLTE_SUCCESS; + + if (nsamples % 4800) { + printf("Warning: nsamples must be a multiple of 4800. Some samples will be ignored\n"); + nsamples = (nsamples/4800) * 4800; + } + nof_input_frames = nsamples/4800; + + for (uint32_t nf=0;nfsfind, q->current_N_id_2); + + INFO("[%3d/%3d]: Searching cells with N_id_2=%d. %d frames\n", + q->current_nof_detected, q->current_nof_total, q->current_N_id_2, nof_input_frames); + + /* Find peak and cell id */ + ret = sync_find(&q->sfind, &signal[nf*4800], 0, &peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + /* If peak position does not allow to read SSS, return error -3 */ + if (ret == LIBLTE_SUCCESS && peak_idx != 0) { + return CS_FRAME_UNALIGNED; + } + + /* Process the peak result */ + if (ret == 1) { + if (sync_sss_detected(&q->sfind)) { + ret = sync_get_cell_id(&q->sfind); + if (ret >= 0) { + /* Save cell id, cp and peak */ + q->candidates[q->current_nof_detected].cell_id = (uint32_t) ret; + q->candidates[q->current_nof_detected].cp = sync_get_cp(&q->sfind); + q->candidates[q->current_nof_detected].peak = sync_get_last_peak_value(&q->sfind); + } + INFO + ("[%3d/%3d]: Found peak at %4d, value %.3f, Cell_id: %d CP: %s\n", + q->current_nof_detected, q->current_nof_total, peak_idx, + q->candidates[q->current_nof_detected].peak, q->candidates[q->current_nof_detected].cell_id, + lte_cp_string(q->candidates[q->current_nof_detected].cp)); + q->current_nof_detected++; + } + } + q->current_nof_total++; + + /* Decide cell ID and CP if we detected up to nof_frames_detected */ + if (q->current_nof_detected == q->nof_frames_detected) { + decide_cell(q, found_cell); + q->current_N_id_2++; + q->current_nof_detected = q->current_nof_total = 0; + ret = CS_CELL_DETECTED; + /* Or go to the next N_id_2 if we didn't detect the cell */ + } else if (q->current_nof_total == q->nof_frames_total) { + q->current_N_id_2++; + q->current_nof_detected = q->current_nof_total = 0; + ret = CS_CELL_NOT_DETECTED; + } + if (q->current_N_id_2 == 3) { + q->current_N_id_2 = 0; + } + } + } + + return ret; +} diff --git a/lte/phy/lib/ue/src/ue_dl.c b/lte/phy/lib/ue/src/ue_dl.c new file mode 100644 index 000000000..0b98459ee --- /dev/null +++ b/lte/phy/lib/ue/src/ue_dl.c @@ -0,0 +1,303 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "liblte/phy/ue/ue_dl.h" + +#include +#include + +#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE, q->cell.cp) + +#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + + +int ue_dl_init(ue_dl_t *q, + lte_cell_t cell, + phich_resources_t phich_resources, phich_length_t phich_length, + uint16_t user_rnti) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + lte_cell_isvalid(&cell)) + { + ret = LIBLTE_ERROR; + + q->cell = cell; + q->user_rnti = user_rnti; + q->pkt_errors = 0; + q->pkts_total = 0; + q->nof_trials = 0; + q->sfn = 0; + q->pbch_decoded = false; + + if (lte_fft_init(&q->fft, q->cell.cp, q->cell.nof_prb)) { + fprintf(stderr, "Error initiating FFT\n"); + goto clean_exit; + } + if (chest_init_LTEDL(&q->chest, cell)) { + fprintf(stderr, "Error initiating channel estimator\n"); + goto clean_exit; + } + if (regs_init(&q->regs, phich_resources, phich_length, q->cell)) { + fprintf(stderr, "Error initiating REGs\n"); + goto clean_exit; + } + if (pbch_init(&q->pbch, q->cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + goto clean_exit; + } + if (pcfich_init(&q->pcfich, &q->regs, q->cell)) { + fprintf(stderr, "Error creating PCFICH object\n"); + goto clean_exit; + } + + if (pdcch_init(&q->pdcch, &q->regs, q->cell)) { + fprintf(stderr, "Error creating PDCCH object\n"); + goto clean_exit; + } + + if (pdsch_init(&q->pdsch, q->cell)) { + fprintf(stderr, "Error creating PDSCH object\n"); + goto clean_exit; + } + for (uint32_t i=0;iharq_process[i], &q->pdsch)) { + fprintf(stderr, "Error initiating HARQ process\n"); + goto clean_exit; + } + } + q->sf_symbols = vec_malloc(CURRENT_SFLEN_RE * sizeof(cf_t)); + if (!q->sf_symbols) { + perror("malloc"); + goto clean_exit; + } + for (uint32_t i=0;icell.nof_ports;i++) { + q->ce[i] = vec_malloc(CURRENT_SFLEN_RE * sizeof(cf_t)); + if (!q->ce[i]) { + perror("malloc"); + goto clean_exit; + } + } + + ret = LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid cell properties: Id=%d, Ports=%d, PRBs=%d\n", + cell.id, cell.nof_ports, cell.nof_prb); + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_dl_free(q); + } + return ret; +} + +void ue_dl_free(ue_dl_t *q) { + if (q) { + lte_fft_free(&q->fft); + chest_free(&q->chest); + regs_free(&q->regs); + pbch_free(&q->pbch); + pcfich_free(&q->pcfich); + pdcch_free(&q->pdcch); + pdsch_free(&q->pdsch); + for (uint32_t i=0;iharq_process[i]); + } + if (q->sf_symbols) { + free(q->sf_symbols); + } + for (uint32_t i=0;icell.nof_ports;i++) { + if (q->ce[i]) { + free(q->ce[i]); + } + } + } +} + +LIBLTE_API float mean_exec_time=0; +int frame_cnt=0; + +int ue_dl_decode(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint16_t rnti) +{ + uint32_t cfi, cfi_distance, i; + ra_pdsch_t ra_dl; + dci_location_t locations[10]; + dci_msg_t dci_msg; + uint32_t nof_locations; + uint16_t crc_rem; + dci_format_t format; + pbch_mib_t mib; + int ret = LIBLTE_ERROR; + cf_t *ce_slot1[MAX_PORTS]; + struct timeval t[3]; + + /* Run FFT for all subframe data */ + lte_fft_run_sf(&q->fft, input, q->sf_symbols); + + gettimeofday(&t[1], NULL); + + /* Get channel estimates for each port */ + chest_ce_sf(&q->chest, q->sf_symbols, q->ce, sf_idx); + + gettimeofday(&t[2], NULL); + get_time_interval(t); + mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, mean_exec_time, frame_cnt); + frame_cnt++; + + for (int i=0;ice[i][SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)]; + } + + /* Decode PBCH if not yet decoded to obtain the System Frame Number (SFN) */ + if (sf_idx == 0) { + // FIXME: There is no need to do this every frame! + pbch_decode_reset(&q->pbch); + if (pbch_decode(&q->pbch, &q->sf_symbols[SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)], ce_slot1, &mib) == 1) { + q->sfn = mib.sfn; + q->pbch_decoded = true; + INFO("Decoded SFN: %d\n", q->sfn); + } else { + INFO("Not decoded MIB (SFN: %d)\n", q->sfn); + q->sfn++; + if (q->sfn == 1024) { + q->sfn = 0; + } + } + } + /* If we are looking for SI Blocks, search only in appropiate places */ + if (((rnti == SIRNTI && (q->sfn % 2) == 0 && sf_idx == 5) || + rnti != SIRNTI)) + { + + /* First decode PCFICH and obtain CFI */ + if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce, sf_idx, &cfi, &cfi_distance)<0) { + fprintf(stderr, "Error decoding PCFICH\n"); + return LIBLTE_ERROR; + } + + INFO("Decoded CFI=%d with distance %d\n", cfi, cfi_distance); + + if (regs_set_cfi(&q->regs, cfi)) { + fprintf(stderr, "Error setting CFI\n"); + return LIBLTE_ERROR; + } + + /* Generate PDCCH candidates */ + if (rnti == SIRNTI) { + nof_locations = pdcch_common_locations(&q->pdcch, locations, 10, cfi); + format = Format1A; + } else { + nof_locations = pdcch_ue_locations(&q->pdcch, locations, 10, sf_idx, cfi, q->user_rnti); + format = Format1; + } + + + crc_rem = 0; + for (i=0;ipdcch, q->sf_symbols, q->ce, locations[i], sf_idx, cfi)) { + fprintf(stderr, "Error extracting LLRs\n"); + return LIBLTE_ERROR; + } + if (pdcch_decode_msg(&q->pdcch, &dci_msg, format, &crc_rem)) { + fprintf(stderr, "Error decoding DCI msg\n"); + return LIBLTE_ERROR; + } + INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem); + } + + if (crc_rem == rnti) { + if (dci_msg_to_ra_dl(&dci_msg, rnti, q->user_rnti, q->cell, cfi, &ra_dl)) { + fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n"); + return LIBLTE_ERROR; + } + + uint32_t rvidx; + if (rnti == SIRNTI) { + switch((q->sfn%8)/2) { + case 0: + rvidx = 0; + break; + case 1: + rvidx = 2; + break; + case 2: + rvidx = 3; + break; + case 3: + rvidx = 1; + break; + } + } else { + rvidx = ra_dl.rv_idx; + } + + if (rvidx == 0) { + if (pdsch_harq_setup(&q->harq_process[0], ra_dl.mcs, &ra_dl.prb_alloc)) { + fprintf(stderr, "Error configuring HARQ process\n"); + return LIBLTE_ERROR; + } + } + if (q->harq_process[0].mcs.mod > 0) { + ret = pdsch_decode(&q->pdsch, q->sf_symbols, q->ce, data, sf_idx, + &q->harq_process[0], rvidx); + if (ret == LIBLTE_ERROR) { + if (rnti == SIRNTI && rvidx == 1) { + q->pkt_errors++; + } else if (rnti != SIRNTI) { + q->pkt_errors++; + } + } else if (ret == LIBLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling pdsch_decode()\n"); + return LIBLTE_ERROR; + } else if (ret == LIBLTE_SUCCESS) { + if (VERBOSE_ISINFO()) { + INFO("Decoded Message: ", 0); + vec_fprint_hex(stdout, data, ra_dl.mcs.tbs); + } + } + if (rnti == SIRNTI && rvidx == 1) { + q->pkts_total++; + } else if (rnti != SIRNTI) { + q->pkts_total++; + } + } + } + if (rnti == SIRNTI && (q->sfn%8) == 0) { + q->nof_trials++; + } + } + + if (crc_rem == rnti && ret == LIBLTE_SUCCESS) { + return ra_dl.mcs.tbs; + } else { + return 0; + } +} diff --git a/lte/phy/lib/ue/src/ue_mib.c b/lte/phy/lib/ue/src/ue_mib.c new file mode 100644 index 000000000..9652e75c8 --- /dev/null +++ b/lte/phy/lib/ue/src/ue_mib.c @@ -0,0 +1,225 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "liblte/phy/ue/ue_mib.h" + +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#define FIND_FFTSIZE 128 +#define FIND_SFLEN 10*SF_LEN(FIND_FFTSIZE) + +int ue_mib_init(ue_mib_t * q, + uint32_t cell_id, + lte_cp_t cp) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + + ret = LIBLTE_ERROR; + + lte_cell_t cell; + cell.nof_ports = MIB_NOF_PORTS; + cell.nof_prb = 6; + cell.id = cell_id; + cell.cp = cp; + + q->cell_id = cell_id; + + bzero(q, sizeof(ue_mib_t)); + + q->slot1_symbols = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t)); + if (!q->slot1_symbols) { + perror("malloc"); + goto clean_exit; + } + + for (int i=0;ice[i] = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t)); + if (!q->ce[i]) { + perror("malloc"); + goto clean_exit; + } + } + + if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) { + goto clean_exit; + } + + sync_set_threshold(&q->sfind, MIB_FIND_THRESHOLD); + sync_sss_en(&q->sfind, true); + sync_set_N_id_2(&q->sfind, cell_id % 3); + + if (lte_fft_init(&q->fft, cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + goto clean_exit; + } + if (chest_init_LTEDL(&q->chest, cell)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto clean_exit; + } + if (pbch_init(&q->pbch, cell)) { + fprintf(stderr, "Error initiating PBCH\n"); + goto clean_exit; + } + ue_mib_reset(q); + + ret = LIBLTE_SUCCESS; + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_mib_free(q); + } + return ret; +} + +void ue_mib_free(ue_mib_t * q) +{ + if (q->slot1_symbols) { + free(q->slot1_symbols); + } + for (int i=0;ice[i]) { + free(q->ce[i]); + } + } + sync_free(&q->sfind); + chest_free(&q->chest); + pbch_free(&q->pbch); + lte_fft_free(&q->fft); +} + + +void ue_mib_reset(ue_mib_t * q) +{ + q->frame_cnt = 0; + q->last_frame_trial = 0; + + pbch_decode_reset(&q->pbch); +} + +void ue_mib_set_threshold(ue_mib_t * q, float threshold) +{ + sync_set_threshold(&q->sfind, threshold); +} + +static int mib_decoder_run(ue_mib_t * q, cf_t *input, pbch_mib_t *mib) +{ + int ret; + + /* Run FFT for the slot symbols */ + lte_fft_run_slot(&q->fft, input, q->slot1_symbols); + + /* Get channel estimates of slot #1 for each port */ + ret = chest_ce_slot(&q->chest, q->slot1_symbols, q->ce, 1); + if (ret == LIBLTE_SUCCESS) { + + /* Reset decoder if we missed a frame */ + if ((q->last_frame_trial && (q->frame_cnt - q->last_frame_trial > 2)) || + q->frame_cnt > 10) + { + ue_mib_reset(q); + INFO("Resetting PBCH decoder: last trial %u, now is %u\n", + q->last_frame_trial, q->frame_cnt); + } + + /* Decode PBCH */ + ret = pbch_decode(&q->pbch, q->slot1_symbols, q->ce, mib); + if (ret < 0) { + fprintf(stderr, "Error decoding PBCH\n"); + } else if (ret == 1) { + INFO("MIB decoded: %u\n", q->frame_cnt/2); + ue_mib_reset(q); + ret = 1; + } else { + INFO("MIB not decoded: %u\n", q->frame_cnt / 2); + q->last_frame_trial = q->frame_cnt; + } + } + return ret; +} + +int ue_mib_decode(ue_mib_t * q, + cf_t *signal, + uint32_t nsamples, + pbch_mib_t *mib) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t peak_idx; + uint32_t nof_input_frames; + + + if (q != NULL && + signal != NULL) + { + ret = LIBLTE_SUCCESS; + + if (nsamples % MIB_FRAME_SIZE) { + printf("Warning: nsamples must be a multiple of %d. Some samples will be ignored\n", MIB_FRAME_SIZE); + nsamples = (nsamples/MIB_FRAME_SIZE) * MIB_FRAME_SIZE; + } + nof_input_frames = nsamples/MIB_FRAME_SIZE; + + for (uint32_t nf=0;nfsfind, signal, nf*MIB_FRAME_SIZE, &peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + /* If peak position does not allow to read SSS, return error -3 */ + if (ret == 1 && + nf*MIB_FRAME_SIZE + peak_idx + 960 <= nsamples && + sync_sss_detected(&q->sfind) && + sync_get_sf_idx(&q->sfind) == 0) + { + + ret = mib_decoder_run(q, &signal[nf*MIB_FRAME_SIZE+peak_idx], mib); + + } else if ((ret == LIBLTE_SUCCESS && peak_idx != 0) || + (ret == 1 && nf*MIB_FRAME_SIZE + peak_idx + 960 > nsamples)) + { + ret = MIB_FRAME_UNALIGNED; + } else { + ret = 0; + } + + q->frame_cnt++; + } + } + return ret; +} diff --git a/lte/phy/lib/ue/src/ue_sync.c b/lte/phy/lib/ue/src/ue_sync.c new file mode 100644 index 000000000..714a6212f --- /dev/null +++ b/lte/phy/lib/ue/src/ue_sync.c @@ -0,0 +1,353 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + + +#include "liblte/phy/ue/ue_sync.h" + +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#define MAX_TIME_OFFSET 128 +cf_t dummy[MAX_TIME_OFFSET]; + +#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE) + +#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + +#define FIND_THRESHOLD 1.0 +#define TRACK_THRESHOLD 0.2 + + +int ue_sync_init(ue_sync_t *q, + lte_cell_t cell, + int (recv_callback)(void*, void*, uint32_t), + void *stream_handler) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + stream_handler != NULL && + lte_cell_isvalid(&cell) && + recv_callback != NULL) + { + ret = LIBLTE_ERROR; + + bzero(q, sizeof(ue_sync_t)); + + ue_sync_reset(q); + + q->decode_sss_on_track = false; + q->stream = stream_handler; + q->recv_callback = recv_callback; + q->cell = cell; + + if(sync_init(&q->sfind, CURRENT_SFLEN, CURRENT_FFTSIZE)) { + fprintf(stderr, "Error initiating sync find\n"); + goto clean_exit; + } + if(sync_init(&q->strack, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) { + fprintf(stderr, "Error initiating sync track\n"); + goto clean_exit; + } + + sync_set_N_id_2(&q->sfind, cell.id%3); + sync_set_threshold(&q->sfind, FIND_THRESHOLD); + q->sfind.cp = cell.cp; + sync_cp_en(&q->sfind, false); + + sync_set_N_id_2(&q->strack, cell.id%3); + sync_set_threshold(&q->strack, TRACK_THRESHOLD); + q->strack.cp = cell.cp; + sync_cp_en(&q->strack, false); + + if (cfo_init(&q->cfocorr, CURRENT_SFLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + q->input_buffer = vec_malloc(5 * CURRENT_SFLEN * sizeof(cf_t)); + if (!q->input_buffer) { + perror("malloc"); + goto clean_exit; + } + + ret = LIBLTE_SUCCESS; + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_sync_free(q); + } + return ret; +} + +void ue_sync_free(ue_sync_t *q) { + if (q->input_buffer) { + free(q->input_buffer); + } + cfo_free(&q->cfocorr); + sync_free(&q->sfind); + sync_free(&q->strack); +} + +uint32_t ue_sync_peak_idx(ue_sync_t *q) { + return q->peak_idx; +} + +ue_sync_state_t ue_sync_get_state(ue_sync_t *q) { + return q->state; +} +uint32_t ue_sync_get_sfidx(ue_sync_t *q) { + return q->sf_idx; +} + +float ue_sync_get_cfo(ue_sync_t *q) { + return 15000 * q->cur_cfo; +} + +float ue_sync_get_sfo(ue_sync_t *q) { + return 1000*q->mean_time_offset/5; +} + +void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) { + q->decode_sss_on_track = enabled; +} + + +static int find_peak_ok(ue_sync_t *q) { + + /* Receive the rest of the next subframe */ + if (q->recv_callback(q->stream, q->input_buffer, q->peak_idx+CURRENT_SFLEN/2) < 0) { + return LIBLTE_ERROR; + } + + if (sync_sss_detected(&q->sfind)) { + + /* Get the subframe index (0 or 5) */ + q->sf_idx = sync_get_sf_idx(&q->sfind) + 1; + + /* Reset variables */ + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + + /* Goto Tracking state */ + q->state = SF_TRACK; + + INFO("Found peak at %d, value %.3f, SF_idx: %d, Cell_id: %d CP: %s\n", + q->peak_idx, sync_get_peak_value(&q->sfind), q->sf_idx, q->cell.id, lte_cp_string(q->cell.cp)); + + } else { + INFO("Found peak at %d, SSS not detected\n", q->peak_idx); + } + return 0; +} + +int track_peak_ok(ue_sync_t *q, uint32_t track_idx) { + + /* Make sure subframe idx is what we expect */ + if ((q->sf_idx != sync_get_sf_idx(&q->strack)) && q->decode_sss_on_track) { + INFO("Warning: Expected SF idx %d but got %d!\n", + q->sf_idx, sync_get_sf_idx(&q->strack)); + q->sf_idx = sync_get_sf_idx(&q->strack); + q->state = SF_TRACK; + } else { + q->time_offset = ((int) track_idx - (int) CURRENT_FFTSIZE); + + /* If the PSS peak is beyond the frame (we sample too slowly), + discard the offseted samples to align next frame */ + if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) { + if (q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset) < 0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + + /* compute cumulative moving average CFO */ + q->cur_cfo = EXPAVERAGE(sync_get_cfo(&q->strack), q->cur_cfo, q->frame_ok_cnt); + + /* compute cumulative moving average time offset */ + q->mean_time_offset = (float) EXPAVERAGE((float) q->time_offset, q->mean_time_offset, q->frame_ok_cnt); + + q->peak_idx = CURRENT_SFLEN/2 + q->time_offset; + q->frame_ok_cnt++; + q->frame_no_cnt = 0; + } + + return 1; +} + +int track_peak_no(ue_sync_t *q) { + + /* if we missed too many PSS go back to FIND */ + q->frame_no_cnt++; + if (q->frame_no_cnt >= TRACK_MAX_LOST) { + printf("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt); + q->state = SF_FIND; + } else { + INFO("Tracking peak not found. Peak %.3f, %d lost\n", + sync_get_peak_value(&q->strack), (int) q->frame_no_cnt); + } + + return 1; +} + +static int receive_samples(ue_sync_t *q) { + + /* A negative time offset means there are samples in our buffer for the next subframe, + because we are sampling too fast. + */ + if (q->time_offset < 0) { + q->time_offset = -q->time_offset; + } + + /* copy last part of the last subframe (use move since there could be overlapping) */ + //memcpy(q->input_buffer, &q->input_buffer[CURRENT_SFLEN-q->time_offset], q->time_offset*sizeof(cf_t)); + + /* Get 1 subframe from the USRP getting more samples and keeping the previous samples, if any */ + if (q->recv_callback(q->stream, &q->input_buffer[q->time_offset], CURRENT_SFLEN - q->time_offset) < 0) { + return LIBLTE_ERROR; + } + + /* reset time offset */ + q->time_offset = 0; + + return LIBLTE_SUCCESS; +} + +int ue_sync_get_buffer(ue_sync_t *q, cf_t **sf_symbols) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t track_idx; + struct timeval t[3]; + + if (q != NULL && + sf_symbols != NULL && + q->input_buffer != NULL) + { + + if (receive_samples(q)) { + fprintf(stderr, "Error receiving samples\n"); + return -1; + } + + switch (q->state) { + case SF_FIND: + ret = sync_find(&q->sfind, q->input_buffer, 0, &q->peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + if (ret == 1) { + ret = find_peak_ok(q); + } else if (q->peak_idx != 0) { + uint32_t rlen; + if (q->peak_idx < CURRENT_SFLEN/2) { + rlen = CURRENT_SFLEN/2-q->peak_idx; + } else { + rlen = q->peak_idx; + } + if (q->recv_callback(q->stream, q->input_buffer, rlen) < 0) { + return LIBLTE_ERROR; + } + } + break; + case SF_TRACK: + ret = 1; + + q->strack.sss_en = q->decode_sss_on_track; + + q->sf_idx = (q->sf_idx + 1) % 10; + + /* Every SF idx 0 and 5, find peak around known position q->peak_idx */ + if (q->sf_idx == 0 || q->sf_idx == 5) { + + #ifdef MEASURE_EXEC_TIME + gettimeofday(&t[1], NULL); + #endif + + track_idx = 0; + + /* track pss around the middle of the subframe, where the PSS is */ + ret = sync_find(&q->strack, q->input_buffer, CURRENT_SFLEN/2-CURRENT_FFTSIZE, &track_idx); + if (ret < 0) { + fprintf(stderr, "Error tracking correlation peak\n"); + return -1; + } + + #ifdef MEASURE_EXEC_TIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + q->mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt); + #endif + + if (ret == 1) { + ret = track_peak_ok(q, track_idx); + } else { + ret = track_peak_no(q); + } + if (ret == LIBLTE_ERROR) { + fprintf(stderr, "Error processing tracking peak\n"); + q->state = SF_FIND; + return LIBLTE_SUCCESS; + } + + q->frame_total_cnt++; + } + + /* Do CFO Correction and deliver the frame */ + cfo_correct(&q->cfocorr, q->input_buffer, q->input_buffer, -q->cur_cfo / CURRENT_FFTSIZE); + *sf_symbols = q->input_buffer; + + break; + } + } + return ret; +} + +void ue_sync_reset(ue_sync_t *q) { + q->state = SF_FIND; + + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + q->cur_cfo = 0; + q->mean_time_offset = 0; + q->time_offset = 0; + #ifdef MEASURE_EXEC_TIME + q->mean_exec_time = 0; + #endif +} + diff --git a/lte/phy/lib/ue/test/CMakeLists.txt b/lte/phy/lib/ue/test/CMakeLists.txt new file mode 100644 index 000000000..44a2361da --- /dev/null +++ b/lte/phy/lib/ue/test/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# UE SYNC TEST (Only compiled if CUHD is available) +######################################################################## +LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND) +LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND) + +IF(${CUHD_FIND} GREATER -1) + ADD_EXECUTABLE(ue_sync_usrp ue_sync_usrp.c) + TARGET_LINK_LIBRARIES(ue_sync_usrp lte_phy cuhd) + + ADD_EXECUTABLE(ue_celldetect_mib_test ue_celldetect_mib_test.c) + TARGET_LINK_LIBRARIES(ue_celldetect_mib_test lte_phy cuhd) + + IF(${GRAPHICS_FIND} EQUAL -1) + SET_TARGET_PROPERTIES(ue_sync_usrp PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") + ELSE(${GRAPHICS_FIND} EQUAL -1) + target_link_libraries(ue_sync_usrp graphics) + ENDIF(${GRAPHICS_FIND} EQUAL -1) + +ENDIF(${CUHD_FIND} GREATER -1) + + diff --git a/lte/phy/lib/ue/test/ue_celldetect_mib_test.c b/lte/phy/lib/ue/test/ue_celldetect_mib_test.c new file mode 100644 index 000000000..c3921aba4 --- /dev/null +++ b/lte/phy/lib/ue/test/ue_celldetect_mib_test.c @@ -0,0 +1,265 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +#include "liblte/cuhd/cuhd.h" + +int nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL; +int nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED; +float threshold = -1; + +float uhd_freq = 0.0, uhd_gain = 20.0; +char *uhd_args = ""; + +void usage(char *prog) { + printf("Usage: %s [agntdv] -f uhd_freq\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-d nof_frames_detected [Default 10]\n"); + printf("\t-t threshold [Default %.2f]\n",threshold); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agndtvf")) != -1) { + switch (opt) { + case 'n': + nof_frames_total = atoi(argv[optind]); + break; + case 'd': + nof_frames_detected = atoi(argv[optind]); + break; + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (uhd_freq == 0.0) { + usage(argv[0]); + exit(-1); + } +} + +int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell) +{ + ue_mib_t uemib; + pbch_mib_t mib; + int n; + + uint32_t nof_frames = 0; + uint32_t flen = MIB_FRAME_SIZE; + + if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) { + fprintf(stderr, "Error initiating PBCH decoder\n"); + return LIBLTE_ERROR; + } + + INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0); + cuhd_set_rx_srate(uhd, 1920000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + do { + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total); + + n = ue_mib_decode(&uemib, buffer, flen, &mib); + if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling ue_mib_decode()\n"); + return LIBLTE_ERROR; + } + if (n == MIB_FRAME_UNALIGNED) { + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + nof_frames++; + } while (n != MIB_FOUND && nof_frames < nof_frames_total); + if (n == MIB_FOUND) { + printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames); + pbch_mib_fprint(stdout, &mib, found_cell->cell_id); + } else { + printf("\nCould not decode MIB\n"); + } + + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + ue_mib_free(&uemib); + + return LIBLTE_SUCCESS; +} + +int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t *found_cell) +{ + int n; + + INFO("Setting sampling frequency 960 KHz for PSS search\n", 0); + cuhd_set_rx_srate(uhd, 960000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + uint32_t nof_scanned_cells = 0; + uint32_t flen = 4800; + + do { + + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + n = ue_celldetect_scan(s, buffer, flen, found_cell); + switch(n) { + case CS_FRAME_UNALIGNED: + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + return LIBLTE_ERROR; + case CS_CELL_DETECTED: + if (found_cell->peak > 0) { + printf("\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n", + found_cell->cell_id, lte_cp_string(found_cell->cp), + found_cell->peak, found_cell->mode, s->nof_frames_detected); + } + nof_scanned_cells++; + break; + case CS_CELL_NOT_DETECTED: + nof_scanned_cells++; + break; + case LIBLTE_ERROR: + case LIBLTE_ERROR_INVALID_INPUTS: + fprintf(stderr, "Error calling cellsearch_scan()\n"); + return LIBLTE_ERROR; + } + } while(nof_scanned_cells < 3 && n != CS_CELL_DETECTED); + + INFO("Stopping receiver...\n", 0); + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + return n; +} + +int main(int argc, char **argv) { + int n; + void *uhd; + ue_celldetect_t s; + ue_celldetect_result_t found_cell; + cf_t *buffer; + + parse_args(argc, argv); + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + cuhd_set_rx_gain(uhd, uhd_gain); + + /* set uhd_freq */ + cuhd_set_rx_freq(uhd, (double) uhd_freq); + cuhd_rx_wait_lo_locked(uhd); + DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq/1000000); + + buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + + if (ue_celldetect_init(&s)) { + fprintf(stderr, "Error initiating UE sync module\n"); + exit(-1); + } + if (threshold > 0) { + ue_celldetect_set_threshold(&s, threshold); + } + + if (nof_frames_total > 0) { + ue_celldetect_set_nof_frames_total(&s, nof_frames_total); + } + if (nof_frames_detected > 0) { + ue_celldetect_set_nof_frames_detected(&s, nof_frames_detected); + } + + n = find_cell(uhd, &s, buffer, &found_cell); + if (n < 0) { + fprintf(stderr, "Error searching cell\n"); + exit(-1); + } + if (n == CS_CELL_DETECTED) { + if (decode_pbch(uhd, buffer, &found_cell)) { + fprintf(stderr, "Error decoding PBCH\n"); + exit(-1); + } + } + + ue_celldetect_free(&s); + cuhd_close(uhd); + exit(0); +} + + diff --git a/lte/phy/lib/ue/test/ue_sync_usrp.c b/lte/phy/lib/ue/test/ue_sync_usrp.c new file mode 100644 index 000000000..62bb1d7c5 --- /dev/null +++ b/lte/phy/lib/ue/test/ue_sync_usrp.c @@ -0,0 +1,258 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +#include "liblte/cuhd/cuhd.h" +void *uhd; + +#ifndef DISABLE_GRAPHICS +#include "liblte/graphics/plot.h" +plot_real_t poutfft; +#endif + +int nof_frames = -1; +float threshold = -1.0; +int N_id_2 = -1; +uint32_t nof_prb = 6; + +float uhd_freq = 0.0, uhd_gain = 20.0; +char *uhd_args = ""; +int disable_plots = 0; + +void usage(char *prog) { + printf("Usage: %s [agntdpv] -f uhd_freq -i N_id_2\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-n nof_frames [Default infinite]\n"); + printf("\t-p nof_prb [Default %d]\n", nof_prb); + printf("\t-t threshold [Default %.2f]\n",threshold); + +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); +#endif + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agntdvfip")) != -1) { + switch (opt) { + case 'i': + N_id_2 = atoi(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'd': + disable_plots = 1; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (uhd_freq == 0.0 || N_id_2 == -1) { + usage(argv[0]); + exit(-1); + } +} + +void input_init() { + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + cuhd_set_rx_gain(uhd, uhd_gain); + + /* set uhd_freq */ + cuhd_set_rx_freq(uhd, (double) uhd_freq); + cuhd_rx_wait_lo_locked(uhd); + DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq/1000000); + + int srate = lte_sampling_freq_hz(nof_prb); + if (srate > 0) { + cuhd_set_rx_srate(uhd, (double) srate); + } else { + fprintf(stderr, "Error invalid nof_prb=%d\n",nof_prb); + } + DEBUG("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + +} + +int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + return cuhd_recv(h, data, nsamples, 1); +} + +#ifndef DISABLE_GRAPHICS + +void init_plots() { + plot_init(); + + plot_real_init(&poutfft); + plot_real_setTitle(&poutfft, "Output FFT - Magnitude"); + plot_real_setLabels(&poutfft, "Index", "dB"); + plot_real_setYAxisScale(&poutfft, -60, 0); + +} + +#endif + +float tmp_plot[100000]; + +int main(int argc, char **argv) { + cf_t *input_buffer, *sf_symbols = NULL; + int frame_cnt; + ue_sync_t s; + int pos; + pss_synch_t pss; + float peak; + struct timeval t[3]; + float mean_ce_time=0; + lte_fft_t fft; + lte_cell_t cell; + + bzero(&cell, sizeof(lte_cell_t)); + + parse_args(argc, argv); + + #ifndef DISABLE_GRAPHICS + if (!disable_plots) { + init_plots(); + } + #endif + + input_init(); + + cell.cp = CPNORM; + cell.id = N_id_2; + cell.nof_ports = 1; + cell.nof_prb = nof_prb; + + if (ue_sync_init(&s, cell, cuhd_recv_wrapper, uhd)) { + fprintf(stderr, "Error initiating UE sync module\n"); + exit(-1); + } + + pss_synch_init_fft(&pss, + SF_LEN(lte_symbol_sz(cell.nof_prb)), + lte_symbol_sz(cell.nof_prb)); + pss_synch_set_N_id_2(&pss, cell.id%3); + sf_symbols = vec_malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!sf_symbols) { + perror("malloc"); + exit(-1); + } + lte_fft_init(&fft, cell.cp, cell.nof_prb); + + frame_cnt = 0; + mean_ce_time=0; + uint32_t valid_frames=0; + //uint32_t unaligned = 0; + while (frame_cnt < nof_frames || nof_frames == -1) { + + int n = ue_sync_get_buffer(&s, &input_buffer); + if (n < 0) { + fprintf(stderr, "Error calling sync work()\n"); + exit(-1); + } + + if (n == 1 && ue_sync_get_sfidx(&s) == 0) { + + mean_ce_time = (float) (mean_ce_time + (float) t[0].tv_usec * valid_frames) / (valid_frames+1); + valid_frames++; + + #ifndef DISABLE_GRAPHICS + if (!disable_plots && !(valid_frames % 5) && sf_symbols) { + + /* Run FFT for the second slot */ + lte_fft_run_slot(&fft, input_buffer, sf_symbols); + + int i; + int nof_re = SLOT_LEN_RE(cell.nof_prb, cell.cp); + for (i = 0; i < nof_re; i++) { + tmp_plot[i] = 10 * log10f(cabsf(sf_symbols[i])); + if (isinf(tmp_plot[i])) { + tmp_plot[i] = -80; + } + } + plot_real_setNewData(&poutfft, tmp_plot, nof_re); + } + #endif + + pos = pss_synch_find_pss(&pss, input_buffer, &peak); + printf("CELL_ID: %3d CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Exec: %3.2f\r", + sync_get_cell_id(&s.sfind), ue_sync_get_cfo(&s)/1000, ue_sync_get_sfo(&s)/1000, pos, + s.mean_exec_time); + fflush(stdout); + if (VERBOSE_ISINFO()) { + printf("\n"); + } + } + + frame_cnt++; + } + + printf("\nBye\n"); + exit(0); +} + + diff --git a/lte/phy/lib/utils/src/bit.c b/lte/phy/lib/utils/src/bit.c index 2a38b30fb..46b27a426 100644 --- a/lte/phy/lib/utils/src/bit.c +++ b/lte/phy/lib/utils/src/bit.c @@ -75,7 +75,7 @@ unsigned int bit_diff(char *x, char *y, int nbits) { } // Counts the number of ones in a word. K&R book exercise 2.9 -int bit_count(unsigned int n) { +uint32_t bit_count(uint32_t n) { int c; for (c = 0; n; c++) n &= n - 1; diff --git a/lte/phy/lib/utils/src/cexptab.c b/lte/phy/lib/utils/src/cexptab.c index 457f80e29..fb9109f6c 100644 --- a/lte/phy/lib/utils/src/cexptab.c +++ b/lte/phy/lib/utils/src/cexptab.c @@ -33,8 +33,8 @@ #include "liblte/phy/utils/cexptab.h" -int cexptab_init(cexptab_t *h, int size) { - int i; +int cexptab_init(cexptab_t *h, uint32_t size) { + uint32_t i; h->size = size; h->tab = malloc(sizeof(cf_t) * size); @@ -42,9 +42,9 @@ int cexptab_init(cexptab_t *h, int size) { for (i = 0; i < size; i++) { h->tab[i] = cexpf(_Complex_I * 2 * M_PI * (float) i / size); } - return 0; + return LIBLTE_SUCCESS; } else { - return -1; + return LIBLTE_ERROR; } } @@ -55,9 +55,9 @@ void cexptab_free(cexptab_t *h) { bzero(h, sizeof(cexptab_t)); } -void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { - int i; - unsigned int idx; +void cexptab_gen(cexptab_t *h, cf_t *x, float freq, uint32_t len) { + uint32_t i; + uint32_t idx; float phase_inc = freq * h->size; float phase=0; @@ -68,15 +68,15 @@ void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { while (phase < 0) { phase += (float) h->size; } - idx = (unsigned int) phase; + idx = (uint32_t) phase; x[i] = h->tab[idx]; phase += phase_inc; } } -void cexptab_gen_direct(cf_t *x, float freq, int len) { - int i; +void cexptab_gen_direct(cf_t *x, float freq, uint32_t len) { + uint32_t i; for (i = 0; i < len; i++) { x[i] = cexpf(_Complex_I * 2 * M_PI * freq * i); } diff --git a/lte/phy/lib/utils/src/convolution.c b/lte/phy/lib/utils/src/convolution.c index 2ab27e041..76a26bdee 100644 --- a/lte/phy/lib/utils/src/convolution.c +++ b/lte/phy/lib/utils/src/convolution.c @@ -34,61 +34,64 @@ #include "liblte/phy/utils/convolution.h" -int conv_fft_cc_init(conv_fft_cc_t *state, int input_len, int filter_len) { - state->input_len = input_len; - state->filter_len = filter_len; - state->output_len = input_len+filter_len-1; - state->input_fft = vec_malloc(sizeof(_Complex float)*state->output_len); - state->filter_fft = vec_malloc(sizeof(_Complex float)*state->output_len); - state->output_fft = vec_malloc(sizeof(_Complex float)*state->output_len); - if (!state->input_fft || !state->filter_fft || !state->output_fft) { - return -1; +int conv_fft_cc_init(conv_fft_cc_t *q, uint32_t input_len, uint32_t filter_len) { + q->input_len = input_len; + q->filter_len = filter_len; + q->output_len = input_len+filter_len; + q->input_fft = vec_malloc(sizeof(cf_t)*q->output_len); + q->filter_fft = vec_malloc(sizeof(cf_t)*q->output_len); + q->output_fft = vec_malloc(sizeof(cf_t)*q->output_len); + if (!q->input_fft || !q->filter_fft || !q->output_fft) { + return LIBLTE_ERROR; } - if (dft_plan(&state->input_plan,state->output_len,FORWARD,COMPLEX)) { - return -2; + if (dft_plan(&q->input_plan,q->output_len,FORWARD,COMPLEX)) { + return LIBLTE_ERROR; } - if (dft_plan(&state->filter_plan,state->output_len,FORWARD,COMPLEX)) { - return -3; + if (dft_plan(&q->filter_plan,q->output_len,FORWARD,COMPLEX)) { + return LIBLTE_ERROR; } - if (dft_plan(&state->output_plan,state->output_len,BACKWARD,COMPLEX)) { - return -4; + if (dft_plan(&q->output_plan,q->output_len,BACKWARD,COMPLEX)) { + return LIBLTE_ERROR; } - return 0; + dft_plan_set_norm(&q->input_plan, true); + dft_plan_set_norm(&q->filter_plan, true); + dft_plan_set_norm(&q->output_plan, false); + return LIBLTE_SUCCESS; } -void conv_fft_cc_free(conv_fft_cc_t *state) { - if (state->input_fft) { - free(state->input_fft); +void conv_fft_cc_free(conv_fft_cc_t *q) { + if (q->input_fft) { + free(q->input_fft); } - if (state->filter_fft) { - free(state->filter_fft); + if (q->filter_fft) { + free(q->filter_fft); } - if (state->output_fft) { - free(state->output_fft); + if (q->output_fft) { + free(q->output_fft); } - dft_plan_free(&state->input_plan); - dft_plan_free(&state->filter_plan); - dft_plan_free(&state->output_plan); + dft_plan_free(&q->input_plan); + dft_plan_free(&q->filter_plan); + dft_plan_free(&q->output_plan); } -int conv_fft_cc_run(conv_fft_cc_t *state, _Complex float *input, _Complex float *filter, _Complex float *output) { +uint32_t conv_fft_cc_run(conv_fft_cc_t *q, cf_t *input, cf_t *filter, cf_t *output) { - dft_run_c(&state->input_plan, input, state->input_fft); - dft_run_c(&state->filter_plan, filter, state->filter_fft); + dft_run_c(&q->input_plan, input, q->input_fft); + dft_run_c(&q->filter_plan, filter, q->filter_fft); - vec_prod_ccc(state->input_fft,state->filter_fft,state->output_fft,state->output_len); + vec_prod_ccc(q->input_fft,q->filter_fft,q->output_fft,q->output_len); - dft_run_c(&state->output_plan, state->output_fft, output); + dft_run_c(&q->output_plan, q->output_fft, output); - return state->output_len; + return q->output_len-1; } -int conv_cc(_Complex float *input, _Complex float *filter, _Complex float *output, int input_len, int filter_len) { - int i,j; - int output_len; +uint32_t conv_cc(cf_t *input, cf_t *filter, cf_t *output, uint32_t input_len, uint32_t filter_len) { + uint32_t i,j; + uint32_t output_len; output_len=input_len+filter_len-1; - memset(output,0,output_len*sizeof(_Complex float)); + memset(output,0,output_len*sizeof(cf_t)); for (i=0;i #include "liblte/phy/utils/dft.h" +#include "liblte/phy/utils/vector.h" #define dft_ceil(a,b) ((a-1)/b+1) #define dft_floor(a,b) (a/b) @@ -144,11 +145,8 @@ void dft_run_c(dft_plan_t *plan, dft_c_t *in, dft_c_t *out) { plan->forward, plan->mirror, plan->dc); fftwf_execute(plan->p); if (plan->norm) { - /**FIXME: Use VOLK */ - norm = sqrtf(plan->size); - for (i=0;isize;i++) { - f_out[i] /= norm; - } + norm = 1.0/sqrtf(plan->size); + vec_sc_prod_cfc(f_out, norm, f_out, plan->size); } if (plan->db) { for (i=0;isize;i++) { @@ -168,10 +166,8 @@ void dft_run_r(dft_plan_t *plan, dft_r_t *in, dft_r_t *out) { memcpy(plan->in,in,sizeof(dft_r_t)*plan->size); fftwf_execute(plan->p); if (plan->norm) { - norm = plan->size; - for (i=0;isize; + vec_sc_prod_fff(f_out, norm, f_out, plan->size); } if (plan->db) { for (i=0;i #include #include +#include + +#include "liblte/phy/utils/vector.h" +#include "liblte/phy/utils/bit.h" #ifdef HAVE_VOLK #include "volk/volk.h" #endif -int vec_acc_ii(int *x, int len) { +int vec_acc_ii(int *x, uint32_t len) { int i; int z=0; for (i=0;im) { m=x[i]; @@ -244,7 +437,30 @@ int vec_max_fi(float *x, int len) { #endif } -void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, int len) { + +uint32_t vec_max_abs_ci(cf_t *x, uint32_t len) { +#ifdef HAVE_VOLK_MAX_ABS_FUNCTION + uint32_t target=0; + volk_32fc_index_max_16u(&target,x,len); + return target; + +#else + uint32_t i; + float m=-FLT_MAX; + uint32_t p=0; + float tmp; + for (i=0;im) { + m=tmp; + p=i; + } + } + return p; +#endif +} + +void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len) { int i; int tmp; for (i=0;i 128 && i threshold) - y=ccf.*x(i-128:i-1); - - y0=y(1:64); - y1=y(65:length(y)); - - eps=angle(conj(sum(y0))*sum(y1))/pi; - else - eps = NaN; - fs = NaN; - end end diff --git a/matlab/sync/find_pss2.m b/matlab/sync/find_pss2.m new file mode 100644 index 000000000..478a47cdb --- /dev/null +++ b/matlab/sync/find_pss2.m @@ -0,0 +1,14 @@ +function [w2, m, idx] = find_pss2( x, N_id_2, fft_size) + c=lte_pss_zc(N_id_2); + cc=[zeros(fft_size/2-31,1); c; zeros(fft_size/2-31,1)]; + ccd=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)]; + ccf=sqrt(fft_size)*conj(ifft(ccd)); + + w2=abs(conv(x,ccf/62)).^2/var(x,1)/sqrt(2); + plot(w2) + [m, idx]=max(w2); + + %fprintf('Frame starts at %d, energy=%g, p=%g, p/en=%g dB\n',i, ... + % en, m, m/en); +end + diff --git a/matlab/sync/lte_generate_sss.m b/matlab/sync/lte_generate_sss.m deleted file mode 100644 index d8b45e914..000000000 --- a/matlab/sync/lte_generate_sss.m +++ /dev/null @@ -1,123 +0,0 @@ -% -% Copyright 2011-2012 Ben Wojtowicz -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU Affero General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU Affero General Public License for more details. -% -% You should have received a copy of the GNU Affero General Public License -% along with this program. If not, see . -% -% Function: lte_generate_sss -% Description: Generates LTE secondary synchronization signals -% Inputs: N_id_1 - Physical layer cell identity group -% N_id_2 - Physical layer identity -% Outputs: sss_d_u_0 - The sequence d(n) used for the secondary -% synchronization signal, an interleaved -% concatenation of two length-31 binary -% sequences for subframe 0 -% Outputs: sss_d_u_5 - The sequence d(n) used for the secondary -% synchronization signal, an interleaved -% concatenation of two length-31 binary -% sequences for subframe 5 -% Spec: 3GPP TS 36.211 section 6.11.2.1 v10.1.0 -% Notes: None -% Rev History: Ben Wojtowicz 10/28/2011 Created -% Ben Wojtowicz 01/29/2012 Fixed license statement -% -function [sss_d_u_0, sss_d_u_5 c0 c1 m0 m1] = lte_generate_sss(N_id_1, N_id_2) - % Validate N_id_1 - if(~(N_id_1 >= 0 && N_id_1 <= 167)) - fprintf('ERROR: Invalid N_id_1 (%u)\n', N_id_1); - sss_d_u_0 = 0; - sss_d_u_5 = 0; - return; - end - - % Validate N_id_2 - if(~(N_id_2 >= 0 && N_id_2 <= 2)) - fprintf('ERROR: Invalid N_id_2 (%u)\n', N_id_2); - sss_d_u_0 = 0; - sss_d_u_5 = 0; - return; - end - - % Generate m0 and m1 - q_prime = floor(N_id_1/30); - q = floor((N_id_1 + (q_prime*(q_prime+1)/2))/30); - m_prime = N_id_1 + (q*(q+1)/2); - m0 = mod(m_prime, 31); - m1 = mod((m0 + floor(m_prime/31) + 1), 31); - - % Generate s_tilda - x_s_tilda(0+1) = 0; - x_s_tilda(1+1) = 0; - x_s_tilda(2+1) = 0; - x_s_tilda(3+1) = 0; - x_s_tilda(4+1) = 1; - for(i_hat=0:25) - x_s_tilda(i_hat+5+1) = mod((x_s_tilda(i_hat+2+1) + x_s_tilda(i_hat+1)), 2); - end - for(idx=0:30) - s_tilda(idx+1) = 1 - 2*x_s_tilda(idx+1); - end - - % Generate c_tilda - x_c_tilda(0+1) = 0; - x_c_tilda(1+1) = 0; - x_c_tilda(2+1) = 0; - x_c_tilda(3+1) = 0; - x_c_tilda(4+1) = 1; - for(i_hat=0:25) - x_c_tilda(i_hat+5+1) = mod((x_c_tilda(i_hat+3+1) + x_c_tilda(i_hat+1)), 2); - end - for(idx=0:30) - c_tilda(idx+1) = 1 - 2*x_c_tilda(idx+1); - end - - % Generate z_tilda - x_z_tilda(0+1) = 0; - x_z_tilda(1+1) = 0; - x_z_tilda(2+1) = 0; - x_z_tilda(3+1) = 0; - x_z_tilda(4+1) = 1; - for(i_hat=0:25) - x_z_tilda(i_hat+5+1) = mod((x_z_tilda(i_hat+4+1) + x_z_tilda(i_hat+2+1) + x_z_tilda(i_hat+1+1) + x_z_tilda(i_hat+1)), 2); - end - for(idx=0:30) - z_tilda(idx+1) = 1 - 2*x_z_tilda(idx+1); - end - - % Generate s0_m0 and s1_m1 - for(n=0:30) - s0_m0(n+1) = s_tilda(mod(n + m0, 31)+1); - s1_m1(n+1) = s_tilda(mod(n + m1, 31)+1); - end - - % Generate c0 and c1 - for(n=0:30) - c0(n+1) = c_tilda(mod(n + N_id_2, 31)+1); - c1(n+1) = c_tilda(mod(n + N_id_2 + 3, 31)+1); - end - - % Generate z1_m0 and z1_m1 - for(n=0:30) - z1_m0(n+1) = z_tilda(mod(n + mod(m0, 8), 31)+1); - z1_m1(n+1) = z_tilda(mod(n + mod(m1, 8), 31)+1); - end - - % Generate SSS - for(n=0:30) - sss_d_u_0(2*n+1) = s0_m0(n+1) * c0(n+1); - sss_d_u_5(2*n+1) = s1_m1(n+1) * c0(n+1); - - sss_d_u_0(2*n+1+1) = s1_m1(n+1) * c1(n+1) * z1_m0(n+1); - sss_d_u_5(2*n+1+1) = s0_m0(n+1) * c1(n+1) * z1_m1(n+1); - end -end diff --git a/matlab/sync/test.m b/matlab/sync/test.m deleted file mode 100644 index 33d3d6e92..000000000 --- a/matlab/sync/test.m +++ /dev/null @@ -1,35 +0,0 @@ -N=128; %128 subcarries -M=16; %QAM order -cp=9; %length of the cyclic prefix... Is increasing the cyclic prefix size gonna increase the efficiency? -scale = 1/sqrt(10); -hMod = modem.qammod(M); %QAM Modulator -hDemod = modem.qamdemod(hMod); %QAM demodulator -loops = 10; -SNR =0:5:35; -t1= cputime ; -% transmited signal. Contains N data points ranging from 0 to M-1 -ber=zeros(5,length(SNR)); -%% Creating the Rayleigh Multipath Channels -Ch = rayleighchan(1/1000,10); -Ch.ResetBeforeFiltering = 0; -sig = 1i*ones(loops,1); -h1 = filter(Ch,sig); -h2 = 0.1072*filter(Ch,sig); -h3 = 0.0120*filter(Ch,sig); -h4 = 0.0052*filter(Ch,sig); -% Delay Values -l1 = 4; -l2 = 7; -l3= 16; -%% -ofdm_cp=[]; - %tx=transmited_data; - for ik=1:loops%number of loops - tx = randi([0 M-1],1,N); % generate random data - sig=modulate(hMod, tx)*scale; % Modulate QAM modulated signal, devide by the square root of 10 to bring the average power of the signal to 1 - ofdm=sqrt(N).*ifft(sig,N); % generate OFDM signal IFFT on the parrellel data,multiply by sqrt(N) to adjust to the matlab computation , - ofdm_cp = [ofdm_cp ofdm(N-cp+1:N) ofdm]; % Add cyclic prefix - - end - - \ No newline at end of file