many eval things
moved obsolete code TeX work
This commit is contained in:
@@ -58,7 +58,7 @@ ADD_DEFINITIONS(
|
||||
-fstack-protector-all
|
||||
|
||||
-g3
|
||||
-O2
|
||||
-O0
|
||||
-march=native
|
||||
|
||||
-DWITH_TESTS
|
||||
|
||||
@@ -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<MACAddress, std::vector<WiFiFingerprint>> 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<float> err = getError(mac, res.model);
|
||||
K::Statistics<float> 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<float> 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<WiFiFingerprint> 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<float> {
|
||||
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<float> 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<WiFiFingerprint> 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<float> {
|
||||
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<float> 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<float>& 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<WiFiFingerprint> 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<float>::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<float>::getProbability(model_rssi, 8, scan_rssi));
|
||||
|
||||
// quadratic
|
||||
float aErr = std::pow(std::abs(err), 2);
|
||||
|
||||
// penalty
|
||||
// if (model.getAP(mac).waf > -2) {aErr += 9999;}
|
||||
|
||||
68
EvalWiFi.h
68
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 <unordered_set>
|
||||
#include <thread>
|
||||
|
||||
template <typename T> 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<Point3> path;
|
||||
K::GnuplotSplotElementLines* gpPath = new K::GnuplotSplotElementLines();
|
||||
gpPath->setStroke(stroke);
|
||||
plot->splot.add(gpPath);
|
||||
|
||||
K::Statistics<float>* stats = new K::Statistics<float>();
|
||||
@@ -275,11 +268,11 @@ private:
|
||||
// parameters
|
||||
float params[3];
|
||||
|
||||
// USE GENETIC
|
||||
// std::minstd_rand gen;
|
||||
// std::uniform_real_distribution<float> distX(mapBBox.getMin().x, mapBBox.getMax().x);
|
||||
// std::uniform_real_distribution<float> distY(mapBBox.getMin().y, mapBBox.getMax().y);
|
||||
// std::uniform_real_distribution<float> 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<float> opt(3);
|
||||
// opt.setPopulationSize(400);
|
||||
// opt.setMaxIterations(20);
|
||||
// opt.calculateOptimum(func, params, init);
|
||||
|
||||
|
||||
// USE RANGE RANDOM WITH COOLING
|
||||
using LeOpt = K::NumOptAlgoRangeRandom<float>;
|
||||
const std::vector<LeOpt::MinMax> valRegion = {
|
||||
LeOpt::MinMax(mapBBox.getMin().x, mapBBox.getMax().x), // x
|
||||
@@ -304,7 +297,7 @@ private:
|
||||
K::NumOptAlgoRangeRandom<float> 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,28 +318,39 @@ 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);
|
||||
|
||||
if (xxx%2 == 0) {
|
||||
|
||||
// fire
|
||||
plot->plot();
|
||||
|
||||
// pet->plot();
|
||||
// pef->plot();
|
||||
pet->plot();
|
||||
pef->plot();
|
||||
|
||||
pet2->plot();
|
||||
pef2->plot();
|
||||
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
219
EvalWifiOptResult.h
Normal file
219
EvalWifiOptResult.h
Normal file
@@ -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 <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementHistogram.h>
|
||||
#include <KLib/misc/gnuplot/objects/GnuplotObjectRectangle.h>
|
||||
|
||||
#include <KLib/math/statistics/Statistics.h>
|
||||
|
||||
#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 <typename Model> 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<ErrPoint> errors;
|
||||
|
||||
// process each location
|
||||
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
|
||||
|
||||
// estimate error for this location
|
||||
K::Statistics<float> 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<ErrPoint> errorsInt;
|
||||
K::Statistics<float> 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
|
||||
6
Helper.h
6
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);
|
||||
|
||||
10
Settings.h
10
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;
|
||||
|
||||
205
main.cpp
205
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<std::pair<std::string, std::vector<int>>> calibWalks = {
|
||||
//// std::make_pair(Settings::path1a, Settings::GroundTruth::path1),
|
||||
//// std::make_pair(Settings::path1b, Settings::GroundTruth::path1),
|
||||
//// std::make_pair(Settings::path2a, Settings::GroundTruth::path2),
|
||||
//// std::make_pair(Settings::path2b, Settings::GroundTruth::path2),
|
||||
//// };
|
||||
//// EvalCompareOpt opt1(Settings::fMap, calibWalks);
|
||||
EvalWiFiOptResult eval1(Settings::fMap);
|
||||
eval1.showErrorPerFingerprint<WiFiModelLogDistCeiling>(Settings::wifiEachOptParPos, fps);
|
||||
|
||||
// // use fingerprints?
|
||||
// EvalCompareOpt opt1(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
|
||||
EvalWiFiOptResult eval2(Settings::fMap);
|
||||
eval2.showErrorPerFingerprint<WiFiModelLogDistCeiling>(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<WiFiModelLogDistCeiling>(Settings::wifiEachOptParPos_only0th, fps);
|
||||
evalfloor.showErrorPerFingerprint<WiFiModelLogDistCeiling>(Settings::wifiEachOptParPos_only1st, fps);
|
||||
evalfloor.showErrorPerFingerprint<WiFiModelLogDistCeiling>(Settings::wifiEachOptParPos_only2nd, fps);
|
||||
evalfloor.showErrorPerFingerprint<WiFiModelLogDistCeiling>(Settings::wifiEachOptParPos_only3rd, fps);
|
||||
evalfloor.showErrorPerFingerprint<WiFiModelPerFloor>(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<WiFiModelLogDistCeiling>(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<WiFiModelLogDistCeiling>(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<Floorplan::FloorObstacleLine*>(obs);
|
||||
// if (line) {
|
||||
// const K::GnuplotPoint3 p1(line->from.x, line->from.y, floor->atHeight);
|
||||
// const K::GnuplotPoint3 p2(line->to.x, line->to.y, floor->atHeight);
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
63
old/main.h
Normal file
63
old/main.h
Normal file
@@ -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<Floorplan::FloorObstacleLine*>(obs);
|
||||
// if (line) {
|
||||
// const K::GnuplotPoint3 p1(line->from.x, line->from.y, floor->atHeight);
|
||||
// const K::GnuplotPoint3 p2(line->to.x, line->to.y, floor->atHeight);
|
||||
// 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
|
||||
@@ -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<MyState, MyControl, MyObservation>* 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<MyGridNode>(Settings::Grid::gridSize_cm);
|
||||
@@ -104,14 +103,12 @@ public:
|
||||
|
||||
pf = new K::ParticleFilter<MyState, MyControl, MyObservation>( Settings::numParticles, std::unique_ptr<PFInit>(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<PFTrans>( new PFTrans(grid)) );
|
||||
|
||||
// resampling step?
|
||||
//pf->setNEffThreshold(0.5);
|
||||
//pf->setNEffThreshold(0.35);
|
||||
//pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingSimple<MyState>>(new K::ParticleFilterResamplingSimple<MyState>()) );
|
||||
|
||||
//pf->setNEffThreshold(0.75);
|
||||
@@ -120,17 +117,27 @@ public:
|
||||
//pf->setNEffThreshold(0.75);
|
||||
//pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingPercent<MyState>>(new K::ParticleFilterResamplingPercent<MyState>(0.05)) );
|
||||
|
||||
K::ParticleFilterResamplingNEff<MyState>* res = new K::ParticleFilterResamplingNEff<MyState>(0.50, 0.05);
|
||||
pf->setNEffThreshold(1.0);
|
||||
pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingNEff<MyState>>(new K::ParticleFilterResamplingNEff<MyState>(0.50, 0.05)) );
|
||||
pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingNEff<MyState>>(res) );
|
||||
|
||||
res->setDrawCallback([&] (K::Particle<MyState>& p) {
|
||||
static std::minstd_rand gen;
|
||||
const MyGridNode* n = grid->getNodePtrFor(p.state.position);
|
||||
std::normal_distribution<float> distTurn(-0.3, +0.3);
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
std::uniform_int_distribution<int> 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<K::ParticleFilterEstimationWeightedAverage<MyState>>(new K::ParticleFilterEstimationWeightedAverage<MyState>()));
|
||||
//pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationWeightedAverage<MyState>>(new K::ParticleFilterEstimationWeightedAverage<MyState>()));
|
||||
//pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationRegionalWeightedAverage<MyState>>(new K::ParticleFilterEstimationRegionalWeightedAverage<MyState>()));
|
||||
//pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationOrderedWeightedAverage<MyState>>(new K::ParticleFilterEstimationOrderedWeightedAverage<MyState>(0.50f)));
|
||||
|
||||
|
||||
|
||||
pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationOrderedWeightedAverage<MyState>>(new K::ParticleFilterEstimationOrderedWeightedAverage<MyState>(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<PFEval> eval = std::unique_ptr<PFEval>( new PFEval(grid, wifiModel, em) );
|
||||
|
||||
12
pf/PF.h
12
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<MyState>& 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<MyState, MyObservation> {
|
||||
|
||||
Grid<MyGridNode>* grid;
|
||||
|
||||
WiFiModelLogDistCeiling& wifiModel;
|
||||
WiFiModel& wifiModel;
|
||||
|
||||
EarthMapping& em;
|
||||
|
||||
@@ -260,7 +260,7 @@ class PFEval : public K::ParticleFilterEvaluation<MyState, MyObservation> {
|
||||
|
||||
public:
|
||||
|
||||
PFEval(Grid<MyGridNode>* grid, WiFiModelLogDistCeiling& wifiModel, EarthMapping& em) :
|
||||
PFEval(Grid<MyGridNode>* grid, WiFiModel& wifiModel, EarthMapping& em) :
|
||||
grid(grid), wifiModel(wifiModel), em(em),
|
||||
|
||||
wiFiProbability(Settings::WiFiModel::sigma, wifiModel) { // WiFi free
|
||||
|
||||
@@ -31,7 +31,7 @@ class PlotErrFunc {
|
||||
K::GnuplotPlot gplot;
|
||||
|
||||
//std::vector<std::string> colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"};
|
||||
std::vector<std::string> colors = {"#000000", "#999999", "#0000ff", "#9999ff", "#ff0000"};
|
||||
std::vector<std::string> 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<T> 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<Floorplan::FloorObstacleLine*>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
328
wifi/EvalWiFiConvex.h
Normal file
328
wifi/EvalWiFiConvex.h
Normal file
@@ -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 <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementMesh.h>
|
||||
|
||||
#include <KLib/math/statistics/Statistics.h>
|
||||
|
||||
//#include "../Structs.h"
|
||||
#include "../plots/Plotty.h"
|
||||
#include "../plots/PlotErrTime.h"
|
||||
#include "../plots/PlotErrFunc.h"
|
||||
#include "../Settings.h"
|
||||
#include "../Helper.h"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <thread>
|
||||
|
||||
class EvalWiFiConvex {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::IndoorMap* map;
|
||||
std::vector<APAtFloor> 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<WiFiFingerprint> 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<float> 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<WiFiFingerprint> 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<float> 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<WiFiFingerprint> 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<float> 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<WiFiFingerprint> getFingerprints(const MACAddress& mac) {
|
||||
|
||||
// get all fingerprints where the given mac was seen
|
||||
const std::vector<WiFiFingerprint> 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<float> getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model) {
|
||||
|
||||
// get all fingerprints where the given mac was seen
|
||||
const std::vector<WiFiFingerprint> 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<float> getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model, const std::vector<WiFiFingerprint>& fps) {
|
||||
|
||||
K::Statistics<float> 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
|
||||
Reference in New Issue
Block a user