#ifndef VAPGROUPER_H #define VAPGROUPER_H #include #include #include #include "../../math/Stats.h" #include "../../misc/Debug.h" #include "WiFiMeasurements.h" class VAPGrouper { public: /** the mode denotes the algorithm that is used for grouping VAPs together */ enum class Mode { /** do NOT group */ DISABLED, /** group VAPs by setting the MAC's last digit to zero */ LAST_MAC_DIGIT_TO_ZERO, }; /** describes how to calculate the final signal-strengh of the VAP-grouped entry */ enum class Aggregation { /** use the average signal-strength of all grouped APs */ AVERAGE, /** use the median signal-strength of all grouped APs */ MEDIAN, /** use the maximum signal-strength of all grouped APs */ MAXIMUM, }; /** how to determine the grouped timestamp */ enum class TimeAggregation { /** use the smallest timestamp among all grouped APs */ MINIMUM, /** use the average timestamp among all grouped APs */ AVERAGE, /** use the maximum timestamp among all grouped APs */ MAXIMUM, }; private: static constexpr const char* name = "VAPGrp"; /** the mode to use for grouping VAPs */ const Mode mode; /** the signal-strength aggregation algorithm to use */ const Aggregation agg; /** how to aggreage the grouped time */ const TimeAggregation timeAgg; /** respect only outputs with at-least X occurences of one physical hardware [can be used to prevent issues] */ int minOccurences; public: /** ctor */ VAPGrouper(const Mode mode, const Aggregation agg, const TimeAggregation timeAgg = TimeAggregation::AVERAGE, const int minOccurences = 2) : mode(mode), agg(agg), timeAgg(timeAgg), minOccurences(minOccurences) { ; } /** set the number of needed occurences per VAP-group to be accepted */ void setMinOccurences(const int min) { this->minOccurences = min; } /** get a vap-grouped version of the given input */ WiFiMeasurements group(const WiFiMeasurements& original) const { // first, group all VAPs into a vector [one vector per VAP-group] // by using the modified MAC address that all VAPs have in common std::unordered_map> grouped; for (const WiFiMeasurement& m : original.entries) { // the vap-base-mac this entry belongs to const MACAddress baseMAC = getBaseMAC(m.getAP().getMAC()); grouped[baseMAC].push_back(m); } // to-be-constructed output WiFiMeasurements result; int skipped = 0; // perform aggregation on each VAP-group for (auto it : grouped) { const MACAddress& base = it.first; const std::vector& vaps = it.second; // remove entries that do not satify the min-occurences metric if ((int)vaps.size() < minOccurences) {++skipped; continue;} // group all VAPs into one measurement const WiFiMeasurement groupedMeasurement = groupVAPs(base, vaps); // add it to the result-vector result.entries.push_back(groupedMeasurement); } // debug Log::add(name, "grouped " + std::to_string(original.entries.size()) + " measurements " + "into " + std::to_string(result.entries.size()) + " [omitted: " + std::to_string(skipped) + "]", true ); // done return result; } /** get the VAP-base-MAC-Address that is the same for all APs that belong to a VAP-Group */ MACAddress getBaseMAC(const MACAddress& mac) const { switch(mode) { case Mode::DISABLED: return mac; case Mode::LAST_MAC_DIGIT_TO_ZERO: return lastMacDigitToZero(mac); default: throw Exception("unsupported vap-grouping mode given"); } } private: struct FieldRSSI { static float get(const WiFiMeasurement& m) { return m.getRSSI(); } }; struct FieldTS { static Timestamp get(const WiFiMeasurement& m) { return m.getTimestamp(); } }; /** combine all of the given VAPs into one entry using the configured aggregation method */ WiFiMeasurement groupVAPs(const MACAddress& baseMAC, const std::vector& vaps) const { // the resulting entry is an AP with the base-MAC all of the given VAPs have in common const AccessPoint baseAP(baseMAC); // the resultign timestamp //Timestamp baseTS = vaps.front().getTimestamp(); // calculate the rssi using the configured aggregate function float rssi = NAN; switch(agg) { case Aggregation::AVERAGE: rssi = getAVG(vaps); break; case Aggregation::MEDIAN: rssi = getMedian(vaps); break; case Aggregation::MAXIMUM: rssi = getMax(vaps); break; default: throw Exception("unsupported rssi-aggregation method"); } // calculate the time using the configured aggregate function Timestamp baseTS; switch(timeAgg) { case TimeAggregation::MINIMUM: baseTS = getMin(vaps); break; case TimeAggregation::AVERAGE: baseTS = getAVG(vaps); break; case TimeAggregation::MAXIMUM: baseTS = getMax(vaps); break; default: throw Exception("unsupported time-aggregation method"); } // create the result measurement return WiFiMeasurement(baseAP, rssi, baseTS); } private: /** get the average signal strength */ template inline T getAVG(const std::vector& vaps) const { // T field = T(); // for (const WiFiMeasurement& vap : vaps) { // field = field + Field::get(vap); // } // return field / vaps.size(); Stats::Average avg; for (const WiFiMeasurement& vap : vaps) { avg.add(Field::get(vap)); } return avg.get(); } /** get the median signal strength */ inline float getMedian(const std::vector& vaps) const { Stats::Median median; for (const WiFiMeasurement& vap : vaps) { median.add(vap.getRSSI()); } return median.get(); } /** get the maximum value */ template inline T getMax(const std::vector& vaps) const { Stats::Maximum max; for (const WiFiMeasurement& vap : vaps) { max.add(Field::get(vap)); } return max.get(); } /** get the minimum value */ template inline T getMin(const std::vector& vaps) const { Stats::Minimum min; for (const WiFiMeasurement& vap : vaps) { min.add(Field::get(vap)); } return min.get(); } private: /** convert the MAC by setting the last (right-most) digit to zero (0) */ static inline MACAddress lastMacDigitToZero(const MACAddress& mac) { std::string str = mac.asString(); str.back() = '0'; return MACAddress(str); } }; #endif // VAPGROUPER_H