had to change the parameter boundaries of the wifi optimizer to be able to use it for bluetooth... this should be refactored to something more generic.. some minor changes in ble
277 lines
8.4 KiB
C++
277 lines
8.4 KiB
C++
#ifndef BEACONMEASUREMENTGROUPER_H
|
|
#define BEACONMEASUREMENTGROUPER_H
|
|
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <cmath>
|
|
|
|
#include "../../math/Stats.h"
|
|
|
|
#include "../../misc/Debug.h"
|
|
|
|
#include "BeaconMeasurements.h"
|
|
|
|
class BeaconMeasurementGrouper {
|
|
|
|
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,
|
|
|
|
/** use std-dev around the signal-strength average of all grouped APs. NOTE not directly useful but for debug! */
|
|
STD_DEV,
|
|
|
|
};
|
|
|
|
/** 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 = "BLEGrp";
|
|
|
|
/** the mode to use for grouping VAPs */
|
|
const Mode mode;
|
|
|
|
/** the signal-strength aggregation algorithm to use */
|
|
const Aggregation rssiAgg;
|
|
|
|
/** 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 */
|
|
BeaconMeasurementGrouper(const Mode mode, const Aggregation rssiAgg, const TimeAggregation timeAgg = TimeAggregation::AVERAGE, const int minOccurences = 2) :
|
|
mode(mode), rssiAgg(rssiAgg), 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 */
|
|
BeaconMeasurements group(const BeaconMeasurements& 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<MACAddress, std::vector<BeaconMeasurement>> grouped;
|
|
|
|
for (const BeaconMeasurement& m : original.entries) {
|
|
|
|
// the vap-base-mac this entry belongs to
|
|
const MACAddress baseMAC = getBaseMAC(m.getBeacon().getMAC());
|
|
grouped[baseMAC].push_back(m);
|
|
|
|
}
|
|
|
|
#ifdef WITH_DEBUG_LOG
|
|
std::stringstream vals;
|
|
vals << std::fixed;
|
|
vals.precision(1);
|
|
#endif
|
|
|
|
// to-be-constructed output
|
|
BeaconMeasurements result;
|
|
int skipped = 0;
|
|
|
|
// perform aggregation on each VAP-group
|
|
for (auto it : grouped) {
|
|
|
|
const MACAddress& base = it.first;
|
|
const std::vector<BeaconMeasurement>& 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 BeaconMeasurement groupedMeasurement = groupVAPs(base, vaps, rssiAgg, timeAgg);
|
|
|
|
// get corresponding std-dev for debug
|
|
#ifdef WITH_DEBUG_LOG
|
|
const BeaconMeasurement groupedStdDev = groupVAPs(base, vaps, Aggregation::STD_DEV, timeAgg);
|
|
vals << groupedMeasurement.getRSSI() << "±";
|
|
vals << groupedStdDev.getRSSI() << " ";
|
|
#endif
|
|
|
|
// add it to the result-vector
|
|
result.entries.push_back(groupedMeasurement);
|
|
|
|
}
|
|
|
|
|
|
// debug
|
|
#ifdef WITH_DEBUG_LOG
|
|
Log::add(name,
|
|
"grouped " + std::to_string(original.entries.size()) + " measurements " +
|
|
"into " + std::to_string(result.entries.size()) + " [omitted: " + std::to_string(skipped) + "]" +
|
|
" Stats:[" + vals.str() + "]",
|
|
true
|
|
);
|
|
#endif
|
|
|
|
// 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 BeaconMeasurement& m) { return m.getRSSI(); }
|
|
};
|
|
struct FieldTS {
|
|
static Timestamp get(const BeaconMeasurement& m) { return m.getTimestamp(); }
|
|
};
|
|
|
|
|
|
|
|
/** combine all of the given VAPs into one entry using the configured aggregation method */
|
|
static BeaconMeasurement groupVAPs(const MACAddress& baseMAC, const std::vector<BeaconMeasurement>& vaps, Aggregation aggRssi, TimeAggregation aggTime) {
|
|
|
|
// the resulting entry is an AP with the base-MAC all of the given VAPs have in common
|
|
const Beacon baseAP(baseMAC);
|
|
|
|
// calculate the rssi using the configured aggregate function
|
|
float rssi = NAN;
|
|
switch(aggRssi) {
|
|
case Aggregation::AVERAGE: rssi = getAVG<float, FieldRSSI>(vaps); break;
|
|
case Aggregation::MEDIAN: rssi = getMedian(vaps); break;
|
|
case Aggregation::MAXIMUM: rssi = getMax<float, FieldRSSI>(vaps); break;
|
|
case Aggregation::STD_DEV: rssi = getStdDev<float, FieldRSSI>(vaps); break;
|
|
default: throw Exception("unsupported rssi-aggregation method");
|
|
}
|
|
|
|
// calculate the time using the configured aggregate function
|
|
Timestamp baseTS;
|
|
switch(aggTime) {
|
|
case TimeAggregation::MINIMUM: baseTS = getMin<Timestamp, FieldTS>(vaps); break;
|
|
case TimeAggregation::AVERAGE: baseTS = getAVG<Timestamp, FieldTS>(vaps); break;
|
|
case TimeAggregation::MAXIMUM: baseTS = getMax<Timestamp, FieldTS>(vaps); break;
|
|
default: throw Exception("unsupported time-aggregation method");
|
|
}
|
|
|
|
|
|
// create the result measurement
|
|
return BeaconMeasurement(baseTS, baseAP, rssi);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
/** get the average signal strength */
|
|
template <typename T, typename Field> static inline T getAVG(const std::vector<BeaconMeasurement>& vaps) {
|
|
|
|
Stats::Average<T> avg;
|
|
for (const BeaconMeasurement& vap : vaps) {
|
|
avg.add(Field::get(vap));
|
|
}
|
|
return avg.get();
|
|
|
|
}
|
|
|
|
/** get the std-dev around the average */
|
|
template <typename T, typename Field> static inline T getStdDev(const std::vector<BeaconMeasurement>& vaps) {
|
|
|
|
Stats::Variance<T> var;
|
|
for (const BeaconMeasurement& vap : vaps) {
|
|
var.add(Field::get(vap));
|
|
}
|
|
return var.getStdDev();
|
|
|
|
}
|
|
|
|
/** get the median signal strength */
|
|
static inline float getMedian(const std::vector<BeaconMeasurement>& vaps) {
|
|
|
|
Stats::Median<float> median;
|
|
for (const BeaconMeasurement& vap : vaps) {
|
|
median.add(vap.getRSSI());
|
|
}
|
|
return median.get();
|
|
|
|
}
|
|
|
|
/** get the maximum value */
|
|
template <typename T, typename Field> static inline T getMax(const std::vector<BeaconMeasurement>& vaps) {
|
|
|
|
Stats::Maximum<T> max;
|
|
for (const BeaconMeasurement& vap : vaps) {
|
|
max.add(Field::get(vap));
|
|
}
|
|
return max.get();
|
|
|
|
}
|
|
|
|
/** get the minimum value */
|
|
template <typename T, typename Field> static inline T getMin(const std::vector<BeaconMeasurement>& vaps) {
|
|
|
|
Stats::Minimum<T> min;
|
|
for (const BeaconMeasurement& 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 // BEACONMEASUREMENTGROUPER_H
|