150 lines
3.3 KiB
C++
150 lines
3.3 KiB
C++
/*
|
||
* © Copyright 2014 – Urheberrechtshinweis
|
||
* Alle Rechte vorbehalten / All Rights Reserved
|
||
*
|
||
* Programmcode ist urheberrechtlich geschuetzt.
|
||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||
* Keine Verwendung ohne explizite Genehmigung.
|
||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||
*/
|
||
|
||
#ifndef WIFIQUALITYANALYZER_H
|
||
#define WIFIQUALITYANALYZER_H
|
||
|
||
#include "WiFiMeasurements.h"
|
||
#include <unordered_set>
|
||
|
||
#ifdef WITH_DEBUG_PLOT
|
||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||
#endif
|
||
|
||
class WiFiQualityAnalyzer {
|
||
|
||
private:
|
||
|
||
int historySize = 3;
|
||
std::vector<WiFiMeasurements> history;
|
||
float quality = 0;
|
||
|
||
#ifdef WITH_DEBUG_PLOT
|
||
K::Gnuplot gp;
|
||
K::GnuplotPlot plot;
|
||
K::GnuplotPlotElementLines line1;
|
||
K::GnuplotPlotElementLines line2;
|
||
int gpX = 0;
|
||
#endif
|
||
|
||
|
||
public:
|
||
|
||
WiFiQualityAnalyzer() {
|
||
#ifdef WITH_DEBUG_PLOT
|
||
plot.add(&line1);
|
||
plot.add(&line2);
|
||
plot.setTitle("WiFi Quality");
|
||
#endif
|
||
}
|
||
|
||
/** attach the current measurement and infer the quality */
|
||
void add(const WiFiMeasurements& mes) {
|
||
|
||
// update the history
|
||
history.push_back(mes);
|
||
while(history.size() > historySize) {
|
||
history.erase(history.begin());
|
||
}
|
||
|
||
// recalculate
|
||
update();
|
||
|
||
}
|
||
|
||
/** get a quality score for the WiFi */
|
||
float getQuality() const {
|
||
return quality;
|
||
}
|
||
|
||
private:
|
||
|
||
/** re-calculate the current quality */
|
||
void update() {
|
||
|
||
const float qCnt = getQualityByCount();
|
||
const float qAvgdB = getQualityByAvgDB();
|
||
|
||
quality = qAvgdB;
|
||
|
||
#ifdef WITH_DEBUG_PLOT
|
||
line1.add(K::GnuplotPoint2(gpX,qAvgdB)); line1.setTitle("dB"); line1.getStroke().setWidth(2);
|
||
line2.add(K::GnuplotPoint2(gpX,qCnt)); line2.setTitle("visible");
|
||
while(line1.size() > 50) {line1.remove(0);}
|
||
while(line2.size() > 50) {line2.remove(0);}
|
||
++gpX;
|
||
gp.draw(plot);
|
||
gp.flush();
|
||
#endif
|
||
|
||
}
|
||
|
||
/** score [0:1] based on the average sig-strength. the higher the better */
|
||
float getQualityByAvgDB() const {
|
||
|
||
// stats
|
||
float sum = 0;
|
||
float sum2 = 0;
|
||
float cnt = 0;
|
||
|
||
for (const WiFiMeasurements& mes : history) {
|
||
for (const WiFiMeasurement& m : mes.entries) {
|
||
const float rssi = m.getRSSI();
|
||
sum += rssi;
|
||
sum2 += rssi*rssi;
|
||
++cnt;
|
||
}
|
||
}
|
||
|
||
// average sig strength
|
||
const float avg = sum/cnt;
|
||
const float avg2 = sum2/cnt;
|
||
const float stdDev = std::sqrt(avg2 - avg*avg);
|
||
|
||
// avg rssi score
|
||
const float minRSSI = -85;
|
||
const float maxRSSI = -70;
|
||
float score1 = (avg-minRSSI) / (maxRSSI-minRSSI); // min = 0; max = 1
|
||
if (score1 > 1) {score1 = 1;}
|
||
if (score1 < 0) {score1 = 0;}
|
||
//const float score1 = 1.0 - std::exp(-0.07 * (avg-minRSSI));
|
||
|
||
// std dev score
|
||
const float score2 = 1.0 - std::exp(-1.0 * stdDev);
|
||
|
||
return score1 * score2;
|
||
|
||
}
|
||
|
||
/** quality score [0:1] according to the number of seen APs */
|
||
float getQualityByCount() const {
|
||
|
||
// distinct macs
|
||
std::unordered_set<MACAddress> macs;
|
||
for (const WiFiMeasurements& mes : history) {
|
||
for (const WiFiMeasurement& m : mes.entries) {
|
||
macs.insert(m.getAP().getMAC());
|
||
}
|
||
}
|
||
|
||
// number of distinct macs
|
||
const int cnt = macs.size();
|
||
|
||
// see function plot. function between [0.0:1.0] has 0.5 around 7 seen APs
|
||
return 1.0 - std::exp(-0.1 * cnt);
|
||
|
||
}
|
||
|
||
};
|
||
|
||
#endif // WIFIQUALITYANALYZER_H
|