/*! * \file ttff.cc * \brief This class implements a test for measuring * the Time-To-First-Fix * \author Carles Fernandez-Prades, 2016. cfernandez(at)cttc.es * * * ----------------------------------------------------------------------------- * * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. * This file is part of GNSS-SDR. * * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) * SPDX-License-Identifier: GPL-3.0-or-later * * ----------------------------------------------------------------------------- */ #include "concurrent_map.h" #include "concurrent_queue.h" #include "control_thread.h" #include "file_configuration.h" #include "gnss_flowgraph.h" #include "gps_acq_assist.h" #include "in_memory_configuration.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USE_GLOG_AND_GFLAGS #include #include #if GFLAGS_OLD_NAMESPACE namespace gflags { using namespace google; } #endif #else #include #include #include #include #endif #if USE_GLOG_AND_GFLAGS DEFINE_int32(fs_in, 4000000, "Sampling rate, in Samples/s"); DEFINE_int32(max_measurement_duration, 90, "Maximum time waiting for a position fix, in seconds"); DEFINE_int32(num_measurements, 2, "Number of measurements"); DEFINE_string(device_address, "192.168.40.2", "USRP device IP address"); DEFINE_string(subdevice, "A:0", "USRP subdevice"); DEFINE_string(config_file_ttff, std::string(""), "File containing the configuration parameters for the TTFF test."); #else ABSL_FLAG(int32_t, fs_in, 4000000, "Sampling rate, in Samples/s"); ABSL_FLAG(int32_t, max_measurement_duration, 90, "Maximum time waiting for a position fix, in seconds"); ABSL_FLAG(int32_t, num_measurements, 2, "Number of measurements"); ABSL_FLAG(std::string, device_address, "192.168.40.2", "USRP device IP address"); ABSL_FLAG(std::string, subdevice, "A:0", "USRP subdevice"); ABSL_FLAG(std::string, config_file_ttff, std::string(""), "File containing the configuration parameters for the TTFF test."); #endif // For GPS NAVIGATION (L1) Concurrent_Queue global_gps_acq_assist_queue; Concurrent_Map global_gps_acq_assist_map; std::vector TTFF_v; typedef struct { long mtype; // required by SysV message double ttff; } ttff_msgbuf; class TtffTest : public ::testing::Test { public: void config_1(); void config_2(); void print_TTFF_report(const std::vector &ttff_v, const std::shared_ptr &config_); std::shared_ptr config; std::shared_ptr config2; const double central_freq = 1575420000.0; const double gain_dB = 40.0; const int number_of_taps = 11; const int number_of_bands = 2; const float band1_begin = 0.0; const float band1_end = 0.48; const float band2_begin = 0.52; const float band2_end = 1.0; const float ampl1_begin = 1.0; const float ampl1_end = 1.0; const float ampl2_begin = 0.0; const float ampl2_end = 0.0; const float band1_error = 1.0; const float band2_error = 1.0; const int grid_density = 16; const float zero = 0.0; const int number_of_channels = 8; const int in_acquisition = 1; const float threshold = 0.01; const float doppler_max = 8000.0; const float doppler_step = 500.0; const int max_dwells = 1; const int tong_init_val = 2; const int tong_max_val = 10; const int tong_max_dwells = 30; const int coherent_integration_time_ms = 1; const float pll_bw_hz = 30.0; const float dll_bw_hz = 4.0; const float early_late_space_chips = 0.5; const int display_rate_ms = 500; const int output_rate_ms = 100; const int averaging_depth = 10; }; void TtffTest::config_1() { config = std::make_shared(); #if USE_GLOG_AND_GFLAGS config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(FLAGS_fs_in)); #else #endif // Set the assistance system parameters config->set_property("GNSS-SDR.SUPL_gps_ephemeris_server", "supl.google.com"); config->set_property("GNSS-SDR.SUPL_gps_ephemeris_port", std::to_string(7275)); config->set_property("GNSS-SDR.SUPL_gps_acquisition_server", "supl.google.com"); config->set_property("GNSS-SDR.SUPL_gps_acquisition_port", std::to_string(7275)); config->set_property("GNSS-SDR.SUPL_MCC", std::to_string(244)); config->set_property("GNSS-SDR.SUPL_MNC", std::to_string(5)); config->set_property("GNSS-SDR.SUPL_LAC", "0x59e2"); config->set_property("GNSS-SDR.SUPL_CI", "0x31b0"); // Set the Signal Source config->set_property("SignalSource.item_type", "cshort"); config->set_property("SignalSource.implementation", "UHD_Signal_Source"); config->set_property("SignalSource.freq", std::to_string(central_freq)); config->set_property("SignalSource.gain", std::to_string(gain_dB)); #if USE_GLOG_AND_GFLAGS config->set_property("SignalSource.sampling_frequency", std::to_string(FLAGS_fs_in)); config->set_property("SignalSource.subdevice", FLAGS_subdevice); config->set_property("SignalSource.samples", std::to_string(FLAGS_fs_in * FLAGS_max_measurement_duration)); config->set_property("SignalSource.device_address", FLAGS_device_address); #else #endif // Set the Signal Conditioner config->set_property("SignalConditioner.implementation", "Signal_Conditioner"); config->set_property("DataTypeAdapter.implementation", "Pass_Through"); config->set_property("DataTypeAdapter.item_type", "cshort"); config->set_property("InputFilter.implementation", "Fir_Filter"); config->set_property("InputFilter.dump", "false"); config->set_property("InputFilter.input_item_type", "cshort"); config->set_property("InputFilter.output_item_type", "gr_complex"); config->set_property("InputFilter.taps_item_type", "float"); config->set_property("InputFilter.number_of_taps", std::to_string(number_of_taps)); config->set_property("InputFilter.number_of_bands", std::to_string(number_of_bands)); config->set_property("InputFilter.band1_begin", std::to_string(band1_begin)); config->set_property("InputFilter.band1_end", std::to_string(band1_end)); config->set_property("InputFilter.band2_begin", std::to_string(band2_begin)); config->set_property("InputFilter.band2_end", std::to_string(band2_end)); config->set_property("InputFilter.ampl1_begin", std::to_string(ampl1_begin)); config->set_property("InputFilter.ampl1_end", std::to_string(ampl1_end)); config->set_property("InputFilter.ampl2_begin", std::to_string(ampl2_begin)); config->set_property("InputFilter.ampl2_end", std::to_string(ampl2_end)); config->set_property("InputFilter.band1_error", std::to_string(band1_error)); config->set_property("InputFilter.band2_error", std::to_string(band2_error)); config->set_property("InputFilter.filter_type", "bandpass"); config->set_property("InputFilter.grid_density", std::to_string(grid_density)); #if USE_GLOG_AND_GFLAGS config->set_property("InputFilter.sampling_frequency", std::to_string(FLAGS_fs_in)); #else #endif config->set_property("InputFilter.IF", std::to_string(zero)); config->set_property("Resampler.implementation", "Pass_Through"); config->set_property("Resampler.dump", "false"); config->set_property("Resampler.item_type", "gr_complex"); #if USE_GLOG_AND_GFLAGS config->set_property("Resampler.sample_freq_in", std::to_string(FLAGS_fs_in)); config->set_property("Resampler.sample_freq_out", std::to_string(FLAGS_fs_in)); #else #endif // Set the number of Channels config->set_property("Channels_1C.count", std::to_string(number_of_channels)); config->set_property("Channels.in_acquisition", std::to_string(in_acquisition)); config->set_property("Channel.signal", "1C"); // Set Acquisition config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Tong_Acquisition"); config->set_property("Acquisition_1C.item_type", "gr_complex"); config->set_property("Acquisition_1C.coherent_integration_time_ms", std::to_string(coherent_integration_time_ms)); config->set_property("Acquisition_1C.threshold", std::to_string(threshold)); config->set_property("Acquisition_1C.doppler_max", std::to_string(doppler_max)); config->set_property("Acquisition_1C.doppler_step", std::to_string(doppler_step)); config->set_property("Acquisition_1C.bit_transition_flag", "false"); config->set_property("Acquisition_1C.max_dwells", std::to_string(max_dwells)); config->set_property("Acquisition_1C.tong_init_val", std::to_string(tong_init_val)); config->set_property("Acquisition_1C.tong_max_val", std::to_string(tong_max_val)); config->set_property("Acquisition_1C.tong_max_dwells", std::to_string(tong_max_dwells)); // Set Tracking config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking"); config->set_property("Tracking_1C.item_type", "gr_complex"); config->set_property("Tracking_1C.dump", "false"); config->set_property("Tracking_1C.dump_filename", "./tracking_ch_"); config->set_property("Tracking_1C.pll_bw_hz", std::to_string(pll_bw_hz)); config->set_property("Tracking_1C.dll_bw_hz", std::to_string(dll_bw_hz)); config->set_property("Tracking_1C.early_late_space_chips", std::to_string(early_late_space_chips)); // Set Telemetry config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder"); config->set_property("TelemetryDecoder_1C.dump", "false"); // Set Observables config->set_property("Observables.implementation", "Hybrid_Observables"); config->set_property("Observables.dump", "false"); config->set_property("Observables.dump_filename", "./observables.dat"); // Set PVT config->set_property("PVT.implementation", "RTKLIB_PVT"); config->set_property("PVT.positioning_mode", "Single"); config->set_property("PVT.output_rate_ms", std::to_string(output_rate_ms)); config->set_property("PVT.display_rate_ms", std::to_string(display_rate_ms)); config->set_property("PVT.dump_filename", "./PVT"); config->set_property("PVT.nmea_dump_filename", "./gnss_sdr_pvt.nmea"); config->set_property("PVT.flag_nmea_tty_port", "false"); config->set_property("PVT.nmea_dump_devname", "/dev/pts/4"); config->set_property("PVT.flag_rtcm_server", "false"); config->set_property("PVT.flag_rtcm_tty_port", "false"); config->set_property("PVT.rtcm_dump_devname", "/dev/pts/1"); config->set_property("PVT.dump", "false"); } void TtffTest::config_2() { #if USE_GLOG_AND_GFLAGS if (FLAGS_config_file_ttff.empty()) { std::string path = std::string(TEST_PATH); std::string filename = path + "../../conf/gnss-sdr_GPS_L1_USRP_X300_realtime.conf"; config2 = std::make_shared(filename); } else { config2 = std::make_shared(FLAGS_config_file_ttff); } int d_sampling_rate; d_sampling_rate = config2->property("GNSS-SDR.internal_fs_sps", FLAGS_fs_in); config2->set_property("SignalSource.samples", std::to_string(d_sampling_rate * FLAGS_max_measurement_duration)); #else #endif } void receive_msg() { ttff_msgbuf msg; ttff_msgbuf msg_stop; msg_stop.mtype = 1; msg_stop.ttff = -200.0; double ttff_msg = 0.0; int msgrcv_size = sizeof(msg.ttff); int msqid; int msqid_stop = -1; key_t key = 1101; key_t key_stop = 1102; bool leave = false; while (!leave) { // wait for the queue to be created while ((msqid = msgget(key, 0644)) == -1) { } if (msgrcv(msqid, &msg, msgrcv_size, 1, 0) != -1) { ttff_msg = msg.ttff; if ((ttff_msg != 0) && (ttff_msg != -1)) { TTFF_v.push_back(ttff_msg); LOG(INFO) << "Valid Time-To-First-Fix: " << ttff_msg << "[s]"; // Stop the receiver while (((msqid_stop = msgget(key_stop, 0644))) == -1) { } double msgsend_size = sizeof(msg_stop.ttff); msgsnd(msqid_stop, &msg_stop, msgsend_size, IPC_NOWAIT); } if (std::abs(ttff_msg - (-1.0)) < 10 * std::numeric_limits::epsilon()) { leave = true; } } } } void TtffTest::print_TTFF_report(const std::vector &ttff_v, const std::shared_ptr &config_) { std::ofstream ttff_report_file; std::string filename = "ttff_report"; std::string filename_; boost::posix_time::ptime pt = boost::posix_time::second_clock::local_time(); tm timeinfo = boost::posix_time::to_tm(pt); std::stringstream strm0; const int year = timeinfo.tm_year - 100; strm0 << year; const int month = timeinfo.tm_mon + 1; if (month < 10) { strm0 << "0"; } strm0 << month; const int day = timeinfo.tm_mday; if (day < 10) { strm0 << "0"; } strm0 << day << "_"; const int hour = timeinfo.tm_hour; if (hour < 10) { strm0 << "0"; } strm0 << hour; const int min = timeinfo.tm_min; if (min < 10) { strm0 << "0"; } strm0 << min; const int sec = timeinfo.tm_sec; if (sec < 10) { strm0 << "0"; } strm0 << sec; filename_ = filename + "_" + strm0.str() + ".txt"; ttff_report_file.open(filename_.c_str()); std::vector ttff = ttff_v; bool read_ephemeris; bool false_bool = false; read_ephemeris = config_->property("GNSS-SDR.SUPL_read_gps_assistance_xml", false_bool); bool agnss; agnss = config_->property("GNSS-SDR.SUPL_gps_enabled", false_bool); double sum = std::accumulate(ttff.begin(), ttff.end(), 0.0); double mean = sum / ttff.size(); double sq_sum = std::inner_product(ttff.begin(), ttff.end(), ttff.begin(), 0.0); double stdev = std::sqrt(sq_sum / ttff.size() - mean * mean); auto max_ttff = std::max_element(std::begin(ttff), std::end(ttff)); auto min_ttff = std::min_element(std::begin(ttff), std::end(ttff)); std::string source; std::string default_str = "default"; source = config_->property("SignalSource.implementation", default_str); std::stringstream stm; stm << "---------------------------\n"; stm << " Time-To-First-Fix Report\n"; stm << "---------------------------\n"; stm << "Initial receiver status: "; if (read_ephemeris) { stm << "Hot start.\n"; } else { stm << "Cold start.\n"; } stm << "A-GNSS: "; if (agnss && read_ephemeris) { stm << "Enabled.\n"; } else { stm << "Disabled.\n"; } #if USE_GLOG_AND_GFLAGS stm << "Valid measurements (" << ttff.size() << "/" << FLAGS_num_measurements << "): "; #else stm << "Valid measurements (" << ttff.size() << "/" << absl::GetFlag(FLAGS_num_measurements) << "): "; #endif for (double ttff_ : ttff) { stm << ttff_ << " "; } stm << '\n'; stm << "TTFF mean: " << mean << " [s]\n"; if (!ttff.empty()) { stm << "TTFF max: " << *max_ttff << " [s]\n"; stm << "TTFF min: " << *min_ttff << " [s]\n"; } stm << "TTFF stdev: " << stdev << " [s]\n"; stm << "Operating System: " << std::string(HOST_SYSTEM) << '\n'; stm << "Navigation mode: " << "3D\n"; if (source != "UHD_Signal_Source") { stm << "Source: File\n"; } else { stm << "Source: Live\n"; } stm << "---------------------------\n"; std::cout << stm.rdbuf(); if (ttff_report_file.is_open()) { ttff_report_file << stm.str(); ttff_report_file.close(); } } TEST_F(TtffTest /*unused*/, ColdStart /*unused*/) { unsigned int num_measurements = 0; config_1(); // Ensure Cold Start config->set_property("GNSS-SDR.SUPL_gps_enabled", "false"); config->set_property("GNSS-SDR.SUPL_read_gps_assistance_xml", "false"); config_2(); // Ensure Cold Start config2->set_property("GNSS-SDR.SUPL_gps_enabled", "false"); config2->set_property("GNSS-SDR.SUPL_read_gps_assistance_xml", "false"); config2->set_property("PVT.flag_rtcm_server", "false"); #if USE_GLOG_AND_GFLAGS for (int n = 0; n < FLAGS_num_measurements; n++) #else for (int n = 0; n < absl::GetFlag(FLAGS_num_measurements); n++) #endif { // Create a new ControlThread object with a smart pointer std::shared_ptr control_thread; #if USE_GLOG_AND_GFLAGS if (FLAGS_config_file_ttff.empty()) #else if (absl::GetFlag(FLAGS_config_file_ttff).empty()) #endif { control_thread = std::make_shared(config); } else { control_thread = std::make_shared(config2); } // record startup time #if USE_GLOG_AND_GFLAGS std::cout << "Starting measurement " << num_measurements + 1 << " / " << FLAGS_num_measurements << '\n'; #else std::cout << "Starting measurement " << num_measurements + 1 << " / " << absl::GetFlag(FLAGS_num_measurements) << '\n'; #endif std::chrono::time_point start; std::chrono::time_point end; start = std::chrono::system_clock::now(); // start receiver try { control_thread->run(); } catch (const boost::exception &e) { std::cout << "Boost exception: " << boost::diagnostic_information(e); } catch (const std::exception &ex) { std::cout << "STD exception: " << ex.what(); } // stop clock end = std::chrono::system_clock::now(); std::chrono::duration elapsed_seconds = end - start; double ttff = elapsed_seconds.count(); std::shared_ptr flowgraph = control_thread->flowgraph(); EXPECT_FALSE(flowgraph->running()); num_measurements = num_measurements + 1; std::cout << "Just finished measurement " << num_measurements << ", which took " << ttff << " seconds.\n"; #if USE_GLOG_AND_GFLAGS if (n < FLAGS_num_measurements - 1) #else if (n < absl::GetFlag(FLAGS_num_measurements) - 1) #endif { std::random_device r; std::default_random_engine e1(r()); std::uniform_real_distribution uniform_dist(0, 1); float random_variable_0_1 = uniform_dist(e1); int random_delay_s = static_cast(random_variable_0_1 * 25.0); std::cout << "Waiting a random amount of time (from 5 to 30 s) to start a new measurement... \n"; std::cout << "This time will wait " << random_delay_s + 5 << " s.\n" << '\n'; std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::seconds(5) + std::chrono::seconds(random_delay_s)); } } // Print TTFF report #if USE_GLOG_AND_GFLAGS if (FLAGS_config_file_ttff.empty()) #else if (absl::GetFlag(FLAGS_config_file_ttff).empty()) #endif { print_TTFF_report(TTFF_v, config); } else { print_TTFF_report(TTFF_v, config2); } std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::seconds(5)); // let the USRP some time to rest before the next test } TEST_F(TtffTest /*unused*/, HotStart /*unused*/) { unsigned int num_measurements = 0; TTFF_v.clear(); config_1(); // Ensure Hot Start config->set_property("GNSS-SDR.SUPL_gps_enabled", "true"); config->set_property("GNSS-SDR.SUPL_read_gps_assistance_xml", "true"); config_2(); // Ensure Hot Start config2->set_property("GNSS-SDR.SUPL_gps_enabled", "true"); config2->set_property("GNSS-SDR.SUPL_read_gps_assistance_xml", "true"); config2->set_property("PVT.flag_rtcm_server", "false"); #if USE_GLOG_AND_GFLAGS for (int n = 0; n < FLAGS_num_measurements; n++) #else for (int n = 0; n < absl::GetFlag(FLAGS_num_measurements); n++) #endif { // Create a new ControlThread object with a smart pointer std::shared_ptr control_thread; #if USE_GLOG_AND_GFLAGS if (FLAGS_config_file_ttff.empty()) #else if (absl::GetFlag(FLAGS_config_file_ttff).empty()) #endif { control_thread = std::make_shared(config); } else { control_thread = std::make_shared(config2); } // record startup time #if USE_GLOG_AND_GFLAGS std::cout << "Starting measurement " << num_measurements + 1 << " / " << FLAGS_num_measurements << '\n'; #else std::cout << "Starting measurement " << num_measurements + 1 << " / " << absl::GetFlag(FLAGS_num_measurements) << '\n'; #endif std::chrono::time_point start; std::chrono::time_point end; start = std::chrono::system_clock::now(); // start receiver try { control_thread->run(); } catch (const boost::exception &e) { std::cout << "Boost exception: " << boost::diagnostic_information(e); } catch (const std::exception &ex) { std::cout << "STD exception: " << ex.what(); } // stop clock end = std::chrono::system_clock::now(); std::chrono::duration elapsed_seconds = end - start; double ttff = elapsed_seconds.count(); std::shared_ptr flowgraph = control_thread->flowgraph(); EXPECT_FALSE(flowgraph->running()); num_measurements = num_measurements + 1; std::cout << "Just finished measurement " << num_measurements << ", which took " << ttff << " seconds.\n"; #if USE_GLOG_AND_GFLAGS if (n < FLAGS_num_measurements - 1) #else if (n < absl::GetFlag(FLAGS_num_measurements) - 1) #endif { std::random_device r; std::default_random_engine e1(r()); std::uniform_real_distribution uniform_dist(0, 1); float random_variable_0_1 = uniform_dist(e1); int random_delay_s = static_cast(random_variable_0_1 * 25.0); std::cout << "Waiting a random amount of time (from 5 to 30 s) to start new measurement... \n"; std::cout << "This time will wait " << random_delay_s + 5 << " s.\n" << '\n'; std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::seconds(5) + std::chrono::seconds(random_delay_s)); } } // Print TTFF report #if USE_GLOG_AND_GFLAGS if (FLAGS_config_file_ttff.empty()) #else if (absl::GetFlag(FLAGS_config_file_ttff).empty()) #endif { print_TTFF_report(TTFF_v, config); } else { print_TTFF_report(TTFF_v, config2); } } int main(int argc, char **argv) { std::cout << "Running Time-To-First-Fix test...\n"; int res = 0; TTFF_v.clear(); try { testing::InitGoogleTest(&argc, argv); } catch (...) { } // catch the "testing::internal::::ClassUniqueToAlwaysTrue" from gtest #if USE_GLOG_AND_GFLAGS gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); #else absl::ParseCommandLine(argc, argv); #endif // Start queue thread std::thread receive_msg_thread(receive_msg); // Run the Tests try { res = RUN_ALL_TESTS(); } catch (...) { LOG(WARNING) << "Unexpected catch"; } // Terminate the queue thread key_t sysv_msg_key; int sysv_msqid; sysv_msg_key = 1101; int msgflg = IPC_CREAT | 0666; if ((sysv_msqid = msgget(sysv_msg_key, msgflg)) == -1) { std::cout << "GNSS-SDR can not create message queues!\n"; return 1; } ttff_msgbuf msg; msg.mtype = 1; msg.ttff = -1; int msgsend_size; msgsend_size = sizeof(msg.ttff); msgsnd(sysv_msqid, &msg, msgsend_size, IPC_NOWAIT); receive_msg_thread.join(); msgctl(sysv_msqid, IPC_RMID, nullptr); #if USE_GLOG_AND_GFLAGS gflags::ShutDownCommandLineFlags(); #endif return res; }