diff --git a/CMakeLists.txt b/CMakeLists.txt index 66fb76a..7837952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,11 +27,13 @@ add_executable(blah2 ${PROJECT_SOURCE_DIR}/process/detection/CfarDetector1D.cpp ${PROJECT_SOURCE_DIR}/process/detection/Centroid.cpp ${PROJECT_SOURCE_DIR}/process/detection/Interpolate.cpp + ${PROJECT_SOURCE_DIR}/process/tracker/Tracker.cpp ${PROJECT_SOURCE_DIR}/process/spectrum/SpectrumAnalyser.cpp ${PROJECT_SOURCE_DIR}/process/meta/HammingNumber.cpp ${PROJECT_SOURCE_DIR}/data/IqData.cpp ${PROJECT_SOURCE_DIR}/data/Map.cpp ${PROJECT_SOURCE_DIR}/data/Detection.cpp + ${PROJECT_SOURCE_DIR}/data/Track.cpp ${PROJECT_SOURCE_DIR}/data/meta/Timing.cpp ) @@ -74,6 +76,7 @@ include_directories("${PROJECT_SOURCE_DIR}/capture/rspduo/") include_directories("${PROJECT_SOURCE_DIR}/process/ambiguity/") include_directories("${PROJECT_SOURCE_DIR}/process/clutter/") include_directories("${PROJECT_SOURCE_DIR}/process/detection/") +include_directories("${PROJECT_SOURCE_DIR}/process/tracker/") include_directories("${PROJECT_SOURCE_DIR}/process/spectrum/") include_directories("${PROJECT_SOURCE_DIR}/process/meta/") include_directories("${PROJECT_SOURCE_DIR}/data/") diff --git a/api/server.js b/api/server.js index 5cba978..5cc4a66 100644 --- a/api/server.js +++ b/api/server.js @@ -12,6 +12,7 @@ const PORT = 3000; const HOST = '0.0.0.0'; var map = ''; var detection = ''; +var track = ''; var timestamp = ''; var timing = ''; var iqdata = ''; @@ -37,6 +38,9 @@ app.get('/api/map', (req, res) => { app.get('/api/detection', (req, res) => { res.send(detection); }); +app.get('/api/tracker', (req, res) => { + res.send(track); +}); app.get('/api/timestamp', (req, res) => { res.send(timestamp); }); @@ -108,6 +112,23 @@ const server_detection = net.createServer((socket)=>{ }); server_detection.listen(3002); +// tcp listener tracker +const server_tracker = net.createServer((socket)=>{ + socket.write("Hello From Server!") + socket.on("data",(msg)=>{ + data = data + msg.toString(); + if (data.slice(-1) === "}") + { + track = data; + data = ''; + } + }); + socket.on("close",()=>{ + console.log("Connection closed."); + }) +}); +server_tracker.listen(3003); + // tcp listener timestamp const server_timestamp = net.createServer((socket)=>{ socket.write("Hello From Server!") diff --git a/config/config.yml b/config/config.yml index 1178e03..6324d0a 100644 --- a/config/config.yml +++ b/config/config.yml @@ -27,6 +27,13 @@ process: minDelay: 5 minDoppler: 15 nCentroid: 10 + tracker: + initiate: + M: 3 + N: 5 + maxAcc: 10 + delete: 10 + smooth: "none" network: ip: 0.0.0.0 @@ -34,13 +41,24 @@ network: api: 3000 map: 3001 detection: 3002 + track: 3003 timestamp: 4000 timing: 4001 iqdata: 4002 +truth: + asdb: + enabled: false + ip: 0.0.0.0 + port: 30000 + ais: + enabled: false + ip: 0.0.0.0 + port: 30001 + save: iq: true map: false detection: false timing: false - path: "/blah2/save/" + path: "/blah2/save/" \ No newline at end of file diff --git a/config/radar4.yml b/config/radar4.yml index 285aec2..2410a43 100644 --- a/config/radar4.yml +++ b/config/radar4.yml @@ -14,12 +14,12 @@ process: overlap: 0 ambiguity: delayMin: -10 - delayMax: 300 - dopplerMin: -250 - dopplerMax: 250 + delayMax: 400 + dopplerMin: -200 + dopplerMax: 200 clutter: delayMin: -10 - delayMax: 300 + delayMax: 400 detection: pfa: 0.00001 nGuard: 2 @@ -27,6 +27,13 @@ process: minDelay: 5 minDoppler: 15 nCentroid: 6 + tracker: + initiate: + M: 3 + N: 5 + maxAcc: 10 + delete: 10 + smooth: "none" network: ip: 0.0.0.0 @@ -34,13 +41,24 @@ network: api: 3000 map: 3001 detection: 3002 + track: 3003 timestamp: 4000 timing: 4001 iqdata: 4002 +truth: + asdb: + enabled: false + ip: 0.0.0.0 + port: 30000 + ais: + enabled: false + ip: 0.0.0.0 + port: 30001 + save: iq: true map: false detection: false timing: false - path: "/blah2/save/" + path: "/blah2/save/" \ No newline at end of file diff --git a/src/blah2.cpp b/src/blah2.cpp index 11270af..e06d00d 100644 --- a/src/blah2.cpp +++ b/src/blah2.cpp @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -89,11 +91,10 @@ int main(int argc, char **argv) IqData *x = new IqData(nSamples); IqData *y = new IqData(nSamples); Map> *map; - Map *mapdb; - std::string mapJson, detectionJson; Detection *detection; Detection *detection1; Detection *detection2; + Track *track; // setup fftw multithread if (fftw_init_threads() == 0) @@ -104,10 +105,12 @@ int main(int argc, char **argv) fftw_plan_with_nthreads(4); // setup socket - uint16_t port_map, port_detection, port_timestamp, port_timing, port_iqdata; + uint16_t port_map, port_detection, port_timestamp, + port_timing, port_iqdata, port_track; std::string ip; tree["network"]["ports"]["map"] >> port_map; tree["network"]["ports"]["detection"] >> port_detection; + tree["network"]["ports"]["track"] >> port_track; tree["network"]["ports"]["timestamp"] >> port_timestamp; tree["network"]["ports"]["timing"] >> port_timing; tree["network"]["ports"]["iqdata"] >> port_iqdata; @@ -115,11 +118,13 @@ int main(int argc, char **argv) asio::io_service io_service; asio::ip::tcp::socket socket_map(io_service); asio::ip::tcp::socket socket_detection(io_service); + asio::ip::tcp::socket socket_track(io_service); asio::ip::tcp::socket socket_timestamp(io_service); asio::ip::tcp::socket socket_timing(io_service); asio::ip::tcp::socket socket_iqdata(io_service); asio::ip::tcp::endpoint endpoint_map; asio::ip::tcp::endpoint endpoint_detection; + asio::ip::tcp::endpoint endpoint_track; asio::ip::tcp::endpoint endpoint_timestamp; asio::ip::tcp::endpoint endpoint_timing; asio::ip::tcp::endpoint endpoint_iqdata; @@ -127,6 +132,8 @@ int main(int argc, char **argv) asio::ip::address::from_string(ip), port_map); endpoint_detection = asio::ip::tcp::endpoint( asio::ip::address::from_string(ip), port_detection); + endpoint_track = asio::ip::tcp::endpoint( + asio::ip::address::from_string(ip), port_track); endpoint_timestamp = asio::ip::tcp::endpoint( asio::ip::address::from_string(ip), port_timestamp); endpoint_timing = asio::ip::tcp::endpoint( @@ -135,6 +142,7 @@ int main(int argc, char **argv) asio::ip::address::from_string(ip), port_iqdata); socket_map.connect(endpoint_map); socket_detection.connect(endpoint_detection); + socket_track.connect(endpoint_track); socket_timestamp.connect(endpoint_timestamp); socket_timing.connect(endpoint_timing); socket_iqdata.connect(endpoint_iqdata); @@ -175,6 +183,16 @@ int main(int argc, char **argv) tree["process"]["detection"]["nCentroid"] >> nCentroid; Centroid *centroid = new Centroid(nCentroid, nCentroid, 1/tCpi); + // setup process tracker + uint8_t m, n, nDelete; + double maxAcc; + std::string smooth; + tree["process"]["tracker"]["initiate"]["M"] >> m; + tree["process"]["tracker"]["initiate"]["N"] >> n; + tree["process"]["tracker"]["delete"] >> nDelete; + tree["process"]["tracker"]["initiate"]["maxAcc"] >> maxAcc; + Tracker *tracker = new Tracker(m, n, nDelete, tCpi, maxAcc); + // setup process spectrum analyser double spectrumBandwidth = 2000; SpectrumAnalyser *spectrumAnalyser = new SpectrumAnalyser(nSamples, spectrumBandwidth); @@ -204,8 +222,8 @@ int main(int argc, char **argv) std::string jsonTiming; std::vector time; - // setup output signal - std::string jsonIqData; + // setup output json + std::string mapJson, detectionJson, jsonTracker, jsonIqData; // run process std::thread t2([&]{ @@ -226,29 +244,33 @@ int main(int argc, char **argv) buffer1->unlock(); buffer2->unlock(); timing_helper(timing_name, timing_time, time, "extract_buffer"); - + // spectrum spectrumAnalyser->process(x); timing_helper(timing_name, timing_time, time, "spectrum"); - + // clutter filter if (!filter->process(x, y)) { continue; } timing_helper(timing_name, timing_time, time, "clutter_filter"); - + // ambiguity process map = ambiguity->process(x, y); map->set_metrics(); timing_helper(timing_name, timing_time, time, "ambiguity_processing"); - + // detection process detection1 = cfarDetector1D->process(map); detection2 = centroid->process(detection1); detection = interpolate->process(detection2, map); timing_helper(timing_name, timing_time, time, "detector"); + // tracker process + // track = tracker->process(detection, time[0]/1000); + // timing_helper(timing_name, timing_time, time, "tracker"); + // output IqData meta data jsonIqData = x->to_json(time[0]/1000); for (int i = 0; i < (jsonIqData.size() + MTU - 1) / MTU; i++) @@ -269,7 +291,6 @@ int main(int argc, char **argv) subdata = mapJson.substr(i * MTU, MTU); socket_map.write_some(asio::buffer(subdata, subdata.size()), err); } - // output detection data detectionJson = detection->to_json(time[0]/1000); detectionJson = detection->delay_bin_to_km(detectionJson, fs); @@ -281,7 +302,15 @@ int main(int argc, char **argv) delete detection; delete detection1; delete detection2; - + + // output tracker data + // jsonTracker = track->to_json(time[0]/1000); + // for (int i = 0; i < (jsonTracker.size() + MTU - 1) / MTU; i++) + // { + // subdata = jsonTracker.substr(i * MTU, MTU); + // socket_track.write_some(asio::buffer(subdata, subdata.size()), err); + // } + // output radar data timer timing_helper(timing_name, timing_time, time, "output_radar_data"); diff --git a/src/data/Detection.cpp b/src/data/Detection.cpp index 4b1d253..d49a751 100644 --- a/src/data/Detection.cpp +++ b/src/data/Detection.cpp @@ -16,6 +16,13 @@ Detection::Detection(std::vector _delay, std::vector _doppler, s snr = _snr; } +Detection::Detection(double _delay, double _doppler, double _snr) +{ + delay.push_back(_delay); + doppler.push_back(_doppler); + snr.push_back(_snr); +} + std::vector Detection::get_delay() { return delay; diff --git a/src/data/Detection.h b/src/data/Detection.h index efbdebc..4e7173c 100644 --- a/src/data/Detection.h +++ b/src/data/Detection.h @@ -29,6 +29,12 @@ public: /// @return The object. Detection(std::vector delay, std::vector doppler, std::vector snr); + /// @brief Constructor for single detection. + /// @param delay Detection in delay (bins). + /// @param doppler Detection in Doppler (Hz). + /// @return The object. + Detection(double delay, double doppler, double snr); + /// @brief Get detections in delay. /// @return Detections in delay (bins). std::vector get_delay(); @@ -46,6 +52,7 @@ public: size_t get_nDetections(); /// @brief Generate JSON of the detections and metadata. + /// @param timestamp Current time (POSIX ms). /// @return JSON string. std::string to_json(uint64_t timestamp); diff --git a/src/data/Track.cpp b/src/data/Track.cpp new file mode 100644 index 0000000..5cd7585 --- /dev/null +++ b/src/data/Track.cpp @@ -0,0 +1,163 @@ +#include "Track.h" +#include +#include +#include +#include + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" + +const uint64_t Track::MAX_INDEX = 65535; +const std::string Track::STATE_ACTIVE = "ACTIVE"; +const std::string Track::STATE_TENTATIVE = "TENTATIVE"; +const std::string Track::STATE_COASTING = "COASTING"; + +// constructor +Track::Track() +{ + iNext = 0; +} + +Track::~Track() +{ +} + +std::string Track::uint2hex(uint64_t number) +{ + std::ostringstream oss; + oss << std::setw(4) << std::setfill('0') << std::uppercase << std::hex << number; + return oss.str(); +} + +void Track::set_state(uint64_t index, std::string _state) +{ + state.at(index) = _state; +} + +void Track::set_current(uint64_t index, Detection smoothed) +{ + current.at(index) = smoothed; + associated.at(index).push_back(smoothed); +} + +void Track::set_acceleration(uint64_t index, double _acceleration) +{ + acceleration.at(index) = _acceleration; +} + +void Track::set_nInactive(uint64_t index, uint64_t n) +{ + nInactive.at(index) = n; +} + +uint64_t Track::get_nActive() +{ + uint64_t n = 0; + for (size_t i = 0; i < id.size(); i++) + { + if (state.at(i) == STATE_ACTIVE) + { + n++; + } + } + return n; +} + +uint64_t Track::get_nTentative() +{ + uint64_t n = 0; + for (size_t i = 0; i < id.size(); i++) + { + if (state.at(i) == STATE_TENTATIVE) + { + n++; + } + } + return n; +} + +uint64_t Track::get_n() +{ + return id.size(); +} + +Detection Track::get_current(uint64_t index) +{ + return current.at(index); +} + +double Track::get_acceleration(uint64_t index) +{ + return acceleration.at(index); +} + +std::string Track::get_state(uint64_t index) +{ + return state.at(index); +} + +uint64_t Track::get_nInactive(uint64_t index) +{ + return nInactive.at(index); +} + +uint64_t Track::add(Detection initial) +{ + id.push_back(uint2hex(iNext)); + state.push_back(STATE_TENTATIVE); + current.push_back(initial); + acceleration.push_back(0); + std::vector _associated; + _associated.push_back(initial); + associated.push_back(_associated); + nInactive.push_back(0); + iNext++; + if (iNext >= MAX_INDEX) + { + iNext = 0; + } + return id.size()-1; +} + +void Track::remove(uint64_t index) +{ + id.erase(id.begin() + index); + state.erase(state.begin() + index); + current.erase(current.begin() + index); + acceleration.erase(acceleration.begin() + index); + associated.erase(associated.begin() + index); +} + +std::string Track::to_json(uint64_t timestamp) +{ + rapidjson::Document document; + document.SetObject(); + rapidjson::Document::AllocatorType &allocator = document.GetAllocator(); + + // store array for non-tentative tracks + rapidjson::Value arrayId(rapidjson::kArrayType); + rapidjson::Value value; + for (int i = 0; i < get_n(); i++) + { + if (state.at(i) != STATE_TENTATIVE) + { + value = rapidjson::StringRef(id.at(i).c_str()); + arrayId.PushBack(value, allocator); + } + } + + document.AddMember("timestamp", timestamp, allocator); + document.AddMember("n", get_n(), allocator); + document.AddMember("nActive", get_nActive(), allocator); + document.AddMember("nTentative", get_nTentative(), allocator); + document.AddMember("id", arrayId, allocator); + + rapidjson::StringBuffer strbuf; + rapidjson::Writer writer(strbuf); + writer.SetMaxDecimalPlaces(2); + document.Accept(writer); + + return strbuf.GetString(); +} \ No newline at end of file diff --git a/src/data/Track.h b/src/data/Track.h new file mode 100644 index 0000000..d6d0b8b --- /dev/null +++ b/src/data/Track.h @@ -0,0 +1,154 @@ +/// @file Track.h +/// @class Track +/// @brief A class to store track data. +/// @details The ID is 4 digit hexadecimal with 16^4 = 65536 combinations. +/// @details The state can be TENTATIVE, ACTIVE or COASTING. +/// @details Associated detections use null detections when no updates. +/// @author 30hours + +#ifndef TRACK_H +#define TRACK_H + +#include +#include +#include +#include +#include + +class Track +{ +private: + /// @brief Track ID (4 digit alpha-numeric). + //std::unique_ptr> id; + std::vector id; + + /// @brief Current state (see VALID_STATE). + std::vector state; + + /// @brief Curent track position. + std::vector current; + + /// @brief Current acceleration (Hz/s). + std::vector acceleration; + + /// @brief Associated detections in track. + std::vector> associated; + + /// @brief Number of updates the track has been tentative/coasting. + /// @details Forms criteria for track deletion. + std::vector nInactive; + + /// @brief Next valid track index. + uint64_t iNext; + + /// @brief Maximum integer index to wrap around. + static const uint64_t MAX_INDEX; + + /// @brief String for state ACTIVE. + static const std::string STATE_ACTIVE; + + /// @brief String for state TENTATIVE. + static const std::string STATE_TENTATIVE; + + /// @brief String for state COASTING. + static const std::string STATE_COASTING; + +public: + /// @brief Constructor. + /// @return The object. + Track(); + + /// @brief Destructor. + /// @return Void. + ~Track(); + + /// @brief Convert an unsigned int to hexadecimal. + /// @details Max number is 16^4 = 65536 before wrap around. + /// @param number Number to convert to hexadecimal. + /// @return hex Hexadecimal number. + std::string uint2hex(uint64_t number); + + /// @brief Set the state of a track. + /// @param index Index of track to change. + /// @param state Updated state. + /// @return Void. + void set_state(uint64_t index, std::string state); + + /// @brief Set the current track position. + /// @details Use to update smoothed current position. + /// @param index Index of track to change. + /// @param smoothed Updated state. + /// @return Void. + void set_current(uint64_t index, Detection smoothed); + + /// @brief Set the current acceleration. + /// @param index Index of track to change. + /// @param acceleration Updated acceleration. + /// @return Void. + void set_acceleration(uint64_t index, double acceleration); + + /// @brief Set the current inactivity. + /// @param index Index of track to change. + /// @param n Updated inactivity index. + /// @return Void. + void set_nInactive(uint64_t index, uint64_t n); + + /// @brief Get number of active tracks. + /// @return Number of active tracks. + uint64_t get_nActive(); + + /// @brief Get number of tentative tracks. + /// @return Number of tentative tracks. + uint64_t get_nTentative(); + + /// @brief Get number of total tracks. + /// @return Number of total tracks. + uint64_t get_n(); + + /// @brief Get current track position for track index. + /// @return Current detection. + Detection get_current(uint64_t index); + + /// @brief Get current acceleration for track index. + /// @return Current acceleration (Hz/s). + double get_acceleration(uint64_t index); + + /// @brief Get current state for track index. + /// @return Current state. + std::string get_state(uint64_t index); + + /// @brief Get number of updates track has been tentative/coasting. + /// @return Number of updates track has been tentative/coasting. + uint64_t get_nInactive(uint64_t index); + + /// @brief Update an associated detection. + /// @param index Index of track to change. + /// @param update New associated detection. + /// @return Void. + void update(uint64_t index, Detection update); + + /// @brief Add track to the track set. + /// @param initial Initial Detection. + /// @details ID is incremented automatically. + /// @details Initial state is always TENTATIVE. + /// @return Index of last track. + uint64_t add(Detection initial); + + /// @brief Remove track based on index. + /// @param index Index of track to remove. + /// @return Void. + void remove(uint64_t index); + + /// @brief Generate JSON of the map and metadata. + /// @param timestamp Current time (POSIX ms). + /// @return JSON string. + std::string to_json(uint64_t timestamp); + + /// @brief Append the map to a save file. + /// @param json JSON string of map and metadata. + /// @param path Path of file to save. + /// @return True is save is successful. + bool save(std::string json, std::string path); +}; + +#endif diff --git a/src/process/tracker/Tracker.cpp b/src/process/tracker/Tracker.cpp new file mode 100644 index 0000000..8fae686 --- /dev/null +++ b/src/process/tracker/Tracker.cpp @@ -0,0 +1,135 @@ +#include "Tracker.h" +#include + +// constructor +Tracker::Tracker(uint32_t _m, uint32_t _n, uint32_t _nDelete, double _cpi, double _maxAccInit) +{ + m = _m; + n = _n; + nDelete = _nDelete; + cpi = _cpi; + maxAccInit = _maxAccInit; + timestamp = 0; + + double resolutionAcc = 1 * (1/(cpi*cpi)); + uint16_t nAcc = (int)maxAccInit/resolutionAcc; + for (int i = 0; i < 2*nAcc+1; i++) + { + accInit.push_back(resolutionAcc*(i-nAcc)); + } + + Track track{}; +} + +Tracker::~Tracker() +{ +} + +Track *Tracker::process(Detection *detection, uint64_t currentTime) +{ + timestamp = currentTime; + doNotInitiate.clear(); + for (size_t i = 0; i < detection->get_nDetections(); i++) + { + doNotInitiate.push_back(false); + } + + if (track.get_n() > 0) + { + update(detection, timestamp); + } + initiate(detection); + + return &track; +} + +void Tracker::update(Detection *detection, uint64_t current) +{ + std::vector delay = detection->get_delay(); + std::vector doppler = detection->get_doppler(); + std::vector snr = detection->get_snr(); + + // init + double delayPredict, dopplerPredict; + double acc; + uint32_t nRemove = 0; + std::string state; + + // get time between detections + double T = (double) current - timestamp; + + // loop over each track + for (int i = 0; i < track.get_n(); i++) + { + // predict next position + Detection detectionCurrent = track.get_current(i); + double delayTrack = detectionCurrent.get_delay().front(); + double dopplerTrack = detectionCurrent.get_doppler().front(); + acc = track.get_acceleration(i); + delayPredict = delayTrack+(1/(2*3.14))*(dopplerTrack+0.5*acc*T*T); + dopplerPredict = dopplerTrack+acc*T; + Detection prediction(delayPredict, dopplerPredict, 0); + + // loop over detections to associate + for (size_t j = 0; j < detection->get_nDetections(); j++) + { + // associate detections + if (delay[j] > delayPredict-1 && + delay[j] < delayPredict+1 && + doppler[j] > dopplerPredict-(2/cpi) && + doppler[j] < dopplerPredict+(2/cpi)) + { + Detection associated(delay[j], doppler[j], snr[j]); + track.set_current(i, associated); + state = "ACTIVE"; + track.set_state(i, state); + track.set_acceleration(i, (doppler[j]-dopplerTrack)/T); + track.set_nInactive(i, 0); + doNotInitiate[j] = true; + break; + // todo: check for track promotion + } + } + + // update state if no detections associated + track.set_current(i, prediction); + if (track.get_state(i) == "ACTIVE") + { + state = "COASTING"; + track.set_state(i, state); + } + track.set_nInactive(i, track.get_nInactive(i)+1); + + // remove if tentative or coasting too long + if (track.get_nInactive(i) > nDelete) + { + track.remove(i+nRemove); + nRemove++; + } + } +} + +void Tracker::initiate(Detection *detection) +{ + std::vector delay = detection->get_delay(); + std::vector doppler = detection->get_doppler(); + std::vector snr = detection->get_snr(); + uint64_t index; + + // loop over new detections + for (size_t i = 0; i < detection->get_nDetections(); i++) + { + // skip if detection used in update + if (doNotInitiate.at(i)) + { + continue; + } + // add tentative detection for each acc + for (size_t j = 0; j < accInit.size(); j++) + { + Detection detectionCurrent(delay[i], doppler[i], snr[i]); + index = track.add(detectionCurrent); + track.set_acceleration(index, accInit[j]); + } + } +} \ No newline at end of file diff --git a/src/process/tracker/Tracker.h b/src/process/tracker/Tracker.h new file mode 100644 index 0000000..4a10353 --- /dev/null +++ b/src/process/tracker/Tracker.h @@ -0,0 +1,77 @@ +/// @file Tracker.h +/// @class Tracker +/// @brief A class to implement a bistatic tracker. +/// @details Key functions are update, initiate, smooth and remove. +/// @details Update before initiate to avoid duplicate tracks. +/// @author 30hours +/// @todo Add smoothing capability. +/// @todo Fix units up. + +#ifndef TRACKER_H +#define TRACKER_H + +#include +#include +#include + +class Tracker +{ +private: + /// @brief Track initiation constant for M of N detections. + uint32_t m; + + /// @brief Track initiation constant for M of N detections. + uint32_t n; + + /// @brief Number of missed predictions to delete a tentative track. + uint32_t nDelete; + + /// @brief True CPI time for acceleration resolution(s). + double cpi; + + /// @brief Maximum acceleration to initiate track (Hz/s). + double maxAccInit; + + /// @brief Acceleration values to initiate track (Hz/s). + std::vector accInit; + + /// @brief Index of detections already updated. + std::vector doNotInitiate; + + /// @brief POSIX timestamp of last update (ms). + uint64_t timestamp; + + /// @brief Track data. + Track track; + +public: + /// @brief Constructor. + /// @param delayMin Minimum clutter filter delay (bins). + /// @param delayMax Maximum clutter filter delay (bins). + /// @param nSamples Number of samples per CPI. + /// @return The object. + Tracker(uint32_t m, uint32_t n, uint32_t nDelete, double cpi, double maxAccInit); + + /// @brief Destructor. + /// @return Void. + ~Tracker(); + + /// @brief Run through key functions of tracker. + /// @param detection Detection data for last CPI. + /// @param timestamp POSIX timestamp (ms). + /// @return Pointer to track data. + Track *process(Detection *detection, uint64_t timestamp); + + /// @brief Update tracks by associating detections. + /// @param detection Detection data for last CPI. + /// @param timestamp POSIX timestamp (ms). + /// @return Void. + void update(Detection *detection, uint64_t timestamp); + + /// @brief Initiate new tentative tracks from detections. + /// @param detection Detection data for last CPI. + /// @return Void. + void initiate(Detection *detection); +}; + +#endif \ No newline at end of file