mirror of
https://github.com/30hours/blah2.git
synced 2024-11-18 12:33:58 +00:00
Amend code style in Ambiguity and refactor ambiguity tests
This commit is contained in:
parent
9d0eb44418
commit
7447edc62b
10 changed files with 236 additions and 204 deletions
|
@ -18,13 +18,12 @@ find_package(Catch2 CONFIG REQUIRED)
|
||||||
set(CMAKE_PREFIX_PATH "/opt/uhd" ${CMAKE_PREFIX_PATH})
|
set(CMAKE_PREFIX_PATH "/opt/uhd" ${CMAKE_PREFIX_PATH})
|
||||||
find_package(UHD "4.6.0.0" CONFIG REQUIRED)
|
find_package(UHD "4.6.0.0" CONFIG REQUIRED)
|
||||||
|
|
||||||
# TODO: when release CI is finished, don't use these dirs, install target should go to prod
|
|
||||||
SET (PROJECT_ROOT "${PROJECT_SOURCE_DIR}")
|
SET (PROJECT_ROOT "${PROJECT_SOURCE_DIR}")
|
||||||
SET (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_ROOT}/bin")
|
SET (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_ROOT}/bin")
|
||||||
SET (PROJECT_BINARY_TEST_DIR "${PROJECT_ROOT}/bin/test")
|
SET (PROJECT_BINARY_TEST_DIR "${PROJECT_ROOT}/bin/test")
|
||||||
SET (PROJECT_BINARY_TEST_UNIT_DIR "${PROJECT_BINARY_TEST_DIR}/unit")
|
SET (PROJECT_BINARY_TEST_UNIT_DIR "${PROJECT_BINARY_TEST_DIR}/unit")
|
||||||
SET (PROJECT_BINARY_TEST_UNIT_DIR "${PROJECT_BINARY_TEST_DIR}/functional")
|
SET (PROJECT_BINARY_TEST_FUNCTIONAL_DIR "${PROJECT_BINARY_TEST_DIR}/functional")
|
||||||
SET (PROJECT_BINARY_TEST_UNIT_DIR "${PROJECT_BINARY_TEST_DIR}/comparison")
|
SET (PROJECT_BINARY_TEST_COMPARISON_DIR "${PROJECT_BINARY_TEST_DIR}/comparison")
|
||||||
|
|
||||||
MESSAGE ("Binary path: ${PROJECT_BINARY_DIR}")
|
MESSAGE ("Binary path: ${PROJECT_BINARY_DIR}")
|
||||||
MESSAGE ("Binary test path: ${PROJECT_BINARY_TEST_DIR}")
|
MESSAGE ("Binary test path: ${PROJECT_BINARY_TEST_DIR}")
|
||||||
|
@ -37,6 +36,7 @@ add_library(sdrplay /usr/local/include/sdrplay_api.h)
|
||||||
set_target_properties(sdrplay PROPERTIES LINKER_LANGUAGE C)
|
set_target_properties(sdrplay PROPERTIES LINKER_LANGUAGE C)
|
||||||
target_link_libraries(sdrplay PUBLIC /usr/local/lib/libsdrplay_api.so.3.14)
|
target_link_libraries(sdrplay PUBLIC /usr/local/lib/libsdrplay_api.so.3.14)
|
||||||
|
|
||||||
|
# TODO: Move to separate src/CMakeLists.txt
|
||||||
add_executable(blah2
|
add_executable(blah2
|
||||||
src/blah2.cpp
|
src/blah2.cpp
|
||||||
src/capture/Capture.cpp
|
src/capture/Capture.cpp
|
||||||
|
@ -80,8 +80,13 @@ add_executable(testAmbiguity
|
||||||
src/data/IqData.cpp
|
src/data/IqData.cpp
|
||||||
src/data/Map.cpp
|
src/data/Map.cpp
|
||||||
src/process/ambiguity/Ambiguity.cpp
|
src/process/ambiguity/Ambiguity.cpp
|
||||||
src/process/meta/HammingNumber.cpp)
|
src/process/meta/HammingNumber.cpp
|
||||||
target_link_libraries(testAmbiguity PRIVATE Catch2::Catch2WithMain fftw3 fftw3_threads)
|
)
|
||||||
|
target_link_libraries(testAmbiguity PRIVATE
|
||||||
|
Catch2::Catch2WithMain
|
||||||
|
fftw3
|
||||||
|
fftw3_threads
|
||||||
|
)
|
||||||
set_target_properties(testAmbiguity PROPERTIES
|
set_target_properties(testAmbiguity PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")
|
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")
|
||||||
|
|
||||||
|
@ -89,10 +94,24 @@ add_executable(testTracker
|
||||||
test/unit/process/tracker/TestTracker.cpp
|
test/unit/process/tracker/TestTracker.cpp
|
||||||
src/data/Detection.cpp
|
src/data/Detection.cpp
|
||||||
src/data/Track.cpp
|
src/data/Track.cpp
|
||||||
src/process/tracker/Tracker.cpp)
|
src/process/tracker/Tracker.cpp
|
||||||
target_link_libraries(testTracker PRIVATE Catch2::Catch2WithMain)
|
)
|
||||||
|
target_link_libraries(testTracker PRIVATE
|
||||||
|
Catch2::Catch2WithMain
|
||||||
|
)
|
||||||
set_target_properties(testTracker PROPERTIES
|
set_target_properties(testTracker PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")
|
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")
|
||||||
|
|
||||||
|
add_executable(testHammingNumber
|
||||||
|
test/unit/process/meta/TestHammingNumber.cpp
|
||||||
|
src/process/meta/HammingNumber.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(testHammingNumber PRIVATE
|
||||||
|
Catch2::Catch2WithMain
|
||||||
|
)
|
||||||
|
set_target_properties(testHammingNumber PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")
|
||||||
|
|
||||||
|
# TODO: Unsure if will be using CTest.
|
||||||
add_test(NAME testAmbiguity COMMAND testAmbiguity)
|
add_test(NAME testAmbiguity COMMAND testAmbiguity)
|
||||||
add_test(NAME testTracker COMMAND testTracker)
|
add_test(NAME testTracker COMMAND testTracker)
|
||||||
|
|
|
@ -178,7 +178,7 @@ int main(int argc, char **argv)
|
||||||
tree["process"]["tracker"]["initiate"]["maxAcc"] >> maxAcc;
|
tree["process"]["tracker"]["initiate"]["maxAcc"] >> maxAcc;
|
||||||
rangeRes = (double)Constants::c/fs;
|
rangeRes = (double)Constants::c/fs;
|
||||||
lambda = (double)Constants::c/fc;
|
lambda = (double)Constants::c/fc;
|
||||||
Tracker *tracker = new Tracker(m, n, nDelete, ambiguity->cpi_length_seconds(), maxAcc, rangeRes, lambda);
|
Tracker *tracker = new Tracker(m, n, nDelete, ambiguity->get_cpi(), maxAcc, rangeRes, lambda);
|
||||||
|
|
||||||
// setup process spectrum analyser
|
// setup process spectrum analyser
|
||||||
double spectrumBandwidth = 2000;
|
double spectrumBandwidth = 2000;
|
||||||
|
|
|
@ -8,183 +8,184 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
Ambiguity::Ambiguity(int32_t delayMin, int32_t delayMax, int32_t dopplerMin, int32_t dopplerMax, uint32_t fs, uint32_t n, bool roundHamming)
|
Ambiguity::Ambiguity(int32_t _delayMin, int32_t _delayMax, int32_t _dopplerMin, int32_t _dopplerMax, uint32_t _fs, uint32_t _n, bool _roundHamming)
|
||||||
: delayMin_{delayMin}
|
: delayMin{_delayMin}
|
||||||
, delayMax_{delayMax}
|
, delayMax{_delayMax}
|
||||||
, dopplerMin_{dopplerMin}
|
, dopplerMin{_dopplerMin}
|
||||||
, dopplerMax_{dopplerMax}
|
, dopplerMax{_dopplerMax}
|
||||||
, fs_{fs}
|
, fs{_fs}
|
||||||
, nSamples_{n}
|
, nSamples{_n}
|
||||||
, nDelayBins_{static_cast<uint16_t>(delayMax - delayMin + 1)} // If delayMin > delayMax = trouble, what's the exception policy?
|
, nDelayBins{static_cast<uint16_t>(_delayMax - _delayMin + 1)} // If delayMin > delayMax = trouble, what's the exception policy?
|
||||||
, dopplerMiddle_{(dopplerMin_ + dopplerMax_) / 2.0}
|
, dopplerMiddle{(_dopplerMin + _dopplerMax) / 2.0}
|
||||||
{
|
{
|
||||||
// doppler calculations
|
// doppler calculations
|
||||||
std::deque<double> doppler;
|
std::deque<double> doppler;
|
||||||
double resolutionDoppler = 1.0 / (static_cast<double>(n) / static_cast<double>(fs));
|
double resolutionDoppler = 1.0 / (static_cast<double>(_n) / static_cast<double>(_fs));
|
||||||
doppler.push_back(dopplerMiddle_);
|
doppler.push_back(dopplerMiddle);
|
||||||
int i = 1;
|
int i = 1;
|
||||||
while (dopplerMiddle_ + (i * resolutionDoppler) <= dopplerMax)
|
while (dopplerMiddle + (i * resolutionDoppler) <= dopplerMax)
|
||||||
{
|
{
|
||||||
doppler.push_back(dopplerMiddle_ + (i * resolutionDoppler));
|
doppler.push_back(dopplerMiddle + (i * resolutionDoppler));
|
||||||
doppler.push_front(dopplerMiddle_ - (i * resolutionDoppler));
|
doppler.push_front(dopplerMiddle - (i * resolutionDoppler));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
nDopplerBins_ = doppler.size();
|
nDopplerBins = doppler.size();
|
||||||
|
|
||||||
// batches constants
|
// batches constants
|
||||||
nCorr_ = n / nDopplerBins_;
|
nCorr = _n / nDopplerBins;
|
||||||
cpi_ = (static_cast<double>(nCorr_) * nDopplerBins_) / fs;
|
cpi = (static_cast<double>(nCorr) * nDopplerBins) / fs;
|
||||||
|
|
||||||
// update doppler bins to true cpi time
|
// update doppler bins to true cpi time
|
||||||
resolutionDoppler = 1.0 / cpi_;
|
resolutionDoppler = 1.0 / cpi;
|
||||||
|
|
||||||
// create ambiguity map
|
// create ambiguity map
|
||||||
map_ = std::make_unique<Map<Complex>>(nDopplerBins_, nDelayBins_);
|
map = std::make_unique<Map<Complex>>(nDopplerBins, nDelayBins);
|
||||||
|
|
||||||
// delay calculations
|
// delay calculations
|
||||||
map_->delay.resize(nDelayBins_);
|
map->delay.resize(nDelayBins);
|
||||||
std::iota(map_->delay.begin(), map_->delay.end(), delayMin_);
|
std::iota(map->delay.begin(), map->delay.end(), delayMin);
|
||||||
|
|
||||||
map_->doppler.push_front(dopplerMiddle_);
|
map->doppler.push_front(dopplerMiddle);
|
||||||
i = 1;
|
i = 1;
|
||||||
while (map_->doppler.size() < nDopplerBins_)
|
while (map->doppler.size() < nDopplerBins)
|
||||||
{
|
{
|
||||||
map_->doppler.push_back(dopplerMiddle_ + (i * resolutionDoppler));
|
map->doppler.push_back(dopplerMiddle + (i * resolutionDoppler));
|
||||||
map_->doppler.push_front(dopplerMiddle_ - (i * resolutionDoppler));
|
map->doppler.push_front(dopplerMiddle - (i * resolutionDoppler));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// other setup
|
// other setup
|
||||||
nfft_ = 2 * nCorr_ - 1;
|
nfft = 2 * nCorr - 1;
|
||||||
if (roundHamming) {
|
if (_roundHamming) {
|
||||||
nfft_ = next_hamming(nfft_);
|
nfft = next_hamming(nfft);
|
||||||
}
|
}
|
||||||
dataCorr_.resize(2 * nDelayBins_ + 1);
|
dataCorr.resize(2 * nDelayBins + 1);
|
||||||
|
|
||||||
// compute FFTW plans in constructor
|
// compute FFTW plans in constructor
|
||||||
dataXi_.resize(nfft_);
|
dataXi.resize(nfft);
|
||||||
dataYi_.resize(nfft_);
|
dataYi.resize(nfft);
|
||||||
dataZi_.resize(nfft_);
|
dataZi.resize(nfft);
|
||||||
dataDoppler_.resize(nfft_);
|
dataDoppler.resize(nfft);
|
||||||
fftXi_ = fftw_plan_dft_1d(nfft_, reinterpret_cast<fftw_complex *>(dataXi_.data()),
|
fftXi = fftw_plan_dft_1d(nfft, reinterpret_cast<fftw_complex *>(dataXi.data()),
|
||||||
reinterpret_cast<fftw_complex *>(dataXi_.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
reinterpret_cast<fftw_complex *>(dataXi.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
fftYi_ = fftw_plan_dft_1d(nfft_, reinterpret_cast<fftw_complex *>(dataYi_.data()),
|
fftYi = fftw_plan_dft_1d(nfft, reinterpret_cast<fftw_complex *>(dataYi.data()),
|
||||||
reinterpret_cast<fftw_complex *>(dataYi_.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
reinterpret_cast<fftw_complex *>(dataYi.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
fftZi_ = fftw_plan_dft_1d(nfft_, reinterpret_cast<fftw_complex *>(dataZi_.data()),
|
fftZi = fftw_plan_dft_1d(nfft, reinterpret_cast<fftw_complex *>(dataZi.data()),
|
||||||
reinterpret_cast<fftw_complex *>(dataZi_.data()), FFTW_BACKWARD, FFTW_ESTIMATE);
|
reinterpret_cast<fftw_complex *>(dataZi.data()), FFTW_BACKWARD, FFTW_ESTIMATE);
|
||||||
fftDoppler_ = fftw_plan_dft_1d(nDopplerBins_, reinterpret_cast<fftw_complex *>(dataDoppler_.data()),
|
fftDoppler = fftw_plan_dft_1d(nDopplerBins, reinterpret_cast<fftw_complex *>(dataDoppler.data()),
|
||||||
reinterpret_cast<fftw_complex *>(dataDoppler_.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
reinterpret_cast<fftw_complex *>(dataDoppler.data()), FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ambiguity::~Ambiguity()
|
Ambiguity::~Ambiguity()
|
||||||
{
|
{
|
||||||
fftw_destroy_plan(fftXi_);
|
fftw_destroy_plan(fftXi);
|
||||||
fftw_destroy_plan(fftYi_);
|
fftw_destroy_plan(fftYi);
|
||||||
fftw_destroy_plan(fftZi_);
|
fftw_destroy_plan(fftZi);
|
||||||
fftw_destroy_plan(fftDoppler_);
|
fftw_destroy_plan(fftDoppler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<std::complex<double>> *Ambiguity::process(IqData *x, IqData *y)
|
Map<std::complex<double>> *Ambiguity::process(IqData *x, IqData *y)
|
||||||
{
|
{
|
||||||
using Timer = std::chrono::steady_clock;
|
|
||||||
auto t0{Timer::now()};
|
|
||||||
Timer::duration range_fft_dur{};
|
|
||||||
|
|
||||||
// shift reference if not 0 centered
|
// shift reference if not 0 centered
|
||||||
if (dopplerMiddle_ != 0)
|
if (dopplerMiddle != 0)
|
||||||
{
|
{
|
||||||
std::complex<double> j = {0, 1};
|
std::complex<double> j = {0, 1};
|
||||||
for (int i = 0; i < x->get_length(); i++)
|
for (int i = 0; i < x->get_length(); i++)
|
||||||
{
|
{
|
||||||
x->push_back(x->pop_front() * std::exp(1.0 * j * 2.0 * M_PI * dopplerMiddle_ * ((double)i / fs_)));
|
x->push_back(x->pop_front() * std::exp(1.0 * j * 2.0 * M_PI * dopplerMiddle * ((double)i / fs)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// range processing
|
// range processing
|
||||||
for (int i = 0; i < nDopplerBins_; i++)
|
for (int i = 0; i < nDopplerBins; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < nCorr_; j++)
|
for (int j = 0; j < nCorr; j++)
|
||||||
{
|
{
|
||||||
dataXi_[j] = x->pop_front();
|
dataXi[j] = x->pop_front();
|
||||||
dataYi_[j] = y->pop_front();
|
dataYi[j] = y->pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = nCorr_; j < nfft_; j++)
|
for (int j = nCorr; j < nfft; j++)
|
||||||
{
|
{
|
||||||
dataXi_[j] = {0, 0};
|
dataXi[j] = {0, 0};
|
||||||
dataYi_[j] = {0, 0};
|
dataYi[j] = {0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto t1{Timer::now()};
|
fftw_execute(fftXi);
|
||||||
fftw_execute(fftXi_);
|
fftw_execute(fftYi);
|
||||||
fftw_execute(fftYi_);
|
|
||||||
range_fft_dur += Timer::now() - t1;
|
|
||||||
|
|
||||||
// compute correlation
|
// compute correlation
|
||||||
for (int j = 0; j < nfft_; j++)
|
for (int j = 0; j < nfft; j++)
|
||||||
{
|
{
|
||||||
dataZi_[j] = (dataYi_[j] * std::conj(dataXi_[j])) / (double)nfft_;
|
dataZi[j] = (dataYi[j] * std::conj(dataXi[j])) / (double)nfft;
|
||||||
}
|
}
|
||||||
|
|
||||||
t1 = Timer::now();
|
fftw_execute(fftZi);
|
||||||
fftw_execute(fftZi_);
|
|
||||||
range_fft_dur += Timer::now() - t1;
|
|
||||||
|
|
||||||
// extract center of corr
|
// extract center of corr
|
||||||
for (int j = 0; j < nDelayBins_; j++)
|
for (int j = 0; j < nDelayBins; j++)
|
||||||
{
|
{
|
||||||
dataCorr_[j] = dataZi_[nfft_ - nDelayBins_ + j];
|
dataCorr[j] = dataZi[nfft - nDelayBins + j];
|
||||||
}
|
}
|
||||||
for (int j = 0; j < nDelayBins_ + 1; j++)
|
for (int j = 0; j < nDelayBins + 1; j++)
|
||||||
{
|
{
|
||||||
dataCorr_[j + nDelayBins_] = dataZi_[j];
|
dataCorr[j + nDelayBins] = dataZi[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
// cast from std::complex to std::vector
|
// cast from std::complex to std::vector
|
||||||
corr_.clear();
|
corr.clear();
|
||||||
for (int j = 0; j < nDelayBins_; j++)
|
for (int j = 0; j < nDelayBins; j++)
|
||||||
{
|
{
|
||||||
corr_.push_back(dataCorr_[nDelayBins_ + delayMin_ + j - 1 + 1]);
|
corr.push_back(dataCorr[nDelayBins + delayMin + j - 1 + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
map_->set_row(i, corr_);
|
map->set_row(i, corr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// doppler processing
|
// doppler processing
|
||||||
auto t1{Timer::now()};
|
for (int i = 0; i < nDelayBins; i++)
|
||||||
for (int i = 0; i < nDelayBins_; i++)
|
|
||||||
{
|
{
|
||||||
delayProfile_ = map_->get_col(i);
|
delayProfile = map->get_col(i);
|
||||||
for (int j = 0; j < nDopplerBins_; j++)
|
for (int j = 0; j < nDopplerBins; j++)
|
||||||
{
|
{
|
||||||
dataDoppler_[j] = {delayProfile_[j].real(), delayProfile_[j].imag()};
|
dataDoppler[j] = {delayProfile[j].real(), delayProfile[j].imag()};
|
||||||
}
|
}
|
||||||
|
|
||||||
fftw_execute(fftDoppler_);
|
fftw_execute(fftDoppler);
|
||||||
|
|
||||||
corr_.clear();
|
corr.clear();
|
||||||
for (int j = 0; j < nDopplerBins_; j++)
|
for (int j = 0; j < nDopplerBins; j++)
|
||||||
{
|
{
|
||||||
corr_.push_back(dataDoppler_[(j + int(nDopplerBins_ / 2) + 1) % nDopplerBins_]);
|
corr.push_back(dataDoppler[(j + int(nDopplerBins / 2) + 1) % nDopplerBins]);
|
||||||
}
|
}
|
||||||
|
|
||||||
map_->set_col(i, corr_);
|
map->set_col(i, corr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto to_ms = [] (const Timer::duration& dur) {
|
return map.get();
|
||||||
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(dur).count();
|
|
||||||
};
|
|
||||||
|
|
||||||
latest_performance_.process_time_ms = to_ms(Timer::now() - t0);
|
|
||||||
latest_performance_.doppler_fft_time_ms = to_ms(Timer::now() - t1);
|
|
||||||
latest_performance_.range_fft_time_ms = to_ms(range_fft_dur);
|
|
||||||
|
|
||||||
return map_.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& str, const Ambiguity::PerformanceStats& stats) {
|
double Ambiguity::get_doppler_middle() const {
|
||||||
return str << "Total time: " << stats.process_time_ms << "ms\n" <<
|
return dopplerMiddle;
|
||||||
"Range FFT time: " << stats.range_fft_time_ms << "ms\n" <<
|
}
|
||||||
"Doppler FFT time: " << stats.doppler_fft_time_ms << "ms";
|
|
||||||
}
|
uint16_t Ambiguity::get_n_delay_bins() const {
|
||||||
|
return nDelayBins;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Ambiguity::get_n_doppler_bins() const {
|
||||||
|
return nDopplerBins;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Ambiguity::get_n_corr() const {
|
||||||
|
return nCorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Ambiguity::get_cpi() const {
|
||||||
|
return cpi;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Ambiguity::get_nfft() const {
|
||||||
|
return nfft;
|
||||||
|
}
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
/// See Fundamentals of Radar Signal Processing (Richards) for more on the pulse-Doppler processing method.
|
/// See Fundamentals of Radar Signal Processing (Richards) for more on the pulse-Doppler processing method.
|
||||||
/// @author 30hours
|
/// @author 30hours
|
||||||
/// @todo Ambiguity maps are still offset by 1 bin.
|
/// @todo Ambiguity maps are still offset by 1 bin.
|
||||||
|
/// @todo Write a performance test for hamming assisted ambiguity processing.
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "data/IqData.h"
|
#include "data/IqData.h"
|
||||||
#include "data/Map.h"
|
#include "data/Map.h"
|
||||||
|
@ -22,13 +21,6 @@ public:
|
||||||
|
|
||||||
using Complex = std::complex<double>;
|
using Complex = std::complex<double>;
|
||||||
|
|
||||||
struct PerformanceStats {
|
|
||||||
double process_time_ms{0};
|
|
||||||
double range_fft_time_ms{0};
|
|
||||||
double doppler_fft_time_ms{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Constructor.
|
/// @brief Constructor.
|
||||||
/// @param delayMin Minimum delay (bins).
|
/// @param delayMin Minimum delay (bins).
|
||||||
/// @param delayMax Maximum delay (bins).
|
/// @param delayMax Maximum delay (bins).
|
||||||
|
@ -36,7 +28,7 @@ public:
|
||||||
/// @param dopplerMax Maximum Doppler (Hz).
|
/// @param dopplerMax Maximum Doppler (Hz).
|
||||||
/// @param fs Sampling frequency (Hz).
|
/// @param fs Sampling frequency (Hz).
|
||||||
/// @param n Number of samples.
|
/// @param n Number of samples.
|
||||||
/// @param roundHamming Round the correlation FFT length to a Hamming number for performance
|
/// @param roundHamming Round the correlation FFT length to a Hamming number for performance.
|
||||||
/// @return The object.
|
/// @return The object.
|
||||||
Ambiguity(int32_t delayMin, int32_t delayMax, int32_t dopplerMin, int32_t dopplerMax, uint32_t fs, uint32_t n, bool roundHamming = false);
|
Ambiguity(int32_t delayMin, int32_t delayMax, int32_t dopplerMin, int32_t dopplerMax, uint32_t fs, uint32_t n, bool roundHamming = false);
|
||||||
|
|
||||||
|
@ -50,81 +42,77 @@ public:
|
||||||
/// @return Ambiguity map data of IQ samples.
|
/// @return Ambiguity map data of IQ samples.
|
||||||
Map<Complex> *process(IqData *x, IqData *y);
|
Map<Complex> *process(IqData *x, IqData *y);
|
||||||
|
|
||||||
double doppler_middle() const { return dopplerMiddle_; }
|
double get_doppler_middle() const;
|
||||||
|
|
||||||
uint16_t delay_bin_count() const { return nDelayBins_; }
|
uint16_t get_n_delay_bins() const;
|
||||||
|
|
||||||
uint16_t doppler_bin_count() const { return nDopplerBins_; }
|
uint16_t get_n_doppler_bins() const;
|
||||||
|
|
||||||
uint16_t corr_samples_per_pulse() const { return nCorr_; }
|
uint16_t get_n_corr() const;
|
||||||
|
|
||||||
double cpi_length_seconds() const { return cpi_; }
|
double get_cpi() const;
|
||||||
|
|
||||||
uint32_t fft_bin_count() const { return nfft_; }
|
uint32_t get_nfft() const;
|
||||||
|
|
||||||
PerformanceStats get_latest_performance() const { return latest_performance_; }
|
|
||||||
private:
|
private:
|
||||||
/// @brief Minimum delay (bins).
|
/// @brief Minimum delay (bins).
|
||||||
int32_t delayMin_;
|
int32_t delayMin;
|
||||||
|
|
||||||
/// @brief Maximum delay (bins).
|
/// @brief Maximum delay (bins).
|
||||||
int32_t delayMax_;
|
int32_t delayMax;
|
||||||
|
|
||||||
/// @brief Minimum Doppler (Hz).
|
/// @brief Minimum Doppler (Hz).
|
||||||
int32_t dopplerMin_;
|
int32_t dopplerMin;
|
||||||
|
|
||||||
/// @brief Maximum Doppler (Hz).
|
/// @brief Maximum Doppler (Hz).
|
||||||
int32_t dopplerMax_;
|
int32_t dopplerMax;
|
||||||
|
|
||||||
/// @brief Sampling frequency (Hz).
|
/// @brief Sampling frequency (Hz).
|
||||||
uint32_t fs_;
|
uint32_t fs;
|
||||||
|
|
||||||
/// @brief Number of samples.
|
/// @brief Number of samples.
|
||||||
uint32_t nSamples_;
|
uint32_t nSamples;
|
||||||
|
|
||||||
/// @brief Center of Doppler bins (Hz).
|
/// @brief Center of Doppler bins (Hz).
|
||||||
double dopplerMiddle_;
|
double dopplerMiddle;
|
||||||
|
|
||||||
/// @brief Number of delay bins.
|
/// @brief Number of delay bins.
|
||||||
uint16_t nDelayBins_;
|
uint16_t nDelayBins;
|
||||||
|
|
||||||
/// @brief Number of Doppler bins.
|
/// @brief Number of Doppler bins.
|
||||||
uint16_t nDopplerBins_;
|
uint16_t nDopplerBins;
|
||||||
|
|
||||||
/// @brief Number of correlation samples per pulse.
|
/// @brief Number of correlation samples per pulse.
|
||||||
uint16_t nCorr_;
|
uint16_t nCorr;
|
||||||
|
|
||||||
/// @brief True CPI time (s).
|
/// @brief True CPI time (s).
|
||||||
double cpi_;
|
double cpi;
|
||||||
|
|
||||||
/// @brief FFTW plans for ambiguity processing.
|
/// @brief FFTW plans for ambiguity processing.
|
||||||
fftw_plan fftXi_;
|
fftw_plan fftXi;
|
||||||
fftw_plan fftYi_;
|
fftw_plan fftYi;
|
||||||
fftw_plan fftZi_;
|
fftw_plan fftZi;
|
||||||
fftw_plan fftDoppler_;
|
fftw_plan fftDoppler;
|
||||||
|
|
||||||
/// @brief FFTW storage for ambiguity processing.
|
/// @brief FFTW storage for ambiguity processing.
|
||||||
/// @{
|
/// @{
|
||||||
std::vector<Complex> dataXi_;
|
std::vector<Complex> dataXi;
|
||||||
std::vector<Complex> dataYi_;
|
std::vector<Complex> dataYi;
|
||||||
std::vector<Complex> dataZi_;
|
std::vector<Complex> dataZi;
|
||||||
std::vector<Complex> dataCorr_;
|
std::vector<Complex> dataCorr;
|
||||||
std::vector<Complex> dataDoppler_;
|
std::vector<Complex> dataDoppler;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// @brief Number of samples to perform FFT per pulse.
|
/// @brief Number of samples to perform FFT per pulse.
|
||||||
uint32_t nfft_;
|
uint32_t nfft;
|
||||||
|
|
||||||
/// @brief Vector storage for ambiguity processing
|
/// @brief Vector storage for ambiguity processing
|
||||||
/// @{
|
/// @{
|
||||||
std::vector<Complex> corr_;
|
std::vector<Complex> corr;
|
||||||
std::vector<Complex> delayProfile_;
|
std::vector<Complex> delayProfile;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// @brief Map to store result.
|
/// @brief Map to store result.
|
||||||
std::unique_ptr<Map<Complex>> map_;
|
std::unique_ptr<Map<Complex>> map;
|
||||||
|
|
||||||
PerformanceStats latest_performance_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& str, const Ambiguity::PerformanceStats& stats);
|
|
|
@ -1,40 +1,48 @@
|
||||||
#include "HammingNumber.h"
|
#include "HammingNumber.h"
|
||||||
|
|
||||||
bool HammingNumber::operator!=(const HammingNumber &other) const {
|
bool HammingNumber::operator!=(const HammingNumber &other) const
|
||||||
return true;
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HammingNumber HammingNumber::begin() const {
|
HammingNumber HammingNumber::begin() const
|
||||||
return *this;
|
{
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
HammingNumber HammingNumber::end() const {
|
HammingNumber HammingNumber::end() const
|
||||||
return *this;
|
{
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int HammingNumber::operator*() const {
|
unsigned int HammingNumber::operator*() const
|
||||||
return _x.back();
|
{
|
||||||
|
return x.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
HammingNumber::HammingNumber(const std::vector<unsigned int> &pfs)
|
HammingNumber::HammingNumber(const std::vector<unsigned int> &pfs)
|
||||||
: _H(pfs), _hp(pfs.size(), 0), _hv({pfs}), _x({1}) {}
|
: H(pfs), hp(pfs.size(), 0), hv({pfs}), x({1}) {}
|
||||||
|
|
||||||
const HammingNumber &HammingNumber::operator++() {
|
const HammingNumber &HammingNumber::operator++()
|
||||||
for (int i = 0; i < _H.size(); i++)
|
{
|
||||||
for (; _hv[i] <= _x.back(); _hv[i] = _x[++_hp[i]] * _H[i])
|
for (int i = 0; i < H.size(); i++)
|
||||||
;
|
for (; hv[i] <= x.back(); hv[i] = x[++hp[i]] * H[i])
|
||||||
_x.push_back(_hv[0]);
|
;
|
||||||
for (int i = 1; i < _H.size(); i++)
|
x.push_back(hv[0]);
|
||||||
if (_hv[i] < _x.back())
|
for (int i = 1; i < H.size(); i++)
|
||||||
_x.back() = _hv[i];
|
if (hv[i] < x.back())
|
||||||
return *this;
|
x.back() = hv[i];
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t next_hamming(uint32_t value) {
|
uint32_t next_hamming(uint32_t value)
|
||||||
for (auto i : HammingNumber({2, 3, 5})) {
|
{
|
||||||
if (i > value) {
|
for (auto i : HammingNumber({2, 3, 5}))
|
||||||
return i;
|
{
|
||||||
}
|
if (i > value)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
return 0;
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
/// @brief Hamming number generator
|
/// @brief Hamming number generator
|
||||||
/// @author Nigel Galloway
|
/// @author Nigel Galloway
|
||||||
/// @cite https://rosettacode.org/wiki/Hamming_numbers
|
/// @cite https://rosettacode.org/wiki/Hamming_numbers
|
||||||
/// @todo Can this be done with constexpr???
|
|
||||||
|
|
||||||
#ifndef HAMMING_GENERATOR_H
|
#ifndef HAMMING_GENERATOR_H
|
||||||
#define HAMMING_GENERATOR_H
|
#define HAMMING_GENERATOR_H
|
||||||
|
@ -15,7 +14,7 @@ class HammingNumber
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<unsigned int> _H, _hp, _hv, _x;
|
std::vector<unsigned int> H, hp, hv, x;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool operator!=(const HammingNumber &other) const;
|
bool operator!=(const HammingNumber &other) const;
|
||||||
|
@ -24,6 +23,7 @@ class HammingNumber
|
||||||
unsigned int operator*() const;
|
unsigned int operator*() const;
|
||||||
HammingNumber(const std::vector<unsigned int> &pfs);
|
HammingNumber(const std::vector<unsigned int> &pfs);
|
||||||
const HammingNumber &operator++();
|
const HammingNumber &operator++();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Calculate the next 5-smooth Hamming Number larger than value
|
/// @brief Calculate the next 5-smooth Hamming Number larger than value
|
||||||
|
|
|
@ -12,7 +12,7 @@ The test files are split across directories defined by the type of test.
|
||||||
|
|
||||||
- **Unit tests** will test the class in isolation. The directory structure mirrors *src*.
|
- **Unit tests** will test the class in isolation. The directory structure mirrors *src*.
|
||||||
- **Functional tests** will test that expected outputs are achieved from defined inputs. An example would be checking the program turns a specific IQ data set to a specific delay-Doppler map. This test category will rely on golden data.
|
- **Functional tests** will test that expected outputs are achieved from defined inputs. An example would be checking the program turns a specific IQ data set to a specific delay-Doppler map. This test category will rely on golden data.
|
||||||
- **Comparison tests** will compare different methods of performing the same task. An example would be comparing 2 methods of clutter filtering. Metrics to be compared may include time and performance. Note there is no pass/fail criteria for comparison tests - this is purely for information.
|
- **Comparison tests** will compare different methods of performing the same task. An example would be comparing 2 methods of clutter filtering. Metrics to be compared may include time and performance. Note there is no specific pass/fail criteria for comparison tests - this is purely for information. A comparison test will pass if executed successfully. Any comparison testing on input parameters for a single class will be handled in the unit test.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -40,4 +40,7 @@ sudo docker exec -it blah2 /blah2/bin/test/comparison/testComparison
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo docker exec -it blah2 /blah2/bin/test/runall.sh
|
sudo docker exec -it blah2 /blah2/bin/test/runall.sh
|
||||||
|
sudo docker exec -it blah2 /blah2/bin/test/unit/runall.sh
|
||||||
|
sudo docker exec -it blah2 /blah2/bin/test/functional/runall.sh
|
||||||
|
sudo docker exec -it blah2 /blah2/bin/test/comparison/runall.sh
|
||||||
```
|
```
|
9
test/data/README.md
Normal file
9
test/data/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# blah2 Test Data
|
||||||
|
|
||||||
|
A set of golden data used for testing.
|
||||||
|
|
||||||
|
## Log
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
| ------------- | ------------- |
|
||||||
|
| `todo.rspduo.iq` | Stores 1 CPI of IQ data for the SDRPlay RspDuo. |
|
|
@ -84,12 +84,12 @@ TEST_CASE("Constructor", "[constructor]")
|
||||||
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
||||||
dopplerMax, fs, nSamples);
|
dopplerMax, fs, nSamples);
|
||||||
|
|
||||||
CHECK_THAT(ambiguity.cpi_length_seconds(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
CHECK_THAT(ambiguity.get_cpi(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
||||||
CHECK(ambiguity.doppler_middle() == 0);
|
CHECK(ambiguity.get_doppler_middle() == 0);
|
||||||
CHECK(ambiguity.corr_samples_per_pulse() == 3322);
|
CHECK(ambiguity.get_n_corr() == 3322);
|
||||||
CHECK(ambiguity.delay_bin_count() == delayMax + std::abs(delayMin) + 1);
|
CHECK(ambiguity.get_n_delay_bins() == delayMax + std::abs(delayMin) + 1);
|
||||||
CHECK(ambiguity.doppler_bin_count() == 301);
|
CHECK(ambiguity.get_n_doppler_bins() == 301);
|
||||||
CHECK(ambiguity.fft_bin_count() == 6643);
|
CHECK(ambiguity.get_nfft() == 6643);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test constructor with rounded Hamming number FFT length.
|
/// @brief Test constructor with rounded Hamming number FFT length.
|
||||||
|
@ -107,12 +107,12 @@ TEST_CASE("Constructor_Round", "[constructor]")
|
||||||
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
Ambiguity ambiguity(delayMin, delayMax, dopplerMin,
|
||||||
dopplerMax, fs, nSamples, true);
|
dopplerMax, fs, nSamples, true);
|
||||||
|
|
||||||
CHECK_THAT(ambiguity.cpi_length_seconds(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
CHECK_THAT(ambiguity.get_cpi(), Catch::Matchers::WithinAbs(tCpi, 0.02));
|
||||||
CHECK(ambiguity.doppler_middle() == 0);
|
CHECK(ambiguity.get_doppler_middle() == 0);
|
||||||
CHECK(ambiguity.corr_samples_per_pulse() == 3322);
|
CHECK(ambiguity.get_n_corr() == 3322);
|
||||||
CHECK(ambiguity.delay_bin_count() == delayMax + std::abs(delayMin) + 1);
|
CHECK(ambiguity.get_n_delay_bins() == delayMax + std::abs(delayMin) + 1);
|
||||||
CHECK(ambiguity.doppler_bin_count() == 301);
|
CHECK(ambiguity.get_n_doppler_bins() == 301);
|
||||||
CHECK(ambiguity.fft_bin_count() == 6750);
|
CHECK(ambiguity.get_nfft() == 6750);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test simple ambiguity processing.
|
/// @brief Test simple ambiguity processing.
|
||||||
|
@ -141,9 +141,6 @@ TEST_CASE("Process_Simple", "[process]")
|
||||||
map->set_metrics();
|
map->set_metrics();
|
||||||
CHECK(map->maxPower > 0.0);
|
CHECK(map->maxPower > 0.0);
|
||||||
CHECK(map->noisePower > 0.0);
|
CHECK(map->noisePower > 0.0);
|
||||||
|
|
||||||
std::cout << "Process_Simple with" << (round_hamming ? " hamming\n" : "out hamming\n")
|
|
||||||
<< ambiguity.get_latest_performance() << "\n-----------" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test processing from a file.
|
/// @brief Test processing from a file.
|
||||||
|
@ -178,15 +175,4 @@ TEST_CASE("Process_File", "[process]")
|
||||||
map->set_metrics();
|
map->set_metrics();
|
||||||
CHECK_THAT(map->maxPower, Catch::Matchers::WithinAbs(30.2816, 0.001));
|
CHECK_THAT(map->maxPower, Catch::Matchers::WithinAbs(30.2816, 0.001));
|
||||||
CHECK_THAT(map->noisePower, Catch::Matchers::WithinAbs(76.918, 0.001));
|
CHECK_THAT(map->noisePower, Catch::Matchers::WithinAbs(76.918, 0.001));
|
||||||
|
|
||||||
std::cout << "Process_File with" << (round_hamming ? " hamming\n" : "out hamming\n")
|
|
||||||
<< ambiguity.get_latest_performance() << "\n-----------" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test Hamming number calculation.
|
|
||||||
TEST_CASE("Next_Hamming", "[hamming]")
|
|
||||||
{
|
|
||||||
CHECK(next_hamming(104) == 108);
|
|
||||||
CHECK(next_hamming(3322) == 3375);
|
|
||||||
CHECK(next_hamming(19043) == 19200);
|
|
||||||
}
|
|
18
test/unit/process/meta/TestHammingNumber.cpp
Normal file
18
test/unit/process/meta/TestHammingNumber.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/// @file TestHammingNumber.cpp
|
||||||
|
/// @brief Unit test for HammingNumber.cpp
|
||||||
|
/// @author 30hours
|
||||||
|
/// @author Dan G
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
|
|
||||||
|
#include "process/meta/HammingNumber.h"
|
||||||
|
|
||||||
|
/// @brief Test Hamming number calculation.
|
||||||
|
TEST_CASE("Next_Hamming", "[hamming]")
|
||||||
|
{
|
||||||
|
CHECK(next_hamming(104) == 108);
|
||||||
|
CHECK(next_hamming(3322) == 3375);
|
||||||
|
CHECK(next_hamming(19043) == 19200);
|
||||||
|
}
|
Loading…
Reference in a new issue