diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c3f02c..607fcf6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ ADD_DEFINITIONS( -fstack-protector-all -g3 - -O2 + -O0 -march=native -DWITH_TESTS diff --git a/EvalCompareOpt2.h b/EvalCompareOpt2.h index 034056b..b4d3319 100644 --- a/EvalCompareOpt2.h +++ b/EvalCompareOpt2.h @@ -24,6 +24,13 @@ #include +// FOR TESTING +#define FAST 1 +#define QUALITY 2 +#define MODE QUALITY + +//#define DO_SHOW_FINGERPRINTS + class EvalCompareOpt2 { int power = 1; @@ -36,14 +43,14 @@ class EvalCompareOpt2 { public: /** ctor with map and fingerprints */ - EvalCompareOpt2(const std::string& mapFile, const std::string& fpFile, std::function remove) { + EvalCompareOpt2(const std::string& mapFile, const std::string& fpFile, std::function remove, bool removeStaircases = false) { setup(mapFile); // load fingerprints calib = WiFiFingerprints(fpFile); // if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);} -// if (ignoreStaircases) {calib = LeHelper::removeStaircases(calib);} + if (removeStaircases) {calib = LeHelper::removeStaircases(calib);} // if (ignoreIndoor) {calib = LeHelper::removeIndoor(calib);} calib = LeHelper::removeIf(calib, remove); @@ -120,7 +127,9 @@ private: } // plot +#ifdef DO_SHOW_FINGERPRINTS LeHelper::plot(map, calib); +#endif } @@ -205,8 +214,13 @@ public: // use simplex float params[3] = {-40, 2, -8}; K::NumOptAlgoDownhillSimplex opt(3); +#if MODE == FAST opt.setMaxIterations(50); + opt.setNumRestarts(10); +#elif MODE == QUALITY + opt.setMaxIterations(200); opt.setNumRestarts(25); +#endif opt.calculateOptimum(optFunc, params); // // use genetic @@ -257,8 +271,13 @@ public: // use simplex float params[3] = {-40, 2, -8}; K::NumOptAlgoDownhillSimplex opt(3); - opt.setMaxIterations(50); - opt.setNumRestarts(25); +#if MODE == FAST + opt.setMaxIterations(50); + opt.setNumRestarts(10); +#elif MODE == QUALITY + opt.setMaxIterations(200); + opt.setNumRestarts(25); +#endif opt.calculateOptimum(optFunc, params); // // use genetic @@ -352,8 +371,15 @@ public: }; K::NumOptAlgoRangeRandom 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); diff --git a/Settings.h b/Settings.h index cb72e5e..12c45a0 100644 --- a/Settings.h +++ b/Settings.h @@ -111,10 +111,10 @@ namespace Settings { // OTHER2017 [frank] const std::vector path1 = {40, 41, 42, 43, 44, 45, 1, 0, 46, 47, 48, 49, 50, 51, 52}; const std::vector path2 = {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66}; - const std::vector 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 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 path_toni_all_1 = {40,41,42,43,44,45, 1, 0, 46,47,48,49,50,51,52}; - const std::vector path_toni_all_2 = {53,54,55,56,57,58,59,60,61,62,63,64,65,66}; + //const std::vector 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 path_toni_all_2 = {53,54,55,56,57,58,59,60,61,62,63,64,65,66}; // same as PATH2 const std::vector path_toni_inst_1 = {0,1,2,3,4,5,6,7,8,9,10}; const std::vector 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}; diff --git a/main.cpp b/main.cpp index 27c2d11..6813501 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,472 @@ #include "plots/PlotErrFunc.h" + +void rebuildAllModels(Floorplan::IndoorMap* map, const int skip = 0, bool ignoreStaircases = false) { + +// // use walks? +// std::vector>> 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> 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* 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 statsAllFixed; + calc(mdlAllFixed, &statsAllFixed); + + WiFiModel* mdlAllOptPar = fac.loadXML(Settings::wifiAllOptPar); + K::Statistics statsAllOptPar; + calc(mdlAllOptPar, &statsAllOptPar); + + WiFiModel* mdlEachOptPar = fac.loadXML(Settings::wifiEachOptPar); + K::Statistics statsEachOptPar; + calc(mdlEachOptPar, &statsEachOptPar); + + WiFiModel* mdlEachOptParPos = fac.loadXML(Settings::wifiEachOptParPos); + K::Statistics statsEachOptParPos; + calc(mdlEachOptParPos, &statsEachOptParPos); + + WiFiModel* mdlFloor = fac.loadXML(Settings::wifiEachOptParPos_multimodel); + K::Statistics statsFloor; + calc(mdlFloor, &statsFloor); + + WiFiModel* mdlBbox = fac.loadXML(Settings::wifiEachOptParPos_perBBox); + K::Statistics 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> 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>& stats, const std::vector& 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 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; + + 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 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 titles = { + "path 1", "path 2", "path 3", "path 4", "path 5" + }; + + Plotty* p = new Plotty(map); + p->buildFloorplan(); + + std::vector 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 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 // generate error plot showing the performance of each // save the resulting wifi-models to XML for later re-use during the walk-eval <<<<<< !!!! if (1 == 0) { - -// // use walks? -// std::vector>> 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); - - // 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);}; + rebuildAllModels(map, 0); + /** detailled error analysis for above optimization routine */ + errorPlotAllModels(map); + } - // use fingerprints? - EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, removeNone); + if (1 == 0) { + rebuildAllModels(map,4); + std::vector> stats4 = errorStatAllModels(map); + rebuildAllModels(map,2); + std::vector> stats2 = errorStatAllModels(map); + rebuildAllModels(map,0,true); + std::vector> statsNoStairs = errorStatAllModels(map); + rebuildAllModels(map,0); // ensure all output files are overwritten with the "all fingerprints" opt!!! + std::vector> stats0 = errorStatAllModels(map); - // 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; + // analyze all 5 opt strategies. skip the empiric one: stats0[0] + for (int i = 1; i < 6; ++i) { + std::string name = "wifi_model_error_num_fingerprints_method_" + std::to_string(i); + errorPlotNumFingerprints( + {stats0[0], stats4[i], stats2[i], stats0[i], statsNoStairs[i]}, + {"empiric", "25%", "50%", "100%", "no stairs"}, + name + ); + 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); + sleep(1); } @@ -302,7 +682,7 @@ void paperOutputs() { pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5)); 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 rmargin 0.4\n"; pef.getGP() << "set tmargin 0.4\n"; @@ -546,8 +926,47 @@ int main(void) { } + // show wifi multimodalities 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 files = { Settings::path1a, Settings::path1b, Settings::path2a, Settings::path2b, @@ -560,33 +979,51 @@ int main(void) { std::vector> gtIndices = { Settings::GroundTruth::path1, Settings::GroundTruth::path1, 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::path1, Settings::GroundTruth::path1, + 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_2, Settings::GroundTruth::path_toni_inst_2, Settings::GroundTruth::path_toni_inst_3, Settings::GroundTruth::path_toni_inst_3, }; -// EvalWiFiPaths ewp(Settings::fMap); -// ewp.loadModel(Settings::wifiAllFixed, "empirc"); -// ewp.walks(files, gtIndices); + int numScans = 0; + for (const std::string& file : files) { + Offline::FileReader reader(file); + numScans += reader.getWiFiGroupedByTime().size(); + } -// ewp.loadModel(Settings::wifiEachOptParPos, "normal model"); -// ewp.walks(files, gtIndices); + std::cout << "num scans: " << numScans << std::endl; -// ewp.loadModel(Settings::wifiEachOptParPos_multimodel, "model per floor"); -// ewp.walks(files, gtIndices); -// ewp.loadModel(Settings::wifiEachOptParPos_perBBox, "model per region"); -// ewp.walks(files, gtIndices); + EvalWiFiPaths ewp(Settings::fMap); + ewp.loadModel(Settings::wifiAllFixed, "\\noOptEmpiric{}"); + ewp.walks(files, gtIndices); - EvalWiFiPathMethods ewpm(Settings::fMap); - ewpm.loadModel(Settings::wifiEachOptParPos_perBBox, "model per region", "original", "alternative"); - ewpm.walks(files, gtIndices); + ewp.loadModel(Settings::wifiEachOptParPos, "\\optParamsPosEachAP{}"); + ewp.walks(files, gtIndices); - // export for paper - // using errFuncOtherExponential and only path1a, path1b - //ewpm.writeGP(Settings::fPathGFX, "normalVsExp"); + ewp.loadModel(Settings::wifiEachOptParPos_multimodel, "\\optPerFloor{}"); + ewp.walks(files, gtIndices); + + 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); diff --git a/pf/EvalWalk.h b/pf/EvalWalk.h index 7ff47a3..93db6a5 100644 --- a/pf/EvalWalk.h +++ b/pf/EvalWalk.h @@ -306,7 +306,7 @@ private: ++updateCount; static PlotErrTime pet("\\small{time (sec)}", "\\small{error (m)}", "\\small{APs visible}"); static PlotErrFunc pef("\\small{error (m)}", "\\small{updates (\\%)}"); - pef.showMarkers(true); + pef.showMarkers(true, true); std::cout << "update" << std::endl; diff --git a/plots/PlotErrFunc.h b/plots/PlotErrFunc.h index 6a42b57..03a2356 100644 --- a/plots/PlotErrFunc.h +++ b/plots/PlotErrFunc.h @@ -31,12 +31,21 @@ class PlotErrFunc { K::GnuplotPlot gplot; //std::vector colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"}; - std::vector colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa"}; + std::vector colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa", "#aa00aa"}; - bool markers = false; + struct Markers { + bool median = false; + bool quantil75 = false; + } markers; std::string codeFile; + struct Range { + int fromPercent = 0; + int toPercent = 0; + int increment = 0; + } range; + public: @@ -44,8 +53,17 @@ public: PlotErrFunc(const std::string& xLabel, const std::string& yLabel) { gplot.getAxisX().setLabel(xLabel); gplot.getAxisY().setLabel(yLabel); - gplot.getAxisX().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO)); - gplot.getAxisY().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO)); + setYRange(0, 90, 5); + //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 are allowed to be altered outside afterwards! */ @@ -75,14 +93,18 @@ public: return gplot; } - void writeCodeTo(const std::string& file) { - this->codeFile = file; + /** write the gnuplot commands to file for later re-use */ + void writePlotToFile(const std::string& file) { + this->gp.writePlotToFile(file); } - void showMarkers(const bool show) { - this->markers = show; + /** whether to show additional markers */ + void showMarkers(const bool median, const bool quantil75) { + this->markers.median = median; + this->markers.quantil75 = quantil75; } + /** plot all curves */ void plot() { @@ -100,42 +122,35 @@ public: const float range = e.stats->getQuantile(0.85) - minX; const float space = range * 0.07; - // 0 - 80% - for (int j = 0; j <= 85; j+= 5) { + // [from:stepsSize:to] + //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 x = e.stats->getQuantile(y); K::GnuplotPoint2 gp2(x, y*100); e.line->add(gp2); - - - if (markers) { - int id = (i+1) * 100; - if (j == 50) { - gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; - gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n"; - gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n"; - gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n"; - } else if (j == 75) { - gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; - gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\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"; - } + // show additional markers? + int id = (i+1) * 100; + if (j == 50 && markers.median) { + gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; + gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n"; + gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n"; + gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n"; + } else if (j == 75 && markers.quantil75) { + gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; + gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\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); - - if (codeFile != "") { - std::ofstream out(codeFile); - out << gp.getBuffer(); - out.close(); - } - gp.flush(); } diff --git a/plots/PlotWiFiGroundProb.h b/plots/PlotWiFiGroundProb.h index ca3f3ca..6a6f58e 100644 --- a/plots/PlotWiFiGroundProb.h +++ b/plots/PlotWiFiGroundProb.h @@ -39,7 +39,7 @@ private: struct Entry { Point3 pos; K::GnuplotObjectPolygon* poly; - float max = 0; + float max = -1; float sum = 0; int cnt = 0; }; @@ -51,19 +51,28 @@ private: public: /** ctor */ - PlotWiFiGroundProb(const Floorplan::IndoorMap* map) : map(map), plot(map) { - buildEvalPoints(); + PlotWiFiGroundProb(const Floorplan::IndoorMap* map, Plotty::Settings settings = Plotty::Settings()) : map(map), plot(map) { - plot.settings.outline = false; + plot.settings = settings; + + buildEvalPoints(); plot.buildFloorplan(); + + + } + Plotty& getPlot() { + return plot; + } + + /** plot the ground-probability for the given measurement */ void show(const WiFiObserverFree& prob, const WiFiMeasurements& _mes) { VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); - const WiFiMeasurements mes = vap.group(_mes); + // determine min/max probability double min = +99999; double max = -99999; for (Entry& e : pos) { @@ -71,9 +80,9 @@ public: if (p < min) {min = p;} if (p > max) {max = p;} } - double diff = max-min; - + const double diff = max-min; + // render for (Entry& e : pos) { const double p = prob.getProbability(e.pos, mes.entries.front().getTimestamp(), mes); const double f = (p-min) / diff * 255; @@ -85,11 +94,51 @@ public: e.poly->getFill().setColor(c); e.max = f; //} + if (f < 0.1) { + e.poly->setEnabled(false); + } //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->getFill().setStyle(K::GnuplotFillStyle::SOLID); e.poly->setStroke(K::GnuplotStroke::NONE()); + e.poly->setZIndex(f->atHeight - 0.001); // between outline and obstacles e.pos = Point3(x, y, f->atHeight); diff --git a/plots/Plotty.h b/plots/Plotty.h index b5ab00e..b41d243 100644 --- a/plots/Plotty.h +++ b/plots/Plotty.h @@ -10,6 +10,9 @@ #include #include #include +#include +#include + #include #include #include @@ -103,6 +106,11 @@ public: K::GnuplotSplotElementLines mapOutlineConcrete; K::GnuplotSplotElementLines mapBBoxes; + K::GnuplotSplotElementEmpty emptyElem; + + + K::GnuplotSplotElementPM3D pm3doutline; + std::string codeFile; struct Settings { @@ -134,6 +142,9 @@ public: mapOutlineDrywall.getStroke().getColor().setHexStr("#888888"); mapOutlineGlass.getStroke().getColor().setHexStr("#888888"); mapOutlineGlass.getStroke().setType(K::GnuplotDashtype::DASHED); mapBBoxes.getStroke().setWidth(2); + + splot.add(&emptyElem); + splot.add(&mapOutlineConcrete); splot.add(&mapOutlineDrywall); splot.add(&mapOutlineGlass); @@ -144,6 +155,8 @@ public: 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(&pm3doutline); + splot.add(&points); points.setPointType(7); points.setPointSize(0.5); @@ -239,7 +252,8 @@ public: } 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) { @@ -271,11 +285,11 @@ public: splot.getObjects().add(poly); } - void addPolygon(const std::vector& points, const std::string& color, bool front = false, bool fill = true, const float alpha = 1) { + K::GnuplotObjectPolygon* addPolygon(const std::vector& points, const std::string& color, bool front = false, bool fill = true, const float alpha = 1) { for (const Point3 p : points) { - if (p.z < settings.minZ) {return;} - if (p.z > settings.maxZ) {return;} + if (p.z < settings.minZ) {return nullptr;} + if (p.z > settings.maxZ) {return nullptr;} } 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 << "\n"; + return poly; + } 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 p2 = pos_m + Point3(+size, -size/ratio, 0); @@ -319,7 +335,7 @@ public: std::vector points = {p1,p2,p3,p4,p1}; - addPolygon(points, c.toHEX(), false, true); + return addPolygon(points, c.toHEX(), false, true); // gp << "set object polygon from "; // for (size_t i = 0; i < points.size(); ++i) { @@ -397,6 +413,8 @@ public: std::vector floors; + BBox3 bbox = FloorplanHelper::getBBox(map); + // only some floors?? if (settings.floors.empty()) { 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 for (Floorplan::Floor* floor : floors) { - // plot obstacles? - if (settings.obstacles) { - for (Floorplan::FloorObstacle* obs : floor->obstacles) { - Floorplan::FloorObstacleLine* line = dynamic_cast(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; - } - - - } - } - } + const float vo = floor->atHeight * 4.5; // plot the floor's outline if (settings.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);} K::GnuplotFill filler(K::GnuplotFillStyle::SOLID, color); K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, K::GnuplotStroke::NONE()); @@ -441,7 +457,56 @@ public: gpol->add(coord); } gpol->close(); + gpol->setZIndex(floor->atHeight-0.1); // below the lines + //gpol->setFront(true); splot.getObjects().add(gpol); + + } + } + + // plot obstacles? + if (settings.obstacles) { + for (Floorplan::FloorObstacle* obs : floor->obstacles) { + Floorplan::FloorObstacleLine* line = dynamic_cast(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) { std::vector quads = Floorplan::getQuads(s->getParts(), floor); 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); + } } } diff --git a/tex/chapters/experiments.tex b/tex/chapters/experiments.tex index d7fe84f..beaaaaf 100644 --- a/tex/chapters/experiments.tex +++ b/tex/chapters/experiments.tex @@ -1,12 +1,5 @@ -experiments -\todo{obwohl das angepasste modell doch recht gut laeuft und der fehler recht klein wird, sind immernoch stellen dabei, -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.} +\section{Experiments} 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 @@ -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] 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 d.h. das bewegungsmodell rettet uns 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 -------------------------------- % - To reduce the amount of misclassifications, where other locations within the building are (almost) - as likely (see \refeq{eq:wifiProb}) as the pedestrians actual location, we examined various - approaches. Unfortunately, none of which provided a viable enhancement under all conditions within - the performed walks. - - 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 - by the Smartphone. Maybe there is an \docAP{} that should be visible at the other locations. However, - 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 - only, if at least two \docAPshort{}s are missing. On the other hand, this obviously requires (at least) - two \docAPshort{}s to actually be different between the two locations, which might not always be - the case. - - 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. - The models used within this work will not accurately predict the signal strength for such locations. - 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) - information will highly increase the error for such situations. - - Using a more strict exponential distribution for - - \begin{figure} - \input{gfx/wifiCompare_normalVsExp_cross.tex} - \input{gfx/wifiCompare_normalVsExp_meter.tex} - \label{fig:normalVsExponential} - \caption{ - Comparison between normal- (black) and exponential-distribution (red) for \refeq{eq:wifiProb}. - While misclassifications are slightly reduced (upper chart), - the error between ground-truth and estimation (lower chart) increases by - about \SI{1}{\meter} for the median. + To reduce the amount such of misclassifications, where other locations within the building are + as likely as the pedestrians actual location, we examined various approaches. + Unfortunately, none of which provided a viable enhancement under all conditions for the performed walks: + + 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 + by the Smartphone. This additional information can be used to rule out all locations where this + \docAP{} should be received (high signal strength from the prediction model). + % There might be an \docAP{} that should be visible at the other locations. However, + %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 even a nearby \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 + only, if at least two \docAPshort{}s are missing. On the other hand, this obviously requires (at least) + two \docAPshort{}s to actually be different between the two locations, and requires a lot of permanently + installed transmitters to work out. + + Furthermore, this requires the signal strength prediction model to be fairly accurate. Within our testing + walks, several places are surrounded by concrete walls, which cause a harsh, local drop in signal strength. + The models used within this work will not accurately predict the signal strength for such locations. + 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) + information will highly increase the error for such situations. + + Using a more strict exponential distribution for the model vs. scan comparison in \refeq{eq:wifiProb} + had a positive effect on the misclassification error for some of the walks, but slightly increased + the estimation error (see figure \reffig{fig:normalVsExponential}) and thus produced negative side effects. + + \begin{figure} + \input{gfx/wifiCompare_normalVsExp_cross.tex} + \input{gfx/wifiCompare_normalVsExp_meter.tex} + \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{ 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{ - 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 -} + % REAL WALKS + \todo{obwohl das angepasste modell doch recht gut laeuft und der fehler recht klein wird, sind immernoch stellen dabei, + 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, 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 wenn noch zeit ist: wie aendert sich die model prediction wenn man z.B. nur die haelfte der referenzmessungen nimmt? + + + diff --git a/tex/chapters/relatedwork.tex b/tex/chapters/relatedwork.tex index 9a96764..995fc6f 100644 --- a/tex/chapters/relatedwork.tex +++ b/tex/chapters/relatedwork.tex @@ -1,5 +1,10 @@ relatedwork 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} diff --git a/tex/misc/keywords.tex b/tex/misc/keywords.tex index 5d729f6..33de07e 100644 --- a/tex/misc/keywords.tex +++ b/tex/misc/keywords.tex @@ -24,3 +24,13 @@ \newcommand{\docsRSSI}{RSSI} \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} diff --git a/wifi/EvalWiFi.h b/wifi/EvalWiFi.h index 6bdef06..fe3f051 100644 --- a/wifi/EvalWiFi.h +++ b/wifi/EvalWiFi.h @@ -108,7 +108,7 @@ private: Plotty* plot; - PlotWiFiGroundProb* groundProb; + //PlotWiFiGroundProb* groundProb; public: @@ -133,10 +133,10 @@ public: vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); pef = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}"); - pef->showMarkers(false); + pef->showMarkers(false, false); pef2 = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}"); - pef2->showMarkers(false); + pef2->showMarkers(false, false); pet = new PlotErrTime("walktime (seconds)", "\\small{error (m)}", ""); pet->getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 25)); diff --git a/wifi/EvalWiFiGround.h b/wifi/EvalWiFiGround.h index d72e681..15481aa 100644 --- a/wifi/EvalWiFiGround.h +++ b/wifi/EvalWiFiGround.h @@ -39,21 +39,25 @@ class EvalWiFiGround { private: + Floorplan::IndoorMap* map; WiFiModel* mdl; WiFiObserverFree* obs; + +public: + PlotWiFiGroundProb* groundProb; 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); 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); 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& 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 diff --git a/wifi/EvalWiFiPathMethods.h b/wifi/EvalWiFiPathMethods.h index 68f3e12..6fed5da 100644 --- a/wifi/EvalWiFiPathMethods.h +++ b/wifi/EvalWiFiPathMethods.h @@ -83,15 +83,15 @@ public: vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); 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->showMarkers(false); + pef_p->showMarkers(false, false); 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->showMarkers(false); + pef_c->showMarkers(false, false); 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 rmargin 0.4\n"; pef.getGP() << "set bmargin 2.0\n"; - pef.writeCodeTo(file+".gp"); + pef.writePlotToFile(file+".gp"); pef.plot(); - pef.writeCodeTo(""); + pef.writePlotToFile(""); } void walk(const std::string& fPath, const std::vector gtIndices) { diff --git a/wifi/EvalWiFiPaths.h b/wifi/EvalWiFiPaths.h index a3ae92d..5152882 100644 --- a/wifi/EvalWiFiPaths.h +++ b/wifi/EvalWiFiPaths.h @@ -72,16 +72,44 @@ public: // how to handle VAPs vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); - pef_m = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}"); - pef_m->showMarkers(false); + pef_m = new PlotErrFunc("error (m)", "measurements (%)"); + pef_m->showMarkers(false, false); - pef_p = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}"); - pef_p->showMarkers(false); + pef_p = new PlotErrFunc("-log(p(..))", "measurements (%)"); + pef_p->showMarkers(false, false); 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) {