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 += ""; + }); + 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 @@ private: /// @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 @@ 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, 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() +void FalseTarget::set_range(double _range) { - return doppler; -} - -double FalseTarget::get_rcs() -{ - 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 @@ private: /// @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 @@ private: /// @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); + std::complex process(IqData *ref_buffer); - /// @brief Getter for target type. - /// @return Target type. - std::string get_type(); - - /// @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 @@ private: /// @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.