current revision
This commit is contained in:
104
ipin/Config.h
Normal file
104
ipin/Config.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef EVALCONFIG_H
|
||||
#define EVALCONFIG_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include "IPINHelper.h"
|
||||
|
||||
class EvalConfig {
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_map<std::string, std::string> keyVal;
|
||||
|
||||
public:
|
||||
|
||||
EvalConfig() {
|
||||
;
|
||||
}
|
||||
|
||||
/** get the value for the given key */
|
||||
const std::string& get(const std::string& key) {
|
||||
static std::string empty = "";
|
||||
auto it = keyVal.find(key);
|
||||
return (it == keyVal.end()) ? (empty) : (it->second);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** load the given configuration file */
|
||||
void load(const std::string& file) {
|
||||
|
||||
std::ifstream inp(file);
|
||||
|
||||
if (!inp.good()) {throw "error while opening config file " + file;}
|
||||
|
||||
|
||||
std::string line;
|
||||
std::string tmp;
|
||||
while (!inp.eof()) {
|
||||
|
||||
// read the next line (might be empty)
|
||||
std::getline(inp, tmp);
|
||||
if (tmp.empty()) {continue;}
|
||||
|
||||
// in case of \r\n linebreaks, remove the remaining \r (might hereafter be empty)
|
||||
if (tmp.back() == '\r') {tmp.erase(tmp.end()-1);}
|
||||
if (tmp.empty()) {continue;}
|
||||
|
||||
// multiline?
|
||||
bool multiline = false;
|
||||
if (tmp.back() == '\\') {multiline = true; tmp.erase(tmp.end()-1);}
|
||||
|
||||
// attach
|
||||
line += tmp;
|
||||
|
||||
// complete line? -> parse it
|
||||
if (!multiline) {parse(line); line = "";}
|
||||
|
||||
}
|
||||
|
||||
// trailing data
|
||||
parse(line);
|
||||
|
||||
inp.close();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** parse one line */
|
||||
void parse(const std::string& line) {
|
||||
|
||||
// check
|
||||
if (line.empty()) {return;}
|
||||
if (line.front() == '[') {return;}
|
||||
|
||||
// split key and value
|
||||
const size_t pos = line.find(" = ");
|
||||
if (pos == std::string::npos) {throw "something wrong";}
|
||||
|
||||
const std::string key = line.substr(0, pos);
|
||||
const std::string val = line.substr(pos+3);
|
||||
|
||||
// add
|
||||
keyVal[key] = val;
|
||||
|
||||
}
|
||||
|
||||
/** get the eval config.ini filename */
|
||||
std::string getConfigFile() const {
|
||||
|
||||
const std::string folder = IPINHelper::getDataFolder();
|
||||
const std::string file = folder + "it.cnr.isti.steplogger.config.ini";
|
||||
return file;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // EVALCONFIG_H
|
||||
22
ipin/IPINHelper.h
Normal file
22
ipin/IPINHelper.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef IPINHELPER_H
|
||||
#define IPINHELPER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
class IPINHelper {
|
||||
|
||||
public:
|
||||
|
||||
/** get the StepLogger data folder */
|
||||
static std::string getDataFolder() {
|
||||
|
||||
#ifdef ANDROID
|
||||
return getenv("DIRECTORY_DOWNLOADS");
|
||||
#else
|
||||
return "/apps/android/workspace/YASMIN_DATA/";
|
||||
#endif
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif // IPINHELPER_H
|
||||
104
ipin/Scaler.h
Normal file
104
ipin/Scaler.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef IPIN_SCALER_H
|
||||
#define IPIN_SCALER_H
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
struct IPIN {
|
||||
double lat;
|
||||
double lon;
|
||||
double floorNr;
|
||||
IPIN(const double lat, const double lon, const double floorNr) : lat(lat), lon(lon), floorNr(floorNr) {;}
|
||||
};
|
||||
|
||||
class IPINScaler {
|
||||
|
||||
private:
|
||||
|
||||
int w;
|
||||
int h;
|
||||
double cenLat;
|
||||
double cenLon;
|
||||
double rotDeg;
|
||||
double mPerPx;
|
||||
|
||||
Point2 mapCenter_m;
|
||||
|
||||
double refLat;
|
||||
double m_per_deg_lat;
|
||||
double m_per_deg_lon;
|
||||
|
||||
const float fixedFloorHeight = 4.0f;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** image width, image height, map-center (lat/lon), map-rotation, meter<->pixel conversion factor */
|
||||
IPINScaler(int w, int h, double cenLat, double cenLon, double rotDeg, double mPerPx) : w(w), h(h), cenLat(cenLat), cenLon(cenLon), rotDeg(rotDeg), mPerPx(mPerPx) {
|
||||
|
||||
mapCenter_m = Point2( (w*mPerPx/2), (h*mPerPx/2) );
|
||||
|
||||
refLat = cenLat / 180.0 * M_PI;
|
||||
m_per_deg_lat = 111132.954 - 559.822 * std::cos( 2.0 * refLat ) + 1.175 * std::cos( 4.0 * refLat);
|
||||
m_per_deg_lon = 111132.954 * std::cos ( refLat );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** convert from (x,y,z) to (lat,lon,floorNr) */
|
||||
IPIN toIPIN3(const float x, const float y, const float z) const {
|
||||
|
||||
Point2 pos(x,y);
|
||||
|
||||
// move to (0,0)
|
||||
pos -= mapCenter_m;
|
||||
|
||||
// undo the rotation
|
||||
pos = pos.rotated(-rotDeg / 180 * M_PI);
|
||||
|
||||
const double lat = cenLat + (pos.y / m_per_deg_lat);
|
||||
const double lon = cenLon + (pos.x / m_per_deg_lon);
|
||||
const double floorNr = z / fixedFloorHeight;
|
||||
|
||||
return IPIN(lat, lon, floorNr);
|
||||
|
||||
}
|
||||
|
||||
IPIN toIPIN3(const Point3 p) const {
|
||||
return toIPIN3(p.x, p.y, p.z);
|
||||
}
|
||||
|
||||
Point3 convert3D(const IPIN& ipin) const {
|
||||
return convert3D(ipin.lat, ipin.lon, ipin.floorNr);
|
||||
}
|
||||
|
||||
Point3 convert3D(double lat, double lon, float floorNr) const {
|
||||
|
||||
Point2 p2 = convert2D(lat, lon);
|
||||
|
||||
return Point3(p2.x, p2.y, floorNr * fixedFloorHeight);
|
||||
|
||||
}
|
||||
|
||||
Point2 convert2D(double lat, double lon) const {
|
||||
|
||||
const double y_m = +(lat-cenLat) * m_per_deg_lat;
|
||||
const double x_m = +(lon-cenLon) * m_per_deg_lon;
|
||||
|
||||
// rotate (our map is axis aligned)
|
||||
Point2 pos(x_m, y_m);
|
||||
pos = pos.rotated(rotDeg / 180 * M_PI);
|
||||
|
||||
// apply movement
|
||||
pos += mapCenter_m;
|
||||
|
||||
return pos;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // IPIN_SCALER_H
|
||||
188
ipin/StepLogger.h
Normal file
188
ipin/StepLogger.h
Normal file
@@ -0,0 +1,188 @@
|
||||
#ifndef STEPLOGGER_H
|
||||
#define STEPLOGGER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include <iomanip>
|
||||
#include <time.h>
|
||||
|
||||
#include <Indoor/data/Timestamp.h>
|
||||
#include "Scaler.h"
|
||||
#include "Config.h"
|
||||
#include "IPINHelper.h"
|
||||
|
||||
class StepLogger {
|
||||
|
||||
private:
|
||||
|
||||
EvalConfig cfg;
|
||||
std::string configFile;
|
||||
std::string competitorID = "navindoor";
|
||||
|
||||
|
||||
std::ofstream outButtons;
|
||||
std::ofstream outPositions;
|
||||
|
||||
/** all step-logger wayponts defined within the config file */
|
||||
std::vector<std::string> waypoints;
|
||||
int curWP = 0;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the map<->world scaler */
|
||||
StepLogger() {
|
||||
loadConfig();
|
||||
start();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** log current timestamp + waypoint and proceed with the next one */
|
||||
void onButtonPress() {
|
||||
|
||||
// sanity check
|
||||
if (isDone()) {throw "all waypoints were processed";}
|
||||
|
||||
// construct output string
|
||||
std::stringstream ss;
|
||||
std::string wpEntry = waypoints[curWP]; ++curWP;
|
||||
wpEntry = replace(wpEntry, ":" , " : ");
|
||||
ss << Timestamp::fromUnixTime().ms() << " : " << wpEntry << "\r\n";
|
||||
|
||||
// log
|
||||
std::cout << ss.str() << std::flush;
|
||||
outButtons << ss.str(); outButtons.flush();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** all waypoints done? */
|
||||
bool isDone() const {
|
||||
return curWP >= (int)waypoints.size();
|
||||
}
|
||||
|
||||
|
||||
/** log our map-relative format */
|
||||
void onNewEstimation(const double lat, const double lon, const double floorNr) {
|
||||
|
||||
// construct output string
|
||||
std::stringstream ss;
|
||||
ss << Timestamp::fromUnixTime().ms() << " : " << lat << " : " << lon << " : " << floorNr << "\r\n";
|
||||
|
||||
// log
|
||||
std::cout << ss.str() << std::endl;
|
||||
outPositions << ss.str(); outPositions.flush();
|
||||
|
||||
}
|
||||
|
||||
/** get the current waypoint's number, starting at 0 */
|
||||
int getCurrentWaypointNumber() const {
|
||||
return curWP;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** split the given string */
|
||||
std::vector<std::string> split(const std::string& str, const char delim) {
|
||||
std::vector<std::string> res;
|
||||
int start = 0;
|
||||
while (true) {
|
||||
size_t pos = str.find(delim, start);
|
||||
if (pos == std::string::npos) {break;}
|
||||
const std::string sub = str.substr(start, pos-start);
|
||||
res.push_back(sub);
|
||||
start = pos + 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** string replacement helper */
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/** get the current time as [YYYYMMDD]T[HHMMSS] */
|
||||
std::string getDate() const {
|
||||
const char* format = "%Y%m%dT%H%m%S";
|
||||
std::time_t t = std::time(nullptr);
|
||||
struct tm* tt = std::localtime(&t);
|
||||
|
||||
char buf[128];
|
||||
strftime(buf, 128, format, tt);
|
||||
return std::string(buf);
|
||||
|
||||
// std::stringstream ss;
|
||||
// ss << std::put_time(tt, format);
|
||||
// return ss.str();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** load the config.ini and parse the waypoints */
|
||||
void loadConfig() {
|
||||
cfg.load(IPINHelper::getDataFolder() + "it.cnr.isti.steplogger.config.ini");
|
||||
const std::string tmp = cfg.get("counter");
|
||||
waypoints = split(tmp, ',');
|
||||
}
|
||||
|
||||
/** create the data-folder, open the two output files and perform sanity checks */
|
||||
void start() {
|
||||
|
||||
// get the output data folder and construct filenames
|
||||
const std::string folder = getOutputFolder();
|
||||
const std::string fBtn = folder + "buttonsPressed.log";
|
||||
const std::string fPos = folder + "positions.log";
|
||||
|
||||
// open two output files
|
||||
outButtons.open(fBtn); if (!outButtons.good()) {throw "error while creating file";}
|
||||
outPositions.open(fPos); if (!outPositions.good()) {throw "error while creating file";}
|
||||
|
||||
// reset current WayPoint
|
||||
curWP = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** get a DateTime dependent data folder */
|
||||
std::string getOutputFolder() {
|
||||
const std::string& folder = IPINHelper::getDataFolder();
|
||||
const std::string date = getDate();
|
||||
const std::string sub = folder + date + "_" + competitorID + "/";
|
||||
mkdir(sub.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
return sub;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // config file
|
||||
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) +
|
||||
// File.separator +
|
||||
// "it.cnr.isti.steplogger.config.ini";
|
||||
|
||||
// buttonsPressed.log
|
||||
// milliseconds " : " number-starting-at-1" : "???" : "???" : "floor? // see configFile config.ini
|
||||
// java current time millis
|
||||
// positions.log
|
||||
// timestamp " " x " " y " " z
|
||||
|
||||
// storage/sdcard0/Download/it.cnr.isti.steplogger/
|
||||
|
||||
// Within the folder it.cnr.isti.steplogger/ StepLogger creates a folder for every measurement session, the logs are placed in folder whose name follows this convention:
|
||||
|
||||
// [YearMonthDay]T[HourMinutesSeconds][Competitor ID]
|
||||
// // Coordinates will need to be in the WGS84 coordinate system (longitude and latitude) for x, y,
|
||||
// //and the number of floor (an integer starting from 0) for z.
|
||||
|
||||
};
|
||||
|
||||
#endif // STEPLOGGER_H
|
||||
97
ipin/StepLoggerWrapper.h
Normal file
97
ipin/StepLoggerWrapper.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef STEPLOGGERWRAPPER_H
|
||||
#define STEPLOGGERWRAPPER_H
|
||||
|
||||
#include "Scaler.h"
|
||||
#include "StepLogger.h"
|
||||
#include "../nav/NavControllerListener.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QWindow>
|
||||
#include <QMainWindow>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
/**
|
||||
* helper class to connect our NavigationController with the StepLogger
|
||||
*/
|
||||
class StepLoggerWrapper : public StepLogger, public NavControllerListener {
|
||||
|
||||
private:
|
||||
|
||||
/** convert from our position-format to ipin position-format */
|
||||
IPINScaler& scaler;
|
||||
|
||||
QMainWindow* window;
|
||||
QPushButton* btn;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
StepLoggerWrapper(IPINScaler& scaler) : scaler(scaler) {
|
||||
setupUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* event fired every 500ms from the navigation controller.
|
||||
* convert from our format to IPIN format
|
||||
* and pass it to the logger
|
||||
*/
|
||||
void onNewEstimation(const Point3 pos_m) override {
|
||||
|
||||
// convert from our coordinate system (meter relative to (0,0,0)) to the WGS84 format
|
||||
const IPIN ipin = scaler.toIPIN3(pos_m);
|
||||
|
||||
// inform the logger
|
||||
StepLogger::onNewEstimation(ipin.lat, ipin.lon, ipin.floorNr);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** create a clickable check button */
|
||||
void setupUI() {
|
||||
|
||||
window = new QMainWindow();
|
||||
//window->setTitle("StepLogger");
|
||||
btn = new QPushButton(window);
|
||||
|
||||
// almost fullscreen
|
||||
QRect geom = QDesktopWidget().availableGeometry();
|
||||
const int w = geom.width() * 0.9;
|
||||
const int h = geom.height() * 0.6;
|
||||
|
||||
// center and set always-on-top
|
||||
window->setGeometry(geom.width()/2 - w/2, geom.height()/2 - h/2, w, h);
|
||||
const Qt::WindowFlags flags = window->windowFlags();
|
||||
window->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
|
||||
|
||||
|
||||
btn->setGeometry(5,5,w-5-5,h-5-5);
|
||||
window->show();
|
||||
|
||||
// button clicked -> next waypoint
|
||||
btn->connect(btn, &QPushButton::clicked, [&] () {
|
||||
StepLogger::onButtonPress();
|
||||
updateButton();
|
||||
});
|
||||
|
||||
// show the current waypoint
|
||||
updateButton();
|
||||
|
||||
}
|
||||
|
||||
/** update the current button label */
|
||||
void updateButton() {
|
||||
|
||||
// show the number of the next waypoint
|
||||
const int nr = getCurrentWaypointNumber() + 1;
|
||||
btn->setText(QString::number(nr));
|
||||
|
||||
// button is visible when we are not yet done
|
||||
btn->setVisible(!StepLogger::isDone());
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // STEPLOGGERWRAPPER_H
|
||||
61
ipin/StepLoggerWrapperAndroid.h
Normal file
61
ipin/StepLoggerWrapperAndroid.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef STEPLOGGERWRAPPERANDROID_H
|
||||
#define STEPLOGGERWRAPPERANDROID_H
|
||||
|
||||
#include "Scaler.h"
|
||||
#include "../nav/NavControllerListener.h"
|
||||
#include <Indoor/misc/Debug.h>
|
||||
|
||||
#ifdef Android
|
||||
#include <QtAndroidExtras>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sends the position-estimation-result to the step-logger service
|
||||
*/
|
||||
class StepLoggerWrapperAndroid : public NavControllerListener {
|
||||
|
||||
public:
|
||||
|
||||
/** convert from our position-format to ipin position-format */
|
||||
IPINScaler& scaler;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
StepLoggerWrapperAndroid(IPINScaler& scaler) : scaler(scaler) {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* event fired every 500ms from the navigation controller.
|
||||
* convert from our format to IPIN format
|
||||
* and pass it to the logger
|
||||
*/
|
||||
void onNewEstimation(const Point3 pos_m) override {
|
||||
|
||||
// convert from our coordinate system (meter relative to (0,0,0)) to the WGS84 format
|
||||
const IPIN ipin = scaler.toIPIN3(pos_m);
|
||||
|
||||
// inform the logger
|
||||
log(ipin.lon, ipin.lat, ipin.floorNr);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** call java */
|
||||
void log(const double x, const double y, const double z) {
|
||||
|
||||
#ifdef Android
|
||||
Log::add("SLWA", "calling android with lon/lat/floor");
|
||||
int res = QAndroidJniObject::callStaticMethod<int>("indoor/java/StepLoggerClient", "log", "(DDD)I", x, y, z);
|
||||
if (res != 1337) {throw Exception("error while logging");}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // STEPLOGGERWRAPPERANDROID_H
|
||||
Reference in New Issue
Block a user