changes from the laptop

- some should be the same as previous commit (sorry!)
- some should be new: LINT checks, ...?
This commit is contained in:
2017-05-24 10:03:39 +02:00
parent f67f95d1ce
commit 04d8ae8c74
42 changed files with 1344 additions and 60 deletions

14
data/XMLload.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef XMLLOAD_H
#define XMLLOAD_H
#include "xml.h"
class XMLload {
public:
virtual void readFromXML(XMLDoc* doc, XMLElem* src) = 0;
};
#endif // XMLLOAD_H

14
data/XMLsave.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef XMLSAVE_H
#define XMLSAVE_H
#include "xml.h"
class XMLsave {
public:
virtual void writeToXML(XMLDoc* doc, XMLElem* dst) = 0;
};
#endif // XMLSAVE_H

44
data/XMLserialize.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef XMLSERIALIZE_H
#define XMLSERIALIZE_H
#include "XMLload.h"
#include "XMLsave.h"
#include "xml.h"
class XMLserialize : public XMLload, public XMLsave {
public:
void loadXML(const std::string& file) {
XMLDoc doc;
assertOK(doc.LoadFile(file.c_str()), doc, "error while loading file");
XMLElem* root = doc.FirstChildElement("root");
readFromXML(&doc, root);
}
void saveXML(const std::string& file) {
XMLDoc doc;
XMLElem* root = doc.NewElement("root");
doc.InsertFirstChild(root);
writeToXML(&doc, root);
assertOK(doc.SaveFile(file.c_str()), doc, "error while saving file");
}
public:
static void assertOK(XMLErr res, XMLDoc& doc, const std::string& txt) {
if (res != tinyxml2::XMLError::XML_SUCCESS) {
const std::string err = doc.ErrorName();
const std::string add = doc.GetErrorStr1();
throw Exception(txt + ": " + err + " - " + add);
}
}
};
#endif // XMLSERIALIZE_H

33
data/xml.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef DATA_XML_H
#define DATA_XML_H
#include "../lib/tinyxml/tinyxml2.h"
using XMLDoc = tinyxml2::XMLDocument;
using XMLErr = tinyxml2::XMLError;
using XMLNode = tinyxml2::XMLNode;
using XMLElem = tinyxml2::XMLElement;
using XMLAttr = tinyxml2::XMLAttribute;
using XMLText = tinyxml2::XMLText;
#define XML_FOREACH_ATTR(attr, root) \
for (const XMLAttr* attr = root->FirstAttribute(); attr != nullptr; attr = attr->Next())
#define XML_FOREACH_NODE(sub, root) \
for (const XMLNode* sub = root->FirstChild(); sub != nullptr; sub = sub->NextSibling())
#define XML_FOREACH_ELEM(sub, root) \
for (XMLElem* sub = (XMLElem*)root->FirstChild(); sub != nullptr; sub = (XMLElem*)sub->NextSibling())
#define XML_FOREACH_ELEM_NAMED(name, sub, root) \
for (XMLElem* sub = root->FirstChildElement(name); sub != nullptr; sub = sub->NextSiblingElement(name))
#define XML_ID(node) node->Attribute("xml:id")
#define XML_WITH_ID(node) node->Attribute("with_id")
#define XML_ASSERT_NODE_NAME(name, node) if (std::string(name) != std::string(node->Name())) {throw Exception("expected " + std::string(name) + " got " + node->Name());}
#define XML_MANDATORY_ATTR(node, attr) (node->Attribute(attr) ? node->Attribute(attr) : throw Exception(std::string("missing XML attribute: ") + attr));
#endif // DATA_XML_H

View File

@@ -0,0 +1,76 @@
#ifndef PLOTWIFIMEASUREMENTS_H
#define PLOTWIFIMEASUREMENTS_H
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
#include "../sensors/radio/WiFiMeasurements.h"
#include "../sensors/radio/WiFiQualityAnalyzer.h"
#include <unordered_map>
/**
* helper-class that plots incoming wifi measurements
* one line per AP
*/
class PlotWifiMeasurements {
K::Gnuplot gp;
K::GnuplotPlot gplot;
K::GnuplotPlotElementLines lineQuality;
std::unordered_map<MACAddress, K::GnuplotPlotElementLines*> lineForMac;
WiFiQualityAnalyzer quality;
public:
PlotWifiMeasurements(const std::string& labelX = "time (sec)", const std::string& labelY = "dBm") {
gplot.getAxisX().setLabel(labelX);
gplot.getAxisY().setLabel(labelY);
// quality indicator using the 2nd y axis
gplot.add(&lineQuality); lineQuality.getStroke().setWidth(2); lineQuality.setUseAxis(1, 2);
gplot.getAxisY2().setTicsVisible(true);
gplot.getAxisY2().setRange(K::GnuplotAxis::Range(0, 1));
}
K::Gnuplot& getGP() {
return gp;
}
K::GnuplotPlot& getPlot() {
return gplot;
}
void add(const WiFiMeasurements& mes) {
const Timestamp ts = mes.entries.front().getTimestamp();
for (const WiFiMeasurement& m : mes.entries) {
const auto& it = lineForMac.find(m.getAP().getMAC());
if (it == lineForMac.end()) {
K::GnuplotPlotElementLines* line = new K::GnuplotPlotElementLines();
line->setTitle(m.getAP().getMAC().asString());
line->getStroke().getColor().setAuto();
lineForMac[m.getAP().getMAC()] = line;
gplot.add(line);
}
const K::GnuplotPoint2 gp2(m.getTimestamp().sec(), m.getRSSI());
lineForMac[m.getAP().getMAC()]->add(gp2);
}
quality.add(mes);
lineQuality.add(K::GnuplotPoint2(ts.sec(), quality.getQuality()));
}
void plot() {
gp.draw(gplot);
gp.flush();
}
};
#endif // PLOTWIFIMEASUREMENTS_H

View File

@@ -46,22 +46,40 @@ namespace Floorplan {
}
/** get the number of ceilings between z1 and z2 */
float numCeilingsBetweenFloat(const Point3 pos1, const Point3 pos2) const {
std::vector<float> getCeilings() const {
return ceilingsAtHeight_m;
}
void addCeiling(const float height_m) {
ceilingsAtHeight_m.push_back(height_m);
}
void clear() {
ceilingsAtHeight_m.clear();
}
/** get a fading number of floors between ap and person using sigmod in the area where the ceiling is */
float numCeilingsBetweenFloat(const Point3 ap, const Point3 person) const {
// sanity checks
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
const float zMin = std::min(pos1.z, pos2.z);
const float zMax = std::max(pos1.z, pos2.z);
// fading between floors using sigmoid
const float near = 1.0;
float cnt = 0;
for (const float z : ceilingsAtHeight_m) {
if (zMin < z && zMax > z) {
const float dmax = zMax - z;
cnt += (dmax > 1) ? (1) : (dmax);
const float myDistToCeil = (ap.z < person.z) ? (person.z - z) : (z - person.z);
if ( std::abs(myDistToCeil) < near ) {
cnt += sigmoid(myDistToCeil*6);
} else if (ap.z < z && person.z >= z+near) { // AP below celing, me above ceiling
cnt += 1;
} else if (ap.z > z && person.z <= z-near) { // AP above ceiling, me below ceiling
cnt += 1;
}
}
return cnt;
@@ -80,14 +98,14 @@ namespace Floorplan {
#ifdef WITH_ASSERTIONS
static int numNear = 0;
static int numFar = 0;
static size_t numNear = 0;
static size_t 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.15,
Assert::isTrue(numNear < numFar*0.5,
"many requests to Floorplan::Ceilings::numCeilingsBetween 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! \
@@ -105,6 +123,12 @@ namespace Floorplan {
}
private:
static inline float sigmoid(const float val) {
return 1.0f / (1.0f + std::exp(-val));
}
};
}

View File

@@ -97,6 +97,11 @@ namespace Floorplan {
checkStair(res, floor, s);
}
// check elevators
for (const Elevator* e : floor->elevators) {
checkElevator(res, floor, e);
}
}
// done
@@ -190,7 +195,7 @@ namespace Floorplan {
for (int i = 0; i < (int) parts.size(); ++i) {
const Floorplan::Quad3& quad = quads[i];
//const Floorplan::Quad3& quad = quads[i];
// disconnected within?
if (i > 0) {
@@ -201,7 +206,17 @@ namespace Floorplan {
}
}
static void checkElevator(Issues& res, const Floor* floor, const Elevator* e) {
if (e->depth < 0.5) {
res.push_back(Issue(Type::ERROR, floor, "elevator's depth @" + e->center.asString() + " is too small: " + std::to_string(e->depth) + "m"));
}
if (e->width < 0.5) {
res.push_back(Issue(Type::ERROR, floor, "elevator's width @" + e->center.asString() + " is too small: " + std::to_string(e->width) + "m"));
}
}

45
geo/BBoxes3.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef BBOXES3_H
#define BBOXES3_H
#include "BBox3.h"
#include <vector>
class BBoxes3 {
private:
/** all contained bboxes */
std::vector<BBox3> bboxes;
public:
/** empty ctor */
BBoxes3() {;}
/** ctor with entries */
BBoxes3(const std::vector<BBox3>& bboxes) : bboxes(bboxes) {;}
/** add the given bbox */
void add(const BBox3& bbox) {
bboxes.push_back(bbox);
}
/** get all contained bboxes */
const std::vector<BBox3>& get() const {
return bboxes;
}
/** does the compound contain the given point? */
bool contains(const Point3& p) const {
for (const BBox3& bb : bboxes) {
if (bb.contains(p)) {return true;}
}
return false;
}
};
#endif // BBOXES3_H

View File

@@ -121,7 +121,7 @@ public:
};
/** get the part of outline the given location belongs to. currently: none, indoor, outdoor */
PartOfOutline isPartOfFloorOutline(const int x_cm, const int y_cm, const Floorplan::FloorOutline& outline) {
static PartOfOutline isPartOfFloorOutline(const int x_cm, const int y_cm, const Floorplan::FloorOutline& outline) {
// assume the point is not part of the outline
PartOfOutline res = PartOfOutline::NO;

View File

@@ -111,9 +111,6 @@ public:
Assert::isBetween(walkImportance, 0.0f, 2.5f, "grid-node's walk-importance is out of range. Did you forget to calculate the importance values after building the grid?");
probability *= walkImportance;// < 0.4f ? (0.1) : (1.0); // "kill" particles that walk near walls (most probably trapped ones)
//probability = std::pow(probability, 5);
// sanity check
Assert::isNotNaN(probability, "detected NaN grid-walk probability");

View File

@@ -74,9 +74,8 @@ public:
// get the difference
const float angularDiff = head.getDiffHalfRAD(stateHead);
if (angularDiff > Angle::degToRad(180)) {return 0.05;}
if (angularDiff > Angle::degToRad(90)) {return 0.25;}
{return 0.70;}
if (angularDiff > Angle::degToRad(100)) {return 0.10;}
{return 0.90;}
}

View File

@@ -100,7 +100,7 @@ public:
// if (potentialNode.getType() == GridNode::TYPE_FLOOR) {return kappa;}
// {return 1-kappa;}
default:
throw Exception("not yet implemented");
throw Exception("not yet implemented activity within WalkModuleActivityControl::getProbability");
}
}

View File

@@ -9,5 +9,6 @@
#include "distribution/Region.h"
#include "distribution/Triangle.h"
#include "distribution/NormalN.h"
#include "distribution/Rectangular.h"
#endif // DISTRIBUTIONS_H

View File

@@ -3,6 +3,7 @@
#include "../Assertions.h"
#include "../Exception.h"
#include "../data/Timestamp.h"
#include <vector>
#include <algorithm>
@@ -49,7 +50,7 @@ public:
// interpolate
const int idx1 = (idx2 > 0) ? (idx2 - 1) : (idx2);
const float percent = (key - entries[idx1].key) / (float) (entries[idx2].key - entries[idx1].key);
const float percent = getPercent(key, entries[idx1].key, entries[idx2].key); //(key - entries[idx1].key) / (float) (entries[idx2].key - entries[idx1].key);
const Value res = entries[idx1].value + (entries[idx2].value - entries[idx1].value) * percent;
return res;
@@ -57,6 +58,16 @@ public:
protected:
/** special interpolation for the timestamp class */
static inline float getPercent(const Timestamp key, const Timestamp t1, const Timestamp t2) {
return (key - t1).ms() / (float) (t2 - t1).ms();
}
/** interpolation for generic datatypes [int, float, double, ..] */
template <typename T> static inline float getPercent(const T key, const T t1, const T t2) {
return (key - t1) / (float) (t2 - t1);
}
/** get the nearest index for the given key */
int getIdxAfter(const Key key) const {

49
math/MovingStdDevTS.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef MOVINGSTDDEVTS_H
#define MOVINGSTDDEVTS_H
#include "MovingAverageTS.h"
/**
* moving stadnard-deviation using a given time-region
*/
template <typename T> class MovingStdDevTS {
private:
MovingAverageTS<T> avg;
MovingAverageTS<T> avg2;
public:
/** ctor with the window-size to use */
MovingStdDevTS(const Timestamp window, const T zeroElement) : avg(window, zeroElement), avg2(window, zeroElement) {
;
}
/** add a new entry */
void add(const Timestamp ts, const T& data) {
// E(x)
avg.add(ts, data);
// E(x^2)
avg2.add(ts, data*data);
}
/** get the current std-dev */
T get() const {
const T e = avg.get();
const T e2 = avg2.get();
const T var = e2 - e*e;
return std::sqrt(var);
}
};
#endif // MOVINGSTDDEVTS_H

View File

@@ -0,0 +1,46 @@
#ifndef RECTANGULAR_H
#define RECTANGULAR_H
#include <cmath>
#include <random>
#include "../Random.h"
#include "../../Assertions.h"
#include "Normal.h"
namespace Distribution {
/** normal distribution */
template <typename T> class Rectangular {
private:
const T mu;
const T h;
const T width;
public:
/** ctor */
Rectangular(const T mu, const T width) : mu(mu), h(1.0/width), width(width) {
}
/** get probability for the given value */
T getProbability(const T val) const {
const T diff = std::abs(val - mu);
return (diff < width/2) ? (h) : (0.0);
}
/** get the probability for the given value */
static T getProbability(const T mu, const T width, const T val) {
Rectangular<T> dist(mu, width);
return dist.getProbability(val);
}
};
}
#endif // RECTANGULAR_H

View File

@@ -0,0 +1,20 @@
#ifndef ACTIVITY_H
#define ACTIVITY_H
enum class Activity {
STANDING,
WALKING,
WALKING_UP,
WALKING_DOWN,
ELEVATOR_UP,
ELEVATOR_DOWN,
};
#endif // ACTIVITY_H

View File

@@ -0,0 +1,126 @@
#ifndef ACTIVITYDETECTOR_H
#define ACTIVITYDETECTOR_H
#include "../imu/AccelerometerData.h"
#include "../pressure/BarometerData.h"
#include "../../data/Timestamp.h"
#include "../../Assertions.h"
#include "../../math/MovingAverageTS.h"
#include "../../math/MovingStdDevTS.h"
#include "../activity/Activity.h"
#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>
/**
* simple step detection based on accelerometer magnitude.
* magnitude > threshold? -> step!
* block for several msec until detecting the next one
*/
class ActivityDetector {
private:
MovingAverageTS<float> avgLong;
MovingAverageTS<float> avgShort;
MovingStdDevTS<float> stdDev;
MovingStdDevTS<float> stdDev2;
MovingAverageTS<float> baroAvgSlow;
MovingAverageTS<float> baroAvgFast;
public:
K::Gnuplot gp;
K::GnuplotPlot gplot;
K::GnuplotPlotElementLines l1;
K::GnuplotPlotElementLines l2;
/** ctor */
ActivityDetector() : avgLong(Timestamp::fromMS(1500), 0), avgShort(Timestamp::fromMS(500), 0),
stdDev(Timestamp::fromMS(200), 0), stdDev2(Timestamp::fromMS(2000), 0) {
;
gplot.add(&l1);
gplot.add(&l2); l2.getStroke().getColor().setHexStr("#ff0000");
}
/** add barometer data */
void add(const Timestamp ts, const BarometerData& baro) {
if (baro.isValid()) {
baroAvgSlow.add(ts, baro.hPa);
baroAvgFast.add(ts, baro.hPa);
}
}
Activity get() {
// delta in acceleration
const float delta_acc = std::abs(avgLong.get() - avgShort.get());
if (delta_acc < 0.3) {
return Activity::STANDING;
}
// delta in pressure
const float delta_hPa = baroAvgFast.get() - baroAvgSlow.get();
if (std::abs(delta_hPa) < 0.1) {
return Activity::WALKING;
} else if (delta_hPa > 0) {
return Activity::WALKING_DOWN;
} else {
return Activity::WALKING_UP;
}
}
/** does the given data indicate a step? */
void add(const Timestamp ts, const AccelerometerData& acc) {
// update averages
avgLong.add(ts, acc.magnitude());
avgShort.add(ts, acc.magnitude());
stdDev.add(ts, acc.magnitude());
stdDev2.add(ts, acc.magnitude());
// const float delta = std::abs(avgLong.get() - avgShort.get());
// static int x = 0; ++x;
//// if (x % 10 == 0) {
//// l1.add({x, avgLong.get()});
//// l2.add({x, avgShort.get()});
//// gp.draw(gplot);
//// gp.flush();
//// }
// if (delta < 0.3) {
// return Activity::STANDING;
// }
// if (avgLong.get() > 9.81+0.5) {
// return Activity::WALKING_UP;
// } else if (avgLong.get() < 9.81-0.5) {
// return Activity::WALKING_DOWN;
// }
// return Activity::WALKING;
}
};
#endif // ACTIVITYDETECTOR_H

View File

@@ -2,7 +2,7 @@
#define GPSDATA_H
#include "../../data/Timestamp.h"
#include "../../geo/EarthPos.h"
struct GPSData {
@@ -25,6 +25,11 @@ struct GPSData {
/** ctor */
GPSData(const Timestamp tsReceived, const float lat, const float lon, const float alt, const float accuracy) : tsReceived(tsReceived), lat(lat), lon(lon), alt(alt), accuracy(accuracy), speed(NAN) {;}
/** get as EarthPos struct */
EarthPos toEarthPos() const {
return EarthPos(lat, lon, alt);
}
/** data valid? */
bool isValid() const {
return (lat == lat) && (lon == lon);

View File

@@ -0,0 +1,65 @@
#ifndef GPSPROBABILITY_H
#define GPSPROBABILITY_H
#include "GPSData.h"
#include "../../geo/Point3.h"
#include "../../math/distribution/Region.h"
#include "../../geo/EarthMapping.h"
class GPSProbability {
private:
/** convert between map and earth */
const EarthMapping& em;
public:
/** ctor with the map<->earth translator */
GPSProbability(const EarthMapping& em) : em(em) {
;
}
/** get the probability for residing at pos_m [in meter, map-coordinates] given some GPS measurement */
double getProbability(const Point3 pos_m, const GPSData& d) const {
// pad GPS? -> no gps eval
if (isBad(d)) {return 1.0;}
// adjust accuracy [sometimes strange values are provided here!]
float accuracy = d.accuracy;
if (accuracy < 3.0) {
std::cout << "note: adjusting gps accuracy as '" << accuracy << "'' seems invalid" << std::endl;
accuracy = 3.0;
}
// convert GPS to map coordinats
const Point3 gpsToMap_m = em.worldToMap(d.toEarthPos());
// distance between given point and GPS's estimation
const float dist = pos_m.getDistance(gpsToMap_m);
// calculate probability
//const double prob = Distribution::Region<double>::getProbability(0, d.accuracy, dist);
const double prob = Distribution::Normal<double>::getProbability(0, accuracy, dist);
// sanity checks
Assert::isNot0(prob, "gps probability is 0.0");
Assert::isNotNaN(prob, "gps probability is NaN");
// done
return prob;
}
private:
/** returns true if the given GPS reading is bad [inaccurate, invalid, ...] */
static inline bool isBad(const GPSData& d) {
return (!d.isValid()) || (d.accuracy == 0) || (d.accuracy > 25);
}
};
#endif // GPSPROBABILITY_H

View File

@@ -0,0 +1,123 @@
#ifndef FILEPLAYER_H
#define FILEPLAYER_H
#include "FileReader.h"
#include <thread>
namespace Offline {
/**
* this class can be used to "play" previously recorded [so-called "offline"] files.
* one can attach itself as listener and is informed whenever new sensor data is available.
* one may choose whether to use full-speed playback [as many events as possible] or
* live-speed playback with the same timing the values were recorded with
*/
class FilePlayer {
private:
FileReader* reader;
bool realtime = false;
bool enabled;
std::thread thread;
/** the listener to inform */
Listener* listener = nullptr;
public:
/** empty ctor */
FilePlayer() : reader(nullptr), listener(nullptr) {
;
}
/** ctor */
FilePlayer(FileReader* reader, Listener* l) : reader(reader), listener(l) {
;
}
/** whether to use realtime playback or "as fast as possible" */
void setRealtime(const bool rt) {
this->realtime = rt;
}
/** set the offline-file-reader to use as data source */
void setReader(FileReader* r) {
this->reader = r;
}
/** set the event listener to inform */
void setListener(Listener* l) {
this->listener = l;
}
/** start playback */
void start() {
// sanity check
Assert::isNotNull(reader, "call FilePlayer::setReader() first");
Assert::isNotNull(listener, "call FilePlayer::setListener() first");
Assert::isFalse(reader->getEntries().empty(), "FileReader has no loaded entries for playback within the FilePlayer!");
enabled = true;
thread = std::thread(&FilePlayer::loop, this);
}
/** stop playback */
void stop() {
enabled = false;
}
/** wait for termination */
void join() {
thread.join();
}
private:
/** background loop */
void loop() {
// get all sensor events from the offline file
const std::vector<Entry> events = reader->getEntries();
// process every event
for (const Entry& e : events) {
// aborted?
if (!enabled) {break;}
// timestamp
const Timestamp ts = Timestamp::fromMS(e.ts);
// event index
const size_t idx = e.idx;
#warning "some sensors todo:"
switch(e.type) {
case Sensor::ACC: listener->onAccelerometer(ts, reader->getAccelerometer()[idx].data); break;
case Sensor::BARO: listener->onBarometer(ts, reader->getBarometer()[idx].data); break;
case Sensor::BEACON: break;//listener->onBe(ts, reader->getBarometer()[idx].data); break;
case Sensor::COMPASS: listener->onCompass(ts, reader->getCompass()[idx].data); break;
case Sensor::GPS: listener->onGPS(ts, reader->getGPS()[idx].data); break;
case Sensor::GRAVITY: listener->onGravity(ts, reader->getGravity()[idx].data); break;
case Sensor::GYRO: listener->onGyroscope(ts, reader->getGyroscope()[idx].data); break;
case Sensor::LIN_ACC: break;//listener->on(ts, reader->getBarometer()[idx].data); break;
case Sensor::WIFI: listener->onWiFi(ts, reader->getWiFiGroupedByTime()[idx].data); break;
default: throw Exception("code error. found not-yet-implemented sensor");
}
}
// done
enabled = false;
}
};
}
#endif // FILEPLAYER_H

View File

@@ -21,6 +21,7 @@
#include "../../grid/factory/v2/GridFactory.h"
#include "../../grid/factory/v2/Importance.h"
#include "../../floorplan/v2/Floorplan.h"
#include "../../floorplan/v2/FloorplanHelper.h"
#include "Splitter.h"
#include "Sensors.h"
@@ -30,10 +31,16 @@
namespace Offline {
/**
* read and parse previously recorded ["offline"] files
*/
class FileReader {
public:
using GroundTruth = Interpolator<Timestamp, Point3>;
/** all entries grouped by sensor */
std::vector<TS<int>> groundTruth;
std::vector<TS<WiFiMeasurements>> wifi;
std::vector<TS<BeaconMeasurement>> beacon;
@@ -45,13 +52,11 @@ namespace Offline {
std::vector<TS<GPSData>> gps;
std::vector<TS<CompassData>> compass;
/** ALL entries */
/** all entries in linear order as they appeared while recording */
std::vector<Entry> entries;
static constexpr char sep = ';';
Listener* listener = nullptr;
public:
/** empty ctor. call open() */
@@ -65,11 +70,26 @@ namespace Offline {
}
/** open the given file */
void open(const std::string& file, Listener* listener = nullptr) {
this->listener = listener;
void open(const std::string& file) {
clear();
parse(file);
}
/** remove all parsed entries */
void clear() {
entries.clear();
groundTruth.clear();
wifi.clear();
beacon.clear();
acc.clear();
gyro.clear();
gps.clear();
compass.clear();
barometer.clear();
lin_acc.clear();
gravity.clear();
}
const std::vector<Entry>& getEntries() const {return entries;}
@@ -93,6 +113,35 @@ namespace Offline {
const std::vector<TS<GravityData>>& getGravity() const {return gravity;}
/** get an interpolateable ground-truth based on the time-clicks during recording */
GroundTruth getGroundTruth(const Floorplan::IndoorMap* map, const std::vector<int> groundTruthPoints) const {
// sanity check: given path [indices to ground-truth points within the map]
// must have the same size as the number of clicks during recording
Assert::equal(groundTruthPoints.size(), groundTruth.size(), "mismatch of ground-truth points between given path and recording");
// allows getting a position on the ground-truth given a timestamp
GroundTruth interpol;
// all ground-truth points within the map
static std::unordered_map<int, Point3> gt = FloorplanHelper::getGroundTruthPoints(map);
// process each "tap smartphone when reaching ground-truth-point"
for (const TS<int>& entry : groundTruth) {
const Timestamp ts = Timestamp::fromMS(entry.ts);
const int idx = entry.data; // starting at 0, incrementing over time [1st point, 2nd points, 3d points, ...]
const int id = groundTruthPoints[idx]; // convert point number to point-id within floorplan
const auto& it = gt.find(id);
if (it == gt.end()) {throw Exception("missing ground-truth point ID:" + std::to_string(id));}
const Point3 pos = it->second;
interpol.add(ts, pos);
}
// done
return interpol;
}
private:
void parse(const std::string& file) {
@@ -165,7 +214,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::GRAVITY, ts, gravity.size()-1));
// inform listener
if (listener) {listener->onGravity(Timestamp::fromMS(ts), gravData);}
//if (listener) {listener->onGravity(Timestamp::fromMS(ts), gravData);}
}
@@ -184,7 +233,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::ACC, ts, acc.size()-1));
// inform listener
if (listener) {listener->onAccelerometer(Timestamp::fromMS(ts), accData);}
//if (listener) {listener->onAccelerometer(Timestamp::fromMS(ts), accData);}
}
@@ -203,7 +252,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::GYRO, ts, gyro.size()-1));
// inform listener
if (listener) {listener->onGyroscope(Timestamp::fromMS(ts), gyroData);}
//if (listener) {listener->onGyroscope(Timestamp::fromMS(ts), gyroData);}
}
@@ -230,7 +279,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::WIFI, ts, this->wifi.size()-1));
// inform listener
if (listener) {listener->onWiFi(Timestamp::fromMS(ts), wifi);}
//if (listener) {listener->onWiFi(Timestamp::fromMS(ts), wifi);}
}
@@ -273,7 +322,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::BARO, ts, barometer.size()-1));
// inform listener
if (listener) {listener->onBarometer(Timestamp::fromMS(ts), baro);}
//if (listener) {listener->onBarometer(Timestamp::fromMS(ts), baro);}
}
@@ -290,7 +339,7 @@ namespace Offline {
entries.push_back(Entry(Sensor::COMPASS, ts, this->compass.size()-1));
// inform listener
if (listener) {listener->onCompass(Timestamp::fromMS(ts), compass);}
//if (listener) {listener->onCompass(Timestamp::fromMS(ts), compass);}
}
@@ -311,12 +360,13 @@ namespace Offline {
entries.push_back(Entry(Sensor::GPS, ts, this->gps.size()-1));
// inform listener
if (listener) {listener->onGPS(Timestamp::fromMS(ts), gps);}
//if (listener) {listener->onGPS(Timestamp::fromMS(ts), gps);}
}
public:
const Interpolator<uint64_t, Point3> getGroundTruthPath(Floorplan::IndoorMap* map, std::vector<int> gtPath) const {
// finde alle positionen der waypoints im gtPath aus map

View File

@@ -51,9 +51,9 @@ public:
K::GnuplotPlotElementLines tendence;
Debug() {
plot.add(&raw); raw.setColorHex("#999999");
plot.add(&avg); avg.setColorHex("#000000");
plot.add(&tendence); tendence.setLineWidth(2);
plot.add(&raw); raw.getStroke().getColor().setHexStr("#999999");
plot.add(&avg); avg.getStroke().getColor().setHexStr("#000000");
plot.add(&tendence); tendence.getStroke().setWidth(2);
tendence.setCustomAttr(" axes x1y2 ");
gp << "set y2tics\n";
gp << "set y2range[-0.3:+0.3]\n";

View File

@@ -45,6 +45,11 @@ public:
;
}
/** equals? */
bool operator == (const AccessPoint& o) {
return (o.mac == mac) && (o.ssid == ssid);
}
public:
/** get the AP's MAC address */

View File

@@ -23,8 +23,8 @@ struct WiFiMeasurements {
}
/** get the measurements for the given MAC [if available] otherwise null */
WiFiMeasurement* getForMac(const MACAddress& mac) {
for (WiFiMeasurement& m : entries) {
const WiFiMeasurement* getForMac(const MACAddress& mac) const {
for (const WiFiMeasurement& m : entries) {
if (m.getAP().getMAC() == mac) {
return &m;
}

View File

@@ -27,15 +27,26 @@ private:
/** the map's floorplan */
Floorplan::IndoorMap* map;
std::vector<AccessPoint> allAPs;
bool useError = false;
public:
WiFiObserverFree(const float sigma, WiFiModel& model) : sigma(sigma), model(model) {
allAPs = model.getAllAPs();
}
void setUseError(const bool useError) {
this->useError = useError;
}
double getProbability(const Point3& pos_m, const Timestamp curTime, const WiFiMeasurements& obs) const {
double prob = 1.0;
//double prob = 0;
double error = 0;
int numMatchingAPs = 0;
// process each measured AP
@@ -50,7 +61,6 @@ public:
// NaN? -> AP not known to the model -> skip
if (modelRSSI != modelRSSI) {continue;}
// the scan's RSSI
const float scanRSSI = entry.getRSSI();
@@ -60,24 +70,81 @@ public:
Assert::isTrue(age.ms() >= 0, "found a negative wifi measurement age. this does not make sense");
Assert::isTrue(age.ms() <= 60000, "found a 60 second old wifi measurement. maybe there is a coding error?");
Assert::isBetween(scanRSSI, -100.0f, -30.0f, "scan-rssi out of range");
//Assert::isBetween(modelRSSI, -160.0f, -10.0f, "model-rssi out of range");
// sigma grows with measurement age
const float sigma = this->sigma + this->sigmaPerSecond * age.sec();
// probability for this AP
//double local = Distribution::Normal<double>::getProbability(modelRSSI, sigma, scanRSSI);
double local = Distribution::Exponential<double>::getProbability(0.1, std::abs(modelRSSI-scanRSSI));
// also add the error value? [location is OK but model is wrong]
if (useError) {
local = 0.95 * local + 0.05 * (1.0-local);
//local = 0.95 * local + 0.05;
#warning "TODO"
}
// update probability
prob *= Distribution::Normal<double>::getProbability(modelRSSI, sigma, scanRSSI);
//prob *= Distribution::Region<double>::getProbability(modelRSSI, sigma, scanRSSI);
prob *= local;
++numMatchingAPs;
}
// 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;
}
/**
* for some locations it might make sense to also look at APs
* that have NOT been discovered by the smartphone but SHOULD
* be very well receivable at a location.
* such locations can be opted-out by using this veto-probability
*/
double getVeto(const Point3& pos_m, const Timestamp curTime, const WiFiMeasurements& obs) const {
(void) curTime;
struct APR {
AccessPoint ap;
float rssi;
APR(const AccessPoint& ap, const float rssi) : ap(ap), rssi(rssi) {;}
};
std::vector<APR> all;
for (const AccessPoint& ap : allAPs) {
const float rssi = model.getRSSI(ap.getMAC(), pos_m);
if (rssi != rssi) {continue;} // unknown to the model
all.push_back(APR(ap, rssi));
}
// stort by RSSI
auto comp = [&] (const APR& apr1, const APR& apr2) {return apr1.rssi > apr2.rssi;};
std::sort(all.begin(), all.end(), comp);
int numVetos = 0;
for (int i = 0; i < 3; ++i) {
const APR& apr = all[i];
const WiFiMeasurement* mes = obs.getForMac(apr.ap.getMAC());
const float rssiScan = (mes) ? (mes->getRSSI()) : (-100);
const float rssiModel = apr.rssi;
const float diff = std::abs(rssiScan - rssiModel);
if (diff > 20) {++numVetos;}
}
if (numVetos == 0) {return 0.70;}
if (numVetos == 1) {return 0.29;}
else {return 0.01;}
}
template <typename Node> double getProbability(const Node& n, const Timestamp curTime, const WiFiMeasurements& obs, const int age_ms = 0) const {
return this->getProbability(n.inMeter() + Point3(0,0,1.3), curTime, obs);

View File

@@ -3,6 +3,11 @@
#include "../AccessPoint.h"
#include "../../../geo/Point3.h"
#include <vector>
#include "../../../data/XMLserialize.h"
/**
* interface for signal-strength prediction models.
@@ -10,7 +15,7 @@
* 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 {
class WiFiModel : public XMLserialize {
public:
@@ -31,6 +36,7 @@ public:
*/
virtual float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const = 0;
};
#endif // WIFIMODEL_H

View File

@@ -0,0 +1,32 @@
#ifndef WIFIMODELFACTORY_H
#define WIFIMODELFACTORY_H
#include "WiFiModel.h"
#include "../../../floorplan/v2/Floorplan.h"
/**
* this class can instantiate WiFiModels based on serialized XML files
*/
class WiFiModelFactory {
private:
Floorplan::IndoorMap* map;
public:
/** ctor. needs the floorplan for some models */
WiFiModelFactory(Floorplan::IndoorMap* map) : map(map) {
;
}
/** parse model from XML file */
WiFiModel* loadXML(const std::string& file);
/** parse model from XML node */
WiFiModel* readFromXML(XMLDoc* doc, XMLElem* src);
};
#endif // WIFIMODELFACTORY_H

View File

@@ -0,0 +1,42 @@
#ifndef WIFIMODELFACTORYIMPL_H
#define WIFIMODELFACTORYIMPL_H
#include "WiFiModelFactory.h"
#include "WiFiModelLogDist.h"
#include "WiFiModelLogDistCeiling.h"
#include "WiFiModelPerFloor.h"
#include "WiFiModelPerBBox.h"
WiFiModel* WiFiModelFactory::loadXML(const std::string& file) {
XMLDoc doc;
XMLserialize::assertOK(doc.LoadFile(file.c_str()), doc, "error while loading file");
XMLElem* root = doc.FirstChildElement("root");
return readFromXML(&doc, root);
}
WiFiModel* WiFiModelFactory::readFromXML(XMLDoc* doc, XMLElem* src) {
// each model attaches its "type" during serialization
const std::string type = src->Attribute("type");
WiFiModel* mdl = nullptr;
// create an instance for the model
if (type == "WiFiModelLogDist") {mdl = new WiFiModelLogDist();}
else if (type == "WiFiModelLogDistCeiling") {mdl = new WiFiModelLogDistCeiling(map);}
else if (type == "WiFiModelPerFloor") {mdl = new WiFiModelPerFloor(map);}
else if (type == "WiFiModelPerBBox") {mdl = new WiFiModelPerBBox(map);}
else {throw Exception("invalid model type given: " + type);}
// load the model from XML
mdl->readFromXML(doc, src);
// done
return mdl;
}
#endif // WIFIMODELFACTORYIMPL_H

View File

@@ -77,6 +77,14 @@ public:
}
void readFromXML(XMLDoc* doc, XMLElem* src) override {
throw Exception("not yet implemented");
}
void writeToXML(XMLDoc* doc, XMLElem* dst) override {
throw Exception("not yet implemented");
}
};
#endif // WIFIMODELLOGDIST_H

View File

@@ -10,6 +10,8 @@
#include "../VAPGrouper.h"
#include "../../../misc/Debug.h"
#include "../../../data/XMLserialize.h"
/**
* signal-strength estimation using log-distance model
* including ceilings between AP and position
@@ -52,6 +54,13 @@ public:
}
/** get the entry for the given mac. exception if missing */
const APEntry& getAP(const MACAddress& mac) const {
const auto& it = accessPoints.find(mac);
if (it == accessPoints.end()) {throw Exception("model does not contain an entry for " + mac.asString());}
return it->second;
}
/** get a list of all APs known to the model */
std::vector<AccessPoint> getAllAPs() const {
std::vector<AccessPoint> aps;
@@ -109,6 +118,14 @@ public:
}
/**
* make the given AP (and its parameters) known to the model
* usually txp,exp,waf are checked for a sane range. if you know what you are doing, set assertSafe to false!
*/
void addAP(const MACAddress& accessPoint, const Point3 pos_m, const float txp, const float exp, const float waf, const bool assertSafe = true) {
addAP(accessPoint, APEntry(pos_m, txp, exp, waf), assertSafe);
}
/** remove all added APs */
void clear() {
accessPoints.clear();
@@ -130,7 +147,8 @@ public:
const float rssiLOS = LogDistanceModel::distanceToRssi(params.txp, params.exp, distance_m);
// WAF loss (params.waf is a negative value!) -> WAF loss is also a negative value
const float wafLoss = params.waf * ceilings.numCeilingsBetween(position_m, params.position_m);
//const float wafLoss = params.waf * ceilings.numCeilingsBetween(position_m, params.position_m);
const float wafLoss = params.waf * ceilings.numCeilingsBetweenFloat(position_m, params.position_m);
// combine
const float res = rssiLOS + wafLoss;
@@ -143,6 +161,59 @@ public:
}
void writeToXML(XMLDoc* doc, XMLElem* dst) override {
// set my type
dst->SetAttribute("type", "WiFiModelLogDistCeiling");
for (const auto& it : accessPoints) {
const MACAddress& mac = it.first;
const APEntry& ape = it.second;
XMLElem* xap = doc->NewElement("ap");
xap->SetAttribute("mac", mac.asString().c_str());
xap->SetAttribute("px", ape.position_m.x);
xap->SetAttribute("py", ape.position_m.y);
xap->SetAttribute("pz", ape.position_m.z);
xap->SetAttribute("txp", ape.txp);
xap->SetAttribute("exp", ape.exp);
xap->SetAttribute("waf", ape.waf);
dst->InsertEndChild(xap);
}
for (const float atHeight_m : ceilings.getCeilings()) {
XMLElem* xceil = doc->NewElement("ceiling");
xceil->SetAttribute("atHeight", atHeight_m);
dst->InsertEndChild(xceil);
}
}
void readFromXML(XMLDoc* doc, XMLElem* src) override {
// check type
if (std::string("WiFiModelLogDistCeiling") != src->Attribute("type")) {throw Exception("invalid model type");}
accessPoints.clear();
ceilings.clear();
XML_FOREACH_ELEM_NAMED("ap", xap, src) {
MACAddress mac = MACAddress(xap->Attribute("mac"));
APEntry ape(
Point3(xap->FloatAttribute("px"), xap->FloatAttribute("py"), xap->FloatAttribute("pz")),
xap->FloatAttribute("txp"),
xap->FloatAttribute("exp"),
xap->FloatAttribute("waf")
);
accessPoints.insert( std::make_pair(mac, ape) );
}
XML_FOREACH_ELEM_NAMED("ceiling", xceil, src) {
const float atHeight_m = xceil->FloatAttribute("atHeight");
ceilings.addCeiling(atHeight_m);
}
}
};

View File

@@ -0,0 +1,164 @@
#ifndef WIFIMODELPERBBOX_H
#define WIFIMODELPERBBOX_H
#include "../AccessPoint.h"
#include "../../../geo/Point3.h"
#include "../../../geo/BBoxes3.h"
#include <vector>
#include "WiFiModelFactory.h"
/**
* FOR TESTING
*
* this model allows using a different sub-models
* identified by a bound-box to reduce the error
*/
class WiFiModelPerBBox : public WiFiModel {
struct ModelForBBoxes {
WiFiModel* mdl;
BBoxes3 bboxes;
/** ctor */
ModelForBBoxes(WiFiModel* mdl, BBoxes3 bboxes) : mdl(mdl), bboxes(bboxes) {;}
/** does this entry apply to the given position? */
bool matches(const Point3 pt) const {
if (bboxes.get().empty()) {throw Exception("no bbox(es) given for model!");}
return bboxes.contains(pt);
}
};
Floorplan::IndoorMap* map;
/** all contained models [one per bbox] */
std::vector<ModelForBBoxes> models;
public:
WiFiModelPerBBox(Floorplan::IndoorMap* map) : map(map) {
;
}
/** dtor */
virtual ~WiFiModelPerBBox() {
}
/** get a list of all APs known to the model */
std::vector<AccessPoint> getAllAPs() const override {
std::vector<AccessPoint> res;
for (const ModelForBBoxes& sub : models) {
for (const AccessPoint& ap : sub.mdl->getAllAPs()) {
if (std::find(res.begin(), res.end(), ap) == res.end()) {
res.push_back(ap);
}
}
}
return res;
}
void add(WiFiModel* mdl, const BBoxes3 bboxes) {
if (bboxes.get().empty()) {throw Exception("no bbox(es) given for model!");}
ModelForBBoxes mfb(mdl, bboxes);
models.push_back(mfb);
}
float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const override {
for (const ModelForBBoxes& mfb : models) {
if (mfb.matches(position_m)) {return mfb.mdl->getRSSI(accessPoint, position_m);}
}
return -120;
}
void readFromXML(XMLDoc* doc, XMLElem* src) override {
// check type
if (std::string("WiFiModelPerBBox") != src->Attribute("type")) {throw Exception("invalid model type");}
models.clear();
// model factory [create models based on XMl content]
WiFiModelFactory fac(map);
// parse all contained models [one per floor]
XML_FOREACH_ELEM_NAMED("entry", xentry, src) {
// all bboxes
BBoxes3 bboxes;
XML_FOREACH_ELEM_NAMED("bbox", xbbox, xentry) {
const float x1 = xbbox->FloatAttribute("x1");
const float y1 = xbbox->FloatAttribute("y1");
const float z1 = xbbox->FloatAttribute("z1");
const float x2 = xbbox->FloatAttribute("x2");
const float y2 = xbbox->FloatAttribute("y2");
const float z2 = xbbox->FloatAttribute("z2");
const BBox3 bbox(Point3(x1,y1,z1), Point3(x2,y2,z2));
bboxes.add(bbox);
}
// node for the model
XMLElem* xmodel = xentry->FirstChildElement("model");
// let the factory instantiate the model
WiFiModel* mdl = fac.readFromXML(doc, xmodel);
// add
models.push_back(ModelForBBoxes(mdl, bboxes));
}
}
void writeToXML(XMLDoc* doc, XMLElem* dst) override {
// set my type
dst->SetAttribute("type", "WiFiModelPerBBox");
for (const ModelForBBoxes& mfb : models) {
// all models
XMLElem* xentry = doc->NewElement("entry"); {
// each bbox
for (const BBox3& bbox : mfb.bboxes.get()) {
XMLElem* xbbox = doc->NewElement("bbox"); {
xbbox->SetAttribute("x1", bbox.getMin().x);
xbbox->SetAttribute("y1", bbox.getMin().y);
xbbox->SetAttribute("z1", bbox.getMin().z);
xbbox->SetAttribute("x2", bbox.getMax().x);
xbbox->SetAttribute("y2", bbox.getMax().y);
xbbox->SetAttribute("z2", bbox.getMax().z);
}; xentry->InsertFirstChild(xbbox);
}
// the corresponding model
XMLElem* xmodel = doc->NewElement("model"); {
mfb.mdl->writeToXML(doc, xmodel);
}; xentry->InsertEndChild(xmodel);
}; dst->InsertEndChild(xentry);
}
}
};
#endif // WIFIMODELPERBBOX_H

View File

@@ -0,0 +1,122 @@
#ifndef WIFIMODELPERFLOOR_H
#define WIFIMODELPERFLOOR_H
#include "../AccessPoint.h"
#include "../../../geo/Point3.h"
#include <vector>
#include "WiFiModelFactory.h"
/**
* FOR TESTING
*
* this model allows using a different sub-models for each floor to reduce the error
*/
class WiFiModelPerFloor : public WiFiModel {
struct ModelForFloor {
float fromZ;
float toZ;
WiFiModel* mdl;
/** ctor */
ModelForFloor(const float fromZ, const float toZ, WiFiModel* mdl) : fromZ(fromZ), toZ(toZ), mdl(mdl) {;}
/** does this entry apply to the given z-position? */
bool matches(const float z) const {
return (fromZ <= z && z < toZ);
}
};
Floorplan::IndoorMap* map;
/** all contained models [one per floor] */
std::vector<ModelForFloor> models;
public:
WiFiModelPerFloor(Floorplan::IndoorMap* map) : map(map) {
;
}
/** dtor */
virtual ~WiFiModelPerFloor() {
}
/** get a list of all APs known to the model */
std::vector<AccessPoint> getAllAPs() const override {
return models.front().mdl->getAllAPs();
}
void add(WiFiModel* mdl, const Floorplan::Floor* floor) {
ModelForFloor mff(floor->atHeight, floor->atHeight+floor->height, mdl);
models.push_back(mff);
}
float getRSSI(const MACAddress& accessPoint, const Point3 position_m) const override {
for (const ModelForFloor& mff : models) {
if (mff.matches(position_m.z)) {return mff.mdl->getRSSI(accessPoint, position_m);}
}
return -120;
}
void readFromXML(XMLDoc* doc, XMLElem* src) override {
// check type
if (std::string("WiFiModelPerFloor") != src->Attribute("type")) {throw Exception("invalid model type");}
models.clear();
// model factory [create models based on XMl content]
WiFiModelFactory fac(map);
// parse all contained models [one per floor]
XML_FOREACH_ELEM_NAMED("floor", xfloor, src) {
// floor params
const float z1 = xfloor->FloatAttribute("z1");
const float z2 = xfloor->FloatAttribute("z2");
// node for the model
XMLElem* xmodel = xfloor->FirstChildElement("model");
// let the factory instantiate the model
WiFiModel* mdl = fac.readFromXML(doc, xmodel);
// add
models.push_back(ModelForFloor(z1, z2, mdl));
}
}
void writeToXML(XMLDoc* doc, XMLElem* dst) override {
// set my type
dst->SetAttribute("type", "WiFiModelPerFloor");
for (const ModelForFloor& mff : models) {
XMLElem* xfloor = doc->NewElement("floor"); {
xfloor->SetAttribute("z1", mff.fromZ);
xfloor->SetAttribute("z2", mff.toZ);
XMLElem* xmodel = doc->NewElement("model"); {
mff.mdl->writeToXML(doc, xmodel);
}; xfloor->InsertEndChild(xmodel);
}; dst->InsertEndChild(xfloor);
}
}
};
#endif // WIFIMODELPERFLOOR_H

View File

@@ -0,0 +1,8 @@
#ifndef WIFIMODELS_H
#define WIFIMODELS_H
#include "WiFiModel.h"
#include "WiFiModelFactory.h"
#include "WiFiModelFactoryImpl.h"
#endif // WIFIMODELS_H

View File

@@ -29,6 +29,8 @@ struct WiFiFingerprint {
/** ctor */
WiFiFingerprint(const Point3 pos_m) : pos_m(pos_m) {;}
/** ctor */
WiFiFingerprint(const Point3 pos_m, const WiFiMeasurements& measurements) : pos_m(pos_m), measurements(measurements) {;}
/** as each AP might be contained more than once (scanned more than once), group them by MAC and use the average RSSI */

View File

@@ -4,7 +4,7 @@
#include <fstream>
#include "WiFiFingerprint.h"
#include <iostream>
/**
* helper class to load and save fingerprints.

View File

@@ -49,12 +49,12 @@ public:
//gp << "set hidden3d front\n";
splot.add(&nodes); nodes.setPointSize(0.5);
splot.add(&lines); lines.setColorHex("#999999");
splot.add(&lines); lines.getStroke().getColor().setHexStr("#999999");
splot.add(&floors);
splot.add(&particles); particles.setColorHex("#0000ff"); particles.setPointSize(0.5);
splot.add(&particles); particles.getColor().setHexStr("#0000ff"); particles.setPointSize(0.5);
floors.setLineWidth(2);
floors.setColorHex("#008800");
floors.getStroke().setWidth(2);
floors.getStroke().getColor().setHexStr("#008800");
}

View File

@@ -59,7 +59,7 @@ TEST(TestAll, Nav) {
gi.addImportance(g, d.getNode(start), d.getNode(end));
// plot path
K::GnuplotSplotElementLines path; path.setColorHex("#0000ff"); path.setLineWidth(2);
K::GnuplotSplotElementLines path; path.getStroke().getColor().setHexStr("#0000ff"); path.getStroke().setWidth(2);
const DijkstraNode<GP>* dn = d.getNode(end);
while (dn->previous != nullptr) {
path.add(K::GnuplotPoint3(dn->element->x_cm, dn->element->y_cm, dn->element->z_cm+50));

View File

@@ -131,8 +131,8 @@ TEST(GgridWalk2, LIVE_walkHeading) {
K::Gnuplot gp;
K::GnuplotSplot splot;
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);
K::GnuplotSplotElementPoints nodes; nodes.setPointSize(0.1); nodes.getColor().setHexStr("#888888"); splot.add(&nodes);
K::GnuplotSplotElementPoints states; states.setPointSize(0.5); states.getColor().setHexStr("#0000ff"); splot.add(&states);
for (const MyNode1239& n : grid) {
static int cnt = 0;

View File

@@ -50,8 +50,8 @@ TEST(Stairs, live_testWalk) {
K::Gnuplot gp;
K::GnuplotSplot splot;
K::GnuplotSplotElementPoints pnodes; splot.add(&pnodes); pnodes.setColorHex("#888888"); pnodes.setPointType(7);
K::GnuplotSplotElementPoints pstates; splot.add(&pstates); pstates.setColorHex("#0000ff"); pstates.setPointType(7); pstates.setPointSize(1);
K::GnuplotSplotElementPoints pnodes; splot.add(&pnodes); pnodes.getColor().setHexStr("#888888"); pnodes.setPointType(7);
K::GnuplotSplotElementPoints pstates; splot.add(&pstates); pstates.getColor().setHexStr("#0000ff"); pstates.setPointType(7); pstates.setPointSize(1);
for (int i = 0; i < g.getNumNodes(); i+=2) {
const MyNode345092134& gp = g[i];

View File

@@ -101,7 +101,7 @@ TEST(Walk, DISABLED_plot) {
std::normal_distribution<float> dWalk(0.3, 0.3);
std::normal_distribution<float> dTurn(0.3, 0.3);
K::GnuplotSplotElementPoints pStates; pStates.setColorHex("#880000");
K::GnuplotSplotElementPoints pStates; pStates.getColor().setHexStr("#880000");
// setup starting states
std::vector<GridWalkState<GP>> states;

View File

@@ -57,7 +57,7 @@ TEST(Butterworth, offlineSinus) {
linesInput.addSegment(input_p1, input_p2);
linesOutput.addSegment(output_p1, output_p2);
}
linesOutput.setColorHex("#00FF00");
linesOutput.getStroke().getColor().setHexStr("#00FF00");
plot.add(&linesInput);
plot.add(&linesOutput);
@@ -104,7 +104,7 @@ TEST(Butterworth, onlineSinus) {
linesInput.addSegment(input_p1, input_p2);
linesOutput.addSegment(output_p1, output_p2);
}
linesOutput.setColorHex("#00FF00");
linesOutput.getStroke().getColor().setHexStr("#00FF00");
plot.add(&linesInput);
plot.add(&linesOutput);
@@ -171,7 +171,7 @@ TEST(Butterworth, offlineOctaveBaro) {
linesInput.addSegment(input_p1, input_p2);
linesOutput.addSegment(output_p1, output_p2);
}
linesOutput.setColorHex("#00FF00");
linesOutput.getStroke().getColor().setHexStr("#00FF00");
plot.add(&linesInput);
plot.add(&linesOutput);
@@ -242,7 +242,7 @@ TEST(Butterworth, onlineOctaveBaro) {
linesInput.addSegment(input_p1, input_p2);
linesOutput.addSegment(output_p1, output_p2);
}
linesOutput.setColorHex("#00FF00");
linesOutput.getStroke().getColor().setHexStr("#00FF00");
plot.add(&linesInput);
plot.add(&linesOutput);