Compare commits
21 Commits
0.0.1
...
workingTon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b18b1a795d | ||
|
|
7ff7334263 | ||
|
|
b1a9286d71 | ||
|
|
2eb9405950 | ||
|
|
e48d3bafcd | ||
|
|
62087fe072 | ||
|
|
54894a0c23 | ||
|
|
ef6f44969f | ||
| ae357ffd4b | |||
|
|
0bf8a9c25c | ||
|
|
a35a22e676 | ||
| cad299cd7c | |||
|
|
d5bc1111f5 | ||
|
|
f7d0d88448 | ||
|
|
b6be58eebc | ||
|
|
5ec0b7e9ee | ||
|
|
e10ba2cfd9 | ||
| 2255904385 | |||
| 272382ae3d | |||
|
|
41c1d90c54 | ||
|
|
c083aae476 |
@@ -68,7 +68,7 @@ ADD_DEFINITIONS(
|
|||||||
-fstack-protector-all
|
-fstack-protector-all
|
||||||
|
|
||||||
-g3
|
-g3
|
||||||
-O0
|
#-O0
|
||||||
-march=native
|
-march=native
|
||||||
|
|
||||||
-DWITH_TESTS
|
-DWITH_TESTS
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "../../geo/Point3.h"
|
#include "../../geo/Point3.h"
|
||||||
#include "../../geo/Point2.h"
|
#include "../../geo/Point2.h"
|
||||||
|
#include "../../geo/EarthPos.h"
|
||||||
|
|
||||||
namespace Floorplan {
|
namespace Floorplan {
|
||||||
|
|
||||||
@@ -21,20 +22,86 @@ namespace Floorplan {
|
|||||||
/** a free key-value meta element */
|
/** a free key-value meta element */
|
||||||
struct Meta {
|
struct Meta {
|
||||||
|
|
||||||
|
struct KeyVal {
|
||||||
|
std::string key;
|
||||||
|
std::string val;
|
||||||
|
KeyVal() {;}
|
||||||
|
KeyVal(const std::string& key, const std::string& val) : key(key), val(val) {;}
|
||||||
|
};
|
||||||
|
|
||||||
const std::string EMPTY = "";
|
const std::string EMPTY = "";
|
||||||
std::unordered_map<std::string, std::string> params;
|
|
||||||
|
// std::unordered_map<std::string, std::string> params;
|
||||||
|
// const std::string& getVal(const std::string& key) const {
|
||||||
|
// const auto it = params.find(key);
|
||||||
|
// return (it == params.end()) ? (EMPTY) : (it->second);
|
||||||
|
// }
|
||||||
|
|
||||||
|
std::vector<KeyVal> params;
|
||||||
|
|
||||||
|
KeyVal* getKV(const std::string& key) {
|
||||||
|
for (KeyVal& kv : params) {
|
||||||
|
if (kv.key == key) {return &kv;}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeyVal* getKV(const std::string& key) const {
|
||||||
|
for (const KeyVal& kv : params) {
|
||||||
|
if (kv.key == key) {return &kv;}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& getVal(const std::string& key) const {
|
const std::string& getVal(const std::string& key) const {
|
||||||
const auto it = params.find(key);
|
static const std::string EMPTY = "";
|
||||||
return (it == params.end()) ? (EMPTY) : (it->second);
|
const KeyVal* kv = getKV(key);
|
||||||
|
return (kv) ? (kv->val) : (EMPTY);
|
||||||
|
}
|
||||||
|
void setVal(const std::string& key, const std::string& val) {(*this)[key] = val;}
|
||||||
|
|
||||||
|
void add(const std::string& key, const std::string& val) {params.push_back(KeyVal(key,val));}
|
||||||
|
|
||||||
|
std::string& operator [] (const std::string& key) {
|
||||||
|
KeyVal* kv = getKV(key);
|
||||||
|
if (!kv) {params.push_back(KeyVal(key, ""));}
|
||||||
|
return getKV(key)->val;
|
||||||
}
|
}
|
||||||
void setVal(const std::string& key, const std::string& val) {params[key] = val;}
|
|
||||||
|
|
||||||
float getFloat(const std::string& key) const { return std::stof(getVal(key)); }
|
float getFloat(const std::string& key) const { return std::stof(getVal(key)); }
|
||||||
void setFloat(const std::string& key, const float val) { params[key] = std::to_string(val); }
|
void setFloat(const std::string& key, const float val) { (*this)[key] = std::to_string(val); }
|
||||||
|
|
||||||
int getInt(const std::string& key) const { return std::stoi(getVal(key)); }
|
int getInt(const std::string& key) const { return std::stoi(getVal(key)); }
|
||||||
void setInt(const std::string& key, const int val) { params[key] = std::to_string(val); }
|
void setInt(const std::string& key, const int val) { (*this)[key] = std::to_string(val); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size_t size() const {return params.size();}
|
||||||
|
|
||||||
|
const std::string& getKey(const int idx) const {return params[idx].key;}
|
||||||
|
const std::string& getVal(const int idx) const {return params[idx].val;}
|
||||||
|
|
||||||
|
void set(const int idx, const KeyVal& kv) {params[idx] = kv;}
|
||||||
|
void setKey(const int idx, const std::string& key) {params[idx].key = key;}
|
||||||
|
void setVal(const int idx, const std::string& val) {params[idx].val = val;}
|
||||||
|
|
||||||
|
/** delete the idx-th entry */
|
||||||
|
void deleteEntry(const int idx) {params.erase(params.begin()+idx);}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class HasMeta {
|
||||||
|
|
||||||
|
Meta* meta = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Meta* getMeta() const {return meta;}
|
||||||
|
|
||||||
|
void setMeta(Meta* meta) {
|
||||||
|
if (this->meta) {delete this->meta;}
|
||||||
|
this->meta = meta;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +184,7 @@ namespace Floorplan {
|
|||||||
struct POI;
|
struct POI;
|
||||||
struct Stair;
|
struct Stair;
|
||||||
struct Elevator;
|
struct Elevator;
|
||||||
|
struct GroundTruthPoint;
|
||||||
|
|
||||||
using FloorOutline = std::vector<FloorOutlinePolygon*>;
|
using FloorOutline = std::vector<FloorOutlinePolygon*>;
|
||||||
using FloorObstacles = std::vector<FloorObstacle*>;
|
using FloorObstacles = std::vector<FloorObstacle*>;
|
||||||
@@ -127,6 +195,7 @@ namespace Floorplan {
|
|||||||
using FloorPOIs = std::vector<POI*>;
|
using FloorPOIs = std::vector<POI*>;
|
||||||
using FloorStairs = std::vector<Stair*>;
|
using FloorStairs = std::vector<Stair*>;
|
||||||
using FloorElevators = std::vector<Elevator*>;
|
using FloorElevators = std::vector<Elevator*>;
|
||||||
|
using FloorGroundTruthPoints = std::vector<GroundTruthPoint*>;
|
||||||
|
|
||||||
/** describes one floor within the map, starting at a given height */
|
/** describes one floor within the map, starting at a given height */
|
||||||
struct Floor {
|
struct Floor {
|
||||||
@@ -143,6 +212,7 @@ namespace Floorplan {
|
|||||||
FloorPOIs pois; // POIs within the floor
|
FloorPOIs pois; // POIs within the floor
|
||||||
FloorStairs stairs; // all stairs within one floor
|
FloorStairs stairs; // all stairs within one floor
|
||||||
FloorElevators elevators; // all elevators within one floor
|
FloorElevators elevators; // all elevators within one floor
|
||||||
|
FloorGroundTruthPoints gtpoints; // all ground truth points within one floor
|
||||||
//FloorKeyValue other; // other, free elements
|
//FloorKeyValue other; // other, free elements
|
||||||
|
|
||||||
Floor() {;}
|
Floor() {;}
|
||||||
@@ -167,11 +237,25 @@ namespace Floorplan {
|
|||||||
bool operator == (const POI& o) const {return (o.type == type) && (o.name == name) && (o.pos == pos);}
|
bool operator == (const POI& o) const {return (o.type == type) && (o.name == name) && (o.pos == pos);}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** a GroundTruthPoint located somewhere on a floor */
|
||||||
|
struct GroundTruthPoint {
|
||||||
|
int id; //TODO: this value can be changed and isn't set incremental within the indoormap
|
||||||
|
Point2 pos;
|
||||||
|
GroundTruthPoint() : id(), pos() {;}
|
||||||
|
GroundTruthPoint(const int& id, const Point2& pos) : id(id), pos(pos) {;}
|
||||||
|
bool operator == (const GroundTruthPoint& o) const {return (o.id == id) && (o.pos == pos);}
|
||||||
|
};
|
||||||
|
|
||||||
/** an AccessPoint located somewhere on a floor */
|
/** an AccessPoint located somewhere on a floor */
|
||||||
struct AccessPoint {
|
struct AccessPoint : public HasMeta {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string mac;
|
std::string mac;
|
||||||
Point3 pos; // z is relative to the floor's height
|
Point3 pos; // z is relative to the floor's height
|
||||||
|
struct Model {
|
||||||
|
float txp = 0;
|
||||||
|
float exp = 0;
|
||||||
|
float waf = 0;
|
||||||
|
} model;
|
||||||
AccessPoint() : name(), mac(), pos() {;}
|
AccessPoint() : name(), mac(), pos() {;}
|
||||||
AccessPoint(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(toUpperCase(mac)), pos(pos) {;}
|
AccessPoint(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(toUpperCase(mac)), pos(pos) {;}
|
||||||
bool operator == (const AccessPoint& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
|
bool operator == (const AccessPoint& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
|
||||||
@@ -182,10 +266,19 @@ namespace Floorplan {
|
|||||||
struct Beacon {
|
struct Beacon {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string mac;
|
std::string mac;
|
||||||
|
std::string major;
|
||||||
|
std::string minor;
|
||||||
|
std::string uuid;
|
||||||
Point3 pos; // z is relative to the floor's height
|
Point3 pos; // z is relative to the floor's height
|
||||||
|
struct Model {
|
||||||
|
float txp;
|
||||||
|
float exp;
|
||||||
|
float waf;
|
||||||
|
} model;
|
||||||
Beacon() : name(), mac(), pos() {;}
|
Beacon() : name(), mac(), pos() {;}
|
||||||
Beacon(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(mac), pos(pos) {;}
|
Beacon(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(mac), pos(pos) {;}
|
||||||
bool operator == (const Beacon& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
|
bool operator == (const Beacon& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
|
||||||
|
Point3 getPos(const Floor* f) const {return pos + Point3(0,0,f->atHeight);} // relative to the floor's ground
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -375,8 +468,7 @@ namespace Floorplan {
|
|||||||
|
|
||||||
|
|
||||||
/** base-class for stairs */
|
/** base-class for stairs */
|
||||||
struct Stair {
|
struct Stair : public HasMeta {
|
||||||
Meta* meta = nullptr;
|
|
||||||
virtual std::vector<StairPart> getParts() const = 0;
|
virtual std::vector<StairPart> getParts() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -437,16 +529,44 @@ namespace Floorplan {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** one correspondence point: earth <-> map */
|
||||||
|
struct EarthPosMapPos {
|
||||||
|
|
||||||
|
EarthPos posOnEarth;
|
||||||
|
|
||||||
|
Point3 posOnMap_m;
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
EarthPosMapPos(const EarthPos posOnEarth, const Point3 posOnMap_m) : posOnEarth(posOnEarth), posOnMap_m(posOnMap_m) {;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** describe the floorplan's location on earth */
|
||||||
|
struct EarthRegistration {
|
||||||
|
|
||||||
|
/** all available correspondences: earth <-> map */
|
||||||
|
std::vector<EarthPosMapPos> correspondences;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/** describes the whole indoor map */
|
/** describes the whole indoor map */
|
||||||
struct IndoorMap {
|
struct IndoorMap {
|
||||||
|
|
||||||
float width;
|
float width;
|
||||||
float depth;
|
float depth;
|
||||||
|
|
||||||
std::vector<Floor*> floors;
|
std::vector<Floor*> floors;
|
||||||
|
|
||||||
|
/** mapping: floorplan <-> earth */
|
||||||
|
EarthRegistration earthReg;
|
||||||
|
|
||||||
IndoorMap() {;}
|
IndoorMap() {;}
|
||||||
|
|
||||||
|
/** no copy */
|
||||||
IndoorMap(const IndoorMap& o) = delete;
|
IndoorMap(const IndoorMap& o) = delete;
|
||||||
|
|
||||||
|
/** no copy assign */
|
||||||
void operator = (const IndoorMap& o) = delete;
|
void operator = (const IndoorMap& o) = delete;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Floorplan {
|
|||||||
|
|
||||||
using XMLAttr = tinyxml2::XMLAttribute;
|
using XMLAttr = tinyxml2::XMLAttribute;
|
||||||
using XMLElem = tinyxml2::XMLElement;
|
using XMLElem = tinyxml2::XMLElement;
|
||||||
|
using XMLNode = tinyxml2::XMLNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read an IndoorMaps from XML-data
|
* read an IndoorMaps from XML-data
|
||||||
@@ -31,7 +32,13 @@ namespace Floorplan {
|
|||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
const tinyxml2::XMLError res = doc.LoadFile(file.c_str());
|
const tinyxml2::XMLError res = doc.LoadFile(file.c_str());
|
||||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {throw Exception("error while loading XML " + file);}
|
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
|
throw Exception(
|
||||||
|
std::string() + "error while loading XML " + file + "\n" +
|
||||||
|
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||||
|
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||||
|
);
|
||||||
|
}
|
||||||
return parse(doc);
|
return parse(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +48,13 @@ namespace Floorplan {
|
|||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
const tinyxml2::XMLError res = doc.Parse(str.c_str(), str.length());
|
const tinyxml2::XMLError res = doc.Parse(str.c_str(), str.length());
|
||||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {throw Exception("error while parsing XML");}
|
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
|
throw Exception(
|
||||||
|
std::string() + "error while parsing XML\n" +
|
||||||
|
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||||
|
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||||
|
);
|
||||||
|
}
|
||||||
return parse(doc);
|
return parse(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +110,7 @@ namespace Floorplan {
|
|||||||
if (std::string("pois") == n->Name()) {floor->pois = parseFloorPOIs(n);}
|
if (std::string("pois") == n->Name()) {floor->pois = parseFloorPOIs(n);}
|
||||||
if (std::string("stairs") == n->Name()) {floor->stairs = parseFloorStairs(n);}
|
if (std::string("stairs") == n->Name()) {floor->stairs = parseFloorStairs(n);}
|
||||||
if (std::string("elevators") == n->Name()) {floor->elevators = parseFloorElevators(n);}
|
if (std::string("elevators") == n->Name()) {floor->elevators = parseFloorElevators(n);}
|
||||||
|
if (std::string("gtpoints") == n->Name()) {floor->gtpoints = parseFloorGroundTruthPoints(n);}
|
||||||
}
|
}
|
||||||
return floor;
|
return floor;
|
||||||
}
|
}
|
||||||
@@ -157,9 +171,7 @@ namespace Floorplan {
|
|||||||
|
|
||||||
// stair meta information?
|
// stair meta information?
|
||||||
const XMLElem* meta = el->FirstChildElement("meta");
|
const XMLElem* meta = el->FirstChildElement("meta");
|
||||||
if (meta) {
|
if (meta) {stair->setMeta(parseMetaElement(meta));}
|
||||||
stair->meta = parseMetaElement(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stair;
|
return stair;
|
||||||
|
|
||||||
@@ -185,6 +197,24 @@ namespace Floorplan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** parse the <gtpoints> tag */
|
||||||
|
static std::vector<GroundTruthPoint*> parseFloorGroundTruthPoints(const XMLElem* el) {
|
||||||
|
std::vector<GroundTruthPoint*> vec;
|
||||||
|
FOREACH_NODE(n, el) {
|
||||||
|
if (std::string("gtpoint") == n->Name()) { vec.push_back(parseFloorGroundTruthPoint(n)); }
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** parse a <gtpoint> tag */
|
||||||
|
static GroundTruthPoint* parseFloorGroundTruthPoint(const XMLElem* el) {
|
||||||
|
GroundTruthPoint* gtp = new GroundTruthPoint();
|
||||||
|
gtp->id = el->IntAttribute("id");
|
||||||
|
gtp->pos = parsePoint2(el);
|
||||||
|
return gtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** parse the <accesspoints> tag */
|
/** parse the <accesspoints> tag */
|
||||||
static std::vector<AccessPoint*> parseFloorAccessPoints(const XMLElem* el) {
|
static std::vector<AccessPoint*> parseFloorAccessPoints(const XMLElem* el) {
|
||||||
std::vector<AccessPoint*> vec;
|
std::vector<AccessPoint*> vec;
|
||||||
@@ -227,10 +257,16 @@ namespace Floorplan {
|
|||||||
/** parse one <meta> element */
|
/** parse one <meta> element */
|
||||||
static Meta* parseMetaElement(const XMLElem* n) {
|
static Meta* parseMetaElement(const XMLElem* n) {
|
||||||
Meta* elem = new Meta();
|
Meta* elem = new Meta();
|
||||||
const XMLAttr* attr = n->FirstAttribute();
|
// const XMLAttr* attr = n->FirstAttribute();
|
||||||
while (attr) {
|
// while (attr) {
|
||||||
elem->params[attr->Name()] = attr->Value();
|
// elem->setVal(attr->Name(), attr->Value());
|
||||||
attr = attr->Next();
|
// attr = attr->Next();
|
||||||
|
// }
|
||||||
|
const XMLElem* sub = n->FirstChildElement();
|
||||||
|
while(sub) {
|
||||||
|
// <entry key="123">abc</entry>
|
||||||
|
elem->add(sub->Attribute("key"), sub->FirstChild()->Value());
|
||||||
|
sub = sub->NextSiblingElement();
|
||||||
}
|
}
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
@@ -241,6 +277,11 @@ namespace Floorplan {
|
|||||||
ap->mac = n->Attribute("mac");
|
ap->mac = n->Attribute("mac");
|
||||||
ap->name = n->Attribute("name");
|
ap->name = n->Attribute("name");
|
||||||
ap->pos = parsePoint3(n);
|
ap->pos = parsePoint3(n);
|
||||||
|
ap->model.txp = n->FloatAttribute("mdl_txp");
|
||||||
|
ap->model.exp = n->FloatAttribute("mdl_exp");
|
||||||
|
ap->model.waf = n->FloatAttribute("mdl_waf");
|
||||||
|
const XMLElem* meta = n->FirstChildElement("meta");
|
||||||
|
if (meta) {ap->setMeta(parseMetaElement(meta));}
|
||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +300,12 @@ namespace Floorplan {
|
|||||||
Beacon* b = new Beacon();
|
Beacon* b = new Beacon();
|
||||||
b->mac = n->Attribute("mac");
|
b->mac = n->Attribute("mac");
|
||||||
b->name = n->Attribute("name");
|
b->name = n->Attribute("name");
|
||||||
|
b->major = n->Attribute("major") ? n->Attribute("major") : "";
|
||||||
|
b->minor = n->Attribute("minor") ? n->Attribute("minor") : "";
|
||||||
|
b->uuid = n->Attribute("uuid") ? n->Attribute("uuid") : "";
|
||||||
|
b->model.txp = n->FloatAttribute("mdl_txp");
|
||||||
|
b->model.exp = n->FloatAttribute("mdl_exp");
|
||||||
|
b->model.waf = n->FloatAttribute("mdl_waf");
|
||||||
b->pos = parsePoint3(n);
|
b->pos = parsePoint3(n);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ namespace Floorplan {
|
|||||||
XMLElem* stairs = doc.NewElement("stairs");
|
XMLElem* stairs = doc.NewElement("stairs");
|
||||||
for (const Stair* _stair : mf->stairs) {
|
for (const Stair* _stair : mf->stairs) {
|
||||||
XMLElem* elem = doc.NewElement("stair");
|
XMLElem* elem = doc.NewElement("stair");
|
||||||
addMetaElement(doc, elem, _stair->meta);
|
addMetaElement(doc, elem, _stair->getMeta());
|
||||||
if (dynamic_cast<const StairFreeform*>(_stair)) {
|
if (dynamic_cast<const StairFreeform*>(_stair)) {
|
||||||
const StairFreeform* stair = (StairFreeform*) _stair;
|
const StairFreeform* stair = (StairFreeform*) _stair;
|
||||||
elem->SetAttribute("type", 0); // TODO: other types?
|
elem->SetAttribute("type", 0); // TODO: other types?
|
||||||
@@ -145,6 +145,16 @@ namespace Floorplan {
|
|||||||
}
|
}
|
||||||
floor->InsertEndChild(pois);
|
floor->InsertEndChild(pois);
|
||||||
|
|
||||||
|
XMLElem* gtpoints = doc.NewElement("gtpoints");
|
||||||
|
for (const GroundTruthPoint* gtp : mf->gtpoints) {
|
||||||
|
XMLElem* elem = doc.NewElement("gtpoint");
|
||||||
|
elem->SetAttribute("id", gtp->id);
|
||||||
|
elem->SetAttribute("x", gtp->pos.x);
|
||||||
|
elem->SetAttribute("y", gtp->pos.y);
|
||||||
|
gtpoints->InsertEndChild(elem);
|
||||||
|
}
|
||||||
|
floor->InsertEndChild(gtpoints);
|
||||||
|
|
||||||
XMLElem* accesspoints = doc.NewElement("accesspoints");
|
XMLElem* accesspoints = doc.NewElement("accesspoints");
|
||||||
for (const AccessPoint* ap : mf->accesspoints) {
|
for (const AccessPoint* ap : mf->accesspoints) {
|
||||||
XMLElem* accesspoint = doc.NewElement("accesspoint");
|
XMLElem* accesspoint = doc.NewElement("accesspoint");
|
||||||
@@ -153,6 +163,10 @@ namespace Floorplan {
|
|||||||
accesspoint->SetAttribute("x", ap->pos.x);
|
accesspoint->SetAttribute("x", ap->pos.x);
|
||||||
accesspoint->SetAttribute("y", ap->pos.y);
|
accesspoint->SetAttribute("y", ap->pos.y);
|
||||||
accesspoint->SetAttribute("z", ap->pos.z);
|
accesspoint->SetAttribute("z", ap->pos.z);
|
||||||
|
accesspoint->SetAttribute("mdl_txp", ap->model.txp);
|
||||||
|
accesspoint->SetAttribute("mdl_exp", ap->model.exp);
|
||||||
|
accesspoint->SetAttribute("mdl_waf", ap->model.waf);
|
||||||
|
addMetaElement(doc, accesspoint, ap->getMeta());
|
||||||
accesspoints->InsertEndChild(accesspoint);
|
accesspoints->InsertEndChild(accesspoint);
|
||||||
}
|
}
|
||||||
floor->InsertEndChild(accesspoints);
|
floor->InsertEndChild(accesspoints);
|
||||||
@@ -162,9 +176,15 @@ namespace Floorplan {
|
|||||||
XMLElem* beacon = doc.NewElement("beacon");
|
XMLElem* beacon = doc.NewElement("beacon");
|
||||||
beacon->SetAttribute("name", b->name.c_str());
|
beacon->SetAttribute("name", b->name.c_str());
|
||||||
beacon->SetAttribute("mac", b->mac.c_str());
|
beacon->SetAttribute("mac", b->mac.c_str());
|
||||||
|
beacon->SetAttribute("major", b->major.c_str());
|
||||||
|
beacon->SetAttribute("minor", b->minor.c_str());
|
||||||
|
beacon->SetAttribute("uuid", b->uuid.c_str());
|
||||||
beacon->SetAttribute("x", b->pos.x);
|
beacon->SetAttribute("x", b->pos.x);
|
||||||
beacon->SetAttribute("y", b->pos.y);
|
beacon->SetAttribute("y", b->pos.y);
|
||||||
beacon->SetAttribute("z", b->pos.z);
|
beacon->SetAttribute("z", b->pos.z);
|
||||||
|
beacon->SetAttribute("mdl_txp", b->model.txp);
|
||||||
|
beacon->SetAttribute("mdl_exp", b->model.exp);
|
||||||
|
beacon->SetAttribute("mdl_waf", b->model.waf);
|
||||||
beacons->InsertEndChild(beacon);
|
beacons->InsertEndChild(beacon);
|
||||||
}
|
}
|
||||||
floor->InsertEndChild(beacons);
|
floor->InsertEndChild(beacons);
|
||||||
@@ -210,8 +230,15 @@ namespace Floorplan {
|
|||||||
static void addMetaElement(XMLDoc& doc, XMLElem* other, const Meta* meta) {
|
static void addMetaElement(XMLDoc& doc, XMLElem* other, const Meta* meta) {
|
||||||
if (meta == nullptr) {return;} // nothing to add
|
if (meta == nullptr) {return;} // nothing to add
|
||||||
XMLElem* elem = doc.NewElement("meta");
|
XMLElem* elem = doc.NewElement("meta");
|
||||||
for (auto it : meta->params) {
|
// for (auto it : meta->params) {
|
||||||
elem->Attribute(it.first.c_str(), it.second.c_str());
|
// elem->Attribute(it.first.c_str(), it.second.c_str());
|
||||||
|
// }
|
||||||
|
for (const Meta::KeyVal& kv : meta->params) {
|
||||||
|
// <entry key="123">abc</entry>
|
||||||
|
XMLElem* subElem = doc.NewElement("entry");
|
||||||
|
subElem->SetAttribute("key", kv.key.c_str());
|
||||||
|
subElem->SetText(kv.val.c_str());
|
||||||
|
elem->InsertEndChild(subElem);
|
||||||
}
|
}
|
||||||
other->InsertEndChild(elem);
|
other->InsertEndChild(elem);
|
||||||
}
|
}
|
||||||
|
|||||||
75
geo/EarthMapping.h
Normal file
75
geo/EarthMapping.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef EARTHMAPPING_H
|
||||||
|
#define EARTHMAPPING_H
|
||||||
|
|
||||||
|
#include "Point3.h"
|
||||||
|
#include "EarthPos.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mapping between positions on earth and positions within the floorplan
|
||||||
|
*/
|
||||||
|
class EarthMapping {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** one 3D position within the floorplan */
|
||||||
|
Point3 center_map_m;
|
||||||
|
|
||||||
|
/** corresponding 3D position on earth */
|
||||||
|
EarthPos center_earth;
|
||||||
|
|
||||||
|
/** rotation [in degrees] to ensure that the map's y-up-axis points towards the north */
|
||||||
|
float rotation_deg;
|
||||||
|
|
||||||
|
double m_per_deg_lat;
|
||||||
|
double m_per_deg_lon;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void build() {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** convert from map-coordinates to earth-coordinates */
|
||||||
|
EarthPos mapToWorld(const Point3 mapPos_m) const {
|
||||||
|
|
||||||
|
Point3 pos = mapPos_m;
|
||||||
|
|
||||||
|
// move to (0,0,0)
|
||||||
|
pos -= center_map_m;
|
||||||
|
|
||||||
|
// undo the rotation
|
||||||
|
const Point2 xy = pos.xy().rotated(-rotation_deg / 180.0 * (float) M_PI);
|
||||||
|
|
||||||
|
// convert this "delta to (0,0,0)" to lon/lat and move it to the lon/lat-center
|
||||||
|
const double lat = cenLat + (xy.y / m_per_deg_lat);
|
||||||
|
const double lon = cenLon + (xy.x / m_per_deg_lon);
|
||||||
|
const float height = pos.z;
|
||||||
|
|
||||||
|
// done
|
||||||
|
return EarthPos(lat, lon, height);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** convert from earth-coordinates to map-coordinates */
|
||||||
|
Point3 worldToMap(const EarthPos earthPos) const {
|
||||||
|
|
||||||
|
const double y_m = +(lat-cenLat) * m_per_deg_lat;
|
||||||
|
const double x_m = +(lon-cenLon) * m_per_deg_lon;
|
||||||
|
|
||||||
|
// rotate (our map is axis aligned)
|
||||||
|
Point2 pos(x_m, y_m);
|
||||||
|
pos = pos.rotated(rotDeg / 180 * M_PI);
|
||||||
|
|
||||||
|
// apply movement
|
||||||
|
pos += mapCenter_m;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EARTHMAPPING_H
|
||||||
21
geo/EarthPos.h
Normal file
21
geo/EarthPos.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef EARTHPOS_H
|
||||||
|
#define EARTHPOS_H
|
||||||
|
|
||||||
|
/** describes the location on the earth's surface */
|
||||||
|
struct EarthPos {
|
||||||
|
|
||||||
|
double lat;
|
||||||
|
|
||||||
|
double lon;
|
||||||
|
|
||||||
|
/** height above sea level */
|
||||||
|
float height;
|
||||||
|
|
||||||
|
/** ctor with values */
|
||||||
|
EarthPos(const double lat, const double lon, const float height) : lon(lon), lat(lat), height(height) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EARTHPOS_H
|
||||||
@@ -17,6 +17,8 @@ struct Point2 {
|
|||||||
/** ctor */
|
/** ctor */
|
||||||
Point2(const float x, const float y) : x(x), y(y) {;}
|
Point2(const float x, const float y) : x(x), y(y) {;}
|
||||||
|
|
||||||
|
Point2 operator - () const {return Point2(-x, -y);}
|
||||||
|
|
||||||
|
|
||||||
Point2 operator + (const Point2& o) const {return Point2(x+o.x, y+o.y);}
|
Point2 operator + (const Point2& o) const {return Point2(x+o.x, y+o.y);}
|
||||||
|
|
||||||
@@ -60,4 +62,9 @@ struct Point2 {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void swap(Point2& p1, Point2& p2) {
|
||||||
|
std::swap(p1.x, p2.x);
|
||||||
|
std::swap(p1.y, p2.y);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // POINT2_H
|
#endif // POINT2_H
|
||||||
|
|||||||
91
grid/walk/v2/modules/WalkModuleActivityControlPercent.h
Normal file
91
grid/walk/v2/modules/WalkModuleActivityControlPercent.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#ifndef WALKMODULEACTIVITYCONTROLPERCENT_H
|
||||||
|
#define WALKMODULEACTIVITYCONTROLPERCENT_H
|
||||||
|
|
||||||
|
#include "WalkModule.h"
|
||||||
|
#include "WalkStateHeading.h"
|
||||||
|
|
||||||
|
#include "../../../../geo/Heading.h"
|
||||||
|
#include "../../../../math/Distributions.h"
|
||||||
|
#include "../../../../sensors/pressure/ActivityButterPressurePercent.h"
|
||||||
|
#include "../../../../grid/GridNode.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** favor z-transitions */
|
||||||
|
template <typename Node, typename WalkState, typename Control> class WalkModuleActivityControlPercent : public WalkModule<Node, WalkState> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Control* ctrl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
WalkModuleActivityControlPercent(Control* ctrl) : ctrl(ctrl) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updateBefore(WalkState& state, const Node& startNode) override {
|
||||||
|
(void) state;
|
||||||
|
(void) startNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updateAfter(WalkState& state, const Node& startNode, const Node& endNode) override {
|
||||||
|
(void) state;
|
||||||
|
(void) startNode;
|
||||||
|
(void) endNode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void step(WalkState& state, const Node& curNode, const Node& nextNode) override {
|
||||||
|
(void) state;
|
||||||
|
(void) curNode;
|
||||||
|
(void) nextNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getProbability(const WalkState& state, const Node& startNode, const Node& curNode, const Node& potentialNode) const override {
|
||||||
|
|
||||||
|
(void) state;
|
||||||
|
(void) startNode;
|
||||||
|
|
||||||
|
|
||||||
|
const int deltaZ_cm = curNode.z_cm - potentialNode.z_cm;
|
||||||
|
|
||||||
|
//floor and doors
|
||||||
|
if(potentialNode.getType() == 0 || potentialNode.getType() == 3){
|
||||||
|
return ctrl->activityPercent.stay;
|
||||||
|
}
|
||||||
|
|
||||||
|
//stairs
|
||||||
|
if(potentialNode.getType() == 1){
|
||||||
|
|
||||||
|
// if(ctrl->barometer.actProbs.stay > ctrl->barometer.actProbs.stairsDown + ctrl->barometer.actProbs.stairsUp){
|
||||||
|
// return ctrl->barometer.actProbs.stay * 0.75;//(ctrl->barometer.actProbs.stairsDown > ctrl->barometer.actProbs.stairsUp ? ctrl->barometer.actProbs.stairsDown : ctrl->barometer.actProbs.stairsUp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (deltaZ_cm > 0){return ctrl->activityPercent.stairsDown;}
|
||||||
|
if (deltaZ_cm < 0){return ctrl->activityPercent.stairsUp;}
|
||||||
|
return (ctrl->activityPercent.stairsDown > ctrl->activityPercent.stairsUp ? ctrl->activityPercent.stairsDown : ctrl->activityPercent.stairsUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//elevators
|
||||||
|
if(potentialNode.getType() == 2){
|
||||||
|
|
||||||
|
// //we need to do this, that particles are able to walk into an elevator even if the prob for that is low,
|
||||||
|
// //that happens often since the activity has some delay.
|
||||||
|
// if(ctrl->barometer.actProbs.stay > ctrl->barometer.actProbs.elevatorDown + ctrl->barometer.actProbs.elevatorUp){
|
||||||
|
// return ctrl->barometer.actProbs.stay * 0.75;//(ctrl->barometer.actProbs.stairsDown > ctrl->barometer.actProbs.stairsUp ? ctrl->barometer.actProbs.stairsDown : ctrl->barometer.actProbs.stairsUp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (deltaZ_cm > 0){return ctrl->activityPercent.elevatorDown;}
|
||||||
|
if (deltaZ_cm < 0){return ctrl->activityPercent.elevatorUp;}
|
||||||
|
|
||||||
|
//for walking out of the elevator
|
||||||
|
return (ctrl->activityPercent.elevatorDown > ctrl->activityPercent.elevatorUp ? ctrl->activityPercent.elevatorDown : ctrl->activityPercent.elevatorUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Node has unknown Type" << std::endl;
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WALKMODULEACTIVITYCONTROLPERCENT_H
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#ifndef WALKMODULEBUTTERACTIVITY_H
|
|
||||||
#define WALKMODULEBUTTERACTIVITY_H
|
|
||||||
|
|
||||||
#include "WalkModule.h"
|
|
||||||
|
|
||||||
#include "../../../../geo/Heading.h"
|
|
||||||
#include "../../../../math/Distributions.h"
|
|
||||||
#include "../../../../sensors/pressure/ActivityButterPressure.h"
|
|
||||||
|
|
||||||
DEPREACTED
|
|
||||||
SEE WalkModuleActivityControl
|
|
||||||
|
|
||||||
struct WalkStateBarometerActivity {
|
|
||||||
|
|
||||||
/** innser-struct to prevent name-clashes */
|
|
||||||
struct Barometer {
|
|
||||||
|
|
||||||
/** activity currently detected from the baromter */
|
|
||||||
ActivityButterPressure::Activity activity;
|
|
||||||
|
|
||||||
Barometer() : activity(ActivityButterPressure::Activity::STAY) {;}
|
|
||||||
|
|
||||||
} barometer;
|
|
||||||
|
|
||||||
/** ctor */
|
|
||||||
WalkStateBarometerActivity() : barometer() {;}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/** favor z-transitions */
|
|
||||||
template <typename Node, typename WalkState> class WalkModuleButterActivity : public WalkModule<Node, WalkState> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/** ctor */
|
|
||||||
WalkModuleButterActivity() {
|
|
||||||
|
|
||||||
// ensure templates WalkState inherits from 'WalkStateBarometerActivity'
|
|
||||||
StaticAssert::AinheritsB<WalkState, WalkStateBarometerActivity>();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void updateBefore(WalkState& state, const Node& startNode) override {
|
|
||||||
(void) state;
|
|
||||||
(void) startNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void updateAfter(WalkState& state, const Node& startNode, const Node& endNode) override {
|
|
||||||
(void) state;
|
|
||||||
(void) startNode;
|
|
||||||
(void) endNode;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void step(WalkState& state, const Node& curNode, const Node& nextNode) override {
|
|
||||||
(void) state;
|
|
||||||
(void) curNode;
|
|
||||||
(void) nextNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getProbability(const WalkState& state, const Node& startNode, const Node& curNode, const Node& potentialNode) const override {
|
|
||||||
|
|
||||||
(void) state;
|
|
||||||
(void) startNode;
|
|
||||||
|
|
||||||
const int deltaZ_cm = curNode.z_cm - potentialNode.z_cm;
|
|
||||||
|
|
||||||
if(state.barometer.activity == ActivityButterPressure::Activity::DOWN){
|
|
||||||
if (deltaZ_cm < 0) {return 0;}
|
|
||||||
if (deltaZ_cm == 0) {return 0.1;}
|
|
||||||
return 0.9;
|
|
||||||
} else if (state.barometer.activity == ActivityButterPressure::Activity::UP){
|
|
||||||
if (deltaZ_cm > 0) {return 0;}
|
|
||||||
if (deltaZ_cm == 0) {return 0.1;}
|
|
||||||
return 0.9;
|
|
||||||
} else {
|
|
||||||
if (deltaZ_cm == 0) {return 0.9;}
|
|
||||||
return 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // WALKMODULEBUTTERACTIVITY_H
|
|
||||||
123
grid/walk/v2/modules/WalkModuleHeadingVonMises.h
Normal file
123
grid/walk/v2/modules/WalkModuleHeadingVonMises.h
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#ifndef WALKMODULEHEADINGVONMISES_H
|
||||||
|
#define WALKMODULEHEADINGVONMISES_H
|
||||||
|
|
||||||
|
#include "WalkModule.h"
|
||||||
|
#include "WalkStateHeading.h"
|
||||||
|
|
||||||
|
#include "../../../../geo/Heading.h"
|
||||||
|
#include "../../../../math/Distributions.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** keep the state's heading */
|
||||||
|
template <typename Node, typename WalkState, typename Control> class WalkModuleHeadingVonMises : public WalkModule<Node, WalkState> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** van-Mises distribution */
|
||||||
|
Distribution::VonMises<float> dist;
|
||||||
|
|
||||||
|
/** random noise */
|
||||||
|
Distribution::Normal<float> distNoise;
|
||||||
|
|
||||||
|
const Control* ctrl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor 3.0 should be OK! */
|
||||||
|
WalkModuleHeadingVonMises(const Control* ctrl, const float sensorNoiseDegreesSigma) :
|
||||||
|
dist(Distribution::VonMises<float>(0.0f, 2.0f)),
|
||||||
|
distNoise(0, Angle::degToRad(sensorNoiseDegreesSigma)),
|
||||||
|
ctrl(ctrl) {
|
||||||
|
|
||||||
|
// ensure the template WalkState inherits from 'WalkStateHeading'!
|
||||||
|
StaticAssert::AinheritsB<WalkState, WalkStateHeading>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void updateBefore(WalkState& state, const Node& startNode) override {
|
||||||
|
|
||||||
|
// NOTE: ctrl->turnAngle is cumulative SINCE the last transition!
|
||||||
|
// reset this one after every transition!
|
||||||
|
Assert::isBetween(ctrl->turnSinceLastTransition_rad, -3.0f, +3.0f, "the given turn angle is too high to make sense.. did you forget to set ctrl->turnAngle = 0 after each transition?");
|
||||||
|
|
||||||
|
// sensor noise
|
||||||
|
float var = distNoise.draw();
|
||||||
|
|
||||||
|
// stair? -> increase variance
|
||||||
|
if (startNode.getType() == GridNode::TYPE_STAIR) {var *= 3;}
|
||||||
|
|
||||||
|
// adjust the state's heading using the control-data
|
||||||
|
state.heading.direction += ctrl->turnSinceLastTransition_rad + var;
|
||||||
|
|
||||||
|
//set kappa of mises
|
||||||
|
float kappa = 5 / std::exp(2 * std::abs(ctrl->motionDeltaAngle_rad));
|
||||||
|
dist.setKappa(kappa);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updateAfter(WalkState& state, const Node& startNode, const Node& endNode) override {
|
||||||
|
(void) state;
|
||||||
|
(void) startNode;
|
||||||
|
(void) endNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void step(WalkState& state, const Node& curNode, const Node& nextNode) override {
|
||||||
|
|
||||||
|
(void) state;
|
||||||
|
|
||||||
|
// ignore for stairs?
|
||||||
|
//if (nextNode.getType() == GridNode::TYPE_STAIR) {return;}
|
||||||
|
|
||||||
|
// for elevator edges [same (x,y) but different z] do not adjust anything
|
||||||
|
if (nextNode.getType() == GridNode::TYPE_ELEVATOR) {return;}
|
||||||
|
if (curNode.getType() == GridNode::TYPE_ELEVATOR) {return;}
|
||||||
|
//if (curNode.x_cm == nextNode.x_cm && curNode.y_cm == nextNode.y_cm && curNode.z_cm != nextNode.z_cm) {return;}
|
||||||
|
|
||||||
|
// get the heading denoted by the way from curNode to nextNode
|
||||||
|
const Heading head(curNode.x_cm, curNode.y_cm, nextNode.x_cm, nextNode.y_cm);
|
||||||
|
|
||||||
|
// get the heading requested by the state
|
||||||
|
const Heading stateHead = state.heading.direction;
|
||||||
|
|
||||||
|
// get the error (signed difference) between both
|
||||||
|
const float angularDiff = stateHead.getSignedDiff(head);
|
||||||
|
|
||||||
|
// adjust the error.
|
||||||
|
// note: the error may get > +/- 2PI but this is not an issue!
|
||||||
|
// when the error is added to the current heading within getProbability(),
|
||||||
|
// it is ensured their sum is within [0:2pi]
|
||||||
|
state.heading.error += angularDiff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
double getProbability(const WalkState& state, const Node& startNode, const Node& curNode, const Node& potentialNode) const override {
|
||||||
|
|
||||||
|
(void) startNode;
|
||||||
|
|
||||||
|
// ignore for stairs?
|
||||||
|
//if (potentialNode.getType() == GridNode::TYPE_STAIR) {return 1.0;}
|
||||||
|
|
||||||
|
// for elevator edges [same (x,y) but different z] just return 1
|
||||||
|
if (potentialNode.getType() == GridNode::TYPE_ELEVATOR) {return 1.0;}
|
||||||
|
if (curNode.getType() == GridNode::TYPE_ELEVATOR) {return 1.0;}
|
||||||
|
//if (curNode.x_cm == potentialNode.x_cm && curNode.y_cm == potentialNode.y_cm && curNode.z_cm != potentialNode.z_cm) {return 1.0;}
|
||||||
|
|
||||||
|
// get the heading between curNode and potentialNode
|
||||||
|
const Heading head(curNode.x_cm, curNode.y_cm, potentialNode.x_cm, potentialNode.y_cm);
|
||||||
|
|
||||||
|
// compare the heading against the state's heading - the last error
|
||||||
|
const Heading stateHead = state.heading.direction + state.heading.error;
|
||||||
|
|
||||||
|
// get the difference
|
||||||
|
const float angularDiff = head.getDiffHalfRAD(stateHead);
|
||||||
|
|
||||||
|
// determine probability
|
||||||
|
return dist.getProbability(angularDiff);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WALKMODULEHEADING_H
|
||||||
6
main.cpp
6
main.cpp
@@ -25,11 +25,11 @@ int main(int argc, char** argv) {
|
|||||||
//::testing::GTEST_FLAG(filter) = "*Grid.*";
|
//::testing::GTEST_FLAG(filter) = "*Grid.*";
|
||||||
//::testing::GTEST_FLAG(filter) = "*Dijkstra.*";
|
//::testing::GTEST_FLAG(filter) = "*Dijkstra.*";
|
||||||
|
|
||||||
//::testing::GTEST_FLAG(filter) = "*LogDistanceCeilingModel*";
|
//::testing::GTEST_FLAG(filter) = "*LogDistanceCeilingModelBeacon*";
|
||||||
::testing::GTEST_FLAG(filter) = "*WiFiOptimizer*";
|
//::testing::GTEST_FLAG(filter) = "*WiFiOptimizer*";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::testing::GTEST_FLAG(filter) = "*KullbackLeibler*";
|
||||||
//::testing::GTEST_FLAG(filter) = "*Barometer*";
|
//::testing::GTEST_FLAG(filter) = "*Barometer*";
|
||||||
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
|
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,6 @@
|
|||||||
#include "distribution/VonMises.h"
|
#include "distribution/VonMises.h"
|
||||||
#include "distribution/Region.h"
|
#include "distribution/Region.h"
|
||||||
#include "distribution/Triangle.h"
|
#include "distribution/Triangle.h"
|
||||||
|
#include "distribution/NormalN.h"
|
||||||
|
|
||||||
#endif // DISTRIBUTIONS_H
|
#endif // DISTRIBUTIONS_H
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ namespace Distribution {
|
|||||||
gen.seed(seed);
|
gen.seed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** get the mean value */
|
||||||
|
const T getMu() {
|
||||||
|
return this->mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the standard deviation */
|
||||||
|
const T getSigma() {
|
||||||
|
return this->sigma;
|
||||||
|
}
|
||||||
|
|
||||||
/** get the probability for the given value */
|
/** get the probability for the given value */
|
||||||
static T getProbability(const T mu, const T sigma, const T val) {
|
static T getProbability(const T mu, const T sigma, const T val) {
|
||||||
|
|||||||
50
math/distribution/NormalN.h
Normal file
50
math/distribution/NormalN.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef NORMALN_H
|
||||||
|
#define NORMALN_H
|
||||||
|
|
||||||
|
#include <eigen3/Eigen/Dense>
|
||||||
|
|
||||||
|
namespace Distribution {
|
||||||
|
|
||||||
|
class NormalDistributionN {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const Eigen::VectorXd mu;
|
||||||
|
const Eigen::MatrixXd sigma;
|
||||||
|
|
||||||
|
const double _a;
|
||||||
|
const Eigen::MatrixXd _sigmaInv;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
NormalDistributionN(const Eigen::VectorXd mu, const Eigen::MatrixXd sigma) :
|
||||||
|
mu(mu), sigma(sigma), _a( 1.0 / std::sqrt( (sigma * 2.0 * M_PI).determinant() ) ), _sigmaInv(sigma.inverse()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** get probability for the given value */
|
||||||
|
double getProbability(const Eigen::VectorXd val) const {
|
||||||
|
const double b = ((val-mu).transpose() * _sigmaInv * (val-mu));
|
||||||
|
return _a * std::exp(-b/2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the mean vector */
|
||||||
|
const Eigen::VectorXd getMu(){
|
||||||
|
return this->mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get covariance matrix */
|
||||||
|
const Eigen::MatrixXd getSigma(){
|
||||||
|
return this->sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::MatrixXd getSigmaInv(){
|
||||||
|
return this->_sigmaInv;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // NORMALN_H
|
||||||
@@ -19,7 +19,7 @@ namespace Distribution {
|
|||||||
const T mu;
|
const T mu;
|
||||||
|
|
||||||
/** like 1.0/variance of the distribution */
|
/** like 1.0/variance of the distribution */
|
||||||
const T kappa;
|
T kappa;
|
||||||
|
|
||||||
/** pre-calcuated look-up-table */
|
/** pre-calcuated look-up-table */
|
||||||
std::vector<T> lut;
|
std::vector<T> lut;
|
||||||
@@ -30,7 +30,7 @@ namespace Distribution {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** ctor */
|
/** ctor */
|
||||||
VonMises(const T mu, const T kappa) : mu(mu), kappa(kappa) {
|
VonMises(const T mu, T kappa) : mu(mu), kappa(kappa) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +49,10 @@ namespace Distribution {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setKappa(T _kappa){
|
||||||
|
kappa = _kappa;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
90
math/divergence/KullbackLeibler.h
Normal file
90
math/divergence/KullbackLeibler.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#ifndef KULLBACKLEIBLER_H
|
||||||
|
#define KULLBACKLEIBLER_H
|
||||||
|
|
||||||
|
#include "../distribution/Normal.h"
|
||||||
|
#include "../distribution/NormalN.h"
|
||||||
|
|
||||||
|
#include "../../Assertions.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Divergence {
|
||||||
|
|
||||||
|
template <typename Scalar> class KullbackLeibler {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Calculate the Kullback Leibler Distance for a univariate Gaussian distribution
|
||||||
|
* Info: https://tgmstat.wordpress.com/2013/07/10/kullback-leibler-divergence/
|
||||||
|
*/
|
||||||
|
static inline Scalar getUnivariateGauss(Distribution::Normal<Scalar> norm1, Distribution::Normal<Scalar> norm2){
|
||||||
|
|
||||||
|
auto sigma1Quad = norm1.getSigma() * norm1.getSigma();
|
||||||
|
auto sigma2Quad = norm2.getSigma() * norm2.getSigma();
|
||||||
|
auto mu12Quad = (norm1.getMu() - norm2.getMu()) * (norm1.getMu() - norm2.getMu());
|
||||||
|
auto log1 = std::log(norm1.getSigma());
|
||||||
|
auto log2 = std::log(norm2.getSigma());
|
||||||
|
|
||||||
|
// kl = log(sigma_2 / sigma_1) + ((sigma_1^2 + (mu_1 - mu_2)^2) / 2 * sigma_2^2) - 0.5
|
||||||
|
double klb = (log2 - log1) + ((sigma1Quad + mu12Quad)/(2.0 * sigma2Quad)) - 0.5;
|
||||||
|
|
||||||
|
//klb is always greater 0
|
||||||
|
if(klb < 0.0){
|
||||||
|
Assert::doThrow("The Kullback Leibler Distance is < 0! Thats not possible");
|
||||||
|
}
|
||||||
|
return klb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate the Kullback Leibler Distance for a univariate Gaussian distribution symmetric*/
|
||||||
|
static inline Scalar getUnivariateGaussSymmetric(Distribution::Normal<Scalar> norm1, Distribution::Normal<Scalar> norm2){
|
||||||
|
return getUnivariateGauss(norm1, norm2) + getUnivariateGauss(norm2, norm1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate the Kullback Leibler Distance for a multivariate Gaussian distribution */
|
||||||
|
static inline Scalar getMultivariateGauss(Distribution::NormalDistributionN norm1, Distribution::NormalDistributionN norm2){
|
||||||
|
|
||||||
|
//both gaussian have the same dimension.
|
||||||
|
Assert::equal(norm1.getMu().rows(), norm2.getMu().rows(), "mean vectors do not have the same dimension");
|
||||||
|
Assert::equal(norm1.getSigma().rows(), norm2.getSigma().rows(), "cov matrices do not have the same dimension");
|
||||||
|
Assert::equal(norm1.getSigma().cols(), norm2.getSigma().cols(), "cov matrices do not have the same dimension");
|
||||||
|
|
||||||
|
//log
|
||||||
|
auto det1 = norm1.getSigma().determinant();
|
||||||
|
auto det2 = norm2.getSigma().determinant();
|
||||||
|
auto log1 = std::log(det1);
|
||||||
|
auto log2 = std::log(det2);
|
||||||
|
|
||||||
|
//trace
|
||||||
|
Eigen::MatrixXd toTrace(norm1.getSigma().rows(),norm1.getSigma().cols());
|
||||||
|
toTrace = norm2.getSigmaInv() * norm1.getSigma();
|
||||||
|
auto trace = toTrace.trace();
|
||||||
|
|
||||||
|
//transpose
|
||||||
|
Eigen::VectorXd toTranspose(norm1.getMu().rows());
|
||||||
|
toTranspose = norm2.getMu() - norm1.getMu();
|
||||||
|
auto transpose = toTranspose.transpose();
|
||||||
|
|
||||||
|
//rawdensity
|
||||||
|
auto rawDensity = transpose * norm2.getSigmaInv() * toTranspose;
|
||||||
|
auto dimension = norm1.getMu().rows();
|
||||||
|
|
||||||
|
//0.5 * ((log(det(cov_2)/det(cov_1)) + tr(cov_2^-1 cov_1) + (mu_2 - mu_1)^T * cov_2^-1 * (mu_2 - mu_1) - dimension)
|
||||||
|
double klb = 0.5 * ((log2 - log1) + trace + rawDensity - dimension);
|
||||||
|
|
||||||
|
//klb is always greater 0
|
||||||
|
if(klb < 0.0){
|
||||||
|
Assert::doThrow("The Kullback Leibler Distance is < 0! Thats not possible");
|
||||||
|
}
|
||||||
|
return klb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate the Kullback Leibler Distance for a multivariate Gaussian distribution symmetric*/
|
||||||
|
static inline Scalar getMultivariateGaussSymmetric(Distribution::NormalDistributionN norm1, Distribution::NormalDistributionN norm2){
|
||||||
|
return getMultivariateGauss(norm1, norm2) + getMultivariateGauss(norm2, norm1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KULLBACKLEIBLER_H
|
||||||
@@ -47,8 +47,7 @@ public:
|
|||||||
MACAddress(const std::string& str) {
|
MACAddress(const std::string& str) {
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (str.size() != 17) {throw Exception("invalid hex string length. must be 17");}
|
if (str.size() == 17 ){
|
||||||
|
|
||||||
mac = 0; // all zeros
|
mac = 0; // all zeros
|
||||||
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
|
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
|
||||||
fields.h4 = hexWordToInt(str[ 3], str[ 4]);
|
fields.h4 = hexWordToInt(str[ 3], str[ 4]);
|
||||||
@@ -56,6 +55,19 @@ public:
|
|||||||
fields.h2 = hexWordToInt(str[ 9], str[10]);
|
fields.h2 = hexWordToInt(str[ 9], str[10]);
|
||||||
fields.h1 = hexWordToInt(str[12], str[13]);
|
fields.h1 = hexWordToInt(str[12], str[13]);
|
||||||
fields.h0 = hexWordToInt(str[15], str[16]);
|
fields.h0 = hexWordToInt(str[15], str[16]);
|
||||||
|
}
|
||||||
|
else if (str.size() == 12){
|
||||||
|
mac = 0; // all zeros
|
||||||
|
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.h0 = hexWordToInt(str[10], str[11]);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw Exception("invalid hex string length. must be 17 or 12 (without :)");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
sensors/beacon/Beacon.h
Normal file
59
sensors/beacon/Beacon.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef BEACON_H
|
||||||
|
#define BEACON_H
|
||||||
|
|
||||||
|
#include "../MACAddress.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* represents a single beacon
|
||||||
|
* a beacon is represented by its MAC-Address and
|
||||||
|
* may provide a sending power TXP
|
||||||
|
*/
|
||||||
|
class Beacon {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** the AP's MAC-Address */
|
||||||
|
MACAddress mac;
|
||||||
|
|
||||||
|
/** OPTIONAL the beacons sending power */
|
||||||
|
float txp;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** empty ctor */
|
||||||
|
Beacon() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ctor with MAC and TXP */
|
||||||
|
Beacon(const MACAddress& mac, const float& txp) : mac(mac), txp(txp) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ctor with MAC and TXP */
|
||||||
|
Beacon(const std::string& mac, const float& txp) : mac(mac), txp(txp) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ctor with MAC and without TXP */
|
||||||
|
Beacon(const MACAddress& mac) : mac(mac), txp() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ctor with MAC and without TXP */
|
||||||
|
Beacon(const std::string& mac) : mac(mac), txp() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** get the AP's MAC address */
|
||||||
|
inline const MACAddress& getMAC() const {return mac;}
|
||||||
|
|
||||||
|
/** OPTIONAL: get the AP's ssid (if any) */
|
||||||
|
inline const float& getTXP() const {return txp;}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BEACON_H
|
||||||
44
sensors/beacon/BeaconMeasurement.h
Normal file
44
sensors/beacon/BeaconMeasurement.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#ifndef BEACONMEASUREMENT_H
|
||||||
|
#define BEACONMEASUREMENT_H
|
||||||
|
|
||||||
|
#include "../MACAddress.h"
|
||||||
|
#include "../../data/Timestamp.h"
|
||||||
|
#include "Beacon.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
/** one observed AP and its signal strength */
|
||||||
|
class BeaconMeasurement {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** the timestamp this beacon was discovered at */
|
||||||
|
Timestamp ts;
|
||||||
|
|
||||||
|
/** the beacon's mac address */
|
||||||
|
Beacon beacon;
|
||||||
|
|
||||||
|
/** signal strength */
|
||||||
|
float rssi;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
BeaconMeasurement(const Timestamp ts, const Beacon& beacon, const float rssi) : ts(ts), beacon(beacon), rssi(rssi) {;}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** get the beacon */
|
||||||
|
const Beacon& getBeacon() const {return beacon;}
|
||||||
|
|
||||||
|
/** get the measurements timestamp */
|
||||||
|
const Timestamp& getTimestamp() const {return ts;}
|
||||||
|
|
||||||
|
/** get the rssi */
|
||||||
|
float getRSSI() const {return rssi;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BEACONMEASUREMENT_H
|
||||||
26
sensors/beacon/BeaconMeasurements.h
Normal file
26
sensors/beacon/BeaconMeasurements.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef BEACONMEASUREMENTS_H
|
||||||
|
#define BEACONMEASUREMENTS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "BeaconMeasurement.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* group of several beacon measurements
|
||||||
|
*/
|
||||||
|
struct BeaconMeasurements {
|
||||||
|
|
||||||
|
std::vector<BeaconMeasurement> entries;
|
||||||
|
|
||||||
|
/** remove entries older then 3000 ms*/
|
||||||
|
void removeOld(const Timestamp latestTS) {
|
||||||
|
auto lambda = [latestTS] (const BeaconMeasurement& e) {
|
||||||
|
Timestamp age = latestTS - e.getTimestamp();
|
||||||
|
return age > Timestamp::fromMS(1000*3);
|
||||||
|
};
|
||||||
|
entries.erase(std::remove_if(entries.begin(), entries.end(), lambda), entries.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BEACONMEASUREMENTS_H
|
||||||
15
sensors/beacon/BeaconProbability.h
Normal file
15
sensors/beacon/BeaconProbability.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef BEACONPROBABILITY_H
|
||||||
|
#define BEACONPROBABILITY_H
|
||||||
|
|
||||||
|
#include "BeaconMeasurements.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base class for all Beacon probability calculators.
|
||||||
|
* such a calculator determines the probabilty for a location (e.g. x,y,z)
|
||||||
|
* given BeaconMeasurements
|
||||||
|
*/
|
||||||
|
class BeaconProbability {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BEACONPROBABILITY_H
|
||||||
94
sensors/beacon/BeaconProbabilityFree.h
Normal file
94
sensors/beacon/BeaconProbabilityFree.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#ifndef BEACONPROBABILITYFREE_H
|
||||||
|
#define BEACONPROBABILITYFREE_H
|
||||||
|
|
||||||
|
#include "BeaconProbability.h"
|
||||||
|
#include "BeaconMeasurements.h"
|
||||||
|
#include "model/BeaconModel.h"
|
||||||
|
#include "../../math/Distributions.h"
|
||||||
|
#include "../../data/Timestamp.h"
|
||||||
|
|
||||||
|
#include "../../floorplan/v2/Floorplan.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare BeaconMeasurements within predictions of a given model.
|
||||||
|
* predictions are just based on the distance to the observed beacon.
|
||||||
|
*/
|
||||||
|
class BeaconObserverFree : public BeaconProbability {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const float sigma = 8.0f;
|
||||||
|
const float sigmaPerSecond = 3.0f;
|
||||||
|
|
||||||
|
/** the RSSI prediction model */
|
||||||
|
BeaconModel& model;
|
||||||
|
|
||||||
|
/** the map's floorplan */
|
||||||
|
Floorplan::IndoorMap* map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
BeaconObserverFree(const float sigma, BeaconModel& model) : sigma(sigma), model(model) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** provides the probability for a specific point in space */
|
||||||
|
double getProbability(const Point3& pos_m, const Timestamp curTime, const BeaconMeasurements& obs) const {
|
||||||
|
|
||||||
|
double prob = 1.0;
|
||||||
|
int numMatchingBeacons = 0;
|
||||||
|
|
||||||
|
// process each measured AP
|
||||||
|
for (const BeaconMeasurement& entry : obs.entries) {
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
Assert::isFalse(entry.getTimestamp().isZero(), "wifi measurement without timestamp. coding error?");
|
||||||
|
|
||||||
|
// updating the beacons sended txp if available
|
||||||
|
if(entry.getBeacon().getTXP() != 0.0f){
|
||||||
|
//TODO: check if this works
|
||||||
|
model.updateBeacon(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the model's RSSI (if possible!)
|
||||||
|
const float modelRSSI = model.getRSSI(entry.getBeacon().getMAC(), pos_m);
|
||||||
|
|
||||||
|
// NaN? -> AP not known to the model -> skip
|
||||||
|
if (modelRSSI != modelRSSI) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// the scan's RSSI
|
||||||
|
const float scanRSSI = entry.getRSSI();
|
||||||
|
|
||||||
|
// the measurement's age
|
||||||
|
const Timestamp age = curTime - entry.getTimestamp();
|
||||||
|
|
||||||
|
Assert::isTrue(age.ms() >= 0, "found a negative wifi measurement age. this does not make sense");
|
||||||
|
Assert::isTrue(age.ms() <= 40000, "found a 40 second old wifi measurement. maybe there is a coding error?");
|
||||||
|
|
||||||
|
// sigma grows with measurement age
|
||||||
|
const float sigma = this->sigma + this->sigmaPerSecond * age.sec();
|
||||||
|
|
||||||
|
// update probability
|
||||||
|
prob *= Distribution::Normal<double>::getProbability(modelRSSI, sigma, scanRSSI);
|
||||||
|
//prob *= Distribution::Region<double>::getProbability(modelRSSI, sigma, scanRSSI);
|
||||||
|
|
||||||
|
++numMatchingBeacons;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
//Assert::isTrue(numMatchingBeacons > 0, "not a single measured Beacon was matched against known ones. coding error? model error?");
|
||||||
|
|
||||||
|
return prob;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIFIPROBABILITYFREE_H
|
||||||
46
sensors/beacon/model/BeaconModel.h
Normal file
46
sensors/beacon/model/BeaconModel.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef BEACONMODEL_H
|
||||||
|
#define BEACONMODEL_H
|
||||||
|
|
||||||
|
#include "../Beacon.h"
|
||||||
|
#include "../BeaconMeasurement.h"
|
||||||
|
#include "../../../geo/Point3.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interface for signal-strength prediction models.
|
||||||
|
*
|
||||||
|
* the model is passed a MAC-address of an AP in question, and a position.
|
||||||
|
* hereafter the model returns the RSSI for this AP at the questioned location.
|
||||||
|
*/
|
||||||
|
class BeaconModel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// /** get the given access-point's RSSI at the provided location */
|
||||||
|
// virtual float getRSSI(const LocatedAccessPoint& ap, const Point3 p) = 0;
|
||||||
|
|
||||||
|
/** get a list of all APs known to the model */
|
||||||
|
virtual std::vector<Beacon> getAllBeacons() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update the beacons signal strength using the current measurement
|
||||||
|
* this could happen if the txp is not updated within the floorplan
|
||||||
|
*
|
||||||
|
* be careful and don't use fantasy values, this could ruin your localitions
|
||||||
|
* completely
|
||||||
|
*/
|
||||||
|
virtual void updateBeacon(const BeaconMeasurement beacon) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the RSSI expected at the given location (in meter)
|
||||||
|
* for an beacon identified by the given MAC.
|
||||||
|
*
|
||||||
|
* if the model can not predict the RSSI for an beacon, it returns NaN!
|
||||||
|
*/
|
||||||
|
virtual float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BEACONMODEL_H
|
||||||
92
sensors/beacon/model/BeaconModelLogDist.h
Normal file
92
sensors/beacon/model/BeaconModelLogDist.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#ifndef BEACONMODELLOGDIST_H
|
||||||
|
#define BEACONMODELLOGDIST_H
|
||||||
|
|
||||||
|
#include "BeaconModel.h"
|
||||||
|
#include "../../radio/model/LogDistanceModel.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* signal-strength estimation using log-distance model
|
||||||
|
*/
|
||||||
|
class BeaconModelLogDist : public BeaconModel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** parameters describing one beacons to the model */
|
||||||
|
struct APEntry {
|
||||||
|
|
||||||
|
Point3 position_m; // the AP's position (in meter)
|
||||||
|
float txp; // sending power (-40)
|
||||||
|
float exp; // path-loss-exponent (~2.0 - 4.0)
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
APEntry(const Point3 position_m, const float txp, const float exp) :
|
||||||
|
position_m(position_m), txp(txp), exp(exp) {;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** map of all beacons (and their parameters) known to the model */
|
||||||
|
std::unordered_map<MACAddress, APEntry> beacons;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
BeaconModelLogDist() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get a list of all beacons known to the model */
|
||||||
|
std::vector<Beacon> getAllBeacons() const {
|
||||||
|
std::vector<Beacon> aps;
|
||||||
|
for (const auto it : beacons) {aps.push_back(Beacon(it.first));}
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** make the given beacon (and its parameters) known to the model */
|
||||||
|
void addAP(const MACAddress& beacon, const APEntry& params) {
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
Assert::isBetween(params.txp, -50.0f, -30.0f, "TXP out of bounds [-90:-30]");
|
||||||
|
Assert::isBetween(params.exp, 1.0f, 4.0f, "EXP out of bounds [1:4]");
|
||||||
|
|
||||||
|
// add
|
||||||
|
beacons.insert( std::pair<MACAddress, APEntry>(beacon, params) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBeacon(const BeaconMeasurement beacon) override{
|
||||||
|
// try to get the corresponding parameters
|
||||||
|
const auto it = beacons.find(MACAddress(beacon.getBeacon().getMAC()));
|
||||||
|
|
||||||
|
// beacon unknown? -> NAN
|
||||||
|
if (it == beacons.end()) {return;}
|
||||||
|
|
||||||
|
it->second.txp = beacon.getBeacon().getTXP();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual float getRSSI(const MACAddress& beacon, const Point3 position_m) const override {
|
||||||
|
|
||||||
|
// try to get the corresponding parameters
|
||||||
|
const auto it = beacons.find(beacon);
|
||||||
|
|
||||||
|
// AP unknown? -> NAN
|
||||||
|
if (it == beacons.end()) {return NAN;}
|
||||||
|
|
||||||
|
// the beacons' parameters
|
||||||
|
const APEntry& params = it->second;
|
||||||
|
|
||||||
|
// free-space (line-of-sight) RSSI
|
||||||
|
const float distance_m = position_m.getDistance(params.position_m);
|
||||||
|
const float rssiLOS = LogDistanceModel::distanceToRssi(params.txp, params.exp, distance_m);
|
||||||
|
|
||||||
|
// done
|
||||||
|
return rssiLOS;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BEACONMODELLOGDIST_H
|
||||||
208
sensors/beacon/model/BeaconModelLogDistCeiling.h
Normal file
208
sensors/beacon/model/BeaconModelLogDistCeiling.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#ifndef BEACONMODELLOGDISTCEILING_H
|
||||||
|
#define BEACONMODELLOGDISTCEILING_H
|
||||||
|
|
||||||
|
#include "../../../floorplan/v2/Floorplan.h"
|
||||||
|
|
||||||
|
#include "../../../Assertions.h"
|
||||||
|
#include "BeaconModel.h"
|
||||||
|
#include "../../radio/model/LogDistanceModel.h"
|
||||||
|
#include "../BeaconMeasurement.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* signal-strength estimation using log-distance model
|
||||||
|
* including ceilings between beacon and position
|
||||||
|
*/
|
||||||
|
class BeaconModelLogDistCeiling : public BeaconModel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** parameters describing one beacon to the model */
|
||||||
|
struct APEntry {
|
||||||
|
|
||||||
|
Point3 position_m; // the beacon's position (in meter)
|
||||||
|
float txp; // sending power (-40)
|
||||||
|
float exp; // path-loss-exponent (~2.0 - 4.0)
|
||||||
|
float waf; // attenuation per ceiling/floor (~-8.0)
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
APEntry(const Point3 position_m, const float txp, const float exp, const float waf) :
|
||||||
|
position_m(position_m), txp(txp), exp(exp), waf(waf) {;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** map of all beacons (and their parameters) known to the model */
|
||||||
|
std::unordered_map<MACAddress, APEntry> beacons;
|
||||||
|
|
||||||
|
/** position (height) of all ceilings (in meter) */
|
||||||
|
std::vector<float> ceilingsAtHeight_m;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor with floorplan (needed for ceiling position) */
|
||||||
|
BeaconModelLogDistCeiling(const Floorplan::IndoorMap* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get a list of all beacons known to the model */
|
||||||
|
std::vector<Beacon> getAllBeacons() const {
|
||||||
|
std::vector<Beacon> aps;
|
||||||
|
for (const auto it : beacons) {aps.push_back(Beacon(it.first));}
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** load beacon information from the floorplan. use the given fixed TXP/EXP/WAF for all APs */
|
||||||
|
void loadBeaconsFromMap(const Floorplan::IndoorMap* map, const float txp = -40.0f, const float exp = 2.5f, const float waf = -8.0f) {
|
||||||
|
|
||||||
|
for (const Floorplan::Floor* floor : map->floors) {
|
||||||
|
for (const Floorplan::Beacon* beacon : floor->beacons) {
|
||||||
|
APEntry ape(beacon->getPos(floor), txp, exp, waf);
|
||||||
|
addBeacon(MACAddress(beacon->mac), ape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** load beacon information from a vector. use the given fixed TXP/EXP/WAF for all APs */
|
||||||
|
void loadBeaconsFromVector(const Floorplan::IndoorMap* map, const float txp = -40.0f, const float exp = 2.5f, const float waf = -8.0f) {
|
||||||
|
|
||||||
|
for (const Floorplan::Floor* floor : map->floors) {
|
||||||
|
for (const Floorplan::Beacon* beacon : floor->beacons) {
|
||||||
|
APEntry ape(beacon->getPos(floor), txp, exp, waf);
|
||||||
|
addBeacon(MACAddress(beacon->mac), ape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** make the given beacon (and its parameters) known to the model */
|
||||||
|
void addBeacon(const MACAddress& beacon, const APEntry& params) {
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
Assert::isBetween(params.waf, -99.0f, 0.0f, "WAF out of bounds [-99:0]");
|
||||||
|
Assert::isBetween(params.txp, -90.0f, -30.0f, "TXP out of bounds [-50:-30]");
|
||||||
|
Assert::isBetween(params.exp, 1.0f, 4.0f, "EXP out of bounds [1:4]");
|
||||||
|
|
||||||
|
Assert::equal(beacons.find(beacon), beacons.end(), "AccessPoint already present!");
|
||||||
|
|
||||||
|
// add
|
||||||
|
beacons.insert( std::pair<MACAddress, APEntry>(beacon, params) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBeacon(const BeaconMeasurement beacon) override {
|
||||||
|
// try to get the corresponding parameters
|
||||||
|
auto it = beacons.find(MACAddress(beacon.getBeacon().getMAC()));
|
||||||
|
|
||||||
|
// beacon unknown? -> NAN
|
||||||
|
if (it == beacons.end()) {return;}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Check if this works as expected
|
||||||
|
it->second.txp = beacon.getBeacon().getTXP();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** remove all added APs */
|
||||||
|
void clear() {
|
||||||
|
beacons.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRSSI(const MACAddress& beacon, const Point3 position_m) const override {
|
||||||
|
|
||||||
|
// try to get the corresponding parameters
|
||||||
|
const auto it = beacons.find(beacon);
|
||||||
|
|
||||||
|
// beacon unknown? -> NAN
|
||||||
|
if (it == beacons.end()) {return NAN;}
|
||||||
|
|
||||||
|
// the access-points' parameters
|
||||||
|
const APEntry& params = it->second;
|
||||||
|
|
||||||
|
// free-space (line-of-sight) RSSI
|
||||||
|
const float distance_m = position_m.getDistance(params.position_m);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// combine
|
||||||
|
return rssiLOS + wafLoss;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
FRIEND_TEST(LogDistanceCeilingModelBeacon, numCeilings);
|
||||||
|
FRIEND_TEST(LogDistanceCeilingModelBeacon, 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // WIFIMODELLOGDISTCEILING_H
|
||||||
53
sensors/imu/GravityData.h
Normal file
53
sensors/imu/GravityData.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef GRAVITYDATA_H
|
||||||
|
#define GRAVITYDATA_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
/** data received from an accelerometer sensor */
|
||||||
|
struct GravityData {
|
||||||
|
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
GravityData() : x(0), y(0), z(0) {;}
|
||||||
|
|
||||||
|
GravityData(const float x, const float y, const float z) : x(x), y(y), z(z) {;}
|
||||||
|
|
||||||
|
float magnitude() const {
|
||||||
|
return std::sqrt( x*x + y*y + z*z );
|
||||||
|
}
|
||||||
|
|
||||||
|
GravityData& operator += (const GravityData& o) {
|
||||||
|
this->x += o.x;
|
||||||
|
this->y += o.y;
|
||||||
|
this->z += o.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GravityData& operator -= (const GravityData& o) {
|
||||||
|
this->x -= o.x;
|
||||||
|
this->y -= o.y;
|
||||||
|
this->z -= o.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GravityData operator - (const GravityData& o) const {
|
||||||
|
return GravityData(x-o.x, y-o.y, z-o.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
GravityData operator / (const float val) const {
|
||||||
|
return GravityData(x/val, y/val, z/val);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string asString() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "(" << x << "," << y << "," << z << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GRAVITYDATA_H
|
||||||
53
sensors/imu/LinearAccelerationData.h
Normal file
53
sensors/imu/LinearAccelerationData.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef LINEARACCELERATIONDATA_H
|
||||||
|
#define LINEARACCELERATIONDATA_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
/** data received from an accelerometer sensor */
|
||||||
|
struct LinearAccelerationData {
|
||||||
|
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
LinearAccelerationData() : x(0), y(0), z(0) {;}
|
||||||
|
|
||||||
|
LinearAccelerationData(const float x, const float y, const float z) : x(x), y(y), z(z) {;}
|
||||||
|
|
||||||
|
float magnitude() const {
|
||||||
|
return std::sqrt( x*x + y*y + z*z );
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearAccelerationData& operator += (const LinearAccelerationData& o) {
|
||||||
|
this->x += o.x;
|
||||||
|
this->y += o.y;
|
||||||
|
this->z += o.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearAccelerationData& operator -= (const LinearAccelerationData& o) {
|
||||||
|
this->x -= o.x;
|
||||||
|
this->y -= o.y;
|
||||||
|
this->z -= o.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearAccelerationData operator - (const LinearAccelerationData& o) const {
|
||||||
|
return LinearAccelerationData(x-o.x, y-o.y, z-o.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearAccelerationData operator / (const float val) const {
|
||||||
|
return LinearAccelerationData(x/val, y/val, z/val);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string asString() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "(" << x << "," << y << "," << z << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LINEARACCELERATIONDATA_H
|
||||||
163
sensors/imu/MotionDetection.h
Normal file
163
sensors/imu/MotionDetection.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#ifndef MOTIONDETECTION_H
|
||||||
|
#define MOTIONDETECTION_H
|
||||||
|
|
||||||
|
#include "GravityData.h"
|
||||||
|
#include "LinearAccelerationData.h"
|
||||||
|
#include "../../data/Timestamp.h"
|
||||||
|
#include "../../math/MovingAverageTS.h"
|
||||||
|
#include "../../misc/Debug.h"
|
||||||
|
|
||||||
|
#include <eigen3/Eigen/Dense>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "../../Assertions.h"
|
||||||
|
|
||||||
|
class MotionDetection {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool newAccelerationMeasurementArrived = false;
|
||||||
|
bool newGravityMeasurementArrived = false;
|
||||||
|
|
||||||
|
Eigen::Vector3f curGravity;
|
||||||
|
Eigen::Vector3f curLinearAcceleration;
|
||||||
|
|
||||||
|
//fast algo
|
||||||
|
Eigen::Matrix2f sumProjectedCov = Eigen::Matrix2f::Identity(); //sum of the projection of curLinearAcceleartion into perpendicular plane of curGravity as semmetric matrix
|
||||||
|
|
||||||
|
int numMeasurementsPerInterval, updateCnt;
|
||||||
|
int updateInterval; //defines how often a new motion axis is calculated in milliseconds. default = 500ms
|
||||||
|
|
||||||
|
struct Motion{
|
||||||
|
Eigen::Vector2f vec = Eigen::Vector2f::Identity();
|
||||||
|
Timestamp lastEstimation;
|
||||||
|
};
|
||||||
|
|
||||||
|
Motion curMotion;
|
||||||
|
Motion prevMotion;
|
||||||
|
|
||||||
|
const char* name = "MotionDetection";
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
MotionDetection(int updateInterval = 500) : updateInterval(updateInterval), numMeasurementsPerInterval(0), updateCnt(0) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addGravity(const Timestamp& ts, const GravityData& grav){
|
||||||
|
|
||||||
|
curGravity << grav.x, grav.y, grav.z;
|
||||||
|
newGravityMeasurementArrived = true;
|
||||||
|
|
||||||
|
updateProjectionVectorFast(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLinearAcceleration(const Timestamp& ts, const LinearAccelerationData& acc) {
|
||||||
|
|
||||||
|
curLinearAcceleration << acc.x, acc.y, acc.z;
|
||||||
|
newAccelerationMeasurementArrived = true;
|
||||||
|
|
||||||
|
updateProjectionVectorFast(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return the current motion axis
|
||||||
|
* NOTE: if no data is available, this vector is the Identity
|
||||||
|
*/
|
||||||
|
Eigen::Vector2f getCurrentMotionAxis(){
|
||||||
|
return curMotion.vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the radians between [-pi, pi] between successive motion axis estimations */
|
||||||
|
float getMotionChangeInRad(){
|
||||||
|
//TODO: put this in an EigenHelper Class within geo
|
||||||
|
const float crossProduct = curMotion.vec.x() * prevMotion.vec.y() - curMotion.vec.y() * prevMotion.vec.x();
|
||||||
|
const float ang = (crossProduct < 0 ? 1:-1) * std::acos(std::min(std::max(curMotion.vec.dot(prevMotion.vec) / curMotion.vec.norm() * prevMotion.vec.norm(), -1.0f), 1.0f));
|
||||||
|
|
||||||
|
//nan?
|
||||||
|
if(std::isnan(ang)){
|
||||||
|
Log::add(name, "The motion change angle is nAn, this is not correct!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(updateCnt < 2){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ang;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
FRIEND_TEST(MotionDetection, motionAngle);
|
||||||
|
FRIEND_TEST(MotionDetection, motionAxis);
|
||||||
|
|
||||||
|
Eigen::Vector2f updateMotionAxis(Eigen::Matrix2f covarianceMatrix){
|
||||||
|
|
||||||
|
Eigen::SelfAdjointEigenSolver<Eigen::Matrix2f> solver(covarianceMatrix);
|
||||||
|
return solver.eigenvectors().col(1); //returns the eigenvector corresponding to the biggest eigenvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateProjectionVectorFast(const Timestamp& ts){
|
||||||
|
|
||||||
|
//make sure we have both measurements for calculation
|
||||||
|
if(newGravityMeasurementArrived && newAccelerationMeasurementArrived){
|
||||||
|
|
||||||
|
numMeasurementsPerInterval++;
|
||||||
|
|
||||||
|
//project acc into perpendicular plane of grav (using standard vector projection)
|
||||||
|
Eigen::Vector3f proj = (curLinearAcceleration.dot(curGravity) / curGravity.dot(curGravity)) * curGravity;
|
||||||
|
|
||||||
|
//if the acc vector is perpendicular to the gravity vector, the dot product results in 0, therefore, we need to do this
|
||||||
|
if(proj.isZero()){
|
||||||
|
proj = curLinearAcceleration;
|
||||||
|
Log::add(name, "The LinearAcceleration vector is perpendicular to the gravity, is this correct?");
|
||||||
|
}
|
||||||
|
|
||||||
|
//we are only interested in x,y
|
||||||
|
Eigen::Vector2f vec;
|
||||||
|
vec << proj.x(), proj.y();
|
||||||
|
|
||||||
|
// sum this up for later averaging.
|
||||||
|
sumProjectedCov += vec*vec.transpose();
|
||||||
|
|
||||||
|
// start with the first available timestamp
|
||||||
|
if (curMotion.lastEstimation.isZero()) {curMotion.lastEstimation = ts;}
|
||||||
|
|
||||||
|
//update the motion axis depending on the update interval
|
||||||
|
if(ts - curMotion.lastEstimation > Timestamp::fromMS(updateInterval)){
|
||||||
|
|
||||||
|
prevMotion = curMotion;
|
||||||
|
|
||||||
|
//calculate the average of the coveriance matrix
|
||||||
|
Eigen::Matrix2f Q = sumProjectedCov / numMeasurementsPerInterval;
|
||||||
|
|
||||||
|
curMotion.vec = updateMotionAxis(Q);
|
||||||
|
curMotion.lastEstimation = ts;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
newGravityMeasurementArrived = false;
|
||||||
|
newAccelerationMeasurementArrived = false;
|
||||||
|
}
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(){
|
||||||
|
numMeasurementsPerInterval = 0;
|
||||||
|
sumProjectedCov = Eigen::Matrix2f::Zero();
|
||||||
|
++updateCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MOTIONDETECTION_H
|
||||||
299
sensors/offline/FileReader.h
Normal file
299
sensors/offline/FileReader.h
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
#ifndef FILEREADER_H
|
||||||
|
#define FILEREADER_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <Indoor/Exception.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "../../math/Interpolator.h"
|
||||||
|
#include "../../sensors/radio/WiFiMeasurements.h"
|
||||||
|
#include "../../sensors/pressure/BarometerData.h"
|
||||||
|
#include "../../sensors/imu/AccelerometerData.h"
|
||||||
|
#include "../../sensors/imu/GyroscopeData.h"
|
||||||
|
#include "../../sensors/imu/GravityData.h"
|
||||||
|
#include "../../sensors/imu/LinearAccelerationData.h"
|
||||||
|
#include "../../sensors/beacon/BeaconMeasurements.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "../../geo/Point2.h"
|
||||||
|
#include "../../grid/factory/v2/GridFactory.h"
|
||||||
|
#include "../../grid/factory/v2/Importance.h"
|
||||||
|
#include "../../floorplan/v2/Floorplan.h"
|
||||||
|
|
||||||
|
class FileReader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename T> struct TS {
|
||||||
|
const uint64_t ts;
|
||||||
|
T data;
|
||||||
|
TS(const uint64_t ts) : ts(ts) {;}
|
||||||
|
TS(const uint64_t ts, const T& data) : ts(ts), data(data) {;}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Sensor {
|
||||||
|
ACC,
|
||||||
|
GYRO,
|
||||||
|
WIFI,
|
||||||
|
POS,
|
||||||
|
BARO,
|
||||||
|
BEACON,
|
||||||
|
LIN_ACC,
|
||||||
|
GRAVITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** entry for one sensor */
|
||||||
|
struct Entry {
|
||||||
|
Sensor type;
|
||||||
|
uint64_t ts;
|
||||||
|
int idx;
|
||||||
|
Entry(Sensor type, uint64_t ts, int idx) : type(type), ts(ts), idx(idx) {;}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TS<int>> groundTruth;
|
||||||
|
std::vector<TS<WiFiMeasurements>> wifi;
|
||||||
|
std::vector<TS<BeaconMeasurement>> beacon;
|
||||||
|
std::vector<TS<AccelerometerData>> acc;
|
||||||
|
std::vector<TS<GyroscopeData>> gyro;
|
||||||
|
std::vector<TS<BarometerData>> barometer;
|
||||||
|
std::vector<TS<LinearAccelerationData>> lin_acc;
|
||||||
|
std::vector<TS<GravityData>> gravity;
|
||||||
|
|
||||||
|
/** ALL entries */
|
||||||
|
std::vector<Entry> entries;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
FileReader(const std::string& file) {
|
||||||
|
parse(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Entry>& getEntries() const {return entries;}
|
||||||
|
|
||||||
|
|
||||||
|
const std::vector<TS<int>>& getGroundTruth() const {return groundTruth;}
|
||||||
|
|
||||||
|
const std::vector<TS<WiFiMeasurements>>& getWiFiGroupedByTime() const {return wifi;}
|
||||||
|
|
||||||
|
const std::vector<TS<BeaconMeasurement>>& getBeacons() const {return beacon;}
|
||||||
|
|
||||||
|
const std::vector<TS<AccelerometerData>>& getAccelerometer() const {return acc;}
|
||||||
|
|
||||||
|
const std::vector<TS<GyroscopeData>>& getGyroscope() const {return gyro;}
|
||||||
|
|
||||||
|
const std::vector<TS<BarometerData>>& getBarometer() const {return barometer;}
|
||||||
|
|
||||||
|
const std::vector<TS<LinearAccelerationData>>& getLinearAcceleration() const {return lin_acc;}
|
||||||
|
|
||||||
|
const std::vector<TS<GravityData>>& getGravity() const {return gravity;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void parse(const std::string& file) {
|
||||||
|
|
||||||
|
std::ifstream inp(file);
|
||||||
|
if (!inp.is_open() || inp.bad() || inp.eof()) {throw Exception("failed to open file" + file);}
|
||||||
|
|
||||||
|
while(!inp.eof() && !inp.bad()) {
|
||||||
|
|
||||||
|
uint64_t ts;
|
||||||
|
char delim;
|
||||||
|
int idx = -1;
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
inp >> ts;
|
||||||
|
inp >> delim;
|
||||||
|
inp >> idx;
|
||||||
|
inp >> delim;
|
||||||
|
inp >> data;
|
||||||
|
|
||||||
|
if (idx == 8) {parseWiFi(ts, data);}
|
||||||
|
else if (idx == 9) {parseBeacons(ts, data);}
|
||||||
|
else if (idx == 99) {parseGroundTruth(ts, data);}
|
||||||
|
else if (idx == 0) {parseAccelerometer(ts, data);}
|
||||||
|
else if (idx == 3) {parseGyroscope(ts, data);}
|
||||||
|
else if (idx == 5) {parseBarometer(ts, data);}
|
||||||
|
else if (idx == 2) {parseLinearAcceleration(ts,data);}
|
||||||
|
else if (idx == 1) {parseGravity(ts,data);}
|
||||||
|
|
||||||
|
// TODO: this is a hack...
|
||||||
|
// the loop is called one additional time after the last entry
|
||||||
|
// and keeps the entries of entry
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inp.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseLinearAcceleration(const uint64_t ts, const std::string& data){
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
const auto pos2 = data.find(';', pos1+1);
|
||||||
|
|
||||||
|
const std::string x = data.substr(0, pos1);
|
||||||
|
const std::string y = data.substr(pos1+1, pos2-pos1-1);
|
||||||
|
const std::string z = data.substr(pos2+1);
|
||||||
|
|
||||||
|
TS<LinearAccelerationData> elem(ts, LinearAccelerationData(std::stof(x), std::stof(y), std::stof(z)));
|
||||||
|
lin_acc.push_back(elem);
|
||||||
|
entries.push_back(Entry(Sensor::LIN_ACC, ts, lin_acc.size()-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseGravity(const uint64_t ts, const std::string& data){
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
const auto pos2 = data.find(';', pos1+1);
|
||||||
|
|
||||||
|
const std::string x = data.substr(0, pos1);
|
||||||
|
const std::string y = data.substr(pos1+1, pos2-pos1-1);
|
||||||
|
const std::string z = data.substr(pos2+1);
|
||||||
|
|
||||||
|
TS<GravityData> elem(ts, GravityData(std::stof(x), std::stof(y), std::stof(z)));
|
||||||
|
gravity.push_back(elem);
|
||||||
|
entries.push_back(Entry(Sensor::GRAVITY, ts, gravity.size()-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseAccelerometer(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
const auto pos2 = data.find(';', pos1+1);
|
||||||
|
|
||||||
|
const std::string x = data.substr(0, pos1);
|
||||||
|
const std::string y = data.substr(pos1+1, pos2-pos1-1);
|
||||||
|
const std::string z = data.substr(pos2+1);
|
||||||
|
|
||||||
|
TS<AccelerometerData> elem(ts, AccelerometerData(std::stof(x), std::stof(y), std::stof(z)));
|
||||||
|
acc.push_back(elem);
|
||||||
|
entries.push_back(Entry(Sensor::ACC, ts, acc.size()-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseGyroscope(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
const auto pos2 = data.find(';', pos1+1);
|
||||||
|
|
||||||
|
const std::string x = data.substr(0, pos1);
|
||||||
|
const std::string y = data.substr(pos1+1, pos2-pos1-1);
|
||||||
|
const std::string z = data.substr(pos2+1);
|
||||||
|
|
||||||
|
TS<GyroscopeData> elem(ts, GyroscopeData(std::stof(x), std::stof(y), std::stof(z)));
|
||||||
|
gyro.push_back(elem);
|
||||||
|
entries.push_back(Entry(Sensor::GYRO, ts, gyro.size()-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseWiFi(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
std::string tmp = data;
|
||||||
|
|
||||||
|
// add new wifi reading
|
||||||
|
wifi.push_back(TS<WiFiMeasurements>(ts, WiFiMeasurements()));
|
||||||
|
entries.push_back(Entry(Sensor::WIFI, ts, wifi.size()-1));
|
||||||
|
|
||||||
|
// process all APs
|
||||||
|
while(!tmp.empty()) {
|
||||||
|
|
||||||
|
auto pos1 = tmp.find(';');
|
||||||
|
auto pos2 = tmp.find(';', pos1+1);
|
||||||
|
auto pos3 = tmp.find(';', pos2+1);
|
||||||
|
|
||||||
|
std::string mac = tmp.substr(0, pos1);
|
||||||
|
std::string freq = tmp.substr(pos1+1, pos2);
|
||||||
|
std::string rssi = tmp.substr(pos2+1, pos3);
|
||||||
|
|
||||||
|
tmp = tmp.substr(pos3);
|
||||||
|
assert(tmp[0] == ';'); tmp = tmp.substr(1);
|
||||||
|
|
||||||
|
// append AP to current scan-entry
|
||||||
|
WiFiMeasurement e(AccessPoint(mac), std::stoi(rssi), Timestamp::fromMS(ts));
|
||||||
|
wifi.back().data.entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseBeacons(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
const auto pos2 = data.find(';', pos1+1);
|
||||||
|
const auto pos3 = data.find(';', pos2+1);
|
||||||
|
|
||||||
|
const std::string mac = data.substr(0, pos1);
|
||||||
|
const std::string rssi = data.substr(pos1+1, pos2);
|
||||||
|
const std::string txp = data.substr(pos2+1, pos3);
|
||||||
|
|
||||||
|
//yes the timestamp is redundant here, but in case of multiusage...
|
||||||
|
TS<BeaconMeasurement> e(ts, BeaconMeasurement(Timestamp::fromMS(ts), Beacon(mac), std::stoi(rssi)));
|
||||||
|
beacon.push_back(e);
|
||||||
|
entries.push_back(Entry(Sensor::BEACON, ts, beacon.size()-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseGroundTruth(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
std::string gtIndex = data.substr(0, pos1);
|
||||||
|
|
||||||
|
TS<int> elem(ts, std::stoi(gtIndex));
|
||||||
|
groundTruth.push_back(elem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseBarometer(const uint64_t ts, const std::string& data) {
|
||||||
|
|
||||||
|
const auto pos1 = data.find(';');
|
||||||
|
|
||||||
|
const std::string hPa = data.substr(0, pos1);
|
||||||
|
|
||||||
|
TS<BarometerData> elem(ts, BarometerData(std::stof(hPa)));
|
||||||
|
barometer.push_back(elem);
|
||||||
|
entries.push_back(Entry(Sensor::BARO, ts, barometer.size()-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Interpolator<uint64_t, Point3> getGroundTruthPath(Floorplan::IndoorMap* map, std::vector<int> gtPath) const {
|
||||||
|
|
||||||
|
// finde alle positionen der waypoints im gtPath aus map
|
||||||
|
std::unordered_map<int, Point3> waypointsMap;
|
||||||
|
for(Floorplan::Floor* f : map->floors){
|
||||||
|
float h = f->atHeight;
|
||||||
|
for (Floorplan::GroundTruthPoint* gtp : f->gtpoints){
|
||||||
|
|
||||||
|
//wenn die gleiche id 2x vergeben wurde, knallt es
|
||||||
|
if(waypointsMap.find(gtp->id) == waypointsMap.end()){
|
||||||
|
waypointsMap.insert({gtp->id, Point3(gtp->pos.x,gtp->pos.y, h)});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw std::string("the floorplan's ground truth contains two points with identical id's!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bringe diese in richtige reihenfolge und füge timestamp hinzu
|
||||||
|
Interpolator<uint64_t, Point3> interpol;
|
||||||
|
|
||||||
|
int it = 0;
|
||||||
|
for(int id : gtPath){
|
||||||
|
auto itMap = waypointsMap.find(id);
|
||||||
|
if(itMap == waypointsMap.end()) {throw std::string("waypoint not found in xml");}
|
||||||
|
|
||||||
|
//the time, when the gt button was clicked on the app
|
||||||
|
uint64_t tsGT = groundTruth[it++].ts;
|
||||||
|
interpol.add(tsGT, itMap->second);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gtPath.empty() || waypointsMap.empty() || groundTruth.empty()){
|
||||||
|
throw std::string("No Ground Truth points found within the map.xml file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return interpol;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEREADER_H
|
||||||
@@ -18,8 +18,6 @@ public:
|
|||||||
|
|
||||||
enum Activity {DOWN, STAY, UP};
|
enum Activity {DOWN, STAY, UP};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct History {
|
struct History {
|
||||||
Timestamp ts;
|
Timestamp ts;
|
||||||
BarometerData data;
|
BarometerData data;
|
||||||
@@ -27,23 +25,25 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//just for debugging and plotting
|
|
||||||
std::vector<History> input;
|
|
||||||
std::vector<History> inputInterp;
|
|
||||||
std::vector<History> output;
|
|
||||||
std::vector<float> sumHist;
|
|
||||||
std::vector<float> mvAvgHist;
|
|
||||||
std::vector<History> actHist;
|
|
||||||
|
|
||||||
|
std::vector<History> output;
|
||||||
Activity currentActivity;
|
Activity currentActivity;
|
||||||
MovingAVG<float> mvAvg = MovingAVG<float>(20);
|
MovingAVG<float> mvAvg = MovingAVG<float>(20);
|
||||||
|
|
||||||
/** change this values for much success */
|
/** change this values for much success
|
||||||
|
*
|
||||||
|
* Nexus 6:
|
||||||
|
* butter = Filter::ButterworthLP<float>(10,0.1f,2);
|
||||||
|
* threshold = 0.025;
|
||||||
|
* diffSize = 20;
|
||||||
|
* FixedFrequencyInterpolator<float> ffi = FixedFrequencyInterpolator<float>(Timestamp::fromMS(100));
|
||||||
|
*/
|
||||||
const bool additionalLowpassFilter = false;
|
const bool additionalLowpassFilter = false;
|
||||||
const int diffSize = 20; //the number values used for finding the activity.
|
const unsigned long diffSize = 20; //the number values used for finding the activity.
|
||||||
const float threshold = 0.025; // if diffSize is getting smaller, treshold needs to be adjusted in the same direction!
|
const float threshold = 0.025f; // if diffSize is getting smaller, treshold needs to be adjusted in the same direction!
|
||||||
Filter::ButterworthLP<float> butter = Filter::ButterworthLP<float>(10,0.1f,2);
|
Filter::ButterworthLP<float> butter = Filter::ButterworthLP<float>(10,0.05f,2);
|
||||||
Filter::ButterworthLP<float> butter2 = Filter::ButterworthLP<float>(10,0.1f,2);
|
Filter::ButterworthLP<float> butter2 = Filter::ButterworthLP<float>(10,0.05f,2);
|
||||||
|
|
||||||
FixedFrequencyInterpolator<float> ffi = FixedFrequencyInterpolator<float>(Timestamp::fromMS(100));
|
FixedFrequencyInterpolator<float> ffi = FixedFrequencyInterpolator<float>(Timestamp::fromMS(100));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -68,14 +68,11 @@ public:
|
|||||||
return STAY;
|
return STAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.push_back(History(ts, baro));
|
|
||||||
|
|
||||||
bool newInterpolatedValues = false;
|
bool newInterpolatedValues = false;
|
||||||
|
|
||||||
//interpolate & butter
|
//interpolate & butter
|
||||||
auto callback = [&] (const Timestamp ts, const float val) {
|
auto callback = [&] (const Timestamp ts, const float val) {
|
||||||
float interpValue = val;
|
float interpValue = val;
|
||||||
inputInterp.push_back(History(ts, BarometerData(interpValue)));
|
|
||||||
|
|
||||||
//butter
|
//butter
|
||||||
float butterValue = butter.process(interpValue);
|
float butterValue = butter.process(interpValue);
|
||||||
@@ -89,10 +86,10 @@ public:
|
|||||||
if(newInterpolatedValues == true){
|
if(newInterpolatedValues == true){
|
||||||
|
|
||||||
//getActivity
|
//getActivity
|
||||||
if((int)output.size() > diffSize){
|
if(output.size() > diffSize){
|
||||||
//diff
|
//diff
|
||||||
std::vector<float> diff;
|
std::vector<float> diff;
|
||||||
for(int i = output.size() - diffSize; i < output.size() - 1; ++i){
|
for(unsigned long i = output.size() - diffSize; i < output.size() - 1; ++i){
|
||||||
|
|
||||||
float diffVal = output[i+1].data.hPa - output[i].data.hPa;
|
float diffVal = output[i+1].data.hPa - output[i].data.hPa;
|
||||||
|
|
||||||
@@ -113,7 +110,6 @@ public:
|
|||||||
}else{
|
}else{
|
||||||
actValue = sum;
|
actValue = sum;
|
||||||
}
|
}
|
||||||
sumHist.push_back(actValue);
|
|
||||||
|
|
||||||
if(actValue > threshold){
|
if(actValue > threshold){
|
||||||
currentActivity = DOWN;
|
currentActivity = DOWN;
|
||||||
@@ -127,8 +123,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actHist.push_back(History(ts, BarometerData(currentActivity)));
|
|
||||||
|
|
||||||
return currentActivity;
|
return currentActivity;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,49 +131,6 @@ public:
|
|||||||
Activity getCurrentActivity() {
|
Activity getCurrentActivity() {
|
||||||
return currentActivity;
|
return currentActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> getSensorHistory(){
|
|
||||||
std::vector<float> tmp;
|
|
||||||
|
|
||||||
for(History val : input){
|
|
||||||
tmp.push_back(val.data.hPa);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> getInterpolatedHistory(){
|
|
||||||
std::vector<float> tmp;
|
|
||||||
|
|
||||||
for(History val : inputInterp){
|
|
||||||
tmp.push_back(val.data.hPa);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> getOutputHistory(){
|
|
||||||
|
|
||||||
std::vector<float> tmp;
|
|
||||||
|
|
||||||
for(History val : output){
|
|
||||||
tmp.push_back(val.data.hPa);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> getSumHistory(){
|
|
||||||
return sumHist;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<History> getActHistory(){
|
|
||||||
|
|
||||||
return actHist;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ACTIVITYBUTTERPRESSURE_H
|
#endif // ACTIVITYBUTTERPRESSURE_H
|
||||||
|
|||||||
187
sensors/pressure/ActivityButterPressurePercent.h
Normal file
187
sensors/pressure/ActivityButterPressurePercent.h
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#ifndef ACTIVITYBUTTERPRESSUREPERCENT_H
|
||||||
|
#define ACTIVITYBUTTERPRESSUREPERCENT_H
|
||||||
|
|
||||||
|
#include "../../data/Timestamp.h"
|
||||||
|
#include "../../math/filter/Butterworth.h"
|
||||||
|
#include "../../math/FixedFrequencyInterpolator.h"
|
||||||
|
#include "../../math/distribution/Normal.h"
|
||||||
|
#include <KLib/Assertions.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
#include "BarometerData.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* receives pressure measurements, interpolates them to a ficex frequency, lowpass filtering
|
||||||
|
* activity recognition based on a small window given by matlabs diff(window)
|
||||||
|
*
|
||||||
|
* todo: if an elevator is detected, first we have a short time the stairs are more prober.
|
||||||
|
*/
|
||||||
|
class ActivityButterPressurePercent {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct ActivityProbabilities{
|
||||||
|
float elevatorDown;
|
||||||
|
float stairsDown;
|
||||||
|
float stay;
|
||||||
|
float stairsUp;
|
||||||
|
float elevatorUp;
|
||||||
|
|
||||||
|
ActivityProbabilities(float elevatorDown, float stairsDown,
|
||||||
|
float stay, float stairsUp, float elevatorUp) :
|
||||||
|
elevatorDown(elevatorDown), stairsDown(stairsDown),
|
||||||
|
stay(stay), stairsUp(stairsUp), elevatorUp(elevatorUp) {;}
|
||||||
|
|
||||||
|
ActivityProbabilities() :
|
||||||
|
elevatorDown(0.01f), stairsDown(0.01f),
|
||||||
|
stay(0.96f), stairsUp(0.01f), elevatorUp(0.01f) {;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct History {
|
||||||
|
Timestamp ts;
|
||||||
|
BarometerData data;
|
||||||
|
History(const Timestamp ts, const BarometerData data) : ts(ts), data(data) {;}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<History> output;
|
||||||
|
bool initialize;
|
||||||
|
|
||||||
|
ActivityProbabilities currentActivity;
|
||||||
|
|
||||||
|
/** change this values for much success */
|
||||||
|
const unsigned long diffSize = 20; //the number values used for finding the activity.
|
||||||
|
Filter::ButterworthLP<float> butter = Filter::ButterworthLP<float>(10,0.05f,2);
|
||||||
|
FixedFrequencyInterpolator<float> ffi = FixedFrequencyInterpolator<float>(Timestamp::fromMS(100));
|
||||||
|
|
||||||
|
const float variance = 0.02f;
|
||||||
|
|
||||||
|
const float muStairs = 0.04f;
|
||||||
|
const float muStay = 0.00f;
|
||||||
|
const float muEleveator = 0.08f;
|
||||||
|
|
||||||
|
std::vector<float> densities = std::vector<float>(5, 1);
|
||||||
|
std::vector<float> densitiesOld = std::vector<float>(5, 1);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
ActivityButterPressurePercent() : currentActivity(ActivityProbabilities(0.01f, 0.01f, 0.96f, 0.01f, 0.01f)){
|
||||||
|
initialize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** add new sensor readings that were received at the given timestamp */
|
||||||
|
ActivityProbabilities add(const Timestamp& ts, const BarometerData& baro) {
|
||||||
|
|
||||||
|
//init
|
||||||
|
if(initialize){
|
||||||
|
butter.stepInitialization(baro.hPa);
|
||||||
|
initialize = false;
|
||||||
|
|
||||||
|
return currentActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool newInterpolatedValues = false;
|
||||||
|
|
||||||
|
//interpolate & butter
|
||||||
|
auto callback = [&] (const Timestamp ts, const float val) {
|
||||||
|
float interpValue = val;
|
||||||
|
|
||||||
|
//butter
|
||||||
|
float butterValue = butter.process(interpValue);
|
||||||
|
output.push_back(History(ts, BarometerData(butterValue)));
|
||||||
|
|
||||||
|
newInterpolatedValues = true;
|
||||||
|
|
||||||
|
};
|
||||||
|
ffi.add(ts, baro.hPa, callback);
|
||||||
|
|
||||||
|
if(newInterpolatedValues == true){
|
||||||
|
|
||||||
|
//getActivity
|
||||||
|
if(output.size() > diffSize){
|
||||||
|
//diff
|
||||||
|
std::vector<float> diff;
|
||||||
|
for(unsigned long i = output.size() - diffSize; i < output.size() - 1; ++i){
|
||||||
|
|
||||||
|
float diffVal = output[i+1].data.hPa - output[i].data.hPa;
|
||||||
|
|
||||||
|
diff.push_back(diffVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sum = 0;
|
||||||
|
for(float val : diff){
|
||||||
|
sum += val;
|
||||||
|
}
|
||||||
|
|
||||||
|
float actValue = sum;
|
||||||
|
|
||||||
|
//calculate the probabilites of walking down/up etc...
|
||||||
|
densitiesOld = densities;
|
||||||
|
|
||||||
|
//in one building there is an ultra fast elevator, therefore we need to clip the activity value...
|
||||||
|
if(actValue > muEleveator){
|
||||||
|
actValue = muEleveator;
|
||||||
|
}
|
||||||
|
if(actValue < -muEleveator){
|
||||||
|
actValue = -muEleveator;
|
||||||
|
}
|
||||||
|
|
||||||
|
float densityElevatorDown = Distribution::Normal<float>::getProbability(muEleveator, variance, actValue);
|
||||||
|
float densityStairsDown = Distribution::Normal<float>::getProbability(muStairs, variance, actValue);
|
||||||
|
float densityStay = Distribution::Normal<float>::getProbability(muStay, variance, actValue);
|
||||||
|
float densityStairsUp = Distribution::Normal<float>::getProbability(-muStairs, variance, actValue);
|
||||||
|
float densityElevatorUp = Distribution::Normal<float>::getProbability(-muEleveator, variance, actValue);
|
||||||
|
|
||||||
|
_assertTrue( (densityElevatorDown == densityElevatorDown), "the probability of densityElevatorDown is null!");
|
||||||
|
_assertTrue( (densityStairsDown == densityStairsDown), "the probability of densityStairsDown is null!");
|
||||||
|
_assertTrue( (densityStay == densityStay), "the probability of densityStay is null!");
|
||||||
|
_assertTrue( (densityStairsUp == densityStairsUp), "the probability of densityStairsUp is null!");
|
||||||
|
_assertTrue( (densityElevatorUp == densityElevatorUp), "the probability of densityElevatorUp is null!");
|
||||||
|
|
||||||
|
_assertTrue( (densityElevatorDown != 0.0f), "the probability of densityElevatorDown is null!");
|
||||||
|
_assertTrue( (densityStairsDown != 0.0f), "the probability of densityStairsDown is null!");
|
||||||
|
_assertTrue( (densityStay != 0.0f), "the probability of densityStay is null!");
|
||||||
|
_assertTrue( (densityStairsUp != 0.0f), "the probability of densityStairsUp is null!");
|
||||||
|
_assertTrue( (densityElevatorUp != 0.0f), "the probability of densityElevatorUp is null!");
|
||||||
|
|
||||||
|
//wenn aufzug / treppe der größte wert, werden für x timestamps auf die jeweilige katerogie multipliziert.
|
||||||
|
densities[0] = densityElevatorDown;
|
||||||
|
densities[1] = densityStairsDown;
|
||||||
|
densities[2] = densityStay;
|
||||||
|
densities[3] = densityStairsUp;
|
||||||
|
densities[4] = densityElevatorUp;
|
||||||
|
|
||||||
|
//normalize
|
||||||
|
float densitySum = densities[0] + densities[1] + densities[2] + densities[3] + densities[4];
|
||||||
|
|
||||||
|
for(unsigned long i = 0; i < densities.size(); ++i){
|
||||||
|
densities[i] /= densitySum;
|
||||||
|
|
||||||
|
//values cant be zero!
|
||||||
|
densities[i] = (densities[i] > 0.0f ? densities[i] : 0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentActivity = ActivityProbabilities(densities[0], densities[1], densities[2], densities[3], densities[4]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//retruns for every call, indepedent of callback.
|
||||||
|
return currentActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the current Activity */
|
||||||
|
ActivityProbabilities getCurrentActivity() {
|
||||||
|
return currentActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ACTIVITYBUTTERPRESSUREPERCENT_H
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef LOCATEDACCESSPOINT_H
|
|
||||||
#define LOCATEDACCESSPOINT_H
|
|
||||||
|
|
||||||
#include "AccessPoint.h"
|
|
||||||
#include "../../geo/Point3.h"
|
|
||||||
#include "../../floorplan/v2/Floorplan.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* describes an access-point including its position (in meter)
|
|
||||||
*/
|
|
||||||
class LocatedAccessPoint : public AccessPoint, public Point3 {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/** ctor */
|
|
||||||
LocatedAccessPoint(const std::string& mac, const Point3 pos_m) : AccessPoint(mac, ""), Point3(pos_m) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** ctor */
|
|
||||||
LocatedAccessPoint(const Floorplan::AccessPoint& ap) : AccessPoint(ap.mac, ap.name), Point3(ap.pos) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // LOCATEDACCESSPOINT_H
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
class WiFiMeasurement {
|
class WiFiMeasurement {
|
||||||
|
|
||||||
public:
|
private:
|
||||||
|
|
||||||
friend class VAPGrouper;
|
friend class VAPGrouper;
|
||||||
|
|
||||||
@@ -19,6 +19,9 @@ public:
|
|||||||
/** the measured signal strength */
|
/** the measured signal strength */
|
||||||
float rssi;
|
float rssi;
|
||||||
|
|
||||||
|
/** OPTIONAL. frequence the signal was received */
|
||||||
|
float freq;
|
||||||
|
|
||||||
/** OPTIONAL. timestamp the measurement was recorded at */
|
/** OPTIONAL. timestamp the measurement was recorded at */
|
||||||
Timestamp ts;
|
Timestamp ts;
|
||||||
|
|
||||||
@@ -29,11 +32,21 @@ public:
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ctor with freq*/
|
||||||
|
WiFiMeasurement(const AccessPoint& ap, const float rssi, const float freq) : ap(ap), rssi(rssi), freq(freq) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/** ctor with timestamp */
|
/** ctor with timestamp */
|
||||||
WiFiMeasurement(const AccessPoint& ap, const float rssi, const Timestamp ts) : ap(ap), rssi(rssi), ts(ts) {
|
WiFiMeasurement(const AccessPoint& ap, const float rssi, const Timestamp ts) : ap(ap), rssi(rssi), ts(ts) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ctor with timestamp and freq*/
|
||||||
|
WiFiMeasurement(const AccessPoint& ap, const float rssi, const float freq, const Timestamp ts) : ap(ap), rssi(rssi), freq(freq), ts(ts) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** get the AP we got the measurement for */
|
/** get the AP we got the measurement for */
|
||||||
@@ -45,6 +58,16 @@ public:
|
|||||||
/** OPTIONAL: get the measurement's timestamp (if known!) */
|
/** OPTIONAL: get the measurement's timestamp (if known!) */
|
||||||
const Timestamp& getTimestamp() const {return ts;}
|
const Timestamp& getTimestamp() const {return ts;}
|
||||||
|
|
||||||
|
/** OPTIONAL: get the measurement's frequence (if known!) */
|
||||||
|
float getFrequency() const {return freq;}
|
||||||
|
|
||||||
|
/** set another signal strength */
|
||||||
|
void setRssi(float value){rssi = value;}
|
||||||
|
|
||||||
|
/** set the timestamp */
|
||||||
|
void setTimestamp(const Timestamp& val){ts = val;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // WIFIMEASUREMENT_H
|
#endif // WIFIMEASUREMENT_H
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "model/WiFiModel.h"
|
#include "model/WiFiModel.h"
|
||||||
#include "../../math/Distributions.h"
|
#include "../../math/Distributions.h"
|
||||||
#include "VAPGrouper.h"
|
#include "VAPGrouper.h"
|
||||||
|
#include "../../floorplan/v2/Floorplan.h"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ public:
|
|||||||
const Timestamp age = curTime - entry.getTimestamp();
|
const Timestamp age = curTime - entry.getTimestamp();
|
||||||
|
|
||||||
Assert::isTrue(age.ms() >= 0, "found a negative wifi measurement age. this does not make sense");
|
Assert::isTrue(age.ms() >= 0, "found a negative wifi measurement age. this does not make sense");
|
||||||
Assert::isTrue(age.ms() <= 40000, "found a 40 second old wifi measurement. maybe there is a coding error?");
|
//Assert::isTrue(age.ms() <= 40000, "found a 40 second old wifi measurement. maybe there is a coding error?");
|
||||||
|
|
||||||
// sigma grows with measurement age
|
// sigma grows with measurement age
|
||||||
const float sigma = this->sigma + this->sigmaPerSecond * age.sec();
|
const float sigma = this->sigma + this->sigmaPerSecond * age.sec();
|
||||||
@@ -71,14 +72,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
Assert::isTrue(numMatchingAPs > 0, "not a single measured AP was matched against known ones. coding error? model error?");
|
//Assert::isTrue(numMatchingAPs > 0, "not a single measured AP was matched against known ones. coding error? model error?");
|
||||||
|
|
||||||
return prob;
|
return prob;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Node> double getProbability(const Node& n, const Timestamp curTime, const WiFiMeasurements& obs, const int age_ms = 0) const {
|
template <typename Node> double getProbability(const Node& n, const Timestamp curTime, const WiFiMeasurements& obs, const int age_ms = 0) const {
|
||||||
throw Exception("todo??");
|
|
||||||
|
return this->getProbability(n.inMeter() + Point3(0,0,1.3), curTime, obs);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
// after some runtime, check whether the wifi timestamps make sense
|
// after some runtime, check whether the wifi timestamps make sense
|
||||||
// those must not be zero, otherwise something is wrong!
|
// those must not be zero, otherwise something is wrong!
|
||||||
if (!obs.entries.empty()) {
|
if (!obs.entries.empty()) {
|
||||||
Assert::isFalse(curTime.ms() > 10000 && obs.entries.front().ts.isZero(), "WiFiMeasurement timestamp is 0. this does not make sense...");
|
Assert::isFalse(curTime.ms() > 10000 && obs.entries.front().getTimestamp().isZero(), "WiFiMeasurement timestamp is 0. this does not make sense...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// process each observed measurement
|
// process each observed measurement
|
||||||
@@ -103,7 +103,6 @@ public:
|
|||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
//Assert::isTrue(numMatchingAPs > 0, "not a single measured AP was matched against known ones. coding error? model error?");
|
//Assert::isTrue(numMatchingAPs > 0, "not a single measured AP was matched against known ones. coding error? model error?");
|
||||||
// if (numMatchingAPs == 0) {return 0;}
|
|
||||||
|
|
||||||
// as not every node has the same number of visible/matching APs
|
// as not every node has the same number of visible/matching APs
|
||||||
// we MUST return something like the average probability
|
// we MUST return something like the average probability
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#ifndef WIFIMODEL_H
|
#ifndef WIFIMODEL_H
|
||||||
#define WIFIMODEL_H
|
#define WIFIMODEL_H
|
||||||
|
|
||||||
#include "../LocatedAccessPoint.h"
|
#include "../AccessPoint.h"
|
||||||
|
#include "../../../geo/Point3.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* interface for signal-strength prediction models.
|
* interface for signal-strength prediction models.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "WiFiModel.h"
|
#include "WiFiModel.h"
|
||||||
#include "LogDistanceModel.h"
|
#include "LogDistanceModel.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* signal-strength estimation using log-distance model
|
* signal-strength estimation using log-distance model
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ struct WiFiFingerprint {
|
|||||||
const WiFiMeasurements& apMeasurements = it.second;
|
const WiFiMeasurements& apMeasurements = it.second;
|
||||||
WiFiMeasurement avg = apMeasurements.entries.front(); // average starts with a copy of the first entry (to get all data-fields beside the rssi)
|
WiFiMeasurement avg = apMeasurements.entries.front(); // average starts with a copy of the first entry (to get all data-fields beside the rssi)
|
||||||
for (int i = 1; i < (int)apMeasurements.entries.size(); ++i) { // sum up all other entries [1:end]
|
for (int i = 1; i < (int)apMeasurements.entries.size(); ++i) { // sum up all other entries [1:end]
|
||||||
avg.rssi += apMeasurements.entries[i].rssi;
|
avg.setRssi(avg.getRSSI() + apMeasurements.entries[i].getRSSI());
|
||||||
}
|
}
|
||||||
avg.rssi /= apMeasurements.entries.size();
|
avg.setRssi(avg.getRSSI() / apMeasurements.entries.size());
|
||||||
res.entries.push_back(avg); // add to output
|
res.entries.push_back(avg); // add to output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ struct WiFiFingerprint {
|
|||||||
out << "pos: " << pos_m.x << " " << pos_m.y << " " << pos_m.z << "\n";
|
out << "pos: " << pos_m.x << " " << pos_m.y << " " << pos_m.z << "\n";
|
||||||
out << "num: " << measurements.entries.size() << "\n";
|
out << "num: " << measurements.entries.size() << "\n";
|
||||||
for (const WiFiMeasurement& wm : measurements.entries) {
|
for (const WiFiMeasurement& wm : measurements.entries) {
|
||||||
out << wm.getTimestamp().ms() << " " << wm.ap.getMAC().asString() << " " << wm.getRSSI() << "\n";
|
out << wm.getTimestamp().ms() << " " << wm.getAP().getMAC().asString() << " " << wm.getRSSI() << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public:
|
|||||||
|
|
||||||
// add each available AP to its slot (lookup map)
|
// add each available AP to its slot (lookup map)
|
||||||
for (const WiFiMeasurement& m : measurements.entries) {
|
for (const WiFiMeasurement& m : measurements.entries) {
|
||||||
const RSSIatPosition rap(fp.pos_m, m.rssi);
|
const RSSIatPosition rap(fp.pos_m, m.getRSSI());
|
||||||
apMap[m.getAP().getMAC()].push_back(rap);
|
apMap[m.getAP().getMAC()].push_back(rap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
static inline std::string getDataFile(const std::string& name) {
|
static inline std::string getDataFile(const std::string& name) {
|
||||||
return "/mnt/data/workspaces/Indoor/tests/data/" + name;
|
//return "/mnt/data/workspaces/Indoor/tests/data/" + name;
|
||||||
//return "/home/toni/Documents/programme/localization/Indoor/tests/data/" + name;
|
return "/home/toni/Documents/programme/localization/Indoor/tests/data/" + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
214
tests/math/divergence/TestKullbackLeibler.cpp
Normal file
214
tests/math/divergence/TestKullbackLeibler.cpp
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
|
#include "../../Tests.h"
|
||||||
|
#include "../../../math/divergence/KullbackLeibler.h"
|
||||||
|
#include "../../../math/Distributions.h"
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, univariateGaussEQ) {
|
||||||
|
//if the distributions are equal, kld is 0
|
||||||
|
Distribution::Normal<float> norm1(0,1);
|
||||||
|
Distribution::Normal<float> norm2(0,1);
|
||||||
|
|
||||||
|
ASSERT_EQ(0.0f, Divergence::KullbackLeibler<float>::getUnivariateGauss(norm1, norm2));
|
||||||
|
ASSERT_EQ(0.0f, Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm1, norm2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, univariateGaussGEmu) {
|
||||||
|
//bigger mu means greater kld
|
||||||
|
Distribution::Normal<float> norm1(0,1);
|
||||||
|
Distribution::Normal<float> norm2(0,1);
|
||||||
|
Distribution::Normal<float> norm3(0,1);
|
||||||
|
Distribution::Normal<float> norm4(1,1);
|
||||||
|
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGauss(norm3, norm4), Divergence::KullbackLeibler<float>::getUnivariateGauss(norm1, norm2));
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm3, norm4), Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm1, norm2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, univariateGaussGEsigma) {
|
||||||
|
//bigger sigma means greater kld
|
||||||
|
Distribution::Normal<float> norm1(0,1);
|
||||||
|
Distribution::Normal<float> norm2(0,1);
|
||||||
|
Distribution::Normal<float> norm5(0,1);
|
||||||
|
Distribution::Normal<float> norm6(0,3);
|
||||||
|
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGauss(norm5, norm6), Divergence::KullbackLeibler<float>::getUnivariateGauss(norm1, norm2));
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm5, norm6), Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm1, norm2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, univariateGaussRAND) {
|
||||||
|
|
||||||
|
for(int i = 0; i < 20; i++){
|
||||||
|
auto randMu1 = rand() % 100;
|
||||||
|
auto randMu2 = rand() % 100 + 100;
|
||||||
|
|
||||||
|
auto randMu3 = rand() % 100;
|
||||||
|
auto randMu4 = rand() % 100 + 200;
|
||||||
|
|
||||||
|
Distribution::Normal<float> norm7(randMu1,1);
|
||||||
|
Distribution::Normal<float> norm8(randMu2,1);
|
||||||
|
|
||||||
|
Distribution::Normal<float> norm9(randMu3,1);
|
||||||
|
Distribution::Normal<float> norm10(randMu4,1);
|
||||||
|
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGauss(norm9, norm10), Divergence::KullbackLeibler<float>::getUnivariateGauss(norm8, norm7));
|
||||||
|
ASSERT_GE(Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm9, norm10), Divergence::KullbackLeibler<float>::getUnivariateGaussSymmetric(norm8, norm7));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, multivariateGaussEQ) {
|
||||||
|
|
||||||
|
//eq
|
||||||
|
Eigen::VectorXd mu1(2);
|
||||||
|
mu1[0] = 1.0;
|
||||||
|
mu1[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu2(2);
|
||||||
|
mu2[0] = 1.0;
|
||||||
|
mu2[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov1(2,2);
|
||||||
|
cov1(0,0) = 1.0;
|
||||||
|
cov1(0,1) = 0.0;
|
||||||
|
cov1(1,0) = 0.0;
|
||||||
|
cov1(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov2(2,2);
|
||||||
|
cov2(0,0) = 1.0;
|
||||||
|
cov2(0,1) = 0.0;
|
||||||
|
cov2(1,0) = 0.0;
|
||||||
|
cov2(1,1) = 1.0;
|
||||||
|
|
||||||
|
Distribution::NormalDistributionN norm1(mu1, cov1);
|
||||||
|
Distribution::NormalDistributionN norm2(mu2, cov2);
|
||||||
|
|
||||||
|
ASSERT_EQ(0.0f, Divergence::KullbackLeibler<float>::getMultivariateGauss(norm1, norm2));
|
||||||
|
ASSERT_EQ(0.0f, Divergence::KullbackLeibler<float>::getMultivariateGaussSymmetric(norm1, norm2));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, multivariateGaussGeMu) {
|
||||||
|
|
||||||
|
//ge mu
|
||||||
|
Eigen::VectorXd mu1(2);
|
||||||
|
mu1[0] = 1.0;
|
||||||
|
mu1[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu2(2);
|
||||||
|
mu2[0] = 1.0;
|
||||||
|
mu2[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu3(2);
|
||||||
|
mu3[0] = 1.0;
|
||||||
|
mu3[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu4(2);
|
||||||
|
mu4[0] = 1.0;
|
||||||
|
mu4[1] = 3.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov1(2,2);
|
||||||
|
cov1(0,0) = 1.0;
|
||||||
|
cov1(0,1) = 0.0;
|
||||||
|
cov1(1,0) = 0.0;
|
||||||
|
cov1(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov2(2,2);
|
||||||
|
cov2(0,0) = 1.0;
|
||||||
|
cov2(0,1) = 0.0;
|
||||||
|
cov2(1,0) = 0.0;
|
||||||
|
cov2(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov3(2,2);
|
||||||
|
cov3(0,0) = 1.0;
|
||||||
|
cov3(0,1) = 0.0;
|
||||||
|
cov3(1,0) = 0.0;
|
||||||
|
cov3(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov4(2,2);
|
||||||
|
cov4(0,0) = 1.0;
|
||||||
|
cov4(0,1) = 0.0;
|
||||||
|
cov4(1,0) = 0.0;
|
||||||
|
cov4(1,1) = 1.0;
|
||||||
|
|
||||||
|
Distribution::NormalDistributionN norm1(mu1, cov1);
|
||||||
|
Distribution::NormalDistributionN norm2(mu2, cov2);
|
||||||
|
Distribution::NormalDistributionN norm3(mu3, cov3);
|
||||||
|
Distribution::NormalDistributionN norm4(mu4, cov4);
|
||||||
|
|
||||||
|
double kld12 = Divergence::KullbackLeibler<float>::getMultivariateGauss(norm1, norm2);
|
||||||
|
double kld34 = Divergence::KullbackLeibler<float>::getMultivariateGauss(norm3, norm4);
|
||||||
|
std::cout << kld34 << " > " << kld12 << std::endl;
|
||||||
|
|
||||||
|
double kld12sym = Divergence::KullbackLeibler<float>::getMultivariateGaussSymmetric(norm1, norm2);
|
||||||
|
double kld34sym = Divergence::KullbackLeibler<float>::getMultivariateGaussSymmetric(norm3, norm4);
|
||||||
|
std::cout << kld34sym << " > " << kld12sym << std::endl;
|
||||||
|
|
||||||
|
ASSERT_GE(kld34, kld12);
|
||||||
|
ASSERT_GE(kld34sym, kld12sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KullbackLeibler, multivariateGaussGeCov) {
|
||||||
|
|
||||||
|
//ge cov
|
||||||
|
Eigen::VectorXd mu1(2);
|
||||||
|
mu1[0] = 1.0;
|
||||||
|
mu1[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu2(2);
|
||||||
|
mu2[0] = 1.0;
|
||||||
|
mu2[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu3(2);
|
||||||
|
mu3[0] = 1.0;
|
||||||
|
mu3[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::VectorXd mu4(2);
|
||||||
|
mu4[0] = 1.0;
|
||||||
|
mu4[1] = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov1(2,2);
|
||||||
|
cov1(0,0) = 1.0;
|
||||||
|
cov1(0,1) = 0.0;
|
||||||
|
cov1(1,0) = 0.0;
|
||||||
|
cov1(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov2(2,2);
|
||||||
|
cov2(0,0) = 1.0;
|
||||||
|
cov2(0,1) = 0.0;
|
||||||
|
cov2(1,0) = 0.0;
|
||||||
|
cov2(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov3(2,2);
|
||||||
|
cov3(0,0) = 1.0;
|
||||||
|
cov3(0,1) = 0.0;
|
||||||
|
cov3(1,0) = 0.0;
|
||||||
|
cov3(1,1) = 1.0;
|
||||||
|
|
||||||
|
Eigen::MatrixXd cov4(2,2);
|
||||||
|
cov4(0,0) = 3.0;
|
||||||
|
cov4(0,1) = 0.0;
|
||||||
|
cov4(1,0) = 0.0;
|
||||||
|
cov4(1,1) = 1.0;
|
||||||
|
|
||||||
|
Distribution::NormalDistributionN norm1(mu1, cov1);
|
||||||
|
Distribution::NormalDistributionN norm2(mu2, cov2);
|
||||||
|
Distribution::NormalDistributionN norm3(mu3, cov3);
|
||||||
|
Distribution::NormalDistributionN norm4(mu4, cov4);
|
||||||
|
|
||||||
|
double kld12 = Divergence::KullbackLeibler<float>::getMultivariateGauss(norm1, norm2);
|
||||||
|
double kld34 = Divergence::KullbackLeibler<float>::getMultivariateGauss(norm3, norm4);
|
||||||
|
std::cout << kld34 << " >" << kld12 << std::endl;
|
||||||
|
|
||||||
|
double kld12sym = Divergence::KullbackLeibler<float>::getMultivariateGaussSymmetric(norm1, norm2);
|
||||||
|
double kld34sym = Divergence::KullbackLeibler<float>::getMultivariateGaussSymmetric(norm3, norm4);
|
||||||
|
std::cout << kld34sym << " > " << kld12sym << std::endl;
|
||||||
|
|
||||||
|
ASSERT_GE(kld34, kld12);
|
||||||
|
ASSERT_GE(kld34sym, kld12sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
121
tests/sensors/beacon/TestLogDistanceCeilingModel.cpp
Normal file
121
tests/sensors/beacon/TestLogDistanceCeilingModel.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
|
#include "../../Tests.h"
|
||||||
|
|
||||||
|
#include "../../../sensors/beacon/model/BeaconModelLogDistCeiling.h"
|
||||||
|
|
||||||
|
TEST(LogDistanceCeilingModelBeacon, calc) {
|
||||||
|
|
||||||
|
// dummy floorplan
|
||||||
|
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||||
|
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||||
|
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||||
|
|
||||||
|
Floorplan::IndoorMap map;
|
||||||
|
map.floors.push_back(f0);
|
||||||
|
map.floors.push_back(f1);
|
||||||
|
map.floors.push_back(f2);
|
||||||
|
|
||||||
|
//LocatedAccessPoint ap0("00:00:00:00:00:00", Point3(0,0,0));
|
||||||
|
//LocatedAccessPoint ap25("00:00:00:00:00:00", Point3(0,0,2.5));
|
||||||
|
|
||||||
|
BeaconModelLogDistCeiling model(&map);
|
||||||
|
|
||||||
|
const MACAddress ap0 = MACAddress("00:00:00:00:00:00");
|
||||||
|
const MACAddress ap25 = MACAddress("00:00:00:00:00:01");
|
||||||
|
|
||||||
|
model.addBeacon(ap0, BeaconModelLogDistCeiling::APEntry( Point3(0,0,0), -40, 1.0, -8.0 ));
|
||||||
|
model.addBeacon(ap25, BeaconModelLogDistCeiling::APEntry( Point3(0,0,2.5), -40, 1.0, -8.0 ));
|
||||||
|
|
||||||
|
|
||||||
|
ASSERT_EQ(-40, model.getRSSI(ap0, Point3(1,0,0)));
|
||||||
|
ASSERT_EQ(-40, model.getRSSI(ap0, Point3(0,1,0)));
|
||||||
|
ASSERT_EQ(-40, model.getRSSI(ap0, Point3(0,0,1)));
|
||||||
|
|
||||||
|
ASSERT_EQ(-40, model.getRSSI(ap25, Point3(1,0,2.5)));
|
||||||
|
ASSERT_EQ(-40, model.getRSSI(ap25, Point3(0,1,2.5)));
|
||||||
|
ASSERT_EQ(-40-8, model.getRSSI(ap25, Point3(0,0,3.5))); // one floor within
|
||||||
|
|
||||||
|
ASSERT_EQ(model.getRSSI(ap0, Point3(8,0,0)), model.getRSSI(ap0, Point3(0,8,0)));
|
||||||
|
ASSERT_EQ(model.getRSSI(ap0, Point3(8,0,0)), model.getRSSI(ap0, Point3(0,0,8))+8+8); // two ceilings within
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LogDistanceCeilingModelBeacon, numCeilings) {
|
||||||
|
|
||||||
|
// dummy floorplan
|
||||||
|
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||||
|
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||||
|
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||||
|
|
||||||
|
Floorplan::IndoorMap map;
|
||||||
|
map.floors.push_back(f0);
|
||||||
|
map.floors.push_back(f1);
|
||||||
|
map.floors.push_back(f2);
|
||||||
|
|
||||||
|
BeaconModelLogDistCeiling model(&map);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,0)) );
|
||||||
|
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,-1)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,1)) );
|
||||||
|
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,1), Point3(0,0,0)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,-0.01), Point3(0,0,+0.01)) );
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,+0.01), Point3(0,0,-0.01)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,2.99), Point3(0,0,3.01)) );
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,3.01), Point3(0,0,2.99)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,6.99), Point3(0,0,7.01)) );
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,7.01), Point3(0,0,6.99)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,7.00), Point3(0,0,99)) );
|
||||||
|
|
||||||
|
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,7)) );
|
||||||
|
ASSERT_EQ(3, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,8)) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LogDistanceCeilingModelBeacon, numCeilingsFloat) {
|
||||||
|
|
||||||
|
// dummy floorplan
|
||||||
|
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||||
|
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||||
|
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||||
|
|
||||||
|
Floorplan::IndoorMap map;
|
||||||
|
map.floors.push_back(f0);
|
||||||
|
map.floors.push_back(f1);
|
||||||
|
map.floors.push_back(f2);
|
||||||
|
|
||||||
|
BeaconModelLogDistCeiling model(&map);
|
||||||
|
|
||||||
|
const float d = 0.01;
|
||||||
|
|
||||||
|
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,0)), d );
|
||||||
|
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,-1)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,1)), d );
|
||||||
|
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,1), Point3(0,0,0)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,-0.01), Point3(0,0,+0.50)), d );
|
||||||
|
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,+0.50), Point3(0,0,-0.01)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,2.99), Point3(0,0,3.20)), d );
|
||||||
|
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,3.20), Point3(0,0,2.99)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,6.99), Point3(0,0,8.33)), d );
|
||||||
|
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,6.99)), d );
|
||||||
|
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,0.00), Point3(0,0,8.33)), d );
|
||||||
|
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,0.00)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,7.00), Point3(0,0,99)), d );
|
||||||
|
|
||||||
|
ASSERT_NEAR(1, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,7)), d );
|
||||||
|
ASSERT_NEAR(3, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,8)), d );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
7
tests/sensors/beacon/TestProbabilityFree.cpp
Normal file
7
tests/sensors/beacon/TestProbabilityFree.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
|
#include "../../Tests.h"
|
||||||
|
|
||||||
|
//todo
|
||||||
|
|
||||||
|
#endif
|
||||||
200
tests/sensors/imu/TestMotionDetection.cpp
Normal file
200
tests/sensors/imu/TestMotionDetection.cpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
|
#include "../../Tests.h"
|
||||||
|
|
||||||
|
#include "../../../sensors/imu/MotionDetection.h"
|
||||||
|
#include "../../../sensors/imu/TurnDetection.h"
|
||||||
|
#include "../../../sensors/offline/FileReader.h"
|
||||||
|
|
||||||
|
/** visualize the motionAxis */
|
||||||
|
TEST(MotionDetection, motionAxis) {
|
||||||
|
|
||||||
|
MotionDetection md;
|
||||||
|
|
||||||
|
//plot.gp << "set arrow 919 from " << tt.pos.x << "," << tt.pos.y << "," << tt.pos.z << " to "<< tt.pos.x << "," << tt.pos.y << "," << tt.pos.z+1 << "lw 3\n";
|
||||||
|
|
||||||
|
//Walking with smartphone straight and always parallel to motion axis
|
||||||
|
//std::string filename = getDataFile("motion/straight_potrait.csv");
|
||||||
|
|
||||||
|
//straight_landscape_left/right: walking ~40 sec straight and changing every 5 seconds the mode. started with potrait. landscape routed either to left or right.
|
||||||
|
std::string filename = getDataFile("motion/straight_landscape_left.csv");
|
||||||
|
//std::string filename = getDataFile("motion/straight_landscape_right.csv");
|
||||||
|
|
||||||
|
//straight_inturn_landscape: walked straight made a left turn and change the phone to landscape mode during the turn-phase
|
||||||
|
//std::string filename = getDataFile("motion/straight_inturn_landscape.csv");
|
||||||
|
|
||||||
|
//rounds_potrait: walked 3 rounds holding the phone in potrait mode. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_potrait.csv");
|
||||||
|
|
||||||
|
//round_landscape: walked 3 rounds holding the phone in landscape mode. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_landscape.csv");
|
||||||
|
|
||||||
|
//round potrait_to_landscape: walked 1 round with potrait, 1 with landscape and again potrait. the mode was change while walking straight not in a turn. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_potrait_to_landscape.csv");
|
||||||
|
|
||||||
|
//rounds_pocket: had the phone in my jeans pocket screen pointed at my body and the phone was headfirst. pulled it shortly out after 2 rounds and rotated the phone 180° z-wise (screen not showing at me)
|
||||||
|
//std::string filename = getDataFile("motion/rounds_pocket.csv");
|
||||||
|
|
||||||
|
//table_flat: phone was flat on the table and moved slowly forward/backward for 60 cm.
|
||||||
|
//std::string filename = getDataFile("motion/table_flat.csv");
|
||||||
|
|
||||||
|
FileReader fr(filename);
|
||||||
|
|
||||||
|
K::Gnuplot gp;
|
||||||
|
K::GnuplotPlot plot;
|
||||||
|
|
||||||
|
gp << "set xrange[-1:1]\n set yrange[-1:1]\n";
|
||||||
|
|
||||||
|
|
||||||
|
Eigen::Vector2f curVec;
|
||||||
|
float motionAxisAngleRad;
|
||||||
|
Timestamp ts;
|
||||||
|
Timestamp lastTs;
|
||||||
|
|
||||||
|
//calc motion axis
|
||||||
|
for (const FileReader::Entry& e : fr.getEntries()) {
|
||||||
|
|
||||||
|
ts = Timestamp::fromMS(e.ts);
|
||||||
|
|
||||||
|
if (e.type == FileReader::Sensor::LIN_ACC) {
|
||||||
|
md.addLinearAcceleration(ts, fr.getLinearAcceleration()[e.idx].data);
|
||||||
|
|
||||||
|
} else if (e.type == FileReader::Sensor::GRAVITY) {
|
||||||
|
md.addGravity(ts, fr.getGravity()[e.idx].data);
|
||||||
|
curVec = md.getCurrentMotionAxis();
|
||||||
|
motionAxisAngleRad = md.getMotionChangeInRad();
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with the first available timestamp
|
||||||
|
if (lastTs.isZero()) {lastTs = ts;}
|
||||||
|
|
||||||
|
if(ts - lastTs > Timestamp::fromMS(500)) {
|
||||||
|
|
||||||
|
lastTs = ts;
|
||||||
|
|
||||||
|
K::GnuplotPoint2 raw_p1(0, 0);
|
||||||
|
K::GnuplotPoint2 raw_p2(curVec(0,0), curVec(1,0));
|
||||||
|
K::GnuplotPlotElementLines motionLines;
|
||||||
|
motionLines.addSegment(raw_p1, raw_p2);
|
||||||
|
plot.add(&motionLines);
|
||||||
|
|
||||||
|
gp << "set label 111 ' Angle: " << motionAxisAngleRad * 180 / 3.14159 << "' at screen 0.1,0.1\n";
|
||||||
|
|
||||||
|
gp.draw(plot);
|
||||||
|
gp.flush();
|
||||||
|
//usleep(5000*33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//was passiert bei grenzwerten. 90° oder sowas.
|
||||||
|
//wie stabil ist die motion axis eigentlich?
|
||||||
|
//erkenn wir aktuell überhaupt einen turn, wenn wir das telefon drehen?
|
||||||
|
//wie hilft mir die motion achse? über einen faktor? in welchem verhältnis stehen motion axis und heading?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** comparing motionAngle and turnAngle */
|
||||||
|
TEST(MotionDetection, motionAngle) {
|
||||||
|
|
||||||
|
MotionDetection md;
|
||||||
|
TurnDetection td;
|
||||||
|
|
||||||
|
//plot.gp << "set arrow 919 from " << tt.pos.x << "," << tt.pos.y << "," << tt.pos.z << " to "<< tt.pos.x << "," << tt.pos.y << "," << tt.pos.z+1 << "lw 3\n";
|
||||||
|
|
||||||
|
//Walking with smartphone straight and always parallel to motion axis
|
||||||
|
std::string filename = getDataFile("motion/straight_potrait.csv");
|
||||||
|
|
||||||
|
//straight_landscape_left/right: walking ~40 sec straight and changing every 5 seconds the mode. started with potrait. landscape routed either to left or right.
|
||||||
|
//std::string filename = getDataFile("motion/straight_landscape_left.csv");
|
||||||
|
//std::string filename = getDataFile("motion/straight_landscape_right.csv");
|
||||||
|
|
||||||
|
//straight_inturn_landscape: walked straight made a left turn and change the phone to landscape mode during the turn-phase
|
||||||
|
//std::string filename = getDataFile("motion/straight_inturn_landscape.csv");
|
||||||
|
|
||||||
|
//rounds_potrait: walked 3 rounds holding the phone in potrait mode. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_potrait.csv");
|
||||||
|
|
||||||
|
//round_landscape: walked 3 rounds holding the phone in landscape mode. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_landscape.csv");
|
||||||
|
|
||||||
|
//round potrait_to_landscape: walked 1 round with potrait, 1 with landscape and again potrait. the mode was change while walking straight not in a turn. always making left turns.
|
||||||
|
//std::string filename = getDataFile("motion/rounds_potrait_to_landscape.csv");
|
||||||
|
|
||||||
|
//rounds_pocket: had the phone in my jeans pocket screen pointed at my body and the phone was headfirst. pulled it shortly out after 2 rounds and rotated the phone 180° z-wise (screen not showing at me)
|
||||||
|
//std::string filename = getDataFile("motion/rounds_pocket.csv");
|
||||||
|
|
||||||
|
//table_flat: phone was flat on the table and moved slowly forward/backward for 60 cm.
|
||||||
|
//std::string filename = getDataFile("motion/table_flat.csv");
|
||||||
|
|
||||||
|
FileReader fr(filename);
|
||||||
|
Timestamp ts;
|
||||||
|
|
||||||
|
//save for later plotting
|
||||||
|
std::vector<float> delta_motionAngles;
|
||||||
|
std::vector<float> delta_turnAngles;
|
||||||
|
|
||||||
|
//calc motion axis
|
||||||
|
for (const FileReader::Entry& e : fr.getEntries()) {
|
||||||
|
|
||||||
|
ts = Timestamp::fromMS(e.ts);
|
||||||
|
|
||||||
|
if (e.type == FileReader::Sensor::LIN_ACC) {
|
||||||
|
md.addLinearAcceleration(ts, fr.getLinearAcceleration()[e.idx].data);
|
||||||
|
|
||||||
|
} else if (e.type == FileReader::Sensor::GRAVITY) {
|
||||||
|
md.addGravity(ts, fr.getGravity()[e.idx].data);
|
||||||
|
delta_motionAngles.push_back(md.getMotionChangeInRad());
|
||||||
|
|
||||||
|
} else if (e.type == FileReader::Sensor::ACC) {
|
||||||
|
const FileReader::TS<AccelerometerData>& _acc = fr.getAccelerometer()[e.idx];
|
||||||
|
td.addAccelerometer(ts, _acc.data);
|
||||||
|
|
||||||
|
} else if (e.type == FileReader::Sensor::GYRO) {
|
||||||
|
const FileReader::TS<GyroscopeData>& _gyr = fr.getGyroscope()[e.idx];
|
||||||
|
delta_turnAngles.push_back(td.addGyroscope(ts, _gyr.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw motion
|
||||||
|
static K::Gnuplot gpMotion;
|
||||||
|
K::GnuplotPlot plotMotion;
|
||||||
|
K::GnuplotPlotElementLines motionLines;
|
||||||
|
|
||||||
|
for(int i = 0; i < delta_motionAngles.size() - 1; ++i){
|
||||||
|
|
||||||
|
K::GnuplotPoint2 raw_p1(i, delta_motionAngles[i]);
|
||||||
|
K::GnuplotPoint2 raw_p2(i + 1, delta_motionAngles[i+1]);
|
||||||
|
motionLines.addSegment(raw_p1, raw_p2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gpMotion << "set title 'Motion Detection'\n";
|
||||||
|
plotMotion.add(&motionLines);
|
||||||
|
gpMotion.draw(plotMotion);
|
||||||
|
gpMotion.flush();
|
||||||
|
|
||||||
|
|
||||||
|
//draw rotation
|
||||||
|
static K::Gnuplot gpTurn;
|
||||||
|
K::GnuplotPlot plotTurn;
|
||||||
|
K::GnuplotPlotElementLines turnLines;
|
||||||
|
|
||||||
|
for(int i = 0; i < delta_turnAngles.size() - 1; ++i){
|
||||||
|
|
||||||
|
K::GnuplotPoint2 raw_p1(i, delta_turnAngles[i]);
|
||||||
|
K::GnuplotPoint2 raw_p2(i + 1, delta_turnAngles[i+1]);
|
||||||
|
turnLines.addSegment(raw_p1, raw_p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpTurn << "set title 'Turn Detection'\n";
|
||||||
|
plotTurn.add(&turnLines);
|
||||||
|
gpTurn.draw(plotTurn);
|
||||||
|
gpTurn.flush();
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "../../../sensors/pressure/RelativePressure.h"
|
#include "../../../sensors/pressure/RelativePressure.h"
|
||||||
#include "../../../sensors/pressure/PressureTendence.h"
|
#include "../../../sensors/pressure/PressureTendence.h"
|
||||||
#include "../../../sensors/pressure/ActivityButterPressure.h"
|
#include "../../../sensors/pressure/ActivityButterPressure.h"
|
||||||
|
#include "../../../sensors/pressure/ActivityButterPressurePercent.h"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
@@ -78,7 +79,7 @@ TEST(Barometer, LIVE_tendence) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(1000);
|
sleep(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ TEST(Barometer, LIVE_tendence2) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(1000);
|
sleep(1);
|
||||||
|
|
||||||
|
|
||||||
// tendence must be clear and smaller than the sigma
|
// tendence must be clear and smaller than the sigma
|
||||||
@@ -130,6 +131,9 @@ TEST(Barometer, Activity) {
|
|||||||
std::string filename = getDataFile("barometer/baro1.dat");
|
std::string filename = getDataFile("barometer/baro1.dat");
|
||||||
std::ifstream infile(filename);
|
std::ifstream infile(filename);
|
||||||
|
|
||||||
|
std::vector<ActivityButterPressure::History> actHist;
|
||||||
|
std::vector<ActivityButterPressure::History> rawHist;
|
||||||
|
|
||||||
while (std::getline(infile, line))
|
while (std::getline(infile, line))
|
||||||
{
|
{
|
||||||
std::istringstream iss(line);
|
std::istringstream iss(line);
|
||||||
@@ -138,34 +142,101 @@ TEST(Barometer, Activity) {
|
|||||||
|
|
||||||
while (iss >> ts >> value) {
|
while (iss >> ts >> value) {
|
||||||
ActivityButterPressure::Activity currentAct = act.add(Timestamp::fromMS(ts), BarometerData(value));
|
ActivityButterPressure::Activity currentAct = act.add(Timestamp::fromMS(ts), BarometerData(value));
|
||||||
|
rawHist.push_back(ActivityButterPressure::History(Timestamp::fromMS(ts), BarometerData(value)));
|
||||||
|
actHist.push_back(ActivityButterPressure::History(Timestamp::fromMS(ts), BarometerData(currentAct)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> sum = act.getSumHistory();
|
|
||||||
std::vector<float> interpolated = act.getInterpolatedHistory();
|
|
||||||
std::vector<float> raw = act.getSensorHistory();
|
|
||||||
std::vector<float> butter = act.getOutputHistory();
|
|
||||||
std::vector<ActivityButterPressure::History> actHist = act.getActHistory();
|
|
||||||
|
|
||||||
K::Gnuplot gp;
|
K::Gnuplot gp;
|
||||||
|
K::Gnuplot gpRaw;
|
||||||
K::GnuplotPlot plot;
|
K::GnuplotPlot plot;
|
||||||
|
K::GnuplotPlot plotRaw;
|
||||||
K::GnuplotPlotElementLines rawLines;
|
K::GnuplotPlotElementLines rawLines;
|
||||||
|
K::GnuplotPlotElementLines resultLines;
|
||||||
|
|
||||||
for(int i=0; i < actHist.size()-1; ++i){
|
for(int i=0; i < actHist.size()-1; ++i){
|
||||||
|
|
||||||
|
//raw
|
||||||
|
K::GnuplotPoint2 raw_p1(rawHist[i].ts.sec(), rawHist[i].data.hPa);
|
||||||
|
K::GnuplotPoint2 raw_p2(rawHist[i+1].ts.sec(), rawHist[i+1].data.hPa);
|
||||||
|
|
||||||
|
rawLines.addSegment(raw_p1, raw_p2);
|
||||||
|
|
||||||
|
//results
|
||||||
K::GnuplotPoint2 input_p1(actHist[i].ts.sec(), actHist[i].data.hPa);
|
K::GnuplotPoint2 input_p1(actHist[i].ts.sec(), actHist[i].data.hPa);
|
||||||
K::GnuplotPoint2 input_p2(actHist[i+1].ts.sec(), actHist[i+1].data.hPa);
|
K::GnuplotPoint2 input_p2(actHist[i+1].ts.sec(), actHist[i+1].data.hPa);
|
||||||
|
|
||||||
rawLines.addSegment(input_p1, input_p2);
|
resultLines.addSegment(input_p1, input_p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
plot.add(&rawLines);
|
plotRaw.add(&rawLines);
|
||||||
|
plot.add(&resultLines);
|
||||||
|
|
||||||
gp.draw(plot);
|
gp.draw(plot);
|
||||||
gp.flush();
|
gp.flush();
|
||||||
|
|
||||||
sleep(1);
|
gpRaw.draw(plotRaw);
|
||||||
|
gpRaw.flush();
|
||||||
|
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Barometer, ActivityPercent) {
|
||||||
|
|
||||||
|
ActivityButterPressurePercent act;
|
||||||
|
|
||||||
|
//read file
|
||||||
|
std::string line;
|
||||||
|
std::string filename = getDataFile("barometer/baro1.dat");
|
||||||
|
std::ifstream infile(filename);
|
||||||
|
|
||||||
|
std::vector<ActivityButterPressurePercent::ActivityProbabilities> actHist;
|
||||||
|
std::vector<double> rawHist;
|
||||||
|
|
||||||
|
while (std::getline(infile, line))
|
||||||
|
{
|
||||||
|
std::istringstream iss(line);
|
||||||
|
int ts;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
while (iss >> ts >> value) {
|
||||||
|
ActivityButterPressurePercent::ActivityProbabilities activity = act.add(Timestamp::fromMS(ts), BarometerData(value));
|
||||||
|
rawHist.push_back(value);
|
||||||
|
actHist.push_back(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
K::Gnuplot gp;
|
||||||
|
K::Gnuplot gpRaw;
|
||||||
|
K::GnuplotPlot plot;
|
||||||
|
K::GnuplotPlot plotRaw;
|
||||||
|
K::GnuplotPlotElementLines rawLines;
|
||||||
|
K::GnuplotPlotElementLines resultLines;
|
||||||
|
|
||||||
|
for(int i=0; i < actHist.size()-1; ++i){
|
||||||
|
|
||||||
|
K::GnuplotPoint2 raw_p1(i, rawHist[i]);
|
||||||
|
K::GnuplotPoint2 raw_p2(i+1, rawHist[i+1]);
|
||||||
|
|
||||||
|
rawLines.addSegment(raw_p1, raw_p2);
|
||||||
|
|
||||||
|
K::GnuplotPoint2 input_p1(i, actHist[i].elevatorDown);
|
||||||
|
K::GnuplotPoint2 input_p2(i+1, actHist[i+1].elevatorDown);
|
||||||
|
|
||||||
|
resultLines.addSegment(input_p1, input_p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
plotRaw.add(&rawLines);
|
||||||
|
plot.add(&resultLines);
|
||||||
|
|
||||||
|
gp.draw(plot);
|
||||||
|
gp.flush();
|
||||||
|
|
||||||
|
gpRaw.draw(plotRaw);
|
||||||
|
gpRaw.flush();
|
||||||
|
|
||||||
|
sleep(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user