mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2025-01-17 20:53:02 +00:00
Merge remote-tracking branch 'cf/add-gnuplot' into next
This merge adds the capability for some tests to output figures with plots of the results, using gnuplot if it is available in the system. The user needs to activate the corresponding flag in the tests in which it is allowed (those flags start with --plot_...). See doc at http://gnss-sdr.org/docs/tutorials/testing-software-receiver/ This will display figures in new windows and will save PostScript files (.ps) in the folder where the test was called.
This commit is contained in:
commit
0eb864a498
@ -158,6 +158,11 @@ if(ENABLE_FPGA)
|
|||||||
add_definitions(-DFPGA_BLOCKS_TEST=1)
|
add_definitions(-DFPGA_BLOCKS_TEST=1)
|
||||||
endif(ENABLE_FPGA)
|
endif(ENABLE_FPGA)
|
||||||
|
|
||||||
|
find_package(Gnuplot)
|
||||||
|
if(GNUPLOT_FOUND)
|
||||||
|
add_definitions(-DGNUPLOT_EXECUTABLE="${GNUPLOT_EXECUTABLE}")
|
||||||
|
endif(GNUPLOT_FOUND)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Optional generator
|
# Optional generator
|
||||||
################################################################################
|
################################################################################
|
||||||
|
1990
src/tests/common-files/gnuplot_i.h
Normal file
1990
src/tests/common-files/gnuplot_i.h
Normal file
File diff suppressed because it is too large
Load Diff
42
src/tests/common-files/test_flags.h
Normal file
42
src/tests/common-files/test_flags.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*!
|
||||||
|
* \file test_flags.h
|
||||||
|
* \brief Helper file for unit testing
|
||||||
|
* \author Carles Fernandez-Prades, 2017. cfernandez(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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GNSS_SDR_TEST_FLAGS_H_
|
||||||
|
#define GNSS_SDR_TEST_FLAGS_H_
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
#if defined GNUPLOT_EXECUTABLE
|
||||||
|
DEFINE_string(gnuplot_executable, std::string(GNUPLOT_EXECUTABLE), "Gnuplot binary path");
|
||||||
|
#elif !defined GNUPLOT_EXECUTABLE
|
||||||
|
DEFINE_string(gnuplot_executable, "", "Gnuplot binary path");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -29,11 +29,13 @@
|
|||||||
* -------------------------------------------------------------------------
|
* -------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@ -43,10 +45,13 @@
|
|||||||
#include "in_memory_configuration.h"
|
#include "in_memory_configuration.h"
|
||||||
#include "file_configuration.h"
|
#include "file_configuration.h"
|
||||||
#include "MATH_CONSTANTS.h"
|
#include "MATH_CONSTANTS.h"
|
||||||
|
#include "gnuplot_i.h"
|
||||||
|
#include "test_flags.h"
|
||||||
#include "signal_generator_flags.h"
|
#include "signal_generator_flags.h"
|
||||||
|
|
||||||
|
|
||||||
DEFINE_string(config_file_ptest, std::string(""), "File containing the configuration parameters for the position test.");
|
DEFINE_string(config_file_ptest, std::string(""), "File containing the configuration parameters for the position test.");
|
||||||
|
DEFINE_bool(plot_position_test, false, "Plots results of FFTLengthTest with gnuplot");
|
||||||
|
|
||||||
// For GPS NAVIGATION (L1)
|
// For GPS NAVIGATION (L1)
|
||||||
concurrent_queue<Gps_Acq_Assist> global_gps_acq_assist_queue;
|
concurrent_queue<Gps_Acq_Assist> global_gps_acq_assist_queue;
|
||||||
@ -73,6 +78,9 @@ public:
|
|||||||
int configure_receiver();
|
int configure_receiver();
|
||||||
int run_receiver();
|
int run_receiver();
|
||||||
void check_results();
|
void check_results();
|
||||||
|
void print_results(const std::vector<double> & east,
|
||||||
|
const std::vector<double> & north,
|
||||||
|
const std::vector<double> & up);
|
||||||
|
|
||||||
double compute_stdev_precision(const std::vector<double> & vec);
|
double compute_stdev_precision(const std::vector<double> & vec);
|
||||||
double compute_stdev_accuracy(const std::vector<double> & vec, double ref);
|
double compute_stdev_accuracy(const std::vector<double> & vec, double ref);
|
||||||
@ -542,9 +550,102 @@ void StaticPositionSystemTest::check_results()
|
|||||||
// Sanity Check
|
// Sanity Check
|
||||||
double precision_SEP = 0.51 * (sigma_E_2_precision + sigma_N_2_precision + sigma_U_2_precision);
|
double precision_SEP = 0.51 * (sigma_E_2_precision + sigma_N_2_precision + sigma_U_2_precision);
|
||||||
ASSERT_LT(precision_SEP, 20.0);
|
ASSERT_LT(precision_SEP, 20.0);
|
||||||
|
|
||||||
|
if(FLAGS_plot_position_test == true)
|
||||||
|
{
|
||||||
|
print_results(pos_e, pos_n, pos_u);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StaticPositionSystemTest::print_results(const std::vector<double> & east,
|
||||||
|
const std::vector<double> & north,
|
||||||
|
const std::vector<double> & up)
|
||||||
|
{
|
||||||
|
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||||
|
if(gnuplot_executable.empty())
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: Although the flag plot_position_test 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
|
||||||
|
{
|
||||||
|
double sigma_E_2_precision = std::pow(compute_stdev_precision(east), 2.0);
|
||||||
|
double sigma_N_2_precision = std::pow(compute_stdev_precision(north), 2.0);
|
||||||
|
double sigma_U_2_precision = std::pow(compute_stdev_precision(up), 2.0);
|
||||||
|
|
||||||
|
double mean_east = std::accumulate(east.begin(), east.end(), 0.0) / east.size();
|
||||||
|
double mean_north = std::accumulate(north.begin(), north.end(), 0.0) / north.size();
|
||||||
|
|
||||||
|
auto it_max_east = std::max_element(std::begin(east), std::end(east));
|
||||||
|
auto it_min_east = std::min_element(std::begin(east), std::end(east));
|
||||||
|
auto it_max_north = std::max_element(std::begin(north), std::end(north));
|
||||||
|
auto it_min_north = std::min_element(std::begin(north), std::end(north));
|
||||||
|
auto it_max_up = std::max_element(std::begin(up), std::end(up));
|
||||||
|
auto it_min_up = std::min_element(std::begin(up), std::end(up));
|
||||||
|
|
||||||
|
auto east_range = std::max(*it_max_east, std::abs(*it_min_east));
|
||||||
|
auto north_range = std::max(*it_max_north, std::abs(*it_min_north));
|
||||||
|
auto up_range = std::max(*it_max_up, std::abs(*it_min_up));
|
||||||
|
|
||||||
|
double range = std::max(east_range, north_range) * 1.1;
|
||||||
|
double range_3d = std::max(std::max(east_range, north_range), up_range) * 1.1;
|
||||||
|
|
||||||
|
double two_drms = 2 * sqrt(sigma_E_2_precision + sigma_N_2_precision);
|
||||||
|
double ninty_sas = 0.833 * (sigma_E_2_precision + sigma_N_2_precision + sigma_U_2_precision);
|
||||||
|
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("points");
|
||||||
|
g1.set_title("2D precision");
|
||||||
|
g1.set_xlabel("East [m]");
|
||||||
|
g1.set_ylabel("North [m]");
|
||||||
|
g1.cmd("set size ratio -1");
|
||||||
|
g1.cmd("set xrange [-" + std::to_string(range) + ":" + std::to_string(range) + "]");
|
||||||
|
g1.cmd("set yrange [-" + std::to_string(range) + ":" + std::to_string(range) + "]");
|
||||||
|
|
||||||
|
g1.plot_xy(east, north, "2D Position Fixes");
|
||||||
|
g1.set_style("lines").plot_circle(mean_east, mean_north, two_drms, "2DRMS");
|
||||||
|
g1.set_style("lines").plot_circle(mean_east, mean_north, two_drms / 2.0, "DRMS");
|
||||||
|
|
||||||
|
g1.cmd("set grid front");
|
||||||
|
g1.cmd("replot");
|
||||||
|
|
||||||
|
g1.savetops("Position_test_2D");
|
||||||
|
g1.showonscreen(); // window output
|
||||||
|
|
||||||
|
Gnuplot g2("points");
|
||||||
|
g2.set_title("3D precision");
|
||||||
|
g2.set_xlabel("East [m]");
|
||||||
|
g2.set_ylabel("North [m]");
|
||||||
|
g2.set_zlabel("Up [m]");
|
||||||
|
g2.cmd("set size ratio -1");
|
||||||
|
g2.cmd("set xrange [-" + std::to_string(range_3d) + ":" + std::to_string(range_3d) + "]");
|
||||||
|
g2.cmd("set yrange [-" + std::to_string(range_3d) + ":" + std::to_string(range_3d) + "]");
|
||||||
|
g2.cmd("set zrange [-" + std::to_string(range_3d) + ":" + std::to_string(range_3d) + "]");
|
||||||
|
g2.cmd("set view equal xyz");
|
||||||
|
|
||||||
|
g2.cmd("set style fill transparent solid 0.30 border\n set parametric\n set urange [0:2.0*pi]\n set vrange [-pi/2:pi/2]\n r = " +
|
||||||
|
std::to_string(ninty_sas) +
|
||||||
|
"\n fx(v,u) = r*cos(v)*cos(u)\n fy(v,u) = r*cos(v)*sin(u)\n fz(v) = r*sin(v) \n splot fx(v,u),fy(v,u),fz(v) title \"90\%-SAS\" lt rgb \"gray\"\n");
|
||||||
|
g2.plot_xyz(east, north, up, "3D Position Fixes");
|
||||||
|
|
||||||
|
g2.savetops("Position_test_3D");
|
||||||
|
g2.showonscreen(); // window output
|
||||||
|
}
|
||||||
|
catch (GnuplotException ge)
|
||||||
|
{
|
||||||
|
std::cout << ge.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(StaticPositionSystemTest, Position_system_test)
|
TEST_F(StaticPositionSystemTest, Position_system_test)
|
||||||
{
|
{
|
||||||
if(FLAGS_config_file_ptest.empty())
|
if(FLAGS_config_file_ptest.empty())
|
||||||
|
@ -33,14 +33,23 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
#include <gnuradio/fft/fft.h>
|
#include <gnuradio/fft/fft.h>
|
||||||
|
#include "gnuplot_i.h"
|
||||||
|
#include "test_flags.h"
|
||||||
|
|
||||||
|
|
||||||
DEFINE_int32(fft_iterations_test, 1000, "Number of averaged iterations in FFT length timing test");
|
DEFINE_int32(fft_iterations_test, 1000, "Number of averaged iterations in FFT length timing test");
|
||||||
|
DEFINE_bool(plot_fft_length_test, false, "Plots results of FFTLengthTest with gnuplot");
|
||||||
|
|
||||||
|
// Note from FFTW documentation: the standard FFTW distribution works most efficiently for arrays whose
|
||||||
|
// size can be factored into small primes (2, 3, 5, and 7), and otherwise it uses a slower general-purpose routine.
|
||||||
|
|
||||||
TEST(FFTLengthTest, MeasureExecutionTime)
|
TEST(FFTLengthTest, MeasureExecutionTime)
|
||||||
{
|
{
|
||||||
unsigned int fft_sizes [] = { 1000, 1024, 1960, 2000, 2048, 4000, 4096, 4725, 5000, 8000, 8192, 10368, 12000, 16000, 16384, 27000, 32768, 50000, 65536 };
|
unsigned int fft_sizes [] = { 512, 1000, 1024, 1100, 1297, 1400, 1500, 1960, 2000, 2048, 2221, 2500, 3000, 3500, 4000,
|
||||||
|
4096, 4200, 4500, 4725, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8192, 8500, 9000, 9500, 10000, 10368, 11000,
|
||||||
|
12000, 15000, 16000, 16384, 27000, 32768, 50000, 65536 };
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||||
|
|
||||||
@ -54,8 +63,12 @@ TEST(FFTLengthTest, MeasureExecutionTime)
|
|||||||
auto gen = std::bind(func, random_number1, random_number2); // Function that returns a random gr_complex
|
auto gen = std::bind(func, random_number1, random_number2); // Function that returns a random gr_complex
|
||||||
|
|
||||||
std::vector<unsigned int> fft_sizes_v(fft_sizes, fft_sizes + sizeof(fft_sizes) / sizeof(unsigned int) );
|
std::vector<unsigned int> fft_sizes_v(fft_sizes, fft_sizes + sizeof(fft_sizes) / sizeof(unsigned int) );
|
||||||
|
std::sort(fft_sizes_v.begin(), fft_sizes_v.end());
|
||||||
std::vector<unsigned int>::const_iterator it;
|
std::vector<unsigned int>::const_iterator it;
|
||||||
unsigned int d_fft_size;
|
unsigned int d_fft_size;
|
||||||
|
std::vector<double> execution_times;
|
||||||
|
std::vector<unsigned int> powers_of_two;
|
||||||
|
std::vector<double> execution_times_powers_of_two;
|
||||||
|
|
||||||
EXPECT_NO_THROW(
|
EXPECT_NO_THROW(
|
||||||
for(it = fft_sizes_v.cbegin(); it != fft_sizes_v.cend(); ++it)
|
for(it = fft_sizes_v.cbegin(); it != fft_sizes_v.cend(); ++it)
|
||||||
@ -73,9 +86,63 @@ TEST(FFTLengthTest, MeasureExecutionTime)
|
|||||||
}
|
}
|
||||||
end = std::chrono::system_clock::now();
|
end = std::chrono::system_clock::now();
|
||||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||||
double execution_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_iterations_test);
|
double exec_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_iterations_test);
|
||||||
std::cout << "FFT execution time for length=" << d_fft_size << " : " << execution_time << " [s]" << std::endl;
|
execution_times.push_back(exec_time * 1e3);
|
||||||
|
std::cout << "FFT execution time for length=" << d_fft_size << " : " << exec_time << " [s]" << std::endl;
|
||||||
delete d_fft;
|
delete d_fft;
|
||||||
|
|
||||||
|
if( (d_fft_size & (d_fft_size - 1)) == 0 ) // if it is a power of two
|
||||||
|
{
|
||||||
|
powers_of_two.push_back(d_fft_size);
|
||||||
|
execution_times_powers_of_two.push_back(exec_time / 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(FLAGS_plot_fft_length_test == true)
|
||||||
|
{
|
||||||
|
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||||
|
if(gnuplot_executable.empty())
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: Although the flag plot_fft_length_test 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
|
||||||
|
{
|
||||||
|
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("linespoints");
|
||||||
|
g1.set_title("FFT execution times for different lengths");
|
||||||
|
g1.set_grid();
|
||||||
|
g1.set_xlabel("FFT length");
|
||||||
|
g1.set_ylabel("Execution time [ms]");
|
||||||
|
g1.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(FLAGS_fft_iterations_test) + " iterations)");
|
||||||
|
g1.set_style("points").plot_xy(powers_of_two, execution_times_powers_of_two, "Power of 2");
|
||||||
|
g1.savetops("FFT_execution_times_extended");
|
||||||
|
g1.showonscreen(); // window output
|
||||||
|
|
||||||
|
Gnuplot g2("linespoints");
|
||||||
|
g2.set_title("FFT execution times for different lengths (up to 2^{14}=16384)");
|
||||||
|
g2.set_grid();
|
||||||
|
g2.set_xlabel("FFT length");
|
||||||
|
g2.set_ylabel("Execution time [ms]");
|
||||||
|
g2.set_xrange(0, 16384);
|
||||||
|
g2.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(FLAGS_fft_iterations_test) + " iterations)");
|
||||||
|
g2.set_style("points").plot_xy(powers_of_two, execution_times_powers_of_two, "Power of 2");
|
||||||
|
g2.savetops("FFT_execution_times");
|
||||||
|
g2.showonscreen(); // window output
|
||||||
|
}
|
||||||
|
catch (GnuplotException ge)
|
||||||
|
{
|
||||||
|
std::cout << ge.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user