#ifndef VAPGROUPER_H #define VAPGROUPER_H #include #include #include #include "../../math/Median.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, }; private: /** the mode to use for grouping VAPs */ const Mode mode; /** the signal-strength aggregation algorithm to use */ const Aggregation agg; public: /** ctor */ VAPGrouper(const Mode mode, const Aggregation agg) : mode(mode), agg(agg) { } /** 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] 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); } // output WiFiMeasurements result; // perform aggregation on each VAP-group for (auto it : grouped) { const MACAddress& base = it.first; const std::vector& vaps = it.second; // group all VAPs into one measurement const WiFiMeasurement groupedMeasurement = groupVAPs(base, vaps); // add it to the result-vector result.entries.push_back(groupedMeasurement); } // 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: /** 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 const 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 vap-aggregation method"); } // create the result measurement return WiFiMeasurement(baseAP, rssi, baseTS); } private: /** get the average signal strength */ inline float getAVG(const std::vector& vaps) const { float rssi = 0; for (const WiFiMeasurement& vap : vaps) { rssi += vap.getRSSI(); } return rssi / vaps.size(); } /** get the median signal strength */ inline float getMedian(const std::vector& vaps) const { Median median; for (const WiFiMeasurement& vap : vaps) { median.add(vap.getRSSI()); } return median.get(); } /** get the maximum signal strength */ inline float getMax(const std::vector& vaps) const { float max = -9999999; for (const WiFiMeasurement& vap : vaps) { if (vap.getRSSI() > max) {max = vap.getRSSI();} } return max; } 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