From db910cb84bb5b1e98af2d90db06f652546da8618 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 20 Nov 2018 11:59:04 +0100 Subject: [PATCH] add CSV metrics for eNB --- .../srslte/interfaces/enb_metrics_interface.h | 3 +- srsenb/enb.conf.example | 9 +- srsenb/hdr/enb.h | 14 +- srsenb/hdr/metrics_csv.h | 67 ++++++++++ srsenb/hdr/metrics_stdout.h | 23 +--- srsenb/src/CMakeLists.txt | 2 +- srsenb/src/main.cc | 34 ++++- srsenb/src/metrics_csv.cc | 123 ++++++++++++++++++ srsenb/src/metrics_stdout.cc | 92 ++++--------- srsue/hdr/metrics_stdout.h | 2 - srsue/src/metrics_stdout.cc | 1 - 11 files changed, 269 insertions(+), 101 deletions(-) create mode 100644 srsenb/hdr/metrics_csv.h create mode 100644 srsenb/src/metrics_csv.cc diff --git a/lib/include/srslte/interfaces/enb_metrics_interface.h b/lib/include/srslte/interfaces/enb_metrics_interface.h index 4e540ab78..f896296ba 100644 --- a/lib/include/srslte/interfaces/enb_metrics_interface.h +++ b/lib/include/srslte/interfaces/enb_metrics_interface.h @@ -29,6 +29,7 @@ #include +#include "srslte/common/metrics_hub.h" #include "srsenb/hdr/upper/common_enb.h" #include "srsenb/hdr/upper/s1ap_metrics.h" #include "srsenb/hdr/upper/rrc_metrics.h" @@ -56,7 +57,7 @@ typedef struct { }enb_metrics_t; // ENB interface -class enb_metrics_interface +class enb_metrics_interface : public srslte::metrics_interface { public: virtual bool get_metrics(enb_metrics_t &m) = 0; diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 4836fdb4f..b26161e97 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -144,7 +144,9 @@ nof_ctrl_symbols = 3 # pusch_max_its: Maximum number of turbo decoder iterations (Default 4) # pusch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental) # nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) -# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# metrics_period_secs: Sets the period at which metrics are requested from the eNB. +# metrics_csv_enable: Write eNB metrics to CSV file. +# metrics_csv_filename: File path to use for CSV metrics. # pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. # tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) # link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. @@ -159,6 +161,9 @@ nof_ctrl_symbols = 3 #pusch_max_its = 8 # These are half iterations #pusch_8bit_decoder = false #nof_phy_threads = 2 +#metrics_period_secs = 1 +#metrics_csv_enable = false +#metrics_csv_filename = /tmp/enb_metrics.csv #pregenerate_signals = false #tx_amplitude = 0.6 #link_failure_nof_err = 50 @@ -166,4 +171,4 @@ nof_ctrl_symbols = 3 #max_prach_offset_us = 30 #enable_mbsfn = false #m1u_multiaddr = 239.255.0.1 -#m1u_if_addr = 127.0.1.201 \ No newline at end of file +#m1u_if_addr = 127.0.1.201 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 4176fbb82..ee0859625 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -122,12 +122,14 @@ typedef struct { }gui_args_t; typedef struct { - phy_args_t phy; - mac_args_t mac; - uint32_t rrc_inactivity_timer; - float metrics_period_secs; - bool enable_mbsfn; - bool print_buffer_state; + phy_args_t phy; + mac_args_t mac; + uint32_t rrc_inactivity_timer; + float metrics_period_secs; + bool metrics_csv_enable; + std::string metrics_csv_filename; + bool enable_mbsfn; + bool print_buffer_state; std::string m1u_multiaddr; std::string m1u_if_addr; }expert_args_t; diff --git a/srsenb/hdr/metrics_csv.h b/srsenb/hdr/metrics_csv.h new file mode 100644 index 000000000..82d8c065b --- /dev/null +++ b/srsenb/hdr/metrics_csv.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: metrics_csv.h + * Description: Metrics class writing to CSV file. + *****************************************************************************/ + +#ifndef SRSENB_METRICS_CSV_H +#define SRSENB_METRICS_CSV_H + +#include +#include +#include +#include +#include + +#include "srslte/common/metrics_hub.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +class metrics_csv : public srslte::metrics_listener +{ +public: + metrics_csv(std::string filename); + ~metrics_csv(); + + void set_metrics(enb_metrics_t &m, const uint32_t period_usec); + void set_handle(enb_metrics_interface *enb_); + void stop(); + +private: + std::string float_to_string(float f, int digits, bool add_semicolon = true); + + float metrics_report_period; + std::ofstream file; + enb_metrics_interface* enb; + uint32_t n_reports; +}; + +} // namespace srsenb + +#endif // SRSENB_METRICS_CSV_H diff --git a/srsenb/hdr/metrics_stdout.h b/srsenb/hdr/metrics_stdout.h index f5af14e25..307a64a0d 100644 --- a/srsenb/hdr/metrics_stdout.h +++ b/srsenb/hdr/metrics_stdout.h @@ -41,32 +41,23 @@ namespace srsenb { -class metrics_stdout +class metrics_stdout : public srslte::metrics_listener { public: metrics_stdout(); - bool init(enb_metrics_interface *u, float report_period_secs=1.0); - void stop(); void toggle_print(bool b); - static void* metrics_thread_start(void *m); - void metrics_thread_run(); + void set_metrics(enb_metrics_t &m, const uint32_t period_usec); + void set_handle(enb_metrics_interface *enb_); + void stop() {}; private: - void print_metrics(); - void print_disconnect(); std::string float_to_string(float f, int digits); std::string float_to_eng_string(float f, int digits); - std::string int_to_eng_string(int f, int digits); - - enb_metrics_interface *enb_; - bool started; - bool do_print; - pthread_t metrics_thread; - enb_metrics_t metrics; - float metrics_report_period; // seconds - uint8_t n_reports; + bool do_print; + uint8_t n_reports; + enb_metrics_interface* enb; }; } // namespace srsenb diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03a8..8eb14a0fd 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -15,7 +15,7 @@ if (RPATH) endif (RPATH) -add_executable(srsenb main.cc enb.cc parser.cc enb_cfg_parser.cc metrics_stdout.cc) +add_executable(srsenb main.cc enb.cc parser.cc enb_cfg_parser.cc metrics_stdout.cc metrics_csv.cc) target_link_libraries(srsenb srsenb_upper srsenb_mac srsenb_phy diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index c9cecbebd..32ab65ced 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -41,6 +41,7 @@ #include "srsenb/hdr/enb.h" #include "srsenb/hdr/metrics_stdout.h" +#include "srsenb/hdr/metrics_csv.h" using namespace std; using namespace srsenb; @@ -147,8 +148,16 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { /* Expert section */ ("expert.metrics_period_secs", - bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), - "Periodicity for metrics in seconds") + bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), + "Periodicity for metrics in seconds") + + ("expert.metrics_csv_enable", + bpo::value(&args->expert.metrics_csv_enable)->default_value(false), + "Write metrics to CSV file") + + ("expert.metrics_csv_filename", + bpo::value(&args->expert.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), + "Metrics CSV filename") ("expert.pregenerate_signals", bpo::value(&args->expert.phy.pregenerate_signals)->default_value(false), @@ -284,7 +293,6 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; } - // Apply all_level to any unset layers if (vm.count("log.all_level")) { if(!vm.count("log.phy_level")) { @@ -398,7 +406,9 @@ int main(int argc, char *argv[]) signal(SIGINT, sig_int_handler); signal(SIGTERM, sig_int_handler); all_args_t args; - metrics_stdout metrics; + srslte::metrics_hub metricshub; + metrics_stdout metrics_screen; + enb *enb = enb::get_instance(); srslte_debug_handle_crash(argc, argv); @@ -409,10 +419,20 @@ int main(int argc, char *argv[]) if(!enb->init(&args)) { exit(1); } - metrics.init(enb, args.expert.metrics_period_secs); + metricshub.init(enb, args.expert.metrics_period_secs); + metricshub.add_listener(&metrics_screen); + metrics_screen.set_handle(enb); + + srsenb::metrics_csv metrics_file(args.expert.metrics_csv_filename); + if (args.expert.metrics_csv_enable) { + metricshub.add_listener(&metrics_file); + metrics_file.set_handle(enb); + } + + // create input thread pthread_t input; - pthread_create(&input, NULL, &input_loop, &metrics); + pthread_create(&input, NULL, &input_loop, &metrics_screen); bool plot_started = false; bool signals_pregenerated = false; @@ -434,7 +454,7 @@ int main(int argc, char *argv[]) usleep(10000); } pthread_cancel(input); - metrics.stop(); + metricshub.stop(); enb->stop(); enb->cleanup(); cout << "--- exiting ---" << endl; diff --git a/srsenb/src/metrics_csv.cc b/srsenb/src/metrics_csv.cc new file mode 100644 index 000000000..bab96431f --- /dev/null +++ b/srsenb/src/metrics_csv.cc @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2015 The srsUE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/metrics_csv.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsenb{ + +metrics_csv::metrics_csv(std::string filename) + :n_reports(0) + ,metrics_report_period(1.0) + ,enb(NULL) +{ + file.open(filename.c_str(), std::ios_base::out); +} + +metrics_csv::~metrics_csv() +{ + stop(); +} + +void metrics_csv::set_handle(enb_metrics_interface *enb_) +{ + enb = enb_; +} + +void metrics_csv::stop() +{ + if (file.is_open()) { + file << "#eof\n"; + file.flush(); + file.close(); + } +} + +void metrics_csv::set_metrics(enb_metrics_t &metrics, const uint32_t period_usec) +{ + if (file.is_open() && enb != NULL) { + if(n_reports == 0) { + file << "time;nof_ue;dl_brate;ul_brate\n"; + } + + // Time + file << (metrics_report_period*n_reports) << ";"; + + // UEs + file << (metrics.rrc.n_ues) << ";"; + + // Sum up rates for all UEs + float dl_rate_sum = 0.0, ul_rate_sum = 0.0; + for (int i = 0; i < metrics.rrc.n_ues; i++) { + dl_rate_sum += metrics.mac[i].tx_brate; + ul_rate_sum += metrics.mac[i].rx_brate; + } + + // DL rate + if (dl_rate_sum > 0) { + file << float_to_string(SRSLTE_MAX(0.1,(float)dl_rate_sum/period_usec*1e6), 2); + } else { + file << float_to_string(0, 2); + } + + // UL rate + if (ul_rate_sum > 0) { + file << float_to_string(SRSLTE_MAX(0.1,(float)ul_rate_sum/period_usec*1e6), 2, false); + } else { + file << float_to_string(0, 2, false); + } + + file << "\n"; + + n_reports++; + } else { + std::cout << "Error, couldn't write CSV file." << std::endl; + } +} + +std::string metrics_csv::float_to_string(float f, int digits, bool add_semicolon) +{ + std::ostringstream os; + const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON; + os << std::fixed << std::setprecision(precision) << f; + if (add_semicolon) + os << ';'; + return os.str(); +} + +} // namespace srsenb diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc index 826a03c92..786ed1f85 100644 --- a/srsenb/src/metrics_stdout.cc +++ b/srsenb/src/metrics_stdout.cc @@ -41,38 +41,23 @@ using namespace std; namespace srsenb{ -#define MAX(a,b) (a>b?a:b) - char const * const prefixes[2][9] = { { "", "m", "u", "n", "p", "f", "a", "z", "y", }, { "", "k", "M", "G", "T", "P", "E", "Z", "Y", }, }; -metrics_stdout::metrics_stdout() : started(false) ,do_print(false), metrics_report_period(0.0f),n_reports(10) + +metrics_stdout::metrics_stdout() + :do_print(false) + ,n_reports(10) + ,enb(NULL) { - enb_ = NULL; - bzero(&metrics_thread, sizeof(metrics_thread)); - bzero(&metrics, sizeof(metrics)); } -bool metrics_stdout::init(enb_metrics_interface *u, float report_period_secs) +void metrics_stdout::set_handle(enb_metrics_interface *enb_) { - enb_ = u; - metrics_report_period = report_period_secs; - - started = true; - pthread_create(&metrics_thread, NULL, &metrics_thread_start, this); - return true; -} - -void metrics_stdout::stop() -{ - if(started) - { - started = false; - pthread_join(metrics_thread, NULL); - } + enb = enb_; } void metrics_stdout::toggle_print(bool b) @@ -80,34 +65,18 @@ void metrics_stdout::toggle_print(bool b) do_print = b; } -void* metrics_stdout::metrics_thread_start(void *m_) +void metrics_stdout::set_metrics(enb_metrics_t &metrics, const uint32_t period_usec) { - metrics_stdout *m = (metrics_stdout*)m_; - m->metrics_thread_run(); - return NULL; -} - -void metrics_stdout::metrics_thread_run() -{ - while(started) - { - usleep(metrics_report_period*1e6); - if(enb_->get_metrics(metrics)) { - if (metrics.rrc.n_ues > 0) { - print_metrics(); - } - } else { - print_disconnect(); - } + if (!do_print || enb == NULL) { + return; } -} -void metrics_stdout::print_metrics() -{ std::ios::fmtflags f(cout.flags()); // For avoiding Coverity defect: Not restoring ostream format - if(!do_print) + if (metrics.rrc.n_ues == 0) { + cout << "--- disconnected ---" << endl; return; + } if(++n_reports > 10) { @@ -117,7 +86,7 @@ void metrics_stdout::print_metrics() cout << "rnti cqi ri mcs brate bler snr phr mcs brate bler bsr" << endl; } if (metrics.rrc.n_ues > 0) { - + for (int i=0;i metrics.mac[i].tx_pkts) { printf("tx caution errors %d > %d\n", metrics.mac[i].tx_errors, metrics.mac[i].tx_pkts); @@ -125,43 +94,43 @@ void metrics_stdout::print_metrics() if (metrics.mac[i].rx_errors > metrics.mac[i].rx_pkts) { printf("rx caution errors %d > %d\n", metrics.mac[i].rx_errors, metrics.mac[i].rx_pkts); } - + cout << std::hex << metrics.mac[i].rnti << " "; - cout << float_to_string(MAX(0.1,metrics.mac[i].dl_cqi), 2); + cout << float_to_string(SRSLTE_MAX(0.1,metrics.mac[i].dl_cqi), 2); cout << float_to_string(metrics.mac[i].dl_ri, 1); if(not isnan(metrics.phy[i].dl.mcs)) { - cout << float_to_string(MAX(0.1,metrics.phy[i].dl.mcs), 2); + cout << float_to_string(SRSLTE_MAX(0.1,metrics.phy[i].dl.mcs), 2); } else { cout << float_to_string(0,2); } - if (metrics.mac[i].tx_brate > 0 && metrics_report_period) { - cout << float_to_eng_string(MAX(0.1,(float) metrics.mac[i].tx_brate/metrics_report_period), 2); + if (metrics.mac[i].tx_brate > 0) { + cout << float_to_eng_string(SRSLTE_MAX(0.1,(float) metrics.mac[i].tx_brate/period_usec*1e6), 2); } else { cout << float_to_string(0, 2) << ""; } if (metrics.mac[i].tx_pkts > 0 && metrics.mac[i].tx_errors) { - cout << float_to_string(MAX(0.1,(float) 100*metrics.mac[i].tx_errors/metrics.mac[i].tx_pkts), 1) << "%"; + cout << float_to_string(SRSLTE_MAX(0.1,(float) 100*metrics.mac[i].tx_errors/metrics.mac[i].tx_pkts), 1) << "%"; } else { cout << float_to_string(0, 1) << "%"; } if(not isnan(metrics.phy[i].ul.sinr)) { - cout << float_to_string(MAX(0.1,metrics.phy[i].ul.sinr), 2); + cout << float_to_string(SRSLTE_MAX(0.1,metrics.phy[i].ul.sinr), 2); } else { cout << float_to_string(0,2); } cout << float_to_string(metrics.mac[i].phr, 2); if(not isnan(metrics.phy[i].ul.mcs)) { - cout << float_to_string(MAX(0.1,metrics.phy[i].ul.mcs), 2); + cout << float_to_string(SRSLTE_MAX(0.1,metrics.phy[i].ul.mcs), 2); } else { cout << float_to_string(0,2); } - if (metrics.mac[i].rx_brate > 0 && metrics_report_period) { - cout << float_to_eng_string(MAX(0.1,(float) metrics.mac[i].rx_brate/metrics_report_period), 2); - } else { + if (metrics.mac[i].rx_brate > 0) { + cout << float_to_eng_string(SRSLTE_MAX(0.1,(float) metrics.mac[i].rx_brate/period_usec*1e6), 2); + } else { cout << float_to_string(0, 2) << ""; } if (metrics.mac[i].rx_pkts > 0 && metrics.mac[i].rx_errors > 0) { - cout << float_to_string(MAX(0.1,(float) 100*metrics.mac[i].rx_errors/metrics.mac[i].rx_pkts), 1) << "%"; + cout << float_to_string(SRSLTE_MAX(0.1,(float) 100*metrics.mac[i].rx_errors/metrics.mac[i].rx_pkts), 1) << "%"; } else { cout << float_to_string(0, 1) << "%"; } @@ -169,7 +138,7 @@ void metrics_stdout::print_metrics() cout << endl; } } else { - cout << "--- No users ---" << endl; + cout << "--- No users ---" << endl; } if(metrics.rf.rf_error) { printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); @@ -178,13 +147,6 @@ void metrics_stdout::print_metrics() cout.flags(f); // For avoiding Coverity defect: Not restoring ostream format } -void metrics_stdout::print_disconnect() -{ - if(do_print) { - cout << "--- disconnected ---" << endl; - } -} - std::string metrics_stdout::float_to_string(float f, int digits) { std::ostringstream os; diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h index 2b0df9ca3..a7bf2ddb8 100644 --- a/srsue/hdr/metrics_stdout.h +++ b/srsue/hdr/metrics_stdout.h @@ -46,7 +46,6 @@ class metrics_stdout : public srslte::metrics_listener public: metrics_stdout(); - void set_periodicity(float metrics_report_period_sec); void toggle_print(bool b); void set_metrics(ue_metrics_t &m, const uint32_t period_usec); void set_ue_handle(ue_metrics_interface *ue_); @@ -55,7 +54,6 @@ public: private: std::string float_to_string(float f, int digits); std::string float_to_eng_string(float f, int digits); - std::string int_to_eng_string(int f, int digits); bool do_print; uint8_t n_reports; diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 0db903cbc..6f0cf04a0 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -107,7 +107,6 @@ void metrics_stdout::set_metrics(ue_metrics_t &metrics, const uint32_t period_us if(metrics.rf.rf_error) { printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); } - } std::string metrics_stdout::float_to_string(float f, int digits)