diff --git a/srsue/hdr/metrics_csv.h b/srsue/hdr/metrics_csv.h index a59de9ab8..a42c011c3 100644 --- a/srsue/hdr/metrics_csv.h +++ b/srsue/hdr/metrics_csv.h @@ -41,21 +41,23 @@ namespace srsue { class metrics_csv : public srslte::metrics_listener { public: - metrics_csv(std::string filename); + metrics_csv(std::string filename, bool append); ~metrics_csv(); void set_metrics(const ue_metrics_t& m, const uint32_t period_usec); void set_ue_handle(ue_metrics_interface* ue_); + void set_flush_period(const uint32_t flush_period_sec); void stop(); private: std::string float_to_string(float f, int digits, bool add_semicolon = true); - float metrics_report_period; + float metrics_report_period = 0.0f; std::ofstream file; - ue_metrics_interface* ue; - uint32_t n_reports; + ue_metrics_interface* ue = nullptr; + uint32_t n_reports = 0; pthread_mutex_t mutex; + uint32_t flush_period_sec = 0; }; } // namespace srsue diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index ae1995fcd..a4afd8d99 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -68,6 +68,8 @@ typedef struct { typedef struct { float metrics_period_secs; bool metrics_csv_enable; + bool metrics_csv_append; + int metrics_csv_flush_period_sec; std::string metrics_csv_filename; } general_args_t; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 2bb06fe31..d62ce71cc 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -397,6 +397,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->general.metrics_csv_filename)->default_value("/tmp/ue_metrics.csv"), "Metrics CSV filename") + ("general.metrics_csv_append", + bpo::value(&args->general.metrics_csv_append)->default_value(false), + "Set to true to append new output to existing CSV file") + + ("general.metrics_csv_flush_period_sec", + bpo::value(&args->general.metrics_csv_flush_period_sec)->default_value(-1), + "Periodicity in ms to flush CSV file to disk (-1 for auto)") + ("stack.have_tti_time_stats", bpo::value(&args->stack.have_tti_time_stats)->default_value(true), "Calculate TTI execution statistics"); @@ -630,10 +638,13 @@ int main(int argc, char* argv[]) metricshub.add_listener(metrics_screen); metrics_screen->set_ue_handle(&ue); - metrics_csv metrics_file(args.general.metrics_csv_filename); + metrics_csv metrics_file(args.general.metrics_csv_filename, args.general.metrics_csv_append); if (args.general.metrics_csv_enable) { metricshub.add_listener(&metrics_file); metrics_file.set_ue_handle(&ue); + if (args.general.metrics_csv_flush_period_sec > 0) { + metrics_file.set_flush_period((uint32_t)args.general.metrics_csv_flush_period_sec); + } } pthread_t input; diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc index d9b8499e5..bf4a2a002 100644 --- a/srsue/src/metrics_csv.cc +++ b/srsue/src/metrics_csv.cc @@ -35,9 +35,13 @@ using namespace std; namespace srsue { -metrics_csv::metrics_csv(std::string filename) : n_reports(0), metrics_report_period(1.0), ue(NULL) +metrics_csv::metrics_csv(std::string filename, bool append) : n_reports(0), metrics_report_period(1.0), ue(NULL) { - file.open(filename.c_str(), std::ios_base::out); + std::ios_base::openmode flags = std::ios_base::out; + if (append) { + flags |= std::ios_base::app; + } + file.open(filename.c_str(), flags); pthread_mutex_init(&mutex, NULL); } @@ -52,6 +56,11 @@ void metrics_csv::set_ue_handle(ue_metrics_interface* ue_) ue = ue_; } +void metrics_csv::set_flush_period(const uint32_t flush_period_sec_) +{ + flush_period_sec = flush_period_sec_; +} + void metrics_csv::stop() { pthread_mutex_lock(&mutex); @@ -68,71 +77,61 @@ void metrics_csv::set_metrics(const ue_metrics_t& metrics, const uint32_t period pthread_mutex_lock(&mutex); if (file.is_open() && ue != NULL) { if (n_reports == 0) { - file << "time;rsrp;pl;cfo;dl_mcs;dl_snr;dl_turbo;dl_brate;dl_bler;ul_ta;ul_mcs;ul_buff;ul_brate;ul_bler;rf_o;rf_" + file << "time;cc;pci;rsrp;pl;cfo;dl_mcs;dl_snr;dl_turbo;dl_brate;dl_bler;ul_ta;ul_mcs;ul_buff;ul_brate;ul_bler;" + "rf_o;rf_" "u;rf_l;is_attached\n"; } - file << (metrics_report_period * n_reports) << ";"; - - // Print PHY metrics for first CC - file << float_to_string(metrics.phy.dl[0].rsrp, 2); - file << float_to_string(metrics.phy.dl[0].pathloss, 2); - file << float_to_string(metrics.phy.sync[0].cfo, 2); - file << float_to_string(metrics.phy.dl[0].mcs, 2); - file << float_to_string(metrics.phy.dl[0].sinr, 2); - file << float_to_string(metrics.phy.dl[0].turbo_iters, 2); - - // Sum DL rate for all CCs - float rx_brate = 0; - for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - rx_brate += metrics.stack.mac[r].rx_brate; - } - file << float_to_string(rx_brate / period_usec * 1e6, 2); - - // Sum BLER for all CCs - int rx_pkts = 0; - int rx_errors = 0; for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - rx_pkts += metrics.stack.mac[r].rx_pkts; - rx_errors += metrics.stack.mac[r].rx_errors; - } - if (rx_pkts > 0) { - file << float_to_string((float)100 * rx_errors / rx_pkts, 1); - } else { - file << float_to_string(0, 2); + file << (metrics_report_period * n_reports) << ";"; + + // Print PHY metrics for first CC + file << float_to_string(metrics.phy.dl[r].rsrp, 2); + file << float_to_string(metrics.phy.dl[r].pathloss, 2); + file << float_to_string(metrics.phy.sync[r].cfo, 2); + file << float_to_string(metrics.phy.dl[r].mcs, 2); + file << float_to_string(metrics.phy.dl[r].sinr, 2); + file << float_to_string(metrics.phy.dl[r].turbo_iters, 2); + + file << float_to_string(metrics.stack.mac[r].rx_brate / period_usec * 1e6, 2); + + int rx_pkts = metrics.stack.mac[r].rx_pkts; + int rx_errors = metrics.stack.mac[r].rx_errors; + if (rx_pkts > 0) { + file << float_to_string((float)100 * rx_errors / rx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + + file << float_to_string(metrics.phy.sync[r].ta_us, 2); + file << float_to_string(metrics.phy.ul[r].mcs, 2); + file << float_to_string((float)metrics.stack.mac[r].ul_buffer, 2); + + file << float_to_string(metrics.stack.mac[r].tx_brate / period_usec * 1e6, 2); + + // Sum UL BLER for all CCs + int tx_pkts = metrics.stack.mac[r].tx_pkts; + int tx_errors = metrics.stack.mac[r].tx_errors; + if (tx_pkts > 0) { + file << float_to_string((float)100 * tx_errors / 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); + file << (metrics.stack.rrc.state == RRC_STATE_CONNECTED ? "1.0" : "0.0"); + file << "\n"; } - file << float_to_string(metrics.phy.sync[0].ta_us, 2); - file << float_to_string(metrics.phy.ul[0].mcs, 2); - file << float_to_string((float)metrics.stack.mac[0].ul_buffer, 2); - - // Sum UL rate for all CCs - float tx_brate = 0; - for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - tx_brate += metrics.stack.mac[r].tx_brate; - } - file << float_to_string(tx_brate / period_usec * 1e6, 2); + n_reports++; - // Sum UL BLER for all CCs - int tx_pkts = 0; - int tx_errors = 0; - for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - tx_pkts += metrics.stack.mac[r].tx_pkts; - tx_errors += metrics.stack.mac[r].tx_errors; + if (flush_period_sec > 0) { + if ((n_reports % flush_period_sec * 1000) == 0) { + file.flush(); + } } - if (tx_pkts > 0) { - file << float_to_string((float)100 * tx_errors / 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); - file << (metrics.stack.rrc.state == RRC_STATE_CONNECTED ? "1.0" : "0.0"); - file << "\n"; - - n_reports++; } else { std::cout << "couldn't write CSV file." << std::endl; }