added class to generate returns from false targets. Currently only static targets implemented

This commit is contained in:
bennysomers 2024-02-05 00:43:11 +11:00
parent 440d74bcbc
commit 6cdc7e0289
11 changed files with 360 additions and 20 deletions

View file

@ -44,6 +44,7 @@ add_executable(blah2
src/capture/rspduo/RspDuo.cpp
src/capture/usrp/Usrp.cpp
src/capture/iqsimulator/IqSimulator.cpp
src/capture/iqsimulator/TgtGen.cpp
src/process/ambiguity/Ambiguity.cpp
src/process/clutter/WienerHopf.cpp
src/process/detection/CfarDetector1D.cpp
@ -58,6 +59,7 @@ add_executable(blah2
src/data/Detection.cpp
src/data/Track.cpp
src/data/meta/Timing.cpp
src/utilities/Conversions.cpp
)
target_link_libraries(blah2 PRIVATE

18
config/false_targets.yml Normal file
View file

@ -0,0 +1,18 @@
targets:
- id: 1
type: "static"
location:
range: 10000 # meters
velocity:
doppler: 50 # Hertz
rcs: -20 # dBsm - this is a bit contrived for a static target
state: "active"
- id: 2
type: "static"
location:
range: 30000 # meters
velocity:
doppler: -150 # Hertz
rcs: -20 # dBsm
state: "active"

View file

@ -63,11 +63,11 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config)
std::unique_ptr<Source> Capture::factory_source(const std::string &type, c4::yml::NodeRef config)
{
if (type == VALID_TYPE[0])
if (type == VALID_TYPE[0]) // RspDuo
{
return std::make_unique<RspDuo>(type, fc, fs, path, &saveIq);
}
else if (type == VALID_TYPE[1])
else if (type == VALID_TYPE[1]) // Usrp
{
std::string address, subdev;
std::vector<std::string> antenna;
@ -88,11 +88,13 @@ std::unique_ptr<Source> Capture::factory_source(const std::string &type, c4::yml
return std::make_unique<Usrp>(type, fc, fs, path, &saveIq,
address, subdev, antenna, gain);
}
else if (type == VALID_TYPE[2])
else if (type == VALID_TYPE[2]) // IqSimulator
{
uint32_t n, n_min;
uint32_t n_min;
n_min = 2000000;
return std::make_unique<IqSimulator>(type, fc, fs, path, &saveIq, n_min);
std::string false_targets_config_file_path = "config/false_targets.yml";
return std::make_unique<IqSimulator>(type, fc, fs, path, &saveIq, n_min,
false_targets_config_file_path);
}
// Handle unknown type
std::cerr << "Error: Source type does not exist." << std::endl;

View file

@ -1,17 +1,15 @@
#include "IqSimulator.h"
#include <string.h>
#include <iostream>
#include <vector>
#include <complex>
#include <random>
// constructor
IqSimulator::IqSimulator(std::string _type, uint32_t _fc, uint32_t _fs,
std::string _path, bool *_saveIq, uint32_t _n_min = 1000)
std::string _path, bool *_saveIq,
uint32_t _n_min = 1000,
std::string _falseTargetsConfigFilePath = "config/false_targets.yml")
: Source(_type, _fc, _fs, _path, _saveIq)
{
n_min = _n_min;
u_int64_t total_samples = 0;
false_targets_config_file_path = _falseTargetsConfigFilePath;
}
void IqSimulator::start()
@ -24,10 +22,15 @@ void IqSimulator::stop()
void IqSimulator::process(IqData *buffer1, IqData *buffer2)
{
const u_int32_t samples_per_iteration = 1000;
TgtGen false_targets = TgtGen(false_targets_config_file_path, fs);
while (true)
{
if (buffer1->get_length() < n_min)
uint32_t n_start = buffer1->get_length();
if (n_start < n_min)
{
// create a random number generator
std::random_device rd;
std::mt19937 gen(rd());
@ -35,11 +38,22 @@ void IqSimulator::process(IqData *buffer1, IqData *buffer2)
buffer1->lock();
buffer2->lock();
for (int i = 0; i < 1000; i++)
for (uint16_t i = 0; i < samples_per_iteration; i++)
{
buffer1->push_back({(double)dis(gen), (double)dis(gen)});
buffer2->push_back({(double)dis(gen), (double)dis(gen)});
try
{
std::complex<double> response = false_targets.process(buffer1);
response += std::complex<double>((double)dis(gen), (double)dis(gen));
buffer2->push_back(response);
}
catch (const std::exception &e)
{
buffer2->push_back({(double)dis(gen), (double)dis(gen)});
}
}
total_samples += samples_per_iteration;
buffer1->unlock();
buffer2->unlock();
}

View file

@ -1,7 +1,9 @@
/// @file IQSimulator.h
/// @class IQSimulator
/// @file IqSimulator.h
/// @class IqSimulator
/// @brief A class to generate simulated IQ data with false targets
/// @details
/// @details This class generates simulated IQ data with false targets.
/// It generates a random reference and surveillance signal and uses the
/// TgtGen class to add false targets to the surveillance signal.
///
/// @author bennysomers
/// @todo Simulate a single false target
@ -13,18 +15,32 @@
#define IQSIMULATOR_H
#include "capture/Source.h"
#include "TgtGen.h"
#include "data/IqData.h"
#include <stdint.h>
#include <string>
#include <iostream>
#include <vector>
#include <complex>
#include <random>
class IqSimulator : public Source
{
private:
/// @brief Number of samples to generate each loop.
/// @details This is the number of samples to generate each time the process method is called. It is also the threshold for the minimum number of samples left in the buffer before new samples will be generated.
/// @details This is the threshold for the minimum number of samples
/// left in the buffer before new samples will be generated.
uint32_t n_min;
/// @brief Total number of samples generated.
/// @details This is used to keep track of the total number of samples
/// generated, so that the Doppler shift can be calculated.
u_int64_t total_samples;
/// @brief Path to the false targets configuration file.
std::string false_targets_config_file_path;
public:
/// @brief Constructor.
/// @param type Type of source. = "IQSimulator"
@ -35,7 +51,7 @@ public:
/// @param n Number of samples.
/// @return The object.
IqSimulator(std::string type, uint32_t fc, uint32_t fs, std::string path,
bool *saveIq, uint32_t n_min);
bool *saveIq, uint32_t n_min, std::string false_targets_config_file_path);
/// @brief Implement capture function on IQSimulator.
/// @param buffer1 Pointer to reference buffer.

View file

@ -0,0 +1,125 @@
#include "TgtGen.h"
// this is straight up copied from blah2.cpp, but I don't know the best way to access that function here.
// edit: put it in utilities?
std::string ryml_get_file_copy(const char *filename);
// constants
const std::string TgtGen::VALID_TYPE[1] = {"static"};
const std::string TgtGen::VALID_STATE[1] = {"active"};
// constructor
TgtGen::TgtGen(std::string configPath, uint32_t fs)
{
// Read in the config file
std::string config = ryml_get_file_copy(configPath.c_str());
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(config));
// Create a FalseTarget object for each target in the config file
for (auto target_node : tree["targets"].children())
{
if (target_node["state"].val() == VALID_STATE[0])
{
try
{
targets.push_back(FalseTarget(target_node, fs));
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
}
}
}
}
std::complex<double> TgtGen::process(IqData *ref_buffer)
{
std::complex<double> response = std::complex<double>(0, 0);
// loop through each target
for (auto target : targets)
{
response += target.process(ref_buffer);
}
return response;
}
std::string FalseTarget::get_type()
{
return type;
}
double FalseTarget::get_range()
{
return range;
}
double FalseTarget::get_doppler()
{
return doppler;
}
double FalseTarget::get_rcs()
{
return rcs;
}
double FalseTarget::get_delay()
{
return delay;
}
u_int32_t FalseTarget::get_id()
{
return id;
}
FalseTarget::FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs)
{
target_node["id"] >> id;
target_node["type"] >> type;
fs = _fs;
if (type == TgtGen::VALID_TYPE[0])
{
target_node["location"]["range"] >> range;
delay = range / 3e8;
delay_samples = delay * fs;
target_node["velocity"]["doppler"] >> doppler;
target_node["rcs"] >> rcs;
}
else
{
throw std::invalid_argument("Invalid target type");
}
}
std::complex<double> FalseTarget::process(IqData *buffer)
{
uint32_t buffer_length = buffer->get_length();
std::complex<double> response = 0;
try
{
response = Conversions::db2lin(rcs) * buffer->get_sample(buffer_length - delay_samples);
response *= std::exp(std::polar<double>(1, 2 * M_PI * doppler * buffer_length / fs));
}
catch (const std::exception &e)
{
}
return response;
}
std::string ryml_get_file_copy(const char *filename)
{
std::ifstream in(filename, std::ios::in | std::ios::binary);
if (!in)
{
std::cerr << "could not open " << filename << std::endl;
exit(1);
}
std::ostringstream contents;
contents << in.rdbuf();
return contents.str();
}

View file

@ -0,0 +1,115 @@
/// @file TgtGen.h
/// @class TgtGen
/// @brief A class to generate false targets.
/// @details
/// Static Targets: remain at a fixed range/delay and Doppler.
/// @author bennysomers
/// @todo Simulate a false target moving in radar coordinates
/// @todo Simulate a false target moving in spatial coordinates
#ifndef TGTGEN_H
#define TGTGEN_H
#include "data/IqData.h"
#include "utilities/Conversions.h"
#include <ryml/ryml.hpp>
#include <ryml/ryml_std.hpp>
#include <c4/format.hpp>
#include <stdint.h>
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <complex>
class FalseTarget
{
private:
/// @brief fs
uint32_t fs;
/// @brief Target type.
std::string type;
/// @brief Target delay
double delay;
/// @brief Target delay in samples
uint32_t delay_samples;
/// @brief Target range
double range;
/// @brief Target Doppler
double doppler;
/// @brief Target RCS
double rcs;
/// @brief Target ID
u_int32_t id;
public:
/// @brief Constructor for targets.
/// @return The object.
FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs);
/// @brief Generate the signal from a false target.
/// @param buffer Pointer to reference buffer.
/// @return Target reflection signal.
std::complex<double> process(IqData *buffer);
/// @brief Getter for target type.
/// @return Target type.
std::string get_type();
/// @brief Getter for target range.type
/// @return Target range.
double get_range();
/// @brief Getter for target Doppler.
/// @return Target Doppler.
double get_doppler();
/// @brief Getter for target RCS.
/// @return Target RCS.
double get_rcs();
/// @brief Getter for target delay.
/// @return Target delay.
double get_delay();
/// @brief Getter for target id.
/// @return Target id.
u_int32_t get_id();
};
class TgtGen
{
private:
/// @brief Vector of false targets.
std::vector<FalseTarget> targets;
public:
/// @brief The valid false target types.
static const std::string VALID_TYPE[1];
/// @brief The valid false target states.
static const std::string VALID_STATE[1];
/// @brief Constructor.
/// @return The object.
TgtGen(std::string configPath, uint32_t fs);
/// @brief Generate the signal from all false targets.
/// @param ref_buffer Pointer to reference buffer.
/// @return Targets reflection signal.
std::complex<double> process(IqData *ref_buffer);
};
#endif
std::string ryml_get_file(const char *filename);

View file

@ -39,6 +39,11 @@ std::deque<std::complex<double>> IqData::get_data()
return *data;
}
std::complex<double> IqData::get_sample(int64_t index)
{
return data->at(index);
}
void IqData::push_back(std::complex<double> sample)
{
if (data->size() < n)

View file

@ -66,6 +66,11 @@ public:
/// @return IQ data.
std::deque<std::complex<double>> get_data();
/// @brief Getter for single sample.
/// @param index Index of sample.
/// @return Sample at index.
std::complex<double> get_sample(int64_t index);
/// @brief Push a sample to the queue.
/// @param sample A single sample.
/// @return Void.

View file

@ -0,0 +1,11 @@
#include "Conversions.h"
double Conversions::db2lin(double db)
{
return pow(10, db / 10);
}
double Conversions::lin2db(double lin)
{
return 10 * log10(lin);
}

View file

@ -0,0 +1,27 @@
/// @file Conversions.h
/// @class Conversions
/// @brief A class to convert between different units.
/// @author bennysomers
/// @todo Add more conversions
#ifndef CONVERSIONS_H
#define CONVERSIONS_H
#include <math.h>
class Conversions
{
public:
/// @brief Convert from dB to linear.
/// @param db Value in dB.
/// @return Value in linear.
static double db2lin(double db);
/// @brief Convert from linear to dB.
/// @param lin Value in linear.
/// @return Value in dB.
static double lin2db(double lin);
};
#endif