fixed some potential issues with MAC addresses

added corresponding test-cases
switched to newer version of tinyxml due to some issues
adjusted affected code-parts accordingly
for better re-use, moved ceiling-calculation to a new class
some minor fixes
new helper methods
worked on wifi-opt
This commit is contained in:
2017-03-20 11:19:57 +01:00
parent d065015f7d
commit 06e0e0a5aa
20 changed files with 1486 additions and 676 deletions

View File

@@ -24,7 +24,7 @@ private:
} fields;
/** store as 64-bit integer */
uint64_t mac;
uint64_t mac = 0;
public:
@@ -61,8 +61,8 @@ public:
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
fields.h4 = hexWordToInt(str[ 2], str[ 3]);
fields.h3 = hexWordToInt(str[ 4], str[ 5]);
fields.h2 = hexWordToInt(str[ 6], str[7]);
fields.h1 = hexWordToInt(str[8], str[9]);
fields.h2 = hexWordToInt(str[ 6], str[ 7]);
fields.h1 = hexWordToInt(str[ 8], str[ 9]);
fields.h0 = hexWordToInt(str[10], str[11]);
}
else{
@@ -89,7 +89,9 @@ public:
/** get the mac address as a long-int value */
uint64_t asLong() const {
return mac;
// even if we initialized all 8 bytes with zero
// we mask the 2 unsued bytes to be absolutely safe
return mac & 0x0000FFFFFFFFFFFF;
}
/** equal? */
@@ -105,13 +107,19 @@ public:
private:
/** convert the given hex char [0-F] to an integer [0-15] */
static uint8_t hexCharToInt(const char _hex) {
// to upper case
const char hex = (_hex >= 'a') ? (_hex - ('a' - 'A')) : (_hex);
static uint8_t hexCharToInt(const char hex) {
// convert
return (hex - '0' < 10) ? (hex - '0') : (hex - 'A' + 10);
if (hex >= '0' && hex <= '9') { // digits
return (hex - '0');
} else if (hex >= 'a' && hex <= 'f') { // lower case
return (hex - 'a' + 10);
} else if (hex >= 'A' && hex <= 'F') { // upper case
return (hex - 'A' + 10);
}
// found an invalid character
throw Exception("invalid character within MAC address");
}

View File

@@ -7,6 +7,8 @@
#include "../../math/Stats.h"
#include "../../misc/Debug.h"
#include "WiFiMeasurements.h"
class VAPGrouper {
@@ -40,6 +42,8 @@ public:
private:
static constexpr const char* name = "VAPGrp";
/** the mode to use for grouping VAPs */
const Mode mode;
@@ -85,6 +89,9 @@ public:
}
Log::add(name, "grouped " + std::to_string(original.entries.size()) + " measurements into " + std::to_string(result.entries.size()), true);
// done
return result;

View File

@@ -2,6 +2,7 @@
#define WIFIMODELLOGDISTCEILING_H
#include "../../../floorplan/v2/Floorplan.h"
#include "../../../floorplan/v2/FloorplanCeilings.h"
#include "../../../Assertions.h"
#include "WiFiModel.h"
@@ -36,21 +37,18 @@ private:
/** map of all APs (and their parameters) known to the model */
std::unordered_map<MACAddress, APEntry> accessPoints;
/** position (height) of all ceilings (in meter) */
std::vector<float> ceilingsAtHeight_m;
// /** position (height) of all ceilings (in meter) */
// std::vector<float> ceilingsAtHeight_m;
Floorplan::Ceilings ceilings;
public:
/** ctor with floorplan (needed for ceiling position) */
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) {
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) : ceilings(map) {
// sanity checks
Assert::isTrue(map->floors.size() >= 1, "map has no floors?!");
// position of all ceilings
for (Floorplan::Floor* f : map->floors) {
ceilingsAtHeight_m.push_back(f->atHeight);
}
}
@@ -123,74 +121,13 @@ public:
const float rssiLOS = LogDistanceModel::distanceToRssi(params.txp, params.exp, distance_m);
// WAF loss (params.waf is a negative value!) -> WAF loss is also a negative value
const float wafLoss = params.waf * numCeilingsBetween(position_m, params.position_m);
const float wafLoss = params.waf * ceilings.numCeilingsBetween(position_m, params.position_m);
// combine
return rssiLOS + wafLoss;
}
protected:
FRIEND_TEST(LogDistanceCeilingModel, numCeilings);
FRIEND_TEST(LogDistanceCeilingModel, numCeilingsFloat);
/** get the number of ceilings between z1 and z2 */
float numCeilingsBetweenFloat(const Point3 pos1, const Point3 pos2) const {
const float zMin = std::min(pos1.z, pos2.z);
const float zMax = std::max(pos1.z, pos2.z);
float cnt = 0;
for (const float z : ceilingsAtHeight_m) {
if (zMin < z && zMax > z) {
const float dmax = zMax - z;
cnt += (dmax > 1) ? (1) : (dmax);
}
}
return cnt;
}
/** get the number of ceilings between z1 and z2 */
int numCeilingsBetween(const Point3 pos1, const Point3 pos2) const {
int cnt = 0;
const float zMin = std::min(pos1.z, pos2.z);
const float zMax = std::max(pos1.z, pos2.z);
#ifdef WITH_ASSERTIONS
static int numNear = 0;
static int numFar = 0;
for (const float z : ceilingsAtHeight_m) {
const float diff = std::min( std::abs(z-zMin), std::abs(z-zMax) );
if (diff < 0.1) {++numNear;} else {++numFar;}
}
if ((numNear + numFar) > 150000) {
Assert::isTrue(numNear < numFar*0.1,
"many requests to the WiFiModel address nodes (very) near to a ground! \
due to rounding issues, determining the number of floors between AP and point-in-question is NOT possible! \
expect very wrong outputs! \
consider adding the person's height to the questioned positions: p += Point3(0,0,1.3) "
);
}
#endif
for (const float z : ceilingsAtHeight_m) {
if (zMin < z && zMax > z) {++cnt;}
}
return cnt;
}
};

View File

@@ -8,10 +8,10 @@
#include <unordered_map>
/**
* denotes a wifi fingerprint
* known position and several measurements conducted at this position
* denotes a wifi fingerprint:
* known position and 1-n measurements [wifi-scan on all channels] conducted at this position
*
* as several measurements were conducted, each AP is usually contained more than once!
* if more than one measurement is conducted, each AP is contained more than once!
*/
struct WiFiFingerprint {
@@ -19,7 +19,7 @@ struct WiFiFingerprint {
/** real-world-position that was measured */
Point3 pos_m;
/** measurements (APs) at the given location */
/** seen APs at the given location */
WiFiMeasurements measurements;
@@ -31,7 +31,7 @@ struct WiFiFingerprint {
/** as each AP is contained more than once (scanned more than once), group them by MAC and use the average RSSI */
/** as each AP might be contained more than once (scanned more than once), group them by MAC and use the average RSSI */
WiFiMeasurements average() {
// group scans by MAC (all measurements for one AP)

View File

@@ -0,0 +1,125 @@
#ifndef WIFIFINGERPRINTS_H
#define WIFIFINGERPRINTS_H
#include <fstream>
#include "WiFiFingerprint.h"
/**
* helper class to load and save fingerprints.
* fingerprints are wifi-measurements given at some known location
* those can be used to e.g. optimize access-point models etc.
*/
class WiFiFingerprints {
private:
/** the file to save the calibration model to */
std::string file;
/** all fingerprints (position -> measurements) within the model */
std::vector<WiFiFingerprint> fingerprints;
public:
WiFiFingerprints(const std::string& file) : file(file) {
load();
}
const std::vector<WiFiFingerprint>& getFingerprints() const {
return fingerprints;
}
/** get all fingerprints that measured exactly the given mac [no VAP grouping] */
const std::vector<WiFiFingerprint> getFingerprintsFor(const MACAddress& mac) const {
std::vector<WiFiFingerprint> res;
// process each fingerprint location
for (const WiFiFingerprint& _fp : fingerprints) {
// start with an empty copy
WiFiFingerprint fp = _fp; fp.measurements.entries.clear();
// only add the measurements that belong to the requested mac
for (const WiFiMeasurement& _m : _fp.measurements.entries) {
if (_m.getAP().getMAC() == mac) {
fp.measurements.entries.push_back(_m);
}
}
if (fp.measurements.entries.size() > 0) { // got some measurements for this AP?
res.push_back(fp);
}
}
return res;
}
/** deserialize the model */
void load() {
// open and check
std::ifstream inp(file.c_str());
if (inp.bad() || inp.eof() || ! inp.is_open()) { return; }
// read all entries
while (!inp.eof()) {
// each section starts with [fingerprint]
std::string section;
inp >> section;
if (inp.eof()) {break;}
if (section != "[fingerprint]") {throw Exception("error!");}
// deserialize it
WiFiFingerprint wfp;
wfp.read(inp);
if (wfp.measurements.entries.empty()) {continue;}
fingerprints.push_back(wfp);
}
inp.close();
}
/** serialize the model */
void save() {
// open and check
std::ofstream out(file.c_str());
if (out.bad()) {throw Exception("error while opening " + file + " for write");}
// write all entries
for (const WiFiFingerprint& wfp : fingerprints) {
out << "[fingerprint]\n";
wfp.write(out);
}
// done
out.close();
}
/** get the fingerprint for the given location. if no fingerprint exists, an empty one is created! */
WiFiFingerprint& getFingerprint(const Point3 pos_m) {
// try to find an existing one
for (WiFiFingerprint& wfp : fingerprints) {
// get within range of floating-point rounding issues
if (wfp.pos_m.getDistance(pos_m) < 0.01) {return wfp;}
}
// create a new one and return its reference
WiFiFingerprint wfp(pos_m);
fingerprints.push_back(wfp);
return fingerprints.back();
}
};
#endif // WIFIFINGERPRINTS_H

View File

@@ -1,247 +1,61 @@
#ifndef OPTIMIZER_H
#define OPTIMIZER_H
#include "../../../floorplan/v2/Floorplan.h"
#include "../../../floorplan/v2/FloorplanHelper.h"
#ifndef WIFIOPTIMIZER_H
#define WIFIOPTIMIZER_H
#include "WiFiFingerprints.h"
#include "WiFiOptimizerStructs.h"
#include "../VAPGrouper.h"
#include "../../../geo/BBox3.h"
#include "../../../misc/Debug.h"
#include "WiFiFingerprint.h"
#include "../model/WiFiModel.h"
#include "../model/WiFiModelLogDistCeiling.h"
#include <unordered_map>
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
#include <KLib/math/optimization/NumOptAlgoRangeRandom.h>
namespace WiFiOptimizer {
#include <string>
#include <sstream>
/** base-class for all WiFiOptimizers */
class Base {
struct WiFiOptimizer {
protected:
private:
/** each MAC-Adress has several position->rssi entries */
std::unordered_map<MACAddress, std::vector<RSSIatPosition>> apMap;
/** combine one RSSI measurement with the position the signal was measured at */
struct RSSIatPosition {
/** how to handle virtual access points [group, not-group, ..] */
const VAPGrouper vg;
/** real-world position (in meter) */
const Point3 pos_m;
/** measured signal strength (for one AP) */
const float rssi;
public:
/** ctor */
RSSIatPosition(const Point3 pos_m, const float rssi) : pos_m(pos_m), rssi(rssi) {;}
Base(const VAPGrouper vg) : vg(vg) {;}
};
public:
struct APParams {
float x;
float y;
float z;
float txp;
float exp;
float waf;
Point3 getPos() const {return Point3(x,y,z);}
APParams() {;}
APParams(float x, float y, float z, float txp, float exp, float waf) : x(x), y(y), z(z), txp(txp), exp(exp), waf(waf) {;}
std::string asString() {
std::stringstream ss;
ss << "Pos:" << getPos().asString() << " TXP:" << txp << " EXP:" << exp << " WAF:" << waf;
return ss.str();
}
};
/** add MAC-info to params */
struct APParamsMAC {
MACAddress mac;
APParams params;
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
};
private:
Floorplan::IndoorMap* map;
const VAPGrouper vg;
/** each MAC-Adress has several position->rssi entries */
std::unordered_map<MACAddress, std::vector<RSSIatPosition>> apMap;
const char* name = "WiFiOptimizer";
public:
/** ctor */
WiFiOptimizer(Floorplan::IndoorMap* map, const VAPGrouper& vg) : map(map), vg(vg) {
;
}
/** add a new fingerprint to the optimizers data-source */
void addFingerprint(const WiFiFingerprint& fp) {
// group the fingerprint's measurements by VAP (if configured)
const WiFiMeasurements measurements = vg.group(fp.measurements);
// add each available AP to its slot (lookup map)
for (const WiFiMeasurement& m : measurements.entries) {
const RSSIatPosition rap(fp.pos_m, m.getRSSI());
apMap[m.getAP().getMAC()].push_back(rap);
/** get a list of all to-be-optimized access-points (given by their mac-address) */
virtual std::vector<MACAddress> getAllMACs() const {
std::vector<MACAddress> res;
for (const auto& it : apMap) {res.push_back(it.first);}
return res;
}
}
/** add a new fingerprint to the optimizer as data-source */
virtual void addFingerprint(const WiFiFingerprint& fp) {
/** get a list of all to-be-optimized access-points (given by their mac-address) */
std::vector<MACAddress> getAllMACs() const {
std::vector<MACAddress> res;
for (const auto& it : apMap) {res.push_back(it.first);}
return res;
}
/** optimize all known APs */
std::vector<APParamsMAC> optimizeAll() const {
// sanity chekc
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization!");
float errSum = 0;
std::vector<APParamsMAC> res;
for (const MACAddress& mac : getAllMACs()) {
float err;
const APParams params = optimize(mac, err);
res.push_back(APParamsMAC(mac, params));
errSum += err;
}
const float avgErr = errSum / getAllMACs().size();
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
return res;
}
/** optimize the given AP */
APParams optimize(const MACAddress& mac, float& errResult) const {
// starting parameters do not matter for the current optimizer!
APParams params(0,0,0, -40, 2.5, -4.0);
constexpr float hugeError = 1e10;
// get all position->rssi measurements for this AP to compare them with the corresponding model estimations
const std::vector<RSSIatPosition>& entries = apMap.find(mac)->second;
// signal-strength-prediction-model...
WiFiModelLogDistCeiling model(map);
auto func = [&] (const float* data) {
const APParams* params = (APParams*) data;
// some sanity checks
if (params->waf > 0) {return hugeError;}
if (params->txp < -50) {return hugeError;}
if (params->txp > -30) {return hugeError;}
if (params->exp > 4) {return hugeError;}
if (params->exp < 1) {return hugeError;}
// current position guess for the AP;
const Point3 apPos_m = params->getPos();
// add the AP [described by the current guess] to the signal-strength-prediction model
model.clear();
model.addAP(mac, WiFiModelLogDistCeiling::APEntry(apPos_m, params->txp, params->exp, params->waf));
float err = 0;
int cnt = 0;
// process each measurement
for (const RSSIatPosition& reading : entries) {
// get the model-estimation for the fingerprint's position
const float rssiModel = model.getRSSI(mac, reading.pos_m);
// difference between estimation and measurement
const float diff = std::abs(rssiModel - reading.rssi);
// adjust the error
err += diff*diff;
++cnt;
// max distance penality
// [unlikely to get a reading for this AP here!]
if (apPos_m.getDistance(reading.pos_m) > 150) {err += 999999;}
// group the fingerprint's measurements by VAP (if configured)
const WiFiMeasurements measurements = vg.group(fp.measurements);
// add each available AP to its slot (lookup map)
for (const WiFiMeasurement& m : measurements.entries) {
const RSSIatPosition rap(fp.pos_m, m.getRSSI());
apMap[m.getAP().getMAC()].push_back(rap);
}
err /= cnt;
err = std::sqrt(err);
}
if (params->txp < -50) {err += 999999;}
if (params->txp > -35) {err += 999999;}
/** add new fingerprints to the optimizer as data-source */
virtual void addFingerprints(const WiFiFingerprints& fps) {
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
addFingerprint(fp);
}
}
if (params->exp > 3.5) {err += 999999;}
if (params->exp < 1.0) {err += 999999;}
return err;
};
};
//
const BBox3 mapBBox = FloorplanHelper::getBBox(map);
}
using LeOpt = K::NumOptAlgoRangeRandom<float>;
const std::vector<LeOpt::MinMax> valRegion = {
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z
LeOpt::MinMax(-50,-30), // txp
LeOpt::MinMax(1,3), // exp
LeOpt::MinMax(-10,-4), // waf
};
// log
Log::add(name, "optimizing parameters for AP " + mac.asString() + " by using " + std::to_string(entries.size()) + " fingerprints", false);
Log::tick();
LeOpt opt(valRegion);
opt.setPopulationSize(500); // USE MORE FOR PRODUCTION
opt.setNumIerations(150);
opt.calculateOptimum(func, (float*) &params);
// using LeOpt = K::NumOptAlgoGenetic<float>;
// LeOpt opt(6);
// opt.setPopulationSize(750);
// opt.setMaxIterations(50);
// opt.setElitism(0.05f);
// opt.setMutation(0.75f);
// //opt.setValRange({0.5, 0.5, 0.5, 0.1, 0.1, 0.1});
// opt.setValRegion(valRegion);
// K::NumOptAlgoDownhillSimplex<float, 6> opt;
// opt.setMaxIterations(100);
// opt.setNumRestarts(10);
opt.calculateOptimum(func, (float*) &params);
errResult = func((float*)&params);
Log::tock();
Log::add(name, mac.asString() + ": " + params.asString() + " @ " + std::to_string(errResult) +" dB err");
return params;
}
};
#endif // OPTIMIZER_H
#endif // WIFIOPTIMIZER_H

View File

@@ -0,0 +1,341 @@
#ifndef WIFI_OPTIMIZER_LOG_DIST_CEILING_H
#define WIFI_OPTIMIZER_LOG_DIST_CEILING_H
#include "../../../floorplan/v2/Floorplan.h"
#include "../../../floorplan/v2/FloorplanHelper.h"
#include "../../../geo/BBox3.h"
#include "../../../misc/Debug.h"
#include "WiFiFingerprint.h"
#include "WiFiFingerprints.h"
#include "../model/WiFiModel.h"
#include "../model/WiFiModelLogDistCeiling.h"
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
#include <KLib/math/optimization/NumOptAlgoRangeRandom.h>
#include <string>
#include <sstream>
#include "WiFiOptimizer.h"
#include "WiFiOptimizerStructs.h"
#include <functional>
namespace WiFiOptimizer {
/**
* optimize access-point parameters,
* given several fingerprints using the log-dist-ceiling model
*/
struct LogDistCeiling : public Base {
public:
enum class Mode {
FAST,
MEDIUM,
QUALITY,
};
/**
* resulting optimization stats for one AP
*/
struct Stats {
/** average model<->scan error after optimzing */
float error_db;
/** number of fingerprints [= locations] that were used for optimzing */
int usedFingerprins;
/** resulting model<->scan error after optimzing for each individual fingerprints [= location] */
std::vector<ErrorAtPosition> errors;
/** get the location where the model estimation reaches the highest negative value [model estimation too low] */
ErrorAtPosition getEstErrorMaxNeg() const {
auto cmpErrAtPos = [] (const ErrorAtPosition& a, const ErrorAtPosition& b) {return a.getError_db() < b.getError_db();};
return *std::min_element(errors.begin(), errors.end(), cmpErrAtPos);
}
/** get the location where the model estimation reaches the highest positive value [model estimation too high] */
ErrorAtPosition getEstErrorMaxPos() const {
auto cmpErrAtPos = [] (const ErrorAtPosition& a, const ErrorAtPosition& b) {return a.getError_db() < b.getError_db();};
return *std::max_element(errors.begin(), errors.end(), cmpErrAtPos);
}
};
/** parameters for one AP when using the LogDistCeiling model */
struct APParams {
float x;
float y;
float z;
float txp;
float exp;
float waf;
Point3 getPos() const {return Point3(x,y,z);}
/** ctor */
APParams() {;}
/** ctor */
APParams(float x, float y, float z, float txp, float exp, float waf) : x(x), y(y), z(z), txp(txp), exp(exp), waf(waf) {;}
std::string asString() const {
std::stringstream ss;
ss << "Pos:" << getPos().asString() << " TXP:" << txp << " EXP:" << exp << " WAF:" << waf;
return ss.str();
}
};
/** add MAC-info to params */
struct APParamsMAC {
MACAddress mac;
APParams params;
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
};
class APParamsList {
std::vector<APParamsMAC> lst;
public:
/** ctor */
APParamsList(const std::vector<APParamsMAC>& lst) : lst(lst) {
}
/** get the list */
const std::vector<APParamsMAC>& get() const {
return lst;
}
/** get params for the given mac [if known, otherwise nullptr] */
const APParamsMAC* get (const MACAddress& mac) const {
for (const APParamsMAC& ap : lst) {
if (ap.mac == mac) {return &ap;}
}
return nullptr;
}
};
using APFilter = std::function<bool(const int numFingerprints, const MACAddress& mac)>;
const APFilter NONE = [] (const int numFingerprints, const MACAddress& mac) {
(void) numFingerprints; (void) mac;
return false;
};
const APFilter MIN_8_FPS = [] (const int numFingerprints, const MACAddress& mac) {
(void) mac;
return numFingerprints < 8;
};
private:
Mode mode = Mode::QUALITY;
Floorplan::IndoorMap* map;
const char* name = "WiFiOptLDC";
public:
/** ctor */
LogDistCeiling(Floorplan::IndoorMap* map, const VAPGrouper& vg, const Mode mode = Mode::QUALITY) : Base(vg), map(map), mode(mode) {
;
}
/** ctor */
LogDistCeiling(Floorplan::IndoorMap* map, const VAPGrouper& vg, const WiFiFingerprints& fps, const Mode mode = Mode::QUALITY) : Base(vg), map(map), mode(mode) {
addFingerprints(fps);
}
/** optimize all known APs */
APParamsList optimizeAll(APFilter filter) const {
// sanity check
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization! call addFingerprint() first!");
float errSum = 0; int errCnt = 0;
std::vector<APParamsMAC> res;
for (const MACAddress& mac : getAllMACs()) {
Stats stats;
const APParams params = optimize(mac, stats);
if (!filter(stats.usedFingerprins, mac)) {
res.push_back(APParamsMAC(mac, params));
errSum += stats.error_db;
++errCnt;
} else {
std::cout << "ignored due to filter!" << std::endl;
}
}
const float avgErr = errSum / errCnt;
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
// done
return APParamsList(res);
}
/** optimize the given AP */
APParams optimize(const MACAddress& mac, Stats& res) const {
// starting parameters do not matter for the current optimizer!
APParams params(0,0,0, -40, 2.5, -4.0);
// get all position->rssi measurements for this AP to compare them with the corresponding model estimations
const std::vector<RSSIatPosition>& entries = apMap.find(mac)->second;
// log
Log::add(name, "optimizing parameters for AP " + mac.asString() + " by using " + std::to_string(entries.size()) + " fingerprints", false);
Log::tick();
// get the map's size
const BBox3 mapBBox = FloorplanHelper::getBBox(map);
using LeOpt = K::NumOptAlgoRangeRandom<float>;
const std::vector<LeOpt::MinMax> valRegion = {
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z
LeOpt::MinMax(-50, -30), // txp
LeOpt::MinMax(1, 5), // exp
LeOpt::MinMax(-15, -0), // waf
};
LeOpt opt(valRegion);
switch(mode) {
case Mode::FAST:
opt.setPopulationSize(100);
opt.setNumIerations(50);
break;
case Mode::MEDIUM:
opt.setPopulationSize(200);
opt.setNumIerations(100);
break;
case Mode::QUALITY:
opt.setPopulationSize(500);
opt.setNumIerations(150);
break;
}
// error function
auto func = [&] (const float* params) {
return getErrorLogDistCeiling(mac, entries, params, nullptr);
};
opt.calculateOptimum(func, (float*) &params);
// using LeOpt = K::NumOptAlgoGenetic<float>;
// LeOpt opt(6);
// opt.setPopulationSize(750);
// opt.setMaxIterations(50);
// opt.setElitism(0.05f);
// opt.setMutation(0.75f);
// //opt.setValRange({0.5, 0.5, 0.5, 0.1, 0.1, 0.1});
// opt.setValRegion(valRegion);
// K::NumOptAlgoDownhillSimplex<float, 6> opt;
// opt.setMaxIterations(100);
// opt.setNumRestarts(10);
opt.calculateOptimum(func, (float*) &params);
res.error_db = getErrorLogDistCeiling(mac, entries, (float*)&params, &res);
res.usedFingerprins = entries.size();
Log::tock();
Log::add(name, mac.asString() + ": " + params.asString() + " @ " + std::to_string(res.error_db) +" dB err");
return params;
}
private:
float getErrorLogDistCeiling(const MACAddress& mac, const std::vector<RSSIatPosition>& entries, const float* data, Stats* stats = nullptr) const {
constexpr float hugeError = 1e10;
const APParams* params = (APParams*) data;
// some sanity checks
if (params->waf > 0) {return hugeError;}
if (params->txp < -50) {return hugeError;}
if (params->txp > -30) {return hugeError;}
if (params->exp > 4) {return hugeError;}
if (params->exp < 1) {return hugeError;}
// current position guess for the AP;
const Point3 apPos_m = params->getPos();
// add the AP [described by the current guess] to the signal-strength-prediction model
// signal-strength-prediction-model...
WiFiModelLogDistCeiling model(map);
model.addAP(mac, WiFiModelLogDistCeiling::APEntry(apPos_m, params->txp, params->exp, params->waf));
float err = 0;
int cnt = 0;
// process each measurement
for (const RSSIatPosition& reading : entries) {
// get the model-estimation for the fingerprint's position
const float rssiModel = model.getRSSI(mac, reading.pos_m);
// difference between estimation and measurement
const float diff = std::abs(rssiModel - reading.rssi);
// add error to stats object?
if (stats) {
stats->errors.push_back(ErrorAtPosition(reading.pos_m, reading.rssi, rssiModel));
}
// adjust the error
err += diff*diff;
++cnt;
// max distance penality
// [unlikely to get a reading for this AP here!]
if (apPos_m.getDistance(reading.pos_m) > 150) {err += 999999;}
}
err /= cnt;
err = std::sqrt(err);
if (params->txp < -50) {err += 999999;}
if (params->txp > -35) {err += 999999;}
if (params->exp > 3.5) {err += 999999;}
if (params->exp < 1.0) {err += 999999;}
return err;
}
};
}
#endif // WIFI_OPTIMIZER_LOG_DIST_CEILING_H

View File

@@ -0,0 +1,55 @@
#ifndef WIFIOPTIMIZERSTRUCTS_H
#define WIFIOPTIMIZERSTRUCTS_H
#include "../../../geo/Point3.h"
namespace WiFiOptimizer {
/**
* one entry that is used during optimization:
* combine one RSSI measurement with the position the signal was measured at
*/
struct RSSIatPosition {
/** real-world position (in meter) */
const Point3 pos_m;
/** measured signal strength (for one AP) */
const float rssi;
/** ctor */
RSSIatPosition(const Point3 pos_m, const float rssi) : pos_m(pos_m), rssi(rssi) {;}
};
/**
* for statistics
* after optimiziaton
*
* denotes the difference [error] between one fingerprinted rssi
* at location (x,y,z) and the model estimation for this location
*/
struct ErrorAtPosition {
/** real-world position (in meter) */
const Point3 pos_m;
/** measured signal strength (for one AP) */
const float scan_rssi;
/** final model's prediction */
const float model_rssi;
/** ctor */
ErrorAtPosition(const Point3 pos_m, const float scan_rssi, const float model_rssi) : pos_m(pos_m), scan_rssi(scan_rssi), model_rssi(model_rssi) {;}
/** get the difference [error] between model-estimated-rssi and fingerprinted-rssi */
float getError_db() const {
return model_rssi - scan_rssi;
}
};
}
#endif // WIFIOPTIMIZERSTRUCTS_H