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:
2018-05-20 18:56:49 +02:00
parent a8123d532d
commit a22290415e
7 changed files with 260 additions and 146 deletions

View File

@@ -13,6 +13,8 @@
*/
class WiFiFingerprints {
static constexpr const char* name = "Fingerprints";
private:
// /** 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();
}

View File

@@ -9,6 +9,13 @@
namespace WiFiOptimizer {
enum class Mode {
FAST,
MEDIUM,
QUALITY,
};
/** base-class for all WiFiOptimizers */
class Base {

View File

@@ -10,8 +10,8 @@
#include "WiFiFingerprint.h"
#include "WiFiFingerprints.h"
#include "../model/WiFiModel.h"
#include "../model/WiFiModelLogDistCeiling.h"
#include "../model/WiFiModels.h"
//#include "../model/WiFiModelLogDistCeiling.h"
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
@@ -35,12 +35,6 @@ namespace WiFiOptimizer {
public:
enum class Mode {
FAST,
MEDIUM,
QUALITY,
};
/**
* resulting optimization stats for one AP
*/
@@ -112,6 +106,13 @@ namespace WiFiOptimizer {
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 {
std::vector<APParamsMAC> lst;
@@ -182,12 +183,15 @@ namespace WiFiOptimizer {
}
/** optimize all known APs */
APParamsList optimizeAll(APFilter filter) const {
APParamsList optimizeAll(APFilter filter, OptResultStats* dst = nullptr) const {
// sanity check
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;
for (const MACAddress& mac : getAllMACs()) {
@@ -198,8 +202,13 @@ namespace WiFiOptimizer {
// filter based on stats (option to ignore/filter some access-points)
if (!filter(stats, mac)) {
res.push_back(APParamsMAC(mac, params));
errSum += stats.error_db;
++errCnt;
//errSum += stats.error_db;
//++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 {
Log::add(name, "ignoring opt-result for AP " + mac.asString() + " due to filter");
//std::cout << "ignored due to filter!" << std::endl;
@@ -207,9 +216,19 @@ namespace WiFiOptimizer {
}
const float avgErr = errSum / errCnt;
Log::add(name, "optimized APs: " + std::to_string(errCnt));
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
//const float avgErr = errSum / 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, "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
return APParamsList(res);
@@ -237,7 +256,7 @@ namespace WiFiOptimizer {
const std::vector<LeOpt::MinMax> valRegion = {
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().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(1, 4), // exp
LeOpt::MinMax(-15, -0), // waf
@@ -258,7 +277,7 @@ namespace WiFiOptimizer {
opt.setNumIerations(100);
break;
case Mode::QUALITY:
opt.setPopulationSize(500);
opt.setPopulationSize(1500);
opt.setNumIerations(150);
break;
}

View File

@@ -15,127 +15,186 @@
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#endif
/**
* uses the log-distance model, but one per floor.
* the model is optimized using all fingerprints that belong to this floor
*/
class WiFiOptimizerPerFloor {
namespace WiFiOptimizer {
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;
std::mutex mtx;
WiFiFingerprints fps;
#ifdef WITH_DEBUG_PLOT
K::Gnuplot gp;
K::GnuplotSplot splot;
K::GnuplotSplotElementColorPoints pts;
K::GnuplotSplotElementLines lines;
#endif
Floorplan::IndoorMap* map;
std::mutex mtx;
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)
mdl = new WiFiModelPerFloor(map);
#ifdef WITH_DEBUG_PLOT
K::Gnuplot gp;
K::GnuplotSplot splot;
K::GnuplotSplotElementColorPoints pts;
K::GnuplotSplotElementLines lines;
#endif
#ifdef WITH_DEBUG_PLOT
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
public:
}
PerFloor(Floorplan::IndoorMap* map, const VAPGrouper& vg, Mode mode) : map(map), vg(vg), mode(mode) {
/** make the given fingerprint known to the optimizer */
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
}
}
// the overall model (contains one sub-model per floor)
mdl = new WiFiModelPerFloor(map);
#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);
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
// 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