From 440d74bcbcf3d64962a0660e35a8f5288d11a212 Mon Sep 17 00:00:00 2001 From: bennysomers Date: Fri, 2 Feb 2024 22:40:39 +1100 Subject: [PATCH 1/3] added class to generate simulated iq data. Currently just generates random data. --- CMakeLists.txt | 1 + src/capture/Capture.cpp | 74 +++++++++++++------------ src/capture/Capture.h | 2 +- src/capture/iqsimulator/IqSimulator.cpp | 51 +++++++++++++++++ src/capture/iqsimulator/IqSimulator.h | 63 +++++++++++++++++++++ 5 files changed, 156 insertions(+), 35 deletions(-) create mode 100644 src/capture/iqsimulator/IqSimulator.cpp create mode 100644 src/capture/iqsimulator/IqSimulator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c8f69a..23a13f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(blah2 src/capture/Source.cpp src/capture/rspduo/RspDuo.cpp src/capture/usrp/Usrp.cpp + src/capture/iqsimulator/IqSimulator.cpp src/process/ambiguity/Ambiguity.cpp src/process/clutter/WienerHopf.cpp src/process/detection/CfarDetector1D.cpp diff --git a/src/capture/Capture.cpp b/src/capture/Capture.cpp index cf04fa5..aae2a38 100644 --- a/src/capture/Capture.cpp +++ b/src/capture/Capture.cpp @@ -4,9 +4,10 @@ #include #include #include +#include "iqsimulator/IqSimulator.h" // constants -const std::string Capture::VALID_TYPE[2] = {"RspDuo", "Usrp"}; +const std::string Capture::VALID_TYPE[3] = {"RspDuo", "Usrp", "IqSimulator"}; // constructor Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _path) @@ -26,7 +27,8 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config) std::unique_ptr device = factory_source(type, config); // capture status thread - std::thread t1([&]{ + std::thread t1([&] + { while (true) { httplib::Client cli("http://127.0.0.1:3000"); @@ -46,8 +48,7 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config) } } sleep(1); - } - }); + } }); if (!replay) { @@ -58,39 +59,44 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config) { device->replay(buffer1, buffer2, file, loop); } - } -std::unique_ptr Capture::factory_source(const std::string& type, c4::yml::NodeRef config) +std::unique_ptr Capture::factory_source(const std::string &type, c4::yml::NodeRef config) { - if (type == VALID_TYPE[0]) - { - return std::make_unique(type, fc, fs, path, &saveIq); - } - else if (type == VALID_TYPE[1]) - { - std::string address, subdev; - std::vector antenna; - std::vector gain; - std::string _antenna; - double _gain; - config["address"] >> address; - config["subdev"] >> subdev; - config["antenna"][0] >> _antenna; - antenna.push_back(_antenna); - config["antenna"][1] >> _antenna; - antenna.push_back(_antenna); - config["gain"][0] >> _gain; - gain.push_back(_gain); - config["gain"][1] >> _gain; - gain.push_back(_gain); - - return std::make_unique(type, fc, fs, path, &saveIq, - address, subdev, antenna, gain); - } - // Handle unknown type - std::cerr << "Error: Source type does not exist." << std::endl; - return nullptr; + if (type == VALID_TYPE[0]) + { + return std::make_unique(type, fc, fs, path, &saveIq); + } + else if (type == VALID_TYPE[1]) + { + std::string address, subdev; + std::vector antenna; + std::vector gain; + std::string _antenna; + double _gain; + config["address"] >> address; + config["subdev"] >> subdev; + config["antenna"][0] >> _antenna; + antenna.push_back(_antenna); + config["antenna"][1] >> _antenna; + antenna.push_back(_antenna); + config["gain"][0] >> _gain; + gain.push_back(_gain); + config["gain"][1] >> _gain; + gain.push_back(_gain); + + return std::make_unique(type, fc, fs, path, &saveIq, + address, subdev, antenna, gain); + } + else if (type == VALID_TYPE[2]) + { + uint32_t n, n_min; + n_min = 2000000; + return std::make_unique(type, fc, fs, path, &saveIq, n_min); + } + // Handle unknown type + std::cerr << "Error: Source type does not exist." << std::endl; + return nullptr; } void Capture::set_replay(bool _loop, std::string _file) diff --git a/src/capture/Capture.h b/src/capture/Capture.h index 760d5bf..ecb4c3d 100644 --- a/src/capture/Capture.h +++ b/src/capture/Capture.h @@ -19,7 +19,7 @@ class Capture { private: /// @brief The valid capture devices. - static const std::string VALID_TYPE[2]; + static const std::string VALID_TYPE[3]; /// @brief The capture device type. std::string type; diff --git a/src/capture/iqsimulator/IqSimulator.cpp b/src/capture/iqsimulator/IqSimulator.cpp new file mode 100644 index 0000000..7d510c3 --- /dev/null +++ b/src/capture/iqsimulator/IqSimulator.cpp @@ -0,0 +1,51 @@ +#include "IqSimulator.h" + +#include +#include +#include +#include +#include + +// constructor +IqSimulator::IqSimulator(std::string _type, uint32_t _fc, uint32_t _fs, + std::string _path, bool *_saveIq, uint32_t _n_min = 1000) + : Source(_type, _fc, _fs, _path, _saveIq) +{ + n_min = _n_min; +} + +void IqSimulator::start() +{ +} + +void IqSimulator::stop() +{ +} + +void IqSimulator::process(IqData *buffer1, IqData *buffer2) +{ + while (true) + { + if (buffer1->get_length() < n_min) + { + // create a random number generator + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(-(2 ^ 14), 2 ^ 14); + + buffer1->lock(); + buffer2->lock(); + for (int i = 0; i < 1000; i++) + { + buffer1->push_back({(double)dis(gen), (double)dis(gen)}); + buffer2->push_back({(double)dis(gen), (double)dis(gen)}); + } + buffer1->unlock(); + buffer2->unlock(); + } + } +} + +void IqSimulator::replay(IqData *buffer1, IqData *buffer2, std::string file, bool loop) +{ +} \ No newline at end of file diff --git a/src/capture/iqsimulator/IqSimulator.h b/src/capture/iqsimulator/IqSimulator.h new file mode 100644 index 0000000..250e6d6 --- /dev/null +++ b/src/capture/iqsimulator/IqSimulator.h @@ -0,0 +1,63 @@ +/// @file IQSimulator.h +/// @class IQSimulator +/// @brief A class to generate simulated IQ data with false targets +/// @details +/// +/// @author bennysomers +/// @todo Simulate a single false target +/// @todo Simulate false targets +/// @todo Simulate realistic target motion +/// @todo Simulate N channels, instead of just 2 + +#ifndef IQSIMULATOR_H +#define IQSIMULATOR_H + +#include "capture/Source.h" +#include "data/IqData.h" + +#include +#include + +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. + uint32_t n_min; + +public: + /// @brief Constructor. + /// @param type Type of source. = "IQSimulator" + /// @param fc Center frequency (Hz). + /// @param fs Sample rate (Hz). + /// @param path Path to save IQ data. + /// @param saveIq Pointer to boolean to save IQ data. + /// @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); + + /// @brief Implement capture function on IQSimulator. + /// @param buffer1 Pointer to reference buffer. + /// @param buffer2 Pointer to surveillance buffer. + /// @return Void. + void process(IqData *buffer1, IqData *buffer2); + + /// @brief Call methods to start capture. + /// @return Void. + void start(); + + /// @brief Call methods to gracefully stop capture. + /// @return Void. + void stop(); + + /// @brief Implement replay function on IQSimulator. + /// @param buffer1 Pointer to reference buffer. + /// @param buffer2 Pointer to surveillance buffer. + /// @param file Path to file to replay data from. + /// @param loop True if samples should loop at EOF. + /// @return Void. + void replay(IqData *buffer1, IqData *buffer2, std::string file, bool loop); +}; + +#endif \ No newline at end of file From 6cdc7e02894ac9c19db8771adb836e2c9d3945fa Mon Sep 17 00:00:00 2001 From: bennysomers Date: Mon, 5 Feb 2024 00:43:11 +1100 Subject: [PATCH 2/3] added class to generate returns from false targets. Currently only static targets implemented --- CMakeLists.txt | 2 + config/false_targets.yml | 18 ++++ src/capture/Capture.cpp | 12 ++- src/capture/iqsimulator/IqSimulator.cpp | 34 +++++-- src/capture/iqsimulator/IqSimulator.h | 26 ++++- src/capture/iqsimulator/TgtGen.cpp | 125 ++++++++++++++++++++++++ src/capture/iqsimulator/TgtGen.h | 115 ++++++++++++++++++++++ src/data/IqData.cpp | 5 + src/data/IqData.h | 5 + src/utilities/Conversions.cpp | 11 +++ src/utilities/Conversions.h | 27 +++++ 11 files changed, 360 insertions(+), 20 deletions(-) create mode 100644 config/false_targets.yml create mode 100644 src/capture/iqsimulator/TgtGen.cpp create mode 100644 src/capture/iqsimulator/TgtGen.h create mode 100644 src/utilities/Conversions.cpp create mode 100644 src/utilities/Conversions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 23a13f1..afd19f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/config/false_targets.yml b/config/false_targets.yml new file mode 100644 index 0000000..50b8adf --- /dev/null +++ b/config/false_targets.yml @@ -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" \ No newline at end of file diff --git a/src/capture/Capture.cpp b/src/capture/Capture.cpp index aae2a38..80eb522 100644 --- a/src/capture/Capture.cpp +++ b/src/capture/Capture.cpp @@ -63,11 +63,11 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config) std::unique_ptr 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(type, fc, fs, path, &saveIq); } - else if (type == VALID_TYPE[1]) + else if (type == VALID_TYPE[1]) // Usrp { std::string address, subdev; std::vector antenna; @@ -88,11 +88,13 @@ std::unique_ptr Capture::factory_source(const std::string &type, c4::yml return std::make_unique(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(type, fc, fs, path, &saveIq, n_min); + std::string false_targets_config_file_path = "config/false_targets.yml"; + return std::make_unique(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; diff --git a/src/capture/iqsimulator/IqSimulator.cpp b/src/capture/iqsimulator/IqSimulator.cpp index 7d510c3..a117f2b 100644 --- a/src/capture/iqsimulator/IqSimulator.cpp +++ b/src/capture/iqsimulator/IqSimulator.cpp @@ -1,17 +1,15 @@ #include "IqSimulator.h" -#include -#include -#include -#include -#include - // 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 response = false_targets.process(buffer1); + response += std::complex((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(); } diff --git a/src/capture/iqsimulator/IqSimulator.h b/src/capture/iqsimulator/IqSimulator.h index 250e6d6..a9297c0 100644 --- a/src/capture/iqsimulator/IqSimulator.h +++ b/src/capture/iqsimulator/IqSimulator.h @@ -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 #include +#include +#include +#include +#include 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 @@ class IqSimulator : public Source /// @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. diff --git a/src/capture/iqsimulator/TgtGen.cpp b/src/capture/iqsimulator/TgtGen.cpp new file mode 100644 index 0000000..e74ce30 --- /dev/null +++ b/src/capture/iqsimulator/TgtGen.cpp @@ -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 TgtGen::process(IqData *ref_buffer) +{ + std::complex response = std::complex(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 FalseTarget::process(IqData *buffer) +{ + uint32_t buffer_length = buffer->get_length(); + std::complex response = 0; + try + { + response = Conversions::db2lin(rcs) * buffer->get_sample(buffer_length - delay_samples); + response *= std::exp(std::polar(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(); +} \ No newline at end of file diff --git a/src/capture/iqsimulator/TgtGen.h b/src/capture/iqsimulator/TgtGen.h new file mode 100644 index 0000000..2aa549d --- /dev/null +++ b/src/capture/iqsimulator/TgtGen.h @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include + +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 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 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 process(IqData *ref_buffer); +}; + +#endif +std::string ryml_get_file(const char *filename); \ No newline at end of file diff --git a/src/data/IqData.cpp b/src/data/IqData.cpp index ff32c74..2dfa710 100644 --- a/src/data/IqData.cpp +++ b/src/data/IqData.cpp @@ -39,6 +39,11 @@ std::deque> IqData::get_data() return *data; } +std::complex IqData::get_sample(int64_t index) +{ + return data->at(index); +} + void IqData::push_back(std::complex sample) { if (data->size() < n) diff --git a/src/data/IqData.h b/src/data/IqData.h index f45e363..530ad8f 100644 --- a/src/data/IqData.h +++ b/src/data/IqData.h @@ -66,6 +66,11 @@ class IqData /// @return IQ data. std::deque> get_data(); + /// @brief Getter for single sample. + /// @param index Index of sample. + /// @return Sample at index. + std::complex get_sample(int64_t index); + /// @brief Push a sample to the queue. /// @param sample A single sample. /// @return Void. diff --git a/src/utilities/Conversions.cpp b/src/utilities/Conversions.cpp new file mode 100644 index 0000000..4694715 --- /dev/null +++ b/src/utilities/Conversions.cpp @@ -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); +} \ No newline at end of file diff --git a/src/utilities/Conversions.h b/src/utilities/Conversions.h new file mode 100644 index 0000000..a6d540a --- /dev/null +++ b/src/utilities/Conversions.h @@ -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 + +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 \ No newline at end of file From 7007efda0e166bc6716a924c20d2810c8672cff7 Mon Sep 17 00:00:00 2001 From: bennysomers Date: Tue, 6 Feb 2024 14:59:12 +1100 Subject: [PATCH 3/3] added false target that moves in radar coordinates. The movement algorithm needs work. Also added a page in controller displaying false target truth --- api/server.js | 120 +++++++++++-------- api/stash/falsetargets.js | 50 ++++++++ config/config.yml | 17 +-- config/false_targets.yml | 14 ++- html/controller/index.html | 1 + html/display/falsetargets/index.html | 25 ++++ html/js/table_falsetargets.js | 60 ++++++++++ src/capture/Capture.cpp | 4 +- src/capture/iqsimulator/IqSimulator.cpp | 6 +- src/capture/iqsimulator/IqSimulator.h | 7 +- src/capture/iqsimulator/TgtGen.cpp | 151 ++++++++++++++++++++---- src/capture/iqsimulator/TgtGen.h | 80 +++++++++---- 12 files changed, 418 insertions(+), 117 deletions(-) create mode 100644 api/stash/falsetargets.js create mode 100644 html/display/falsetargets/index.html create mode 100644 html/js/table_falsetargets.js diff --git a/api/server.js b/api/server.js index eae752d..6b2e15d 100644 --- a/api/server.js +++ b/api/server.js @@ -6,6 +6,7 @@ var stash_map = require('./stash/maxhold.js'); var stash_detection = require('./stash/detection.js'); var stash_iqdata = require('./stash/iqdata.js'); var stash_timing = require('./stash/timing.js'); +var stash_falsetargets = require('./stash/falsetargets.js'); // constants const PORT = 3000; @@ -16,6 +17,7 @@ var track = ''; var timestamp = ''; var timing = ''; var iqdata = ''; +var falsetargets = ''; var data = ''; var data_map; var data_detection; @@ -23,12 +25,13 @@ var data_tracker; var data_timestamp; var data_timing; var data_iqdata; +var data_falsetargets; var capture = false; // api server const app = express(); // header on all requests -app.use(function(req, res, next) { +app.use(function (req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate'); res.header('Expires', '-1'); @@ -56,6 +59,9 @@ app.get('/api/timing', (req, res) => { app.get('/api/iqdata', (req, res) => { res.send(iqdata); }); +app.get('/api/falsetargets', (req, res) => { + res.send(falsetargets); +}); // stash API app.get('/stash/map', (req, res) => { @@ -70,6 +76,9 @@ app.get('/stash/iqdata', (req, res) => { app.get('/stash/timing', (req, res) => { res.send(stash_timing.get_data_timing()); }); +app.get('/stash/falsetargets', (req, res) => { + res.send(stash_falsetargets.get_data_falsetargets()); +}); // read state of capture app.get('/capture', (req, res) => { @@ -85,100 +94,111 @@ app.listen(PORT, HOST, () => { }); // tcp listener map -const server_map = net.createServer((socket)=>{ - socket.write("Hello From Server!") - socket.on("data",(msg)=>{ - data_map = data_map + msg.toString(); - if (data_map.slice(-1) === "}") - { - map = data_map; - data_map = ''; - } - }); - socket.on("close",()=>{ - console.log("Connection closed."); - }) +const server_map = net.createServer((socket) => { + socket.write("Hello From Server!") + socket.on("data", (msg) => { + data_map = data_map + msg.toString(); + if (data_map.slice(-1) === "}") { + map = data_map; + data_map = ''; + } + }); + socket.on("close", () => { + console.log("Connection closed."); + }) }); server_map.listen(3001); // tcp listener detection -const server_detection = net.createServer((socket)=>{ +const server_detection = net.createServer((socket) => { socket.write("Hello From Server!") - socket.on("data",(msg)=>{ - data_detection = data_detection + msg.toString(); - if (data_detection.slice(-1) === "}") - { - detection = data_detection; - data_detection = ''; - } + socket.on("data", (msg) => { + data_detection = data_detection + msg.toString(); + if (data_detection.slice(-1) === "}") { + detection = data_detection; + data_detection = ''; + } }); - socket.on("close",()=>{ - console.log("Connection closed."); + socket.on("close", () => { + console.log("Connection closed."); }) }); server_detection.listen(3002); // tcp listener tracker -const server_tracker = net.createServer((socket)=>{ +const server_tracker = net.createServer((socket) => { socket.write("Hello From Server!") - socket.on("data",(msg)=>{ - data_tracker = data_tracker + msg.toString(); - if (data_tracker.slice(-1) === "}") - { - track = data_tracker; - data_tracker = ''; - } + socket.on("data", (msg) => { + data_tracker = data_tracker + msg.toString(); + if (data_tracker.slice(-1) === "}") { + track = data_tracker; + data_tracker = ''; + } }); - socket.on("close",()=>{ - console.log("Connection closed."); + socket.on("close", () => { + console.log("Connection closed."); }) }); server_tracker.listen(3003); // tcp listener timestamp -const server_timestamp = net.createServer((socket)=>{ +const server_timestamp = net.createServer((socket) => { socket.write("Hello From Server!") - socket.on("data",(msg)=>{ + socket.on("data", (msg) => { data_timestamp = data_timestamp + msg.toString(); timestamp = data_timestamp; data_timestamp = ''; }); - socket.on("close",()=>{ - console.log("Connection closed."); + socket.on("close", () => { + console.log("Connection closed."); }) }); server_timestamp.listen(4000); // tcp listener timing -const server_timing = net.createServer((socket)=>{ +const server_timing = net.createServer((socket) => { socket.write("Hello From Server!") - socket.on("data",(msg)=>{ + socket.on("data", (msg) => { data_timing = data_timing + msg.toString(); - if (data_timing.slice(-1) === "}") - { + if (data_timing.slice(-1) === "}") { timing = data_timing; data_timing = ''; } }); - socket.on("close",()=>{ - console.log("Connection closed."); + socket.on("close", () => { + console.log("Connection closed."); }) }); server_timing.listen(4001); // tcp listener iqdata metadata -const server_iqdata = net.createServer((socket)=>{ +const server_iqdata = net.createServer((socket) => { socket.write("Hello From Server!") - socket.on("data",(msg)=>{ + socket.on("data", (msg) => { data_iqdata = data_iqdata + msg.toString(); - if (data_iqdata.slice(-1) === "}") - { + if (data_iqdata.slice(-1) === "}") { iqdata = data_iqdata; data_iqdata = ''; } }); - socket.on("close",()=>{ - console.log("Connection closed."); + socket.on("close", () => { + console.log("Connection closed."); }) }); server_iqdata.listen(4002); + +// tcp listener falsetargets +const server_falsetargets = net.createServer((socket) => { + socket.write("Hello From Server!") + socket.on("data", (msg) => { + data_falsetargets = data_falsetargets + msg.toString(); + if (data_falsetargets.slice(-1) === "}") { + falsetargets = data_falsetargets; + data_falsetargets = ''; + } + }); + socket.on("close", () => { + console.log("Connection closed."); + }) +}); +server_falsetargets.listen(4003); diff --git a/api/stash/falsetargets.js b/api/stash/falsetargets.js new file mode 100644 index 0000000..72de7b9 --- /dev/null +++ b/api/stash/falsetargets.js @@ -0,0 +1,50 @@ +const http = require('http'); + +var falsetargets = []; +frequency = []; +var ts = ''; +var output = []; +const options_falsetargets = { + host: '127.0.0.1', + path: '/api/falsetargets', + port: 3000 +}; + +function update_data() { + + // check if timestamp is updated + http.get(options_falsetargets, function (res) { + res.setEncoding('utf8'); + res.on('data', function (body) { + if (ts != body) { + ts = body; + http.get(options_falsetargets, function (res) { + let body_map = ''; + res.setEncoding('utf8'); + res.on('data', (chunk) => { + body_map += chunk; + }); + res.on('end', () => { + try { + + output = JSON.parse(body_map); + // false targets + falsetargets.push(output.falsetargets); + } catch (e) { + console.error(e.message); + } + }); + }); + } + }); + }); + +}; + +setInterval(update_data, 100); + +function get_data() { + return output; +}; + +module.exports.get_data_falsetargets = get_data; \ No newline at end of file diff --git a/config/config.yml b/config/config.yml index 47b6c8b..e7eb5b3 100644 --- a/config/config.yml +++ b/config/config.yml @@ -2,7 +2,7 @@ capture: fs: 2000000 fc: 204640000 device: - type: "RspDuo" + type: "IqSimulator" replay: state: false loop: true @@ -14,13 +14,13 @@ process: buffer: 1.5 overlap: 0 ambiguity: - delayMin: -10 - delayMax: 400 - dopplerMin: -200 - dopplerMax: 200 + delayMin: -10 # bins + delayMax: 400 # bins + dopplerMin: -200 # Hz + dopplerMax: 200 # Hz clutter: - delayMin: -10 - delayMax: 400 + delayMin: -10 # bins + delayMax: 400 # bins detection: pfa: 0.00001 nGuard: 2 @@ -46,6 +46,7 @@ network: timestamp: 4000 timing: 4001 iqdata: 4002 + falsetargets: 4003 truth: asdb: @@ -58,7 +59,7 @@ truth: port: 30001 save: - iq: true + iq: false map: false detection: false timing: false diff --git a/config/false_targets.yml b/config/false_targets.yml index 50b8adf..aeb9481 100644 --- a/config/false_targets.yml +++ b/config/false_targets.yml @@ -1,5 +1,5 @@ targets: - - id: 1 + - id: 0 type: "static" location: range: 10000 # meters @@ -8,11 +8,21 @@ targets: rcs: -20 # dBsm - this is a bit contrived for a static target state: "active" - - id: 2 + - id: 1 type: "static" location: range: 30000 # meters velocity: doppler: -150 # Hertz rcs: -20 # dBsm + state: "active" + + - id: 2 + type: "moving_radar" + location: + range: 5000 # meters + velocity: + doppler: 100 # Hertz + dopplerRate: 0 # Hertz/second + rcs: -20 # dBsm - this is also contrived state: "active" \ No newline at end of file diff --git a/html/controller/index.html b/html/controller/index.html index 24818c9..9ec0841 100644 --- a/html/controller/index.html +++ b/html/controller/index.html @@ -38,6 +38,7 @@
  • Detections in delay-Doppler over time
  • Spectrum reference
  • Timing display
  • +
  • False Target Data
  • diff --git a/html/display/falsetargets/index.html b/html/display/falsetargets/index.html new file mode 100644 index 0000000..5f03537 --- /dev/null +++ b/html/display/falsetargets/index.html @@ -0,0 +1,25 @@ + + + + + + blah2 + + + + + + + + +
    +
    +
    +
    + + + + diff --git a/html/js/table_falsetargets.js b/html/js/table_falsetargets.js new file mode 100644 index 0000000..81f4cbb --- /dev/null +++ b/html/js/table_falsetargets.js @@ -0,0 +1,60 @@ + +var host = window.location.hostname; +var isLocalHost = (host === "localhost" || host === "127.0.0.1" || host === "192.168.0.112"); + +// setup API +var urlFalseTargets; + +if (isLocalHost) { + urlFalseTargets = '//' + host + ':3000/api/falsetargets'; +} else { + urlFalseTargets = '//' + host + '/api/falsetargets'; +} + +//callback function +var intervalId = window.setInterval(function () { + $.getJSON(urlFalseTargets, function (data) { + if (data != null) { + var table = document.getElementById("data"); + + + // PLEASE SOMEONE FORMAT THIS NICER! // + var output = ""; + data.false_targets.forEach((target) => { + output += "id: " + target.id + "
    "; + output += "
    • " + target.type + "
    • "; + if (target.type === "static") { + output += "
    • delay: " + target.delay + "
    • "; + output += "
    • delay_samples: " + target.delay_samples + "
    • "; + output += "
    • range: " + target.range + "
    • "; + output += "
    • doppler: " + target.doppler + "
    • "; + output += "
    • rcs: " + target.rcs + "
    • "; + } else if (target.type === "moving_radar") { + output += "
    • delay: " + target.delay + "
    • "; + output += "
    • delay_samples: " + target.delay_samples + "
    • "; + output += "
    • range: " + target.range + "
    • "; + output += "
    • start_range: " + target.start_range + "
    • "; + output += "
    • doppler: " + target.doppler + "
    • "; + output += "
    • doppler_rate: " + target.doppler_rate + "
    • "; + output += "
    • rcs: " + target.rcs + "
    • "; + } + output += "
    "; + }); + table.innerHTML = output; + // data.false_targets.foreach((targetjson) => { + // target = JSON.parse(targetjson); + // console.log(target); + // }); + + // for (var i = 0; i < data.length; i++) { + // var row = table.insertRow(i + 1); + // var cell1 = row.insertCell(0); + // var cell2 = row.insertCell(1); + // var cell3 = row.insertCell(2); + // cell1.innerHTML = data[i].x; + // cell2.innerHTML = data[i].y; + // cell3.innerHTML = data[i].z; + // } + } + }); +}, 100); \ No newline at end of file diff --git a/src/capture/Capture.cpp b/src/capture/Capture.cpp index 80eb522..170e6a7 100644 --- a/src/capture/Capture.cpp +++ b/src/capture/Capture.cpp @@ -92,9 +92,7 @@ std::unique_ptr Capture::factory_source(const std::string &type, c4::yml { uint32_t n_min; n_min = 2000000; - std::string false_targets_config_file_path = "config/false_targets.yml"; - return std::make_unique(type, fc, fs, path, &saveIq, n_min, - false_targets_config_file_path); + return std::make_unique(type, fc, fs, path, &saveIq, n_min); } // Handle unknown type std::cerr << "Error: Source type does not exist." << std::endl; diff --git a/src/capture/iqsimulator/IqSimulator.cpp b/src/capture/iqsimulator/IqSimulator.cpp index a117f2b..cce5c7a 100644 --- a/src/capture/iqsimulator/IqSimulator.cpp +++ b/src/capture/iqsimulator/IqSimulator.cpp @@ -4,12 +4,14 @@ IqSimulator::IqSimulator(std::string _type, uint32_t _fc, uint32_t _fs, std::string _path, bool *_saveIq, uint32_t _n_min = 1000, - std::string _falseTargetsConfigFilePath = "config/false_targets.yml") + std::string _falseTargetsConfigFilePath, + std::string _configFilePath) : Source(_type, _fc, _fs, _path, _saveIq) { n_min = _n_min; u_int64_t total_samples = 0; false_targets_config_file_path = _falseTargetsConfigFilePath; + config_file_path = _configFilePath; } void IqSimulator::start() @@ -24,7 +26,7 @@ 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); + TgtGen false_targets = TgtGen(false_targets_config_file_path, config_file_path, fs, fc); while (true) { uint32_t n_start = buffer1->get_length(); diff --git a/src/capture/iqsimulator/IqSimulator.h b/src/capture/iqsimulator/IqSimulator.h index a9297c0..223a210 100644 --- a/src/capture/iqsimulator/IqSimulator.h +++ b/src/capture/iqsimulator/IqSimulator.h @@ -41,6 +41,9 @@ class IqSimulator : public Source /// @brief Path to the false targets configuration file. std::string false_targets_config_file_path; + /// @brief Path to the radar configuration file. + std::string config_file_path; + public: /// @brief Constructor. /// @param type Type of source. = "IQSimulator" @@ -51,7 +54,9 @@ class IqSimulator : public Source /// @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, std::string false_targets_config_file_path); + bool *saveIq, uint32_t n_min, + std::string false_targets_config_file_path = "config/false_targets.yml", + std::string config_file_path = "config/config.yml"); /// @brief Implement capture function on IQSimulator. /// @param buffer1 Pointer to reference buffer. diff --git a/src/capture/iqsimulator/TgtGen.cpp b/src/capture/iqsimulator/TgtGen.cpp index e74ce30..521a052 100644 --- a/src/capture/iqsimulator/TgtGen.cpp +++ b/src/capture/iqsimulator/TgtGen.cpp @@ -5,14 +5,14 @@ std::string ryml_get_file_copy(const char *filename); // constants -const std::string TgtGen::VALID_TYPE[1] = {"static"}; +const std::string TgtGen::VALID_TYPE[2] = {"static", "moving_radar"}; const std::string TgtGen::VALID_STATE[1] = {"active"}; // constructor -TgtGen::TgtGen(std::string configPath, uint32_t fs) +TgtGen::TgtGen(std::string false_tgt_config_path, std::string config_path, uint32_t fs, uint32_t fc) { - // Read in the config file - std::string config = ryml_get_file_copy(configPath.c_str()); + // Read in the false targets config file + std::string config = ryml_get_file_copy(false_tgt_config_path.c_str()); ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(config)); // Create a FalseTarget object for each target in the config file @@ -22,7 +22,7 @@ TgtGen::TgtGen(std::string configPath, uint32_t fs) { try { - targets.push_back(FalseTarget(target_node, fs)); + targets.push_back(FalseTarget(target_node, fs, fc)); } catch (const std::exception &e) { @@ -30,23 +30,55 @@ TgtGen::TgtGen(std::string configPath, uint32_t fs) } } } + + // Create the socket using details from the config file. + config = ryml_get_file_copy(config_path.c_str()); + tree = ryml::parse_in_arena(ryml::to_csubstr(config)); + + std::string ip; + uint16_t port; + + tree["network"]["ip"] >> ip; + tree["network"]["ports"]["falsetargets"] >> port; + + socket = new Socket(ip, port); + + sample_counter = 0; } std::complex TgtGen::process(IqData *ref_buffer) { std::complex response = std::complex(0, 0); // loop through each target - for (auto target : targets) + for (auto &target : targets) { response += target.process(ref_buffer); } - return response; -} + // output false target truth + if (sample_counter % 100000 == 0) + { + rapidjson::Document document; + document.SetObject(); + rapidjson::Document::AllocatorType &allocator = document.GetAllocator(); + rapidjson::Value json_false_targets(rapidjson::kArrayType); + for (auto target : targets) + { + json_false_targets.PushBack(target.to_json(allocator), allocator); + } -std::string FalseTarget::get_type() -{ - return type; + document.AddMember("false_targets", json_false_targets, allocator); + rapidjson::StringBuffer strbuf; + rapidjson::Writer writer(strbuf); + writer.SetMaxDecimalPlaces(2); + document.Accept(writer); + + socket->sendData(strbuf.GetString()); + } + + sample_counter++; + + return response; } double FalseTarget::get_range() @@ -54,14 +86,11 @@ double FalseTarget::get_range() return range; } -double FalseTarget::get_doppler() -{ - return doppler; -} - -double FalseTarget::get_rcs() +void FalseTarget::set_range(double _range) { - return rcs; + range = _range; + delay = range / Constants::c; + delay_samples = delay * fs; } double FalseTarget::get_delay() @@ -69,40 +98,72 @@ double FalseTarget::get_delay() return delay; } -u_int32_t FalseTarget::get_id() +void FalseTarget::set_delay(double _delay) { - return id; + delay = _delay; + range = delay * Constants::c; + delay_samples = delay * fs; } -FalseTarget::FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs) +FalseTarget::FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs, uint32_t _fc) { target_node["id"] >> id; target_node["type"] >> type; fs = _fs; + fc = _fc; + sample_counter = 0; if (type == TgtGen::VALID_TYPE[0]) { target_node["location"]["range"] >> range; - delay = range / 3e8; + delay = range / Constants::c; delay_samples = delay * fs; target_node["velocity"]["doppler"] >> doppler; target_node["rcs"] >> rcs; } + else if (type == TgtGen::VALID_TYPE[1]) + { + target_node["location"]["range"] >> range; + start_range = range; + delay = range / Constants::c; + delay_samples = delay * fs; + target_node["velocity"]["doppler"] >> doppler; + target_node["velocity"]["dopplerRate"] >> doppler_rate; + target_node["rcs"] >> rcs; + } else { throw std::invalid_argument("Invalid target type"); } } -std::complex FalseTarget::process(IqData *buffer) +std::complex FalseTarget::process(IqData *ref_buffer) { - uint32_t buffer_length = buffer->get_length(); + uint32_t buffer_length = ref_buffer->get_length(); std::complex response = 0; try { - response = Conversions::db2lin(rcs) * buffer->get_sample(buffer_length - delay_samples); + response = Conversions::db2lin(rcs) * ref_buffer->get_sample(buffer_length - delay_samples); response *= std::exp(std::polar(1, 2 * M_PI * doppler * buffer_length / fs)); + + if (type == TgtGen::VALID_TYPE[1]) + { + double range_rate = -1 * doppler * Constants::c / 2.0 / fc; + set_range(range + (range_rate / fs)); + + // very basic PD controller + // will need tuning for different fs + doppler_rate += 0.0000001 * (range - start_range) / start_range; // target tends towards start_range + // doppler_rate -= doppler / std::abs(doppler) / std::max(std::abs(doppler), 0.1) / 100; // target tends towards 0 Doppler + doppler_rate = std::clamp(doppler_rate, -5.0, 5.0); // clamp to a reasonable value + doppler += doppler_rate / fs; // update doppler + doppler = std::clamp(doppler, + std::max(-range, -250.0), + std::min(range, 250.0)); // clamp to range + } + + sample_counter++; } catch (const std::exception &e) { @@ -111,6 +172,46 @@ std::complex FalseTarget::process(IqData *buffer) return response; } +rapidjson::Value FalseTarget::to_json(rapidjson::Document::AllocatorType &allocator) +{ + + rapidjson::Value target(rapidjson::kObjectType); + + target.AddMember("id", id, allocator); + target.AddMember("type", rapidjson::Value(type.c_str(), allocator).Move(), allocator); + + try + { + if (type == TgtGen::VALID_TYPE[0]) + { + target.AddMember("range", range, allocator); + target.AddMember("delay", delay, allocator); + target.AddMember("delay_samples", delay_samples, allocator); + target.AddMember("doppler", doppler, allocator); + target.AddMember("rcs", rcs, allocator); + } + else if (type == TgtGen::VALID_TYPE[1]) + { + target.AddMember("range", range, allocator); + target.AddMember("start_range", start_range, allocator); + target.AddMember("delay", delay, allocator); + target.AddMember("delay_samples", delay_samples, allocator); + target.AddMember("doppler", doppler, allocator); + target.AddMember("doppler_rate", doppler_rate, allocator); + target.AddMember("rcs", rcs, allocator); + } + else + { + throw std::invalid_argument("Invalid target type"); + } + } + catch (const std::exception &e) + { + std::cerr << e.what() << '\n'; + } + return target; +} + std::string ryml_get_file_copy(const char *filename) { std::ifstream in(filename, std::ios::in | std::ios::binary); diff --git a/src/capture/iqsimulator/TgtGen.h b/src/capture/iqsimulator/TgtGen.h index 2aa549d..42966e5 100644 --- a/src/capture/iqsimulator/TgtGen.h +++ b/src/capture/iqsimulator/TgtGen.h @@ -14,17 +14,27 @@ #include "data/IqData.h" #include "utilities/Conversions.h" +#include "data/meta/Constants.h" +#include "process/utility/Socket.h" #include #include #include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" + #include #include #include #include #include #include +#include + +#include class FalseTarget { @@ -32,8 +42,8 @@ class FalseTarget /// @brief fs uint32_t fs; - /// @brief Target type. - std::string type; + /// @brief fc + uint32_t fc; /// @brief Target delay double delay; @@ -44,48 +54,56 @@ class FalseTarget /// @brief Target range double range; + /// @brief Target starting range + double start_range; + + /// @brief Sample counter + uint64_t sample_counter; + +public: + /// @brief Target type. + std::string type; + /// @brief Target Doppler double doppler; + /// @brief Target Doppler Rate + double doppler_rate; + /// @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); + FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs, uint32_t _fc); /// @brief Generate the signal from a false target. - /// @param buffer Pointer to reference buffer. + /// @param ref_buffer Pointer to reference buffer. /// @return Target reflection signal. - std::complex process(IqData *buffer); - - /// @brief Getter for target type. - /// @return Target type. - std::string get_type(); + std::complex process(IqData *ref_buffer); - /// @brief Getter for target range.type - /// @return Target range. + /// @brief Getter for range. + /// @return Range in meters. double get_range(); - /// @brief Getter for target Doppler. - /// @return Target Doppler. - double get_doppler(); + /// @brief Setter for range. + /// @param range Range in meters. + void set_range(double range); - /// @brief Getter for target RCS. - /// @return Target RCS. - double get_rcs(); - - /// @brief Getter for target delay. - /// @return Target delay. + /// @brief Getter for delay. + /// @return Delay in seconds. double get_delay(); - /// @brief Getter for target id. - /// @return Target id. - u_int32_t get_id(); + /// @brief Setter for delay. + /// @param delay Delay in seconds. + void set_delay(double delay); + + /// @brief Outputs false target truth as JSON + /// @return JSON string. + rapidjson::Value to_json(rapidjson::Document::AllocatorType &allocator); }; class TgtGen @@ -94,16 +112,26 @@ class TgtGen /// @brief Vector of false targets. std::vector targets; + /// @brief Socket to send false target data. + Socket *socket; + + /// @brief Sample counter + uint64_t sample_counter; + public: /// @brief The valid false target types. - static const std::string VALID_TYPE[1]; + static const std::string VALID_TYPE[2]; /// @brief The valid false target states. static const std::string VALID_STATE[1]; /// @brief Constructor. + /// @param false_tgt_config_path Path to false targets configuration file. + /// @param config_path Path to blah2 config file. + /// @param fs Sample rate (Hz). + /// @param fc Center frequency (Hz). /// @return The object. - TgtGen(std::string configPath, uint32_t fs); + TgtGen(std::string false_tgt_config_path, std::string config_path, uint32_t fs, uint32_t fc); /// @brief Generate the signal from all false targets. /// @param ref_buffer Pointer to reference buffer.