diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp index 76fc676..973435e 100644 --- a/apps/ccam/ccam.cpp +++ b/apps/ccam/ccam.cpp @@ -1,116 +1,71 @@ //===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===// // // The RoSA Framework -- Application CCAM // //===----------------------------------------------------------------------===// /// /// \file apps/ccam/ccam.cpp /// /// \author Maximilian Goetzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief The application CCAM implements the case study from the paper: /// M. Goetzinger, N. TaheriNejad, H. A. Kholerdi, A. Jantsch, E. Willegger, /// T. Glatzl, A.M. Rahmani, T.Sauter, P. Liljeberg: Model - Free Condition /// Monitoring with Confidence //===----------------------------------------------------------------------===// #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/SignalStateDetector.hpp" #include using namespace rosa::agent; int main(void) { - // Just some tests :D - std::vector vec = {7, 3, 5, 1, 9}; + /* +// Just some tests :D +std::vector vec = {7, 3, 5, 1, 9}; - std::sort(vec.rbegin(), vec.rend()); - // std::reverse(vec.begin(), vec.end()); +std::sort(vec.rbegin(), vec.rend()); +// std::reverse(vec.begin(), vec.end()); - for (auto it = vec.cbegin(); it != vec.cend(); ++it) { - std::cout << *it << ' '; - } +for (auto it = vec.cbegin(); it != vec.cend(); ++it) { + std::cout << *it << ' '; +} + */ std::shared_ptr> PartFunc( new PartialFunction( { {{0.f, 3.f}, std::make_shared>(0.f, 1.f / 3)}, {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, {{6.f, 9.f}, std::make_shared>(3.f, -1.f / 3)}, }, 0)); std::shared_ptr> StepFunc( new StepFunction(1 / 10)); SignalStateDetector TestSigSD( 10000, PartFunc, PartFunc, StepFunc, StepFunc, PartFunc, PartFunc, 10, 5, 1000); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(50.3f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); - TestSigSD.detectSignalState(100.6f); + unsigned int i; + + for (i = 1; i <= 30; i++) { + TestSigSD.detectSignalState(50.3f); + // std::cout << "test"; + } + + for (; i <= 60; i++) { + TestSigSD.detectSignalState(100.6f); + } return 0; } diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp index f8d66d1..4b834c2 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,548 +1,579 @@ //===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/History.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *history* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_HISTORY_HPP #define ROSA_AGENT_HISTORY_HPP #include "rosa/agent/Functionality.h" #include "rosa/config/config.h" #include "rosa/support/debug.hpp" #include "rosa/support/type_helper.hpp" #include +#include #include namespace rosa { namespace agent { /// Retention policies defining what a \c rosa::agent::History instance should /// do when the number of recorded entries reached its capacity. enum class HistoryPolicy { SRWF, ///< Stop Recording When Full -- no new entry is recorded when full FIFO, ///< First In First Out -- overwrite the earliest entry with a new one LIFO ///< Last In First Out -- overwrite the latest entry with a new one }; template class History : public Functionality { public: History(void) noexcept {} /// Destroys \p this object. virtual ~History(void) = default; /// Tells the retention policy applied to \p this object. /// /// \return \c rosa::agent::History::P static constexpr HistoryPolicy policy(void) noexcept { return P; } /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// /// \return The max number of entries that may be recorded virtual size_t maxLength(void) const noexcept = 0; /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode virtual size_t numberOfEntries(void) const noexcept = 0; /// Tells if \p this object has not recorded anything yet. /// /// \return if \p this object has no entries recorded bool empty(void) const noexcept { return numberOfEntries() == 0; } /// Tells if the history reached it's maximum length /// /// \return if the history reached it's maximum length. bool full(void) const noexcept { return numberOfEntries() == maxLength(); } /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code /// 0 <= I && I < numberOfEntries() /// \endcode virtual const T &entry(const size_t I = 0) const noexcept = 0; /// Removes all entries recorded in \p this object. virtual void clear() noexcept = 0; private: /// Pushes a new entry into the history. /// /// \note The earliest entry gets overwritten if the history is full. /// /// \param V value to push into the history virtual void pushBack(const T &V) noexcept = 0; /// Replaces the most recent entry in the history. /// /// \param V value to replace the most current value with virtual void replaceFront(const T &V) noexcept = 0; public: /// Adds a new entry to \p this object and tells if the operation was /// successful. /// /// \note Success of the operation depends on the actual policy. /// /// \param V value to store /// /// \return if \p V was successfully stored bool addEntry(const T &V) noexcept { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); case HistoryPolicy::LIFO: if (full()) { replaceFront(V); return true; } case HistoryPolicy::SRWF: if (full()) { return false; } // \note Fall through to FIFO which unconditionally pushes the new entry. case HistoryPolicy::FIFO: // FIFO and SRWF not full. pushBack(V); return true; } } /// Tells the trend set by the entries recorded by \p this object. /// /// The number of steps to go back when calculating the trend is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \return trend set by analyzed entries /// /// \pre Statically, \p this object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if< std::is_arithmetic::value && std::is_signed::value, X>::type trend(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No entries for computing trend. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); size_t I = S; // Compute trend with linear regression. size_t SumIndices = 0; T SumEntries = {}; T SumSquareEntries = {}; T SumProduct = {}; while (I > 0) { // \note Indexing for the regression starts in the past. const size_t Index = S - I; const T Entry = entry(--I); SumIndices += Index; SumEntries += Entry; SumSquareEntries += Entry * Entry; SumProduct += Entry * Index; } return (SumProduct * S - SumEntries * SumIndices) / (SumSquareEntries * S - SumEntries * SumEntries); } } /// Tells the average absolute difference between consecutive entries recorded /// by \p this object /// The number of steps to go back when calculating the average is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, \p this object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if::value, size_t>::type averageAbsDiff(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No difference to average. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); // Sum up differences as non-negative values only, hence using an // unsigned variable for that. size_t Diffs = {}; // Init to zero. // Count down entry indices and sum up all the absolute differences. size_t I = S; T Last = entry(I); while (I > 0) { T Next = entry(--I); Diffs += Last < Next ? Next - Last : Last - Next; Last = Next; } // Return the average of the summed differences. return Diffs / S; } } /// Tells the average of all entries recorded by \p this object /// /// \tparam R type of the result template R average() const noexcept { R Average = 0; for (size_t I = 0; I < numberOfEntries(); I++) { Average += entry(I); } Average /= numberOfEntries(); return Average; } }; /// Implements *history* by recording and storing values. /// The length of the underlying std::array is static and must be set at /// compile-time /// /// \note Not thread-safe implementation, which should not be a problem as any /// instance of \c rosa::agent::Functionality is an internal component of a /// \c rosa::Agent, which is the basic unit of concurrency. /// /// \tparam T type of values to store /// \tparam N number of values to store at most /// \tparam P retention policy to follow when capacity is reached /// /// \invariant The size of the underlying \c std::array is `N + 1`:\code /// max_size() == N + 1 && N == max_size() - 1 /// \endcode template class StaticLengthHistory : public History, private std::array { // Bring into scope inherited functions that are used. using std::array::max_size; using std::array::operator[]; /// The index of the first data element in the circular buffer. size_t Data; /// The index of the first empty slot in the circular buffer. size_t Space; public: using History::policy; using History::empty; using History::full; using History::addEntry; using History::trend; using History::averageAbsDiff; /// Creates an instances by initializing the indices for the circular buffer. StaticLengthHistory(void) noexcept : Data(0), Space(0) {} /// Destroys \p this object. ~StaticLengthHistory(void) override = default; /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// /// \return \c rosa::agent::History::N size_t maxLength(void) const noexcept override { return N; } /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode size_t numberOfEntries(void) const noexcept override { return Data <= Space ? Space - Data : max_size() - Data + Space; } /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code /// 0 <= I && I < numberOfEntries() /// \endcode const T &entry(const size_t I = 0) const noexcept override { ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. // Position counted back from the last recorded entry. typename std::make_signed::type Pos = Space - (1 + I); // Actual index wrapped around to the end of the buffer if negative. return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; } /// Removes all entries recorded in \p this object. void clear() noexcept override { Data = 0; Space = 0; } private: /// Pushes a new entry into the circular buffer. /// /// \note The earliest entry gets overwritten if the buffer is full. /// /// \param V value to push into the buffer void pushBack(const T &V) noexcept override { // Store value to the first empty slot and step Space index. (*this)[Space] = V; Space = (Space + 1) % max_size(); if (Data == Space) { // Buffer was full, step Data index. Data = (Data + 1) % max_size(); } } /// Replaces the most recent entry in the history. /// /// \param V value to replace the most current value with void replaceFront(const T &V) noexcept override { (*this)[(Space - 1) % max_size()] = V; } }; /// Adds a new entry to a \c rosa::agent::History instance. /// /// \note The result of \c rosa::agent::History::addEntry is ignored. /// /// \tparam T type of values stored in \p H /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p V to it template StaticLengthHistory &operator<<(StaticLengthHistory &H, const T &V) noexcept { H.addEntry(V); return H; } /// Implements *DynamicLengthHistory* by recording and storing values. /// /// \note Not thread-safe implementation, which should not be a problem as any /// instance of \c rosa::agent::Functionality is an internal component of a /// \c rosa::Agent, which is the basic unit of concurrency. /// /// \tparam T type of values to store /// \tparam P retention policy to follow when capacity is reached template class DynamicLengthHistory : public History, private std::vector { //@benedikt: if i dont make these public, I cannot iterate from outside // through the history. E.g., "for (auto &SavedSignalState : // DetectedSignalStates)" at line ~297 in "SignalStateDetector.hpp". Do you // have an idea to make this in a better/more beautiful way? public: // Bring into scope inherited functions that are used. using std::vector::erase; using std::vector::begin; using std::vector::end; using std::vector::rbegin; using std::vector::rend; using std::vector::size; using std::vector::max_size; using std::vector::resize; using std::vector::push_back; using std::vector::pop_back; using std::vector::operator[]; /// The current length of the DynamicLengthHistory. size_t Length; public: using History::policy; using History::empty; using History::full; using History::addEntry; using History::trend; using History::averageAbsDiff; /// Creates an instances by setting an initial length DynamicLengthHistory(size_t Length) noexcept : Length(Length) { this->resize(Length); } /// Destroys \p this object. ~DynamicLengthHistory(void) override = default; /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// /// \return \c rosa::agent::DynamicLengthHistory::N size_t maxLength(void) const noexcept override { return Length; } /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= /// lengthOfHistory() \endcode size_t numberOfEntries(void) const noexcept { return size(); } /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code /// 0 <= I && I < numberOfEntries() /// \endcode const T &entry(const size_t I = 0) const noexcept override { ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. return this->operator[](size() - I - 1); } /// Removes all entries recorded in \p this object. void clear() noexcept override { erase(begin(), end()); } /// Sort all entries in ascending order. void sortAscending(void) noexcept { std::sort(begin(), end()); } /// Sort all entries in descending order. void sortDescending(void) noexcept { std::sort(rbegin(), rend()); } /// Delets one element of the history. /// /// \param the element which shall be deleted. // @benedikt: is this ok like that? should there be some "error handling"? // checking if V is not null, or if V is member of the vector? void deleteEntry(T &V) { erase(std::find(begin(), end(), V)); } + /// Gives back the lowest entry of the history. + /// + /// \return the lowest entry. In case of an empty history, the maximum value + /// of the chosen data type is returned. + //@benedikt: please check if it is right + T lowestEntry() { + auto it = std::min_element(begin(), end()); + + if (it == end()) { + return std::numeric_limits::max(); + } else { + return *it; + } + } + + /// Gives back the highest entry of the history. + /// + /// \return the highest entry. In case of an empty history, the minimum value + /// of the chosen data type is returned. + //@benedikt: please check if it is right + T highestEntry() { + auto it = std::max_element(begin(), end()); + + if (it == end()) { + return std::numeric_limits::min(); + } else { + return *it; + } + } + private: /// Pushes a new entry into the circular buffer. /// /// \note The earliest entry gets overwritten if the buffer is full. /// /// \param V value to push into the buffer void pushBack(const T &V) noexcept override { if (full()) { erase(begin()); } push_back(V); } /// Replaces the most recent entry in the history. /// /// \param V value to replace the most current value with void replaceFront(const T &V) noexcept override { (void)pop_back(); push_back(V); } public: /// Resizes the History length. If the new length is smaller than the number /// of currently stored values, values are deleted according to the /// HistoryPolicy. /// /// @param NewLength The new Length of the History. void setLength(size_t NewLength) noexcept { Length = NewLength; if (NewLength < numberOfEntries()) { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); case HistoryPolicy::LIFO: case HistoryPolicy::SRWF: // Delete last numberOfEntries() - NewLength items from the back erase(begin() + NewLength, end()); break; case HistoryPolicy::FIFO: // Delete last numberOfEntries() - NewLength items from the front erase(begin(), begin() + (numberOfEntries() - NewLength)); break; } } this->resize(Length); } }; /// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance. /// /// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is /// ignored. /// /// \tparam T type of values stored in \p H /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p V to it template DynamicLengthHistory &operator<<(DynamicLengthHistory &H, const T &V) noexcept { H.addEntry(V); return H; } } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_HISTORY_HPP diff --git a/include/rosa/agent/SignalState.hpp b/include/rosa/agent/SignalState.hpp index 3a68820..9cde3d9 100644 --- a/include/rosa/agent/SignalState.hpp +++ b/include/rosa/agent/SignalState.hpp @@ -1,380 +1,474 @@ //===-- rosa/agent/SignalState.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/SignalState.hpp /// /// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *signal state* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_SIGNALSTATE_HPP #define ROSA_AGENT_SIGNALSTATE_HPP #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/Functionality.h" #include "rosa/agent/History.hpp" #include "rosa/support/math.hpp" #include namespace rosa { namespace agent { /// Signal state conditions defining how the condition of a \c /// rosa::agent::SignalState is saved in \c rosa::agent::SignalStateInformation. enum class SignalStateCondition { STABLE, ///< The signal state is stable DRIFTING, ///< The signal state is drifting UNKNOWN ///< The signal state is unknown }; template struct SignalStateInformation { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "confidence type is not to arithmetic"); /// The signal state ID saved as an unsigned integer number unsigned int SignalStateID; /// The SignalStateConfidence shows the overall confidence value of the signal /// state. CONFDATATYPE SignalStateConfidence; /// The SignalStateCondition shows the condition of a signal state (stable or /// drifting) SignalStateCondition SignalStateCondition; + /// The SignalStateIsValid saves the number of samples which have been + /// inserted into the state after entering it. + unsigned int NumberOfInsertedSamplesAfterEntrance; /// The SignalStateIsValid shows whether a signal state is valid or invalid. /// In this context, valid means that enough samples which are in close /// proximitry have been inserted into the signal state. bool SignalStateIsValid; /// The SignalStateJustGotValid shows whether a signal state got valid /// (toggled from invalid to valid) during the current inserted sample. bool SignalStateJustGotValid; /// The SignalStateIsValidAfterReentrance shows whether a signal state is /// valid after the variable changed back to it again. bool SignalStateIsValidAfterReentrance; + /// The SignalIsStableNotDrifting shows whether a signa is stable and not + /// drifting. + bool SignalIsStable; }; /// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of /// data in that the confidence values are given, \param PROCDATATYPE type of /// the relative distance and the type of data in which DABs are saved. template class SignalState : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "input data type not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "confidence data type is not to arithmetic"); STATIC_ASSERT( (std::is_arithmetic::value), "process data type (DAB and Relative Distance) is not to arithmetic"); private: // For the convinience to write a shorter data type name using PartFuncPointer = std::shared_ptr>; // @Benedikt: are INDATATYPE, CONFDATATYPE right here? using StepFuncPointer = std::shared_ptr>; /// SignalStateInfo is a struct SignalStateInformation that contains /// information about the current state. SignalStateInformation SignalStateInfo; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// confidence how good the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMatches; /// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the /// confidence how bad the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMismatches; /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. StepFuncPointer FuzzyFunctionNumOfSamplesMatches; /// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives /// the confidence how many samples from the sampe history mismatch the new /// sample. StepFuncPointer FuzzyFunctionNumOfSamplesMismatches; /// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the /// confidence how likely it is that the signal (resp. the state of a signal) /// is drifting. PartFuncPointer FuzzyFunctionSignalIsDrifting; /// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the /// confidence how likely it is that the signal (resp. the state of a signal) /// is stable (not drifting). PartFuncPointer FuzzyFunctionSignalIsStable; /// SampleHistory is a history in that the last sample values are stored. DynamicLengthHistory SampleHistory; /// DAB is a (usually) small history of the last sample values of which a /// average is calculated if the DAB is full. DynamicLengthHistory DAB; /// DABHistory is a history in that the last DABs (to be exact, the averages /// of the last DABs) are stored. DynamicLengthHistory DABHistory; - /// The SignalStateIsValid shows whether a signal state is valid or invalid. - /// In this context, valid means that enough samples which are in close - /// proximitry have been inserted into the signal state. - bool SignalStateIsValid; - /// The SignalStateIsValidAfterReentrance shows whether a signal state is - /// valid after the variable changed back to it again. - bool SignalStateIsValidAfterReentrance; + /// LowestConfidenceMatchingHistory is a history in that the lowest confidence + /// for the current sample matches all history samples are saved. + DynamicLengthHistory + LowestConfidenceMatchingHistory; + /// HighestConfidenceMatchingHistory is a history in that the highest + /// confidence for the current sample matches all history samples are saved. + DynamicLengthHistory + HighestConfidenceMismatchingHistory; public: // @Maxi doxygen per default doesn't display private attributes of a class. So // I copied them to the constructor. So the user has more information. /// Creates an instance by setting all parameters /// \param SignalStateID The Id of the SignalStateinfo \c /// SignalStateInformation. /// /// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the /// fuzzy function that gives the confidence how good the new sample matches /// another sample in the sample history. /// /// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is /// the fuzzy function that gives the confidence how bad the new sample /// matches another sample in the sample history. /// /// \param FuzzyFunctionNumOfSamplesMatches The /// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. /// /// \param FuzzyFunctionNumOfSamplesMismatches The /// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the /// confidence how many samples from the sampe history mismatch the new /// sample. /// /// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is /// the fuzzy function that gives the confidence how likely it is that the /// signal (resp. the state of a signal) is drifting. /// /// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the /// fuzzy function that gives the confidence how likely it is that the signal /// (resp. the state of a signal) is stable (not drifting). /// /// \param SampleHistorySize Size of the Sample History \c /// DynamicLengthHistory . SampleHistory is a history in that the last sample /// values are stored. /// /// \param DABSize Size of DAB \c DynamicLengthHistory . DAB is a (usually) /// small history of the last sample values of which a average is calculated /// if the DAB is full. /// /// \param DABHistorySize Size of the DABHistory \c DynamicLengthHistory . /// DABHistory is a history in that the last DABs (to be exact, the averages /// of the last DABs) are stored. /// SignalState(unsigned int SignalStateID, unsigned int SampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize, PartFuncPointer FuzzyFunctionSampleMatches, PartFuncPointer FuzzyFunctionSampleMismatches, StepFuncPointer FuzzyFunctionNumOfSamplesMatches, StepFuncPointer FuzzyFunctionNumOfSamplesMismatches, PartFuncPointer FuzzyFunctionSignalIsDrifting, PartFuncPointer FuzzyFunctionSignalIsStable) noexcept - : SignalStateInfo{SignalStateID, 0, SignalStateCondition::UNKNOWN, - false, false, false}, - SampleHistory(SampleHistorySize), DAB(DABSize), + : SignalStateInfo{SignalStateID, + 0, + SignalStateCondition::UNKNOWN, + 0, + false, + false, + false}, + SampleHistory(SampleHistorySize), + LowestConfidenceMatchingHistory(SampleHistorySize), + HighestConfidenceMismatchingHistory(SampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), FuzzyFunctionNumOfSamplesMismatches( FuzzyFunctionNumOfSamplesMismatches), FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting), FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable) {} /// Destroys \p this object. ~SignalState(void) = default; void leaveSignalState(void) noexcept { DAB.clear(); - SignalStateIsValidAfterReentrance = false; + SignalStateInfo.NumberOfInsertedSamplesAfterEntrance = 0; + SignalStateInfo.SignalStateIsValidAfterReentrance = false; } SignalStateInformation insertSample(INDATATYPE Sample) noexcept { + + validateSignalState(Sample); + SampleHistory.addEntry(Sample); DAB.addEntry(Sample); if (DAB.full()) { PROCDATATYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } //@Benedikt: Do I really have to cast here? FuzzyFunctionNumOfSamplesMatches->setRightLimit( static_cast(SampleHistory.numberOfEntries())); FuzzyFunctionNumOfSamplesMismatches->setRightLimit( static_cast(SampleHistory.numberOfEntries())); - // TODO: calculate whether signal state is valid and properly set - // SignalStateIsValid, SignalStateJustGotValid, - // SignalStateIsValidAfterReentrance - - // TODO: check current signal state whether it drifts - - // TODO: write in SignalStateInfo + checkSignalStability(); return SignalStateInfo; } /// Gives the confidence how likely the new sample matches the signal state. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the confidence of the new sample is matching the signal state. CONFDATATYPE confidenceSampleMatchesSignalState(INDATATYPE Sample) noexcept { CONFDATATYPE ConfidenceOfBestCase = 0; DynamicLengthHistory RelativeDistanceHistory(SampleHistory.maxLength()); // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { PROCDATATYPE RelativeDistance = relativeDistance(Sample, HistorySample); RelativeDistanceHistory.addEntry(RelativeDistance); } // sort all calculated distances so that the lowest distance (will get the // highest confidence) is at the beginning. RelativeDistanceHistory.sortAscending(); CONFDATATYPE ConfidenceOfWorstFittingSample = 1; // Case 1 means that one (the best fitting) sample of the history is // compared with the new sample. Case 2 means the two best history samples // are compared with the new sample. And so on. // TODO (future): to accelerate -> don't start with 1 start with some higher // number because a low number (i guess lower than 5) will definetely lead // to a low confidence. except the history is not full. for (unsigned int Case = 0; Case < RelativeDistanceHistory.numberOfEntries(); Case++) { CONFDATATYPE ConfidenceFromRelativeDistance; if (std::isinf(RelativeDistanceHistory[Case])) { // TODO (future) if fuzzy is defined in a way that infinity is not 0 it // would be a problem //@benedikt: check if your partialfunctions can take infinity as // argument //@benedikt: same as before "->operator()" ConfidenceFromRelativeDistance = 0; } else { ConfidenceFromRelativeDistance = FuzzyFunctionSampleMatches->operator()( RelativeDistanceHistory[Case]); } ConfidenceOfWorstFittingSample = fuzzyAND( 2, ConfidenceOfWorstFittingSample, ConfidenceFromRelativeDistance); //@benedikt: do i have to pass the number 2 to tell the function how many // arguments are following? //@benedikt: same as before with "->operator()" ConfidenceOfBestCase = fuzzyOR( 2, ConfidenceOfBestCase, fuzzyAND(2, ConfidenceOfWorstFittingSample, FuzzyFunctionNumOfSamplesMatches->operator()( static_cast(Case) + 1))); } return ConfidenceOfBestCase; } /// Gives the confidence how likely the new sample mismatches the signal /// state. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the confidence of the new sample is mismatching the signal state. CONFDATATYPE confidenceSampleMismatchesSignalState(INDATATYPE Sample) noexcept { float ConfidenceOfWorstCase = 1; DynamicLengthHistory RelativeDistanceHistory(SampleHistory.maxLength()); // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { RelativeDistanceHistory.addEntry( relativeDistance(Sample, HistorySample)); } // sort all calculated distances so that the highest distance (will get the // lowest confidence) is at the beginning. RelativeDistanceHistory.sortDescending(); CONFDATATYPE ConfidenceOfBestFittingSample = 0; // Case 1 means that one (the worst fitting) sample of the history is // compared with the new sample. Case 2 means the two worst history samples // are compared with the new sample. And so on. // TODO (future): to accelerate -> don't go until end. Confidences will only // get higher. See comment in "CONFDATATYPE // confidenceSampleMatchesSignalState(INDATATYPE Sample)". for (unsigned int Case = 0; Case < RelativeDistanceHistory.numberOfEntries(); Case++) { CONFDATATYPE ConfidenceFromRelativeDistance; if (std::isinf(RelativeDistanceHistory[Case])) { ConfidenceFromRelativeDistance = 1; } else { //@benedikt: I had to change the following line. The outcommented line // was the original one. I think it is ugly like that (new line). Do you // have an idea how to make it better/more beautiful? ConfidenceFromRelativeDistance = FuzzyFunctionSampleMismatches->operator()( RelativeDistanceHistory[Case]); // FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]); } //@benedikt: do i have to pass the number 2 to tell the function how many // arguments are following? ConfidenceOfBestFittingSample = fuzzyOR( 2, ConfidenceOfBestFittingSample, ConfidenceFromRelativeDistance); //@benedikt: do i have to pass the number 2 to tell the function how many // arguments are following? //@benedikt: same as before with "->operator()" ConfidenceOfWorstCase = fuzzyAND( 2, ConfidenceOfWorstCase, fuzzyOR(2, ConfidenceOfBestFittingSample, FuzzyFunctionNumOfSamplesMismatches->operator()( static_cast(Case) + 1))); } return ConfidenceOfWorstCase; } /// Gives information about the current signal state. /// /// \return a struct SignalStateInformation that contains information about /// the current signal state. SignalStateInformation signalStateInformation(void) noexcept { return SignalStateInfo; } + +private: + void validateSignalState(INDATATYPE Sample) { + // TODO (future): WorstConfidenceDistance and BestConfidenceDistance could + // be set already in "CONFDATATYPE + // confidenceSampleMatchesSignalState(INDATATYPE Sample)" and "CONFDATATYPE + // confidenceSampleMismatchesSignalState(INDATATYPE Sample)" when the new + // sample is compared to all history samples. This would save a lot time + // because the comparisons are done only once. However, it has to be asured + // that the these two functions are called before the insertation, and the + // FuzzyFunctions for validation and matching have to be the same! + CONFDATATYPE LowestConfidenceMatching = 1; + CONFDATATYPE HighestConfidenceMismatching = 0; + for (auto &HistorySample : SampleHistory) { + // TODO (future): think about using different fuzzy functions for + // validation and matching. + //@benedikt: same with "->operator()" + LowestConfidenceMatching = + fuzzyAND(2, LowestConfidenceMatching, + FuzzyFunctionSampleMatches->operator()( + relativeDistance( + Sample, HistorySample))); + //@benedikt: same with "->operator()" + HighestConfidenceMismatching = + fuzzyOR(2, HighestConfidenceMismatching, + FuzzyFunctionSampleMismatches->operator()( + relativeDistance( + Sample, HistorySample))); + } + LowestConfidenceMatchingHistory.addEntry(LowestConfidenceMatching); + HighestConfidenceMismatchingHistory.addEntry(HighestConfidenceMismatching); + + LowestConfidenceMatching = LowestConfidenceMatchingHistory.lowestEntry(); + HighestConfidenceMismatching = + HighestConfidenceMismatchingHistory.highestEntry(); + + //@benedikt: same with "->operator()" + CONFDATATYPE ConfidenceSignalStateIsValid = fuzzyAND( + 2, LowestConfidenceMatching, + FuzzyFunctionNumOfSamplesMatches->operator()(static_cast( + SignalStateInfo.NumberOfInsertedSamplesAfterEntrance))); + //@benedikt: same with "->operator()" + CONFDATATYPE ConfidenceSignalStateIsInvalid = fuzzyOR( + 2, HighestConfidenceMismatching, + FuzzyFunctionNumOfSamplesMismatches->operator()(static_cast( + SignalStateInfo.NumberOfInsertedSamplesAfterEntrance))); + + if (ConfidenceSignalStateIsValid > ConfidenceSignalStateIsInvalid) { + if (SignalStateInfo.SignalStateIsValid) { + SignalStateInfo.SignalStateJustGotValid = false; + } else { + SignalStateInfo.SignalStateJustGotValid = true; + } + SignalStateInfo.SignalStateIsValid = true; + SignalStateInfo.SignalStateIsValidAfterReentrance = true; + } + } + + void checkSignalStability(void) { + CONFDATATYPE ConfidenceSignalIsStable; + CONFDATATYPE ConfidenceSignalIsDrifting; + + if (DABHistory.numberOfEntries() >= 2) { + //@benedikt: same "->operator()" + ConfidenceSignalIsStable = FuzzyFunctionSignalIsStable->operator()( + relativeDistance( + DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0])); + //@benedikt: same "->operator()" + ConfidenceSignalIsDrifting = FuzzyFunctionSignalIsDrifting->operator()( + relativeDistance( + DABHistory[DABHistory.numberOfEntries() - 1], DABHistory[0])); + } else { + // QUESTION: is it ok to say stable = 1 and drift = 0, when I simply don't + // know because the state is so new. Is there an option for saying don't + // know? + ConfidenceSignalIsStable = 1; + ConfidenceSignalIsDrifting = 0; + } + + SignalStateInfo.SignalIsStable = + ConfidenceSignalIsStable >= ConfidenceSignalIsDrifting; + } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SIGNALSTATE_HPP diff --git a/include/rosa/agent/SignalStateDetector.hpp b/include/rosa/agent/SignalStateDetector.hpp index d1ebec5..bd62965 100644 --- a/include/rosa/agent/SignalStateDetector.hpp +++ b/include/rosa/agent/SignalStateDetector.hpp @@ -1,357 +1,285 @@ //===-- rosa/agent/SignalStateDetector.hpp ----------------------------*- C++ //-*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/SignalStateDetector.hpp /// /// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *signal state detector* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_SIGNALSTATEDETECTOR_HPP #define ROSA_AGENT_SIGNALSTATEDETECTOR_HPP #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/Functionality.h" #include "rosa/agent/SignalState.hpp" #include namespace rosa { namespace agent { /// Implements \c rosa::agent::SignalStateDetector as a functionality that /// detects signal states given on input samples. /// /// \note This implementation is supposed to be used for samples of an /// arithmetic type. /// /// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of /// data in that the confidence values are given, \param PROCDATATYPE type of /// the relative distance and the type of data in which DABs are saved. template class SignalStateDetector : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "input data type not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "confidence abstraction type is not to arithmetic"); private: // For the convinience to write a shorter data type name using PartFuncPointer = std::shared_ptr>; using StepFuncPointer = std::shared_ptr>; using SignalStatePtr = std::shared_ptr>; /// The NextSignalStateID is a counter variable which stores the ID which the /// next signal state shall have. unsigned int NextSignalStateID; /// The SignalStateHasChanged is a flag that show whether a signal has changed /// its state. bool SignalStateHasChanged; /// The CurrentSignalState is a pointer to the (saved) signal state in which /// the actual variable (signal) of the observed system is. SignalStatePtr CurrentSignalState; /// The DetectedSignalStates is vector in that all detected signal states are /// saved. DynamicLengthHistory DetectedSignalStates; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// confidence how good the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMatches; /// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the /// confidence how bad the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMismatches; /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. StepFuncPointer FuzzyFunctionNumOfSamplesMatches; /// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives /// the confidence how many samples from the sampe history mismatch the new /// sample. StepFuncPointer FuzzyFunctionNumOfSamplesMismatches; /// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the /// confidence how likely it is that the signal is drifting. PartFuncPointer FuzzyFunctionSignalIsDrifting; /// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the /// confidence how likely it is that the signal is stable (not drifting). PartFuncPointer FuzzyFunctionSignalIsStable; /// SampleHistorySize is the (maximum) size of the sample history. unsigned int SampleHistorySize; /// DABSize the size of a DAB (Discrete Average Block). unsigned int DABSize; /// DABHistorySize is the (maximum) size of the DAB history. unsigned int DABHistorySize; public: /// Creates an instance by setting all parameters /// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the /// fuzzy function that gives the confidence how good the new sample matches /// another sample in the sample history. /// /// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is /// the fuzzy function that gives the confidence how bad the new sample /// matches another sample in the sample history. /// /// \param FuzzyFunctionNumOfSamplesMatches The /// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. /// /// \param FuzzyFunctionNumOfSamplesMismatches The /// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the /// confidence how many samples from the sampe history mismatch the new /// sample. /// /// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is /// the fuzzy function that gives the confidence how likely it is that the /// signal (resp. the state of a signal) is drifting. /// /// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the /// fuzzy function that gives the confidence how likely it is that the signal /// (resp. the state of a signal) is stable (not drifting). /// /// \param SampleHistorySize Sets the History size which will be used by \c /// SignalState. /// /// \param DABSize Sets the DAB size which will be used by \c SignalState. /// /// \param DABHistorySize Sets the size which will be used by \c SignalState. /// SignalStateDetector(unsigned int MaximumNumberOfSignalStates, PartFuncPointer FuzzyFunctionSampleMatches, PartFuncPointer FuzzyFunctionSampleMismatches, StepFuncPointer FuzzyFunctionNumOfSamplesMatches, StepFuncPointer FuzzyFunctionNumOfSamplesMismatches, PartFuncPointer FuzzyFunctionSignalIsDrifting, PartFuncPointer FuzzyFunctionSignalIsStable, unsigned int SampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept : NextSignalStateID(1), SignalStateHasChanged(false), CurrentSignalState(NULL), FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), FuzzyFunctionNumOfSamplesMismatches( FuzzyFunctionNumOfSamplesMismatches), FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting), FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable), SampleHistorySize(SampleHistorySize), DABSize(DABSize), DABHistorySize(DABHistorySize), DetectedSignalStates(MaximumNumberOfSignalStates) {} /// Destroys \p this object. ~SignalStateDetector(void) = default; - /// Detects a signal state to which the new sample belongs or create a new - /// signal state if the new sample does not match to any of the saved signal - /// states. - /// - /// \param Sample is the actual sample of the observed signal. - /// - /// \return the signal state ID as unsigend integer type. Signal state IDs - /// start with number 1; that means if there is no current signal state, the - /// return value is 0. - unsigned int detectSignalState(INDATATYPE Sample) noexcept { - SignalStateInformation SignalStateInfo = - detectSignalState__debug(Sample); - return SignalStateInfo.SignalStateID; - } - - /// Gives information about the current signal state. - /// - /// \return a the signal state ID (as unsigned integer type) of the current - /// signal state. Signal state IDs start with number 1; that means if there is - /// no current signal state, the return value is 0. - unsigned int currentSignalStateInformation(void) noexcept { - SignalStateInformation SignalStateInfo = - currentSignalStateInformation__debug(); - if (SignalStateInfo) { - return SignalStateInfo.SignalStateID; - } else { - return 0; - } - } - - /// Gives information whether a signal state change has happened or not. - /// - /// \return true if a signal state change has happened, and false if not. - bool signalStateHasChanged(void) noexcept { return SignalStateHasChanged; } - -private: - /// Creates a new signal state and adds it to the signal state vector in which - /// all known states are saved. - /// - /// \return a pointer to the newly created signal state or NULL if no state - /// could be created. - SignalStatePtr createNewSignalState(void) noexcept { - SignalStatePtr S(new SignalState( - NextSignalStateID, SampleHistorySize, DABSize, DABHistorySize, - FuzzyFunctionSampleMatches, FuzzyFunctionSampleMismatches, - FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches, - FuzzyFunctionSignalIsDrifting, FuzzyFunctionSignalIsStable)); - - // @benedikt: todo: assert in history, which checks if push_back worked - DetectedSignalStates.addEntry(S); - - return S; - } - -#ifdef SIGNALSTATEDETECTORDEBUGMODE -public: -#else -private: -#endif // SIGNALSTATEDETECTORDEBUGMODE - - // @maxi is this a debug method or is it a method that will be used and - // you simply want to have access to it in debug mode? - // debug -> extend the preprocessor around the function - // access -> remove the __debug from the name ( it is confusing) - // if you want to have it marked as a debug method for auto - // complete you can do something like this : - // - //#ifdef STATEDETECTORDEBUGMODE - // public: - // StateInfoPtr debug_detectState(INDATATYPE Sample) { - // return detectState(Sample); - // } - //#endif // STATEDETECTORDEBUGMODE - // private : - // StateInfoPtr detectState(INDATATYPE Sample) { ... - // /// Detects the signal state to which the new sample belongs or create a new /// signal state if the new sample does not match to any of the saved states. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the information of the current signal state (signal state ID and - /// other - /// parameters). - // TODO: return something const.. cannot remember exactly (ask benedikt) - // - // maybe: you are returning a pointer to the state info so who ever has that - // pointer can actually change the information if you want to return only the - // *current info* return a copy of the state info - // like this: - // - // StateInfoPtr detectState__debug(INDATATYPE Sample) -> - // StateInformation detectState__debug(INDATATYPE Sample) - // - // return CurrentState->stateInformation(); -> - // return *(CurrentState->stateInformation()); + /// other parameters). SignalStateInformation - detectSignalState__debug(INDATATYPE Sample) noexcept { + detectSignalState(INDATATYPE Sample) noexcept { if (!CurrentSignalState) { ASSERT(DetectedSignalStates.empty()); SignalStatePtr S = createNewSignalState(); CurrentSignalState = S; } else { CONFDATATYPE ConfidenceSampleMatchesSignalState = CurrentSignalState->confidenceSampleMatchesSignalState(Sample); CONFDATATYPE ConfidenceSampleMismatchesSignalState = CurrentSignalState->confidenceSampleMismatchesSignalState(Sample); if (ConfidenceSampleMatchesSignalState > ConfidenceSampleMismatchesSignalState) { SignalStateHasChanged = false; } else { SignalStateHasChanged = true; if (CurrentSignalState->signalStateInformation().SignalStateIsValid) { CurrentSignalState->leaveSignalState(); } else { //@benedikt: changed from vector to history. can i still do the next // line? DetectedSignalStates.deleteEntry(CurrentSignalState); } // TODO (future): additionally save averages to enable fast iteration // through recorded signl state history (maybe sort vector based on // these // average values) CurrentSignalState = nullptr; //@benedikt: same question for (auto &SavedSignalState : DetectedSignalStates) { if (SavedSignalState != CurrentSignalState) { ConfidenceSampleMatchesSignalState = SavedSignalState->confidenceSampleMatchesSignalState(Sample); ConfidenceSampleMismatchesSignalState = SavedSignalState->confidenceSampleMismatchesSignalState(Sample); if (ConfidenceSampleMatchesSignalState > ConfidenceSampleMismatchesSignalState) { // TODO (future): maybe it would be better to compare // ConfidenceSampleMatchesSignalState of all signal states in the // vector in order to find the best matching signal state. CurrentSignalState = SavedSignalState; break; } } } if (!CurrentSignalState) { SignalStatePtr S = createNewSignalState(); CurrentSignalState = S; } } } SignalStateInformation SignalStateInfo = CurrentSignalState->insertSample(Sample); if (SignalStateInfo.SignalStateJustGotValid) { NextSignalStateID++; } return SignalStateInfo; } -#ifdef SIGNALSTATEDETECTORDEBUGMODE -public: -#else -private: -#endif // SIGNALSTATEDETECTORDEBUGMODE - /// Gives information about the current signal state. /// /// \return a struct SignalStateInformation that contains information about - /// the - /// current signal state or NULL if no current signal state exists. + /// the current signal state or NULL if no current signal state exists. SignalStateInformation - currentSignalStateInformation__debug(void) noexcept { + currentSignalStateInformation(void) noexcept { if (CurrentSignalState) { return CurrentSignalState->signalStateInformation(); } else { return NULL; } } + + /// Gives information whether a signal state change has happened or not. + /// + /// \return true if a signal state change has happened, and false if not. + bool signalStateHasChanged(void) noexcept { return SignalStateHasChanged; } + +private: + /// Creates a new signal state and adds it to the signal state vector in which + /// all known states are saved. + /// + /// \return a pointer to the newly created signal state or NULL if no state + /// could be created. + SignalStatePtr createNewSignalState(void) noexcept { + SignalStatePtr S(new SignalState( + NextSignalStateID, SampleHistorySize, DABSize, DABHistorySize, + FuzzyFunctionSampleMatches, FuzzyFunctionSampleMismatches, + FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches, + FuzzyFunctionSignalIsDrifting, FuzzyFunctionSignalIsStable)); + + // @benedikt: todo: assert in history, which checks if push_back worked + DetectedSignalStates.addEntry(S); + + return S; + } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_SIGNALSTATEDETECTOR_HPP