diff --git a/src/data/Track.cpp b/src/data/Track.cpp index 41cd8836..ae96a1d8 100644 --- a/src/data/Track.cpp +++ b/src/data/Track.cpp @@ -11,6 +11,7 @@ #include "rapidjson/filewritestream.h" const uint64_t Track::MAX_INDEX = 65535; +const uint64_t Track::MAX_HISTORY = 100; const std::string Track::STATE_ACTIVE = "ACTIVE"; const std::string Track::STATE_TENTATIVE = "TENTATIVE"; const std::string Track::STATE_COASTING = "COASTING"; @@ -36,12 +37,21 @@ std::string Track::uint2hex(uint64_t number) void Track::set_state(uint64_t index, std::string _state) { state.at(index).push_back(_state); + if (state.at(index).size() > MAX_HISTORY) + { + state.at(index).erase(state.at(index).begin()); + } } void Track::set_current(uint64_t index, Detection smoothed) { current.at(index) = smoothed; associated.at(index).push_back(smoothed); + nAssociated.at(index)++; + if (associated.at(index).size() > MAX_HISTORY) + { + associated.at(index).erase(associated.at(index).begin()); + } } void Track::set_acceleration(uint64_t index, double _acceleration) @@ -92,6 +102,11 @@ uint64_t Track::get_nInactive(uint64_t index) return nInactive.at(index); } +uint64_t Track::get_nAssociated(uint64_t index) +{ + return nAssociated.at(index); +} + uint64_t Track::add(Detection initial) { id.push_back(uint2hex(iNext)); @@ -103,6 +118,7 @@ uint64_t Track::add(Detection initial) std::vector _associated; _associated.push_back(initial); associated.push_back(_associated); + nAssociated.push_back(1); nInactive.push_back(0); iNext++; if (iNext >= MAX_INDEX) @@ -167,6 +183,12 @@ void Track::remove(uint64_t index) } else { throw std::out_of_range("Index out of bounds for 'associated' vector"); } + + if (index < nAssociated.size()) { + nAssociated.erase(nAssociated.begin() + index); + } else { + throw std::out_of_range("Index out of bounds for 'nAssociated' vector"); + } } std::string Track::to_json(uint64_t timestamp) @@ -195,7 +217,7 @@ std::string Track::to_json(uint64_t timestamp) document.GetAllocator()); object1.AddMember("acceleration", acceleration.at(i), document.GetAllocator()); - object1.AddMember("n", associated.at(i).size(), + object1.AddMember("n", get_nAssociated(i), document.GetAllocator()); rapidjson::Value associatedDelay(rapidjson::kArrayType); rapidjson::Value associatedDoppler(rapidjson::kArrayType); @@ -233,4 +255,4 @@ std::string Track::to_json(uint64_t timestamp) document.Accept(writer); return strbuf.GetString(); -} \ No newline at end of file +} diff --git a/src/data/Track.h b/src/data/Track.h index e17d23a0..2224323c 100644 --- a/src/data/Track.h +++ b/src/data/Track.h @@ -28,7 +28,7 @@ class Track /// @brief Track ID (4 digit alpha-numeric). std::vector id; - /// @brief State history for each track. + /// @brief State history for each track, most recent MAX_HISTORY entries only. std::vector> state; /// @brief Curent track position. @@ -37,9 +37,12 @@ class Track /// @brief Current acceleration (Hz/s). std::vector acceleration; - /// @brief Associated detections in track. + /// @brief Associated detections in track, most recent MAX_HISTORY entries only. std::vector> associated; + /// @brief Total number of detections ever associated with each track. + std::vector nAssociated; + /// @brief Number of updates the track has been tentative/coasting. /// @details Forms criteria for track deletion. std::vector nInactive; @@ -50,6 +53,9 @@ class Track /// @brief Maximum integer index to wrap around. static const uint64_t MAX_INDEX; + /// @brief Maximum retained state and associated detection history. + static const uint64_t MAX_HISTORY; + /// @brief String for state ACTIVE. static const std::string STATE_ACTIVE; @@ -127,6 +133,10 @@ class Track /// @return Number of updates track has been tentative/coasting. uint64_t get_nInactive(uint64_t index); + /// @brief Get total number of detections ever associated with a track. + /// @return Total number of associated detections. + uint64_t get_nAssociated(uint64_t index); + /// @brief Update an associated detection. /// @param index Index of track to change. /// @param update New associated detection. diff --git a/test/unit/process/tracker/TestTracker.cpp b/test/unit/process/tracker/TestTracker.cpp index 13c27d95..06f060a9 100644 --- a/test/unit/process/tracker/TestTracker.cpp +++ b/test/unit/process/tracker/TestTracker.cpp @@ -10,6 +10,8 @@ #include "process/tracker/Tracker.h" #include "data/meta/Constants.h" +#include "rapidjson/document.h" + #include #include #include @@ -81,4 +83,31 @@ TEST_CASE("Test predict", "[predict]") Catch::Matchers::WithinAbs(prediction_truth.get_delay().front(), 0.01)); CHECK_THAT(prediction.get_doppler().front(), Catch::Matchers::WithinAbs(prediction_truth.get_doppler().front(), 0.01)); -} \ No newline at end of file +} + +/// @brief Test that long-lived track histories remain bounded. +TEST_CASE("Track history is bounded for a long-lived track", "[track]") +{ + Track track; + Detection initial(10, -20, 0); + uint64_t index = track.add(initial); + + uint32_t nCycles = 500; + for (uint32_t i = 0; i < nCycles; i++) + { + track.set_current(index, initial); + track.set_state(index, "ASSOCIATED"); + } + + CHECK(track.get_nAssociated(index) == nCycles + 1); + + std::string json = track.to_json(0); + rapidjson::Document doc; + doc.Parse(json.c_str()); + const rapidjson::Value& data = doc["data"][0]; + + CHECK(data["n"].GetUint64() == nCycles + 1); + CHECK(data["associated_delay"].Size() <= 100); + CHECK(data["associated_doppler"].Size() <= 100); + CHECK(data["associated_state"].Size() <= 100); +}