diff --git a/lib/include/srslte/common/metrics_hub.h b/lib/include/srslte/common/metrics_hub.h index 9575347a2..6020271e2 100644 --- a/lib/include/srslte/common/metrics_hub.h +++ b/lib/include/srslte/common/metrics_hub.h @@ -24,7 +24,7 @@ template class metrics_listener { public: - virtual void set_metrics(metrics_t &m) = 0; + virtual void set_metrics(metrics_t &m, float report_period_secs=1.0) = 0; }; template @@ -38,6 +38,7 @@ public: } void stop() { thread_cancel(); + wait_thread_finish(); } void add_listener(metrics_listener *listener) { diff --git a/srsue/hdr/metrics_csv.h b/srsue/hdr/metrics_csv.h new file mode 100644 index 000000000..a897265d2 --- /dev/null +++ b/srsue/hdr/metrics_csv.h @@ -0,0 +1,64 @@ +/** + * + * \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 METRICS_CSV_H +#define METRICS_CSV_H + +#include +#include +#include +#include +#include + +#include "srslte/common/metrics_hub.h" +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_csv : public srslte::metrics_listener +{ +public: + metrics_csv(std::string filename); + + void set_metrics(ue_metrics_t &m, float report_period_secs); + void set_ue_handle(ue_metrics_interface *ue_); + +private: + std::string float_to_string(float f, int digits, bool add_semicolon = true); + + std::ofstream file; + ue_metrics_interface* ue; + uint32_t n_reports; +}; + +} // namespace srsue + +#endif // METRICS_CSV_H diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h index cd3efc1a3..8ae33b24e 100644 --- a/srsue/hdr/metrics_stdout.h +++ b/srsue/hdr/metrics_stdout.h @@ -36,36 +36,28 @@ #include #include +#include "srslte/common/metrics_hub.h" #include "ue_metrics_interface.h" namespace srsue { -class metrics_stdout +class metrics_stdout : public srslte::metrics_listener { public: metrics_stdout(); - bool init(ue_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(ue_metrics_t &m, float report_period_secs); + void set_ue_handle(ue_metrics_interface *ue_); 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); - - ue_metrics_interface *ue_; - bool started; - bool do_print; - pthread_t metrics_thread; - ue_metrics_t metrics; - float metrics_report_period; // seconds - uint8_t n_reports; + bool do_print; + uint8_t n_reports; + ue_metrics_interface* ue; }; } // namespace srsue diff --git a/srsue/hdr/ue_base.h b/srsue/hdr/ue_base.h index b105b2088..411896f70 100644 --- a/srsue/hdr/ue_base.h +++ b/srsue/hdr/ue_base.h @@ -107,6 +107,8 @@ typedef struct { float metrics_period_secs; bool pregenerate_signals; std::string ue_cateogry; + bool metrics_csv_enable; + std::string metrics_csv_filename; }expert_args_t; typedef struct { diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h index 9555574f3..20ee6f031 100644 --- a/srsue/hdr/ue_metrics_interface.h +++ b/srsue/hdr/ue_metrics_interface.h @@ -29,6 +29,7 @@ #include +#include "srslte/common/metrics_hub.h" #include "upper/gw_metrics.h" #include "srslte/upper/rlc_metrics.h" #include "mac/mac_metrics.h" @@ -52,10 +53,11 @@ typedef struct { }ue_metrics_t; // UE interface -class ue_metrics_interface +class ue_metrics_interface : public srslte::metrics_interface { public: virtual bool get_metrics(ue_metrics_t &m) = 0; + virtual bool is_attached() = 0; }; } // namespace srsue diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 5c6000f63..0755a6490 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -31,7 +31,7 @@ if (RPATH) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif (RPATH) -add_executable(srsue main.cc ue_base.cc ue.cc metrics_stdout.cc) +add_executable(srsue main.cc ue_base.cc ue.cc metrics_stdout.cc metrics_csv.cc) target_link_libraries(srsue srsue_mac srsue_phy srsue_upper diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 02394f3e4..569083998 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -38,6 +38,8 @@ #include "ue.h" #include "metrics_stdout.h" +#include "metrics_csv.h" +#include "srslte/common/metrics_hub.h" #include "srslte/version.h" using namespace std; @@ -134,6 +136,14 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { 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 UE metrics to CSV file") + + ("expert.metrics_csv_filename", + bpo::value(&args->expert.metrics_csv_filename)->default_value("/tmp/ue_metrics.csv"), + "Metrics CSV filename") + ("expert.pregenerate_signals", bpo::value(&args->expert.pregenerate_signals)->default_value(false), "Pregenerate uplink signals after attach. Improves CPU performance.") @@ -320,13 +330,13 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { static bool running = true; static bool do_metrics = false; +metrics_stdout metrics_screen; void sig_int_handler(int signo) { running = false; } void *input_loop(void *m) { - metrics_stdout *metrics = (metrics_stdout *) m; char key; while (running) { cin >> key; @@ -337,7 +347,7 @@ void *input_loop(void *m) { } else { cout << "Enter t to restart trace." << endl; } - metrics->toggle_print(do_metrics); + metrics_screen.toggle_print(do_metrics); } } return NULL; @@ -345,6 +355,7 @@ void *input_loop(void *m) { int main(int argc, char *argv[]) { + srslte::metrics_hub metricshub; signal(SIGINT, sig_int_handler); all_args_t args; parse_args(&args, argc, argv); @@ -361,11 +372,18 @@ int main(int argc, char *argv[]) exit(1); } - metrics_stdout metrics; - metrics.init(ue, args.expert.metrics_period_secs); + metricshub.init(ue, args.expert.metrics_period_secs); + metricshub.add_listener(&metrics_screen); + metrics_screen.set_ue_handle(ue); + + metrics_csv metrics_file(args.expert.metrics_csv_filename); + if (args.expert.metrics_csv_enable) { + metricshub.add_listener(&metrics_file); + metrics_file.set_ue_handle(ue); + } pthread_t input; - pthread_create(&input, NULL, &input_loop, &metrics); + pthread_create(&input, NULL, &input_loop, &args); bool plot_started = false; bool signals_pregenerated = false; @@ -383,7 +401,7 @@ int main(int argc, char *argv[]) sleep(1); } pthread_cancel(input); - metrics.stop(); + metricshub.stop(); ue->stop(); ue->cleanup(); cout << "--- exiting ---" << endl; diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc new file mode 100644 index 000000000..18c761263 --- /dev/null +++ b/srsue/src/metrics_csv.cc @@ -0,0 +1,109 @@ +/** + * + * \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 "metrics_csv.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsue{ + +metrics_csv::metrics_csv(std::string filename) + :n_reports(0) + ,ue(NULL) +{ + file.open(filename.c_str()); +} + +void metrics_csv::set_ue_handle(ue_metrics_interface *ue_) +{ + ue = ue_; +} + +void metrics_csv::set_metrics(ue_metrics_t &metrics, float metrics_report_period) +{ + if (file.is_open()) { + if (!ue->is_attached()) { + file << "# disconnected" << endl; + return; + } + + if(n_reports == 0) { + file << "time;rsrp;pl;cfo;dl_mcs;dl_snr;dl_turbo;dl_brate;dl_bler;ul_mcs;ul_buff;ul_brate;ul_bler;rf_o;rf_u;rf_l" << endl; + } + file << (metrics_report_period*n_reports) << ";"; + file << float_to_string(metrics.phy.dl.rsrp, 2); + file << float_to_string(metrics.phy.dl.pathloss, 2); + file << float_to_string(metrics.phy.sync.cfo, 2); + file << float_to_string(metrics.phy.dl.mcs, 2); + file << float_to_string(metrics.phy.dl.sinr, 2); + file << float_to_string(metrics.phy.dl.turbo_iters, 2); + file << float_to_string((float) metrics.mac.rx_brate/metrics_report_period, 2); + if (metrics.mac.rx_pkts > 0) { + file << float_to_string((float) 100*metrics.mac.rx_errors/metrics.mac.rx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + file << float_to_string(metrics.phy.ul.mcs, 2); + file << float_to_string((float) metrics.mac.ul_buffer, 2); + file << float_to_string((float) metrics.mac.tx_brate/metrics_report_period, 2); + if (metrics.mac.tx_pkts > 0) { + file << float_to_string((float) 100*metrics.mac.tx_errors/metrics.mac.tx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + file << float_to_string(metrics.rf.rf_o, 2); + file << float_to_string(metrics.rf.rf_u, 2); + file << float_to_string(metrics.rf.rf_l, 2, false); + file << endl; + + 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 srsue diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 0a86683d2..6828c912f 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -48,29 +48,15 @@ char const * const prefixes[2][9] = }; metrics_stdout::metrics_stdout() - :started(false) - ,do_print(false) + :do_print(false) ,n_reports(10) + ,ue(NULL) { } -bool metrics_stdout::init(ue_metrics_interface *u, float report_period_secs) +void metrics_stdout::set_ue_handle(ue_metrics_interface *ue_) { - ue_ = 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); - } + ue = ue_; } void metrics_stdout::toggle_print(bool b) @@ -78,30 +64,16 @@ void metrics_stdout::toggle_print(bool b) do_print = b; } -void* metrics_stdout::metrics_thread_start(void *m_) -{ - metrics_stdout *m = (metrics_stdout*)m_; - m->metrics_thread_run(); - return NULL; -} -void metrics_stdout::metrics_thread_run() +void metrics_stdout::set_metrics(ue_metrics_t &metrics, float metrics_report_period) { - while(started) - { - usleep(metrics_report_period*1e6); - if(ue_->get_metrics(metrics)) { - print_metrics(); - } else { - print_disconnect(); - } - } -} + if(!do_print || ue == NULL) + return; -void metrics_stdout::print_metrics() -{ - if(!do_print) + if (!ue->is_attached()) { + cout << "--- disconnected ---" << endl; return; + } if(++n_reports > 10) { @@ -138,13 +110,6 @@ void metrics_stdout::print_metrics() } -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/src/ue.cc b/srsue/src/ue.cc index 646b87612..03e4b6546 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -247,7 +247,7 @@ void ue::stop() bool ue::is_attached() { - return (EMM_STATE_REGISTERED == nas.get_state()); + return (RRC_STATE_CONNECTED == rrc.get_state()); } void ue::start_plot() { diff --git a/srsue/test/CMakeLists.txt b/srsue/test/CMakeLists.txt index c9949a7e2..217c369ee 100644 --- a/srsue/test/CMakeLists.txt +++ b/srsue/test/CMakeLists.txt @@ -21,3 +21,7 @@ add_subdirectory(phy) add_subdirectory(mac) add_subdirectory(upper) + +add_executable(metrics_test metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc) +target_link_libraries(metrics_test srslte_phy srslte_common) +add_test(metrics_test metrics_test -o ${CMAKE_CURRENT_BINARY_DIR}/ue_metrics.csv) diff --git a/srsue/test/metrics_test.cc b/srsue/test/metrics_test.cc new file mode 100644 index 000000000..9cd17f824 --- /dev/null +++ b/srsue/test/metrics_test.cc @@ -0,0 +1,122 @@ +/** + * + * \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/. + * + */ + +#include +#include +#include +#include +#include +#include "ue_metrics_interface.h" +#include "srslte/common/metrics_hub.h" +#include "metrics_stdout.h" +#include "metrics_csv.h" + +using namespace srsue; + +namespace srsue { + +char *csv_file_name = NULL; + +// fake classes +class ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t &m) + { + // fill dummy values + bzero(&m, sizeof(ue_metrics_t)); + m.rf.rf_o = 10; + m.phy.dl.rsrp = -10.0; + m.phy.dl.pathloss = 74; + return true; + } + + bool is_attached() + { + return true; + } +}; +} + +void usage(char *prog) { + printf("Usage: %s -o csv_output_file\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + + while ((opt = getopt(argc, argv, "o")) != -1) { + switch(opt) { + case 'o': + csv_file_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!csv_file_name) { + usage(argv[0]); + exit(-1); + } +} + + +int main(int argc, char **argv) +{ + float period = 1.0; + ue_dummy ue; + + if (argc < 3) { + usage(argv[0]); + exit(-1); + } + + parse_args(argc,argv); + + // the default metrics type for stdout output + metrics_stdout metrics_screen; + metrics_screen.set_ue_handle(&ue); + + // the CSV file writer + metrics_csv metrics_file(csv_file_name); + metrics_file.set_ue_handle(&ue); + + // create metrics hub and register metrics for stdout + srslte::metrics_hub metricshub; + metricshub.init(&ue, period); + metricshub.add_listener(&metrics_screen); + metricshub.add_listener(&metrics_file); + + // enable printing + metrics_screen.toggle_print(true); + + std::cout << "Running for 2 seconds .." << std::endl; + usleep(2e6); + + metricshub.stop(); + return 0; +} diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 30b152280..332d00e59 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -130,6 +130,10 @@ enable = false # # pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. # +# metrics_csv_enable: Write UE metrics to CSV file. +# +# metrics_csv_filename: File path to use for CSV metrics. +# ##################################################################### [expert] #ue_category = 4 @@ -149,6 +153,8 @@ enable = false #sss_algorithm = full #estimator_fil_w = 0.1 #pregenerate_signals = false +#metrics_csv_enable = false +#metrics_csv_filename = /tmp/ue_metrics.csv ##################################################################### # Manual RF calibration