From cd463d1b87a5b2985be39155bfb43282893594fd Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 5 Jun 2020 10:46:23 +0200 Subject: [PATCH] UHD: Initial C++ porting UHD: cmakefix x3 --- cmake/modules/FindUHD.cmake | 35 + lib/src/phy/rf/CMakeLists.txt | 7 +- lib/src/phy/rf/rf_uhd_imp.c | 1164 ---------------------------- lib/src/phy/rf/rf_uhd_imp.cc | 1340 +++++++++++++++++++++++++++++++++ lib/src/phy/rf/rf_uhd_imp.h | 11 +- lib/src/phy/rf/rf_uhd_safe.h | 235 ++++++ lib/src/phy/rf/uhd_c_api.cpp | 75 -- lib/src/phy/rf/uhd_c_api.h | 36 - lib/src/radio/radio.cc | 4 + srsue/src/phy/sync.cc | 8 +- 10 files changed, 1636 insertions(+), 1279 deletions(-) delete mode 100644 lib/src/phy/rf/rf_uhd_imp.c create mode 100644 lib/src/phy/rf/rf_uhd_imp.cc create mode 100644 lib/src/phy/rf/rf_uhd_safe.h delete mode 100644 lib/src/phy/rf/uhd_c_api.cpp delete mode 100644 lib/src/phy/rf/uhd_c_api.h diff --git a/cmake/modules/FindUHD.cmake b/cmake/modules/FindUHD.cmake index 0c0eb742e..438118f46 100644 --- a/cmake/modules/FindUHD.cmake +++ b/cmake/modules/FindUHD.cmake @@ -28,4 +28,39 @@ INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(UHD DEFAULT_MSG UHD_LIBRARIES UHD_INCLUDE_DIRS) MARK_AS_ADVANCED(UHD_LIBRARIES UHD_INCLUDE_DIRS) +include(CheckCXXSourceCompiles) + +IF(UHD_FOUND) + # Checks whether the UHD driver supports X300 reset from srsLTE. This functionality requires changing the function + # `x300_make_ctrl_iface_enet` visibility in the file `uhd/host/lib/usrp/x300_fw_ctrl.cpp`. This can be accomplished + # adding the following line: + # `UHD_API wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors);` + set(_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES uhd boost_program_options boost_system) + check_cxx_source_compiles("#include + #include + #include + + uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors); + + uhd_error try_usrp_x300_reset() + { + uhd::transport::udp_simple::sptr udp_simple = uhd::transport::udp_simple::make_connected(\"\", \"49152\"); + uhd::wb_iface::sptr x300_ctrl = x300_make_ctrl_iface_enet(udp_simple, true); + x300_ctrl->poke32(0x100058, 1); + return UHD_ERROR_NONE; + } + + int main(int argc, char** argv) + { + try_usrp_x300_reset(); + return 0; + }" UHD_ENABLE_X300_FW_RESET) + + set(CMAKE_REQUIRED_FLAGS ${_CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES}) +ENDIF(UHD_FOUND) + ENDIF(NOT UHD_FOUND) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 76edc1a57..5f075dc35 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -29,7 +29,12 @@ if(RF_FOUND) if (UHD_FOUND) add_definitions(-DENABLE_UHD) - list(APPEND SOURCES_RF rf_uhd_imp.c uhd_c_api.cpp) + list(APPEND SOURCES_RF rf_uhd_imp.cc) + + # If found, add a macro to inform the UHD driver about the available feature + if (UHD_ENABLE_X300_FW_RESET) + add_definitions(-DENABLE_UHD_X300_FW_RESET) + endif(UHD_ENABLE_X300_FW_RESET) endif (UHD_FOUND) if (BLADERF_FOUND) diff --git a/lib/src/phy/rf/rf_uhd_imp.c b/lib/src/phy/rf/rf_uhd_imp.c deleted file mode 100644 index a3600f10f..000000000 --- a/lib/src/phy/rf/rf_uhd_imp.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * Copyright 2013-2020 Software Radio Systems Limited - * - * This file is part of srsLTE. - * - * srsLTE 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. - * - * srsLTE 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. - * - * A copy of the GNU Affero 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 "rf_helper.h" -#include "srslte/srslte.h" -#include "uhd_c_api.h" - -#include "rf_uhd_imp.h" - -#define HAVE_ASYNC_THREAD 1 - -#if UHD_VERSION < 3130000 -#define UHD_SUPPORTS_COMMAND_TIME -#endif /* UHD_VERSION < 3140000 */ - -typedef struct { - char* devname; - uhd_usrp_handle usrp; - uhd_rx_streamer_handle rx_stream; - uhd_tx_streamer_handle tx_stream; - - uhd_rx_metadata_handle rx_md, rx_md_first; - uhd_tx_metadata_handle tx_md; - - srslte_rf_info_t info; - size_t rx_nof_samples; - size_t tx_nof_samples; - double tx_rate; - bool dynamic_rate; - bool has_rssi; - uint32_t nof_rx_channels; - int nof_tx_channels; - - srslte_rf_error_handler_t uhd_error_handler; - void* uhd_error_handler_arg; - - float current_master_clock; - - bool async_thread_running; - pthread_t async_thread; - - pthread_mutex_t tx_mutex; -} rf_uhd_handler_t; - -void suppress_handler(const char* x) -{ - // do nothing -} - -static cf_t zero_mem[64 * 1024]; - -static void log_overflow(rf_uhd_handler_t* h) -{ - if (h->uhd_error_handler) { - srslte_rf_error_t error; - bzero(&error, sizeof(srslte_rf_error_t)); - error.type = SRSLTE_RF_ERROR_OVERFLOW; - h->uhd_error_handler(h->uhd_error_handler_arg, error); - } -} - -static void log_late(rf_uhd_handler_t* h, bool is_rx) -{ - if (h->uhd_error_handler) { - srslte_rf_error_t error; - bzero(&error, sizeof(srslte_rf_error_t)); - error.opt = is_rx ? 1 : 0; - error.type = SRSLTE_RF_ERROR_LATE; - h->uhd_error_handler(h->uhd_error_handler_arg, error); - } -} - -#if HAVE_ASYNC_THREAD -static void log_underflow(rf_uhd_handler_t* h) -{ - if (h->uhd_error_handler) { - srslte_rf_error_t error; - bzero(&error, sizeof(srslte_rf_error_t)); - error.type = SRSLTE_RF_ERROR_UNDERFLOW; - h->uhd_error_handler(h->uhd_error_handler_arg, error); - } -} -#endif - -static void log_rx_error(rf_uhd_handler_t* h) -{ - if (h->uhd_error_handler) { - char error_string[512]; - uhd_usrp_last_error(h->usrp, error_string, 512); - ERROR("USRP reported the following error: %s\n", error_string); - - srslte_rf_error_t error; - bzero(&error, sizeof(srslte_rf_error_t)); - error.type = SRSLTE_RF_ERROR_RX; - h->uhd_error_handler(h->uhd_error_handler_arg, error); - } -} - -#if HAVE_ASYNC_THREAD -static void* async_thread(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_async_metadata_handle md; - uhd_async_metadata_make(&md); - while (handler->async_thread_running) { - bool valid; - uhd_error err = uhd_tx_streamer_recv_async_msg(handler->tx_stream, &md, 0.5, &valid); - if (err == UHD_ERROR_NONE) { - if (valid) { - uhd_async_metadata_event_code_t event_code; - uhd_async_metadata_event_code(md, &event_code); - if (event_code == UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW || - event_code == UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW_IN_PACKET) { - log_underflow(handler); - } else if (event_code == UHD_ASYNC_METADATA_EVENT_CODE_TIME_ERROR) { - log_late(handler, false); - } - } - } else { - ERROR("Error while receiving aync metadata: 0x%x\n", err); - return NULL; - } - } - uhd_async_metadata_free(&md); - return NULL; -} -#endif - -static inline void uhd_free(rf_uhd_handler_t* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - // NULL handler, return - if (!handler) { - return; - } - - uhd_tx_metadata_free(&handler->tx_md); - uhd_rx_metadata_free(&handler->rx_md_first); - uhd_rx_metadata_free(&handler->rx_md); - -#if HAVE_ASYNC_THREAD - if (handler->async_thread_running) { - handler->async_thread_running = false; - pthread_join(handler->async_thread, NULL); - } -#endif - - uhd_tx_streamer_free(&handler->tx_stream); - uhd_rx_streamer_free(&handler->rx_stream); - uhd_usrp_free(&handler->usrp); - - free(handler); -} - -static inline int uhd_alloc(rf_uhd_handler_t* handler, char* args) -{ - uhd_error error; - - error = uhd_usrp_make(&handler->usrp, args); - if (error) { - ERROR("Error opening UHD: code %d\n", error); - return SRSLTE_ERROR; - } - - error = uhd_rx_streamer_make(&handler->rx_stream); - if (error) { - ERROR("Error making RX stream: %d\n", error); - return SRSLTE_ERROR; - } - - error = uhd_tx_streamer_make(&handler->tx_stream); - if (error) { - ERROR("Error making TX stream: %d\n", error); - return SRSLTE_ERROR; - } - - // Make metadata objects for RX/TX - error = uhd_rx_metadata_make(&handler->rx_md); - if (error) { - ERROR("Error making RX Metadata: %d\n", error); - return SRSLTE_ERROR; - } - - error = uhd_rx_metadata_make(&handler->rx_md_first); - if (error) { - ERROR("Error making RX Metadata first: %d\n", error); - return SRSLTE_ERROR; - } - - error = uhd_tx_metadata_make(&handler->tx_md, false, 0, 0, false, false); - if (error) { - ERROR("Error making TX Metadata: %d\n", error); - return SRSLTE_ERROR; - } - - return SRSLTE_SUCCESS; -} - -void rf_uhd_suppress_stdout(void* h) -{ - rf_uhd_register_msg_handler_c(suppress_handler); -} - -void rf_uhd_register_error_handler(void* h, srslte_rf_error_handler_t new_handler, void* arg) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - handler->uhd_error_handler = new_handler; - handler->uhd_error_handler_arg = arg; -} - -static bool find_string(uhd_string_vector_handle h, const char* str) -{ - char buff[128]; - size_t n; - uhd_string_vector_size(h, &n); - for (int i = 0; i < n; i++) { - uhd_string_vector_at(h, i, buff, 128); - if (strstr(buff, str)) { - return true; - } - } - return false; -} - -/** - * Set the USRP time to the current GPS time (if sensor is found) - * - * The GPS time is read and the USRP time is set to the next full second during the next PPS. - * It appears, however, that "uhd_usrp_set_time_next_pps()" which seems to be the correct function - * to use, doesn't work. The C API call "uhd_usrp_set_time_unknown_pps()" works well. - * @param handler Pointer to RF handler - * @return Any error returned by UHD - */ -static uhd_error set_time_to_gps_time(rf_uhd_handler_t* handler) -{ - const char sensor_name[] = "gps_time"; - - uhd_string_vector_handle sensors; - uhd_string_vector_make(&sensors); - - uhd_error error = uhd_usrp_get_mboard_sensor_names(handler->usrp, 0, &sensors); - -#if PRINT_SENSOR_NAMES - size_t sensors_len; - uhd_string_vector_size(sensors, &sensors_len); - for (int i = 0; i < sensors_len; i++) { - char buff[256]; - uhd_string_vector_at(sensors, i, buff, 128); - printf("sensor %s present\n", buff); - } -#endif - - if (error == UHD_ERROR_NONE && find_string(sensors, sensor_name)) { - uhd_sensor_value_handle value_h; - uhd_sensor_value_make_from_string(&value_h, "w", "t", "f"); - error = uhd_usrp_get_mboard_sensor(handler->usrp, sensor_name, 0, &value_h); - - int full_secs = 0; - if (error == UHD_ERROR_NONE) { - uhd_sensor_value_data_type_t sensor_dtype; - uhd_sensor_value_data_type(value_h, &sensor_dtype); - full_secs = uhd_sensor_value_to_int(value_h, &full_secs); - // printf("GPS full_secs=%d\n", full_secs); - - double frac_secs; - uhd_sensor_value_data_type(value_h, &sensor_dtype); - full_secs = uhd_sensor_value_to_realnum(value_h, &frac_secs); - // printf("GPS frac_secs=%f\n", frac_secs); - - full_secs = frac_secs; - printf("Setting USRP time to %ds\n", full_secs); - error = uhd_usrp_set_time_unknown_pps(handler->usrp, full_secs + 1, 0.0); - if (error != UHD_ERROR_NONE) { - char err_msg[256]; - uhd_usrp_last_error(handler->usrp, err_msg, 256); - fprintf(stderr, "Error code %d: %s\n", error, err_msg); - } - - // sleep a second to make sure values are set correctly - sleep(1); - } - uhd_sensor_value_free(&value_h); - } - if (error != UHD_ERROR_NONE) { - fprintf(stderr, "USRP has no sensor \"%s\", or reading failed. UHD error: %i\n", sensor_name, error); - } - - uhd_string_vector_free(&sensors); - - return error; -} - -// timeout in ms -static uhd_error -wait_sensor_locked(rf_uhd_handler_t* handler, char* sensor_name, bool is_mboard, int timeout, bool* is_locked) -{ - uhd_error error; - *is_locked = false; - - uhd_sensor_value_handle value_h; - uhd_string_vector_handle sensors; - - uhd_string_vector_make(&sensors); - uhd_sensor_value_make_from_bool(&value_h, "", true, "True", "False"); - if (is_mboard) { - // motherboard sensor - error = uhd_usrp_get_mboard_sensor_names(handler->usrp, 0, &sensors); - } else { - // daughterboard sensor - error = uhd_usrp_get_rx_sensor_names(handler->usrp, 0, &sensors); - } - - if (error == UHD_ERROR_NONE && find_string(sensors, sensor_name)) { - do { - if (is_mboard) { - error = uhd_usrp_get_mboard_sensor(handler->usrp, sensor_name, 0, &value_h); - } else { - error = uhd_usrp_get_rx_sensor(handler->usrp, sensor_name, 0, &value_h); - } - if (error != UHD_ERROR_NONE) { - break; - } - uhd_sensor_value_to_bool(value_h, is_locked); - usleep(1000); // 1ms - timeout -= 1; // 1ms - } while (*is_locked == false && timeout > 0); - } - if (error != UHD_ERROR_NONE) { - fprintf(stderr, - "%s has no sensor \"%s\", or reading failed. UHD error: %i\n", - is_mboard ? "Motherboard" : "Daugherboard", - sensor_name, - error); - // board doesn't have this sensor, sleep for timeout - *is_locked = true; - usleep(timeout * 1000); - } - - uhd_string_vector_free(&sensors); - uhd_sensor_value_free(&value_h); - - return error; -} - -const char* rf_uhd_devname(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - return handler->devname; -} - -bool rf_uhd_rx_wait_lo_locked(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - // wait for clock source to lock - char* sensor_name = "lo_locked"; - bool is_locked = false; - - // blocks until sensor is blocked - uhd_error error = wait_sensor_locked(handler, sensor_name, false, 300, &is_locked); - - if (!is_locked || error != UHD_ERROR_NONE) { - fprintf(stderr, - "Could not lock reference clock source. Sensor: %s=%s, UHD error: %i\n", - sensor_name, - is_locked ? "true" : "false", - error); - } - - return is_locked; -} - -int rf_uhd_start_rx_stream(void* h, bool now) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - uhd_stream_cmd_t stream_cmd = {.stream_mode = UHD_STREAM_MODE_START_CONTINUOUS, .stream_now = now}; - if (!now) { - uhd_usrp_get_time_now(handler->usrp, 0, &stream_cmd.time_spec_full_secs, &stream_cmd.time_spec_frac_secs); - stream_cmd.time_spec_frac_secs += 0.5; - if (stream_cmd.time_spec_frac_secs > 1) { - stream_cmd.time_spec_frac_secs -= 1; - stream_cmd.time_spec_full_secs += 1; - } - } - uhd_rx_streamer_issue_stream_cmd(handler->rx_stream, &stream_cmd); - return 0; -} - -int rf_uhd_stop_rx_stream(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_stream_cmd_t stream_cmd = {.stream_mode = UHD_STREAM_MODE_STOP_CONTINUOUS, .stream_now = true}; - uhd_rx_streamer_issue_stream_cmd(handler->rx_stream, &stream_cmd); - return 0; -} - -void rf_uhd_flush_buffer(void* h) -{ - int n; - void* data[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - data[i] = zero_mem; - } - do { - n = rf_uhd_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); - } while (n > 0); -} - -bool rf_uhd_has_rssi(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - return handler->has_rssi; -} - -bool get_has_rssi(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_string_vector_handle rx_sensors; - uhd_string_vector_make(&rx_sensors); - uhd_usrp_get_rx_sensor_names(handler->usrp, 0, &rx_sensors); - bool ret = find_string(rx_sensors, "rssi"); - uhd_string_vector_free(&rx_sensors); - return ret; -} - -float rf_uhd_get_rssi(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - if (handler->has_rssi) { - double val_out; - - uhd_sensor_value_handle rssi_value; - uhd_sensor_value_make_from_realnum(&rssi_value, "rssi", 0, "dBm", "%f"); - uhd_usrp_get_rx_sensor(handler->usrp, "rssi", 0, &rssi_value); - uhd_sensor_value_to_realnum(rssi_value, &val_out); - uhd_sensor_value_free(&rssi_value); - - return val_out; - } else { - return 0.0; - } -} - -int rf_uhd_open(char* args, void** h) -{ - return rf_uhd_open_multi(args, h, 1); -} - -int rf_uhd_open_multi(char* args, void** h, uint32_t nof_channels) -{ - uhd_error error; - - if (h) { - *h = NULL; - - if (nof_channels > SRSLTE_MAX_CHANNELS) { - fprintf(stderr, - "Error opening UHD: maximum number of channels exceeded (%d>%d)\n", - nof_channels, - SRSLTE_MAX_CHANNELS); - return SRSLTE_ERROR; - } - - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)malloc(sizeof(rf_uhd_handler_t)); - if (!handler) { - perror("malloc"); - return SRSLTE_ERROR; - } - - bzero(handler, sizeof(rf_uhd_handler_t)); - *h = handler; - - // Disable fast-path (U/L/O) messages - setenv("UHD_LOG_FASTPATH_DISABLE", "1", 0); - - /* Set priority to UHD threads */ - uhd_set_thread_priority(uhd_default_thread_priority, true); - - /* Find available devices */ - uhd_string_vector_handle devices_str; - uhd_string_vector_make(&devices_str); - uhd_usrp_find("", &devices_str); - - char args2[512]; - - handler->dynamic_rate = true; - - // Allow NULL parameter - if (args == NULL) { - args = ""; - } - handler->devname = NULL; - - pthread_mutex_init(&handler->tx_mutex, NULL); - - // Initialize handler - handler->uhd_error_handler = NULL; - - srslte_vec_cf_zero(zero_mem, 64 * 1024); - - // Check external clock argument - enum { DEFAULT, EXTERNAL, GPSDO } clock_src; - if (strstr(args, "clock=external")) { - REMOVE_SUBSTRING_WITHCOMAS(args, "clock=external"); - clock_src = EXTERNAL; - } else if (strstr(args, "clock=gpsdo")) { - printf("Using GPSDO clock\n"); - REMOVE_SUBSTRING_WITHCOMAS(args, "clock=gpsdo"); - clock_src = GPSDO; - } else { - clock_src = DEFAULT; - } - -#if HAVE_ASYNC_THREAD - bool start_async_thread = true; - if (strstr(args, "silent")) { - REMOVE_SUBSTRING_WITHCOMAS(args, "silent"); - start_async_thread = false; - } -#endif - - // Set over the wire format - char* otw_format = "sc16"; - if (strstr(args, "otw_format=sc12")) { - REMOVE_SUBSTRING_WITHCOMAS(args, "otw_format=sc12"); - otw_format = "sc12"; - } else if (strstr(args, "otw_format=sc16")) { - REMOVE_SUBSTRING_WITHCOMAS(args, "otw_format=sc16"); - /* Do nothing */ - } else if (strstr(args, "otw_format=")) { - ERROR("Wrong over the wire format. Valid formats: sc12, sc16\n"); - return SRSLTE_ERROR; - } - - // Set transmitter subdevice spec string - const char tx_subdev_arg[] = "tx_subdev_spec="; - char tx_subdev_str[64] = {0}; - char* tx_subdev_ptr = strstr(args, tx_subdev_arg); - if (tx_subdev_ptr) { - copy_subdev_string(tx_subdev_str, tx_subdev_ptr + strlen(tx_subdev_arg)); - } - - // Set receiver subdevice spec string - const char rx_subdev_arg[] = "rx_subdev_spec="; - char rx_subdev_str[64] = {0}; - char* rx_subdev_ptr = strstr(args, rx_subdev_arg); - if (rx_subdev_ptr) { - copy_subdev_string(rx_subdev_str, rx_subdev_ptr + strlen(rx_subdev_arg)); - } - - if (tx_subdev_ptr) { - remove_substring(args, tx_subdev_arg); - remove_substring(args, tx_subdev_str); - } - - if (rx_subdev_ptr) { - remove_substring(args, rx_subdev_arg); - remove_substring(args, rx_subdev_str); - } - - /* If device type or name not given in args, choose a B200 */ - if (args[0] == '\0') { - if (find_string(devices_str, "type=b200") && !strstr(args, "recv_frame_size")) { - // If B200 is available, use it - args = "type=b200,master_clock_rate=23.04e6"; - handler->current_master_clock = 23040000; - handler->devname = DEVNAME_B200; - } else if (find_string(devices_str, "type=x300")) { - // Else if X300 is available, set master clock rate now (can't be changed later) - args = "type=x300,master_clock_rate=184.32e6"; - handler->current_master_clock = 184320000; - handler->dynamic_rate = false; - handler->devname = DEVNAME_X300; - } else if (find_string(devices_str, "type=e3x0")) { - // Else if E3X0 is available, set master clock rate now (can't be changed later) - args = "type=e3x0,master_clock_rate=30.72e6"; - handler->dynamic_rate = false; - handler->devname = DEVNAME_E3X0; - } else if (find_string(devices_str, "type=n3xx")) { - args = "type=n3xx,master_clock_rate=122.88e6"; - handler->current_master_clock = 122880000; - handler->dynamic_rate = false; - handler->devname = DEVNAME_N300; - srslte_use_standard_symbol_size(true); - } - } else { - // If args is set and x300 type is specified, make sure master_clock_rate is defined - if (strstr(args, "type=x300")) { - sprintf(args2, "%s,master_clock_rate=184.32e6", args); - args = args2; - handler->current_master_clock = 184320000; - handler->dynamic_rate = false; - handler->devname = DEVNAME_X300; - } else if (strstr(args, "type=n3xx")) { - sprintf(args2, "%s,master_clock_rate=122.88e6", args); - args = args2; - handler->current_master_clock = 122880000; - handler->dynamic_rate = false; - handler->devname = DEVNAME_N300; - srslte_use_standard_symbol_size(true); - } else if (strstr(args, "type=e3x0")) { - snprintf(args2, sizeof(args2), "%s,master_clock_rate=30.72e6", args); - args = args2; - handler->devname = DEVNAME_E3X0; - } else { - snprintf(args2, sizeof(args2), "%s,master_clock_rate=23.04e6", args); - args = args2; - handler->current_master_clock = 23040000; - handler->devname = DEVNAME_B200; - } - } - - uhd_string_vector_free(&devices_str); - - /* Create UHD handler */ - printf("Opening USRP channels=%d, args: %s\n", nof_channels, args); - if (uhd_alloc(handler, args)) { - uhd_free(handler); - return SRSLTE_ERROR; - } - - /* Set transmitter subdev spec if specified */ - if (strlen(tx_subdev_str)) { - uhd_subdev_spec_handle subdev_spec_handle = {0}; - - printf("Setting tx_subdev_spec to '%s'\n", tx_subdev_str); - - uhd_subdev_spec_make(&subdev_spec_handle, tx_subdev_str); - uhd_usrp_set_tx_subdev_spec(handler->usrp, subdev_spec_handle, 0); - uhd_subdev_spec_free(&subdev_spec_handle); - } - - /* Set receiver subdev spec if specified */ - if (strlen(rx_subdev_str)) { - uhd_subdev_spec_handle subdev_spec_handle = {0}; - - printf("Setting rx_subdev_spec to '%s'\n", rx_subdev_str); - - uhd_subdev_spec_make(&subdev_spec_handle, rx_subdev_str); - uhd_usrp_set_rx_subdev_spec(handler->usrp, subdev_spec_handle, 0); - uhd_subdev_spec_free(&subdev_spec_handle); - } - - if (!handler->devname) { - char dev_str[1024]; - uhd_usrp_get_mboard_name(handler->usrp, 0, dev_str, 1024); - if (strstr(dev_str, "B2") || strstr(dev_str, "B2")) { - handler->devname = DEVNAME_B200; - } else if (strstr(dev_str, "X3") || strstr(dev_str, "X3")) { - handler->devname = DEVNAME_X300; - } else if (strstr(dev_str, "n3xx")) { - handler->devname = DEVNAME_N300; - } - } - if (!handler->devname) { - handler->devname = "uhd_unknown"; - } - - bool is_locked = false; - char* sensor_name = ""; - - // Set external clock reference - if (clock_src == EXTERNAL) { - uhd_usrp_set_clock_source(handler->usrp, "external", 0); - uhd_usrp_set_time_source(handler->usrp, "external", 0); - sensor_name = "ref_locked"; - } else if (clock_src == GPSDO) { - uhd_usrp_set_clock_source(handler->usrp, "gpsdo", 0); - uhd_usrp_set_time_source(handler->usrp, "gpsdo", 0); - set_time_to_gps_time(handler); - sensor_name = "gps_locked"; - } - // wait until external reference / GPS is locked - if (clock_src != DEFAULT) { - // blocks until clock source is locked - error = wait_sensor_locked(handler, sensor_name, true, 300, &is_locked); - - if (!is_locked || error != UHD_ERROR_NONE) { - fprintf(stderr, - "Could not lock reference clock source. Sensor: %s=%s, UHD error: %i\n", - sensor_name, - is_locked ? "true" : "false", - error); - } - } - - handler->has_rssi = get_has_rssi(handler); - - size_t channel[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - channel[i] = i; - } - uhd_stream_args_t stream_args = { - .cpu_format = "fc32", - .otw_format = otw_format, - .args = "", - .channel_list = channel, - .n_channels = nof_channels, - }; - - handler->nof_rx_channels = nof_channels; - handler->nof_tx_channels = nof_channels; - - /* Set default rate to avoid decimation warnings and warn about USB2 low throughput */ - double default_srate = (double)srslte_sampling_freq_hz(SRSLTE_MAX_PRB); // Consider standard rates - for (int i = 0; i < nof_channels; i++) { - uhd_usrp_set_rx_rate(handler->usrp, default_srate, i); - uhd_usrp_set_tx_rate(handler->usrp, default_srate, i); - } - - if (nof_channels > 1 && clock_src != GPSDO) { - uhd_usrp_set_time_unknown_pps(handler->usrp, 0, 0.0); - } - - /* Initialize rx and tx stremers */ - error = uhd_usrp_get_rx_stream(handler->usrp, &stream_args, handler->rx_stream); - if (error) { - uhd_free(handler); - ERROR("Error opening RX stream: %d\n", error); - return SRSLTE_ERROR; - } - - error = uhd_usrp_get_tx_stream(handler->usrp, &stream_args, handler->tx_stream); - if (error) { - uhd_free(handler); - ERROR("Error opening TX stream: %d\n", error); - return SRSLTE_ERROR; - } - - uhd_rx_streamer_max_num_samps(handler->rx_stream, &handler->rx_nof_samples); - uhd_tx_streamer_max_num_samps(handler->tx_stream, &handler->tx_nof_samples); - - uhd_meta_range_handle rx_gain_range = NULL; - uhd_meta_range_make(&rx_gain_range); - uhd_usrp_get_rx_gain_range(handler->usrp, "", 0, rx_gain_range); - uhd_meta_range_start(rx_gain_range, &handler->info.min_rx_gain); - uhd_meta_range_stop(rx_gain_range, &handler->info.max_rx_gain); - uhd_meta_range_free(&rx_gain_range); - - uhd_meta_range_handle tx_gain_range = NULL; - uhd_meta_range_make(&tx_gain_range); - uhd_usrp_get_tx_gain_range(handler->usrp, "", 0, tx_gain_range); - uhd_meta_range_start(tx_gain_range, &handler->info.min_tx_gain); - uhd_meta_range_stop(tx_gain_range, &handler->info.max_tx_gain); - uhd_meta_range_free(&tx_gain_range); - - // Set starting gain to half maximum in case of using AGC - rf_uhd_set_rx_gain(handler, handler->info.max_rx_gain * 0.7); - -#if HAVE_ASYNC_THREAD - if (start_async_thread) { - // Start low priority thread to receive async commands - handler->async_thread_running = true; - if (pthread_create(&handler->async_thread, NULL, async_thread, handler)) { - uhd_free(handler); - ERROR("pthread_create\n"); - return SRSLTE_ERROR; - } - } -#endif - - /* Restore priorities */ - uhd_set_thread_priority(0, false); - - return SRSLTE_SUCCESS; - } else { - return SRSLTE_ERROR_INVALID_INPUTS; - } -} - -int rf_uhd_close(void* h) -{ - rf_uhd_stop_rx_stream(h); - - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - /// Free all made UHD objects - uhd_free(handler); - - /** Something else to close the USRP?? */ - return SRSLTE_SUCCESS; -} - -void rf_uhd_set_master_clock_rate(void* h, double rate) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - if (handler->dynamic_rate && handler->current_master_clock != rate) { - uhd_usrp_set_master_clock_rate(handler->usrp, rate, 0); - handler->current_master_clock = rate; - } -} - -double rf_uhd_set_rx_srate(void* h, double freq) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - // Set master clock rate - if (fmod(handler->current_master_clock, freq)) { - rf_uhd_set_master_clock_rate(handler, freq); - } - - if (handler->nof_rx_channels > 1) { -#ifdef UHD_SUPPORTS_COMMAND_TIME - time_t full; - double frac; - uhd_usrp_get_time_now(handler->usrp, 0, &full, &frac); - frac += 0.100; - if (frac >= 1.0) { - full++; - frac -= 1.0; - }; - uhd_usrp_set_command_time(handler->usrp, full, frac, 0); -#endif /* UHD_SUPPORTS_COMMAND_TIME */ - for (int i = 0; i < handler->nof_rx_channels; i++) { - uhd_usrp_set_rx_rate(handler->usrp, freq, i); - } -#ifdef UHD_SUPPORTS_COMMAND_TIME - usleep(100000); -#endif /* UHD_SUPPORTS_COMMAND_TIME */ - } else { - uhd_usrp_set_rx_rate(handler->usrp, freq, 0); - } - return freq; -} - -double rf_uhd_set_tx_srate(void* h, double freq) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - // Set master clock rate - if (fmod(handler->current_master_clock, freq)) { - rf_uhd_set_master_clock_rate(handler, 4 * freq); - } - - if (handler->nof_tx_channels > 1) { -#ifdef UHD_SUPPORTS_COMMAND_TIME - time_t full; - double frac; - uhd_usrp_get_time_now(handler->usrp, 0, &full, &frac); - frac += 0.100; - if (frac >= 1.0) { - full++; - frac -= 1.0; - }; - uhd_usrp_set_command_time(handler->usrp, full, frac, 0); -#endif /* UHD_SUPPORTS_COMMAND_TIME */ - for (int i = 0; i < handler->nof_tx_channels; i++) { - uhd_usrp_set_tx_rate(handler->usrp, freq, i); - } -#ifdef UHD_SUPPORTS_COMMAND_TIME - usleep(100000); -#endif /* UHD_SUPPORTS_COMMAND_TIME */ - } else { - uhd_usrp_set_tx_rate(handler->usrp, freq, 0); - } - handler->tx_rate = freq; - return freq; -} - -double rf_uhd_set_rx_gain(void* h, double gain) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - for (int i = 0; i < handler->nof_rx_channels; i++) { - uhd_usrp_set_rx_gain(handler->usrp, gain, i, ""); - } - return gain; -} - -double rf_uhd_set_tx_gain(void* h, double gain) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - for (int i = 0; i < handler->nof_tx_channels; i++) { - uhd_usrp_set_tx_gain(handler->usrp, gain, i, ""); - } - return gain; -} - -double rf_uhd_get_rx_gain(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - double gain; - uhd_usrp_get_rx_gain(handler->usrp, 0, "", &gain); - return gain; -} - -double rf_uhd_get_tx_gain(void* h) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - double gain; - uhd_usrp_get_tx_gain(handler->usrp, 0, "", &gain); - return gain; -} - -srslte_rf_info_t* rf_uhd_get_info(void* h) -{ - srslte_rf_info_t* info = NULL; - if (h) { - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - info = &handler->info; - } - return info; -} - -double rf_uhd_set_rx_freq(void* h, uint32_t ch, double freq) -{ - uhd_tune_request_t tune_request = { - .target_freq = freq, - .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, - .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, - }; - uhd_tune_result_t tune_result; - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - if (ch < handler->nof_rx_channels) { - uhd_usrp_set_rx_freq(handler->usrp, &tune_request, ch, &tune_result); - } else { - for (int i = 0; i < handler->nof_rx_channels; i++) { - uhd_usrp_set_rx_freq(handler->usrp, &tune_request, i, &tune_result); - } - } - rf_uhd_rx_wait_lo_locked(handler); - return freq; -} - -double rf_uhd_set_tx_freq(void* h, uint32_t ch, double freq) -{ - uhd_tune_request_t tune_request = { - .target_freq = freq, - .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, - .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, - }; - uhd_tune_result_t tune_result; - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - if (ch < handler->nof_tx_channels) { - uhd_usrp_set_tx_freq(handler->usrp, &tune_request, ch, &tune_result); - } else { - for (int i = 0; i < handler->nof_tx_channels; i++) { - uhd_usrp_set_tx_freq(handler->usrp, &tune_request, i, &tune_result); - } - } - return freq; -} - -void rf_uhd_get_time(void* h, time_t* secs, double* frac_secs) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_usrp_get_time_now(handler->usrp, 0, secs, frac_secs); -} - -void rf_uhd_sync_pps(void* h) -{ - if (h) { - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_usrp_set_time_unknown_pps(handler->usrp, 0, 0); - } -} - -int rf_uhd_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) -{ - return rf_uhd_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); -} - -int rf_uhd_recv_with_time_multi(void* h, - void* data[SRSLTE_MAX_PORTS], - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - uhd_rx_metadata_handle* md = &handler->rx_md_first; - size_t rxd_samples = 0; - size_t rxd_samples_total = 0; - int trials = 0; - if (blocking) { - while (rxd_samples_total < nsamples && trials < 100) { - void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < handler->nof_rx_channels; i++) { - cf_t* data_c = (cf_t*)data[i]; - buffs_ptr[i] = &data_c[rxd_samples_total]; - } - - size_t num_samps_left = nsamples - rxd_samples_total; - size_t num_rx_samples = (num_samps_left > handler->rx_nof_samples) ? handler->rx_nof_samples : num_samps_left; - - rxd_samples = 0; - uhd_error error = - uhd_rx_streamer_recv(handler->rx_stream, buffs_ptr, num_rx_samples, md, 1.0, false, &rxd_samples); - if (error) { - ERROR("Error receiving from UHD: %d\n", error); - log_rx_error(handler); - return -1; - } - - uhd_rx_metadata_error_code_t error_code = 0; - uhd_rx_metadata_error_code(*md, &error_code); - - md = &handler->rx_md; - rxd_samples_total += rxd_samples; - trials++; - - if (error_code == UHD_RX_METADATA_ERROR_CODE_OVERFLOW) { - log_overflow(handler); - } else if (error_code == UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND) { - log_late(handler, true); - } else if (error_code == UHD_RX_METADATA_ERROR_CODE_TIMEOUT) { - ERROR("Error timed out while receiving samples from UHD.\n"); - return -1; - } else if (error_code != UHD_RX_METADATA_ERROR_CODE_NONE) { - ERROR("Error code 0x%x was returned during streaming. Aborting.\n", error_code); - INFO("Error code 0x%x was returned during streaming. Aborting.\n", error_code); - } - } - } else { - uhd_error error = uhd_rx_streamer_recv(handler->rx_stream, data, nsamples, md, 0.0, false, &rxd_samples); - rxd_samples_total = rxd_samples; - if (error) { - ERROR("Error receiving from UHD: %d\n", error); - log_rx_error(handler); - return -1; - } - } - if (secs && frac_secs) { - uhd_rx_metadata_time_spec(handler->rx_md_first, secs, frac_secs); - } - return rxd_samples_total; -} - -int rf_uhd_send_timed(void* h, - void* data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst) -{ - // Maximum number of channels to NULL - void* _data[SRSLTE_MAX_CHANNELS] = {NULL}; - - // Set only first channel - _data[0] = data; - - return rf_uhd_send_timed_multi( - h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); -} - -int rf_uhd_send_timed_multi(void* h, - void** data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst) -{ - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - - pthread_mutex_lock(&handler->tx_mutex); - int ret = -1; - - /* Resets the USRP time TODO: this might cause problems for burst transmissions */ - if (!has_time_spec && is_start_of_burst && handler->nof_tx_channels > 1) { - uhd_usrp_set_time_now(handler->usrp, 0, 0, 0); - uhd_tx_metadata_set_time_spec(&handler->tx_md, 0, 0.1); - } - - size_t txd_samples; - int trials = 0; - if (blocking) { - if (has_time_spec) { - uhd_tx_metadata_set_time_spec(&handler->tx_md, secs, frac_secs); - } - int n = 0; - cf_t* data_c[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - data_c[i] = data[i] ? data[i] : zero_mem; - } - do { - size_t tx_samples = handler->tx_nof_samples; - - // First packet is start of burst if so defined, others are never - if (n == 0) { - uhd_tx_metadata_set_start(&handler->tx_md, is_start_of_burst); - } else { - uhd_tx_metadata_set_start(&handler->tx_md, false); - } - - // middle packets are never end of burst, last one as defined - if (nsamples - n > tx_samples) { - uhd_tx_metadata_set_end(&handler->tx_md, false); - } else { - tx_samples = nsamples - n; - uhd_tx_metadata_set_end(&handler->tx_md, is_end_of_burst); - } - - const void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - void* buff = (void*)&data_c[i][n]; - buffs_ptr[i] = buff; - } - uhd_error error = - uhd_tx_streamer_send(handler->tx_stream, buffs_ptr, tx_samples, &handler->tx_md, 1.0, &txd_samples); - if (error) { - ERROR("Error sending to UHD: %d\n", error); - goto unlock; - } - // Increase time spec - uhd_tx_metadata_add_time_spec(&handler->tx_md, txd_samples / handler->tx_rate); - n += txd_samples; - trials++; - } while (n < nsamples && trials < 100); - - ret = nsamples; - - } else { - - const void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - buffs_ptr[i] = data[i]; - } - uhd_tx_metadata_set_has_time_spec(&handler->tx_md, is_start_of_burst); - uhd_tx_metadata_set_start(&handler->tx_md, is_start_of_burst); - uhd_tx_metadata_set_end(&handler->tx_md, is_end_of_burst); - uhd_error error = uhd_tx_streamer_send(handler->tx_stream, buffs_ptr, nsamples, &handler->tx_md, 0.0, &txd_samples); - if (error) { - ERROR("Error sending to UHD: %d\n", error); - goto unlock; - } - - ret = txd_samples; - } -unlock: - pthread_mutex_unlock(&handler->tx_mutex); - return ret; -} diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc new file mode 100644 index 000000000..6df6ff8eb --- /dev/null +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -0,0 +1,1340 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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. + * + * A copy of the GNU Affero 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 "rf_helper.h" +#include "srslte/srslte.h" + +#include "rf_uhd_imp.h" +#include "rf_uhd_safe.h" + +#define HAVE_ASYNC_THREAD 1 + +/** + * Transmitter finite state machine. This FSM manages the burst state of the transmitter. If the driver detects an + * Underflow, Overflow or Late, the FSM will make sure the burst is ended and started at the next transmission. The FSM + * has the following states: + * - BURST: A burst has started + * - END_OF_BURST: An underflow, overflow or late has been detected, the next transmission shall be aborted and an end + * of burst will be send in the next transmission; + * - START_BURST: The next transmission will be flagged as start of burst. + * + * +-------+ L/O/U detected +--------------+ EoB Sent +-------------+ + * | Burst |--------------->| End-of-burst |------------->| Start burst |<--- Initial state + * +-------+ | +--------------+ ^ +-------------+ + * ^ | | | + * | | Burst ACK | | + * | +-------------------------------------+ | + * | | + * | Start of burst is transmitted | + * +----------------------------------------------------------+ + */ +typedef enum { + RF_UHD_IMP_TX_STATE_START_BURST = 0, + RF_UHD_IMP_TX_STATE_BURST, + RF_UHD_IMP_TX_STATE_END_OF_BURST, +} rf_uhd_imp_underflow_state_t; + +/** + * List of devices that do NOT support dynamic master-clock-rate + */ +const std::set RH_UHD_IMP_FIX_MASTER_CLOCK_RATE_DEVICE_LIST = {"x300", "n3xx", "e3x0"}; + +/** + * List of devices that do NOT support stream stop/start after a time out + */ +const std::set RH_UHD_IMP_PROHIBITED_STOP_START = {DEVNAME_X300, DEVNAME_N300, DEVNAME_E3X0}; + +/** + * Defines a delay used between the current USRP time and the start of the transmission. This value needs to be high + * enough for being distributed to all the devices before the time expires and short enough to be as seamless as + * possible. + */ +static const double RF_UHD_IMP_STREAM_DELAY_S = 0.1; + +/** + * Defines a delay used between the current USRP time and a timed command. + */ +static const double RF_UHD_IMP_TIMED_COMMAND_DELAY_S = 0.1; + +/** + * Send and receive timeout in seconds + */ +static const double RF_UHD_IMP_TRX_TIMEOUT_S = 0.5; + +/** + * Transmit asynchronous message receiver timeout + */ +static const double RF_UHD_IMP_ASYNCH_MSG_TIMEOUT_S = 0.1; + +/** + * Maximum of Rx Trials + */ +static const uint32_t RF_UHD_IMP_MAX_RX_TRIALS = 100; + +struct rf_uhd_handler_t { + std::string devname; + rf_uhd_safe uhd; + + srslte_rf_info_t info; + size_t rx_nof_samples = 0; + size_t tx_nof_samples = 0; + double tx_rate = 1.92e6; + double rx_rate = 1.92e6; + bool dynamic_master_rate = true; + uint32_t nof_rx_channels = 0; + uint32_t nof_tx_channels = 0; + + srslte_rf_error_handler_t uhd_error_handler = nullptr; + void* uhd_error_handler_arg = nullptr; + rf_uhd_imp_underflow_state_t tx_state = RF_UHD_IMP_TX_STATE_START_BURST; + + double current_master_clock = 0.0; + + uhd::stream_args_t stream_args; + bool rx_stream_enabled = false; + + std::mutex tx_mutex; + std::mutex rx_mutex; + +#if HAVE_ASYNC_THREAD + // Asynchronous transmission message thread + bool async_thread_running = false; + std::thread async_thread; + std::mutex async_mutex; + std::condition_variable async_cvar; +#endif /* HAVE_ASYNC_THREAD */ +}; + +#if UHD_VERSION < 31100 +static void (*handler)(const char*); + +void translate_handler(uhd::msg::type_t type, const std::string& msg) +{ + if (handler) + handler(msg.c_str()); +} +#endif + +void rf_uhd_register_msg_handler_c(void (*new_handler)(const char*)) +{ +#if UHD_VERSION < 31100 + handler = new_handler; + uhd::msg::register_handler(translate_handler); +#endif +} + +void suppress_handler(const char* x) +{ + // do nothing +} + +static cf_t zero_mem[64 * 1024] = {}; + +#define print_usrp_error(h) \ + do { \ + ERROR("USRP reported the following error: %s\n", h->uhd.last_error.c_str()); \ + } while (false) + +static void log_overflow(rf_uhd_handler_t* h) +{ + if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } + + if (h->uhd_error_handler != nullptr) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW; + h->uhd_error_handler(h->uhd_error_handler_arg, error); + } +} + +static void log_late(rf_uhd_handler_t* h, bool is_rx) +{ + if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } + + if (h->uhd_error_handler != nullptr) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.opt = is_rx ? 1 : 0; + error.type = srslte_rf_error_t::SRSLTE_RF_ERROR_LATE; + h->uhd_error_handler(h->uhd_error_handler_arg, error); + } +} + +#if HAVE_ASYNC_THREAD +static void log_underflow(rf_uhd_handler_t* h) +{ + // Flag underflow + if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } + if (h->uhd_error_handler != nullptr) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW; + h->uhd_error_handler(h->uhd_error_handler_arg, error); + } +} +#endif + +static void log_rx_error(rf_uhd_handler_t* h) +{ + if (h->uhd_error_handler) { + ERROR("USRP reported the following error: %s\n", h->uhd.last_error.c_str()); + + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = srslte_rf_error_t::SRSLTE_RF_ERROR_RX; + h->uhd_error_handler(h->uhd_error_handler_arg, error); + } +} + +#if HAVE_ASYNC_THREAD +static void* async_thread(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + uhd::async_metadata_t md; + + while (handler->async_thread_running) { + std::unique_lock lock(handler->async_mutex); + bool valid = false; + + // If the Tx stream is NULL wait for tx_cvar + if (not handler->uhd.is_tx_ready()) { + handler->async_cvar.wait(lock); + } + + if (handler->uhd.is_tx_ready()) { + lock.unlock(); + if (handler->uhd.recv_async_msg(md, RF_UHD_IMP_ASYNCH_MSG_TIMEOUT_S, valid) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return nullptr; + } + + if (valid) { + const uhd::async_metadata_t::event_code_t& event_code = md.event_code; + if (event_code == uhd::async_metadata_t::EVENT_CODE_UNDERFLOW || + event_code == uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) { + log_underflow(handler); + } else if (event_code == uhd::async_metadata_t::EVENT_CODE_TIME_ERROR) { + log_late(handler, false); + } else if (event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { + // Makes sure next block will be start of burst + if (handler->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; + } + } else { + ERROR("UHD unhandled event code %d\n", event_code); + } + } + } + } + + return nullptr; +} +#endif + +static inline void uhd_free(rf_uhd_handler_t* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + + // NULL handler, return + if (handler == nullptr) { + return; + } + +#if HAVE_ASYNC_THREAD + if (handler->async_thread_running) { + handler->async_thread_running = false; + handler->async_cvar.notify_all(); + handler->async_thread.join(); + } +#endif + + delete handler; +} + +void rf_uhd_suppress_stdout(void* h) +{ + rf_uhd_register_msg_handler_c(suppress_handler); +} + +void rf_uhd_register_error_handler(void* h, srslte_rf_error_handler_t new_handler, void* arg) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + handler->uhd_error_handler = new_handler; + handler->uhd_error_handler_arg = arg; +} + +static bool find_string(uhd_string_vector_handle h, const char* str) +{ + char buff[128]; + size_t n; + uhd_string_vector_size(h, &n); + for (size_t i = 0; i < n; i++) { + uhd_string_vector_at(h, i, buff, 128); + if (strstr(buff, str)) { + return true; + } + } + return false; +} + +/** + * Set the USRP time to the current GPS time (if sensor is found) + * + * The GPS time is read and the USRP time is set to the next full second during the next PPS. + * It appears, however, that "uhd_usrp_set_time_next_pps()" which seems to be the correct function + * to use, doesn't work. The C API call "uhd_usrp_set_time_unknown_pps()" works well. + * @param handler Pointer to RF handler + * @return Any error returned by UHD + */ +static int set_time_to_gps_time(rf_uhd_handler_t* handler) +{ + const std::string sensor_name = "gps_time"; + + std::vector sensors; + if (handler->uhd.get_mboard_sensor_names(sensors) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Find sensor + bool found = false; + for (std::string& sensor : sensors) { +#if PRINT_SENSOR_NAMES + printf("sensor %s present\n", sensor.c_str()); +#endif + + if (sensor == sensor_name) { + found = true; + break; + } + } + + // No sensor found + if (not found) { + ERROR("Sensor '%s` not found.\n", sensor_name.c_str()); + return UHD_ERROR_NONE; + } + + // Get actual sensor value + uhd::sensor_value_t sensor_value("w", "t", "f"); + if (handler->uhd.get_sensor(sensor_name, sensor_value) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Get time and set + double frac_secs = sensor_value.to_real(); + printf("Setting USRP time to %fs\n", frac_secs); + if (handler->uhd.set_time_unknown_pps(uhd::time_spec_t(frac_secs)) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +// timeout in ms +static int wait_sensor_locked(rf_uhd_handler_t* handler, + const std::string& sensor_name, + bool is_mboard, + int timeout, + bool& is_locked) +{ + is_locked = false; + + // Get sensor list + std::vector sensors; + if (is_mboard) { + // motherboard sensor + if (handler->uhd.get_mboard_sensor_names(sensors) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } else { + // daughterboard sensor + if (handler->uhd.get_rx_sensor_names(sensors) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + // Find sensor + bool found = false; + for (std::string& sensor : sensors) { +#if PRINT_SENSOR_NAMES + printf("sensor %s present\n", sensor.c_str()); +#endif + + if (sensor == sensor_name) { + found = true; + break; + } + } + + // No sensor found + if (not found) { + ERROR("Sensor '%s` not found.\n", sensor_name.c_str()); + return UHD_ERROR_NONE; + } + + do { + // Get actual sensor value + uhd::sensor_value_t sensor_value("", true, "True", "False"); + if (is_mboard) { + if (handler->uhd.get_sensor(sensor_name, sensor_value) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } else { + if (handler->uhd.get_rx_sensor(sensor_name, sensor_value) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + // Read value and wait + is_locked = sensor_value.to_bool(); + usleep(1000); // 1ms + timeout -= 1; // 1ms + } while (not is_locked and timeout > 0); + + return SRSLTE_SUCCESS; +} + +const char* rf_uhd_devname(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + return handler->devname.c_str(); +} + +bool rf_uhd_rx_wait_lo_locked(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + + // wait for clock source to lock + std::string sensor_name = "lo_locked"; + bool is_locked = false; + + // blocks until sensor is blocked + int error = wait_sensor_locked(handler, sensor_name, false, 300, is_locked); + + if (not is_locked and error == SRSLTE_SUCCESS) { + ERROR("Could not lock reference clock source. Sensor: %s=%s\n", sensor_name.c_str(), is_locked ? "true" : "false"); + } + + return is_locked; +} + +static inline int rf_uhd_start_rx_stream_unsafe(rf_uhd_handler_t* handler) +{ + // Check if stream was not created or started + if (not handler->uhd.is_rx_ready() or handler->rx_stream_enabled) { + // Ignores command, the stream will start as soon as the Rx sampling rate is set + return SRSLTE_SUCCESS; + } + + // Issue stream command + if (handler->uhd.start_rx_stream(RF_UHD_IMP_STREAM_DELAY_S) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + handler->rx_stream_enabled = true; + + return SRSLTE_SUCCESS; +} + +int rf_uhd_start_rx_stream(void* h, bool now) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->rx_mutex); + + return rf_uhd_start_rx_stream_unsafe(handler); +} + +static inline int rf_uhd_stop_rx_stream_unsafe(rf_uhd_handler_t* handler) +{ + // Check if stream was created or stream was not started + if (not handler->uhd.is_rx_ready() or not handler->rx_stream_enabled) { + // Ignores command, the stream will start as soon as the Rx sampling rate is set + return SRSLTE_SUCCESS; + } + + // Issue stream command + if (handler->uhd.stop_rx_stream() != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + handler->rx_stream_enabled = false; + + return SRSLTE_SUCCESS; +} + +int rf_uhd_stop_rx_stream(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->rx_mutex); + + return rf_uhd_stop_rx_stream_unsafe(handler); +} + +void rf_uhd_flush_buffer(void* h) +{ + int n; + void* data[SRSLTE_MAX_CHANNELS] = {}; + + // Set all pointers to zero buffer + for (auto& i : data) { + i = zero_mem; + } + + // Receive until times out + do { + n = rf_uhd_recv_with_time_multi(h, data, 1024, false, nullptr, nullptr); + } while (n > 0); +} + +bool rf_uhd_has_rssi(void* h) +{ + return false; +} + +float rf_uhd_get_rssi(void* h) +{ + return NAN; +} + +int rf_uhd_open(char* args, void** h) +{ + return rf_uhd_open_multi(args, h, 1); +} + +int rf_uhd_open_multi(char* args, void** h, uint32_t nof_channels) +{ + // Check valid handler pointer + if (h == nullptr) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (nof_channels > SRSLTE_MAX_CHANNELS) { + ERROR("Error opening UHD: maximum number of channels exceeded (%d>%d)\n", nof_channels, SRSLTE_MAX_CHANNELS); + return SRSLTE_ERROR; + } + + rf_uhd_handler_t* handler = new rf_uhd_handler_t; + *h = handler; + + // Disable fast-path (U/L/O) messages + setenv("UHD_LOG_FASTPATH_DISABLE", "1", 0); + + // Set priority to UHD threads + uhd_set_thread_priority(uhd_default_thread_priority, true); + + // Allow NULL args + char* empty_args = (char*)""; + if (args == nullptr) { + args = empty_args; + } + handler->devname = ""; + + // Parse args into dictionary + uhd::device_addr_t device_addr((std::string(args))); + + // Initialize handler + handler->uhd_error_handler = nullptr; + + // Check external clock argument + std::string clock_src = "internal"; + if (device_addr.has_key("clock")) { + clock_src = device_addr.pop("clock"); + } + +#if HAVE_ASYNC_THREAD + bool start_async_thread = true; + if (device_addr.has_key("silent")) { + device_addr.pop("silent"); + start_async_thread = false; + } +#endif + + // Set over the wire format + std::string otw_format = "sc16"; + if (device_addr.has_key("otw_format")) { + otw_format = device_addr.pop("otw_format"); + } + + // Set transmitter subdevice spec string + std::string tx_subdev; + if (device_addr.has_key("tx_subdev_spec")) { + tx_subdev = device_addr.pop("tx_subdev_spec"); + } + + // Set receiver subdevice spec string + std::string rx_subdev; + if (device_addr.has_key("rx_subdev_spec")) { + rx_subdev = device_addr.pop("rx_subdev_spec"); + } + + // If device type or name not given in args, select device from found list + if (not device_addr.has_key("type")) { + // Find available devices + uhd_string_vector_handle devices_str = {}; + uhd_string_vector_make(&devices_str); + uhd_usrp_find("", &devices_str); + + std::string type; + + if (find_string(devices_str, "type=b200")) { + type = "b200"; + } else if (find_string(devices_str, "type=x300")) { + type = "x300"; + } else if (find_string(devices_str, "type=e3x0")) { + type = "e3x0"; + } else if (find_string(devices_str, "type=n3xx")) { + type = "n3xx"; + } + + if (not type.empty()) { + device_addr.set("type", type); + } + + uhd_string_vector_free(&devices_str); + } + + // Parse/Select master clock rate + if (not device_addr.has_key("master_clock_rate") and device_addr.has_key("type")) { + // Default master clock rate for B200 series + std::string mcr = "23.04e6"; + + if (device_addr["type"] == "x300") { + mcr = "184.32e6"; + } else if (device_addr["type"] == "n3xx") { + mcr = "122.88e6"; + } else if (device_addr["type"] == "e3x0") { + mcr = "30.72e6"; + } + + device_addr.set("master_clock_rate", mcr); + } + handler->current_master_clock = device_addr.cast("master_clock_rate", 0.0); + + // Set dynamic master clock rate configuration + if (device_addr.has_key("type")) { + handler->dynamic_master_rate = RH_UHD_IMP_FIX_MASTER_CLOCK_RATE_DEVICE_LIST.count(device_addr["type"]) == 0; + } + + // Parse initial sample rate + if (device_addr.has_key("sampling_rate")) { + handler->tx_rate = device_addr.cast("sampling_rate", handler->tx_rate); + handler->rx_rate = handler->tx_rate; + device_addr.pop("sampling_rate"); + } + + // Create UHD handler + printf("Opening USRP channels=%d, args: %s\n", nof_channels, device_addr.to_string().c_str()); + + // Make USRP + if (handler->uhd.usrp_make(device_addr) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Set transmitter subdev spec if specified + if (not tx_subdev.empty()) { + printf("Setting tx_subdev_spec to '%s'\n", tx_subdev.c_str()); + if (handler->uhd.set_tx_subdev(tx_subdev) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + // Set receiver subdev spec if specified + if (not rx_subdev.empty()) { + printf("Setting rx_subdev_spec to '%s'\n", rx_subdev.c_str()); + if (handler->uhd.set_rx_subdev(rx_subdev) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + // Set device internal name, it sets the device name to B200 by default + if (device_addr.has_key("type")) { + if (device_addr["type"] == "x300") { + handler->devname = DEVNAME_X300; + } else if (device_addr["type"] == "n3xx") { + handler->devname = DEVNAME_N300; + } else if (device_addr["type"] == "e3x0") { + handler->devname = DEVNAME_E3X0; + } else if (device_addr["type"] == "b200") { + handler->devname = DEVNAME_B200; + } + } + + // If device name is not set, get it from motherboard + if (handler->devname.empty()) { + std::string mboard_name; + if (handler->uhd.get_mboard_name(mboard_name) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Make upper case + for (auto& x : mboard_name) { + x = std::toupper(x); + } + + if (mboard_name.find("B2") != std::string::npos) { + handler->devname = DEVNAME_B200; + } else if (mboard_name.find("X3") != std::string::npos) { + handler->devname = DEVNAME_X300; + } else if (mboard_name.find("N3") != std::string::npos) { + handler->devname = DEVNAME_N300; + } else { + handler->devname = DEVNAME_UNKNOWN; + } + } + + bool is_locked = false; + std::string sensor_name; + + // Set sync source + if (handler->uhd.set_sync_source(clock_src) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + if (clock_src == "gpsdo") { + set_time_to_gps_time(handler); + sensor_name = "gps_locked"; + } else { + sensor_name = "ref_locked"; + } + + // Wait until external reference / GPS is locked + if (clock_src != "internal") { + // blocks until clock source is locked + int error = wait_sensor_locked(handler, sensor_name, true, 300, is_locked); + // Print Not lock error if the return was succesful, wait_sensor_locked prints the error before returning + if (not is_locked and error == SRSLTE_SUCCESS) { + ERROR( + "Could not lock reference clock source. Sensor: %s=%s\n", sensor_name.c_str(), is_locked ? "true" : "false"); + } + } + + // Initialize TX/RX stream args + handler->stream_args.cpu_format = "fc32"; + handler->stream_args.otw_format = otw_format; + handler->stream_args.channels.resize(nof_channels); + for (size_t i = 0; i < (size_t)nof_channels; i++) { + handler->stream_args.channels[i] = i; + } + + handler->nof_rx_channels = nof_channels; + handler->nof_tx_channels = nof_channels; + + if (handler->uhd.set_rx_rate(handler->rx_rate) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + if (handler->uhd.set_tx_rate(handler->tx_rate) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + if (nof_channels > 1 and clock_src != "gpsdo") { + handler->uhd.set_time_unknown_pps(uhd::time_spec_t()); + } + + if (handler->uhd.get_rx_stream(handler->stream_args, handler->rx_nof_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + if (handler->uhd.get_tx_stream(handler->stream_args, handler->tx_nof_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Populate RF device info + uhd::gain_range_t tx_gain_range; + uhd::gain_range_t rx_gain_range; + if (handler->uhd.get_gain_range(tx_gain_range, rx_gain_range) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + handler->info.min_tx_gain = tx_gain_range.start(); + handler->info.max_tx_gain = tx_gain_range.stop(); + handler->info.min_rx_gain = rx_gain_range.start(); + handler->info.max_rx_gain = rx_gain_range.stop(); + + // Set starting gain to half maximum in case of using AGC + rf_uhd_set_rx_gain(handler, handler->info.max_rx_gain * 0.7); + +#if HAVE_ASYNC_THREAD + if (start_async_thread) { + // Start low priority thread to receive async commands + handler->async_thread_running = true; + handler->async_thread = std::thread(async_thread, handler); + } +#endif + + // Restore priorities + if (uhd_set_thread_priority(0, false) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int rf_uhd_close(void* h) +{ + + // Makes sure Tx is ended + void* buff[SRSLTE_MAX_CHANNELS] = {}; + rf_uhd_send_timed_multi(h, buff, 0, 0, 0, false, true, false, true); + + // Makes sure Rx stream is stopped + rf_uhd_stop_rx_stream(h); + + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + + /// Free all UHD safe class + uhd_free(handler); + + return SRSLTE_SUCCESS; +} + +static inline void rf_uhd_set_master_clock_rate_unsafe(rf_uhd_handler_t* handler, double rate) +{ + // Set master clock rate if it is allowed and change is required + if (handler->dynamic_master_rate && handler->current_master_clock != rate) { + if (handler->uhd.set_master_clock_rate(rate) != UHD_ERROR_NONE) { + print_usrp_error(handler); + } + handler->current_master_clock = rate; + } +} + +static inline int rf_uhd_imp_end_burst(rf_uhd_handler_t* handler) +{ + uhd::tx_metadata_t md; + void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; + size_t txd_samples = 0; + + // Set buffer pointers + for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + buffs_ptr[i] = zero_mem; + } + + // Set metadata + md.has_time_spec = false; + md.start_of_burst = false; + md.end_of_burst = true; + + // Actual base-band transmission + if (handler->uhd.send(buffs_ptr, 0, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Update TX state + handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; + + return SRSLTE_SUCCESS; +} + +double rf_uhd_set_rx_srate(void* h, double freq) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->rx_mutex); + + // Early return if the current rate matches and Rx stream has been created + if (freq == handler->rx_rate and handler->uhd.is_rx_ready()) { + return freq; + } + + // Stop RX streamer + if (rf_uhd_stop_rx_stream_unsafe(handler) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Set master clock rate + if (fmod(handler->current_master_clock, freq) > 0.0) { + rf_uhd_set_master_clock_rate_unsafe(handler, 4 * freq); + } + + if (handler->nof_rx_channels > 1) { + uhd::time_spec_t timespec; + if (handler->uhd.get_time_now(timespec) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + timespec += RF_UHD_IMP_TIMED_COMMAND_DELAY_S; + handler->uhd.set_command_time(timespec); + } + + // Set RX rate + if (handler->uhd.set_rx_rate(freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + if (RH_UHD_IMP_PROHIBITED_STOP_START.count(handler->devname) == 0) { + if (handler->uhd.get_rx_stream(handler->stream_args, handler->rx_nof_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + // Update current rate + handler->rx_rate = freq; + + return freq; +} + +double rf_uhd_set_tx_srate(void* h, double freq) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->tx_mutex); +#if HAVE_ASYNC_THREAD + std::unique_lock lock_async(handler->async_mutex); +#endif /* HAVE_ASYNC_THREAD */ + + // Early return if the current rate matches and Tx stream has been created + if (freq == handler->tx_rate and handler->uhd.is_tx_ready()) { + return freq; + } + + // End burst + if (handler->uhd.is_tx_ready() and handler->tx_state != RF_UHD_IMP_TX_STATE_START_BURST) { + if (rf_uhd_imp_end_burst(handler) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + } + + // Set master clock rate + if (fmod(handler->current_master_clock, freq) > 0.0) { + rf_uhd_set_master_clock_rate_unsafe(handler, 4 * freq); + } + + if (handler->nof_tx_channels > 1) { + uhd::time_spec_t timespec; + if (handler->uhd.get_time_now(timespec) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + timespec += RF_UHD_IMP_TIMED_COMMAND_DELAY_S; + handler->uhd.set_command_time(timespec); + } + + // Set TX rate + if (handler->uhd.set_tx_rate(freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + if (RH_UHD_IMP_PROHIBITED_STOP_START.count(handler->devname) == 0) { + if (handler->uhd.get_tx_stream(handler->stream_args, handler->tx_nof_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + + handler->tx_rate = freq; + +#if HAVE_ASYNC_THREAD + // Notifies the Asynchronous thread about a tx stream change + handler->async_cvar.notify_all(); +#endif /* HAVE_ASYNC_THREAD */ + + return freq; +} + +double rf_uhd_set_rx_gain(void* h, double gain) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + for (size_t i = 0; i < handler->nof_rx_channels; i++) { + if (handler->uhd.set_rx_gain(i, gain) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + return gain; +} + +double rf_uhd_set_tx_gain(void* h, double gain) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + for (size_t i = 0; i < handler->nof_tx_channels; i++) { + if (handler->uhd.set_tx_gain(i, gain) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + return gain; +} + +double rf_uhd_get_rx_gain(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + double gain = 0.0; + + if (handler->uhd.get_rx_gain(gain) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + return gain; +} + +double rf_uhd_get_tx_gain(void* h) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + double gain = 0.0; + + if (handler->uhd.get_tx_gain(gain) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + return gain; +} + +srslte_rf_info_t* rf_uhd_get_info(void* h) +{ + srslte_rf_info_t* info = nullptr; + + if (h != nullptr) { + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + info = &handler->info; + } + + return info; +} + +double rf_uhd_set_rx_freq(void* h, uint32_t ch, double freq) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + if (ch < handler->nof_rx_channels) { + if (handler->uhd.set_rx_freq(ch, freq, freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } else { + for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { + if (handler->uhd.set_rx_freq(i, freq, freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + } + rf_uhd_rx_wait_lo_locked(handler); + return freq; +} + +double rf_uhd_set_tx_freq(void* h, uint32_t ch, double freq) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + if (ch < handler->nof_tx_channels) { + if (handler->uhd.set_tx_freq(ch, freq, freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } else { + for (uint32_t i = 0; i < handler->nof_tx_channels; i++) { + if (handler->uhd.set_tx_freq(i, freq, freq) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + } + } + return freq; +} + +void rf_uhd_get_time(void* h, time_t* secs, double* frac_secs) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + uhd::time_spec_t timespec; + if (handler->uhd.get_time_now(timespec) != UHD_ERROR_NONE) { + print_usrp_error(handler); + } + if (secs != nullptr) { + *secs = timespec.get_full_secs(); + } + + if (frac_secs != nullptr) { + *frac_secs = timespec.get_frac_secs(); + } +} + +void rf_uhd_sync_pps(void* h) +{ + if (h == nullptr) { + return; + } + + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + uhd::time_spec_t timespec(0.0); + if (handler->uhd.set_time_unknown_pps(timespec) != UHD_ERROR_NONE) { + print_usrp_error(handler); + } +} + +int rf_uhd_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) +{ + return rf_uhd_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + +int rf_uhd_recv_with_time_multi(void* h, + void* data[SRSLTE_MAX_PORTS], + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->rx_mutex); + size_t rxd_samples = 0; + size_t rxd_samples_total = 0; + uint32_t trials = 0; + int ret = SRSLTE_ERROR; + uhd::time_spec_t timespec; + uhd::rx_metadata_t md; + + // Check Rx stream has been created + if (not handler->uhd.is_rx_ready()) { + // Ignores reception, the stream will start as soon as the Rx sampling rate is set + return SRSLTE_SUCCESS; + } + + // Start stream if not started + if (not handler->rx_stream_enabled) { + if (rf_uhd_start_rx_stream_unsafe(handler) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + } + + // Receive stream in multiple blocks + while (rxd_samples_total < nsamples && trials < RF_UHD_IMP_MAX_RX_TRIALS) { + void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; + for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { + cf_t* data_c = (cf_t*)data[i]; + buffs_ptr[i] = &data_c[rxd_samples_total]; + } + + size_t num_samps_left = nsamples - rxd_samples_total; + size_t num_rx_samples = (num_samps_left > handler->rx_nof_samples) ? handler->rx_nof_samples : num_samps_left; + + if (handler->uhd.receive(buffs_ptr, num_rx_samples, md, 1.0, false, rxd_samples) != UHD_ERROR_NONE) { + log_rx_error(handler); + print_usrp_error(handler); + return SRSLTE_ERROR; + } + rxd_samples = num_rx_samples; + + // Save timespec for first block + if (rxd_samples_total == 0) { + timespec = md.time_spec; + } + uhd::rx_metadata_t::error_code_t& error_code = md.error_code; + + rxd_samples_total += rxd_samples; + trials++; + + if (error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) { + log_overflow(handler); + } else if (error_code == uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND) { + log_late(handler, true); + } else if (error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) { + ERROR("Error in metadata '%s'. Recovering (%d/%d).\n", + md.to_pp_string(true).c_str(), + trials, + RF_UHD_IMP_MAX_RX_TRIALS); + + if (handler->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + handler->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } + + if (RH_UHD_IMP_PROHIBITED_STOP_START.count(handler->devname) == 0) { + // Stop Rx stream + rf_uhd_stop_rx_stream_unsafe(handler); + } + } + } + + if (trials >= RF_UHD_IMP_MAX_RX_TRIALS) { + return SRSLTE_ERROR; + } + + ret = rxd_samples_total; + + // Set timestamp if provided + if (secs != nullptr and frac_secs != nullptr) { + *secs = timespec.get_full_secs(); + *frac_secs = timespec.get_frac_secs(); + } + + return ret; +} + +int rf_uhd_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + // Maximum number of channels to NULL + void* _data[SRSLTE_MAX_CHANNELS] = {}; + + // Set only first channel + _data[0] = data; + + return rf_uhd_send_timed_multi( + h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int rf_uhd_send_timed_multi(void* h, + void** data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + std::unique_lock lock(handler->tx_mutex); + uhd::tx_metadata_t md; + void* buffs_ptr[SRSLTE_MAX_CHANNELS] = {}; + size_t txd_samples = 0; + int n = 0; + + // Check Tx stream has been created + if (not handler->uhd.is_tx_ready()) { + return SRSLTE_ERROR; + } + + // Run Underflow recovery state machine + switch (handler->tx_state) { + case RF_UHD_IMP_TX_STATE_BURST: + // Normal case, do nothing + break; + case RF_UHD_IMP_TX_STATE_END_OF_BURST: + if (rf_uhd_imp_end_burst(handler) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; + return SRSLTE_ERROR; + case RF_UHD_IMP_TX_STATE_START_BURST: + // Set tart of burst to false if recovering from the Underflow + is_start_of_burst = true; + handler->tx_state = RF_UHD_IMP_TX_STATE_BURST; + break; + } + + if (not has_time_spec) { + // If it the beginning of a burst, set timestamp + if (is_start_of_burst) { + // It gets the USRP time for transmissions without time + if (handler->uhd.get_time_now(md.time_spec) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Add time to metadata + md.time_spec += RF_UHD_IMP_STREAM_DELAY_S; + } + } else { + // Otherwise, it gets given time in the arguments + md.time_spec = uhd::time_spec_t(secs, frac_secs); + } + + // Generate transmission buffer pointers + cf_t* data_c[SRSLTE_MAX_CHANNELS] = {}; + for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + if (i < handler->nof_tx_channels) { + data_c[i] = (data[i] != nullptr) ? (cf_t*)(data[i]) : zero_mem; + } else { + data_c[i] = zero_mem; + } + } + + // it transmits in chunks of `handler->tx_nof_samples` except last block + do { + size_t tx_samples = handler->tx_nof_samples; + + // Set start of burst. Time spec only for the first packet in the burst + md.start_of_burst = is_start_of_burst; + md.has_time_spec = has_time_spec; + + // middle packets are never end of burst, last one as defined + if (nsamples - n > (int)tx_samples) { + md.end_of_burst = false; + } else { + tx_samples = nsamples - n; + md.end_of_burst = is_end_of_burst; + } + + for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + void* buff = (void*)&data_c[i][n]; + buffs_ptr[i] = buff; + } + + if (handler->uhd.send(buffs_ptr, tx_samples, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) { + print_usrp_error(handler); + return SRSLTE_ERROR; + } + + // Next packets are not start of burst + is_start_of_burst = false; + md.time_spec += txd_samples / handler->tx_rate; + + n += txd_samples; + } while (n < nsamples); + + // If end of burst, make sure it will be start next call + if (md.end_of_burst and handler->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; + } + + return nsamples; +} diff --git a/lib/src/phy/rf/rf_uhd_imp.h b/lib/src/phy/rf/rf_uhd_imp.h index 9a37f9b7c..8f6bd84de 100644 --- a/lib/src/phy/rf/rf_uhd_imp.h +++ b/lib/src/phy/rf/rf_uhd_imp.h @@ -24,6 +24,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #include "srslte/config.h" #include "srslte/phy/common/phy_common.h" #include "srslte/phy/rf/rf.h" @@ -32,6 +36,7 @@ #define DEVNAME_X300 "uhd_x300" #define DEVNAME_N300 "uhd_n300" #define DEVNAME_E3X0 "uhd_e3x0" +#define DEVNAME_UNKNOWN "uhd_unknown" SRSLTE_API int rf_uhd_open(char* args, void** handler); @@ -43,8 +48,6 @@ SRSLTE_API int rf_uhd_close(void* h); SRSLTE_API int rf_uhd_start_rx_stream(void* h, bool now); -SRSLTE_API int rf_uhd_start_rx_stream_nsamples(void* h, uint32_t nsamples); - SRSLTE_API int rf_uhd_stop_rx_stream(void* h); SRSLTE_API void rf_uhd_flush_buffer(void* h); @@ -105,4 +108,8 @@ SRSLTE_API int rf_uhd_send_timed_multi(void* h, bool is_start_of_burst, bool is_end_of_burst); +#ifdef __cplusplus +} +#endif + #endif /* SRSLTE_RF_UHD_IMP_H_ */ diff --git a/lib/src/phy/rf/rf_uhd_safe.h b/lib/src/phy/rf/rf_uhd_safe.h new file mode 100644 index 000000000..a9be75d03 --- /dev/null +++ b/lib/src/phy/rf/rf_uhd_safe.h @@ -0,0 +1,235 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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. + * + * A copy of the GNU Affero 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 SRSLTE_RF_UHD_SAFE_H +#define SRSLTE_RF_UHD_SAFE_H + +#ifdef ENABLE_UHD_X300_FW_RESET +#include + +uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors); +#endif /* ENABLE_UHD_X300_FW_RESET */ + +class rf_uhd_safe +{ +private: + // List of errors that can happen in the USRP make that need to restart the device + const std::set USRP_MAKE_RESET_ERR = {UHD_ERROR_IO}; + + // UHD pointers + uhd::usrp::multi_usrp::sptr usrp = nullptr; + uhd::rx_streamer::sptr rx_stream = nullptr; + uhd::tx_streamer::sptr tx_stream = nullptr; + + uhd_error usrp_make_internal(const uhd::device_addr_t& dev_addr) + { + // Destroy any previous USRP instance + usrp.reset(); + + UHD_SAFE_C_SAVE_ERROR(this, usrp = uhd::usrp::multi_usrp::make(dev_addr);) + } + +#ifdef ENABLE_UHD_X300_FW_RESET + uhd_error try_usrp_x300_reset(const uhd::device_addr_t& dev_addr) + { + UHD_SAFE_C_SAVE_ERROR( + this, + if (not dev_addr.has_key("addr")) { return UHD_ERROR_NONE; } + + printf("Reseting X300 in address %s\n", dev_addr["addr"].c_str()); + + { // Reset Scope + // Create UDP connection + uhd::transport::udp_simple::sptr udp_simple = + uhd::transport::udp_simple::make_connected(dev_addr["addr"], "49152"); + + // Create X300 control + uhd::wb_iface::sptr x300_ctrl = x300_make_ctrl_iface_enet(udp_simple, true); + + // Reset FPGA firmware + x300_ctrl->poke32(0x100058, 1); + + printf("Reset Done!\n"); + } + + return UHD_ERROR_NONE;) + } +#endif /* ENABLE_UHD_X300_FW_RESET */ + +public: + std::string last_error; + + inline uhd_error usrp_make(const uhd::device_addr_t& dev_addr) + { + uhd_error err = usrp_make_internal(dev_addr); + +#ifdef ENABLE_UHD_X300_FW_RESET + // Looks up error in reset table + if (USRP_MAKE_RESET_ERR.count(err) == 0 or not dev_addr.has_key("addr")) { + // If no error in table or not specified type, returns + return err; + } + + // Reset device + err = try_usrp_x300_reset(dev_addr); + if (err != UHD_ERROR_NONE) { + return err; + } + + // Try opening the device one more time + return usrp_make_internal(dev_addr); +#else /* ENABLE_UHD_X300_FW_RESET */ + return err; +#endif /* ENABLE_UHD_X300_FW_RESET */ + } + inline uhd_error set_tx_subdev(const std::string& string) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_subdev_spec(string);) + } + inline uhd_error set_rx_subdev(const std::string& string) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_subdev_spec(string);) + } + inline uhd_error get_mboard_name(std::string& mboard_name) + { + UHD_SAFE_C_SAVE_ERROR(this, mboard_name = usrp->get_mboard_name();) + } + inline uhd_error get_mboard_sensor_names(std::vector& sensors) + { + UHD_SAFE_C_SAVE_ERROR(this, sensors = usrp->get_mboard_sensor_names();) + } + inline uhd_error get_rx_sensor_names(std::vector& sensors) + { + UHD_SAFE_C_SAVE_ERROR(this, sensors = usrp->get_rx_sensor_names();) + } + inline uhd_error get_sensor(const std::string& sensor_name, uhd::sensor_value_t& sensor_value) + { + UHD_SAFE_C_SAVE_ERROR(this, sensor_value = usrp->get_mboard_sensor(sensor_name);) + } + inline uhd_error get_rx_sensor(const std::string& sensor_name, uhd::sensor_value_t& sensor_value) + { + UHD_SAFE_C_SAVE_ERROR(this, sensor_value = usrp->get_rx_sensor(sensor_name);) + } + inline uhd_error set_time_unknown_pps(const uhd::time_spec_t& timespec) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_time_unknown_pps(timespec);) + } + inline uhd_error set_time_now(const uhd::time_spec_t& timespec) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_time_now(timespec);) + } + inline uhd_error get_time_now(uhd::time_spec_t& timespec) + { + UHD_SAFE_C_SAVE_ERROR(this, timespec = usrp->get_time_now();) + } + inline uhd_error start_rx_stream(double delay) + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.time_spec = usrp->get_time_now(); + stream_cmd.time_spec += 0.1; + stream_cmd.stream_now = false; + + rx_stream->issue_stream_cmd(stream_cmd);) + } + inline uhd_error stop_rx_stream() + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + rx_stream->issue_stream_cmd(stream_cmd);) + } + inline uhd_error set_sync_source(const std::string& source) + { +#if UHD_VERSION < 3140099 + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_clock_source(source); usrp->set_time_source(source);) +#else + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_sync_source(source, source);) +#endif + } + inline uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) + { + UHD_SAFE_C_SAVE_ERROR(this, tx_gain_range = usrp->get_tx_gain_range(); rx_gain_range = usrp->get_rx_gain_range();) + } + inline uhd_error set_master_clock_rate(double rate) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_master_clock_rate(rate);) + } + inline uhd_error set_rx_rate(double rate) { UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_rate(rate);) } + inline uhd_error set_tx_rate(double rate) { UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_rate(rate);) } + inline uhd_error set_command_time(const uhd::time_spec_t& timespec) + { + UHD_SAFE_C_SAVE_ERROR(this, usrp->set_command_time(timespec);) + } + inline uhd_error destroy_rx_stream() { UHD_SAFE_C_SAVE_ERROR(this, rx_stream = nullptr;) } + inline uhd_error get_rx_stream(const uhd::stream_args_t& args, size_t& max_num_samps) + { + UHD_SAFE_C_SAVE_ERROR(this, rx_stream = nullptr; rx_stream = usrp->get_rx_stream(args); + max_num_samps = rx_stream->get_max_num_samps();) + } + inline uhd_error destroy_tx_stream() { UHD_SAFE_C_SAVE_ERROR(this, rx_stream = nullptr;) } + inline uhd_error get_tx_stream(const uhd::stream_args_t& args, size_t& max_num_samps) + { + UHD_SAFE_C_SAVE_ERROR(this, tx_stream = nullptr; tx_stream = usrp->get_tx_stream(args); + max_num_samps = tx_stream->get_max_num_samps();) + } + inline uhd_error set_tx_gain(size_t ch, double gain) { UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_gain(gain, ch);) } + inline uhd_error set_rx_gain(size_t ch, double gain) { UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_gain(gain, ch);) } + inline uhd_error get_rx_gain(double& gain) { UHD_SAFE_C_SAVE_ERROR(this, gain = usrp->get_rx_gain();) } + inline uhd_error get_tx_gain(double& gain) { UHD_SAFE_C_SAVE_ERROR(this, gain = usrp->get_tx_gain();) } + inline uhd_error set_tx_freq(uint32_t ch, double target_freq, double& actual_freq) + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::tune_request_t tune_request(target_freq); + uhd::tune_result_t tune_result = usrp->set_tx_freq(tune_request, ch); + actual_freq = tune_result.target_rf_freq;) + } + inline uhd_error set_rx_freq(uint32_t ch, double target_freq, double& actual_freq) + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::tune_request_t tune_request(target_freq); + uhd::tune_result_t tune_result = usrp->set_rx_freq(tune_request, ch); + actual_freq = tune_result.target_rf_freq;) + } + inline uhd_error receive(void** buffs, + const size_t nsamps_per_buff, + uhd::rx_metadata_t& metadata, + const double timeout, + const bool one_packet, + size_t& nof_rxd_samples) + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::rx_streamer::buffs_type buffs_cpp(buffs, rx_stream->get_num_channels()); + nof_rxd_samples = rx_stream->recv(buffs_cpp, nsamps_per_buff, metadata, timeout, one_packet);) + } + inline uhd_error recv_async_msg(uhd::async_metadata_t& async_metadata, double timeout, bool& valid) + { + UHD_SAFE_C_SAVE_ERROR(this, valid = tx_stream->recv_async_msg(async_metadata, timeout); if (valid) { + return UHD_ERROR_NONE; + } valid = usrp.get()->get_device()->recv_async_msg(async_metadata);) + } + inline uhd_error send(void** buffs, + const size_t nsamps_per_buff, + const uhd::tx_metadata_t& metadata, + const double timeout, + size_t& nof_txd_samples) + { + UHD_SAFE_C_SAVE_ERROR(this, uhd::tx_streamer::buffs_type buffs_cpp(buffs, tx_stream->get_num_channels()); + nof_txd_samples = tx_stream->send(buffs_cpp, nsamps_per_buff, metadata, timeout);) + } + inline bool is_rx_ready() { return rx_stream != nullptr; } + inline bool is_tx_ready() { return tx_stream != nullptr; } +}; + +#endif // SRSLTE_RF_UHD_SAFE_H diff --git a/lib/src/phy/rf/uhd_c_api.cpp b/lib/src/phy/rf/uhd_c_api.cpp deleted file mode 100644 index 36e2b9fd3..000000000 --- a/lib/src/phy/rf/uhd_c_api.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2013-2019 Software Radio Systems Limited - * - * This file is part of srsLTE. - * - * srsLTE 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. - * - * srsLTE 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. - * - * A copy of the GNU Affero 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/. - * - */ - -/* This file implements a few features not currently provided by the UHD C-API */ -#include -#include - -extern "C" { -#include "srslte/phy/rf/rf.h" -#include "uhd_c_api.h" -} - -#if UHD_VERSION < 31100 -static void (*handler)(const char*); - -void translate_handler(uhd::msg::type_t type, const std::string & msg) -{ - if(handler) - handler(msg.c_str()); -} -#endif - -void rf_uhd_register_msg_handler_c(void (*new_handler)(const char*)) -{ -#if UHD_VERSION < 31100 - handler = new_handler; - uhd::msg::register_handler(translate_handler); -#endif -} - -void uhd_tx_metadata_set_time_spec(uhd_tx_metadata_handle *md, time_t secs, double frac_secs) -{ - (*md)->tx_metadata_cpp.time_spec = uhd::time_spec_t(secs, frac_secs); - (*md)->tx_metadata_cpp.has_time_spec = true; -} - -void uhd_tx_metadata_set_start(uhd_tx_metadata_handle *md, bool is_start_of_burst) -{ - (*md)->tx_metadata_cpp.start_of_burst = is_start_of_burst; -} - -void uhd_tx_metadata_set_has_time_spec(uhd_tx_metadata_handle *md, bool has_time_spec) -{ - (*md)->tx_metadata_cpp.has_time_spec = has_time_spec; -} - - -void uhd_tx_metadata_set_end(uhd_tx_metadata_handle *md, bool is_end_of_burst) -{ - (*md)->tx_metadata_cpp.end_of_burst = is_end_of_burst; -} - -void uhd_tx_metadata_add_time_spec(uhd_tx_metadata_handle *md, double frac_secs) -{ - (*md)->tx_metadata_cpp.time_spec += frac_secs; -} - diff --git a/lib/src/phy/rf/uhd_c_api.h b/lib/src/phy/rf/uhd_c_api.h deleted file mode 100644 index ec454cc30..000000000 --- a/lib/src/phy/rf/uhd_c_api.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2013-2020 Software Radio Systems Limited - * - * This file is part of srsLTE. - * - * srsLTE 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. - * - * srsLTE 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. - * - * A copy of the GNU Affero 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 SRSLTE_UHD_C_API_C_ -#define SRSLTE_UHD_C_API_C_ - -#include "srslte/config.h" -#include "srslte/phy/rf/rf.h" -#include - -/* Declare functions not currently provided by the C-API */ -SRSLTE_API void rf_uhd_register_msg_handler_c(void (*new_handler)(const char*)); -SRSLTE_API void uhd_tx_metadata_set_time_spec(uhd_tx_metadata_handle* md, time_t secs, double frac_secs); -SRSLTE_API void uhd_tx_metadata_set_start(uhd_tx_metadata_handle* md, bool is_start_of_burst); -SRSLTE_API void uhd_tx_metadata_set_has_time_spec(uhd_tx_metadata_handle* md, bool has_time_spec); -SRSLTE_API void uhd_tx_metadata_set_end(uhd_tx_metadata_handle* md, bool is_end_of_burst); -SRSLTE_API void uhd_tx_metadata_add_time_spec(uhd_tx_metadata_handle* md, double frac_secs); - -#endif /* SRSLTE_UHD_C_API_C_ */ diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 8356da955..f7b428bd2 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -339,6 +339,7 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const return false; } + log_h->debug("radio_tx: time=%ld:%f, is_start=%d\n", tx_time.full_secs, tx_time.frac_secs, is_start_of_burst); int ret = srslte_rf_send_timed_multi( &rf_device, radio_buffers, nof_samples, tx_time.full_secs, tx_time.frac_secs, true, is_start_of_burst, false); is_start_of_burst = false; @@ -351,6 +352,7 @@ void radio::tx_end() return; } if (!is_start_of_burst) { + log_h->debug("radio_tx_end: time=%ld:%f\n", end_of_burst_time.full_secs, end_of_burst_time.frac_secs); srslte_rf_send_timed2(&rf_device, zeros, 0, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, true); is_start_of_burst = true; } @@ -424,6 +426,7 @@ void radio::set_rx_srate(const double& srate) if (!is_initialized) { return; } + log_h->debug("Setting RX sample rate=%f\n", srate); srslte_rf_set_rx_srate(&rf_device, srate); } @@ -489,6 +492,7 @@ void radio::set_tx_srate(const double& srate) if (!is_initialized) { return; } + log_h->debug("Setting TX sample rate=%f\n", srate); cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); int nsamples = 0; diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 4dc1f8195..e45cfd9bf 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -483,7 +483,14 @@ void sync::run_camping_state() break; case 0: Warning("SYNC: Out-of-sync detected in PSS/SSS\n"); + + // End transmission burst, avoid Tx Underflow + radio_h->tx_end(); + + // Inform about the out-of-synch to upper layer out_of_sync(); + + // Releases assigned worker worker->release(); // Force decoding MIB, for making sure that the TTI will be right @@ -784,7 +791,6 @@ void sync::set_sampling_rate() srate_mode = SRATE_CAMP; radio_h->set_rx_srate(current_srate); - radio_h->set_tx_srate(current_srate); } else { Error("Error setting sampling rate for cell with %d PRBs\n", cell.nof_prb); }