From 18f48e23a8927e2c03758900ae067efe6e441879 Mon Sep 17 00:00:00 2001 From: FrankE Date: Mon, 20 Mar 2017 19:24:29 +0100 Subject: [PATCH 1/2] fixed LINT issue --- floorplan/v2/FloorplanLINT.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/floorplan/v2/FloorplanLINT.h b/floorplan/v2/FloorplanLINT.h index ebb5db9..34ca7b8 100644 --- a/floorplan/v2/FloorplanLINT.h +++ b/floorplan/v2/FloorplanLINT.h @@ -166,6 +166,11 @@ namespace Floorplan { static void checkStair(Issues& res, const Floor* floor, const Stair* stair) { + if (stair->getParts().empty()) { + res.push_back(Issue(Type::ERROR, floor, "stair does not contain any parts! [empty stair]")); + return; + } + const std::vector parts = stair->getParts(); const std::vector quads = Floorplan::getQuads(parts, floor); From bb43e7f0fef044c0212b4c8f7db08af6dc549d63 Mon Sep 17 00:00:00 2001 From: kazu Date: Tue, 21 Mar 2017 16:25:36 +0100 Subject: [PATCH 2/2] fixed some compiler warnings added equality checks to sensor-data classes more robust sensor reader [fixed some issues] added support for gps added support for compass added sensor-data-writer added test-cases minor changes --- geo/Point2.h | 1 + main.cpp | 2 +- math/filter/Butterworth.h | 4 +- sensors/gps/GPSData.h | 21 + sensors/imu/AccelerometerData.h | 24 +- sensors/imu/CompassData.h | 56 +++ sensors/imu/GravityData.h | 24 +- sensors/imu/GyroscopeData.h | 24 +- sensors/imu/LinearAccelerationData.h | 24 +- sensors/offline/FileReader.h | 422 ++++++++++-------- sensors/offline/FileWriter.h | 92 ++++ sensors/offline/Listener.h | 33 ++ sensors/offline/OfflineAndroid.h | 91 +++- sensors/offline/Sensors.h | 38 ++ sensors/offline/Splitter.h | 53 +++ sensors/pressure/BarometerData.h | 13 + sensors/radio/WiFiMeasurement.h | 4 +- .../radio/setup/WiFiOptimizerLogDistCeiling.h | 34 +- tests/sensors/imu/TestMotionDetection.cpp | 24 +- tests/sensors/offline/TestReadWrite.cpp | 89 ++++ 20 files changed, 807 insertions(+), 266 deletions(-) create mode 100644 sensors/imu/CompassData.h create mode 100644 sensors/offline/FileWriter.h create mode 100644 sensors/offline/Listener.h create mode 100644 sensors/offline/Sensors.h create mode 100644 sensors/offline/Splitter.h create mode 100644 tests/sensors/offline/TestReadWrite.cpp diff --git a/geo/Point2.h b/geo/Point2.h index 0d94f12..f28e23d 100644 --- a/geo/Point2.h +++ b/geo/Point2.h @@ -3,6 +3,7 @@ #include #include +#include /** * 2D Point diff --git a/main.cpp b/main.cpp index 52a00bc..de06c02 100755 --- a/main.cpp +++ b/main.cpp @@ -29,7 +29,7 @@ int main(int argc, char** argv) { //::testing::GTEST_FLAG(filter) = "*WiFiOptimizer*"; - ::testing::GTEST_FLAG(filter) = "*FloorplanCeilings*"; + ::testing::GTEST_FLAG(filter) = "*Offline.readWrite*"; //::testing::GTEST_FLAG(filter) = "*Barometer*"; //::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*"; diff --git a/math/filter/Butterworth.h b/math/filter/Butterworth.h index c41fc71..f83d4aa 100644 --- a/math/filter/Butterworth.h +++ b/math/filter/Butterworth.h @@ -77,9 +77,9 @@ namespace Filter { const Scalar _b0, _b1, _b2, _a1, _a2, _gain; - const Scalar _preCompStateSpaceOutputVec1, _preCompStateSpaceOutputVec2; + Scalar _z1, _z2; - Scalar _z1, _z2; + const Scalar _preCompStateSpaceOutputVec1, _preCompStateSpaceOutputVec2; }; diff --git a/sensors/gps/GPSData.h b/sensors/gps/GPSData.h index 0f52963..e314d85 100644 --- a/sensors/gps/GPSData.h +++ b/sensors/gps/GPSData.h @@ -3,6 +3,7 @@ #include "../../data/Timestamp.h" + struct GPSData { /** time this measurement was received (NOT the GPS-time) */ @@ -15,12 +16,32 @@ struct GPSData { float accuracy; // m [might be NAN] float speed; // m/s [might be NAN] + /** ctor for invalid/unknown data */ GPSData() : tsReceived(), lat(NAN), lon(NAN), alt(NAN), accuracy(NAN), speed(NAN) {;} + /** ctor */ GPSData(const Timestamp tsReceived, const float lat, const float lon, const float alt) : tsReceived(tsReceived), lat(lat), lon(lon), alt(alt), accuracy(NAN), speed(NAN) {;} + /** 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) {;} + /** data valid? */ + bool isValid() const { + return (lat == lat) && (lon == lon); + } + + bool operator == (const GPSData& o) const { + return EQ_OR_NAN(lat, o.lat) && + EQ_OR_NAN(lon, o.lon) && + EQ_OR_NAN(alt, o.alt) && + EQ_OR_NAN(accuracy, o.accuracy) && + EQ_OR_NAN(speed, o.speed); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} + }; #endif // GPSDATA_H diff --git a/sensors/imu/AccelerometerData.h b/sensors/imu/AccelerometerData.h index be9f262..4a1566f 100644 --- a/sensors/imu/AccelerometerData.h +++ b/sensors/imu/AccelerometerData.h @@ -42,11 +42,25 @@ struct AccelerometerData { return AccelerometerData(x/val, y/val, z/val); } - std::string asString() const { - std::stringstream ss; - ss << "(" << x << "," << y << "," << z << ")"; - return ss.str(); - } + std::string asString() const { + std::stringstream ss; + ss << "(" << x << "," << y << "," << z << ")"; + return ss.str(); + } + + bool isValid() const { + return (x == x) && (y == y) && (z == z); + } + + bool operator == (const AccelerometerData& o ) const { + return EQ_OR_NAN(x, o.x) && + EQ_OR_NAN(y, o.y) && + EQ_OR_NAN(z, o.z); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} }; diff --git a/sensors/imu/CompassData.h b/sensors/imu/CompassData.h new file mode 100644 index 0000000..c336091 --- /dev/null +++ b/sensors/imu/CompassData.h @@ -0,0 +1,56 @@ +#ifndef COMPASSDATA_H +#define COMPASSDATA_H + + +#include +#include + + +/** data received from a compass sensor */ +struct CompassData { + + /** azimuth angle. NAN if not available */ + float azimuth = NAN; + + /** describes the sensor's quality */ + float quality01 = 0; + + + /** empty ctor */ + CompassData() : azimuth(NAN) {;} + + /** data ctor */ + CompassData(const float azimuth) : azimuth(azimuth), quality01(0) {;} + + /** data ctor */ + CompassData(const float azimuth, const float quality01) : azimuth(azimuth), quality01(quality01) {;} + + /** get an instance describing invalid data */ + static CompassData INVALID() { + return CompassData(NAN); + } + + /** convert to string */ + std::string asString() const { + std::stringstream ss; + ss << "(" << azimuth << ")"; + return ss.str(); + } + + /** is the compass data valid? [compass present] */ + bool isValid() const { + return azimuth == azimuth; + } + + bool operator == (const CompassData& o) const { + return EQ_OR_NAN(azimuth, o.azimuth) && + EQ_OR_NAN(quality01, o.quality01); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} + +}; + +#endif // COMPASSDATA_H diff --git a/sensors/imu/GravityData.h b/sensors/imu/GravityData.h index d522f20..3d87b0b 100644 --- a/sensors/imu/GravityData.h +++ b/sensors/imu/GravityData.h @@ -42,11 +42,25 @@ struct GravityData { return GravityData(x/val, y/val, z/val); } - std::string asString() const { - std::stringstream ss; - ss << "(" << x << "," << y << "," << z << ")"; - return ss.str(); - } + std::string asString() const { + std::stringstream ss; + ss << "(" << x << "," << y << "," << z << ")"; + return ss.str(); + } + + bool isValid() const { + return (x == x) && (y == y) && (z == z); + } + + bool operator == (const GravityData& o ) const { + return EQ_OR_NAN(x, o.x) && + EQ_OR_NAN(y, o.y) && + EQ_OR_NAN(z, o.z); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} }; diff --git a/sensors/imu/GyroscopeData.h b/sensors/imu/GyroscopeData.h index de7e725..d4f0ee3 100644 --- a/sensors/imu/GyroscopeData.h +++ b/sensors/imu/GyroscopeData.h @@ -23,11 +23,25 @@ struct GyroscopeData { return std::sqrt( x*x + y*y + z*z ); } - std::string asString() const { - std::stringstream ss; - ss << "(" << x << "," << y << "," << z << ")"; - return ss.str(); - } + std::string asString() const { + std::stringstream ss; + ss << "(" << x << "," << y << "," << z << ")"; + return ss.str(); + } + + bool isValid() const { + return (x == x) && (y == y) && (z == z); + } + + bool operator == (const GyroscopeData& o ) const { + return EQ_OR_NAN(x, o.x) && + EQ_OR_NAN(y, o.y) && + EQ_OR_NAN(z, o.z); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} }; diff --git a/sensors/imu/LinearAccelerationData.h b/sensors/imu/LinearAccelerationData.h index 9ae116c..df6962e 100644 --- a/sensors/imu/LinearAccelerationData.h +++ b/sensors/imu/LinearAccelerationData.h @@ -42,11 +42,25 @@ struct LinearAccelerationData { return LinearAccelerationData(x/val, y/val, z/val); } - std::string asString() const { - std::stringstream ss; - ss << "(" << x << "," << y << "," << z << ")"; - return ss.str(); - } + std::string asString() const { + std::stringstream ss; + ss << "(" << x << "," << y << "," << z << ")"; + return ss.str(); + } + + bool isValid() const { + return (x == x) && (y == y) && (z == z); + } + + bool operator == (const LinearAccelerationData& o ) const { + return EQ_OR_NAN(x, o.x) && + EQ_OR_NAN(y, o.y) && + EQ_OR_NAN(z, o.z); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} }; diff --git a/sensors/offline/FileReader.h b/sensors/offline/FileReader.h index bc30410..99d0fd4 100644 --- a/sensors/offline/FileReader.h +++ b/sensors/offline/FileReader.h @@ -14,286 +14,320 @@ #include "../../sensors/imu/GravityData.h" #include "../../sensors/imu/LinearAccelerationData.h" #include "../../sensors/beacon/BeaconMeasurements.h" - +#include "../../sensors/gps/GPSData.h" +#include "../../sensors/imu/CompassData.h" #include "../../geo/Point2.h" #include "../../grid/factory/v2/GridFactory.h" #include "../../grid/factory/v2/Importance.h" #include "../../floorplan/v2/Floorplan.h" -class FileReader { +#include "Splitter.h" +#include "Sensors.h" -public: +#warning "adjust to to use the new splitter for all parsers [gps, compass, etc. already do!]" - template 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) {;} - }; +namespace Offline { - enum class Sensor { - ACC, - GYRO, - WIFI, - POS, - BARO, - BEACON, - LIN_ACC, - GRAVITY, - }; + class FileReader { - /** 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) {;} - }; + public: - std::vector> groundTruth; - std::vector> wifi; - std::vector> beacon; - std::vector> acc; - std::vector> gyro; - std::vector> barometer; - std::vector> lin_acc; - std::vector> gravity; + std::vector> groundTruth; + std::vector> wifi; + std::vector> beacon; + std::vector> acc; + std::vector> gyro; + std::vector> barometer; + std::vector> lin_acc; + std::vector> gravity; + std::vector> gps; + std::vector> compass; - /** ALL entries */ - std::vector entries; + /** ALL entries */ + std::vector entries; -public: + static constexpr char sep = ';'; - FileReader(const std::string& file) { - parse(file); - } + public: - const std::vector& getEntries() const {return entries;} + /** empty ctor. call open() */ + FileReader() { + ; + } + /** ctor with filename */ + FileReader(const std::string& file) { + open(file); + } - const std::vector>& getGroundTruth() const {return groundTruth;} + /** open the given file */ + void open(const std::string& file) { + parse(file); + } - const std::vector>& getWiFiGroupedByTime() const {return wifi;} + const std::vector& getEntries() const {return entries;} - const std::vector>& getBeacons() const {return beacon;} - const std::vector>& getAccelerometer() const {return acc;} + const std::vector>& getGroundTruth() const {return groundTruth;} - const std::vector>& getGyroscope() const {return gyro;} + const std::vector>& getWiFiGroupedByTime() const {return wifi;} - const std::vector>& getBarometer() const {return barometer;} + const std::vector>& getBeacons() const {return beacon;} - const std::vector>& getLinearAcceleration() const {return lin_acc;} + const std::vector>& getAccelerometer() const {return acc;} - const std::vector>& getGravity() const {return gravity;} + const std::vector>& getGyroscope() const {return gyro;} -private: + const std::vector>& getGPS() const {return gps;} - void parse(const std::string& file) { + const std::vector>& getCompass() const {return compass;} - std::ifstream inp(file); - if (!inp.is_open() || inp.bad() || inp.eof()) {throw Exception("failed to open file" + file);} + const std::vector>& getBarometer() const {return barometer;} - while(!inp.eof() && !inp.bad()) { + const std::vector>& getLinearAcceleration() const {return lin_acc;} - uint64_t ts; - char delim; - int idx = -1; - std::string data; + const std::vector>& getGravity() const {return gravity;} - inp >> ts; - inp >> delim; - inp >> idx; - inp >> delim; - inp >> data; + private: - 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);} + void parse(const std::string& file) { - // TODO: this is a hack... - // the loop is called one additional time after the last entry - // and keeps the entries of entry + std::ifstream inp(file); + if (!inp.is_open() || inp.bad() || inp.eof()) {throw Exception("failed to open file" + file);} - } + while(!inp.eof() && !inp.bad()) { - inp.close(); + uint64_t ts; + char delim; + int idx = -1; + std::string data; - } + inp >> ts; + inp >> delim; + inp >> idx; + inp >> delim; + inp >> data; - void parseLinearAcceleration(const uint64_t ts, const std::string& data){ + if (idx == (int)Sensor::WIFI) {parseWiFi(ts, data);} + else if (idx == (int)Sensor::BEACON) {parseBeacons(ts, data);} + else if (idx == (int)Sensor::GROUND_TRUTH) {parseGroundTruth(ts, data);} + else if (idx == (int)Sensor::ACC) {parseAccelerometer(ts, data);} + else if (idx == (int)Sensor::GYRO) {parseGyroscope(ts, data);} + else if (idx == (int)Sensor::BARO) {parseBarometer(ts, data);} + else if (idx == (int)Sensor::LIN_ACC) {parseLinearAcceleration(ts,data);} + else if (idx == (int)Sensor::GRAVITY) {parseGravity(ts,data);} + else if (idx == (int)Sensor::COMPASS) {parseCompass(ts,data);} + else if (idx == (int)Sensor::GPS) {parseGPS(ts,data);} - const auto pos1 = data.find(';'); - const auto pos2 = data.find(';', pos1+1); + // TODO: this is a hack... + // the loop is called one additional time after the last entry + // and keeps the entries of entry - 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 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)); - } + inp.close(); - void parseGravity(const uint64_t ts, const std::string& data){ + } - const auto pos1 = data.find(';'); - const auto pos2 = data.find(';', pos1+1); + void parseLinearAcceleration(const uint64_t ts, const std::string& data){ - 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); + const auto pos1 = data.find(';'); + const auto pos2 = data.find(';', pos1+1); - TS 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)); - } + 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); - void parseAccelerometer(const uint64_t ts, const std::string& data) { + TS 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)); + } - const auto pos1 = data.find(';'); - const auto pos2 = data.find(';', pos1+1); + void parseGravity(const uint64_t ts, const std::string& data){ - 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); + const auto pos1 = data.find(';'); + const auto pos2 = data.find(';', pos1+1); - TS 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)); + 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 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 parseGyroscope(const uint64_t ts, const std::string& data) { + void parseAccelerometer(const uint64_t ts, const std::string& data) { - const auto pos1 = data.find(';'); - const auto pos2 = data.find(';', pos1+1); + 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); + 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 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)); + TS 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 parseWiFi(const uint64_t ts, const std::string& data) { + void parseGyroscope(const uint64_t ts, const std::string& data) { - std::string tmp = data; + const auto pos1 = data.find(';'); + const auto pos2 = data.find(';', pos1+1); - // add new wifi reading - wifi.push_back(TS(ts, WiFiMeasurements())); - entries.push_back(Entry(Sensor::WIFI, ts, wifi.size()-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); - // process all APs - while(!tmp.empty()) { + TS 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)); - 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); + void parseWiFi(const uint64_t ts, const std::string& data) { - tmp = tmp.substr(pos3); - assert(tmp[0] == ';'); tmp = tmp.substr(1); + WiFiMeasurements wifi; + Splitter s(data, sep); - // append AP to current scan-entry - WiFiMeasurement e(AccessPoint(mac), std::stoi(rssi), Timestamp::fromMS(ts)); - wifi.back().data.entries.push_back(e); - } + for (size_t i = 0; i < s.size(); i += 3) { - } + const std::string mac = s.get(i+0); + const float freq = s.getFloat(i+1); + const float rssi = s.getFloat(i+2); - void parseBeacons(const uint64_t ts, const std::string& data) { + // append AP to current scan-entry + WiFiMeasurement e(AccessPoint(mac), rssi, freq, Timestamp::fromMS(ts)); + wifi.entries.push_back(e); - 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); + // add new wifi reading + this->wifi.push_back(TS(ts, wifi)); + entries.push_back(Entry(Sensor::WIFI, ts, this->wifi.size()-1)); - //yes the timestamp is redundant here, but in case of multiusage... - TS 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 parseBeacons(const uint64_t ts, const std::string& data) { - void parseGroundTruth(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 auto pos1 = data.find(';'); - std::string gtIndex = data.substr(0, pos1); + 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); - TS elem(ts, std::stoi(gtIndex)); - groundTruth.push_back(elem); + //yes the timestamp is redundant here, but in case of multiusage... + TS 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 parseBarometer(const uint64_t ts, const std::string& data) { + void parseGroundTruth(const uint64_t ts, const std::string& data) { - const auto pos1 = data.find(';'); + const auto pos1 = data.find(';'); + std::string gtIndex = data.substr(0, pos1); - const std::string hPa = data.substr(0, pos1); + TS elem(ts, std::stoi(gtIndex)); + groundTruth.push_back(elem); - TS elem(ts, BarometerData(std::stof(hPa))); - barometer.push_back(elem); - entries.push_back(Entry(Sensor::BARO, ts, barometer.size()-1)); + } - } + void parseBarometer(const uint64_t ts, const std::string& data) { -public: - const Interpolator getGroundTruthPath(Floorplan::IndoorMap* map, std::vector gtPath) const { + BarometerData baro; + Splitter s(data, sep); - // finde alle positionen der waypoints im gtPath aus map - std::unordered_map waypointsMap; - for(Floorplan::Floor* f : map->floors){ - float h = f->atHeight; - for (Floorplan::GroundTruthPoint* gtp : f->gtpoints){ + baro.hPa = s.has(0) ? (s.getFloat(0)) : (NAN); - //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!"); - } + TS elem(ts, baro); + barometer.push_back(elem); + entries.push_back(Entry(Sensor::BARO, ts, barometer.size()-1)); - } - } + } - // bringe diese in richtige reihenfolge und füge timestamp hinzu - Interpolator interpol; + void parseCompass(const uint64_t ts, const std::string& data) { - int it = 0; - for(int id : gtPath){ - auto itMap = waypointsMap.find(id); - if(itMap == waypointsMap.end()) {throw std::string("waypoint not found in xml");} + CompassData compass; + Splitter s(data, sep); - //the time, when the gt button was clicked on the app - uint64_t tsGT = groundTruth[it++].ts; - interpol.add(tsGT, itMap->second); + compass.azimuth = s.has(0) ? (s.getFloat(0)) : (NAN); + compass.quality01 = s.has(1) ? (s.getFloat(1)) : (NAN); - } + TS elem(ts, compass); + this->compass.push_back(elem); + entries.push_back(Entry(Sensor::COMPASS, ts, this->compass.size()-1)); - if(gtPath.empty() || waypointsMap.empty() || groundTruth.empty()){ - throw std::string("No Ground Truth points found within the map.xml file"); - } + } - return interpol; - } + /** parse the given GPS entry */ + void parseGPS(const uint64_t ts, const std::string& data) { -}; + GPSData gps; + Splitter s(data, sep); + + gps.lat = s.has(0) ? (s.getFloat(0)) : (NAN); + gps.lon = s.has(1) ? (s.getFloat(1)) : (NAN); + gps.alt = s.has(2) ? (s.getFloat(2)) : (NAN); + gps.accuracy = s.has(3) ? (s.getFloat(3)) : (NAN); + gps.speed = s.has(4) ? (s.getFloat(4)) : (NAN); + + TS elem(ts, gps); + this->gps.push_back(elem); + entries.push_back(Entry(Sensor::GPS, ts, this->gps.size()-1)); + + + } + + + public: + const Interpolator getGroundTruthPath(Floorplan::IndoorMap* map, std::vector gtPath) const { + + // finde alle positionen der waypoints im gtPath aus map + std::unordered_map 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 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 diff --git a/sensors/offline/FileWriter.h b/sensors/offline/FileWriter.h new file mode 100644 index 0000000..4e2f97e --- /dev/null +++ b/sensors/offline/FileWriter.h @@ -0,0 +1,92 @@ +#ifndef FILEWRITER_H +#define FILEWRITER_H + +#include "../gps/GPSData.h" +#include "../imu/CompassData.h" +#include "../imu/LinearAccelerationData.h" +#include "../imu/GravityData.h" +#include "../pressure/BarometerData.h" +#include "../imu/GyroscopeData.h" +#include "../imu/AccelerometerData.h" +#include "../radio/WiFiMeasurements.h" +#include "Sensors.h" + +#include + +namespace Offline { + + class FileWriter { + + private: + + std::ofstream out; + static constexpr char sep = ';'; + const std::string nl = "\n"; + + public: + + FileWriter() { + ; + } + + ~FileWriter() { + close(); + } + + void open(const std::string& file) { + out.open(file); + if (!out) {throw Exception("error opening file: " + file);} + } + + void close() { + out.flush(); + out.close(); + } + + void flush() { + out.flush(); + } + + void add(const Timestamp ts, const AccelerometerData& data) { + out << ts.ms() << sep << (int) Sensor::ACC << sep << data.x << sep << data.y << sep << data.z << nl; + } + + void add(const Timestamp ts, const LinearAccelerationData& data) { + out << ts.ms() << sep << (int) Sensor::LIN_ACC << sep << data.x << sep << data.y << sep << data.z << nl; + } + + void add(const Timestamp ts, const GravityData& data) { + out << ts.ms() << sep << (int) Sensor::GRAVITY << sep << data.x << sep << data.y << sep << data.z << nl; + } + + void add(const Timestamp ts, const GyroscopeData& data) { + out << ts.ms() << sep << (int) Sensor::GYRO << sep << data.x << sep << data.y << sep << data.z << nl; + } + + void add(const Timestamp ts, const BarometerData& data) { + out << ts.ms() << sep << (int) Sensor::BARO << sep << data.hPa << nl; + } + + void add(const Timestamp ts, const GPSData& data) { + out << ts.ms() << sep << (int) Sensor::GPS << sep << data.lat << sep << data.lon << sep << data.alt << sep << data.accuracy << sep << data.speed << nl; + } + + void add(const Timestamp ts, const CompassData& data) { + out << ts.ms() << sep << (int) Sensor::COMPASS << sep << data.azimuth << sep << data.quality01 << nl; + } + + void add(const Timestamp ts, const WiFiMeasurements& data) { + out << ts.ms() << sep << (int) Sensor::WIFI; + for (const WiFiMeasurement& m : data.entries) { + out << sep << m.getAP().getMAC().asString(); + out << sep << m.getFrequency(); + out << sep << m.getRSSI(); + } + out << "\n"; + } + + }; + +} + +#endif // FILEWRITER_H diff --git a/sensors/offline/Listener.h b/sensors/offline/Listener.h new file mode 100644 index 0000000..d9be0a6 --- /dev/null +++ b/sensors/offline/Listener.h @@ -0,0 +1,33 @@ +#ifndef OFFLINE_LISTENER_H +#define OFFLINE_LISTENER_H + +#include "../gps/GPSData.h" +#include "../imu/CompassData.h" +#include "../imu/GravityData.h" +#include "../pressure/BarometerData.h" +#include "../imu/GyroscopeData.h" +#include "../imu/AccelerometerData.h" +#include "../radio/WiFiMeasurements.h" + +namespace Offline { + + /** + * listen for events/callbacks while parsing offline files + */ + class Listener { + + public: + + virtual void onGyroscope(const Timestamp ts, const GyroscopeData data) = 0; + virtual void onAccelerometer(const Timestamp ts, const AccelerometerData data) = 0; + virtual void onGravity(const Timestamp ts, const AccelerometerData data) = 0; + virtual void onWiFi(const Timestamp ts, const WiFiMeasurements data) = 0; + virtual void onBarometer(const Timestamp ts, const BarometerData data) = 0; + virtual void onGPS(const Timestamp ts, const GPSData data) = 0; + virtual void onCompass(const Timestamp ts, const CompassData data) = 0; + + }; + +} + +#endif // OFFLINE_LISTENER_H diff --git a/sensors/offline/OfflineAndroid.h b/sensors/offline/OfflineAndroid.h index 6d8801d..19049b1 100644 --- a/sensors/offline/OfflineAndroid.h +++ b/sensors/offline/OfflineAndroid.h @@ -12,8 +12,14 @@ #include "../radio/WiFiMeasurements.h" #include "../imu/AccelerometerData.h" #include "../imu/GyroscopeData.h" +#include "../imu/CompassData.h" +#include "../gps/GPSData.h" #include "../pressure/BarometerData.h" +#include "Splitter.h" +#include "Listener.h" +#include "Sensors.h" + template struct OfflineEntry { Timestamp ts; @@ -35,20 +41,12 @@ struct WalkedPath { }; -/** listener for event callbacks */ -class OfflineAndroidListener { -public: - virtual void onGyroscope(const Timestamp ts, const GyroscopeData data) = 0; - virtual void onAccelerometer(const Timestamp ts, const AccelerometerData data) = 0; - virtual void onGravity(const Timestamp ts, const AccelerometerData data) = 0; - virtual void onWiFi(const Timestamp ts, const WiFiMeasurements data) = 0; - virtual void onBarometer(const Timestamp ts, const BarometerData data) = 0; -}; - -/** read recorded android sensor data files */ +/** + * read sensor data files that were recorded using + * the old java android app + */ class OfflineAndroid { - private: std::vector> wifi; @@ -57,11 +55,15 @@ private: std::vector> accel; std::vector> gravity; + std::vector> compass; std::vector> barometer; + std::vector> gps; + WalkedPath walkedPath; + static constexpr char sep = ';'; const char* name = "OfflineData"; public: @@ -89,6 +91,12 @@ public: /** get all barometer readings */ const std::vector>& getBarometer() const {return barometer;} + /** get all compass readings */ + const std::vector>& getCompass() const {return compass;} + + /** get all gps readings */ + const std::vector>& getGPS() const {return gps;} + /** get the walked path */ const WalkedPath& getWalkedPath() const {return walkedPath;} @@ -105,7 +113,7 @@ public: public: - void parse(const std::string& file, OfflineAndroidListener* listener = nullptr) { + void parse(const std::string& file, Offline::Listener* listener = nullptr) { Log::add(name, "parsing data file: " + file , false); Log::tick(); @@ -152,47 +160,61 @@ public: private: /** parse the given data */ - void parse(const Timestamp ts, const int32_t sensorID, const std::string& sensorData, OfflineAndroidListener* listener) { + void parse(const Timestamp ts, const int32_t sensorID, const std::string& sensorData, Offline::Listener* listener) { // how to parse switch(sensorID) { - case 0: { + case (int) Offline::Sensor::ACC: { const AccelerometerData data = parseAccelerometer(sensorData); accel.push_back(OfflineEntry(ts, data)); if (listener) {listener->onAccelerometer(ts, data);} break; } - case 1: { + case (int) Offline::Sensor::GRAVITY: { const AccelerometerData data = parseAccelerometer(sensorData); gravity.push_back(OfflineEntry(ts, data)); if (listener) {listener->onGravity(ts, data);} break; } - case 3: { + case (int) Offline::Sensor::GYRO: { const GyroscopeData data = parseGyroscope(sensorData); gyro.push_back(OfflineEntry(ts, data)); if (listener) {listener->onGyroscope(ts, data);} break; } - case 5: { + case (int) Offline::Sensor::BARO: { const BarometerData data = parseBarometer(sensorData); barometer.push_back(OfflineEntry(ts, data)); if (listener) {listener->onBarometer(ts, data);} break; } - case 8: { + case (int) Offline::Sensor::WIFI: { const WiFiMeasurements data = parseWiFi(ts, sensorData); wifi.push_back(OfflineEntry(ts, data)); if (listener) {listener->onWiFi(ts, data);} break; } - case 99: { + case (int) Offline::Sensor::COMPASS: { + const CompassData data = parseCompass(sensorData); + compass.push_back(OfflineEntry(ts, data)); + if (listener) {listener->onCompass(ts, data);} + break; + } + + case (int) Offline::Sensor::GPS: { + const GPSData data = parseGPS(sensorData); + gps.push_back(OfflineEntry(ts, data)); + if (listener) {listener->onGPS(ts, data);} + break; + } + + case (int) Offline::Sensor::GROUND_TRUTH: { const GroundTruthID data = parseGroundTruthTick(sensorData); groundTruth.push_back(OfflineEntry(ts, data)); // TODO listener @@ -326,6 +348,35 @@ private: } + /** parse the given Compass entry */ + static inline CompassData parseCompass(const std::string& data) { + + CompassData compass; + Splitter s(data, sep); + + compass.azimuth = s.has(0) ? (s.getFloat(0)) : (NAN); + compass.quality01 = s.has(1) ? (s.getFloat(1)) : (NAN); + + return compass; + + } + + /** parse the given GPS entry */ + static inline GPSData parseGPS(const std::string& data) { + + GPSData gps; + Splitter s(data, sep); + + gps.lat = s.has(0) ? (s.getFloat(0)) : (NAN); + gps.lon = s.has(1) ? (s.getFloat(1)) : (NAN); + gps.alt = s.has(2) ? (s.getFloat(2)) : (NAN); + gps.accuracy = s.has(3) ? (s.getFloat(3)) : (NAN); + gps.speed = s.has(4) ? (s.getFloat(4)) : (NAN); + + return gps; + + } + }; #endif // OFFLINEANDROID_H diff --git a/sensors/offline/Sensors.h b/sensors/offline/Sensors.h new file mode 100644 index 0000000..47ffec6 --- /dev/null +++ b/sensors/offline/Sensors.h @@ -0,0 +1,38 @@ +#ifndef OFFLINE_SENSORS_H +#define OFFLINE_SENSORS_H + +namespace Offline { + + enum class Sensor { + ACC = 0, + GRAVITY = 1, + LIN_ACC = 2, + GYRO = 3, + BARO = 5, + WIFI = 8, + BEACON = 9, + COMPASS = 15, + GPS = 16, + GROUND_TRUTH = 99, + POS = 1001, // IPIN2016 + }; + + template 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) {;} + }; + + /** 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) {;} + }; + + +} + +#endif // OFFLINE_SENSORS_H diff --git a/sensors/offline/Splitter.h b/sensors/offline/Splitter.h new file mode 100644 index 0000000..ae09d43 --- /dev/null +++ b/sensors/offline/Splitter.h @@ -0,0 +1,53 @@ +#ifndef DATA_SPLITTER_H +#define DATA_SPLITTER_H + +#include +#include + +/** + * split an input-file into various tokens + */ +class Splitter { + + std::string str; + char sep = ';'; + std::vector split; + +public: + + /** ctor */ + Splitter(const std::string& str, const char sep = ';') : str(str), sep(sep) { + build(); + } + + bool has(const int idx) const {return split.size() > idx;} + + const std::string& get(const int idx) const {return split.at(idx);} + + const float getFloat(const int idx) const {return std::stof(get(idx));} + + size_t size() const {return split.size();} + +private: + + void build() { + + std::string cur; + + for (char c : str) { + if (c == sep) { + split.push_back(cur); + cur = ""; + } else { + cur += c; + } + } + + split.push_back(cur); + + } + +}; + + +#endif // DATA_SPLITTER_H diff --git a/sensors/pressure/BarometerData.h b/sensors/pressure/BarometerData.h index b9e6e6d..23d17c6 100644 --- a/sensors/pressure/BarometerData.h +++ b/sensors/pressure/BarometerData.h @@ -13,6 +13,19 @@ struct BarometerData { explicit BarometerData(const float hPa) : hPa(hPa) {;} + /** valid data? */ + bool isValid() const { + return hPa == hPa; + } + + bool operator == (const BarometerData& o ) const { + return EQ_OR_NAN(hPa, o.hPa); + } + +private: + + static inline bool EQ_OR_NAN(const float a, const float b) {return (a==b) || ( (a!=a) && (b!=b) );} + }; #endif // BAROMETERDATA_H diff --git a/sensors/radio/WiFiMeasurement.h b/sensors/radio/WiFiMeasurement.h index d84327d..e8d5d81 100644 --- a/sensors/radio/WiFiMeasurement.h +++ b/sensors/radio/WiFiMeasurement.h @@ -20,7 +20,7 @@ private: float rssi; /** OPTIONAL. frequence the signal was received */ - float freq; + float freq = NAN; /** OPTIONAL. timestamp the measurement was recorded at */ Timestamp ts; @@ -28,7 +28,7 @@ private: public: /** ctor */ - WiFiMeasurement(const AccessPoint& ap, const float rssi) : ap(ap), rssi(rssi) { + WiFiMeasurement(const AccessPoint& ap, const float rssi) : ap(ap), rssi(rssi), freq(NAN) { ; } diff --git a/sensors/radio/setup/WiFiOptimizerLogDistCeiling.h b/sensors/radio/setup/WiFiOptimizerLogDistCeiling.h index 8bd3e42..d093b94 100644 --- a/sensors/radio/setup/WiFiOptimizerLogDistCeiling.h +++ b/sensors/radio/setup/WiFiOptimizerLogDistCeiling.h @@ -94,6 +94,15 @@ namespace WiFiOptimizer { return ss.str(); } + /** we add some constraints to the parameter range */ + bool outOfRange() const { + return (waf > 0) || + (txp < -50) || + (txp > -30) || + (exp > 4) || + (exp < 1); + } + }; /** add MAC-info to params */ @@ -136,17 +145,17 @@ namespace WiFiOptimizer { return false; }; - const APFilter MIN_8_FPS = [] (const int numFingerprints, const MACAddress& mac) { + const APFilter MIN_5_FPS = [] (const int numFingerprints, const MACAddress& mac) { (void) mac; - return numFingerprints < 8; + return numFingerprints < 5; }; private: - Mode mode = Mode::QUALITY; - Floorplan::IndoorMap* map; + Mode mode = Mode::QUALITY; + const char* name = "WiFiOptLDC"; public: @@ -182,6 +191,7 @@ namespace WiFiOptimizer { } const float avgErr = errSum / errCnt; + Log::add(name, "optimized APs: " + std::to_string(errCnt)); Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB"); // done @@ -189,6 +199,7 @@ namespace WiFiOptimizer { } + /** optimize the given AP */ APParams optimize(const MACAddress& mac, Stats& res) const { @@ -210,8 +221,8 @@ namespace WiFiOptimizer { LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z - LeOpt::MinMax(-50, -30), // txp - LeOpt::MinMax(1, 5), // exp + LeOpt::MinMax(-50, -30), // txp + LeOpt::MinMax(1, 4), // exp LeOpt::MinMax(-15, -0), // waf }; @@ -271,17 +282,10 @@ namespace WiFiOptimizer { float getErrorLogDistCeiling(const MACAddress& mac, const std::vector& entries, const float* data, Stats* stats = nullptr) const { - constexpr float hugeError = 1e10; const APParams* params = (APParams*) data; // some sanity checks - if (params->waf > 0) {return hugeError;} - - if (params->txp < -50) {return hugeError;} - if (params->txp > -30) {return hugeError;} - - if (params->exp > 4) {return hugeError;} - if (params->exp < 1) {return hugeError;} + if (params->outOfRange()) {return 1e10;} // current position guess for the AP; const Point3 apPos_m = params->getPos(); @@ -309,7 +313,7 @@ namespace WiFiOptimizer { } // adjust the error - err += diff*diff; + err += std::pow(std::abs(diff), 2.0); ++cnt; // max distance penality diff --git a/tests/sensors/imu/TestMotionDetection.cpp b/tests/sensors/imu/TestMotionDetection.cpp index 47bc411..eb76a40 100644 --- a/tests/sensors/imu/TestMotionDetection.cpp +++ b/tests/sensors/imu/TestMotionDetection.cpp @@ -38,7 +38,7 @@ TEST(MotionDetection, motionAxis) { //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); + Offline::FileReader fr(filename); K::Gnuplot gp; K::GnuplotPlot plot; @@ -52,14 +52,14 @@ TEST(MotionDetection, motionAxis) { Timestamp lastTs; //calc motion axis - for (const FileReader::Entry& e : fr.getEntries()) { + for (const Offline::Entry& e : fr.getEntries()) { ts = Timestamp::fromMS(e.ts); - if (e.type == FileReader::Sensor::LIN_ACC) { + if (e.type == Offline::Sensor::LIN_ACC) { md.addLinearAcceleration(ts, fr.getLinearAcceleration()[e.idx].data); - } else if (e.type == FileReader::Sensor::GRAVITY) { + } else if (e.type == Offline::Sensor::GRAVITY) { md.addGravity(ts, fr.getGravity()[e.idx].data); curVec = md.getCurrentMotionAxis(); motionAxisAngleRad = md.getMotionChangeInRad(); @@ -126,7 +126,7 @@ TEST(MotionDetection, motionAngle) { //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); + Offline::FileReader fr(filename); Timestamp ts; //save for later plotting @@ -134,23 +134,23 @@ TEST(MotionDetection, motionAngle) { std::vector delta_turnAngles; //calc motion axis - for (const FileReader::Entry& e : fr.getEntries()) { + for (const Offline::Entry& e : fr.getEntries()) { ts = Timestamp::fromMS(e.ts); - if (e.type == FileReader::Sensor::LIN_ACC) { + if (e.type == Offline::Sensor::LIN_ACC) { md.addLinearAcceleration(ts, fr.getLinearAcceleration()[e.idx].data); - } else if (e.type == FileReader::Sensor::GRAVITY) { + } else if (e.type == Offline::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& _acc = fr.getAccelerometer()[e.idx]; + } else if (e.type == Offline::Sensor::ACC) { + const Offline::TS& _acc = fr.getAccelerometer()[e.idx]; td.addAccelerometer(ts, _acc.data); - } else if (e.type == FileReader::Sensor::GYRO) { - const FileReader::TS& _gyr = fr.getGyroscope()[e.idx]; + } else if (e.type == Offline::Sensor::GYRO) { + const Offline::TS& _gyr = fr.getGyroscope()[e.idx]; delta_turnAngles.push_back(td.addGyroscope(ts, _gyr.data)); } diff --git a/tests/sensors/offline/TestReadWrite.cpp b/tests/sensors/offline/TestReadWrite.cpp new file mode 100644 index 0000000..c13c6f2 --- /dev/null +++ b/tests/sensors/offline/TestReadWrite.cpp @@ -0,0 +1,89 @@ +#ifdef WITH_TESTS + +#include "../../Tests.h" + +#include "../../../sensors/offline/FileReader.h" +#include "../../../sensors/offline/FileWriter.h" + +TEST(Offline, readWrite) { + + std::string fileName = "/tmp/test.dat"; + + Offline::FileWriter out; + out.open(fileName); + + const GPSData gps(Timestamp::fromMS(1), 2, 3, 4); + out.add(Timestamp::fromMS(11), gps); + + const CompassData compass(4, 2); + out.add(Timestamp::fromMS(13), compass); + + const BarometerData baro(3); + out.add(Timestamp::fromMS(15), baro); + + const AccelerometerData acc(3,4,5); + out.add(Timestamp::fromMS(17), acc); + + const GravityData grav(5,9,7); + out.add(Timestamp::fromMS(19), grav); + + const GyroscopeData gyro(8, 5,11); + out.add(Timestamp::fromMS(21), gyro); + + const LinearAccelerationData lina(13, 12, 11); + out.add(Timestamp::fromMS(23), lina); + + WiFiMeasurements w1; + w1.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:55:66")), -70)); + w1.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:55:67")), -72)); + w1.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:55:68")), -74)); + out.add(Timestamp::fromMS(25), w1); + + WiFiMeasurements w2; + w2.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:aa:66")), -60)); + w2.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:aa:67")), -62)); + w2.entries.push_back(WiFiMeasurement(AccessPoint(MACAddress("11:22:33:44:aa:68")), -64)); + out.add(Timestamp::fromMS(27), w2); + + out.close(); + + Offline::FileReader reader; + reader.open(fileName); + + // check number of entries + ASSERT_EQ(1, reader.getGPS().size()); + ASSERT_EQ(1, reader.getCompass().size()); + ASSERT_EQ(1, reader.getBarometer().size()); + ASSERT_EQ(1, reader.getAccelerometer().size()); + ASSERT_EQ(1, reader.getGravity().size()); + ASSERT_EQ(1, reader.getGyroscope().size()); + ASSERT_EQ(1, reader.getLinearAcceleration().size()); + ASSERT_EQ(2, reader.getWiFiGroupedByTime().size()); + + // check timestamps + ASSERT_EQ(11, reader.getGPS().front().ts); + ASSERT_EQ(13, reader.getCompass().front().ts); + ASSERT_EQ(15, reader.getBarometer().front().ts); + ASSERT_EQ(17, reader.getAccelerometer().front().ts); + ASSERT_EQ(19, reader.getGravity().front().ts); + ASSERT_EQ(21, reader.getGyroscope().front().ts); + ASSERT_EQ(23, reader.getLinearAcceleration().front().ts); + ASSERT_EQ(25, reader.getWiFiGroupedByTime().front().ts); + ASSERT_EQ(27, reader.getWiFiGroupedByTime().back().ts); + + // check content + ASSERT_EQ(gps, reader.getGPS().front().data); + ASSERT_EQ(compass, reader.getCompass().front().data); + ASSERT_EQ(baro, reader.getBarometer().front().data); + ASSERT_EQ(acc, reader.getAccelerometer().front().data); + ASSERT_EQ(grav, reader.getGravity().front().data); + ASSERT_EQ(gyro, reader.getGyroscope().front().data); + ASSERT_EQ(lina, reader.getLinearAcceleration().front().data); + + int i = 0; (void) i; + +} + + + +#endif