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 63e4c74..034056b 100644 --- a/EvalCompareOpt2.h +++ b/EvalCompareOpt2.h @@ -167,7 +167,16 @@ public: Result fixedPosOptParamsForAll() { - // resulting error using the given txp,exp,waf for ALL APs + + // get fingerprints for each AP [once] + std::unordered_map> fps; + for (const auto _ap : mapAPs) { + const Floorplan::AccessPoint* ap = _ap.first; + const MACAddress mac(ap->mac); + fps[mac] = calib.getFingerprintsFor(mac); + } + + // resulting error auto errFunc = [&] (const float txp, const float exp, const float waf) -> Result { Result res(map); @@ -179,7 +188,7 @@ public: const MACAddress mac(ap->mac); const Point3 pos_m = ap->getPos(floor); res.model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params - K::Statistics err = getError(mac, res.model); + K::Statistics err = getError(mac, res.model, fps[mac]); res.errAvg.add(err.getAvg()); res.errSingle.add(err); } @@ -188,16 +197,16 @@ public: }; - // to-be-optimized function: reduce average error among all fingerprints/AP's + // to-be-optimized function auto optFunc = [&] (const float* params) { - return errFunc(params[0], params[1], params[2]).errSingle.getAvg(); + return whatToOpt(errFunc(params[0], params[1], params[2]).errSingle); }; // use simplex float params[3] = {-40, 2, -8}; K::NumOptAlgoDownhillSimplex opt(3); - opt.setMaxIterations(40); - opt.setNumRestarts(20); + opt.setMaxIterations(50); + opt.setNumRestarts(25); opt.calculateOptimum(optFunc, params); // // use genetic @@ -232,7 +241,7 @@ public: const Point3 pos_m = ap->getPos(floor); const std::vector fps = getFingerprints(mac); - // resulting error using the given txp,exp,waf for ALL APs + // resulting error auto errFunc = [&] (const float txp, const float exp, const float waf) -> K::Statistics { WiFiModelLogDistCeiling model(map); // new, empty model for this AP model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params @@ -240,16 +249,16 @@ public: return err; }; - // to-be-optimized function: reduce average error among all fingerprints/AP's + // to-be-optimized function auto optFunc = [&] (const float* params) { - return errFunc(params[0], params[1], params[2]).getAvg(); + return whatToOpt(errFunc(params[0], params[1], params[2])); }; // use simplex float params[3] = {-40, 2, -8}; K::NumOptAlgoDownhillSimplex opt(3); - opt.setMaxIterations(40); - opt.setNumRestarts(20); + opt.setMaxIterations(50); + opt.setNumRestarts(25); opt.calculateOptimum(optFunc, params); // // use genetic @@ -289,7 +298,7 @@ public: const MACAddress mac(ap->mac); const std::vector fps = getFingerprints(mac); - // resulting error using the given txp,exp,waf for ALL APs + // resulting error auto errFunc = [&] (const Point3 pos_m, const float txp, const float exp, const float waf) -> K::Statistics { WiFiModelLogDistCeiling model(map); // new, empty model for this AP model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params @@ -297,10 +306,10 @@ public: return err; }; - // to-be-optimized function: reduce average error among all fingerprints/AP's + // to-be-optimized function auto optFunc = [&] (const float* params) { const Point3 pos_m(params[0], params[1], params[2]); - return errFunc(pos_m, params[3], params[4], params[5]).getAvg(); // AVG + return whatToOpt(errFunc(pos_m, params[3], params[4], params[5])); }; // params @@ -344,7 +353,7 @@ public: K::NumOptAlgoRangeRandom opt(valRegion); opt.setPopulationSize(300); - opt.setNumIerations(75); + opt.setNumIerations(100); opt.calculateOptimum(optFunc, params); @@ -376,6 +385,14 @@ public: private: + // the stats to optimize + static inline float whatToOpt(const K::Statistics& s) { + //return s.getAvg() + s.getStdDev() / 2 + s.getMax() / 4; // for dBm + //return s.getAvg() + s.getStdDev() * 2 + s.getMax() / 2; // for probability + //return s.getQuantile(0.96); + return s.getAvg(); + } + std::vector getFingerprints(const MACAddress& mac) { // get all fingerprints where the given mac was seen @@ -432,10 +449,14 @@ private: if (scan_rssi < -100) {throw Exception("scan rssi out of range for " + mac.asString());} if (scan_rssi > -35) {throw Exception("scan rssi out of range for " + mac.asString());} - // difference - //const float err = model_rssi - scan_rssi; - const float err = -std::log(Distribution::Normal::getProbability(model_rssi, 7, scan_rssi)); - float aErr = std::abs(err); + // dB error + const float err = model_rssi - scan_rssi; + + // probability matching error + //const float err = -std::log(Distribution::Normal::getProbability(model_rssi, 8, scan_rssi)); + + // quadratic + float aErr = std::pow(std::abs(err), 2); // penalty // if (model.getAP(mac).waf > -2) {aErr += 9999;} diff --git a/EvalWiFi.h b/EvalWiFi.h index 1eb9ed4..94ec152 100644 --- a/EvalWiFi.h +++ b/EvalWiFi.h @@ -5,6 +5,7 @@ #include "Indoor/sensors/radio/setup/WiFiFingerprint.h" #include "Indoor/sensors/radio/setup/WiFiFingerprints.h" #include "Indoor/sensors/radio/setup/WiFiOptimizer.h" +#include "Indoor/sensors/radio/model/WiFiModels.h" #include "Indoor/sensors/radio/VAPGrouper.h" #include "Indoor/sensors/offline/FileReader.h" @@ -32,6 +33,7 @@ #include "CSV.h" #include +#include template class Line { @@ -122,7 +124,7 @@ public: // opt = new WiFiOptimizer::LogDistCeiling(map, *vap, *calib, WiFiOptimizer::LogDistCeiling::Mode::MEDIUM); // how to handle VAPs - vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN); + vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); pef = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}"); pef->showMarkers(false); @@ -130,10 +132,10 @@ public: pef2 = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}"); pef2->showMarkers(false); - pet = new PlotErrTime("2", "3", ""); - pet->getPlot().setRangeY(K::GnuplotAxisRange(0, 40)); + pet = new PlotErrTime("walktime (seconds)", "\\small{error (m)}", ""); + pet->getPlot().getAxisY().setRange(K::GnuplotAxis::Range(0, 40)); - pet2 = new PlotErrTime("2", "3", ""); + pet2 = new PlotErrTime("walktime (seconds)", "\\small{-log(p(groundTruth | measurements))", ""); plot = new Plotty(map); plot->buildFloorplan(); @@ -143,35 +145,25 @@ public: void load(const std::string& xmlFile, const std::string& name) { + WiFiModelFactory fac(map); + // setup the model - WiFiModelLogDistCeiling* wiModel = new WiFiModelLogDistCeiling(map); - wiModel->loadXML(xmlFile); - this->wiModel = wiModel; + this->wiModel = fac.loadXML(xmlFile); // fire build(name); } -// void fixedParams(const float txp, const float exp, const float waf) { - -// // how to handle VAPs -// vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); - -// // setup the model -// WiFiModelLogDistCeiling* wiModel = new WiFiModelLogDistCeiling(map); -// wiModel->loadAPs(map, *vap, txp, exp, waf, false); -// this->wiModel = wiModel; - -// // fire -// run(); - -// } private: void build(const std::string& name) { + K::GnuplotStroke stroke( + K::GnuplotDashtype::SOLID, 1.5, + K::GnuplotColor::AUTO() + ); static int idx = -1; ++idx; @@ -179,6 +171,7 @@ private: Line path; K::GnuplotSplotElementLines* gpPath = new K::GnuplotSplotElementLines(); + gpPath->setStroke(stroke); plot->splot.add(gpPath); K::Statistics* stats = new K::Statistics(); @@ -275,11 +268,11 @@ private: // parameters float params[3]; + // USE GENETIC // std::minstd_rand gen; // std::uniform_real_distribution distX(mapBBox.getMin().x, mapBBox.getMax().x); // std::uniform_real_distribution distY(mapBBox.getMin().y, mapBBox.getMax().y); // std::uniform_real_distribution distZ(mapBBox.getMin().z, mapBBox.getMax().z); - // // initializer for the optimizer: random position within the map's bbox // auto init = [&] (const int childIdx, float* params) { // (void) childIdx; @@ -287,13 +280,13 @@ private: // params[1] = distY(gen); // params[2] = distZ(gen); // }; - // K::NumOptAlgoGenetic opt(3); // opt.setPopulationSize(400); // opt.setMaxIterations(20); // opt.calculateOptimum(func, params, init); + // USE RANGE RANDOM WITH COOLING using LeOpt = K::NumOptAlgoRangeRandom; const std::vector valRegion = { LeOpt::MinMax(mapBBox.getMin().x, mapBBox.getMax().x), // x @@ -304,7 +297,7 @@ private: K::NumOptAlgoRangeRandom opt(valRegion); opt.setPopulationSize(200); opt.setNumIerations(50); - //opt.calculateOptimum(func, params); + opt.calculateOptimum(func, params); std::cout << params[0] << "," << params[1] << "," << params[2] << std::endl; @@ -325,27 +318,38 @@ private: plot->gp << "set arrow 1 at " << gt.x << "," << gt.y << "," << gt.z << " to " << gt.x << "," << gt.y << "," << (gt.z+1) << "\n"; // error - //const float err_m = gt.xy().getDistance(curEst.xy()); - const float err_m = gt.getDistance(curEst); + const float err_m = gt.xy().getDistance(curEst.xy()); // 2D + //const float err_m = gt.getDistance(curEst); // 3D stats->add(err_m); float gtFloat[3] = {gt.x, gt.y, gt.z}; const double probOnGT = -func(gtFloat); - const double logProbOnGT = -log(probOnGT); - statsProbOnGT->add(logProbOnGT); + const double logProbOnGT = -std::log10(probOnGT); + const double logProbOnGTNorm = (logProbOnGT/mes.entries.size()); + + statsProbOnGT->add(logProbOnGTNorm); + + static int xxx = 0; ++xxx; // plot err pet->addErr(ts, err_m, idx); - pet2->addErr(ts, logProbOnGT/mes.entries.size(), idx); + pet2->addErr(ts, logProbOnGTNorm, idx); - // fire - plot->plot(); + if (xxx%2 == 0) { + + plot->plot(); + + pet->plot(); + pef->plot(); + + pet2->plot(); + pef2->plot(); + + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } -// pet->plot(); -// pef->plot(); - pet2->plot(); - pef2->plot(); } diff --git a/EvalWifiOptResult.h b/EvalWifiOptResult.h new file mode 100644 index 0000000..e8eb79a --- /dev/null +++ b/EvalWifiOptResult.h @@ -0,0 +1,219 @@ +#ifndef EVALWIFIOPTRESULT_H +#define EVALWIFIOPTRESULT_H + +#include "Indoor/sensors/radio/setup/WiFiOptimizer.h" +#include "Indoor/sensors/radio/setup/WiFiFingerprint.h" +#include "Indoor/sensors/radio/setup/WiFiFingerprints.h" +#include "Indoor/sensors/radio/setup/WiFiOptimizer.h" + +#include "Indoor/sensors/radio/VAPGrouper.h" +#include "Indoor/sensors/offline/FileReader.h" + +#include "Indoor/floorplan/v2/Floorplan.h" +#include "Indoor/floorplan/v2/FloorplanReader.h" +#include "Indoor/floorplan/v2/FloorplanHelper.h" +#include "Indoor/floorplan/v2/FloorplanCeilings.h" + +#include "Indoor/grid/factory/v2/Helper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Structs.h" +#include "plots/Plotty.h" +#include "plots/PlotErrTime.h" +#include "plots/PlotErrFunc.h" + +#include "Settings.h" + +/** plot results of the wifi optimization */ +class EvalWiFiOptResult { + + Floorplan::IndoorMap* map; + VAPGrouper vap; + +public: + + /** ctor */ + EvalWiFiOptResult(const std::string& mapFile) : vap(Settings::WiFiModel::vg_eval) { + map = Floorplan::Reader::readFromFile(mapFile); + } + + /** + * plot the error for every fingerprint + */ + template void showErrorPerFingerprint(const std::string& modelParamsXML, const WiFiFingerprints& fps) { + + Plotty* plot = new Plotty(map); + + plot->settings.obstacles = true; + plot->settings.outline = false; + plot->settings.stairs = false; + plot->settings.outlineColor = K::GnuplotColor::fromRGB(0,0,0); + plot->buildFloorplan(); + plot->equalXY(); + //plot.gp << "unset border\n"; + plot->splot.getAxisX().setTicsVisible(false); + plot->splot.getAxisY().setTicsVisible(false); + plot->splot.getAxisZ().setTicsVisible(false); + plot->setScale(1.5, 1.5); + + // the sig strength model + Model model(map); + model.loadXML(modelParamsXML); +// WiFiModelLogDistCeiling model(map); +// model.loadXML(modelParamsXML); + + // combine position and error + struct ErrPoint { + Point3 pos; + float err; + ErrPoint(const Point3 pos, const float err) : pos(pos), err(err) {;} + }; + + std::vector errors; + + // process each location + for (const WiFiFingerprint& fp : fps.getFingerprints()) { + + // estimate error for this location + K::Statistics errAbs; + + // vap grouping + const WiFiMeasurements mes = vap.group(fp.measurements); + + // process each measured AP + for (const WiFiMeasurement& m : mes.entries) { + + // fingerprint RSSI + const float scan_rssi = m.getRSSI(); + + // model RSSI + const float model_rssi = model.getRSSI(m.getAP().getMAC(), fp.pos_m); + + // AP unknown -> skip + if (model_rssi != model_rssi) {continue;} + + // update error + const float err = model_rssi - scan_rssi; + errAbs.add(std::abs(err)); + + } + + // enqueue AVG error + errors.push_back(ErrPoint(fp.pos_m, errAbs.getMax())); + + } + + // interpolated error points + std::vector errorsInt; + K::Statistics errStats; + const float oz = 1.3; + const float ss = 1.5; + + for (Floorplan::Floor* floor : map->floors) { + + for (float y = -20; y < 70; y+=ss) { + for (float x = -10; x < 130; x+=ss) { + + const Point3 pos(x,y,floor->atHeight+oz); + float errSum = 0; + float distSum = 0; + int cnt = 0; + + float minDist = 99999; + + for (const ErrPoint& ep : errors) { + if (ep.pos.z != pos.z) {continue;} + const float dist = ep.pos.getDistance(pos); + //if (dist > 4.0) {continue;} + //const float imp = 1.0 / (std::pow((dist+0.01)/4.0, 3)); + //errSum += ep.err * imp; + //distSum += imp; + if (dist < minDist) { + errSum = ep.err; + distSum = 1.0; + minDist = dist; + } + + + ++cnt; + } + + // limit estimations to the floorplan's outline + bool contained = false; + for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { + HelperPoly hp(*poly); + if (hp.contains(pos.xy()*100)) { + if (poly->method == Floorplan::OutlineMethod::ADD) { + contained = true; + } else { + //contained = false; break; + } + } + } + + if (distSum == 0) {continue;} + if (!contained) {continue;} + const float err = errSum/distSum; + + // add + errStats.add(err); + errorsInt.push_back(ErrPoint(pos, err)); + + } + } + + } + + const float minErr = 0.0;//errStats.getMin(); + const float maxErr = 18.0;//errStats.getQuantile(0.9); + + plot->splot.setTitle("max error: " + std::to_string(errStats.getQuantile(0.9)) + " dB"); + + // draw interpolated errors between min/max + for (const ErrPoint& ep : errorsInt) { + + float factor = (ep.err - minErr) / (maxErr-minErr); + if (factor > 1) {factor = 1;} + + // hsv green->yellow->red +// const float h = 90 * (1 - std::pow(factor, 1.0)); +// K::GnuplotColor color = K::GnuplotColor::fromHSV(h, 255, 255); + + // hsv white->red + const float sat = 255 * (std::pow(factor, 1.5)); + K::GnuplotColor color = K::GnuplotColor::fromHSV(0, sat, 255); + + K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon( + K::GnuplotFill(K::GnuplotFillStyle::SOLID, color), + K::GnuplotStroke::NONE() + ); + + const float s = ss/2; + + poly->add(K::GnuplotCoordinate3(ep.pos.x-s, ep.pos.y-s, ep.pos.z-oz, K::GnuplotCoordinateSystem::FIRST)); + poly->add(K::GnuplotCoordinate3(ep.pos.x+s, ep.pos.y-s, ep.pos.z-oz, K::GnuplotCoordinateSystem::FIRST)); + poly->add(K::GnuplotCoordinate3(ep.pos.x+s, ep.pos.y+s, ep.pos.z-oz, K::GnuplotCoordinateSystem::FIRST)); + poly->add(K::GnuplotCoordinate3(ep.pos.x-s, ep.pos.y+s, ep.pos.z-oz, K::GnuplotCoordinateSystem::FIRST)); + poly->close(); + poly->setZIndex(ep.pos.z); + plot->splot.getObjects().add(poly); + + } + + plot->plot(); + + } + +}; + +#endif // EVALWIFIOPTRESULT_H diff --git a/Helper.h b/Helper.h index c628a32..415df8d 100644 --- a/Helper.h +++ b/Helper.h @@ -149,6 +149,12 @@ public: } + static void writeCode(const std::string& file, const std::string& code) { + std::ofstream out(file); + out << code; + out.close(); + } + static void plot(const Floorplan::IndoorMap* map, const WiFiFingerprints& inp, const std::string& title = "Fingerprints") { Plotty* p = new Plotty(map); diff --git a/Settings.h b/Settings.h index 845bbe2..180e2c5 100644 --- a/Settings.h +++ b/Settings.h @@ -20,7 +20,13 @@ namespace Settings { const std::string wifiAllOptPar = pathWiFi + "allOptPar.xml"; const std::string wifiEachOptPar = pathWiFi + "eachOptPar.xml"; const std::string wifiEachOptParPos = pathWiFi + "eachOptParPos.xml"; - const std::string wifiEachOptParPosNoStairNoOut = pathWiFi + "eachOptParPosNoStairNoOut.xml"; + + const std::string wifiEachOptParPos_only0th = pathWiFi + "eachOptParPos_only0th.xml"; + const std::string wifiEachOptParPos_only1st = pathWiFi + "eachOptParPos_only1st.xml"; + const std::string wifiEachOptParPos_only2nd = pathWiFi + "eachOptParPos_only2nd.xml"; + const std::string wifiEachOptParPos_only3rd = pathWiFi + "eachOptParPos_only3rd.xml"; + + const std::string wifiEachOptParPos_multimodel = pathWiFi + "eachOptParPos_multimodel.xml"; @@ -58,7 +64,7 @@ namespace Settings { namespace WiFiModel { - constexpr float sigma = 11.0; + constexpr float sigma = 8.0; /** if the wifi-signal-strengths are stored on the grid-nodes, this needs a grid rebuild! */ // constexpr float TXP = -44; diff --git a/main.cpp b/main.cpp index 3b0cfa1..872d2e1 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include "Indoor/sensors/radio/setup/WiFiFingerprint.h" #include "Indoor/sensors/radio/setup/WiFiFingerprints.h" #include "Indoor/sensors/radio/setup/WiFiOptimizer.h" +#include "Indoor/sensors/radio/model/WiFiModels.h" #include "Indoor/sensors/radio/VAPGrouper.h" @@ -28,6 +29,9 @@ #include "EvalWiFi.h" #include "EvalWiFiSigStrength.h" #include "pf/EvalWalk.h" +#include "EvalWifiOptResult.h" +#include "wifi/EvalWiFiConvex.h" + #include "plots/PlotErrFunc.h" @@ -36,6 +40,18 @@ void paperOutputs() { Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap); + + // show optimization behaviour + if (1 == 1) { + + EvalWiFiConvex eval(map, Settings::fCalib); + eval.showParams(); + //eval.showPos(); + eval.showPosZ(); + return; + + } + // show fingerprints as plot if (1==0){ EvalWiFiSigStrength sig(Settings::fMap, Settings::fCalib); @@ -69,28 +85,62 @@ void paperOutputs() { // }; // EvalCompareOpt2 opt1(Settings::fMap, calibWalks); - auto remove = [] (const WiFiFingerprint& fp) -> bool { - return fp.pos_m.z != 4; + auto removeNone = [] (const WiFiFingerprint& fp) -> bool { + return false; + }; + + 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; }; // use fingerprints? - bool ignoreStaircases = false; - bool ignoreOutdoor = false; - bool ignoreIndoor = false; - EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, remove); - + EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, removeNone); + // 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 only for the 0th 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(); // 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); PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}"); pef.add("\\small{empiric}", &s1.errSingle); @@ -106,68 +156,39 @@ void paperOutputs() { pef.getGP() << "set tmargin 0.4\n"; pef.plot(); - return; + int i = 0; (void) i; + //return; //sleep(1000); } - // REPLACED BY EVAL OPT 2 -// // 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) { + /** plot wifi eval results */ + if (1 == 1) { -// bool ignoreStaircases = false; -// bool ignoreOutdoor = false; -// bool ignoreIndoor = false; + WiFiFingerprints fps; + fps.load(Settings::fCalib); -// // 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), -//// }; -//// EvalCompareOpt opt1(Settings::fMap, calibWalks); + EvalWiFiOptResult eval1(Settings::fMap); + eval1.showErrorPerFingerprint(Settings::wifiEachOptParPos, fps); -// // use fingerprints? -// EvalCompareOpt opt1(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor); + EvalWiFiOptResult eval2(Settings::fMap); + eval2.showErrorPerFingerprint(Settings::wifiAllFixed, fps); -// EvalCompareOpt::Result s1 = opt1.fixedPosFixedParamsForAll(); //BREAK; -// EvalCompareOpt::Result s2 = opt1.fixedPosOptParamsForAll(); //BREAK; -// EvalCompareOpt::Result s3 = opt1.fixedPosOptParamsForEach(); //BREAK; -// EvalCompareOpt::Result s4 = opt1.optPosOptParamsForEach(); //BREAK; -// ignoreStaircases = true; -// ignoreOutdoor = true; -// EvalCompareOpt opt2(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor); -// EvalCompareOpt::Result s4b = opt2.optPosOptParamsForEach(); //BREAK; + EvalWiFiOptResult evalfloor(Settings::fMap); + evalfloor.showErrorPerFingerprint(Settings::wifiEachOptParPos_only0th, fps); + evalfloor.showErrorPerFingerprint(Settings::wifiEachOptParPos_only1st, fps); + evalfloor.showErrorPerFingerprint(Settings::wifiEachOptParPos_only2nd, fps); + evalfloor.showErrorPerFingerprint(Settings::wifiEachOptParPos_only3rd, fps); + evalfloor.showErrorPerFingerprint(Settings::wifiEachOptParPos_multimodel, fps); -// // save models to file -// s1.model.saveXML(Settings::wifiAllFixed); -// s2.model.saveXML(Settings::wifiAllOptPar); -// s3.model.saveXML(Settings::wifiEachOptPar); -// s4.model.saveXML(Settings::wifiEachOptParPos); -// s4b.model.saveXML(Settings::wifiEachOptParPosNoStairNoOut); + int i = 0; (void) i; -// PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}"); -// pef.add("\\small{empiric}", s1.errAbs); -// pef.add("\\small{real pos, opt params}", s2.errAbs); -// pef.add("\\small{real pos, opt params [per AP]}", s3.errAbs); -// pef.add("\\small{opt pos, opt params [per AP]}", s4.errAbs); -// pef.add("\\small{opt pos, opt params [per AP, no stair, no out]}", s4b.errAbs); + } -// 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(); -// } // error histogram all pos, all params, between in/out/stair, in/out, in/stair, in if(1==0){ @@ -218,7 +239,7 @@ void paperOutputs() { ); plot.getPlot().setGrid(true); - plot.getPlot().setLegend(false); + plot.getPlot().getKey().setVisible(false); plot.getPlot().getObjects().add(&rect); plot.plot(); @@ -263,7 +284,7 @@ int main(void) { //testWAF(); - //paperOutputs(); // return 0; + paperOutputs(); // return 0; // calib error in/out if (1 == 0) { @@ -297,7 +318,7 @@ int main(void) { } // walks - if (1 == 0) { + if (1 == 1) { Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap);; EvalWalk walk(map); walk.walk1(); @@ -329,10 +350,12 @@ int main(void) { //ew1.fixedParams(-64.5905, 1.25988, -2.47863); BREAK; //ew1.fixedParams(-59.4903,1.52411,-3.25077); BREAK; //ew1.load(Settings::wifiEachOptParPos); - ew1.load(Settings::wifiAllFixed, "empirc"); - ew1.load(Settings::wifiAllOptPar, "opt params all APs"); - ew1.load(Settings::wifiEachOptPar, "opt params each AP"); + //ew1.load(Settings::wifiAllFixed, "empirc"); + //ew1.load(Settings::wifiAllOptPar, "opt params all APs"); + //ew1.load(Settings::wifiEachOptPar, "opt params each AP"); ew1.load(Settings::wifiEachOptParPos, "everything opt"); + ew1.load(Settings::wifiEachOptParPos_multimodel, "model per floor"); + //ew1.load(Settings::wifiEachOptParPos_only1st, "everything opt"); sleep(1000); @@ -394,66 +417,4 @@ int main(void) { } - - - - -// Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile("/apps/android/workspace/OTHER2017/data/SHL33a.xml"); - - -// WiFiFingerprints calib("/apps/android/workspace/OTHER2017/data/wifi_fp.dat"); - -// VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); - -// WiFiOptimizer opt(map, vap); -// opt.addFingerprints(calib); -// WiFiOptimizer::APParamsList list = opt.optimizeAll(); - -// for (Floorplan::Floor* floor : map->floors) { -// for (Floorplan::AccessPoint* ap : floor->accesspoints) { -// const WiFiOptimizer::APParamsMAC* params = list.get(ap->mac); -// if (params) { -// const float delta = ap->getPos(floor).getDistance(Point3(params->params.x, params->params.y, params->params.z)); -// std::cout << ap->mac << ": " << delta << "m" << std::endl; -// } -// } -// } - - - -// static K::Gnuplot gp; -// gp << "set view equal xy\n"; - -// K::GnuplotSplot splot; -// K::GnuplotSplotElementPoints points; splot.add(&points); points.setPointType(7); points.setPointSize(0.5); -// K::GnuplotSplotElementLines lines; splot.add(&lines); - -// for (Floorplan::Floor* floor : map->floors) { -// 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); -// lines.addSegment(p1, p2); -// } -// } -// } - -// for (const WiFiOptimizer::APParamsMAC& ap : list.get()) { -// const K::GnuplotPoint3 p3(ap.params.x, ap.params.y, ap.params.z); -// points.add(p3); -// const Floorplan::AccessPoint* fap = FloorplanHelper::getAP(map, ap.mac); -// std::string lbl = (fap) ? (fap->name) : (ap.mac.asString()); -// if (!fap) { -// gp << "set label '" << lbl << "' at " << ap.params.x+1 << "," << ap.params.y+1 << "," << ap.params.z+0.3 << "\n"; - -// std::cout << "AP missing in Map: " << ap.mac.asString() << " @ " << ap.params.x << "," << ap.params.y << "," << ap.params.z << std::endl; -// } -// } - -// gp.draw(splot); -// gp.flush(); - -// int i = 0; (void) i; - } diff --git a/old/main.h b/old/main.h new file mode 100644 index 0000000..ab3f541 --- /dev/null +++ b/old/main.h @@ -0,0 +1,63 @@ +#ifndef MAIN_H +#define MAIN_H + + +// Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile("/apps/android/workspace/OTHER2017/data/SHL33a.xml"); + + +// WiFiFingerprints calib("/apps/android/workspace/OTHER2017/data/wifi_fp.dat"); + +// VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); + +// WiFiOptimizer opt(map, vap); +// opt.addFingerprints(calib); +// WiFiOptimizer::APParamsList list = opt.optimizeAll(); + +// for (Floorplan::Floor* floor : map->floors) { +// for (Floorplan::AccessPoint* ap : floor->accesspoints) { +// const WiFiOptimizer::APParamsMAC* params = list.get(ap->mac); +// if (params) { +// const float delta = ap->getPos(floor).getDistance(Point3(params->params.x, params->params.y, params->params.z)); +// std::cout << ap->mac << ": " << delta << "m" << std::endl; +// } +// } +// } + + + +// static K::Gnuplot gp; +// gp << "set view equal xy\n"; + +// K::GnuplotSplot splot; +// K::GnuplotSplotElementPoints points; splot.add(&points); points.setPointType(7); points.setPointSize(0.5); +// K::GnuplotSplotElementLines lines; splot.add(&lines); + +// for (Floorplan::Floor* floor : map->floors) { +// 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); +// lines.addSegment(p1, p2); +// } +// } +// } + +// for (const WiFiOptimizer::APParamsMAC& ap : list.get()) { +// const K::GnuplotPoint3 p3(ap.params.x, ap.params.y, ap.params.z); +// points.add(p3); +// const Floorplan::AccessPoint* fap = FloorplanHelper::getAP(map, ap.mac); +// std::string lbl = (fap) ? (fap->name) : (ap.mac.asString()); +// if (!fap) { +// gp << "set label '" << lbl << "' at " << ap.params.x+1 << "," << ap.params.y+1 << "," << ap.params.z+0.3 << "\n"; + +// std::cout << "AP missing in Map: " << ap.mac.asString() << " @ " << ap.params.x << "," << ap.params.y << "," << ap.params.z << std::endl; +// } +// } + +// gp.draw(splot); +// gp.flush(); + +// int i = 0; (void) i; + +#endif // MAIN_H diff --git a/pf/EvalWalk.h b/pf/EvalWalk.h index d036457..1714ab5 100644 --- a/pf/EvalWalk.h +++ b/pf/EvalWalk.h @@ -31,7 +31,7 @@ #include "Indoor/floorplan/v2/FloorplanHelper.h" #include "Indoor/floorplan/v2/FloorplanCeilings.h" -#include "Indoor/sensors/radio/model/WiFiModelLogDistCeiling.h" +#include "Indoor/sensors/radio/model/WiFiModels.h" #include "Indoor/sensors/offline/FileReader.h" #include "../Helper.h" @@ -50,7 +50,6 @@ class EvalWalk : public Offline::Listener { K::ParticleFilter* pf; std::string runName; - WiFiModelLogDistCeiling wifiModel; Plotty plotty; PlotWifiMeasurements plotWifi; @@ -79,7 +78,7 @@ class EvalWalk : public Offline::Listener { public: - EvalWalk(Floorplan::IndoorMap* map) : wifiModel(map), plotty(map), map(map), em(map) { + EvalWalk(Floorplan::IndoorMap* map) : plotty(map), map(map), em(map) { const std::string saveFile = Settings::pathData + "/grid.dat"; grid = new Grid(Settings::Grid::gridSize_cm); @@ -104,14 +103,12 @@ public: pf = new K::ParticleFilter( Settings::numParticles, std::unique_ptr(new PFInit(grid)) ); - // TODO: flexible model - //wifiModel.loadAPs(map, Settings::WiFiModel::TXP, Settings::WiFiModel::EXP, Settings::WiFiModel::WAF, false); // transition pf->setTransition( std::unique_ptr( new PFTrans(grid)) ); // resampling step? - //pf->setNEffThreshold(0.5); + //pf->setNEffThreshold(0.35); //pf->setResampling( std::unique_ptr>(new K::ParticleFilterResamplingSimple()) ); //pf->setNEffThreshold(0.75); @@ -120,17 +117,27 @@ public: //pf->setNEffThreshold(0.75); //pf->setResampling( std::unique_ptr>(new K::ParticleFilterResamplingPercent(0.05)) ); + K::ParticleFilterResamplingNEff* res = new K::ParticleFilterResamplingNEff(0.50, 0.05); pf->setNEffThreshold(1.0); - pf->setResampling( std::unique_ptr>(new K::ParticleFilterResamplingNEff(0.50, 0.05)) ); + pf->setResampling( std::unique_ptr>(res) ); + res->setDrawCallback([&] (K::Particle& p) { + static std::minstd_rand gen; + const MyGridNode* n = grid->getNodePtrFor(p.state.position); + std::normal_distribution distTurn(-0.3, +0.3); + for (int j = 0; j < 2; ++j) { + std::uniform_int_distribution distIdx(0, n->getNumNeighbors()-1); + const int idx = distIdx(gen); + n = &(grid->getNeighbor(*n, idx)); + } + p.state.position = *n; + p.state.heading.direction += distTurn(gen); + }); // state estimation step - pf->setEstimation( std::unique_ptr>(new K::ParticleFilterEstimationWeightedAverage())); + //pf->setEstimation( std::unique_ptr>(new K::ParticleFilterEstimationWeightedAverage())); //pf->setEstimation( std::unique_ptr>(new K::ParticleFilterEstimationRegionalWeightedAverage())); - //pf->setEstimation( std::unique_ptr>(new K::ParticleFilterEstimationOrderedWeightedAverage(0.50f))); - - - + pf->setEstimation( std::unique_ptr>(new K::ParticleFilterEstimationOrderedWeightedAverage(0.25f))); } @@ -152,8 +159,11 @@ public: groundTruth = FloorplanHelper::getGroundTruth(map, pathPoints); // wifi model - WiFiModelLogDistCeiling wifiModel(map); - wifiModel.loadXML(Settings::wifiEachOptParPos); + //WiFiModelLogDistCeiling wifiModel(map); + //wifiModel.loadXML(Settings::wifiAllFixed); + //wifiModel.loadXML(Settings::wifiEachOptParPos); + WiFiModelPerFloor wifiModel(map); + wifiModel.loadXML(Settings::wifiEachOptParPos_multimodel); // eval std::unique_ptr eval = std::unique_ptr( new PFEval(grid, wifiModel, em) ); diff --git a/pf/PF.h b/pf/PF.h index 47e4cad..61def65 100644 --- a/pf/PF.h +++ b/pf/PF.h @@ -184,7 +184,7 @@ public: walker.addModule(&modRelHead); walker.addModule(&modAbsHead); - walker.addModule(&modActivity); + //walker.addModule(&modActivity); //walker.addModule(&modFavorZ); //walker.addModule(&modImportance); @@ -219,8 +219,8 @@ public: K::Particle& p = particles[i]; - p.weight = std::pow(p.weight, 0.8); - + p.weight = std::pow(p.weight, 0.5); + //p.weight = 1; double prob; p.state = walker.getDestination(*grid, p.state, dist_m, prob); //p.weight *= prob;//(prob > 0.01) ? (1.0) : (0.15); @@ -228,7 +228,7 @@ public: //p.weight = prob; //p.weight = 1.0; // reset //p.weight = std::pow(p.weight, 0.1); // make all particles a little more equal [less strict] - //p.weight *= std::pow(prob, 0.1); // add grid-walk-probability + //p.weight *= std::pow(prob, 0.7); // add grid-walk-probability p.weight *= prob; // grid-walk-probability if (p.weight != p.weight) {throw Exception("nan");} @@ -247,7 +247,7 @@ class PFEval : public K::ParticleFilterEvaluation { Grid* grid; - WiFiModelLogDistCeiling& wifiModel; + WiFiModel& wifiModel; EarthMapping& em; @@ -260,7 +260,7 @@ class PFEval : public K::ParticleFilterEvaluation { public: - PFEval(Grid* grid, WiFiModelLogDistCeiling& wifiModel, EarthMapping& em) : + PFEval(Grid* grid, WiFiModel& wifiModel, EarthMapping& em) : grid(grid), wifiModel(wifiModel), em(em), wiFiProbability(Settings::WiFiModel::sigma, wifiModel) { // WiFi free diff --git a/plots/PlotErrFunc.h b/plots/PlotErrFunc.h index 9edb94c..3bca967 100644 --- a/plots/PlotErrFunc.h +++ b/plots/PlotErrFunc.h @@ -31,7 +31,7 @@ class PlotErrFunc { K::GnuplotPlot gplot; //std::vector colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"}; - std::vector colors = {"#000000", "#999999", "#0000ff", "#9999ff", "#ff0000"}; + std::vector colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa"}; bool markers = false; @@ -42,10 +42,10 @@ public: /** ctor with x-axis label */ PlotErrFunc(const std::string& xLabel, const std::string& yLabel) { - gplot.setLabelX(xLabel); - gplot.setLabelY(yLabel); - gplot.setRangeX(K::GnuplotAxisRange(0, K::GnuplotAxisRange::AUTO)); - gplot.setRangeY(K::GnuplotAxisRange(0, K::GnuplotAxisRange::AUTO)); + 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)); } /** add one curve. Statistics are allowed to be altered outside afterwards! */ @@ -53,7 +53,7 @@ public: Entry entry(name, stats); entry.line = new K::GnuplotPlotElementLines(); entry.line->setTitle(name); - entry.line->setLineWidth(2); + entry.line->getStroke().setWidth(2); gplot.add(entry.line); entries.push_back(entry); } @@ -89,7 +89,7 @@ public: const Entry& e = entries[i]; e.line->clear(); - e.line->setColorHex(colors[i]); + e.line->getStroke().getColor().setHexStr(colors[i]); // distancen between min and max const float minX = e.stats->getQuantile(0); diff --git a/plots/PlotErrTime.h b/plots/PlotErrTime.h index 31b6278..380185c 100644 --- a/plots/PlotErrTime.h +++ b/plots/PlotErrTime.h @@ -25,16 +25,16 @@ public: /** ctor */ PlotErrTime(const std::string& xLabel, const std::string& yLabel, const std::string& y2Label) { - gpplot.setLabelX(xLabel); - gpplot.setLabelY(yLabel); - gpplot.setLabelY2(y2Label); + gpplot.getAxisX().setLabel(xLabel); + gpplot.getAxisY().setLabel(yLabel); + gpplot.getAxisY2().setLabel(y2Label); - gpplot.add(&lineErr[0]); lineErr[0].setLineWidth(1.5); lineErr[0].setColorHex("#000000"); - gpplot.add(&lineErr[1]); lineErr[1].setLineWidth(1.5); lineErr[1].setColorHex("#FF0000"); - gpplot.add(&lineErr[2]); lineErr[2].setLineWidth(1.5); lineErr[2].setColorHex("#00FF00"); - gpplot.add(&lineErr[3]); lineErr[3].setLineWidth(1.5); lineErr[3].setColorHex("#0000FF"); + gpplot.add(&lineErr[0]); lineErr[0].getStroke().setWidth(1.5); lineErr[0].getStroke().getColor().setHexStr("#000000"); + gpplot.add(&lineErr[1]); lineErr[1].getStroke().setWidth(1.5); lineErr[1].getStroke().getColor().setHexStr("#FF0000"); + gpplot.add(&lineErr[2]); lineErr[2].getStroke().setWidth(1.5); lineErr[2].getStroke().getColor().setHexStr("#00FF00"); + gpplot.add(&lineErr[3]); lineErr[3].getStroke().setWidth(1.5); lineErr[3].getStroke().getColor().setHexStr("#0000FF"); - gpplot.add(&lineB); lineB.setLineWidth(1); lineB.setColorHex("#0000ff"); + gpplot.add(&lineB); lineB.getStroke().setWidth(1); lineB.getStroke().getColor().setHexStr("#0000ff"); gpplot.add(&lineC); gpplot.setGrid(true); diff --git a/plots/Plotty.h b/plots/Plotty.h index da99fb9..f3920fe 100644 --- a/plots/Plotty.h +++ b/plots/Plotty.h @@ -107,6 +107,7 @@ public: bool stairs = true; bool obstacles = true; bool outline = true; + K::GnuplotColor outlineColor = K::GnuplotColor::fromRGB(128,128,128); float minZ = -9999; float maxZ = +9999; } settings; @@ -126,17 +127,17 @@ public: // how to draw the floorplan - mapOutlineConcrete.setColorHex("#888888"); mapOutlineConcrete.setLineWidth(2); - mapOutlineDrywall.setColorHex("#888888"); - mapOutlineGlass.setColorHex("#888888"); mapOutlineGlass.setDashType(2); + mapOutlineConcrete.getStroke().getColor().setHexStr("#888888"); mapOutlineConcrete.getStroke().setWidth(2); + mapOutlineDrywall.getStroke().getColor().setHexStr("#888888"); + mapOutlineGlass.getStroke().getColor().setHexStr("#888888"); mapOutlineGlass.getStroke().setType(K::GnuplotDashtype::DASHED); splot.add(&mapOutlineConcrete); splot.add(&mapOutlineDrywall); splot.add(&mapOutlineGlass); splot.add(&particles); particles.setPointSize(0.20); //particles.setColorHex("#777777"); - splot.add(&pathReal); pathReal.setLineWidth(2); pathReal.setColorHex("#000000"); - splot.add(&pathEst); pathEst.setLineWidth(2); pathEst.setColorHex("#0000ff"); + 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(&points); points.setPointType(7); @@ -208,16 +209,29 @@ public: if (p.z > settings.maxZ) {return;} } - gp << "set object polygon from "; - for (size_t i = 0; i < points.size(); ++i) { - const Point3 p = points[i]; - if (i > 0) {gp << " to ";} - gp << p.x << "," << p.y << "," << p.z << " "; + const K::GnuplotFill pfill = (fill) ? (K::GnuplotFill(K::GnuplotFillStyle::SOLID, K::GnuplotColor::fromHexStr(color))) : (K::GnuplotFill::NONE()); + const K::GnuplotStroke pstroke = (!fill) ? (K::GnuplotStroke(K::GnuplotDashtype::SOLID, 1.0, K::GnuplotColor::fromHexStr(color))) : (K::GnuplotStroke::NONE()); + + K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon(pfill, pstroke); + for (const Point3 p : points) { + poly->add(K::GnuplotCoordinate3(p.x, p.y, p.z, K::GnuplotCoordinateSystem::FIRST)); + poly->setZIndex(p.z); // manual depth ordering } - gp << (front ? "front" : ""); - if (fill) {gp << " fs solid ";} else {gp << " fs transparent ";} - gp << " fc rgb " << "'" << color << "'"; - gp << "\n"; + poly->setFront(front); + + splot.getObjects().add(poly); + + +// gp << "set object polygon from "; +// for (size_t i = 0; i < points.size(); ++i) { +// const Point3 p = points[i]; +// if (i > 0) {gp << " to ";} +// gp << p.x << "," << p.y << "," << p.z << " "; +// } +// gp << (front ? "front" : ""); +// if (fill) {gp << " fs solid ";} else {gp << " fs transparent ";} +// gp << " fc rgb " << "'" << color << "'"; +// gp << "\n"; } @@ -289,6 +303,11 @@ public: } void plot() { + + this->mapOutlineConcrete.getStroke().setColor(settings.outlineColor); + this->mapOutlineDrywall.getStroke().setColor(settings.outlineColor); + this->mapOutlineGlass.getStroke().setColor(settings.outlineColor); + gp.draw(splot); gp << "unset multiplot\n"; // scaling if (codeFile != "") { @@ -313,10 +332,10 @@ public: } } - + // process each selected floor for (Floorplan::Floor* floor : floors) { - // plot obstacles + // plot obstacles? if (settings.obstacles) { for (Floorplan::FloorObstacle* obs : floor->obstacles) { Floorplan::FloorObstacleLine* line = dynamic_cast(obs); @@ -338,14 +357,15 @@ public: // plot the floor's outline if (settings.outline) { for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { - gp << "set object polygon from "; - for (size_t i = 0; i < poly->poly.points.size() + 1; ++i) { - if (i > 0) {gp << " to ";} - const Point2 pt = poly->poly.points[i % poly->poly.points.size()]; // ensures closing the polygon - gp << pt.x << "," << pt.y << "," << floor->atHeight << " "; + K::GnuplotColor color = (poly->outdoor) ? (K::GnuplotColor::fromRGB(200, 240, 200)) : (K::GnuplotColor::fromRGB(210,210,210)); + K::GnuplotFill filler(K::GnuplotFillStyle::SOLID, color); + K::GnuplotObjectPolygon* gpol = new K::GnuplotObjectPolygon(filler, K::GnuplotStroke::NONE()); + for (Point2 pt : poly->poly.points) { + K::GnuplotCoordinate3 coord(pt.x, pt.y, floor->atHeight, K::GnuplotCoordinateSystem::FIRST); + gpol->add(coord); } - gp << " fs solid fc rgb " << ( poly->outdoor ? "'#bbeebb'" : "'#dddddd'"); - gp << "\n"; + gpol->close(); + splot.getObjects().add(gpol); } } diff --git a/tex/chapters/abstract.tex b/tex/chapters/abstract.tex index af14975..4b2c036 100644 --- a/tex/chapters/abstract.tex +++ b/tex/chapters/abstract.tex @@ -1 +1,8 @@ abstract +system setup kostet oft sehr viel zeit [fingerprinting kostet] +deshalb werden alternativen untersucht: +bekannte AP position mit empirischen parametern +und optimierung durch einige referenzmessungen + +floorplan wird für die navigation bzw orientierung des anwenders eh gebraucht +dann kann man ihn auch gleich für ein bewegungsmodell nutzen diff --git a/tex/chapters/conclusion.tex b/tex/chapters/conclusion.tex index c62279b..ffd345e 100644 --- a/tex/chapters/conclusion.tex +++ b/tex/chapters/conclusion.tex @@ -1 +1,17 @@ conclusion + +beide ansaetze sind in unserem szenario/gebaeude OK: +bekannte AP-pos + empirische parameter +komplette optimierung über fingerprints + +100 prozent optimierung ist nicht moeglich, es gibt +immer stellen, die, zugunsten von anderen, schlechter werden. +es haengt auch stark davon ab, was man optimiert, das modell, +die uebereinstimmung, welche fingerprints [schlechte vs. gute stellen] + +zudem ist das modell fuer unser gebaeude nicht gut ggeeignet. +zu viele verschiedene materialien und trennwaende, APs immer in raeumen, +nie auf dem flur. viele hindernisse, wenige freie raeume. +andere modelle koennten hier helfen, erfordern dann aber zur +laufzeit mehr berechnung, oder muessten vorab auf einem grid berechnet +werden \todo{cite auf competition} diff --git a/tex/chapters/experiments.tex b/tex/chapters/experiments.tex index 34c4553..6005421 100644 --- a/tex/chapters/experiments.tex +++ b/tex/chapters/experiments.tex @@ -8,6 +8,24 @@ modell direkt fuer den gelaufenen pfad optimiert (also wirklich jede wifi messun der fehler wird zwar kleiner, ist aber immernoch deutlich spürbar. das spricht dafür, dass das modell einfach nicht gut geeignet ist. +outdoor fehler kann gemnindert werden mit z.B. +nicht nur die APs nehmen die ich sehe, sondern auch die, die ich sehen müsste +dann wird klar, dass es nicht gut passt. allerdings ist das auch gefaehrlich +[nicht immer tauchen alle APs im scan auf] und welchen fehler bzw. welche +dBm zahl nimmt man fuer fehlende APs an? das ist eine hardware-frage. + +ein workaround fuer steigende fehler durch optimierung koennte sein, +dass man nicht jeden AP einzeln optimiert, sondern das gesamtsystem. +und auch nicht den dB fehler, sondern 'die wahrscheinlichkeit an dieser stelle zu sein' +bzw: 'die wahrscheinlichkeit aller anderen positionen minimieren' dass keine +fehl-positionierungen [wie outdoor] mehr stattfinden. allerdings ist das +problematisch da man auch hier entscheiden müsste wann ein AP nicht mehr +sichtbar ist etc. + +walk1 hat eine issue kurz bevor man zur tuer zum hoersaalgebaude reingeht +je nach resampling killt dieser wlan error evtl alle partikel! + + 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 @@ -75,3 +93,5 @@ outdoor hat insgesamt nicht all zu viel einfluss, da die meisten APs an den outdoor punkten kaum gesehen werden. auf einzelne APs kann 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/introduction.tex b/tex/chapters/introduction.tex index 17bbd55..ddbabe2 100644 --- a/tex/chapters/introduction.tex +++ b/tex/chapters/introduction.tex @@ -1 +1,30 @@ introduction + + +setupzeiten von indoor systemen sind hoch [fingerprinting] +auch re-calibration kostet oft zeit + +meistens hat man einen gebäudeplan +oft auch die info wo APs hängen +warum das nicht nutzen und mit einer groben AP position ++ fixen, empirischen param starten? + +was bekomme ich für eine genauigkeit raus? + +was kann ich machen um das zu verbessern? +model parameter anlernen? + +wo sind die schwächen? +verschiedene modelle mit unterschiedlichem berechnungsaufwand. + +indoor komplett-system mit IMU, abs-heading, rel-heading, wifi sensor +gebäudeplan, bewegungsmodell + +fokus: +- wlan parameter + optimierung +- evaluation der einzel und gesamtergebnisse + +contribution: +- neues wifi modell, +- neues resampling, +- model param optimierung + eval was es bringt diff --git a/tex/chapters/work.tex b/tex/chapters/work.tex index fd5375e..967228a 100644 --- a/tex/chapters/work.tex +++ b/tex/chapters/work.tex @@ -1,5 +1,97 @@ -log normal shadowing model -so wie log-dist modell nur mit waf. hier: fuer decken. waende werden aktuell nicht betrachtet +\section{Indoor Positioning System} + + nur grob beschreiben wie unser system funktioniert, + dass die absolute positionierung aus dem wlan kommt, + dass man dafür entweder viele fingerprints oder ein modell braucht + dann kommts zu dem modell + + \subsection{Sensor Fusion} + + Gesamtsystem + dann einzel-komponenten + + \subsection{Signal Strength Prediction} + + \begin{equation} + x = \mTXP{} + 10 \mPLE{} + \log_{10} \frac{d}{d_0} + \mGaussNoise{} + \label{eq:logDistModel} + \end{equation} + + The log distance model \todo{cite} in \refeq{eq:logDistModel} is a commonly used signal strength prediction model that + is intended for line-of-sight predictions. However, depending on the surroundings, the model is versatile enough + to also serve for indoor purposes. + % + This model predicts an \docAP{}'s signal strength + for an arbitrary location given the distance between both and two environmental parameters: + The \docAPshort{}'s signal strength \mTXP{} measurable at a known distance $d_0$ (usually \SI{1}{\meter}) and + the signal's depletion over distance \mPLE{}, which depends on the \docAPshort{}'s surroundings like walls + and other obstacles. + \mGaussNoise{} is a zero-mean Gaussian noise and models the uncertainty. + + The log normal shadowing model is a slight modification, to adapt the log distance model to indoor use cases. + It introduces an additional parameter, that models obstalces between (line-of-sight) the \docAPshort{} and the + location in question by attenuating the signal with a constant value. + % + Depending on the use case, this value describes the number and type of walls, ceilings, floors etc. between both locations. + For obstacles, this requires an intersection-test of each obstacle with the line-of-sight, which is costly + for larger buildings. For realtime use on a smartphone, a (discretized) model pre-computation might thus be necessary + \todo{cite competition}. + + \begin{equation} + x = \mTXP{} + 10 \mPLE{} + \log_{10} \frac{d}{d_0} + \numFloors{} \mWAF{} + \mGaussNoise{} + \label{eq:logNormShadowModel} + \end{equation} + + Throughout this work, walls are ignored and only floors/ceilings are considered for the model. + In \refeq{eq:logNormShadowModel}, floors/ceilings + are included using a constant attenuation factor \mWAF{} multiplied by the number of floors/ceilings \numFloors{} + between sender and the location in question. Assuming \todo{passendes wort?} buildings, this number can be determined + without costly intersection checks and thus allows for realtime use cases. + The attenuation \mWAF{} depends on the building's architecture and for common, steel enforced concrete floors + $\approx 8.0$ might be a viable choice \todo{cite}. + + + + \subsection {Model Setup} + + As previously mentioned, for the prediction model to work, one needs to know the locations of all + permanently installed \docAP{}s within the building plus their environmental parameters. + % + While it is possible to use empiric values for \mTXP, \mPLE and \mWAF \cite{Ebner-15}, the positions are mandtatory. + + For many installations, there should be floorplans that include the locations of all installed transmitters. + If so, a model setup takes only several minutes to (vaguely) position the \docAPshort{}s within a virtual + map and assigning them some fixed, empirically choosen parameters for \mTXP, \mPLE and \mWAF. + Depending on the building's architecture this might already provide enough accuracy for some use-cases + where a vague location information is sufficient. + + + \subsection{Parameter Optimization} + + + + + + + + + \begin{figure} + \input{gfx/wifiop_show_optfunc_params} + \label{fig:wifiOptFuncParams} + \caption{The average error (in \SI{}{\decibel}) between reference measurements and model predictions for one \docAPshort{} dependent on \docTXP{} and \docEXP{} [fixed position and \mWAF{}] denotes a convex function.} + \end{figure} + + \begin{figure} + \input{gfx/wifiop_show_optfunc_pos_yz} + \label{fig:wifiOptFuncPosYZ} + \caption{The average error (in \SI{}{\decibel}) between reference measurements and model predictions for one \docAPshort{} dependent on $y$- and $z$-position [fixed $x$, \mTXP{}, \mPLE{} and \mWAF{}] usually denotes a non-convex function with multiple [here: two] local minima.} + \end{figure} + + while optimizing txp and exp usually means optimizing a concave function, + optimzing the positiong usually isn't. Especially when the z-coordinate + [influencing the WAF] is involved. While this can be mitigated by introducing + a continuous function for the WAF like a sigmoid, this will still not work + for all situations @@ -9,6 +101,24 @@ b) bekannte pos + opt params (fur alle APs gleich) [simplex] c) bekannte pos + opt params (eigene je AP) [simplex] d) alles opt: pos und params (je ap) [range-random] +wenn man nur die fingerprints des floors nimmt in dem gelaufen wird, ist alles gut +sobald man andere floors drueber/drunter dazu nimmt, ist es nicht mehr gnaz so gut, oder wird schlechter +das spricht dafuer dass das modell nicht gut passt +koennte man zeigen indem man den durchschnittlichen fehler je fingerprint plottet??? + +the optimization-result also depends on the optimzation target: +a) the (average) error between measurment and model prediction +b) the (average) probability for the model's prediction given the fingerprint, ... +c) ... + + + + + + \subsection {VAP grouping} + VAP grouping erklaeren + + probleme bei der optimierung beschreiben. convex usw.. wo geht simplex gut, wo eher nicht diff --git a/tex/misc/functions.tex b/tex/misc/functions.tex index 9a098c9..a0b559e 100644 --- a/tex/misc/functions.tex +++ b/tex/misc/functions.tex @@ -4,9 +4,6 @@ \newcommand{\mLogDistGamma}{\ensuremath{\gamma}} \newcommand{\mLogDistTX}{TX} -\newcommand{\mDongle}[1]{\ensuremath{D_{#1}}} -%\newcommand{\mDongle}{d} % dongle -\newcommand{\mBeacon}[1]{\ensuremath{B_{#1}}} % beacon \newcommand{\mRssi}{\ensuremath{s}} % client's signal-strength measurement \newcommand{\mMdlRSSI}{\ensuremath{\varsigma}} % model's signal-strength @@ -39,8 +36,6 @@ \newcommand{\mSteps}{n_\text{steps}} \newcommand{\mObsSteps}{\mSteps} -\newcommand{\mNN}{\text{nn}} -\newcommand{\mKNN}{\text{knn}} \newcommand{\fPos}[1]{\textbf{pos}(#1)} \newcommand{\fDistance}[2]{\delta(#1, #2)} \newcommand{\fWA}[1]{\text{wall}(#1)} @@ -122,7 +117,9 @@ \newcommand{\mPLE}{\ensuremath{\gamma}} % path-loss exponent \newcommand{\mTXP}{\ensuremath{P_0}} % tx-power +\newcommand{\numFloors}{\ensuremath{n}} \newcommand{\mWAF}{\ensuremath{\beta}} % wall attenuation factor +\newcommand{\mGaussNoise}{\ensuremath{X}} \newcommand{\mMdlDist}{\ensuremath{d}} % distance used within propagation models diff --git a/tex/misc/keywords.tex b/tex/misc/keywords.tex index 2d928f2..5d729f6 100644 --- a/tex/misc/keywords.tex +++ b/tex/misc/keywords.tex @@ -1,10 +1,6 @@ % keyword macros \newcommand{\docIBeacon}{iBeacon} -% wifi naming -\newcommand{\wifiRSSI}{RSSI} -\newcommand{\wifiTxPower}{TX-Power} -\newcommand{\wifiPathLossExp}{PathLoss} \newcommand{\wifiPropLogScale}{Log-Scale} \newcommand{\wifiPropLogScaleWalls}{Log-Scale-Walls} @@ -13,9 +9,8 @@ % misc -\newcommand{\docTxPower}{TX-Power} -\newcommand{\docPathLossExp}{PathLoss} -\newcommand{\docPathLoss}{Pathloss} +\newcommand{\docTXP}{TX-power} +\newcommand{\docEXP}{path loss exponent} \newcommand{\docsAP}{AP} \newcommand{\docAPshort}{AP} diff --git a/wifi/EvalWiFiConvex.h b/wifi/EvalWiFiConvex.h new file mode 100644 index 0000000..985cceb --- /dev/null +++ b/wifi/EvalWiFiConvex.h @@ -0,0 +1,328 @@ +#ifndef EVALWIFICONVEX_H +#define EVALWIFICONVEX_H + +#include "Indoor/sensors/radio/setup/WiFiOptimizer.h" +#include "Indoor/sensors/radio/setup/WiFiFingerprint.h" +#include "Indoor/sensors/radio/setup/WiFiFingerprints.h" +#include "Indoor/sensors/radio/setup/WiFiOptimizer.h" +#include "Indoor/sensors/radio/model/WiFiModels.h" + +#include "Indoor/sensors/radio/VAPGrouper.h" +#include "Indoor/sensors/offline/FileReader.h" + +#include "Indoor/floorplan/v2/Floorplan.h" +#include "Indoor/floorplan/v2/FloorplanReader.h" +#include "Indoor/floorplan/v2/FloorplanHelper.h" +#include "Indoor/floorplan/v2/FloorplanCeilings.h" + +#include +#include +#include +#include +#include +#include + +#include + +//#include "../Structs.h" +#include "../plots/Plotty.h" +#include "../plots/PlotErrTime.h" +#include "../plots/PlotErrFunc.h" +#include "../Settings.h" +#include "../Helper.h" + +#include +#include + +class EvalWiFiConvex { + +private: + + Floorplan::IndoorMap* map; + std::vector mapAPs; + WiFiFingerprints calib; + +public: + + /** ctor */ + EvalWiFiConvex(Floorplan::IndoorMap* map, const std::string& fpFile) : map(map){ + + // all APs within the map + mapAPs = FloorplanHelper::getAPs(map); + + // load fingerprints + calib = WiFiFingerprints(fpFile); + + // VAP-group + VAPGrouper vap(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN); + for (WiFiFingerprint& fp : calib.getFingerprints()) { + fp.measurements = vap.group(fp.measurements); + } + + } + + void showParams() { + + K::Gnuplot gp; + K::GnuplotSplot splot; + K::GnuplotSplotElementMesh mesh; splot.add(&mesh); + + //splot.setTitle("optimizing TXP and EXP}"); + splot.getAxisX().setLabel("TXP (dBm)"); + splot.getAxisX().setLabelOffset(-3.9, -1.9); + splot.getAxisX().setTicsStep(2.5); + splot.getAxisX().setTicsOffset(-1, -0.1); + + splot.getAxisY().setLabel("EXP"); + splot.getAxisY().setLabelOffset(-0.5, -0.6); + splot.getAxisY().setTicsOffset(-0.3, -0.5); + + splot.getAxisZ().setLabel("error (dB)"); + + splot.getAxisCB().setTicsStep(4); + + splot.getView().setCamera(55, 115); + splot.getAxisZ().setLabelRotation(90); + splot.setStringMod(new K::GnuplotStringModLaTeX()); + + std::string name = Settings::fPathGFX + "/wifiop_show_optfunc_params"; + gp.setOutput(name + ".tex"); + gp.setTerminal("epslatex", K::GnuplotSize(8.7, 5.0)); + splot.getAxisZ().setTicsVisible(false); + splot.getAxisZ().setLabel(""); + gp << "set palette defined (0 '#00ff00', 1 '#eeeeee', 9 '#222222')\n"; + + gp << "set margins 0,0,0,0\n"; + gp << "set multiplot layout 1,1 scale 1.25, 1.4 offset 0.02, 0.05\n"; + gp << "set colorbox horizontal user origin 0.03, 0.95 size 0.4, 0.03\n"; + + //p mac="D8:84:66:4A:23:F0" px="46" py="47.400002" pz="13.8" txp="-40" exp="2.6500001" waf="-6.5"/> + const MACAddress mac("D8:84:66:4A:23:F0"); + const Point3 pos_m(46, 47.4, 13.8); + const float waf = -6; + + const std::vector fps = getFingerprints(mac); + + for (float txp = -45; txp <= -35; txp += 0.5) { + for (float exp = 1.5; exp <= 3.5; exp += 0.05) { + + WiFiModelLogDistCeiling model(map); + model.addAP(mac, pos_m, txp, exp, waf); + + const K::Statistics res = getError(mac, model, fps); + const float err = res.getAvg(); + mesh.add(K::GnuplotPoint3(txp, exp, err)); + + } + } + + + gp.draw(splot); + LeHelper::writeCode(name + ".gp", gp.getBuffer()); + gp.flush(); + + //sleep(100); + + } + + +// void showPos() { + +// K::Gnuplot gp; +// K::GnuplotSplot splot; +// K::GnuplotSplotElementMesh mesh; splot.add(&mesh); + +// //splot.setTitle("optimizing position"); +// splot.getAxisX().setLabel("x-position (meter)"); +// splot.getAxisY().setLabel("y-position (meter)"); +// splot.getAxisZ().setLabel("error (dB)"); + +// splot.getAxisZ().setLabelRotation(90); +// splot.setStringMod(new K::GnuplotStringModLaTeX()); +// gp << "set palette defined (0 '#00ff00', 1 '#eeeeee', 9 '#000000')\n"; + +// const MACAddress mac("D8:84:66:4A:23:F0"); +// const std::vector fps = getFingerprints(mac); + +// const Point3 pos_m(46, 47.4, 13.8); +// const float txp = -40; +// const float exp = 2.2; +// const float waf = -6; + +// for (float ox = -10; ox <= +10; ox += 0.5) { +// for (float oy = -19; oy <= +8; oy += 0.5) { + +// const Point3 pos = pos_m + Point3(ox, oy, 0); + +// WiFiModelLogDistCeiling model(map); +// model.addAP(mac, pos, txp, exp, waf); + +// const K::Statistics res = getError(mac, model, fps); +// const float err = res.getAvg(); +// mesh.add(K::GnuplotPoint3(pos.x, pos.y, err)); + +// } +// } + +// gp.draw(splot); +// gp.flush(); + +// //sleep(100); + +// } + + void showPosZ() { + + K::Gnuplot gp; + K::GnuplotSplot splot; + K::GnuplotSplotElementMesh mesh; splot.add(&mesh); + K::GnuplotSplotElementLines lines1; splot.add(&lines1); lines1.getStroke().setWidth(2); lines1.getStroke().setType(K::GnuplotDashtype::DOTTED); + K::GnuplotSplotElementLines lines2; splot.add(&lines2); lines2.getStroke().setWidth(2); lines2.getStroke().setType(K::GnuplotDashtype::DASHED); + K::GnuplotSplotElementLines lines3; splot.add(&lines3); lines3.getStroke().setWidth(2); + K::GnuplotSplotElementPoints points; splot.add(&points); points.setPointSize(1.5); points.setPointType(7); + + //splot.setTitle("optimizing position"); + splot.getAxisX().setLabel("y-pos (meter)"); + splot.getAxisX().setLabelOffset(-0.9, -1.4); + splot.getAxisX().setTicsOffset(-0.9, 0); + splot.getAxisX().setTicsStep(25); + + splot.getAxisY().setLabel("z-pos (meter)"); + splot.getAxisY().setLabelOffset(0, -0.3); + splot.getAxisY().setTicsOffset(0, -0.5); + + splot.getAxisY().setTicsStep(3); + + //splot.getAxisZ().setLabel("error (dB)"); + //splot.getAxisZ().setTicsStep(3); + //splot.getAxisZ().setLabelRotation(90); + splot.getAxisZ().setLabel(""); + splot.getAxisZ().setTicsVisible(false); + splot.getAxisCB().setTicsStep(3); + + + std::string name = Settings::fPathGFX + "/wifiop_show_optfunc_pos_yz"; + gp.setOutput(name + ".tex"); + gp.setTerminal("epslatex", K::GnuplotSize(8.7, 5.0)); + gp << "set palette defined (0 '#00ff00', 1 '#eeeeee', 9 '#222222')\n"; + gp << "set margins 0,0,0,0\n"; + gp << "set multiplot layout 1,1 scale 1.25,1.4 offset 0.02, 0.05\n"; + gp << "set colorbox horizontal user origin 0.57, 0.95 size 0.4, 0.03\n"; + + // paper out + splot.setStringMod(new K::GnuplotStringModLaTeX()); + splot.getView().setCamera(38, 109); + + const MACAddress mac("D8:84:66:4A:23:F0"); + const std::vector fps = getFingerprints(mac); + + const Point3 pos_m(46-15, 47.4-15, 13.8); + const float txp = -40; + const float exp = 2.2; + const float waf = -6; + + const int steps = 35; + + for (float sz = 0; sz < steps; ++sz) { + for (int sy = 0; sy < steps; ++sy) { + + const float oz = -15.0f + 20.0f * sz / steps; + const float oy = -35.0f + 90.0f * sy / steps; + + const Point3 pos = pos_m + Point3(0, oy, oz); + + WiFiModelLogDistCeiling model(map); + model.addAP(mac, pos, txp, exp, waf); + + const K::Statistics res = getError(mac, model, fps); + const float err = res.getAvg(); + const K::GnuplotPoint3 gp3(pos.y, pos.z, err); + mesh.add(gp3); + + if (sy == 0) {lines1.add(gp3);} + if (sy == 17) {lines2.add(gp3);} + if (sz == 24) {lines3.add(gp3);} + + if (sy == 9 && sz == 24) {points.add(gp3);} + if (sy == 26 && sz == 24) {points.add(gp3);} + + } + } + + gp.draw(splot); + LeHelper::writeCode(name + ".gp", gp.getBuffer()); + gp.flush(); + + int i = 0; (void) i; + + } + +private: + + std::vector getFingerprints(const MACAddress& mac) { + + // get all fingerprints where the given mac was seen + const std::vector fps = calib.getFingerprintsFor(mac); + + // sanity check + if (fps.size() < 5) {throw Exception("not enought fingerprints for AP " + mac.asString());} + + return fps; + + } + + + /** calculate the error for the given AP [mac + configured model] for all fingerprints for this mac */ + K::Statistics getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model) { + + // get all fingerprints where the given mac was seen + const std::vector fps = getFingerprints(mac); + + // fire + return getError(mac, model, fps); + + } + + /** calculate the error for the given AP [mac + configured model] for all of the given FPS */ + K::Statistics getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model, const std::vector& fps) { + + K::Statistics res; + + // calculate the model error for each fingerprint + for (const WiFiFingerprint& fp : fps) { + + // sanity checks + if (fp.measurements.entries.size() != 1) {throw Exception("invalid fingerprint");} + if (fp.measurements.entries.front().getAP().getMAC() != mac) {throw Exception("invalid fingerprint");} + + // rssi prediction from the configured model + const float model_rssi = model.getRSSI(mac, fp.pos_m); + + // sanity check + if (model_rssi != model_rssi) {throw Exception("model rssi not available for " + mac.asString());} + + // rssi measured during scan at this location + const float scan_rssi = fp.measurements.entries.front().getRSSI(); + + // sanity check + if (scan_rssi < -100) {throw Exception("scan rssi out of range for " + mac.asString());} + if (scan_rssi > -35) {throw Exception("scan rssi out of range for " + mac.asString());} + + // dB error + const float err = model_rssi - scan_rssi; + + // quadratic + float aErr = std::pow(std::abs(err), 1); + + res.add(aErr); + + } + + // done + return res; + + } + +}; + +#endif // EVALWIFICONVEX_H