/** * * \section COPYRIGHT * * Copyright 2013-2020 Software Radio Systems Limited * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the distribution. * */ #include "srsue/hdr/ue.h" #include "srslte/build_info.h" #include "srslte/common/band_helper.h" #include "srslte/common/string_helpers.h" #include "srslte/radio/radio.h" #include "srslte/radio/radio_null.h" #include "srslte/srslte.h" #include "srsue/hdr/phy/phy.h" #include "srsue/hdr/stack/ue_stack_lte.h" #ifdef HAVE_5GNR #include "srsue/hdr/phy/vnf_phy_nr.h" #include "srsue/hdr/stack/ue_stack_nr.h" #endif #include #include #include using namespace srslte; namespace srsue { ue::ue(srslog::sink& log_sink) : old_logger(nullptr), log_sink(log_sink), logger(srslog::fetch_basic_logger("UE", log_sink, false)) { // print build info std::cout << std::endl << get_build_string() << std::endl << std::endl; } ue::~ue() { stack.reset(); } int ue::init(const all_args_t& args_, srslte::logger* logger_) { int ret = SRSLTE_SUCCESS; old_logger = logger_; // Init UE log logger.set_level(srslog::basic_levels::info); logger.info("%s", get_build_string().c_str()); // Validate arguments if (parse_args(args_)) { srslte::console("Error processing arguments. Please check %s for more details.\n", args_.log.filename.c_str()); return SRSLTE_ERROR; } // Instantiate layers and stack together our UE if (args.stack.type == "lte") { std::unique_ptr lte_stack(new ue_stack_lte(log_sink)); if (!lte_stack) { srslte::console("Error creating LTE stack instance.\n"); return SRSLTE_ERROR; } std::unique_ptr gw_ptr(new gw()); if (!gw_ptr) { srslte::console("Error creating a GW instance.\n"); return SRSLTE_ERROR; } std::unique_ptr lte_phy = std::unique_ptr(new srsue::phy(log_sink)); if (!lte_phy) { srslte::console("Error creating LTE PHY instance.\n"); return SRSLTE_ERROR; } std::unique_ptr lte_radio = std::unique_ptr(new srslte::radio); if (!lte_radio) { srslte::console("Error creating radio multi instance.\n"); return SRSLTE_ERROR; } // init layers if (lte_radio->init(args.rf, lte_phy.get())) { srslte::console("Error initializing radio.\n"); return SRSLTE_ERROR; } // from here onwards do not exit immediately if something goes wrong as sub-layers may already use interfaces if (lte_phy->init(args.phy, lte_stack.get(), lte_radio.get())) { srslte::console("Error initializing PHY.\n"); ret = SRSLTE_ERROR; } #ifdef HAVE_5GNR srsue::phy_args_nr_t phy_args_nr = {}; phy_args_nr.nof_prb = args.phy.nr_nof_prb; phy_args_nr.nof_carriers = args.phy.nof_nr_carriers; phy_args_nr.nof_phy_threads = args.phy.nof_phy_threads; phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; phy_args_nr.log = args.phy.log; if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { srslte::console("Error initializing NR PHY.\n"); ret = SRSLTE_ERROR; } #endif // HAVE_5GNR if (lte_stack->init(args.stack, old_logger, lte_phy.get(), gw_ptr.get())) { srslte::console("Error initializing stack.\n"); ret = SRSLTE_ERROR; } if (gw_ptr->init(args.gw, old_logger, lte_stack.get())) { srslte::console("Error initializing GW.\n"); ret = SRSLTE_ERROR; } // move ownership stack = std::move(lte_stack); gw_inst = std::move(gw_ptr); phy = std::move(lte_phy); radio = std::move(lte_radio); } else if (args.stack.type == "nr") { logger.info("Initializing NR stack"); #ifdef HAVE_5GNR std::unique_ptr nr_stack(new srsue::ue_stack_nr(old_logger)); std::unique_ptr nr_radio(new srslte::radio_null); std::unique_ptr nr_phy(new srsue::vnf_phy_nr); std::unique_ptr gw_ptr(new gw()); // Init layers if (nr_radio->init(args.rf, nullptr)) { srslte::console("Error initializing radio.\n"); return SRSLTE_ERROR; } if (nr_phy->init(args.phy, nr_stack.get())) { srslte::console("Error initializing PHY.\n"); return SRSLTE_ERROR; } if (nr_stack->init(args.stack, nr_phy.get(), gw_ptr.get())) { srslte::console("Error initializing stack.\n"); return SRSLTE_ERROR; } if (gw_ptr->init(args.gw, old_logger, nr_stack.get())) { srslte::console("Error initializing GW.\n"); return SRSLTE_ERROR; } // move ownership stack = std::move(nr_stack); gw_inst = std::move(gw_ptr); phy = std::move(nr_phy); radio = std::move(nr_radio); #else srslte::console("ERROR: 5G NR stack not compiled. Please, activate CMAKE HAVE_5GNR flag.\n"); logger.error("5G NR stack not compiled. Please, activate CMAKE HAVE_5GNR flag."); #endif } else { srslte::console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str()); ret = SRSLTE_ERROR; } if (phy) { srslte::console("Waiting PHY to initialize ... "); phy->wait_initialize(); srslte::console("done!\n"); } return ret; } int ue::parse_args(const all_args_t& args_) { // set member variable args = args_; // carry out basic sanity checks if (args.stack.rrc.mbms_service_id > -1) { if (!args.phy.interpolate_subframe_enabled) { logger.error("interpolate_subframe_enabled = %d, While using MBMS, " "please set interpolate_subframe_enabled to true", args.phy.interpolate_subframe_enabled); return SRSLTE_ERROR; } if (args.phy.nof_phy_threads > 2) { logger.error("nof_phy_threads = %d, While using MBMS, please set " "number of phy threads to 1 or 2", args.phy.nof_phy_threads); return SRSLTE_ERROR; } if ((0 == args.phy.snr_estim_alg.find("refs"))) { logger.error("snr_estim_alg = refs, While using MBMS, please set " "algorithm to pss or empty"); return SRSLTE_ERROR; } } if (args.rf.nof_antennas > SRSLTE_MAX_PORTS) { fprintf(stderr, "Maximum number of antennas exceeded (%d > %d)\n", args.rf.nof_antennas, SRSLTE_MAX_PORTS); return SRSLTE_ERROR; } if (args.rf.nof_carriers > SRSLTE_MAX_CARRIERS) { fprintf(stderr, "Maximum number of carriers exceeded (%d > %d)\n", args.rf.nof_carriers, SRSLTE_MAX_CARRIERS); return SRSLTE_ERROR; } if (args.rf.nof_carriers <= args.phy.nof_nr_carriers) { fprintf(stderr, "Maximum number of carriers enough for NR and LTE (%d <= %d)\n", args.rf.nof_carriers, args.phy.nof_nr_carriers); return SRSLTE_ERROR; } // replicate some RF parameter to make them available to PHY args.phy.nof_lte_carriers = args.rf.nof_carriers - args.phy.nof_nr_carriers; args.phy.nof_rx_ant = args.rf.nof_antennas; args.phy.agc_enable = args.rf.rx_gain < 0.0f; // populate DL EARFCN list if (not args.phy.dl_earfcn.empty()) { // Parse DL-EARFCN list srslte::string_parse_list(args.phy.dl_earfcn, ',', args.phy.dl_earfcn_list); // Populates supported bands args.stack.rrc.nof_supported_bands = 0; for (uint32_t& earfcn : args.phy.dl_earfcn_list) { uint8_t band = srslte_band_get_band(earfcn); // Try to find band, if not appends it if (std::find(args.stack.rrc.supported_bands.begin(), args.stack.rrc.supported_bands.end(), band) == args.stack.rrc.supported_bands.end()) { args.stack.rrc.supported_bands[args.stack.rrc.nof_supported_bands++] = band; } } } else { logger.error("Error: dl_earfcn list is empty"); srslte::console("Error: dl_earfcn list is empty\n"); return SRSLTE_ERROR; } // populate UL EARFCN list if (not args.phy.ul_earfcn.empty()) { std::vector ul_earfcn_list; srslte::string_parse_list(args.phy.ul_earfcn, ',', ul_earfcn_list); // For each parsed UL-EARFCN links it to the corresponding DL-EARFCN args.phy.ul_earfcn_map.clear(); for (size_t i = 0; i < SRSLTE_MIN(ul_earfcn_list.size(), args.phy.dl_earfcn_list.size()); i++) { args.phy.ul_earfcn_map[args.phy.dl_earfcn_list[i]] = ul_earfcn_list[i]; } } srslte_band_helper bands_helper; // populate NR DL ARFCNs if (args.phy.nof_nr_carriers > 0) { if (not args.phy.dl_nr_arfcn.empty()) { // Parse list srslte::string_parse_list(args.phy.dl_nr_arfcn, ',', args.phy.dl_nr_arfcn_list); // Populates supported bands for (uint32_t& arfcn : args.phy.dl_nr_arfcn_list) { std::vector bands = bands_helper.get_bands_nr(arfcn); for (const auto& band : bands) { // make sure we don't add duplicates if (std::find(args.stack.rrc_nr.supported_bands.begin(), args.stack.rrc_nr.supported_bands.end(), band) == args.stack.rrc_nr.supported_bands.end()) { args.stack.rrc_nr.supported_bands.push_back(band); } } } } else { logger.error("Error: dl_nr_arfcn list is empty"); srslte::console("Error: dl_nr_arfcn list is empty\n"); return SRSLTE_ERROR; } } // Set UE category args.stack.rrc.ue_category = (uint32_t)strtoul(args.stack.rrc.ue_category_str.c_str(), nullptr, 10); // Consider Carrier Aggregation support if more than one args.stack.rrc.support_ca = (args.phy.nof_lte_carriers > 1); return SRSLTE_SUCCESS; } void ue::stop() { // tear down UE in reverse order if (stack) { stack->stop(); } if (gw_inst) { gw_inst->stop(); } if (phy) { phy->stop(); } if (radio) { radio->stop(); } } bool ue::switch_on() { return stack->switch_on(); } bool ue::switch_off() { if (gw_inst) { gw_inst->stop(); } return stack->switch_off(); } void ue::start_plot() { phy->start_plot(); } bool ue::get_metrics(ue_metrics_t* m) { bzero(m, sizeof(ue_metrics_t)); phy->get_metrics(&m->phy); radio->get_metrics(&m->rf); stack->get_metrics(&m->stack); gw_inst->get_metrics(m->gw, m->stack.mac[0].nof_tti); return true; } std::string ue::get_build_mode() { return std::string(srslte_get_build_mode()); } std::string ue::get_build_info() { if (std::string(srslte_get_build_info()).find(" ") != std::string::npos) { return std::string(srslte_get_version()); } return std::string(srslte_get_build_info()); } std::string ue::get_build_string() { std::stringstream ss; ss << "Built in " << get_build_mode() << " mode using " << get_build_info() << "."; return ss.str(); } } // namespace srsue