diff --git a/lib/include/srslte/common/enb_events.h b/lib/include/srslte/common/enb_events.h new file mode 100644 index 000000000..296ab3297 --- /dev/null +++ b/lib/include/srslte/common/enb_events.h @@ -0,0 +1,70 @@ +/** + * + * \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. + * + */ + +#ifndef SRSENB_ENB_EVENTS_H +#define SRSENB_ENB_EVENTS_H + +#include +#include + +namespace srslog { +class log_channel; +} + +namespace srsenb { + +/// This interface logs different kinds of events to the configured channel. By default, if no log channel is configured +/// logging will be disabled. +class event_logger_interface +{ +public: + virtual ~event_logger_interface() = default; + + /// Logs into the underlying log channel the RRC connected event. + virtual void log_rrc_connected(unsigned cause) = 0; + + /// Logs into the underlying log channel the RRC disconnected event. + virtual void log_rrc_disconnect(unsigned reason) = 0; + + /// Logs into the underlying log channel the S1 context create event. + virtual void log_s1_ctx_create(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0; + + /// Logs into the underlying log channel the S1 context delete event. + virtual void log_s1_ctx_delete(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0; + + /// Logs into the underlying log channel the when a sector has been started. + virtual void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) = 0; + + /// Logs into the underlying log channel the when a sector has been stopped. + virtual void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) = 0; +}; + +/// Singleton class to provide global access to the event_logger_interface interface. +class event_logger +{ + event_logger() = default; + +public: + /// Returns the instance of the event logger. + static event_logger_interface& get(); + + /// Uses the specified log channel for event logging. + /// NOTE: This method is not thread safe. + static void configure(srslog::log_channel& c); + +private: + static std::unique_ptr pimpl; +}; + +} // namespace srsenb + +#endif // SRSENB_ENB_EVENTS_H diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 7640beabe..79abeabac 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -284,8 +284,8 @@ typedef enum { SRSLTE_DCI_FORMAT0 = 0, SRSLTE_DCI_FORMAT1, SRSLTE_DCI_FORMAT1A, - SRSLTE_DCI_FORMAT1C, SRSLTE_DCI_FORMAT1B, + SRSLTE_DCI_FORMAT1C, SRSLTE_DCI_FORMAT1D, SRSLTE_DCI_FORMAT2, SRSLTE_DCI_FORMAT2A, diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 38aaf3d10..9ae4a2f91 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES arch_select.cc + enb_events.cc backtrace.c buffer_pool.cc crash_handler.c diff --git a/lib/src/common/enb_events.cc b/lib/src/common/enb_events.cc new file mode 100644 index 000000000..d4398123b --- /dev/null +++ b/lib/src/common/enb_events.cc @@ -0,0 +1,150 @@ +/** + * + * \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 "srslte/common/enb_events.h" +#include "srslte/srslog/context.h" +#include "srslte/srslog/log_channel.h" + +using namespace srsenb; + +namespace { + +/// Null object implementation that is used when no log channel is configured. +class null_event_logger : public event_logger_interface +{ +public: + void log_rrc_connected(unsigned cause) override {} + void log_rrc_disconnect(unsigned reason) override {} + void log_s1_ctx_create(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} + void log_s1_ctx_delete(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} + void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} + void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} +}; + +} // namespace + +namespace { + +/// Common metrics to all events. +DECLARE_METRIC("type", metric_type_tag, std::string, ""); +DECLARE_METRIC("event_name", metric_event_name, std::string, ""); + +/// Context for sector start/stop. +DECLARE_METRIC("pci", metric_pci, uint32_t, ""); +DECLARE_METRIC("cell_identity", metric_cell_identity, uint32_t, ""); +DECLARE_METRIC("sector_id", metric_sector_id, uint32_t, ""); +DECLARE_METRIC_SET("event_data", mset_sector_event, metric_pci, metric_cell_identity, metric_sector_id); +using sector_event_t = srslog::build_context_type; + +/// Context for RRC connect/disconnect. +DECLARE_METRIC("cause", metric_cause, uint32_t, ""); +DECLARE_METRIC_SET("event_data", mset_rrc_event, metric_cause); +using rrc_event_t = srslog::build_context_type; + +/// Context for S1 context create/delete. +DECLARE_METRIC("mme_ue_s1ap_id", metric_ue_mme_id, uint32_t, ""); +DECLARE_METRIC("enb_ue_s1ap_id", metric_ue_enb_id, uint32_t, ""); +DECLARE_METRIC("rnti", metric_rnti, uint16_t, ""); +DECLARE_METRIC_SET("event_data", mset_s1apctx_event, metric_ue_mme_id, metric_ue_enb_id, metric_rnti); +using s1apctx_event_t = srslog::build_context_type; + +/// Logs events into the configured log channel. +class logging_event_logger : public event_logger_interface +{ +public: + explicit logging_event_logger(srslog::log_channel& c) : event_channel(c) {} + + void log_rrc_connected(unsigned cause) override + { + rrc_event_t ctx(""); + + ctx.write("event"); + ctx.write("rrc_connect"); + ctx.get().write(cause); + event_channel(ctx); + } + + void log_rrc_disconnect(unsigned reason) override + { + rrc_event_t ctx(""); + + ctx.write("event"); + ctx.write("rrc_disconnect"); + ctx.get().write(reason); + event_channel(ctx); + } + + void log_s1_ctx_create(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override + { + s1apctx_event_t ctx(""); + + ctx.write("event"); + ctx.write("s1_context_create"); + ctx.get().write(mme_id); + ctx.get().write(enb_id); + ctx.get().write(rnti); + event_channel(ctx); + } + + void log_s1_ctx_delete(uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override + { + s1apctx_event_t ctx(""); + + ctx.write("event"); + ctx.write("s1_context_delete"); + ctx.get().write(mme_id); + ctx.get().write(enb_id); + ctx.get().write(rnti); + event_channel(ctx); + } + + void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override + { + sector_event_t ctx(""); + + ctx.write("event"); + ctx.write("sector_start"); + ctx.get().write(pci); + ctx.get().write(cell_id); + ctx.get().write(cc_idx); + event_channel(ctx); + } + + void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override + { + sector_event_t ctx(""); + + ctx.write("event"); + ctx.write("sector_stop"); + ctx.get().write(pci); + ctx.get().write(cell_id); + ctx.get().write(cc_idx); + event_channel(ctx); + } + +private: + srslog::log_channel& event_channel; +}; + +} // namespace + +std::unique_ptr event_logger::pimpl = std::unique_ptr(new null_event_logger); + +event_logger_interface& event_logger::get() +{ + return *pimpl; +} + +void event_logger::configure(srslog::log_channel& c) +{ + pimpl = std::unique_ptr(new logging_event_logger(c)); +} diff --git a/lib/src/mac/pdu.cc b/lib/src/mac/pdu.cc index 5d23aa988..b31e5ac0e 100644 --- a/lib/src/mac/pdu.cc +++ b/lib/src/mac/pdu.cc @@ -565,13 +565,13 @@ bool sch_subh::is_var_len_ce() uint16_t sch_subh::get_c_rnti() { - return (uint16_t)payload[0] << 8 | payload[1]; + return le16toh((uint16_t)payload[0] << 8 | payload[1]); } uint64_t sch_subh::get_con_res_id() { - return ((uint64_t)payload[5]) | (((uint64_t)payload[4]) << 8) | (((uint64_t)payload[3]) << 16) | - (((uint64_t)payload[2]) << 24) | (((uint64_t)payload[1]) << 32) | (((uint64_t)payload[0]) << 40); + return le64toh(((uint64_t)payload[5]) | (((uint64_t)payload[4]) << 8) | (((uint64_t)payload[3]) << 16) | + (((uint64_t)payload[2]) << 24) | (((uint64_t)payload[1]) << 32) | (((uint64_t)payload[0]) << 40)); } float sch_subh::get_phr() @@ -711,6 +711,7 @@ bool sch_subh::set_bsr(uint32_t buff_size[4], ul_sch_lcid format) bool sch_subh::set_c_rnti(uint16_t crnti) { + crnti = htole32(crnti); if (((sch_pdu*)parent)->has_space_ce(2)) { w_payload_ce[0] = (uint8_t)((crnti & 0xff00) >> 8); w_payload_ce[1] = (uint8_t)((crnti & 0x00ff)); @@ -724,6 +725,7 @@ bool sch_subh::set_c_rnti(uint16_t crnti) } bool sch_subh::set_con_res_id(uint64_t con_res_id) { + con_res_id = htole64(con_res_id); if (((sch_pdu*)parent)->has_space_ce(6)) { w_payload_ce[0] = (uint8_t)((con_res_id & 0xff0000000000) >> 40); w_payload_ce[1] = (uint8_t)((con_res_id & 0x00ff00000000) >> 32); diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index e767568a6..c6098856e 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -294,6 +294,10 @@ enable = false #metrics_period_secs = 1 #metrics_csv_enable = false #metrics_csv_filename = /tmp/enb_metrics.csv +#report_json_enable = true +#report_json_filename = /tmp/enb_report.json +#alarms_log_enable = true +#alarms_filename = /tmp/enb_alarms.log #pregenerate_signals = false #tx_amplitude = 0.6 #rrc_inactivity_timer = 30000 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index abc8e0978..ec530171a 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -29,6 +29,7 @@ #include "srsenb/hdr/phy/enb_phy_base.h" #include "srsenb/hdr/stack/enb_stack_base.h" +#include "srsenb/hdr/stack/rrc/rrc_config.h" #include "srslte/common/bcd_helpers.h" #include "srslte/common/buffer_pool.h" @@ -81,6 +82,10 @@ struct general_args_t { float metrics_period_secs; bool metrics_csv_enable; std::string metrics_csv_filename; + bool report_json_enable; + std::string report_json_filename; + bool alarms_log_enable; + std::string alarms_filename; bool print_buffer_state; std::string eia_pref_list; std::string eea_pref_list; @@ -129,11 +134,6 @@ private: int parse_args(const all_args_t& args_, rrc_cfg_t& rrc_cfg); - // eNB components - std::unique_ptr stack; - std::unique_ptr radio; - std::unique_ptr phy; - srslte::logger* logger = nullptr; srslte::log_ref log; // Own logger for eNB @@ -145,6 +145,12 @@ private: bool started = false; phy_cfg_t phy_cfg = {}; + rrc_cfg_t rrc_cfg = {}; + + // eNB components + std::unique_ptr stack = nullptr; + std::unique_ptr radio = nullptr; + std::unique_ptr phy = nullptr; srslte::LOG_LEVEL_ENUM level(std::string l); diff --git a/srsenb/hdr/metrics_json.h b/srsenb/hdr/metrics_json.h new file mode 100644 index 000000000..20efe6d7f --- /dev/null +++ b/srsenb/hdr/metrics_json.h @@ -0,0 +1,42 @@ +/** + * + * \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. + * + */ + +/****************************************************************************** + * File: metrics_json.h + * Description: Metrics class printing to a json file. + *****************************************************************************/ + +#ifndef SRSENB_METRICS_JSON_H +#define SRSENB_METRICS_JSON_H + +#include "srslte/interfaces/enb_metrics_interface.h" +#include "srslte/srslog/log_channel.h" + +namespace srsenb { + +class metrics_json : public srslte::metrics_listener +{ +public: + metrics_json(srslog::log_channel& c) : log_c(c) {} + + void set_metrics(const enb_metrics_t& m, const uint32_t period_usec) override; + void set_handle(enb_metrics_interface* enb_); + void stop() override {} + +private: + srslog::log_channel& log_c; + enb_metrics_interface* enb; +}; + +} // namespace srsenb + +#endif // SRSENB_METRICS_JSON_H diff --git a/srsenb/hdr/phy/phy_ue_db.h b/srsenb/hdr/phy/phy_ue_db.h index 33753919b..02ee79a43 100644 --- a/srsenb/hdr/phy/phy_ue_db.h +++ b/srsenb/hdr/phy/phy_ue_db.h @@ -283,6 +283,14 @@ public: */ bool is_pcell(uint16_t rnti, uint32_t enb_cc_idx) const; + /** + * Asserts a given eNb cell is part of the given RNTI + * @param rnti identifier of the UE + * @param enb_cc_idx eNb cell/carrier index + * @return It returns true if the cell is part of the UE, othwerwise it returns false + */ + bool ue_has_cell(uint16_t rnti, uint32_t enb_cc_idx) const; + /** * Get the current down-link physical layer configuration for an RNTI and an eNb cell/carrier * @@ -330,7 +338,7 @@ public: * @param dci carries the Transport Block and required scheduling information * */ - void set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_dci_dl_t& dci); + bool set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_dci_dl_t& dci); /** * Fills the Uplink Control Information (UCI) configuration and returns true/false idicating if UCI bits are required. @@ -339,14 +347,14 @@ public: * @param rnti is the UE identifier * @param aperiodic_cqi_request indicates if aperiodic CQI was requested * @param uci_cfg brings the UCI configuration - * @return true if UCI decoding is required and false otherwise + * @return 1 if UCI decoding is required, 0 if not, -1 if error */ - bool fill_uci_cfg(uint32_t tti, - uint32_t enb_cc_idx, - uint16_t rnti, - bool aperiodic_cqi_request, - bool is_pusch_available, - srslte_uci_cfg_t& uci_cfg); + int fill_uci_cfg(uint32_t tti, + uint32_t enb_cc_idx, + uint16_t rnti, + bool aperiodic_cqi_request, + bool is_pusch_available, + srslte_uci_cfg_t& uci_cfg); /** * Sends the decoded Uplink Control Information by PUCCH or PUSCH to MAC diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 71883c76b..955067692 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -23,7 +23,7 @@ endif (RPATH) add_library(enb_cfg_parser STATIC parser.cc enb_cfg_parser.cc) target_link_libraries(enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) -add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc) +add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_upper srsenb_mac srsenb_rrc srslog) set(SRSLTE_SOURCES srslte_common srslte_mac srslte_phy srslte_upper srslte_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog) diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index 54523c8d8..23ecca411 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -14,6 +14,7 @@ #include "srsenb/hdr/stack/enb_stack_lte.h" #include "srsenb/src/enb_cfg_parser.h" #include "srslte/build_info.h" +#include "srslte/common/enb_events.h" #include "srslte/radio/radio_null.h" #ifdef HAVE_5GNR #include "srsenb/hdr/phy/vnf_phy_nr.h" @@ -46,7 +47,6 @@ int enb::init(const all_args_t& args_, srslte::logger* logger_) log->info("%s", get_build_string().c_str()); // Validate arguments - rrc_cfg_t rrc_cfg = {}; if (parse_args(args_, rrc_cfg)) { srslte::console("Error processing arguments.\n"); return SRSLTE_ERROR; @@ -145,6 +145,11 @@ int enb::init(const all_args_t& args_, srslte::logger* logger_) started = true; // set to true in any case to allow stopping the eNB if an error happened + // Now that everything is setup, log sector start events. + for (unsigned i = 0, e = rrc_cfg.cell_list.size(); i != e; ++i) { + event_logger::get().log_sector_start(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id); + } + if (ret == SRSLTE_SUCCESS) { srslte::console("\n==== eNodeB started ===\n"); srslte::console("Type to view trace\n"); @@ -152,6 +157,7 @@ int enb::init(const all_args_t& args_, srslte::logger* logger_) // if any of the layers failed to start, make sure the rest is stopped in a controlled manner stop(); } + return ret; } @@ -171,15 +177,20 @@ void enb::stop() radio->stop(); } + // Now that everything is teared down, log sector stop events. + for (unsigned i = 0, e = rrc_cfg.cell_list.size(); i != e; ++i) { + event_logger::get().log_sector_stop(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id); + } + started = false; } } -int enb::parse_args(const all_args_t& args_, rrc_cfg_t& rrc_cfg) +int enb::parse_args(const all_args_t& args_, rrc_cfg_t& _rrc_cfg) { // set member variable args = args_; - return enb_conf_sections::parse_cfg_files(&args, &rrc_cfg, &phy_cfg); + return enb_conf_sections::parse_cfg_files(&args, &_rrc_cfg, &phy_cfg); } void enb::start_plot() diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index ddc491863..e0f48b236 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -32,7 +32,9 @@ #include "srsenb/hdr/enb.h" #include "srsenb/hdr/metrics_csv.h" +#include "srsenb/hdr/metrics_json.h" #include "srsenb/hdr/metrics_stdout.h" +#include "srslte/common/enb_events.h" using namespace std; using namespace srsenb; @@ -191,6 +193,10 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.equalizer_mode", bpo::value(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode") ("expert.estimator_fil_w", bpo::value(&args->phy.estimator_fil_w)->default_value(0.1), "Chooses the coefficients for the 3-tap channel estimator centered filter.") ("expert.lte_sample_rates", bpo::value(&use_standard_lte_rates)->default_value(false), "Whether to use default LTE sample rates instead of shorter variants.") + ("expert.report_json_enable", bpo::value(&args->general.report_json_enable)->default_value(true), "Write eNB report to JSON file") + ("expert.report_json_filename", bpo::value(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename") + ("expert.alarms_log_enable", bpo::value(&args->general.alarms_log_enable)->default_value(true), "Log alarms") + ("expert.alarms_filename", bpo::value(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms filename") ("expert.rrc_inactivity_timer", bpo::value(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.") ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") @@ -478,6 +484,11 @@ int main(int argc, char* argv[]) } srslte::srslog_wrapper log_wrapper(*chan); + // Alarms log channel creation. + srslog::sink& alarm_sink = srslog::fetch_file_sink(args.general.alarms_filename); + srslog::log_channel& alarms_channel = srslog::fetch_log_channel("alarms", alarm_sink, {"ALRM", '\0', false}); + alarms_channel.set_enabled(args.general.alarms_log_enable); + // Start the log backend. srslog::init(); @@ -487,6 +498,17 @@ int main(int argc, char* argv[]) srslte::check_scaling_governor(args.rf.device_name); + // Set up the JSON log channel used by metrics and events. + srslog::sink& json_sink = + srslog::fetch_file_sink(args.general.report_json_filename, 0, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(args.general.report_json_enable); + + // Configure the event logger just before starting the eNB class. + if (args.general.report_json_enable) { + event_logger::configure(json_channel); + } + // Create eNB unique_ptr enb{new srsenb::enb}; if (enb->init(args, &log_wrapper) != SRSLTE_SUCCESS) { @@ -505,6 +527,12 @@ int main(int argc, char* argv[]) metrics_file.set_handle(enb.get()); } + srsenb::metrics_json json_metrics(json_channel); + if (args.general.report_json_enable) { + metricshub.add_listener(&json_metrics); + json_metrics.set_handle(enb.get()); + } + // create input thread std::thread input(&input_loop, &metrics_screen, (enb_command_interface*)enb.get()); diff --git a/srsenb/src/metrics_json.cc b/srsenb/src/metrics_json.cc new file mode 100644 index 000000000..94cc09515 --- /dev/null +++ b/srsenb/src/metrics_json.cc @@ -0,0 +1,166 @@ +/** + * + * \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 "srsenb/hdr/metrics_json.h" +#include "srslte/srslog/context.h" + +using namespace srsenb; + +void metrics_json::set_handle(enb_metrics_interface* enb_) +{ + enb = enb_; +} + +namespace { + +/// Bearer container metrics. +DECLARE_METRIC("bearer_id", metric_bearer_id, uint32_t, ""); +DECLARE_METRIC("qci", metric_qci, uint32_t, ""); +DECLARE_METRIC_SET("bearer_container", mset_bearer_container, metric_bearer_id, metric_qci); + +/// UE container metrics. +DECLARE_METRIC("ue_rnti", metric_ue_rnti, uint32_t, ""); +DECLARE_METRIC("dl_cqi", metric_dl_cqi, float, ""); +DECLARE_METRIC("dl_mcs", metric_dl_mcs, float, ""); +DECLARE_METRIC("dl_bitrate", metric_dl_bitrate, float, ""); +DECLARE_METRIC("dl_bler", metric_dl_bler, float, ""); +DECLARE_METRIC("ul_snr", metric_ul_snr, float, ""); +DECLARE_METRIC("ul_mcs", metric_ul_mcs, float, ""); +DECLARE_METRIC("ul_bitrate", metric_ul_bitrate, float, ""); +DECLARE_METRIC("ul_bler", metric_ul_bler, float, ""); +DECLARE_METRIC("ul_phr", metric_ul_phr, float, ""); +DECLARE_METRIC("bsr", metric_bsr, int, ""); +DECLARE_METRIC_LIST("bearer_list", mlist_bearers, std::vector); +DECLARE_METRIC_SET("ue_container", + mset_ue_container, + metric_ue_rnti, + metric_dl_cqi, + metric_dl_mcs, + metric_dl_bitrate, + metric_dl_bler, + metric_ul_snr, + metric_ul_mcs, + metric_ul_bitrate, + metric_ul_bler, + metric_ul_phr, + metric_bsr, + mlist_bearers); + +/// Sector container metrics. +DECLARE_METRIC("sector_id", metric_sector_id, uint32_t, ""); +DECLARE_METRIC("sector_rach", metric_sector_rach, uint32_t, ""); +DECLARE_METRIC_LIST("ue_list", mlist_ues, std::vector); +DECLARE_METRIC_SET("sector_container", mset_sector_container, metric_sector_id, metric_sector_rach, mlist_ues); + +/// Metrics root object. +DECLARE_METRIC("type", metric_type_tag, std::string, ""); +DECLARE_METRIC_LIST("sector_list", mlist_sector, std::vector); + +/// Metrics context. +using metric_context_t = srslog::build_context_type; + +} // namespace + +/// Fill the metrics for the i'th UE in the enb metrics struct. +static void fill_ue_metrics(mset_ue_container& ue, const enb_metrics_t& m, unsigned i) +{ + ue.write(m.stack.mac.ues[i].rnti); + ue.write(std::max(0.1f, m.stack.mac.ues[i].dl_cqi)); + if (!std::isnan(m.phy[i].dl.mcs)) { + ue.write(std::max(0.1f, m.phy[i].dl.mcs)); + } + if (m.stack.mac.ues[i].tx_brate > 0) { + ue.write( + std::max(0.1f, (float)m.stack.mac.ues[i].tx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f))); + } + if (m.stack.mac.ues[i].tx_pkts > 0 && m.stack.mac.ues[i].tx_errors > 0) { + ue.write(std::max(0.1f, (float)100 * m.stack.mac.ues[i].tx_errors / m.stack.mac.ues[i].tx_pkts)); + } + if (!std::isnan(m.phy[i].ul.sinr)) { + ue.write(std::max(0.1f, m.phy[i].ul.sinr)); + } + if (!std::isnan(m.phy[i].ul.mcs)) { + ue.write(std::max(0.1f, m.phy[i].ul.mcs)); + } + if (m.stack.mac.ues[i].rx_brate > 0) { + ue.write( + std::max(0.1f, (float)m.stack.mac.ues[i].rx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f))); + } + if (m.stack.mac.ues[i].rx_pkts > 0 && m.stack.mac.ues[i].rx_errors > 0) { + ue.write(std::max(0.1f, (float)100 * m.stack.mac.ues[i].rx_errors / m.stack.mac.ues[i].rx_pkts)); + } + ue.write(m.stack.mac.ues[i].phr); + ue.write(m.stack.mac.ues[i].ul_buffer); + + // For each data bearer of this UE... + auto& bearer_list = ue.get(); + for (const auto& drb : m.stack.rrc.ues[i].drb_qci_map) { + bearer_list.emplace_back(); + auto& bearer_container = bearer_list.back(); + bearer_container.write(drb.first); + bearer_container.write(drb.second); + } +} + +/// Returns false if the input index is out of bounds in the metrics struct. +static bool has_valid_metric_ranges(const enb_metrics_t& m, unsigned index) +{ + if (index >= m.phy.size()) { + return false; + } + if (index >= m.stack.mac.ues.size()) { + return false; + } + + return true; +} + +void metrics_json::set_metrics(const enb_metrics_t& m, const uint32_t period_usec) +{ + if (!enb) { + return; + } + if (m.stack.mac.cc_rach_counter.empty()) { + return; + } + + metric_context_t ctx("JSON Metrics"); + + // Fill root object. + ctx.write("metrics"); + auto& sector_list = ctx.get(); + sector_list.resize(m.stack.mac.cc_rach_counter.size()); + + // For each sector... + for (unsigned cc_idx = 0, e = sector_list.size(); cc_idx != e; ++cc_idx) { + auto& sector = sector_list[cc_idx]; + sector.write(cc_idx); + sector.write(m.stack.mac.cc_rach_counter[cc_idx]); + + // For each UE in this sector... + for (unsigned i = 0; i != m.stack.rrc.ues.size(); ++i) { + if (!has_valid_metric_ranges(m, i)) { + continue; + } + + // Only record UEs that belong to this sector. + if (m.stack.mac.ues[i].cc_idx != cc_idx) { + continue; + } + sector.get().emplace_back(); + fill_ue_metrics(sector.get().back(), m, i); + } + } + + // Log the context. + log_c(ctx); +} diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index 166f6fd31..5f472c989 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -390,6 +390,11 @@ void phy_ue_db::activate_deactivate_scell(uint16_t rnti, uint32_t ue_cc_idx, boo cell_info.state = (activate) ? cell_state_secondary_active : cell_state_secondary_inactive; } +bool phy_ue_db::ue_has_cell(uint16_t rnti, uint32_t enb_cc_idx) const +{ + return _assert_enb_cc(rnti, enb_cc_idx) == SRSLTE_SUCCESS; +} + bool phy_ue_db::is_pcell(uint16_t rnti, uint32_t enb_cc_idx) const { std::lock_guard lock(mutex); @@ -440,16 +445,16 @@ srslte_dci_cfg_t phy_ue_db::get_dci_ul_config(uint16_t rnti, uint32_t enb_cc_idx return _get_rnti_config(rnti, enb_cc_idx).dl_cfg.dci; } -void phy_ue_db::set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_dci_dl_t& dci) +bool phy_ue_db::set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_dci_dl_t& dci) { std::lock_guard lock(mutex); // Assert rnti and cell exits and it is active if (_assert_active_enb_cc(dci.rnti, enb_cc_idx) != SRSLTE_SUCCESS) { - return; + return false; } - common_ue& ue = ue_db[dci.rnti]; + common_ue& ue = ue_db.at(dci.rnti); uint32_t ue_cc_idx = _get_ue_cc_idx(dci.rnti, enb_cc_idx); srslte_pdsch_ack_cc_t& pdsch_ack_cc = ue.pdsch_ack[tti].cc[ue_cc_idx]; @@ -473,14 +478,15 @@ void phy_ue_db::set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_ pdsch_ack_m.value[tb_idx] = 2; } } + return true; } -bool phy_ue_db::fill_uci_cfg(uint32_t tti, - uint32_t enb_cc_idx, - uint16_t rnti, - bool aperiodic_cqi_request, - bool is_pusch_available, - srslte_uci_cfg_t& uci_cfg) +int phy_ue_db::fill_uci_cfg(uint32_t tti, + uint32_t enb_cc_idx, + uint16_t rnti, + bool aperiodic_cqi_request, + bool is_pusch_available, + srslte_uci_cfg_t& uci_cfg) { std::lock_guard lock(mutex); @@ -489,12 +495,12 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, // Assert Cell List configuration if (_assert_cell_list_cfg() != SRSLTE_SUCCESS) { - return false; + return -1; } // Assert eNb Cell/Carrier for the given RNTI if (_assert_active_enb_cc(rnti, enb_cc_idx) != SRSLTE_SUCCESS) { - return false; + return -1; } // Get the eNb cell/carrier index with lowest serving cell index (ue_cc_idx) that has an available grant. @@ -503,17 +509,17 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, // There is a PUSCH grant available for the provided RNTI in at least one serving cell and this call is for PUCCH if (pusch_grant_available and not is_pusch_available) { - return false; + return 0; } // There is a PUSCH grant and enb_cc_idx with lowest ue_cc_idx with a grant if (pusch_grant_available and uci_enb_cc_id != enb_cc_idx) { - return false; + return 0; } // No PUSCH grant for this TTI and cell and no enb_cc_idx is not the PCell if (not pusch_grant_available and _get_ue_cc_idx(rnti, enb_cc_idx) != 0) { - return false; + return 0; } common_ue& ue = ue_db.at(rnti); @@ -564,7 +570,7 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, uci_required |= (srslte_uci_cfg_total_ack(&uci_cfg) > 0); // Return whether UCI needs to be decoded - return uci_required; + return uci_required ? 1 : 0; } void phy_ue_db::send_uci_data(uint32_t tti, diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 2b9c82044..b95f3c3c7 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -15,6 +15,7 @@ #include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" #include "srslte/asn1/rrc_utils.h" +#include "srslte/common/enb_events.h" #include "srslte/common/int_helpers.h" using namespace asn1::rrc; @@ -305,6 +306,9 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsl parent->s1ap->initial_ue(rnti, s1ap_cause, std::move(pdu)); } state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + + // Log event. + event_logger::get().log_rrc_connected(static_cast(s1ap_cause.value)); } void rrc::ue::send_connection_reject() @@ -642,6 +646,9 @@ void rrc::ue::send_connection_release() } send_dl_dcch(&dl_dcch_msg); + + // Log rrc release event. + event_logger::get().log_rrc_disconnect(static_cast(rel_ies.release_cause)); } /* diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index eb655e338..1ee11e5ae 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -14,6 +14,7 @@ #include "srsenb/hdr/stack/upper/common_enb.h" #include "srslte/adt/scope_exit.h" #include "srslte/common/bcd_helpers.h" +#include "srslte/common/enb_events.h" #include "srslte/common/int_helpers.h" #include "srslte/common/logmap.h" @@ -1115,6 +1116,9 @@ bool s1ap::ue::send_uectxtreleasecomplete() container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + // Log event. + event_logger::get().log_s1_ctx_delete(ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); + return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); } @@ -1146,6 +1150,9 @@ bool s1ap::ue::send_initial_ctxt_setup_response(const asn1::s1ap::init_context_s } } + // Log event. + event_logger::get().log_s1_ctx_create(ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); + return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); } diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index 621b040d0..402102c9c 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -1288,7 +1288,7 @@ public: radio = unique_dummy_radio_t( new dummy_radio(args.nof_enb_cells * args.cell.nof_ports, args.cell.nof_prb, args.log_level)); - /// Create Dummy Stack isntance + /// Create Dummy Stack instance stack = unique_dummy_stack_t(new dummy_stack(phy_cfg, phy_rrc_cfg, args.log_level, args.rnti)); stack->set_active_cell_list(args.ue_cell_list);