From 41b536524423b326eb6c252d798c3c1b7a01bdda Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sat, 28 Oct 2017 18:15:59 +0200 Subject: [PATCH] Add acquisition grid plot --- src/tests/common-files/gnuplot_i.h | 61 ++++++++ .../gps_l1_ca_pcps_acquisition_test.cc | 135 ++++++++++++++---- .../libs/CMakeLists.txt | 1 + .../libs/acquisition_dump_reader.cc | 109 ++++++++++++++ .../libs/acquisition_dump_reader.h | 61 ++++++++ 5 files changed, 343 insertions(+), 24 deletions(-) create mode 100644 src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.cc create mode 100644 src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.h diff --git a/src/tests/common-files/gnuplot_i.h b/src/tests/common-files/gnuplot_i.h index 8d1afe8ab..3627a38af 100644 --- a/src/tests/common-files/gnuplot_i.h +++ b/src/tests/common-files/gnuplot_i.h @@ -510,6 +510,10 @@ public: Gnuplot& plot_xy_err(const X &x, const Y &y, const E &dy, const std::string &title = ""); + template + Gnuplot& plot_grid3d(const X &x, const Y &y, const E &mag, + const std::string &title = ""); + /// plot x,y,z triples: x y z /// from file Gnuplot& plotfile_xyz(const std::string &filename, @@ -804,6 +808,63 @@ Gnuplot& Gnuplot::plot_xy_err(const X &x, } +//------------------------------------------------------------------------------ +// +// Plots a 3d grid +// +template +Gnuplot& Gnuplot::plot_grid3d(const X &x, + const Y &y, + const E &mag, + const std::string &title) +{ + if (x.size() == 0 || y.size() == 0 ) + { + throw GnuplotException("std::vectors too small"); + return *this; + } + std::ofstream tmp; + std::string name = create_tmpfile(tmp); + if (name == "") + return *this; + + // + // write the data to file + // + for (unsigned int i = 0; i < x.size(); i++) + { + for (unsigned int k = 0; k < y.size(); k++) + { + tmp << static_cast(x.at(i)) << " " << static_cast(y.at(k)) << " " << mag.at(i).at(k) << std::endl; + } + tmp.flush(); + } + + tmp.close(); + + std::ostringstream cmdstr; + cmdstr << "set ticslevel 0\n"; + cmdstr << "set hidden3d\n"; + cmdstr << "unset colorbox\n"; + cmdstr << "set border 5\n"; + cmdstr << "unset ztics\n"; + + cmdstr << " splot \"" << name << "\" u 1:2:3"; + + if (title == "") + cmdstr << " notitle with " << pstyle << " palette"; + else + cmdstr << " title \"" << title << "\" with " << pstyle << " palette"; + cmdstr << "\n"; + + // + // Do the actual plot + // + cmd(cmdstr.str()); + + return *this; +} + //------------------------------------------------------------------------------ // // Plots a 3d graph from a list of doubles: x y z diff --git a/src/tests/unit-tests/signal-processing-blocks/acquisition/gps_l1_ca_pcps_acquisition_test.cc b/src/tests/unit-tests/signal-processing-blocks/acquisition/gps_l1_ca_pcps_acquisition_test.cc index 76aedce64..e1ebcb4c4 100644 --- a/src/tests/unit-tests/signal-processing-blocks/acquisition/gps_l1_ca_pcps_acquisition_test.cc +++ b/src/tests/unit-tests/signal-processing-blocks/acquisition/gps_l1_ca_pcps_acquisition_test.cc @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -47,8 +48,14 @@ #include "in_memory_configuration.h" #include "gnss_sdr_valve.h" #include "gnss_synchro.h" +#include "gnuplot_i.h" +#include "test_flags.h" +#include "acquisition_dump_reader.h" #include "gps_l1_ca_pcps_acquisition.h" +DEFINE_bool(plot_acq_grid, false, "Plots results of GpsL1CaPcpsAcquisitionTest with gnuplot"); + + // ######## GNURADIO BLOCK MESSAGE RECEVER ######### class GpsL1CaPcpsAcquisitionTest_msg_rx; @@ -120,6 +127,7 @@ protected: {} void init(); + void plot_grid(); gr::top_block_sptr top_block; std::shared_ptr factory; @@ -136,24 +144,88 @@ void GpsL1CaPcpsAcquisitionTest::init() std::string signal = "1C"; signal.copy(gnss_synchro.Signal, 2, 0); gnss_synchro.PRN = 1; + config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition"); config->set_property("GNSS-SDR.internal_fs_sps", "4000000"); - config->set_property("Acquisition.item_type", "gr_complex"); - config->set_property("Acquisition.if", "0"); - config->set_property("Acquisition.coherent_integration_time_ms", "1"); - config->set_property("Acquisition.dump", "false"); - config->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_Acquisition"); - config->set_property("Acquisition.threshold", "0.00001"); - config->set_property("Acquisition.doppler_max", "5000"); - config->set_property("Acquisition.doppler_step", "500"); - config->set_property("Acquisition.repeat_satellite", "false"); - //config->set_property("Acquisition.pfa", "0.0"); + config->set_property("Acquisition_1C.item_type", "gr_complex"); + config->set_property("Acquisition_1C.if", "0"); + config->set_property("Acquisition_1C.coherent_integration_time_ms", "1"); + if(FLAGS_plot_acq_grid == true) + { + config->set_property("Acquisition_1C.dump", "true"); + } + else + { + config->set_property("Acquisition_1C.dump", "false"); + } + config->set_property("Acquisition_1C.threshold", "0.00001"); + config->set_property("Acquisition_1C.doppler_max", "5000"); + config->set_property("Acquisition_1C.doppler_step", "500"); + config->set_property("Acquisition_1C.repeat_satellite", "false"); + //config->set_property("Acquisition_1C.pfa", "0.0"); +} + + +void GpsL1CaPcpsAcquisitionTest::plot_grid() +{ + //load the measured values + std::string basename = "./data/acquisition_G_1C"; + unsigned int sat = static_cast(gnss_synchro.PRN); + unsigned int doppler_max = 5000; // !!! + unsigned int doppler_step = 100; // !! + unsigned int samples_per_code = static_cast(round(4000000 / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS))); // !! + acquisition_dump_reader acq_dump(basename, sat, doppler_max, doppler_step, samples_per_code); + + if(!acq_dump.read_binary_acq()) std::cout << "Error reading files" << std::endl; + + std::vector doppler = acq_dump.doppler; + std::vector samples = acq_dump.samples; + std::vector > mag = acq_dump.mag; + + const std::string gnuplot_executable(FLAGS_gnuplot_executable); + if(gnuplot_executable.empty()) + { + std::cout << "WARNING: Although the flag plot_acq_grid has been set to TRUE," << std::endl; + std::cout << "gnuplot has not been found in your system." << std::endl; + std::cout << "Test results will not be plotted." << std::endl; + } + else + { + std::cout << "Plotting the acquisition grid..." << std::endl; + try + { + boost::filesystem::path p(gnuplot_executable); + boost::filesystem::path dir = p.parent_path(); + std::string gnuplot_path = dir.native(); + Gnuplot::set_GNUPlotPath(gnuplot_path); + + Gnuplot g1("lines"); + g1.set_title("GPS L1 C/A signal acquisition for satellite PRN #" + std::to_string(gnss_synchro.PRN)); + g1.set_xlabel("Doppler [Hz]"); + g1.set_ylabel("Sample"); + //g1.cmd("set view 60, 105, 1, 1"); + g1.plot_grid3d(doppler, samples, mag); + + g1.savetops("GPS_L1_acq_grid"); + g1.savetopdf("GPS_L1_acq_grid"); + g1.showonscreen(); + } + catch (const GnuplotException & ge) + { + std::cout << ge.what() << std::endl; + } + } + std::string data_str = "./data"; + if (boost::filesystem::exists(data_str)) + { + boost::filesystem::remove_all(data_str); + } } TEST_F(GpsL1CaPcpsAcquisitionTest, Instantiate) { init(); - boost::shared_ptr acquisition = boost::make_shared(config.get(), "Acquisition", 1, 1); + boost::shared_ptr acquisition = boost::make_shared(config.get(), "Acquisition_1C", 1, 1); } @@ -167,7 +239,7 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ConnectAndRun) top_block = gr::make_top_block("Acquisition test"); init(); - boost::shared_ptr acquisition = boost::make_shared(config.get(), "Acquisition", 1, 1); + boost::shared_ptr acquisition = boost::make_shared(config.get(), "Acquisition_1C", 1, 1); boost::shared_ptr msg_rx = GpsL1CaPcpsAcquisitionTest_msg_rx_make(); ASSERT_NO_THROW( { @@ -178,14 +250,14 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ConnectAndRun) top_block->connect(valve, 0, acquisition->get_left_block(), 0); top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events")); - }) << "Failure connecting the blocks of acquisition test." << std::endl; + }) << "Failure connecting the blocks of acquisition test."; EXPECT_NO_THROW( { start = std::chrono::system_clock::now(); top_block->run(); // Start threads and wait end = std::chrono::system_clock::now(); elapsed_seconds = end - start; - }) << "Failure running the top_block." << std::endl; + }) << "Failure running the top_block."; std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds" << std::endl; } @@ -194,7 +266,7 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ConnectAndRun) TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults) { std::chrono::time_point start, end; - std::chrono::duration elapsed_seconds(0); + std::chrono::duration elapsed_seconds(0.0); top_block = gr::make_top_block("Acquisition test"); double expected_delay_samples = 524; @@ -202,32 +274,42 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults) init(); - std::shared_ptr acquisition = std::make_shared(config.get(), "Acquisition", 1, 1); + if(FLAGS_plot_acq_grid == true) + { + std::string data_str = "./data"; + if (boost::filesystem::exists(data_str)) + { + boost::filesystem::remove_all(data_str); + } + boost::filesystem::create_directory(data_str); + } + + std::shared_ptr acquisition = std::make_shared(config.get(), "Acquisition_1C", 1, 1); boost::shared_ptr msg_rx = GpsL1CaPcpsAcquisitionTest_msg_rx_make(); ASSERT_NO_THROW( { acquisition->set_channel(1); - }) << "Failure setting channel." << std::endl; + }) << "Failure setting channel."; ASSERT_NO_THROW( { acquisition->set_gnss_synchro(&gnss_synchro); - }) << "Failure setting gnss_synchro." << std::endl; + }) << "Failure setting gnss_synchro."; ASSERT_NO_THROW( { acquisition->set_threshold(0.001); - }) << "Failure setting threshold." << std::endl; + }) << "Failure setting threshold."; ASSERT_NO_THROW( { acquisition->set_doppler_max(5000); - }) << "Failure setting doppler_max." << std::endl; + }) << "Failure setting doppler_max."; ASSERT_NO_THROW( { acquisition->set_doppler_step(100); - }) << "Failure setting doppler_step." << std::endl; + }) << "Failure setting doppler_step."; ASSERT_NO_THROW( { acquisition->connect(top_block); - }) << "Failure connecting acquisition to the top_block." << std::endl; + }) << "Failure connecting acquisition to the top_block."; ASSERT_NO_THROW( { std::string path = std::string(TEST_PATH); @@ -236,7 +318,7 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults) gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false); top_block->connect(file_source, 0, acquisition->get_left_block(), 0); top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events")); - }) << "Failure connecting the blocks of acquisition test." << std::endl; + }) << "Failure connecting the blocks of acquisition test."; acquisition->set_local_code(); acquisition->set_state(1); // Ensure that acquisition starts at the first sample @@ -247,7 +329,7 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults) top_block->run(); // Start threads and wait end = std::chrono::system_clock::now(); elapsed_seconds = end - start; - }) << "Failure running the top_block." << std::endl; + }) << "Failure running the top_block."; unsigned long int nsamples = gnss_synchro.Acq_samplestamp_samples; std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds" << std::endl; @@ -259,4 +341,9 @@ TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults) EXPECT_LE(doppler_error_hz, 666) << "Doppler error exceeds the expected value: 666 Hz = 2/(3*integration period)"; EXPECT_LT(delay_error_chips, 0.5) << "Delay error exceeds the expected value: 0.5 chips"; + + if(FLAGS_plot_acq_grid == true) + { + plot_grid(); + } } diff --git a/src/tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt b/src/tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt index 0ee8ec658..14c2f5bd2 100644 --- a/src/tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt +++ b/src/tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt @@ -18,6 +18,7 @@ set(SIGNAL_PROCESSING_TESTING_LIB_SOURCES + acquisition_dump_reader.cc tracking_dump_reader.cc tlm_dump_reader.cc observables_dump_reader.cc diff --git a/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.cc b/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.cc new file mode 100644 index 000000000..3c9fcfda0 --- /dev/null +++ b/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.cc @@ -0,0 +1,109 @@ +/*! + * \file acquisition_dump_reader.cc + * \brief Helper file for unit testing + * \author Javier Arribas, 2017. jarribas(at)cttc.es + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#include +#include "acquisition_dump_reader.h" + +bool acquisition_dump_reader::read_binary_acq() +{ + std::complex* aux = new std::complex[1]; + for(int i = 0; i < d_num_doppler_bins; i++) + { + try + { + std::ifstream ifs; + ifs.exceptions( std::ifstream::failbit | std::ifstream::badbit ); + ifs.open(d_dump_filenames.at(i).c_str(), std::ios::in | std::ios::binary); + d_dump_files.at(i).swap(ifs); + if (d_dump_files.at(i).is_open()) + { + for(int k = 0; k < d_samples_per_code; k++) + { + d_dump_files.at(i).read(reinterpret_cast(&aux[0]), sizeof(std::complex)); + mag.at(i).at(k) = std::abs(*aux) / std::pow(d_samples_per_code, 2); + } + } + else + { + std::cout << "File " << d_dump_filenames.at(i).c_str() << " not found." << std::endl; + delete[] aux; + return false; + } + d_dump_files.at(i).close(); + } + catch (const std::ifstream::failure &e) + { + std::cout << e.what() << std::endl; + delete[] aux; + return false; + } + } + delete[] aux; + return true; +} + + +acquisition_dump_reader::acquisition_dump_reader(std::string & basename, unsigned int sat, unsigned int doppler_max, unsigned int doppler_step, unsigned int samples_per_code) +{ + d_basename = basename; + d_sat = sat; + d_doppler_max = doppler_max; + d_doppler_step = doppler_step; + d_samples_per_code = samples_per_code; + d_num_doppler_bins = static_cast(ceil( static_cast(static_cast(d_doppler_max) - static_cast(-d_doppler_max)) / static_cast(d_doppler_step))); + std::vector > mag_aux(d_num_doppler_bins, std::vector(d_samples_per_code)); + mag = mag_aux; + for (unsigned int doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) + { + doppler.push_back(-static_cast(d_doppler_max) + d_doppler_step * doppler_index); + d_dump_filenames.push_back(d_basename + "_sat_" + std::to_string(d_sat) + "_doppler_" + std::to_string(doppler.at(doppler_index)) + ".dat"); + } + for (unsigned int k = 0; k < d_samples_per_code; k++) + { + samples.push_back(k); + } + for(int i = 0; i < d_num_doppler_bins; i++) + { + std::ifstream is; + d_dump_files.push_back(std::move(is)); + } +} + + +acquisition_dump_reader::~acquisition_dump_reader() +{ + for(int i = 0; i < d_num_doppler_bins; i++) + { + if (d_dump_files.at(i).is_open() == true) + { + d_dump_files.at(i).close(); + } + } +} diff --git a/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.h b/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.h new file mode 100644 index 000000000..8d346cf7d --- /dev/null +++ b/src/tests/unit-tests/signal-processing-blocks/libs/acquisition_dump_reader.h @@ -0,0 +1,61 @@ +/*! + * \file acquisition_dump_reader.h + * \brief Helper file for unit testing + * \author Javier Arribas, 2017. jarribas(at)cttc.es + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_ACQUISITION_DUMP_READER_H +#define GNSS_SDR_ACQUISITION_DUMP_READER_H + +#include +#include +#include +#include + +class acquisition_dump_reader +{ +public: + acquisition_dump_reader(std::string & basename, unsigned int sat, unsigned int doppler_max, unsigned int doppler_step, unsigned int samples_per_code); + ~acquisition_dump_reader(); + bool read_binary_acq(); + + std::vector doppler; + std::vector samples; + std::vector > mag; + +private: + std::string d_basename; + unsigned int d_sat; + unsigned int d_doppler_max; + unsigned int d_doppler_step; + unsigned int d_samples_per_code; + unsigned int d_num_doppler_bins; + std::vector d_dump_filenames; + std::vector d_dump_files; +}; + +#endif // GNSS_SDR_ACQUISITION_DUMP_READER_H