many changes and updates

- changed the wifi-estimation api
- adjusted test-cases
- worked on grid-bulding and grid-importance
- new walking modules
- fixed some minor issues
This commit is contained in:
2016-08-29 19:02:32 +02:00
parent a2c9e575a2
commit a203305628
23 changed files with 505 additions and 251 deletions

View File

@@ -2,6 +2,11 @@
#define ASSERTIONS_H
#include "Exception.h"
#include <sstream>
// for GTEST Testcases
#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
/**
* this file provides assertion methods that may be enabled to trace
@@ -62,7 +67,8 @@ namespace Assert {
}
template <typename T, typename STR> static inline void isBetween(const T v, const T min, const T max, const STR err) {
if (v < min || v > max) {doThrow(err);}
std::stringstream ss; ss << "\n[" << min << ":" << max << "] but is " << v << "\n";
if (v < min || v > max) {doThrow(err+ss.str());}
}
}

View File

@@ -54,6 +54,15 @@ namespace Floorplan {
Point3 p4;
Quad3(const Point3 p1, const Point3 p2, const Point3 p3, const Point3 p4) : p1(p1), p2(p2), p3(p3), p4(p4) {;}
Quad3 operator * (const float v) const {return Quad3(p1*v, p2*v, p3*v, p4*v);}
const Point3& operator [] (const int idx) const {
switch (idx) {
case 0: return p1;
case 1: return p2;
case 2: return p3;
case 3: return p4;
default: throw Exception("out of bounds");
}
}
};
/** additional type-info for obstacles */

View File

@@ -221,13 +221,14 @@ public:
Assert::isBetween((int32_t)p.y_cm, -max, +max, "y out of bounds");
Assert::isBetween((int32_t)p.z_cm, -max, +max, "z out of bounds");
// shift by half of the allowed width of 20 bit to allow negative regions
// shift by half of the allowed width of 20 bit to allow negative regions:
// -> 19 bit positive and 19 bit negative
const uint64_t center = 1 << 19;
// build
const uint64_t x = std::round((p.x_cm+center) / (float)gridSize_cm);
const uint64_t y = std::round((p.y_cm+center) / (float)gridSize_cm);
const uint64_t z = std::round((p.z_cm+center) / (float)gridSize_cm * 5); // z is usually much lower and not always aligned -> allow more room for hashes
const uint64_t x = center + (int64_t) std::round((p.x_cm) / (float)gridSize_cm);
const uint64_t y = center + (int64_t) std::round((p.y_cm) / (float)gridSize_cm);
const uint64_t z = center + (int64_t) std::round((p.z_cm) / (float)gridSize_cm * 5); // z is usually much lower and not always aligned -> allow more room for hashes
return (z << 40) | (y << 20) | (x << 0);
@@ -256,6 +257,11 @@ public:
disconnectUniDir(n2, n1);
}
/** remove the connection from n1 to n2 (not the other way round!) */
void disconnectUniDir(const int idx1, const int idx2) {
disconnectUniDir(nodes[idx1], nodes[idx2]);
}
/** remove the connection from n1 to n2 (not the other way round!) */
void disconnectUniDir(T& n1, T& n2) {
for (int n = 0; n < n1._numNeighbors; ++n) {
@@ -291,7 +297,11 @@ public:
// disconnect from all neighbors
while (node._numNeighbors) {
// by removing one neighbor, all others are shifted to close the gap within the array
// its therefor ok to always delete array index [0]
disconnectBiDir(node._idx, node._neighbors[0]);
}
// remove from hash-list

View File

@@ -54,166 +54,63 @@ public:
// the number of neighbors to use
static constexpr int numNeighbors = 12;
// create list of all doors
// create list of all door-nodes
std::vector<T> doors;
// create list of all stair-nodes
std::vector<T> stairs;
// process each node
for (T& n1 : g) {
// is the current node a door?
//if (isDoor(n1, neighbors)) {doors.push_back(n1);} // OLD
if (n1.getType() == GridNode::TYPE_DOOR) {doors.push_back(n1);} // NEW!
// favor stairs just like doors
//if (isStaircase(g, n1)) {doors.push_back(n1);} // OLD
if (n1.getType() == GridNode::TYPE_STAIR) {doors.push_back(n1);} // NEW
switch(n1.getType()) {
case GridNode::TYPE_DOOR: doors.push_back(n1); break;
case GridNode::TYPE_STAIR: stairs.push_back(n1); break;
}
}
// KNN for doors
KNNArray<std::vector<T>> knnArrDoors(doors);
KNN<KNNArray<std::vector<T>>, 3> knnDoors(knnArrDoors);
// KNN for stairs
KNNArray<std::vector<T>> knnArrStairs(stairs);
KNN<KNNArray<std::vector<T>>, 3> knnStairs(knnArrStairs);
// probability adjustments
Distribution::Normal<float> avoidWalls(0.0, 0.35);
Distribution::Normal<float> favorDoors(0.0f, 0.5f);
Distribution::Normal<float> favorStairs(0.0f, 3.5f);
// process each node again
for (T& n1 : g) {
// skip nodes on other than the requested floor-level
//if (n1.z_cm != z_cm) {continue;}
// get the 10 nearest neighbors and their distance
size_t indices[numNeighbors];
float squaredDist[numNeighbors];
float point[3] = {n1.x_cm, n1.y_cm, n1.z_cm};
knn.get(point, numNeighbors, indices, squaredDist);
// get the neighbors
std::vector<T*> neighbors;
for (int i = 0; i < numNeighbors; ++i) {
neighbors.push_back(&inv[indices[i]]);
}
n1.navImportance = 1.0f;
//if (n1.getType() == GridNode::TYPE_FLOOR) {
// get the distance to the nearest door
const float distToWall_m = Units::cmToM(std::sqrt(squaredDist[0]) + g.getGridSize_cm());
// get the distance to the nearest wall
const float distToWall_m = Units::cmToM(knn.getNearestDistance( {n1.x_cm, n1.y_cm, n1.z_cm} ));
// get the distance to the nearest door
const float distToDoor_m = Units::cmToM(knnDoors.getNearestDistance( {n1.x_cm, n1.y_cm, n1.z_cm} ));
n1.navImportance =
1 +
getWallImportance( distToWall_m ) +
favorDoors.getProbability(distToDoor_m) * 1.5f;
// get the distance to the nearest stair
const float distToStair_m = Units::cmToM(knnStairs.getNearestDistance( {n1.x_cm, n1.y_cm, n1.z_cm} ));
const bool useNormal = (distToWall_m < distToDoor_m && distToWall_m < distToStair_m);
//}
//addDoor(n1, neighbors);
// final probability
n1.navImportance = 1.0f;
n1.navImportance += favorDoors.getProbability(distToDoor_m) * 1.25f;
n1.navImportance += favorStairs.getProbability(distToStair_m) * 3.5f;
// importance for this node (based on the distance from the next door)
//n1.navImportance += favorDoors.getProbability(dist_m) * 0.30;
if (useNormal) {
n1.navImportance -= avoidWalls.getProbability(distToWall_m);
}
//n1.navImportance = (dist_m < 0.2) ? (1) : (0.5);
}
}
/** is the given node connected to a staircase? */
template <typename T> static bool isStaircase(Grid<T>& g, T& node) {
return node.getType() == GridNode::TYPE_STAIR;
// // if this node has a neighbor with a different z, this is a stair
// for (T& neighbor : g.neighbors(node)) {
// if (neighbor.z_cm != node.z_cm) {return true;}
// }
// return false;
}
// /** is the given node (and its inverted neighbors) a door? */
// template <typename T> static bool isDoor( T& nSrc, std::vector<T*> neighbors ) {
// if (nSrc.getType() != GridNode::TYPE_FLOOR) {return false;}
// MiniMat2 m1;
//// MiniMat2 m2;
// Point3 center = nSrc;
// // calculate the centroid of the nSrc's nearest-neighbors
// Point3 centroid(0,0,0);
// for (const T* n : neighbors) {
// centroid = centroid + (Point3)*n;
// }
// centroid /= neighbors.size();
// // if nSrc is too far from the centroid, this does not make sense
// if ((centroid-center).length() > 40) {return false;}
// // build covariance of the nearest-neighbors
// int used = 0;
// for (const T* n : neighbors) {
// const Point3 d1 = (Point3)*n - centroid;
// if (d1.length() > 100) {continue;} // radius search
// m1.addSquared(d1.x, d1.y);
//// const Point3 d2 = (Point3)*n - center;
//// if (d2.length() > 100) {continue;} // radius search
//// m2.addSquared(d2.x, d2.y);
// ++used;
// }
// // we need at least two points for the covariance
// if (used < 6) {return false;}
// // check eigenvalues
// MiniMat2::EV ev1 = m1.getEigenvalues();
//// MiniMat2::EV ev2 = m2.getEigenvalues();
// // ensure e1 > e2
// if (ev1.e1 < ev1.e2) {std::swap(ev1.e1, ev1.e2);}
//// if (ev2.e1 < ev2.e2) {std::swap(ev2.e1, ev2.e2);}
// // door?
// const float ratio1 = (ev1.e2/ev1.e1);
//// const float ratio2 = (ev2.e2/ev2.e1);
//// const float ratio3 = std::max(ratio1, ratio2) / std::min(ratio1, ratio2);
// return (ratio1 < 0.30 && ratio1 > 0.05) ;
// }
/** get the importance of the given node depending on its nearest wall */
static float getWallImportance(float dist_m) {
// avoid sticking too close to walls (unlikely)
static Distribution::Normal<float> avoidWalls(0.0, 0.35);
// favour walking near walls (likely)
//static Distribution::Normal<float> stickToWalls(0.9, 0.7);
// favour walking far away (likely)
//static Distribution::Normal<float> farAway(2.2, 0.5);
//if (dist_m > 2.0) {dist_m = 2.0;}
// overall importance
// return - avoidWalls.getProbability(dist_m) * 0.30 // avoid walls
// + stickToWalls.getProbability(dist_m) * 0.15 // walk near walls
// + farAway.getProbability(dist_m) * 0.15 // walk in the middle
return - avoidWalls.getProbability(dist_m) // avoid walls
//+ stickToWalls.getProbability(dist_m) // walk near walls
//+ farAway.getProbability(dist_m) // walk in the middle
;
}
};

View File

@@ -8,7 +8,7 @@
#include "Helper.h"
#include "../../../floorplan/v2/Floorplan.h"
#include <fstream>
@@ -23,6 +23,10 @@ private:
Helper<T> helper;
std::ofstream outStairs;
std::ofstream outDelete;
private:
// struct Entry {
@@ -68,10 +72,14 @@ public:
/** ctor */
Stairs(Grid<T>& grid) : grid(grid), helper(grid) {
;
outStairs.open("/tmp/quads.dat");
outDelete.open("/tmp/delete.dat");
}
~Stairs() {
outStairs.close();
outDelete.close();
}
@@ -87,6 +95,7 @@ public:
const std::vector<Floorplan::StairPart> parts = stair->getParts();
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
for (int i = 0; i < (int)parts.size(); ++i) {
//const Floorplan::StairPart& part = parts[i];
@@ -97,6 +106,13 @@ public:
const Point3 p3 = quad.p3;
const Point3 p4 = quad.p4;
outStairs << p1.x << " " << p1.y << " " << p1.z << "\n";
outStairs << p2.x << " " << p2.y << " " << p2.z << "\n";
outStairs << p3.x << " " << p3.y << " " << p3.z << "\n";
outStairs << p4.x << " " << p4.y << " " << p4.z << "\n";
outStairs << p1.x << " " << p1.y << " " << p1.z << "\n\n\n";
HelperPoly poly; poly.add(p1.xy()); poly.add(p2.xy()); poly.add(p3.xy()); poly.add(p4.xy());
const int x1 = helper.align(poly.bbox_cm.getMin().x); // ALIGNF?
@@ -111,6 +127,9 @@ public:
for (int y = y1; y <= y2; y+=gs_cm) {
if (!poly.contains( Point2(x,y) )) {continue;}
points.push_back(XYZ(x,y,-1));
if (y < 500) {
int i = 0; (void) i;
}
}
}
@@ -185,6 +204,7 @@ public:
// keep a list of all vertices below stairwells and remove them hereafter
std::vector<T*> toDelete;
// add all stair nodes or replace them with already existing ones
for (Intermediate& iNode : stairNodes) {
@@ -192,7 +212,9 @@ public:
const float minDiff = gs_cm * 0.49;
// nearer than maxDiff? -> delete as this one is unreachable below the stair
const float maxDiff = 150;
// to prevent errors (e.g. a platform at 1.0 meter) we use 0.98 meter as deleting threshold
// otherwise we get problems in some places
const float maxDiff = 98;
// distance between the stair node and the floor above / below
const float zDiff1 = std::abs(iNode.node.z_cm - 100*floor->getStartingZ());
@@ -223,11 +245,19 @@ public:
// remove nodes directly below the stair
} else if (zDiff1 < maxDiff) {
T* n2 = (T*) grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getStartingZ()));
outDelete << n2->x_cm << " " << n2->y_cm << " " << n2->z_cm << "\n";
if (n2->y_cm < 500) {
int i = 0; (void) i;
}
if (n2) {toDelete.push_back(n2);}
// remove nodes directly above the stair
} else if (zDiff2 < maxDiff) {
T* n2 = (T*) grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getEndingZ()));
outDelete << n2->x_cm << " " << n2->y_cm << " " << n2->z_cm << "\n";
if (n2->y_cm < 500) {
int i = 0; (void) i;
}
if (n2) {toDelete.push_back(n2);}
}

View File

@@ -0,0 +1,54 @@
#ifndef WALKMODULEFAVORZ_H
#define WALKMODULEFAVORZ_H
#include "WalkModule.h"
#include "WalkStateHeading.h"
#include "../../../../geo/Heading.h"
#include "../../../../math/Distributions.h"
/** favor z-transitions */
template <typename Node, typename WalkState> class WalkModuleFavorZ : public WalkModule<Node, WalkState> {
public:
/** ctor */
WalkModuleFavorZ() {
;
}
virtual void updateBefore(WalkState& state) override {
(void) state;
}
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;
if (curNode.z_cm != potentialNode.z_cm) {
return 8;
} else {
return 1;
}
}
};
#endif // WALKMODULEFAVORZ_H

View File

@@ -26,14 +26,14 @@ private:
public:
/** ctor 3.0 should be OK! */
WalkModuleHeadingControl(Control* ctrl) : dist(Distribution::VonMises<double>(0.0f, 1.5).getLUT()), draw(dist.getDrawList()), ctrl(ctrl) {
WalkModuleHeadingControl(Control* ctrl) : dist(Distribution::VonMises<double>(0.0f, 2.0).getLUT()), draw(dist.getDrawList()), ctrl(ctrl) {
;
}
virtual void updateBefore(WalkState& state) override {
const float var = draw.get() * 0.15;//0.05;
const float var = draw.get() * 0.20;//0.05;
//const float var = 0;
state.startHeading += ctrl->turnAngle + var;

View File

@@ -44,8 +44,8 @@ public:
(void) curNode;
const double prob = potentialNode.getNavImportance();
return std::pow(prob, 10);
//return prob;
//return std::pow(prob, 10);
return prob;
}

View File

@@ -15,7 +15,23 @@ int main(int argc, char** argv) {
#ifdef WITH_TESTS
::testing::InitGoogleTest(&argc, argv);
::testing::GTEST_FLAG(filter) = "*huge*";
// skip all tests starting with LIVE_
::testing::GTEST_FLAG(filter) = "-*.LIVE_*";
::testing::GTEST_FLAG(filter) = "*Grid.*";
//::testing::GTEST_FLAG(filter) = "*Dijkstra.*";
//::testing::GTEST_FLAG(filter) = "*LogDistanceCeilingModel*";
//::testing::GTEST_FLAG(filter) = "*Barometer*";
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
//::testing::GTEST_FLAG(filter) = "Heading*";
//::testing::GTEST_FLAG(filter) = "*WiFiVAPGrouper*";
//::testing::GTEST_FLAG(filter) = "*Offline*";
//::testing::GTEST_FLAG(filter) = "*Walk*";
return RUN_ALL_TESTS();

View File

@@ -43,13 +43,13 @@ public:
* as nodes only provide a limited number of rssi-entries,
* store only the strongest ones.
*/
template <typename Node> static void estimate(Grid<Node>& grid, WiFiModel& mdl, const std::vector<LocatedAccessPoint> aps) {
template <typename Node> static void estimate(Grid<Node>& grid, WiFiModel& mdl, const std::vector<AccessPoint> aps) {
// sanity checks
Assert::isTrue(Node::getMapAPs().empty(), "there are already some processed APs available!");
// attach the access-points to the shared node-vector
for (const LocatedAccessPoint& ap : aps) {
for (const AccessPoint& ap : aps) {
Node::getMapAPs().push_back(ap);
}
@@ -63,7 +63,7 @@ public:
for (int apIdx = 0; apIdx < (int) aps.size(); ++apIdx) {
// estimate the signal-strength
const float rssi = mdl.getRSSI(aps[apIdx], n.inMeter());
const float rssi = mdl.getRSSI(aps[apIdx].getMAC(), n.inMeter());
// keep it
nodeAPs.push_back(WiFiGridNodeAP(apIdx, rssi));

View File

@@ -12,7 +12,7 @@ struct WiFiGridNodeAP {
private:
static const int UNUSED_IDX = 255;
static const uint8_t UNUSED_IDX = 255;
private:
@@ -25,10 +25,14 @@ private:
public:
/** empty ctor */
WiFiGridNodeAP() : idx(UNUSED_IDX), rssi(0) {;}
WiFiGridNodeAP() : idx(UNUSED_IDX), rssi(0) {
int i = 0; (void) i;
}
/** ctor */
WiFiGridNodeAP(const int idx, const float rssi) : idx(idx), rssi(stretch(rssi)) {;}
WiFiGridNodeAP(const int idx, const float rssi) : idx(idx), rssi(stretch(rssi)) {
int i = 0; (void) i;
}
/** is this entry valid/used? */
@@ -70,13 +74,14 @@ private:
*/
template <int maxAccessPoints> struct WiFiGridNode {
/** contains the 10 strongest APs measureable at this position */
WiFiGridNodeAP strongestAPs[maxAccessPoints];
/** ctor */
WiFiGridNode() {;}
explicit WiFiGridNode() : strongestAPs() {
;
}
/** get the maximum number of APs each node is able to store */

View File

@@ -8,8 +8,8 @@
#include <unordered_map>
/**
* compare WiFi-Measurements within LIVE (exact) predictions
* just based on the distance to the access-point
* compare WiFi-Measurements within predictions of a given model.
* predictions are just based on the distance to the access-point.
*/
class WiFiObserverFree : public WiFiProbability {
@@ -19,17 +19,16 @@ private:
const float sigmaPerSecond = 1.5f;
/** the RSSI prediction model */
WiFiModel& model;
std::unordered_map<MACAddress, LocatedAccessPoint> aps;
/** the map's floorplan */
Floorplan::IndoorMap* map;
public:
WiFiObserverFree(const float sigma, WiFiModel& model, const std::vector<LocatedAccessPoint>& aps) : sigma(sigma), model(model) {
for (const LocatedAccessPoint& ap : aps) {
this->aps.insert(std::pair<MACAddress, LocatedAccessPoint>(ap.getMAC(), ap));
}
WiFiObserverFree(const float sigma, WiFiModel& model) : sigma(sigma), model(model) {
}
@@ -38,18 +37,17 @@ public:
double prob = 1.0;
// process each measured AP
for (const WiFiMeasurement& entry : obs.entries) {
auto it = aps.find(entry.getAP().getMAC());
// get the model's RSSI (if possible!)
const float modelRSSI = model.getRSSI(entry.getAP().getMAC(), pos);
// AP is unknown
if (it == aps.end()) {continue;}
// NaN? -> AP not known to the model -> skip
if (modelRSSI != modelRSSI) {continue;}
// get the AP
const LocatedAccessPoint& ap = it->second;
// model and scan rssi
const float modelRSSI = model.getRSSI(ap, pos);
// the scan's RSSI
const float scanRSSI = entry.getRSSI();
// the measurement's age

View File

@@ -4,14 +4,25 @@
#include "../LocatedAccessPoint.h"
/**
* interface for signal-strength prediction models
* 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 WiFiModel {
public:
/** get the given access-point's RSSI at the provided location */
virtual float getRSSI(const LocatedAccessPoint& ap, const Point3 p) = 0;
// /** get the given access-point's RSSI at the provided location */
// virtual float getRSSI(const LocatedAccessPoint& ap, const Point3 p) = 0;
/**
* get the RSSI expected at the given location (in meter)
* for an AP identified by the given MAC.
*
* if the model can not predict the RSSI for an AP, it returns NaN!
*/
virtual float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const = 0;
};

View File

@@ -9,21 +9,63 @@
*/
class WiFiModelLogDist : public WiFiModel {
public:
/** parameters describing one AP 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:
float txp;
float exp;
/** map of all APs (and their parameters) known to the model */
std::unordered_map<MACAddress, APEntry> accessPoints;
public:
/** ctor */
WiFiModelLogDist(const float txp, const float exp) : txp(txp), exp(exp) {
WiFiModelLogDist() {
;
}
/** get the given access-point's RSSI at the provided location */
float getRSSI(const LocatedAccessPoint& ap, const Point3 p) override {
return LogDistanceModel::distanceToRssi(txp, exp, ap.getDistance(p));
/** make the given AP (and its parameters) known to the model */
void addAP(const MACAddress& accessPoint, 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
accessPoints.insert( std::pair<MACAddress, APEntry>(accessPoint, params) );
}
virtual float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const override {
// try to get the corresponding parameters
const auto it = accessPoints.find(accessPoint);
// AP unknown? -> NAN
if (it == accessPoints.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);
// done
return rssiLOS;
}
};

View File

@@ -3,6 +3,7 @@
#include "../../../floorplan/v2/Floorplan.h"
#include "../../../Assertions.h"
#include "WiFiModel.h"
#include "LogDistanceModel.h"
@@ -12,11 +13,26 @@
*/
class WiFiModelLogDistCeiling : public WiFiModel {
public:
/** parameters describing one AP 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)
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:
float txp; // sending power (-40)
float exp; // path-loss-exponent (~2.0 - 4.0)
float waf; // attenuation per ceiling/floor (~-8.0)
/** map of all APs (and their parameters) known to the model */
std::unordered_map<MACAddress, APEntry> accessPoints;
/** position (height) of all ceilings (in meter) */
std::vector<float> ceilingsAtHeight_m;
@@ -24,9 +40,7 @@ private:
public:
/** ctor */
WiFiModelLogDistCeiling(const float txp, const float exp, const float waf, const Floorplan::IndoorMap* map) : txp(txp), exp(exp), waf(waf) {
Assert::isTrue(waf <= 0, "WAF must be a negative number!");
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) {
// position of all ceilings
for (Floorplan::Floor* f : map->floors) {
@@ -35,23 +49,56 @@ public:
}
/** get the given access-point's RSSI at the provided location */
float getRSSI(const LocatedAccessPoint& ap, const Point3 p) override {
const int numCeilings = numCeilingsBetween(ap.z, p.z);
const float rssi = LogDistanceModel::distanceToRssi(txp, exp, ap.getDistance(p));
return rssi + (numCeilings * waf);
/** make the given AP (and its parameters) known to the model */
void addAP(const MACAddress& accessPoint, const APEntry& params) {
// sanity check
Assert::isBetween(params.waf, -99.0f, 0.0f, "WAF out of bounds [-99:0]");
Assert::isBetween(params.txp, -50.0f, -30.0f, "TXP out of bounds [-50:-30]");
Assert::isBetween(params.exp, 1.0f, 4.0f, "EXP out of bounds [1:4]");
// add
accessPoints.insert( std::pair<MACAddress, APEntry>(accessPoint, params) );
}
private:
float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const override {
// try to get the corresponding parameters
const auto it = accessPoints.find(accessPoint);
// AP unknown? -> NAN
if (it == accessPoints.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(LogDistanceCeilingModel, numCeilings);
/** get the number of ceilings between z1 and z2 */
int numCeilingsBetween(const float z1, const float z2) const {
int numCeilingsBetween(const Point3 pos1, const Point3 pos2) const {
int cnt = 0;
const float zMin = std::min(z1, z2);
const float zMax = std::max(z1, z2);
const float zMin = std::min(pos1.z, pos2.z);
const float zMax = std::max(pos1.z, pos2.z);
for (float z : ceilingsAtHeight_m) {
for (const float z : ceilingsAtHeight_m) {
if (zMin < z && zMax > z) {++cnt;}
}

View File

@@ -39,7 +39,7 @@ struct MyState : public WalkState, public WalkStateRelativePressure {
TEST(GridWalk2RelPressure, walkHeading) {
TEST(GridWalk2RelPressure, LIVE_walkHeading) {
Grid<MyNode> grid(20);

View File

@@ -32,7 +32,9 @@ TEST(Grid, add) {
ASSERT_THROW(grid.add(GP(10,10,10)), std::exception);
ASSERT_THROW(grid.add(GP(10,20,20)), std::exception);
ASSERT_THROW(grid.add(GP(20,10,20)), std::exception);
ASSERT_THROW(grid.add(GP(20,20,10)), std::exception);
//ASSERT_THROW(grid.add(GP(20,20,11)), std::exception); // unaligned z is OK
// aligned -> OK
ASSERT_EQ(1, grid.add(GP(20,20,20)));
// access
@@ -127,13 +129,64 @@ TEST(Grid, uid) {
Grid<GP> grid(20);
GP gp(20,40,60);
uint64_t uid = grid.getUID(gp);
GP gp1(+20, +40, +60);
GP gp2(-20, -40, -60);
const uint64_t uid1 = grid.getUID(gp1);
const uint64_t uid2 = grid.getUID(gp2);
// must be different
ASSERT_NE(uid1, uid2);
const uint64_t center = 1 << 19;
const int mask = (1 << 20) - 1; // 20-bit mask
ASSERT_EQ(3, uid >> 40 & mask); // x
ASSERT_EQ(2, uid >> 20 & mask); // y
ASSERT_EQ(1, uid >> 0 & mask); // z
ASSERT_EQ(center+60/20*5, (uid1) >> 40 & mask); // z (z UID has a higher resolution: * 5
ASSERT_EQ(center+40/20, (uid1) >> 20 & mask); // y
ASSERT_EQ(center+20/20, (uid1) >> 0 & mask); // x
ASSERT_EQ(center-60/20*5, (uid2) >> 40 & mask); // z (z UID has a higher resolution: * 5
ASSERT_EQ(center-40/20, (uid2) >> 20 & mask); // y
ASSERT_EQ(center-20/20, (uid2) >> 0 & mask); // x
}
TEST(Grid, unconnect) {
Grid<GP> grid(1);
int idx1 = grid.add(GP(0,0,0));
int idx2 = grid.add(GP(1,0,0));
int idx3 = grid.add(GP(1,1,0));
grid.connectBiDir(idx1,idx2);
grid.connectBiDir(idx2,idx3);
grid.connectBiDir(idx3,idx1);
ASSERT_EQ(2, grid.getNumNeighbors(idx1));
ASSERT_EQ(2, grid.getNumNeighbors(idx2));
ASSERT_EQ(2, grid.getNumNeighbors(idx3));
grid.disconnectUniDir(idx1, idx2);
ASSERT_EQ(1, grid.getNumNeighbors(idx1)); ASSERT_EQ(idx3, grid.getNeighbor(idx1,0).getIdx());
ASSERT_EQ(2, grid.getNumNeighbors(idx2));
grid.disconnectUniDir(idx2,idx1);
ASSERT_EQ(1, grid.getNumNeighbors(idx1)); ASSERT_EQ(idx3, grid.getNeighbor(idx1,0).getIdx());
ASSERT_EQ(1, grid.getNumNeighbors(idx2)); ASSERT_EQ(idx3, grid.getNeighbor(idx2,0).getIdx());
grid.disconnectUniDir(idx1, idx3);
ASSERT_EQ(0, grid.getNumNeighbors(idx1));
ASSERT_EQ(2, grid.getNumNeighbors(idx3));
grid.disconnectUniDir(idx3, idx1);
ASSERT_EQ(0, grid.getNumNeighbors(idx1));
ASSERT_EQ(1, grid.getNumNeighbors(idx3)); ASSERT_EQ(idx2, grid.getNeighbor(idx3,0).getIdx());
grid.disconnectUniDir(idx2, idx3);
ASSERT_EQ(0, grid.getNumNeighbors(idx2));
ASSERT_EQ(1, grid.getNumNeighbors(idx3));
grid.disconnectUniDir(idx3, idx2);
ASSERT_EQ(0, grid.getNumNeighbors(idx2));
ASSERT_EQ(0, grid.getNumNeighbors(idx3));
}
@@ -230,19 +283,19 @@ TEST(Grid, nearest) {
ASSERT_EQ(c1, grid.getNodeFor(GP(15,20,20)));
ASSERT_EQ(c1, grid.getNodeFor(GP(20,15,20)));
ASSERT_EQ(c1, grid.getNodeFor(GP(20,20,15)));
//ASSERT_EQ(c1, grid.getNodeFor(GP(20,20,15))); // Z has a higher resolution!
ASSERT_EQ(c1, grid.getNodeFor(GP(25,20,20)));
ASSERT_EQ(c1, grid.getNodeFor(GP(20,25,20)));
ASSERT_EQ(c1, grid.getNodeFor(GP(20,20,25)));
//ASSERT_EQ(c1, grid.getNodeFor(GP(20,20,25))); // Z has a higher resolution!
ASSERT_EQ(c2, grid.getNodeFor(GP(35,40,40)));
ASSERT_EQ(c2, grid.getNodeFor(GP(40,35,40)));
ASSERT_EQ(c2, grid.getNodeFor(GP(40,40,35)));
//ASSERT_EQ(c2, grid.getNodeFor(GP(40,40,35))); // Z has a higher resolution!
ASSERT_EQ(c2, grid.getNodeFor(GP(45,40,40)));
ASSERT_EQ(c2, grid.getNodeFor(GP(40,45,40)));
ASSERT_EQ(c2, grid.getNodeFor(GP(40,40,45)));
//ASSERT_EQ(c2, grid.getNodeFor(GP(40,40,45))); // Z has a higher resolution!
}

View File

@@ -24,9 +24,10 @@
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
struct MyNode : public GridPoint, public GridNode, public GridNodeImportance, public WiFiGridNode<10> {
MyNode() {;}
MyNode(const int x, const int y, const int z) : GridPoint(x,y,z) {;}
// ENSURE UNIQUE CLASS NAME
struct MyNode1239 : public GridPoint, public GridNode, public GridNodeImportance, public WiFiGridNode<10> {
MyNode1239() {;}
MyNode1239(const int x, const int y, const int z) : GridPoint(x,y,z) {;}
static void staticDeserialize(std::istream& inp) {
WiFiGridNode::staticDeserialize(inp);
}
@@ -36,16 +37,17 @@ struct MyNode : public GridPoint, public GridNode, public GridNodeImportance, pu
}
};
struct MyState : public WalkState, public WalkStateHeading {
MyState(const GridPoint& pos, const Heading head) : WalkState(pos), WalkStateHeading(head) {;}
// ENSURE UNIQUE CLASS NAME
struct MyState23452 : public WalkState, public WalkStateHeading {
MyState23452(const GridPoint& pos, const Heading head) : WalkState(pos), WalkStateHeading(head) {;}
};
TEST(GridWalk2, error) {
TEST(GridWalk2, LIVE_error) {
Grid<MyNode> grid(20);
GridFactory<MyNode> gf(grid);
Grid<MyNode1239> grid(20);
GridFactory<MyNode1239> gf(grid);
Floorplan::Floor floor;
Floorplan::FloorOutlinePolygon poly;
@@ -61,13 +63,13 @@ TEST(GridWalk2, error) {
float turnAngle = 0; // keep the angle as-is!
} ctrl;
GridWalker<MyNode, MyState> walker;
WalkModuleHeadingControl<MyNode, MyState, Control> modHead(&ctrl);
GridWalker<MyNode1239, MyState23452> walker;
WalkModuleHeadingControl<MyNode1239, MyState23452, Control> modHead(&ctrl);
walker.addModule(&modHead);
MyState state(GridPoint(800,800,0), Heading(3.1415/2));
MyState23452 state(GridPoint(800,800,0), Heading(3.1415/2));
K::Gnuplot gp;
gp << "set xrange[0:5000]\n";
@@ -98,9 +100,9 @@ TEST(GridWalk2, error) {
}
TEST(GgridWalk2, walkHeading) {
TEST(GgridWalk2, LIVE_walkHeading) {
Grid<MyNode> grid(20);
Grid<MyNode1239> grid(20);
std::ifstream inp("/tmp/grid.dat");
grid.read(inp);
@@ -118,9 +120,9 @@ TEST(GgridWalk2, walkHeading) {
// GridFactory<MyNode> fac(grid);
// fac.addFloor(&floor);
GridWalker<MyNode, MyState> walker;
WalkModuleHeading<MyNode, MyState> wmHead;
WalkModuleFollowDestination<MyNode, MyState> wmDest(grid, grid.getNodeFor(GridPoint(7200,4800,400+340+340)));
GridWalker<MyNode1239, MyState23452> walker;
WalkModuleHeading<MyNode1239, MyState23452> wmHead;
WalkModuleFollowDestination<MyNode1239, MyState23452> wmDest(grid, grid.getNodeFor(GridPoint(7200,4800,400+340+340)));
//walker.addModule(&wmHead);
walker.addModule(&wmDest);
@@ -132,7 +134,7 @@ TEST(GgridWalk2, walkHeading) {
K::GnuplotSplotElementPoints nodes; nodes.setPointSize(0.1); nodes.setColorHex("#888888"); splot.add(&nodes);
K::GnuplotSplotElementPoints states; states.setPointSize(0.5); states.setColorHex("#0000ff"); splot.add(&states);
for (const MyNode& n : grid) {
for (const MyNode1239& n : grid) {
static int cnt = 0;
if (++cnt % 5 == 0) {
nodes.add(K::GnuplotPoint3(n.x_cm, n.y_cm, n.z_cm));
@@ -143,8 +145,8 @@ TEST(GgridWalk2, walkHeading) {
for (int j = 0; j < 500; ++j) {
states.clear();
for (int i = 0; i < points.size(); ++i) {
MyState start(points[i], Heading(0.0));
MyState next = walker.getDestination(grid, start, 2.0);
MyState23452 start(points[i], Heading(0.0));
MyState23452 next = walker.getDestination(grid, start, 2.0);
points[i] = next.startPos;
states.add(K::GnuplotPoint3(points[i].x_cm, points[i].y_cm, points[i].z_cm));
}

View File

@@ -59,7 +59,7 @@ void build(Grid<GP>& grid) {
}
}
std::set<int> done;
//std::set<int> done;
for (int x = 0; x < size; x += gs) {
for (int y = 0; y < size; y += gs) {
@@ -69,17 +69,19 @@ void build(Grid<GP>& grid) {
for (int x1 = -gs; x1 <= +gs; x1 += gs) {
for (int y1 = -gs; y1 <= +gs; y1 += gs) {
if (x1 == 0 && y1 == 0) {continue;} // the node itself
const GridPoint gp2(x+x1, y+y1, 0);
if (grid.hasNodeFor(gp2)) {
int idx2 = grid.getNodeFor(gp2).getIdx();
if (done.find(idx2) != done.end()) {continue;}
grid.connectBiDir(idx1, idx2);
//if (done.find(idx2) != done.end()) {continue;}
//grid.connectBiDir(idx1, idx2);
grid.connectUniDir(idx1, idx2);
}
}
}
done.insert(idx1);
//done.insert(idx1);
}
}

View File

@@ -57,7 +57,7 @@ TEST(Barometer, relCalib) {
#include <fstream>
TEST(Barometer, tendence) {
TEST(Barometer, LIVE_tendence) {
std::ifstream inp ("/tmp/baro1.dat");
@@ -81,7 +81,7 @@ TEST(Barometer, tendence) {
}
TEST(Barometer, tendence2) {
TEST(Barometer, LIVE_tendence2) {
const float timeframe = 5.0;

View File

@@ -6,6 +6,7 @@
TEST(LogDistanceCeilingModel, 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;
@@ -15,10 +16,16 @@ TEST(LogDistanceCeilingModel, calc) {
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));
//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));
WiFiModelLogDistCeiling model(-40, 1.0, -8.0, &map);
WiFiModelLogDistCeiling model(&map);
const MACAddress ap0 = MACAddress("00:00:00:00:00:00");
const MACAddress ap25 = MACAddress("00:00:00:00:00:01");
model.addAP(ap0, WiFiModelLogDistCeiling::APEntry( Point3(0,0,0), -40, 1.0, -8.0 ));
model.addAP(ap25, WiFiModelLogDistCeiling::APEntry( Point3(0,0,2.5), -40, 1.0, -8.0 ));
ASSERT_EQ(-40, model.getRSSI(ap0, Point3(1,0,0)));
@@ -34,6 +41,41 @@ TEST(LogDistanceCeilingModel, calc) {
}
TEST(LogDistanceCeilingModel, 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);
WiFiModelLogDistCeiling 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)) );
}
#endif

View File

@@ -86,6 +86,7 @@ WiFiObservation groupVAP(const WiFiObservation& inp) {
}
*/
/*
struct WiFiEvalTestBase {
// all training files
@@ -117,11 +118,9 @@ struct WiFiEvalTestBase {
if (Floorplan::toUpperCase(ap->mac) == "00:04:96:6C:3A:A9") {continue;}
if (Floorplan::toUpperCase(ap->mac) == "00:04:96:6B:DB:69") {continue;}
aps.push_back(LocatedAccessPoint(*ap));
apsMap.insert(std::pair<MACAddress, LocatedAccessPoint>(ap->mac, LocatedAccessPoint(*ap)));
}
}
@@ -142,7 +141,8 @@ struct WiFiEvalTestBase {
int cnt = 0;
// model
WiFiModelLogDistCeiling model(params.txp, params.exp, params.waf, im);
WiFiModelLogDistCeiling model(im);
model.addAP();
VAPGrouper vg(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
@@ -422,6 +422,6 @@ TEST(aaiFiEval, getBestTXP_EXP) {
}
*/
#endif

View File

@@ -11,9 +11,10 @@
#include "../../../grid/Grid.h"
struct MyNode : GridNode, GridPoint, WiFiGridNode<10> {
// ensure the class-name is unique!
struct TestNode190231 : public GridNode, public GridPoint, public WiFiGridNode<10> {
MyNode(const int x_cm, const int y_cm, const int z_cm) : GridPoint(x_cm, y_cm, z_cm) {;}
TestNode190231(const int x_cm, const int y_cm, const int z_cm) : GridPoint(x_cm, y_cm, z_cm) {;}
};
@@ -42,30 +43,57 @@ TEST(WiFiGridNodeAP, rssiLimits) {
/** gnuplot debug dumps */
TEST(WiFiGridNode, ctor) {
// ensure nodes are correctly empty initialized
WiFiGridNode<10> node;
ASSERT_EQ(0, node.getNumVisibleAPs());
TestNode190231 node2(0,0,0);
ASSERT_EQ(0, node2.getNumVisibleAPs());
}
// gnuplot debug dumps
TEST(WiFiGridModelLogDist, create) {
int gs = 20;
Grid<MyNode> grid(gs);
Grid<TestNode190231> grid(gs);
for (int x = 0; x < 2000; x += gs) {
for (int y = 0; y < 2000; y += gs) {
grid.add(MyNode(x,y,0));
grid.add(TestNode190231(x,y,0));
}
}
std::cout << "GridNodeSize: " << sizeof(MyNode) << std::endl;
std::cout << "GridNodeSize: " << sizeof(TestNode190231) << std::endl;
LocatedAccessPoint ap1("00:00:00:00:00:01", Point3( 0, 0,0));
LocatedAccessPoint ap2("00:00:00:00:00:02", Point3(20, 0,0));
LocatedAccessPoint ap3("00:00:00:00:00:03", Point3( 0,20,0));
LocatedAccessPoint ap4("00:00:00:00:00:04", Point3(20,20,0));
const MACAddress ap1 = MACAddress("00:00:00:00:00:01");
const MACAddress ap2 = MACAddress("00:00:00:00:00:02");
const MACAddress ap3 = MACAddress("00:00:00:00:00:03");
const MACAddress ap4 = MACAddress("00:00:00:00:00:04");
std::vector<LocatedAccessPoint> aps = {ap1, ap2, ap3, ap4};
// LocatedAccessPoint ap1, Point3( 0, 0,0));
// LocatedAccessPoint ap2("00:00:00:00:00:02", Point3(20, 0,0));
// LocatedAccessPoint ap3("00:00:00:00:00:03", Point3( 0,20,0));
// LocatedAccessPoint ap4("00:00:00:00:00:04", Point3(20,20,0));
// std::vector<LocatedAccessPoint> aps = {ap1, ap2, ap3, ap4};
// model not populated -> must be empty
ASSERT_EQ(0, grid[0].getNumVisibleAPs());
WiFiModelLogDist model(-40, 1.5);
WiFiModelLogDist model;
model.addAP(ap1, WiFiModelLogDist::APEntry( Point3(0,0,0), -40, 1.5));
model.addAP(ap2, WiFiModelLogDist::APEntry( Point3(20,0,0), -40, 1.5));
model.addAP(ap3, WiFiModelLogDist::APEntry( Point3(0,20,0), -40, 1.5));
model.addAP(ap4, WiFiModelLogDist::APEntry( Point3(20,20,0), -40, 1.5));
std::vector<AccessPoint> aps = {
AccessPoint(ap1), AccessPoint(ap2), AccessPoint(ap3), AccessPoint(ap4)
};
WiFiGridEstimator::estimate(grid, model, aps);
@@ -90,13 +118,15 @@ TEST(WiFiGridModelLogDist, create) {
obs.entries.push_back(WiFiMeasurement(MACAddress("00:00:00:00:00:04"), -55, ts));
WiFiObserverGrid observer(5.0f);
const MyNode& gn = grid.getNodeFor(GridPoint(1000,1000,0));
const TestNode190231& gn = grid.getNodeFor(GridPoint(1000,1000,0));
const float p = observer.getProbability(gn, ts, obs);
observer.dump(grid, ts, obs, "/tmp/eval1.gp");
std::cout << "see: /tmp/eval1.gp" << std::endl;
int i = 0; (void) i;
}
#endif