1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2025-01-17 12:43:01 +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:
Carles Fernandez 2017-10-22 20:37:54 +02:00
commit 0eb864a498
5 changed files with 2208 additions and 3 deletions

View File

@ -158,6 +158,11 @@ if(ENABLE_FPGA)
add_definitions(-DFPGA_BLOCKS_TEST=1)
endif(ENABLE_FPGA)
find_package(Gnuplot)
if(GNUPLOT_FOUND)
add_definitions(-DGNUPLOT_EXECUTABLE="${GNUPLOT_EXECUTABLE}")
endif(GNUPLOT_FOUND)
################################################################################
# Optional generator
################################################################################

File diff suppressed because it is too large Load Diff

View 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

View File

@ -29,11 +29,13 @@
* -------------------------------------------------------------------------
*/
#include <algorithm>
#include <chrono>
#include <cmath>
#include <fstream>
#include <numeric>
#include <thread>
#include <boost/filesystem.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
@ -43,10 +45,13 @@
#include "in_memory_configuration.h"
#include "file_configuration.h"
#include "MATH_CONSTANTS.h"
#include "gnuplot_i.h"
#include "test_flags.h"
#include "signal_generator_flags.h"
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)
concurrent_queue<Gps_Acq_Assist> global_gps_acq_assist_queue;
@ -73,6 +78,9 @@ public:
int configure_receiver();
int run_receiver();
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_accuracy(const std::vector<double> & vec, double ref);
@ -542,9 +550,102 @@ void StaticPositionSystemTest::check_results()
// Sanity Check
double precision_SEP = 0.51 * (sigma_E_2_precision + sigma_N_2_precision + sigma_U_2_precision);
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)
{
if(FLAGS_config_file_ptest.empty())

View File

@ -33,14 +33,23 @@
#include <chrono>
#include <functional>
#include <random>
#include <boost/filesystem.hpp>
#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_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)
{
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;
@ -54,8 +63,12 @@ TEST(FFTLengthTest, MeasureExecutionTime)
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::sort(fft_sizes_v.begin(), fft_sizes_v.end());
std::vector<unsigned int>::const_iterator it;
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(
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();
std::chrono::duration<double> elapsed_seconds = end - start;
double execution_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;
double exec_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_iterations_test);
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;
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;
}
}
}
}