fixed some issues with stats::variance
fixed umbrella header for stats added error-feedback to wifi optimizers improved logging for wifi optimizers adjusted calling-API for wifi-optimizers
This commit is contained in:
@@ -5,5 +5,6 @@
|
|||||||
#include "stats/Median.h"
|
#include "stats/Median.h"
|
||||||
#include "stats/Minimum.h"
|
#include "stats/Minimum.h"
|
||||||
#include "stats/Maximum.h"
|
#include "stats/Maximum.h"
|
||||||
|
#include "stats/Variance.h"
|
||||||
|
|
||||||
#endif // MATH_STATS_H
|
#endif // MATH_STATS_H
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Stats {
|
|||||||
|
|
||||||
/** get the current stadard-deviation */
|
/** get the current stadard-deviation */
|
||||||
Scalar getStdDev() const {
|
Scalar getStdDev() const {
|
||||||
return std::sqrt(get);
|
return std::sqrt(get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ public:
|
|||||||
/** use the maximum signal-strength of all grouped APs */
|
/** use the maximum signal-strength of all grouped APs */
|
||||||
MAXIMUM,
|
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 */
|
/** how to determine the grouped timestamp */
|
||||||
@@ -62,7 +65,7 @@ private:
|
|||||||
const Mode mode;
|
const Mode mode;
|
||||||
|
|
||||||
/** the signal-strength aggregation algorithm to use */
|
/** the signal-strength aggregation algorithm to use */
|
||||||
const Aggregation agg;
|
const Aggregation rssiAgg;
|
||||||
|
|
||||||
/** how to aggreage the grouped time */
|
/** how to aggreage the grouped time */
|
||||||
const TimeAggregation timeAgg;
|
const TimeAggregation timeAgg;
|
||||||
@@ -73,8 +76,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** ctor */
|
/** ctor */
|
||||||
VAPGrouper(const Mode mode, const Aggregation agg, const TimeAggregation timeAgg = TimeAggregation::AVERAGE, const int minOccurences = 2) :
|
VAPGrouper(const Mode mode, const Aggregation rssiAgg, const TimeAggregation timeAgg = TimeAggregation::AVERAGE, const int minOccurences = 2) :
|
||||||
mode(mode), agg(agg), timeAgg(timeAgg), minOccurences(minOccurences) {
|
mode(mode), rssiAgg(rssiAgg), timeAgg(timeAgg), minOccurences(minOccurences) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +101,12 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_DEBUG_LOG
|
||||||
|
std::stringstream vals;
|
||||||
|
vals << std::fixed;
|
||||||
|
vals.precision(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
// to-be-constructed output
|
// to-be-constructed output
|
||||||
WiFiMeasurements result;
|
WiFiMeasurements result;
|
||||||
int skipped = 0;
|
int skipped = 0;
|
||||||
@@ -112,19 +121,30 @@ public:
|
|||||||
if ((int)vaps.size() < minOccurences) {++skipped; continue;}
|
if ((int)vaps.size() < minOccurences) {++skipped; continue;}
|
||||||
|
|
||||||
// group all VAPs into one measurement
|
// group all VAPs into one measurement
|
||||||
const WiFiMeasurement groupedMeasurement = groupVAPs(base, vaps);
|
const WiFiMeasurement groupedMeasurement = groupVAPs(base, vaps, rssiAgg, timeAgg);
|
||||||
|
|
||||||
|
// get corresponding std-dev for debug
|
||||||
|
#ifdef WITH_DEBUG_LOG
|
||||||
|
const WiFiMeasurement groupedStdDev = groupVAPs(base, vaps, Aggregation::STD_DEV, timeAgg);
|
||||||
|
vals << groupedMeasurement.getRSSI() << "±";
|
||||||
|
vals << groupedStdDev.getRSSI() << " ";
|
||||||
|
#endif
|
||||||
|
|
||||||
// add it to the result-vector
|
// add it to the result-vector
|
||||||
result.entries.push_back(groupedMeasurement);
|
result.entries.push_back(groupedMeasurement);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
Log::add(name,
|
#ifdef WITH_DEBUG_LOG
|
||||||
|
Log::add(name,
|
||||||
"grouped " + std::to_string(original.entries.size()) + " measurements " +
|
"grouped " + std::to_string(original.entries.size()) + " measurements " +
|
||||||
"into " + std::to_string(result.entries.size()) + " [omitted: " + std::to_string(skipped) + "]",
|
"into " + std::to_string(result.entries.size()) + " [omitted: " + std::to_string(skipped) + "]" +
|
||||||
|
" Stats:[" + vals.str() + "]",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
// done
|
// done
|
||||||
return result;
|
return result;
|
||||||
@@ -135,9 +155,9 @@ public:
|
|||||||
MACAddress getBaseMAC(const MACAddress& mac) const {
|
MACAddress getBaseMAC(const MACAddress& mac) const {
|
||||||
|
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case Mode::DISABLED: return mac;
|
case Mode::DISABLED: return mac;
|
||||||
case Mode::LAST_MAC_DIGIT_TO_ZERO: return lastMacDigitToZero(mac);
|
case Mode::LAST_MAC_DIGIT_TO_ZERO: return lastMacDigitToZero(mac);
|
||||||
default: throw Exception("unsupported vap-grouping mode given");
|
default: throw Exception("unsupported vap-grouping mode given");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -154,26 +174,24 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
/** combine all of the given VAPs into one entry using the configured aggregation method */
|
/** combine all of the given VAPs into one entry using the configured aggregation method */
|
||||||
WiFiMeasurement groupVAPs(const MACAddress& baseMAC, const std::vector<WiFiMeasurement>& vaps) const {
|
static WiFiMeasurement groupVAPs(const MACAddress& baseMAC, const std::vector<WiFiMeasurement>& vaps, Aggregation aggRssi, TimeAggregation aggTime) {
|
||||||
|
|
||||||
// the resulting entry is an AP with the base-MAC all of the given VAPs have in common
|
// the resulting entry is an AP with the base-MAC all of the given VAPs have in common
|
||||||
const AccessPoint baseAP(baseMAC);
|
const AccessPoint baseAP(baseMAC);
|
||||||
|
|
||||||
// the resultign timestamp
|
|
||||||
//Timestamp baseTS = vaps.front().getTimestamp();
|
|
||||||
|
|
||||||
// calculate the rssi using the configured aggregate function
|
// calculate the rssi using the configured aggregate function
|
||||||
float rssi = NAN;
|
float rssi = NAN;
|
||||||
switch(agg) {
|
switch(aggRssi) {
|
||||||
case Aggregation::AVERAGE: rssi = getAVG<float, FieldRSSI>(vaps); break;
|
case Aggregation::AVERAGE: rssi = getAVG<float, FieldRSSI>(vaps); break;
|
||||||
case Aggregation::MEDIAN: rssi = getMedian(vaps); break;
|
case Aggregation::MEDIAN: rssi = getMedian(vaps); break;
|
||||||
case Aggregation::MAXIMUM: rssi = getMax<float, FieldRSSI>(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");
|
default: throw Exception("unsupported rssi-aggregation method");
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the time using the configured aggregate function
|
// calculate the time using the configured aggregate function
|
||||||
Timestamp baseTS;
|
Timestamp baseTS;
|
||||||
switch(timeAgg) {
|
switch(aggTime) {
|
||||||
case TimeAggregation::MINIMUM: baseTS = getMin<Timestamp, FieldTS>(vaps); break;
|
case TimeAggregation::MINIMUM: baseTS = getMin<Timestamp, FieldTS>(vaps); break;
|
||||||
case TimeAggregation::AVERAGE: baseTS = getAVG<Timestamp, FieldTS>(vaps); break;
|
case TimeAggregation::AVERAGE: baseTS = getAVG<Timestamp, FieldTS>(vaps); break;
|
||||||
case TimeAggregation::MAXIMUM: baseTS = getMax<Timestamp, FieldTS>(vaps); break;
|
case TimeAggregation::MAXIMUM: baseTS = getMax<Timestamp, FieldTS>(vaps); break;
|
||||||
@@ -189,13 +207,8 @@ private:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
/** get the average signal strength */
|
/** get the average signal strength */
|
||||||
template <typename T, typename Field> inline T getAVG(const std::vector<WiFiMeasurement>& vaps) const {
|
template <typename T, typename Field> static inline T getAVG(const std::vector<WiFiMeasurement>& vaps) {
|
||||||
|
|
||||||
// T field = T();
|
|
||||||
// for (const WiFiMeasurement& vap : vaps) {
|
|
||||||
// field = field + Field::get(vap);
|
|
||||||
// }
|
|
||||||
// return field / vaps.size();
|
|
||||||
Stats::Average<T> avg;
|
Stats::Average<T> avg;
|
||||||
for (const WiFiMeasurement& vap : vaps) {
|
for (const WiFiMeasurement& vap : vaps) {
|
||||||
avg.add(Field::get(vap));
|
avg.add(Field::get(vap));
|
||||||
@@ -204,8 +217,19 @@ private:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** get the std-dev around the average */
|
||||||
|
template <typename T, typename Field> static inline T getStdDev(const std::vector<WiFiMeasurement>& vaps) {
|
||||||
|
|
||||||
|
Stats::Variance<T> var;
|
||||||
|
for (const WiFiMeasurement& vap : vaps) {
|
||||||
|
var.add(Field::get(vap));
|
||||||
|
}
|
||||||
|
return var.getStdDev();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** get the median signal strength */
|
/** get the median signal strength */
|
||||||
inline float getMedian(const std::vector<WiFiMeasurement>& vaps) const {
|
static inline float getMedian(const std::vector<WiFiMeasurement>& vaps) {
|
||||||
|
|
||||||
Stats::Median<float> median;
|
Stats::Median<float> median;
|
||||||
for (const WiFiMeasurement& vap : vaps) {
|
for (const WiFiMeasurement& vap : vaps) {
|
||||||
@@ -216,7 +240,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** get the maximum value */
|
/** get the maximum value */
|
||||||
template <typename T, typename Field> inline T getMax(const std::vector<WiFiMeasurement>& vaps) const {
|
template <typename T, typename Field> static inline T getMax(const std::vector<WiFiMeasurement>& vaps) {
|
||||||
|
|
||||||
Stats::Maximum<T> max;
|
Stats::Maximum<T> max;
|
||||||
for (const WiFiMeasurement& vap : vaps) {
|
for (const WiFiMeasurement& vap : vaps) {
|
||||||
@@ -227,7 +251,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** get the minimum value */
|
/** get the minimum value */
|
||||||
template <typename T, typename Field> inline T getMin(const std::vector<WiFiMeasurement>& vaps) const {
|
template <typename T, typename Field> static inline T getMin(const std::vector<WiFiMeasurement>& vaps) {
|
||||||
|
|
||||||
Stats::Minimum<T> min;
|
Stats::Minimum<T> min;
|
||||||
for (const WiFiMeasurement& vap : vaps) {
|
for (const WiFiMeasurement& vap : vaps) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
*/
|
*/
|
||||||
class WiFiFingerprints {
|
class WiFiFingerprints {
|
||||||
|
|
||||||
|
static constexpr const char* name = "Fingerprints";
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// /** the file to save the calibration model to */
|
// /** the file to save the calibration model to */
|
||||||
@@ -96,6 +98,8 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::add(name, "loaded " + std::to_string(fingerprints.size()) + " fingerprints");
|
||||||
|
|
||||||
inp.close();
|
inp.close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,13 @@
|
|||||||
|
|
||||||
namespace WiFiOptimizer {
|
namespace WiFiOptimizer {
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
FAST,
|
||||||
|
MEDIUM,
|
||||||
|
QUALITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** base-class for all WiFiOptimizers */
|
/** base-class for all WiFiOptimizers */
|
||||||
class Base {
|
class Base {
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
#include "WiFiFingerprint.h"
|
#include "WiFiFingerprint.h"
|
||||||
#include "WiFiFingerprints.h"
|
#include "WiFiFingerprints.h"
|
||||||
|
|
||||||
#include "../model/WiFiModel.h"
|
#include "../model/WiFiModels.h"
|
||||||
#include "../model/WiFiModelLogDistCeiling.h"
|
//#include "../model/WiFiModelLogDistCeiling.h"
|
||||||
|
|
||||||
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
|
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
|
||||||
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
|
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
|
||||||
@@ -35,12 +35,6 @@ namespace WiFiOptimizer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum class Mode {
|
|
||||||
FAST,
|
|
||||||
MEDIUM,
|
|
||||||
QUALITY,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* resulting optimization stats for one AP
|
* resulting optimization stats for one AP
|
||||||
*/
|
*/
|
||||||
@@ -112,6 +106,13 @@ namespace WiFiOptimizer {
|
|||||||
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
|
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OptResultStats {
|
||||||
|
K::Statistics<float> avgApErrors; // contains one average-error per optimized AP
|
||||||
|
K::Statistics<float> singleFPErrors; // contains one error for each fingerprint<->ap SIGNED
|
||||||
|
K::Statistics<float> singleFPErrorsAbs; // contains one error for each fingerprint<->ap ABSOLUTE
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class APParamsList {
|
class APParamsList {
|
||||||
|
|
||||||
std::vector<APParamsMAC> lst;
|
std::vector<APParamsMAC> lst;
|
||||||
@@ -182,12 +183,15 @@ namespace WiFiOptimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** optimize all known APs */
|
/** optimize all known APs */
|
||||||
APParamsList optimizeAll(APFilter filter) const {
|
APParamsList optimizeAll(APFilter filter, OptResultStats* dst = nullptr) const {
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization! call addFingerprint() first!");
|
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization! call addFingerprint() first!");
|
||||||
|
|
||||||
float errSum = 0; int errCnt = 0;
|
K::Statistics<float> avgErrors;
|
||||||
|
K::Statistics<float> singleErrors;
|
||||||
|
K::Statistics<float> singleErrorsAbs;
|
||||||
|
|
||||||
std::vector<APParamsMAC> res;
|
std::vector<APParamsMAC> res;
|
||||||
for (const MACAddress& mac : getAllMACs()) {
|
for (const MACAddress& mac : getAllMACs()) {
|
||||||
|
|
||||||
@@ -198,8 +202,13 @@ namespace WiFiOptimizer {
|
|||||||
// filter based on stats (option to ignore/filter some access-points)
|
// filter based on stats (option to ignore/filter some access-points)
|
||||||
if (!filter(stats, mac)) {
|
if (!filter(stats, mac)) {
|
||||||
res.push_back(APParamsMAC(mac, params));
|
res.push_back(APParamsMAC(mac, params));
|
||||||
errSum += stats.error_db;
|
//errSum += stats.error_db;
|
||||||
++errCnt;
|
//++errCnt;
|
||||||
|
avgErrors.add(stats.error_db);
|
||||||
|
for (const auto e : stats.errors) {
|
||||||
|
singleErrors.add(e.getError_db());
|
||||||
|
singleErrorsAbs.add(std::abs(e.getError_db()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log::add(name, "ignoring opt-result for AP " + mac.asString() + " due to filter");
|
Log::add(name, "ignoring opt-result for AP " + mac.asString() + " due to filter");
|
||||||
//std::cout << "ignored due to filter!" << std::endl;
|
//std::cout << "ignored due to filter!" << std::endl;
|
||||||
@@ -207,9 +216,19 @@ namespace WiFiOptimizer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const float avgErr = errSum / errCnt;
|
//const float avgErr = errSum / errCnt;
|
||||||
Log::add(name, "optimized APs: " + std::to_string(errCnt));
|
//Log::add(name, "optimized APs: " + std::to_string(errCnt));
|
||||||
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
|
//Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
|
||||||
|
Log::add(name, "optimization result: ");
|
||||||
|
Log::add(name, " - AvgPerAP " + avgErrors.asString());
|
||||||
|
Log::add(name, " - Single: " + singleErrors.asString());
|
||||||
|
Log::add(name, " - SingleAbs: " + singleErrorsAbs.asString());
|
||||||
|
|
||||||
|
if (dst) {
|
||||||
|
dst->avgApErrors = avgErrors;
|
||||||
|
dst->singleFPErrors = singleErrors;
|
||||||
|
dst->singleFPErrorsAbs = singleErrorsAbs;
|
||||||
|
}
|
||||||
|
|
||||||
// done
|
// done
|
||||||
return APParamsList(res);
|
return APParamsList(res);
|
||||||
@@ -237,7 +256,7 @@ namespace WiFiOptimizer {
|
|||||||
const std::vector<LeOpt::MinMax> valRegion = {
|
const std::vector<LeOpt::MinMax> valRegion = {
|
||||||
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
|
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
|
||||||
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
|
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
|
||||||
LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z
|
LeOpt::MinMax(mapBBox.getMin().z - 6, mapBBox.getMax().z + 6), // z
|
||||||
LeOpt::MinMax(-50, -30), // txp
|
LeOpt::MinMax(-50, -30), // txp
|
||||||
LeOpt::MinMax(1, 4), // exp
|
LeOpt::MinMax(1, 4), // exp
|
||||||
LeOpt::MinMax(-15, -0), // waf
|
LeOpt::MinMax(-15, -0), // waf
|
||||||
@@ -258,7 +277,7 @@ namespace WiFiOptimizer {
|
|||||||
opt.setNumIerations(100);
|
opt.setNumIerations(100);
|
||||||
break;
|
break;
|
||||||
case Mode::QUALITY:
|
case Mode::QUALITY:
|
||||||
opt.setPopulationSize(500);
|
opt.setPopulationSize(1500);
|
||||||
opt.setNumIerations(150);
|
opt.setNumIerations(150);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,127 +15,186 @@
|
|||||||
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
namespace WiFiOptimizer {
|
||||||
* uses the log-distance model, but one per floor.
|
|
||||||
* the model is optimized using all fingerprints that belong to this floor
|
|
||||||
*/
|
|
||||||
class WiFiOptimizerPerFloor {
|
|
||||||
|
|
||||||
WiFiModelPerFloor* mdl = nullptr;
|
/**
|
||||||
|
* uses the log-distance model, but one per floor.
|
||||||
|
* the model is optimized using all fingerprints that belong to this floor
|
||||||
|
*/
|
||||||
|
class PerFloor {
|
||||||
|
|
||||||
WiFiFingerprints fps;
|
WiFiModelPerFloor* mdl = nullptr;
|
||||||
|
|
||||||
Floorplan::IndoorMap* map;
|
WiFiFingerprints fps;
|
||||||
std::mutex mtx;
|
|
||||||
|
|
||||||
#ifdef WITH_DEBUG_PLOT
|
Floorplan::IndoorMap* map;
|
||||||
K::Gnuplot gp;
|
std::mutex mtx;
|
||||||
K::GnuplotSplot splot;
|
|
||||||
K::GnuplotSplotElementColorPoints pts;
|
|
||||||
K::GnuplotSplotElementLines lines;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
VAPGrouper vg;
|
||||||
|
Mode mode;
|
||||||
|
|
||||||
WiFiOptimizerPerFloor(Floorplan::IndoorMap* map) : map(map) {
|
static constexpr const char* name = "WiFiOptFloor";
|
||||||
|
|
||||||
// the overall model (contains one sub-model per floor)
|
#ifdef WITH_DEBUG_PLOT
|
||||||
mdl = new WiFiModelPerFloor(map);
|
K::Gnuplot gp;
|
||||||
|
K::GnuplotSplot splot;
|
||||||
|
K::GnuplotSplotElementColorPoints pts;
|
||||||
|
K::GnuplotSplotElementLines lines;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_DEBUG_PLOT
|
public:
|
||||||
splot.add(&pts); pts.setPointSize(1); pts.setPointType(7);
|
|
||||||
splot.add(&lines);
|
|
||||||
BBox3 bb = FloorplanHelper::getBBox(map);
|
|
||||||
splot.getAxisX().setRange(bb.getMin().x, bb.getMax().x);
|
|
||||||
splot.getAxisY().setRange(bb.getMin().y, bb.getMax().y);
|
|
||||||
splot.getAxisZ().setRange(bb.getMin().z, bb.getMax().z);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
PerFloor(Floorplan::IndoorMap* map, const VAPGrouper& vg, Mode mode) : map(map), vg(vg), mode(mode) {
|
||||||
|
|
||||||
|
// the overall model (contains one sub-model per floor)
|
||||||
/** make the given fingerprint known to the optimizer */
|
mdl = new WiFiModelPerFloor(map);
|
||||||
void addFingerprint(const WiFiFingerprint& fp) {
|
|
||||||
fps.add(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiModelPerFloor* optimizeAll() {
|
|
||||||
|
|
||||||
const VAPGrouper vg = VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MAXIMUM, VAPGrouper::TimeAggregation::AVERAGE, 1);
|
|
||||||
|
|
||||||
// optimize each floor on its own
|
|
||||||
//for (WiFiModelPerFloor::ModelForFloor& mdlForFloor : mdl->getFloorModels()) {
|
|
||||||
for (size_t i = 0; i < map->floors.size(); ++i) {
|
|
||||||
|
|
||||||
const Floorplan::Floor* floor = map->floors[i];
|
|
||||||
|
|
||||||
// 1) create a new optimizer for the current floor
|
|
||||||
WiFiOptimizer::LogDistCeiling opt(map, vg);
|
|
||||||
|
|
||||||
// 2) create the model for this floor
|
|
||||||
mtx.lock();
|
|
||||||
WiFiModelLogDistCeiling* mdlForFloor = new WiFiModelLogDistCeiling(map);
|
|
||||||
mdl->add(mdlForFloor, floor);
|
|
||||||
mtx.unlock();
|
|
||||||
|
|
||||||
// 3) get the floor's bbox and adjust the z-region (needed for museum in Rothenburg)
|
|
||||||
BBox3 bb = FloorplanHelper::getBBox(floor);
|
|
||||||
bb.setMinZ(floor->atHeight+0.25);
|
|
||||||
bb.setMaxZ(floor->atHeight+2);
|
|
||||||
|
|
||||||
// 4) find all fingerprints that belong to the floor/model and add them to the optimizer
|
|
||||||
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
|
|
||||||
//if (mdlForFloor.matches(fp.pos_m.z)) {
|
|
||||||
// std::cout << fp.pos_m.z << std::endl;
|
|
||||||
// opt.addFingerprint(fp);
|
|
||||||
//}
|
|
||||||
if (bb.contains(fp.pos_m)) {
|
|
||||||
//if (fp.pos_m.z >= floor->atHeight && fp.pos_m.z < floor->atHeight+floor->height) {
|
|
||||||
std::cout << fp.pos_m.z << std::endl;
|
|
||||||
opt.addFingerprint(fp);
|
|
||||||
|
|
||||||
#ifdef WITH_DEBUG_PLOT
|
|
||||||
pts.add(K::GnuplotPoint3(fp.pos_m.x, fp.pos_m.y, fp.pos_m.z), i);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WITH_DEBUG_PLOT
|
#ifdef WITH_DEBUG_PLOT
|
||||||
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
splot.add(&pts); pts.setPointSize(1); pts.setPointType(7);
|
||||||
for (const Point2 pt : poly->poly.points) {
|
splot.add(&lines);
|
||||||
lines.add(K::GnuplotPoint3(pt.x, pt.y, floor->atHeight));
|
BBox3 bb = FloorplanHelper::getBBox(map);
|
||||||
}
|
splot.getAxisX().setRange(bb.getMin().x, bb.getMax().x);
|
||||||
lines.splitFace(); lines.splitFace();
|
splot.getAxisY().setRange(bb.getMin().y, bb.getMax().y);
|
||||||
for (const Point2 pt : poly->poly.points) {
|
splot.getAxisZ().setRange(bb.getMin().z, bb.getMax().z);
|
||||||
lines.add(K::GnuplotPoint3(pt.x, pt.y, floor->atHeight+floor->height));
|
|
||||||
}
|
|
||||||
lines.splitFace(); lines.splitFace();
|
|
||||||
}
|
|
||||||
gp.draw(splot);
|
|
||||||
gp.flush();
|
|
||||||
pts.clear();
|
|
||||||
lines.clear();
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 5) run the optimizer
|
|
||||||
const WiFiOptimizer::LogDistCeiling::APParamsList res = opt.optimizeAll(opt.MIN_2_FPS);
|
|
||||||
|
|
||||||
// 6) add all optimized APs to the floor's model
|
|
||||||
for (const WiFiOptimizer::LogDistCeiling::APParamsMAC& ap : res.get()) {
|
|
||||||
// model is per-floor. so model cant optimize waf.. set to VERY HIGH manually
|
|
||||||
const WiFiModelLogDistCeiling::APEntry entry(ap.params.getPos(), ap.params.txp, ap.params.exp, ap.params.waf);
|
|
||||||
mdlForFloor->addAP(ap.mac, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mdl;
|
|
||||||
|
|
||||||
}
|
/** make the given fingerprint known to the optimizer */
|
||||||
|
void addFingerprint(const WiFiFingerprint& fp) {
|
||||||
|
fps.add(fp);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
/** add new fingerprints to the optimizer as data-source */
|
||||||
|
virtual void addFingerprints(const WiFiFingerprints& fps) {
|
||||||
|
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
|
||||||
|
addFingerprint(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OptResultStatsSingle {
|
||||||
|
K::Statistics<float> avgApErrors; // contains one average-error per optimized AP
|
||||||
|
K::Statistics<float> singleFPErrors; // contains one error for each fingerprint<->ap SIGNED
|
||||||
|
K::Statistics<float> singleFPErrorsAbs; // contains one error for each fingerprint<->ap ABSOLUTE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptResultStats {
|
||||||
|
OptResultStatsSingle all;
|
||||||
|
std::vector<OptResultStatsSingle> perFloor;
|
||||||
|
};
|
||||||
|
|
||||||
|
WiFiModelPerFloor* optimizeAll(LogDistCeiling::APFilter filter, OptResultStats* dst = nullptr) {
|
||||||
|
|
||||||
|
// be sure we got stats to write to
|
||||||
|
OptResultStats tmp;
|
||||||
|
if (!dst) {dst = &tmp;}
|
||||||
|
|
||||||
|
// alloc place for per-floor stats
|
||||||
|
dst->perFloor.resize(map->floors.size());
|
||||||
|
|
||||||
|
// optimize each floor on its own
|
||||||
|
//for (WiFiModelPerFloor::ModelForFloor& mdlForFloor : mdl->getFloorModels()) {
|
||||||
|
for (size_t i = 0; i < map->floors.size(); ++i) {
|
||||||
|
|
||||||
|
WiFiOptimizer::LogDistCeiling::OptResultStats floorStats;
|
||||||
|
|
||||||
|
const Floorplan::Floor* floor = map->floors[i];
|
||||||
|
|
||||||
|
// 1) create a new optimizer for the current floor
|
||||||
|
WiFiOptimizer::LogDistCeiling opt(map, vg, mode);
|
||||||
|
|
||||||
|
// 2) create the model for this floor
|
||||||
|
mtx.lock();
|
||||||
|
WiFiModelLogDistCeiling* mdlForFloor = new WiFiModelLogDistCeiling(map);
|
||||||
|
mdl->add(mdlForFloor, floor);
|
||||||
|
mtx.unlock();
|
||||||
|
|
||||||
|
// 3) get the floor's bbox and adjust the z-region (needed for museum in Rothenburg)
|
||||||
|
BBox3 bb = FloorplanHelper::getBBox(floor);
|
||||||
|
bb.setMinZ(floor->atHeight+0.25);
|
||||||
|
bb.setMaxZ(floor->atHeight+2);
|
||||||
|
|
||||||
|
// 4) find all fingerprints that belong to the floor/model and add them to the optimizer
|
||||||
|
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
|
||||||
|
//if (mdlForFloor.matches(fp.pos_m.z)) {
|
||||||
|
// std::cout << fp.pos_m.z << std::endl;
|
||||||
|
// opt.addFingerprint(fp);
|
||||||
|
//}
|
||||||
|
if (bb.contains(fp.pos_m)) {
|
||||||
|
//if (fp.pos_m.z >= floor->atHeight && fp.pos_m.z < floor->atHeight+floor->height) {
|
||||||
|
std::cout << fp.pos_m.z << std::endl;
|
||||||
|
opt.addFingerprint(fp);
|
||||||
|
|
||||||
|
#ifdef WITH_DEBUG_PLOT
|
||||||
|
pts.add(K::GnuplotPoint3(fp.pos_m.x, fp.pos_m.y, fp.pos_m.z), i);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_DEBUG_PLOT
|
||||||
|
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||||
|
for (const Point2 pt : poly->poly.points) {
|
||||||
|
lines.add(K::GnuplotPoint3(pt.x, pt.y, floor->atHeight));
|
||||||
|
}
|
||||||
|
lines.splitFace(); lines.splitFace();
|
||||||
|
for (const Point2 pt : poly->poly.points) {
|
||||||
|
lines.add(K::GnuplotPoint3(pt.x, pt.y, floor->atHeight+floor->height));
|
||||||
|
}
|
||||||
|
lines.splitFace(); lines.splitFace();
|
||||||
|
}
|
||||||
|
gp.draw(splot);
|
||||||
|
gp.flush();
|
||||||
|
pts.clear();
|
||||||
|
lines.clear();
|
||||||
|
sleep(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 5) run the optimizer
|
||||||
|
const WiFiOptimizer::LogDistCeiling::APParamsList res = opt.optimizeAll(filter, &floorStats);
|
||||||
|
|
||||||
|
// 6) add all optimized APs to the floor's model
|
||||||
|
for (const WiFiOptimizer::LogDistCeiling::APParamsMAC& ap : res.get()) {
|
||||||
|
// model is per-floor. so model cant optimize waf.. set to VERY HIGH manually
|
||||||
|
const WiFiModelLogDistCeiling::APEntry entry(ap.params.getPos(), ap.params.txp, ap.params.exp, ap.params.waf);
|
||||||
|
mdlForFloor->addAP(ap.mac, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// overall stats
|
||||||
|
dst->all.avgApErrors.add(floorStats.avgApErrors);
|
||||||
|
dst->all.singleFPErrors.add(floorStats.singleFPErrors);
|
||||||
|
dst->all.singleFPErrorsAbs.add(floorStats.singleFPErrorsAbs);
|
||||||
|
|
||||||
|
// per-floor stats
|
||||||
|
dst->perFloor[i].avgApErrors.add(floorStats.avgApErrors);
|
||||||
|
dst->perFloor[i].singleFPErrors.add(floorStats.singleFPErrors);
|
||||||
|
dst->perFloor[i].singleFPErrorsAbs.add(floorStats.singleFPErrorsAbs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// overall stats
|
||||||
|
Log::add(name, "optimization result: ");
|
||||||
|
Log::add(name, " - AvgPerAP " + dst->all.avgApErrors.asString());
|
||||||
|
Log::add(name, " - Single: " + dst->all.singleFPErrors.asString());
|
||||||
|
Log::add(name, " - SingleAbs: " + dst->all.singleFPErrorsAbs.asString());
|
||||||
|
|
||||||
|
// per-floor stats
|
||||||
|
for (size_t i = 0; i < dst->perFloor.size(); ++i) {
|
||||||
|
Log::add(name, "----------------------------------------------------------------------------------------------------");
|
||||||
|
std::string fn = std::to_string(i);
|
||||||
|
Log::add(name, " - F"+fn+" AvgPerAP " + dst->perFloor[i].avgApErrors.asString());
|
||||||
|
Log::add(name, " - F"+fn+" Single: " + dst->perFloor[i].singleFPErrors.asString());
|
||||||
|
Log::add(name, " - F"+fn+" SingleAbs: " + dst->perFloor[i].singleFPErrorsAbs.asString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return mdl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WIFIOPTIMIZERPERFLOOR_H
|
#endif // WIFIOPTIMIZERPERFLOOR_H
|
||||||
|
|||||||
Reference in New Issue
Block a user