current state

This commit is contained in:
2017-04-24 16:12:15 +02:00
parent 67a9f02d6d
commit 755f5662ae
15 changed files with 1211 additions and 329 deletions

View File

@@ -58,7 +58,7 @@ ADD_DEFINITIONS(
-fstack-protector-all -fstack-protector-all
-g3 -g3
-O2 -O0
-march=native -march=native
-DWITH_TESTS -DWITH_TESTS

View File

@@ -24,6 +24,13 @@
#include <vector> #include <vector>
// FOR TESTING
#define FAST 1
#define QUALITY 2
#define MODE QUALITY
//#define DO_SHOW_FINGERPRINTS
class EvalCompareOpt2 { class EvalCompareOpt2 {
int power = 1; int power = 1;
@@ -36,14 +43,14 @@ class EvalCompareOpt2 {
public: public:
/** ctor with map and fingerprints */ /** ctor with map and fingerprints */
EvalCompareOpt2(const std::string& mapFile, const std::string& fpFile, std::function<bool(const WiFiFingerprint& fp)> remove) { EvalCompareOpt2(const std::string& mapFile, const std::string& fpFile, std::function<bool(const WiFiFingerprint& fp)> remove, bool removeStaircases = false) {
setup(mapFile); setup(mapFile);
// load fingerprints // load fingerprints
calib = WiFiFingerprints(fpFile); calib = WiFiFingerprints(fpFile);
// if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);} // if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);}
// if (ignoreStaircases) {calib = LeHelper::removeStaircases(calib);} if (removeStaircases) {calib = LeHelper::removeStaircases(calib);}
// if (ignoreIndoor) {calib = LeHelper::removeIndoor(calib);} // if (ignoreIndoor) {calib = LeHelper::removeIndoor(calib);}
calib = LeHelper::removeIf(calib, remove); calib = LeHelper::removeIf(calib, remove);
@@ -120,7 +127,9 @@ private:
} }
// plot // plot
#ifdef DO_SHOW_FINGERPRINTS
LeHelper::plot(map, calib); LeHelper::plot(map, calib);
#endif
} }
@@ -205,8 +214,13 @@ public:
// use simplex // use simplex
float params[3] = {-40, 2, -8}; float params[3] = {-40, 2, -8};
K::NumOptAlgoDownhillSimplex<float> opt(3); K::NumOptAlgoDownhillSimplex<float> opt(3);
#if MODE == FAST
opt.setMaxIterations(50); opt.setMaxIterations(50);
opt.setNumRestarts(10);
#elif MODE == QUALITY
opt.setMaxIterations(200);
opt.setNumRestarts(25); opt.setNumRestarts(25);
#endif
opt.calculateOptimum(optFunc, params); opt.calculateOptimum(optFunc, params);
// // use genetic // // use genetic
@@ -257,8 +271,13 @@ public:
// use simplex // use simplex
float params[3] = {-40, 2, -8}; float params[3] = {-40, 2, -8};
K::NumOptAlgoDownhillSimplex<float> opt(3); K::NumOptAlgoDownhillSimplex<float> opt(3);
opt.setMaxIterations(50); #if MODE == FAST
opt.setNumRestarts(25); opt.setMaxIterations(50);
opt.setNumRestarts(10);
#elif MODE == QUALITY
opt.setMaxIterations(200);
opt.setNumRestarts(25);
#endif
opt.calculateOptimum(optFunc, params); opt.calculateOptimum(optFunc, params);
// // use genetic // // use genetic
@@ -352,8 +371,15 @@ public:
}; };
K::NumOptAlgoRangeRandom<float> opt(valRegion); K::NumOptAlgoRangeRandom<float> opt(valRegion);
opt.setPopulationSize(300);
opt.setNumIerations(100); #if MODE == FAST
opt.setPopulationSize(100);
opt.setNumIerations(50);
#elif MODE == QUALITY
opt.setPopulationSize(500);
opt.setNumIerations(250);
#endif
opt.calculateOptimum(optFunc, params); opt.calculateOptimum(optFunc, params);

View File

@@ -111,10 +111,10 @@ namespace Settings {
// OTHER2017 [frank] // OTHER2017 [frank]
const std::vector<int> path1 = {40, 41, 42, 43, 44, 45, 1, 0, 46, 47, 48, 49, 50, 51, 52}; const std::vector<int> path1 = {40, 41, 42, 43, 44, 45, 1, 0, 46, 47, 48, 49, 50, 51, 52};
const std::vector<int> path2 = {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66}; const std::vector<int> path2 = {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66};
const std::vector<int> path3 = {52, 51, 67, 68, 50, 49, 69, 70, 71, 72, 73, 74, 75, 76, 77, 73, 78, 79, 80, 81, 61, 82, 83, 84, 42, 41, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; //const std::vector<int> path3 = {52, 51, 67, 68, 50, 49, 69, 70, 71, 72, 73, 74, 75, 76, 77, 73, 78, 79, 80, 81, 61, 82, 83, 84, 42, 41, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95};
const std::vector<int> path_toni_all_1 = {40,41,42,43,44,45, 1, 0, 46,47,48,49,50,51,52}; //const std::vector<int> path_toni_all_1 = {40,41,42,43,44,45, 1, 0, 46,47,48,49,50,51,52}; // same as PATH1
const std::vector<int> path_toni_all_2 = {53,54,55,56,57,58,59,60,61,62,63,64,65,66}; //const std::vector<int> path_toni_all_2 = {53,54,55,56,57,58,59,60,61,62,63,64,65,66}; // same as PATH2
const std::vector<int> path_toni_inst_1 = {0,1,2,3,4,5,6,7,8,9,10}; const std::vector<int> path_toni_inst_1 = {0,1,2,3,4,5,6,7,8,9,10};
const std::vector<int> path_toni_inst_2 = {11, 12, 3, 2, 1, 0, 13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, 11}; const std::vector<int> path_toni_inst_2 = {11, 12, 3, 2, 1, 0, 13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, 11};

715
main.cpp
View File

@@ -39,6 +39,472 @@
#include "plots/PlotErrFunc.h" #include "plots/PlotErrFunc.h"
void rebuildAllModels(Floorplan::IndoorMap* map, const int skip = 0, bool ignoreStaircases = false) {
// // use walks?
// std::vector<std::pair<std::string, std::vector<int>>> calibWalks = {
// std::make_pair(Settings::path1a, Settings::GroundTruth::path1),
// std::make_pair(Settings::path1b, Settings::GroundTruth::path1),
// std::make_pair(Settings::path2a, Settings::GroundTruth::path2),
// std::make_pair(Settings::path2b, Settings::GroundTruth::path2),
// };
// EvalCompareOpt2 opt1(Settings::fMap, calibWalks);
int skipCnt = 0;
auto skipper = [&] (const WiFiFingerprint& fp) -> bool {
if (skip == 0) {return false;}
++skipCnt;
return ((skipCnt % skip) != 0);
};
// all combined
auto removeNone = [] (const WiFiFingerprint& fp) -> bool {return false;};
// per floor
auto only0th = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || std::abs(fp.pos_m.z - (1.3)) > 0.1;};
auto only1st = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || std::abs(fp.pos_m.z - (4+1.3)) > 0.1;};
auto only2nd = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || std::abs(fp.pos_m.z - (4+3.4+1.3)) > 0.1;};
auto only3rd = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || std::abs(fp.pos_m.z - (4+3.4+3.4+1.3)) > 0.1;};
// per bbox
#include "bboxes.h"
auto only0H = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes0H.contains(fp.pos_m);};
auto only0O = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes0O.contains(fp.pos_m);};
auto only0I = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes0I.contains(fp.pos_m);};
auto only1H = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes1H.contains(fp.pos_m);};
auto only1O = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes1O.contains(fp.pos_m);};
auto only1I = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes1I.contains(fp.pos_m);};
auto only2H = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes2H.contains(fp.pos_m);};
auto only3H = [&] (const WiFiFingerprint& fp) -> bool {return skipper(fp) || !bboxes3H.contains(fp.pos_m);};
// use fingerprints?
EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, skipper, ignoreStaircases);
// optimize using all floors
EvalCompareOpt2::Result s1 = opt1.fixedPosFixedParamsForAll(); //BREAK;
EvalCompareOpt2::Result s2 = opt1.fixedPosOptParamsForAll(); //BREAK;
EvalCompareOpt2::Result s3 = opt1.fixedPosOptParamsForEach(); //BREAK;
EvalCompareOpt2::Result s4 = opt1.optPosOptParamsForEach(); //BREAK;
// optimize per floor
EvalCompareOpt2 opt_f0(Settings::fMap, Settings::fCalib, only0th, ignoreStaircases);
EvalCompareOpt2::Result sf0 = opt_f0.optPosOptParamsForEach();
EvalCompareOpt2 opt_f1(Settings::fMap, Settings::fCalib, only1st, ignoreStaircases);
EvalCompareOpt2::Result sf1 = opt_f1.optPosOptParamsForEach();
EvalCompareOpt2 opt_f2(Settings::fMap, Settings::fCalib, only2nd, ignoreStaircases);
EvalCompareOpt2::Result sf2 = opt_f2.optPosOptParamsForEach();
EvalCompareOpt2 opt_f3(Settings::fMap, Settings::fCalib, only3rd, ignoreStaircases);
EvalCompareOpt2::Result sf3 = opt_f3.optPosOptParamsForEach();
// optimize per bbox
EvalCompareOpt2 opt_0H(Settings::fMap, Settings::fCalib, only0H, ignoreStaircases);
EvalCompareOpt2::Result sf0H = opt_0H.optPosOptParamsForEach();
EvalCompareOpt2 opt_0O(Settings::fMap, Settings::fCalib, only0O, ignoreStaircases);
EvalCompareOpt2::Result sf0O = opt_0O.optPosOptParamsForEach();
EvalCompareOpt2 opt_0I(Settings::fMap, Settings::fCalib, only0I, ignoreStaircases);
EvalCompareOpt2::Result sf0I = opt_0I.optPosOptParamsForEach();
EvalCompareOpt2 opt_1H(Settings::fMap, Settings::fCalib, only1H, ignoreStaircases);
EvalCompareOpt2::Result sf1H = opt_1H.optPosOptParamsForEach();
EvalCompareOpt2 opt_1O(Settings::fMap, Settings::fCalib, only1O, ignoreStaircases);
EvalCompareOpt2::Result sf1O = opt_1O.optPosOptParamsForEach();
EvalCompareOpt2 opt_1I(Settings::fMap, Settings::fCalib, only1I, ignoreStaircases);
EvalCompareOpt2::Result sf1I = opt_1I.optPosOptParamsForEach();
EvalCompareOpt2 opt_2H(Settings::fMap, Settings::fCalib, only2H, ignoreStaircases);
EvalCompareOpt2::Result sf2H = opt_2H.optPosOptParamsForEach();
EvalCompareOpt2 opt_3H(Settings::fMap, Settings::fCalib, only3H, ignoreStaircases);
EvalCompareOpt2::Result sf3H = opt_3H.optPosOptParamsForEach();
// save models to file
s1.model.saveXML(Settings::wifiAllFixed);
s2.model.saveXML(Settings::wifiAllOptPar);
s3.model.saveXML(Settings::wifiEachOptPar);
s4.model.saveXML(Settings::wifiEachOptParPos);
sf0.model.saveXML(Settings::wifiEachOptParPos_only0th);
sf1.model.saveXML(Settings::wifiEachOptParPos_only1st);
sf2.model.saveXML(Settings::wifiEachOptParPos_only2nd);
sf3.model.saveXML(Settings::wifiEachOptParPos_only3rd);
// fancy combined model
WiFiModelPerFloor wmpf(map);
wmpf.add(&sf0.model, map->floors[0]);
wmpf.add(&sf1.model, map->floors[1]);
wmpf.add(&sf2.model, map->floors[2]);
wmpf.add(&sf3.model, map->floors[3]);
wmpf.saveXML(Settings::wifiEachOptParPos_multimodel);
// ultra fancy combined model
WiFiModelPerBBox wmbb(map);
wmbb.add(&sf0H.model, bboxes0H);
wmbb.add(&sf0O.model, bboxes0O);
wmbb.add(&sf0I.model, bboxes0I);
wmbb.add(&sf1H.model, bboxes1H);
wmbb.add(&sf1O.model, bboxes1O);
wmbb.add(&sf1I.model, bboxes1I);
wmbb.add(&sf2H.model, bboxes2H);
wmbb.add(&sf3H.model, bboxes3H);
wmbb.saveXML(Settings::wifiEachOptParPos_perBBox);
PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}");
pef.add("\\small{empiric}", &s1.errSingle);
pef.add("\\small{real pos, opt params}", &s2.errSingle);
pef.add("\\small{real pos, opt params [per AP]}", &s3.errSingle);
pef.add("\\small{opt pos, opt params [per AP]}", &s4.errSingle);
pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-methods.tex");
pef.writePlotToFile(Settings::fPathGFX + "wifi-opt-error-hist-methods.gp");
pef.getGP() << "set key right bottom width -4 samplen 0.5\n";
pef.getGP() << "set rmargin 0.4\n";
pef.getGP() << "set tmargin 0.4\n";
pef.plot();
int i = 0; (void) i;
//return;
//sleep(1000);
}
std::vector<K::Statistics<float>> errorStatAllModels(Floorplan::IndoorMap* map) {
WiFiModelFactory fac(map);
WiFiFingerprints calib(Settings::fCalib);
VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
for (WiFiFingerprint& fp : calib.getFingerprints()) {
fp.measurements = vap.group(fp.measurements);
}
// calculate the error (model vs scan) for each fingerprint using the given model
auto calc = [&] (const WiFiModel* model, K::Statistics<float>* stats) {
for (const WiFiFingerprint& fp : calib.getFingerprints()) {
for (const WiFiMeasurement& m : fp.measurements.entries) {
const float scanRSSI = m.getRSSI();
const float modelRSSI = model->getRSSI(m.getAP().getMAC(), fp.pos_m);
if (modelRSSI != modelRSSI) {
std::cout << "unknown AP: " << m.getAP().getMAC().asString() << std::endl;
continue;
}
const float errAbs = std::abs(modelRSSI-scanRSSI);
stats->add(errAbs);
}
}
};
// models
WiFiModel* mdlAllFixed = fac.loadXML(Settings::wifiAllFixed);
K::Statistics<float> statsAllFixed;
calc(mdlAllFixed, &statsAllFixed);
WiFiModel* mdlAllOptPar = fac.loadXML(Settings::wifiAllOptPar);
K::Statistics<float> statsAllOptPar;
calc(mdlAllOptPar, &statsAllOptPar);
WiFiModel* mdlEachOptPar = fac.loadXML(Settings::wifiEachOptPar);
K::Statistics<float> statsEachOptPar;
calc(mdlEachOptPar, &statsEachOptPar);
WiFiModel* mdlEachOptParPos = fac.loadXML(Settings::wifiEachOptParPos);
K::Statistics<float> statsEachOptParPos;
calc(mdlEachOptParPos, &statsEachOptParPos);
WiFiModel* mdlFloor = fac.loadXML(Settings::wifiEachOptParPos_multimodel);
K::Statistics<float> statsFloor;
calc(mdlFloor, &statsFloor);
WiFiModel* mdlBbox = fac.loadXML(Settings::wifiEachOptParPos_perBBox);
K::Statistics<float> statsBbox;
calc(mdlBbox, &statsBbox);
return {
statsAllFixed,
statsAllOptPar,
statsEachOptPar,
statsEachOptParPos,
statsFloor,
statsBbox,
};
}
/** error plot for all used optimization models */
void errorPlotAllModels(Floorplan::IndoorMap* map) {
std::vector<K::Statistics<float>> errors = errorStatAllModels(map);
PlotErrFunc plot("", "fingerprints (%)");
plot.getPlot().getAxisX().setTicsLabelFormat("%h dB");
plot.add("\\noOptEmpiric{}", &errors[0]);
plot.add("\\optParamsAllAP{}", &errors[1]);
plot.add("\\optParamsEachAP{}", &errors[2]);
plot.add("\\optParamsPosEachAP{}", &errors[3]);
plot.add("\\optPerFloor{}", &errors[4]);
plot.add("\\optPerRegion{}", &errors[5]);
plot.getPlot().getKey().setVisible(true);
plot.getPlot().getKey().setPosition(K::GnuplotKey::Hor::RIGHT, K::GnuplotKey::Ver::BOTTOM);
// debug view
plot.setYRange(0, 95, 5);
plot.plot();
sleep(2);
plot.setYRange(95, 100, 1);
plot.plot();
sleep(2);
// LATEX
// plot.getGP() << "set lmargin 4.2\n";
// plot.getGP() << "set tmargin 0.1\n";
// plot.getGP() << "set rmargin 0.2\n";
// plot.getGP() << "set bmargin 1.9\n";
plot.getPlot().getMargin().set(4.2, 0.2, 0.1, 1.9);
plot.getPlot().getAxisY().setLabelOffset(3.0,0);
plot.getPlot().setStringMod(new K::GnuplotStringModLaTeX());
plot.getPlot().getKey().setSampleLength(0.5);
plot.getPlot().getKey().setWidthIncrement(+7.0);
plot.setYRange(0, 95, 5);
plot.getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 100));
plot.getPlot().getAxisY().setTicsStep(0, 25, 100);
plot.getGP().setTerminal("epslatex", K::GnuplotSize(8.6, 4.0));
plot.getPlot().getAxisX().setTicsStep(4); // 4dB
plot.getPlot().getAxisX().setRange(K::GnuplotAxis::Range(0, 16));
plot.getGP().setOutput(Settings::fPathGFX + "/wifi_model_error_0_95.tex");
plot.writePlotToFile(Settings::fPathGFX + "/wifi_model_error_0_95.gp");
plot.plot();
plot.getPlot().getKey().setVisible(false);
plot.setYRange(97, 100, 1);
plot.getPlot().getAxisY().setTicsStep(1);
plot.getGP().setTerminal("epslatex", K::GnuplotSize(8.6, 2.6));
plot.getPlot().getAxisX().setRange(K::GnuplotAxis::Range(5, 35));
plot.getPlot().getAxisX().setTicsStep(5); // 5dB
plot.getGP().setOutput(Settings::fPathGFX + "/wifi_model_error_95_100.tex");
plot.writePlotToFile(Settings::fPathGFX + "/wifi_model_error_95_100.gp");
plot.plot();
int i = 0; (void) i;
//sleep(1000);
}
/** error plot for the given stats. used for fingerprint errors */
void errorPlotNumFingerprints(const std::vector<K::Statistics<float>>& stats, const std::vector<std::string>& titles, const std::string& name) {
PlotErrFunc plot("", "fingerprints (%)");
plot.getPlot().getAxisX().setTicsLabelFormat("%h dB");
plot.getPlot().getKey().setVisible(true);
plot.getPlot().getKey().setPosition(K::GnuplotKey::Hor::RIGHT, K::GnuplotKey::Ver::BOTTOM);
plot.clear();
for (int j = 0; j < titles.size(); ++j) {
plot.add(titles[j], &stats[j]);
}
// // debug view
// plot.setYRange(0, 90, 5);
// plot.plot();
// sleep(5);
// plot.setYRange(90, 100, 5);
// plot.plot();
// sleep(5);
// LATEX
// plot.getGP() << "set lmargin 4.2\n";
// plot.getGP() << "set tmargin 0.1\n";
// plot.getGP() << "set rmargin 0.2\n";
// plot.getGP() << "set bmargin 1.9\n";
plot.getPlot().getMargin().set(4.2, 0.2, 0.1, 1.9);
plot.getPlot().getAxisY().setLabelOffset(3.0,0);
plot.getPlot().setStringMod(new K::GnuplotStringModLaTeX());
plot.getPlot().getKey().setSampleLength(0.5);
plot.getPlot().getKey().setWidthIncrement(-5.0);
plot.setYRange(0, 90, 5);
plot.getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 100));
plot.getPlot().getAxisY().setTicsStep(0, 25, 100);
plot.getGP().setTerminal("epslatex", K::GnuplotSize(8.6, 3.3));
plot.getPlot().getAxisX().setTicsStep(4); // 4dB
plot.getPlot().getAxisX().setRange(K::GnuplotAxis::Range(0, 16));
plot.getGP().setOutput(Settings::fPathGFX + "/" + name + "_0_90.tex");
plot.writePlotToFile(Settings::fPathGFX + "/" + name + "_0_90.gp");
plot.plot();
plot.getPlot().getKey().setVisible(false);
plot.setYRange(90, 100, 1);
plot.getPlot().getAxisY().setTicsStep(2);
plot.getGP().setTerminal("epslatex", K::GnuplotSize(8.6, 2.6));
plot.getPlot().getAxisX().setRange(K::GnuplotAxis::Range(5, 35));
plot.getPlot().getAxisX().setTicsStep(5); // 5dB
plot.getGP().setOutput(Settings::fPathGFX + "/" + name + "_90_100.tex");
plot.writePlotToFile(Settings::fPathGFX + "/" + name + "_90_100.gp");
plot.plot();
}
/** show all fingerprints within the building */
void plotAllFingerprints(Floorplan::IndoorMap* map) {
WiFiFingerprints calib(Settings::fCalib);
LeHelper::removeNonFHWS(calib);
VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
for (WiFiFingerprint& fp : calib.getFingerprints()) {
fp.measurements = vap.group(fp.measurements);
}
// estimate stats
K::Statistics<float> fpVisible;
for (const WiFiFingerprint& fp : calib.getFingerprints()) {
fpVisible.add(fp.measurements.entries.size());
}
Plotty* p = new Plotty(map);
p->buildFloorplan();
p->cpoints.clear();
for (const WiFiFingerprint& fp : calib.getFingerprints()) {
const K::GnuplotPoint3 gp(fp.pos_m.x, fp.pos_m.y, fp.pos_m.z);
const float size = fp.measurements.entries.size() / 10.0 * 1.0;
const Point3 pos = fp.pos_m - Point3(0,0,1.2);
Color c;
if (pos.z < 4) {c = Color::fromRGB(128,128,128);}
else if (pos.z < 6) {c = Color::fromRGB(255,96,96);}
else if (pos.z < 9) {c = Color::fromRGB(128,255,128);}
else {c = Color::fromRGB(128,128,255);}
K::GnuplotObjectPolygon* poly = p->addFloorRect(pos, size, c);
poly->setZIndex(pos.z + 0.1); // above the floor
const int visibleAPs = fp.measurements.entries.size();
if (visibleAPs == fpVisible.getMin() || visibleAPs == fpVisible.getMax()) {
p->addLabel("\\footnotesize{" + std::to_string(fp.measurements.entries.size()) + "}", pos+Point3(0,1,1)*0.25);
}
}
std::cout << fpVisible.asString() << std::endl;
p->splot.getCustom() << "set view equal xy\n";
p->splot.getCustom() << "unset border\n";
p->splot.getMargin().set(1, 0,0,0);
p->splot.getAxisX().setTicsVisible(false);
p->splot.getAxisY().setTicsVisible(false);
p->splot.getAxisZ().setTicsVisible(false);
p->splot.getAxisZ().setRange(K::GnuplotAxis::Range(-8, 19.5));
p->splot.getView().setCamera(74,30);
p->splot.getView().setScaleAll(3.8);
p->splot.getObjects().reOrderByZIndex();
p->plot();
p->gp.setTerminal("epslatex", K::GnuplotSize(8.6, 5.0));
p->gp.setOutput(Settings::fPathGFX + "/all_fingerprints.tex");
p->gp.writePlotToFile(Settings::fPathGFX + "/all_fingerprints.gp");
p->plot();
sleep(100);
}
/** show all walked paths */
void plotAllWalks(Floorplan::IndoorMap* map) {
using Walk = std::vector<Point3>;
Walk path1 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path1);
Walk path2 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path2);
//Walk path_toni_all_1 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path_toni_all_1);
//Walk path_toni_all_2 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path_toni_all_2);
Walk path_toni_inst_1 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path_toni_inst_1);
Walk path_toni_inst_2 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path_toni_inst_2);
Walk path_toni_inst_3 = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path_toni_inst_3);
const std::vector<Walk> walks = {
path1,
path2,
//path_toni_all_1, same as path1
//path_toni_all_2, same as path2
path_toni_inst_1,
path_toni_inst_2,
path_toni_inst_3
};
const std::vector<std::string> titles = {
"path 1", "path 2", "path 3", "path 4", "path 5"
};
Plotty* p = new Plotty(map);
p->buildFloorplan();
std::vector<std::string> colors = {"#000000", "#ff0000", "#00cc00", "#0000ff", "#009999", "#aa00aa"};
int i = 0;
for (const Walk& walk : walks) {
K::GnuplotSplotElementLines* line = new K::GnuplotSplotElementLines();
line->getStroke().setWidth(2);
line->getStroke().getColor().setHexStr(colors[i]);
line->setTitle(titles[i]);
//line->getStroke().setType( (i > 3) ? K::GnuplotDashtype::DASHED : K::GnuplotDashtype::SOLID );
float oy = 0;
if (i == 2) {oy -= 0.5;}
if (i == 3) {oy += 0.5;}
for (const Point3& pt : walk) {
line->add(K::GnuplotPoint3(pt.x, pt.y+oy, pt.z));
}
p->splot.add(line);
++i;
}
p->splot.getKey().setVisible(true);
//p->splot.getKey().setPosition(K::GnuplotKey::Hor::RIGHT, K::GnuplotKey::Ver::TOP);
p->splot.getKey().setPosition(K::GnuplotCoordinate2(0.99, 0.99, K::GnuplotCoordinateSystem::SCREEN));
p->splot.getKey().setSampleLength(0.5);
//p->splot.getKey().setWidthIncrement(-4);
p->splot.setStringMod(new K::GnuplotStringModLaTeX());
p->splot.getCustom() << "set view equal xy\n";
p->splot.getCustom() << "unset border\n";
p->splot.getMargin().set(1, 0,0,0);
p->splot.getAxisX().setTicsVisible(false);
p->splot.getAxisY().setTicsVisible(false);
p->splot.getAxisZ().setTicsVisible(false);
p->splot.getAxisZ().setRange(K::GnuplotAxis::Range(-8, 19.5));
p->splot.getView().setCamera(74,30);
p->splot.getView().setScaleAll(3.8);
p->splot.getObjects().reOrderByZIndex();
p->plot();
p->gp.setTerminal("epslatex", K::GnuplotSize(8.6, 5.0));
p->gp.setOutput(Settings::fPathGFX + "/all_walks.tex");
p->gp.writePlotToFile(Settings::fPathGFX + "/all_walks.gp");
p->plot();
sleep(100);
}
// build plots for the paper // build plots for the paper
void paperOutputs() { void paperOutputs() {
@@ -117,137 +583,51 @@ void paperOutputs() {
} }
// show all fingerprints
if (1 == 0) {
plotAllFingerprints(map);
}
// show all walks
if (1 == 0) {
plotAllWalks(map);
}
// perform varios AP-param optimizations // perform varios AP-param optimizations
// generate error plot showing the performance of each // generate error plot showing the performance of each
// save the resulting wifi-models to XML for later re-use during the walk-eval <<<<<< !!!! // save the resulting wifi-models to XML for later re-use during the walk-eval <<<<<< !!!!
if (1 == 0) { if (1 == 0) {
rebuildAllModels(map, 0);
// // use walks? /** detailled error analysis for above optimization routine */
// std::vector<std::pair<std::string, std::vector<int>>> calibWalks = { errorPlotAllModels(map);
// std::make_pair(Settings::path1a, Settings::GroundTruth::path1), }
// std::make_pair(Settings::path1b, Settings::GroundTruth::path1),
// std::make_pair(Settings::path2a, Settings::GroundTruth::path2),
// std::make_pair(Settings::path2b, Settings::GroundTruth::path2),
// };
// EvalCompareOpt2 opt1(Settings::fMap, calibWalks);
// all combined
auto removeNone = [] (const WiFiFingerprint& fp) -> bool {return false;};
// per floor
auto only0th = [] (const WiFiFingerprint& fp) -> bool {return std::abs(fp.pos_m.z - (1.3)) > 0.1;};
auto only1st = [] (const WiFiFingerprint& fp) -> bool {return std::abs(fp.pos_m.z - (4+1.3)) > 0.1;};
auto only2nd = [] (const WiFiFingerprint& fp) -> bool {return std::abs(fp.pos_m.z - (4+3.4+1.3)) > 0.1;};
auto only3rd = [] (const WiFiFingerprint& fp) -> bool {return std::abs(fp.pos_m.z - (4+3.4+3.4+1.3)) > 0.1;};
// per bbox
#include "bboxes.h"
auto only0H = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes0H.contains(fp.pos_m);};
auto only0O = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes0O.contains(fp.pos_m);};
auto only0I = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes0I.contains(fp.pos_m);};
auto only1H = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes1H.contains(fp.pos_m);};
auto only1O = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes1O.contains(fp.pos_m);};
auto only1I = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes1I.contains(fp.pos_m);};
auto only2H = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes2H.contains(fp.pos_m);};
auto only3H = [&] (const WiFiFingerprint& fp) -> bool {return !bboxes3H.contains(fp.pos_m);};
// use fingerprints? if (1 == 0) {
EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, removeNone);
rebuildAllModels(map,4);
std::vector<K::Statistics<float>> stats4 = errorStatAllModels(map);
rebuildAllModels(map,2);
std::vector<K::Statistics<float>> stats2 = errorStatAllModels(map);
rebuildAllModels(map,0,true);
std::vector<K::Statistics<float>> statsNoStairs = errorStatAllModels(map);
rebuildAllModels(map,0); // ensure all output files are overwritten with the "all fingerprints" opt!!!
std::vector<K::Statistics<float>> stats0 = errorStatAllModels(map);
// optimize using all floors // analyze all 5 opt strategies. skip the empiric one: stats0[0]
EvalCompareOpt2::Result s1 = opt1.fixedPosFixedParamsForAll(); //BREAK; for (int i = 1; i < 6; ++i) {
EvalCompareOpt2::Result s2 = opt1.fixedPosOptParamsForAll(); //BREAK; std::string name = "wifi_model_error_num_fingerprints_method_" + std::to_string(i);
EvalCompareOpt2::Result s3 = opt1.fixedPosOptParamsForEach(); //BREAK; errorPlotNumFingerprints(
EvalCompareOpt2::Result s4 = opt1.optPosOptParamsForEach(); //BREAK; {stats0[0], stats4[i], stats2[i], stats0[i], statsNoStairs[i]},
{"empiric", "25%", "50%", "100%", "no stairs"},
name
);
sleep(1);
}
sleep(1);
// optimize per floor
EvalCompareOpt2 opt_f0(Settings::fMap, Settings::fCalib, only0th);
EvalCompareOpt2::Result sf0 = opt_f0.optPosOptParamsForEach();
EvalCompareOpt2 opt_f1(Settings::fMap, Settings::fCalib, only1st);
EvalCompareOpt2::Result sf1 = opt_f1.optPosOptParamsForEach();
EvalCompareOpt2 opt_f2(Settings::fMap, Settings::fCalib, only2nd);
EvalCompareOpt2::Result sf2 = opt_f2.optPosOptParamsForEach();
EvalCompareOpt2 opt_f3(Settings::fMap, Settings::fCalib, only3rd);
EvalCompareOpt2::Result sf3 = opt_f3.optPosOptParamsForEach();
// optimize per bbox
EvalCompareOpt2 opt_0H(Settings::fMap, Settings::fCalib, only0H);
EvalCompareOpt2::Result sf0H = opt_0H.optPosOptParamsForEach();
EvalCompareOpt2 opt_0O(Settings::fMap, Settings::fCalib, only0O);
EvalCompareOpt2::Result sf0O = opt_0O.optPosOptParamsForEach();
EvalCompareOpt2 opt_0I(Settings::fMap, Settings::fCalib, only0I);
EvalCompareOpt2::Result sf0I = opt_0I.optPosOptParamsForEach();
EvalCompareOpt2 opt_1H(Settings::fMap, Settings::fCalib, only1H);
EvalCompareOpt2::Result sf1H = opt_1H.optPosOptParamsForEach();
EvalCompareOpt2 opt_1O(Settings::fMap, Settings::fCalib, only1O);
EvalCompareOpt2::Result sf1O = opt_1O.optPosOptParamsForEach();
EvalCompareOpt2 opt_1I(Settings::fMap, Settings::fCalib, only1I);
EvalCompareOpt2::Result sf1I = opt_1I.optPosOptParamsForEach();
EvalCompareOpt2 opt_2H(Settings::fMap, Settings::fCalib, only2H);
EvalCompareOpt2::Result sf2H = opt_2H.optPosOptParamsForEach();
EvalCompareOpt2 opt_3H(Settings::fMap, Settings::fCalib, only3H);
EvalCompareOpt2::Result sf3H = opt_3H.optPosOptParamsForEach();
// save models to file
s1.model.saveXML(Settings::wifiAllFixed);
s2.model.saveXML(Settings::wifiAllOptPar);
s3.model.saveXML(Settings::wifiEachOptPar);
s4.model.saveXML(Settings::wifiEachOptParPos);
sf0.model.saveXML(Settings::wifiEachOptParPos_only0th);
sf1.model.saveXML(Settings::wifiEachOptParPos_only1st);
sf2.model.saveXML(Settings::wifiEachOptParPos_only2nd);
sf3.model.saveXML(Settings::wifiEachOptParPos_only3rd);
// fancy combined model
WiFiModelPerFloor wmpf(map);
wmpf.add(&sf0.model, map->floors[0]);
wmpf.add(&sf1.model, map->floors[1]);
wmpf.add(&sf2.model, map->floors[2]);
wmpf.add(&sf3.model, map->floors[3]);
wmpf.saveXML(Settings::wifiEachOptParPos_multimodel);
// ultra fancy combined model
WiFiModelPerBBox wmbb(map);
wmbb.add(&sf0H.model, bboxes0H);
wmbb.add(&sf0O.model, bboxes0O);
wmbb.add(&sf0I.model, bboxes0I);
wmbb.add(&sf1H.model, bboxes1H);
wmbb.add(&sf1O.model, bboxes1O);
wmbb.add(&sf1I.model, bboxes1I);
wmbb.add(&sf2H.model, bboxes2H);
wmbb.add(&sf3H.model, bboxes3H);
wmbb.saveXML(Settings::wifiEachOptParPos_perBBox);
PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}");
pef.add("\\small{empiric}", &s1.errSingle);
pef.add("\\small{real pos, opt params}", &s2.errSingle);
pef.add("\\small{real pos, opt params [per AP]}", &s3.errSingle);
pef.add("\\small{opt pos, opt params [per AP]}", &s4.errSingle);
pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-methods.tex");
pef.writeCodeTo(Settings::fPathGFX + "wifi-opt-error-hist-methods.gp");
pef.getGP() << "set key right bottom width -4 samplen 0.5\n";
pef.getGP() << "set rmargin 0.4\n";
pef.getGP() << "set tmargin 0.4\n";
pef.plot();
int i = 0; (void) i;
//return;
//sleep(1000);
} }
@@ -302,7 +682,7 @@ void paperOutputs() {
pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5)); pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.tex"); pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.tex");
pef.writeCodeTo(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.gp"); pef.writePlotToFile(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.gp");
pef.getGP() << "set key right bottom width -3\n"; pef.getGP() << "set key right bottom width -3\n";
pef.getGP() << "set rmargin 0.4\n"; pef.getGP() << "set rmargin 0.4\n";
pef.getGP() << "set tmargin 0.4\n"; pef.getGP() << "set tmargin 0.4\n";
@@ -546,8 +926,47 @@ int main(void) {
} }
// show wifi multimodalities
if (1 == 1) { if (1 == 1) {
Plotty::Settings settings;
settings.maxZ = 8;
EvalWiFiGround ewg(map, Settings::wifiEachOptParPos_perBBox, settings);
//ewg.show(Settings::path1a, 30);
//ewg.show(Settings::path1a, 70);
//ewg.show(Settings::path1a, 90);
int hueGreen = 120*255/360;
int hueYellow = 60*255/360;
int hueBlue = 210*255/360;
int hueRed = 0*255/360;
ewg.addGT(Settings::path1a, 100, hueGreen, Settings::GroundTruth::path1);
ewg.addGT(Settings::path1a, 170-8, hueBlue, Settings::GroundTruth::path1);
ewg.addGT(Settings::path1a, 200, hueRed, Settings::GroundTruth::path1);
// green
ewg.add(Settings::path1a, 100, hueGreen);
// yellow
ewg.add(Settings::path1a, 170, hueBlue);
// red
ewg.add(Settings::path1a, 200, hueRed);
ewg.groundProb->getPlot().splot.getObjects().reOrderByZIndex();
ewg.groundProb->plotMe();
ewg.writeGP(Settings::fPathGFX, "wifiMultimodality");
sleep(1000);
}
if (1 == 0) {
std::vector<std::string> files = { std::vector<std::string> files = {
Settings::path1a, Settings::path1b, Settings::path1a, Settings::path1b,
Settings::path2a, Settings::path2b, Settings::path2a, Settings::path2b,
@@ -560,33 +979,51 @@ int main(void) {
std::vector<std::vector<int>> gtIndices = { std::vector<std::vector<int>> gtIndices = {
Settings::GroundTruth::path1, Settings::GroundTruth::path1, Settings::GroundTruth::path1, Settings::GroundTruth::path1,
Settings::GroundTruth::path2, Settings::GroundTruth::path2, Settings::GroundTruth::path2, Settings::GroundTruth::path2,
Settings::GroundTruth::path_toni_all_1, Settings::GroundTruth::path_toni_all_1, Settings::GroundTruth::path1, Settings::GroundTruth::path1,
Settings::GroundTruth::path_toni_all_2, Settings::GroundTruth::path_toni_all_2, Settings::GroundTruth::path2, Settings::GroundTruth::path2,
// Settings::GroundTruth::path_toni_all_1, Settings::GroundTruth::path_toni_all_1,
// Settings::GroundTruth::path_toni_all_2, Settings::GroundTruth::path_toni_all_2,
Settings::GroundTruth::path_toni_inst_1, Settings::GroundTruth::path_toni_inst_1, Settings::GroundTruth::path_toni_inst_1, Settings::GroundTruth::path_toni_inst_1,
Settings::GroundTruth::path_toni_inst_2, Settings::GroundTruth::path_toni_inst_2, Settings::GroundTruth::path_toni_inst_2, Settings::GroundTruth::path_toni_inst_2,
Settings::GroundTruth::path_toni_inst_3, Settings::GroundTruth::path_toni_inst_3, Settings::GroundTruth::path_toni_inst_3, Settings::GroundTruth::path_toni_inst_3,
}; };
// EvalWiFiPaths ewp(Settings::fMap); int numScans = 0;
// ewp.loadModel(Settings::wifiAllFixed, "empirc"); for (const std::string& file : files) {
// ewp.walks(files, gtIndices); Offline::FileReader reader(file);
numScans += reader.getWiFiGroupedByTime().size();
}
// ewp.loadModel(Settings::wifiEachOptParPos, "normal model"); std::cout << "num scans: " << numScans << std::endl;
// ewp.walks(files, gtIndices);
// ewp.loadModel(Settings::wifiEachOptParPos_multimodel, "model per floor");
// ewp.walks(files, gtIndices);
// ewp.loadModel(Settings::wifiEachOptParPos_perBBox, "model per region"); EvalWiFiPaths ewp(Settings::fMap);
// ewp.walks(files, gtIndices); ewp.loadModel(Settings::wifiAllFixed, "\\noOptEmpiric{}");
ewp.walks(files, gtIndices);
EvalWiFiPathMethods ewpm(Settings::fMap); ewp.loadModel(Settings::wifiEachOptParPos, "\\optParamsPosEachAP{}");
ewpm.loadModel(Settings::wifiEachOptParPos_perBBox, "model per region", "original", "alternative"); ewp.walks(files, gtIndices);
ewpm.walks(files, gtIndices);
// export for paper ewp.loadModel(Settings::wifiEachOptParPos_multimodel, "\\optPerFloor{}");
// using errFuncOtherExponential and only path1a, path1b ewp.walks(files, gtIndices);
//ewpm.writeGP(Settings::fPathGFX, "normalVsExp");
ewp.loadModel(Settings::wifiEachOptParPos_perBBox, "\\optPerRegion{}");
ewp.walks(files, gtIndices);
ewp.writeGP(Settings::fPathGFX, "modelPerformance");
// examine various modifications
if (1 == 0) {
EvalWiFiPathMethods ewpm(Settings::fMap);
ewpm.loadModel(Settings::wifiEachOptParPos_perBBox, "model per region", "original", "alternative");
ewpm.walks(files, gtIndices);
// export for paper
// using errFuncOtherExponential and only path1a, path1b
//ewpm.writeGP(Settings::fPathGFX, "normalVsExp");
}
sleep(10000); sleep(10000);

View File

@@ -306,7 +306,7 @@ private:
++updateCount; ++updateCount;
static PlotErrTime pet("\\small{time (sec)}", "\\small{error (m)}", "\\small{APs visible}"); static PlotErrTime pet("\\small{time (sec)}", "\\small{error (m)}", "\\small{APs visible}");
static PlotErrFunc pef("\\small{error (m)}", "\\small{updates (\\%)}"); static PlotErrFunc pef("\\small{error (m)}", "\\small{updates (\\%)}");
pef.showMarkers(true); pef.showMarkers(true, true);
std::cout << "update" << std::endl; std::cout << "update" << std::endl;

View File

@@ -31,12 +31,21 @@ class PlotErrFunc {
K::GnuplotPlot gplot; K::GnuplotPlot gplot;
//std::vector<std::string> colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"}; //std::vector<std::string> colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"};
std::vector<std::string> colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa"}; std::vector<std::string> colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa", "#aa00aa"};
bool markers = false; struct Markers {
bool median = false;
bool quantil75 = false;
} markers;
std::string codeFile; std::string codeFile;
struct Range {
int fromPercent = 0;
int toPercent = 0;
int increment = 0;
} range;
public: public:
@@ -44,8 +53,17 @@ public:
PlotErrFunc(const std::string& xLabel, const std::string& yLabel) { PlotErrFunc(const std::string& xLabel, const std::string& yLabel) {
gplot.getAxisX().setLabel(xLabel); gplot.getAxisX().setLabel(xLabel);
gplot.getAxisY().setLabel(yLabel); gplot.getAxisY().setLabel(yLabel);
gplot.getAxisX().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO)); setYRange(0, 90, 5);
gplot.getAxisY().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO)); //gplot.getAxisX().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO));
gplot.getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO));
}
/** set the percentage range to show */
void setYRange(const int fromPercent, const int toPercent, const int increment = 5) {
this->range.fromPercent = fromPercent;
this->range.toPercent = toPercent;
this->range.increment = increment;
gplot.getAxisY().setRange(K::GnuplotAxis::Range(this->range.fromPercent, K::GnuplotAxis::Range::AUTO));
} }
/** add one curve. Statistics<T> are allowed to be altered outside afterwards! */ /** add one curve. Statistics<T> are allowed to be altered outside afterwards! */
@@ -75,14 +93,18 @@ public:
return gplot; return gplot;
} }
void writeCodeTo(const std::string& file) { /** write the gnuplot commands to file for later re-use */
this->codeFile = file; void writePlotToFile(const std::string& file) {
this->gp.writePlotToFile(file);
} }
void showMarkers(const bool show) { /** whether to show additional markers */
this->markers = show; void showMarkers(const bool median, const bool quantil75) {
this->markers.median = median;
this->markers.quantil75 = quantil75;
} }
/** plot all curves */ /** plot all curves */
void plot() { void plot() {
@@ -100,42 +122,35 @@ public:
const float range = e.stats->getQuantile(0.85) - minX; const float range = e.stats->getQuantile(0.85) - minX;
const float space = range * 0.07; const float space = range * 0.07;
// 0 - 80% // [from:stepsSize:to]
for (int j = 0; j <= 85; j+= 5) { //for (int j = this->range.fromPercent; j <= this->range.toPercent; j += this->range.increment) {
for (int j = 0; j <= 100; j += this->range.increment) {
const float y = j / 100.0f; const float y = j / 100.0f;
const float x = e.stats->getQuantile(y); const float x = e.stats->getQuantile(y);
K::GnuplotPoint2 gp2(x, y*100); K::GnuplotPoint2 gp2(x, y*100);
e.line->add(gp2); e.line->add(gp2);
// show additional markers?
int id = (i+1) * 100;
if (markers) { if (j == 50 && markers.median) {
int id = (i+1) * 100; gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n";
if (j == 50) { gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n";
gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n";
gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n"; gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n";
gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n"; } else if (j == 75 && markers.quantil75) {
gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n"; gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n";
} else if (j == 75) { gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\n";
gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; gp << "set arrow " << id+7 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n";
gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\n"; gp << "set arrow " << id+8 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n";
gp << "set arrow " << id+7 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n";
gp << "set arrow " << id+8 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n";
}
} }
} }
} }
// render
gp.draw(gplot); gp.draw(gplot);
if (codeFile != "") {
std::ofstream out(codeFile);
out << gp.getBuffer();
out.close();
}
gp.flush(); gp.flush();
} }

View File

@@ -39,7 +39,7 @@ private:
struct Entry { struct Entry {
Point3 pos; Point3 pos;
K::GnuplotObjectPolygon* poly; K::GnuplotObjectPolygon* poly;
float max = 0; float max = -1;
float sum = 0; float sum = 0;
int cnt = 0; int cnt = 0;
}; };
@@ -51,19 +51,28 @@ private:
public: public:
/** ctor */ /** ctor */
PlotWiFiGroundProb(const Floorplan::IndoorMap* map) : map(map), plot(map) { PlotWiFiGroundProb(const Floorplan::IndoorMap* map, Plotty::Settings settings = Plotty::Settings()) : map(map), plot(map) {
buildEvalPoints();
plot.settings.outline = false; plot.settings = settings;
buildEvalPoints();
plot.buildFloorplan(); plot.buildFloorplan();
} }
Plotty& getPlot() {
return plot;
}
/** plot the ground-probability for the given measurement */
void show(const WiFiObserverFree& prob, const WiFiMeasurements& _mes) { void show(const WiFiObserverFree& prob, const WiFiMeasurements& _mes) {
VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
const WiFiMeasurements mes = vap.group(_mes); const WiFiMeasurements mes = vap.group(_mes);
// determine min/max probability
double min = +99999; double min = +99999;
double max = -99999; double max = -99999;
for (Entry& e : pos) { for (Entry& e : pos) {
@@ -71,9 +80,9 @@ public:
if (p < min) {min = p;} if (p < min) {min = p;}
if (p > max) {max = p;} if (p > max) {max = p;}
} }
double diff = max-min; const double diff = max-min;
// render
for (Entry& e : pos) { for (Entry& e : pos) {
const double p = prob.getProbability(e.pos, mes.entries.front().getTimestamp(), mes); const double p = prob.getProbability(e.pos, mes.entries.front().getTimestamp(), mes);
const double f = (p-min) / diff * 255; const double f = (p-min) / diff * 255;
@@ -85,11 +94,51 @@ public:
e.poly->getFill().setColor(c); e.poly->getFill().setColor(c);
e.max = f; e.max = f;
//} //}
if (f < 0.1) {
e.poly->setEnabled(false);
}
//plot.cpoints.add(K::GnuplotPoint3(pt.x, pt.y, pt.z), p); //plot.cpoints.add(K::GnuplotPoint3(pt.x, pt.y, pt.z), p);
} }
}
void showStrongest(const WiFiObserverFree& prob, const WiFiMeasurements& mes, const float hue) {
// determine min/max probability
double min = +99999;
double max = -99999;
for (Entry& e : pos) {
const double p = prob.getProbability(e.pos, mes.entries.front().getTimestamp(), mes);
if (p < min) {min = p;}
if (p > max) {max = p;}
}
const double diff = max-min;
// render
for (Entry& e : pos) {
// get probability within [0:255]
const double p = prob.getProbability(e.pos, mes.entries.front().getTimestamp(), mes);
const double f = (p-min) / diff * 255;
if (p != p) {
throw Exception("nan");
}
// overwrite weak entries
if (f > e.max) {
const K::GnuplotColor c = K::GnuplotColor::fromHSV(hue, f, 245);
e.poly->getFill().setColor(c);
e.max = f;
// disable unlikely onces to save performance and disk-space
e.poly->setEnabled(f > 32);
}
}
} }
@@ -143,6 +192,7 @@ private:
e.poly->close(); e.poly->close();
e.poly->getFill().setStyle(K::GnuplotFillStyle::SOLID); e.poly->getFill().setStyle(K::GnuplotFillStyle::SOLID);
e.poly->setStroke(K::GnuplotStroke::NONE()); e.poly->setStroke(K::GnuplotStroke::NONE());
e.poly->setZIndex(f->atHeight - 0.001); // between outline and obstacles
e.pos = Point3(x, y, f->atHeight); e.pos = Point3(x, y, f->atHeight);

View File

@@ -10,6 +10,9 @@
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h> #include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h> #include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h> #include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementPM3D.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementEmpty.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h> #include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementHistogram.h> #include <KLib/misc/gnuplot/GnuplotPlotElementHistogram.h>
#include <KLib/misc/gnuplot/objects/GnuplotObjects.h> #include <KLib/misc/gnuplot/objects/GnuplotObjects.h>
@@ -103,6 +106,11 @@ public:
K::GnuplotSplotElementLines mapOutlineConcrete; K::GnuplotSplotElementLines mapOutlineConcrete;
K::GnuplotSplotElementLines mapBBoxes; K::GnuplotSplotElementLines mapBBoxes;
K::GnuplotSplotElementEmpty emptyElem;
K::GnuplotSplotElementPM3D pm3doutline;
std::string codeFile; std::string codeFile;
struct Settings { struct Settings {
@@ -134,6 +142,9 @@ public:
mapOutlineDrywall.getStroke().getColor().setHexStr("#888888"); mapOutlineDrywall.getStroke().getColor().setHexStr("#888888");
mapOutlineGlass.getStroke().getColor().setHexStr("#888888"); mapOutlineGlass.getStroke().setType(K::GnuplotDashtype::DASHED); mapOutlineGlass.getStroke().getColor().setHexStr("#888888"); mapOutlineGlass.getStroke().setType(K::GnuplotDashtype::DASHED);
mapBBoxes.getStroke().setWidth(2); mapBBoxes.getStroke().setWidth(2);
splot.add(&emptyElem);
splot.add(&mapOutlineConcrete); splot.add(&mapOutlineConcrete);
splot.add(&mapOutlineDrywall); splot.add(&mapOutlineDrywall);
splot.add(&mapOutlineGlass); splot.add(&mapOutlineGlass);
@@ -144,6 +155,8 @@ public:
splot.add(&pathReal); pathReal.getStroke().setWidth(2); pathReal.getStroke().getColor().setHexStr("#000000"); splot.add(&pathReal); pathReal.getStroke().setWidth(2); pathReal.getStroke().getColor().setHexStr("#000000");
splot.add(&pathEst); pathEst.getStroke().setWidth(2); pathEst.getStroke().getColor().setHexStr("#0000ff"); splot.add(&pathEst); pathEst.getStroke().setWidth(2); pathEst.getStroke().getColor().setHexStr("#0000ff");
splot.add(&pm3doutline);
splot.add(&points); splot.add(&points);
points.setPointType(7); points.setPointType(7);
points.setPointSize(0.5); points.setPointSize(0.5);
@@ -239,7 +252,8 @@ public:
} }
void addLabel(const std::string& txt, const Point3 pos) { void addLabel(const std::string& txt, const Point3 pos) {
gp << "set label '" << txt << "' at " << pos.x << "," << pos.y << "," << pos.z << "\n"; //gp << "set label '" << txt << "' at " << pos.x << "," << pos.y << "," << pos.z << "\n";
splot.getCustom() << "set label '" << txt << "' at " << pos.x << "," << pos.y << "," << pos.z << " front\n";
} }
void addRectangle(const Point3 p1, const Point3 p2, const Color c, bool front = false, bool fill = true) { void addRectangle(const Point3 p1, const Point3 p2, const Color c, bool front = false, bool fill = true) {
@@ -271,11 +285,11 @@ public:
splot.getObjects().add(poly); splot.getObjects().add(poly);
} }
void addPolygon(const std::vector<Point3>& points, const std::string& color, bool front = false, bool fill = true, const float alpha = 1) { K::GnuplotObjectPolygon* addPolygon(const std::vector<Point3>& points, const std::string& color, bool front = false, bool fill = true, const float alpha = 1) {
for (const Point3 p : points) { for (const Point3 p : points) {
if (p.z < settings.minZ) {return;} if (p.z < settings.minZ) {return nullptr;}
if (p.z > settings.maxZ) {return;} if (p.z > settings.maxZ) {return nullptr;}
} }
const K::GnuplotFill pfill = (fill) ? (K::GnuplotFill(K::GnuplotFillStyle::SOLID, K::GnuplotColor::fromHexStr(color))) : (K::GnuplotFill::NONE()); const K::GnuplotFill pfill = (fill) ? (K::GnuplotFill(K::GnuplotFillStyle::SOLID, K::GnuplotColor::fromHexStr(color))) : (K::GnuplotFill::NONE());
@@ -303,6 +317,8 @@ public:
// gp << " fc rgb " << "'" << color << "'"; // gp << " fc rgb " << "'" << color << "'";
// gp << "\n"; // gp << "\n";
return poly;
} }
void setZRange(const float min, const float max) { void setZRange(const float min, const float max) {
@@ -310,7 +326,7 @@ public:
} }
void addFloorRect(const Point3 pos_m, const float size, Color c, float ratio = 1.0) { K::GnuplotObjectPolygon* addFloorRect(const Point3 pos_m, const float size, Color c, float ratio = 1.0) {
const Point3 p1 = pos_m + Point3(-size, -size/ratio, 0); const Point3 p1 = pos_m + Point3(-size, -size/ratio, 0);
const Point3 p2 = pos_m + Point3(+size, -size/ratio, 0); const Point3 p2 = pos_m + Point3(+size, -size/ratio, 0);
@@ -319,7 +335,7 @@ public:
std::vector<Point3> points = {p1,p2,p3,p4,p1}; std::vector<Point3> points = {p1,p2,p3,p4,p1};
addPolygon(points, c.toHEX(), false, true); return addPolygon(points, c.toHEX(), false, true);
// gp << "set object polygon from "; // gp << "set object polygon from ";
// for (size_t i = 0; i < points.size(); ++i) { // for (size_t i = 0; i < points.size(); ++i) {
@@ -397,6 +413,8 @@ public:
std::vector<Floorplan::Floor*> floors; std::vector<Floorplan::Floor*> floors;
BBox3 bbox = FloorplanHelper::getBBox(map);
// only some floors?? // only some floors??
if (settings.floors.empty()) { if (settings.floors.empty()) {
floors = map->floors; floors = map->floors;
@@ -406,33 +424,31 @@ public:
} }
} }
// mapOutlineDrywall.addSegment(
// K::GnuplotPoint3(bbox.getMin().x, bbox.getMin().y, bbox.getMin().z),
// K::GnuplotPoint3(bbox.getMax().x, bbox.getMax().y, bbox.getMax().z)
// );
splot.getAxisX().setRange(K::GnuplotAxis::Range(bbox.getMin().x, bbox.getMax().x));
splot.getAxisY().setRange(K::GnuplotAxis::Range(bbox.getMin().y, bbox.getMax().y));
splot.getAxisZ().setRange(K::GnuplotAxis::Range(0, 11));
// process each selected floor // process each selected floor
for (Floorplan::Floor* floor : floors) { for (Floorplan::Floor* floor : floors) {
// plot obstacles? const float vo = floor->atHeight * 4.5;
if (settings.obstacles) {
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
if (line) {
const K::GnuplotPoint3 p1(line->from.x, line->from.y, floor->atHeight);
const K::GnuplotPoint3 p2(line->to.x, line->to.y, floor->atHeight);
switch(line->material) {
case Floorplan::Material::CONCRETE: mapOutlineConcrete.addSegment(p1, p2); break;
case Floorplan::Material::GLASS: mapOutlineGlass.addSegment(p1, p2); break;
case Floorplan::Material::UNKNOWN:
case Floorplan::Material::DRYWALL: mapOutlineDrywall.addSegment(p1, p2); break;
}
}
}
}
// plot the floor's outline // plot the floor's outline
if (settings.outline) { if (settings.outline) {
for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { for (Floorplan::FloorOutlinePolygon* poly : floor->outline) {
K::GnuplotColor color = K::GnuplotColor::fromRGB(210,210,210);
if (poly->outdoor) {color = K::GnuplotColor::fromRGB(200, 240, 200);} if (floor->atHeight < settings.minZ) {continue;}
if (floor->atHeight > settings.maxZ) {continue;}
const float v = 180 + vo;
K::GnuplotColor color = K::GnuplotColor::fromRGB(v,v,v);
if (poly->outdoor) {color = K::GnuplotColor::fromRGB(180, 240, 180);}
//if (poly->method == Floorplan::OutlineMethod::REMOVE) {color = K::GnuplotColor::fromRGB(245,245,245);} //if (poly->method == Floorplan::OutlineMethod::REMOVE) {color = K::GnuplotColor::fromRGB(245,245,245);}
K::GnuplotFill filler(K::GnuplotFillStyle::SOLID, color); K::GnuplotFill filler(K::GnuplotFillStyle::SOLID, color);
K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, K::GnuplotStroke::NONE()); K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, K::GnuplotStroke::NONE());
@@ -441,7 +457,56 @@ public:
gpol->add(coord); gpol->add(coord);
} }
gpol->close(); gpol->close();
gpol->setZIndex(floor->atHeight-0.1); // below the lines
//gpol->setFront(true);
splot.getObjects().add(gpol); splot.getObjects().add(gpol);
}
}
// plot obstacles?
if (settings.obstacles) {
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
if (line) {
if (floor->atHeight < settings.minZ) {continue;}
if (floor->atHeight > settings.maxZ) {continue;}
// const K::GnuplotPoint3 p1(line->from.x, line->from.y, floor->atHeight);
// const K::GnuplotPoint3 p2(line->to.x, line->to.y, floor->atHeight);
// switch(line->material) {
// case Floorplan::Material::CONCRETE: mapOutlineConcrete.addSegment(p1, p2); break;
// case Floorplan::Material::GLASS: mapOutlineGlass.addSegment(p1, p2); break;
// case Floorplan::Material::UNKNOWN:
// case Floorplan::Material::DRYWALL: mapOutlineDrywall.addSegment(p1, p2); break;
// }
// K::GnuplotObjectArrow* arrow = new K::GnuplotObjectArrow(
// K::GnuplotCoordinate3(line->from.x, line->from.y, floor->atHeight, K::GnuplotCoordinateSystem::FIRST),
// K::GnuplotCoordinate3(line->to.x, line->to.y, floor->atHeight, K::GnuplotCoordinateSystem::FIRST)
// );
// arrow->setHead(K::GnuplotObjectArrow::Head::NONE);
// splot.getObjects().add(arrow);
const float v = 140 + vo;
// drawing outlines as polygon is a hack for correct depth-order in gnuplot
K::GnuplotColor color = K::GnuplotColor::fromRGB(v,v,v);
K::GnuplotFill filler = K::GnuplotFill(K::GnuplotFillStyle::EMPTY_BORDER, color);
K::GnuplotStroke stroke(K::GnuplotDashtype::NONE, 1, color);
//K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(K::GnuplotFill::NONE(), stroke);
K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, stroke);
//K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(K::GnuplotFill::NONE(), K::GnuplotStroke::NONE());
gpol->add(K::GnuplotCoordinate3(line->from.x, line->from.y, floor->atHeight, K::GnuplotCoordinateSystem::FIRST));
gpol->add(K::GnuplotCoordinate3(line->to.x, line->to.y, floor->atHeight, K::GnuplotCoordinateSystem::FIRST));
gpol->close();
gpol->setZIndex(floor->atHeight); // above the ground polygon
//gpol->setFront(true);
splot.getObjects().add(gpol);
}
} }
} }
@@ -450,7 +515,32 @@ public:
for (Floorplan::Stair* s : floor->stairs) { for (Floorplan::Stair* s : floor->stairs) {
std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(s->getParts(), floor); std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(s->getParts(), floor);
for (const Floorplan::Quad3& q : quads) { for (const Floorplan::Quad3& q : quads) {
addPolygon({q.p1, q.p2, q.p3, q.p4, q.p1}, "#c0c0c0"); // K::GnuplotObjectPolygon* poly = addPolygon({q.p1, q.p2, q.p3, q.p4, q.p1}, "#c0c0c0");
// if (poly) {
// poly->setZIndex(floor->atHeight+1.5); // above the floor
// }
const float v1 = 180 + q.p1.z * 4.5;
const float v2 = 140 + q.p1.z * 4.5;
const float z = (q.p1.z + q.p2.z + q.p3.z + q.p4.z) / 4.0f;
if (z < settings.minZ) {continue;}
if (z > settings.maxZ) {continue;}
K::GnuplotColor color = K::GnuplotColor::fromRGB(v1,v1,v1);
K::GnuplotColor color2 = K::GnuplotColor::fromRGB(v2,v2,v2);
K::GnuplotFill filler(K::GnuplotFillStyle::SOLID, color);
K::GnuplotStroke stroke(K::GnuplotDashtype::SOLID, 1, color2);
K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, stroke);
gpol->add(K::GnuplotCoordinate3(q.p1.x, q.p1.y, q.p1.z, K::GnuplotCoordinateSystem::FIRST));
gpol->add(K::GnuplotCoordinate3(q.p2.x, q.p2.y, q.p2.z, K::GnuplotCoordinateSystem::FIRST));
gpol->add(K::GnuplotCoordinate3(q.p3.x, q.p3.y, q.p3.z, K::GnuplotCoordinateSystem::FIRST));
gpol->add(K::GnuplotCoordinate3(q.p4.x, q.p4.y, q.p4.z, K::GnuplotCoordinateSystem::FIRST));
gpol->close();
gpol->setZIndex(z); // above the ground
splot.getObjects().add(gpol);
} }
} }
} }

View File

@@ -1,12 +1,5 @@
experiments
\todo{obwohl das angepasste modell doch recht gut laeuft und der fehler recht klein wird, sind immernoch stellen dabei, \section{Experiments}
wo es einfach nicht gut passt, unguenstige mehrdeutigkeiten vorliegen, oder regionen einfach nicht passen wie sie sollten.
das liegt teils auch daran, dass die fingerprints drehend aufgenommen wurden und beim laufen nach hinten durch den
menschen abgeschottet wird. auch zeitlicher verzug kann ein problem darstellen.}
\todo{GPS ist leider kaum eine hilfe. entweder kein empfang wegen ueberdachung oder abschattung, oder
zu kurz draußen um einen guten gps-fix zu bekommen.}
wir betrachten nur die fest-installierten APs die man meist anhand einer bestimmten mac-range ausmachen kann wir betrachten nur die fest-installierten APs die man meist anhand einer bestimmten mac-range ausmachen kann
portable geraete von studenten, beamer, aehnliches werden ignoriert portable geraete von studenten, beamer, aehnliches werden ignoriert
@@ -37,90 +30,248 @@ optimierungs input: alle 4 walks samt ground-truth
dann kommt fuer die 4 typen [fixed, all same par, each par, each par pos] dann kommt fuer die 4 typen [fixed, all same par, each par, each par pos]
log probability 50 75, meter 50, 75 log probability 50 75, meter 50, 75
path1
31.8|38.9 7.8|11.6
27.3|36.8 7.2|9.8
24.0|30.3 5.8|10.24
22.9|29.9 5.0|7.6
hoherer fehler weil mehr outdoor anteil
path2
32.0|42.4 12.6|20.9
28.4|35.2 10.1|16.1
27.0|34.0 7.0|10.1
25.4|33.3 8.0|17.2
je mehr outdoor, desto schlechter wird es.
outdoor schadet auch der optimierung
outdoor schadet mehr als indoor, weil das wifi modell fuer indoor noch halbwegs passt
aber fuer outdoor so garned
fenster sind metallbedampft und schirmen stark ab
siehe beispielgrafik
gps wird so schnell nicht warm, versagt denn auf dem hof als hilfestellung
reines wifi eval mittels num-opt springt stark durch die gegend reines wifi eval mittels num-opt springt stark durch die gegend
d.h. das bewegungsmodell rettet uns d.h. das bewegungsmodell rettet uns
kann man auch testen wenn man beim particle-filter das resampling ganz aus macht kann man auch testen wenn man beim particle-filter das resampling ganz aus macht
\todo{
we analyzed various paths throughout the whole building
}
\todo{
mit grafik: exp-dist vergroesert teils den abstand zu anderen locations , der GT selbst wird also besser,
aber an anderen stellen geht dafür der fehler hoch und kann zu verlaufen führen (z.B. treppenhaus)
}
% -------------------------------- optimization -------------------------------- %
% used reference measurements
\begin{figure}
{
\centering
\input{gfx/all_fingerprints.tex}
}
\label{fig:referenceMeasurements}
\caption{
Locations of the 121 reference measurements.
The size of each square denotes the number of permanently installed \docAPshort{}s
that are visible at this location,
and ranges between 2 and 22 with an average of 9.
}
\end{figure}
% visible APs:
% cnt(121) min(2.000000) max(22.000000) range(20.000000) med(8.000000) avg(9.322314) stdDev(4.386709)
\begin{figure}
\input{gfx/wifi_model_error_0_95.tex}
\input{gfx/wifi_model_error_95_100.tex}
\label{fig:wifiModelError}%
\caption{%
Comparison between different optimization strategies by examining the error (in \decibel) at each reference measurement.%
The higher the number of variable parameters, the better the model resembles real world conditions. %
}%
\end{figure}
\begin{figure}
\centering
\input{gfx/compare-wifi-in-out.tex}
\caption{
Measurable signal strengths of a testing \docAPshort{} (black dot).
While the signal diminishes slowly along the corridor (upper rectangle)
the metallised windows (dashed outline) attenuate the signal by over \SI{30}{\decibel} (lower rectangle).
}
\end{figure}
fenster sind metallbedampft und schirmen stark ab
siehe beispielgrafik
\todo{
distance between AP pos estimation and real position???
}
% -------------------------------- number of fingerprints -------------------------------- %
wie viele fingerprints sind genug?
Haengt vom modell ab
bei den einfachen modellen aendert sich erstmal nicht viel. man hat ja viele testdaten für ein modell mit wenigen parametern.
je mehr variable wird, z.B. position, und das ganze pro AP und nicht füer alle, desto wichtiger wird, dass die fingerprints passen.
neuralgische schwachpunkte wie betonierte treppenhäuser kann man weglassen, dadurch wird der rest etwas besser,
die treppenhäuser ansich aber natürlich nochmal schlechter. siehe \ref{fig:wifiNumFingerprints}
\begin{figure}
\input{gfx/wifi_model_error_num_fingerprints_method_5_0_90.tex}
\input{gfx/wifi_model_error_num_fingerprints_method_5_90_100.tex}
\label{fig:wifiNumFingerprints}%
\caption{%
number of fingerprints
}%
\end{figure}
% -------------------------------- wifi walk error -------------------------------- %
Using aforementioned model setups and the measurements $\mRssiVec$ determined by scanning for nearby \docAPshort{}s,
we can directly perform a location estimation by rewriting \refeq{eq:wifiProb}:
\begin{equation}
p(\mPosVec \mid \mRssiVec) =
\frac{p(\mRssiVec \mid \mPosVec) p(\mPosVec)}{p(\mRssiVec)}
\approx p(\mRssiVec \mid \mPosVec),\enskip
p(\mPosVec) = p(\mRssiVec) = \text{const}
.
\label{eq:wifiBayes}
\end{equation}
The pedestrian's current location $\mPosVec^*$ given $\mRssiVec$ satisfies
\begin{equation}
\mPosVec^* = \argmax_{\mPosVec}
p(\mRssiVec \mid \mPosVec)
.
\label{eq:bestWiFiPos}
\end{equation}
The quality of the estimated location is determined by comparing the estimation
$\mPosVec^*$ with the pedestrian's ground truth at the time the scan $\mRssiVec$
has been received.
We therefore conducted 10 walks on 5 different paths within our building,
each of which is defined by connecting several marker points at well known positions
(see figure \ref{fig:allWalks}).
Whenever the pedestrian reached such a marker, the current time was recorded.
Due to constant walking speeds, the ground-truth for any timestamp can be approximated
using linear interpolation between adjacent markers.
% walked paths
\begin{figure}
{
\centering
\input{gfx/all_walks.tex}
}
\label{fig:allWalks}
\caption{
Overview of all conducted paths.
Outdoor areas are marked in green.
}
\end{figure}
To estimate the performance of the prediction models, we compare the position estimation
for each \docWIFI{} measurement within the recorded paths (3756 \docAPshort{} scans in total)
against the corresponding ground-truth, which indicates the absolute 3D error in meter.
\begin{figure}
\input{gfx/modelPerformance_meter.tex}
\label{fig:modelPerformance}
\caption{
Error between ground truth and estimation using \refeq{eq:bestWiFiPos} depending
on the underlying signal strength prediction model
}
\end{figure}
As can be seen in figure \ref{fig:modelPerformance}, the quality of the location estimation
directly scales with the quality of the signal strength prediction model.
However, depending on the model, the maximal estimation error might increase (see \optParamsPosEachAP{}).
%
This is either due to multimodalities, where more than one area is possible based on the recent
\docWIFI{} observation, or optimization yields an overadaption where the average signal
strength prediction error is small, but the maximum error is dramatically increased for some regions.
% -------------------------------- plots indicating optimization issues -------------------------------- %
\begin{figure}
\input{gfx/wifiMultimodality.tex}
\label{fig:wifiMultimodality}
\caption{
Location probability \refeq{eq:bestWiFiPos} for three scans. Higher color intensities are more likely.
Ideally, places near the ground truth (black) are highly highly probable (green).
Often, other locations are just as likely as the ground truth (blue),
or the location with the highest probability does not match at all (red).
}
\end{figure}
Figure \ref{fig:wifiMultimodality} depicts aforementioned issues of multimodal (blue) or wrong (red) location
estimations. Filtering (\refeq{eq:recursiveDensity}) thus is highly recommended as minor errors are compensated
using other sensors and/or a movement model that prevents the estimation from leaping within the building.
However, if wrong sensor values (red) are observed for longer time periods, even filtering will produce erroneous
results and might get stranded (density is trapped e.g. within a room),
as the movement model is constrained by the actual floorplan.
% -------------------------------- other distributions, unseen APs, etc -------------------------------- % % -------------------------------- other distributions, unseen APs, etc -------------------------------- %
To reduce the amount of misclassifications, where other locations within the building are (almost) To reduce the amount such of misclassifications, where other locations within the building are
as likely (see \refeq{eq:wifiProb}) as the pedestrians actual location, we examined various as likely as the pedestrians actual location, we examined various approaches.
approaches. Unfortunately, none of which provided a viable enhancement under all conditions within Unfortunately, none of which provided a viable enhancement under all conditions for the performed walks:
the performed walks.
One possibility to dissolve an equal \docWIFI{}-likelihood between two (or more) locations within in the building
One possibility to dissolve an equal \docWIFI{}-likelihood between two (or more) locations within in the building is, to not only consider the \docAPshort{}s seen by the Smartphone, but also the \docAPshort{}s not seen
is, to not only consider the \docAPshort{}s seen by the Smartphone, but also the \docAPshort{}s not seen by the Smartphone. This additional information can be used to rule out all locations where this
by the Smartphone. Maybe there is an \docAP{} that should be visible at the other locations. However, \docAP{} should be received (high signal strength from the prediction model).
as the Smartphone did not see this \docAPshort{} the other location can be ruled out. % There might be an \docAP{} that should be visible at the other locations. However,
While this works in theory, evaluations revealed several issues: %as the Smartphone did not see this \docAPshort{} the other location can be ruled out.
While this works in theory, evaluations revealed several issues:
There is a chance that an \docAPshort{} is unseen during a scan due to packet collisions or
temporal effects within the surrounding. It thus might make sense to opt-out other locations There is a chance that even a nearby \docAPshort{} is unseen during a scan due to packet collisions or
only, if at least two \docAPshort{}s are missing. On the other hand, this obviously requires (at least) temporal effects within the surrounding. It thus might make sense to opt-out other locations
two \docAPshort{}s to actually be different between the two locations, which might not always be only, if at least two \docAPshort{}s are missing. On the other hand, this obviously requires (at least)
the case. two \docAPshort{}s to actually be different between the two locations, and requires a lot of permanently
installed transmitters to work out.
Also, this requires the signal strength prediction model to be fairly accurate. Within our testing
walks there are several places surrounded by concrete walls, which cause a harsh, local drop in signal strength. Furthermore, this requires the signal strength prediction model to be fairly accurate. Within our testing
The models used within this work will not accurately predict the signal strength for such locations. walks, several places are surrounded by concrete walls, which cause a harsh, local drop in signal strength.
Including \docAPshort{}s unseen by the Smartphone thus often increases the estimation error instead The models used within this work will not accurately predict the signal strength for such locations.
of fixing the multimodality. Including \docAPshort{}s unseen by the Smartphone thus often increases the estimation error instead
of fixing the multimodality.
We therefore examined variations of the probability calculation from \refeq{eq:wifiProb}.
Removing the strongest/weakest \docAPshort{} from $\mRssiVecWiFi{}$ yielded similar results.
While some estimations were improved, the overall estimation error increased for our walks,
as there are many situations where only a handful \docAP{}s can be seen. Removing (valid) We therefore examined variations of the probability calculation from \refeq{eq:wifiProb}.
information will highly increase the error for such situations. Removing the strongest/weakest \docAPshort{} from $\mRssiVecWiFi{}$ yielded similar results.
While some estimations were improved, the overall estimation error increased for our walks,
Using a more strict exponential distribution for as there are many situations where only a handful \docAP{}s can be seen. Removing (valid)
information will highly increase the error for such situations.
\begin{figure}
\input{gfx/wifiCompare_normalVsExp_cross.tex} Using a more strict exponential distribution for the model vs. scan comparison in \refeq{eq:wifiProb}
\input{gfx/wifiCompare_normalVsExp_meter.tex} had a positive effect on the misclassification error for some of the walks, but slightly increased
\label{fig:normalVsExponential} the estimation error (see figure \reffig{fig:normalVsExponential}) and thus produced negative side effects.
\caption{
Comparison between normal- (black) and exponential-distribution (red) for \refeq{eq:wifiProb}. \begin{figure}
While misclassifications are slightly reduced (upper chart), \input{gfx/wifiCompare_normalVsExp_cross.tex}
the error between ground-truth and estimation (lower chart) increases by \input{gfx/wifiCompare_normalVsExp_meter.tex}
about \SI{1}{\meter} for the median. \label{fig:normalVsExponential}
\caption{
Comparison between normal- (black) and exponential-distribution (red) for \refeq{eq:wifiProb}.
While misclassifications are slightly reduced (upper chart),
the median error between ground-truth and estimation (lower chart) increases by
about \SI{1}{\meter}.
}
\end{figure}
\todo{
wir wollen nicht, dass die position des ground-truths durch das wifi so wahrscheinlich wie möglich ist,
wir wollen dass die position des ground-truth einfach eine höhere wahrscheinlichkeit hat, als alle anderen punkte im gebäude
das pruefen wir ab
} }
\end{figure}
\todo{ \todo{
erkenntnisse: erkenntnisse:
@@ -141,32 +292,33 @@ kann man auch testen wenn man beim particle-filter das resampling ganz aus macht
} }
\todo{
das bbox modell hat probleme an den uebergängen zwischen bboxes da dort teils starke spruenge sind
die nicht immer in der realität so auch vorliegen. z.B. z-wechsel machen teils probleme.
hier wäre ein kontinuierliches modell hilfreich bzw interpolation in randbereichen
}
\todo{ % REAL WALKS
wenn ich beim fingerprinten einen AP an einer stelle NICHT gesehen habe, \todo{obwohl das angepasste modell doch recht gut laeuft und der fehler recht klein wird, sind immernoch stellen dabei,
ist das auch eine aussage für die model optimierung.. da kann dann sicher keine signatlstaerke > -90 an der stelle raus kommen wo es einfach nicht gut passt, unguenstige mehrdeutigkeiten vorliegen, oder regionen einfach nicht passen wie sie sollten.
} das liegt teils auch daran, dass die fingerprints drehend aufgenommen wurden und beim laufen nach hinten durch den
menschen abgeschottet wird. auch zeitlicher verzug kann ein problem darstellen.}
\todo{GPS ist leider kaum eine hilfe. entweder kein empfang wegen ueberdachung oder abschattung, oder
zu kurz draußen um einen guten gps-fix zu bekommen.}
\todo{
das bbox modell hat probleme an den uebergängen zwischen bboxes da dort teils starke spruenge sind
die nicht immer in der realität so auch vorliegen. z.B. z-wechsel machen teils probleme.
hier wäre ein kontinuierliches modell hilfreich bzw interpolation in randbereichen
}
\todo{
wenn ich beim fingerprinten einen AP an einer stelle NICHT gesehen habe,
ist das auch eine aussage für die model optimierung.. da kann dann sicher keine signatlstaerke > -90 an der stelle raus kommen
}
\todo{gps wird so schnell nicht warm, versagt denn auf dem hof als hilfestellung}
\todo{
wir wollen nicht, dass die position des ground-truths durch das wifi so wahrscheinlich wie möglich ist,
wir wollen dass die position des ground-truth einfach eine höhere wahrscheinlichkeit hat, als alle anderen punkte im gebäude
das pruefen wir ab
}
\begin{figure}
\centering
\input{gfx/compare-wifi-in-out.tex}
\caption{
Measurable signal strengths of a testing \docAPshort{} (black dot).
While the signal diminishes slowly along the corridor (upper rectangle)
the metallised windows (dashed outline) attenuate the signal by over \SI{30}{\decibel} (lower rectangle).
}
\end{figure}
ware das grid-model nicht da, wuerde der outdoor teil richtig schlecht laufen, ware das grid-model nicht da, wuerde der outdoor teil richtig schlecht laufen,
weil das wlan hier absolut ungenau ist.. da die partikel aber aufgrund des vorherigen weil das wlan hier absolut ungenau ist.. da die partikel aber aufgrund des vorherigen
@@ -200,3 +352,6 @@ der einfluss jedoch recht groß sein, siehe den fingerprint plot von
dem einen ausgewählten AP dem einen ausgewählten AP
wenn noch zeit ist: wie aendert sich die model prediction wenn man z.B. nur die haelfte der referenzmessungen nimmt? wenn noch zeit ist: wie aendert sich die model prediction wenn man z.B. nur die haelfte der referenzmessungen nimmt?

View File

@@ -1,5 +1,10 @@
relatedwork relatedwork
wifi anfänge von radar (microsoft) etc wifi anfänge von radar (microsoft) etc
\cite{radar} \cite{horus} \cite{secureAndRobust}
andere methoden neben signalstärke
\cite{TimeDifferenceOfArrival1} \cite{TOAAOA}
\cite{Ebner-15} \cite{Ebner-15}

View File

@@ -24,3 +24,13 @@
\newcommand{\docsRSSI}{RSSI} \newcommand{\docsRSSI}{RSSI}
\newcommand{\docDSimplex}{downhill-simplex} \newcommand{\docDSimplex}{downhill-simplex}
% optimizations
\newcommand{\noOptEmpiric}{empiric params}
\newcommand{\optParamsAllAP}{optimization 1}
\newcommand{\optParamsEachAP}{optimization 2}
\newcommand{\optParamsPosEachAP}{optimization 3}
\newcommand{\optPerFloor}{model per floor}
\newcommand{\optPerRegion}{model per region}

View File

@@ -108,7 +108,7 @@ private:
Plotty* plot; Plotty* plot;
PlotWiFiGroundProb* groundProb; //PlotWiFiGroundProb* groundProb;
public: public:
@@ -133,10 +133,10 @@ public:
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
pef = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}"); pef = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}");
pef->showMarkers(false); pef->showMarkers(false, false);
pef2 = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}"); pef2 = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}");
pef2->showMarkers(false); pef2->showMarkers(false, false);
pet = new PlotErrTime("walktime (seconds)", "\\small{error (m)}", ""); pet = new PlotErrTime("walktime (seconds)", "\\small{error (m)}", "");
pet->getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 25)); pet->getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 25));

View File

@@ -39,21 +39,25 @@ class EvalWiFiGround {
private: private:
Floorplan::IndoorMap* map;
WiFiModel* mdl; WiFiModel* mdl;
WiFiObserverFree* obs; WiFiObserverFree* obs;
public:
PlotWiFiGroundProb* groundProb; PlotWiFiGroundProb* groundProb;
public: public:
EvalWiFiGround(Floorplan::IndoorMap* map, const std::string calibModel) { EvalWiFiGround(Floorplan::IndoorMap* map, const std::string calibModel, Plotty::Settings settings = Plotty::Settings()) : map(map) {
mdl = WiFiModelFactory(map).loadXML(calibModel); mdl = WiFiModelFactory(map).loadXML(calibModel);
obs = new WiFiObserverFree(8.0, *mdl); obs = new WiFiObserverFree(8.0, *mdl);
groundProb = new PlotWiFiGroundProb(map); groundProb = new PlotWiFiGroundProb(map, settings);
} }
void show(const std::string walkFile) { void show(const std::string walkFile, const int idx = -1) {
Offline::FileReader reader(walkFile); Offline::FileReader reader(walkFile);
int cnt = 0; int cnt = 0;
@@ -72,6 +76,68 @@ public:
} }
void add(const std::string walkFile, const int idx, const float hue) {
VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
Offline::FileReader reader(walkFile);
const auto& entry = reader.getWiFiGroupedByTime()[idx];
const WiFiMeasurements& _mes = entry.data;
const WiFiMeasurements mes = vap.group(_mes);
groundProb->showStrongest(*obs, mes, hue);
//groundProb->plotMe();
//std::this_thread::sleep_for(std::chrono::milliseconds(2));
//groundProb->update();
//groundProb->plotMe();
int i = 0; (void) i;
}
void addGT(const std::string walkFile, const int idx, const float hue, const std::vector<int>& path) {
Offline::FileReader reader(walkFile);
const auto& entry = reader.getWiFiGroupedByTime()[idx];
const WiFiMeasurements& mes = entry.data;
// gt at the time of the wifi measurement
const Timestamp ts = mes.entries.front().getTimestamp();
const Point3 pt = reader.getGroundTruth(map, path).get(ts);
Color c1 = Color::fromRGB(0,0,0);
Color c2 = Color::fromHSV(hue, 255, 255);
groundProb->getPlot().addFloorRect(pt+Point3(0,0,0.200), 1.5, c1);
groundProb->getPlot().addFloorRect(pt+Point3(0,0,0.201), 1.0, c2);
}
void writeGP(const std::string& path, const std::string& name) {
const std::string file = path + "/" + name;
groundProb->getPlot().splot.getCustom() << "unset border \n";
groundProb->getPlot().splot.getCustom() << "set view equal xy\n";
groundProb->getPlot().splot.getAxisX().setTicsVisible(false);
groundProb->getPlot().splot.getAxisY().setTicsVisible(false);
groundProb->getPlot().splot.getAxisZ().setTicsVisible(false);
groundProb->getPlot().splot.getAxisZ().setRange(-12.500000, 21.200000);
groundProb->getPlot().splot.getView().setCamera(72, 28);
groundProb->getPlot().splot.getView().setScaleAll(5.5);
groundProb->getPlot().gp.setTerminal("epslatex", K::GnuplotSize(8.6, 4.0));
groundProb->getPlot().gp.setOutput(file+".tex");
groundProb->getPlot().gp.writePlotToFile(file+".gp");
groundProb->plotMe();
groundProb->getPlot().gp.writePlotToFile("");
}
}; };
#endif // EVALWIFIGROUND_H #endif // EVALWIFIGROUND_H

View File

@@ -83,15 +83,15 @@ public:
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
pef_m = new PlotErrFunc("error (meter)", "measurements (%)"); pef_m = new PlotErrFunc("error (meter)", "measurements (%)");
pef_m->showMarkers(false); pef_m->showMarkers(false, false);
pef_p = new PlotErrFunc("-log(p(..))", "measurements (%)"); pef_p = new PlotErrFunc("-log(p(..))", "measurements (%)");
pef_p->showMarkers(false); pef_p->showMarkers(false, false);
pef_p->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO)); pef_p->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO));
pef_c = new PlotErrFunc("cross error", "measurements (%)"); pef_c = new PlotErrFunc("cross error", "measurements (%)");
pef_c->showMarkers(false); pef_c->showMarkers(false, false);
pef_c->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO)); pef_c->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO));
@@ -220,9 +220,9 @@ public:
pef.getGP() << "set tmargin 0.1\n"; pef.getGP() << "set tmargin 0.1\n";
pef.getGP() << "set rmargin 0.4\n"; pef.getGP() << "set rmargin 0.4\n";
pef.getGP() << "set bmargin 2.0\n"; pef.getGP() << "set bmargin 2.0\n";
pef.writeCodeTo(file+".gp"); pef.writePlotToFile(file+".gp");
pef.plot(); pef.plot();
pef.writeCodeTo(""); pef.writePlotToFile("");
} }
void walk(const std::string& fPath, const std::vector<int> gtIndices) { void walk(const std::string& fPath, const std::vector<int> gtIndices) {

View File

@@ -72,16 +72,44 @@ public:
// how to handle VAPs // how to handle VAPs
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
pef_m = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}"); pef_m = new PlotErrFunc("error (m)", "measurements (%)");
pef_m->showMarkers(false); pef_m->showMarkers(false, false);
pef_p = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}"); pef_p = new PlotErrFunc("-log(p(..))", "measurements (%)");
pef_p->showMarkers(false); pef_p->showMarkers(false, false);
pef_p->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO)); pef_p->getPlot().getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO));
} }
void writeGP(const std::string& path, const std::string& name) {
writeGP(*pef_m, K::GnuplotSize(8.6, 3.3), path + "/" + name + "_meter");
writeGP(*pef_p, K::GnuplotSize(8.6, 3.3), path + "/" + name + "_logprob");
}
void writeGP(PlotErrFunc& pef, K::GnuplotSize size, const std::string& file) {
pef.getGP().setTerminal("epslatex", size);
pef.getGP().setOutput(file+".tex");
pef.getPlot().getKey().setVisible(true);
pef.getPlot().getKey().setSampleLength(0.5);
pef.getPlot().getKey().setPosition(K::GnuplotKey::Hor::RIGHT, K::GnuplotKey::Ver::BOTTOM);
pef.getPlot().getKey().setWidthIncrement(7);
pef.getPlot().getAxisY().setLabelOffset(2.5, 0);
pef.getPlot().getAxisY().setTicsStep(0, 25, 95);
// MANUAL AXIS RANGE SETTINGS
pef.getPlot().getAxisX().setRange(K::GnuplotAxis::Range(0,25));
pef.getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0,95));
pef.getPlot().getAxisX().setLabel("");
pef.getPlot().setStringMod(new K::GnuplotStringModLaTeX());
pef.getPlot().getMargin().set(4.5, 0.4, 0.1, 2.0);
pef.writePlotToFile(file+".gp");
pef.plot();
pef.writePlotToFile("");
}
void loadModel(const std::string& xmlFile, const std::string& name) { void loadModel(const std::string& xmlFile, const std::string& name) {