mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2025-12-05 16:18:06 +00:00
Remove build and data folders, move tests and utils to the base of the source tree
This commit is contained in:
399
tests/unit-tests/arithmetic/code_generation_test.cc
Normal file
399
tests/unit-tests/arithmetic/code_generation_test.cc
Normal file
@@ -0,0 +1,399 @@
|
||||
/*!
|
||||
* \file code_generation_test.cc
|
||||
* \brief This file implements tests for the generation of complex
|
||||
* exponentials.
|
||||
* \author Carles Fernandez-Prades, 2014. 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 "Galileo_E6.h"
|
||||
#include "galileo_e6_signal_replica.h"
|
||||
#include "gnss_signal_replica.h"
|
||||
#include "gps_sdr_signal_replica.h"
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
|
||||
TEST(CodeGenerationTest, CodeGenGPSL1Test)
|
||||
{
|
||||
std::array<std::complex<float>, 1023> _dest{};
|
||||
signed int _prn = 1;
|
||||
unsigned int _chip_shift = 4;
|
||||
|
||||
int iterations = 1000;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
gps_l1_ca_code_gen_complex(_dest, _prn, _chip_shift);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
ASSERT_LE(0, elapsed_seconds.count());
|
||||
std::cout << "Generation completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST(CodeGenerationTest, CodeGenGPSL1SampledTest)
|
||||
{
|
||||
signed int _prn = 1;
|
||||
unsigned int _chip_shift = 4;
|
||||
double _fs = 8000000.0;
|
||||
const signed int codeFreqBasis = 1023000; // Hz
|
||||
const signed int codeLength = 1023;
|
||||
int samplesPerCode = round(_fs / static_cast<double>(codeFreqBasis / codeLength));
|
||||
std::vector<std::complex<float>> _dest(samplesPerCode);
|
||||
|
||||
int iterations = 1000;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
gps_l1_ca_code_gen_complex_sampled(_dest, _prn, _fs, _chip_shift);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
ASSERT_LE(0, elapsed_seconds.count());
|
||||
std::cout << "Generation completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST(CodeGenerationTest, ComplexConjugateTest)
|
||||
{
|
||||
double _fs = 8000000.0;
|
||||
double _f = 4000.0;
|
||||
const signed int codeFreqBasis = 1023000; // Hz
|
||||
const signed int codeLength = 1023;
|
||||
int samplesPerCode = round(_fs / static_cast<double>(codeFreqBasis / codeLength));
|
||||
std::vector<std::complex<float>> _dest(samplesPerCode);
|
||||
|
||||
int iterations = 1000;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
complex_exp_gen_conj(_dest, _f, _fs);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
ASSERT_LE(0, elapsed_seconds.count());
|
||||
std::cout << "Generation completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST(CodeGenerationTest, GalileoE6CSecondaryTest)
|
||||
{
|
||||
const auto len = static_cast<int32_t>(GALILEO_E6_C_SECONDARY_CODE_LENGTH_CHIPS);
|
||||
std::array<float, len> secondary_code{};
|
||||
int prn = 1;
|
||||
galileo_e6_c_secondary_code_gen_float(secondary_code, prn);
|
||||
std::array<float, len> expected_output = {-1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1,
|
||||
-1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1,
|
||||
-1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1};
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
ASSERT_FLOAT_EQ(secondary_code[i], expected_output[i]);
|
||||
}
|
||||
prn = 2;
|
||||
std::string generated_string = galileo_e6_c_secondary_code(prn);
|
||||
std::string expected_string("0110011001010101100010111101001111001110000011000111011110010010111010000011001101010000010100100101");
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
ASSERT_FLOAT_EQ(generated_string[i], expected_string[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CodeGenerationTest, GalileoE6BTest)
|
||||
{
|
||||
const auto len = static_cast<int32_t>(GALILEO_E6_B_CODE_LENGTH_CHIPS);
|
||||
std::array<std::complex<float>, len> gale6b_code{};
|
||||
int prn = 1;
|
||||
galileo_e6_b_code_gen_complex_primary(gale6b_code, prn);
|
||||
std::array<std::complex<float>, len> expected_output = {};
|
||||
|
||||
std::array<float, len> expected_bin = {-1, -1, -1, 1, 1, -1, -1, 1, 1, -1,
|
||||
-1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1,
|
||||
-1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1,
|
||||
-1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1,
|
||||
-1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1,
|
||||
1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1,
|
||||
1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1,
|
||||
-1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1,
|
||||
1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1,
|
||||
-1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1,
|
||||
-1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1,
|
||||
-1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1,
|
||||
1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1,
|
||||
-1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1,
|
||||
-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1,
|
||||
1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1,
|
||||
-1, 1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1,
|
||||
-1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1,
|
||||
-1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1,
|
||||
-1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1,
|
||||
1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1,
|
||||
1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, -1, 1,
|
||||
-1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1,
|
||||
-1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1,
|
||||
-1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1,
|
||||
1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1,
|
||||
-1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1,
|
||||
1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1,
|
||||
-1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1,
|
||||
-1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1,
|
||||
-1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1,
|
||||
1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1,
|
||||
-1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1,
|
||||
1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1,
|
||||
-1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1,
|
||||
-1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1,
|
||||
-1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1,
|
||||
1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1,
|
||||
-1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1,
|
||||
-1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1,
|
||||
1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1,
|
||||
-1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1,
|
||||
1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1,
|
||||
-1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1,
|
||||
-1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1,
|
||||
1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1,
|
||||
1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1,
|
||||
-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1,
|
||||
-1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1,
|
||||
1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1,
|
||||
-1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1,
|
||||
-1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1,
|
||||
-1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1,
|
||||
1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1,
|
||||
-1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1,
|
||||
1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1,
|
||||
1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1,
|
||||
1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1,
|
||||
1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1,
|
||||
-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1,
|
||||
1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1,
|
||||
1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1,
|
||||
1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1,
|
||||
-1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1,
|
||||
1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1,
|
||||
-1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1,
|
||||
-1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1,
|
||||
-1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1,
|
||||
-1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1,
|
||||
-1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1,
|
||||
1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1,
|
||||
-1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1,
|
||||
1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1,
|
||||
-1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1,
|
||||
1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1,
|
||||
1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1,
|
||||
1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1,
|
||||
-1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1,
|
||||
-1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1,
|
||||
-1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1,
|
||||
1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1,
|
||||
1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1,
|
||||
1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1,
|
||||
1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1,
|
||||
1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1,
|
||||
-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1,
|
||||
-1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1,
|
||||
1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1,
|
||||
-1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1,
|
||||
-1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1,
|
||||
1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1,
|
||||
1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1,
|
||||
1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, -1,
|
||||
1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1,
|
||||
-1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1,
|
||||
-1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1,
|
||||
-1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1,
|
||||
1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1,
|
||||
-1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1,
|
||||
-1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1,
|
||||
-1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1,
|
||||
1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1,
|
||||
-1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1,
|
||||
-1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1,
|
||||
1, -1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1,
|
||||
1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1,
|
||||
-1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1,
|
||||
-1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1,
|
||||
1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1,
|
||||
-1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1,
|
||||
-1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1,
|
||||
-1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1,
|
||||
-1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1,
|
||||
-1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1,
|
||||
-1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1,
|
||||
-1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1,
|
||||
-1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1,
|
||||
1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1,
|
||||
-1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1,
|
||||
-1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1,
|
||||
1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1,
|
||||
-1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1,
|
||||
-1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1,
|
||||
-1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1,
|
||||
-1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1,
|
||||
1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1,
|
||||
1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1,
|
||||
1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1,
|
||||
-1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1,
|
||||
-1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1,
|
||||
-1, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1,
|
||||
-1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1,
|
||||
-1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1,
|
||||
1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1,
|
||||
-1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1,
|
||||
-1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1,
|
||||
1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1,
|
||||
1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1,
|
||||
-1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1,
|
||||
-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1,
|
||||
-1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1,
|
||||
-1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1,
|
||||
1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1,
|
||||
-1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1,
|
||||
1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1,
|
||||
-1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1,
|
||||
-1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1,
|
||||
-1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, -1,
|
||||
-1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1,
|
||||
-1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1,
|
||||
1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1,
|
||||
1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1,
|
||||
-1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1,
|
||||
-1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1,
|
||||
-1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1,
|
||||
-1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1,
|
||||
-1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, 1,
|
||||
-1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1,
|
||||
-1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1,
|
||||
1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1,
|
||||
1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, 1,
|
||||
1, 1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1,
|
||||
-1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1,
|
||||
-1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1,
|
||||
-1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1,
|
||||
1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1,
|
||||
-1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1,
|
||||
-1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1,
|
||||
-1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1,
|
||||
-1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1,
|
||||
1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1,
|
||||
1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1,
|
||||
-1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1,
|
||||
-1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1,
|
||||
-1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1,
|
||||
-1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, 1,
|
||||
-1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1,
|
||||
-1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1,
|
||||
-1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1,
|
||||
-1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1,
|
||||
-1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1,
|
||||
1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1,
|
||||
-1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1,
|
||||
-1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1,
|
||||
1, 1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1,
|
||||
1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1,
|
||||
1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1,
|
||||
-1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1,
|
||||
-1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1,
|
||||
-1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1,
|
||||
-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1,
|
||||
1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1,
|
||||
-1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1,
|
||||
1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1,
|
||||
1, -1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, -1,
|
||||
1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1,
|
||||
1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1,
|
||||
-1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1,
|
||||
-1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1,
|
||||
1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, -1,
|
||||
-1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1,
|
||||
-1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1,
|
||||
-1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1,
|
||||
-1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, 1, 1,
|
||||
1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1,
|
||||
-1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1,
|
||||
1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1,
|
||||
-1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1,
|
||||
-1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1,
|
||||
1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1,
|
||||
-1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1,
|
||||
-1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1,
|
||||
1, -1, -1, 1};
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
expected_output[i] = std::complex<float>(expected_bin[i], 0.0);
|
||||
}
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
ASSERT_FLOAT_EQ(gale6b_code[i].real(), expected_output[i].real());
|
||||
}
|
||||
}
|
||||
190
tests/unit-tests/arithmetic/complex_carrier_test.cc
Normal file
190
tests/unit-tests/arithmetic/complex_carrier_test.cc
Normal file
@@ -0,0 +1,190 @@
|
||||
/*!
|
||||
* \file complex_carrier_test.cc
|
||||
* \brief This file implements tests for the generation of complex exponentials.
|
||||
* \author Carles Fernandez-Prades, 2014. 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 "GPS_L1_CA.h"
|
||||
#include "gnss_signal_replica.h"
|
||||
#include <armadillo>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(size_carrier_test, 100000, "Size of the arrays used for complex carrier testing");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, size_carrier_test, 100000, "Size of the arrays used for complex carrier testing");
|
||||
#endif
|
||||
|
||||
TEST(ComplexCarrierTest, StandardComplexImplementation)
|
||||
{
|
||||
// Dynamic allocation creates new usable space on the program STACK
|
||||
// (an area of RAM specifically allocated to the program)
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* output = new std::complex<float>[FLAGS_size_carrier_test];
|
||||
#else
|
||||
auto* output = new std::complex<float>[absl::GetFlag(FLAGS_size_carrier_test)];
|
||||
#endif
|
||||
const double _f = 2000.0;
|
||||
const double _fs = 2000000.0;
|
||||
const auto phase_step = (TWO_PI * _f) / _fs;
|
||||
double phase = 0.0;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
output[i] = std::complex<float>(cos(phase), sin(phase));
|
||||
phase += phase_step;
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "A " << FLAGS_size_carrier_test
|
||||
#else
|
||||
std::cout << "A " << absl::GetFlag(FLAGS_size_carrier_test)
|
||||
#endif
|
||||
<< "-length complex carrier in standard C++ (dynamic allocation) generated in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
|
||||
std::complex<float> expected(1, 0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::vector<std::complex<float>> mag(FLAGS_size_carrier_test);
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
std::vector<std::complex<float>> mag(absl::GetFlag(FLAGS_size_carrier_test));
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
mag[i] = output[i] * std::conj(output[i]);
|
||||
}
|
||||
delete[] output;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
ASSERT_FLOAT_EQ(std::norm(expected), std::norm(mag[i]));
|
||||
}
|
||||
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(ComplexCarrierTest, C11ComplexImplementation)
|
||||
{
|
||||
// declaration: load data onto the program data segment
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::vector<std::complex<float>> output(FLAGS_size_carrier_test);
|
||||
#else
|
||||
std::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_carrier_test));
|
||||
#endif
|
||||
const double _f = 2000.0;
|
||||
const double _fs = 2000000.0;
|
||||
const auto phase_step = (TWO_PI * _f) / _fs;
|
||||
double phase = 0.0;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
output[i] = std::complex<float>(cos(phase), sin(phase));
|
||||
phase += phase_step;
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "A " << FLAGS_size_carrier_test
|
||||
#else
|
||||
std::cout << "A " << absl::GetFlag(FLAGS_size_carrier_test)
|
||||
#endif
|
||||
<< "-length complex carrier in standard C++ (declaration) generated in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
std::complex<float> expected(1, 0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::vector<std::complex<float>> mag(FLAGS_size_carrier_test);
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
std::vector<std::complex<float>> mag(absl::GetFlag(FLAGS_size_carrier_test));
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
mag[i] = output[i] * std::conj(output[i]);
|
||||
ASSERT_FLOAT_EQ(std::norm(expected), std::norm(mag[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(ComplexCarrierTest, OwnComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::vector<std::complex<float>> output(FLAGS_size_carrier_test);
|
||||
#else
|
||||
std::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_carrier_test));
|
||||
#endif
|
||||
double _f = 2000.0;
|
||||
double _fs = 2000000.0;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
complex_exp_gen(output, _f, _fs);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "A " << FLAGS_size_carrier_test
|
||||
#else
|
||||
std::cout << "A " << absl::GetFlag(FLAGS_size_carrier_test)
|
||||
#endif
|
||||
<< "-length complex carrier using fixed point generated in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
|
||||
std::complex<float> expected(1, 0);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::vector<std::complex<float>> mag(FLAGS_size_carrier_test);
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
std::vector<std::complex<float>> mag(absl::GetFlag(FLAGS_size_carrier_test));
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
mag[i] = output[i] * std::conj(output[i]);
|
||||
}
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_carrier_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_carrier_test); i++)
|
||||
#endif
|
||||
{
|
||||
ASSERT_NEAR(std::norm(expected), std::norm(mag[i]), 0.0001);
|
||||
}
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
195
tests/unit-tests/arithmetic/conjugate_test.cc
Normal file
195
tests/unit-tests/arithmetic/conjugate_test.cc
Normal file
@@ -0,0 +1,195 @@
|
||||
/*!
|
||||
* \file conjugate_test.cc
|
||||
* \brief This file implements tests for conjugation of long arrays.
|
||||
* \author Carles Fernandez-Prades, 2012. 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 <armadillo>
|
||||
#include <volk/volk.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr_alloc.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(size_conjugate_test, 100000, "Size of the arrays used for conjugate testing");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, size_conjugate_test, 100000, "Size of the arrays used for conjugate testing");
|
||||
#endif
|
||||
|
||||
|
||||
TEST(ConjugateTest, StandardCComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = new std::complex<float>[FLAGS_size_conjugate_test];
|
||||
auto* output = new std::complex<float>[FLAGS_size_conjugate_test];
|
||||
std::fill_n(input, FLAGS_size_conjugate_test, std::complex<float>(0.0, 0.0));
|
||||
#else
|
||||
auto* input = new std::complex<float>[absl::GetFlag(FLAGS_size_conjugate_test)];
|
||||
auto* output = new std::complex<float>[absl::GetFlag(FLAGS_size_conjugate_test)];
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_conjugate_test), std::complex<float>(0.0, 0.0));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_conjugate_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_conjugate_test); i++)
|
||||
#endif
|
||||
{
|
||||
output[i] = std::conj(input[i]);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Conjugate of a " << FLAGS_size_conjugate_test
|
||||
#else
|
||||
std::cout << "Conjugate of a " << absl::GetFlag(FLAGS_size_conjugate_test)
|
||||
#endif
|
||||
<< "-length complex float vector in standard C finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
|
||||
delete[] input;
|
||||
delete[] output;
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(ConjugateTest, C11ComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::vector<std::complex<float>> input(FLAGS_size_conjugate_test);
|
||||
std::vector<std::complex<float>> output(FLAGS_size_conjugate_test);
|
||||
#else
|
||||
const std::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
std::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
int pos = 0;
|
||||
for (const auto& item : input)
|
||||
{
|
||||
output[pos++] = std::conj(item);
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Conjugate of a " << FLAGS_size_conjugate_test
|
||||
#else
|
||||
std::cout << "Conjugate of a " << absl::GetFlag(FLAGS_size_conjugate_test)
|
||||
#endif
|
||||
<< " complex<float> vector (C++11-style) finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
|
||||
std::complex<float> expected(0, 0);
|
||||
std::complex<float> result(0, 0);
|
||||
for (const auto& item : output)
|
||||
{
|
||||
result += item;
|
||||
}
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
|
||||
TEST(ConjugateTest, ArmadilloComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
arma::cx_fvec input(FLAGS_size_conjugate_test, arma::fill::zeros);
|
||||
arma::cx_fvec output(FLAGS_size_conjugate_test);
|
||||
#else
|
||||
arma::cx_fvec input(absl::GetFlag(FLAGS_size_conjugate_test), arma::fill::zeros);
|
||||
arma::cx_fvec output(absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
output = arma::conj(input);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Conjugate of a " << FLAGS_size_conjugate_test
|
||||
#else
|
||||
std::cout << "Conjugate of a " << absl::GetFlag(FLAGS_size_conjugate_test)
|
||||
#endif
|
||||
<< "-length complex float Armadillo vector finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(ConjugateTest, VolkComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(FLAGS_size_conjugate_test * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
auto* output = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(FLAGS_size_conjugate_test * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, FLAGS_size_conjugate_test, std::complex<float>(0.0, 0.0));
|
||||
#else
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_conjugate_test) * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
auto* output = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_conjugate_test) * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_conjugate_test), std::complex<float>(0.0, 0.0));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_conjugate_32fc(output, input, FLAGS_size_conjugate_test);
|
||||
#else
|
||||
volk_32fc_conjugate_32fc(output, input, absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
#endif
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Conjugate of a " << FLAGS_size_conjugate_test
|
||||
#else
|
||||
std::cout << "Conjugate of a " << absl::GetFlag(FLAGS_size_conjugate_test)
|
||||
#endif
|
||||
<< "-length complex float vector using VOLK finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
volk_gnsssdr_free(input);
|
||||
volk_gnsssdr_free(output);
|
||||
}
|
||||
|
||||
|
||||
TEST(ConjugateTest, VolkComplexImplementationAlloc)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_gnsssdr::vector<std::complex<float>> input(FLAGS_size_conjugate_test, std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<std::complex<float>> output(FLAGS_size_conjugate_test);
|
||||
#else
|
||||
volk_gnsssdr::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_conjugate_test), std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_conjugate_32fc(output.data(), input.data(), FLAGS_size_conjugate_test);
|
||||
#else
|
||||
volk_32fc_conjugate_32fc(output.data(), input.data(), absl::GetFlag(FLAGS_size_conjugate_test));
|
||||
#endif
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Conjugate of a " << FLAGS_size_conjugate_test
|
||||
#else
|
||||
std::cout << "Conjugate of a " << absl::GetFlag(FLAGS_size_conjugate_test)
|
||||
#endif
|
||||
<< "-length complex float vector using VOLK ALLOC finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
186
tests/unit-tests/arithmetic/fft_length_test.cc
Normal file
186
tests/unit-tests/arithmetic/fft_length_test.cc
Normal file
@@ -0,0 +1,186 @@
|
||||
/*!
|
||||
* \file fft_length_test.cc
|
||||
* \brief This file implements timing tests for the FFT.
|
||||
* \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 "gnss_sdr_fft.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "test_flags.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
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");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, fft_iterations_test, 1000, "Number of averaged iterations in FFT length timing test");
|
||||
ABSL_FLAG(bool, plot_fft_length_test, false, "Plots results of FFTLengthTest with gnuplot");
|
||||
#endif
|
||||
|
||||
// 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[] = {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::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::default_random_engine e2(r());
|
||||
std::uniform_real_distribution<float> uniform_dist(-1, 1);
|
||||
auto func = [](float a, float b) { return gr_complex(a, b); }; // Helper lambda function that returns a gr_complex
|
||||
auto random_number1 = std::bind(uniform_dist, e1);
|
||||
auto random_number2 = std::bind(uniform_dist, e2);
|
||||
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) {
|
||||
d_fft_size = *it;
|
||||
auto d_fft = gnss_fft_fwd_make_unique(d_fft_size);
|
||||
std::generate_n(d_fft->get_inbuf(), d_fft_size, gen);
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_fft_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_fft_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
d_fft->execute();
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
double exec_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_iterations_test);
|
||||
#else
|
||||
double exec_time = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_fft_iterations_test));
|
||||
#endif
|
||||
execution_times.push_back(exec_time * 1e3);
|
||||
std::cout << "FFT execution time for length=" << d_fft_size << " : " << exec_time << " [s]\n";
|
||||
|
||||
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 USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_fft_length_test == true)
|
||||
{
|
||||
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_fft_length_test) == true)
|
||||
{
|
||||
const std::string gnuplot_executable(absl::GetFlag(FLAGS_gnuplot_executable));
|
||||
#endif
|
||||
if (gnuplot_executable.empty())
|
||||
{
|
||||
std::cout << "WARNING: Although the flag plot_fft_length_test has been set to TRUE,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string& gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("linespoints");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
g1.set_title("FFT execution times for different lengths");
|
||||
g1.set_grid();
|
||||
g1.set_xlabel("FFT length");
|
||||
g1.set_ylabel("Execution time [ms]");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
g1.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(FLAGS_fft_iterations_test) + " iterations)");
|
||||
#else
|
||||
g1.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(absl::GetFlag(FLAGS_fft_iterations_test)) + " iterations)");
|
||||
#endif
|
||||
g1.set_style("points").plot_xy(powers_of_two, execution_times_powers_of_two, "Power of 2");
|
||||
g1.savetops("FFT_execution_times_extended");
|
||||
g1.savetopdf("FFT_execution_times_extended", 18);
|
||||
|
||||
Gnuplot g2("linespoints");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g2.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g2.disablescreen();
|
||||
}
|
||||
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);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
g2.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(FLAGS_fft_iterations_test) + " iterations)");
|
||||
#else
|
||||
g2.plot_xy(fft_sizes_v, execution_times, "FFT execution time (averaged over " + std::to_string(absl::GetFlag(FLAGS_fft_iterations_test)) + " iterations)");
|
||||
#endif
|
||||
|
||||
g2.set_style("points").plot_xy(powers_of_two, execution_times_powers_of_two, "Power of 2");
|
||||
g2.savetops("FFT_execution_times");
|
||||
g2.savetopdf("FFT_execution_times", 18);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g2.showonscreen(); // window output
|
||||
}
|
||||
}
|
||||
catch (const GnuplotException& ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
tests/unit-tests/arithmetic/fft_speed_test.cc
Normal file
85
tests/unit-tests/arithmetic/fft_speed_test.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* \file fft_speed_test.cc
|
||||
* \brief This file implements timing tests for the Armadillo
|
||||
* and GNU Radio FFT implementations
|
||||
* \author Antonio Ramos, 2017. antonio.ramos(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 "gnss_sdr_fft.h"
|
||||
#include <armadillo>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(fft_speed_iterations_test, 100, "Number of averaged iterations in FFT length timing test");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, fft_speed_iterations_test, 100, "Number of averaged iterations in FFT length timing test");
|
||||
#endif
|
||||
|
||||
TEST(FFTSpeedTest, ArmadilloVSGNURadioExecutionTime)
|
||||
{
|
||||
unsigned int d_fft_size;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds;
|
||||
|
||||
unsigned int fft_sizes[19] = {16, 25, 32, 45, 64, 95, 128, 195, 256, 325, 512, 785, 1024, 1503, 2048, 3127, 4096, 6349, 8192};
|
||||
double d_execution_time;
|
||||
EXPECT_NO_THROW(
|
||||
for (unsigned int fft_size
|
||||
: fft_sizes) {
|
||||
d_fft_size = fft_size;
|
||||
auto d_gr_fft = gnss_fft_fwd_make_unique(d_fft_size);
|
||||
arma::arma_rng::set_seed_random();
|
||||
arma::cx_fvec d_arma_fft = arma::cx_fvec(d_fft_size).randn() + gr_complex(0.0, 1.0) * arma::cx_fvec(d_fft_size).randn();
|
||||
arma::cx_fvec d_arma_fft_result(d_fft_size);
|
||||
std::copy_n(d_arma_fft.memptr(), d_fft_size, d_gr_fft->get_inbuf());
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_fft_speed_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_fft_speed_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
d_gr_fft->execute();
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
d_execution_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_speed_iterations_test);
|
||||
#else
|
||||
d_execution_time = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_fft_speed_iterations_test));
|
||||
#endif
|
||||
std::cout << "GNU Radio FFT execution time for length = " << d_fft_size << " : " << d_execution_time * 1e6 << " [us]\n";
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_fft_speed_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_fft_speed_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
d_arma_fft_result = arma::fft(d_arma_fft);
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
d_execution_time = elapsed_seconds.count() / static_cast<double>(FLAGS_fft_speed_iterations_test);
|
||||
#else
|
||||
d_execution_time = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_fft_speed_iterations_test));
|
||||
#endif
|
||||
std::cout << "Armadillo FFT execution time for length = " << d_fft_size << " : " << d_execution_time * 1e6 << " [us]\n";
|
||||
});
|
||||
}
|
||||
212
tests/unit-tests/arithmetic/magnitude_squared_test.cc
Normal file
212
tests/unit-tests/arithmetic/magnitude_squared_test.cc
Normal file
@@ -0,0 +1,212 @@
|
||||
/*!
|
||||
* \file magnitude_squared_test.cc
|
||||
* \brief This file implements tests for the computation of magnitude squared
|
||||
* in long arrays.
|
||||
* \author Carles Fernandez-Prades, 2014. 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 <armadillo>
|
||||
#include <volk/volk.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr_alloc.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(size_magnitude_test, 100000, "Size of the arrays used for magnitude testing");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, size_magnitude_test, 100000, "Size of the arrays used for magnitude testing");
|
||||
#endif
|
||||
|
||||
TEST(MagnitudeSquaredTest, StandardCComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = new std::complex<float>[FLAGS_size_magnitude_test];
|
||||
auto* output = new float[FLAGS_size_magnitude_test];
|
||||
#else
|
||||
auto* input = new std::complex<float>[absl::GetFlag(FLAGS_size_magnitude_test)];
|
||||
auto* output = new float[absl::GetFlag(FLAGS_size_magnitude_test)];
|
||||
#endif
|
||||
unsigned int number = 0;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (number = 0; number < static_cast<unsigned int>(FLAGS_size_magnitude_test); number++)
|
||||
#else
|
||||
for (number = 0; number < static_cast<unsigned int>(absl::GetFlag(FLAGS_size_magnitude_test)); number++)
|
||||
#endif
|
||||
{
|
||||
input[number] = std::complex<float>(0.0, 0.0);
|
||||
}
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (number = 0; number < static_cast<unsigned int>(FLAGS_size_magnitude_test); number++)
|
||||
#else
|
||||
for (number = 0; number < static_cast<unsigned int>(absl::GetFlag(FLAGS_size_magnitude_test)); number++)
|
||||
#endif
|
||||
{
|
||||
output[number] = (input[number].real() * input[number].real()) + (input[number].imag() * input[number].imag());
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "The squared magnitude of a " << FLAGS_size_magnitude_test
|
||||
#else
|
||||
std::cout << "The squared magnitude of a " << absl::GetFlag(FLAGS_size_magnitude_test)
|
||||
#endif
|
||||
<< "-length complex vector in standard C computed in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
delete[] input;
|
||||
delete[] output;
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(MagnitudeSquaredTest, C11ComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::vector<std::complex<float>> input(FLAGS_size_magnitude_test);
|
||||
std::vector<float> output(FLAGS_size_magnitude_test);
|
||||
#else
|
||||
const std::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_magnitude_test));
|
||||
std::vector<float> output(absl::GetFlag(FLAGS_size_magnitude_test));
|
||||
#endif
|
||||
int pos = 0;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
for (const auto& item : input)
|
||||
{
|
||||
output[pos++] = std::norm(item);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "The squared magnitude of a " << FLAGS_size_magnitude_test
|
||||
#else
|
||||
std::cout << "The squared magnitude of a " << absl::GetFlag(FLAGS_size_magnitude_test)
|
||||
#endif
|
||||
<< " complex<float> vector (C++11-style) finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
|
||||
std::complex<float> expected(0, 0);
|
||||
std::complex<float> result(0, 0);
|
||||
for (const auto& item : output)
|
||||
{
|
||||
result += item;
|
||||
}
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
|
||||
TEST(MagnitudeSquaredTest, ArmadilloComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
arma::cx_fvec input(FLAGS_size_magnitude_test, arma::fill::zeros);
|
||||
arma::fvec output(FLAGS_size_magnitude_test);
|
||||
#else
|
||||
arma::cx_fvec input(absl::GetFlag(FLAGS_size_magnitude_test), arma::fill::zeros);
|
||||
arma::fvec output(absl::GetFlag(FLAGS_size_magnitude_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
output = arma::abs(arma::square(input));
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "The squared magnitude of a " << FLAGS_size_magnitude_test
|
||||
#else
|
||||
std::cout << "The squared magnitude of a " << absl::GetFlag(FLAGS_size_magnitude_test)
|
||||
#endif
|
||||
<< "-length vector using Armadillo computed in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(MagnitudeSquaredTest, VolkComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(FLAGS_size_magnitude_test * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, FLAGS_size_magnitude_test, std::complex<float>(0.0, 0.0));
|
||||
auto* output = static_cast<float*>(volk_gnsssdr_malloc(FLAGS_size_magnitude_test * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
#else
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_magnitude_test) * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_magnitude_test), std::complex<float>(0.0, 0.0));
|
||||
auto* output = static_cast<float*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_magnitude_test) * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_magnitude_squared_32f(output, input, static_cast<unsigned int>(FLAGS_size_magnitude_test));
|
||||
#else
|
||||
volk_32fc_magnitude_squared_32f(output, input, static_cast<unsigned int>(absl::GetFlag(FLAGS_size_magnitude_test)));
|
||||
#endif
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "The squared magnitude of a " << FLAGS_size_magnitude_test
|
||||
#else
|
||||
std::cout << "The squared magnitude of a " << absl::GetFlag(FLAGS_size_magnitude_test)
|
||||
#endif
|
||||
<< "-length vector using VOLK computed in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
volk_gnsssdr_free(input);
|
||||
volk_gnsssdr_free(output);
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
|
||||
TEST(MagnitudeSquaredTest, VolkComplexImplementationAlloc)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_gnsssdr::vector<std::complex<float>> input(FLAGS_size_magnitude_test); // or: input(FLAGS_size_magnitude_test, std::complex<float>(0.0, 0.0));
|
||||
std::fill_n(input.begin(), FLAGS_size_magnitude_test, std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<float> output(FLAGS_size_magnitude_test);
|
||||
#else
|
||||
volk_gnsssdr::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_magnitude_test));
|
||||
std::fill_n(input.begin(), absl::GetFlag(FLAGS_size_magnitude_test), std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<float> output(absl::GetFlag(FLAGS_size_magnitude_test));
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_magnitude_squared_32f(output.data(), input.data(), static_cast<unsigned int>(FLAGS_size_magnitude_test));
|
||||
#else
|
||||
volk_32fc_magnitude_squared_32f(output.data(), input.data(), static_cast<unsigned int>(absl::GetFlag(FLAGS_size_magnitude_test)));
|
||||
#endif
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "The squared magnitude of a " << FLAGS_size_magnitude_test
|
||||
#else
|
||||
std::cout << "The squared magnitude of a " << absl::GetFlag(FLAGS_size_magnitude_test)
|
||||
#endif
|
||||
<< "-length vector using VOLK ALLOC computed in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
}
|
||||
|
||||
// volk_32f_accumulator_s32f(&d_input_power, d_magnitude, d_fft_size);
|
||||
150
tests/unit-tests/arithmetic/matio_test.cc
Normal file
150
tests/unit-tests/arithmetic/matio_test.cc
Normal file
@@ -0,0 +1,150 @@
|
||||
/*!
|
||||
* \file matio_test.cc
|
||||
* \brief This file implements tests for the matio library
|
||||
* in long arrays.
|
||||
* \author Carles Fernandez-Prades, 2017. 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 "gnss_sdr_filesystem.h"
|
||||
#include <gnuradio/gr_complex.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <matio.h>
|
||||
#include <array>
|
||||
|
||||
TEST(MatioTest, WriteAndReadDoubles)
|
||||
{
|
||||
// Write a .mat file
|
||||
mat_t *matfp;
|
||||
matvar_t *matvar;
|
||||
std::string filename = "./test.mat";
|
||||
matfp = Mat_CreateVer(filename.c_str(), nullptr, MAT_FT_MAT73);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matfp) == nullptr) << "Error creating .mat file";
|
||||
|
||||
std::array<double, 10> x{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
std::array<size_t, 2> dims{10, 1};
|
||||
matvar = Mat_VarCreate("x", MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims.data(), x.data(), 0);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matvar) == nullptr) << "Error creating variable for ’x’";
|
||||
|
||||
Mat_VarWrite(matfp, matvar, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE
|
||||
Mat_VarFree(matvar);
|
||||
|
||||
Mat_Close(matfp);
|
||||
|
||||
// Read a .mat file
|
||||
mat_t *matfp_read;
|
||||
matvar_t *matvar_read;
|
||||
|
||||
matfp_read = Mat_Open(filename.c_str(), MAT_ACC_RDONLY);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matfp_read) == nullptr) << "Error reading .mat file";
|
||||
|
||||
matvar_read = Mat_VarReadInfo(matfp_read, "x");
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matvar_read) == nullptr) << "Error reading variable in .mat file";
|
||||
|
||||
matvar_read = Mat_VarRead(matfp_read, "x");
|
||||
auto *x_read = reinterpret_cast<double *>(matvar_read->data);
|
||||
Mat_Close(matfp_read);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
EXPECT_DOUBLE_EQ(x[i], x_read[i]);
|
||||
}
|
||||
Mat_VarFree(matvar_read);
|
||||
errorlib::error_code ec;
|
||||
ASSERT_EQ(fs::remove(fs::path(filename), ec), true);
|
||||
}
|
||||
|
||||
|
||||
TEST(MatioTest, WriteAndReadGrComplex)
|
||||
{
|
||||
// Write a .mat file
|
||||
mat_t *matfp;
|
||||
matvar_t *matvar1;
|
||||
std::string filename = "./test3.mat";
|
||||
matfp = Mat_CreateVer(filename.c_str(), nullptr, MAT_FT_MAT73);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matfp) == nullptr) << "Error creating .mat file";
|
||||
|
||||
const std::vector<gr_complex> x_v = {{1, 10}, {2, 9}, {3, 8}, {4, 7}, {5, 6}, {6, -5}, {7, -4}, {8, 3}, {9, 2}, {10, 1}};
|
||||
const unsigned int size = x_v.size();
|
||||
std::array<float, 10> x_real{};
|
||||
std::array<float, 10> x_imag{};
|
||||
unsigned int i = 0;
|
||||
for (auto it : x_v)
|
||||
{
|
||||
x_real[i] = it.real();
|
||||
x_imag[i] = it.imag();
|
||||
i++;
|
||||
}
|
||||
|
||||
struct mat_complex_split_t x = {x_real.data(), x_imag.data()};
|
||||
std::array<size_t, 2> dims{static_cast<size_t>(size), 1};
|
||||
matvar1 = Mat_VarCreate("x", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims.data(), &x, MAT_F_COMPLEX);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matvar1) == nullptr) << "Error creating variable for ’x’";
|
||||
|
||||
std::vector<gr_complex> x2 = {{1.1, -10}, {2, -9}, {3, -8}, {4, -7}, {5, 6}, {6, -5}, {7, -4}, {8, 3}, {9, 2}, {10, 1}};
|
||||
const unsigned int size_y = x2.size();
|
||||
std::array<float, 10> y_real{};
|
||||
std::array<float, 10> y_imag{};
|
||||
i = 0;
|
||||
for (auto it : x2)
|
||||
{
|
||||
y_real[i] = it.real();
|
||||
y_imag[i] = it.imag();
|
||||
i++;
|
||||
}
|
||||
|
||||
struct mat_complex_split_t y = {y_real.data(), y_imag.data()};
|
||||
std::array<size_t, 2> dims_y{static_cast<size_t>(size_y), 1};
|
||||
matvar_t *matvar2;
|
||||
matvar2 = Mat_VarCreate("y", MAT_C_SINGLE, MAT_T_SINGLE, 2, dims_y.data(), &y, MAT_F_COMPLEX);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matvar2) == nullptr) << "Error creating variable for ’y’";
|
||||
|
||||
Mat_VarWrite(matfp, matvar1, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE
|
||||
Mat_VarWrite(matfp, matvar2, MAT_COMPRESSION_ZLIB); // or MAT_COMPRESSION_NONE
|
||||
Mat_VarFree(matvar1);
|
||||
Mat_VarFree(matvar2);
|
||||
|
||||
Mat_Close(matfp);
|
||||
|
||||
// Read a .mat file
|
||||
mat_t *matfp_read;
|
||||
matvar_t *matvar_read;
|
||||
|
||||
matfp_read = Mat_Open(filename.c_str(), MAT_ACC_RDONLY);
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matfp_read) == nullptr) << "Error reading .mat file";
|
||||
|
||||
matvar_read = Mat_VarReadInfo(matfp_read, "x");
|
||||
ASSERT_FALSE(reinterpret_cast<long *>(matvar_read) == nullptr) << "Error reading variable in .mat file";
|
||||
|
||||
matvar_read = Mat_VarRead(matfp_read, "x");
|
||||
auto *x_read_st = reinterpret_cast<mat_complex_split_t *>(matvar_read->data);
|
||||
auto *x_read_real = reinterpret_cast<float *>(x_read_st->Re);
|
||||
auto *x_read_imag = reinterpret_cast<float *>(x_read_st->Im);
|
||||
std::vector<gr_complex> x_v_read;
|
||||
x_v_read.reserve(size);
|
||||
for (unsigned int k = 0; k < size; k++)
|
||||
{
|
||||
x_v_read.emplace_back(x_read_real[k], x_read_imag[k]);
|
||||
}
|
||||
|
||||
Mat_Close(matfp_read);
|
||||
Mat_VarFree(matvar_read);
|
||||
|
||||
for (unsigned int k = 0; k < size; k++)
|
||||
{
|
||||
EXPECT_FLOAT_EQ(x_v[k].real(), x_v_read[k].real());
|
||||
EXPECT_FLOAT_EQ(x_v[k].imag(), x_v_read[k].imag());
|
||||
}
|
||||
errorlib::error_code ec;
|
||||
ASSERT_EQ(fs::remove(fs::path(filename), ec), true);
|
||||
}
|
||||
335
tests/unit-tests/arithmetic/multiply_test.cc
Normal file
335
tests/unit-tests/arithmetic/multiply_test.cc
Normal file
@@ -0,0 +1,335 @@
|
||||
/*!
|
||||
* \file multiply_test.cc
|
||||
* \brief This file implements tests for the multiplication of long arrays.
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2012. 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 <armadillo>
|
||||
#include <volk/volk.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <numeric>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(size_multiply_test, 100000, "Size of the arrays used for multiply testing");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, size_multiply_test, 100000, "Size of the arrays used for multiply testing");
|
||||
#endif
|
||||
|
||||
TEST(MultiplyTest, StandardCDoubleImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = new double[FLAGS_size_multiply_test];
|
||||
auto* output = new double[FLAGS_size_multiply_test];
|
||||
std::fill_n(input, FLAGS_size_multiply_test, 0.0);
|
||||
#else
|
||||
auto* input = new double[absl::GetFlag(FLAGS_size_multiply_test)];
|
||||
auto* output = new double[absl::GetFlag(FLAGS_size_multiply_test)];
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_multiply_test), 0.0);
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_multiply_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_multiply_test); i++)
|
||||
#endif
|
||||
{
|
||||
output[i] = input[i] * input[i];
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< " doubles in standard C finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
|
||||
double acc = 0.0;
|
||||
double expected = 0.0;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_multiply_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_multiply_test); i++)
|
||||
#endif
|
||||
{
|
||||
acc += output[i];
|
||||
}
|
||||
delete[] input;
|
||||
delete[] output;
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
ASSERT_EQ(expected, acc);
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, ArmadilloImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
arma::vec input(FLAGS_size_multiply_test, arma::fill::zeros);
|
||||
arma::vec output(FLAGS_size_multiply_test);
|
||||
#else
|
||||
arma::vec input(absl::GetFlag(FLAGS_size_multiply_test), arma::fill::zeros);
|
||||
arma::vec output(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
output = input % input;
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< "-length double Armadillo vectors finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
ASSERT_EQ(0, arma::norm(output, 2));
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, StandardCComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = new std::complex<float>[FLAGS_size_multiply_test];
|
||||
auto* output = new std::complex<float>[FLAGS_size_multiply_test];
|
||||
std::fill_n(input, FLAGS_size_multiply_test, std::complex<float>(0.0, 0.0));
|
||||
#else
|
||||
auto* input = new std::complex<float>[absl::GetFlag(FLAGS_size_multiply_test)];
|
||||
auto* output = new std::complex<float>[absl::GetFlag(FLAGS_size_multiply_test)];
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_multiply_test), std::complex<float>(0.0, 0.0));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_multiply_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_multiply_test); i++)
|
||||
#endif
|
||||
{
|
||||
output[i] = input[i] * input[i];
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< " complex<float> in standard C finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
|
||||
std::complex<float> expected(0.0, 0.0);
|
||||
std::complex<float> result(0.0, 0.0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int i = 0; i < FLAGS_size_multiply_test; i++)
|
||||
#else
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_size_multiply_test); i++)
|
||||
#endif
|
||||
{
|
||||
result += output[i];
|
||||
}
|
||||
delete[] input;
|
||||
delete[] output;
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, C11ComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::vector<std::complex<float>> input(FLAGS_size_multiply_test);
|
||||
std::vector<std::complex<float>> output(FLAGS_size_multiply_test);
|
||||
#else
|
||||
const std::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
std::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
int pos = 0;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
// Trying a range-based for
|
||||
for (const auto& item : input)
|
||||
{
|
||||
output[pos++] = item * item;
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< " complex<float> vector (C++11-style) finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
|
||||
std::complex<float> expected(0.0, 0.0);
|
||||
auto result = std::inner_product(output.begin(), output.end(), output.begin(), expected);
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, ArmadilloComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
arma::cx_fvec input(FLAGS_size_multiply_test, arma::fill::zeros);
|
||||
arma::cx_fvec output(FLAGS_size_multiply_test);
|
||||
#else
|
||||
arma::cx_fvec input(absl::GetFlag(FLAGS_size_multiply_test), arma::fill::zeros);
|
||||
arma::cx_fvec output(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
output = input % input;
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< "-length complex float Armadillo vectors finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
ASSERT_EQ(0, arma::norm(output, 2));
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, VolkComplexImplementation)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(FLAGS_size_multiply_test * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
auto* output = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(FLAGS_size_multiply_test * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, FLAGS_size_multiply_test, std::complex<float>(0.0, 0.0));
|
||||
#else
|
||||
auto* input = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_multiply_test) * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
auto* output = static_cast<std::complex<float>*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_multiply_test) * sizeof(std::complex<float>), volk_gnsssdr_get_alignment()));
|
||||
std::fill_n(input, absl::GetFlag(FLAGS_size_multiply_test), std::complex<float>(0.0, 0.0));
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_x2_multiply_32fc(output, input, input, FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_32fc_x2_multiply_32fc(output, input, input, absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< "-length complex float vector using VOLK finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto* mag = static_cast<float*>(volk_gnsssdr_malloc(FLAGS_size_multiply_test * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
volk_32fc_magnitude_32f(mag, output, FLAGS_size_multiply_test);
|
||||
#else
|
||||
auto* mag = static_cast<float*>(volk_gnsssdr_malloc(absl::GetFlag(FLAGS_size_multiply_test) * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
volk_32fc_magnitude_32f(mag, output, absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
auto* result = new float(0.0);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32f_accumulator_s32f(result, mag, FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_32f_accumulator_s32f(result, mag, absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
// Comparing floating-point numbers is tricky.
|
||||
// Due to round-off errors, it is very unlikely that two floating-points will match exactly.
|
||||
// See https://google.github.io/googletest/reference/assertions.html#floating-point
|
||||
float expected = 0.0;
|
||||
ASSERT_FLOAT_EQ(expected, result[0]);
|
||||
volk_gnsssdr_free(input);
|
||||
volk_gnsssdr_free(output);
|
||||
volk_gnsssdr_free(mag);
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiplyTest, VolkComplexImplementationAlloc)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_gnsssdr::vector<std::complex<float>> input(FLAGS_size_multiply_test, std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<std::complex<float>> output(FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_gnsssdr::vector<std::complex<float>> input(absl::GetFlag(FLAGS_size_multiply_test), std::complex<float>(0.0, 0.0));
|
||||
volk_gnsssdr::vector<std::complex<float>> output(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32fc_x2_multiply_32fc(output.data(), input.data(), input.data(), FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_32fc_x2_multiply_32fc(output.data(), input.data(), input.data(), absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
std::cout << "Element-wise multiplication of " << FLAGS_size_multiply_test
|
||||
#else
|
||||
std::cout << "Element-wise multiplication of " << absl::GetFlag(FLAGS_size_multiply_test)
|
||||
#endif
|
||||
<< "-length complex float vector using VOLK ALLOC finished in " << elapsed_seconds.count() * 1e6
|
||||
<< " microseconds\n";
|
||||
ASSERT_LE(0, elapsed_seconds.count() * 1e6);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_gnsssdr::vector<float> mag(FLAGS_size_multiply_test);
|
||||
volk_32fc_magnitude_32f(mag.data(), output.data(), FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_gnsssdr::vector<float> mag(absl::GetFlag(FLAGS_size_multiply_test));
|
||||
volk_32fc_magnitude_32f(mag.data(), output.data(), absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
|
||||
auto* result = new float(0.0);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
volk_32f_accumulator_s32f(result, mag.data(), FLAGS_size_multiply_test);
|
||||
#else
|
||||
volk_32f_accumulator_s32f(result, mag.data(), absl::GetFlag(FLAGS_size_multiply_test));
|
||||
#endif
|
||||
// Comparing floating-point numbers is tricky.
|
||||
// Due to round-off errors, it is very unlikely that two floating-points will match exactly.
|
||||
// See https://google.github.io/googletest/reference/assertions.html#floating-point
|
||||
float expected = 0.0;
|
||||
ASSERT_FLOAT_EQ(expected, result[0]);
|
||||
}
|
||||
114
tests/unit-tests/arithmetic/preamble_correlator_test.cc
Normal file
114
tests/unit-tests/arithmetic/preamble_correlator_test.cc
Normal file
@@ -0,0 +1,114 @@
|
||||
/*!
|
||||
* \file preamble_correlator_test.cc
|
||||
* \brief This file implements tests for preamble detection.
|
||||
* \author Carles Fernandez-Prades, 2020. 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 "GPS_L1_CA.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional> // for std::plus
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
TEST(PreambleCorrelationTest, TestMethods)
|
||||
{
|
||||
int64_t n_iter = 100000;
|
||||
int32_t corr_value = 0;
|
||||
int32_t corr_value2 = 0;
|
||||
int32_t corr_value3 = 0;
|
||||
|
||||
int32_t sum_corr1 = 0;
|
||||
int32_t sum_corr2 = 0;
|
||||
int32_t sum_corr3 = 0;
|
||||
|
||||
std::vector<float> d_symbol_history(GPS_CA_PREAMBLE_LENGTH_SYMBOLS, 0.0);
|
||||
std::array<int32_t, GPS_CA_PREAMBLE_LENGTH_BITS> d_preamble_samples{};
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end, start2, end2, start3, end3;
|
||||
|
||||
// fill the inputs
|
||||
std::random_device rd;
|
||||
std::default_random_engine e2(rd());
|
||||
std::uniform_real_distribution<> dist(-1.0, 1.0);
|
||||
std::generate(d_symbol_history.begin(), d_symbol_history.end(), [&dist, &e2]() { return dist(e2); });
|
||||
|
||||
std::generate(d_preamble_samples.begin(), d_preamble_samples.end(), [n = 0]() mutable { return (GPS_CA_PREAMBLE[n++] == '1' ? 1 : -1); });
|
||||
|
||||
// Compute correlation, method 1
|
||||
start = std::chrono::system_clock::now();
|
||||
for (int64_t iter = 0; iter < n_iter; iter++)
|
||||
{
|
||||
corr_value = 0;
|
||||
for (int32_t i = 0; i < GPS_CA_PREAMBLE_LENGTH_BITS; i++)
|
||||
{
|
||||
if (d_symbol_history[i] < 0.0)
|
||||
{
|
||||
corr_value -= d_preamble_samples[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
corr_value += d_preamble_samples[i];
|
||||
}
|
||||
}
|
||||
sum_corr1 += corr_value;
|
||||
}
|
||||
end = std::chrono::system_clock::now();
|
||||
|
||||
// Compute correlation, method 2
|
||||
start2 = std::chrono::system_clock::now();
|
||||
for (int64_t iter = 0; iter < n_iter; iter++)
|
||||
{
|
||||
corr_value2 = std::accumulate(d_symbol_history.begin(),
|
||||
d_symbol_history.begin() + GPS_CA_PREAMBLE_LENGTH_BITS,
|
||||
0,
|
||||
[&d_preamble_samples, n = 0](float a, float b) mutable { return (b > 0.0 ? a + d_preamble_samples[n++] : a - d_preamble_samples[n++]); });
|
||||
sum_corr2 += corr_value2;
|
||||
}
|
||||
end2 = std::chrono::system_clock::now();
|
||||
|
||||
// Compute correlation, method 3
|
||||
start3 = std::chrono::system_clock::now();
|
||||
for (int64_t iter = 0; iter < n_iter; iter++)
|
||||
{
|
||||
corr_value3 = std::inner_product(d_symbol_history.begin(),
|
||||
d_symbol_history.begin() + GPS_CA_PREAMBLE_LENGTH_BITS,
|
||||
d_preamble_samples.begin(),
|
||||
0,
|
||||
#if COMPILER_HAS_STD_PLUS_VOID
|
||||
std::plus<>(),
|
||||
#else
|
||||
std::plus<int32_t>(),
|
||||
#endif
|
||||
[](float a, int32_t b) { return (std::signbit(a) ? -b : b); });
|
||||
sum_corr3 += corr_value3;
|
||||
}
|
||||
end3 = std::chrono::system_clock::now();
|
||||
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
std::chrono::duration<double> elapsed_seconds2 = end2 - start2;
|
||||
std::chrono::duration<double> elapsed_seconds3 = end3 - start3;
|
||||
|
||||
EXPECT_EQ(corr_value, corr_value2);
|
||||
EXPECT_EQ(sum_corr1, sum_corr2);
|
||||
EXPECT_EQ(corr_value, corr_value3);
|
||||
EXPECT_EQ(sum_corr1, sum_corr3);
|
||||
|
||||
std::cout << "Correlation computed with 'C for' : done in " << elapsed_seconds.count() * 1.0e9 / n_iter << " nanoseconds\n";
|
||||
std::cout << "Correlation computed with accumulate : done in " << elapsed_seconds2.count() * 1.0e9 / n_iter << " nanoseconds\n";
|
||||
std::cout << "Correlation computed with inner_product : done in " << elapsed_seconds3.count() * 1.0e9 / n_iter << " nanoseconds\n";
|
||||
}
|
||||
256
tests/unit-tests/control-plane/control_thread_test.cc
Normal file
256
tests/unit-tests/control-plane/control_thread_test.cc
Normal file
@@ -0,0 +1,256 @@
|
||||
/*!
|
||||
* \file control_thread_test.cc
|
||||
* \brief This file implements tests for the ControlThread.
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2013. 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 "channel_event.h"
|
||||
#include "command_event.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "control_thread.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
#include <boost/exception_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
class ControlThreadTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static int stop_receiver();
|
||||
typedef struct
|
||||
{
|
||||
long mtype; // required by SysV message
|
||||
double message;
|
||||
} message_buffer;
|
||||
};
|
||||
|
||||
|
||||
int ControlThreadTest::stop_receiver()
|
||||
{
|
||||
message_buffer msg_stop;
|
||||
msg_stop.mtype = 1;
|
||||
msg_stop.message = -200.0;
|
||||
int msqid_stop = -1;
|
||||
int msgsend_size = sizeof(msg_stop.message);
|
||||
key_t key_stop = 1102;
|
||||
|
||||
// wait for the receiver control queue to be created
|
||||
while (((msqid_stop = msgget(key_stop, 0644))) == -1)
|
||||
{
|
||||
}
|
||||
|
||||
// wait for a couple of seconds
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
|
||||
// Stop the receiver
|
||||
msgsnd(msqid_stop, &msg_stop, msgsend_size, IPC_NOWAIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ControlThreadTest /*unused*/, InstantiateRunControlMessages /*unused*/)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
config->set_property("SignalSource.filename", file_name);
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("SignalConditioner.item_type", "gr_complex");
|
||||
config->set_property("Channels_1C.count", "2");
|
||||
config->set_property("Channels_1E.count", "0");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.threshold", "1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C.doppler_min", "-5000");
|
||||
config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C.item_type", "gr_complex");
|
||||
config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C.item_type", "gr_complex");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("Observables.item_type", "gr_complex");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
config->set_property("PVT.item_type", "gr_complex");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
|
||||
std::shared_ptr<ControlThread> control_thread = std::make_shared<ControlThread>(config);
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> control_queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
control_queue->push(pmt::make_any(channel_event_make(0, 0)));
|
||||
control_queue->push(pmt::make_any(channel_event_make(1, 0)));
|
||||
control_queue->push(pmt::make_any(command_event_make(200, 0)));
|
||||
|
||||
control_thread->set_control_queue(control_queue);
|
||||
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();
|
||||
}
|
||||
|
||||
unsigned int expected3 = 3;
|
||||
unsigned int expected1 = 1;
|
||||
EXPECT_EQ(expected3, control_thread->processed_control_messages());
|
||||
EXPECT_EQ(expected1, control_thread->applied_actions());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ControlThreadTest /*unused*/, InstantiateRunControlMessages2 /*unused*/)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
config->set_property("SignalSource.filename", file_name);
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("SignalConditioner.item_type", "gr_complex");
|
||||
config->set_property("Channels_1C.count", "4");
|
||||
config->set_property("Channels_1E.count", "0");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.threshold", "1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C.doppler_min", "-5000");
|
||||
config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C.item_type", "gr_complex");
|
||||
config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C.item_type", "gr_complex");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("Observables.item_type", "gr_complex");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
config->set_property("PVT.item_type", "gr_complex");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
|
||||
auto control_thread2 = std::make_unique<ControlThread>(config);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> control_queue2 = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
control_queue2->push(pmt::make_any(channel_event_make(0, 0)));
|
||||
control_queue2->push(pmt::make_any(channel_event_make(2, 0)));
|
||||
control_queue2->push(pmt::make_any(channel_event_make(1, 0)));
|
||||
control_queue2->push(pmt::make_any(channel_event_make(3, 0)));
|
||||
control_queue2->push(pmt::make_any(command_event_make(200, 0)));
|
||||
|
||||
control_thread2->set_control_queue(control_queue2);
|
||||
|
||||
try
|
||||
{
|
||||
control_thread2->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();
|
||||
}
|
||||
|
||||
unsigned int expected5 = 5;
|
||||
unsigned int expected1 = 1;
|
||||
EXPECT_EQ(expected5, control_thread2->processed_control_messages());
|
||||
EXPECT_EQ(expected1, control_thread2->applied_actions());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ControlThreadTest /*unused*/, StopReceiverProgrammatically /*unused*/)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
config->set_property("SignalSource.filename", file_name);
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("SignalConditioner.item_type", "gr_complex");
|
||||
config->set_property("Channels_1C.count", "4");
|
||||
config->set_property("Channels_1E.count", "0");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.threshold", "1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C.doppler_min", "-5000");
|
||||
config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C.item_type", "gr_complex");
|
||||
config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C.item_type", "gr_complex");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("Observables.item_type", "gr_complex");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
config->set_property("PVT.item_type", "gr_complex");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
|
||||
std::shared_ptr<ControlThread> control_thread = std::make_shared<ControlThread>(config);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> control_queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
control_thread->set_control_queue(control_queue);
|
||||
|
||||
std::thread stop_receiver_thread(stop_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_receiver_thread.join();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
57
tests/unit-tests/control-plane/file_configuration_test.cc
Normal file
57
tests/unit-tests/control-plane/file_configuration_test.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
/*!
|
||||
* \file file_configuration_test.cc
|
||||
* \brief This file implements tests for the file_configuration.
|
||||
* \author Carles Fernandez-Prades, 2012. 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 "file_configuration.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
TEST(FileConfigurationTest, OverridedProperties)
|
||||
{
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "data/config_file_sample.txt";
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<FileConfiguration>(filename);
|
||||
std::unique_ptr<ConfigurationInterface> configuration = std::make_unique<FileConfiguration>(filename);
|
||||
std::string default_value = "default_value";
|
||||
std::string value = configuration->property("NotThere", default_value);
|
||||
EXPECT_STREQ("default_value", value.c_str());
|
||||
configuration->set_property("NotThere", "Yes!");
|
||||
value = configuration->property("NotThere", std::move(default_value));
|
||||
EXPECT_STREQ("Yes!", value.c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(FileConfigurationTest, LoadFromNonExistentFile)
|
||||
{
|
||||
std::unique_ptr<ConfigurationInterface> configuration = std::make_unique<FileConfiguration>("./i_dont_exist.conf");
|
||||
std::string default_value = "default_value";
|
||||
std::string value = configuration->property("whatever.whatever", std::move(default_value));
|
||||
EXPECT_STREQ("default_value", value.c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(FileConfigurationTest, PropertyDoesNotExist)
|
||||
{
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "data/config_file_sample.txt";
|
||||
std::unique_ptr<ConfigurationInterface> configuration = std::make_unique<FileConfiguration>(filename);
|
||||
std::string default_value = "default_value";
|
||||
std::string value = configuration->property("whatever.whatever", std::move(default_value));
|
||||
EXPECT_STREQ("default_value", value.c_str());
|
||||
}
|
||||
486
tests/unit-tests/control-plane/gnss_block_factory_test.cc
Normal file
486
tests/unit-tests/control-plane/gnss_block_factory_test.cc
Normal file
@@ -0,0 +1,486 @@
|
||||
/*!
|
||||
* \file gnss_block_factory_test.cc
|
||||
* \brief This class implements a Unit Test for the GNSSBlockFactory class.
|
||||
* \authors <ul>
|
||||
* <li> Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* <li> Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
* </ul>
|
||||
*
|
||||
* This class test the instantiation of all blocks in gnss_block_factory
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "acquisition_interface.h"
|
||||
#include "channel.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "observables_interface.h"
|
||||
#include "pvt_interface.h"
|
||||
#include "signal_source_interface.h"
|
||||
#include "tracking_interface.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateFileSignalSource)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
configuration->set_property("SignalSource.filename", std::move(filename));
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
// Example of a factory as a shared_ptr
|
||||
std::shared_ptr<GNSSBlockFactory> factory = std::make_shared<GNSSBlockFactory>();
|
||||
// Example of a block as a shared_ptr
|
||||
auto signal_source = factory->GetSignalSource(configuration.get(), queue.get());
|
||||
EXPECT_STREQ("SignalSource", signal_source->role().c_str());
|
||||
EXPECT_STREQ("File_Signal_Source", signal_source->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongSignalSource)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalSource.implementation", "Parapsychological_Source");
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
// Example of a factory as a unique_ptr
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
// Example of a block as a unique_ptr
|
||||
auto signal_source = factory->GetSignalSource(configuration.get(), queue.get());
|
||||
EXPECT_EQ(nullptr, signal_source);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongSignalSource2)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalSource.implementation", "Pass_Through");
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
// Example of a factory as a unique_ptr
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
// Example of a block as a unique_ptr
|
||||
auto signal_source = factory->GetSignalSource(configuration.get(), queue.get());
|
||||
EXPECT_EQ(nullptr, signal_source);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateSignalConditioner)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalConditioner.implementation", "Signal_Conditioner");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> signal_conditioner = factory->GetSignalConditioner(configuration.get());
|
||||
EXPECT_STREQ("SignalConditioner", signal_conditioner->role().c_str());
|
||||
EXPECT_STREQ("Signal_Conditioner", signal_conditioner->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongSignalConditioner)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalConditioner.implementation", "Signal_Ruinder");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> signal_conditioner = factory->GetSignalConditioner(configuration.get());
|
||||
EXPECT_EQ(nullptr, signal_conditioner);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongSignalConditioner2)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("SignalConditioner.implementation", "Fir_Filter");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> signal_conditioner = factory->GetSignalConditioner(configuration.get());
|
||||
EXPECT_EQ(nullptr, signal_conditioner);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateFIRFilter)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
configuration->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
|
||||
configuration->set_property("InputFilter.number_of_taps", "4");
|
||||
configuration->set_property("InputFilter.number_of_bands", "2");
|
||||
|
||||
configuration->set_property("InputFilter.band1_begin", "0.0");
|
||||
configuration->set_property("InputFilter.band1_end", "0.45");
|
||||
configuration->set_property("InputFilter.band2_begin", "0.55");
|
||||
configuration->set_property("InputFilter.band2_end", "1.0");
|
||||
|
||||
configuration->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
configuration->set_property("InputFilter.ampl1_end", "1.0");
|
||||
configuration->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
configuration->set_property("InputFilter.ampl2_end", "0.0");
|
||||
|
||||
configuration->set_property("InputFilter.band1_error", "1.0");
|
||||
configuration->set_property("InputFilter.band2_error", "1.0");
|
||||
|
||||
configuration->set_property("InputFilter.filter_type", "bandpass");
|
||||
configuration->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
|
||||
EXPECT_STREQ("InputFilter", input_filter->role().c_str());
|
||||
EXPECT_STREQ("Fir_Filter", input_filter->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateFreqXlatingFIRFilter)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
configuration->set_property("InputFilter.implementation", "Freq_Xlating_Fir_Filter");
|
||||
|
||||
configuration->set_property("InputFilter.number_of_taps", "4");
|
||||
configuration->set_property("InputFilter.number_of_bands", "2");
|
||||
|
||||
configuration->set_property("InputFilter.band1_begin", "0.0");
|
||||
configuration->set_property("InputFilter.band1_end", "0.45");
|
||||
configuration->set_property("InputFilter.band2_begin", "0.55");
|
||||
configuration->set_property("InputFilter.band2_end", "1.0");
|
||||
|
||||
configuration->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
configuration->set_property("InputFilter.ampl1_end", "1.0");
|
||||
configuration->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
configuration->set_property("InputFilter.ampl2_end", "0.0");
|
||||
|
||||
configuration->set_property("InputFilter.band1_error", "1.0");
|
||||
configuration->set_property("InputFilter.band2_error", "1.0");
|
||||
|
||||
configuration->set_property("InputFilter.filter_type", "bandpass");
|
||||
configuration->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
configuration->set_property("InputFilter.sampling_frequency", "4000000");
|
||||
configuration->set_property("InputFilter.IF", "34000");
|
||||
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
|
||||
EXPECT_STREQ("InputFilter", input_filter->role().c_str());
|
||||
EXPECT_STREQ("Freq_Xlating_Fir_Filter", input_filter->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiatePulseBlankingFilter)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
configuration->set_property("InputFilter.implementation", "Pulse_Blanking_Filter");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
EXPECT_STREQ("InputFilter", input_filter->role().c_str());
|
||||
EXPECT_STREQ("Pulse_Blanking_Filter", input_filter->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateNotchFilter)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
configuration->set_property("InputFilter.implementation", "Notch_Filter");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
EXPECT_STREQ("InputFilter", input_filter->role().c_str());
|
||||
EXPECT_STREQ("Notch_Filter", input_filter->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateNotchFilterLite)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
configuration->set_property("InputFilter.implementation", "Notch_Filter_Lite");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
EXPECT_STREQ("InputFilter", input_filter->role().c_str());
|
||||
EXPECT_STREQ("Notch_Filter_Lite", input_filter->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongFilter)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
configuration->set_property("InputFilter.implementation", "Pollen_Filter");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> input_filter = factory->GetBlock(configuration.get(), "InputFilter", 1, 1);
|
||||
EXPECT_EQ(nullptr, input_filter);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateDirectResampler)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Resampler.implementation", "Direct_Resampler");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> resampler = factory->GetBlock(configuration.get(), "Resampler", 1, 1);
|
||||
EXPECT_STREQ("Resampler", resampler->role().c_str());
|
||||
EXPECT_STREQ("Direct_Resampler", resampler->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongResampler)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Resampler.implementation", "RaNdOm_Resampler");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> resampler = factory->GetBlock(configuration.get(), "Resampler", 1, 1);
|
||||
EXPECT_EQ(nullptr, resampler);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGpsL1CaPcpsAcquisition)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(configuration.get(), "Acquisition", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
EXPECT_STREQ("Acquisition", acquisition->role().c_str());
|
||||
EXPECT_STREQ("GPS_L1_CA_PCPS_Acquisition", acquisition->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGpsL1CaPcpsQuickSyncAcquisition)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_QuickSync_Acquisition");
|
||||
std::shared_ptr<GNSSBlockFactory> factory = std::make_shared<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(configuration.get(), "Acquisition", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
EXPECT_STREQ("Acquisition", acquisition->role().c_str());
|
||||
EXPECT_STREQ("GPS_L1_CA_PCPS_QuickSync_Acquisition", acquisition->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGalileoE1PcpsQuickSyncAmbiguousAcquisition)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Acquisition.implementation", "Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition");
|
||||
std::shared_ptr<GNSSBlockFactory> factory = std::make_shared<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(configuration.get(), "Acquisition", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
EXPECT_STREQ("Acquisition", acquisition->role().c_str());
|
||||
EXPECT_STREQ("Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition", acquisition->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGalileoE1PcpsAmbiguousAcquisition)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Acquisition.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(configuration.get(), "Acquisition", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
EXPECT_STREQ("Acquisition", acquisition->role().c_str());
|
||||
EXPECT_STREQ("Galileo_E1_PCPS_Ambiguous_Acquisition", acquisition->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongAcquisition)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_Alchemy");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(configuration.get(), "Acquisition", 1, 0);
|
||||
EXPECT_EQ(nullptr, acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGpsL1CaDllPllTracking)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Tracking.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(configuration.get(), "Tracking", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
EXPECT_STREQ("Tracking", tracking->role().c_str());
|
||||
EXPECT_STREQ("GPS_L1_CA_DLL_PLL_Tracking", tracking->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGpsL1CaTcpConnectorTracking)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Tracking.implementation", "GPS_L1_CA_TCP_CONNECTOR_Tracking");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(configuration.get(), "Tracking", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
EXPECT_STREQ("Tracking", tracking->role().c_str());
|
||||
EXPECT_STREQ("GPS_L1_CA_TCP_CONNECTOR_Tracking", tracking->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGalileoE1DllPllVemlTracking)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Tracking.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(configuration.get(), "Tracking", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
EXPECT_STREQ("Tracking", tracking->role().c_str());
|
||||
EXPECT_STREQ("Galileo_E1_DLL_PLL_VEML_Tracking", tracking->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongTracking)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Tracking.implementation", "The perfect tracking");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(configuration.get(), "Tracking", 1, 1);
|
||||
EXPECT_EQ(nullptr, trk_);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateGpsL1CaTelemetryDecoder)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("TelemetryDecoder.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> telemetry_decoder = factory->GetBlock(configuration.get(), "TelemetryDecoder", 1, 1);
|
||||
EXPECT_STREQ("TelemetryDecoder", telemetry_decoder->role().c_str());
|
||||
EXPECT_STREQ("GPS_L1_CA_Telemetry_Decoder", telemetry_decoder->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongTelemetryDecoder)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("TelemetryDecoder.implementation", "GPS_Xenomorphic_Telemetry_Decoder");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> telemetry_decoder = factory->GetBlock(configuration.get(), "TelemetryDecoder", 1, 1);
|
||||
EXPECT_EQ(nullptr, telemetry_decoder);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateEmptyTelemetryDecoder)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("TelemetryDecoder.implementation", std::string(""));
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> telemetry_decoder = factory->GetBlock(configuration.get(), "TelemetryDecoder", 1, 1);
|
||||
EXPECT_EQ(nullptr, telemetry_decoder);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateChannels)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Channels_1C.count", "2");
|
||||
configuration->set_property("Channels_1E.count", "0");
|
||||
configuration->set_property("Channels.in_acquisition", "2");
|
||||
configuration->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
configuration->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
configuration->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<std::vector<std::unique_ptr<GNSSBlockInterface>>> channels = factory->GetChannels(configuration.get(), queue.get());
|
||||
EXPECT_EQ(static_cast<unsigned int>(2), channels->size());
|
||||
channels->erase(channels->begin(), channels->end());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongObservables)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Observables.implementation", "Supercalifragilistic_Observables");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
auto observables = factory->GetObservables(configuration.get());
|
||||
EXPECT_EQ(nullptr, observables);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongObservables2)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Observables.implementation", "Pass_Through");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
auto observables = factory->GetObservables(configuration.get());
|
||||
EXPECT_EQ(nullptr, observables);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongObservables3)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Observables.implementation", "RTKLIB_PVT");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
auto observables = factory->GetObservables(configuration.get());
|
||||
EXPECT_EQ(nullptr, observables);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateObservables)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::unique_ptr<GNSSBlockInterface> observables = factory->GetObservables(configuration.get());
|
||||
EXPECT_STREQ("Observables", observables->role().c_str());
|
||||
EXPECT_STREQ("Hybrid_Observables", observables->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateRTKLIBPvt)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
std::unique_ptr<GNSSBlockFactory> factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> pvt_ = factory->GetPVT(configuration.get());
|
||||
std::shared_ptr<PvtInterface> pvt = std::dynamic_pointer_cast<PvtInterface>(pvt_);
|
||||
EXPECT_STREQ("PVT", pvt->role().c_str());
|
||||
EXPECT_STREQ("RTKLIB_PVT", pvt->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongPvt)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("PVT.implementation", "Pepito");
|
||||
auto factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> pvt_ = factory->GetPVT(configuration.get());
|
||||
auto pvt = std::dynamic_pointer_cast<PvtInterface>(pvt_);
|
||||
EXPECT_EQ(nullptr, pvt);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongPvt2)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("PVT.implementation", "Pass_Through");
|
||||
auto factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> pvt_ = factory->GetPVT(configuration.get());
|
||||
auto pvt = std::dynamic_pointer_cast<PvtInterface>(pvt_);
|
||||
EXPECT_EQ(nullptr, pvt);
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSBlockFactoryTest, InstantiateWrongPvt3)
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
configuration->set_property("PVT.implementation", "Quantum_Particle_PVT");
|
||||
auto factory = std::make_unique<GNSSBlockFactory>();
|
||||
std::shared_ptr<GNSSBlockInterface> pvt_ = factory->GetPVT(configuration.get());
|
||||
auto pvt = std::dynamic_pointer_cast<PvtInterface>(pvt_);
|
||||
EXPECT_EQ(nullptr, pvt);
|
||||
}
|
||||
252
tests/unit-tests/control-plane/gnss_flowgraph_test.cc
Normal file
252
tests/unit-tests/control-plane/gnss_flowgraph_test.cc
Normal file
@@ -0,0 +1,252 @@
|
||||
/*!
|
||||
* \file gnss_flowgraph_test.cc
|
||||
* \brief This file implements tests for a flowgraph
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2012. 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 "acquisition_interface.h"
|
||||
#include "channel.h"
|
||||
#include "channel_interface.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "file_configuration.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_flowgraph.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "tracking_interface.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
TEST(GNSSFlowgraph /*unused*/, InstantiateConnectStartStopOldNotation /*unused*/)
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.SUPL_gps_enabled", "false");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
config->set_property("SignalSource.filename", std::move(filename));
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("Channels_1C.count", "1");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.threshold", "1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "5000");
|
||||
config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
|
||||
std::shared_ptr<GNSSFlowgraph> flowgraph = std::make_shared<GNSSFlowgraph>(config, std::make_shared<Concurrent_Queue<pmt::pmt_t>>());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->connect());
|
||||
EXPECT_TRUE(flowgraph->connected());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->start());
|
||||
EXPECT_TRUE(flowgraph->running());
|
||||
flowgraph->stop();
|
||||
EXPECT_FALSE(flowgraph->running());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSFlowgraph /*unused*/, InstantiateConnectStartStop /*unused*/)
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
config->set_property("SignalSource.filename", std::move(filename));
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("Channels_1C.count", "8");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Channel.signal", "1C");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.threshold", "1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "5000");
|
||||
config->set_property("Tracking_1C.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("TelemetryDecoder_1C.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
|
||||
std::shared_ptr<GNSSFlowgraph> flowgraph = std::make_shared<GNSSFlowgraph>(config, std::make_shared<Concurrent_Queue<pmt::pmt_t>>());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->connect());
|
||||
EXPECT_TRUE(flowgraph->connected());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->start());
|
||||
EXPECT_TRUE(flowgraph->running());
|
||||
flowgraph->stop();
|
||||
EXPECT_FALSE(flowgraph->running());
|
||||
}
|
||||
|
||||
TEST(GNSSFlowgraph /*unused*/, InstantiateConnectStartStopGalileoE1B /*unused*/)
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
config->set_property("SignalSource.filename", std::move(filename));
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("Channels_1B.count", "8");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
config->set_property("Channel.signal", "1B");
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.threshold", "1");
|
||||
config->set_property("Acquisition_1B.doppler_max", "5000");
|
||||
config->set_property("Tracking_1B.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("TelemetryDecoder_1B.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
|
||||
std::shared_ptr<GNSSFlowgraph> flowgraph = std::make_shared<GNSSFlowgraph>(config, std::make_shared<Concurrent_Queue<pmt::pmt_t>>());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->connect());
|
||||
EXPECT_TRUE(flowgraph->connected());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->start());
|
||||
EXPECT_TRUE(flowgraph->running());
|
||||
flowgraph->stop();
|
||||
EXPECT_FALSE(flowgraph->running());
|
||||
}
|
||||
|
||||
|
||||
TEST(GNSSFlowgraph /*unused*/, InstantiateConnectStartStopHybrid /*unused*/)
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("SignalSource.sampling_frequency", "4000000");
|
||||
config->set_property("SignalSource.implementation", "File_Signal_Source");
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
config->set_property("SignalSource.filename", std::move(filename));
|
||||
config->set_property("SignalConditioner.implementation", "Pass_Through");
|
||||
config->set_property("Channels_1C.count", "8");
|
||||
config->set_property("Channels_1B.count", "8");
|
||||
config->set_property("Channels.in_acquisition", "1");
|
||||
|
||||
config->set_property("Acquisition_1C0.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C0.threshold", "1");
|
||||
config->set_property("Acquisition_1C0.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C1.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C1.threshold", "1");
|
||||
config->set_property("Acquisition_1C1.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C2.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C2.threshold", "1");
|
||||
config->set_property("Acquisition_1C2.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C3.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C3.threshold", "1");
|
||||
config->set_property("Acquisition_1C3.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C4.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C4.threshold", "1");
|
||||
config->set_property("Acquisition_1C4.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C5.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C5.threshold", "1");
|
||||
config->set_property("Acquisition_1C5.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C6.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C6.threshold", "1");
|
||||
config->set_property("Acquisition_1C6.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1C7.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C7.threshold", "1");
|
||||
config->set_property("Acquisition_1C7.doppler_max", "5000");
|
||||
|
||||
config->set_property("Acquisition_1B8.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B8.threshold", "1");
|
||||
config->set_property("Acquisition_1B8.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B9.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B9.threshold", "1");
|
||||
config->set_property("Acquisition_1B9.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B10.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B10.threshold", "1");
|
||||
config->set_property("Acquisition_1B10.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B11.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B11.threshold", "1");
|
||||
config->set_property("Acquisition_1B11.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B12.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B12.threshold", "1");
|
||||
config->set_property("Acquisition_1B12.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B13.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B13.threshold", "1");
|
||||
config->set_property("Acquisition_1B13.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B14.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B14.threshold", "1");
|
||||
config->set_property("Acquisition_1B14.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1B15.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B15.threshold", "1");
|
||||
config->set_property("Acquisition_1B15.doppler_max", "5000");
|
||||
|
||||
config->set_property("Tracking_1C0.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C1.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C2.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C3.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C4.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C5.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C6.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1C7.implementation", "GPS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1B8.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B9.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B10.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B11.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B12.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B13.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B14.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B15.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
|
||||
config->set_property("TelemetryDecoder_1C0.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C1.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C2.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C3.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C4.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C5.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C6.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1C7.implementation", "GPS_L1_CA_Telemetry_Decoder");
|
||||
|
||||
config->set_property("TelemetryDecoder_1B8.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B9.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B10.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B11.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B12.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B13.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B14.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
config->set_property("TelemetryDecoder_1B15.implementation", "Galileo_E1B_Telemetry_Decoder");
|
||||
|
||||
config->set_property("Observables.implementation", "Hybrid_Observables");
|
||||
config->set_property("PVT.implementation", "RTKLIB_PVT");
|
||||
|
||||
std::shared_ptr<GNSSFlowgraph> flowgraph = std::make_shared<GNSSFlowgraph>(config, std::make_shared<Concurrent_Queue<pmt::pmt_t>>());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->connect());
|
||||
EXPECT_TRUE(flowgraph->connected());
|
||||
|
||||
EXPECT_NO_THROW(flowgraph->start());
|
||||
EXPECT_TRUE(flowgraph->running());
|
||||
flowgraph->stop();
|
||||
EXPECT_FALSE(flowgraph->running());
|
||||
}
|
||||
115
tests/unit-tests/control-plane/in_memory_configuration_test.cc
Normal file
115
tests/unit-tests/control-plane/in_memory_configuration_test.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
/*!
|
||||
* \file in_memory_configuration_test.cc
|
||||
* \brief This file implements tests for the in_memory_configuration.
|
||||
* \author Carles Fernandez-Prades, 2013. 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 "configuration_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <utility>
|
||||
|
||||
TEST(InMemoryConfiguration, IsPresent)
|
||||
{
|
||||
// std::shared_ptr<InMemoryConfiguration> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
EXPECT_FALSE(configuration->is_present("NotThere"));
|
||||
configuration->set_property("NotThere", "Yes!");
|
||||
EXPECT_TRUE(configuration->is_present("NotThere"));
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, StoreAndRetrieve)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
configuration->set_property("Foo.property1", "value");
|
||||
std::string default_value = "default_value";
|
||||
std::string value = configuration->property("Foo.property1", std::move(default_value));
|
||||
EXPECT_STREQ("value", value.c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, NoStoringAndRetrieve)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
std::string default_value = "default_value";
|
||||
std::string value = configuration->property("Foo.property1", std::move(default_value));
|
||||
EXPECT_STREQ("default_value", value.c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveBool)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
configuration->set_property("Foo.property1", "true");
|
||||
bool value = configuration->property("Foo.property1", false);
|
||||
bool expectedtrue = true;
|
||||
EXPECT_EQ(expectedtrue, value);
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveBoolFail)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
configuration->set_property("Foo.property1", "tru");
|
||||
bool value = configuration->property("Foo.property1", false);
|
||||
bool expectedfalse = false;
|
||||
EXPECT_EQ(expectedfalse, value);
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveBoolNoDefine)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
bool value = configuration->property("Foo.property1", false);
|
||||
bool expectedfalse = false;
|
||||
EXPECT_EQ(expectedfalse, value);
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveSizeT)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
configuration->set_property("Foo.property1", "8");
|
||||
unsigned int value = configuration->property("Foo.property1", 4);
|
||||
unsigned int expected8 = 8;
|
||||
EXPECT_EQ(expected8, value);
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveSizeTFail)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
configuration->set_property("Foo.property1", "true");
|
||||
unsigned int value = configuration->property("Foo.property1", 4);
|
||||
unsigned int expected4 = 4;
|
||||
EXPECT_EQ(expected4, value);
|
||||
}
|
||||
|
||||
|
||||
TEST(InMemoryConfiguration, RetrieveSizeTNoDefine)
|
||||
{
|
||||
// std::shared_ptr<ConfigurationInterface> configuration = std::make_shared<InMemoryConfiguration>();
|
||||
auto configuration = std::make_unique<InMemoryConfiguration>();
|
||||
unsigned int value = configuration->property("Foo.property1", 4);
|
||||
unsigned int expected4 = 4;
|
||||
EXPECT_EQ(expected4, value);
|
||||
}
|
||||
98
tests/unit-tests/control-plane/protobuf_test.cc
Normal file
98
tests/unit-tests/control-plane/protobuf_test.cc
Normal file
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
* \file protobuf_test.cc
|
||||
* \brief This file implements tests for Serdes_Gnss_Synchro
|
||||
* \author Carles Fernandez-Prades, 2019. 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 "serdes_gnss_synchro.h"
|
||||
|
||||
TEST(Protobuf, Works)
|
||||
{
|
||||
uint32_t prn_true = 17;
|
||||
uint32_t prn_true2 = 23;
|
||||
std::string sys = "G";
|
||||
std::string sig = "1C";
|
||||
|
||||
Gnss_Synchro gs = Gnss_Synchro();
|
||||
gs.System = *sys.c_str();
|
||||
std::memcpy(static_cast<void*>(gs.Signal), sig.c_str(), 3);
|
||||
gs.PRN = prn_true;
|
||||
|
||||
gs.Channel_ID = 3;
|
||||
|
||||
gs.Acq_delay_samples = 431;
|
||||
gs.Acq_doppler_hz = 1234;
|
||||
gs.Acq_samplestamp_samples = 10000;
|
||||
gs.Acq_doppler_step = 125;
|
||||
gs.Flag_valid_acquisition = true;
|
||||
|
||||
gs.fs = 10000000;
|
||||
gs.Prompt_I = 10000.0;
|
||||
gs.Prompt_Q = 0.01;
|
||||
gs.CN0_dB_hz = 39;
|
||||
gs.Carrier_Doppler_hz = 321;
|
||||
gs.Tracking_sample_counter = 11000;
|
||||
gs.Flag_valid_symbol_output = false;
|
||||
gs.correlation_length_ms = 1;
|
||||
|
||||
gs.Flag_valid_word = false;
|
||||
gs.TOW_at_current_symbol_ms = 12345;
|
||||
|
||||
gs.Pseudorange_m = 22000002.1;
|
||||
gs.RX_time = 4321;
|
||||
gs.Flag_valid_pseudorange = false;
|
||||
gs.interp_TOW_ms = 20;
|
||||
|
||||
gs.Pseudorange_m = 22000002.1;
|
||||
gs.Carrier_phase_rads = 45.4;
|
||||
gs.Carrier_Doppler_hz = 321;
|
||||
gs.CN0_dB_hz = 39;
|
||||
|
||||
Serdes_Gnss_Synchro serdes = Serdes_Gnss_Synchro();
|
||||
|
||||
// Create a vector of Gnss_Synchro objects
|
||||
std::vector<Gnss_Synchro> vgs;
|
||||
vgs.push_back(gs);
|
||||
gs.PRN = prn_true2;
|
||||
vgs.push_back(gs);
|
||||
|
||||
// Serialize data
|
||||
std::string serialized_data = serdes.createProtobuffer(vgs);
|
||||
|
||||
// Recover data from serialization
|
||||
gnss_sdr::Observables obs;
|
||||
obs.ParseFromString(serialized_data);
|
||||
|
||||
// Check that recovered data is ok
|
||||
|
||||
// We can access like this:
|
||||
std::vector<Gnss_Synchro> vgs_read = serdes.readProtobuffer(obs);
|
||||
Gnss_Synchro gs_read{};
|
||||
gs_read = vgs_read[0];
|
||||
uint32_t prn_read = gs_read.PRN;
|
||||
uint32_t prn_read2 = vgs_read[1].PRN;
|
||||
std::string system_read(1, gs_read.System);
|
||||
std::string signal_read(gs_read.Signal);
|
||||
|
||||
// or without the need of gnss_synchro:
|
||||
int obs_size = obs.observable_size();
|
||||
uint32_t prn_read3 = obs.observable(0).prn();
|
||||
|
||||
EXPECT_EQ(sig, signal_read);
|
||||
EXPECT_EQ(sys, system_read);
|
||||
EXPECT_EQ(prn_true2, prn_read2);
|
||||
EXPECT_EQ(prn_true, prn_read3);
|
||||
EXPECT_EQ(prn_read, prn_read3);
|
||||
EXPECT_EQ(2, obs_size);
|
||||
}
|
||||
59
tests/unit-tests/control-plane/string_converter_test.cc
Normal file
59
tests/unit-tests/control-plane/string_converter_test.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* \file string_converter_test.cc
|
||||
* \brief This file implements unit tests for the StringConverter class.
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2012. 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 "gnss_sdr_make_unique.h"
|
||||
#include "string_converter.h"
|
||||
|
||||
|
||||
TEST(StringConverterTest, StringToBool)
|
||||
{
|
||||
auto converter = std::make_unique<StringConverter>();
|
||||
bool conversion_result = converter->convert("false", true);
|
||||
bool expected_false = false;
|
||||
EXPECT_EQ(expected_false, conversion_result);
|
||||
}
|
||||
|
||||
|
||||
TEST(StringConverterTest, StringToSizeT)
|
||||
{
|
||||
// Example using a raw pointer
|
||||
StringConverter* converter;
|
||||
converter = new StringConverter();
|
||||
size_t conversion_result = converter->convert("8", 1);
|
||||
unsigned int expected8 = 8;
|
||||
EXPECT_EQ(expected8, conversion_result);
|
||||
delete converter;
|
||||
}
|
||||
|
||||
|
||||
TEST(StringConverterTest, StringToBoolFail)
|
||||
{
|
||||
auto converter = std::make_unique<StringConverter>();
|
||||
bool conversion_result = converter->convert("lse", true);
|
||||
bool expected_true = true;
|
||||
EXPECT_EQ(expected_true, conversion_result);
|
||||
}
|
||||
|
||||
|
||||
TEST(StringConverterTest, StringToSizeTFail)
|
||||
{
|
||||
auto converter = std::make_unique<StringConverter>();
|
||||
size_t conversion_result = converter->convert("false", 1);
|
||||
unsigned int expected1 = 1;
|
||||
EXPECT_EQ(expected1, conversion_result);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,372 @@
|
||||
/*!
|
||||
* \file beidou_b1i_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* BeidouB1iPcpsAcquisition class based on some input parameters.
|
||||
* \author Sergi Segura, 2018. sergi.segura.munoz(at)gmail.com
|
||||
* \author Damian Miralles, 2019. dmiralles2009(at)gmail.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "Beidou_B1I.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "beidou_b1i_pcps_acquisition.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class BeidouB1iPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using BeidouB1iPcpsAcquisitionTest_msg_rx_sptr = std::shared_ptr<BeidouB1iPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
BeidouB1iPcpsAcquisitionTest_msg_rx_sptr BeidouB1iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
class BeidouB1iPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend BeidouB1iPcpsAcquisitionTest_msg_rx_sptr BeidouB1iPcpsAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
BeidouB1iPcpsAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~BeidouB1iPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
BeidouB1iPcpsAcquisitionTest_msg_rx_sptr BeidouB1iPcpsAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return BeidouB1iPcpsAcquisitionTest_msg_rx_sptr(new BeidouB1iPcpsAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void BeidouB1iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast &e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BeidouB1iPcpsAcquisitionTest_msg_rx::BeidouB1iPcpsAcquisitionTest_msg_rx() : gr::block("BeidouB1iPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto &&PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&BeidouB1iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&BeidouB1iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
BeidouB1iPcpsAcquisitionTest_msg_rx::~BeidouB1iPcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class BeidouB1iPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
BeidouB1iPcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
doppler_max = 5000;
|
||||
doppler_step = 100;
|
||||
}
|
||||
|
||||
~BeidouB1iPcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void plot_grid();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
};
|
||||
|
||||
|
||||
void BeidouB1iPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'C';
|
||||
std::string signal = "B1";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "25000000");
|
||||
config->set_property("Acquisition_B1.implementation", "BEIDOU_B1I_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_B1.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_B1.coherent_integration_time_ms", "1");
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
{
|
||||
config->set_property("Acquisition_B1.dump", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
config->set_property("Acquisition_B1.dump", "false");
|
||||
}
|
||||
config->set_property("Acquisition_B1.dump_filename", "./tmp-acq-bds-b1i/acquisition");
|
||||
config->set_property("Acquisition_B1.dump_channel", "1");
|
||||
config->set_property("Acquisition_B1.threshold", "0.0038");
|
||||
config->set_property("Acquisition_B1.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition_B1.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition_B1.repeat_satellite", "false");
|
||||
// config->set_property("Acquisition_B1.pfa", "0.0");
|
||||
}
|
||||
|
||||
|
||||
void BeidouB1iPcpsAcquisitionTest::plot_grid()
|
||||
{
|
||||
// load the measured values
|
||||
std::string basename = "./tmp-acq-bds-b1i/acquisition_C_B1";
|
||||
auto sat = static_cast<unsigned int>(gnss_synchro.PRN);
|
||||
|
||||
auto samples_per_code = static_cast<unsigned int>(round(25000000 / (BEIDOU_B1I_CODE_RATE_CPS / BEIDOU_B1I_CODE_LENGTH_CHIPS))); // !!
|
||||
Acquisition_Dump_Reader acq_dump(basename, sat, doppler_max, doppler_step, samples_per_code, 1);
|
||||
|
||||
if (!acq_dump.read_binary_acq())
|
||||
{
|
||||
std::cout << "Error reading files\n";
|
||||
}
|
||||
|
||||
std::vector<int> *doppler = &acq_dump.doppler;
|
||||
std::vector<unsigned int> *samples = &acq_dump.samples;
|
||||
std::vector<std::vector<float>> *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,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Plotting the acquisition grid. This can take a while...\n";
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string &gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("lines");
|
||||
if (FLAGS_show_plots)
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
g1.set_title("BeiDou B1I 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("BeiDou_B1I_acq_grid");
|
||||
g1.savetopdf("BeiDou_B1I_acq_grid");
|
||||
}
|
||||
catch (const GnuplotException &ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
std::string data_str = "./tmp-acq-bds-b1i";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB1iPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
std::shared_ptr<BeidouB1iPcpsAcquisition> acquisition = boost::make_shared<BeidouB1iPcpsAcquisition>(config.get(), "Acquisition_B1", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB1iPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 25000000;
|
||||
int nsamples = 25000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
init();
|
||||
std::shared_ptr<BeidouB1iPcpsAcquisition> acquisition = boost::make_shared<BeidouB1iPcpsAcquisition>(config.get(), "Acquisition_B1", 1, 0);
|
||||
std::shared_ptr<BeidouB1iPcpsAcquisitionTest_msg_rx> msg_rx = BeidouB1iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB1iPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
double expected_delay_samples = 22216;
|
||||
double expected_doppler_hz = 125;
|
||||
|
||||
init();
|
||||
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
{
|
||||
std::string data_str = "./tmp-acq-bds-b1i";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
fs::create_directory(data_str);
|
||||
}
|
||||
|
||||
std::shared_ptr<BeidouB1iPcpsAcquisition> acquisition = std::make_shared<BeidouB1iPcpsAcquisition>(config.get(), "Acquisition_B1", 1, 0);
|
||||
std::shared_ptr<BeidouB1iPcpsAcquisitionTest_msg_rx> msg_rx = BeidouB1iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.0038);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/BdsB1IStr01_fs25e6_if0_4ms.dat";
|
||||
const char *file_name = file.c_str();
|
||||
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.";
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
acquisition->init();
|
||||
|
||||
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.";
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * BEIDOU_B1I_CODE_LENGTH_CHIPS / 25000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*!
|
||||
* \file beidou_b3i_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* BeidouB3iPcpsAcquisition class based on some input parameters.
|
||||
* \author Damian Miralles, 2019. dmiralles2009(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "Beidou_B3I.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "beidou_b3i_pcps_acquisition.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class BeidouB3iPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using BeidouB3iPcpsAcquisitionTest_msg_rx_sptr = std::shared_ptr<BeidouB3iPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
BeidouB3iPcpsAcquisitionTest_msg_rx_sptr BeidouB3iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
class BeidouB3iPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend BeidouB3iPcpsAcquisitionTest_msg_rx_sptr BeidouB3iPcpsAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
BeidouB3iPcpsAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~BeidouB3iPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
BeidouB3iPcpsAcquisitionTest_msg_rx_sptr BeidouB3iPcpsAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return BeidouB3iPcpsAcquisitionTest_msg_rx_sptr(new BeidouB3iPcpsAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void BeidouB3iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast &e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BeidouB3iPcpsAcquisitionTest_msg_rx::BeidouB3iPcpsAcquisitionTest_msg_rx() : gr::block("BeidouB3iPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto &&PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&BeidouB3iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&BeidouB3iPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
BeidouB3iPcpsAcquisitionTest_msg_rx::~BeidouB3iPcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class BeidouB3iPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
BeidouB3iPcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
doppler_max = 5000;
|
||||
doppler_step = 100;
|
||||
}
|
||||
|
||||
~BeidouB3iPcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void plot_grid();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
};
|
||||
|
||||
|
||||
void BeidouB3iPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'C';
|
||||
std::string signal = "B3";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "50000000");
|
||||
config->set_property("Acquisition_B3.implementation", "BEIDOU_B3I_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_B3.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_B3.coherent_integration_time_ms", "1");
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
{
|
||||
config->set_property("Acquisition_B3.dump", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
config->set_property("Acquisition_B3.dump", "false");
|
||||
}
|
||||
config->set_property("Acquisition_B3.dump_filename", "./tmp-acq-bds-b3i/acquisition");
|
||||
config->set_property("Acquisition_B3.dump_channel", "1");
|
||||
config->set_property("Acquisition_B3.threshold", "0.00001");
|
||||
config->set_property("Acquisition_B3.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition_B3.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition_B3.repeat_satellite", "false");
|
||||
}
|
||||
|
||||
|
||||
void BeidouB3iPcpsAcquisitionTest::plot_grid()
|
||||
{
|
||||
// load the measured values
|
||||
std::string basename = "./tmp-acq-bds-b3i/acquisition_C_B3";
|
||||
auto sat = static_cast<unsigned int>(gnss_synchro.PRN);
|
||||
|
||||
auto samples_per_code = static_cast<unsigned int>(round(50000000 / (BEIDOU_B3I_CODE_RATE_CPS / BEIDOU_B3I_CODE_LENGTH_CHIPS))); // !!
|
||||
Acquisition_Dump_Reader acq_dump(basename, sat, doppler_max, doppler_step, samples_per_code, 1);
|
||||
|
||||
if (!acq_dump.read_binary_acq())
|
||||
{
|
||||
std::cout << "Error reading files\n";
|
||||
}
|
||||
|
||||
std::vector<int> *doppler = &acq_dump.doppler;
|
||||
std::vector<unsigned int> *samples = &acq_dump.samples;
|
||||
std::vector<std::vector<float>> *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,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Plotting the acquisition grid. This can take a while...\n";
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string &gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("lines");
|
||||
if (FLAGS_show_plots)
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
g1.set_title("BeiDou B3I 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("BDS_B3I_acq_grid");
|
||||
g1.savetopdf("BDS_B3I_acq_grid");
|
||||
}
|
||||
catch (const GnuplotException &ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
std::string data_str = "./tmp-acq-bds-b3i";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB3iPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
std::shared_ptr<BeidouB3iPcpsAcquisition> acquisition = boost::make_shared<BeidouB3iPcpsAcquisition>(config.get(), "Acquisition_B3", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB3iPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 50000000;
|
||||
int nsamples = 50000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
init();
|
||||
std::shared_ptr<BeidouB3iPcpsAcquisition> acquisition = boost::make_shared<BeidouB3iPcpsAcquisition>(config.get(), "Acquisition_B3", 1, 0);
|
||||
std::shared_ptr<BeidouB3iPcpsAcquisitionTest_msg_rx> msg_rx = BeidouB3iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BeidouB3iPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
double expected_delay_samples = 3380;
|
||||
double expected_doppler_hz = 700;
|
||||
|
||||
init();
|
||||
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
{
|
||||
std::string data_str = "./tmp-acq-bds-b3i";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
fs::create_directory(data_str);
|
||||
}
|
||||
|
||||
std::shared_ptr<BeidouB3iPcpsAcquisition> acquisition = std::make_shared<BeidouB3iPcpsAcquisition>(config.get(), "Acquisition_B3", 1, 0);
|
||||
std::shared_ptr<BeidouB3iPcpsAcquisitionTest_msg_rx> msg_rx = BeidouB3iPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.0002);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/BdsB3IStr01_fs50e6_if0_4ms.dat";
|
||||
const char *file_name = file.c_str();
|
||||
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.";
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
acquisition->init();
|
||||
|
||||
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.";
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * BEIDOU_B3I_CODE_LENGTH_CHIPS / 50000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_8ms_ambiguous_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1Pcps8msAmbiguousAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e1_pcps_8ms_ambiguous_acquisition.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr(new GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx::GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx::~GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
|
||||
~GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE1Pcps8msAmbiguousAcquisition> acquisition;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0;
|
||||
float max_delay_error_chips = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 8;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_8ms_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms", std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.threshold", "0.2");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 8;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_8ms_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms", std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.pfa", "0.1");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
try
|
||||
{
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
}
|
||||
catch (const boost::exception& e)
|
||||
{
|
||||
LOG(WARNING) << "Boost exception: " << boost::diagnostic_information(e);
|
||||
}
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (static_cast<double>(gnss_synchro.Acq_delay_samples) - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
auto acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1Pcps8msAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0, queue.get());
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1Pcps8msAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
// acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1Pcps8msAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1Pcps8msAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
// acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
stop_queue();
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_ambiguous_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsAmbiguousAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e1_pcps_ambiguous_acquisition.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr(new GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx::GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx::~GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE1PcpsAmbiguousAcquisition> acquisition;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0;
|
||||
float max_delay_error_chips = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter = 0;
|
||||
unsigned int detection_counter = 0;
|
||||
unsigned int correct_estimation_counter = 0;
|
||||
unsigned int acquired_samples = 0;
|
||||
unsigned int mean_acq_time_us = 0;
|
||||
|
||||
double mse_doppler = 0.0;
|
||||
double mse_delay = 0.0;
|
||||
|
||||
double Pd = 0.0;
|
||||
double Pfa_p = 0.0;
|
||||
double Pfa_a = 0.0;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.bit_transition_flag", "false");
|
||||
// config->set_property("Acquisition_1B.threshold", "2.5");
|
||||
config->set_property("Acquisition_1B.pfa", "0.0001");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1B.pfa", "0.0001");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (static_cast<double>(gnss_synchro.Acq_delay_samples) - 5) * 1023.0 / (fs_in * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= static_cast<double>(num_of_realizations);
|
||||
mse_doppler /= static_cast<double>(num_of_realizations);
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= static_cast<double>(num_of_realizations);
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config_1();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 36; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
stop_queue();
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_ambiguous_acquisition_gsoc_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsAmbiguousAcquisition class based on GSoC 2012 experiments.
|
||||
*
|
||||
* This test is a part of an experiment performed by Luis Esteve in the
|
||||
* framework of the Google Summer of Code (GSoC) 2012, with the collaboration
|
||||
* of Javier Arribas and Carles Fernández, related to the extension of GNSS-SDR
|
||||
* to Galileo. The objective is perform a positive acquisition of in-orbit
|
||||
* Galileo signals in the E1 band.
|
||||
*
|
||||
* Report:
|
||||
* https://docs.google.com/document/d/1SZ3m1K7Qf9GsZQGEF7VSOEewBDCjbylCClw9rSXwG7Y/edit?pli=1
|
||||
*
|
||||
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "galileo_e1_pcps_ambiguous_acquisition.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_signal.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx;
|
||||
|
||||
using GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx>;
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_sptr(new GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx::GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx::~GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionGSoCTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsAmbiguousAcquisitionGSoCTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GalileoE1PcpsAmbiguousAcquisitionGSoCTest() = default;
|
||||
|
||||
void init();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoCTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms", "4");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
// config->set_property("Acquisition_1B.threshold", "2.5");
|
||||
config->set_property("Acquisition_1B.pfa", "0.001");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "125");
|
||||
config->set_property("Acquisition_1B.repeat_satellite", "false");
|
||||
config->set_property("Acquisition_1B.cboc", "true");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoCTest::start_queue()
|
||||
{
|
||||
ch_thread = std::thread(&GalileoE1PcpsAmbiguousAcquisitionGSoCTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoCTest::wait_message()
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
stop_queue();
|
||||
}
|
||||
catch (const boost::exception& e)
|
||||
{
|
||||
DLOG(WARNING) << "Boost exception: " << boost::diagnostic_information(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionGSoCTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoCTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
EXPECT_STREQ("Galileo_E1_PCPS_Ambiguous_Acquisition", acquisition->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoCTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
int nsamples = 4 * fs_in;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionGSoCTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<GalileoE1PcpsAmbiguousAcquisition> acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionGSoCTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.00001));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 250));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
// std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
std::string file = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
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.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
start_queue();
|
||||
acquisition->set_local_code();
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
}) << "Failure starting acquisition";
|
||||
|
||||
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.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 0=ACQ STOP.";
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_ambiguous_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsAmbiguousAcquisition class based on some input parameters.
|
||||
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "Galileo_E1.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "galileo_e1_pcps_ambiguous_acquisition.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_signal.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx;
|
||||
|
||||
using GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx>;
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_make();
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_sptr(new GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx::GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx() : gr::block("GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx::~GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsAmbiguousAcquisitionTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
doppler_max = 10000;
|
||||
doppler_step = 250;
|
||||
}
|
||||
|
||||
~GalileoE1PcpsAmbiguousAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void plot_grid();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1B";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms", "4");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
config->set_property("Acquisition_1B.dump", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
config->set_property("Acquisition_1B.dump_filename", "./tmp-acq-gal1/acquisition");
|
||||
// config->set_property("Acquisition_1B.threshold", "2.5");
|
||||
config->set_property("Acquisition_1B.pfa", "0.001");
|
||||
config->set_property("Acquisition_1B.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition_1B.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition_1B.repeat_satellite", "false");
|
||||
config->set_property("Acquisition_1B.cboc", "true");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionTest::plot_grid()
|
||||
{
|
||||
// load the measured values
|
||||
std::string basename = "./tmp-acq-gal1/acquisition_E_1B";
|
||||
auto sat = static_cast<unsigned int>(gnss_synchro.PRN);
|
||||
|
||||
auto samples_per_code = static_cast<unsigned int>(round(4000000 / (GALILEO_E1_CODE_CHIP_RATE_CPS / GALILEO_E1_B_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\n";
|
||||
}
|
||||
|
||||
std::vector<int>* doppler = &acq_dump.doppler;
|
||||
std::vector<unsigned int>* samples = &acq_dump.samples;
|
||||
std::vector<std::vector<float>>* mag = &acq_dump.mag;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||
#else
|
||||
const std::string gnuplot_executable(absl::GetFlag(FLAGS_gnuplot_executable));
|
||||
#endif
|
||||
if (gnuplot_executable.empty())
|
||||
{
|
||||
std::cout << "WARNING: Although the flag plot_acq_grid has been set to TRUE,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Plotting the acquisition grid. This can take a while...\n";
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string& gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("lines");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
g1.set_title("Galileo E1b/c 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("Galileo_E1_acq_grid");
|
||||
g1.savetopdf("Galileo_E1_acq_grid");
|
||||
}
|
||||
catch (const GnuplotException& ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
std::string data_str = "./tmp-acq-gal1";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
int nsamples = 4 * fs_in;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
init();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<AcquisitionInterface> acquisition = std::dynamic_pointer_cast<AcquisitionInterface>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
std::string data_str = "./tmp-acq-gal1";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
fs::create_directory(data_str);
|
||||
}
|
||||
|
||||
double expected_delay_samples = 2920; // 18250;
|
||||
double expected_doppler_hz = -632;
|
||||
init();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
std::shared_ptr<GalileoE1PcpsAmbiguousAcquisition> acquisition = std::dynamic_pointer_cast<GalileoE1PcpsAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsAmbiguousAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 1e-9));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", doppler_max));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", doppler_step));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
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.";
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
|
||||
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.";
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
std::cout << "Delay: " << gnss_synchro.Acq_delay_samples << '\n';
|
||||
std::cout << "Doppler: " << gnss_synchro.Acq_doppler_hz << '\n';
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
EXPECT_LE(doppler_error_hz, 166) << "Doppler error exceeds the expected value: 166 Hz = 2/(3*integration period)";
|
||||
EXPECT_LT(delay_error_chips, 0.175) << "Delay error exceeds the expected value: 0.175 chips";
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
plot_grid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_acquisition_test_fpga.cc
|
||||
* \brief This class implements an acquisition test Galileo FPGA acquisition
|
||||
* \authors <ul>
|
||||
* <li> Marc Majoral, 2019. mmajoral(at)cttc.cat
|
||||
* <li> Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
* </ul>
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "Galileo_E1.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "fpga_switch.h"
|
||||
#include "galileo_e1_pcps_ambiguous_acquisition_fpga.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_signal.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cmath> // for abs, pow, floor
|
||||
#include <fcntl.h> // for O_WRONLY
|
||||
#include <pthread.h>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct DMA_handler_args_galileo_e1_pcps_ambiguous_acq_test
|
||||
{
|
||||
std::string file;
|
||||
int32_t nsamples_tx;
|
||||
int32_t skip_used_samples;
|
||||
float scaling_factor;
|
||||
};
|
||||
|
||||
struct acquisition_handler_args_galileo_e1_pcps_ambiguous_acq_test
|
||||
{
|
||||
std::shared_ptr<AcquisitionInterface> acquisition;
|
||||
};
|
||||
|
||||
class GalileoE1PcpsAmbiguousAcquisitionTestFpga : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
bool acquire_signal();
|
||||
std::string implementation = "GPS_L1_CA_DLL_PLL_Tracking_FPGA";
|
||||
std::vector<Gnss_Synchro> gnss_synchro_vec;
|
||||
|
||||
const int32_t TEST_ACQ_SKIP_SAMPLES = 1024;
|
||||
const int BASEBAND_SAMPLING_FREQ = 4000000;
|
||||
const float MAX_SAMPLE_VALUE = 0.096257761120796;
|
||||
const int DMA_BITS_PER_SAMPLE = 8;
|
||||
const float DMA_SIGNAL_SCALING_FACTOR = (pow(2, DMA_BITS_PER_SAMPLE - 1) - 1) / MAX_SAMPLE_VALUE;
|
||||
|
||||
protected:
|
||||
GalileoE1PcpsAmbiguousAcquisitionTestFpga();
|
||||
~GalileoE1PcpsAmbiguousAcquisitionTestFpga() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
unsigned int nsamples_to_transfer;
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsAmbiguousAcquisitionTestFpga::GalileoE1PcpsAmbiguousAcquisitionTestFpga()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
doppler_max = 5000;
|
||||
doppler_step = 100;
|
||||
nsamples_to_transfer = 0;
|
||||
}
|
||||
|
||||
|
||||
void* handler_DMA_galileo_e1_pcps_ambiguous_acq_test(void* arguments)
|
||||
{
|
||||
const int MAX_INPUT_SAMPLES_TOTAL = 16384;
|
||||
|
||||
auto* args = (struct DMA_handler_args_galileo_e1_pcps_ambiguous_acq_test*)arguments;
|
||||
|
||||
std::string Filename = args->file; // input filename
|
||||
int32_t skip_used_samples = args->skip_used_samples;
|
||||
int32_t nsamples_tx = args->nsamples_tx;
|
||||
|
||||
std::vector<float> input_samples(MAX_INPUT_SAMPLES_TOTAL * 2);
|
||||
std::vector<int8_t> input_samples_dma(MAX_INPUT_SAMPLES_TOTAL * 2 * 2);
|
||||
bool file_completed = false;
|
||||
int32_t nsamples_remaining;
|
||||
int32_t nsamples_block_size;
|
||||
unsigned int dma_index;
|
||||
|
||||
int tx_fd; // DMA descriptor
|
||||
std::ifstream infile;
|
||||
|
||||
infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
|
||||
try
|
||||
{
|
||||
infile.open(Filename, std::ios::binary);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception opening file " << Filename << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// Open DMA device
|
||||
// *************************************************************************
|
||||
tx_fd = open("/dev/loop_tx", O_WRONLY);
|
||||
if (tx_fd < 0)
|
||||
{
|
||||
std::cout << "Cannot open loop device\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// Open input file
|
||||
// *************************************************************************
|
||||
uint32_t skip_samples = 0; // static_cast<uint32_t>(FLAGS_skip_samples);
|
||||
|
||||
if (skip_samples + skip_used_samples > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
infile.ignore((skip_samples + skip_used_samples) * 2);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception reading file " << Filename << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
nsamples_remaining = nsamples_tx;
|
||||
nsamples_block_size = 0;
|
||||
|
||||
while (file_completed == false)
|
||||
{
|
||||
dma_index = 0;
|
||||
|
||||
if (nsamples_remaining > MAX_INPUT_SAMPLES_TOTAL)
|
||||
{
|
||||
nsamples_block_size = MAX_INPUT_SAMPLES_TOTAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
nsamples_block_size = nsamples_remaining;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 2 bytes per complex sample
|
||||
infile.read(reinterpret_cast<char*>(input_samples.data()), nsamples_block_size * 2 * sizeof(float));
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception reading file " << Filename << '\n';
|
||||
}
|
||||
|
||||
for (int index0 = 0; index0 < (nsamples_block_size * 2); index0 += 2)
|
||||
{
|
||||
// channel 1 (queue 1) -> E5/L5
|
||||
input_samples_dma[dma_index] = static_cast<int8_t>(input_samples[index0] * args->scaling_factor);
|
||||
input_samples_dma[dma_index + 1] = static_cast<int8_t>(input_samples[index0 + 1] * args->scaling_factor);
|
||||
// channel 0 (queue 0) -> E1/L1
|
||||
input_samples_dma[dma_index + 2] = 0;
|
||||
input_samples_dma[dma_index + 3] = 0;
|
||||
|
||||
dma_index += 4;
|
||||
}
|
||||
|
||||
if (write(tx_fd, input_samples_dma.data(), nsamples_block_size * 2 * 2) != nsamples_block_size * 2 * 2)
|
||||
{
|
||||
std::cerr << "Error: DMA could not send all the required samples \n";
|
||||
}
|
||||
|
||||
// Throttle the DMA
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
nsamples_remaining -= nsamples_block_size;
|
||||
|
||||
if (nsamples_remaining == 0)
|
||||
{
|
||||
file_completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
infile.close();
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception closing files " << Filename << '\n';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
close(tx_fd);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception closing loop device \n";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void* handler_acquisition_galileo_e1_pcps_ambiguous_acq_test(void* arguments)
|
||||
{
|
||||
// the acquisition is a blocking function so we have to
|
||||
// create a thread
|
||||
auto* args = (struct acquisition_handler_args_galileo_e1_pcps_ambiguous_acq_test*)arguments;
|
||||
args->acquisition->reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// When using the FPGA the acquisition class calls the states
|
||||
// of the channel finite state machine directly. This is done
|
||||
// in order to reduce the latency of the receiver when going
|
||||
// from acquisition to tracking. In order to execute the
|
||||
// acquisition in the unit tests we need to create a derived
|
||||
// class of the channel finite state machine. Some of the states
|
||||
// of the channel state machine are modified here, in order to
|
||||
// simplify the instantiation of the acquisition class in the
|
||||
// unit test.
|
||||
class ChannelFsm_galileo_e1_pcps_ambiguous_acq_test : public ChannelFsm
|
||||
{
|
||||
public:
|
||||
bool Event_valid_acquisition() override
|
||||
{
|
||||
acquisition_successful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_failed_acquisition_repeat() override
|
||||
{
|
||||
acquisition_successful = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_failed_acquisition_no_repeat() override
|
||||
{
|
||||
acquisition_successful = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_check_test_result()
|
||||
{
|
||||
return acquisition_successful;
|
||||
}
|
||||
|
||||
void Event_clear_test_result()
|
||||
{
|
||||
acquisition_successful = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool acquisition_successful{};
|
||||
};
|
||||
|
||||
|
||||
bool GalileoE1PcpsAmbiguousAcquisitionTestFpga::acquire_signal()
|
||||
{
|
||||
pthread_t thread_DMA, thread_acquisition;
|
||||
|
||||
// 1. Setup GNU Radio flowgraph (file_source -> Acquisition_10m)
|
||||
int SV_ID = 1; // initial sv id
|
||||
|
||||
// fsm
|
||||
std::shared_ptr<ChannelFsm_galileo_e1_pcps_ambiguous_acq_test> channel_fsm_;
|
||||
channel_fsm_ = std::make_shared<ChannelFsm_galileo_e1_pcps_ambiguous_acq_test>();
|
||||
bool acquisition_successful;
|
||||
|
||||
// Satellite signal definition
|
||||
Gnss_Synchro tmp_gnss_synchro;
|
||||
tmp_gnss_synchro.Channel_ID = 0;
|
||||
|
||||
std::shared_ptr<AcquisitionInterface> acquisition;
|
||||
|
||||
std::string signal;
|
||||
struct DMA_handler_args_galileo_e1_pcps_ambiguous_acq_test args;
|
||||
struct acquisition_handler_args_galileo_e1_pcps_ambiguous_acq_test args_acq;
|
||||
|
||||
// set the scaling factor
|
||||
args.scaling_factor = DMA_SIGNAL_SCALING_FACTOR;
|
||||
|
||||
std::string file = "data/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
|
||||
args.file = std::move(file); // DMA file configuration
|
||||
|
||||
// instantiate the FPGA switch and set the
|
||||
// switch position to DMA.
|
||||
std::shared_ptr<Fpga_Switch> switch_fpga;
|
||||
switch_fpga = std::make_shared<Fpga_Switch>();
|
||||
switch_fpga->set_switch_position(0); // set switch position to DMA
|
||||
|
||||
// create the correspondign acquisition block according to the desired tracking signal
|
||||
tmp_gnss_synchro.System = 'E';
|
||||
signal = "1B";
|
||||
const char* str = signal.c_str(); // get a C style null terminated string
|
||||
std::memcpy(static_cast<void*>(tmp_gnss_synchro.Signal), str, 2); // copy string into synchro char array: 2 char + null
|
||||
tmp_gnss_synchro.PRN = SV_ID;
|
||||
acquisition = std::make_shared<GalileoE1PcpsAmbiguousAcquisitionFpga>(config.get(), "Acquisition", 0, 0);
|
||||
|
||||
acquisition->set_gnss_synchro(&tmp_gnss_synchro);
|
||||
acquisition->set_channel_fsm(channel_fsm_);
|
||||
acquisition->set_channel(1);
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
acquisition->set_doppler_center(0);
|
||||
acquisition->set_threshold(0.001);
|
||||
|
||||
nsamples_to_transfer = static_cast<unsigned int>(std::round(static_cast<double>(BASEBAND_SAMPLING_FREQ) / (GALILEO_E1_CODE_CHIP_RATE_CPS / GALILEO_E1_B_CODE_LENGTH_CHIPS)));
|
||||
|
||||
channel_fsm_->Event_clear_test_result();
|
||||
|
||||
acquisition->stop_acquisition(); // reset the whole system including the sample counters
|
||||
acquisition->init();
|
||||
acquisition->set_local_code();
|
||||
|
||||
args.skip_used_samples = 0;
|
||||
|
||||
// Configure the DMA to send the required samples to perform an acquisition
|
||||
args.nsamples_tx = nsamples_to_transfer;
|
||||
|
||||
// run the acquisition. The acquisition must run in a separate thread because it is a blocking function
|
||||
args_acq.acquisition = std::move(acquisition);
|
||||
|
||||
if (pthread_create(&thread_acquisition, nullptr, handler_acquisition_galileo_e1_pcps_ambiguous_acq_test, reinterpret_cast<void*>(&args_acq)) < 0)
|
||||
{
|
||||
std::cout << "ERROR cannot create acquisition Process\n";
|
||||
}
|
||||
|
||||
// wait to give time for the acquisition thread to set up the acquisition HW accelerator in the FPGA
|
||||
usleep(1000000);
|
||||
|
||||
// create DMA child process
|
||||
if (pthread_create(&thread_DMA, nullptr, handler_DMA_galileo_e1_pcps_ambiguous_acq_test, reinterpret_cast<void*>(&args)) < 0)
|
||||
{
|
||||
std::cout << "ERROR cannot create DMA Process\n";
|
||||
}
|
||||
|
||||
// wait until the acquisition is finished
|
||||
pthread_join(thread_acquisition, nullptr);
|
||||
|
||||
// wait for the child DMA process to finish
|
||||
pthread_join(thread_DMA, nullptr);
|
||||
|
||||
acquisition_successful = channel_fsm_->Event_check_test_result();
|
||||
|
||||
if (acquisition_successful)
|
||||
{
|
||||
gnss_synchro_vec.push_back(tmp_gnss_synchro);
|
||||
}
|
||||
|
||||
if (!gnss_synchro_vec.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsAmbiguousAcquisitionTestFpga::init()
|
||||
{
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("Acquisition.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition_FPGA");
|
||||
config->set_property("Acquisition.threshold", "0.00001");
|
||||
config->set_property("Acquisition.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition.repeat_satellite", "false");
|
||||
|
||||
// the test file is sampled @ 4MSPs only ,so we have to use the FPGA queue corresponding
|
||||
// to the L5/E5a frequency band in order to avoid the L1/E1 factor :4 downsampling filter
|
||||
config->set_property("Acquisition.downsampling_factor", "1");
|
||||
config->set_property("Acquisition.select_queue_Fpga", "1");
|
||||
config->set_property("Acquisition.total_block_exp", "14");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTestFpga, ValidationOfResults)
|
||||
{
|
||||
struct DMA_handler_args_galileo_e1_pcps_ambiguous_acq_test args;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
double expected_delay_samples = 2920; // 18250;
|
||||
double expected_doppler_hz = -632;
|
||||
|
||||
init();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
ASSERT_EQ(acquire_signal(), true);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
uint32_t n = 0; // there is only one channel
|
||||
std::cout << "Acquired " << nsamples_to_transfer << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro_vec.at(n).Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro_vec.at(n).Acq_doppler_hz);
|
||||
|
||||
// the acquisition grid is not available when using the FPGA
|
||||
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_cccwsr_ambiguous_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsCccwsrAmbiguousAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e1_pcps_cccwsr_ambiguous_acquisition.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx;
|
||||
|
||||
using GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx>;
|
||||
|
||||
GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_sptr GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_sptr(new GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx::GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx::~GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE1PcpsCccwsrAmbiguousAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsCccwsrAmbiguousAcquisitionTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
|
||||
~GalileoE1PcpsCccwsrAmbiguousAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE1PcpsCccwsrAmbiguousAcquisition> acquisition;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0;
|
||||
float max_delay_error_chips = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_CCCWSR_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.threshold", "0.7");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_CCCWSR_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.threshold", "0.00215"); // Pfa,a = 0.1
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsCccwsrAmbiguousAcquisitionTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsCccwsrAmbiguousAcquisitionTest, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsCccwsrAmbiguousAcquisition>(acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsCccwsrAmbiguousAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsCccwsrAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsCccwsrAmbiguousAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsCccwsrAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.00001));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
// EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsCccwsrAmbiguousAcquisitionTest, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsCccwsrAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsCccwsrAmbiguousAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.00215));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,930 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_quicksync_ambiguous_acquisition_gsoc2014_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsQuickSyncAmbiguousAcquisition class.
|
||||
* \author Damian Miralles, 2014. dmiralles2009@gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "fir_filter.h"
|
||||
#include "galileo_e1_pcps_quicksync_ambiguous_acquisition.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_double(e1_value_threshold, 0.3, "Value of the threshold for the acquisition");
|
||||
DEFINE_int32(e1_value_CN0_dB_0, 50, "Value for the CN0_dB_0 in channel 0");
|
||||
#else
|
||||
ABSL_FLAG(double, e1_value_threshold, 0.3, "Value of the threshold for the acquisition");
|
||||
ABSL_FLAG(int32_t, e1_value_CN0_dB_0, 50, "Value for the CN0_dB_0 in channel 0");
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx;
|
||||
|
||||
using GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx>;
|
||||
|
||||
GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_sptr GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_sptr GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_sptr GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_sptr(new GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx::GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx::~GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
~GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void config_3();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE1PcpsQuickSyncAmbiguousAcquisition> acquisition;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
unsigned int folding_factor = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd; // Probability of detection
|
||||
double Pfa_p; // Probability of false alarm on present satellite
|
||||
double Pfa_a; // Probability of false alarm on absent satellite
|
||||
double Pmd; // Probability of miss detection
|
||||
|
||||
std::ofstream pdpfafile;
|
||||
unsigned int miss_detection_counter;
|
||||
bool dump_test_results = false;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
|
||||
miss_detection_counter = 0;
|
||||
Pmd = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 8;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1B.threshold", "1");
|
||||
config->set_property("Acquisition_1Bdoppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.folding_factor", "2");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 8;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
/*Unset this flag to eliminates data logging for the Validation of results
|
||||
probabilities test*/
|
||||
dump_test_results = true;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(FLAGS_e1_value_CN0_dB_0));
|
||||
#else
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(absl::GetFlag(FLAGS_e1_value_CN0_dB_0)));
|
||||
#endif
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.bit_transition_flag", "false");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("Acquisition_1B.threshold", std::to_string(FLAGS_e1_value_threshold));
|
||||
#else
|
||||
config->set_property("Acquisition_1B.threshold", std::to_string(absl::GetFlag(FLAGS_e1_value_threshold)));
|
||||
#endif
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "125");
|
||||
config->set_property("Acquisition_1B.folding_factor", "2");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::config_3()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 8;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(FLAGS_e1_value_CN0_dB_0));
|
||||
#else
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(absl::GetFlag(FLAGS_e1_value_CN0_dB_0)));
|
||||
#endif
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false"); //
|
||||
config->set_property("SignalSource.data_flag", "false"); //
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_QuickSync_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.max_dwells", "1");
|
||||
config->set_property("Acquisition_1B.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1B.threshold", "0.2");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "125");
|
||||
config->set_property("Acquisition_1B.folding_factor", "4");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
begin = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
else if (message == 2 && gnss_synchro.PRN == 10)
|
||||
{
|
||||
/*
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
miss_detection_counter++;
|
||||
}
|
||||
*/
|
||||
miss_detection_counter++;
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= static_cast<double>(num_of_realizations);
|
||||
mse_doppler /= static_cast<double>(num_of_realizations);
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pmd = static_cast<double>(miss_detection_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= static_cast<double>(num_of_realizations);
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsQuickSyncAmbiguousAcquisition>(acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test, ConnectAndRun)
|
||||
{
|
||||
LOG(INFO) << "**Start connect and run test";
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
config_1();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsQuickSyncAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source =
|
||||
gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve =
|
||||
gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
LOG(INFO) << "----end connect and run test-----";
|
||||
LOG(INFO) << "**End connect and run test";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test, ValidationOfResults)
|
||||
{
|
||||
LOG(INFO) << "Start validation of results test";
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsQuickSyncAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(0);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 125));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(1);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
DLOG(INFO) << "End validation of results test";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test, ValidationOfResultsWithNoise)
|
||||
{
|
||||
LOG(INFO) << "Start validation of results with noise+interference test";
|
||||
config_3();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsQuickSyncAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(50);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(5);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
DLOG(INFO) << "End validation of results with noise+interference test";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsQuickSyncAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsQuickSyncAmbiguousAcquisitionGSoC2014Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->reset();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Estimated probability of miss detection (satellite present) = " << Pmd << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
|
||||
if (dump_test_results)
|
||||
{
|
||||
std::stringstream filenamepd;
|
||||
filenamepd.str("");
|
||||
filenamepd << "../data/test_statistics_" << gnss_synchro.System
|
||||
<< "_" << gnss_synchro.Signal << "_sat_"
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << FLAGS_e1_value_CN0_dB_0 << "_dBHz.csv";
|
||||
#else
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << absl::GetFlag(FLAGS_e1_value_CN0_dB_0) << "_dBHz.csv";
|
||||
#endif
|
||||
pdpfafile.open(filenamepd.str().c_str(), std::ios::app | std::ios::out);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
pdpfafile << FLAGS_e1_value_threshold << "," << Pd << "," << Pfa_p << "," << Pmd << '\n';
|
||||
#else
|
||||
pdpfafile << absl::GetFlag(FLAGS_e1_value_threshold) << "," << Pd << "," << Pfa_p << "," << Pmd << '\n';
|
||||
#endif
|
||||
pdpfafile.close();
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
|
||||
if (dump_test_results)
|
||||
{
|
||||
std::stringstream filenamepf;
|
||||
filenamepf.str("");
|
||||
filenamepf << "../data/test_statistics_" << gnss_synchro.System
|
||||
<< "_" << gnss_synchro.Signal << "_sat_"
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << FLAGS_e1_value_CN0_dB_0 << "_dBHz.csv";
|
||||
#else
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << absl::GetFlag(FLAGS_e1_value_CN0_dB_0) << "_dBHz.csv";
|
||||
#endif
|
||||
pdpfafile.open(filenamepf.str().c_str(), std::ios::app | std::ios::out);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
pdpfafile << FLAGS_e1_value_threshold << "," << Pfa_a << '\n';
|
||||
#else
|
||||
pdpfafile << absl::GetFlag(FLAGS_e1_value_threshold) << "," << Pfa_a << '\n';
|
||||
#endif
|
||||
pdpfafile.close();
|
||||
}
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
/*!
|
||||
* \file galileo_e1_pcps_tong_ambiguous_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE1PcpsTongAmbiguousAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com *
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e1_pcps_tong_ambiguous_acquisition.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_sptr(new GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx::GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx::~GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx() = default;
|
||||
|
||||
|
||||
class GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
|
||||
~GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE1PcpsTongAmbiguousAcquisition> acquisition;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0;
|
||||
float max_delay_error_chips = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1B";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Tong_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.tong_init_val", "1");
|
||||
config->set_property("Acquisition_1B.tong_max_val", "8");
|
||||
config->set_property("Acquisition_1B.threshold", "0.3");
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1B";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "50");
|
||||
config->set_property("SignalSource.doppler_Hz_0",
|
||||
std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0",
|
||||
std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1B.implementation", "Galileo_E1_PCPS_Tong_Ambiguous_Acquisition");
|
||||
config->set_property("Acquisition_1B.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1B.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1B.tong_init_val", "1");
|
||||
config->set_property("Acquisition_1B.tong_max_val", "8");
|
||||
config->set_property("Acquisition_1B.threshold", "0.00028"); // Pfa,a = 0.1
|
||||
config->set_property("Acquisition_1B.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1B.doppler_step", "250");
|
||||
config->set_property("Acquisition_1B.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsTongAmbiguousAcquisition>(acq_);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config_1();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsTongAmbiguousAcquisition>(acq_);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, acquisition->get_left_block(), 0);
|
||||
}) << "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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsTongAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(5000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(100);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.01);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
acquisition->reset();
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
// std::cout << "Delay: " << gnss_synchro.Acq_delay_samples << '\n';
|
||||
// std::cout << "Doppler: " << gnss_synchro.Acq_doppler_hz << '\n';
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GNSSBlockInterface> acq_ = factory->GetBlock(config.get(), "Acquisition_1B", 1, 0);
|
||||
acquisition = std::dynamic_pointer_cast<GalileoE1PcpsTongAmbiguousAcquisition>(acq_);
|
||||
auto msg_rx = GalileoE1PcpsTongAmbiguousAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1B.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1B.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1B.threshold", 0.00028));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,667 @@
|
||||
/*!
|
||||
* \file galileo_e5a_pcps_acquisition_gsoc2014_gensource_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE5a3msNoncoherentIQAcquisition class.
|
||||
* \author Marc Sales, 2014. marcsales92(at)gmail.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e5a_noncoherent_iq_acquisition_caf.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx;
|
||||
|
||||
using GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_sptr = gnss_shared_ptr<GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx>;
|
||||
|
||||
GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_sptr GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_sptr GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_sptr GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_sptr(new GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx::GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx::~GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx() = default;
|
||||
|
||||
|
||||
class GalileoE5aPcpsAcquisitionGSoC2014GensourceTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE5aPcpsAcquisitionGSoC2014GensourceTest()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
|
||||
~GalileoE5aPcpsAcquisitionGSoC2014GensourceTest() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void config_3();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GalileoE5aNoncoherentIQAcquisitionCaf> acquisition;
|
||||
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_delay_sec = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
double expected_delay_chips1 = 0.0;
|
||||
double expected_delay_sec1 = 0.0;
|
||||
double expected_doppler_hz1 = 0.0;
|
||||
double expected_delay_chips2 = 0.0;
|
||||
double expected_delay_sec2 = 0.0;
|
||||
double expected_doppler_hz2 = 0.0;
|
||||
double expected_delay_chips3 = 0.0;
|
||||
double expected_delay_sec3 = 0.0;
|
||||
double expected_doppler_hz3 = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
int CAF_window_hz = 0;
|
||||
int Zero_padding = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
|
||||
int sat = 0;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "5X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 32e6;
|
||||
|
||||
expected_delay_chips = round(14000.0 * 10230000.0 / static_cast<double>(fs_in));
|
||||
expected_doppler_hz = 2800;
|
||||
expected_delay_sec = 94;
|
||||
CAF_window_hz = 0;
|
||||
Zero_padding = 0;
|
||||
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("Channel.signal", std::move(signal));
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.signal_0", "5X");
|
||||
config->set_property("SignalSource.PRN_0", "11");
|
||||
config->set_property("SignalSource.CN0_dB_0", "50");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
config->set_property("SignalSource.delay_sec_0", std::to_string(expected_delay_sec));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("SignalSource.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/signal_source.dat");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_5X.implementation", "Galileo_E5a_Noncoherent_IQ_Acquisition_CAF");
|
||||
config->set_property("Acquisition_5X.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_5X.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_5X.max_dwells", "1");
|
||||
config->set_property("Acquisition_5X.CAF_window_hz", std::to_string(CAF_window_hz));
|
||||
config->set_property("Acquisition_5X.Zero_padding", std::to_string(Zero_padding));
|
||||
config->set_property("Acquisition_5X.pfa", "0.003");
|
||||
// config->set_property("Acquisition_5X.threshold", "0.01");
|
||||
config->set_property("Acquisition_5X.doppler_max", "10000");
|
||||
config->set_property("Acquisition_5X.doppler_step", "250");
|
||||
config->set_property("Acquisition_5X.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_5X.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/acquisition.dat");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "5X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 3;
|
||||
|
||||
fs_in = 12e6;
|
||||
|
||||
expected_delay_chips = 1000;
|
||||
expected_doppler_hz = 250;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("Acquisition_5X.implementation", "Galileo_E5a_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_5X.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_5X.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_5X.max_dwells", "1");
|
||||
config->set_property("Acquisition_5X.threshold", "0.1");
|
||||
config->set_property("Acquisition_5X.doppler_max", "10000");
|
||||
config->set_property("Acquisition_5X.doppler_step", "250");
|
||||
config->set_property("Acquisition_5X.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_5X.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/acquisition.dat");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::config_3()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
// std::string signal = "5Q";
|
||||
std::string signal = "5X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 3;
|
||||
fs_in = 12e6;
|
||||
|
||||
expected_delay_chips = 0;
|
||||
expected_delay_sec = 0;
|
||||
expected_doppler_hz = 0;
|
||||
expected_delay_chips1 = 6000;
|
||||
expected_delay_sec1 = 10;
|
||||
expected_doppler_hz1 = 700;
|
||||
expected_delay_chips2 = 9000;
|
||||
expected_delay_sec2 = 26;
|
||||
expected_doppler_hz2 = -1500;
|
||||
expected_delay_chips3 = 2000;
|
||||
expected_delay_sec3 = 77;
|
||||
expected_doppler_hz3 = 5000;
|
||||
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 10;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.signal_0", "5X");
|
||||
config->set_property("SignalSource.PRN_0", "11");
|
||||
config->set_property("SignalSource.CN0_dB_0", "46");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
config->set_property("SignalSource.delay_sec_0", std::to_string(expected_delay_sec));
|
||||
|
||||
config->set_property("SignalSource.system_1", "E");
|
||||
config->set_property("SignalSource.signal_1", "5X");
|
||||
config->set_property("SignalSource.PRN_1", "12");
|
||||
config->set_property("SignalSource.CN0_dB_1", "46");
|
||||
config->set_property("SignalSource.doppler_Hz_1", std::to_string(expected_doppler_hz1));
|
||||
config->set_property("SignalSource.delay_chips_1", std::to_string(expected_delay_chips1));
|
||||
config->set_property("SignalSource.delay_sec_1", std::to_string(expected_delay_sec1));
|
||||
|
||||
config->set_property("SignalSource.system_2", "E");
|
||||
config->set_property("SignalSource.signal_2", "5X");
|
||||
config->set_property("SignalSource.PRN_2", "19");
|
||||
config->set_property("SignalSource.CN0_dB_2", "43");
|
||||
config->set_property("SignalSource.doppler_Hz_2", std::to_string(expected_doppler_hz2));
|
||||
config->set_property("SignalSource.delay_chips_2", std::to_string(expected_delay_chips2));
|
||||
config->set_property("SignalSource.delay_sec_2", std::to_string(expected_delay_sec2));
|
||||
|
||||
config->set_property("SignalSource.system_3", "E");
|
||||
config->set_property("SignalSource.signal_3", "5X");
|
||||
config->set_property("SignalSource.PRN_3", "20");
|
||||
config->set_property("SignalSource.CN0_dB_3", "39");
|
||||
config->set_property("SignalSource.doppler_Hz_3", std::to_string(expected_doppler_hz3));
|
||||
config->set_property("SignalSource.delay_chips_3", std::to_string(expected_delay_chips3));
|
||||
config->set_property("SignalSource.delay_sec_3", std::to_string(expected_delay_sec3));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("SignalSource.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/signal_source.dat");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_5X.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_5X.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_5X.max_dwells", "1");
|
||||
config->set_property("Acquisition_5X.implementation", "Galileo_E5a_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_5X.threshold", "0.5");
|
||||
config->set_property("Acquisition_5X.doppler_max", "10000");
|
||||
config->set_property("Acquisition_5X.doppler_step", "250");
|
||||
config->set_property("Acquisition_5X.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_5X.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/acquisition.dat");
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
double delay_error_chips = 0.0;
|
||||
double doppler_error_hz = 0.0;
|
||||
switch (sat)
|
||||
{
|
||||
case 0:
|
||||
delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 10230.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
break;
|
||||
case 1:
|
||||
delay_error_chips = std::abs(static_cast<double>(expected_delay_chips1) - (gnss_synchro.Acq_delay_samples - 5) * 10230.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
doppler_error_hz = std::abs(expected_doppler_hz1 - gnss_synchro.Acq_doppler_hz);
|
||||
break;
|
||||
case 2:
|
||||
delay_error_chips = std::abs(static_cast<double>(expected_delay_chips2) - (gnss_synchro.Acq_delay_samples - 5) * 10230.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
doppler_error_hz = std::abs(expected_doppler_hz2 - gnss_synchro.Acq_doppler_hz);
|
||||
break;
|
||||
case 3:
|
||||
delay_error_chips = std::abs(static_cast<double>(expected_delay_chips3) - (gnss_synchro.Acq_delay_samples - 5) * 10230.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
doppler_error_hz = std::abs(expected_doppler_hz3 - gnss_synchro.Acq_doppler_hz);
|
||||
break;
|
||||
default: // case 3
|
||||
std::cout << "Error: message from unexpected acquisition channel\n";
|
||||
break;
|
||||
}
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
/*
|
||||
double delay_error_chips = abs((double)expected_delay_chips - (double)(gnss_synchro.Acq_delay_samples-5)*10230.0/((double)fs_in*1e-3));
|
||||
double doppler_error_hz = abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
*/
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
// std::cout << correct_estimation_counter << "correct estimation counter\n";
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter / num_of_realizations * 100)) << "% \r" << std::flush;
|
||||
// std::cout << message << "message'\n'";
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5aPcpsAcquisitionGSoC2014GensourceTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5aPcpsAcquisitionGSoC2014GensourceTest, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GalileoE5aNoncoherentIQAcquisitionCaf>(config.get(), "Acquisition_5X", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5aPcpsAcquisitionGSoC2014GensourceTest, ConnectAndRun)
|
||||
{
|
||||
config_1();
|
||||
// int nsamples = floor(5*fs_in*integration_time_ms*1e-3);
|
||||
int nsamples = 21000 * 3;
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
acquisition = std::make_shared<GalileoE5aNoncoherentIQAcquisitionCaf>(config.get(), "Acquisition_5X", 1, 0);
|
||||
auto msg_rx = GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_make(channel_internal_queue);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5aPcpsAcquisitionGSoC2014GensourceTest, ValidationOfSIM)
|
||||
{
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
acquisition = std::make_shared<GalileoE5aNoncoherentIQAcquisitionCaf>(config.get(), "Acquisition_5X", 1, 0);
|
||||
auto msg_rx = GalileoE5aPcpsAcquisitionGSoC2014GensourceTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(0);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_5X.doppler_max", 5000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_5X.doppler_step", 100));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_5X.threshold", 0.0001));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
// USING THE SIGNAL GENERATOR
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
filter->connect(top_block);
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->init();
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 1; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
gnss_synchro.PRN = 11; // present
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
gnss_synchro.PRN = 19; // not present
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
ch_thread.join();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
// std::cout << gnss_synchro.Acq_delay_samples << "acq delay'\n'";
|
||||
// std::cout << gnss_synchro.Acq_doppler_hz << "acq doppler'\n'";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*!
|
||||
* \file galileo_e5b_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE5bPcpsAcquisition class based on some input parameters.
|
||||
* \author Piyush Gupta, 2020. piyush04111999@gmail.com
|
||||
* \note Code added as GSoC 2020 Program.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e5b_pcps_acquisition.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE5bPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GalileoE5bPcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GalileoE5bPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GalileoE5bPcpsAcquisitionTest_msg_rx_sptr GalileoE5bPcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
class GalileoE5bPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE5bPcpsAcquisitionTest_msg_rx_sptr GalileoE5bPcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE5bPcpsAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE5bPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE5bPcpsAcquisitionTest_msg_rx_sptr GalileoE5bPcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE5bPcpsAcquisitionTest_msg_rx_sptr(new GalileoE5bPcpsAcquisitionTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
std::cout << "msg_handler_telemetry Bad any cast!" << std::endl;
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE5bPcpsAcquisitionTest_msg_rx::GalileoE5bPcpsAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE5bPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](pmt::pmt_t&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE5bPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE5bPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE5bPcpsAcquisitionTest_msg_rx::~GalileoE5bPcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE5bPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE5bPcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
stop = false;
|
||||
message = 0;
|
||||
}
|
||||
|
||||
~GalileoE5bPcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gnss_shared_ptr<GalileoE5bPcpsAcquisition> acquisition;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
std::thread ch_thread;
|
||||
|
||||
Gnss_Synchro gnss_synchro;
|
||||
|
||||
size_t item_size;
|
||||
|
||||
double fs_in = 32e6;
|
||||
double expected_doppler_hz = -632.0;
|
||||
double expected_delay_chips = 1234;
|
||||
double expected_delay_sec = 51;
|
||||
|
||||
double mse_doppler = 0.0;
|
||||
double mse_delay = 0.0;
|
||||
double Pd = 0.0;
|
||||
double Pfa_p = 0.0;
|
||||
double Pfa_a = 0.0;
|
||||
|
||||
float integration_time_ms = 1;
|
||||
float max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
float max_delay_error_chips = 0.5;
|
||||
|
||||
unsigned int num_of_realizations = 1;
|
||||
unsigned int realization_counter = 0;
|
||||
unsigned int detection_counter = 0;
|
||||
unsigned int correct_estimation_counter = 0;
|
||||
unsigned int acquired_samples = 0;
|
||||
unsigned int mean_acq_time_us = 0;
|
||||
|
||||
bool stop;
|
||||
int message;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "7X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.signal_0", "7X");
|
||||
config->set_property("SignalSource.PRN_0", "11");
|
||||
config->set_property("SignalSource.CN0_dB_0", "50");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
config->set_property("SignalSource.delay_sec_0", std::to_string(expected_delay_sec));
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
config->set_property("SignalSource.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/signal_source.dat");
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
config->set_property("Acquisition_7X.implementation", "Galileo_E5b_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_7X.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_7X.coherent_integration_time_ms", std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_7X.dump", "true");
|
||||
config->set_property("Acquisition_7X.dump_filename", "./acquisition");
|
||||
config->set_property("Acquisition_7X.threshold", "0.001");
|
||||
config->set_property("Acquisition_7X.doppler_max", "10000");
|
||||
config->set_property("Acquisition_7X.doppler_step", "250");
|
||||
config->set_property("Acquisition_7X.repeat_satellite", "false");
|
||||
}
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE5bPcpsAcquisitionTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 10230.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
/*
|
||||
double delay_error_chips = abs((double)expected_delay_chips - (double)(gnss_synchro.Acq_delay_samples-5)*10230.0/((double)fs_in*1e-3));
|
||||
double doppler_error_hz = abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
*/
|
||||
|
||||
detection_counter++;
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
// std::cout << correct_estimation_counter << "correct estimation counter\n";
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter / num_of_realizations * 100)) << "% \r" << std::flush;
|
||||
// std::cout << message << "message'\n'";
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE5bPcpsAcquisitionTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5bPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
acquisition = gnss_make_shared<GalileoE5bPcpsAcquisition>(config.get(), "Acquisition_7X", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5bPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int nsamples = 21000 * 3;
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
|
||||
acquisition = gnss_make_shared<GalileoE5bPcpsAcquisition>(config.get(), "Acquisition_7X", 1, 0);
|
||||
|
||||
auto msg_rx = GalileoE5bPcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5bPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
|
||||
acquisition = gnss_make_shared<GalileoE5bPcpsAcquisition>(config.get(), "Acquisition_7X", 1, 0);
|
||||
|
||||
std::shared_ptr<FirFilter> input_filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto msg_rx = GalileoE5bPcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(0);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.0001);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(5000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(100);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
filter->connect(top_block);
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->init();
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 1; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
gnss_synchro.PRN = 11; // present
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
gnss_synchro.PRN = 19; // not present
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
ch_thread.join();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
// std::cout << gnss_synchro.Acq_delay_samples << "acq delay'\n'";
|
||||
// std::cout << gnss_synchro.Acq_doppler_hz << "acq doppler'\n'";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
/*!
|
||||
* \file galileo_e6_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GalileoE6PcpsAcquisition class based on some input parameters.
|
||||
* \author Carles Fernandez-Prades, 2020 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 "Galileo_E6.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "fir_filter.h"
|
||||
#include "galileo_e6_pcps_acquisition.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GalileoE6PcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GalileoE6PcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GalileoE6PcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GalileoE6PcpsAcquisitionTest_msg_rx_sptr GalileoE6PcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
class GalileoE6PcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GalileoE6PcpsAcquisitionTest_msg_rx_sptr GalileoE6PcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GalileoE6PcpsAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GalileoE6PcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GalileoE6PcpsAcquisitionTest_msg_rx_sptr GalileoE6PcpsAcquisitionTest_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GalileoE6PcpsAcquisitionTest_msg_rx_sptr(new GalileoE6PcpsAcquisitionTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
std::cout << "msg_handler_telemetry Bad any cast!" << std::endl;
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GalileoE6PcpsAcquisitionTest_msg_rx::GalileoE6PcpsAcquisitionTest_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GalileoE6PcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](pmt::pmt_t&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GalileoE6PcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GalileoE6PcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GalileoE6PcpsAcquisitionTest_msg_rx::~GalileoE6PcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GalileoE6PcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE6PcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
stop = false;
|
||||
message = 0;
|
||||
}
|
||||
|
||||
~GalileoE6PcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gnss_shared_ptr<GalileoE6PcpsAcquisition> acquisition;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
std::thread ch_thread;
|
||||
|
||||
Gnss_Synchro gnss_synchro;
|
||||
|
||||
size_t item_size;
|
||||
|
||||
double fs_in = 32e6;
|
||||
double expected_doppler_hz = -632.0;
|
||||
double expected_delay_chips = 1234;
|
||||
double expected_delay_sec = 51;
|
||||
|
||||
double mse_doppler = 0.0;
|
||||
double mse_delay = 0.0;
|
||||
double Pd = 0.0;
|
||||
double Pfa_p = 0.0;
|
||||
double Pfa_a = 0.0;
|
||||
|
||||
float integration_time_ms = 1;
|
||||
float max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
float max_delay_error_chips = 0.5;
|
||||
|
||||
unsigned int num_of_realizations = 1;
|
||||
unsigned int realization_counter = 0;
|
||||
unsigned int detection_counter = 0;
|
||||
unsigned int correct_estimation_counter = 0;
|
||||
unsigned int acquired_samples = 0;
|
||||
unsigned int mean_acq_time_us = 0;
|
||||
|
||||
bool stop;
|
||||
int message;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "E6";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
config->set_property("SignalSource.system_0", "E");
|
||||
config->set_property("SignalSource.signal_0", "E6");
|
||||
config->set_property("SignalSource.PRN_0", "11");
|
||||
config->set_property("SignalSource.CN0_dB_0", "50");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
config->set_property("SignalSource.delay_sec_0", std::to_string(expected_delay_sec));
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
config->set_property("SignalSource.dump", "false");
|
||||
config->set_property("SignalSource.dump_filename", "../data/signal_source.dat");
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
config->set_property("Acquisition_E6.implementation", "Galileo_E6_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_E6.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_E6.coherent_integration_time_ms", std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_E6.dump", "true");
|
||||
config->set_property("Acquisition_E6.dump_filename", "./acquisition");
|
||||
config->set_property("Acquisition_E6.pfa", "0.01");
|
||||
config->set_property("Acquisition_E6.doppler_max", "10000");
|
||||
config->set_property("Acquisition_E6.doppler_step", "250");
|
||||
config->set_property("Acquisition_E6.repeat_satellite", "false");
|
||||
}
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GalileoE6PcpsAcquisitionTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * GALILEO_E6_B_CODE_LENGTH_CHIPS / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
/*
|
||||
double delay_error_chips = abs((double)expected_delay_chips - (double)(gnss_synchro.Acq_delay_samples-5)*10230.0/((double)fs_in*1e-3));
|
||||
double doppler_error_hz = abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
*/
|
||||
detection_counter++;
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
// std::cout << correct_estimation_counter << "correct estimation counter\n";
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter / num_of_realizations * 100)) << "% \r" << std::flush;
|
||||
// std::cout << message << "message'\n'";
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GalileoE6PcpsAcquisitionTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE6PcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
acquisition = gnss_make_shared<GalileoE6PcpsAcquisition>(config.get(), "Acquisition_E6", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE6PcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int nsamples = 21000 * 3;
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
|
||||
acquisition = gnss_make_shared<GalileoE6PcpsAcquisition>(config.get(), "Acquisition_E6", 1, 0);
|
||||
|
||||
auto msg_rx = GalileoE6PcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE6PcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
init();
|
||||
|
||||
acquisition = gnss_make_shared<GalileoE6PcpsAcquisition>(config.get(), "Acquisition_E6", 1, 0);
|
||||
|
||||
std::shared_ptr<FirFilter> input_filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto msg_rx = GalileoE6PcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(0);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(5000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(100);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
filter->connect(top_block);
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->init();
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 1; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
gnss_synchro.PRN = 11; // present
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
gnss_synchro.PRN = 19; // not present
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
ch_thread.join();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
// std::cout << gnss_synchro.Acq_delay_samples << "acq delay'\n'";
|
||||
// std::cout << gnss_synchro.Acq_doppler_hz << "acq doppler'\n'";
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,642 @@
|
||||
/*!
|
||||
* \file glonass_l1_ca_pcps_acquisition_gsoc2017_test.cc
|
||||
* \brief Tests a PCPS acquisition block for Glonass L1 C/A signals
|
||||
* \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com
|
||||
* \author Luis Esteve, 2017. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "freq_xlating_fir_filter.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "glonass_l1_ca_pcps_acquisition.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx;
|
||||
|
||||
using GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_sptr = gnss_shared_ptr<GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx>;
|
||||
|
||||
GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_sptr GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_sptr GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx(); // Default destructor
|
||||
};
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_sptr GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_sptr(new GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx::GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx::~GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GlonassL1CaPcpsAcquisitionGSoC2017Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GlonassL1CaPcpsAcquisitionGSoC2017Test()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
acquisition = nullptr;
|
||||
init();
|
||||
}
|
||||
|
||||
~GlonassL1CaPcpsAcquisitionGSoC2017Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GlonassL1CaPcpsAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "1G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 31.75e6;
|
||||
|
||||
expected_delay_chips = 255;
|
||||
expected_doppler_hz = -1500;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "R");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Freq_Xlating_Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
config->set_property("InputFilter.sampling_frequency", std::to_string(fs_in));
|
||||
config->set_property("InputFilter.IF", "4000000");
|
||||
|
||||
config->set_property("Acquisition.item_type", "gr_complex");
|
||||
config->set_property("Acquisition.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition.max_dwells", "1");
|
||||
config->set_property("Acquisition.implementation", "GLONASS_L1_CA_PCPS_Acquisition");
|
||||
// config->set_property("Acquisition.threshold", "2.5");
|
||||
config->set_property("Acquisition.pfa", "0.001");
|
||||
config->set_property("Acquisition.doppler_max", "10000");
|
||||
config->set_property("Acquisition.doppler_step", "250");
|
||||
config->set_property("Acquisition.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "1G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 31.75e6;
|
||||
|
||||
expected_delay_chips = 374;
|
||||
expected_doppler_hz = -2000;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "R");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "R");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "R");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "R");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Freq_Xlating_Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
config->set_property("InputFilter.sampling_frequency", std::to_string(fs_in));
|
||||
config->set_property("InputFilter.IF", "4000000");
|
||||
|
||||
config->set_property("Acquisition.item_type", "gr_complex");
|
||||
config->set_property("Acquisition.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition.max_dwells", "1");
|
||||
config->set_property("Acquisition.implementation", "GLONASS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition.pfa", "0.001");
|
||||
config->set_property("Acquisition.doppler_max", "10000");
|
||||
config->set_property("Acquisition.doppler_step", "250");
|
||||
config->set_property("Acquisition.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GlonassL1CaPcpsAcquisitionGSoC2017Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::wait_message()
|
||||
{
|
||||
struct timeval tv;
|
||||
int64_t begin = 0;
|
||||
int64_t end = 0;
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
begin = tv.tv_sec * 1e6 + tv.tv_usec;
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
end = tv.tv_sec * 1e6 + tv.tv_usec;
|
||||
|
||||
mean_acq_time_us += (end - begin);
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
// The value 511.0 must be a variable, chips/length
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (static_cast<double>(gnss_synchro.Acq_delay_samples) - 5.0) * 511.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = (static_cast<double>(detection_counter) - static_cast<double>(correct_estimation_counter)) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionGSoC2017Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionGSoC2017Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionGSoC2017Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
auto msg_rx = GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionGSoC2017Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
acquisition = acquisition = std::make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
auto msg_rx = GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(500);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<SignalGenerator> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<FreqXlatingFirFilter> filter = std::make_shared<FreqXlatingFirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
signal_generator->connect(top_block);
|
||||
top_block->connect(signal_generator->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionGSoC2017Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
acquisition = std::make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
auto msg_rx = GlonassL1CaPcpsAcquisitionGSoC2017Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<SignalGenerator> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<FreqXlatingFirFilter> filter = std::make_shared<FreqXlatingFirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
signal_generator->connect(top_block);
|
||||
top_block->connect(signal_generator->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
std::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 1; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block."
|
||||
<< '\n';
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop"
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*!
|
||||
* \file glonass_l1_ca_pcps_acquisition_test.cc
|
||||
* \brief Tests a PCPS acquisition block for Glonass L1 C/A signals
|
||||
* \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com
|
||||
* \author Luis Esteve, 2017. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "freq_xlating_fir_filter.h"
|
||||
#include "glonass_l1_ca_pcps_acquisition.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GlonassL1CaPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GlonassL1CaPcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GlonassL1CaPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GlonassL1CaPcpsAcquisitionTest_msg_rx_sptr GlonassL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
class GlonassL1CaPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GlonassL1CaPcpsAcquisitionTest_msg_rx_sptr GlonassL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GlonassL1CaPcpsAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GlonassL1CaPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionTest_msg_rx_sptr GlonassL1CaPcpsAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return GlonassL1CaPcpsAcquisitionTest_msg_rx_sptr(new GlonassL1CaPcpsAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
std::cout << "msg_handler_telemetry Bad any cast!\n";
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionTest_msg_rx::GlonassL1CaPcpsAcquisitionTest_msg_rx() : gr::block("GlonassL1CaPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GlonassL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GlonassL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GlonassL1CaPcpsAcquisitionTest_msg_rx::~GlonassL1CaPcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GlonassL1CaPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GlonassL1CaPcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GlonassL1CaPcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
void GlonassL1CaPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "1G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "62314000");
|
||||
config->set_property("InputFilter.IF", "9540000");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
config->set_property("InputFilter.sampling_frequency", "62314000");
|
||||
config->set_property("Acquisition_1G.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1G.coherent_integration_time_ms", "1");
|
||||
config->set_property("Acquisition_1G.dump", "true");
|
||||
config->set_property("Acquisition_1G.dump_filename", "./acquisition");
|
||||
config->set_property("Acquisition_1G.implementation", "Glonass_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1G.threshold", "0.001");
|
||||
config->set_property("Acquisition_1G.doppler_max", "5000");
|
||||
config->set_property("Acquisition_1G.doppler_step", "500");
|
||||
config->set_property("Acquisition_1G.repeat_satellite", "false");
|
||||
// config->set_property("Acquisition_1G.pfa", "0.0");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
auto acquisition = gnss_make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition_1G", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 62314000;
|
||||
int nsamples = 62314;
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
init();
|
||||
auto acquisition = gnss_make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition_1G", 1, 0);
|
||||
|
||||
auto msg_rx = GlonassL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
double expected_delay_samples = 31874;
|
||||
double expected_doppler_hz = -9500;
|
||||
init();
|
||||
std::shared_ptr<GlonassL1CaPcpsAcquisition> acquisition = std::make_shared<GlonassL1CaPcpsAcquisition>(config.get(), "Acquisition_1G", 1, 0);
|
||||
std::shared_ptr<FreqXlatingFirFilter> input_filter = std::make_shared<FreqXlatingFirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto msg_rx = GlonassL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.005);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(500);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/Glonass_L1_CA_SIM_Fs_62Msps_4ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
top_block->connect(file_source, 0, input_filter->get_left_block(), 0);
|
||||
top_block->connect(input_filter->get_right_block(), 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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
float delay_error_chips = static_cast<float>(delay_error_samples) * 511.0 / 62316.0;
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
/*!
|
||||
* \file glonass_l2_ca_pcps_acquisition_test.cc
|
||||
* \brief Tests a PCPS acquisition block for Glonass L2 C/A signals
|
||||
* \author Damian Miralles, 2018, dmiralles2009(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "fir_filter.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "glonass_l2_ca_pcps_acquisition.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GlonassL2CaPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GlonassL2CaPcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GlonassL2CaPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GlonassL2CaPcpsAcquisitionTest_msg_rx_sptr GlonassL2CaPcpsAcquisitionTest_msg_rx_make(concurrent_queue<int>& queue);
|
||||
|
||||
|
||||
class GlonassL2CaPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GlonassL2CaPcpsAcquisitionTest_msg_rx_sptr GlonassL2CaPcpsAcquisitionTest_msg_rx_make(concurrent_queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GlonassL2CaPcpsAcquisitionTest_msg_rx(concurrent_queue<int>& queue);
|
||||
concurrent_queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GlonassL2CaPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GlonassL2CaPcpsAcquisitionTest_msg_rx_sptr GlonassL2CaPcpsAcquisitionTest_msg_rx_make(concurrent_queue<int>& queue)
|
||||
{
|
||||
return GlonassL2CaPcpsAcquisitionTest_msg_rx_sptr(new GlonassL2CaPcpsAcquisitionTest_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(msg);
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GlonassL2CaPcpsAcquisitionTest_msg_rx::GlonassL2CaPcpsAcquisitionTest_msg_rx(concurrent_queue<int>& queue) : gr::block("GlonassL2CaPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GlonassL2CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GlonassL2CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GlonassL2CaPcpsAcquisitionTest_msg_rx::~GlonassL2CaPcpsAcquisitionTest_msg_rx()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GlonassL2CaPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GlonassL2CaPcpsAcquisitionTest()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
acquisition = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
~GlonassL2CaPcpsAcquisitionTest()
|
||||
{
|
||||
}
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t> > queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GlonassL2CaPcpsAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "2G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 31.75e6;
|
||||
|
||||
expected_delay_chips = 255;
|
||||
expected_doppler_hz = -1500;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "R");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_2G.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_2G.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_2G.max_dwells", "1");
|
||||
config->set_property("Acquisition_2G.implementation", "GLONASS_L2_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_2G.threshold", "0.8");
|
||||
config->set_property("Acquisition_2G.doppler_max", "10000");
|
||||
config->set_property("Acquisition_2G.doppler_step", "250");
|
||||
config->set_property("Acquisition_2G.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_2G.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "2G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 31.75e6;
|
||||
|
||||
expected_delay_chips = 374;
|
||||
expected_doppler_hz = -2000;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "R");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "R");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "R");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "R");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_2G.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_2G.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_2G.max_dwells", "1");
|
||||
config->set_property("Acquisition_2G.implementation", "GLONASS_L2_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_2G.pfa", "0.01");
|
||||
config->set_property("Acquisition_2G.doppler_max", "10000");
|
||||
config->set_property("Acquisition_2G.doppler_step", "250");
|
||||
config->set_property("Acquisition_2G.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_2G.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GlonassL2CaPcpsAcquisitionTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::wait_message()
|
||||
{
|
||||
struct timeval tv;
|
||||
int64_t begin = 0;
|
||||
int64_t end = 0;
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
begin = tv.tv_sec * 1e6 + tv.tv_usec;
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
end = tv.tv_sec * 1e6 + tv.tv_usec;
|
||||
|
||||
mean_acq_time_us += (end - begin);
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
// The value 511.0 must be a variable, chips/length
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (static_cast<double>(gnss_synchro.Acq_delay_samples) - 5.0) * 511.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = (static_cast<double>(detection_counter) - static_cast<double>(correct_estimation_counter)) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GlonassL2CaPcpsAcquisitionTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL2CaPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GlonassL2CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL2CaPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> begin, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t> >();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GlonassL2CaPcpsAcquisition>(config.get(), "Acquisition_2G", 1, 0);
|
||||
auto msg_rx = GlonassL2CaPcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
begin = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - begin;
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
std::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL2CaPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t> >();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
acquisition = std::make_shared<GlonassL2CaPcpsAcquisition>(config.get(), "Acquisition_2G", 1, 0);
|
||||
auto msg_rx = GlonassL2CaPcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(500);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.0005);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL2CaPcpsAcquisitionTest, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t> >();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
acquisition = std::make_shared<GlonassL2CaPcpsAcquisition>(config.get(), "Acquisition_2G", 1, 0);
|
||||
auto msg_rx = GlonassL2CaPcpsAcquisitionTest_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_2G.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_2G.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_2G.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
std::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 1; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "fir_filter.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_pcps_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_sptr(new GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx::GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx::~GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CaPcpsAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL1CaPcpsAcquisitionGSoC2013Test()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
acquisition = nullptr;
|
||||
init();
|
||||
}
|
||||
|
||||
~GpsL1CaPcpsAcquisitionGSoC2013Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GpsL1CaPcpsAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
// config->set_property("Acquisition_1C.threshold", "2.5");
|
||||
config->set_property("Acquisition_1C.pfa", "0.001");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "G");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "G");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "G");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
config->set_property("Acquisition_1C.pfa", "0.001");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GpsL1CaPcpsAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
acquisition = std::make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(500);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
acquisition = std::make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1C.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1C.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 0, acquisition->get_left_block(), 0);
|
||||
}) << "Failure connecting the blocks of acquisition test.";
|
||||
|
||||
std::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
ch_thread.join();
|
||||
}) << "Failure while waiting the queue to stop";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsAcquisition class based on some input parameters.
|
||||
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "GPS_L1_CA.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "gps_l1_ca_pcps_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CaPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GpsL1CaPcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GpsL1CaPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GpsL1CaPcpsAcquisitionTest_msg_rx_sptr GpsL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
class GpsL1CaPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CaPcpsAcquisitionTest_msg_rx_sptr GpsL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t &msg);
|
||||
GpsL1CaPcpsAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message{0};
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsAcquisitionTest_msg_rx_sptr GpsL1CaPcpsAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return GpsL1CaPcpsAcquisitionTest_msg_rx_sptr(new GpsL1CaPcpsAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t &msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(msg);
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast &e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsAcquisitionTest_msg_rx::GpsL1CaPcpsAcquisitionTest_msg_rx()
|
||||
: gr::block("GpsL1CaPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto &&PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CaPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CaPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL1CaPcpsAcquisitionTest()
|
||||
: item_size(sizeof(gr_complex))
|
||||
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
void init();
|
||||
void plot_grid() const;
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
unsigned int doppler_max{5000};
|
||||
unsigned int doppler_step{100};
|
||||
};
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 1;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms", "1");
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
config->set_property("Acquisition_1C.dump", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
config->set_property("Acquisition_1C.dump_filename", "./tmp-acq-gps1/acquisition");
|
||||
config->set_property("Acquisition_1C.dump_channel", "1");
|
||||
config->set_property("Acquisition_1C.threshold", "0.00001");
|
||||
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.repeat_satellite", "false");
|
||||
// config->set_property("Acquisition_1C.pfa", "0.0");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionTest::plot_grid() const
|
||||
{
|
||||
// load the measured values
|
||||
std::string basename = "./tmp-acq-gps1/acquisition_G_1C";
|
||||
auto sat = static_cast<unsigned int>(gnss_synchro.PRN);
|
||||
|
||||
auto samples_per_code = static_cast<unsigned int>(round(4000000 / (GPS_L1_CA_CODE_RATE_CPS / GPS_L1_CA_CODE_LENGTH_CHIPS))); // !!
|
||||
Acquisition_Dump_Reader acq_dump(basename, sat, doppler_max, doppler_step, samples_per_code, 1);
|
||||
|
||||
if (!acq_dump.read_binary_acq())
|
||||
{
|
||||
std::cout << "Error reading files\n";
|
||||
}
|
||||
|
||||
std::vector<int> *doppler = &acq_dump.doppler;
|
||||
std::vector<unsigned int> *samples = &acq_dump.samples;
|
||||
std::vector<std::vector<float> > *mag = &acq_dump.mag;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||
#else
|
||||
const std::string gnuplot_executable(absl::GetFlag(FLAGS_gnuplot_executable));
|
||||
#endif
|
||||
|
||||
if (gnuplot_executable.empty())
|
||||
{
|
||||
std::cout << "WARNING: Although the flag plot_acq_grid has been set to TRUE,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Plotting the acquisition grid. This can take a while...\n";
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string &gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("lines");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
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");
|
||||
}
|
||||
catch (const GnuplotException &ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
std::string data_str = "./tmp-acq-gps1";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionTest /*unused*/, Instantiate /*unused*/)
|
||||
{
|
||||
std::shared_ptr<GpsL1CaPcpsAcquisition> acquisition = std::make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionTest /*unused*/, ConnectAndRun /*unused*/)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
int nsamples = 4000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t> > queue = std::make_shared<Concurrent_Queue<pmt::pmt_t> >();
|
||||
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
init();
|
||||
gnss_shared_ptr<GpsL1CaPcpsAcquisition> acquisition = gnss_make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
gnss_shared_ptr<GpsL1CaPcpsAcquisitionTest_msg_rx> msg_rx = GpsL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionTest /*unused*/, ValidationOfResults /*unused*/)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
|
||||
double expected_delay_samples = 524;
|
||||
double expected_doppler_hz = 1680;
|
||||
|
||||
init();
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
std::string data_str = "./tmp-acq-gps1";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
fs::create_directory(data_str);
|
||||
}
|
||||
|
||||
auto acquisition = gnss_make_shared<GpsL1CaPcpsAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.001);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
const char *file_name = file.c_str();
|
||||
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.";
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
acquisition->init();
|
||||
|
||||
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.";
|
||||
|
||||
uint64_t nsamples = gnss_synchro.Acq_samplestamp_samples;
|
||||
std::cout << "Acquired " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
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 USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
plot_grid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_acquisition_test_fpga.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsAcquisitionFpga class based on some input parameters.
|
||||
* \authors <ul>
|
||||
* <li> Marc Majoral, 2019. mmajoral(at)cttc.cat
|
||||
* <li> Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
* </ul>
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "GPS_L1_CA.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "fpga_switch.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_pcps_acquisition_fpga.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <chrono>
|
||||
#include <cmath> // for abs, pow, floor
|
||||
#include <fcntl.h> // for O_WRONLY
|
||||
#include <pthread.h>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct DMA_handler_args_gps_l1_acq_test
|
||||
{
|
||||
std::string file;
|
||||
int32_t nsamples_tx;
|
||||
int32_t skip_used_samples;
|
||||
float scaling_factor;
|
||||
};
|
||||
|
||||
struct acquisition_handler_args_gps_l1_acq_test
|
||||
{
|
||||
std::shared_ptr<AcquisitionInterface> acquisition;
|
||||
};
|
||||
|
||||
class GpsL1CaPcpsAcquisitionTestFpga : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
bool acquire_signal();
|
||||
std::string implementation = "GPS_L1_CA_DLL_PLL_Tracking_FPGA";
|
||||
std::vector<Gnss_Synchro> gnss_synchro_vec;
|
||||
|
||||
const int32_t TEST_ACQ_SKIP_SAMPLES = 1024;
|
||||
const int BASEBAND_SAMPLING_FREQ = 4000000;
|
||||
const float MAX_SAMPLE_VALUE = 0.096257761120796;
|
||||
const int DMA_BITS_PER_SAMPLE = 8;
|
||||
const float DMA_SIGNAL_SCALING_FACTOR = (pow(2, DMA_BITS_PER_SAMPLE - 1) - 1) / MAX_SAMPLE_VALUE;
|
||||
|
||||
protected:
|
||||
GpsL1CaPcpsAcquisitionTestFpga();
|
||||
~GpsL1CaPcpsAcquisitionTestFpga() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
unsigned int nsamples_to_transfer;
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsAcquisitionTestFpga::GpsL1CaPcpsAcquisitionTestFpga()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
doppler_max = 5000;
|
||||
doppler_step = 100;
|
||||
nsamples_to_transfer = 0;
|
||||
}
|
||||
|
||||
|
||||
void* handler_DMA_gps_l1_acq_test(void* arguments)
|
||||
{
|
||||
const int MAX_INPUT_SAMPLES_TOTAL = 16384;
|
||||
|
||||
auto* args = (struct DMA_handler_args_gps_l1_acq_test*)arguments;
|
||||
|
||||
std::string Filename = args->file; // input filename
|
||||
int32_t skip_used_samples = args->skip_used_samples;
|
||||
int32_t nsamples_tx = args->nsamples_tx;
|
||||
|
||||
std::vector<float> input_samples(MAX_INPUT_SAMPLES_TOTAL * 2);
|
||||
std::vector<int8_t> input_samples_dma(MAX_INPUT_SAMPLES_TOTAL * 2 * 2);
|
||||
bool file_completed = false;
|
||||
int32_t nsamples_remaining;
|
||||
int32_t nsamples_block_size;
|
||||
unsigned int dma_index;
|
||||
|
||||
int tx_fd; // DMA descriptor
|
||||
std::ifstream infile;
|
||||
|
||||
infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
|
||||
try
|
||||
{
|
||||
infile.open(Filename, std::ios::binary);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception opening file " << Filename << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// Open DMA device
|
||||
// *************************************************************************
|
||||
tx_fd = open("/dev/loop_tx", O_WRONLY);
|
||||
if (tx_fd < 0)
|
||||
{
|
||||
std::cout << "Cannot open loop device\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// Open input file
|
||||
// *************************************************************************
|
||||
uint32_t skip_samples = 0; // static_cast<uint32_t>(FLAGS_skip_samples);
|
||||
|
||||
if (skip_samples + skip_used_samples > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
infile.ignore((skip_samples + skip_used_samples) * 2);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception reading file " << Filename << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
nsamples_remaining = nsamples_tx;
|
||||
nsamples_block_size = 0;
|
||||
|
||||
while (file_completed == false)
|
||||
{
|
||||
dma_index = 0;
|
||||
|
||||
if (nsamples_remaining > MAX_INPUT_SAMPLES_TOTAL)
|
||||
{
|
||||
nsamples_block_size = MAX_INPUT_SAMPLES_TOTAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
nsamples_block_size = nsamples_remaining;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 2 bytes per complex sample
|
||||
infile.read(reinterpret_cast<char*>(input_samples.data()), nsamples_block_size * 2 * sizeof(float));
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception reading file " << Filename << '\n';
|
||||
}
|
||||
|
||||
for (int index0 = 0; index0 < (nsamples_block_size * 2); index0 += 2)
|
||||
{
|
||||
// channel 1 (queue 1) -> E5/L5
|
||||
input_samples_dma[dma_index] = static_cast<int8_t>(input_samples[index0] * args->scaling_factor);
|
||||
input_samples_dma[dma_index + 1] = static_cast<int8_t>(input_samples[index0 + 1] * args->scaling_factor);
|
||||
// channel 0 (queue 0) -> E1/L1
|
||||
input_samples_dma[dma_index + 2] = 0;
|
||||
input_samples_dma[dma_index + 3] = 0;
|
||||
|
||||
dma_index += 4;
|
||||
}
|
||||
|
||||
if (write(tx_fd, input_samples_dma.data(), nsamples_block_size * 2 * 2) != nsamples_block_size * 2 * 2)
|
||||
{
|
||||
std::cerr << "Error: DMA could not send all the required samples \n";
|
||||
}
|
||||
|
||||
// Throttle the DMA
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
nsamples_remaining -= nsamples_block_size;
|
||||
|
||||
if (nsamples_remaining == 0)
|
||||
{
|
||||
file_completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
infile.close();
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception closing files " << Filename << '\n';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
close(tx_fd);
|
||||
}
|
||||
catch (const std::ifstream::failure& e)
|
||||
{
|
||||
std::cerr << "Exception closing loop device \n";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void* handler_acquisition_gps_l1_acq_test(void* arguments)
|
||||
{
|
||||
// the acquisition is a blocking function so we have to
|
||||
// create a thread
|
||||
auto* args = (struct acquisition_handler_args_gps_l1_acq_test*)arguments;
|
||||
args->acquisition->reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// When using the FPGA the acquisition class calls the states
|
||||
// of the channel finite state machine directly. This is done
|
||||
// in order to reduce the latency of the receiver when going
|
||||
// from acquisition to tracking. In order to execute the
|
||||
// acquisition in the unit tests we need to create a derived
|
||||
// class of the channel finite state machine. Some of the states
|
||||
// of the channel state machine are modified here, in order to
|
||||
// simplify the instantiation of the acquisition class in the
|
||||
// unit test.
|
||||
class ChannelFsm_gps_l1_acq_test : public ChannelFsm
|
||||
{
|
||||
public:
|
||||
bool Event_valid_acquisition() override
|
||||
{
|
||||
acquisition_successful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_failed_acquisition_repeat() override
|
||||
{
|
||||
acquisition_successful = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_failed_acquisition_no_repeat() override
|
||||
{
|
||||
acquisition_successful = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event_check_test_result()
|
||||
{
|
||||
return acquisition_successful;
|
||||
}
|
||||
|
||||
void Event_clear_test_result()
|
||||
{
|
||||
acquisition_successful = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool acquisition_successful{};
|
||||
};
|
||||
|
||||
|
||||
bool GpsL1CaPcpsAcquisitionTestFpga::acquire_signal()
|
||||
{
|
||||
pthread_t thread_DMA, thread_acquisition;
|
||||
|
||||
// 1. Setup GNU Radio flowgraph (file_source -> Acquisition_10m)
|
||||
int SV_ID = 1; // initial sv id
|
||||
|
||||
// fsm
|
||||
std::shared_ptr<ChannelFsm_gps_l1_acq_test> channel_fsm_;
|
||||
channel_fsm_ = std::make_shared<ChannelFsm_gps_l1_acq_test>();
|
||||
bool acquisition_successful;
|
||||
|
||||
// Satellite signal definition
|
||||
Gnss_Synchro tmp_gnss_synchro;
|
||||
tmp_gnss_synchro.Channel_ID = 0;
|
||||
|
||||
std::shared_ptr<AcquisitionInterface> acquisition;
|
||||
|
||||
std::string signal;
|
||||
struct DMA_handler_args_gps_l1_acq_test args;
|
||||
struct acquisition_handler_args_gps_l1_acq_test args_acq;
|
||||
|
||||
// set the scaling factor
|
||||
args.scaling_factor = DMA_SIGNAL_SCALING_FACTOR;
|
||||
|
||||
std::string file = "data/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
args.file = std::move(file); // DMA file configuration
|
||||
|
||||
// instantiate the FPGA switch and set the
|
||||
// switch position to DMA.
|
||||
std::shared_ptr<Fpga_Switch> switch_fpga;
|
||||
switch_fpga = std::make_shared<Fpga_Switch>();
|
||||
switch_fpga->set_switch_position(0); // set switch position to DMA
|
||||
|
||||
// create the correspondign acquisition block according to the desired tracking signal
|
||||
tmp_gnss_synchro.System = 'G';
|
||||
signal = "1C";
|
||||
const char* str = signal.c_str(); // get a C style null terminated string
|
||||
std::memcpy(static_cast<void*>(tmp_gnss_synchro.Signal), str, 3); // copy string into synchro char array: 2 char + null
|
||||
tmp_gnss_synchro.PRN = SV_ID;
|
||||
acquisition = std::make_shared<GpsL1CaPcpsAcquisitionFpga>(config.get(), "Acquisition", 0, 0);
|
||||
|
||||
acquisition->set_gnss_synchro(&tmp_gnss_synchro);
|
||||
acquisition->set_channel_fsm(channel_fsm_);
|
||||
acquisition->set_channel(1);
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
acquisition->set_doppler_center(0);
|
||||
acquisition->set_threshold(0.001);
|
||||
|
||||
nsamples_to_transfer = static_cast<unsigned int>(std::round(static_cast<double>(BASEBAND_SAMPLING_FREQ) / (GPS_L1_CA_CODE_RATE_CPS / GPS_L1_CA_CODE_LENGTH_CHIPS)));
|
||||
|
||||
channel_fsm_->Event_clear_test_result();
|
||||
|
||||
acquisition->stop_acquisition(); // reset the whole system including the sample counters
|
||||
acquisition->init();
|
||||
acquisition->set_local_code();
|
||||
|
||||
args.skip_used_samples = 0;
|
||||
|
||||
// Configure the DMA to send the required samples to perform an acquisition
|
||||
args.nsamples_tx = nsamples_to_transfer;
|
||||
|
||||
// run the acquisition. The acquisition must run in a separate thread because it is a blocking function
|
||||
args_acq.acquisition = std::move(acquisition);
|
||||
|
||||
if (pthread_create(&thread_acquisition, nullptr, handler_acquisition_gps_l1_acq_test, reinterpret_cast<void*>(&args_acq)) < 0)
|
||||
{
|
||||
std::cout << "ERROR cannot create acquisition Process\n";
|
||||
}
|
||||
|
||||
// wait to give time for the acquisition thread to set up the acquisition HW accelerator in the FPGA
|
||||
usleep(1000000);
|
||||
|
||||
// create DMA child process
|
||||
if (pthread_create(&thread_DMA, nullptr, handler_DMA_gps_l1_acq_test, reinterpret_cast<void*>(&args)) < 0)
|
||||
{
|
||||
std::cout << "ERROR cannot create DMA Process\n";
|
||||
}
|
||||
|
||||
// wait until the acquisition is finished
|
||||
pthread_join(thread_acquisition, nullptr);
|
||||
|
||||
// wait for the child DMA process to finish
|
||||
pthread_join(thread_DMA, nullptr);
|
||||
|
||||
acquisition_successful = channel_fsm_->Event_check_test_result();
|
||||
|
||||
if (acquisition_successful)
|
||||
{
|
||||
gnss_synchro_vec.push_back(tmp_gnss_synchro);
|
||||
}
|
||||
|
||||
if (!gnss_synchro_vec.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsAcquisitionTestFpga::init()
|
||||
{
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
|
||||
config->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_Acquisition_FPGA");
|
||||
config->set_property("Acquisition.threshold", "0.00001");
|
||||
config->set_property("Acquisition.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition.repeat_satellite", "false");
|
||||
|
||||
// the test file is sampled @ 4MSPs only ,so we have to use the FPGA queue corresponding
|
||||
// to the L5/E5a frequency band in order to avoid the L1/E1 factor :4 downsampling filter
|
||||
config->set_property("Acquisition.downsampling_factor", "1");
|
||||
config->set_property("Acquisition.select_queue_Fpga", "1");
|
||||
config->set_property("Acquisition.total_block_exp", "12");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsAcquisitionTestFpga, ValidationOfResults)
|
||||
{
|
||||
struct DMA_handler_args_gps_l1_acq_test args;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
double expected_delay_samples = 524;
|
||||
double expected_doppler_hz = 1680;
|
||||
|
||||
init();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
ASSERT_EQ(acquire_signal(), true);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
uint32_t n = 0; // there is only one channel
|
||||
std::cout << "Acquired " << nsamples_to_transfer << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro_vec.at(n).Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro_vec.at(n).Acq_doppler_hz);
|
||||
|
||||
// the acquisition grid is not available when using the FPGA
|
||||
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,642 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_opencl_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsOpenClAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "fir_filter.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_pcps_opencl_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_sptr(new GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(msg);
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx::GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx::~GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CaPcpsOpenClAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL1CaPcpsOpenClAcquisitionGSoC2013Test()
|
||||
{
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
init();
|
||||
}
|
||||
|
||||
~GpsL1CaPcpsOpenClAcquisitionGSoC2013Test()
|
||||
{
|
||||
}
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GpsL1CaPcpsOpenClAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0;
|
||||
float max_delay_error_chips = 0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter;
|
||||
unsigned int detection_counter;
|
||||
unsigned int correct_estimation_counter;
|
||||
unsigned int acquired_samples;
|
||||
unsigned int mean_acq_time_us;
|
||||
|
||||
double mse_doppler;
|
||||
double mse_delay;
|
||||
|
||||
double Pd;
|
||||
double Pfa_p;
|
||||
double Pfa_a;
|
||||
};
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_OpenCl_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
config->set_property("Acquisition_1C.threshold", "0.8");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 10; // Change here the number of realizations
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "G");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "G");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "G");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_OpenCl_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
config->set_property("Acquisition_1C.pfa", "0.1");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsOpenClAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsOpenClAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsOpenClAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsOpenClAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsOpenClAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsOpenClAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
|
||||
acquisition = std::make_shared<GpsL1CaPcpsOpenClAcquisition>(config.get(), "Acquisition", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1C.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1C.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1C.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
if (!acquisition->opencl_ready())
|
||||
{
|
||||
std::cout << "OpenCL Platform is not ready.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsOpenClAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
|
||||
acquisition = std::make_shared<GpsL1CaPcpsOpenClAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsOpenClAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1C.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1C.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1C.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
if (!acquisition->opencl_ready())
|
||||
{
|
||||
std::cout << "OpenCL Platform is not ready.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,899 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_quicksync_acquisition_gsoc2014_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsQuickSyncAcquisition class based on some input parameters.
|
||||
* \author Damian Miralles Sanchez, 2014. dmiralles2009(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_pcps_quicksync_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_double(value_threshold, 1, "Value of the threshold for the acquisition");
|
||||
DEFINE_int32(value_CN0_dB_0, 44, "Value for the CN0_dB_0 in channel 0");
|
||||
#else
|
||||
ABSL_FLAG(double, value_threshold, 1, "Value of the threshold for the acquisition");
|
||||
ABSL_FLAG(int32_t, value_CN0_dB_0, 44, "Value for the CN0_dB_0 in channel 0");
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx;
|
||||
|
||||
using GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_sptr = gnss_shared_ptr<GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx>;
|
||||
|
||||
GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_sptr GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_sptr GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_sptr GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx_sptr(new GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx::GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx::~GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void config_3();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GpsL1CaPcpsQuickSyncAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
unsigned int folding_factor = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter = 0;
|
||||
unsigned int detection_counter = 0;
|
||||
unsigned int correct_estimation_counter = 0;
|
||||
unsigned int acquired_samples = 0;
|
||||
unsigned int mean_acq_time_us = 0;
|
||||
|
||||
double mse_doppler = 0.0;
|
||||
double mse_delay = 0.0;
|
||||
|
||||
double Pd = 0.0;
|
||||
double Pfa_p = 0.0;
|
||||
double Pfa_a = 0.0;
|
||||
double Pmd = 0.0;
|
||||
|
||||
std::ofstream pdpfafile;
|
||||
unsigned int miss_detection_counter = 0;
|
||||
bool dump_test_results = false;
|
||||
};
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
|
||||
miss_detection_counter = 0;
|
||||
Pmd = 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 8e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
config->set_property("SignalSource.repeat", "true");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_QuickSync_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
config->set_property("Acquisition_1C.threshold", "250");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 8e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
/*Unset this flag to eliminates data logging for the Validation of results
|
||||
probabilities test*/
|
||||
dump_test_results = false;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(FLAGS_value_CN0_dB_0));
|
||||
#else
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(absl::GetFlag(FLAGS_value_CN0_dB_0)));
|
||||
#endif
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "G");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "G");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "G");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_QuickSync_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "1");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("Acquisition_1C.threshold", std::to_string(FLAGS_value_threshold));
|
||||
#else
|
||||
config->set_property("Acquisition_1C.threshold", std::to_string(absl::GetFlag(FLAGS_value_threshold)));
|
||||
#endif
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "100");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::config_3()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 4;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
/*Unset this flag to eliminates data logging for the Validation of results
|
||||
probabilities test*/
|
||||
dump_test_results = true;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(FLAGS_value_CN0_dB_0));
|
||||
#else
|
||||
config->set_property("SignalSource.CN0_dB_0", std::to_string(absl::GetFlag(FLAGS_value_CN0_dB_0)));
|
||||
#endif
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "G");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "G");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "G");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_QuickSync_Acquisition");
|
||||
config->set_property("Acquisition_1C.item_type", "gr_complex");
|
||||
config->set_property("Acquisition_1C.coherent_integration_time_ms",
|
||||
std::to_string(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.max_dwells", "2");
|
||||
config->set_property("Acquisition_1C.threshold", "0.01");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.bit_transition_flag", "false");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
else if (message == 2 && gnss_synchro.PRN == 10)
|
||||
{
|
||||
miss_detection_counter++;
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pmd = static_cast<double>(miss_detection_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsQuickSyncAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsQuickSyncAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsQuickSyncAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(250);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(100);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter)
|
||||
<< "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message)
|
||||
<< "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test, ValidationOfResultsWithNoise)
|
||||
{
|
||||
// config_3();
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsQuickSyncAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(10000);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(250);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(100);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
// acquisition->set_local_code();
|
||||
acquisition->reset();
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsQuickSyncAcquisitionGSoC2014Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsQuickSyncAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
acquisition->reset();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->reset();
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Estimated probability of miss detection (satellite present) = " << Pmd << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
|
||||
if (dump_test_results)
|
||||
{
|
||||
std::stringstream filenamepd;
|
||||
filenamepd.str("");
|
||||
filenamepd << "../data/test_statistics_" << gnss_synchro.System
|
||||
<< "_" << gnss_synchro.Signal << "_sat_"
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << FLAGS_value_CN0_dB_0 << "_dBHz.csv";
|
||||
#else
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << absl::GetFlag(FLAGS_value_CN0_dB_0) << "_dBHz.csv";
|
||||
|
||||
#endif
|
||||
pdpfafile.open(filenamepd.str().c_str(), std::ios::app | std::ios::out);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
pdpfafile << FLAGS_value_threshold << "," << Pd << "," << Pfa_p << "," << Pmd << '\n';
|
||||
#else
|
||||
pdpfafile << absl::GetFlag(FLAGS_value_threshold) << "," << Pd << "," << Pfa_p << "," << Pmd << '\n';
|
||||
#endif
|
||||
pdpfafile.close();
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
|
||||
if (dump_test_results)
|
||||
{
|
||||
std::stringstream filenamepf;
|
||||
filenamepf.str("");
|
||||
filenamepf << "../data/test_statistics_" << gnss_synchro.System
|
||||
<< "_" << gnss_synchro.Signal << "_sat_"
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << FLAGS_value_CN0_dB_0 << "_dBHz.csv";
|
||||
#else
|
||||
<< gnss_synchro.PRN << "CN0_dB_0_" << absl::GetFlag(FLAGS_value_CN0_dB_0) << "_dBHz.csv";
|
||||
#endif
|
||||
pdpfafile.open(filenamepf.str().c_str(), std::ios::app | std::ios::out);
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
pdpfafile << FLAGS_value_threshold << "," << Pfa_a << '\n';
|
||||
#else
|
||||
pdpfafile << absl::GetFlag(FLAGS_value_threshold) << "," << Pfa_a << '\n';
|
||||
#endif
|
||||
pdpfafile.close();
|
||||
}
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_pcps_tong_acquisition_gsoc2013_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsTongAcquisition class.
|
||||
* \author Marc Molina, 2013. marc.molina.pena(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "configuration_interface.h"
|
||||
#include "fir_filter.h"
|
||||
#include "gen_signal_source.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_pcps_tong_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator.h"
|
||||
#include "signal_generator_c.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx;
|
||||
|
||||
using GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_sptr = gnss_shared_ptr<GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx>;
|
||||
|
||||
GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
|
||||
|
||||
class GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue);
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
explicit GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue);
|
||||
Concurrent_Queue<int>& channel_internal_queue;
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_sptr GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(Concurrent_Queue<int>& queue)
|
||||
{
|
||||
return GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_sptr(new GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx(queue));
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
channel_internal_queue.push(rx_message);
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx::GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx(Concurrent_Queue<int>& queue) : gr::block("GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)), channel_internal_queue(queue)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx::~GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CaPcpsTongAcquisitionGSoC2013Test : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL1CaPcpsTongAcquisitionGSoC2013Test()
|
||||
{
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL1CaPcpsTongAcquisitionGSoC2013Test() = default;
|
||||
|
||||
void init();
|
||||
void config_1();
|
||||
void config_2();
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
|
||||
Concurrent_Queue<int> channel_internal_queue;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GpsL1CaPcpsTongAcquisition> acquisition;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
std::thread ch_thread;
|
||||
|
||||
unsigned int integration_time_ms = 0;
|
||||
unsigned int fs_in = 0;
|
||||
|
||||
double expected_delay_chips = 0.0;
|
||||
double expected_doppler_hz = 0.0;
|
||||
float max_doppler_error_hz = 0.0;
|
||||
float max_delay_error_chips = 0.0;
|
||||
|
||||
unsigned int num_of_realizations = 0;
|
||||
unsigned int realization_counter = 0;
|
||||
unsigned int detection_counter = 0;
|
||||
unsigned int correct_estimation_counter = 0;
|
||||
unsigned int acquired_samples = 0;
|
||||
unsigned int mean_acq_time_us = 0;
|
||||
|
||||
double mse_doppler = 0.0;
|
||||
double mse_delay = 0.0;
|
||||
|
||||
double Pd = 0.0;
|
||||
double Pfa_p = 0.0;
|
||||
double Pfa_a = 0.0;
|
||||
};
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::init()
|
||||
{
|
||||
message = 0;
|
||||
realization_counter = 0;
|
||||
detection_counter = 0;
|
||||
correct_estimation_counter = 0;
|
||||
acquired_samples = 0;
|
||||
mse_doppler = 0;
|
||||
mse_delay = 0;
|
||||
mean_acq_time_us = 0;
|
||||
Pd = 0;
|
||||
Pfa_p = 0;
|
||||
Pfa_a = 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::config_1()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 1;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "1");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "false");
|
||||
config->set_property("SignalSource.data_flag", "false");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
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(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.threshold", "0.8");
|
||||
config->set_property("Acquisition_1C.tong_init_val", "1");
|
||||
config->set_property("Acquisition_1C.tong_max_val", "8");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::config_2()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
|
||||
integration_time_ms = 1;
|
||||
fs_in = 4e6;
|
||||
|
||||
expected_delay_chips = 600;
|
||||
expected_doppler_hz = 750;
|
||||
max_doppler_error_hz = 2 / (3 * integration_time_ms * 1e-3);
|
||||
max_delay_error_chips = 0.50;
|
||||
|
||||
num_of_realizations = 100;
|
||||
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.fs_hz", std::to_string(fs_in));
|
||||
|
||||
config->set_property("SignalSource.item_type", "gr_complex");
|
||||
|
||||
config->set_property("SignalSource.num_satellites", "4");
|
||||
|
||||
config->set_property("SignalSource.system_0", "G");
|
||||
config->set_property("SignalSource.PRN_0", "10");
|
||||
config->set_property("SignalSource.CN0_dB_0", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_0", std::to_string(expected_doppler_hz));
|
||||
config->set_property("SignalSource.delay_chips_0", std::to_string(expected_delay_chips));
|
||||
|
||||
config->set_property("SignalSource.system_1", "G");
|
||||
config->set_property("SignalSource.PRN_1", "15");
|
||||
config->set_property("SignalSource.CN0_dB_1", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_1", "1000");
|
||||
config->set_property("SignalSource.delay_chips_1", "100");
|
||||
|
||||
config->set_property("SignalSource.system_2", "G");
|
||||
config->set_property("SignalSource.PRN_2", "21");
|
||||
config->set_property("SignalSource.CN0_dB_2", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_2", "2000");
|
||||
config->set_property("SignalSource.delay_chips_2", "200");
|
||||
|
||||
config->set_property("SignalSource.system_3", "G");
|
||||
config->set_property("SignalSource.PRN_3", "22");
|
||||
config->set_property("SignalSource.CN0_dB_3", "44");
|
||||
config->set_property("SignalSource.doppler_Hz_3", "3000");
|
||||
config->set_property("SignalSource.delay_chips_3", "300");
|
||||
|
||||
config->set_property("SignalSource.noise_flag", "true");
|
||||
config->set_property("SignalSource.data_flag", "true");
|
||||
config->set_property("SignalSource.BW_BB", "0.97");
|
||||
|
||||
config->set_property("InputFilter.implementation", "Fir_Filter");
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "11");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.97");
|
||||
config->set_property("InputFilter.band2_begin", "0.98");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
|
||||
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(integration_time_ms));
|
||||
config->set_property("Acquisition_1C.threshold", "0.00108"); // Pfa,a = 0.1
|
||||
config->set_property("Acquisition_1C.tong_init_val", "1");
|
||||
config->set_property("Acquisition_1C.tong_max_val", "8");
|
||||
config->set_property("Acquisition_1C.doppler_max", "10000");
|
||||
config->set_property("Acquisition_1C.doppler_step", "250");
|
||||
config->set_property("Acquisition_1C.dump", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&GpsL1CaPcpsTongAcquisitionGSoC2013Test::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::wait_message()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
acquisition->reset();
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
channel_internal_queue.wait_and_pop(message);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
|
||||
mean_acq_time_us += elapsed_seconds.count() * 1e6;
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::process_message()
|
||||
{
|
||||
if (message == 1)
|
||||
{
|
||||
detection_counter++;
|
||||
|
||||
// The term -5 is here to correct the additional delay introduced by the FIR filter
|
||||
double delay_error_chips = std::abs(static_cast<double>(expected_delay_chips) - (gnss_synchro.Acq_delay_samples - 5) * 1023.0 / (static_cast<double>(fs_in) * 1e-3));
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
mse_delay += std::pow(delay_error_chips, 2);
|
||||
mse_doppler += std::pow(doppler_error_hz, 2);
|
||||
|
||||
if ((delay_error_chips < max_delay_error_chips) && (doppler_error_hz < max_doppler_error_hz))
|
||||
{
|
||||
correct_estimation_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
realization_counter++;
|
||||
|
||||
std::cout << "Progress: " << round(static_cast<float>(realization_counter) / static_cast<float>(num_of_realizations) * 100.0) << "% \r" << std::flush;
|
||||
|
||||
if (realization_counter == num_of_realizations)
|
||||
{
|
||||
mse_delay /= num_of_realizations;
|
||||
mse_doppler /= num_of_realizations;
|
||||
|
||||
Pd = static_cast<double>(correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_a = static_cast<double>(detection_counter) / static_cast<double>(num_of_realizations);
|
||||
Pfa_p = static_cast<double>(detection_counter - correct_estimation_counter) / static_cast<double>(num_of_realizations);
|
||||
|
||||
mean_acq_time_us /= num_of_realizations;
|
||||
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CaPcpsTongAcquisitionGSoC2013Test::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsTongAcquisitionGSoC2013Test, Instantiate)
|
||||
{
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsTongAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsTongAcquisitionGSoC2013Test, ConnectAndRun)
|
||||
{
|
||||
int nsamples = floor(fs_in * integration_time_ms * 1e-3);
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
config_1();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsTongAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsTongAcquisitionGSoC2013Test, ValidationOfResults)
|
||||
{
|
||||
config_1();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
acquisition = std::make_shared<GpsL1CaPcpsTongAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1C.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1C.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1C.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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.";
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible
|
||||
// i = 1 --> satellite in acquisition is not visible
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
if (message == 1)
|
||||
{
|
||||
EXPECT_EQ(static_cast<unsigned int>(1), correct_estimation_counter) << "Acquisition failure. Incorrect parameters estimation.";
|
||||
}
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
EXPECT_EQ(2, message) << "Acquisition failure. Expected message: 2=ACQ FAIL.";
|
||||
}
|
||||
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CaPcpsTongAcquisitionGSoC2013Test, ValidationOfResultsProbabilities)
|
||||
{
|
||||
config_2();
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
acquisition = std::make_shared<GpsL1CaPcpsTongAcquisition>(config.get(), "Acquisition_1C", 1, 0);
|
||||
auto msg_rx = GpsL1CaPcpsTongAcquisitionGSoC2013Test_msg_rx_make(channel_internal_queue);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(config->property("Acquisition_1C.doppler_max", 10000));
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(config->property("Acquisition_1C.doppler_step", 500));
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(config->property("Acquisition_1C.threshold", 0.0));
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
acquisition->init();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<GNSSBlockInterface> signal_generator = std::make_shared<SignalGenerator>(config.get(), "SignalSource", 0, 1, queue.get());
|
||||
std::shared_ptr<GNSSBlockInterface> filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
std::shared_ptr<GNSSBlockInterface> signal_source = std::make_shared<GenSignalSource>(signal_generator, filter, "SignalSource", queue.get());
|
||||
signal_source->connect(top_block);
|
||||
top_block->connect(signal_source->get_right_block(), 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::cout << "Probability of false alarm (target) = " << 0.1 << '\n';
|
||||
|
||||
// i = 0 --> satellite in acquisition is visible (prob of detection and prob of detection with wrong estimation)
|
||||
// i = 1 --> satellite in acquisition is not visible (prob of false detection)
|
||||
for (unsigned int i = 0; i < 2; i++)
|
||||
{
|
||||
init();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
gnss_synchro.PRN = 10; // This satellite is visible
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
gnss_synchro.PRN = 20; // This satellite is not visible
|
||||
}
|
||||
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1);
|
||||
start_queue();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->run(); // Start threads and wait
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
stop_queue();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
std::cout << "Estimated probability of detection = " << Pd << '\n';
|
||||
std::cout << "Estimated probability of false alarm (satellite present) = " << Pfa_p << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
std::cout << "Estimated probability of false alarm (satellite absent) = " << Pfa_a << '\n';
|
||||
std::cout << "Mean acq time = " << mean_acq_time_us << " microseconds.\n";
|
||||
}
|
||||
ch_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
/*!
|
||||
* \file gps_l2_m_pcps_acquisition_test.cc
|
||||
* \brief This class implements an acquisition test for
|
||||
* GpsL1CaPcpsAcquisition class based on some input parameters.
|
||||
* \author Javier Arribas, 2015 (jarribas@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 "GPS_L2C.h"
|
||||
#include "acquisition_dump_reader.h"
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "gps_l2_m_pcps_acquisition.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "test_flags.h"
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/char_to_short.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/interleaved_short_to_complex.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL2MPcpsAcquisitionTest_msg_rx;
|
||||
|
||||
using GpsL2MPcpsAcquisitionTest_msg_rx_sptr = gnss_shared_ptr<GpsL2MPcpsAcquisitionTest_msg_rx>;
|
||||
|
||||
GpsL2MPcpsAcquisitionTest_msg_rx_sptr GpsL2MPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
class GpsL2MPcpsAcquisitionTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL2MPcpsAcquisitionTest_msg_rx_sptr GpsL2MPcpsAcquisitionTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GpsL2MPcpsAcquisitionTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL2MPcpsAcquisitionTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
GpsL2MPcpsAcquisitionTest_msg_rx_sptr GpsL2MPcpsAcquisitionTest_msg_rx_make()
|
||||
{
|
||||
return GpsL2MPcpsAcquisitionTest_msg_rx_sptr(new GpsL2MPcpsAcquisitionTest_msg_rx());
|
||||
}
|
||||
|
||||
void GpsL2MPcpsAcquisitionTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast &e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GpsL2MPcpsAcquisitionTest_msg_rx::GpsL2MPcpsAcquisitionTest_msg_rx() : gr::block("GpsL2MPcpsAcquisitionTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto &&PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL2MPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL2MPcpsAcquisitionTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL2MPcpsAcquisitionTest_msg_rx::~GpsL2MPcpsAcquisitionTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL2MPcpsAcquisitionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL2MPcpsAcquisitionTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
sampling_frequency_hz = 5000000;
|
||||
nsamples = 0;
|
||||
doppler_max = 3000;
|
||||
doppler_step = 125;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL2MPcpsAcquisitionTest() = default;
|
||||
|
||||
void init();
|
||||
void plot_grid();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
int sampling_frequency_hz;
|
||||
int nsamples;
|
||||
unsigned int doppler_max;
|
||||
unsigned int doppler_step;
|
||||
};
|
||||
|
||||
|
||||
void GpsL2MPcpsAcquisitionTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "2S";
|
||||
std::memcpy(static_cast<void *>(gnss_synchro.Signal), signal.c_str(), 3); // copy string into synchro char array: 2 char + null
|
||||
gnss_synchro.Signal[2] = 0; // make sure that string length is only two characters
|
||||
gnss_synchro.PRN = 7;
|
||||
|
||||
nsamples = round(static_cast<double>(sampling_frequency_hz) * GPS_L2_M_PERIOD_S) * 2;
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(sampling_frequency_hz));
|
||||
config->set_property("Acquisition_2S.implementation", "GPS_L2_M_PCPS_Acquisition");
|
||||
config->set_property("Acquisition_2S.item_type", "gr_complex");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
config->set_property("Acquisition_2S.dump", "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
config->set_property("Acquisition_2S.dump", "false");
|
||||
}
|
||||
config->set_property("Acquisition_2S.dump_filename", "./tmp-acq-gps2/acquisition_test");
|
||||
config->set_property("Acquisition_2S.dump_channel", "1");
|
||||
config->set_property("Acquisition_2S.threshold", "0.001");
|
||||
config->set_property("Acquisition_2S.doppler_max", std::to_string(doppler_max));
|
||||
config->set_property("Acquisition_2S.doppler_step", std::to_string(doppler_step));
|
||||
config->set_property("Acquisition_2S.repeat_satellite", "false");
|
||||
config->set_property("Acquisition_2S.make_two_steps", "false");
|
||||
}
|
||||
|
||||
|
||||
void GpsL2MPcpsAcquisitionTest::plot_grid()
|
||||
{
|
||||
// load the measured values
|
||||
std::string basename = "./tmp-acq-gps2/acquisition_test_G_2S";
|
||||
auto sat = static_cast<unsigned int>(gnss_synchro.PRN);
|
||||
|
||||
auto samples_per_code = static_cast<unsigned int>(floor(static_cast<double>(sampling_frequency_hz) / (GPS_L2_M_CODE_RATE_CPS / static_cast<double>(GPS_L2_M_CODE_LENGTH_CHIPS))));
|
||||
Acquisition_Dump_Reader acq_dump(basename, sat, doppler_max, doppler_step, samples_per_code, 1);
|
||||
if (!acq_dump.read_binary_acq())
|
||||
{
|
||||
std::cout << "Error reading files\n";
|
||||
}
|
||||
|
||||
std::vector<int> *doppler = &acq_dump.doppler;
|
||||
std::vector<unsigned int> *samples = &acq_dump.samples;
|
||||
std::vector<std::vector<float>> *mag = &acq_dump.mag;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||
#else
|
||||
const std::string gnuplot_executable(absl::GetFlag(FLAGS_gnuplot_executable));
|
||||
#endif
|
||||
if (gnuplot_executable.empty())
|
||||
{
|
||||
std::cout << "WARNING: Although the flag plot_acq_grid has been set to TRUE,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Plotting the acquisition grid. This can take a while...\n";
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string &gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
Gnuplot g1("impulses");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
g1.set_title("GPS L2CM 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_L2CM_acq_grid");
|
||||
g1.savetopdf("GPS_L2CM_acq_grid");
|
||||
}
|
||||
catch (const GnuplotException &ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
std::string data_str = "./tmp-acq-gps2";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL2MPcpsAcquisitionTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
std::shared_ptr<GpsL2MPcpsAcquisition> acquisition = std::make_shared<GpsL2MPcpsAcquisition>(config.get(), "Acquisition_2S", 1, 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL2MPcpsAcquisitionTest, ConnectAndRun)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
init();
|
||||
std::shared_ptr<GpsL2MPcpsAcquisition> acquisition = std::make_shared<GpsL2MPcpsAcquisition>(config.get(), "Acquisition_2S", 1, 0);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(sampling_frequency_hz, gr::analog::GR_SIN_WAVE, 2000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, acquisition->get_left_block(), 0);
|
||||
auto msg_rx = GpsL2MPcpsAcquisitionTest_msg_rx_make();
|
||||
}) << "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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL2MPcpsAcquisitionTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Acquisition test");
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
double expected_delay_samples = 1; // 2004;
|
||||
double expected_doppler_hz = 1200; // 3000;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
std::string data_str = "./tmp-acq-gps2";
|
||||
if (fs::exists(data_str))
|
||||
{
|
||||
fs::remove_all(data_str);
|
||||
}
|
||||
fs::create_directory(data_str);
|
||||
}
|
||||
|
||||
init();
|
||||
std::shared_ptr<GpsL2MPcpsAcquisition> acquisition = std::make_shared<GpsL2MPcpsAcquisition>(config.get(), "Acquisition_2S", 1, 0);
|
||||
auto msg_rx = GpsL2MPcpsAcquisitionTest_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_channel(1);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_threshold(0.001);
|
||||
}) << "Failure setting threshold.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_max(doppler_max);
|
||||
}) << "Failure setting doppler_max.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_doppler_step(doppler_step);
|
||||
}) << "Failure setting doppler_step.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->connect(top_block);
|
||||
}) << "Failure connecting acquisition to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
// std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
std::string file = path + "signal_samples/gps_l2c_m_prn7_5msps.dat";
|
||||
// std::string file = "/datalogger/signals/Fraunhofer/L125_III1b_210s_L2_resampled.bin";
|
||||
const char *file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
// gr::blocks::interleaved_short_to_complex::sptr gr_interleaved_short_to_complex_ = gr::blocks::interleaved_short_to_complex::make();
|
||||
// gr::blocks::char_to_short::sptr gr_char_to_short_ = gr::blocks::char_to_short::make();
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
// top_block->connect(file_source, 0, gr_char_to_short_, 0);
|
||||
// top_block->connect(gr_char_to_short_, 0, gr_interleaved_short_to_complex_ , 0);
|
||||
top_block->connect(file_source, 0, valve, 0);
|
||||
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.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
acquisition->set_local_code();
|
||||
acquisition->set_state(1); // Ensure that acquisition starts at the first sample
|
||||
acquisition->init();
|
||||
}) << "Failure set_state and init 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::cout << "Acquisition process runtime duration: " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
|
||||
std::cout << "gnss_synchro.Acq_doppler_hz = " << gnss_synchro.Acq_doppler_hz << " Hz\n";
|
||||
std::cout << "gnss_synchro.Acq_delay_samples = " << gnss_synchro.Acq_delay_samples << " Samples\n";
|
||||
|
||||
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
|
||||
|
||||
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
|
||||
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000);
|
||||
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
|
||||
|
||||
EXPECT_LE(doppler_error_hz, 200) << "Doppler error exceeds the expected value: 2/(3*integration period)";
|
||||
EXPECT_LT(delay_error_chips, 0.5) << "Delay error exceeds the expected value: 0.5 chips";
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_acq_grid == true)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_acq_grid) == true)
|
||||
#endif
|
||||
{
|
||||
plot_grid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
/*!
|
||||
* \file adapter_test.cc
|
||||
* \brief This file implements tests for the DataTypeAdapter block
|
||||
* \author Carles Fernandez-Prades, 2017. 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 "byte_to_short.h"
|
||||
#include "ibyte_to_cbyte.h"
|
||||
#include "ibyte_to_complex.h"
|
||||
#include "ibyte_to_cshort.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "ishort_to_complex.h"
|
||||
#include "ishort_to_cshort.h"
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class DataTypeAdapter : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
DataTypeAdapter();
|
||||
int run_byte_to_short_block() const;
|
||||
int run_ibyte_to_cbyte_block() const;
|
||||
int run_ibyte_to_complex_block() const;
|
||||
int run_ibyte_to_cshort_block() const;
|
||||
int run_ishort_to_complex_block() const;
|
||||
int run_ishort_to_cshort_block() const;
|
||||
std::string file_name_input;
|
||||
std::string file_name_output;
|
||||
std::vector<int8_t> input_data_bytes;
|
||||
std::vector<int16_t> input_data_shorts;
|
||||
};
|
||||
|
||||
|
||||
DataTypeAdapter::DataTypeAdapter()
|
||||
: file_name_input("adapter_test_input.dat"),
|
||||
file_name_output("adapter_test_output.dat")
|
||||
{
|
||||
std::array<int8_t, 6> input_bytes{2, 23, -1, 127, -127, 0};
|
||||
std::array<int16_t, 8> input_shorts{2, 23, -1, 127, -127, 0, 255, 255};
|
||||
|
||||
const std::vector<int8_t> input_data_bytes_(input_bytes.data(), input_bytes.data() + input_bytes.size() / sizeof(int8_t));
|
||||
input_data_bytes = input_data_bytes_;
|
||||
|
||||
const std::vector<int16_t> input_data_shorts_(input_shorts.data(), input_shorts.data() + input_shorts.size() / sizeof(int16_t));
|
||||
input_data_shorts = input_data_shorts_;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_ishort_to_cshort_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Ishort_To_Cshort");
|
||||
std::shared_ptr<IshortToCshort> ishort_to_cshort = std::make_shared<IshortToCshort>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Ishort_To_Cshort";
|
||||
EXPECT_EQ(expected_implementation, ishort_to_cshort->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str(), std::ofstream::binary);
|
||||
for (int16_t aux : input_data_shorts)
|
||||
{
|
||||
ofs.write(reinterpret_cast<const char*>(&aux), sizeof(int16_t));
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Ishort_To_Cshort test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int16_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(lv_16sc_t), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, ishort_to_cshort->get_left_block(), 0);
|
||||
top_block->connect(ishort_to_cshort->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_ishort_to_complex_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Ishort_To_Complex");
|
||||
std::shared_ptr<IshortToComplex> ishort_to_complex = std::make_shared<IshortToComplex>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Ishort_To_Complex";
|
||||
EXPECT_EQ(expected_implementation, ishort_to_complex->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str(), std::ofstream::binary);
|
||||
for (int16_t aux : input_data_shorts)
|
||||
{
|
||||
ofs.write(reinterpret_cast<const char*>(&aux), sizeof(int16_t));
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Ishort_To_Complex test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int16_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(gr_complex), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, ishort_to_complex->get_left_block(), 0);
|
||||
top_block->connect(ishort_to_complex->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_ibyte_to_cshort_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Ibyte_To_Cshort");
|
||||
std::shared_ptr<IbyteToCshort> ibyte_to_cshort = std::make_shared<IbyteToCshort>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Ibyte_To_Cshort";
|
||||
EXPECT_EQ(expected_implementation, ibyte_to_cshort->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str());
|
||||
for (signed char input_data_byte : input_data_bytes)
|
||||
{
|
||||
ofs << input_data_byte;
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Ibyte_To_Cshort test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(lv_16sc_t), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, ibyte_to_cshort->get_left_block(), 0);
|
||||
top_block->connect(ibyte_to_cshort->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_ibyte_to_complex_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Ibyte_To_Complex");
|
||||
std::shared_ptr<IbyteToComplex> ibyte_to_complex = std::make_shared<IbyteToComplex>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Ibyte_To_Complex";
|
||||
EXPECT_EQ(expected_implementation, ibyte_to_complex->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str());
|
||||
for (signed char input_data_byte : input_data_bytes)
|
||||
{
|
||||
ofs << input_data_byte;
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Ibyte_To_Complex test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(gr_complex), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, ibyte_to_complex->get_left_block(), 0);
|
||||
top_block->connect(ibyte_to_complex->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_ibyte_to_cbyte_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Ibyte_To_Cbyte");
|
||||
std::shared_ptr<IbyteToCbyte> ibyte_to_cbyte = std::make_shared<IbyteToCbyte>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Ibyte_To_Cbyte";
|
||||
EXPECT_EQ(expected_implementation, ibyte_to_cbyte->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str());
|
||||
for (signed char input_data_byte : input_data_bytes)
|
||||
{
|
||||
ofs << input_data_byte;
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Ibyte_To_Cbyte test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(int16_t), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, ibyte_to_cbyte->get_left_block(), 0);
|
||||
top_block->connect(ibyte_to_cbyte->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DataTypeAdapter::run_byte_to_short_block() const
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.implementation", "Byte_To_Short");
|
||||
std::shared_ptr<ByteToShort> byte_to_short = std::make_shared<ByteToShort>(config.get(), "Test", 1, 1);
|
||||
std::string expected_implementation = "Byte_To_Short";
|
||||
EXPECT_EQ(expected_implementation, byte_to_short->implementation());
|
||||
|
||||
std::ofstream ofs(file_name_input.c_str());
|
||||
for (signed char input_data_byte : input_data_bytes)
|
||||
{
|
||||
ofs << input_data_byte;
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
auto top_block = gr::make_top_block("Byte_To_Short test");
|
||||
auto file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name_input.c_str());
|
||||
auto sink = gr::blocks::file_sink::make(sizeof(int16_t), file_name_output.c_str(), false);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(file_source, 0, byte_to_short->get_left_block(), 0);
|
||||
top_block->connect(byte_to_short->get_right_block(), 0, sink, 0);
|
||||
top_block->run();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, ByteToShortValidationOfResults)
|
||||
{
|
||||
run_byte_to_short_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
|
||||
int16_t iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(int16_t)))
|
||||
{
|
||||
EXPECT_EQ(input_data_bytes.at(i), static_cast<int8_t>(iSample / 256)); // Scale down!
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, IbyteToCbyteValidationOfResults)
|
||||
{
|
||||
run_ibyte_to_cbyte_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
lv_8sc_t iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(lv_8sc_t)))
|
||||
{
|
||||
EXPECT_EQ(input_data_bytes.at(i), iSample.real());
|
||||
i++;
|
||||
EXPECT_EQ(input_data_bytes.at(i), iSample.imag());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, IbyteToComplexValidationOfResults)
|
||||
{
|
||||
run_ibyte_to_cbyte_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
gr_complex iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(gr_complex)))
|
||||
{
|
||||
EXPECT_EQ(input_data_bytes.at(i), static_cast<int8_t>(iSample.real()));
|
||||
i++;
|
||||
EXPECT_EQ(input_data_bytes.at(i), static_cast<int8_t>(iSample.imag()));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, IbyteToCshortValidationOfResults)
|
||||
{
|
||||
run_ibyte_to_cshort_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
lv_16sc_t iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(lv_16sc_t)))
|
||||
{
|
||||
EXPECT_EQ(input_data_bytes.at(i), static_cast<int8_t>(iSample.real()));
|
||||
i++;
|
||||
EXPECT_EQ(input_data_bytes.at(i), static_cast<int8_t>(iSample.imag()));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, IshortToComplexValidationOfResults)
|
||||
{
|
||||
run_ishort_to_complex_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
gr_complex iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(gr_complex)))
|
||||
{
|
||||
EXPECT_EQ(input_data_shorts.at(i), iSample.real());
|
||||
i++;
|
||||
EXPECT_EQ(input_data_shorts.at(i), iSample.imag());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DataTypeAdapter, IshortToCshortValidationOfResults)
|
||||
{
|
||||
run_ishort_to_cshort_block();
|
||||
std::ifstream ifs(file_name_output.data(), std::ifstream::binary | std::ifstream::in);
|
||||
lv_16sc_t iSample;
|
||||
int i = 0;
|
||||
try
|
||||
{
|
||||
while (ifs.read(reinterpret_cast<char*>(&iSample), sizeof(lv_16sc_t)))
|
||||
{
|
||||
EXPECT_EQ(input_data_shorts.at(i), iSample.real());
|
||||
i++;
|
||||
EXPECT_EQ(input_data_shorts.at(i), iSample.imag());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.code().message() << '\n';
|
||||
}
|
||||
ifs.close();
|
||||
ASSERT_EQ(remove(file_name_input.c_str()), 0) << "Problem deleting temporary file";
|
||||
ASSERT_EQ(remove(file_name_output.c_str()), 0) << "Problem deleting temporary file";
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* \file pass_through_test.cc
|
||||
* \brief This file implements tests for the Pass_Through block
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2012. 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 "in_memory_configuration.h"
|
||||
#include "pass_through.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
TEST(PassThroughTest, Instantiate)
|
||||
{
|
||||
std::shared_ptr<ConfigurationInterface> config = std::make_shared<InMemoryConfiguration>();
|
||||
config->set_property("Test.item_type", "gr_complex");
|
||||
std::shared_ptr<Pass_Through> signal_conditioner = std::make_shared<Pass_Through>(config.get(), "Test", 1, 1);
|
||||
EXPECT_STREQ("gr_complex", signal_conditioner->item_type().c_str());
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*!
|
||||
* \file fir_filter_test.cc
|
||||
* \brief Implements Unit Test for the FirFilter class.
|
||||
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "fir_filter.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "interleaved_byte_to_complex_byte.h"
|
||||
#include "interleaved_short_to_complex_short.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
DEFINE_int32(filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
ABSL_FLAG(int32_t, filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#endif
|
||||
|
||||
|
||||
class FirFilterTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
FirFilterTest() : item_size(sizeof(gr_complex)),
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
nsamples(FLAGS_filter_test_nsamples)
|
||||
#else
|
||||
nsamples(absl::GetFlag(FLAGS_filter_test_nsamples))
|
||||
#endif
|
||||
{
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
}
|
||||
|
||||
void init();
|
||||
void configure_cbyte_cbyte();
|
||||
void configure_cbyte_gr_complex();
|
||||
void configure_gr_complex_gr_complex();
|
||||
void configure_cshort_cshort();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
size_t item_size;
|
||||
int nsamples;
|
||||
};
|
||||
|
||||
|
||||
void FirFilterTest::init()
|
||||
{
|
||||
config->set_property("InputFilter.taps_item_type", "float");
|
||||
config->set_property("InputFilter.number_of_taps", "5");
|
||||
config->set_property("InputFilter.number_of_bands", "2");
|
||||
|
||||
config->set_property("InputFilter.band1_begin", "0.0");
|
||||
config->set_property("InputFilter.band1_end", "0.45");
|
||||
config->set_property("InputFilter.band2_begin", "0.55");
|
||||
config->set_property("InputFilter.band2_end", "1.0");
|
||||
|
||||
config->set_property("InputFilter.ampl1_begin", "1.0");
|
||||
config->set_property("InputFilter.ampl1_end", "1.0");
|
||||
config->set_property("InputFilter.ampl2_begin", "0.0");
|
||||
config->set_property("InputFilter.ampl2_end", "0.0");
|
||||
|
||||
config->set_property("InputFilter.band1_error", "1.0");
|
||||
config->set_property("InputFilter.band2_error", "1.0");
|
||||
|
||||
config->set_property("InputFilter.filter_type", "bandpass");
|
||||
config->set_property("InputFilter.grid_density", "16");
|
||||
// config->set_property("InputFilter.dump", "true");
|
||||
}
|
||||
|
||||
|
||||
void FirFilterTest::configure_cbyte_cbyte()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "cbyte");
|
||||
config->set_property("InputFilter.output_item_type", "cbyte");
|
||||
}
|
||||
|
||||
|
||||
void FirFilterTest::configure_gr_complex_gr_complex()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
}
|
||||
|
||||
|
||||
void FirFilterTest::configure_cshort_cshort()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "cshort");
|
||||
config->set_property("InputFilter.output_item_type", "cshort");
|
||||
}
|
||||
|
||||
|
||||
void FirFilterTest::configure_cbyte_gr_complex()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "cbyte");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, InstantiateGrComplexGrComplex)
|
||||
{
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_unique<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, InstantiateCshortCshort)
|
||||
{
|
||||
init();
|
||||
configure_cshort_cshort();
|
||||
auto filter = std::make_unique<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, InstantiateCbyteCbyte)
|
||||
{
|
||||
init();
|
||||
configure_cbyte_cbyte();
|
||||
auto filter = std::make_unique<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, InstantiateCbyteGrComplex)
|
||||
{
|
||||
init();
|
||||
configure_cbyte_gr_complex();
|
||||
auto filter = std::make_unique<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Fir filter test");
|
||||
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
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::cout << "Filtered " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, ConnectAndRunGrcomplex)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Fir filter test");
|
||||
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "gr_complex");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
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::cout << "Filtered " << nsamples << " gr_complex samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, ConnectAndRunCshorts)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Fir filter test");
|
||||
|
||||
init();
|
||||
configure_cshort_cshort();
|
||||
auto filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "ishort");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(std::complex<int16_t>);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
interleaved_short_to_complex_short_sptr ishort_to_cshort_ = make_interleaved_short_to_complex_short();
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, ishort_to_cshort_, 0);
|
||||
top_block->connect(ishort_to_cshort_, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
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::cout << "Filtered " << nsamples << " std::complex<int16_t> samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, ConnectAndRunCbytes)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Fir filter test");
|
||||
|
||||
init();
|
||||
configure_cbyte_cbyte();
|
||||
auto filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "ibyte");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(std::complex<int8_t>);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
interleaved_byte_to_complex_byte_sptr ibyte_to_cbyte_ = make_interleaved_byte_to_complex_byte();
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, ibyte_to_cbyte_, 0);
|
||||
top_block->connect(ibyte_to_cbyte_, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
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::cout << "Filtered " << nsamples << " std::complex<int8_t> samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(FirFilterTest, ConnectAndRunCbyteGrcomplex)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Fir filter test");
|
||||
|
||||
init();
|
||||
configure_cbyte_gr_complex();
|
||||
auto filter = std::make_shared<FirFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "ibyte");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
interleaved_byte_to_complex_byte_sptr ibyte_to_cbyte_ = make_interleaved_byte_to_complex_byte();
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, ibyte_to_cbyte_, 0);
|
||||
top_block->connect(ibyte_to_cbyte_, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
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::cout << "Filtered " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*!
|
||||
* \file notch_filter_lite_test.cc
|
||||
* \brief Implements Unit Test for the NotchFilterLite class.
|
||||
* \author Antonio Ramos, 2017. antonio.ramos(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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "notch_filter_lite.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
DEFINE_int32(notch_filter_lite_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
ABSL_FLAG(int32_t, notch_filter_lite_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#endif
|
||||
|
||||
|
||||
class NotchFilterLiteTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
NotchFilterLiteTest() : item_size(sizeof(gr_complex)),
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
nsamples(FLAGS_notch_filter_lite_test_nsamples)
|
||||
#else
|
||||
nsamples(absl::GetFlag(FLAGS_notch_filter_lite_test_nsamples))
|
||||
#endif
|
||||
{
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
}
|
||||
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
void init();
|
||||
void configure_gr_complex_gr_complex();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
pmt::pmt_t message;
|
||||
std::thread ch_thread;
|
||||
size_t item_size;
|
||||
int nsamples;
|
||||
bool stop{false};
|
||||
};
|
||||
|
||||
|
||||
void NotchFilterLiteTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&NotchFilterLiteTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterLiteTest::wait_message()
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
queue->wait_and_pop(message);
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterLiteTest::process_message()
|
||||
{
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterLiteTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterLiteTest::init()
|
||||
{
|
||||
config->set_property("InputFilter.pfa", "0.01");
|
||||
config->set_property("InputFilter.p_c_factor", "0.9");
|
||||
config->set_property("InputFilter.length", "32");
|
||||
config->set_property("InputFilter.segments_est", "12500");
|
||||
config->set_property("InputFilter.segments_reset", "5000000");
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterLiteTest::configure_gr_complex_gr_complex()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterLiteTest, InstantiateGrComplexGrComplex)
|
||||
{
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_unique<NotchFilterLite>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterLiteTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Notch filter lite test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<NotchFilterLite>(config.get(), "InputFilter", 1, 1);
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterLiteTest, ConnectAndRunGrcomplex)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Notch filter lite test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<NotchFilterLite>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "gr_complex");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " gr_complex samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*!
|
||||
* \file notch_filter_test.cc
|
||||
* \brief Implements Unit Test for the NotchFilter class.
|
||||
* \author Antonio Ramos, 2017. antonio.ramos(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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "notch_filter.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
DEFINE_int32(notch_filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
ABSL_FLAG(int32_t, notch_filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#endif
|
||||
|
||||
class NotchFilterTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
NotchFilterTest() : item_size(sizeof(gr_complex)),
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
nsamples(FLAGS_notch_filter_test_nsamples)
|
||||
#else
|
||||
nsamples(absl::GetFlag(FLAGS_notch_filter_test_nsamples))
|
||||
#endif
|
||||
{
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
}
|
||||
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
void init();
|
||||
void configure_gr_complex_gr_complex();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
std::thread ch_thread;
|
||||
pmt::pmt_t message;
|
||||
size_t item_size;
|
||||
int nsamples;
|
||||
bool stop{false};
|
||||
};
|
||||
|
||||
|
||||
void NotchFilterTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&NotchFilterTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterTest::wait_message()
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
queue->wait_and_pop(message);
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterTest::process_message()
|
||||
{
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterTest::init()
|
||||
{
|
||||
config->set_property("InputFilter.pfa", "0.01");
|
||||
config->set_property("InputFilter.p_c_factor", "0.9");
|
||||
config->set_property("InputFilter.length", "32");
|
||||
config->set_property("InputFilter.segments_est", "12500");
|
||||
config->set_property("InputFilter.segments_reset", "5000000");
|
||||
}
|
||||
|
||||
|
||||
void NotchFilterTest::configure_gr_complex_gr_complex()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterTest, InstantiateGrComplexGrComplex)
|
||||
{
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_unique<NotchFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Notch filter test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<NotchFilter>(config.get(), "InputFilter", 1, 1);
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NotchFilterTest, ConnectAndRunGrcomplex)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Notch filter test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<NotchFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "gr_complex");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " gr_complex samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*!
|
||||
* \file pulse_blanking_filter_test.cc
|
||||
* \brief Implements Unit Test for the PulseBlankingFilter class.
|
||||
* \author Antonio Ramos, 2017. antonio.ramos(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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pulse_blanking_filter.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
DEFINE_int32(pb_filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
ABSL_FLAG(int32_t, pb_filter_test_nsamples, 1000000, "Number of samples to filter in the tests (max: 2147483647)");
|
||||
#endif
|
||||
|
||||
class PulseBlankingFilterTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
PulseBlankingFilterTest() : item_size(sizeof(gr_complex)),
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
nsamples(FLAGS_pb_filter_test_nsamples)
|
||||
#else
|
||||
nsamples(absl::GetFlag(FLAGS_pb_filter_test_nsamples))
|
||||
#endif
|
||||
{
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
}
|
||||
|
||||
void start_queue();
|
||||
void wait_message();
|
||||
void process_message();
|
||||
void stop_queue();
|
||||
void init();
|
||||
void configure_gr_complex_gr_complex();
|
||||
|
||||
std::thread ch_thread;
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
gr::top_block_sptr top_block;
|
||||
pmt::pmt_t message;
|
||||
size_t item_size;
|
||||
int nsamples;
|
||||
bool stop{false};
|
||||
};
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::start_queue()
|
||||
{
|
||||
stop = false;
|
||||
ch_thread = std::thread(&PulseBlankingFilterTest::wait_message, this);
|
||||
}
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::wait_message()
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
queue->wait_and_pop(message);
|
||||
process_message();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::process_message()
|
||||
{
|
||||
stop_queue();
|
||||
top_block->stop();
|
||||
}
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::stop_queue()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::init()
|
||||
{
|
||||
config->set_property("InputFilter.pfa", "0.04");
|
||||
config->set_property("InputFilter.length", "32");
|
||||
config->set_property("InputFilter.segments_est", "12500");
|
||||
config->set_property("InputFilter.segments_reset", "5000000");
|
||||
}
|
||||
|
||||
|
||||
void PulseBlankingFilterTest::configure_gr_complex_gr_complex()
|
||||
{
|
||||
config->set_property("InputFilter.input_item_type", "gr_complex");
|
||||
config->set_property("InputFilter.output_item_type", "gr_complex");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PulseBlankingFilterTest, InstantiateGrComplexGrComplex)
|
||||
{
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_unique<PulseBlankingFilter>(config.get(), "InputFilter", 1, 1);
|
||||
int res = 0;
|
||||
if (filter)
|
||||
{
|
||||
res = 1;
|
||||
}
|
||||
ASSERT_EQ(1, res);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PulseBlankingFilterTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 4000000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Pulse Blanking filter test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<PulseBlankingFilter>(config.get(), "InputFilter", 1, 1);
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PulseBlankingFilterTest, ConnectAndRunGrcomplex)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
top_block = gr::make_top_block("Pulse Blanking filter test");
|
||||
init();
|
||||
configure_gr_complex_gr_complex();
|
||||
auto filter = std::make_shared<PulseBlankingFilter>(config.get(), "InputFilter", 1, 1);
|
||||
auto config2 = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config2->set_property("Test_Source.samples", std::to_string(nsamples));
|
||||
config2->set_property("Test_Source.sampling_frequency", "4000000");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config2->set_property("Test_Source.filename", std::move(filename));
|
||||
config2->set_property("Test_Source.item_type", "gr_complex");
|
||||
config2->set_property("Test_Source.repeat", "true");
|
||||
|
||||
item_size = sizeof(gr_complex);
|
||||
ASSERT_NO_THROW({
|
||||
filter->connect(top_block);
|
||||
|
||||
auto source = std::make_shared<FileSignalSource>(config2.get(), "Test_Source", 0, 1, queue.get());
|
||||
source->connect(top_block);
|
||||
|
||||
auto null_sink = gr::blocks::null_sink::make(item_size);
|
||||
|
||||
top_block->connect(source->get_right_block(), 0, filter->get_left_block(), 0);
|
||||
top_block->connect(filter->get_right_block(), 0, null_sink, 0);
|
||||
}) << "Failure connecting the top_block.";
|
||||
start_queue();
|
||||
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.";
|
||||
ch_thread.join();
|
||||
std::cout << "Filtered " << nsamples << " gr_complex samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
108
tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt
Normal file
108
tests/unit-tests/signal-processing-blocks/libs/CMakeLists.txt
Normal file
@@ -0,0 +1,108 @@
|
||||
# GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
# This file is part of GNSS-SDR.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2010-2020 C. Fernandez-Prades cfernandez(at)cttc.es
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
|
||||
set(SIGNAL_PROCESSING_TESTING_LIB_SOURCES
|
||||
acquisition_dump_reader.cc
|
||||
acquisition_msg_rx.cc
|
||||
tracking_dump_reader.cc
|
||||
tlm_dump_reader.cc
|
||||
observables_dump_reader.cc
|
||||
tracking_true_obs_reader.cc
|
||||
true_observables_reader.cc
|
||||
)
|
||||
|
||||
file(GLOB SIGNAL_PROCESSING_TESTING_LIB_HEADERS "*.h")
|
||||
list(SORT SIGNAL_PROCESSING_TESTING_LIB_HEADERS)
|
||||
|
||||
if(USE_CMAKE_TARGET_SOURCES)
|
||||
add_library(signal_processing_testing_lib STATIC)
|
||||
target_sources(signal_processing_testing_lib
|
||||
PRIVATE
|
||||
${SIGNAL_PROCESSING_TESTING_LIB_SOURCES}
|
||||
PUBLIC
|
||||
${SIGNAL_PROCESSING_TESTING_LIB_HEADERS}
|
||||
)
|
||||
else()
|
||||
source_group(Headers FILES ${SIGNAL_PROCESSING_TESTING_LIB_HEADERS})
|
||||
add_library(signal_processing_testing_lib
|
||||
${SIGNAL_PROCESSING_TESTING_LIB_SOURCES}
|
||||
${SIGNAL_PROCESSING_TESTING_LIB_HEADERS}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(signal_processing_testing_lib
|
||||
PUBLIC
|
||||
Armadillo::armadillo
|
||||
Gnuradio::runtime
|
||||
Gnuradio::pmt
|
||||
PRIVATE
|
||||
Boost::headers
|
||||
Matio::matio
|
||||
)
|
||||
|
||||
if(ENABLE_GLOG_AND_GFLAGS)
|
||||
target_link_libraries(signal_processing_testing_lib PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(signal_processing_testing_lib PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(signal_processing_testing_lib PRIVATE absl::flags absl::log)
|
||||
endif()
|
||||
|
||||
target_include_directories(signal_processing_testing_lib
|
||||
PUBLIC
|
||||
${GNSSSDR_SOURCE_DIR}/src/core/interfaces
|
||||
INTERFACE
|
||||
${GNSSSDR_SOURCE_DIR}/tests/common-files
|
||||
)
|
||||
|
||||
if(USE_GENERIC_LAMBDAS)
|
||||
set(has_generic_lambdas HAS_GENERIC_LAMBDA=1)
|
||||
set(no_has_generic_lambdas HAS_GENERIC_LAMBDA=0)
|
||||
target_compile_definitions(signal_processing_testing_lib
|
||||
PRIVATE
|
||||
"$<$<COMPILE_FEATURES:cxx_generic_lambdas>:${has_generic_lambdas}>"
|
||||
"$<$<NOT:$<COMPILE_FEATURES:cxx_generic_lambdas>>:${no_has_generic_lambdas}>"
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(signal_processing_testing_lib
|
||||
PRIVATE
|
||||
-DHAS_GENERIC_LAMBDA=0
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_BOOST_BIND_PLACEHOLDERS)
|
||||
target_compile_definitions(signal_processing_testing_lib
|
||||
PRIVATE
|
||||
-DUSE_BOOST_BIND_PLACEHOLDERS=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(GNURADIO_USES_STD_POINTERS)
|
||||
target_compile_definitions(signal_processing_testing_lib
|
||||
PUBLIC -DGNURADIO_USES_STD_POINTERS=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(PMT_USES_BOOST_ANY)
|
||||
target_compile_definitions(signal_processing_testing_lib
|
||||
PRIVATE
|
||||
-DPMT_USES_BOOST_ANY=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_CLANG_TIDY)
|
||||
if(CLANG_TIDY_EXE)
|
||||
set_target_properties(signal_processing_testing_lib
|
||||
PROPERTIES
|
||||
CXX_CLANG_TIDY "${DO_CLANG_TIDY}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_property(TARGET signal_processing_testing_lib
|
||||
APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
)
|
||||
@@ -0,0 +1,306 @@
|
||||
/*!
|
||||
* \file acquisition_dump_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \authors Carles Fernandez-Prades, 2017. cfernandez(at)cttc.es
|
||||
* Antonio Ramos, 2018. antonio.ramos(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 "acquisition_dump_reader.h"
|
||||
#include <matio.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool Acquisition_Dump_Reader::read_binary_acq()
|
||||
{
|
||||
mat_t* matfile = Mat_Open(d_dump_filename.c_str(), MAT_ACC_RDONLY);
|
||||
if (matfile == nullptr)
|
||||
{
|
||||
std::cout << "Unreachable Acquisition dump file " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
matvar_t* var_ = Mat_VarRead(matfile, "acq_grid");
|
||||
if (var_ == nullptr)
|
||||
{
|
||||
std::cout << "Unreachable grid variable in Acquisition dump file.\n";
|
||||
Mat_Close(matfile);
|
||||
return false;
|
||||
}
|
||||
if (var_->rank != 2)
|
||||
{
|
||||
std::cout << "Invalid Acquisition dump file: rank error\n";
|
||||
Mat_VarFree(var_);
|
||||
Mat_Close(matfile);
|
||||
return false;
|
||||
}
|
||||
if ((var_->dims[0] != d_samples_per_code) or (var_->dims[1] != d_num_doppler_bins))
|
||||
{
|
||||
std::cout << "Invalid Acquisition dump file: dimension matrix error\n";
|
||||
if (var_->dims[0] != d_samples_per_code)
|
||||
{
|
||||
std::cout << "Expected " << d_samples_per_code << " samples per code. Obtained " << var_->dims[0] << '\n';
|
||||
}
|
||||
if (var_->dims[1] != d_num_doppler_bins)
|
||||
{
|
||||
std::cout << "Expected " << d_num_doppler_bins << " Doppler bins. Obtained " << var_->dims[1] << '\n';
|
||||
}
|
||||
Mat_VarFree(var_);
|
||||
Mat_Close(matfile);
|
||||
return false;
|
||||
}
|
||||
if (var_->data_type != MAT_T_SINGLE)
|
||||
{
|
||||
std::cout << "Invalid Acquisition dump file: data type error\n";
|
||||
Mat_VarFree(var_);
|
||||
Mat_Close(matfile);
|
||||
return false;
|
||||
}
|
||||
matvar_t* var2_ = Mat_VarRead(matfile, "doppler_max");
|
||||
d_doppler_max = *static_cast<unsigned int*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "doppler_step");
|
||||
d_doppler_step = *static_cast<unsigned int*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "input_power");
|
||||
input_power = *static_cast<float*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "acq_doppler_hz");
|
||||
acq_doppler_hz = *static_cast<float*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "acq_delay_samples");
|
||||
acq_delay_samples = *static_cast<float*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "test_statistic");
|
||||
test_statistic = *static_cast<float*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "threshold");
|
||||
threshold = *static_cast<float*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "sample_counter");
|
||||
sample_counter = *static_cast<uint64_t*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "d_positive_acq");
|
||||
positive_acq = *static_cast<int*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "num_dwells");
|
||||
num_dwells = *static_cast<int*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
var2_ = Mat_VarRead(matfile, "PRN");
|
||||
PRN = *static_cast<int*>(var2_->data);
|
||||
Mat_VarFree(var2_);
|
||||
|
||||
std::vector<std::vector<float> >::iterator it1;
|
||||
std::vector<float>::iterator it2;
|
||||
auto* aux = static_cast<float*>(var_->data);
|
||||
int k = 0;
|
||||
float normalization_factor = std::pow(d_samples_per_code, 4) * input_power;
|
||||
for (it1 = mag.begin(); it1 != mag.end(); it1++)
|
||||
{
|
||||
for (it2 = it1->begin(); it2 != it1->end(); it2++)
|
||||
{
|
||||
*it2 = static_cast<float>(aux[k]) / normalization_factor;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
Mat_VarFree(var_);
|
||||
Mat_Close(matfile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Acquisition_Dump_Reader::Acquisition_Dump_Reader(const std::string& basename,
|
||||
int channel,
|
||||
int execution)
|
||||
{
|
||||
unsigned int sat_ = 0;
|
||||
unsigned int doppler_max_ = 0;
|
||||
unsigned int doppler_step_ = 0;
|
||||
unsigned int samples_per_code_ = 0;
|
||||
|
||||
mat_t* matfile = Mat_Open(d_dump_filename.c_str(), MAT_ACC_RDONLY);
|
||||
if (matfile != nullptr)
|
||||
{
|
||||
matvar_t* var_ = Mat_VarRead(matfile, "doppler_max");
|
||||
doppler_max_ = *static_cast<unsigned int*>(var_->data);
|
||||
Mat_VarFree(var_);
|
||||
|
||||
var_ = Mat_VarRead(matfile, "doppler_step");
|
||||
doppler_step_ = *static_cast<unsigned int*>(var_->data);
|
||||
Mat_VarFree(var_);
|
||||
|
||||
var_ = Mat_VarRead(matfile, "PRN");
|
||||
sat_ = *static_cast<int*>(var_->data);
|
||||
Mat_VarFree(var_);
|
||||
|
||||
var_ = Mat_VarRead(matfile, "grid");
|
||||
samples_per_code_ = var_->dims[0];
|
||||
Mat_VarFree(var_);
|
||||
|
||||
Mat_Close(matfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unreachable Acquisition dump file " << d_dump_filename << '\n';
|
||||
}
|
||||
acq_doppler_hz = 0.0;
|
||||
acq_delay_samples = 0.0;
|
||||
test_statistic = 0.0;
|
||||
input_power = 0.0;
|
||||
threshold = 0.0;
|
||||
positive_acq = 0;
|
||||
sample_counter = 0;
|
||||
PRN = 0;
|
||||
d_sat = 0;
|
||||
d_doppler_max = doppler_max_;
|
||||
d_doppler_step = doppler_step_;
|
||||
d_samples_per_code = samples_per_code_;
|
||||
d_num_doppler_bins = 0;
|
||||
num_dwells = 0;
|
||||
|
||||
*this = Acquisition_Dump_Reader(basename,
|
||||
sat_,
|
||||
doppler_max_,
|
||||
doppler_step_,
|
||||
samples_per_code_,
|
||||
channel,
|
||||
execution);
|
||||
}
|
||||
|
||||
|
||||
Acquisition_Dump_Reader::Acquisition_Dump_Reader(const std::string& basename,
|
||||
unsigned int sat,
|
||||
unsigned int doppler_max,
|
||||
unsigned int doppler_step,
|
||||
unsigned int samples_per_code,
|
||||
int channel,
|
||||
int execution)
|
||||
: d_basename(basename),
|
||||
d_sat(sat),
|
||||
d_doppler_max(doppler_max),
|
||||
d_doppler_step(doppler_step),
|
||||
d_samples_per_code(samples_per_code)
|
||||
{
|
||||
if (d_doppler_step == 0)
|
||||
{
|
||||
d_doppler_step = 1;
|
||||
}
|
||||
d_num_doppler_bins = static_cast<unsigned int>(ceil(static_cast<double>(static_cast<int>(d_doppler_max) - static_cast<int>(-d_doppler_max)) / static_cast<double>(d_doppler_step)));
|
||||
std::vector<std::vector<float> > mag_aux(d_num_doppler_bins, std::vector<float>(d_samples_per_code));
|
||||
mag = std::move(mag_aux);
|
||||
d_dump_filename = d_basename + "_ch_" + std::to_string(channel) + "_" + std::to_string(execution) + "_sat_" + std::to_string(d_sat) + ".mat";
|
||||
for (unsigned int doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++)
|
||||
{
|
||||
doppler.push_back(-static_cast<int>(d_doppler_max) + d_doppler_step * doppler_index);
|
||||
}
|
||||
for (unsigned int k = 0; k < d_samples_per_code; k++)
|
||||
{
|
||||
samples.push_back(k);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Copy assignment operator
|
||||
Acquisition_Dump_Reader& Acquisition_Dump_Reader::operator=(const Acquisition_Dump_Reader& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
doppler = other.doppler;
|
||||
samples = other.samples;
|
||||
mag = other.mag;
|
||||
acq_doppler_hz = other.acq_doppler_hz;
|
||||
acq_delay_samples = other.acq_delay_samples;
|
||||
test_statistic = other.test_statistic;
|
||||
input_power = other.input_power;
|
||||
threshold = other.threshold;
|
||||
positive_acq = other.positive_acq;
|
||||
PRN = other.PRN;
|
||||
num_dwells = other.num_dwells;
|
||||
sample_counter = other.sample_counter;
|
||||
d_basename = other.d_basename;
|
||||
d_dump_filename = other.d_dump_filename;
|
||||
d_sat = other.d_sat;
|
||||
d_doppler_max = other.d_doppler_max;
|
||||
d_doppler_step = other.d_doppler_step;
|
||||
d_samples_per_code = other.d_samples_per_code;
|
||||
d_num_doppler_bins = other.d_num_doppler_bins;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Move constructor
|
||||
Acquisition_Dump_Reader::Acquisition_Dump_Reader(Acquisition_Dump_Reader&& other) noexcept
|
||||
: doppler(std::move(other.doppler)),
|
||||
samples(std::move(other.samples)),
|
||||
mag(std::move(other.mag)),
|
||||
acq_doppler_hz(other.acq_doppler_hz),
|
||||
acq_delay_samples(other.acq_delay_samples),
|
||||
test_statistic(other.test_statistic),
|
||||
input_power(other.input_power),
|
||||
threshold(other.threshold),
|
||||
positive_acq(other.positive_acq),
|
||||
PRN(other.PRN),
|
||||
num_dwells(other.num_dwells),
|
||||
sample_counter(other.sample_counter),
|
||||
d_basename(std::move(other.d_basename)),
|
||||
d_dump_filename(std::move(other.d_dump_filename)),
|
||||
d_sat(other.d_sat),
|
||||
d_doppler_max(other.d_doppler_max),
|
||||
d_doppler_step(other.d_doppler_step),
|
||||
d_samples_per_code(other.d_samples_per_code),
|
||||
d_num_doppler_bins(other.d_num_doppler_bins)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Move assignment operator
|
||||
Acquisition_Dump_Reader& Acquisition_Dump_Reader::operator=(Acquisition_Dump_Reader&& other) noexcept
|
||||
{
|
||||
if (this != &other) // Check for self-assignment
|
||||
{
|
||||
// Move member variables from the other object to this object
|
||||
d_basename = std::move(other.d_basename);
|
||||
d_dump_filename = std::move(other.d_dump_filename);
|
||||
d_sat = other.d_sat;
|
||||
d_doppler_max = other.d_doppler_max;
|
||||
d_doppler_step = other.d_doppler_step;
|
||||
d_samples_per_code = other.d_samples_per_code;
|
||||
d_num_doppler_bins = other.d_num_doppler_bins;
|
||||
doppler = std::move(other.doppler);
|
||||
samples = std::move(other.samples);
|
||||
mag = std::move(other.mag);
|
||||
acq_doppler_hz = other.acq_doppler_hz;
|
||||
acq_delay_samples = other.acq_delay_samples;
|
||||
test_statistic = other.test_statistic;
|
||||
input_power = other.input_power;
|
||||
threshold = other.threshold;
|
||||
positive_acq = other.positive_acq;
|
||||
PRN = other.PRN;
|
||||
num_dwells = other.num_dwells;
|
||||
sample_counter = other.sample_counter;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* \file acquisition_dump_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \authors Carles Fernandez-Prades, 2017. cfernandez(at)cttc.es
|
||||
* Antonio Ramos, 2018. antonio.ramos(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_ACQUISITION_DUMP_READER_H
|
||||
#define GNSS_SDR_ACQUISITION_DUMP_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Acquisition_Dump_Reader
|
||||
{
|
||||
public:
|
||||
Acquisition_Dump_Reader(const std::string& basename,
|
||||
unsigned int sat,
|
||||
unsigned int doppler_max,
|
||||
unsigned int doppler_step,
|
||||
unsigned int samples_per_code,
|
||||
int channel = 0,
|
||||
int execution = 1);
|
||||
|
||||
Acquisition_Dump_Reader(const std::string& basename,
|
||||
int channel = 0,
|
||||
int execution = 1);
|
||||
|
||||
Acquisition_Dump_Reader(const Acquisition_Dump_Reader& other) = default; //!< Copy constructor
|
||||
Acquisition_Dump_Reader& operator=(const Acquisition_Dump_Reader& other); //!< Copy assignment operator
|
||||
Acquisition_Dump_Reader(Acquisition_Dump_Reader&& other) noexcept; //!< Move constructor
|
||||
Acquisition_Dump_Reader& operator=(Acquisition_Dump_Reader&& other) noexcept; //!< Move assignment operator
|
||||
|
||||
bool read_binary_acq();
|
||||
|
||||
std::vector<int> doppler;
|
||||
std::vector<unsigned int> samples;
|
||||
std::vector<std::vector<float> > mag;
|
||||
float acq_doppler_hz{};
|
||||
float acq_delay_samples{};
|
||||
float test_statistic{};
|
||||
float input_power{};
|
||||
float threshold{};
|
||||
int positive_acq{};
|
||||
unsigned int PRN{};
|
||||
unsigned int num_dwells{};
|
||||
uint64_t sample_counter{};
|
||||
|
||||
private:
|
||||
std::string d_basename;
|
||||
std::string d_dump_filename;
|
||||
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{};
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_ACQUISITION_DUMP_READER_H
|
||||
@@ -0,0 +1,84 @@
|
||||
/*!
|
||||
* \file acquisition_msg_rx.cc
|
||||
* \brief This is a helper class to catch the asynchronous messages
|
||||
* emitted by an acquisition block.
|
||||
* \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.cat
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "acquisition_msg_rx.h"
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
#include <boost/any.hpp>
|
||||
namespace wht = boost;
|
||||
#else
|
||||
#include <any>
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
Acquisition_msg_rx_sptr Acquisition_msg_rx_make()
|
||||
{
|
||||
return Acquisition_msg_rx_sptr(new Acquisition_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void Acquisition_msg_rx::msg_handler_channel_events(const pmt::pmt_t& msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(msg);
|
||||
rx_message = message;
|
||||
top_block->stop(); // stop the flowgraph
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_acquisition Bad cast!\n";
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Acquisition_msg_rx::Acquisition_msg_rx()
|
||||
: gr::block("Acquisition_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0)),
|
||||
rx_message(0)
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&Acquisition_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&Acquisition_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Acquisition_msg_rx::~Acquisition_msg_rx() = default;
|
||||
@@ -0,0 +1,50 @@
|
||||
/*!
|
||||
* \file acquisition_msg_rx.h
|
||||
* \brief This is a helper class to catch the asynchronous messages
|
||||
* emitted by an acquisition block.
|
||||
* \author Carles Fernandez-Prades, 2018. cfernandez(at)cttc.cat
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_ACQUISITION_MSG_RX_H
|
||||
#define GNSS_SDR_ACQUISITION_MSG_RX_H
|
||||
|
||||
#include "gnss_block_interface.h"
|
||||
#include <gnuradio/block.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <pmt/pmt.h>
|
||||
|
||||
|
||||
// ######## GNURADIO ACQUISITION BLOCK MESSAGE RECEVER #########
|
||||
class Acquisition_msg_rx;
|
||||
|
||||
using Acquisition_msg_rx_sptr = gnss_shared_ptr<Acquisition_msg_rx>;
|
||||
|
||||
Acquisition_msg_rx_sptr Acquisition_msg_rx_make();
|
||||
|
||||
|
||||
class Acquisition_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend Acquisition_msg_rx_sptr Acquisition_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t& msg);
|
||||
Acquisition_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
gr::top_block_sptr top_block;
|
||||
~Acquisition_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
#endif // GNSS_SDR_ACQUISITION_MSG_RX_H
|
||||
@@ -0,0 +1,250 @@
|
||||
/*!
|
||||
* \file item_type_helpers_test.cc
|
||||
* \brief This file implements unit tests for the item_type_helpers
|
||||
* custom block
|
||||
* \author Cillian O'Driscoll, 2019. cillian.odriscoll (at) gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "item_type_helpers.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
class ItemTypeHelpersTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static constexpr size_t N = 1000;
|
||||
|
||||
public:
|
||||
ItemTypeHelpersTest()
|
||||
{
|
||||
std::random_device r;
|
||||
std::default_random_engine e(r());
|
||||
|
||||
std::uniform_int_distribution<int8_t> udist_int8(-100, 100);
|
||||
std::uniform_int_distribution<int16_t> udist_int16(-100, 100);
|
||||
std::uniform_real_distribution<float> udist_float(-100, 100);
|
||||
|
||||
std::generate(byte_array_in.begin(), byte_array_in.end(), [&udist_int8, &e]() { return udist_int8(e); });
|
||||
|
||||
std::generate(short_array_in.begin(), short_array_in.end(), [&udist_int16, &e]() { return udist_int16(e); });
|
||||
|
||||
std::generate(float_array_in.begin(), float_array_in.end(), [&udist_float, &e]() { return udist_float(e); });
|
||||
}
|
||||
|
||||
std::vector<std::string> valid_item_types = {"byte", "ibyte", "cbyte",
|
||||
"short", "ishort", "cshort", "float", "gr_complex"};
|
||||
|
||||
std::vector<std::string> invalid_item_types = {"i8", "tfgs", "cbite",
|
||||
"shirt", "qshort", "csort", "flat", "igr_complex"};
|
||||
|
||||
std::array<int8_t, 2 * N> byte_array_in;
|
||||
std::array<int8_t, 2 * N> byte_array_out;
|
||||
|
||||
std::array<int16_t, 2 * N> short_array_in;
|
||||
std::array<int16_t, 2 * N> short_array_out;
|
||||
|
||||
std::array<float, 2 * N> float_array_in;
|
||||
std::array<float, 2 * N> float_array_out;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(ItemTypeHelpersTest, CheckValidTypes)
|
||||
{
|
||||
for (auto &valid_type : valid_item_types)
|
||||
{
|
||||
EXPECT_TRUE(item_type_valid(valid_type));
|
||||
}
|
||||
|
||||
for (auto &invalid_type : invalid_item_types)
|
||||
{
|
||||
EXPECT_FALSE(item_type_valid(invalid_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ItemTypeHelpersTest, CheckSizes)
|
||||
{
|
||||
EXPECT_EQ(item_type_size("byte"), 1);
|
||||
EXPECT_EQ(item_type_size("ibyte"), 1);
|
||||
EXPECT_EQ(item_type_size("cbyte"), 2);
|
||||
|
||||
EXPECT_EQ(item_type_size("short"), 2);
|
||||
EXPECT_EQ(item_type_size("ishort"), 2);
|
||||
EXPECT_EQ(item_type_size("cshort"), 4);
|
||||
|
||||
EXPECT_EQ(item_type_size("float"), 4);
|
||||
EXPECT_EQ(item_type_size("gr_complex"), 8);
|
||||
|
||||
for (auto &invalid_type : invalid_item_types)
|
||||
{
|
||||
EXPECT_EQ(item_type_size(invalid_type), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ItemTypeHelpersTest, CheckMakeConverters)
|
||||
{
|
||||
for (auto &input_type : valid_item_types)
|
||||
{
|
||||
for (auto &output_type : valid_item_types)
|
||||
{
|
||||
item_type_converter_t converter = nullptr;
|
||||
|
||||
if (item_type_is_complex(input_type) == item_type_is_complex(output_type))
|
||||
{
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_THROW(converter = make_vector_converter(input_type, output_type), std::runtime_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ItemTypeHelpersTest, CheckConversionsReal)
|
||||
{
|
||||
std::string input_type = "byte";
|
||||
std::string output_type = "byte";
|
||||
item_type_converter_t converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), byte_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_in.begin(), byte_array_in.begin() + N, byte_array_out.begin()));
|
||||
|
||||
input_type = "byte";
|
||||
output_type = "short";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), byte_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), short_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_out.begin(), byte_array_out.begin() + N, byte_array_in.begin()));
|
||||
|
||||
input_type = "byte";
|
||||
output_type = "float";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), byte_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), float_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_out.begin(), byte_array_out.begin() + N, byte_array_in.begin()));
|
||||
|
||||
input_type = "short";
|
||||
output_type = "short";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), short_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(short_array_in.begin(), short_array_in.begin() + N, short_array_out.begin()));
|
||||
|
||||
input_type = "short";
|
||||
output_type = "float";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), short_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), float_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(short_array_out.begin(), short_array_out.begin() + N, short_array_in.begin()));
|
||||
|
||||
input_type = "float";
|
||||
output_type = "float";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), float_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(float_array_in.begin(), float_array_in.begin() + N, float_array_out.begin()));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ItemTypeHelpersTest, CheckConversionsComplex)
|
||||
{
|
||||
std::string input_type = "cbyte";
|
||||
std::string output_type = "cbyte";
|
||||
item_type_converter_t converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), byte_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_in.begin(), byte_array_in.begin() + N, byte_array_out.begin()));
|
||||
|
||||
input_type = "cbyte";
|
||||
output_type = "ibyte";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), byte_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_in.begin(), byte_array_in.begin() + N, byte_array_out.begin()));
|
||||
|
||||
input_type = "cbyte";
|
||||
output_type = "cshort";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), byte_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), short_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_out.begin(), byte_array_out.begin() + N, byte_array_in.begin()));
|
||||
|
||||
input_type = "cbyte";
|
||||
output_type = "ishort";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), byte_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), short_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_out.begin(), byte_array_out.begin() + N, byte_array_in.begin()));
|
||||
|
||||
input_type = "cbyte";
|
||||
output_type = "gr_complex";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), byte_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(byte_array_out.data(), float_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(byte_array_out.begin(), byte_array_out.begin() + N, byte_array_in.begin()));
|
||||
|
||||
input_type = "cshort";
|
||||
output_type = "cshort";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), short_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(short_array_in.begin(), short_array_in.begin() + N, short_array_out.begin()));
|
||||
|
||||
input_type = "cshort";
|
||||
output_type = "ishort";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), short_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(short_array_in.begin(), short_array_in.begin() + N, short_array_out.begin()));
|
||||
|
||||
input_type = "cshort";
|
||||
output_type = "gr_complex";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), short_array_in.data(), N);
|
||||
converter = make_vector_converter(output_type, input_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(short_array_out.data(), float_array_out.data(), N);
|
||||
EXPECT_TRUE(std::equal(short_array_out.begin(), short_array_out.begin() + N, short_array_in.begin()));
|
||||
|
||||
input_type = "gr_complex";
|
||||
output_type = "gr_complex";
|
||||
converter = make_vector_converter(input_type, output_type);
|
||||
EXPECT_NE(converter, nullptr);
|
||||
converter(float_array_out.data(), float_array_in.data(), N);
|
||||
EXPECT_TRUE(std::equal(float_array_in.begin(), float_array_in.begin() + N, float_array_out.begin()));
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*!
|
||||
* \file observables_dump_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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 "observables_dump_reader.h"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool Observables_Dump_Reader::read_binary_obs()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < n_channels; i++)
|
||||
{
|
||||
d_dump_file.read(reinterpret_cast<char *>(&RX_time[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&TOW_at_current_symbol_s[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&Carrier_Doppler_hz[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&Acc_carrier_phase_hz[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&Pseudorange_m[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&PRN[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&valid[i]), sizeof(double));
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Observables_Dump_Reader::restart()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_file.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t Observables_Dump_Reader::num_epochs()
|
||||
{
|
||||
std::ifstream::pos_type size;
|
||||
int number_of_vars_in_epoch = n_channels * 7;
|
||||
int epoch_size_bytes = sizeof(double) * number_of_vars_in_epoch;
|
||||
std::ifstream tmpfile(d_dump_filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (tmpfile.is_open())
|
||||
{
|
||||
size = tmpfile.tellg();
|
||||
int64_t nepoch = size / epoch_size_bytes;
|
||||
return nepoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Observables_Dump_Reader::open_obs_file(std::string out_file)
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_filename = std::move(out_file);
|
||||
d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
d_dump_file.open(d_dump_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
return true;
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cout << "Problem opening Observables dump Log file: " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Observables_Dump_Reader::close_obs_file()
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Observables_Dump_Reader::Observables_Dump_Reader(int n_channels_)
|
||||
: n_channels(n_channels_)
|
||||
{
|
||||
RX_time = std::vector<double>(n_channels);
|
||||
TOW_at_current_symbol_s = std::vector<double>(n_channels);
|
||||
Carrier_Doppler_hz = std::vector<double>(n_channels);
|
||||
Acc_carrier_phase_hz = std::vector<double>(n_channels);
|
||||
Pseudorange_m = std::vector<double>(n_channels);
|
||||
PRN = std::vector<double>(n_channels);
|
||||
valid = std::vector<double>(n_channels);
|
||||
}
|
||||
|
||||
|
||||
Observables_Dump_Reader::~Observables_Dump_Reader()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cerr << "Problem closing Observables dump Log file: " << d_dump_filename << '\n';
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*!
|
||||
* \file observables_dump_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_OBSERVABLES_DUMP_READER_H
|
||||
#define GNSS_SDR_OBSERVABLES_DUMP_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Observables_Dump_Reader
|
||||
{
|
||||
public:
|
||||
explicit Observables_Dump_Reader(int n_channels);
|
||||
~Observables_Dump_Reader();
|
||||
bool read_binary_obs();
|
||||
bool restart();
|
||||
int64_t num_epochs();
|
||||
bool open_obs_file(std::string out_file);
|
||||
void close_obs_file();
|
||||
|
||||
// dump variables
|
||||
std::vector<double> RX_time;
|
||||
std::vector<double> TOW_at_current_symbol_s;
|
||||
std::vector<double> Carrier_Doppler_hz;
|
||||
std::vector<double> Acc_carrier_phase_hz;
|
||||
std::vector<double> Pseudorange_m;
|
||||
std::vector<double> PRN;
|
||||
std::vector<double> valid;
|
||||
|
||||
private:
|
||||
int n_channels;
|
||||
std::string d_dump_filename;
|
||||
std::ifstream d_dump_file;
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_OBSERVABLES_DUMP_READER_H
|
||||
@@ -0,0 +1,111 @@
|
||||
/*!
|
||||
* \file tlm_dump_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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 "tlm_dump_reader.h"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool Tlm_Dump_Reader::read_binary_obs()
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_file.read(reinterpret_cast<char *>(&TOW_at_current_symbol), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&Tracking_sample_counter), sizeof(uint64_t));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&d_TOW_at_Preamble), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&nav_symbol), sizeof(int32_t));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&prn), sizeof(int32_t));
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Tlm_Dump_Reader::restart()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_file.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t Tlm_Dump_Reader::num_epochs()
|
||||
{
|
||||
std::ifstream::pos_type size;
|
||||
int number_of_double_vars_in_epoch = 2;
|
||||
int number_of_int_vars_in_epoch = 2;
|
||||
int epoch_size_bytes = sizeof(double) * number_of_double_vars_in_epoch + sizeof(uint64_t) + sizeof(int32_t) * number_of_int_vars_in_epoch;
|
||||
std::ifstream tmpfile(d_dump_filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (tmpfile.is_open())
|
||||
{
|
||||
size = tmpfile.tellg();
|
||||
int64_t nepoch = size / epoch_size_bytes;
|
||||
return nepoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Tlm_Dump_Reader::open_obs_file(std::string out_file)
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_filename = std::move(out_file);
|
||||
d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
d_dump_file.open(d_dump_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
std::cout << "TLM dump enabled, Log file: " << d_dump_filename.c_str() << '\n';
|
||||
return true;
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cout << "Problem opening TLM dump Log file: " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Tlm_Dump_Reader::~Tlm_Dump_Reader()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cerr << "Problem closing TLM dump Log file: " << d_dump_filename << '\n';
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*!
|
||||
* \file tlm_dump_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_TLM_DUMP_READER_H
|
||||
#define GNSS_SDR_TLM_DUMP_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Tlm_Dump_Reader
|
||||
{
|
||||
public:
|
||||
~Tlm_Dump_Reader();
|
||||
bool read_binary_obs();
|
||||
bool restart();
|
||||
int64_t num_epochs();
|
||||
bool open_obs_file(std::string out_file);
|
||||
|
||||
// telemetry decoder dump variables
|
||||
double TOW_at_current_symbol;
|
||||
uint64_t Tracking_sample_counter;
|
||||
double d_TOW_at_Preamble;
|
||||
int32_t nav_symbol;
|
||||
int32_t prn;
|
||||
|
||||
private:
|
||||
std::string d_dump_filename;
|
||||
std::ifstream d_dump_file;
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_TLM_DUMP_READER_H
|
||||
@@ -0,0 +1,130 @@
|
||||
/*!
|
||||
* \file tracking_dump_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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 "tracking_dump_reader.h"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool Tracking_Dump_Reader::read_binary_obs()
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_file.read(reinterpret_cast<char *>(&abs_VE), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&abs_E), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&abs_P), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&abs_L), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&abs_VL), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&prompt_I), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&prompt_Q), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&PRN_start_sample_count), sizeof(uint64_t));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&acc_carrier_phase_rad), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carrier_doppler_hz), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carrier_doppler_rate_hz_s), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&code_freq_chips), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&code_freq_rate_chips), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carr_error_hz), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carr_error_filt_hz), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&code_error_chips), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&code_error_filt_chips), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&CN0_SNV_dB_Hz), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carrier_lock_test), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&aux1), sizeof(float));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&aux2), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&PRN), sizeof(unsigned int));
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Tracking_Dump_Reader::restart()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_file.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t Tracking_Dump_Reader::num_epochs()
|
||||
{
|
||||
std::ifstream::pos_type size;
|
||||
int number_of_double_vars = 1;
|
||||
int number_of_float_vars = 19;
|
||||
int epoch_size_bytes = sizeof(uint64_t) + sizeof(double) * number_of_double_vars +
|
||||
sizeof(float) * number_of_float_vars + sizeof(unsigned int);
|
||||
std::ifstream tmpfile(d_dump_filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (tmpfile.is_open())
|
||||
{
|
||||
size = tmpfile.tellg();
|
||||
int64_t nepoch = size / epoch_size_bytes;
|
||||
return nepoch;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Tracking_Dump_Reader::open_obs_file(std::string out_file)
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_filename = std::move(out_file);
|
||||
d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
d_dump_file.open(d_dump_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
return true;
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cout << "Problem opening Tracking dump Log file: " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Tracking_Dump_Reader::~Tracking_Dump_Reader()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cerr << "Problem closing Tracking dump Log file: " << d_dump_filename << '\n';
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*!
|
||||
* \file tracking_dump_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_TRACKING_DUMP_READER_H
|
||||
#define GNSS_SDR_TRACKING_DUMP_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Tracking_Dump_Reader
|
||||
{
|
||||
public:
|
||||
~Tracking_Dump_Reader();
|
||||
bool read_binary_obs();
|
||||
bool restart();
|
||||
int64_t num_epochs();
|
||||
bool open_obs_file(std::string out_file);
|
||||
|
||||
// tracking dump variables
|
||||
// VEPLVL
|
||||
float abs_VE;
|
||||
float abs_E;
|
||||
float abs_P;
|
||||
float abs_L;
|
||||
float abs_VL;
|
||||
// PROMPT I and Q (to analyze navigation symbols)
|
||||
float prompt_I;
|
||||
float prompt_Q;
|
||||
// PRN start sample stamp
|
||||
uint64_t PRN_start_sample_count;
|
||||
|
||||
// accumulated carrier phase
|
||||
float acc_carrier_phase_rad;
|
||||
|
||||
// carrier and code frequency
|
||||
float carrier_doppler_hz;
|
||||
float carrier_doppler_rate_hz_s;
|
||||
float code_freq_chips;
|
||||
float code_freq_rate_chips;
|
||||
|
||||
// PLL commands
|
||||
float carr_error_hz;
|
||||
float carr_error_filt_hz;
|
||||
|
||||
// DLL commands
|
||||
float code_error_chips;
|
||||
float code_error_filt_chips;
|
||||
|
||||
// CN0 and carrier lock test
|
||||
float CN0_SNV_dB_Hz;
|
||||
float carrier_lock_test;
|
||||
|
||||
// AUX vars (for debug purposes)
|
||||
float aux1;
|
||||
double aux2;
|
||||
|
||||
unsigned int PRN;
|
||||
|
||||
private:
|
||||
std::string d_dump_filename;
|
||||
std::ifstream d_dump_file;
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_TRACKING_DUMP_READER_H
|
||||
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* \file tracking_true_obs_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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 "tracking_true_obs_reader.h"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool Tracking_True_Obs_Reader::read_binary_obs()
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_file.read(reinterpret_cast<char *>(&signal_timestamp_s), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&acc_carrier_phase_cycles), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&doppler_l1_hz), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&prn_delay_chips), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&tow), sizeof(double));
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Tracking_True_Obs_Reader::restart()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_file.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t Tracking_True_Obs_Reader::num_epochs()
|
||||
{
|
||||
std::ifstream::pos_type size;
|
||||
int number_of_vars_in_epoch = 5;
|
||||
int epoch_size_bytes = sizeof(double) * number_of_vars_in_epoch;
|
||||
std::ifstream tmpfile(d_dump_filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (tmpfile.is_open())
|
||||
{
|
||||
size = tmpfile.tellg();
|
||||
int64_t nepoch = size / epoch_size_bytes;
|
||||
return nepoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Tracking_True_Obs_Reader::open_obs_file(std::string out_file)
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_filename = std::move(out_file);
|
||||
d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
d_dump_file.open(d_dump_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
return true;
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cout << "Problem opening Tracking dump Log file: " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Tracking_True_Obs_Reader::close_obs_file()
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
Tracking_True_Obs_Reader::~Tracking_True_Obs_Reader()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cerr << "Problem closing Tracking dump Log file: " << d_dump_filename << '\n';
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* \file tracking_true_obs_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_TRACKING_TRUE_OBS_READER_H
|
||||
#define GNSS_SDR_TRACKING_TRUE_OBS_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Tracking_True_Obs_Reader
|
||||
{
|
||||
public:
|
||||
~Tracking_True_Obs_Reader();
|
||||
bool read_binary_obs();
|
||||
bool restart();
|
||||
int64_t num_epochs();
|
||||
bool open_obs_file(std::string out_file);
|
||||
void close_obs_file();
|
||||
bool d_dump;
|
||||
|
||||
double signal_timestamp_s;
|
||||
double acc_carrier_phase_cycles;
|
||||
double doppler_l1_hz;
|
||||
double prn_delay_chips;
|
||||
double tow;
|
||||
|
||||
private:
|
||||
std::string d_dump_filename;
|
||||
std::ifstream d_dump_file;
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_RACKING_TRUE_OBS_READER_H
|
||||
@@ -0,0 +1,115 @@
|
||||
/*!
|
||||
* \file true_observables_reader.cc
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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 "true_observables_reader.h"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
bool True_Observables_Reader::read_binary_obs()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
d_dump_file.read(reinterpret_cast<char *>(&gps_time_sec[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&doppler_l1_hz[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&acc_carrier_phase_l1_cycles[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&dist_m[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&true_dist_m[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&carrier_phase_l1_cycles[i]), sizeof(double));
|
||||
d_dump_file.read(reinterpret_cast<char *>(&prn[i]), sizeof(double));
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool True_Observables_Reader::restart()
|
||||
{
|
||||
if (d_dump_file.is_open())
|
||||
{
|
||||
d_dump_file.clear();
|
||||
d_dump_file.seekg(0, std::ios::beg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t True_Observables_Reader::num_epochs()
|
||||
{
|
||||
std::ifstream::pos_type size;
|
||||
int number_of_vars_in_epoch = 6 * 12;
|
||||
int epoch_size_bytes = sizeof(double) * number_of_vars_in_epoch;
|
||||
std::ifstream tmpfile(d_dump_filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (tmpfile.is_open())
|
||||
{
|
||||
size = tmpfile.tellg();
|
||||
int64_t nepoch = size / epoch_size_bytes;
|
||||
return nepoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool True_Observables_Reader::open_obs_file(std::string out_file)
|
||||
{
|
||||
if (d_dump_file.is_open() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
d_dump_filename = std::move(out_file);
|
||||
d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
d_dump_file.open(d_dump_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
std::cout << "True observables Log file opened: " << d_dump_filename.c_str() << '\n';
|
||||
return true;
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cout << "Problem opening true Observables Log file: " << d_dump_filename << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
True_Observables_Reader::~True_Observables_Reader()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (d_dump_file.is_open() == true)
|
||||
{
|
||||
d_dump_file.close();
|
||||
}
|
||||
}
|
||||
catch (const std::ifstream::failure &e)
|
||||
{
|
||||
std::cerr << "Problem closing true Observables dump Log file: " << d_dump_filename << '\n';
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* \file true_observables_reader.h
|
||||
* \brief Helper file for unit testing
|
||||
* \author Javier Arribas, 2017. jarribas(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
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GNSS_SDR_TRUE_OBSERVABLES_READER_H
|
||||
#define GNSS_SDR_TRUE_OBSERVABLES_READER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class True_Observables_Reader
|
||||
{
|
||||
public:
|
||||
~True_Observables_Reader();
|
||||
bool read_binary_obs();
|
||||
bool restart();
|
||||
int64_t num_epochs();
|
||||
bool open_obs_file(std::string out_file);
|
||||
|
||||
double gps_time_sec[12];
|
||||
double doppler_l1_hz[12];
|
||||
double acc_carrier_phase_l1_cycles[12];
|
||||
double dist_m[12];
|
||||
double true_dist_m[12];
|
||||
double carrier_phase_l1_cycles[12];
|
||||
double prn[12];
|
||||
|
||||
private:
|
||||
std::string d_dump_filename;
|
||||
std::ifstream d_dump_file;
|
||||
};
|
||||
|
||||
#endif // GNSS_SDR_TRUE_OBSERVABLES_READER_H
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
/*!
|
||||
* \file gnss_crypto_test.cc
|
||||
* \brief Tests for the Gnss_Crypto class.
|
||||
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
|
||||
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "gnss_crypto.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
class GnssCryptoTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, VerifyPubKeyImport)
|
||||
{
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
// Input taken from RG 1.3 A7.1
|
||||
// compressed ECDSA P-256 format
|
||||
std::vector<uint8_t> publicKey = {
|
||||
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
|
||||
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
|
||||
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
|
||||
0x79, 0x80, 0xEA};
|
||||
|
||||
ASSERT_FALSE(d_crypto->have_public_key());
|
||||
|
||||
d_crypto->set_public_key(publicKey);
|
||||
|
||||
ASSERT_TRUE(d_crypto->have_public_key());
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, VerifyPublicKeyStorage)
|
||||
{
|
||||
const std::string f1("./osnma_test_file1.pem");
|
||||
const std::string f2("./osnma_test_file2.pem");
|
||||
const std::string f3("./osnma_test_file3.pem");
|
||||
|
||||
// Input taken from RG 1.3 A7.1
|
||||
// compressed ECDSA P-256 format
|
||||
std::vector<uint8_t> publicKey = {
|
||||
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
|
||||
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
|
||||
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
|
||||
0x79, 0x80, 0xEA};
|
||||
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
ASSERT_FALSE(d_crypto->have_public_key());
|
||||
ASSERT_TRUE(d_crypto->get_public_key_type() == "Unknown");
|
||||
d_crypto->set_public_key(publicKey);
|
||||
ASSERT_TRUE(d_crypto->have_public_key());
|
||||
ASSERT_TRUE(d_crypto->store_public_key(f1));
|
||||
|
||||
auto d_crypto2 = std::make_unique<Gnss_Crypto>(f1, "");
|
||||
ASSERT_TRUE(d_crypto2->have_public_key());
|
||||
ASSERT_TRUE(d_crypto2->get_public_key_type() == "ECDSA P-256");
|
||||
ASSERT_TRUE(d_crypto2->store_public_key(f2));
|
||||
|
||||
std::ifstream t(f1);
|
||||
std::string content_file((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
|
||||
|
||||
std::ifstream t2(f2);
|
||||
std::string content_file2((std::istreambuf_iterator<char>(t2)), std::istreambuf_iterator<char>());
|
||||
|
||||
ASSERT_EQ(content_file, content_file2);
|
||||
|
||||
// P-521 Public key in compressed X format
|
||||
std::vector<uint8_t> publicKey_P521 = {
|
||||
0x03, 0x00, 0x28, 0x35, 0xBB, 0xE9, 0x24, 0x59, 0x4E, 0xF0,
|
||||
0xE3, 0xA2, 0xDB, 0xC0, 0x49, 0x30, 0x60, 0x7C, 0x61, 0x90,
|
||||
0xE4, 0x03, 0xE0, 0xC7, 0xB8, 0xC2, 0x62, 0x37, 0xF7, 0x58,
|
||||
0x56, 0xBE, 0x63, 0x5C, 0x97, 0xF7, 0x53, 0x64, 0x7E, 0xE1,
|
||||
0x0C, 0x07, 0xD3, 0x97, 0x8D, 0x58, 0x46, 0xFD, 0x6E, 0x06,
|
||||
0x44, 0x01, 0xA7, 0xAA, 0xC4, 0x95, 0x13, 0x5D, 0xC9, 0x77,
|
||||
0x26, 0xE9, 0xF8, 0x72, 0x0C, 0xD3, 0x88};
|
||||
|
||||
d_crypto->set_public_key(publicKey_P521);
|
||||
ASSERT_TRUE(d_crypto->have_public_key());
|
||||
ASSERT_TRUE(d_crypto->get_public_key_type() == "ECDSA P-521");
|
||||
ASSERT_TRUE(d_crypto->store_public_key(f3));
|
||||
|
||||
auto d_crypto3 = std::make_unique<Gnss_Crypto>(f3, "");
|
||||
ASSERT_TRUE(d_crypto3->have_public_key());
|
||||
ASSERT_TRUE(d_crypto3->get_public_key_type() == "ECDSA P-521");
|
||||
|
||||
std::vector<uint8_t> wrong_publicKey = {
|
||||
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
|
||||
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
|
||||
0x63, 0xFF, 0xA0};
|
||||
|
||||
auto d_crypto4 = std::make_unique<Gnss_Crypto>();
|
||||
d_crypto4->set_public_key(wrong_publicKey);
|
||||
ASSERT_FALSE(d_crypto4->have_public_key());
|
||||
ASSERT_TRUE(d_crypto4->get_public_key_type() == "Unknown");
|
||||
|
||||
errorlib::error_code ec;
|
||||
ASSERT_TRUE(fs::remove(fs::path(f1), ec));
|
||||
ASSERT_TRUE(fs::remove(fs::path(f2), ec));
|
||||
ASSERT_TRUE(fs::remove(fs::path(f3), ec));
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, TestComputeSHA_256)
|
||||
{
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
std::vector<uint8_t> message{
|
||||
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0x18, 0x94, 0xA1, 0x9C, 0x85, 0xBA, 0x15, 0x3A, 0xCB, 0xF7,
|
||||
0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04,
|
||||
0xB2, 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7,
|
||||
0xC4, 0x8F};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_SHA_256(message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, TestComputeSHA3_256)
|
||||
{
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
std::vector<uint8_t> message{
|
||||
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0xCC, 0xB8, 0xF9, 0x23, 0x5F, 0x4A, 0x93, 0x2C, 0xA0, 0xAB,
|
||||
0xBB, 0x2C, 0x24, 0x36, 0x72, 0x5E, 0x2E, 0x8D, 0xC7, 0x5B,
|
||||
0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6,
|
||||
0x3B, 0x3F};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_SHA3_256(message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
// Unit test for computeHMAC_SHA_256 function.
|
||||
TEST(GnssCryptoTest, TestComputeHMACSHA256)
|
||||
{
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
std::vector<uint8_t> key = {
|
||||
0x24, 0x24, 0x3B, 0x76, 0xF9, 0x14, 0xB1, 0xA7,
|
||||
0x7D, 0x48, 0xE7, 0xF1, 0x48, 0x0C, 0xC2, 0x98,
|
||||
0xEB, 0x62, 0x3E, 0x95, 0x6B, 0x2B, 0xCE, 0xA3,
|
||||
0xB4, 0xD4, 0xDB, 0x31, 0xEE, 0x96, 0xAB, 0xFA};
|
||||
|
||||
std::vector<uint8_t> message{
|
||||
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0xC3, 0x51, 0xF6, 0xFD, 0xDD, 0xC9, 0x8B, 0x41,
|
||||
0xD6, 0xF4, 0x77, 0x6D, 0xAC, 0xE8, 0xE0, 0x14,
|
||||
0xB2, 0x7A, 0xCC, 0x22, 0x00, 0xAA, 0xD2, 0x37,
|
||||
0xD0, 0x79, 0x06, 0x12, 0x83, 0x40, 0xB7, 0xA6};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, TestComputeHMACSHA256_m0)
|
||||
{
|
||||
// key and message generated from RG A.6.5.1
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
// RG K4 @ 345690
|
||||
std::vector<uint8_t> key = {
|
||||
0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6,
|
||||
0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};
|
||||
|
||||
// m0
|
||||
std::vector<uint8_t> message = {
|
||||
0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5,
|
||||
0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
|
||||
0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB,
|
||||
0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
|
||||
0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37,
|
||||
0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
|
||||
0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0,
|
||||
0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
|
||||
0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07,
|
||||
0xF7, 0xFC, 0x00};
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0xE3, 0x7B, 0xC4, 0xF8, 0x58, 0xAE, 0x1E, 0x5C,
|
||||
0xFD, 0xC4, 0x6F, 0x05, 0x4B, 0x1F, 0x47, 0xB9,
|
||||
0xD2, 0xEA, 0x61, 0xE1, 0xEF, 0x09, 0x11, 0x5C,
|
||||
0xFE, 0x70, 0x68, 0x52, 0xBF, 0xF2, 0x3A, 0x83};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, TestComputeHMACSHA256_adkd4)
|
||||
{
|
||||
// key and message generated from RG A.6.5.2
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
// RG K4 @ 345690
|
||||
std::vector<uint8_t> key = {
|
||||
0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6,
|
||||
0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};
|
||||
|
||||
std::vector<uint8_t> message = {
|
||||
0x02, 0x02, 0x4E, 0x05, 0x46, 0x3C, 0x03, 0xBF,
|
||||
0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x44, 0x92,
|
||||
0x38, 0x22, 0x78, 0x97, 0xFD, 0xEF, 0xF9, 0x30,
|
||||
0x40};
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0x7B, 0xB2, 0x38, 0xC8, 0x83, 0xC0, 0x6A, 0x2B,
|
||||
0x50, 0x8F, 0xE6, 0x3F, 0xB7, 0xF4, 0xF5, 0x4D,
|
||||
0x44, 0xAB, 0xEE, 0x4D, 0xCE, 0xB9, 0x3D, 0xCF,
|
||||
0x65, 0xCB, 0x3A, 0x5B, 0x81, 0x4A, 0x34, 0xE9};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_HMAC_SHA_256(key, message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, TestComputeCMAC_AES)
|
||||
{
|
||||
// Tests vectors from https://datatracker.ietf.org/doc/html/rfc4493#appendix-A
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
std::vector<uint8_t> key = {
|
||||
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
|
||||
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C};
|
||||
|
||||
std::vector<uint8_t> message{
|
||||
0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96,
|
||||
0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A};
|
||||
|
||||
std::vector<uint8_t> expected_output = {
|
||||
0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44,
|
||||
0xF7, 0x9B, 0xDD, 0x9D, 0xD0, 0x4A, 0x28, 0x7C};
|
||||
|
||||
std::vector<uint8_t> output = d_crypto->compute_CMAC_AES(key, message);
|
||||
|
||||
ASSERT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, VerifySignatureP256)
|
||||
{
|
||||
auto d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
// RG example - import crt certificate
|
||||
std::vector<uint8_t> message = {
|
||||
0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF,
|
||||
0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04,
|
||||
0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF};
|
||||
|
||||
// ECDSA P-256 signature, raw format
|
||||
std::vector<uint8_t> signature = {
|
||||
0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20,
|
||||
0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F,
|
||||
0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B,
|
||||
0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D,
|
||||
0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22,
|
||||
0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87,
|
||||
0x28, 0xC1, 0x77, 0xFB};
|
||||
|
||||
// Input taken from RG 1.3 A7.1
|
||||
// compressed ECDSA P-256 format
|
||||
std::vector<uint8_t> publicKey = {
|
||||
0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B,
|
||||
0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D,
|
||||
0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7,
|
||||
0x79, 0x80, 0xEA};
|
||||
|
||||
d_crypto->set_public_key(publicKey);
|
||||
ASSERT_TRUE(d_crypto->verify_signature_ecdsa_p256(message, signature));
|
||||
|
||||
std::vector<uint8_t> wrong_signature = std::move(signature);
|
||||
wrong_signature[1] = 1;
|
||||
ASSERT_FALSE(d_crypto->verify_signature_ecdsa_p256(message, wrong_signature));
|
||||
}
|
||||
|
||||
|
||||
TEST(GnssCryptoTest, VerifySignatureP521)
|
||||
{
|
||||
std::unique_ptr<Gnss_Crypto> d_crypto = std::make_unique<Gnss_Crypto>();
|
||||
|
||||
// Message to be verified
|
||||
std::vector<uint8_t> message = {
|
||||
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // "Hello world\n"
|
||||
|
||||
// Public key in compressed X format
|
||||
std::vector<uint8_t> publicKey = {
|
||||
0x03, 0x00, 0x28, 0x35, 0xBB, 0xE9, 0x24, 0x59, 0x4E, 0xF0,
|
||||
0xE3, 0xA2, 0xDB, 0xC0, 0x49, 0x30, 0x60, 0x7C, 0x61, 0x90,
|
||||
0xE4, 0x03, 0xE0, 0xC7, 0xB8, 0xC2, 0x62, 0x37, 0xF7, 0x58,
|
||||
0x56, 0xBE, 0x63, 0x5C, 0x97, 0xF7, 0x53, 0x64, 0x7E, 0xE1,
|
||||
0x0C, 0x07, 0xD3, 0x97, 0x8D, 0x58, 0x46, 0xFD, 0x6E, 0x06,
|
||||
0x44, 0x01, 0xA7, 0xAA, 0xC4, 0x95, 0x13, 0x5D, 0xC9, 0x77,
|
||||
0x26, 0xE9, 0xF8, 0x72, 0x0C, 0xD3, 0x88};
|
||||
|
||||
// ECDSA P-521 signature, raw format
|
||||
std::vector<uint8_t> signature = {
|
||||
0x01, 0x5C, 0x23, 0xC0, 0xBE, 0xAD, 0x1E, 0x44, 0x60, 0xD4,
|
||||
0xE0, 0x81, 0x38, 0xF2, 0xBA, 0xF5, 0xB5, 0x37, 0x5A, 0x34,
|
||||
0xB5, 0xCA, 0x6B, 0xC8, 0x0F, 0xCD, 0x75, 0x1D, 0x5E, 0xC0,
|
||||
0x8A, 0xD3, 0xD7, 0x79, 0xA7, 0xC1, 0xB8, 0xA2, 0xC6, 0xEA,
|
||||
0x5A, 0x7D, 0x60, 0x66, 0x50, 0x97, 0x37, 0x6C, 0xF9, 0x0A,
|
||||
0xF6, 0x3D, 0x77, 0x9A, 0xE2, 0x19, 0xF7, 0xF9, 0xDD, 0x52,
|
||||
0xC4, 0x0F, 0x98, 0xAA, 0xA2, 0xA4, 0x01, 0xC9, 0x41, 0x0B,
|
||||
0xD0, 0x25, 0xDD, 0xC9, 0x7C, 0x3F, 0x70, 0x32, 0x23, 0xCF,
|
||||
0xFE, 0x37, 0x67, 0x3A, 0xBC, 0x0B, 0x76, 0x16, 0x82, 0x83,
|
||||
0x27, 0x3D, 0x1D, 0x19, 0x15, 0x78, 0x08, 0x2B, 0xD4, 0xA7,
|
||||
0xC2, 0x0F, 0x11, 0xF4, 0xDD, 0xE5, 0x5A, 0x5D, 0x04, 0x8D,
|
||||
0x6D, 0x5E, 0xC4, 0x1F, 0x54, 0x44, 0xA9, 0x13, 0x34, 0x71,
|
||||
0x0F, 0xF7, 0x57, 0x9A, 0x9F, 0x2E, 0xF4, 0x97, 0x7D, 0xAE,
|
||||
0x28, 0xEF};
|
||||
|
||||
d_crypto->set_public_key(publicKey);
|
||||
ASSERT_TRUE(d_crypto->verify_signature_ecdsa_p521(message, signature));
|
||||
|
||||
std::vector<uint8_t> wrong_signature = std::move(signature);
|
||||
wrong_signature[1] = 1;
|
||||
ASSERT_FALSE(d_crypto->verify_signature_ecdsa_p521(message, wrong_signature));
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/*!
|
||||
* \file osmna_msg_receiver_test.cc
|
||||
* \brief Tests for the osnma_msg_receiver class.
|
||||
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
|
||||
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "Galileo_OSNMA.h"
|
||||
#include "gnss_crypto.h"
|
||||
#include "osnma_helper.h"
|
||||
#include "osnma_msg_receiver.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h> // for LOG
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
class OsnmaMsgReceiverTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
Osnma_Helper helper;
|
||||
osnma_msg_receiver_sptr osnma;
|
||||
OSNMA_msg osnma_msg{};
|
||||
std::array<int8_t, 15> nma_position_filled;
|
||||
uint32_t d_GST_SIS{};
|
||||
uint32_t TOW{};
|
||||
uint32_t WN{};
|
||||
std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0, 0, 0, 0, 0}; // months start with 0 and years since 1900 in std::tm
|
||||
const uint32_t LEAP_SECONDS = 0; // tried with 13 + 5, which is the official count, but won't parse correctly
|
||||
void set_time(std::tm& input);
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
// std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 1
|
||||
std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 2
|
||||
set_time(input_time);
|
||||
osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, ComputeMerkleRoot)
|
||||
{
|
||||
// input data taken from Receiver Guidelines v1.3, A.7
|
||||
// Arrange
|
||||
std::vector<uint8_t> computed_merkle_root;
|
||||
std::vector<uint8_t> expected_merkle_root = helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D");
|
||||
DSM_PKR_message dsm_pkr_message;
|
||||
dsm_pkr_message.npkt = 0x01;
|
||||
dsm_pkr_message.npktid = 0x2;
|
||||
dsm_pkr_message.mid = 0x01;
|
||||
std::vector<uint8_t> base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
|
||||
|
||||
// ITN
|
||||
std::vector<uint8_t> vec = helper.convert_from_hex_string(
|
||||
"7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06"
|
||||
"956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7"
|
||||
"407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1"
|
||||
"24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87");
|
||||
std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin());
|
||||
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
|
||||
|
||||
// Act
|
||||
computed_merkle_root = osnma->compute_merkle_root(dsm_pkr_message, base_leaf);
|
||||
|
||||
// Assert
|
||||
ASSERT_EQ(computed_merkle_root, expected_merkle_root);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, ComputeBaseLeaf)
|
||||
{
|
||||
// input data taken from Receiver Guidelines v1.3, A.7
|
||||
// Arrange
|
||||
std::vector<uint8_t> expected_base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
|
||||
DSM_PKR_message dsm_pkr_message;
|
||||
dsm_pkr_message.npkt = 0x01;
|
||||
dsm_pkr_message.npktid = 0x2;
|
||||
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
|
||||
|
||||
// Act
|
||||
std::vector<uint8_t> computed_base_leaf = osnma->get_merkle_tree_leaves(dsm_pkr_message);
|
||||
|
||||
// Assert
|
||||
ASSERT_EQ(computed_base_leaf, expected_base_leaf);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, VerifyPublicKey)
|
||||
{
|
||||
// Input data taken from Receiver Guidelines v1.3, A.7
|
||||
// Arrange
|
||||
osnma->d_crypto->set_merkle_root(helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D"));
|
||||
DSM_PKR_message dsm_pkr_message;
|
||||
dsm_pkr_message.npkt = 0x01;
|
||||
dsm_pkr_message.npktid = 0x2;
|
||||
dsm_pkr_message.mid = 0x01;
|
||||
std::vector<uint8_t> vec = helper.convert_from_hex_string(
|
||||
"7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06"
|
||||
"956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7"
|
||||
"407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1"
|
||||
"24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87");
|
||||
std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin());
|
||||
dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA");
|
||||
|
||||
// Act
|
||||
bool result = osnma->verify_dsm_pkr(dsm_pkr_message); // TODO - refactor method so that output is more than a boolean.
|
||||
|
||||
// Assert
|
||||
ASSERT_TRUE(result);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, BuildTagMessageM0)
|
||||
{
|
||||
// input data taken from Receiver Guidelines v1.3, A.6.5.1
|
||||
// Arrange
|
||||
std::vector<uint8_t> expected_message = {
|
||||
0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
|
||||
0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
|
||||
0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
|
||||
0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
|
||||
0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, 0xF7, 0xFC, 0x00};
|
||||
|
||||
uint32_t TOW_Tag0 = 345660;
|
||||
uint32_t TOW_NavData = TOW_Tag0 - 30;
|
||||
uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
|
||||
uint32_t WN = 1248;
|
||||
uint32_t PRNa = 2;
|
||||
uint8_t CTR = 1;
|
||||
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
|
||||
osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDB, 0xBC, 0x73}; // K4
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
|
||||
osnma->d_nav_data_manager->add_navigation_data(
|
||||
"000011101001011001000100000101000111010110100100100101100000000000"
|
||||
"011101101011001111101110101010000001010000011011111100000011101011"
|
||||
"011100101101011010101011011011001001110111101011110110111111001111"
|
||||
"001000011111101110011000111111110111111010000011101011111111110000"
|
||||
"110111000000100000001110110000110110001110000100001110101100010100"
|
||||
"110100010001000110001110011010110000111010000010000000000001101000"
|
||||
"000000000011100101100100010000000000000110110100110001111100000000"
|
||||
"000000100110100000000101010010100000001011000010001001100000011111"
|
||||
"110111111111000000000",
|
||||
PRNa, TOW_NavData);
|
||||
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
|
||||
|
||||
MACK_tag_and_info MTI;
|
||||
MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
|
||||
MTI.tag_info.PRN_d = 0x02;
|
||||
MTI.tag_info.ADKD = 0x00;
|
||||
MTI.tag_info.cop = 0x0F;
|
||||
Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
|
||||
|
||||
// Act
|
||||
auto computed_message = osnma->build_message(t0);
|
||||
|
||||
// Assert
|
||||
ASSERT_TRUE(computed_message == expected_message);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, TagVerification)
|
||||
{
|
||||
// input data taken from Receiver Guidelines v1.3, A.6.5.1
|
||||
// Arrange
|
||||
// Tag0
|
||||
uint32_t TOW_Tag0 = 345660;
|
||||
uint32_t TOW_NavData = TOW_Tag0 - 30;
|
||||
uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
|
||||
uint32_t WN = 1248;
|
||||
uint32_t PRNa = 2;
|
||||
uint8_t CTR = 1;
|
||||
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
|
||||
osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
|
||||
osnma->d_nav_data_manager->add_navigation_data(
|
||||
"000011101001011001000100000101000111010110100100100101100000000000"
|
||||
"011101101011001111101110101010000001010000011011111100000011101011"
|
||||
"011100101101011010101011011011001001110111101011110110111111001111"
|
||||
"001000011111101110011000111111110111111010000011101011111111110000"
|
||||
"110111000000100000001110110000110110001110000100001110101100010100"
|
||||
"110100010001000110001110011010110000111010000010000000000001101000"
|
||||
"000000000011100101100100010000000000000110110100110001111100000000"
|
||||
"000000100110100000000101010010100000001011000010001001100000011111"
|
||||
"110111111111000000000",
|
||||
PRNa, TOW_NavData);
|
||||
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
|
||||
|
||||
MACK_tag_and_info MTI;
|
||||
MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
|
||||
MTI.tag_info.PRN_d = 0x02;
|
||||
MTI.tag_info.ADKD = 0x00;
|
||||
MTI.tag_info.cop = 0x0F;
|
||||
Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
|
||||
|
||||
// Act
|
||||
bool result_tag0 = osnma->verify_tag(t0);
|
||||
|
||||
// Assert
|
||||
|
||||
// Tag3
|
||||
uint32_t TOW_Key_Tag3 = TOW_Tag0 + 30;
|
||||
WN = 1248;
|
||||
PRNa = 2;
|
||||
CTR = 3;
|
||||
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
|
||||
osnma->d_tesla_keys[TOW_Key_Tag3] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
|
||||
osnma->d_nav_data_manager->add_navigation_data(
|
||||
"111111111111111111111111111111110000000000000000000000010001001001001000"
|
||||
"111000001000100111100010010111111111011110111111111001001100000100000",
|
||||
PRNa, TOW_NavData);
|
||||
osnma->d_osnma_data.d_nma_header.nmas = 0b10;
|
||||
|
||||
MTI.tag = static_cast<uint64_t>(0x7BB238C883);
|
||||
MTI.tag_info.PRN_d = 0x02;
|
||||
MTI.tag_info.ADKD = 0x04;
|
||||
MTI.tag_info.cop = 0x0F;
|
||||
Tag t3(MTI, TOW_Tag0, WN, PRNa, CTR);
|
||||
|
||||
bool result_tag3 = osnma->verify_tag(t3);
|
||||
|
||||
ASSERT_TRUE(result_tag0 && result_tag3);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OsnmaMsgReceiverTest, TeslaKeyVerification)
|
||||
{
|
||||
// input data taken from Receiver Guidelines v1.3, A.5.2
|
||||
// Arrange
|
||||
osnma->d_tesla_key_verified = false;
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.kroot = {0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; // Kroot, TOW 345570 GST_0 - 30
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.ks = 4; // TABLE 10 --> 128 bits
|
||||
osnma->d_osnma_data.d_dsm_kroot_message.alpha = 0x610BDF26D77B;
|
||||
osnma->d_GST_SIS = (1248 & 0x00000FFF) << 20 | (345630 & 0x000FFFFF);
|
||||
osnma->d_GST_0 = ((1248 & 0x00000FFF) << 20 | (345600 & 0x000FFFFF)); // applicable time (GST_Kroot + 30)
|
||||
osnma->d_GST_Sf = osnma->d_GST_0 + 30 * std::floor((osnma->d_GST_SIS - osnma->d_GST_0) / 30); // Eq. 3 R.G.
|
||||
|
||||
osnma->d_tesla_keys.insert((std::pair<uint32_t, std::vector<uint8_t>>(345600, {0xEF, 0xF9, 0x99, 0x04, 0x0E, 0x19, 0xB5, 0x70, 0x83, 0x50, 0x60, 0xBE, 0xBD, 0x23, 0xED, 0x92}))); // K1, not needed, just for reference.
|
||||
std::vector<uint8_t> key = {0x2D, 0xC3, 0xA3, 0xCD, 0xB1, 0x17, 0xFA, 0xAD, 0xB8, 0x3B, 0x5F, 0x0B, 0x6F, 0xEA, 0x88, 0xEB}; // K2
|
||||
uint32_t TOW = 345630;
|
||||
|
||||
// Act
|
||||
bool result = osnma->verify_tesla_key(key, TOW); // TODO - refactor so that output is not a boolean. Or use last_verified_tesla_key?
|
||||
|
||||
// Assert
|
||||
ASSERT_TRUE(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets the time based on the given input.
|
||||
*
|
||||
* This function calculates the week number (WN) and time of week (TOW)
|
||||
* based on the input time and the GST_START_EPOCH. It then stores the
|
||||
* calculated values in the WN and TOW member variables. Finally, it
|
||||
* combines the WN and TOW into a single 32-bit value and stores it in
|
||||
* the d_GST_SIS member variable.
|
||||
*
|
||||
* @param input The input time as a tm struct.
|
||||
*/
|
||||
void OsnmaMsgReceiverTest::set_time(std::tm& input)
|
||||
{
|
||||
auto epoch_time_point = std::chrono::system_clock::from_time_t(mktime(&GST_START_EPOCH));
|
||||
auto input_time_point = std::chrono::system_clock::from_time_t(mktime(&input));
|
||||
|
||||
// Get the duration from epoch in seconds
|
||||
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(input_time_point - epoch_time_point);
|
||||
|
||||
// Calculate the week number (WN) and time of week (TOW)
|
||||
uint32_t sec_in_week = 7 * 24 * 60 * 60;
|
||||
uint32_t week_number = duration_sec.count() / sec_in_week;
|
||||
uint32_t time_of_week = duration_sec.count() % sec_in_week;
|
||||
this->WN = week_number;
|
||||
this->TOW = time_of_week + LEAP_SECONDS;
|
||||
// Return the week number and time of week as a pair
|
||||
|
||||
// TODO: d_GST_SIS or d_receiver_time? doubt
|
||||
// I am assuming that local realisation of receiver is identical to SIS GST time coming from W5 or W0
|
||||
this->d_GST_SIS = (this->WN & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
/*!
|
||||
* \file osmna_test_vectors.cc
|
||||
* \brief Tests for the osnma_msg_receiver class.
|
||||
* \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es
|
||||
* Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "gnss_crypto.h"
|
||||
#include "osnma_msg_receiver.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <glog/logging.h> // for LOG
|
||||
#else
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
|
||||
struct TestVector
|
||||
{
|
||||
int svId;
|
||||
int numNavBits;
|
||||
std::vector<uint8_t> navBits;
|
||||
};
|
||||
|
||||
|
||||
class OsnmaTestVectors : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<uint8_t> parseNavBits(const std::string& hex);
|
||||
std::vector<TestVector> readTestVectorsFromFile(const std::string& filename);
|
||||
std::string bytes_to_str(const std::vector<uint8_t>& bytes);
|
||||
std::vector<uint8_t> extract_page_bytes(const TestVector& tv, int byte_index, int num_bytes);
|
||||
bool feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles);
|
||||
void set_time(std::tm& input);
|
||||
void SetUp() override
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t d_GST_SIS{};
|
||||
uint32_t TOW{};
|
||||
uint32_t WN{};
|
||||
std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0, 0, 0, 0, 0}; // months start with 0 and years since 1900 in std::tm
|
||||
const uint32_t LEAP_SECONDS = 0;
|
||||
const int SIZE_PAGE_BYTES{240 / 8}; // total bytes of a page
|
||||
const int SIZE_SUBFRAME_PAGES{15}; // number of pages of a subframe
|
||||
const int DURATION_SUBFRAME{30}; // duration of a subframe, in seconds// 13 + 5;
|
||||
|
||||
bool d_flag_NPK{false}; // flag for NPK, new MT will be set when the new Kroot is received.
|
||||
};
|
||||
|
||||
TEST_F(OsnmaTestVectors, NominalTestConf1)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_1/PublicKey/OSNMA_PublicKey_20230803105952_newPKID_1.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_1/MerkleTree/OSNMA_MerkleTree_20230803105953_newPKID_1.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::vector<std::tm> input_times = {input_time};
|
||||
|
||||
std::vector<TestVector> testVector = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/configuration_1/16_AUG_2023_GST_05_00_01.csv");
|
||||
if (testVector.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVector};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, NominalTestConf2)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20230720113300_newPKID_2.crt"; // conf. 2
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20230720113300_newPKID_2.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; // conf. 2
|
||||
std::vector<std::tm> input_times = {input_time};
|
||||
|
||||
std::vector<TestVector> testVector = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/configuration_2/27_JUL_2023_GST_00_00_01.csv");
|
||||
if (testVector.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVector};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, PublicKeyRenewal)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time_step1 = {0, 45, 2, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step2 = {0, 45, 3, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step3 = {0, 45, 4, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
|
||||
|
||||
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step1/07_OCT_2023_GST_02_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step2/07_OCT_2023_GST_03_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/npk_step3/07_OCT_2023_GST_04_45_01.csv");
|
||||
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
|
||||
|
||||
// Act
|
||||
d_flag_NPK = true;
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, PublicKeyRevocation)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007081500_PKID_8.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007081500_PKID_8.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time_step1 = {0, 45, 7, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step2 = {0, 30, 9, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step3 = {0, 30, 10, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
|
||||
|
||||
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step1/07_OCT_2023_GST_07_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step2/07_OCT_2023_GST_09_30_01.csv");
|
||||
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step3/07_OCT_2023_GST_10_30_01.csv");
|
||||
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, ChainRenewal)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time_step1 = {0, 45, 16, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step2 = {0, 30, 18, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::vector<std::tm> input_times = {input_time_step1, input_time_step2};
|
||||
|
||||
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step1/06_OCT_2023_GST_16_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step2/06_OCT_2023_GST_18_30_01.csv");
|
||||
if (testVectors_step1.empty() || testVectors_step2.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, ChainRevocation)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time_step1 = {0, 45, 21, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step2 = {0, 30, 23, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step3 = {0, 30, 00, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
|
||||
std::vector<std::tm> input_times = {input_time_step1, input_time_step2, input_time_step3};
|
||||
|
||||
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step1/06_OCT_2023_GST_21_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step2/06_OCT_2023_GST_23_30_01.csv");
|
||||
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/crev_step3/07_OCT_2023_GST_00_30_01.csv");
|
||||
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
TEST_F(OsnmaTestVectors, AlertMessage)
|
||||
{
|
||||
// Arrange
|
||||
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/PublicKey/OSNMA_PublicKey_20231007201500_PKID_1.crt";
|
||||
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/MerkleTree/OSNMA_MerkleTree_20231007201500_PKID_1.xml";
|
||||
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
|
||||
|
||||
std::tm input_time_step1 = {0, 45, 18, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::tm input_time_step2 = {0, 45, 19, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
|
||||
std::vector<std::tm> input_times = {input_time_step1, input_time_step2};
|
||||
|
||||
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step1/07_OCT_2023_GST_18_45_01.csv");
|
||||
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step2/07_OCT_2023_GST_19_45_01.csv");
|
||||
if (testVectors_step1.empty() || testVectors_step2.empty())
|
||||
{
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2};
|
||||
|
||||
// Act
|
||||
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// Assert
|
||||
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
|
||||
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
|
||||
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
|
||||
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
|
||||
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
|
||||
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
|
||||
ASSERT_EQ(osnma->d_count_failed_tags, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
|
||||
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
|
||||
}
|
||||
|
||||
// Auxiliary functions for the OsnmaTestVectorsSimulation test fixture.
|
||||
// Essentially, they perform same work as the telemetry decoder block, but adapted to the osnma-test-vector files.
|
||||
bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles)
|
||||
{
|
||||
bool end_of_hex_stream;
|
||||
int offset_byte{0};
|
||||
int byte_index{0}; // index containing the last byte position of the hex stream that was retrieved. Takes advantage that all TVs have same size
|
||||
|
||||
const int DUMMY_PAGE{63};
|
||||
bool flag_dummy_page{false};
|
||||
// Act
|
||||
// loop over all bytes of data. Note: all TestVectors have same amount of data.
|
||||
// if needed, add global flags so that particular logic may be done at certain points in between files
|
||||
for (size_t test_step = 0; test_step < testVectors.size(); test_step++)
|
||||
{
|
||||
// set variables for each file
|
||||
end_of_hex_stream = false;
|
||||
offset_byte = 0;
|
||||
byte_index = 0;
|
||||
set_time(startTimesFiles[test_step]);
|
||||
std::cout << "OsnmaTestVectorsSimulation:"
|
||||
<< " d_GST_SIS= " << d_GST_SIS
|
||||
<< ", TOW=" << TOW
|
||||
<< ", WN=" << WN << std::endl;
|
||||
|
||||
if (test_step == 1 && d_flag_NPK == true)
|
||||
{
|
||||
// step 2: this simulates the osnma connecting to the GSC server and downloading the Merkle tree of the next public key
|
||||
osnma_object->read_merkle_xml(
|
||||
std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007081500_PKID_8.xml");
|
||||
}
|
||||
|
||||
while (!end_of_hex_stream)
|
||||
{
|
||||
// loop over all SVs, extract a subframe
|
||||
for (const TestVector& tv : testVectors[test_step])
|
||||
{ // loop over all SVs, extract a subframe
|
||||
std::cout << "OsnmaTestVectorsSimulation: SVID (PRN_a) " << tv.svId << std::endl;
|
||||
auto osnmaMsg_sptr = std::make_shared<OSNMA_msg>();
|
||||
std::array<uint8_t, 15> hkroot{};
|
||||
std::array<uint32_t, 15> mack{};
|
||||
byte_index = offset_byte; // reset byte_index to the offset position for the next test vector. Offset is updated at the end of each Subframe (every 30 s or 450 Bytes)
|
||||
std::map<uint8_t, std::bitset<128>> words_for_OSNMA; // structure containing <WORD_NUMBER> and <EXTRACTED_BITS>
|
||||
|
||||
for (int idx = 0; idx < SIZE_SUBFRAME_PAGES; ++idx) // extract all pages of a subframe
|
||||
{
|
||||
// extract bytes of complete page (odd+even) -- extract SIZE_PAGE from tv.navBits, starting from byte_index
|
||||
std::vector<uint8_t> page_bytes = extract_page_bytes(tv, byte_index, SIZE_PAGE_BYTES);
|
||||
if (page_bytes.empty())
|
||||
{
|
||||
std::cout << "OsnmaTestVectorsSimulation: end of TestVectors \n"
|
||||
<< "byte_index=" << byte_index << " expected= " << 432000 / 8 << std::endl;
|
||||
end_of_hex_stream = true;
|
||||
break;
|
||||
}
|
||||
// convert them to bitset representation using bytes_to_string
|
||||
std::string page_bits = bytes_to_str(page_bytes);
|
||||
// Extract the 40 OSNMA bits starting from the 18th bit
|
||||
std::string even_page = page_bits.substr(0, page_bits.size() / 2);
|
||||
std::string odd_page = page_bits.substr(page_bits.size() / 2);
|
||||
if (even_page.size() < 120 || odd_page.size() < 120)
|
||||
{
|
||||
std::cout << "OsnmaTestVectorsSimulation: error parsing pages" << std::endl;
|
||||
}
|
||||
bool even_odd_OK = even_page[0] == '0' && odd_page[0] == '1';
|
||||
bool page_type_OK = even_page[1] == '0' && odd_page[1] == '0';
|
||||
bool tail_bits_OK = even_page.substr(even_page.size() - 6) == "000000" && odd_page.substr(odd_page.size() - 6) == "000000";
|
||||
if (!even_odd_OK || !page_type_OK || !tail_bits_OK)
|
||||
std::cerr << "OsnmaTestVectorsSimulation: error parsing pages." << std::endl;
|
||||
|
||||
std::bitset<112> data_k(even_page.substr(2, 112));
|
||||
std::bitset<16> data_j(odd_page.substr(2, 16));
|
||||
std::bitset<112> shifted_data_k = data_k;
|
||||
uint8_t word_type = static_cast<uint8_t>((shifted_data_k >>= 106).to_ulong()); // word type is the first 6 bits of the word
|
||||
// std::cout << "OsnmaTestVectorsSimulation: received Word " << static_cast<int>(word_type) << std::endl;
|
||||
if ((word_type >= 1 && word_type <= 5) || word_type == 6 || word_type == 10)
|
||||
{
|
||||
// store raw word
|
||||
std::bitset<128> data_combined(data_k.to_string() + data_j.to_string());
|
||||
words_for_OSNMA[word_type] = data_combined;
|
||||
}
|
||||
if (word_type == DUMMY_PAGE)
|
||||
flag_dummy_page = true;
|
||||
|
||||
// place it into osnma object.
|
||||
std::bitset<40> osnmaBits(odd_page.substr(18, 40));
|
||||
|
||||
// Extract bits for hkroot and mack
|
||||
std::bitset<8> hkrootBits(osnmaBits.to_string().substr(0, 8));
|
||||
std::bitset<32> mackBits(osnmaBits.to_string().substr(8, 32));
|
||||
hkroot[idx] = static_cast<uint8_t>(hkrootBits.to_ulong());
|
||||
mack[idx] = static_cast<uint32_t>(mackBits.to_ulong());
|
||||
|
||||
byte_index += SIZE_PAGE_BYTES;
|
||||
}
|
||||
|
||||
// std::cout << "----------" << std::endl;
|
||||
if (end_of_hex_stream)
|
||||
break;
|
||||
if (flag_dummy_page)
|
||||
{
|
||||
flag_dummy_page = false;
|
||||
continue; // skip this SV
|
||||
}
|
||||
|
||||
// Fill osnma object
|
||||
osnmaMsg_sptr->hkroot = hkroot;
|
||||
osnmaMsg_sptr->mack = mack;
|
||||
|
||||
osnmaMsg_sptr->TOW_sf0 = d_GST_SIS & 0x000FFFFF;
|
||||
osnmaMsg_sptr->WN_sf0 = (d_GST_SIS & 0xFFF00000) >> 20;
|
||||
osnmaMsg_sptr->PRN = tv.svId; // PRNa
|
||||
|
||||
// TODO - refactor this logic, currently it is split
|
||||
// check if words_for_OSNMA 1--> 5 words_for_OSNMA are received => fill EphClockStatus data vector
|
||||
bool ephClockStatusWordsReceived = true;
|
||||
for (int i = 1; i <= 5; ++i)
|
||||
{
|
||||
if (words_for_OSNMA.find(i) == words_for_OSNMA.end())
|
||||
{
|
||||
ephClockStatusWordsReceived = false;
|
||||
std::cerr << "OsnmaTestVectorsSimulation: error parsing words_for_OSNMA 1->5. "
|
||||
"Word "
|
||||
<< i << " should be received for each subframe but was not." << std::endl;
|
||||
}
|
||||
}
|
||||
// extract bits as needed by osnma block
|
||||
if (ephClockStatusWordsReceived)
|
||||
{
|
||||
// Define the starting position and length of bits to extract for each word
|
||||
std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
|
||||
{1, {6, 120}},
|
||||
{2, {6, 120}},
|
||||
{3, {6, 122}},
|
||||
{4, {6, 120}},
|
||||
{5, {6, 67}},
|
||||
};
|
||||
|
||||
// Fill NavData bits -- Iterate over the extraction parameters
|
||||
std::string nav_data_ADKD_0_12 = "";
|
||||
for (const auto& param : extractionParams)
|
||||
{
|
||||
uint8_t wordKey = param.first;
|
||||
uint8_t start = param.second.first;
|
||||
uint8_t length = param.second.second;
|
||||
|
||||
// Extract the required bits and fill osnma block
|
||||
nav_data_ADKD_0_12 += words_for_OSNMA[wordKey].to_string().substr(start, length);
|
||||
}
|
||||
// send to osnma block
|
||||
bool check_size_is_ok = nav_data_ADKD_0_12.size() == 549;
|
||||
if (check_size_is_ok)
|
||||
{
|
||||
std::cout << "Galileo OSNMA: sending ADKD=0/12 navData, PRN_d (" << tv.svId << ") "
|
||||
<< "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
|
||||
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
|
||||
tv.svId,
|
||||
nav_data_ADKD_0_12,
|
||||
osnmaMsg_sptr->TOW_sf0);
|
||||
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_0_12;
|
||||
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
|
||||
}
|
||||
}
|
||||
|
||||
// check w6 && w10 is received => fill TimingData data vector
|
||||
bool timingWordsReceived = words_for_OSNMA.find(6) != words_for_OSNMA.end() &&
|
||||
words_for_OSNMA.find(10) != words_for_OSNMA.end();
|
||||
// extract bits as needed by osnma block
|
||||
if (timingWordsReceived)
|
||||
{
|
||||
// Define the starting position and length of bits to extract for each word
|
||||
std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
|
||||
{6, {6, 99}},
|
||||
{10, {86, 42}}};
|
||||
|
||||
std::string nav_data_ADKD_4 = "";
|
||||
// Fill NavData bits -- Iterate over the extraction parameters
|
||||
for (const auto& param : extractionParams)
|
||||
{
|
||||
uint8_t wordKey = param.first;
|
||||
uint8_t start = param.second.first;
|
||||
uint8_t length = param.second.second;
|
||||
|
||||
// Extract the required bits and fill osnma block
|
||||
nav_data_ADKD_4 += words_for_OSNMA[wordKey].to_string().substr(start, length);
|
||||
}
|
||||
// send to osnma block
|
||||
bool check_size_is_ok = nav_data_ADKD_4.size() == 141;
|
||||
if (check_size_is_ok)
|
||||
{
|
||||
std::cout << "Galileo OSNMA: sending ADKD=04 navData, PRN_d (" << tv.svId << ") "
|
||||
<< "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
|
||||
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
|
||||
tv.svId,
|
||||
nav_data_ADKD_4,
|
||||
osnmaMsg_sptr->TOW_sf0);
|
||||
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_4;
|
||||
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
|
||||
}
|
||||
}
|
||||
|
||||
// Call the handler, as if it came from telemetry decoder block
|
||||
auto temp_obj = pmt::make_any(osnmaMsg_sptr);
|
||||
|
||||
osnma_object->msg_handler_osnma(temp_obj); // osnma entry point
|
||||
}
|
||||
if (!end_of_hex_stream)
|
||||
{
|
||||
offset_byte = byte_index; // update offset for the next subframe
|
||||
d_GST_SIS += DURATION_SUBFRAME;
|
||||
TOW = d_GST_SIS & 0x000FFFFF;
|
||||
WN = (d_GST_SIS & 0xFFF00000) >> 20;
|
||||
std::cout << "OsnmaTestVectorsSimulation:"
|
||||
<< " d_GST_SIS= " << d_GST_SIS
|
||||
<< ", TOW=" << TOW
|
||||
<< ", WN=" << WN << std::endl;
|
||||
}
|
||||
}
|
||||
if (end_of_hex_stream)
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<TestVector> OsnmaTestVectors::readTestVectorsFromFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename);
|
||||
std::vector<TestVector> testVectors;
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cerr << "Error reading the file \"" << filename << "\" \n";
|
||||
return testVectors;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
if (line != "SVID,NumNavBits,NavBitsHEX\r")
|
||||
{
|
||||
std::cerr << "Error parsing first line"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
std::stringstream ss(line);
|
||||
TestVector tv;
|
||||
|
||||
std::string val;
|
||||
|
||||
std::getline(ss, val, ',');
|
||||
tv.svId = std::stoi(val);
|
||||
|
||||
std::getline(ss, val, ',');
|
||||
tv.numNavBits = std::stoi(val);
|
||||
|
||||
std::getline(ss, val, ',');
|
||||
tv.navBits = OsnmaTestVectors::parseNavBits(val);
|
||||
|
||||
testVectors.push_back(tv);
|
||||
}
|
||||
|
||||
return testVectors;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> OsnmaTestVectors::parseNavBits(const std::string& hexadecimal)
|
||||
{
|
||||
std::vector<uint8_t> bytes;
|
||||
|
||||
for (unsigned int i = 0; i < hexadecimal.length() - 1; i += 2)
|
||||
{
|
||||
std::string byteString = hexadecimal.substr(i, 2);
|
||||
uint8_t byte = static_cast<uint8_t>(strtol(byteString.c_str(), nullptr, 16));
|
||||
bytes.push_back(byte);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string OsnmaTestVectors::bytes_to_str(const std::vector<uint8_t>& bytes)
|
||||
{
|
||||
std::string bit_string;
|
||||
bit_string.reserve(bytes.size() * 8);
|
||||
for (const auto& byte : bytes)
|
||||
{
|
||||
std::bitset<8> bits(byte);
|
||||
bit_string += bits.to_string();
|
||||
}
|
||||
return bit_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts a range of bytes from a TestVector's navBits vector.
|
||||
*
|
||||
* This function extracts a extracts the bytes of complete page (odd+even)
|
||||
* from the navBits vector of a TestVector object.
|
||||
*
|
||||
*
|
||||
* @param tv The TestVector object from which to extract bytes.
|
||||
* @param byte_index The index of the first byte to extract.
|
||||
* @param num_bytes The number of bytes to extract.
|
||||
* @return A vector containing the extracted bytes, or an empty vector if extraction is not possible.
|
||||
*/
|
||||
std::vector<uint8_t> OsnmaTestVectors::extract_page_bytes(const TestVector& tv, int byte_index, int num_bytes)
|
||||
{
|
||||
// Ensure we don't go beyond the end of tv.navBits
|
||||
int num_bytes_to_extract = std::min(num_bytes, static_cast<int>(tv.navBits.size() - byte_index));
|
||||
|
||||
// If byte_index is beyond the end of tv.navBits, return an empty vector
|
||||
if (num_bytes_to_extract <= 0)
|
||||
{
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
// Use std::next to get an iterator to the range to extract
|
||||
std::vector<uint8_t> extracted_bytes(tv.navBits.begin() + byte_index, tv.navBits.begin() + byte_index + num_bytes_to_extract);
|
||||
|
||||
return extracted_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the time based on the given input.
|
||||
*
|
||||
* This function calculates the week number (WN) and time of week (TOW)
|
||||
* based on the input time and the GST_START_EPOCH. It then stores the
|
||||
* calculated values in the WN and TOW member variables. Finally, it
|
||||
* combines the WN and TOW into a single 32-bit value and stores it in
|
||||
* the d_GST_SIS member variable.
|
||||
* \post WN, TOW and GST_SIS are set up based on the input time.
|
||||
*
|
||||
* @param input The input time as a tm struct.
|
||||
*/
|
||||
void OsnmaTestVectors::set_time(std::tm& input)
|
||||
{
|
||||
auto epoch_time_point = std::chrono::system_clock::from_time_t(mktime(&GST_START_EPOCH));
|
||||
auto input_time_point = std::chrono::system_clock::from_time_t(mktime(&input));
|
||||
|
||||
// Get the duration from epoch in seconds
|
||||
auto duration_sec = std::chrono::duration_cast<std::chrono::seconds>(input_time_point - epoch_time_point);
|
||||
|
||||
// Calculate the week number (WN) and time of week (TOW)
|
||||
uint32_t sec_in_week = 7 * 24 * 60 * 60;
|
||||
uint32_t week_number = duration_sec.count() / sec_in_week;
|
||||
uint32_t time_of_week = duration_sec.count() % sec_in_week;
|
||||
this->WN = week_number;
|
||||
this->TOW = time_of_week + LEAP_SECONDS;
|
||||
// Return the week number and time of week as a pair
|
||||
|
||||
// TODO: d_GST_SIS or d_receiver_time? doubt
|
||||
// I am assuming that local realisation of receiver is identical to SIS GST time coming from W5 or W0
|
||||
this->d_GST_SIS = (this->WN & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* \file geohash_test.cc
|
||||
* \brief Implements Unit Tests for the Geohash class.
|
||||
* \author Carles Fernandez-Prades, 2023. 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-2023 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "geohash.h"
|
||||
|
||||
TEST(Geohash_Test, Encode)
|
||||
{
|
||||
Geohash gh = Geohash();
|
||||
std::string geohash;
|
||||
EXPECT_NO_THROW(geohash = gh.encode(52.205, 0.119, 7));
|
||||
EXPECT_EQ(0, geohash.compare("u120fxw"));
|
||||
EXPECT_NO_THROW(geohash = gh.encode(41.274966141209, 1.987518053501));
|
||||
EXPECT_EQ(0, geohash.compare("sp36v1zk0e2g"));
|
||||
EXPECT_THROW(gh.encode(52.205, 0.119, 0), std::invalid_argument);
|
||||
}
|
||||
|
||||
|
||||
TEST(Geohash_Test, Decode)
|
||||
{
|
||||
Geohash gh = Geohash();
|
||||
auto latlon = gh.decode("sp36v1zk0e2g");
|
||||
EXPECT_NEAR(41.274966141209, latlon[0], 1e-8);
|
||||
EXPECT_NEAR(1.987518053501, latlon[1], 1e-8);
|
||||
EXPECT_THROW(gh.decode(""), std::runtime_error);
|
||||
latlon = gh.decode("w21zd2mkt");
|
||||
EXPECT_NEAR(1.320527, latlon[0], 1e-8);
|
||||
EXPECT_NEAR(103.81726, latlon[1], 1e-8);
|
||||
latlon = gh.decode("W21ZD2MKT");
|
||||
EXPECT_NEAR(1.320527, latlon[0], 1e-8);
|
||||
EXPECT_NEAR(103.81726, latlon[1], 1e-8);
|
||||
}
|
||||
|
||||
|
||||
TEST(Geohash_Test, Precision)
|
||||
{
|
||||
Geohash gh = Geohash();
|
||||
std::string hash;
|
||||
EXPECT_NO_THROW(hash = gh.encode(52.205, 0.119, 6));
|
||||
EXPECT_EQ(0, hash.compare("u120fx"));
|
||||
EXPECT_NO_THROW(hash = gh.encode(52.205, 0.119, 5));
|
||||
EXPECT_EQ(0, hash.compare("u120f"));
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*!
|
||||
* \file nmea_printer_test.cc
|
||||
* \brief Implements Unit Tests for the Nmea_Printer class.
|
||||
* \author Carles Fernandez-Prades, 2017. 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 "gnss_sdr_filesystem.h"
|
||||
#include "nmea_printer.h"
|
||||
#include "pvt_conf.h"
|
||||
#include "rtklib_rtkpos.h"
|
||||
#include "rtklib_solver.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
|
||||
class NmeaPrinterTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
NmeaPrinterTest()
|
||||
{
|
||||
this->conf();
|
||||
}
|
||||
~NmeaPrinterTest() = default;
|
||||
void conf();
|
||||
rtk_t rtk;
|
||||
};
|
||||
|
||||
void NmeaPrinterTest::conf()
|
||||
{
|
||||
snrmask_t snrmask = {{}, {{}, {}}};
|
||||
int positioning_mode = 0; // Single
|
||||
int number_of_frequencies = 1;
|
||||
double elevation_mask = 5;
|
||||
int navigation_system = 1; // GPS
|
||||
int integer_ambiguity_resolution_gps = 0;
|
||||
int integer_ambiguity_resolution_glo = 0;
|
||||
int integer_ambiguity_resolution_bds = 0;
|
||||
int outage_reset_ambiguity = 5;
|
||||
int min_lock_to_fix_ambiguity = 0;
|
||||
int iono_model = 0;
|
||||
int trop_model = 0;
|
||||
int dynamics_model = 0;
|
||||
int earth_tide = 0;
|
||||
int number_filter_iter = 1;
|
||||
double code_phase_error_ratio_l1 = 100.0;
|
||||
double code_phase_error_ratio_l2 = 100.0;
|
||||
double code_phase_error_ratio_l5 = 100.0;
|
||||
double carrier_phase_error_factor_a = 0.003;
|
||||
double carrier_phase_error_factor_b = 0.003;
|
||||
double bias_0 = 30.0;
|
||||
double iono_0 = 0.03;
|
||||
double trop_0 = 0.3;
|
||||
double sigma_bias = 1e-4;
|
||||
double sigma_iono = 1e-3;
|
||||
double sigma_trop = 1e-4;
|
||||
double sigma_acch = 1e-1;
|
||||
double sigma_accv = 1e-2;
|
||||
double sigma_pos = 0.0;
|
||||
double min_ratio_to_fix_ambiguity = 3.0;
|
||||
double min_elevation_to_fix_ambiguity = 0.0;
|
||||
double slip_threshold = 0.05;
|
||||
double threshold_reject_innovation = 30.0;
|
||||
double threshold_reject_gdop = 30.0;
|
||||
int sat_PCV = 0;
|
||||
int rec_PCV = 0;
|
||||
int phwindup = 0;
|
||||
int reject_GPS_IIA = 0;
|
||||
int raim_fde = 0;
|
||||
|
||||
prcopt_t rtklib_configuration_options = {
|
||||
positioning_mode, /* positioning mode (PMODE_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
0, /* solution type (0:forward,1:backward,2:combined) */
|
||||
number_of_frequencies, /* number of frequencies (1:L1, 2:L1+L2, 3:L1+L2+L5)*/
|
||||
navigation_system, /* navigation system */
|
||||
elevation_mask * D2R, /* elevation mask angle (degrees) */
|
||||
snrmask, /* snrmask_t snrmask SNR mask */
|
||||
0, /* satellite ephemeris/clock (EPHOPT_XXX) */
|
||||
integer_ambiguity_resolution_gps, /* AR mode (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */
|
||||
integer_ambiguity_resolution_glo, /* GLONASS AR mode (0:off,1:on,2:auto cal,3:ext cal) */
|
||||
integer_ambiguity_resolution_bds, /* BeiDou AR mode (0:off,1:on) */
|
||||
outage_reset_ambiguity, /* obs outage count to reset bias */
|
||||
min_lock_to_fix_ambiguity, /* min lock count to fix ambiguity */
|
||||
10, /* min fix count to hold ambiguity */
|
||||
1, /* max iteration to resolve ambiguity */
|
||||
iono_model, /* ionosphere option (IONOOPT_XXX) */
|
||||
trop_model, /* troposphere option (TROPOPT_XXX) */
|
||||
dynamics_model, /* dynamics model (0:none, 1:velocity, 2:accel) */
|
||||
earth_tide, /* earth tide correction (0:off,1:solid,2:solid+otl+pole) */
|
||||
number_filter_iter, /* number of filter iteration */
|
||||
0, /* code smoothing window size (0:none) */
|
||||
0, /* interpolate reference obs (for post mission) */
|
||||
0, /* sbssat_t sbssat SBAS correction options */
|
||||
0, /* sbsion_t sbsion[MAXBAND+1] SBAS satellite selection (0:all) */
|
||||
0, /* rover position for fixed mode */
|
||||
0, /* base position for relative mode */
|
||||
/* 0:pos in prcopt, 1:average of single pos, */
|
||||
/* 2:read from file, 3:rinex header, 4:rtcm pos */
|
||||
{code_phase_error_ratio_l1, code_phase_error_ratio_l2, code_phase_error_ratio_l5}, /* eratio[NFREQ] code/phase error ratio */
|
||||
{100.0, carrier_phase_error_factor_a, carrier_phase_error_factor_b, 0.0, 1.0}, /* err[5]: measurement error factor [0]:reserved, [1-3]:error factor a/b/c of phase (m) , [4]:doppler frequency (hz) */
|
||||
{bias_0, iono_0, trop_0}, /* std[3]: initial-state std [0]bias,[1]iono [2]trop*/
|
||||
{sigma_bias, sigma_iono, sigma_trop, sigma_acch, sigma_accv, sigma_pos}, /* prn[6] process-noise std */
|
||||
5e-12, /* sclkstab: satellite clock stability (sec/sec) */
|
||||
{min_ratio_to_fix_ambiguity, 0.9999, 0.25, 0.1, 0.05, 0.0, 0.0, 0.0}, /* thresar[8]: AR validation threshold */
|
||||
min_elevation_to_fix_ambiguity, /* elevation mask of AR for rising satellite (deg) */
|
||||
0.0, /* elevation mask to hold ambiguity (deg) */
|
||||
slip_threshold, /* slip threshold of geometry-free phase (m) */
|
||||
30.0, /* max difference of time (sec) */
|
||||
threshold_reject_innovation, /* reject threshold of innovation (m) */
|
||||
threshold_reject_gdop, /* reject threshold of gdop */
|
||||
{}, /* double baseline[2] baseline length constraint {const,sigma} (m) */
|
||||
{}, /* double ru[3] rover position for fixed mode {x,y,z} (ecef) (m) */
|
||||
{}, /* double rb[3] base position for relative mode {x,y,z} (ecef) (m) */
|
||||
{"", ""}, /* char anttype[2][MAXANT] antenna types {rover,base} */
|
||||
{{}, {}}, /* double antdel[2][3] antenna delta {{rov_e,rov_n,rov_u},{ref_e,ref_n,ref_u}} */
|
||||
{}, /* pcv_t pcvr[2] receiver antenna parameters {rov,base} */
|
||||
{}, /* unsigned char exsats[MAXSAT] excluded satellites (1:excluded, 2:included) */
|
||||
0, /* max averaging epoches */
|
||||
0, /* initialize by restart */
|
||||
1, /* output single by dgps/float/fix/ppp outage */
|
||||
{"", ""}, /* char rnxopt[2][256] rinex options {rover,base} */
|
||||
{sat_PCV, rec_PCV, phwindup, reject_GPS_IIA, raim_fde}, /* posopt[6] positioning options [0]: satellite and receiver antenna PCV model; [1]: interpolate antenna parameters; [2]: apply phase wind-up correction for PPP modes; [3]: exclude measurements of GPS Block IIA satellites satellite [4]: RAIM FDE (fault detection and exclusion) [5]: handle day-boundary clock jump */
|
||||
0, /* solution sync mode (0:off,1:on) */
|
||||
{{}, {}}, /* odisp[2][6*11] ocean tide loading parameters {rov,base} */
|
||||
{{}, {{}, {}}, {{}, {}}, {}, {}}, /* exterr_t exterr extended receiver error model */
|
||||
0, /* disable L2-AR */
|
||||
{}, /* char pppopt[256] ppp option "-GAP_RESION=" default gap to reset iono parameters (ep) */
|
||||
true /* enable Bancroft initialization for the first iteration of the PVT computation, useful in some geometries */
|
||||
};
|
||||
|
||||
rtkinit(&rtk, &rtklib_configuration_options);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(NmeaPrinterTest, PrintLine)
|
||||
{
|
||||
std::string filename("nmea_test.nmea");
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
std::shared_ptr<Rtklib_Solver> pvt_solution = std::make_shared<Rtklib_Solver>(rtk, conf, "filename", 1, false, false);
|
||||
|
||||
boost::posix_time::ptime pt(boost::gregorian::date(1994, boost::date_time::Nov, 19),
|
||||
boost::posix_time::hours(22) + boost::posix_time::minutes(54) + boost::posix_time::seconds(46));
|
||||
std::time_t tim = (pt - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds();
|
||||
gtime_t gtime;
|
||||
gtime.time = tim;
|
||||
gtime.sec = 0.0;
|
||||
|
||||
pvt_solution->pvt_sol.rr[0] = -2282104.0; // 49.27416667;
|
||||
pvt_solution->pvt_sol.rr[1] = -3489369.0; // -123.18533333;
|
||||
pvt_solution->pvt_sol.rr[2] = 4810507.0; // 0
|
||||
pvt_solution->pvt_sol.rr[3] = 0.0;
|
||||
pvt_solution->pvt_sol.rr[4] = 0.0;
|
||||
pvt_solution->pvt_sol.rr[5] = 0.0;
|
||||
pvt_solution->pvt_sol.stat = 1; // SOLQ_FIX
|
||||
pvt_solution->pvt_sol.time = gtime;
|
||||
|
||||
bool flag_nmea_output_file = true;
|
||||
ASSERT_NO_THROW({
|
||||
std::shared_ptr<Nmea_Printer> nmea_printer = std::make_shared<Nmea_Printer>(filename, flag_nmea_output_file, false, "");
|
||||
nmea_printer->Print_Nmea_Line(pvt_solution.get());
|
||||
}) << "Failure printing NMEA messages.";
|
||||
|
||||
std::ifstream test_file(filename);
|
||||
std::string line;
|
||||
std::string GPRMC("$GPRMC");
|
||||
if (test_file.is_open())
|
||||
{
|
||||
while (getline(test_file, line))
|
||||
{
|
||||
std::size_t found = line.find(GPRMC);
|
||||
if (found != std::string::npos)
|
||||
{
|
||||
EXPECT_EQ(line, "$GPRMC,225436.00,A,4916.4497617,N,12311.1202744,W,0.00,0.00,191194,0.0,E,D*21\r");
|
||||
}
|
||||
}
|
||||
test_file.close();
|
||||
}
|
||||
errorlib::error_code ec;
|
||||
EXPECT_EQ(true, fs::remove(fs::path(filename), ec)) << "Failure deleting a temporary file.";
|
||||
}
|
||||
1035
tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc
Normal file
1035
tests/unit-tests/signal-processing-blocks/pvt/rinex_printer_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
/*!
|
||||
* \file rtcm_printer_test.cc
|
||||
* \brief Implements Unit Test for the Rtcm_Printer class.
|
||||
* \author Carles Fernandez-Prades, 2013. 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 "gnss_sdr_make_unique.h"
|
||||
#include "rtcm_printer.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
TEST(RtcmPrinterTest, Instantiate)
|
||||
{
|
||||
std::string filename = "hello.rtcm";
|
||||
bool flag_rtcm_tty_port = false;
|
||||
std::string rtcm_dump_devname = "/dev/pts/4";
|
||||
bool flag_rtcm_server = false;
|
||||
bool rtcm_file_output_enabled = false;
|
||||
unsigned short rtcm_tcp_port = 2101;
|
||||
unsigned short rtcm_station_id = 1234;
|
||||
auto RTCM_printer = std::make_unique<Rtcm_Printer>(filename, rtcm_file_output_enabled, flag_rtcm_server, flag_rtcm_tty_port, rtcm_tcp_port, rtcm_station_id, rtcm_dump_devname);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmPrinterTest, Run)
|
||||
{
|
||||
std::string filename = "test.rtcm";
|
||||
bool flag_rtcm_tty_port = false;
|
||||
bool rtcm_file_output_enabled = false;
|
||||
std::string rtcm_dump_devname = "/dev/pts/4";
|
||||
bool flag_rtcm_server = false;
|
||||
unsigned short rtcm_tcp_port = 2101;
|
||||
unsigned short rtcm_station_id = 1234;
|
||||
|
||||
auto RTCM_printer = std::make_unique<Rtcm_Printer>(filename, rtcm_file_output_enabled, flag_rtcm_server, flag_rtcm_tty_port, rtcm_tcp_port, rtcm_station_id, rtcm_dump_devname);
|
||||
|
||||
std::string reference_msg = "D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B98";
|
||||
|
||||
/* Convert the reference message to binary data */
|
||||
std::string reference_msg_binary;
|
||||
unsigned char c[1];
|
||||
for (unsigned int i = 0; i < reference_msg.length(); i = i + 2)
|
||||
{
|
||||
uint64_t n, n2;
|
||||
std::istringstream(reference_msg.substr(i, 1)) >> std::hex >> n;
|
||||
std::istringstream(reference_msg.substr(i + 1, 1)) >> std::hex >> n2;
|
||||
c[0] = static_cast<unsigned char>(n * 16) + static_cast<unsigned char>(n2);
|
||||
std::string ret(c, c + 1);
|
||||
reference_msg_binary += ret;
|
||||
}
|
||||
|
||||
std::string testing_msg = RTCM_printer->print_MT1005_test();
|
||||
|
||||
EXPECT_EQ(0, reference_msg_binary.compare(testing_msg));
|
||||
}
|
||||
630
tests/unit-tests/signal-processing-blocks/pvt/rtcm_test.cc
Normal file
630
tests/unit-tests/signal-processing-blocks/pvt/rtcm_test.cc
Normal file
@@ -0,0 +1,630 @@
|
||||
/*!
|
||||
* \file rtcm_test.cc
|
||||
* \brief This file implements unit tests for the Rtcm class.
|
||||
* \author Carles Fernandez-Prades, 2015. 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 "Galileo_INAV.h"
|
||||
#include "rtcm.h"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
TEST(RtcmTest, HexToBin)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
|
||||
std::string test1 = "2A";
|
||||
std::string test1_bin = rtcm->hex_to_bin(test1);
|
||||
EXPECT_EQ(0, test1_bin.compare("00101010"));
|
||||
|
||||
std::string test2 = "FF";
|
||||
std::string test2_bin = rtcm->hex_to_bin(test2);
|
||||
EXPECT_EQ(0, test2_bin.compare("11111111"));
|
||||
|
||||
std::string test3 = "ff";
|
||||
std::string test3_bin = rtcm->hex_to_bin(test3);
|
||||
EXPECT_EQ(0, test3_bin.compare("11111111"));
|
||||
|
||||
std::string test4 = "100";
|
||||
std::string test4_bin = rtcm->hex_to_bin(test4);
|
||||
EXPECT_EQ(0, test4_bin.compare("000100000000"));
|
||||
|
||||
std::string test5 = "1101";
|
||||
std::string test5_bin = rtcm->hex_to_bin(test5);
|
||||
EXPECT_EQ(0, test5_bin.compare("0001000100000001"));
|
||||
|
||||
std::string test6 = "3";
|
||||
std::string test6_bin = rtcm->hex_to_bin(test6);
|
||||
EXPECT_EQ(0, test6_bin.compare("0011"));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, BinToHex)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
|
||||
std::string test1 = "00101010";
|
||||
std::string test1_hex = rtcm->bin_to_hex(test1);
|
||||
EXPECT_EQ(0, test1_hex.compare("2A"));
|
||||
|
||||
std::string test2 = "11111111";
|
||||
std::string test2_hex = rtcm->bin_to_hex(test2);
|
||||
EXPECT_EQ(0, test2_hex.compare("FF"));
|
||||
|
||||
std::string test4 = "000100000000";
|
||||
std::string test4_hex = rtcm->bin_to_hex(test4);
|
||||
EXPECT_EQ(0, test4_hex.compare("100"));
|
||||
|
||||
std::string test5 = "0001000100000001";
|
||||
std::string test5_hex = rtcm->bin_to_hex(test5);
|
||||
EXPECT_EQ(0, test5_hex.compare("1101"));
|
||||
|
||||
std::string test6 = "0011";
|
||||
std::string test6_hex = rtcm->bin_to_hex(test6);
|
||||
EXPECT_EQ(0, test6_hex.compare("3"));
|
||||
|
||||
std::string test7 = "11";
|
||||
std::string test7_hex = rtcm->bin_to_hex(test7);
|
||||
EXPECT_EQ(0, test7_hex.compare("3"));
|
||||
|
||||
std::string test8 = "1000100000001";
|
||||
std::string test8_hex = rtcm->bin_to_hex(test8);
|
||||
EXPECT_EQ(0, test8_hex.compare("1101"));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, HexToInt)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
|
||||
std::string test1 = "2A";
|
||||
int64_t test1_int = rtcm->hex_to_int(test1);
|
||||
int64_t expected1 = 42;
|
||||
EXPECT_EQ(expected1, test1_int);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, HexToUint)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
uint64_t expected1 = 42;
|
||||
EXPECT_EQ(expected1, rtcm->hex_to_uint(rtcm->bin_to_hex("00101010")));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, BinToDouble)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
|
||||
std::bitset<4> test1(5);
|
||||
int64_t test1_int = static_cast<int64_t>(rtcm->bin_to_double(test1.to_string()));
|
||||
int64_t expected1 = 5;
|
||||
EXPECT_EQ(expected1, test1_int);
|
||||
|
||||
std::bitset<4> test2(-5);
|
||||
EXPECT_DOUBLE_EQ(-5, rtcm->bin_to_double(test2.to_string()));
|
||||
|
||||
std::bitset<65> test3(-5);
|
||||
EXPECT_DOUBLE_EQ(0, rtcm->bin_to_double(test3.to_string()));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, BinToUint)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
uint32_t expected1 = 42;
|
||||
EXPECT_EQ(expected1, rtcm->bin_to_uint("00101010"));
|
||||
uint32_t expected2 = 214;
|
||||
EXPECT_EQ(expected2, rtcm->bin_to_uint("11010110"));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, BinToInt)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
int32_t expected1 = 42;
|
||||
EXPECT_EQ(expected1, rtcm->bin_to_int("00101010"));
|
||||
int32_t expected2 = -42;
|
||||
EXPECT_EQ(expected2, rtcm->bin_to_int("11010110"));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, BinToBinaryData)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
std::string bin_str("1101101011010110");
|
||||
std::string data_str = rtcm->bin_to_binary_data(bin_str);
|
||||
|
||||
std::string test_binary = data_str.substr(0, 1);
|
||||
std::string test_bin = rtcm->binary_data_to_bin(test_binary);
|
||||
std::string test_hex = rtcm->bin_to_hex(test_bin);
|
||||
EXPECT_EQ(0, test_hex.compare("DA"));
|
||||
|
||||
std::string recovered_str = rtcm->binary_data_to_bin(data_str);
|
||||
EXPECT_EQ(0, recovered_str.compare(bin_str));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, CheckCRC)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
bool expected_true = true;
|
||||
bool expected_false = false;
|
||||
std::string good_crc = rtcm->bin_to_binary_data(rtcm->hex_to_bin("D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B98"));
|
||||
std::string bad_crc = rtcm->bin_to_binary_data(rtcm->hex_to_bin("D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B99"));
|
||||
EXPECT_EQ(expected_true, rtcm->check_CRC(good_crc));
|
||||
EXPECT_EQ(expected_false, rtcm->check_CRC(bad_crc));
|
||||
|
||||
EXPECT_EQ(expected_true, rtcm->check_CRC(rtcm->print_MT1005_test()));
|
||||
EXPECT_EQ(expected_true, rtcm->check_CRC(rtcm->print_MT1005_test())); // Run twice to check that CRC has no memory
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1001)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
Gps_Ephemeris gps_eph = Gps_Ephemeris();
|
||||
Gnss_Synchro gnss_synchro;
|
||||
gnss_synchro.PRN = 2;
|
||||
std::string sys = "G";
|
||||
bool expected_true = true;
|
||||
unsigned short station_id = 1234;
|
||||
|
||||
std::string sig = "1C";
|
||||
gnss_synchro.System = *sys.c_str();
|
||||
std::memcpy(static_cast<void*>(gnss_synchro.Signal), sig.c_str(), 3);
|
||||
gnss_synchro.Pseudorange_m = 20000000.0;
|
||||
double obs_time = 25.0;
|
||||
std::map<int, Gnss_Synchro> pseudoranges;
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro));
|
||||
|
||||
std::string MT1001 = rtcm->print_MT1001(gps_eph, obs_time, pseudoranges, station_id);
|
||||
EXPECT_EQ(expected_true, rtcm->check_CRC(MT1001));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1005)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
std::string reference_msg = rtcm->print_MT1005_test();
|
||||
std::string reference_msg2 = rtcm->print_MT1005(2003, 1114104.5999, -4850729.7108, 3975521.4643, true, false, false, false, false, 0);
|
||||
EXPECT_EQ(0, reference_msg.compare(reference_msg2));
|
||||
|
||||
unsigned int ref_id;
|
||||
double ecef_x;
|
||||
double ecef_y;
|
||||
double ecef_z;
|
||||
bool gps;
|
||||
bool glonass;
|
||||
bool galileo;
|
||||
bool expected_true = true;
|
||||
bool expected_false = false;
|
||||
|
||||
rtcm->read_MT1005(reference_msg, ref_id, ecef_x, ecef_y, ecef_z, gps, glonass, galileo);
|
||||
|
||||
EXPECT_EQ(expected_true, gps);
|
||||
EXPECT_EQ(expected_false, glonass);
|
||||
EXPECT_EQ(expected_false, galileo);
|
||||
|
||||
EXPECT_EQ(static_cast<unsigned int>(2003), ref_id);
|
||||
EXPECT_DOUBLE_EQ(1114104.5999, ecef_x);
|
||||
EXPECT_DOUBLE_EQ(-4850729.7108, ecef_y);
|
||||
EXPECT_DOUBLE_EQ(3975521.4643, ecef_z);
|
||||
|
||||
gps = false;
|
||||
ecef_x = 0.0;
|
||||
|
||||
rtcm->read_MT1005(rtcm->bin_to_binary_data(rtcm->hex_to_bin("D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B98")), ref_id, ecef_x, ecef_y, ecef_z, gps, glonass, galileo);
|
||||
|
||||
EXPECT_EQ(expected_true, gps);
|
||||
EXPECT_EQ(expected_false, glonass);
|
||||
EXPECT_EQ(expected_false, galileo);
|
||||
|
||||
EXPECT_EQ(static_cast<unsigned int>(2003), ref_id);
|
||||
EXPECT_DOUBLE_EQ(1114104.5999, ecef_x);
|
||||
EXPECT_DOUBLE_EQ(-4850729.7108, ecef_y);
|
||||
EXPECT_DOUBLE_EQ(3975521.4643, ecef_z);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1019)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
bool expected_true = true;
|
||||
|
||||
Gps_Ephemeris gps_eph = Gps_Ephemeris();
|
||||
Gps_Ephemeris gps_eph_read = Gps_Ephemeris();
|
||||
|
||||
gps_eph.PRN = 3;
|
||||
gps_eph.IODC = 4;
|
||||
gps_eph.ecc = 2.0 * ECCENTRICITY_LSB;
|
||||
gps_eph.fit_interval_flag = true;
|
||||
std::string tx_msg = rtcm->print_MT1019(gps_eph);
|
||||
|
||||
EXPECT_EQ(0, rtcm->read_MT1019(tx_msg, gps_eph_read));
|
||||
EXPECT_EQ(static_cast<unsigned int>(3), gps_eph_read.PRN);
|
||||
EXPECT_DOUBLE_EQ(4, gps_eph_read.IODC);
|
||||
EXPECT_DOUBLE_EQ(2.0 * ECCENTRICITY_LSB, gps_eph_read.ecc);
|
||||
EXPECT_EQ(expected_true, gps_eph_read.fit_interval_flag);
|
||||
EXPECT_EQ(1, rtcm->read_MT1019(rtcm->bin_to_binary_data(rtcm->hex_to_bin("FFFFFFFFFFF")), gps_eph_read));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1020)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
|
||||
// Objects to populate the ephemeris and utc fields
|
||||
Glonass_Gnav_Ephemeris gnav_ephemeris = Glonass_Gnav_Ephemeris();
|
||||
Glonass_Gnav_Utc_Model gnav_utc_model = Glonass_Gnav_Utc_Model();
|
||||
// Objects read, used for comparison
|
||||
Glonass_Gnav_Ephemeris gnav_ephemeris_read = Glonass_Gnav_Ephemeris();
|
||||
Glonass_Gnav_Utc_Model gnav_utc_model_read = Glonass_Gnav_Utc_Model();
|
||||
|
||||
// Perform data read and print of special values types
|
||||
gnav_ephemeris.d_P_1 = 0.;
|
||||
// Bit distribution per fields
|
||||
gnav_ephemeris.d_t_k = 7560.;
|
||||
// Glonass signed values
|
||||
gnav_ephemeris.d_VXn = -0.490900039672852;
|
||||
// Bit distribution per fields dependent on other factors
|
||||
gnav_ephemeris.d_t_b = 8100.;
|
||||
// Binary flag representation
|
||||
gnav_ephemeris.d_P_3 = true;
|
||||
|
||||
std::string tx_msg = rtcm->print_MT1020(gnav_ephemeris, gnav_utc_model);
|
||||
|
||||
EXPECT_EQ(0, rtcm->read_MT1020(tx_msg, gnav_ephemeris_read, gnav_utc_model_read));
|
||||
EXPECT_EQ(gnav_ephemeris.d_P_1, gnav_ephemeris_read.d_P_1);
|
||||
EXPECT_TRUE(gnav_ephemeris.d_t_b - gnav_ephemeris_read.d_t_b < FLT_EPSILON);
|
||||
EXPECT_TRUE(gnav_ephemeris.d_VXn - gnav_ephemeris_read.d_VXn < FLT_EPSILON);
|
||||
EXPECT_TRUE(gnav_ephemeris.d_t_k - gnav_ephemeris.d_t_k < FLT_EPSILON);
|
||||
EXPECT_EQ(gnav_ephemeris.d_P_3, gnav_ephemeris_read.d_P_3);
|
||||
EXPECT_EQ(1, rtcm->read_MT1020(rtcm->bin_to_binary_data(rtcm->hex_to_bin("FFFFFFFFFFF")), gnav_ephemeris_read, gnav_utc_model_read));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1029)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
std::string s_test("UTF-8 проверка wörter");
|
||||
unsigned int ref_id = 23;
|
||||
double obs_time = 0;
|
||||
Gps_Ephemeris gps_eph = Gps_Ephemeris();
|
||||
std::string m1029 = rtcm->bin_to_hex(rtcm->binary_data_to_bin(rtcm->print_MT1029(ref_id, gps_eph, obs_time, s_test)));
|
||||
std::string encoded_text = m1029.substr(24, 60);
|
||||
std::string expected_encoded_text("5554462D3820D0BFD180D0BED0B2D0B5D180D0BAD0B02077C3B672746572");
|
||||
EXPECT_EQ(0, expected_encoded_text.compare(encoded_text));
|
||||
|
||||
std::string characters_to_follow = m1029.substr(22, 2);
|
||||
std::string expected_characters_to_follow("1E");
|
||||
EXPECT_EQ(0, expected_characters_to_follow.compare(characters_to_follow));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MT1045)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
bool expected_true = true;
|
||||
|
||||
Galileo_Ephemeris gal_eph = Galileo_Ephemeris();
|
||||
Galileo_Ephemeris gal_eph_read = Galileo_Ephemeris();
|
||||
|
||||
gal_eph.PRN = 5;
|
||||
gal_eph.OMEGAdot = 53.0 * OMEGA_DOT_3_LSB;
|
||||
gal_eph.E5a_DVS = true;
|
||||
|
||||
std::string tx_msg = rtcm->print_MT1045(gal_eph);
|
||||
|
||||
EXPECT_EQ(0, rtcm->read_MT1045(tx_msg, gal_eph_read));
|
||||
EXPECT_EQ(expected_true, gal_eph_read.E5a_DVS);
|
||||
EXPECT_DOUBLE_EQ(53.0 * OMEGA_DOT_3_LSB, gal_eph_read.OMEGAdot);
|
||||
EXPECT_EQ(static_cast<unsigned int>(5), gal_eph_read.PRN);
|
||||
EXPECT_EQ(1, rtcm->read_MT1045(rtcm->bin_to_binary_data(rtcm->hex_to_bin("FFFFFFFFFFF")), gal_eph_read));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MSMCell)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
Gps_Ephemeris gps_eph = Gps_Ephemeris();
|
||||
Galileo_Ephemeris gal_eph = Galileo_Ephemeris();
|
||||
// Glonass_Gnav_Ephemeris glo_gnav_eph = Glonass_Gnav_Ephemeris();
|
||||
std::map<int, Gnss_Synchro> pseudoranges;
|
||||
|
||||
Gnss_Synchro gnss_synchro;
|
||||
Gnss_Synchro gnss_synchro2;
|
||||
Gnss_Synchro gnss_synchro3;
|
||||
Gnss_Synchro gnss_synchro4;
|
||||
Gnss_Synchro gnss_synchro5;
|
||||
Gnss_Synchro gnss_synchro6;
|
||||
|
||||
gnss_synchro.PRN = 4;
|
||||
gnss_synchro2.PRN = 8;
|
||||
gnss_synchro3.PRN = 32;
|
||||
gnss_synchro4.PRN = 10;
|
||||
gnss_synchro5.PRN = 10;
|
||||
gnss_synchro6.PRN = 10;
|
||||
|
||||
std::string gps = "G";
|
||||
std::string gal = "E";
|
||||
std::string glo = "R";
|
||||
|
||||
std::string c1 = "1C";
|
||||
std::string s2 = "2S";
|
||||
std::string x5 = "5X";
|
||||
|
||||
gnss_synchro.System = *gal.c_str();
|
||||
gnss_synchro2.System = *gps.c_str();
|
||||
gnss_synchro3.System = *gps.c_str();
|
||||
gnss_synchro4.System = *gal.c_str();
|
||||
gnss_synchro5.System = *gps.c_str();
|
||||
gnss_synchro6.System = *glo.c_str();
|
||||
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro.Signal), x5.c_str(), 3);
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro2.Signal), s2.c_str(), 3);
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro3.Signal), c1.c_str(), 3);
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro4.Signal), x5.c_str(), 3);
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro5.Signal), c1.c_str(), 3);
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro6.Signal), c1.c_str(), 3);
|
||||
|
||||
gnss_synchro.Pseudorange_m = 20000000.0;
|
||||
gnss_synchro2.Pseudorange_m = 20001010.0;
|
||||
gnss_synchro3.Pseudorange_m = 24002020.0;
|
||||
gnss_synchro4.Pseudorange_m = 20003010.1;
|
||||
gnss_synchro5.Pseudorange_m = 22003010.1;
|
||||
gnss_synchro6.Pseudorange_m = 22003010.1;
|
||||
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(2, gnss_synchro2));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(3, gnss_synchro3));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(4, gnss_synchro4));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(5, gnss_synchro5));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(6, gnss_synchro5));
|
||||
|
||||
unsigned int ref_id = 1234;
|
||||
unsigned int clock_steering_indicator = 0;
|
||||
unsigned int external_clock_indicator = 0;
|
||||
int smooth_int = 0;
|
||||
bool divergence_free = false;
|
||||
bool more_messages = false;
|
||||
double obs_time = 25.0;
|
||||
|
||||
gps_eph.PRN = gnss_synchro2.PRN;
|
||||
gal_eph.PRN = gnss_synchro.PRN;
|
||||
// glo_gnav_eph.PRN = gnss_synchro.PRN;
|
||||
|
||||
std::string MSM1 = rtcm->print_MSM_1(gps_eph,
|
||||
{},
|
||||
gal_eph,
|
||||
{},
|
||||
obs_time,
|
||||
pseudoranges,
|
||||
ref_id,
|
||||
clock_steering_indicator,
|
||||
external_clock_indicator,
|
||||
smooth_int,
|
||||
divergence_free,
|
||||
more_messages);
|
||||
|
||||
std::string MSM1_bin = rtcm->binary_data_to_bin(MSM1);
|
||||
unsigned int Nsat = 4;
|
||||
unsigned int Nsig = 3;
|
||||
unsigned int size_header = 14;
|
||||
unsigned int size_msg_length = 10;
|
||||
EXPECT_EQ(0, MSM1_bin.substr(size_header + size_msg_length + 169, Nsat * Nsig).compare("001010101100")); // check cell mask
|
||||
|
||||
std::map<int, Gnss_Synchro> pseudoranges2;
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro6));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro5));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(2, gnss_synchro4));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(3, gnss_synchro3));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(4, gnss_synchro2));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(5, gnss_synchro));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(6, gnss_synchro));
|
||||
std::string MSM1_2 = rtcm->print_MSM_1(gps_eph,
|
||||
{},
|
||||
gal_eph,
|
||||
{},
|
||||
obs_time,
|
||||
pseudoranges2,
|
||||
ref_id,
|
||||
clock_steering_indicator,
|
||||
external_clock_indicator,
|
||||
smooth_int,
|
||||
divergence_free,
|
||||
more_messages);
|
||||
std::string MSM1_bin_2 = rtcm->binary_data_to_bin(MSM1_2);
|
||||
EXPECT_EQ(0, MSM1_bin_2.substr(size_header + size_msg_length + 169, Nsat * Nsig).compare("001010001100")); // check cell mask
|
||||
|
||||
Gnss_Synchro gnss_synchro7;
|
||||
gnss_synchro7.PRN = 10;
|
||||
gnss_synchro7.System = *gps.c_str();
|
||||
std::memcpy(reinterpret_cast<void*>(gnss_synchro7.Signal), s2.c_str(), 3);
|
||||
gnss_synchro7.Pseudorange_m = 24000000.0;
|
||||
|
||||
std::map<int, Gnss_Synchro> pseudoranges3;
|
||||
pseudoranges3.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro));
|
||||
pseudoranges3.insert(std::pair<int, Gnss_Synchro>(2, gnss_synchro2));
|
||||
pseudoranges3.insert(std::pair<int, Gnss_Synchro>(3, gnss_synchro7));
|
||||
pseudoranges3.insert(std::pair<int, Gnss_Synchro>(4, gnss_synchro4));
|
||||
pseudoranges3.insert(std::pair<int, Gnss_Synchro>(5, gnss_synchro5));
|
||||
|
||||
std::string MSM1_3 = rtcm->print_MSM_1(gps_eph,
|
||||
{},
|
||||
gal_eph,
|
||||
{},
|
||||
obs_time,
|
||||
pseudoranges3,
|
||||
ref_id,
|
||||
clock_steering_indicator,
|
||||
external_clock_indicator,
|
||||
smooth_int,
|
||||
divergence_free,
|
||||
more_messages);
|
||||
std::string MSM1_bin_3 = rtcm->binary_data_to_bin(MSM1_3);
|
||||
EXPECT_EQ(0, MSM1_bin_3.substr(size_header + size_msg_length + 169, (Nsat - 1) * Nsig).compare("001010111")); // check cell mask
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, MSM1)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
bool expected_true = true;
|
||||
Gps_Ephemeris gps_eph = Gps_Ephemeris();
|
||||
std::map<int, Gnss_Synchro> pseudoranges;
|
||||
|
||||
Gnss_Synchro gnss_synchro;
|
||||
Gnss_Synchro gnss_synchro2;
|
||||
Gnss_Synchro gnss_synchro3;
|
||||
Gnss_Synchro gnss_synchro4;
|
||||
|
||||
gnss_synchro.PRN = 2;
|
||||
gnss_synchro2.PRN = 4;
|
||||
gnss_synchro3.PRN = 32;
|
||||
gnss_synchro4.PRN = 4;
|
||||
|
||||
std::string sys = "G";
|
||||
|
||||
std::string sig = "1C";
|
||||
std::string sig2 = "2S";
|
||||
|
||||
gnss_synchro.System = *sys.c_str();
|
||||
gnss_synchro2.System = *sys.c_str();
|
||||
gnss_synchro3.System = *sys.c_str();
|
||||
gnss_synchro4.System = *sys.c_str();
|
||||
|
||||
std::memcpy(static_cast<void*>(gnss_synchro.Signal), sig.c_str(), 3);
|
||||
std::memcpy(static_cast<void*>(gnss_synchro2.Signal), sig.c_str(), 3);
|
||||
std::memcpy(static_cast<void*>(gnss_synchro3.Signal), sig2.c_str(), 3);
|
||||
std::memcpy(static_cast<void*>(gnss_synchro4.Signal), sig2.c_str(), 3);
|
||||
|
||||
gnss_synchro.Pseudorange_m = 20000000.0;
|
||||
gnss_synchro2.Pseudorange_m = 20001010.0;
|
||||
gnss_synchro3.Pseudorange_m = 24002020.0;
|
||||
gnss_synchro4.Pseudorange_m = 20003010.1;
|
||||
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(2, gnss_synchro2));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(3, gnss_synchro3));
|
||||
pseudoranges.insert(std::pair<int, Gnss_Synchro>(4, gnss_synchro4));
|
||||
|
||||
unsigned short ref_id = 1234;
|
||||
unsigned int clock_steering_indicator = 0;
|
||||
unsigned int external_clock_indicator = 0;
|
||||
int smooth_int = 0;
|
||||
bool divergence_free = false;
|
||||
bool more_messages = false;
|
||||
double obs_time = 25.0;
|
||||
|
||||
gps_eph.PRN = gnss_synchro.PRN;
|
||||
|
||||
std::string MSM1 = rtcm->print_MSM_1(gps_eph,
|
||||
{}, {}, {},
|
||||
obs_time,
|
||||
pseudoranges,
|
||||
ref_id,
|
||||
clock_steering_indicator,
|
||||
external_clock_indicator,
|
||||
smooth_int,
|
||||
divergence_free,
|
||||
more_messages);
|
||||
|
||||
EXPECT_EQ(expected_true, rtcm->check_CRC(MSM1));
|
||||
|
||||
std::string MSM1_bin = rtcm->binary_data_to_bin(MSM1);
|
||||
unsigned int Nsat = 3;
|
||||
unsigned int Nsig = 2;
|
||||
unsigned int size_header = 14;
|
||||
unsigned int size_crc = 24;
|
||||
unsigned int size_msg_length = 10;
|
||||
unsigned int upper_bound = 169 + Nsat * 10 + 43 * Nsig;
|
||||
unsigned int data_size = MSM1_bin.length() - size_header - size_msg_length - size_crc;
|
||||
EXPECT_EQ(expected_true, upper_bound >= data_size);
|
||||
EXPECT_EQ(0, MSM1_bin.substr(0, size_header).compare("11010011000000"));
|
||||
EXPECT_EQ(ref_id, rtcm->bin_to_uint(MSM1_bin.substr(size_header + size_msg_length + 12, 12)));
|
||||
EXPECT_EQ(0, MSM1_bin.substr(size_header + size_msg_length + 169, Nsat * Nsig).compare("101101")); // check cell mask
|
||||
|
||||
double meters_to_miliseconds = SPEED_OF_LIGHT_M_S * 0.001;
|
||||
unsigned int rough_range_1 = static_cast<unsigned int>(std::floor(std::round(gnss_synchro.Pseudorange_m / meters_to_miliseconds / TWO_N10)) + 0.5) & 0x3FFu;
|
||||
unsigned int rough_range_2 = static_cast<unsigned int>(std::floor(std::round(gnss_synchro2.Pseudorange_m / meters_to_miliseconds / TWO_N10)) + 0.5) & 0x3FFu;
|
||||
unsigned int rough_range_4 = static_cast<unsigned int>(std::floor(std::round(gnss_synchro3.Pseudorange_m / meters_to_miliseconds / TWO_N10)) + 0.5) & 0x3FFu;
|
||||
unsigned int read_pseudorange_1 = rtcm->bin_to_uint(MSM1_bin.substr(size_header + size_msg_length + 169 + Nsat * Nsig, 10));
|
||||
unsigned int read_pseudorange_2 = rtcm->bin_to_uint(MSM1_bin.substr(size_header + size_msg_length + 169 + Nsat * Nsig + 10, 10));
|
||||
unsigned int read_pseudorange_4 = rtcm->bin_to_uint(MSM1_bin.substr(size_header + size_msg_length + 169 + Nsat * Nsig + 20, 10));
|
||||
|
||||
EXPECT_EQ(rough_range_1, read_pseudorange_1);
|
||||
EXPECT_EQ(rough_range_2, read_pseudorange_2);
|
||||
EXPECT_EQ(rough_range_4, read_pseudorange_4);
|
||||
|
||||
int psrng4_s = static_cast<int>(std::round((gnss_synchro3.Pseudorange_m - std::round(gnss_synchro3.Pseudorange_m / meters_to_miliseconds / TWO_N10) * meters_to_miliseconds * TWO_N10) / meters_to_miliseconds / TWO_N24));
|
||||
int read_psrng4_s = rtcm->bin_to_int(MSM1_bin.substr(size_header + size_msg_length + 169 + (Nsat * Nsig) + 30 + 15 * 3, 15));
|
||||
EXPECT_EQ(psrng4_s, read_psrng4_s);
|
||||
|
||||
std::map<int, Gnss_Synchro> pseudoranges2;
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(1, gnss_synchro4));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(2, gnss_synchro3));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(3, gnss_synchro2));
|
||||
pseudoranges2.insert(std::pair<int, Gnss_Synchro>(4, gnss_synchro));
|
||||
std::string MSM1_2 = rtcm->print_MSM_1(gps_eph,
|
||||
{}, {}, {},
|
||||
obs_time,
|
||||
pseudoranges2,
|
||||
ref_id,
|
||||
clock_steering_indicator,
|
||||
external_clock_indicator,
|
||||
smooth_int,
|
||||
divergence_free,
|
||||
more_messages);
|
||||
std::string MSM1_bin2 = rtcm->binary_data_to_bin(MSM1_2);
|
||||
int read_psrng4_s_2 = rtcm->bin_to_int(MSM1_bin2.substr(size_header + size_msg_length + 169 + (Nsat * Nsig) + 30 + 15 * 3, 15));
|
||||
EXPECT_EQ(psrng4_s, read_psrng4_s_2);
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, InstantiateServer)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
rtcm->run_server();
|
||||
std::string msg("Hello");
|
||||
rtcm->send_message(msg);
|
||||
std::string test3 = "ff";
|
||||
std::string test3_bin = rtcm->hex_to_bin(test3);
|
||||
EXPECT_EQ(0, test3_bin.compare("11111111"));
|
||||
rtcm->stop_server();
|
||||
std::string test6 = "0011";
|
||||
std::string test6_hex = rtcm->bin_to_hex(test6);
|
||||
EXPECT_EQ(0, test6_hex.compare("3"));
|
||||
uint64_t expected1 = 42;
|
||||
EXPECT_EQ(expected1, rtcm->bin_to_uint("00101010"));
|
||||
rtcm->run_server();
|
||||
std::string test4_bin = rtcm->hex_to_bin(test3);
|
||||
std::string s = rtcm->bin_to_binary_data(test4_bin);
|
||||
rtcm->send_message(s);
|
||||
rtcm->stop_server();
|
||||
EXPECT_EQ(0, test4_bin.compare("11111111"));
|
||||
}
|
||||
|
||||
|
||||
TEST(RtcmTest, InstantiateServerWithoutClosing)
|
||||
{
|
||||
auto rtcm = std::make_shared<Rtcm>();
|
||||
rtcm->run_server();
|
||||
std::string msg("Hello");
|
||||
rtcm->send_message(msg);
|
||||
std::string test3 = "ff";
|
||||
std::string test3_bin = rtcm->hex_to_bin(test3);
|
||||
EXPECT_EQ(0, test3_bin.compare("11111111"));
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
/*!
|
||||
* \file rtklib_solver_test.cc
|
||||
* \brief Implements Unit Test for the rtklib PVT solver class.
|
||||
* \author Javier Arribas, 2018. jarribas(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 "geofunctions.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "gnss_sdr_supl_client.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "pvt_conf.h"
|
||||
#include "rtklib_solver.h"
|
||||
#include <armadillo>
|
||||
#include <boost/archive/xml_iarchive.hpp>
|
||||
#include <boost/archive/xml_oarchive.hpp>
|
||||
#include <boost/serialization/map.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
rtk_t configure_rtklib_options()
|
||||
{
|
||||
std::shared_ptr<InMemoryConfiguration> configuration;
|
||||
configuration = std::make_shared<InMemoryConfiguration>();
|
||||
std::string role = "rtklib_solver";
|
||||
// custom options
|
||||
configuration->set_property("rtklib_solver.positioning_mode", "Single");
|
||||
configuration->set_property("rtklib_solver.elevation_mask", "0");
|
||||
configuration->set_property("rtklib_solver.iono_model", "OFF");
|
||||
configuration->set_property("rtklib_solver.trop_model", "OFF");
|
||||
// RTKLIB PVT solver options
|
||||
|
||||
// Settings 1
|
||||
int positioning_mode = -1;
|
||||
std::string default_pos_mode("Single");
|
||||
std::string positioning_mode_str = configuration->property(role + ".positioning_mode", default_pos_mode); /* (PMODE_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
if (positioning_mode_str == "Single")
|
||||
{
|
||||
positioning_mode = PMODE_SINGLE;
|
||||
}
|
||||
if (positioning_mode_str == "Static")
|
||||
{
|
||||
positioning_mode = PMODE_STATIC;
|
||||
}
|
||||
if (positioning_mode_str == "Kinematic")
|
||||
{
|
||||
positioning_mode = PMODE_KINEMA;
|
||||
}
|
||||
if (positioning_mode_str == "PPP_Static")
|
||||
{
|
||||
positioning_mode = PMODE_PPP_STATIC;
|
||||
}
|
||||
if (positioning_mode_str == "PPP_Kinematic")
|
||||
{
|
||||
positioning_mode = PMODE_PPP_KINEMA;
|
||||
}
|
||||
|
||||
if (positioning_mode == -1)
|
||||
{
|
||||
// warn user and set the default
|
||||
std::cout << "WARNING: Bad specification of positioning mode.\n";
|
||||
std::cout << "positioning_mode possible values: Single / Static / Kinematic / PPP_Static / PPP_Kinematic\n";
|
||||
std::cout << "positioning_mode specified value: " << positioning_mode_str << '\n';
|
||||
std::cout << "Setting positioning_mode to Single\n";
|
||||
positioning_mode = PMODE_SINGLE;
|
||||
}
|
||||
|
||||
int num_bands = 1;
|
||||
|
||||
// if ((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0)) num_bands = 1;
|
||||
// if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0)) && ((gps_2S_count > 0) || (glo_2G_count > 0))) num_bands = 2;
|
||||
// if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0)) && ((gal_E5a_count > 0) || (gal_E5b_count > 0) || (gps_L5_count > 0))) num_bands = 2;
|
||||
// if (((gps_1C_count > 0) || (gal_1B_count > 0) || (glo_1G_count > 0)) && ((gps_2S_count > 0) || (glo_2G_count > 0)) && ((gal_E5a_count > 0) || (gal_E5b_count > 0) || (gps_L5_count > 0))) num_bands = 3;
|
||||
|
||||
int number_of_frequencies = configuration->property(role + ".num_bands", num_bands); /* (1:L1, 2:L1+L2, 3:L1+L2+L5) */
|
||||
if ((number_of_frequencies < 1) || (number_of_frequencies > 3))
|
||||
{
|
||||
// warn user and set the default
|
||||
number_of_frequencies = num_bands;
|
||||
}
|
||||
|
||||
double elevation_mask = configuration->property(role + ".elevation_mask", 15.0);
|
||||
if ((elevation_mask < 0.0) || (elevation_mask > 90.0))
|
||||
{
|
||||
// warn user and set the default
|
||||
LOG(WARNING) << "Erroneous Elevation Mask. Setting to default value of 15.0 degrees";
|
||||
elevation_mask = 15.0;
|
||||
}
|
||||
|
||||
int dynamics_model = configuration->property(role + ".dynamics_model", 0); /* dynamics model (0:none, 1:velocity, 2:accel) */
|
||||
if ((dynamics_model < 0) || (dynamics_model > 2))
|
||||
{
|
||||
// warn user and set the default
|
||||
LOG(WARNING) << "Erroneous Dynamics Model configuration. Setting to default value of (0:none)";
|
||||
dynamics_model = 0;
|
||||
}
|
||||
|
||||
std::string default_iono_model("OFF");
|
||||
std::string iono_model_str = configuration->property(role + ".iono_model", default_iono_model); /* (IONOOPT_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
int iono_model = -1;
|
||||
if (iono_model_str == "OFF")
|
||||
{
|
||||
iono_model = IONOOPT_OFF;
|
||||
}
|
||||
if (iono_model_str == "Broadcast")
|
||||
{
|
||||
iono_model = IONOOPT_BRDC;
|
||||
}
|
||||
if (iono_model_str == "SBAS")
|
||||
{
|
||||
iono_model = IONOOPT_SBAS;
|
||||
}
|
||||
if (iono_model_str == "Iono-Free-LC")
|
||||
{
|
||||
iono_model = IONOOPT_IFLC;
|
||||
}
|
||||
if (iono_model_str == "Estimate_STEC")
|
||||
{
|
||||
iono_model = IONOOPT_EST;
|
||||
}
|
||||
if (iono_model_str == "IONEX")
|
||||
{
|
||||
iono_model = IONOOPT_TEC;
|
||||
}
|
||||
if (iono_model == -1)
|
||||
{
|
||||
// warn user and set the default
|
||||
std::cout << "WARNING: Bad specification of ionospheric model.\n";
|
||||
std::cout << "iono_model possible values: OFF / Broadcast / SBAS / Iono-Free-LC / Estimate_STEC / IONEX\n";
|
||||
std::cout << "iono_model specified value: " << iono_model_str << '\n';
|
||||
std::cout << "Setting iono_model to OFF\n";
|
||||
iono_model = IONOOPT_OFF; /* 0: ionosphere option: correction off */
|
||||
}
|
||||
|
||||
std::string default_trop_model("OFF");
|
||||
int trop_model = -1;
|
||||
std::string trop_model_str = configuration->property(role + ".trop_model", default_trop_model); /* (TROPOPT_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
if (trop_model_str == "OFF")
|
||||
{
|
||||
trop_model = TROPOPT_OFF;
|
||||
}
|
||||
if (trop_model_str == "Saastamoinen")
|
||||
{
|
||||
trop_model = TROPOPT_SAAS;
|
||||
}
|
||||
if (trop_model_str == "SBAS")
|
||||
{
|
||||
trop_model = TROPOPT_SBAS;
|
||||
}
|
||||
if (trop_model_str == "Estimate_ZTD")
|
||||
{
|
||||
trop_model = TROPOPT_EST;
|
||||
}
|
||||
if (trop_model_str == "Estimate_ZTD_Grad")
|
||||
{
|
||||
trop_model = TROPOPT_ESTG;
|
||||
}
|
||||
if (trop_model == -1)
|
||||
{
|
||||
// warn user and set the default
|
||||
std::cout << "WARNING: Bad specification of tropospheric model.\n";
|
||||
std::cout << "trop_model possible values: OFF / Saastamoinen / SBAS / Estimate_ZTD / Estimate_ZTD_Grad\n";
|
||||
std::cout << "trop_model specified value: " << trop_model_str << '\n';
|
||||
std::cout << "Setting trop_model to OFF\n";
|
||||
trop_model = TROPOPT_OFF;
|
||||
}
|
||||
|
||||
/* RTKLIB positioning options */
|
||||
int sat_PCV = 0; /* Set whether the satellite antenna PCV (phase center variation) model is used or not. This feature requires a Satellite Antenna PCV File. */
|
||||
int rec_PCV = 0; /* Set whether the receiver antenna PCV (phase center variation) model is used or not. This feature requires a Receiver Antenna PCV File. */
|
||||
|
||||
/* Set whether the phase windup correction for PPP modes is applied or not. Only applicable to PPP‐* modes.*/
|
||||
int phwindup = configuration->property(role + ".phwindup", 0);
|
||||
|
||||
/* Set whether the GPS Block IIA satellites in eclipse are excluded or not.
|
||||
The eclipsing Block IIA satellites often degrade the PPP solutions due to unpredicted behavior of yaw‐attitude. Only applicable to PPP‐* modes.*/
|
||||
int reject_GPS_IIA = configuration->property(role + ".reject_GPS_IIA", 0);
|
||||
|
||||
/* Set whether RAIM (receiver autonomous integrity monitoring) FDE (fault detection and exclusion) feature is enabled or not.
|
||||
In case of RAIM FDE enabled, a satellite is excluded if SSE (sum of squared errors) of residuals is over a threshold.
|
||||
The excluded satellite is selected to indicate the minimum SSE. */
|
||||
int raim_fde = configuration->property(role + ".raim_fde", 0);
|
||||
|
||||
int earth_tide = configuration->property(role + ".earth_tide", 0);
|
||||
|
||||
int nsys = SYS_GPS;
|
||||
// if ((gps_1C_count > 0) || (gps_2S_count > 0) || (gps_L5_count > 0)) nsys += SYS_GPS;
|
||||
// if ((gal_1B_count > 0) || (gal_E5a_count > 0) || (gal_E5b_count > 0)) nsys += SYS_GAL;
|
||||
// if ((glo_1G_count > 0) || (glo_2G_count > 0)) nsys += SYS_GLO;
|
||||
int navigation_system = configuration->property(role + ".navigation_system", nsys); /* (SYS_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
if ((navigation_system < 1) || (navigation_system > 255)) /* GPS: 1 SBAS: 2 GPS+SBAS: 3 Galileo: 8 Galileo+GPS: 9 GPS+SBAS+Galileo: 11 All: 255 */
|
||||
{
|
||||
// warn user and set the default
|
||||
LOG(WARNING) << "Erroneous Navigation System. Setting to default value of (0:none)";
|
||||
navigation_system = nsys;
|
||||
}
|
||||
|
||||
// Settings 2
|
||||
std::string default_gps_ar("Continuous");
|
||||
std::string integer_ambiguity_resolution_gps_str = configuration->property(role + ".AR_GPS", default_gps_ar); /* Integer Ambiguity Resolution mode for GPS (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */
|
||||
int integer_ambiguity_resolution_gps = -1;
|
||||
if (integer_ambiguity_resolution_gps_str == "OFF")
|
||||
{
|
||||
integer_ambiguity_resolution_gps = ARMODE_OFF;
|
||||
}
|
||||
if (integer_ambiguity_resolution_gps_str == "Continuous")
|
||||
{
|
||||
integer_ambiguity_resolution_gps = ARMODE_CONT;
|
||||
}
|
||||
if (integer_ambiguity_resolution_gps_str == "Instantaneous")
|
||||
{
|
||||
integer_ambiguity_resolution_gps = ARMODE_INST;
|
||||
}
|
||||
if (integer_ambiguity_resolution_gps_str == "Fix-and-Hold")
|
||||
{
|
||||
integer_ambiguity_resolution_gps = ARMODE_FIXHOLD;
|
||||
}
|
||||
if (integer_ambiguity_resolution_gps_str == "PPP-AR")
|
||||
{
|
||||
integer_ambiguity_resolution_gps = ARMODE_PPPAR;
|
||||
}
|
||||
if (integer_ambiguity_resolution_gps == -1)
|
||||
{
|
||||
// warn user and set the default
|
||||
std::cout << "WARNING: Bad specification of GPS ambiguity resolution method.\n";
|
||||
std::cout << "AR_GPS possible values: OFF / Continuous / Instantaneous / Fix-and-Hold / PPP-AR\n";
|
||||
std::cout << "AR_GPS specified value: " << integer_ambiguity_resolution_gps_str << '\n';
|
||||
std::cout << "Setting AR_GPS to OFF\n";
|
||||
integer_ambiguity_resolution_gps = ARMODE_OFF;
|
||||
}
|
||||
|
||||
int integer_ambiguity_resolution_glo = configuration->property(role + ".AR_GLO", 1); /* Integer Ambiguity Resolution mode for GLONASS (0:off,1:on,2:auto cal,3:ext cal) */
|
||||
if ((integer_ambiguity_resolution_glo < 0) || (integer_ambiguity_resolution_glo > 3))
|
||||
{
|
||||
// warn user and set the default
|
||||
LOG(WARNING) << "Erroneous Integer Ambiguity Resolution for GLONASS . Setting to default value of (1:on)";
|
||||
integer_ambiguity_resolution_glo = 1;
|
||||
}
|
||||
|
||||
int integer_ambiguity_resolution_bds = configuration->property(role + ".AR_DBS", 1); /* Integer Ambiguity Resolution mode for BEIDOU (0:off,1:on) */
|
||||
if ((integer_ambiguity_resolution_bds < 0) || (integer_ambiguity_resolution_bds > 1))
|
||||
{
|
||||
// warn user and set the default
|
||||
LOG(WARNING) << "Erroneous Integer Ambiguity Resolution for BEIDOU . Setting to default value of (1:on)";
|
||||
integer_ambiguity_resolution_bds = 1;
|
||||
}
|
||||
|
||||
double min_ratio_to_fix_ambiguity = configuration->property(role + ".min_ratio_to_fix_ambiguity", 3.0); /* Set the integer ambiguity validation threshold for ratio‐test,
|
||||
which uses the ratio of squared residuals of the best integer vector to the second‐best vector. */
|
||||
|
||||
int min_lock_to_fix_ambiguity = configuration->property(role + ".min_lock_to_fix_ambiguity", 0); /* Set the minimum lock count to fix integer ambiguity.
|
||||
If the lock count is less than the value, the ambiguity is excluded from the fixed integer vector. */
|
||||
|
||||
double min_elevation_to_fix_ambiguity = configuration->property(role + ".min_elevation_to_fix_ambiguity", 0.0); /* Set the minimum elevation (deg) to fix integer ambiguity.
|
||||
If the elevation of the satellite is less than the value, the ambiguity is excluded from the fixed integer vector. */
|
||||
|
||||
int outage_reset_ambiguity = configuration->property(role + ".outage_reset_ambiguity", 5); /* Set the outage count to reset ambiguity. If the data outage count is over the value, the estimated ambiguity is reset to the initial value. */
|
||||
|
||||
double slip_threshold = configuration->property(role + ".slip_threshold", 0.05); /* set the cycle‐slip threshold (m) of geometry‐free LC carrier‐phase difference between epochs */
|
||||
|
||||
double threshold_reject_gdop = configuration->property(role + ".threshold_reject_gdop", 30.0); /* reject threshold of GDOP. If the GDOP is over the value, the observable is excluded for the estimation process as an outlier. */
|
||||
|
||||
double threshold_reject_innovation = configuration->property(role + ".threshold_reject_innovation", 30.0); /* reject threshold of innovation (m). If the innovation is over the value, the observable is excluded for the estimation process as an outlier. */
|
||||
|
||||
int number_filter_iter = configuration->property(role + ".number_filter_iter", 1); /* Set the number of iteration in the measurement update of the estimation filter.
|
||||
If the baseline length is very short like 1 m, the iteration may be effective to handle
|
||||
the nonlinearity of measurement equation. */
|
||||
|
||||
/// Statistics
|
||||
double bias_0 = configuration->property(role + ".bias_0", 30.0);
|
||||
|
||||
double iono_0 = configuration->property(role + ".iono_0", 0.03);
|
||||
|
||||
double trop_0 = configuration->property(role + ".trop_0", 0.3);
|
||||
|
||||
double sigma_bias = configuration->property(role + ".sigma_bias", 1e-4); /* Set the process noise standard deviation of carrier‐phase
|
||||
bias (ambiguity) (cycle/sqrt(s)) */
|
||||
|
||||
double sigma_iono = configuration->property(role + ".sigma_iono", 1e-3); /* Set the process noise standard deviation of vertical ionospheric delay per 10 km baseline (m/sqrt(s)). */
|
||||
|
||||
double sigma_trop = configuration->property(role + ".sigma_trop", 1e-4); /* Set the process noise standard deviation of zenith tropospheric delay (m/sqrt(s)). */
|
||||
|
||||
double sigma_acch = configuration->property(role + ".sigma_acch", 1e-1); /* Set the process noise standard deviation of the receiver acceleration as
|
||||
the horizontal component. (m/s2/sqrt(s)). If Receiver Dynamics is set to OFF, they are not used. */
|
||||
|
||||
double sigma_accv = configuration->property(role + ".sigma_accv", 1e-2); /* Set the process noise standard deviation of the receiver acceleration as
|
||||
the vertical component. (m/s2/sqrt(s)). If Receiver Dynamics is set to OFF, they are not used. */
|
||||
|
||||
double sigma_pos = configuration->property(role + ".sigma_pos", 0.0);
|
||||
|
||||
double code_phase_error_ratio_l1 = configuration->property(role + ".code_phase_error_ratio_l1", 100.0);
|
||||
double code_phase_error_ratio_l2 = configuration->property(role + ".code_phase_error_ratio_l2", 100.0);
|
||||
double code_phase_error_ratio_l5 = configuration->property(role + ".code_phase_error_ratio_l5", 100.0);
|
||||
double carrier_phase_error_factor_a = configuration->property(role + ".carrier_phase_error_factor_a", 0.003);
|
||||
double carrier_phase_error_factor_b = configuration->property(role + ".carrier_phase_error_factor_b", 0.003);
|
||||
|
||||
snrmask_t snrmask = {{}, {{}, {}}};
|
||||
|
||||
prcopt_t rtklib_configuration_options = {
|
||||
positioning_mode, /* positioning mode (PMODE_XXX) see src/algorithms/libs/rtklib/rtklib.h */
|
||||
0, /* solution type (0:forward,1:backward,2:combined) */
|
||||
number_of_frequencies, /* number of frequencies (1:L1, 2:L1+L2, 3:L1+L2+L5)*/
|
||||
navigation_system, /* navigation system */
|
||||
elevation_mask * D2R, /* elevation mask angle (degrees) */
|
||||
snrmask, /* snrmask_t snrmask SNR mask */
|
||||
0, /* satellite ephemeris/clock (EPHOPT_XXX) */
|
||||
integer_ambiguity_resolution_gps, /* AR mode (0:off,1:continuous,2:instantaneous,3:fix and hold,4:ppp-ar) */
|
||||
integer_ambiguity_resolution_glo, /* GLONASS AR mode (0:off,1:on,2:auto cal,3:ext cal) */
|
||||
integer_ambiguity_resolution_bds, /* BeiDou AR mode (0:off,1:on) */
|
||||
outage_reset_ambiguity, /* obs outage count to reset bias */
|
||||
min_lock_to_fix_ambiguity, /* min lock count to fix ambiguity */
|
||||
10, /* min fix count to hold ambiguity */
|
||||
1, /* max iteration to resolve ambiguity */
|
||||
iono_model, /* ionosphere option (IONOOPT_XXX) */
|
||||
trop_model, /* troposphere option (TROPOPT_XXX) */
|
||||
dynamics_model, /* dynamics model (0:none, 1:velocity, 2:accel) */
|
||||
earth_tide, /* earth tide correction (0:off,1:solid,2:solid+otl+pole) */
|
||||
number_filter_iter, /* number of filter iteration */
|
||||
0, /* code smoothing window size (0:none) */
|
||||
0, /* interpolate reference obs (for post mission) */
|
||||
0, /* sbssat_t sbssat SBAS correction options */
|
||||
0, /* sbsion_t sbsion[MAXBAND+1] SBAS satellite selection (0:all) */
|
||||
0, /* rover position for fixed mode */
|
||||
0, /* base position for relative mode */
|
||||
/* 0:pos in prcopt, 1:average of single pos, */
|
||||
/* 2:read from file, 3:rinex header, 4:rtcm pos */
|
||||
{code_phase_error_ratio_l1, code_phase_error_ratio_l2, code_phase_error_ratio_l5}, /* eratio[NFREQ] code/phase error ratio */
|
||||
{100.0, carrier_phase_error_factor_a, carrier_phase_error_factor_b, 0.0, 1.0}, /* err[5]: measurement error factor [0]:reserved, [1-3]:error factor a/b/c of phase (m) , [4]:doppler frequency (hz) */
|
||||
{bias_0, iono_0, trop_0}, /* std[3]: initial-state std [0]bias,[1]iono [2]trop*/
|
||||
{sigma_bias, sigma_iono, sigma_trop, sigma_acch, sigma_accv, sigma_pos}, /* prn[6] process-noise std */
|
||||
5e-12, /* sclkstab: satellite clock stability (sec/sec) */
|
||||
{min_ratio_to_fix_ambiguity, 0.9999, 0.25, 0.1, 0.05, 0.0, 0.0, 0.0}, /* thresar[8]: AR validation threshold */
|
||||
min_elevation_to_fix_ambiguity, /* elevation mask of AR for rising satellite (deg) */
|
||||
0.0, /* elevation mask to hold ambiguity (deg) */
|
||||
slip_threshold, /* slip threshold of geometry-free phase (m) */
|
||||
30.0, /* max difference of time (sec) */
|
||||
threshold_reject_innovation, /* reject threshold of innovation (m) */
|
||||
threshold_reject_gdop, /* reject threshold of gdop */
|
||||
{}, /* double baseline[2] baseline length constraint {const,sigma} (m) */
|
||||
{}, /* double ru[3] rover position for fixed mode {x,y,z} (ecef) (m) */
|
||||
{}, /* double rb[3] base position for relative mode {x,y,z} (ecef) (m) */
|
||||
{"", ""}, /* char anttype[2][MAXANT] antenna types {rover,base} */
|
||||
{{}, {}}, /* double antdel[2][3] antenna delta {{rov_e,rov_n,rov_u},{ref_e,ref_n,ref_u}} */
|
||||
{}, /* pcv_t pcvr[2] receiver antenna parameters {rov,base} */
|
||||
{}, /* unsigned char exsats[MAXSAT] excluded satellites (1:excluded, 2:included) */
|
||||
0, /* max averaging epoches */
|
||||
0, /* initialize by restart */
|
||||
1, /* output single by dgps/float/fix/ppp outage */
|
||||
{"", ""}, /* char rnxopt[2][256] rinex options {rover,base} */
|
||||
{sat_PCV, rec_PCV, phwindup, reject_GPS_IIA, raim_fde}, /* posopt[6] positioning options [0]: satellite and receiver antenna PCV model; [1]: interpolate antenna parameters; [2]: apply phase wind-up correction for PPP modes; [3]: exclude measurements of GPS Block IIA satellites satellite [4]: RAIM FDE (fault detection and exclusion) [5]: handle day-boundary clock jump */
|
||||
0, /* solution sync mode (0:off,1:on) */
|
||||
{{}, {}}, /* odisp[2][6*11] ocean tide loading parameters {rov,base} */
|
||||
{{}, {{}, {}}, {{}, {}}, {}, {}}, /* exterr_t exterr extended receiver error model */
|
||||
0, /* disable L2-AR */
|
||||
{}, /* char pppopt[256] ppp option "-GAP_RESION=" default gap to reset iono parameters (ep) */
|
||||
true /* enable Bancroft initialization for the first iteration of the PVT computation, useful in some geometries */
|
||||
};
|
||||
|
||||
rtk_t rtk;
|
||||
rtkinit(&rtk, &rtklib_configuration_options);
|
||||
return rtk;
|
||||
}
|
||||
|
||||
|
||||
// todo: add test cases for Galileo E1, E5 and GPS L5
|
||||
TEST(RTKLibSolverTest, test1)
|
||||
{
|
||||
// test case #1: GPS L1 CA simulated with gnss-sim
|
||||
std::string path = std::string(TEST_PATH);
|
||||
int nchannels = 8;
|
||||
std::string dump_filename = ".rtklib_solver_dump.dat";
|
||||
bool flag_dump_to_file = false;
|
||||
bool save_to_mat = false;
|
||||
rtk_t rtk = configure_rtklib_options();
|
||||
|
||||
Pvt_Conf conf;
|
||||
conf.use_e6_for_pvt = false;
|
||||
auto d_ls_pvt = std::make_unique<Rtklib_Solver>(rtk, conf, nchannels, dump_filename, 1, flag_dump_to_file, save_to_mat);
|
||||
d_ls_pvt->set_averaging_depth(1);
|
||||
|
||||
// load ephemeris
|
||||
std::string eph_xml_filename = path + "data/rtklib_test/eph_GPS_L1CA_test1.xml";
|
||||
Gnss_Sdr_Supl_Client supl_client_ephemeris_;
|
||||
|
||||
std::cout << "SUPL: Try read GPS ephemeris from XML file " << eph_xml_filename << '\n';
|
||||
if (supl_client_ephemeris_.load_ephemeris_xml(eph_xml_filename) == true)
|
||||
{
|
||||
std::map<int, Gps_Ephemeris>::const_iterator gps_eph_iter;
|
||||
for (gps_eph_iter = supl_client_ephemeris_.gps_ephemeris_map.cbegin();
|
||||
gps_eph_iter != supl_client_ephemeris_.gps_ephemeris_map.cend();
|
||||
gps_eph_iter++)
|
||||
{
|
||||
std::cout << "SUPL: Read XML Ephemeris for GPS SV " << gps_eph_iter->first << '\n';
|
||||
std::shared_ptr<Gps_Ephemeris> tmp_obj = std::make_shared<Gps_Ephemeris>(gps_eph_iter->second);
|
||||
// update/insert new ephemeris record to the global ephemeris map
|
||||
d_ls_pvt->gps_ephemeris_map[gps_eph_iter->first] = *tmp_obj;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ERROR: SUPL client error reading XML\n";
|
||||
}
|
||||
|
||||
// insert observables epoch
|
||||
std::map<int, Gnss_Synchro> gnss_synchro_map;
|
||||
// Gnss_Synchro tmp_obs;
|
||||
// tmp_obs.System = 'G';
|
||||
// std::string signal = "1C";
|
||||
// const char* str = signal.c_str(); // get a C style null terminated string
|
||||
// std::memcpy(static_cast<void*>(tmp_obs.Signal), str, 3); // copy string into synchro char array: 2 char + null
|
||||
//
|
||||
// gnss_synchro_map[0] = tmp_obs;
|
||||
// gnss_synchro_map[0].PRN = 1;
|
||||
|
||||
// load from xml (boost serialize)
|
||||
std::string file_name = path + "data/rtklib_test/obs_test1.xml";
|
||||
|
||||
std::ifstream ifs;
|
||||
try
|
||||
{
|
||||
ifs.open(file_name.c_str(), std::ifstream::binary | std::ifstream::in);
|
||||
boost::archive::xml_iarchive xml(ifs);
|
||||
gnss_synchro_map.clear();
|
||||
xml >> boost::serialization::make_nvp("GNSS-SDR_gnss_synchro_map", gnss_synchro_map);
|
||||
std::cout << "Loaded gnss_synchro map data with " << gnss_synchro_map.size() << " pseudoranges\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << "File: " << file_name;
|
||||
}
|
||||
ifs.close();
|
||||
|
||||
// solve
|
||||
bool pvt_valid = false;
|
||||
if (d_ls_pvt->get_PVT(gnss_synchro_map, false))
|
||||
{
|
||||
// DEBUG MESSAGE: Display position in console output
|
||||
if (d_ls_pvt->is_valid_position())
|
||||
{
|
||||
std::streamsize ss = std::cout.precision(); // save current precision
|
||||
std::cout.setf(std::ios::fixed, std::ios::floatfield);
|
||||
|
||||
auto facet = new boost::posix_time::time_facet("%Y-%b-%d %H:%M:%S.%f %z");
|
||||
std::cout.imbue(std::locale(std::cout.getloc(), facet));
|
||||
|
||||
std::cout << "Position at " << d_ls_pvt->get_position_UTC_time()
|
||||
<< " UTC using " << d_ls_pvt->get_num_valid_observations()
|
||||
<< std::fixed << std::setprecision(9)
|
||||
<< " observations is Lat = " << d_ls_pvt->get_latitude() << " [deg], Long = " << d_ls_pvt->get_longitude()
|
||||
<< std::fixed << std::setprecision(3)
|
||||
<< " [deg], Height = " << d_ls_pvt->get_height() << " [m]\n";
|
||||
std::cout << std::setprecision(ss);
|
||||
std::cout << "RX clock offset: " << d_ls_pvt->get_time_offset_s() << "[s]\n";
|
||||
|
||||
// boost::posix_time::ptime p_time;
|
||||
// gtime_t rtklib_utc_time = gpst2time(adjgpsweek(d_ls_pvt->gps_ephemeris_map.cbegin()->second.i_GPS_week), d_rx_time);
|
||||
// p_time = boost::posix_time::from_time_t(rtklib_utc_time.time);
|
||||
// p_time += boost::posix_time::microseconds(round(rtklib_utc_time.sec * 1e6));
|
||||
// std::cout << TEXT_MAGENTA << "Observable RX time (GPST) " << boost::posix_time::to_simple_string(p_time) << TEXT_RESET << '\n';
|
||||
|
||||
std::cout << "RTKLIB Position at RX TOW = " << gnss_synchro_map.begin()->second.RX_time
|
||||
<< " in ECEF (X,Y,Z,t[meters]) = " << std::fixed << std::setprecision(16)
|
||||
<< d_ls_pvt->pvt_sol.rr[0] << ","
|
||||
<< d_ls_pvt->pvt_sol.rr[1] << ","
|
||||
<< d_ls_pvt->pvt_sol.rr[2] << '\n';
|
||||
/* std::cout << "Dilution of Precision at " << boost::posix_time::to_simple_string(d_ls_pvt->get_position_UTC_time())
|
||||
<< " UTC using "<< d_ls_pvt->get_num_valid_observations() <<" observations is HDOP = " << d_ls_pvt->get_hdop() << " VDOP = "
|
||||
<< d_ls_pvt->get_vdop()
|
||||
<< " GDOP = " << d_ls_pvt->get_gdop() << '\n'; */
|
||||
|
||||
// todo: check here the positioning error against the reference position generated with gnss-sim
|
||||
// reference position on in WGS84: Lat (deg), Long (deg) , H (m): 30.286502,120.032669,100
|
||||
arma::vec LLH = {30.286502, 120.032669, 100}; // ref position for this scenario
|
||||
|
||||
double error_LLH_m = great_circle_distance(LLH(0), LLH(1), d_ls_pvt->get_latitude(), d_ls_pvt->get_longitude());
|
||||
std::cout << "Lat, Long, H error: " << d_ls_pvt->get_latitude() - LLH(0)
|
||||
<< "," << d_ls_pvt->get_longitude() - LLH(1)
|
||||
<< "," << d_ls_pvt->get_height() - LLH(2) << " [deg,deg,meters]\n";
|
||||
|
||||
std::cout << "Haversine Great Circle error LLH distance: " << error_LLH_m << " [meters]\n";
|
||||
|
||||
arma::vec v_eb_n = {0.0, 0.0, 0.0};
|
||||
arma::vec true_r_eb_e;
|
||||
arma::vec true_v_eb_e;
|
||||
pv_Geo_to_ECEF(degtorad(LLH(0)), degtorad(LLH(1)), LLH(2), v_eb_n, true_r_eb_e, true_v_eb_e);
|
||||
|
||||
arma::vec measured_r_eb_e = {d_ls_pvt->pvt_sol.rr[0], d_ls_pvt->pvt_sol.rr[1], d_ls_pvt->pvt_sol.rr[2]};
|
||||
|
||||
arma::vec error_r_eb_e = measured_r_eb_e - true_r_eb_e;
|
||||
|
||||
std::cout << "ECEF position error vector: " << error_r_eb_e << " [meters]\n";
|
||||
|
||||
double error_3d_m = arma::norm(error_r_eb_e, 2);
|
||||
|
||||
std::cout << "3D positioning error: " << error_3d_m << " [meters]\n";
|
||||
|
||||
// check results against the test tolerance
|
||||
ASSERT_LT(error_3d_m, 0.2);
|
||||
pvt_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(true, pvt_valid);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*!
|
||||
* \file serdes_monitor_pvt_test.cc
|
||||
* \brief Implements Unit Test for the serdes_monitor_pvt class.
|
||||
* \author Carles Fernandez-Prades, 2019. 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 "serdes_galileo_eph.h"
|
||||
#include "serdes_monitor_pvt.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
TEST(Serdes_Monitor_Pvt_Test, Simpletest)
|
||||
{
|
||||
auto monitor = std::make_shared<Monitor_Pvt>(Monitor_Pvt());
|
||||
double true_latitude = 23.4;
|
||||
monitor->latitude = true_latitude;
|
||||
|
||||
Serdes_Monitor_Pvt serdes = Serdes_Monitor_Pvt();
|
||||
std::string serialized_data = serdes.createProtobuffer(monitor.get());
|
||||
|
||||
gnss_sdr::MonitorPvt mon;
|
||||
mon.ParseFromString(serialized_data);
|
||||
double read_latitude = mon.latitude();
|
||||
EXPECT_NEAR(true_latitude, read_latitude, 0.000001);
|
||||
}
|
||||
|
||||
|
||||
TEST(Serdes_Monitor_Pvt_Test, GalileoEphemerisSerdes)
|
||||
{
|
||||
auto eph = std::make_shared<Galileo_Ephemeris>();
|
||||
int true_tow = 12345;
|
||||
eph->tow = true_tow;
|
||||
|
||||
Serdes_Galileo_Eph gal_serdes = Serdes_Galileo_Eph();
|
||||
std::string serialized_data = gal_serdes.createProtobuffer(std::move(eph));
|
||||
|
||||
gnss_sdr::GalileoEphemeris ephgal;
|
||||
ephgal.ParseFromString(serialized_data);
|
||||
|
||||
double true_delta_n = 0.33;
|
||||
ephgal.set_delta_n(true_delta_n);
|
||||
|
||||
int read_tow = ephgal.tow();
|
||||
EXPECT_EQ(true_tow, read_tow);
|
||||
|
||||
double read_delta_n = ephgal.delta_n();
|
||||
EXPECT_NEAR(true_delta_n, read_delta_n, 0.000001);
|
||||
|
||||
auto eph2 = gal_serdes.readProtobuffer(ephgal);
|
||||
double read2_delta_n = eph2.delta_n;
|
||||
int read2_tow = eph2.tow;
|
||||
EXPECT_EQ(true_tow, read2_tow);
|
||||
EXPECT_NEAR(true_delta_n, read2_delta_n, 0.000001);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*!
|
||||
* \file direct_resampler_conditioner_cc_test.cc
|
||||
* \brief Executes a resampler based on some input parameters.
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "direct_resampler_conditioner_cc.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
|
||||
|
||||
TEST(DirectResamplerConditionerCcTest, InstantiationAndRunTest)
|
||||
{
|
||||
double fs_in = 8000000.0; // Input sampling frequency in Hz
|
||||
double fs_out = 4000000.0; // sampling freuqncy of the resampled signal in Hz
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int nsamples = 1000000; // Number of samples to be computed
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto top_block = gr::make_top_block("direct_resampler_conditioner_cc_test");
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
direct_resampler_conditioner_cc_sptr resampler = direct_resampler_make_conditioner_cc(fs_in, fs_out);
|
||||
}) << "Failure in instantiation of direct_resampler_conditioner.";
|
||||
|
||||
auto resampler = direct_resampler_make_conditioner_cc(fs_in, fs_out);
|
||||
auto sink = gr::blocks::null_sink::make(sizeof(gr_complex));
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, resampler, 0);
|
||||
top_block->connect(resampler, 0, sink, 0);
|
||||
}) << "Connection failure of direct_resampler_conditioner.";
|
||||
|
||||
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;
|
||||
top_block->stop();
|
||||
}) << "Failure running direct_resampler_conditioner.";
|
||||
|
||||
std::cout << "Resampled " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*!
|
||||
* \file mmse_resampler_test.cc
|
||||
* \brief Executes a resampler based on some input parameters.
|
||||
* \author Carles Fernandez-Prades 2018 cfernandez (at) cttc.cat
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "mmse_resampler_conditioner.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
|
||||
TEST(MmseResamplerTest, InstantiationAndRunTestWarning)
|
||||
{
|
||||
double fs_in = 8000000.0; // Input sampling frequency in Hz
|
||||
double fs_out = 4000000.0; // sampling freuqncy of the resampled signal in Hz
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int nsamples = 1000000; // Number of samples to be computed
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto top_block = gr::make_top_block("mmse_resampler_conditioner_cc_test");
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("Resampler.sample_freq_in", std::to_string(fs_in));
|
||||
config->set_property("Resampler.sample_freq_out", std::to_string(fs_out));
|
||||
|
||||
auto resampler = std::make_shared<MmseResamplerConditioner>(config.get(), "Resampler", 1, 1);
|
||||
|
||||
auto sink = gr::blocks::null_sink::make(sizeof(gr_complex));
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
resampler->connect(top_block);
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, resampler->get_left_block(), 0);
|
||||
top_block->connect(resampler->get_right_block(), 0, sink, 0);
|
||||
}) << "Connection failure of direct_resampler_conditioner.";
|
||||
|
||||
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;
|
||||
top_block->stop();
|
||||
}) << "Failure running direct_resampler_conditioner.";
|
||||
|
||||
std::cout << "Resampled " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST(MmseResamplerTest, InstantiationAndRunTest2)
|
||||
{
|
||||
double fs_in = 8000000.0; // Input sampling frequency in Hz
|
||||
double fs_out = 4000000.0; // sampling freuqncy of the resampled signal in Hz
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int nsamples = 1000000; // Number of samples to be computed
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto top_block = gr::make_top_block("mmse_resampler_conditioner_cc_test");
|
||||
auto source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000.0, 1.0, gr_complex(0.0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("Resampler.sample_freq_in", std::to_string(fs_in));
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(fs_out));
|
||||
|
||||
auto resampler = std::make_shared<MmseResamplerConditioner>(config.get(), "Resampler", 1, 1);
|
||||
|
||||
auto sink = gr::blocks::null_sink::make(sizeof(gr_complex));
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
resampler->connect(top_block);
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, resampler->get_left_block(), 0);
|
||||
top_block->connect(resampler->get_right_block(), 0, sink, 0);
|
||||
}) << "Connection failure of direct_resampler_conditioner.";
|
||||
|
||||
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;
|
||||
top_block->stop();
|
||||
}) << "Failure running direct_resampler_conditioner.";
|
||||
|
||||
std::cout << "Resampled " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*!
|
||||
* \file file_signal_source_test.cc
|
||||
* \brief This class implements a Unit Test for the class FileSignalSource.
|
||||
* \author Carlos Avilés, 2010. carlos.avilesr(at)googlemail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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_queue.h"
|
||||
#include "file_signal_source.h"
|
||||
#include "gnss_sdr_make_unique.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
TEST(FileSignalSource, Instantiate)
|
||||
{
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("Test.samples", "0");
|
||||
config->set_property("Test.sampling_frequency", "0");
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string filename = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
|
||||
config->set_property("Test.filename", std::move(filename));
|
||||
config->set_property("Test.item_type", "gr_complex");
|
||||
config->set_property("Test.repeat", "false");
|
||||
|
||||
auto signal_source = std::make_unique<FileSignalSource>(config.get(), "Test", 0, 1, queue.get());
|
||||
|
||||
EXPECT_STREQ("gr_complex", signal_source->item_type().c_str());
|
||||
EXPECT_TRUE(signal_source->repeat() == false);
|
||||
}
|
||||
|
||||
TEST(FileSignalSource, InstantiateFileNotExists)
|
||||
{
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
auto config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
config->set_property("Test.samples", "0");
|
||||
config->set_property("Test.sampling_frequency", "0");
|
||||
config->set_property("Test.filename", "./signal_samples/i_dont_exist.dat");
|
||||
config->set_property("Test.item_type", "gr_complex");
|
||||
config->set_property("Test.repeat", "false");
|
||||
|
||||
// the file existence test was moved from the ctor to the connect() call. The argument to
|
||||
// connect doesn't matter, since the exception should be thrown sooner than any connections
|
||||
auto top = gr::make_top_block("GNSSUnitTest");
|
||||
auto uptr = std::make_shared<FileSignalSource>(config.get(), "Test", 0, 1, queue.get());
|
||||
EXPECT_THROW({ uptr->connect(std::move(top)); }, std::exception);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*!
|
||||
* \file gnss_sdr_valve_test.cc
|
||||
* \brief This file implements unit tests for the valve custom block.
|
||||
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
|
||||
* Carles Fernandez-Prades, 2012. 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 <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_f.h>
|
||||
#endif
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <pmt/pmt.h>
|
||||
|
||||
TEST(ValveTest, CheckEventSentAfter100Samples)
|
||||
{
|
||||
auto queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
|
||||
auto top_block = gr::make_top_block("gnss_sdr_valve_test");
|
||||
|
||||
auto source = gr::analog::sig_source_f::make(100, gr::analog::GR_CONST_WAVE, 100, 1, 0);
|
||||
auto valve = gnss_sdr_make_valve(sizeof(float), 100, queue.get());
|
||||
auto sink = gr::blocks::null_sink::make(sizeof(float));
|
||||
|
||||
bool expected0 = false;
|
||||
pmt::pmt_t msg;
|
||||
EXPECT_EQ(expected0, queue->timed_wait_and_pop(msg, 100));
|
||||
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, sink, 0);
|
||||
|
||||
top_block->run();
|
||||
top_block->stop();
|
||||
|
||||
bool expected1 = true;
|
||||
EXPECT_EQ(expected1, queue->timed_wait_and_pop(msg, 100));
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*!
|
||||
* \file unpack_2bit_samples_test.cc
|
||||
* \brief This file implements unit tests for the unpack_2bit_samples
|
||||
* custom block
|
||||
* \author Cillian O'Driscoll, 2015. cillian.odriscoll (at) gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "unpack_2bit_samples.h"
|
||||
#include <gnuradio/blocks/stream_to_vector.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/blocks/vector_sink.h>
|
||||
#include <gnuradio/blocks/vector_source.h>
|
||||
#else
|
||||
#include <gnuradio/blocks/vector_sink_b.h>
|
||||
#include <gnuradio/blocks/vector_source_b.h>
|
||||
#include <gnuradio/blocks/vector_source_s.h>
|
||||
#endif
|
||||
|
||||
std::vector<uint8_t> packData(std::vector<int8_t> const &raw_data,
|
||||
bool big_endian)
|
||||
{
|
||||
std::vector<uint8_t> packed_data(raw_data.size() / 4);
|
||||
|
||||
int shift = (big_endian ? 6 : 0);
|
||||
unsigned int j = 0;
|
||||
|
||||
for (signed char i : raw_data)
|
||||
{
|
||||
auto val = static_cast<unsigned>((i - 1) / 2 & 0x03);
|
||||
|
||||
packed_data[j] |= val << shift;
|
||||
|
||||
if (big_endian)
|
||||
{
|
||||
shift -= 2;
|
||||
if (shift < 0)
|
||||
{
|
||||
shift = 6;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shift += 2;
|
||||
if (shift > 6)
|
||||
{
|
||||
shift = 0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packed_data;
|
||||
}
|
||||
|
||||
|
||||
TEST(Unpack2bitSamplesTest, CheckBigEndianByte)
|
||||
{
|
||||
bool big_endian_bytes = true;
|
||||
size_t item_size = 1;
|
||||
bool big_endian_items = false;
|
||||
|
||||
std::vector<int8_t> raw_data = {-1, 3, 1, -1, -3, 1, 3, 1};
|
||||
std::vector<uint8_t> packed_data = packData(raw_data, big_endian_bytes);
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
|
||||
gr::top_block_sptr top_block = gr::make_top_block("Unpack2bitSamplesTest");
|
||||
|
||||
gr::blocks::vector_source_b::sptr source =
|
||||
gr::blocks::vector_source_b::make(packed_data);
|
||||
|
||||
auto unpacker =
|
||||
make_unpack_2bit_samples(big_endian_bytes,
|
||||
item_size,
|
||||
big_endian_items);
|
||||
|
||||
gr::blocks::stream_to_vector::sptr stov =
|
||||
gr::blocks::stream_to_vector::make(item_size, raw_data.size());
|
||||
|
||||
gr::blocks::vector_sink_b::sptr sink =
|
||||
gr::blocks::vector_sink_b::make(raw_data.size());
|
||||
|
||||
top_block->connect(source, 0, unpacker, 0);
|
||||
top_block->connect(unpacker, 0, stov, 0);
|
||||
top_block->connect(stov, 0, sink, 0);
|
||||
|
||||
top_block->run();
|
||||
top_block->stop();
|
||||
|
||||
unpacked_data = sink->data();
|
||||
|
||||
EXPECT_EQ(raw_data.size(), unpacked_data.size());
|
||||
|
||||
for (size_t i = 0; i < raw_data.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(raw_data[i], static_cast<int8_t>(unpacked_data[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Unpack2bitSamplesTest, CheckLittleEndianByte)
|
||||
{
|
||||
bool big_endian_bytes = false;
|
||||
size_t item_size = 1;
|
||||
bool big_endian_items = false;
|
||||
|
||||
std::vector<int8_t> raw_data = {-1, 3, 1, -1, -3, 1, 3, 1};
|
||||
std::vector<uint8_t> packed_data = packData(raw_data, big_endian_bytes);
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
|
||||
gr::top_block_sptr top_block = gr::make_top_block("Unpack2bitSamplesTest");
|
||||
|
||||
gr::blocks::vector_source_b::sptr source =
|
||||
gr::blocks::vector_source_b::make(packed_data);
|
||||
|
||||
auto unpacker =
|
||||
make_unpack_2bit_samples(big_endian_bytes,
|
||||
item_size,
|
||||
big_endian_items);
|
||||
|
||||
gr::blocks::stream_to_vector::sptr stov =
|
||||
gr::blocks::stream_to_vector::make(item_size, raw_data.size());
|
||||
|
||||
gr::blocks::vector_sink_b::sptr sink =
|
||||
gr::blocks::vector_sink_b::make(raw_data.size());
|
||||
|
||||
top_block->connect(source, 0, unpacker, 0);
|
||||
top_block->connect(unpacker, 0, stov, 0);
|
||||
top_block->connect(stov, 0, sink, 0);
|
||||
|
||||
top_block->run();
|
||||
top_block->stop();
|
||||
|
||||
unpacked_data = sink->data();
|
||||
|
||||
EXPECT_EQ(raw_data.size(), unpacked_data.size());
|
||||
|
||||
for (size_t i = 0; i < raw_data.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(raw_data[i], static_cast<int8_t>(unpacked_data[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Unpack2bitSamplesTest, CheckBigEndianShortBigEndianByte)
|
||||
{
|
||||
bool big_endian_bytes = true;
|
||||
size_t item_size = 2;
|
||||
bool big_endian_items = true;
|
||||
|
||||
std::vector<int8_t> raw_data = {-1, 3, 1, -1, -3, 1, 3, 1};
|
||||
std::vector<uint8_t> packed_data = packData(raw_data, big_endian_bytes);
|
||||
// change the order of each pair of bytes:
|
||||
for (size_t ii = 0; ii < packed_data.size(); ii += item_size)
|
||||
{
|
||||
unsigned int kk = ii + item_size - 1;
|
||||
unsigned int jj = ii;
|
||||
while (kk > jj)
|
||||
{
|
||||
uint8_t tmp = packed_data[jj];
|
||||
packed_data[jj] = packed_data[kk];
|
||||
packed_data[kk] = tmp;
|
||||
--kk;
|
||||
++jj;
|
||||
}
|
||||
}
|
||||
|
||||
// Now create a new big endian buffer:
|
||||
std::vector<int16_t> packed_data_short(
|
||||
reinterpret_cast<int16_t *>(&packed_data[0]),
|
||||
reinterpret_cast<int16_t *>(&packed_data[0]) + packed_data.size() / item_size);
|
||||
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
|
||||
gr::top_block_sptr top_block = gr::make_top_block("Unpack2bitSamplesTest");
|
||||
|
||||
gr::blocks::vector_source_s::sptr source =
|
||||
gr::blocks::vector_source_s::make(packed_data_short);
|
||||
|
||||
auto unpacker =
|
||||
make_unpack_2bit_samples(big_endian_bytes,
|
||||
item_size,
|
||||
big_endian_items);
|
||||
|
||||
gr::blocks::stream_to_vector::sptr stov =
|
||||
gr::blocks::stream_to_vector::make(1, raw_data.size());
|
||||
|
||||
gr::blocks::vector_sink_b::sptr sink =
|
||||
gr::blocks::vector_sink_b::make(raw_data.size());
|
||||
|
||||
top_block->connect(source, 0, unpacker, 0);
|
||||
top_block->connect(unpacker, 0, stov, 0);
|
||||
top_block->connect(stov, 0, sink, 0);
|
||||
|
||||
top_block->run();
|
||||
top_block->stop();
|
||||
|
||||
unpacked_data = sink->data();
|
||||
|
||||
EXPECT_EQ(raw_data.size(), unpacked_data.size());
|
||||
|
||||
for (size_t i = 0; i < raw_data.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(raw_data[i], static_cast<int8_t>(unpacked_data[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Unpack2bitSamplesTest, CheckBigEndianShortLittleEndianByte)
|
||||
{
|
||||
bool big_endian_bytes = false;
|
||||
size_t item_size = 2;
|
||||
bool big_endian_items = true;
|
||||
|
||||
std::vector<int8_t> raw_data = {-1, 3, 1, -1, -3, 1, 3, 1};
|
||||
std::vector<uint8_t> packed_data = packData(raw_data, big_endian_bytes);
|
||||
// change the order of each pair of bytes:
|
||||
for (size_t ii = 0; ii < packed_data.size(); ii += item_size)
|
||||
{
|
||||
unsigned int kk = ii + item_size - 1;
|
||||
unsigned int jj = ii;
|
||||
while (kk > jj)
|
||||
{
|
||||
uint8_t tmp = packed_data[jj];
|
||||
packed_data[jj] = packed_data[kk];
|
||||
packed_data[kk] = tmp;
|
||||
--kk;
|
||||
++jj;
|
||||
}
|
||||
}
|
||||
|
||||
// Now create a new big endian buffer:
|
||||
std::vector<int16_t> packed_data_short(
|
||||
reinterpret_cast<int16_t *>(&packed_data[0]),
|
||||
reinterpret_cast<int16_t *>(&packed_data[0]) + packed_data.size() / item_size);
|
||||
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
|
||||
gr::top_block_sptr top_block = gr::make_top_block("Unpack2bitSamplesTest");
|
||||
|
||||
gr::blocks::vector_source_s::sptr source =
|
||||
gr::blocks::vector_source_s::make(packed_data_short);
|
||||
|
||||
auto unpacker =
|
||||
make_unpack_2bit_samples(big_endian_bytes,
|
||||
item_size,
|
||||
big_endian_items);
|
||||
|
||||
gr::blocks::stream_to_vector::sptr stov =
|
||||
gr::blocks::stream_to_vector::make(1, raw_data.size());
|
||||
|
||||
gr::blocks::vector_sink_b::sptr sink =
|
||||
gr::blocks::vector_sink_b::make(raw_data.size());
|
||||
|
||||
top_block->connect(source, 0, unpacker, 0);
|
||||
top_block->connect(unpacker, 0, stov, 0);
|
||||
top_block->connect(stov, 0, sink, 0);
|
||||
|
||||
top_block->run();
|
||||
top_block->stop();
|
||||
|
||||
unpacked_data = sink->data();
|
||||
|
||||
EXPECT_EQ(raw_data.size(), unpacked_data.size());
|
||||
|
||||
for (size_t i = 0; i < raw_data.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(raw_data[i], static_cast<int8_t>(unpacked_data[i]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*!
|
||||
* \file galileo_fnav_inav_decoder_test.cc
|
||||
* \brief This class implements the unit test for the Galileo FNAV and INAV frames
|
||||
* according to the Galileo ICD
|
||||
* \author Javier Arribas, 2018. jarribas(at)cttc.es
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "galileo_fnav_message.h"
|
||||
#include "galileo_inav_message.h"
|
||||
#include "gnss_sdr_make_unique.h" // for std::make_unique in C++11
|
||||
#include "viterbi_decoder.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm> // for copy
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <iterator> // for std::back_inserter
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
class Galileo_FNAV_INAV_test : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
Galileo_FNAV_INAV_test()
|
||||
{
|
||||
viterbi_fnav = std::make_unique<Viterbi_Decoder>(KK, nn, ((488 / nn) - mm), g_encoder);
|
||||
viterbi_inav = std::make_unique<Viterbi_Decoder>(KK, nn, ((240 / nn) - mm), g_encoder);
|
||||
flag_even_word_arrived = 0;
|
||||
}
|
||||
|
||||
~Galileo_FNAV_INAV_test() = default;
|
||||
|
||||
Galileo_Inav_Message INAV_decoder;
|
||||
Galileo_Fnav_Message FNAV_decoder;
|
||||
const int32_t nn = 2; // Coding rate 1/n
|
||||
const int32_t KK = 7; // Constraint Length
|
||||
const int32_t mm = KK - 1;
|
||||
const std::array<int32_t, 2> g_encoder{{121, 91}};
|
||||
std::unique_ptr<Viterbi_Decoder> viterbi_fnav;
|
||||
std::unique_ptr<Viterbi_Decoder> viterbi_inav;
|
||||
int32_t flag_even_word_arrived;
|
||||
|
||||
void deinterleaver(int32_t rows, int32_t cols, const float *in, float *out)
|
||||
{
|
||||
for (int32_t r = 0; r < rows; r++)
|
||||
{
|
||||
for (int32_t c = 0; c < cols; c++)
|
||||
{
|
||||
out[c * rows + r] = in[r * cols + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool decode_INAV_word(float *page_part_symbols, int32_t frame_length)
|
||||
{
|
||||
// 1. De-interleave
|
||||
std::vector<float> page_part_symbols_deint = std::vector<float>(frame_length / 2);
|
||||
std::copy(&page_part_symbols[0], &page_part_symbols[frame_length / 2], std::back_inserter(page_part_symbols_deint));
|
||||
deinterleaver(GALILEO_INAV_INTERLEAVER_ROWS, GALILEO_INAV_INTERLEAVER_COLS, page_part_symbols, page_part_symbols_deint.data());
|
||||
|
||||
// 2. Viterbi decoder
|
||||
// 2.1 Take into account the NOT gate in G2 polynomial (Galileo ICD Figure 13, FEC encoder)
|
||||
// 2.2 Take into account the possible inversion of the polarity due to PLL lock at 180º
|
||||
for (int32_t i = 0; i < frame_length; i++)
|
||||
{
|
||||
if ((i + 1) % 2 == 0)
|
||||
{
|
||||
page_part_symbols_deint[i] = -page_part_symbols_deint[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int32_t> page_part_bits = std::vector<int32_t>(frame_length / 2);
|
||||
viterbi_inav->decode(page_part_bits, page_part_symbols_deint);
|
||||
|
||||
// 3. Call the Galileo page decoder
|
||||
std::string page_String;
|
||||
for (int32_t i = 0; i < (frame_length / 2); i++)
|
||||
{
|
||||
if (page_part_bits[i] > 0)
|
||||
{
|
||||
page_String.push_back('1');
|
||||
}
|
||||
else
|
||||
{
|
||||
page_String.push_back('0');
|
||||
}
|
||||
}
|
||||
|
||||
bool crc_ok = false;
|
||||
if (page_part_bits[0] == 1)
|
||||
{
|
||||
// DECODE COMPLETE WORD (even + odd) and TEST CRC
|
||||
INAV_decoder.split_page(std::move(page_String), flag_even_word_arrived);
|
||||
if (INAV_decoder.get_flag_CRC_test() == true)
|
||||
{
|
||||
crc_ok = true;
|
||||
}
|
||||
flag_even_word_arrived = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// STORE HALF WORD (even page)
|
||||
INAV_decoder.split_page(page_String.c_str(), flag_even_word_arrived);
|
||||
flag_even_word_arrived = 1;
|
||||
}
|
||||
return crc_ok;
|
||||
}
|
||||
|
||||
bool decode_FNAV_word(float *page_symbols, int32_t frame_length)
|
||||
{
|
||||
// 1. De-interleave
|
||||
std::vector<float> page_symbols_deint = std::vector<float>(frame_length);
|
||||
std::copy(&page_symbols[0], &page_symbols[frame_length / 2], std::back_inserter(page_symbols_deint));
|
||||
deinterleaver(GALILEO_FNAV_INTERLEAVER_ROWS, GALILEO_FNAV_INTERLEAVER_COLS, page_symbols, page_symbols_deint.data());
|
||||
|
||||
// 2. Viterbi decoder
|
||||
// 2.1 Take into account the NOT gate in G2 polynomial (Galileo ICD Figure 13, FEC encoder)
|
||||
// 2.2 Take into account the possible inversion of the polarity due to PLL lock at 180
|
||||
for (int32_t i = 0; i < frame_length; i++)
|
||||
{
|
||||
if ((i + 1) % 2 == 0)
|
||||
{
|
||||
page_symbols_deint[i] = -page_symbols_deint[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int32_t> page_bits = std::vector<int32_t>(frame_length);
|
||||
viterbi_fnav->decode(page_bits, page_symbols_deint);
|
||||
|
||||
// 3. Call the Galileo page decoder
|
||||
std::string page_String;
|
||||
for (int32_t i = 0; i < frame_length; i++)
|
||||
{
|
||||
if (page_bits[i] > 0)
|
||||
{
|
||||
page_String.push_back('1');
|
||||
}
|
||||
else
|
||||
{
|
||||
page_String.push_back('0');
|
||||
}
|
||||
}
|
||||
|
||||
// DECODE COMPLETE WORD (even + odd) and TEST CRC
|
||||
FNAV_decoder.split_page(page_String);
|
||||
if (FNAV_decoder.get_flag_CRC_test() == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(Galileo_FNAV_INAV_test, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
start = std::chrono::system_clock::now();
|
||||
int repetitions = 10;
|
||||
// FNAV FULLY ENCODED FRAME
|
||||
float FNAV_frame[488] = {-1, 1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1,
|
||||
-1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1,
|
||||
-1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1,
|
||||
-1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1,
|
||||
-1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1,
|
||||
1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1,
|
||||
-1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1,
|
||||
1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1,
|
||||
-1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1,
|
||||
1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1,
|
||||
1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1};
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
for (int n = 0; n < repetitions; n++)
|
||||
{
|
||||
EXPECT_EQ(decode_FNAV_word(&FNAV_frame[0], 488), true);
|
||||
}
|
||||
}) << "Exception during FNAV frame decoding";
|
||||
|
||||
|
||||
// INAV FULLY ENCODED FRAME
|
||||
float INAV_frame_even[240] = {-1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, 1,
|
||||
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1,
|
||||
1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1,
|
||||
-1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1};
|
||||
|
||||
float INAV_frame_odd[240] = {1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, -1, 1,
|
||||
1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1,
|
||||
-1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1,
|
||||
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, -1,
|
||||
1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1,
|
||||
1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1,
|
||||
-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1,
|
||||
1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1};
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
for (int n = 0; n < repetitions; n++)
|
||||
{
|
||||
decode_INAV_word(&INAV_frame_even[0], 240);
|
||||
EXPECT_EQ(decode_INAV_word(&INAV_frame_odd[0], 240), true);
|
||||
}
|
||||
}) << "Exception during INAV frame decoding";
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
std::cout << "Galileo INAV/FNAV CRC and Viterbi decoder test completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_dll_pll_tracking_test.cc
|
||||
* \brief This class implements a telemetry decoder test for GPS_L1_CA_Telemetry_Decoder
|
||||
* implementation based on some input parameters.
|
||||
* \author Javier Arribas, 2015. jarribas(at)cttc.es
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <armadillo>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#include "GPS_L1_CA.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_dll_pll_tracking.h"
|
||||
#include "gps_l1_ca_telemetry_decoder.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator_flags.h"
|
||||
#include "telemetry_decoder_interface.h"
|
||||
#include "tlm_dump_reader.h"
|
||||
#include "tracking_dump_reader.h"
|
||||
#include "tracking_interface.h"
|
||||
#include "tracking_true_obs_reader.h"
|
||||
#include <gnuradio/blocks/interleaved_char_to_complex.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER FOR TRACKING MESSAGES #########
|
||||
class GpsL1CADllPllTelemetryDecoderTest_msg_rx;
|
||||
|
||||
using GpsL1CADllPllTelemetryDecoderTest_msg_rx_sptr = gnss_shared_ptr<GpsL1CADllPllTelemetryDecoderTest_msg_rx>;
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_msg_rx_make();
|
||||
|
||||
class GpsL1CADllPllTelemetryDecoderTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CADllPllTelemetryDecoderTest_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GpsL1CADllPllTelemetryDecoderTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CADllPllTelemetryDecoderTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_msg_rx_make()
|
||||
{
|
||||
return GpsL1CADllPllTelemetryDecoderTest_msg_rx_sptr(new GpsL1CADllPllTelemetryDecoderTest_msg_rx());
|
||||
}
|
||||
|
||||
void GpsL1CADllPllTelemetryDecoderTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_msg_rx::GpsL1CADllPllTelemetryDecoderTest_msg_rx() : gr::block("GpsL1CADllPllTelemetryDecoderTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CADllPllTelemetryDecoderTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CADllPllTelemetryDecoderTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_msg_rx::~GpsL1CADllPllTelemetryDecoderTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER FOR TLM MESSAGES #########
|
||||
class GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx;
|
||||
|
||||
using GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_sptr = std::shared_ptr<GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx>;
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_make();
|
||||
|
||||
class GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_sptr GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_make()
|
||||
{
|
||||
return GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_sptr(new GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx());
|
||||
}
|
||||
|
||||
void GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx::GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx() : gr::block("GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
boost::bind(&GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx::~GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
class GpsL1CATelemetryDecoderTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
std::string generator_binary;
|
||||
std::string p1;
|
||||
std::string p2;
|
||||
std::string p3;
|
||||
std::string p4;
|
||||
std::string p5;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const int baseband_sampling_freq = FLAGS_fs_gen_sps;
|
||||
std::string filename_rinex_obs = FLAGS_filename_rinex_obs;
|
||||
std::string filename_raw_data = FLAGS_filename_raw_data;
|
||||
#else
|
||||
const int baseband_sampling_freq = absl::GetFlag(FLAGS_fs_gen_sps);
|
||||
std::string filename_rinex_obs = absl::GetFlag(FLAGS_filename_rinex_obs);
|
||||
std::string filename_raw_data = absl::GetFlag(FLAGS_filename_raw_data);
|
||||
#endif
|
||||
int configure_generator();
|
||||
int generate_signal();
|
||||
void check_results(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value);
|
||||
|
||||
GpsL1CATelemetryDecoderTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL1CATelemetryDecoderTest() = default;
|
||||
|
||||
void configure_receiver();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
int GpsL1CATelemetryDecoderTest::configure_generator()
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
// Configure signal generator
|
||||
generator_binary = FLAGS_generator_binary;
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + FLAGS_rinex_nav_file;
|
||||
if (FLAGS_dynamic_position.empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + FLAGS_static_position + std::string(",") + std::to_string(FLAGS_duration * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(FLAGS_dynamic_position);
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + FLAGS_filename_rinex_obs; // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + FLAGS_filename_raw_data; // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#else
|
||||
// Configure signal generator
|
||||
generator_binary = absl::GetFlag(FLAGS_generator_binary);
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + absl::GetFlag(FLAGS_rinex_nav_file);
|
||||
if (absl::GetFlag(FLAGS_dynamic_position).empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + absl::GetFlag(FLAGS_static_position) + std::string(",") + std::to_string(absl::GetFlag(FLAGS_duration) * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(absl::GetFlag(FLAGS_dynamic_position));
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + absl::GetFlag(FLAGS_filename_rinex_obs); // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + absl::GetFlag(FLAGS_filename_raw_data); // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GpsL1CATelemetryDecoderTest::generate_signal()
|
||||
{
|
||||
int child_status;
|
||||
|
||||
char* const parmList[] = {&generator_binary[0], &generator_binary[0], &p1[0], &p2[0], &p3[0], &p4[0], &p5[0], nullptr};
|
||||
|
||||
int pid;
|
||||
if ((pid = fork()) == -1)
|
||||
{
|
||||
perror("fork err");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
execv(&generator_binary[0], parmList);
|
||||
std::cout << "Return not expected. Must be an execv err.\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
waitpid(pid, &child_status, 0);
|
||||
|
||||
std::cout << "Signal and Observables RINEX and RAW files created.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CATelemetryDecoderTest::configure_receiver()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
gnss_synchro.PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
gnss_synchro.PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(baseband_sampling_freq));
|
||||
|
||||
// Set Tracking
|
||||
config->set_property("Tracking_1C.item_type", "gr_complex");
|
||||
config->set_property("Tracking_1C.dump", "true");
|
||||
config->set_property("Tracking_1C.dump_filename", "./tracking_ch_");
|
||||
config->set_property("Tracking_1C.pll_bw_hz", "20.0");
|
||||
config->set_property("Tracking_1C.dll_bw_hz", "1.5");
|
||||
config->set_property("Tracking_1C.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_1C.unified", "true");
|
||||
config->set_property("TelemetryDecoder_1C.dump", "true");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CATelemetryDecoderTest::check_results(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
// arma::vec err = meas_value - true_value_interp + 0.001;
|
||||
arma::vec err = meas_value - true_value_interp; // - 0.001;
|
||||
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TLM TOW RMSE="
|
||||
<< rmse << ", mean=" << error_mean
|
||||
<< ", stdev=" << sqrt(error_var)
|
||||
<< " (max,min)=" << max_error
|
||||
<< "," << min_error
|
||||
<< " [Seconds]\n";
|
||||
std::cout.precision(ss);
|
||||
|
||||
ASSERT_LT(rmse, 0.3E-6);
|
||||
ASSERT_LT(error_mean, 0.3E-6);
|
||||
ASSERT_GT(error_mean, -0.3E-6);
|
||||
ASSERT_LT(error_var, 0.3E-6);
|
||||
ASSERT_LT(max_error, 0.5E-6);
|
||||
ASSERT_GT(min_error, -0.5E-6);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CATelemetryDecoderTest, ValidationOfResults)
|
||||
{
|
||||
// Configure the signal generator
|
||||
configure_generator();
|
||||
|
||||
// Generate signal raw signal samples and observations RINEX file
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_disable_generator == false)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_disable_generator) == false)
|
||||
#endif
|
||||
{
|
||||
generate_signal();
|
||||
}
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
configure_receiver();
|
||||
|
||||
// open true observables log file written by the simulator
|
||||
Tracking_True_Obs_Reader true_obs_data;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int test_satellite_PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
int test_satellite_PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
std::cout << "Testing satellite PRN=" << test_satellite_PRN << '\n';
|
||||
std::string true_obs_file = std::string("./gps_l1_ca_obs_prn");
|
||||
true_obs_file.append(std::to_string(test_satellite_PRN));
|
||||
true_obs_file.append(".dat");
|
||||
ASSERT_NO_THROW({
|
||||
if (true_obs_data.open_obs_file(true_obs_file) == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
}) << "Failure opening true observables file";
|
||||
|
||||
top_block = gr::make_top_block("Telemetry_Decoder test");
|
||||
std::shared_ptr<TrackingInterface> tracking = std::make_shared<GpsL1CaDllPllTracking>(config.get(), "Tracking_1C", 1, 1);
|
||||
// std::shared_ptr<TrackingInterface> tracking = std::make_shared<GpsL1CaDllPllCAidTracking>(config.get(), "Tracking_1C", 1, 1);
|
||||
|
||||
auto msg_rx = GpsL1CADllPllTelemetryDecoderTest_msg_rx_make();
|
||||
|
||||
// load acquisition data based on the first epoch of the true observations
|
||||
ASSERT_NO_THROW({
|
||||
if (true_obs_data.read_binary_obs() == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
}) << "Failure reading true observables file";
|
||||
|
||||
// restart the epoch counter
|
||||
true_obs_data.restart();
|
||||
|
||||
std::cout << "Initial Doppler [Hz]=" << true_obs_data.doppler_l1_hz << " Initial code delay [Chips]=" << true_obs_data.prn_delay_chips << '\n';
|
||||
gnss_synchro.Acq_delay_samples = (GPS_L1_CA_CODE_LENGTH_CHIPS - true_obs_data.prn_delay_chips / GPS_L1_CA_CODE_LENGTH_CHIPS) * baseband_sampling_freq * GPS_L1_CA_CODE_PERIOD_S;
|
||||
gnss_synchro.Acq_doppler_hz = true_obs_data.doppler_l1_hz;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
std::shared_ptr<TelemetryDecoderInterface> tlm = std::make_shared<GpsL1CaTelemetryDecoder>(config.get(), "TelemetryDecoder_1C", 1, 1);
|
||||
tlm->set_channel(0);
|
||||
|
||||
std::shared_ptr<GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx> tlm_msg_rx = GpsL1CADllPllTelemetryDecoderTest_tlm_msg_rx_make();
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string file = "./" + filename_raw_data;
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name, false);
|
||||
gr::blocks::interleaved_char_to_complex::sptr gr_interleaved_char_to_complex = gr::blocks::interleaved_char_to_complex::make();
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, gr_interleaved_char_to_complex, 0);
|
||||
top_block->connect(gr_interleaved_char_to_complex, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, tlm->get_left_block(), 0);
|
||||
top_block->connect(tlm->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting the blocks.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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.";
|
||||
|
||||
// check results
|
||||
// load the true values
|
||||
int64_t nepoch = true_obs_data.num_epochs();
|
||||
std::cout << "True observation epochs=" << nepoch << '\n';
|
||||
|
||||
arma::vec true_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec true_acc_carrier_phase_cycles = arma::zeros(nepoch, 1);
|
||||
arma::vec true_Doppler_Hz = arma::zeros(nepoch, 1);
|
||||
arma::vec true_prn_delay_chips = arma::zeros(nepoch, 1);
|
||||
arma::vec true_tow_s = arma::zeros(nepoch, 1);
|
||||
|
||||
int64_t epoch_counter = 0;
|
||||
while (true_obs_data.read_binary_obs())
|
||||
{
|
||||
true_timestamp_s(epoch_counter) = true_obs_data.signal_timestamp_s;
|
||||
true_acc_carrier_phase_cycles(epoch_counter) = true_obs_data.acc_carrier_phase_cycles;
|
||||
true_Doppler_Hz(epoch_counter) = true_obs_data.doppler_l1_hz;
|
||||
true_prn_delay_chips(epoch_counter) = true_obs_data.prn_delay_chips;
|
||||
true_tow_s(epoch_counter) = true_obs_data.tow;
|
||||
epoch_counter++;
|
||||
}
|
||||
|
||||
// load the measured values
|
||||
Tlm_Dump_Reader tlm_dump;
|
||||
ASSERT_NO_THROW({
|
||||
if (tlm_dump.open_obs_file(std::string("./telemetry0.dat")) == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
}) << "Failure opening telemetry dump file";
|
||||
|
||||
nepoch = tlm_dump.num_epochs();
|
||||
std::cout << "Measured observation epochs=" << nepoch << '\n';
|
||||
|
||||
arma::vec tlm_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec tlm_TOW_at_Preamble = arma::zeros(nepoch, 1);
|
||||
arma::vec tlm_tow_s = arma::zeros(nepoch, 1);
|
||||
|
||||
epoch_counter = 0;
|
||||
while (tlm_dump.read_binary_obs())
|
||||
{
|
||||
tlm_timestamp_s(epoch_counter) = static_cast<double>(tlm_dump.Tracking_sample_counter) / static_cast<double>(baseband_sampling_freq);
|
||||
tlm_TOW_at_Preamble(epoch_counter) = tlm_dump.d_TOW_at_Preamble;
|
||||
tlm_tow_s(epoch_counter) = tlm_dump.TOW_at_current_symbol;
|
||||
epoch_counter++;
|
||||
}
|
||||
|
||||
// Cut measurement initial transitory of the measurements
|
||||
arma::uvec initial_meas_point = arma::find(tlm_tow_s >= true_tow_s(0), 1, "first");
|
||||
ASSERT_EQ(initial_meas_point.is_empty(), false);
|
||||
tlm_timestamp_s = tlm_timestamp_s.subvec(initial_meas_point(0), tlm_timestamp_s.size() - 1);
|
||||
tlm_tow_s = tlm_tow_s.subvec(initial_meas_point(0), tlm_tow_s.size() - 1);
|
||||
|
||||
check_results(true_timestamp_s, true_tow_s, tlm_timestamp_s, tlm_tow_s);
|
||||
|
||||
std::cout << "Test completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* \file bayesian_estimation_test.cc
|
||||
* \brief This file implements feasibility test for the BCE library.
|
||||
* \author Gerald LaMountain, 2018. gerald(at)ece.neu.edu
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "bayesian_estimation.h"
|
||||
#include <armadillo>
|
||||
#include <gtest/gtest.h>
|
||||
#include <random>
|
||||
|
||||
#define BAYESIAN_TEST_N_TRIALS 100
|
||||
#define BAYESIAN_TEST_ITER 10000
|
||||
|
||||
TEST(BayesianEstimationPositivityTest, BayesianPositivityTest)
|
||||
{
|
||||
Bayesian_estimator bayes;
|
||||
arma::vec bayes_mu = arma::zeros(1, 1);
|
||||
int bayes_nu = 0;
|
||||
int bayes_kappa = 0;
|
||||
arma::mat bayes_Psi = arma::ones(1, 1);
|
||||
arma::vec input = arma::zeros(1, 1);
|
||||
|
||||
// -- Perform initializations ------------------------------
|
||||
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::normal_distribution<float> normal_dist(0, 5);
|
||||
|
||||
for (int k = 0; k < BAYESIAN_TEST_N_TRIALS; k++)
|
||||
{
|
||||
bayes.init(bayes_mu, bayes_kappa, bayes_nu, bayes_Psi);
|
||||
|
||||
for (int n = 0; n < BAYESIAN_TEST_ITER; n++)
|
||||
{
|
||||
input(0) = static_cast<double>(normal_dist(e1));
|
||||
bayes.update_sequential(input);
|
||||
|
||||
arma::mat output = bayes.get_Psi_est();
|
||||
double output0 = output(0, 0);
|
||||
ASSERT_EQ(output0 > 0.0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*!
|
||||
* \file cpu_multicorrelator_real_codes_test.cc
|
||||
* \brief This file implements timing tests for the FFT.
|
||||
* \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 "GPS_L1_CA.h"
|
||||
#include "cpu_multicorrelator_real_codes.h"
|
||||
#include "gps_sdr_signal_replica.h"
|
||||
#include <gnuradio/gr_complex.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr_alloc.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
DEFINE_int32(cpu_multicorrelator_real_codes_iterations_test, 100, "Number of averaged iterations in CPU multicorrelator test timing test");
|
||||
DEFINE_int32(cpu_multicorrelator_real_codes_max_threads_test, 12, "Number of maximum concurrent correlators in CPU multicorrelator test timing test");
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
ABSL_FLAG(int32_t, cpu_multicorrelator_real_codes_iterations_test, 100, "Number of averaged iterations in CPU multicorrelator test timing test");
|
||||
ABSL_FLAG(int32_t, cpu_multicorrelator_real_codes_max_threads_test, 12, "Number of maximum concurrent correlators in CPU multicorrelator test timing test");
|
||||
#endif
|
||||
|
||||
|
||||
void run_correlator_cpu_real_codes(Cpu_Multicorrelator_Real_Codes* correlator,
|
||||
float d_rem_carrier_phase_rad,
|
||||
float d_carrier_phase_step_rad,
|
||||
float d_code_phase_step_chips,
|
||||
float d_code_phase_rate_step_chips,
|
||||
float d_rem_code_phase_chips,
|
||||
int correlation_size)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_cpu_multicorrelator_real_codes_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_cpu_multicorrelator_real_codes_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
correlator->Carrier_wipeoff_multicorrelator_resampler(d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
d_code_phase_rate_step_chips,
|
||||
correlation_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CpuMulticorrelatorRealCodesTest, MeasureExecutionTime)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int max_threads = FLAGS_cpu_multicorrelator_real_codes_max_threads_test;
|
||||
#else
|
||||
int max_threads = absl::GetFlag(FLAGS_cpu_multicorrelator_real_codes_max_threads_test);
|
||||
#endif
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<Cpu_Multicorrelator_Real_Codes*> correlator_pool(max_threads);
|
||||
unsigned int correlation_sizes[3] = {2048, 4096, 8192};
|
||||
double execution_times[3];
|
||||
|
||||
float* d_ca_code;
|
||||
gr_complex* in_cpu;
|
||||
gr_complex* d_correlator_outs;
|
||||
|
||||
int d_n_correlator_taps = 3;
|
||||
int d_vector_length = correlation_sizes[2]; // max correlation size to allocate all the necessary memory
|
||||
float* d_local_code_shift_chips;
|
||||
|
||||
// allocate host memory
|
||||
// Get space for a vector with the C/A code replica sampled 1x/chip
|
||||
d_ca_code = static_cast<float*>(volk_gnsssdr_malloc(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS) * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
in_cpu = static_cast<gr_complex*>(volk_gnsssdr_malloc(2 * d_vector_length * sizeof(gr_complex), volk_gnsssdr_get_alignment()));
|
||||
|
||||
// correlator outputs (scalar)
|
||||
d_n_correlator_taps = 3; // Early, Prompt, and Late
|
||||
d_correlator_outs = static_cast<gr_complex*>(volk_gnsssdr_malloc(d_n_correlator_taps * sizeof(gr_complex), volk_gnsssdr_get_alignment()));
|
||||
for (int n = 0; n < d_n_correlator_taps; n++)
|
||||
{
|
||||
d_correlator_outs[n] = gr_complex(0, 0);
|
||||
}
|
||||
d_local_code_shift_chips = static_cast<float*>(volk_gnsssdr_malloc(d_n_correlator_taps * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
// Set TAPs delay values [chips]
|
||||
float d_early_late_spc_chips = 0.5;
|
||||
d_local_code_shift_chips[0] = -d_early_late_spc_chips;
|
||||
d_local_code_shift_chips[1] = 0.0;
|
||||
d_local_code_shift_chips[2] = d_early_late_spc_chips;
|
||||
|
||||
// --- Perform initializations ------------------------------
|
||||
|
||||
// local code resampler on GPU
|
||||
// generate local reference (1 sample per chip)
|
||||
gps_l1_ca_code_gen_float(own::span<float>(d_ca_code, static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS) * sizeof(float)), 1, 0);
|
||||
// generate inut signal
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::uniform_real_distribution<float> uniform_dist(0, 1);
|
||||
for (int n = 0; n < 2 * d_vector_length; n++)
|
||||
{
|
||||
in_cpu[n] = std::complex<float>(uniform_dist(e1), uniform_dist(e1));
|
||||
}
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n] = new Cpu_Multicorrelator_Real_Codes();
|
||||
correlator_pool[n]->init(d_vector_length, d_n_correlator_taps);
|
||||
correlator_pool[n]->set_input_output_vectors(d_correlator_outs, in_cpu);
|
||||
correlator_pool[n]->set_local_code_and_taps(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS), d_ca_code, d_local_code_shift_chips);
|
||||
}
|
||||
|
||||
float d_rem_carrier_phase_rad = 0.0;
|
||||
float d_carrier_phase_step_rad = 0.1;
|
||||
float d_code_phase_step_chips = 0.3;
|
||||
float d_code_phase_rate_step_chips = 0.00001;
|
||||
float d_rem_code_phase_chips = 0.4;
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
for (int correlation_sizes_idx = 0; correlation_sizes_idx < 3; correlation_sizes_idx++) {
|
||||
for (int current_max_threads = 1; current_max_threads < (max_threads + 1); current_max_threads++)
|
||||
{
|
||||
std::cout << "Running " << current_max_threads << " concurrent correlators\n";
|
||||
start = std::chrono::system_clock::now();
|
||||
// create the concurrent correlator threads
|
||||
for (int current_thread = 0; current_thread < current_max_threads; current_thread++)
|
||||
{
|
||||
thread_pool.emplace_back(run_correlator_cpu_real_codes,
|
||||
correlator_pool[current_thread],
|
||||
d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_code_phase_rate_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_sizes[correlation_sizes_idx]);
|
||||
}
|
||||
// wait the threads to finish they work and destroy the thread objects
|
||||
for (auto& t : thread_pool)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
thread_pool.clear();
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(FLAGS_cpu_multicorrelator_real_codes_iterations_test);
|
||||
#else
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_cpu_multicorrelator_real_codes_iterations_test));
|
||||
#endif
|
||||
std::cout << "CPU Multicorrelator (real codes) execution time for length=" << correlation_sizes[correlation_sizes_idx]
|
||||
<< " : " << execution_times[correlation_sizes_idx] << " [s]\n";
|
||||
}
|
||||
});
|
||||
|
||||
volk_gnsssdr_free(d_local_code_shift_chips);
|
||||
volk_gnsssdr_free(d_correlator_outs);
|
||||
volk_gnsssdr_free(d_ca_code);
|
||||
volk_gnsssdr_free(in_cpu);
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n]->free();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CpuMulticorrelatorRealCodesTest, MeasureExecutionTimeAlloc)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int max_threads = FLAGS_cpu_multicorrelator_real_codes_max_threads_test;
|
||||
#else
|
||||
int max_threads = absl::GetFlag(FLAGS_cpu_multicorrelator_real_codes_max_threads_test);
|
||||
#endif
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<Cpu_Multicorrelator_Real_Codes*> correlator_pool(max_threads);
|
||||
unsigned int correlation_sizes[3] = {2048, 4096, 8192};
|
||||
double execution_times[3];
|
||||
volk_gnsssdr::vector<float> d_ca_code(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS));
|
||||
int d_n_correlator_taps = 3;
|
||||
int d_vector_length = correlation_sizes[2]; // max correlation size to allocate all the necessary memory
|
||||
volk_gnsssdr::vector<gr_complex> in_cpu(2 * d_vector_length);
|
||||
|
||||
// correlator outputs (scalar)
|
||||
d_n_correlator_taps = 3; // Early, Prompt, and Late
|
||||
volk_gnsssdr::vector<gr_complex> d_correlator_outs(d_n_correlator_taps, gr_complex(0.0, 0.0));
|
||||
volk_gnsssdr::vector<float> d_local_code_shift_chips(d_n_correlator_taps);
|
||||
|
||||
// Set TAPs delay values [chips]
|
||||
float d_early_late_spc_chips = 0.5;
|
||||
d_local_code_shift_chips[0] = -d_early_late_spc_chips;
|
||||
d_local_code_shift_chips[1] = 0.0;
|
||||
d_local_code_shift_chips[2] = d_early_late_spc_chips;
|
||||
|
||||
// --- Perform initializations ------------------------------
|
||||
|
||||
// local code resampler on GPU
|
||||
// generate local reference (1 sample per chip)
|
||||
gps_l1_ca_code_gen_float(d_ca_code, 1, 0);
|
||||
// generate inut signal
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::uniform_real_distribution<float> uniform_dist(0, 1);
|
||||
for (int n = 0; n < 2 * d_vector_length; n++)
|
||||
{
|
||||
in_cpu[n] = std::complex<float>(uniform_dist(e1), uniform_dist(e1));
|
||||
}
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n] = new Cpu_Multicorrelator_Real_Codes();
|
||||
correlator_pool[n]->init(d_vector_length, d_n_correlator_taps);
|
||||
correlator_pool[n]->set_input_output_vectors(d_correlator_outs.data(), in_cpu.data());
|
||||
correlator_pool[n]->set_local_code_and_taps(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS), d_ca_code.data(), d_local_code_shift_chips.data());
|
||||
}
|
||||
|
||||
float d_rem_carrier_phase_rad = 0.0;
|
||||
float d_carrier_phase_step_rad = 0.1;
|
||||
float d_code_phase_step_chips = 0.3;
|
||||
float d_code_phase_rate_step_chips = 0.00001;
|
||||
float d_rem_code_phase_chips = 0.4;
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
for (int correlation_sizes_idx = 0; correlation_sizes_idx < 3; correlation_sizes_idx++) {
|
||||
for (int current_max_threads = 1; current_max_threads < (max_threads + 1); current_max_threads++)
|
||||
{
|
||||
std::cout << "Running " << current_max_threads << " concurrent correlators\n";
|
||||
start = std::chrono::system_clock::now();
|
||||
// create the concurrent correlator threads
|
||||
for (int current_thread = 0; current_thread < current_max_threads; current_thread++)
|
||||
{
|
||||
thread_pool.emplace_back(run_correlator_cpu_real_codes,
|
||||
correlator_pool[current_thread],
|
||||
d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_code_phase_rate_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_sizes[correlation_sizes_idx]);
|
||||
}
|
||||
// wait the threads to finish they work and destroy the thread objects
|
||||
for (auto& t : thread_pool)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
thread_pool.clear();
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(FLAGS_cpu_multicorrelator_real_codes_iterations_test);
|
||||
#else
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_cpu_multicorrelator_real_codes_iterations_test));
|
||||
#endif
|
||||
std::cout << "CPU Multicorrelator (real codes) execution time for length=" << correlation_sizes[correlation_sizes_idx]
|
||||
<< " : " << execution_times[correlation_sizes_idx] << " [s]\n";
|
||||
}
|
||||
});
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n]->free();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
/*!
|
||||
* \file fft_length_test.cc
|
||||
* \brief This file implements timing tests for the FFT.
|
||||
* \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 "GPS_L1_CA.h"
|
||||
#include "cpu_multicorrelator.h"
|
||||
#include "gps_sdr_signal_replica.h"
|
||||
#include <gnuradio/gr_complex.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <volk_gnsssdr/volk_gnsssdr_alloc.h>
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(cpu_multicorrelator_iterations_test, 100, "Number of averaged iterations in CPU multicorrelator test timing test");
|
||||
DEFINE_int32(cpu_multicorrelator_max_threads_test, 12, "Number of maximum concurrent correlators in CPU multicorrelator test timing test");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, cpu_multicorrelator_iterations_test, 100, "Number of averaged iterations in CPU multicorrelator test timing test");
|
||||
ABSL_FLAG(int32_t, cpu_multicorrelator_max_threads_test, 12, "Number of maximum concurrent correlators in CPU multicorrelator test timing test");
|
||||
#endif
|
||||
|
||||
void run_correlator_cpu(Cpu_Multicorrelator* correlator,
|
||||
float d_rem_carrier_phase_rad,
|
||||
float d_carrier_phase_step_rad,
|
||||
float d_code_phase_step_chips,
|
||||
float d_rem_code_phase_chips,
|
||||
int correlation_size)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_cpu_multicorrelator_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_cpu_multicorrelator_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
correlator->Carrier_wipeoff_multicorrelator_resampler(d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CpuMulticorrelatorTest, MeasureExecutionTime)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int max_threads = FLAGS_cpu_multicorrelator_max_threads_test;
|
||||
#else
|
||||
int max_threads = absl::GetFlag(FLAGS_cpu_multicorrelator_max_threads_test);
|
||||
#endif
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<Cpu_Multicorrelator*> correlator_pool(max_threads);
|
||||
unsigned int correlation_sizes[3] = {2048, 4096, 8192};
|
||||
double execution_times[3];
|
||||
|
||||
gr_complex* d_ca_code;
|
||||
gr_complex* in_cpu;
|
||||
gr_complex* d_correlator_outs;
|
||||
|
||||
int d_n_correlator_taps = 3;
|
||||
int d_vector_length = correlation_sizes[2]; // max correlation size to allocate all the necessary memory
|
||||
float* d_local_code_shift_chips;
|
||||
|
||||
// allocate host memory
|
||||
// Get space for a vector with the C/A code replica sampled 1x/chip
|
||||
d_ca_code = static_cast<gr_complex*>(volk_gnsssdr_malloc(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS) * sizeof(gr_complex), volk_gnsssdr_get_alignment()));
|
||||
in_cpu = static_cast<gr_complex*>(volk_gnsssdr_malloc(2 * d_vector_length * sizeof(gr_complex), volk_gnsssdr_get_alignment()));
|
||||
|
||||
// correlator outputs (scalar)
|
||||
d_n_correlator_taps = 3; // Early, Prompt, and Late
|
||||
d_correlator_outs = static_cast<gr_complex*>(volk_gnsssdr_malloc(d_n_correlator_taps * sizeof(gr_complex), volk_gnsssdr_get_alignment()));
|
||||
for (int n = 0; n < d_n_correlator_taps; n++)
|
||||
{
|
||||
d_correlator_outs[n] = gr_complex(0, 0);
|
||||
}
|
||||
d_local_code_shift_chips = static_cast<float*>(volk_gnsssdr_malloc(d_n_correlator_taps * sizeof(float), volk_gnsssdr_get_alignment()));
|
||||
// Set TAPs delay values [chips]
|
||||
float d_early_late_spc_chips = 0.5;
|
||||
d_local_code_shift_chips[0] = -d_early_late_spc_chips;
|
||||
d_local_code_shift_chips[1] = 0.0;
|
||||
d_local_code_shift_chips[2] = d_early_late_spc_chips;
|
||||
|
||||
// -- Perform initializations ------------------------------
|
||||
|
||||
// local code resampler on GPU
|
||||
// generate local reference (1 sample per chip)
|
||||
gps_l1_ca_code_gen_complex(own::span<gr_complex>(d_ca_code, static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS) * sizeof(gr_complex)), 1, 0);
|
||||
// generate inut signal
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::uniform_real_distribution<float> uniform_dist(0, 1);
|
||||
for (int n = 0; n < 2 * d_vector_length; n++)
|
||||
{
|
||||
in_cpu[n] = std::complex<float>(uniform_dist(e1), uniform_dist(e1));
|
||||
}
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n] = new Cpu_Multicorrelator();
|
||||
correlator_pool[n]->init(d_vector_length, d_n_correlator_taps);
|
||||
correlator_pool[n]->set_input_output_vectors(d_correlator_outs, in_cpu);
|
||||
correlator_pool[n]->set_local_code_and_taps(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS), d_ca_code, d_local_code_shift_chips);
|
||||
}
|
||||
|
||||
float d_rem_carrier_phase_rad = 0.0;
|
||||
float d_carrier_phase_step_rad = 0.1;
|
||||
float d_code_phase_step_chips = 0.3;
|
||||
float d_rem_code_phase_chips = 0.4;
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
for (int correlation_sizes_idx = 0; correlation_sizes_idx < 3; correlation_sizes_idx++) {
|
||||
for (int current_max_threads = 1; current_max_threads < (max_threads + 1); current_max_threads++)
|
||||
{
|
||||
std::cout << "Running " << current_max_threads << " concurrent correlators\n";
|
||||
start = std::chrono::system_clock::now();
|
||||
// create the concurrent correlator threads
|
||||
for (int current_thread = 0; current_thread < current_max_threads; current_thread++)
|
||||
{
|
||||
thread_pool.emplace_back(run_correlator_cpu,
|
||||
correlator_pool[current_thread],
|
||||
d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_sizes[correlation_sizes_idx]);
|
||||
}
|
||||
// wait the threads to finish they work and destroy the thread objects
|
||||
for (auto& t : thread_pool)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
thread_pool.clear();
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(FLAGS_cpu_multicorrelator_iterations_test);
|
||||
#else
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_cpu_multicorrelator_iterations_test));
|
||||
#endif
|
||||
std::cout << "CPU Multicorrelator execution time for length=" << correlation_sizes[correlation_sizes_idx]
|
||||
<< " : " << execution_times[correlation_sizes_idx] << " [s]\n";
|
||||
}
|
||||
});
|
||||
|
||||
volk_gnsssdr_free(d_local_code_shift_chips);
|
||||
volk_gnsssdr_free(d_correlator_outs);
|
||||
volk_gnsssdr_free(d_ca_code);
|
||||
volk_gnsssdr_free(in_cpu);
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n]->free();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CpuMulticorrelatorTest, MeasureExecutionTimeAlloc)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int max_threads = FLAGS_cpu_multicorrelator_max_threads_test;
|
||||
#else
|
||||
int max_threads = absl::GetFlag(FLAGS_cpu_multicorrelator_max_threads_test);
|
||||
#endif
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<Cpu_Multicorrelator*> correlator_pool(max_threads);
|
||||
unsigned int correlation_sizes[3] = {2048, 4096, 8192};
|
||||
double execution_times[3];
|
||||
|
||||
int d_n_correlator_taps = 3;
|
||||
int d_vector_length = correlation_sizes[2]; // max correlation size to allocate all the necessary memory
|
||||
|
||||
// allocate host memory
|
||||
// Get space for a vector with the C/A code replica sampled 1x/chip
|
||||
volk_gnsssdr::vector<gr_complex> d_ca_code(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS));
|
||||
volk_gnsssdr::vector<gr_complex> in_cpu(2 * d_vector_length);
|
||||
|
||||
// correlator outputs (scalar)
|
||||
d_n_correlator_taps = 3; // Early, Prompt, and Late
|
||||
volk_gnsssdr::vector<gr_complex> d_correlator_outs(d_n_correlator_taps, gr_complex(0.0, 0.0));
|
||||
volk_gnsssdr::vector<float> d_local_code_shift_chips(d_n_correlator_taps);
|
||||
// Set TAPs delay values [chips]
|
||||
float d_early_late_spc_chips = 0.5;
|
||||
d_local_code_shift_chips[0] = -d_early_late_spc_chips;
|
||||
d_local_code_shift_chips[1] = 0.0;
|
||||
d_local_code_shift_chips[2] = d_early_late_spc_chips;
|
||||
|
||||
// -- Perform initializations ------------------------------
|
||||
|
||||
// local code resampler on GPU
|
||||
// generate local reference (1 sample per chip)
|
||||
gps_l1_ca_code_gen_complex(d_ca_code, 1, 0);
|
||||
// generate inut signal
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::uniform_real_distribution<float> uniform_dist(0, 1);
|
||||
for (int n = 0; n < 2 * d_vector_length; n++)
|
||||
{
|
||||
in_cpu[n] = std::complex<float>(uniform_dist(e1), uniform_dist(e1));
|
||||
}
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n] = new Cpu_Multicorrelator();
|
||||
correlator_pool[n]->init(d_vector_length, d_n_correlator_taps);
|
||||
correlator_pool[n]->set_input_output_vectors(d_correlator_outs.data(), in_cpu.data());
|
||||
correlator_pool[n]->set_local_code_and_taps(static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS), d_ca_code.data(), d_local_code_shift_chips.data());
|
||||
}
|
||||
|
||||
float d_rem_carrier_phase_rad = 0.0;
|
||||
float d_carrier_phase_step_rad = 0.1;
|
||||
float d_code_phase_step_chips = 0.3;
|
||||
float d_rem_code_phase_chips = 0.4;
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
for (int correlation_sizes_idx = 0; correlation_sizes_idx < 3; correlation_sizes_idx++) {
|
||||
for (int current_max_threads = 1; current_max_threads < (max_threads + 1); current_max_threads++)
|
||||
{
|
||||
std::cout << "Running " << current_max_threads << " concurrent correlators\n";
|
||||
start = std::chrono::system_clock::now();
|
||||
// create the concurrent correlator threads
|
||||
for (int current_thread = 0; current_thread < current_max_threads; current_thread++)
|
||||
{
|
||||
thread_pool.emplace_back(run_correlator_cpu,
|
||||
correlator_pool[current_thread],
|
||||
d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_sizes[correlation_sizes_idx]);
|
||||
}
|
||||
// wait the threads to finish they work and destroy the thread objects
|
||||
for (auto& t : thread_pool)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
thread_pool.clear();
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(FLAGS_cpu_multicorrelator_iterations_test);
|
||||
#else
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_cpu_multicorrelator_iterations_test));
|
||||
#endif
|
||||
std::cout << "CPU Multicorrelator execution time for length=" << correlation_sizes[correlation_sizes_idx]
|
||||
<< " : " << execution_times[correlation_sizes_idx] << " [s]\n";
|
||||
}
|
||||
});
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n]->free();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*!
|
||||
* \file cubature_filter_test.cc
|
||||
* \brief This file implements numerical accuracy test for the CKF library.
|
||||
* \author Gerald LaMountain, 2019. gerald(at)ece.neu.edu
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "nonlinear_tracking.h"
|
||||
#include <armadillo>
|
||||
#include <gtest/gtest.h>
|
||||
#include <random>
|
||||
|
||||
#define CUBATURE_TEST_N_TRIALS 1000
|
||||
#define CUBATURE_TEST_TOLERANCE 0.01
|
||||
|
||||
class TransitionModel : public ModelFunction
|
||||
{
|
||||
public:
|
||||
explicit TransitionModel(const arma::mat& kf_F) : coeff_mat(kf_F){};
|
||||
arma::vec operator()(const arma::vec& input) override { return coeff_mat * input; };
|
||||
|
||||
private:
|
||||
arma::mat coeff_mat;
|
||||
};
|
||||
|
||||
class MeasurementModel : public ModelFunction
|
||||
{
|
||||
public:
|
||||
explicit MeasurementModel(const arma::mat& kf_H) : coeff_mat(kf_H){};
|
||||
arma::vec operator()(const arma::vec& input) override { return coeff_mat * input; };
|
||||
|
||||
private:
|
||||
arma::mat coeff_mat;
|
||||
};
|
||||
|
||||
TEST(CubatureFilterComputationTest, CubatureFilterTest)
|
||||
{
|
||||
CubatureFilter kf_cubature;
|
||||
|
||||
arma::vec kf_x;
|
||||
arma::mat kf_P_x;
|
||||
|
||||
arma::vec kf_x_pre;
|
||||
arma::mat kf_P_x_pre;
|
||||
|
||||
arma::vec ckf_x_pre;
|
||||
arma::mat ckf_P_x_pre;
|
||||
|
||||
arma::vec kf_x_post;
|
||||
arma::mat kf_P_x_post;
|
||||
|
||||
arma::vec ckf_x_post;
|
||||
arma::mat ckf_P_x_post;
|
||||
|
||||
arma::mat kf_F;
|
||||
arma::mat kf_H;
|
||||
|
||||
arma::mat kf_Q;
|
||||
arma::mat kf_R;
|
||||
|
||||
arma::vec eta;
|
||||
arma::vec nu;
|
||||
|
||||
arma::vec kf_y;
|
||||
arma::mat kf_P_y;
|
||||
arma::mat kf_K;
|
||||
|
||||
ModelFunction* transition_function;
|
||||
ModelFunction* measurement_function;
|
||||
|
||||
// -- Perform initializations ------------------------------
|
||||
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::normal_distribution<float> normal_dist(0, 5);
|
||||
std::uniform_real_distribution<float> uniform_dist(0.1, 5.0);
|
||||
std::uniform_int_distribution<> uniform_dist_int(1, 5);
|
||||
|
||||
uint8_t nx = 0;
|
||||
uint8_t ny = 0;
|
||||
|
||||
for (uint16_t k = 0; k < CUBATURE_TEST_N_TRIALS; k++)
|
||||
{
|
||||
nx = static_cast<uint8_t>(uniform_dist_int(e1));
|
||||
ny = static_cast<uint8_t>(uniform_dist_int(e1));
|
||||
|
||||
kf_x = arma::randn<arma::vec>(nx, 1);
|
||||
|
||||
kf_P_x_post = 5.0 * arma::diagmat(arma::randu<arma::vec>(nx, 1));
|
||||
kf_x_post = arma::mvnrnd(kf_x, kf_P_x_post);
|
||||
|
||||
kf_cubature.initialize(kf_x_post, kf_P_x_post);
|
||||
|
||||
// Prediction Step
|
||||
kf_F = arma::randu<arma::mat>(nx, nx);
|
||||
kf_Q = arma::diagmat(arma::randu<arma::vec>(nx, 1));
|
||||
|
||||
transition_function = new TransitionModel(kf_F);
|
||||
arma::mat ttx = (*transition_function)(kf_x_post);
|
||||
|
||||
kf_cubature.predict_sequential(kf_x_post, kf_P_x_post, transition_function, kf_Q);
|
||||
|
||||
ckf_x_pre = kf_cubature.get_x_pred();
|
||||
ckf_P_x_pre = kf_cubature.get_P_x_pred();
|
||||
|
||||
kf_x_pre = kf_F * kf_x_post;
|
||||
kf_P_x_pre = kf_F * kf_P_x_post * kf_F.t() + kf_Q;
|
||||
|
||||
EXPECT_TRUE(arma::approx_equal(ckf_x_pre, kf_x_pre, "absdiff", CUBATURE_TEST_TOLERANCE));
|
||||
EXPECT_TRUE(arma::approx_equal(ckf_P_x_pre, kf_P_x_pre, "absdiff", CUBATURE_TEST_TOLERANCE));
|
||||
|
||||
// Update Step
|
||||
kf_H = arma::randu<arma::mat>(ny, nx);
|
||||
kf_R = arma::diagmat(arma::randu<arma::vec>(ny, 1));
|
||||
|
||||
eta = arma::mvnrnd(arma::zeros<arma::vec>(nx, 1), kf_Q);
|
||||
nu = arma::mvnrnd(arma::zeros<arma::vec>(ny, 1), kf_R);
|
||||
|
||||
kf_y = kf_H * (kf_F * kf_x + eta) + nu;
|
||||
|
||||
measurement_function = new MeasurementModel(kf_H);
|
||||
kf_cubature.update_sequential(kf_y, kf_x_pre, kf_P_x_pre, measurement_function, kf_R);
|
||||
|
||||
ckf_x_post = kf_cubature.get_x_est();
|
||||
ckf_P_x_post = kf_cubature.get_P_x_est();
|
||||
|
||||
kf_P_y = kf_H * kf_P_x_pre * kf_H.t() + kf_R;
|
||||
kf_K = (kf_P_x_pre * kf_H.t()) * arma::inv(kf_P_y);
|
||||
|
||||
kf_x_post = kf_x_pre + kf_K * (kf_y - kf_H * kf_x_pre);
|
||||
kf_P_x_post = (arma::eye(nx, nx) - kf_K * kf_H) * kf_P_x_pre;
|
||||
|
||||
EXPECT_TRUE(arma::approx_equal(ckf_x_post, kf_x_post, "absdiff", CUBATURE_TEST_TOLERANCE));
|
||||
EXPECT_TRUE(arma::approx_equal(ckf_P_x_post, kf_P_x_post, "absdiff", CUBATURE_TEST_TOLERANCE));
|
||||
|
||||
delete transition_function;
|
||||
delete measurement_function;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*!
|
||||
* \file discriminator_test.cc
|
||||
* \brief This file implements tests for the tracking discriminators
|
||||
* \author Cillian O'Driscoll, 2019. cillian.odriscoll(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "tracking_discriminators.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
double BpskCorrelationFunction(double offset_in_chips)
|
||||
{
|
||||
double abs_tau = std::abs(offset_in_chips);
|
||||
|
||||
if (abs_tau > 1.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
return 1.0 - abs_tau;
|
||||
}
|
||||
|
||||
|
||||
TEST(DllNcEMinusLNormalizedTest, Bpsk)
|
||||
{
|
||||
std::vector<gr_complex> complex_amplitude_vector = {{1.0, 0.0}, {-1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}};
|
||||
std::vector<double> spacing_vector = {0.5, 0.25, 0.1, 0.01};
|
||||
std::vector<double> error_vector = {0.0, 0.01, 0.1, 0.25, -0.25, -0.1, -0.01};
|
||||
|
||||
for (auto A : complex_amplitude_vector)
|
||||
{
|
||||
for (auto spacing : spacing_vector)
|
||||
{
|
||||
for (auto err : error_vector)
|
||||
{
|
||||
gr_complex E = A * static_cast<float>(BpskCorrelationFunction(err - spacing));
|
||||
gr_complex L = A * static_cast<float>(BpskCorrelationFunction(err + spacing));
|
||||
|
||||
double disc_out = dll_nc_e_minus_l_normalized(E, L, spacing);
|
||||
|
||||
if (std::abs(err) < 2.0 * spacing)
|
||||
{
|
||||
EXPECT_NEAR(disc_out, err, 1e-4) << " Spacing: " << spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(err * disc_out >= 0.0);
|
||||
}
|
||||
|
||||
if (spacing != 0.5 and err != 0.0)
|
||||
{
|
||||
double disc_out_old = dll_nc_e_minus_l_normalized(E, L);
|
||||
|
||||
EXPECT_NE(disc_out_old, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(DllNcEMinusLNormalizedTest, SinBoc11)
|
||||
{
|
||||
std::vector<gr_complex> complex_amplitude_vector = {{1.0, 0.0}, {-1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}};
|
||||
std::vector<double> spacing_vector = {0.75, 0.6666, 5.0 / 12.0, 0.25, 1.0 / 6.0, 0.01};
|
||||
std::vector<double> error_vector = {0.0, 0.01, 0.1, 0.25, -0.25, -0.1, -0.01};
|
||||
|
||||
for (auto A : complex_amplitude_vector)
|
||||
{
|
||||
for (auto spacing : spacing_vector)
|
||||
{
|
||||
double corr_slope = -CalculateSlopeAbs(&SinBocCorrelationFunction<1, 1>, spacing);
|
||||
double y_intercept = GetYInterceptAbs(&SinBocCorrelationFunction<1, 1>, spacing);
|
||||
|
||||
for (auto err : error_vector)
|
||||
{
|
||||
gr_complex E = A * static_cast<float>(SinBocCorrelationFunction<1, 1>(err - spacing));
|
||||
gr_complex L = A * static_cast<float>(SinBocCorrelationFunction<1, 1>(err + spacing));
|
||||
|
||||
double disc_out = dll_nc_e_minus_l_normalized(E, L, spacing, corr_slope, y_intercept);
|
||||
double corr_slope_at_err = -CalculateSlopeAbs(&SinBocCorrelationFunction<1, 1>, spacing + err);
|
||||
double corr_slope_at_neg_err = -CalculateSlopeAbs(&SinBocCorrelationFunction<1, 1>, spacing - err);
|
||||
|
||||
bool in_linear_region = (std::abs(err) < spacing) and (std::abs(corr_slope_at_err - corr_slope_at_neg_err) < 0.01);
|
||||
double norm_factor = (y_intercept - corr_slope * spacing) / spacing;
|
||||
|
||||
if (in_linear_region)
|
||||
{
|
||||
EXPECT_NEAR(disc_out, err, 1e-4) << " Spacing: " << spacing << ", slope : " << corr_slope << ", y_intercept: " << y_intercept << ", norm: " << norm_factor << " E: " << E << ", L: " << L;
|
||||
if (norm_factor != 0.5 and err != 0.0)
|
||||
{
|
||||
double disc_out_old = dll_nc_e_minus_l_normalized(E, L);
|
||||
EXPECT_NE(disc_out_old, err) << " Spacing: " << spacing << ", slope : " << corr_slope << ", y_intercept: " << y_intercept << ", norm: " << norm_factor << " E: " << E << ", L: " << L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(CosBocCorrelationFunction, FixedPoints)
|
||||
{
|
||||
double res = CosBocCorrelationFunction<1, 1>(0.0);
|
||||
EXPECT_NEAR(res, 1.0, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(0.2);
|
||||
EXPECT_NEAR(res, 0.0, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(0.25);
|
||||
EXPECT_NEAR(res, -0.25, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(0.5);
|
||||
EXPECT_NEAR(res, -0.5, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(0.75);
|
||||
EXPECT_NEAR(res, 0.25, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(1.0);
|
||||
EXPECT_NEAR(res, 0.0, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(-0.2);
|
||||
EXPECT_NEAR(res, 0.0, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(-0.5);
|
||||
EXPECT_NEAR(res, -0.5, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(-0.75);
|
||||
EXPECT_NEAR(res, 0.25, 1e-4);
|
||||
res = CosBocCorrelationFunction<1, 1>(-1.0);
|
||||
EXPECT_NEAR(res, 0.0, 1e-4);
|
||||
}
|
||||
|
||||
|
||||
TEST(DllNcEMinusLNormalizedTest, CosBoc11)
|
||||
{
|
||||
std::vector<gr_complex> complex_amplitude_vector = {{1.0, 0.0}, {-1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}};
|
||||
std::vector<double> spacing_vector = {0.875, 0.588, 0.1, 0.01};
|
||||
std::vector<double> error_vector = {0.0, 0.01, 0.1, 0.25, -0.25, -0.1, -0.01};
|
||||
|
||||
for (auto A : complex_amplitude_vector)
|
||||
{
|
||||
for (auto spacing : spacing_vector)
|
||||
{
|
||||
double corr_slope = -CalculateSlopeAbs(&CosBocCorrelationFunction<1, 1>, spacing);
|
||||
double y_intercept = GetYInterceptAbs(&CosBocCorrelationFunction<1, 1>, spacing);
|
||||
for (auto err : error_vector)
|
||||
{
|
||||
gr_complex E = A * static_cast<float>(CosBocCorrelationFunction<1, 1>(err - spacing));
|
||||
gr_complex L = A * static_cast<float>(CosBocCorrelationFunction<1, 1>(err + spacing));
|
||||
|
||||
double disc_out = dll_nc_e_minus_l_normalized(E, L, spacing, corr_slope, y_intercept);
|
||||
double corr_slope_at_err = -CalculateSlopeAbs(&CosBocCorrelationFunction<1, 1>, spacing + err);
|
||||
double corr_slope_at_neg_err = -CalculateSlopeAbs(&CosBocCorrelationFunction<1, 1>, spacing - err);
|
||||
|
||||
bool in_linear_region = (std::abs(err) < spacing) and (std::abs(corr_slope_at_err - corr_slope_at_neg_err) < 0.01);
|
||||
double norm_factor = (y_intercept - corr_slope * spacing) / spacing;
|
||||
|
||||
if (in_linear_region)
|
||||
{
|
||||
EXPECT_NEAR(disc_out, err, 1e-4) << " Spacing: " << spacing << ", slope : " << corr_slope << ", y_intercept: " << y_intercept << ", norm: " << norm_factor << " E: " << E << ", L: " << L;
|
||||
if (norm_factor != 0.5 and err != 0.0)
|
||||
{
|
||||
double disc_out_old = dll_nc_e_minus_l_normalized(E, L);
|
||||
EXPECT_NE(disc_out_old, err) << " Spacing: " << spacing << ", slope : " << corr_slope << ", y_intercept: " << y_intercept << ", norm: " << norm_factor << " E: " << E << ", L: " << L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*!
|
||||
* \file galileo_e1_dll_pll_veml_tracking_test.cc
|
||||
* \brief This class implements a tracking test for GalileoE1DllPllVemlTracking
|
||||
* class based on some input parameters.
|
||||
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "galileo_e1_dll_pll_veml_tracking.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <chrono>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
class GalileoE1DllPllVemlTrackingInternalTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE1DllPllVemlTrackingInternalTest()
|
||||
: item_size(sizeof(gr_complex))
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
}
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
int message{0};
|
||||
bool stop{false};
|
||||
};
|
||||
|
||||
|
||||
void GalileoE1DllPllVemlTrackingInternalTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "1B";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "8000000");
|
||||
config->set_property("Tracking_1B.implementation", "Galileo_E1_DLL_PLL_VEML_Tracking");
|
||||
config->set_property("Tracking_1B.item_type", "gr_complex");
|
||||
config->set_property("Tracking_1B.dump", "false");
|
||||
config->set_property("Tracking_1B.dump_filename", "../data/veml_tracking_ch_");
|
||||
config->set_property("Tracking_1B.early_late_space_chips", "0.15");
|
||||
config->set_property("Tracking_1B.very_early_late_space_chips", "0.6");
|
||||
config->set_property("Tracking_1B.pll_bw_hz", "30.0");
|
||||
config->set_property("Tracking_1B.dll_bw_hz", "2.0");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1DllPllVemlTrackingInternalTest, Instantiate)
|
||||
{
|
||||
init();
|
||||
auto tracking = factory->GetBlock(config.get(), "Tracking_1B", 1, 1);
|
||||
EXPECT_STREQ("Galileo_E1_DLL_PLL_VEML_Tracking", tracking->implementation().c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1DllPllVemlTrackingInternalTest, ConnectAndRun)
|
||||
{
|
||||
int fs_in = 8000000;
|
||||
int nsamples = 40000000;
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
|
||||
// Example using smart pointers and the block factory
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(config.get(), "Tracking_1B", 1, 1);
|
||||
std::shared_ptr<GalileoE1DllPllVemlTracking> tracking = std::dynamic_pointer_cast<GalileoE1DllPllVemlTracking>(trk_);
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
gr::analog::sig_source_c::sptr source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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::cout << "Processed " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE1DllPllVemlTrackingInternalTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
// int num_samples = 40000000; // 4 Msps
|
||||
// unsigned int skiphead_sps = 24000000; // 4 Msps
|
||||
int num_samples = 80000000; // 8 Msps
|
||||
unsigned int skiphead_sps = 8000000; // 8 Msps
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
|
||||
// Example using smart pointers and the block factory
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(config.get(), "Tracking_1B", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
|
||||
// gnss_synchro.Acq_delay_samples = 1753; // 4 Msps
|
||||
// gnss_synchro.Acq_doppler_hz = -9500; // 4 Msps
|
||||
gnss_synchro.Acq_delay_samples = 17256; // 8 Msps
|
||||
gnss_synchro.Acq_doppler_hz = -8750; // 8 Msps
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/GSoC_CTTC_capture_2012_07_26_4Msps_4ms.dat";
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
gr::blocks::skiphead::sptr skip_head = gr::blocks::skiphead::make(sizeof(gr_complex), skiphead_sps);
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), num_samples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, skip_head, 0);
|
||||
top_block->connect(skip_head, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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::cout << "Tracked " << num_samples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*!
|
||||
* \file galileo_e1_dll_pll_veml_tracking_test.cc
|
||||
* \brief This class implements a tracking test for Galileo_E5a_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Marc Sales, 2014. marcsales92(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "galileo_e5a_dll_pll_tracking.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <chrono>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
|
||||
class GalileoE5aTrackingTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE5aTrackingTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GalileoE5aTrackingTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE5aTrackingTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "5X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "32000000");
|
||||
config->set_property("Tracking_5X.implementation", "Galileo_E5a_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_5X.item_type", "gr_complex");
|
||||
config->set_property("Tracking_5X.dump", "false");
|
||||
config->set_property("Tracking_5X.dump_filename", "../data/e5a_tracking_ch_");
|
||||
config->set_property("Tracking_5X.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_5X.order", "2");
|
||||
config->set_property("Tracking_5X.pll_bw_hz", "20.0");
|
||||
config->set_property("Tracking_5X.dll_bw_hz", "5.0");
|
||||
config->set_property("Tracking_5X.pll_bw_narrow_hz", "2.0");
|
||||
config->set_property("Tracking_5X.pll_bw_narrow_hz", "2.0");
|
||||
config->set_property("Tracking_5X.ti_ms", "1");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5aTrackingTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int fs_in = 32000000;
|
||||
int nsamples = 32000000 * 5;
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
|
||||
// Example using smart pointers and the block factory
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(config.get(), "Tracking_5X", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
|
||||
// REAL
|
||||
gnss_synchro.Acq_delay_samples = 10; // 32 Msps
|
||||
// gnss_synchro.Acq_doppler_hz = 3500; // 32 Msps
|
||||
gnss_synchro.Acq_doppler_hz = 2000; // 500 Hz resolution
|
||||
// gnss_synchro.Acq_samplestamp_samples = 98000;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
gr::analog::sig_source_c::sptr source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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::cout << "Tracked " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*!
|
||||
* \file galileo_e5b_dll_pll_tracking_test.cc
|
||||
* \brief This class implements a tracking test for Galileo_E5b_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Piyush Gupta, 2020. piyush04111999@gmail.com
|
||||
* \note Code added as part of GSoC 2020 Program.
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "galileo_e5b_dll_pll_tracking.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <chrono>
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
|
||||
class GalileoE5bTrackingTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GalileoE5bTrackingTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
stop = false;
|
||||
message = 0;
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GalileoE5bTrackingTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
bool stop;
|
||||
int message;
|
||||
};
|
||||
|
||||
|
||||
void GalileoE5bTrackingTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'E';
|
||||
std::string signal = "7X";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "32000000");
|
||||
config->set_property("Tracking_7X.implementation", "Galileo_E5b_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_7X.item_type", "gr_complex");
|
||||
config->set_property("Tracking_7X.dump", "false");
|
||||
config->set_property("Tracking_7X.dump_filename", "../data/e5b_tracking");
|
||||
config->set_property("Tracking_7X.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_7X.order", "2");
|
||||
config->set_property("Tracking_7X.pll_bw_hz", "20.0");
|
||||
config->set_property("Tracking_7X.dll_bw_hz", "5.0");
|
||||
config->set_property("Tracking_7X.pll_bw_narrow_hz", "2.0");
|
||||
config->set_property("Tracking_7X.pll_bw_narrow_hz", "2.0");
|
||||
config->set_property("Tracking_7X.ti_ms", "1");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GalileoE5bTrackingTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int fs_in = 32000000;
|
||||
int nsamples = fs_in * 5;
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
|
||||
// Example using smart pointers and the block factory
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(config.get(), "Tracking_7X", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_);
|
||||
|
||||
// REAL
|
||||
gnss_synchro.Acq_delay_samples = 10; // 32 Msps
|
||||
gnss_synchro.Acq_doppler_hz = 2000; // 500 Hz resolution
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
gr::analog::sig_source_c::sptr source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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::cout << "Tracked " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*!
|
||||
* \file glonass_l1_ca_dll_pll_c_aid_tracking_test.cc
|
||||
* \brief This class implements a tracking test for GLONASS_L1_CA_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com
|
||||
* \author Luis Esteve, 2017. luis(at)epsilon-formacion.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "glonass_l1_ca_dll_pll_c_aid_tracking.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "tracking_interface.h"
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GlonassL1CaDllPllCAidTrackingTest_msg_rx;
|
||||
|
||||
using GlonassL1CaDllPllCAidTrackingTest_msg_rx_sptr = gnss_shared_ptr<GlonassL1CaDllPllCAidTrackingTest_msg_rx>;
|
||||
|
||||
GlonassL1CaDllPllCAidTrackingTest_msg_rx_sptr GlonassL1CaDllPllCAidTrackingTest_msg_rx_make();
|
||||
|
||||
class GlonassL1CaDllPllCAidTrackingTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GlonassL1CaDllPllCAidTrackingTest_msg_rx_sptr GlonassL1CaDllPllCAidTrackingTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GlonassL1CaDllPllCAidTrackingTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GlonassL1CaDllPllCAidTrackingTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
GlonassL1CaDllPllCAidTrackingTest_msg_rx_sptr GlonassL1CaDllPllCAidTrackingTest_msg_rx_make()
|
||||
{
|
||||
return GlonassL1CaDllPllCAidTrackingTest_msg_rx_sptr(new GlonassL1CaDllPllCAidTrackingTest_msg_rx());
|
||||
}
|
||||
|
||||
void GlonassL1CaDllPllCAidTrackingTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GlonassL1CaDllPllCAidTrackingTest_msg_rx::GlonassL1CaDllPllCAidTrackingTest_msg_rx() : gr::block("GlonassL1CaDllPllCAidTrackingTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GlonassL1CaDllPllCAidTrackingTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GlonassL1CaDllPllCAidTrackingTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GlonassL1CaDllPllCAidTrackingTest_msg_rx::~GlonassL1CaDllPllCAidTrackingTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
class GlonassL1CaDllPllCAidTrackingTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GlonassL1CaDllPllCAidTrackingTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GlonassL1CaDllPllCAidTrackingTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
void GlonassL1CaDllPllCAidTrackingTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "1G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "6625000");
|
||||
config->set_property("Tracking_1G.implementation", "GLONASS_L1_CA_DLL_PLL_C_Aid_Tracking");
|
||||
config->set_property("Tracking_1G.item_type", "gr_complex");
|
||||
config->set_property("Tracking_1G.dump", "false");
|
||||
config->set_property("Tracking_1G.dump_filename", "./tracking_ch_");
|
||||
config->set_property("Tracking_1G.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_1G.order", "2");
|
||||
config->set_property("Tracking_1G.pll_bw_hz", "2");
|
||||
config->set_property("Tracking_1G.dll_bw_hz", "0.5");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GlonassL1CaDllPllCAidTrackingTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int fs_in = 6625000;
|
||||
int nsamples = fs_in * 4e-3 * 2;
|
||||
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
std::shared_ptr<TrackingInterface> tracking = std::make_shared<GlonassL1CaDllPllCAidTracking>(config.get(), "Tracking_1G", 1, 1);
|
||||
auto msg_rx = GlonassL1CaDllPllCAidTrackingTest_msg_rx_make();
|
||||
|
||||
gnss_synchro.Acq_delay_samples = 1343;
|
||||
gnss_synchro.Acq_doppler_hz = -2750;
|
||||
// gnss_synchro.Acq_doppler_hz = -2750;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/NT1065_GLONASS_L1_20160831_fs6625e6_if0e3_4ms.bin";
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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.";
|
||||
|
||||
// TODO: Verify tracking results
|
||||
std::cout << "Tracked " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*!
|
||||
* \file glonass_l1_ca_dll_pll_tracking_test.cc
|
||||
* \brief This class implements a tracking test for GLONASS_L1_CA_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Gabriel Araujo, 2017. gabriel.araujo.5000(at)gmail.com
|
||||
* \author Luis Esteve, 2017. luis(at)epsilon-formacion.com
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "glonass_l1_ca_dll_pll_tracking.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "tracking_interface.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GlonassL1CaDllPllTrackingTest_msg_rx;
|
||||
|
||||
using GlonassL1CaDllPllTrackingTest_msg_rx_sptr = gnss_shared_ptr<GlonassL1CaDllPllTrackingTest_msg_rx>;
|
||||
|
||||
GlonassL1CaDllPllTrackingTest_msg_rx_sptr GlonassL1CaDllPllTrackingTest_msg_rx_make();
|
||||
|
||||
class GlonassL1CaDllPllTrackingTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GlonassL1CaDllPllTrackingTest_msg_rx_sptr GlonassL1CaDllPllTrackingTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GlonassL1CaDllPllTrackingTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GlonassL1CaDllPllTrackingTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
GlonassL1CaDllPllTrackingTest_msg_rx_sptr GlonassL1CaDllPllTrackingTest_msg_rx_make()
|
||||
{
|
||||
return GlonassL1CaDllPllTrackingTest_msg_rx_sptr(new GlonassL1CaDllPllTrackingTest_msg_rx());
|
||||
}
|
||||
|
||||
void GlonassL1CaDllPllTrackingTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GlonassL1CaDllPllTrackingTest_msg_rx::GlonassL1CaDllPllTrackingTest_msg_rx() : gr::block("GlonassL1CaDllPllTrackingTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GlonassL1CaDllPllTrackingTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GlonassL1CaDllPllTrackingTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
GlonassL1CaDllPllTrackingTest_msg_rx::~GlonassL1CaDllPllTrackingTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
|
||||
class GlonassL1CaDllPllTrackingTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GlonassL1CaDllPllTrackingTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GlonassL1CaDllPllTrackingTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
void GlonassL1CaDllPllTrackingTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'R';
|
||||
std::string signal = "1G";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 11;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "6625000");
|
||||
config->set_property("Tracking_1G.implementation", "GLONASS_L1_CA_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_1G.item_type", "gr_complex");
|
||||
config->set_property("Tracking_1G.dump", "false");
|
||||
config->set_property("Tracking_1G.dump_filename", "./tracking_ch_");
|
||||
config->set_property("Tracking_1G.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_1G.order", "2");
|
||||
config->set_property("Tracking_1G.pll_bw_hz", "2");
|
||||
config->set_property("Tracking_1G.dll_bw_hz", "0.5");
|
||||
}
|
||||
|
||||
TEST_F(GlonassL1CaDllPllTrackingTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
int fs_in = 6625000;
|
||||
int nsamples = fs_in * 4e-3 * 2;
|
||||
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
std::shared_ptr<TrackingInterface> tracking = std::make_shared<GlonassL1CaDllPllTracking>(config.get(), "Tracking_1G", 1, 1);
|
||||
auto msg_rx = GlonassL1CaDllPllTrackingTest_msg_rx_make();
|
||||
|
||||
gnss_synchro.Acq_delay_samples = 1343;
|
||||
gnss_synchro.Acq_doppler_hz = -2750;
|
||||
// gnss_synchro.Acq_doppler_hz = -2750;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
gr::analog::sig_source_c::sptr sin_source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/NT1065_GLONASS_L1_20160831_fs6625e6_if0e3_4ms.bin";
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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.";
|
||||
|
||||
// TODO: Verify tracking results
|
||||
std::cout << "Tracked " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,662 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_dll_pll_tracking_test_fpga.cc
|
||||
* \brief This class implements a tracking test for Galileo_E5a_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Marc Majoral, 2017. mmajoral(at)cttc.cat
|
||||
* \author Javier Arribas, 2017. jarribas(at)cttc.es
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "GPS_L1_CA.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l1_ca_dll_pll_tracking_fpga.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "interleaved_byte_to_complex_short.h"
|
||||
#include "signal_generator_flags.h"
|
||||
#include "tracking_dump_reader.h"
|
||||
#include "tracking_interface.h"
|
||||
#include "tracking_true_obs_reader.h"
|
||||
#include <armadillo>
|
||||
#include <boost/thread.hpp> // to test the FPGA we have to create a simultaneous task to send the samples using the DMA and stop the test
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/interleaved_char_to_complex.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <cstdio> // FPGA read input file
|
||||
#include <fcntl.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/log/log.h>
|
||||
#endif
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
#define DMA_TRACK_TRANSFER_SIZE 2046 // DMA transfer size for tracking
|
||||
#define MIN_SAMPLES_REMAINING 20000 // number of remaining samples in the DMA that causes the CPU to stop the flowgraph (it has to be a bit alrger than 2x max packet size)
|
||||
#define FIVE_SECONDS 5000000 // five seconds in microseconds
|
||||
|
||||
void send_tracking_gps_input_samples(FILE *rx_signal_file,
|
||||
int num_remaining_samples, const gr::top_block_sptr &top_block)
|
||||
{
|
||||
int num_samples_transferred = 0; // number of samples that have been transferred to the DMA so far
|
||||
static int flowgraph_stopped = 0; // flag to indicate if the flowgraph is stopped already
|
||||
char *buffer_DMA; // temporary buffer to store the samples to be sent to the DMA
|
||||
int dma_descr; // DMA descriptor
|
||||
dma_descr = open("/dev/loop_tx", O_WRONLY);
|
||||
if (dma_descr < 0)
|
||||
{
|
||||
std::cerr << "Can't open loop device\n";
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_DMA = reinterpret_cast<char *>(malloc(DMA_TRACK_TRANSFER_SIZE));
|
||||
if (!buffer_DMA)
|
||||
{
|
||||
std::cerr << "Memory error!\n";
|
||||
close(dma_descr);
|
||||
return;
|
||||
}
|
||||
|
||||
while (num_remaining_samples > 0)
|
||||
{
|
||||
if (num_remaining_samples < MIN_SAMPLES_REMAINING)
|
||||
{
|
||||
if (flowgraph_stopped == 0)
|
||||
{
|
||||
// stop top module
|
||||
top_block->stop();
|
||||
flowgraph_stopped = 1;
|
||||
}
|
||||
}
|
||||
if (num_remaining_samples > DMA_TRACK_TRANSFER_SIZE)
|
||||
{
|
||||
size_t result = fread(buffer_DMA, DMA_TRACK_TRANSFER_SIZE, 1, rx_signal_file);
|
||||
if (result != DMA_TRACK_TRANSFER_SIZE)
|
||||
{
|
||||
std::cerr << "Error reading from DMA\n";
|
||||
}
|
||||
|
||||
assert(DMA_TRACK_TRANSFER_SIZE == write(dma_descr, &buffer_DMA[0], DMA_TRACK_TRANSFER_SIZE));
|
||||
num_remaining_samples = num_remaining_samples - DMA_TRACK_TRANSFER_SIZE;
|
||||
num_samples_transferred = num_samples_transferred + DMA_TRACK_TRANSFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t result = fread(buffer_DMA, num_remaining_samples, 1, rx_signal_file);
|
||||
if (static_cast<int>(result) != num_remaining_samples)
|
||||
{
|
||||
std::cerr << "Error reading from DMA\n";
|
||||
}
|
||||
assert(num_remaining_samples == write(dma_descr, &buffer_DMA[0], num_remaining_samples));
|
||||
num_samples_transferred = num_samples_transferred + num_remaining_samples;
|
||||
num_remaining_samples = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer_DMA);
|
||||
close(dma_descr);
|
||||
}
|
||||
|
||||
|
||||
// thread that sends the samples to the FPGA
|
||||
void sending_thread(const gr::top_block_sptr &top_block, const char *file_name)
|
||||
{
|
||||
// file descriptor
|
||||
FILE *rx_signal_file; // file descriptor
|
||||
int file_length; // length of the file containing the received samples
|
||||
|
||||
rx_signal_file = fopen(file_name, "rb");
|
||||
if (!rx_signal_file)
|
||||
{
|
||||
std::cerr << "Unable to open file!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(rx_signal_file, 0, SEEK_END);
|
||||
file_length = ftell(rx_signal_file);
|
||||
fseek(rx_signal_file, 0, SEEK_SET);
|
||||
|
||||
usleep(FIVE_SECONDS); // wait for some time to give time to the other thread to program the device
|
||||
|
||||
// send_tracking_gps_input_samples(dma_descr, rx_signal_file, file_length);
|
||||
send_tracking_gps_input_samples(rx_signal_file, file_length, top_block);
|
||||
|
||||
fclose(rx_signal_file);
|
||||
}
|
||||
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CADllPllTrackingTestFpga_msg_rx;
|
||||
|
||||
using GpsL1CADllPllTrackingTestFpga_msg_rx_sptr = gnss_shared_ptr<GpsL1CADllPllTrackingTestFpga_msg_rx>;
|
||||
|
||||
GpsL1CADllPllTrackingTestFpga_msg_rx_sptr GpsL1CADllPllTrackingTestFpga_msg_rx_make();
|
||||
|
||||
|
||||
class GpsL1CADllPllTrackingTestFpga_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CADllPllTrackingTestFpga_msg_rx_sptr GpsL1CADllPllTrackingTestFpga_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t &msg);
|
||||
GpsL1CADllPllTrackingTestFpga_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message{0};
|
||||
~GpsL1CADllPllTrackingTestFpga_msg_rx() override; //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CADllPllTrackingTestFpga_msg_rx_sptr GpsL1CADllPllTrackingTestFpga_msg_rx_make()
|
||||
{
|
||||
return GpsL1CADllPllTrackingTestFpga_msg_rx_sptr(
|
||||
new GpsL1CADllPllTrackingTestFpga_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CADllPllTrackingTestFpga_msg_rx::msg_handler_channel_events(const pmt::pmt_t &msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(msg);
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast &e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CADllPllTrackingTestFpga_msg_rx::GpsL1CADllPllTrackingTestFpga_msg_rx()
|
||||
: gr::block("GpsL1CADllPllTrackingTestFpga_msg_rx",
|
||||
gr::io_signature::make(0, 0, 0),
|
||||
gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto &&PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CADllPllTrackingTestFpga_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CADllPllTrackingTestFpga_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
GpsL1CADllPllTrackingTestFpga_msg_rx::~GpsL1CADllPllTrackingTestFpga_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CADllPllTrackingTestFpga : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
std::string generator_binary;
|
||||
std::string p1;
|
||||
std::string p2;
|
||||
std::string p3;
|
||||
std::string p4;
|
||||
std::string p5;
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const int baseband_sampling_freq = FLAGS_fs_gen_sps;
|
||||
std::string filename_rinex_obs = FLAGS_filename_rinex_obs;
|
||||
std::string filename_raw_data = FLAGS_filename_raw_data;
|
||||
#else
|
||||
const int baseband_sampling_freq = absl::GetFlag(FLAGS_fs_gen_sps);
|
||||
std::string filename_rinex_obs = absl::GetFlag(FLAGS_filename_rinex_obs);
|
||||
std::string filename_raw_data = absl::GetFlag(FLAGS_filename_raw_data);
|
||||
#endif
|
||||
int configure_generator();
|
||||
int generate_signal();
|
||||
void check_results_doppler(arma::vec &true_time_s, arma::vec &true_value,
|
||||
arma::vec &meas_time_s, arma::vec &meas_value);
|
||||
void check_results_acc_carrier_phase(arma::vec &true_time_s,
|
||||
arma::vec &true_value, arma::vec &meas_time_s, arma::vec &meas_value);
|
||||
void check_results_codephase(arma::vec &true_time_s, arma::vec &true_value,
|
||||
arma::vec &meas_time_s, arma::vec &meas_value);
|
||||
|
||||
GpsL1CADllPllTrackingTestFpga() : item_size(sizeof(gr_complex))
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL1CADllPllTrackingTestFpga() override = default;
|
||||
|
||||
void configure_receiver();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
int GpsL1CADllPllTrackingTestFpga::configure_generator()
|
||||
{
|
||||
// Configure signal generator
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
generator_binary = FLAGS_generator_binary;
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + FLAGS_rinex_nav_file;
|
||||
if (FLAGS_dynamic_position.empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + FLAGS_static_position + std::string(",") + std::to_string(FLAGS_duration * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(FLAGS_dynamic_position);
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + FLAGS_filename_rinex_obs; // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + FLAGS_filename_raw_data; // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#else
|
||||
generator_binary = absl::GetFlag(FLAGS_generator_binary);
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + absl::GetFlag(FLAGS_rinex_nav_file);
|
||||
if (absl::GetFlag(FLAGS_dynamic_position).empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + absl::GetFlag(FLAGS_static_position) + std::string(",") + std::to_string(absl::GetFlag(FLAGS_duration) * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(absl::GetFlag(FLAGS_dynamic_position));
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + absl::GetFlag(FLAGS_filename_rinex_obs); // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + absl::GetFlag(FLAGS_filename_raw_data); // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GpsL1CADllPllTrackingTestFpga::generate_signal()
|
||||
{
|
||||
int child_status;
|
||||
|
||||
char *const parmList[] = {&generator_binary[0], &generator_binary[0], &p1[0], &p2[0], &p3[0],
|
||||
&p4[0], &p5[0], nullptr};
|
||||
|
||||
int pid;
|
||||
if ((pid = fork()) == -1)
|
||||
{
|
||||
perror("fork err");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
execv(&generator_binary[0], parmList);
|
||||
std::cout << "Return not expected. Must be an execv err.\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
waitpid(pid, &child_status, 0);
|
||||
|
||||
std::cout << "Signal and Observables RINEX and RAW files created.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CADllPllTrackingTestFpga::configure_receiver()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
gnss_synchro.PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
gnss_synchro.PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps",
|
||||
std::to_string(baseband_sampling_freq));
|
||||
// Set Tracking
|
||||
config->set_property("Tracking_1C.implementation",
|
||||
"GPS_L1_CA_DLL_PLL_Tracking_FPGA");
|
||||
config->set_property("Tracking_1C.item_type", "cshort");
|
||||
config->set_property("Tracking_1C.dump", "true");
|
||||
config->set_property("Tracking_1C.dump_filename", "./tracking_ch_");
|
||||
config->set_property("Tracking_1C.pll_bw_hz", "30.0");
|
||||
config->set_property("Tracking_1C.dll_bw_hz", "2.0");
|
||||
config->set_property("Tracking_1C.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_1C.devicename", "/dev/uio");
|
||||
config->set_property("Tracking_1C.device_base", "1");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CADllPllTrackingTestFpga::check_results_doppler(arma::vec &true_time_s,
|
||||
arma::vec &true_value, arma::vec &meas_time_s, arma::vec &meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK Doppler RMSE=" << rmse
|
||||
<< ", mean=" << error_mean << ", stdev=" << sqrt(error_var)
|
||||
<< " (max,min)=" << max_error << "," << min_error << " [Hz]"
|
||||
<< '\n';
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CADllPllTrackingTestFpga::check_results_acc_carrier_phase(
|
||||
arma::vec &true_time_s, arma::vec &true_value, arma::vec &meas_time_s,
|
||||
arma::vec &meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK acc carrier phase RMSE=" << rmse
|
||||
<< ", mean=" << error_mean << ", stdev=" << sqrt(error_var)
|
||||
<< " (max,min)=" << max_error << "," << min_error << " [Hz]"
|
||||
<< '\n';
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CADllPllTrackingTestFpga::check_results_codephase(
|
||||
arma::vec &true_time_s, arma::vec &true_value, arma::vec &meas_time_s,
|
||||
arma::vec &meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK code phase RMSE=" << rmse
|
||||
<< ", mean=" << error_mean << ", stdev=" << sqrt(error_var)
|
||||
<< " (max,min)=" << max_error << "," << min_error << " [Chips]"
|
||||
<< '\n';
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CADllPllTrackingTestFpga, ValidationOfResultsFpga)
|
||||
{
|
||||
configure_generator();
|
||||
|
||||
// DO not generate signal raw signal samples and observations RINEX file by default
|
||||
// generate_signal();
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
std::chrono::time_point<std::chrono::system_clock> end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
|
||||
configure_receiver();
|
||||
|
||||
// open true observables log file written by the simulator
|
||||
Tracking_True_Obs_Reader true_obs_data;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int test_satellite_PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
int test_satellite_PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
std::cout << "Testing satellite PRN=" << test_satellite_PRN << '\n';
|
||||
std::string true_obs_file = std::string("./gps_l1_ca_obs_prn");
|
||||
true_obs_file.append(std::to_string(test_satellite_PRN));
|
||||
true_obs_file.append(".dat");
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
if (true_obs_data.open_obs_file(true_obs_file) == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
})
|
||||
<< "Failure opening true observables file";
|
||||
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
// std::shared_ptr<GpsL1CaDllPllCAidTrackingFpga> tracking = std::make_shared<GpsL1CaDllPllCAidTrackingFpga> (config.get(), "Tracking_1C", 1, 1);
|
||||
std::shared_ptr<GpsL1CaDllPllTrackingFpga> tracking = std::make_shared<GpsL1CaDllPllTrackingFpga>(config.get(), "Tracking_1C", 1, 1);
|
||||
|
||||
auto msg_rx = GpsL1CADllPllTrackingTestFpga_msg_rx_make();
|
||||
|
||||
// load acquisition data based on the first epoch of the true observations
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
if (true_obs_data.read_binary_obs() == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
})
|
||||
<< "Failure reading true observables file";
|
||||
|
||||
// restart the epoch counter
|
||||
true_obs_data.restart();
|
||||
|
||||
std::cout << "Initial Doppler [Hz]=" << true_obs_data.doppler_l1_hz
|
||||
<< " Initial code delay [Chips]=" << true_obs_data.prn_delay_chips
|
||||
<< '\n';
|
||||
|
||||
gnss_synchro.Acq_delay_samples = (GPS_L1_CA_CODE_LENGTH_CHIPS - true_obs_data.prn_delay_chips / GPS_L1_CA_CODE_LENGTH_CHIPS) * baseband_sampling_freq * GPS_L1_CA_CODE_PERIOD_S;
|
||||
gnss_synchro.Acq_doppler_hz = true_obs_data.doppler_l1_hz;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
})
|
||||
<< "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
})
|
||||
<< "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
tracking->connect(top_block);
|
||||
})
|
||||
<< "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
})
|
||||
<< "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
// assemble again the file name in a null terminated string (not available by default in the main program flow)
|
||||
std::string file = "./" + filename_raw_data;
|
||||
const char *file_name = file.c_str();
|
||||
|
||||
// start thread that sends the DMA samples to the FPGA
|
||||
boost::thread t{sending_thread, top_block, file_name};
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
{
|
||||
start = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
// tracking->reset(); // unlock the channel
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
})
|
||||
<< "Failure running the top_block.";
|
||||
|
||||
// wait until child thread terminates
|
||||
t.join();
|
||||
|
||||
// check results
|
||||
// load the true values
|
||||
int64_t nepoch = true_obs_data.num_epochs();
|
||||
std::cout << "True observation epochs=" << nepoch << '\n';
|
||||
|
||||
arma::vec true_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec true_acc_carrier_phase_cycles = arma::zeros(nepoch, 1);
|
||||
arma::vec true_Doppler_Hz = arma::zeros(nepoch, 1);
|
||||
arma::vec true_prn_delay_chips = arma::zeros(nepoch, 1);
|
||||
arma::vec true_tow_s = arma::zeros(nepoch, 1);
|
||||
|
||||
int64_t epoch_counter = 0;
|
||||
while (true_obs_data.read_binary_obs())
|
||||
{
|
||||
true_timestamp_s(epoch_counter) = true_obs_data.signal_timestamp_s;
|
||||
true_acc_carrier_phase_cycles(epoch_counter) = true_obs_data.acc_carrier_phase_cycles;
|
||||
true_Doppler_Hz(epoch_counter) = true_obs_data.doppler_l1_hz;
|
||||
true_prn_delay_chips(epoch_counter) = true_obs_data.prn_delay_chips;
|
||||
true_tow_s(epoch_counter) = true_obs_data.tow;
|
||||
epoch_counter++;
|
||||
}
|
||||
|
||||
// load the measured values
|
||||
Tracking_Dump_Reader trk_dump;
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
if (trk_dump.open_obs_file(std::string("./tracking_ch_0.dat")) == false)
|
||||
{
|
||||
throw std::exception();
|
||||
};
|
||||
})
|
||||
<< "Failure opening tracking dump file";
|
||||
|
||||
nepoch = trk_dump.num_epochs();
|
||||
std::cout << "Measured observation epochs=" << nepoch << '\n';
|
||||
|
||||
arma::vec trk_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_acc_carrier_phase_cycles = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_Doppler_Hz = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_prn_delay_chips = arma::zeros(nepoch, 1);
|
||||
|
||||
epoch_counter = 0;
|
||||
while (trk_dump.read_binary_obs())
|
||||
{
|
||||
trk_timestamp_s(epoch_counter) = static_cast<double>(trk_dump.PRN_start_sample_count) / static_cast<double>(baseband_sampling_freq);
|
||||
trk_acc_carrier_phase_cycles(epoch_counter) = trk_dump.acc_carrier_phase_rad / TWO_PI;
|
||||
trk_Doppler_Hz(epoch_counter) = trk_dump.carrier_doppler_hz;
|
||||
|
||||
double delay_chips = GPS_L1_CA_CODE_LENGTH_CHIPS - GPS_L1_CA_CODE_LENGTH_CHIPS * (fmod((static_cast<double>(trk_dump.PRN_start_sample_count) + trk_dump.aux1) / static_cast<double>(baseband_sampling_freq), 1.0e-3) / 1.0e-3);
|
||||
|
||||
trk_prn_delay_chips(epoch_counter) = delay_chips;
|
||||
epoch_counter++;
|
||||
}
|
||||
|
||||
// Align initial measurements and cut the tracking pull-in transitory
|
||||
double pull_in_offset_s = 1.0;
|
||||
arma::uvec initial_meas_point = arma::find(trk_timestamp_s >= (true_timestamp_s(0) + pull_in_offset_s), 1, "first");
|
||||
|
||||
trk_timestamp_s = trk_timestamp_s.subvec(initial_meas_point(0), trk_timestamp_s.size() - 1);
|
||||
trk_acc_carrier_phase_cycles = trk_acc_carrier_phase_cycles.subvec(initial_meas_point(0), trk_acc_carrier_phase_cycles.size() - 1);
|
||||
trk_Doppler_Hz = trk_Doppler_Hz.subvec(initial_meas_point(0), trk_Doppler_Hz.size() - 1);
|
||||
trk_prn_delay_chips = trk_prn_delay_chips.subvec(initial_meas_point(0), trk_prn_delay_chips.size() - 1);
|
||||
|
||||
check_results_doppler(true_timestamp_s, true_Doppler_Hz, trk_timestamp_s, trk_Doppler_Hz);
|
||||
check_results_codephase(true_timestamp_s, true_prn_delay_chips, trk_timestamp_s, trk_prn_delay_chips);
|
||||
check_results_acc_carrier_phase(true_timestamp_s,
|
||||
true_acc_carrier_phase_cycles, trk_timestamp_s,
|
||||
trk_acc_carrier_phase_cycles);
|
||||
|
||||
std::cout << "Signal tracking completed in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
/*!
|
||||
* \file gps_l1_ca_gaussian_tracking_test.cc
|
||||
* \brief This class implements a tracking test for GPS_L1_CA_Gaussian_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Carles Fernandez, 2018
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "GPS_L1_CA.h"
|
||||
#include "gnss_block_factory.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_flags.h"
|
||||
#include "gnuplot_i.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "signal_generator_flags.h"
|
||||
#include "test_flags.h"
|
||||
#include "tracking_dump_reader.h"
|
||||
#include "tracking_interface.h"
|
||||
#include "tracking_true_obs_reader.h"
|
||||
#include <armadillo>
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/interleaved_char_to_complex.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_bool(plot_gps_l1_gaussian_tracking_test, false, "Plots results of GpsL1CAGaussianTrackingTest with gnuplot");
|
||||
#else
|
||||
ABSL_FLAG(bool, plot_gps_l1_gaussian_tracking_test, false, "Plots results of GpsL1CAGaussianTrackingTest with gnuplot");
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL1CAGaussianTrackingTest_msg_rx;
|
||||
|
||||
|
||||
using GpsL1CAGaussianTrackingTest_msg_rx_sptr = gnss_shared_ptr<GpsL1CAGaussianTrackingTest_msg_rx>;
|
||||
|
||||
GpsL1CAGaussianTrackingTest_msg_rx_sptr GpsL1CAGaussianTrackingTest_msg_rx_make();
|
||||
|
||||
class GpsL1CAGaussianTrackingTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL1CAGaussianTrackingTest_msg_rx_sptr GpsL1CAGaussianTrackingTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GpsL1CAGaussianTrackingTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL1CAGaussianTrackingTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL1CAGaussianTrackingTest_msg_rx_sptr GpsL1CAGaussianTrackingTest_msg_rx_make()
|
||||
{
|
||||
return GpsL1CAGaussianTrackingTest_msg_rx_sptr(new GpsL1CAGaussianTrackingTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CAGaussianTrackingTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
long int message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL1CAGaussianTrackingTest_msg_rx::GpsL1CAGaussianTrackingTest_msg_rx() : gr::block("GpsL1CAGaussianTrackingTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL1CAGaussianTrackingTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL1CAGaussianTrackingTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GpsL1CAGaussianTrackingTest_msg_rx::~GpsL1CAGaussianTrackingTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL1CAGaussianTrackingTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
std::string generator_binary;
|
||||
std::string p1;
|
||||
std::string p2;
|
||||
std::string p3;
|
||||
std::string p4;
|
||||
std::string p5;
|
||||
|
||||
std::string implementation = "GPS_L1_CA_Gaussian_Tracking";
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
const int baseband_sampling_freq = FLAGS_fs_gen_sps;
|
||||
std::string filename_rinex_obs = FLAGS_filename_rinex_obs;
|
||||
std::string filename_raw_data = FLAGS_filename_raw_data;
|
||||
#else
|
||||
const int baseband_sampling_freq = absl::GetFlag(FLAGS_fs_gen_sps);
|
||||
std::string filename_rinex_obs = absl::GetFlag(FLAGS_filename_rinex_obs);
|
||||
std::string filename_raw_data = absl::GetFlag(FLAGS_filename_raw_data);
|
||||
#endif
|
||||
|
||||
int configure_generator();
|
||||
int generate_signal();
|
||||
void check_results_doppler(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value);
|
||||
void check_results_acc_carrier_phase(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value);
|
||||
void check_results_codephase(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value);
|
||||
|
||||
GpsL1CAGaussianTrackingTest()
|
||||
{
|
||||
factory = std::make_shared<GNSSBlockFactory>();
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL1CAGaussianTrackingTest() = default;
|
||||
|
||||
void configure_receiver();
|
||||
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<GNSSBlockFactory> factory;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
int GpsL1CAGaussianTrackingTest::configure_generator()
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
// Configure signal generator
|
||||
generator_binary = FLAGS_generator_binary;
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + FLAGS_rinex_nav_file;
|
||||
if (FLAGS_dynamic_position.empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + FLAGS_static_position + std::string(",") + std::to_string(FLAGS_duration * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(FLAGS_dynamic_position);
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + FLAGS_filename_rinex_obs; // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + FLAGS_filename_raw_data; // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#else
|
||||
// Configure signal generator
|
||||
generator_binary = absl::GetFlag(FLAGS_generator_binary);
|
||||
|
||||
p1 = std::string("-rinex_nav_file=") + absl::GetFlag(FLAGS_rinex_nav_file);
|
||||
if (absl::GetFlag(FLAGS_dynamic_position).empty())
|
||||
{
|
||||
p2 = std::string("-static_position=") + absl::GetFlag(FLAGS_static_position) + std::string(",") + std::to_string(absl::GetFlag(FLAGS_duration) * 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = std::string("-obs_pos_file=") + std::string(absl::GetFlag(FLAGS_dynamic_position));
|
||||
}
|
||||
p3 = std::string("-rinex_obs_file=") + absl::GetFlag(FLAGS_filename_rinex_obs); // RINEX 2.10 observation file output
|
||||
p4 = std::string("-sig_out_file=") + absl::GetFlag(FLAGS_filename_raw_data); // Baseband signal output file. Will be stored in int8_t IQ multiplexed samples
|
||||
p5 = std::string("-sampling_freq=") + std::to_string(baseband_sampling_freq); // Baseband sampling frequency [MSps]
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GpsL1CAGaussianTrackingTest::generate_signal()
|
||||
{
|
||||
int child_status;
|
||||
|
||||
char* const parmList[] = {&generator_binary[0], &generator_binary[0], &p1[0], &p2[0], &p3[0], &p4[0], &p5[0], nullptr};
|
||||
|
||||
int pid;
|
||||
if ((pid = fork()) == -1)
|
||||
{
|
||||
perror("fork err");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
execv(&generator_binary[0], parmList);
|
||||
std::cout << "Return not expected. Must be an execv err.\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
waitpid(pid, &child_status, 0);
|
||||
|
||||
std::cout << "Signal and Observables RINEX and RAW files created.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CAGaussianTrackingTest::configure_receiver()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "1C";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
gnss_synchro.PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
gnss_synchro.PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", std::to_string(baseband_sampling_freq));
|
||||
// Set Tracking
|
||||
config->set_property("Tracking_1C.implementation", implementation);
|
||||
config->set_property("Tracking_1C.item_type", "gr_complex");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_dll_bw_hz != 0.0)
|
||||
{
|
||||
config->set_property("Tracking_1C.dll_bw_hz", std::to_string(FLAGS_dll_bw_hz));
|
||||
}
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_dll_bw_hz) != 0.0)
|
||||
{
|
||||
config->set_property("Tracking_1C.dll_bw_hz", std::to_string(absl::GetFlag(FLAGS_dll_bw_hz)));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
config->set_property("Tracking_1C.dll_bw_hz", "2.0");
|
||||
}
|
||||
config->set_property("Tracking_1C.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_1C.extend_correlation_ms", "1");
|
||||
config->set_property("Tracking_1C.dump", "true");
|
||||
config->set_property("Tracking_1C.dump_filename", "./tracking_ch_");
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CAGaussianTrackingTest::check_results_doppler(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK Doppler RMSE=" << rmse
|
||||
<< ", mean=" << error_mean
|
||||
<< ", stdev=" << sqrt(error_var) << " (max,min)=" << max_error << "," << min_error << " [Hz]\n";
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CAGaussianTrackingTest::check_results_acc_carrier_phase(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK acc carrier phase RMSE=" << rmse
|
||||
<< ", mean=" << error_mean
|
||||
<< ", stdev=" << sqrt(error_var) << " (max,min)=" << max_error << "," << min_error << " [Hz]\n";
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
void GpsL1CAGaussianTrackingTest::check_results_codephase(arma::vec& true_time_s,
|
||||
arma::vec& true_value,
|
||||
arma::vec& meas_time_s,
|
||||
arma::vec& meas_value)
|
||||
{
|
||||
// 1. True value interpolation to match the measurement times
|
||||
arma::vec true_value_interp;
|
||||
arma::uvec true_time_s_valid = find(true_time_s > 0);
|
||||
true_time_s = true_time_s(true_time_s_valid);
|
||||
true_value = true_value(true_time_s_valid);
|
||||
arma::uvec meas_time_s_valid = find(meas_time_s > 0);
|
||||
meas_time_s = meas_time_s(meas_time_s_valid);
|
||||
meas_value = meas_value(meas_time_s_valid);
|
||||
|
||||
arma::interp1(true_time_s, true_value, meas_time_s, true_value_interp);
|
||||
|
||||
// 2. RMSE
|
||||
arma::vec err;
|
||||
|
||||
err = meas_value - true_value_interp;
|
||||
arma::vec err2 = arma::square(err);
|
||||
double rmse = sqrt(arma::mean(err2));
|
||||
|
||||
// 3. Mean err and variance
|
||||
double error_mean = arma::mean(err);
|
||||
double error_var = arma::var(err);
|
||||
|
||||
// 4. Peaks
|
||||
double max_error = arma::max(err);
|
||||
double min_error = arma::min(err);
|
||||
|
||||
// 5. report
|
||||
std::streamsize ss = std::cout.precision();
|
||||
std::cout << std::setprecision(10) << "TRK code phase RMSE=" << rmse
|
||||
<< ", mean=" << error_mean
|
||||
<< ", stdev=" << sqrt(error_var) << " (max,min)=" << max_error << "," << min_error << " [Chips]\n";
|
||||
std::cout.precision(ss);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL1CAGaussianTrackingTest, ValidationOfResults)
|
||||
{
|
||||
// Configure the signal generator
|
||||
configure_generator();
|
||||
|
||||
// Generate signal raw signal samples and observations RINEX file
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_disable_generator == false)
|
||||
{
|
||||
generate_signal();
|
||||
}
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_disable_generator) == false)
|
||||
{
|
||||
generate_signal();
|
||||
}
|
||||
#endif
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
|
||||
configure_receiver();
|
||||
|
||||
// open true observables log file written by the simulator
|
||||
Tracking_True_Obs_Reader true_obs_data;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int test_satellite_PRN = FLAGS_test_satellite_PRN;
|
||||
#else
|
||||
int test_satellite_PRN = absl::GetFlag(FLAGS_test_satellite_PRN);
|
||||
#endif
|
||||
std::cout << "Testing satellite PRN=" << test_satellite_PRN << '\n';
|
||||
std::string true_obs_file = std::string("./gps_l1_ca_obs_prn");
|
||||
true_obs_file.append(std::to_string(test_satellite_PRN));
|
||||
true_obs_file.append(".dat");
|
||||
ASSERT_EQ(true_obs_data.open_obs_file(true_obs_file), true) << "Failure opening true observables file";
|
||||
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
|
||||
std::shared_ptr<GNSSBlockInterface> trk_ = factory->GetBlock(config.get(), "Tracking_1C", 1, 1);
|
||||
std::shared_ptr<TrackingInterface> tracking = std::dynamic_pointer_cast<TrackingInterface>(trk_); // std::make_shared<GpsL1CaDllPllCAidTracking>(config.get(), "Tracking_1C", 1, 1);
|
||||
|
||||
auto msg_rx = GpsL1CAGaussianTrackingTest_msg_rx_make();
|
||||
|
||||
// load acquisition data based on the first epoch of the true observations
|
||||
ASSERT_EQ(true_obs_data.read_binary_obs(), true)
|
||||
<< "Failure reading true tracking dump file.\n"
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
<< "Maybe sat PRN #" + std::to_string(FLAGS_test_satellite_PRN) +
|
||||
#else
|
||||
<< "Maybe sat PRN #" + std::to_string(absl::GetFlag(FLAGS_test_satellite_PRN)) +
|
||||
#endif
|
||||
" is not available?";
|
||||
|
||||
// restart the epoch counter
|
||||
true_obs_data.restart();
|
||||
|
||||
std::cout << "Initial Doppler [Hz]=" << true_obs_data.doppler_l1_hz << " Initial code delay [Chips]=" << true_obs_data.prn_delay_chips << '\n';
|
||||
gnss_synchro.Acq_delay_samples = (GPS_L1_CA_CODE_LENGTH_CHIPS - true_obs_data.prn_delay_chips / GPS_L1_CA_CODE_LENGTH_CHIPS) * baseband_sampling_freq * GPS_L1_CA_CODE_PERIOD_S;
|
||||
gnss_synchro.Acq_doppler_hz = true_obs_data.doppler_l1_hz;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
std::string file = "./" + filename_raw_data;
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(int8_t), file_name, false);
|
||||
gr::blocks::interleaved_char_to_complex::sptr gr_interleaved_char_to_complex = gr::blocks::interleaved_char_to_complex::make();
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, gr_interleaved_char_to_complex, 0);
|
||||
top_block->connect(gr_interleaved_char_to_complex, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
start = std::chrono::system_clock::now();
|
||||
top_block->run(); // Start threads and wait
|
||||
end = std::chrono::system_clock::now();
|
||||
}) << "Failure running the top_block.";
|
||||
|
||||
// check results
|
||||
// load the true values
|
||||
long int nepoch = true_obs_data.num_epochs();
|
||||
std::cout << "True observation epochs=" << nepoch << '\n';
|
||||
|
||||
arma::vec true_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec true_acc_carrier_phase_cycles = arma::zeros(nepoch, 1);
|
||||
arma::vec true_Doppler_Hz = arma::zeros(nepoch, 1);
|
||||
arma::vec true_prn_delay_chips = arma::zeros(nepoch, 1);
|
||||
arma::vec true_tow_s = arma::zeros(nepoch, 1);
|
||||
|
||||
long int epoch_counter = 0;
|
||||
while (true_obs_data.read_binary_obs())
|
||||
{
|
||||
true_timestamp_s(epoch_counter) = true_obs_data.signal_timestamp_s;
|
||||
true_acc_carrier_phase_cycles(epoch_counter) = true_obs_data.acc_carrier_phase_cycles;
|
||||
true_Doppler_Hz(epoch_counter) = true_obs_data.doppler_l1_hz;
|
||||
true_prn_delay_chips(epoch_counter) = true_obs_data.prn_delay_chips;
|
||||
true_tow_s(epoch_counter) = true_obs_data.tow;
|
||||
epoch_counter++;
|
||||
}
|
||||
|
||||
// load the measured values
|
||||
Tracking_Dump_Reader trk_dump;
|
||||
|
||||
ASSERT_EQ(trk_dump.open_obs_file(std::string("./tracking_ch_0.dat")), true)
|
||||
<< "Failure opening tracking dump file";
|
||||
|
||||
nepoch = trk_dump.num_epochs();
|
||||
std::cout << "Measured observation epochs=" << nepoch << '\n';
|
||||
// trk_dump.restart();
|
||||
|
||||
arma::vec trk_timestamp_s = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_acc_carrier_phase_cycles = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_Doppler_Hz = arma::zeros(nepoch, 1);
|
||||
arma::vec trk_prn_delay_chips = arma::zeros(nepoch, 1);
|
||||
|
||||
std::vector<double> prompt;
|
||||
std::vector<double> early;
|
||||
std::vector<double> late;
|
||||
std::vector<double> promptI;
|
||||
std::vector<double> promptQ;
|
||||
|
||||
epoch_counter = 0;
|
||||
while (trk_dump.read_binary_obs())
|
||||
{
|
||||
trk_timestamp_s(epoch_counter) = static_cast<double>(trk_dump.PRN_start_sample_count) / static_cast<double>(baseband_sampling_freq);
|
||||
trk_acc_carrier_phase_cycles(epoch_counter) = trk_dump.acc_carrier_phase_rad / TWO_PI;
|
||||
trk_Doppler_Hz(epoch_counter) = trk_dump.carrier_doppler_hz;
|
||||
double delay_chips = GPS_L1_CA_CODE_LENGTH_CHIPS - GPS_L1_CA_CODE_LENGTH_CHIPS * (fmod((static_cast<double>(trk_dump.PRN_start_sample_count) + trk_dump.aux1) / static_cast<double>(baseband_sampling_freq), 1.0e-3) / 1.0e-3);
|
||||
|
||||
trk_prn_delay_chips(epoch_counter) = delay_chips;
|
||||
epoch_counter++;
|
||||
prompt.push_back(trk_dump.abs_P);
|
||||
early.push_back(trk_dump.abs_E);
|
||||
late.push_back(trk_dump.abs_L);
|
||||
promptI.push_back(trk_dump.prompt_I);
|
||||
promptQ.push_back(trk_dump.prompt_Q);
|
||||
}
|
||||
|
||||
// Align initial measurements and cut the tracking pull-in transitory
|
||||
double pull_in_offset_s = 1.0;
|
||||
arma::uvec initial_meas_point = arma::find(trk_timestamp_s >= (true_timestamp_s(0) + pull_in_offset_s), 1, "first");
|
||||
|
||||
trk_timestamp_s = trk_timestamp_s.subvec(initial_meas_point(0), trk_timestamp_s.size() - 1);
|
||||
trk_acc_carrier_phase_cycles = trk_acc_carrier_phase_cycles.subvec(initial_meas_point(0), trk_acc_carrier_phase_cycles.size() - 1);
|
||||
trk_Doppler_Hz = trk_Doppler_Hz.subvec(initial_meas_point(0), trk_Doppler_Hz.size() - 1);
|
||||
trk_prn_delay_chips = trk_prn_delay_chips.subvec(initial_meas_point(0), trk_prn_delay_chips.size() - 1);
|
||||
|
||||
check_results_doppler(true_timestamp_s, true_Doppler_Hz, trk_timestamp_s, trk_Doppler_Hz);
|
||||
check_results_codephase(true_timestamp_s, true_prn_delay_chips, trk_timestamp_s, trk_prn_delay_chips);
|
||||
check_results_acc_carrier_phase(true_timestamp_s, true_acc_carrier_phase_cycles, trk_timestamp_s, trk_acc_carrier_phase_cycles);
|
||||
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
std::cout << "Signal tracking completed in " << elapsed_seconds.count() << " seconds.\n";
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_plot_gps_l1_gaussian_tracking_test == true)
|
||||
{
|
||||
const std::string gnuplot_executable(FLAGS_gnuplot_executable);
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_plot_gps_l1_gaussian_tracking_test) == true)
|
||||
{
|
||||
const std::string gnuplot_executable(absl::GetFlag(FLAGS_gnuplot_executable));
|
||||
#endif
|
||||
if (gnuplot_executable.empty())
|
||||
{
|
||||
std::cout << "WARNING: Although the flag plot_gps_l1_tracking_test has been set to TRUE,\n";
|
||||
std::cout << "gnuplot has not been found in your system.\n";
|
||||
std::cout << "Test results will not be plotted.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
fs::path p(gnuplot_executable);
|
||||
fs::path dir = p.parent_path();
|
||||
const std::string& gnuplot_path = dir.native();
|
||||
Gnuplot::set_GNUPlotPath(gnuplot_path);
|
||||
|
||||
std::vector<double> timevec;
|
||||
double t = 0.0;
|
||||
for (auto it = prompt.begin(); it != prompt.end(); it++)
|
||||
{
|
||||
timevec.push_back(t);
|
||||
t = t + GPS_L1_CA_CODE_PERIOD_S;
|
||||
}
|
||||
Gnuplot g1("linespoints");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
g1.set_title("GPS L1 C/A signal tracking correlators' output (satellite PRN #" + std::to_string(FLAGS_test_satellite_PRN) + ")");
|
||||
#else
|
||||
g1.set_title("GPS L1 C/A signal tracking correlators' output (satellite PRN #" + std::to_string(absl::GetFlag(FLAGS_test_satellite_PRN)) + ")");
|
||||
#endif
|
||||
g1.set_grid();
|
||||
g1.set_xlabel("Time [s]");
|
||||
g1.set_ylabel("Correlators' output");
|
||||
g1.cmd("set key box opaque");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
auto decimate = static_cast<unsigned int>(FLAGS_plot_decimate);
|
||||
#else
|
||||
auto decimate = static_cast<unsigned int>(absl::GetFlag(FLAGS_plot_decimate));
|
||||
#endif
|
||||
g1.plot_xy(timevec, prompt, "Prompt", decimate);
|
||||
g1.plot_xy(timevec, early, "Early", decimate);
|
||||
g1.plot_xy(timevec, late, "Late", decimate);
|
||||
g1.savetops("Correlators_outputs");
|
||||
g1.savetopdf("Correlators_outputs", 18);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g1.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g1.disablescreen();
|
||||
}
|
||||
|
||||
Gnuplot g2("points");
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
g2.set_title("Constellation diagram (satellite PRN #" + std::to_string(FLAGS_test_satellite_PRN) + ")");
|
||||
#else
|
||||
g2.set_title("Constellation diagram (satellite PRN #" + std::to_string(absl::GetFlag(FLAGS_test_satellite_PRN)) + ")");
|
||||
#endif
|
||||
g2.set_grid();
|
||||
g2.set_xlabel("Inphase");
|
||||
g2.set_ylabel("Quadrature");
|
||||
g2.cmd("set size ratio -1");
|
||||
g2.plot_xy(promptI, promptQ);
|
||||
g2.savetops("Constellation");
|
||||
g2.savetopdf("Constellation", 18);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
if (FLAGS_show_plots)
|
||||
#else
|
||||
if (absl::GetFlag(FLAGS_show_plots))
|
||||
#endif
|
||||
{
|
||||
g2.showonscreen(); // window output
|
||||
}
|
||||
else
|
||||
{
|
||||
g2.disablescreen();
|
||||
}
|
||||
}
|
||||
catch (const GnuplotException& ge)
|
||||
{
|
||||
std::cout << ge.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*!
|
||||
* \file gps_l2_m_dll_pll_tracking_test.cc
|
||||
* \brief This class implements a tracking test for Galileo_E5a_DLL_PLL_Tracking
|
||||
* implementation based on some input parameters.
|
||||
* \author Javier Arribas, 2015. jarribas(at)cttc.es
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2012-2020 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "concurrent_queue.h"
|
||||
#include "gnss_block_interface.h"
|
||||
#include "gnss_sdr_valve.h"
|
||||
#include "gnss_synchro.h"
|
||||
#include "gps_l2_m_dll_pll_tracking.h"
|
||||
#include "in_memory_configuration.h"
|
||||
#include "tracking_interface.h"
|
||||
#include <gnuradio/analog/sig_source_waveform.h>
|
||||
#include <gnuradio/blocks/file_source.h>
|
||||
#include <gnuradio/blocks/null_sink.h>
|
||||
#include <gnuradio/blocks/skiphead.h>
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pmt/pmt.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
#else
|
||||
#include <boost/bind/bind.hpp>
|
||||
#endif
|
||||
#ifdef GR_GREATER_38
|
||||
#include <gnuradio/analog/sig_source.h>
|
||||
#else
|
||||
#include <gnuradio/analog/sig_source_c.h>
|
||||
#endif
|
||||
#if PMT_USES_BOOST_ANY
|
||||
namespace wht = boost;
|
||||
#else
|
||||
namespace wht = std;
|
||||
#endif
|
||||
|
||||
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
|
||||
class GpsL2MDllPllTrackingTest_msg_rx;
|
||||
|
||||
using GpsL2MDllPllTrackingTest_msg_rx_sptr = gnss_shared_ptr<GpsL2MDllPllTrackingTest_msg_rx>;
|
||||
|
||||
GpsL2MDllPllTrackingTest_msg_rx_sptr GpsL2MDllPllTrackingTest_msg_rx_make();
|
||||
|
||||
class GpsL2MDllPllTrackingTest_msg_rx : public gr::block
|
||||
{
|
||||
private:
|
||||
friend GpsL2MDllPllTrackingTest_msg_rx_sptr GpsL2MDllPllTrackingTest_msg_rx_make();
|
||||
void msg_handler_channel_events(const pmt::pmt_t msg);
|
||||
GpsL2MDllPllTrackingTest_msg_rx();
|
||||
|
||||
public:
|
||||
int rx_message;
|
||||
~GpsL2MDllPllTrackingTest_msg_rx(); //!< Default destructor
|
||||
};
|
||||
|
||||
|
||||
GpsL2MDllPllTrackingTest_msg_rx_sptr GpsL2MDllPllTrackingTest_msg_rx_make()
|
||||
{
|
||||
return GpsL2MDllPllTrackingTest_msg_rx_sptr(new GpsL2MDllPllTrackingTest_msg_rx());
|
||||
}
|
||||
|
||||
|
||||
void GpsL2MDllPllTrackingTest_msg_rx::msg_handler_channel_events(const pmt::pmt_t msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
int64_t message = pmt::to_long(std::move(msg));
|
||||
rx_message = message;
|
||||
}
|
||||
catch (const wht::bad_any_cast& e)
|
||||
{
|
||||
LOG(WARNING) << "msg_handler_channel_events Bad any_cast: " << e.what();
|
||||
rx_message = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpsL2MDllPllTrackingTest_msg_rx::GpsL2MDllPllTrackingTest_msg_rx() : gr::block("GpsL2MDllPllTrackingTest_msg_rx", gr::io_signature::make(0, 0, 0), gr::io_signature::make(0, 0, 0))
|
||||
{
|
||||
this->message_port_register_in(pmt::mp("events"));
|
||||
this->set_msg_handler(pmt::mp("events"),
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
[this](auto&& PH1) { msg_handler_channel_events(PH1); });
|
||||
#else
|
||||
#if USE_BOOST_BIND_PLACEHOLDERS
|
||||
boost::bind(&GpsL2MDllPllTrackingTest_msg_rx::msg_handler_channel_events, this, boost::placeholders::_1));
|
||||
#else
|
||||
boost::bind(&GpsL2MDllPllTrackingTest_msg_rx::msg_handler_channel_events, this, _1));
|
||||
#endif
|
||||
#endif
|
||||
rx_message = 0;
|
||||
}
|
||||
|
||||
|
||||
GpsL2MDllPllTrackingTest_msg_rx::~GpsL2MDllPllTrackingTest_msg_rx() = default;
|
||||
|
||||
|
||||
// ###########################################################
|
||||
|
||||
class GpsL2MDllPllTrackingTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GpsL2MDllPllTrackingTest()
|
||||
{
|
||||
config = std::make_shared<InMemoryConfiguration>();
|
||||
item_size = sizeof(gr_complex);
|
||||
gnss_synchro = Gnss_Synchro();
|
||||
}
|
||||
|
||||
~GpsL2MDllPllTrackingTest() = default;
|
||||
|
||||
void init();
|
||||
|
||||
std::shared_ptr<Concurrent_Queue<pmt::pmt_t>> queue;
|
||||
gr::top_block_sptr top_block;
|
||||
std::shared_ptr<InMemoryConfiguration> config;
|
||||
Gnss_Synchro gnss_synchro;
|
||||
size_t item_size;
|
||||
};
|
||||
|
||||
|
||||
void GpsL2MDllPllTrackingTest::init()
|
||||
{
|
||||
gnss_synchro.Channel_ID = 0;
|
||||
gnss_synchro.System = 'G';
|
||||
std::string signal = "2S";
|
||||
signal.copy(gnss_synchro.Signal, 2, 0);
|
||||
gnss_synchro.PRN = 7;
|
||||
|
||||
config->set_property("GNSS-SDR.internal_fs_sps", "5000000");
|
||||
config->set_property("Tracking_2S.item_type", "gr_complex");
|
||||
config->set_property("Tracking_2S.dump", "false");
|
||||
config->set_property("Tracking_2S.dump_filename", "../data/L2m_tracking_ch_");
|
||||
config->set_property("Tracking_2S.implementation", "GPS_L2_M_DLL_PLL_Tracking");
|
||||
config->set_property("Tracking_2S.early_late_space_chips", "0.5");
|
||||
config->set_property("Tracking_2S.order", "2");
|
||||
config->set_property("Tracking_2S.pll_bw_hz", "2");
|
||||
config->set_property("Tracking_2S.dll_bw_hz", "0.5");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GpsL2MDllPllTrackingTest, ValidationOfResults)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0.0);
|
||||
int fs_in = 5000000;
|
||||
int nsamples = fs_in * 9;
|
||||
|
||||
init();
|
||||
queue = std::make_shared<Concurrent_Queue<pmt::pmt_t>>();
|
||||
top_block = gr::make_top_block("Tracking test");
|
||||
std::shared_ptr<TrackingInterface> tracking = std::make_shared<GpsL2MDllPllTracking>(config.get(), "Tracking_2S", 1, 1);
|
||||
auto msg_rx = GpsL2MDllPllTrackingTest_msg_rx_make();
|
||||
|
||||
gnss_synchro.Acq_delay_samples = 1;
|
||||
gnss_synchro.Acq_doppler_hz = 1200;
|
||||
gnss_synchro.Acq_samplestamp_samples = 0;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_channel(gnss_synchro.Channel_ID);
|
||||
}) << "Failure setting channel.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->set_gnss_synchro(&gnss_synchro);
|
||||
}) << "Failure setting gnss_synchro.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
tracking->connect(top_block);
|
||||
}) << "Failure connecting tracking to the top_block.";
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
// gr::analog::sig_source_c::sptr source = gr::analog::sig_source_c::make(fs_in, gr::analog::GR_SIN_WAVE, 1000, 1, gr_complex(0));
|
||||
std::string path = std::string(TEST_PATH);
|
||||
std::string file = path + "signal_samples/gps_l2c_m_prn7_5msps.dat";
|
||||
const char* file_name = file.c_str();
|
||||
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
|
||||
auto valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue.get());
|
||||
gr::blocks::null_sink::sptr sink = gr::blocks::null_sink::make(sizeof(Gnss_Synchro));
|
||||
top_block->connect(file_source, 0, valve, 0);
|
||||
top_block->connect(valve, 0, tracking->get_left_block(), 0);
|
||||
top_block->connect(tracking->get_right_block(), 0, sink, 0);
|
||||
top_block->msg_connect(tracking->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
|
||||
}) << "Failure connecting the blocks of tracking test.";
|
||||
|
||||
tracking->start_tracking();
|
||||
|
||||
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.";
|
||||
|
||||
// TODO: Verify tracking results
|
||||
std::cout << "Tracked " << nsamples << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds\n";
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*!
|
||||
* \file fft_length_test.cc
|
||||
* \brief This file implements timing tests for the FFT.
|
||||
* \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 "GPS_L1_CA.h"
|
||||
#include "cuda_multicorrelator.h"
|
||||
#include "gps_sdr_signal_replica.h"
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <cuda.h>
|
||||
#include <cuda_profiler_api.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <thread>
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(gpu_multicorrelator_iterations_test, 1000, "Number of averaged iterations in GPU multicorrelator test timing test");
|
||||
DEFINE_int32(gpu_multicorrelator_max_threads_test, 12, "Number of maximum concurrent correlators in GPU multicorrelator test timing test");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, gpu_multicorrelator_iterations_test, 1000, "Number of averaged iterations in GPU multicorrelator test timing test");
|
||||
ABSL_FLAG(int32_t, gpu_multicorrelator_max_threads_test, 12, "Number of maximum concurrent correlators in GPU multicorrelator test timing test");
|
||||
#endif
|
||||
|
||||
void run_correlator_gpu(cuda_multicorrelator* correlator,
|
||||
float d_rem_carrier_phase_rad,
|
||||
float d_carrier_phase_step_rad,
|
||||
float d_code_phase_step_chips,
|
||||
float d_rem_code_phase_chips,
|
||||
int correlation_size,
|
||||
int d_n_correlator_taps)
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
for (int k = 0; k < FLAGS_cpu_multicorrelator_iterations_test; k++)
|
||||
#else
|
||||
for (int k = 0; k < absl::GetFlag(FLAGS_cpu_multicorrelator_iterations_test); k++)
|
||||
#endif
|
||||
{
|
||||
correlator->Carrier_wipeoff_multicorrelator_resampler_cuda(d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_size,
|
||||
d_n_correlator_taps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(GpuMulticorrelatorTest, MeasureExecutionTime)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
std::chrono::duration<double> elapsed_seconds(0);
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
int max_threads = FLAGS_gpu_multicorrelator_max_threads_test;
|
||||
#else
|
||||
int max_threads = absl::GetFlag(FLAGS_gpu_multicorrelator_max_threads_test);
|
||||
#endif
|
||||
std::vector<std::thread> thread_pool;
|
||||
cuda_multicorrelator* correlator_pool[max_threads];
|
||||
unsigned int correlation_sizes[3] = {2048, 4096, 8192};
|
||||
double execution_times[3];
|
||||
|
||||
gr_complex* d_ca_code;
|
||||
gr_complex* in_gpu;
|
||||
gr_complex* d_correlator_outs;
|
||||
|
||||
int d_n_correlator_taps = 3;
|
||||
int d_vector_length = correlation_sizes[2]; // max correlation size to allocate all the necessary memory
|
||||
float* d_local_code_shift_chips;
|
||||
// Set GPU flags
|
||||
cudaSetDeviceFlags(cudaDeviceMapHost);
|
||||
// allocate host memory
|
||||
// pinned memory mode - use special function to get OS-pinned memory
|
||||
d_n_correlator_taps = 3; // Early, Prompt, and Late
|
||||
// Get space for a vector with the C/A code replica sampled 1x/chip
|
||||
cudaHostAlloc(reinterpret_cast<void**>(&d_ca_code), (static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS) * sizeof(gr_complex)), cudaHostAllocMapped | cudaHostAllocWriteCombined);
|
||||
// Get space for the resampled early / prompt / late local replicas
|
||||
cudaHostAlloc(reinterpret_cast<void**>(&d_local_code_shift_chips), d_n_correlator_taps * sizeof(float), cudaHostAllocMapped | cudaHostAllocWriteCombined);
|
||||
cudaHostAlloc(reinterpret_cast<void**>(&in_gpu), 2 * d_vector_length * sizeof(gr_complex), cudaHostAllocMapped | cudaHostAllocWriteCombined);
|
||||
// correlator outputs (scalar)
|
||||
cudaHostAlloc(reinterpret_cast<void**>(&d_correlator_outs), sizeof(gr_complex) * d_n_correlator_taps, cudaHostAllocMapped | cudaHostAllocWriteCombined);
|
||||
|
||||
// --- Perform initializations ------------------------------
|
||||
// local code resampler on GPU
|
||||
// generate local reference (1 sample per chip)
|
||||
gps_l1_ca_code_gen_complex(own::span<gr_complex>(d_ca_code, static_cast<int>(GPS_L1_CA_CODE_LENGTH_CHIPS)), 1, 0);
|
||||
// generate inut signal
|
||||
for (int n = 0; n < 2 * d_vector_length; n++)
|
||||
{
|
||||
in_gpu[n] = std::complex<float>(static_cast<float>(rand()) / static_cast<float>(RAND_MAX), static_cast<float>(rand()) / static_cast<float>(RAND_MAX));
|
||||
}
|
||||
// Set TAPs delay values [chips]
|
||||
float d_early_late_spc_chips = 0.5;
|
||||
d_local_code_shift_chips[0] = -d_early_late_spc_chips;
|
||||
d_local_code_shift_chips[1] = 0.0;
|
||||
d_local_code_shift_chips[2] = d_early_late_spc_chips;
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n] = new cuda_multicorrelator();
|
||||
correlator_pool[n]->init_cuda_integrated_resampler(d_vector_length, GPS_L1_CA_CODE_LENGTH_CHIPS, d_n_correlator_taps);
|
||||
correlator_pool[n]->set_input_output_vectors(d_correlator_outs, in_gpu);
|
||||
}
|
||||
|
||||
float d_rem_carrier_phase_rad = 0.0;
|
||||
float d_carrier_phase_step_rad = 0.1;
|
||||
float d_code_phase_step_chips = 0.3;
|
||||
float d_rem_code_phase_chips = 0.4;
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
for (int correlation_sizes_idx = 0; correlation_sizes_idx < 3; correlation_sizes_idx++) {
|
||||
for (int current_max_threads = 1; current_max_threads < (max_threads + 1); current_max_threads++)
|
||||
{
|
||||
std::cout << "Running " << current_max_threads << " concurrent correlators\n";
|
||||
start = std::chrono::system_clock::now();
|
||||
// create the concurrent correlator threads
|
||||
for (int current_thread = 0; current_thread < current_max_threads; current_thread++)
|
||||
{
|
||||
// cudaProfilerStart();
|
||||
thread_pool.push_back(std::thread(run_correlator_gpu,
|
||||
correlator_pool[current_thread],
|
||||
d_rem_carrier_phase_rad,
|
||||
d_carrier_phase_step_rad,
|
||||
d_code_phase_step_chips,
|
||||
d_rem_code_phase_chips,
|
||||
correlation_sizes[correlation_sizes_idx],
|
||||
d_n_correlator_taps));
|
||||
// cudaProfilerStop();
|
||||
}
|
||||
// wait the threads to finish they work and destroy the thread objects
|
||||
for (auto& t : thread_pool)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
thread_pool.clear();
|
||||
end = std::chrono::system_clock::now();
|
||||
elapsed_seconds = end - start;
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(FLAGS_gpu_multicorrelator_iterations_test);
|
||||
#else
|
||||
execution_times[correlation_sizes_idx] = elapsed_seconds.count() / static_cast<double>(absl::GetFlag(FLAGS_gpu_multicorrelator_iterations_test));
|
||||
#endif
|
||||
std::cout << "GPU Multicorrelator execution time for length=" << correlation_sizes[correlation_sizes_idx] << " : " << execution_times[correlation_sizes_idx] << " [s]\n";
|
||||
}
|
||||
});
|
||||
|
||||
cudaFreeHost(in_gpu);
|
||||
cudaFreeHost(d_correlator_outs);
|
||||
cudaFreeHost(d_local_code_shift_chips);
|
||||
cudaFreeHost(d_ca_code);
|
||||
|
||||
for (int n = 0; n < max_threads; n++)
|
||||
{
|
||||
correlator_pool[n]->free_cuda();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*!
|
||||
* \file tracking_loop_filter_test.cc
|
||||
* \brief This file implements tests for the general loop filter
|
||||
* \author Cillian O'Driscoll, 2015. cillian.odriscoll(at)gmail.com
|
||||
*
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "tracking_loop_filter.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstddef>
|
||||
|
||||
TEST(TrackingLoopFilterTest, FirstOrderLoop)
|
||||
{
|
||||
int loop_order = 1;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = false;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float g1 = noise_bandwidth * 4.0;
|
||||
|
||||
float result = 0.0;
|
||||
for (float i : sample_data)
|
||||
{
|
||||
result = theFilter.apply(i);
|
||||
EXPECT_FLOAT_EQ(result, i * g1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TrackingLoopFilterTest, FirstOrderLoopWithLastIntegrator)
|
||||
{
|
||||
int loop_order = 1;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = true;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
std::vector<float> expected_out = {0.0, 0.0, 0.01, 0.02, 0.02, 0.02};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float result = 0.0;
|
||||
for (size_t i = 0; i < sample_data.size(); ++i)
|
||||
{
|
||||
result = theFilter.apply(sample_data[i]);
|
||||
EXPECT_NEAR(result, expected_out[i], 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TrackingLoopFilterTest, SecondOrderLoop)
|
||||
{
|
||||
int loop_order = 2;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = false;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
std::vector<float> expected_out = {0.0, 0.0, 13.37778, 0.0889, 0.0889, 0.0889};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float result = 0.0;
|
||||
for (size_t i = 0; i < sample_data.size(); ++i)
|
||||
{
|
||||
result = theFilter.apply(sample_data[i]);
|
||||
EXPECT_NEAR(result, expected_out[i], 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TrackingLoopFilterTest, SecondOrderLoopWithLastIntegrator)
|
||||
{
|
||||
int loop_order = 2;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = true;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
std::vector<float> expected_out = {0.0, 0.0, 0.006689, 0.013422, 0.013511, 0.013600};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float result = 0.0;
|
||||
for (size_t i = 0; i < sample_data.size(); ++i)
|
||||
{
|
||||
result = theFilter.apply(sample_data[i]);
|
||||
EXPECT_NEAR(result, expected_out[i], 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TrackingLoopFilterTest, ThirdOrderLoop)
|
||||
{
|
||||
int loop_order = 3;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = false;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
std::vector<float> expected_out = {0.0, 0.0, 15.31877, 0.04494, 0.04520, 0.04546};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float result = 0.0;
|
||||
for (size_t i = 0; i < sample_data.size(); ++i)
|
||||
{
|
||||
result = theFilter.apply(sample_data[i]);
|
||||
EXPECT_NEAR(result, expected_out[i], 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TrackingLoopFilterTest, ThirdOrderLoopWithLastIntegrator)
|
||||
{
|
||||
int loop_order = 3;
|
||||
float noise_bandwidth = 5.0;
|
||||
float update_interval = 0.001;
|
||||
bool include_last_integrator = true;
|
||||
|
||||
Tracking_loop_filter theFilter(update_interval,
|
||||
noise_bandwidth,
|
||||
loop_order,
|
||||
include_last_integrator);
|
||||
|
||||
EXPECT_EQ(theFilter.get_noise_bandwidth(), noise_bandwidth);
|
||||
EXPECT_EQ(theFilter.get_update_interval(), update_interval);
|
||||
EXPECT_EQ(theFilter.get_include_last_integrator(), include_last_integrator);
|
||||
EXPECT_EQ(theFilter.get_order(), loop_order);
|
||||
|
||||
std::vector<float> sample_data = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
|
||||
std::vector<float> expected_out = {0.0, 0.0, 0.007659, 0.015341, 0.015386, 0.015432};
|
||||
|
||||
theFilter.initialize(0.0);
|
||||
|
||||
float result = 0.0;
|
||||
for (size_t i = 0; i < sample_data.size(); ++i)
|
||||
{
|
||||
result = theFilter.apply(sample_data[i]);
|
||||
EXPECT_NEAR(result, expected_out[i], 1e-4);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
/*!
|
||||
* \file unscented_filter_test.cc
|
||||
* \brief This file implements numerical accuracy test for the CKF library.
|
||||
* \author Gerald LaMountain, 2019. gerald(at)ece.neu.edu
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 "nonlinear_tracking.h"
|
||||
#include <armadillo>
|
||||
#include <gtest/gtest.h>
|
||||
#include <random>
|
||||
|
||||
#define UNSCENTED_TEST_N_TRIALS 10
|
||||
#define UNSCENTED_TEST_TOLERANCE 10
|
||||
|
||||
class TransitionModelUKF : public ModelFunction
|
||||
{
|
||||
public:
|
||||
explicit TransitionModelUKF(const arma::mat& kf_F) { coeff_mat = kf_F; };
|
||||
arma::vec operator()(const arma::vec& input) override { return coeff_mat * input; };
|
||||
|
||||
private:
|
||||
arma::mat coeff_mat;
|
||||
};
|
||||
|
||||
class MeasurementModelUKF : public ModelFunction
|
||||
{
|
||||
public:
|
||||
explicit MeasurementModelUKF(const arma::mat& kf_H) { coeff_mat = kf_H; };
|
||||
arma::vec operator()(const arma::vec& input) override { return coeff_mat * input; };
|
||||
|
||||
private:
|
||||
arma::mat coeff_mat;
|
||||
};
|
||||
|
||||
TEST(UnscentedFilterComputationTest, UnscentedFilterTest)
|
||||
{
|
||||
UnscentedFilter kf_unscented;
|
||||
|
||||
arma::vec kf_x;
|
||||
arma::mat kf_P_x;
|
||||
|
||||
arma::vec kf_x_pre;
|
||||
arma::mat kf_P_x_pre;
|
||||
|
||||
arma::vec ukf_x_pre;
|
||||
arma::mat ukf_P_x_pre;
|
||||
|
||||
arma::vec kf_x_post;
|
||||
arma::mat kf_P_x_post;
|
||||
|
||||
arma::vec ukf_x_post;
|
||||
arma::mat ukf_P_x_post;
|
||||
|
||||
arma::mat kf_F;
|
||||
arma::mat kf_H;
|
||||
|
||||
arma::mat kf_Q;
|
||||
arma::mat kf_R;
|
||||
|
||||
arma::vec eta;
|
||||
arma::vec nu;
|
||||
|
||||
arma::vec kf_y;
|
||||
arma::mat kf_P_y;
|
||||
arma::mat kf_K;
|
||||
|
||||
ModelFunction* transition_function;
|
||||
ModelFunction* measurement_function;
|
||||
|
||||
// -- Perform initializations ------------------------------
|
||||
|
||||
std::random_device r;
|
||||
std::default_random_engine e1(r());
|
||||
std::normal_distribution<float> normal_dist(0, 5);
|
||||
std::uniform_real_distribution<float> uniform_dist(0.1, 5.0);
|
||||
std::uniform_int_distribution<> uniform_dist_int(1, 5);
|
||||
|
||||
uint8_t nx = 0;
|
||||
uint8_t ny = 0;
|
||||
|
||||
for (uint16_t k = 0; k < UNSCENTED_TEST_N_TRIALS; k++)
|
||||
{
|
||||
nx = static_cast<uint8_t>(uniform_dist_int(e1));
|
||||
ny = static_cast<uint8_t>(uniform_dist_int(e1));
|
||||
|
||||
kf_x = arma::randn<arma::vec>(nx, 1);
|
||||
|
||||
kf_P_x_post = 5.0 * arma::diagmat(arma::randu<arma::vec>(nx, 1));
|
||||
kf_x_post = arma::mvnrnd(kf_x, kf_P_x_post);
|
||||
|
||||
kf_unscented.initialize(kf_x_post, kf_P_x_post);
|
||||
|
||||
// Prediction Step
|
||||
kf_F = arma::randu<arma::mat>(nx, nx);
|
||||
kf_Q = arma::diagmat(arma::randu<arma::vec>(nx, 1));
|
||||
|
||||
transition_function = new TransitionModelUKF(kf_F);
|
||||
arma::mat ttx = (*transition_function)(kf_x_post);
|
||||
|
||||
kf_unscented.predict_sequential(kf_x_post, kf_P_x_post, transition_function, kf_Q);
|
||||
|
||||
ukf_x_pre = kf_unscented.get_x_pred();
|
||||
ukf_P_x_pre = kf_unscented.get_P_x_pred();
|
||||
|
||||
kf_x_pre = kf_F * kf_x_post;
|
||||
kf_P_x_pre = kf_F * kf_P_x_post * kf_F.t() + kf_Q;
|
||||
|
||||
EXPECT_TRUE(arma::approx_equal(ukf_x_pre, kf_x_pre, "absdiff", UNSCENTED_TEST_TOLERANCE));
|
||||
EXPECT_TRUE(arma::approx_equal(ukf_P_x_pre, kf_P_x_pre, "absdiff", UNSCENTED_TEST_TOLERANCE));
|
||||
|
||||
// Update Step
|
||||
kf_H = arma::randu<arma::mat>(ny, nx);
|
||||
kf_R = arma::diagmat(arma::randu<arma::vec>(ny, 1));
|
||||
|
||||
eta = arma::mvnrnd(arma::zeros<arma::vec>(nx, 1), kf_Q);
|
||||
nu = arma::mvnrnd(arma::zeros<arma::vec>(ny, 1), kf_R);
|
||||
|
||||
kf_y = kf_H * (kf_F * kf_x + eta) + nu;
|
||||
|
||||
measurement_function = new MeasurementModelUKF(kf_H);
|
||||
kf_unscented.update_sequential(kf_y, kf_x_pre, kf_P_x_pre, measurement_function, kf_R);
|
||||
|
||||
ukf_x_post = kf_unscented.get_x_est();
|
||||
ukf_P_x_post = kf_unscented.get_P_x_est();
|
||||
|
||||
kf_P_y = kf_H * kf_P_x_pre * kf_H.t() + kf_R;
|
||||
kf_K = (kf_P_x_pre * kf_H.t()) * arma::inv(kf_P_y);
|
||||
|
||||
kf_x_post = kf_x_pre + kf_K * (kf_y - kf_H * kf_x_pre);
|
||||
kf_P_x_post = (arma::eye(nx, nx) - kf_K * kf_H) * kf_P_x_pre;
|
||||
|
||||
EXPECT_TRUE(arma::approx_equal(ukf_x_post, kf_x_post, "absdiff", UNSCENTED_TEST_TOLERANCE));
|
||||
EXPECT_TRUE(arma::approx_equal(ukf_P_x_post, kf_P_x_post, "absdiff", UNSCENTED_TEST_TOLERANCE));
|
||||
|
||||
delete transition_function;
|
||||
delete measurement_function;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user