1
0
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:
Carles Fernandez
2024-10-04 11:55:09 +02:00
parent 5be2971c9b
commit 825037592a
251 changed files with 154 additions and 179 deletions

View 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());
}
}

View 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);
}

View 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);
}

View 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';
}
}
}
}

View 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";
});
}

View 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);

View 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);
}

View 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]);
}

View 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";
}

View 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));
}

View 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());
}

View 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);
}

View 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());
}

View 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);
}

View 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);
}

View 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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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";
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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.";
}
}
}

View File

@@ -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.";
}
}
}

View File

@@ -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.";
}
}
}

View File

@@ -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';
}
}

View File

@@ -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";
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}

View File

@@ -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";
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}

View 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());
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View 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}>
)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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()));
}

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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"));
}

View File

@@ -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.";
}

File diff suppressed because it is too large Load Diff

View File

@@ -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));
}

View 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"));
}

View File

@@ -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 yawattitude. 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 ratiotest,
which uses the ratio of squared residuals of the best integer vector to the secondbest 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 cycleslip threshold (m) of geometryfree LC carrierphase 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 carrierphase
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);
}

View File

@@ -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);
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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]));
}
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}
}
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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';
}
}
}
}

View File

@@ -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";
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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