2023-12-18 12:34:47 +00:00
|
|
|
/// @file TestAmbiguity.cpp
|
|
|
|
/// @brief Unit test for Ambiguity.cpp
|
|
|
|
/// @author 30hours
|
|
|
|
/// @author Dan G
|
|
|
|
/// @todo Add golden data IqData file for testing.
|
|
|
|
/// @todo Declaration match to coding style?
|
|
|
|
|
2023-12-24 01:07:03 +00:00
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
|
|
#include <catch2/generators/catch_generators.hpp>
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-24 05:35:27 +00:00
|
|
|
#include "process/ambiguity/Ambiguity.h"
|
2023-12-24 05:42:15 +00:00
|
|
|
|
2023-12-15 06:24:00 +00:00
|
|
|
#include <random>
|
2023-12-15 06:25:00 +00:00
|
|
|
#include <iostream>
|
2023-12-24 05:42:15 +00:00
|
|
|
#include <filesystem>
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Use random_device as RNG.
|
2023-12-15 06:24:00 +00:00
|
|
|
std::random_device g_rd;
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Generate random IQ data.
|
|
|
|
/// @param iqData Address of IqData object.
|
|
|
|
/// @details Have to use out ref parameter because there's no copy/move ctors.
|
|
|
|
/// @return Void.
|
2023-12-15 06:24:00 +00:00
|
|
|
void random_iq(IqData& iq_data) {
|
|
|
|
std::mt19937 gen(g_rd());
|
|
|
|
std::uniform_real_distribution<> dist(-100.0, 100.0);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < iq_data.get_n(); ++i) {
|
|
|
|
iq_data.push_back({dist(gen), dist(gen)});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Read file to IqData buffer.
|
|
|
|
/// @param buffer1 IqData buffer reference.
|
|
|
|
/// @param buffer2 IqData buffer surveillance.
|
|
|
|
/// @param file String of file name.
|
|
|
|
/// @return Void.
|
2023-12-15 06:24:00 +00:00
|
|
|
void read_file(IqData& buffer1, IqData& buffer2, const std::string& file)
|
|
|
|
{
|
|
|
|
short i1, q1, i2, q2;
|
|
|
|
auto file_replay = fopen(file.c_str(), "rb");
|
|
|
|
if (!file_replay) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto read_short = [](short& v, FILE* fid) {
|
|
|
|
auto rv{fread(&v, 1, sizeof(short), fid)};
|
|
|
|
return rv == sizeof(short);
|
|
|
|
};
|
|
|
|
|
|
|
|
while (!feof(file_replay))
|
|
|
|
{
|
|
|
|
if (!read_short(i1, file_replay)) break;
|
|
|
|
if (!read_short(q1, file_replay)) break;
|
|
|
|
if (!read_short(i2, file_replay)) break;
|
|
|
|
if (!read_short(q2, file_replay)) break;
|
|
|
|
|
|
|
|
buffer1.push_back({(double)i1, (double)q1});
|
|
|
|
buffer2.push_back({(double)i2, (double)q2});
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
// only read for the buffer length - this class is very poorly designed
|
2023-12-15 06:24:00 +00:00
|
|
|
if (buffer1.get_length() == buffer1.get_n()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file_replay);
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Test constructor.
|
|
|
|
/// @details Check constructor parameters created correctly.
|
2023-12-15 06:24:00 +00:00
|
|
|
TEST_CASE("Constructor", "[constructor]")
|
|
|
|
{
|
2023-12-18 12:34:47 +00:00
|
|
|
int32_t delayMin{-10};
|
|
|
|
int32_t delayMax{300};
|
|
|
|
int32_t dopplerMin{-300};
|
|
|
|
int32_t dopplerMax{300};
|
2023-12-15 06:24:00 +00:00
|
|
|
|
|
|
|
uint32_t fs{2'000'000};
|
2023-12-18 12:34:47 +00:00
|
|
|
float tCpi{0.5};
|
|
|
|
uint32_t nSamples = tCpi * fs; // narrow on purpose
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
|
|
|
dopplerMax, fs, nSamples);
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2024-05-04 02:41:33 +00:00
|
|
|
CHECK_THAT(ambiguity.get_cpi(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
|
|
|
CHECK(ambiguity.get_doppler_middle() == 0);
|
|
|
|
CHECK(ambiguity.get_n_corr() == 3322);
|
|
|
|
CHECK(ambiguity.get_n_delay_bins() == delayMax + std::abs(delayMin) + 1);
|
|
|
|
CHECK(ambiguity.get_n_doppler_bins() == 301);
|
|
|
|
CHECK(ambiguity.get_nfft() == 6643);
|
2023-12-15 06:24:00 +00:00
|
|
|
}
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Test constructor with rounded Hamming number FFT length.
|
2023-12-15 06:25:00 +00:00
|
|
|
TEST_CASE("Constructor_Round", "[constructor]")
|
|
|
|
{
|
2023-12-18 12:34:47 +00:00
|
|
|
int32_t delayMin{-10};
|
|
|
|
int32_t delayMax{300};
|
|
|
|
int32_t dopplerMin{-300};
|
|
|
|
int32_t dopplerMax{300};
|
2023-12-15 06:25:00 +00:00
|
|
|
|
|
|
|
uint32_t fs{2'000'000};
|
2023-12-18 12:34:47 +00:00
|
|
|
float tCpi{0.5};
|
|
|
|
uint32_t nSamples = tCpi * fs; // narrow on purpose
|
2023-12-15 06:25:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
|
|
|
dopplerMax, fs, nSamples, true);
|
2023-12-15 06:25:00 +00:00
|
|
|
|
2024-05-04 02:41:33 +00:00
|
|
|
CHECK_THAT(ambiguity.get_cpi(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
|
|
|
CHECK(ambiguity.get_doppler_middle() == 0);
|
|
|
|
CHECK(ambiguity.get_n_corr() == 3322);
|
|
|
|
CHECK(ambiguity.get_n_delay_bins() == delayMax + std::abs(delayMin) + 1);
|
|
|
|
CHECK(ambiguity.get_n_doppler_bins() == 301);
|
|
|
|
CHECK(ambiguity.get_nfft() == 6750);
|
2023-12-15 06:25:00 +00:00
|
|
|
}
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Test simple ambiguity processing.
|
2023-12-15 06:24:00 +00:00
|
|
|
TEST_CASE("Process_Simple", "[process]")
|
|
|
|
{
|
2023-12-15 06:25:00 +00:00
|
|
|
auto round_hamming = GENERATE(true, false);
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
int32_t delayMin{-10};
|
|
|
|
int32_t delayMax{300};
|
|
|
|
int32_t dopplerMin{-300};
|
|
|
|
int32_t dopplerMax{300};
|
2023-12-15 06:24:00 +00:00
|
|
|
|
|
|
|
uint32_t fs{2'000'000};
|
2023-12-18 12:34:47 +00:00
|
|
|
float tCpi{0.5};
|
|
|
|
uint32_t nSamples = tCpi * fs; // narrow on purpose
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
|
|
|
dopplerMax, fs, nSamples, round_hamming);
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
IqData x{nSamples};
|
|
|
|
IqData y{nSamples};
|
2023-12-15 06:24:00 +00:00
|
|
|
|
|
|
|
random_iq(x);
|
|
|
|
random_iq(y);
|
|
|
|
auto map{ambiguity.process(&x, &y)};
|
|
|
|
map->set_metrics();
|
|
|
|
CHECK(map->maxPower > 0.0);
|
|
|
|
CHECK(map->noisePower > 0.0);
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
/// @brief Test processing from a file.
|
2023-12-15 06:24:00 +00:00
|
|
|
TEST_CASE("Process_File", "[process]")
|
|
|
|
{
|
2023-12-24 05:42:15 +00:00
|
|
|
std::filesystem::path test_input_file("20231214-230611.rspduo");
|
|
|
|
// Bail if the test file doesn't exist
|
|
|
|
if (!std::filesystem::exists(test_input_file)) {
|
|
|
|
SKIP("Input test file does not exist.");
|
|
|
|
}
|
|
|
|
|
2023-12-15 06:25:00 +00:00
|
|
|
auto round_hamming = GENERATE(true, false);
|
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
int32_t delayMin{-10};
|
|
|
|
int32_t delayMax{300};
|
|
|
|
int32_t dopplerMin{-300};
|
|
|
|
int32_t dopplerMax{300};
|
2023-12-15 06:24:00 +00:00
|
|
|
|
|
|
|
uint32_t fs{2'000'000};
|
2023-12-18 12:34:47 +00:00
|
|
|
float tCpi{0.5};
|
|
|
|
uint32_t nSamples = tCpi * fs; // narrow on purpose
|
2023-12-15 06:24:00 +00:00
|
|
|
|
2023-12-18 12:34:47 +00:00
|
|
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
|
|
|
dopplerMax, fs, nSamples, round_hamming);
|
|
|
|
IqData x{nSamples};
|
|
|
|
IqData y{nSamples};
|
2023-12-15 06:24:00 +00:00
|
|
|
|
|
|
|
read_file(x, y, "20231214-230611.rspduo");
|
|
|
|
REQUIRE(x.get_length() == x.get_n());
|
|
|
|
|
|
|
|
auto map{ambiguity.process(&x ,&y)};
|
|
|
|
map->set_metrics();
|
|
|
|
CHECK_THAT(map->maxPower, Catch::Matchers::WithinAbs(30.2816, 0.001));
|
|
|
|
CHECK_THAT(map->noisePower, Catch::Matchers::WithinAbs(76.918, 0.001));
|
2023-12-15 06:25:00 +00:00
|
|
|
}
|