fixed baraomter issue (skip first few readings due to sensor errors)

added new eval using shortest-path + plotting
removed compiler warnings for clean-code
fixed some minor issues
added new TeX code and new graphics
This commit is contained in:
2016-02-07 13:30:04 +01:00
parent 004d1f48fd
commit deb21fc550
23 changed files with 4480 additions and 51 deletions

View File

@@ -64,7 +64,7 @@ ADD_DEFINITIONS(
-fstack-protector-all
-g
-O2
-O0
-DWITH_TESTS
-DWITH_ASSERTIONS

View File

@@ -0,0 +1,62 @@
#ifndef DEBUGSHORTESTPATH_H
#define DEBUGSHORTESTPATH_H
#include <Indoor/grid/walk/GridWalkShortestPathControl.h>
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotSplot.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
#include "../Helper.h"
#include "../Vis.h"
template <typename T> class DebugShortestPath : public GridWalkShortestPathControl<T> {
private:
Vis vis;
public:
/** ctor */
template <typename Access> DebugShortestPath(Grid<T>& grid, const Access& acc, const T& target, Helper::FHWSFloors& floors) : GridWalkShortestPathControl<T>(grid, acc, target) {
vis.particles.setColorHex("#0000ff");
vis.particles.setPointSize(1.5);
vis.addFloor(floors.f0, floors.h0);
vis.addFloor(floors.f1, floors.h1);
vis.addFloor(floors.f2, floors.h2);
vis.addFloor(floors.f3, floors.h3);
}
GridWalkState<T> getDestination(Grid<T>& grid, const GridWalkState<T>& start, float distance_m, float headChange_rad) {
GridWalkState<T> s = GridWalkShortestPathControl<T>::getDestination(grid, start, distance_m, headChange_rad);
if (this->recalc == 0){
vis.estPath.clear();
vis.particles.clear();
vis.particles.add(K::GnuplotPoint3(this->centerOfMass.x, this->centerOfMass.y, this->centerOfMass.z));
for (int i = 0; i < (int)this->path->size()-1; ++i) {
const DijkstraNode<T>& dn1 = (*this->path)[i+0];
const DijkstraNode<T>& dn2 = (*this->path)[i+1];
K::GnuplotPoint3 p1 (dn1.element->x_cm, dn1.element->y_cm, dn1.element->z_cm);
K::GnuplotPoint3 p2 (dn2.element->x_cm, dn2.element->y_cm, dn2.element->z_cm);
vis.estPath.addSegment(p1, p2);
}
vis.show();
}
return s;
}
};
#endif // DEBUGSHORTESTPATH_H

View File

@@ -10,6 +10,8 @@
#include <Indoor/grid/walk/GridWalkSimpleControl.h>
#include <Indoor/grid/walk/GridWalkPathControl.h>
#include <Indoor/grid/walk/GridWalkShortestPathControl.h>
#include "DebugShortestPath.h"
#include <KLib/math/filter/particles/resampling/ParticleFilterResamplingSimple.h>
#include <KLib/math/filter/particles/resampling/ParticleFilterResamplingPercent.h>
@@ -106,6 +108,7 @@ public:
// forward
runName = "path2_forward_simple";
BarometerEvaluation::barometerSigma = 0.16;
sr = new SensorReader("./measurements/path2/1/1454345775306.csv");
srt = new SensorReaderTurn("./measurements/path2/1/Turns.txt");
srs = new SensorReaderStep("./measurements/path2/1/Steps2.txt");
@@ -121,6 +124,7 @@ public:
// forward
runName = "path2_forward_path";
BarometerEvaluation::barometerSigma = 0.16;
sr = new SensorReader("./measurements/path2/1/1454345775306.csv");
srt = new SensorReaderTurn("./measurements/path2/1/Turns.txt");
srs = new SensorReaderStep("./measurements/path2/1/Steps2.txt");
@@ -142,6 +146,7 @@ public:
// forward
runName = "path3_forward_simple";
BarometerEvaluation::barometerSigma = 0.16;
sr = new SensorReader("./measurements/path3/1/1454345546308.csv"); // forward
srt = new SensorReaderTurn("./measurements/path3/1/Turns.txt");
srs = new SensorReaderStep("./measurements/path3/1/Steps2.txt");
@@ -161,6 +166,7 @@ public:
// forward
runName = "path3_forward_path";
BarometerEvaluation::barometerSigma = 0.16;
sr = new SensorReader("./measurements/path3/1/1454345546308.csv"); // forward
srt = new SensorReaderTurn("./measurements/path3/1/Turns.txt");
srs = new SensorReaderStep("./measurements/path3/1/Steps2.txt");
@@ -176,6 +182,7 @@ public:
runName = "path4_nexus_simple";
BarometerEvaluation::barometerSigma = 0.16;
sr = new SensorReader("./measurements/path4/nexus/1454695040555.csv"); // forward
srt = new SensorReaderTurn("./measurements/path4/nexus/Turns.txt");
srs = new SensorReaderStep("./measurements/path4/nexus/Steps2.txt");
@@ -193,6 +200,7 @@ public:
runName = "path4_nexus_importance";
BarometerEvaluation::barometerSigma = 0.05;
sr = new SensorReader("./measurements/path4/nexus/1454695040555.csv"); // forward
srt = new SensorReaderTurn("./measurements/path4/nexus/Turns.txt");
srs = new SensorReaderStep("./measurements/path4/nexus/Steps2.txt");
@@ -207,6 +215,7 @@ public:
runName = "path4_nexus_path";
BarometerEvaluation::barometerSigma = 0.05;
sr = new SensorReader("./measurements/path4/nexus/1454695040555.csv"); // forward
srt = new SensorReaderTurn("./measurements/path4/nexus/Turns.txt");
srs = new SensorReaderStep("./measurements/path4/nexus/Steps2.txt");
@@ -218,32 +227,37 @@ public:
}
void path4_nexus_path_b() {
//wifi also uniform dist 0/1 fuer bereiche die OK sind?
//steps hochzaehlen weil mehr als einer in einer transition??
//increase regional average region
runName = "path4_nexus_path";
// void setEval1() {
BarometerEvaluation::barometerSigma = 0.05;
sr = new SensorReader("./measurements/path4/nexus/1454695040555.csv"); // forward
srt = new SensorReaderTurn("./measurements/path4/nexus/Turns.txt");
srs = new SensorReaderStep("./measurements/path4/nexus/Steps2.txt");
gtw = getGroundTruthWay(*sr, floors.gtwp, path4dbl);
MyGridNode& end = (MyGridNode&)grid.getNodeFor( conv(floors.gtwp[path4dbl.back()]) );
DebugShortestPath<MyGridNode>* walk = new DebugShortestPath<MyGridNode>(grid, DijkstraMapper(grid), end, this->floors);
pf->setTransition( std::unique_ptr<MyTransition>( new MyTransition(grid, *walk)) );
}
void bergwerk_path1_nexus_simple() {
// // the particle filter's evaluation method
// std::unique_ptr<MyEvaluation> eval = std::unique_ptr<MyEvaluation>( new MyEvaluation() );
// eval.get()->setUsage(true, true, true, true, true); // TODO: STEP TURN
// pf->setEvaluation( std::move(eval) );
runName = "bergwerk_path1_nexus_simple";
// // resampling step?
// pf->setNEffThreshold(1.0);
// pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingSimple<MyState>>(new K::ParticleFilterResamplingSimple<MyState>()) );
// //pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingPercent<MyState>>(new K::ParticleFilterResamplingPercent<MyState>(0.10)) );
BarometerEvaluation::barometerSigma = 0.10;
sr = new SensorReader("./measurements/bergwerk/path1/nexus/vor/1454775984079.csv"); // forward
srt = new SensorReaderTurn("./measurements/bergwerk/path1/nexus/vor/Turns.txt");
srs = new SensorReaderStep("./measurements/bergwerk/path1/nexus/vor/Steps2.txt");
gtw = getGroundTruthWay(*sr, floors.gtwp, path1dbl);
// // state estimation step
// pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationWeightedAverage<MyState>>(new K::ParticleFilterEstimationWeightedAverage<MyState>()));
// //pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationRegionalWeightedAverage<MyState>>(new K::ParticleFilterEstimationRegionalWeightedAverage<MyState>()));
// //pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationOrderedWeightedAverage<MyState>>(new K::ParticleFilterEstimationOrderedWeightedAverage<MyState>(0.50f)));
GridWalkSimpleControl<MyGridNode>* walk = new GridWalkSimpleControl<MyGridNode>();
pf->setTransition( std::unique_ptr<MyTransition>( new MyTransition(grid, *walk)) );
}
// }
};

View File

@@ -56,8 +56,11 @@ protected:
// NEW
std::vector<int> path1 = {29, 28,27,26,255,25,24,23,22,21,20};
std::vector<int> path1dbl = {29, 29, 28,27,26,255,25,24,23,22,21,20};
std::vector<int> path2 = {19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 23, 7, 6};
std::vector<int> path2dbl = {19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 23, 7, 6};
std::vector<int> path3 = {5, 27, 26, 255, 25, 4, 3, 2, 215, 1, 0, 30, 31};
std::vector<int> path3dbl = {5, 5, 27, 26, 255, 25, 4, 3, 2, 215, 1, 0, 30, 31};
std::vector<int> path4 = {29, 28, 27, 32, 33, 34, 35, 36, 10, 9, 8, 22, 37, 38, 39, 40, 41, 42, 43, 44};
std::vector<int> path4dbl = {29, 29, 28, 27, 32, 33, 34, 35, 36, 10, 9, 8, 22, 37, 38, 39, 40, 41, 42, 43, 44}; // duplicate 1st waypoint!
@@ -120,7 +123,7 @@ public:
// sensor numbers
const int s_wifi = 8; const int s_beacons = 9; const int s_barometer = 5; const int s_orientation = 6;
const int s_linearAcceleration = 2;
//const int s_linearAcceleration = 2;
std::list<TurnObservation> turn_observations;
std::list<StepObservation> step_observations;
@@ -206,10 +209,10 @@ public:
break;
}
case s_linearAcceleration:{
baroSensorReader.readVerticalAcceleration(se);
break;
}
// case s_linearAcceleration:{
// baroSensorReader.readVerticalAcceleration(se);
// break;
// }
case s_orientation: {
obs.orientation = OrientationSensorReader::read(se);
@@ -287,7 +290,7 @@ public:
vis.show();
// prevent gnuplot errors
usleep(1000*33);
usleep(1000*333);
}

View File

@@ -38,13 +38,13 @@ public:
// use node-importance as grid-color
struct ColorizerImp {
float get(const MyGridNode& n) const {return n.imp;}
bool skip(const MyGridNode& n) const {return false;}
bool skip(const MyGridNode& n) const {(void) n; return false;}
};
// use node-distance as grid-color
struct ColorizerDist {
float get(const MyGridNode& n) const {return n.distToTarget;}
bool skip(const MyGridNode& n) const {return false;}
bool skip(const MyGridNode& n) const {(void) n; return false;}
};
// use num-visited as grid-color
@@ -166,7 +166,7 @@ public:
if (i % 250 == 0) {std::cout << i << std::endl;}
const MyGridNode& nStart = gnEnd;
GridWalkState<MyGridNode> sStart(&nStart, Heading::rnd());
GridWalkState<MyGridNode> sEnd = walk.getDestination(grid, sStart, 135, 0);
//GridWalkState<MyGridNode> sEnd = walk.getDestination(grid, sStart, 135, 0);
}

View File

@@ -22,7 +22,7 @@ public:
const double pl = 2.7; // 2.7
const double tx = -46;
const float ibPLE = 1.9;
const float ibPLE = 1.5;
addAP(("00:04:96:6b:64:99"), "i.3.20", 290, 1300, Helper::getHeight(3), tx, pl);
addAP(("00:04:96:6b:70:c9"), "i.3.25", 290, 3930, Helper::getHeight(3), tx, pl);

View File

@@ -19,6 +19,9 @@ public:
double getProbability(const MyState& state, const StepObservation* obs) const {
(void) state;
(void) obs;
return 1;
// see: particle-filter-control-data

View File

@@ -17,6 +17,10 @@ public:
double getProbability(const MyState& state, const TurnObservation* obs, bool simple = false) const {
(void) state;
(void) obs;
(void) simple;
return 1;
// see: particle-filter-control-data

View File

@@ -18,6 +18,9 @@
#include "eval/PaperVisDijkstra.h"
#include "eval/PaperVisGrid.h"
float BarometerEvaluation::barometerSigma = NAN;
Settings settings;
void testModelWalk() {
@@ -28,7 +31,7 @@ void testModelWalk() {
Helper::buildTheGrid(grid, floors);
MyGridNode& start = (MyGridNode&)grid.getNodeFor(GridPoint(500,300,floors.h0.cm()));
MyGridNode& end = (MyGridNode&)grid.getNodeFor(GridPoint(7000,5000,floors.h3.cm()));
//MyGridNode& end = (MyGridNode&)grid.getNodeFor(GridPoint(7000,5000,floors.h3.cm()));
Vis vis;
vis.addFloor(floors.f0, floors.h0);
@@ -89,9 +92,14 @@ int main(void) {
//eval.path2_forward_path();
//eval.path3_forward_simple();
//eval.path3_forward_path();
//eval.path4_nexus_simple();
eval.path4_nexus_imp();
//eval.path4_nexus_imp();
//eval.path4_nexus_path();
//eval.path4_nexus_path_b();
eval.bergwerk_path1_nexus_simple();
eval.run();
// PaperVisGrid::showStairs();

View File

@@ -9,12 +9,16 @@
class MyInitializer : public K::ParticleFilterInitializer<MyState> {
private:
Grid<MyGridNode>& grid;
int x_cm;
int y_cm;
int z_cm;
int heading;
Grid<MyGridNode>& grid;
public:

View File

@@ -104,7 +104,7 @@ public:
// randomly move the particle within its target grid (box)
// (z remains unchanged!)
const int grid_size_cm = grid.getGridSize_cm();
//const int grid_size_cm = grid.getGridSize_cm();
// new position (x,y) is randomly distributed within the target node
Point3 noise = Point3(0,0,0); // TODO

View File

@@ -34,12 +34,10 @@ public:
/** read the next sensor entry */
SensorEntryStep getNext() {
char delim;
SensorEntryStep entry;
fp >> entry.ts;
int i = 0;
return entry;

View File

@@ -9,13 +9,17 @@ static constexpr double g_BarometerObservation = 0.0;
class BarometerEvaluation {
public:
static float barometerSigma;//= 0.12+0.04;
public:
double getProbability(const MyState& state, const BarometerObservation* obs) const {
// //rho_z
double barometerSigma = 0.06;//0.12+0.04;//0.09;
//double barometerSigma = 0.06;//0.12+0.04;//0.09;
// //The height of the single floor levels.
// const static double floor_height[3] = {4.1, 3.4, 3.4};

View File

@@ -1,6 +1,6 @@
#pragma once
#include "circular.h"
//#include "circular.h"
#include "BarometerObservation.h"
#include "../reader/SensorReader.h"
#include <sstream>
@@ -23,6 +23,9 @@ private:
static constexpr int avgSize = 10;
static constexpr int startAvgSize = 10;
// skip the first 1-2 seconds and let the sensor settle itself
uint64_t skipTS = 0;
public:
BarometerSensorReader(): avg(avgSize), avgStart(startAvgSize) {
@@ -30,16 +33,21 @@ public:
}
BarometerObservation* readBarometer(const SensorEntry& se) {
// skip the first few 1.5 seconds
if (skipTS == 0) {skipTS = se.ts;}
if (se.ts - skipTS < 3000) {return nullptr;}
std::string tmp = se.data;
BarometerObservation* obs = new BarometerObservation();
const float cur = std::stof(tmp);
// get the next hPa reading and average it
avg.add(std::stof(tmp));
avg.add(cur);
// average the first few readings as reference
if (avgStart.getNumUsed() < startAvgSize) {
avgStart.add(std::stof(tmp));
avgStart.add(cur);
}
// current average relative to the start-average
@@ -50,13 +58,13 @@ public:
}
//TODO
void readVerticalAcceleration(const SensorEntry& se){
// //TODO
// void readVerticalAcceleration(const SensorEntry& se){
//Problem: Koordinatensystem LinearAcceleraton ist relativ zum Telefon und nicht zum
//Weltkoordinatensystem. Brauchen die Beschleunigung nach Oben in Weltkoordinaten.
// //Problem: Koordinatensystem LinearAcceleraton ist relativ zum Telefon und nicht zum
// //Weltkoordinatensystem. Brauchen die Beschleunigung nach Oben in Weltkoordinaten.
}
// }
};

View File

@@ -1,3 +1,5 @@
#define BAROMETRIC
#ifndef BAROMETRIC
#define BAROMETRIC

View File

@@ -21,3 +21,6 @@
mit pfad laeuft es falsch, weil die andere treppe kuerzer zum ziel ist und das wlan dort besser passt}
\commentByFrank{zu grosser einfluss vom pfad ist also kein allheilmittel.. kann, wie beim treppenhaus, auch nach hinten los gehen}
\commentByFrank{path1: bad start due to nearby AP and bad parameters (path-loss too high)}

View File

@@ -12,3 +12,9 @@
\subsection{Weighting}
\subsection{Pathfinding}
\commentByFrank{describe the multi-path version}
\commentByFrank{describe the single-path version}
\commentByFrank{exp-dist for distance to the path. more distance = less-likely}
\commentByFrank{lambda-factor controls the allowed deviation from the shortest-path}

View File

@@ -6,26 +6,41 @@
preferred over absolute ones. However, due to noisy sensors \todo{cite oder grafik? je nach platz}, one
single reading is not enough as a relative base. Harnessing the usual setup time of a navigation-system (
route calculation, user checking the route) we use the average of all barometer readings during this
timeframe as realtive base $\overline{\mPressure}$.
timeframe as realtive base $\overline{\mObsPressure}$. However, it is often necessary to omit the first few
sensors readings, as the sensor needs some time to settle and the estimated base would otherwise be far off
the real values (see fig. \ref{fig:baroSetupError}). Besides, we use the system's setup time to estimate the
sensors uncertainty $\sigma_\text{baro}$ for later use within the evaluation.
During each transition from $\mStateVec_{t-1}$ to $\mStateVec_t$, the predicted pressure $\mStatePressure$ is
adjusted according to the resulting $z$-change, if any:
\begin{figure}
\include{gfx/baro/baro_setup_issue}
\caption{Sometimes the barometer provides erroneous \SI{}{\hpa} readings during the first seconds. Those
need to be omitted before $\sigma_\text{baro}$ and $\overline{\mObsPressure}$ are estimated.}
\label{fig:baroSetupError}
\end{figure}
During each transition from $\mStateVec_{t-1}$ to $\mStateVec_t$, we need a corresponding, relative pressure
prediction $\mStatePressure$ which is adjusted according to the resulting $z$-change, if any:
\begin{equation}
\mState_{t}^{\mStatePressure} = \mState_{t-1}^{\mStatePressure} + \Delta z \cdot \SI{0.105}{\hpa}
,\enskip
\Delta z = \mState_{t-1}^{z} - \mState_{t}^z
.
\label{eq:baroTransition}
\end{equation}
Within the evaluation bla bla
The evaluation following the transition then compares the predicted relative pressure with the observed one
using a normal distribution with the previously estimated $\sigma_\text{baro}$:
\begin{equation}
xx
p(\mObsVec_t \mid \mStateVec_t)_\text{baro} = \mathcal{N}(\mObs_t^{\mObsPressure} \mid \mState_t^{\mStatePressure}, \sigma_\text{baro}).
\label{eq:baroEval}
\end{equation}
we use the system's setup time to not only determine the relative base but also for estimating the barometers
uncertainty \sigma_\text{baro} used within the evaluation.
\subsection{Wi-Fi \& iBeacons}
For additional absolute location hints, we use the Smartphones Wi-Fi and iBeacon sensor to measure the signal-strengths
@@ -40,7 +55,7 @@
and calculate the resulting probability as described in \cite{ipin2015}:
\begin{equation}
\mProb(\mObsVec \mid \mStateVec)_\text{wifi} =
\mProb(\mObsVec_t \mid \mStateVec_t)_\text{wifi} =
\prod\limits_{i=1}^{n} \mathcal{N}(\mRssi_\text{wifi}^{i} \mid P_{r}(\mMdlDist_{i}, \Delta{f_{i}}), \sigma_{\text{wifi}}^2).
\label{eq:wifiTotal}
\end{equation}
@@ -56,6 +71,9 @@
Again, $\mPLE$ is determined emprically. \todo{faellt hier meist kleiner aus, weil ja kuerzere reichweite etc}
\subsection{Step- \& Turn-Detection}
To prevent degradation within the particle-filter \cite{??} due to downvoting of particles with increased

View File

@@ -0,0 +1,24 @@
\relax
\@setckpt{gfx/baro/baro_setup_issue}{
\setcounter{page}{3}
\setcounter{equation}{3}
\setcounter{enumi}{0}
\setcounter{enumii}{0}
\setcounter{enumiii}{0}
\setcounter{enumiv}{0}
\setcounter{footnote}{0}
\setcounter{mpfootnote}{0}
\setcounter{section}{4}
\setcounter{subsection}{1}
\setcounter{subsubsection}{0}
\setcounter{paragraph}{0}
\setcounter{IEEEsubequation}{0}
\setcounter{figure}{0}
\setcounter{table}{0}
\setcounter{IEEEbiography}{0}
\setcounter{parentequation}{0}
\setcounter{ALC@unique}{0}
\setcounter{ALC@line}{0}
\setcounter{ALC@rem}{0}
\setcounter{ALC@depth}{0}
}

1880
tex/gfx/baro/baro_setup_issue.csv Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
set terminal epslatex size 3.5,1.4
set output "baro_setup_issue.eps"
unset arrow
set yrange[980.5:981.5]
set xrange[0:50]
set ytics 980.5, 0.2, 981.5
set lmargin 5.0
set rmargin 1.4
set tmargin 0.5
set object 1 rectangle from 0,980.5 to 4.000,981.3 fs solid fc rgb "#EEEEEE" behind
set object 2 rectangle from 4.000,980.5 to 10.000,981.1 fs solid fc rgb "#DDDDDD" behind
set object 3 rectangle from 10.000,980.5 to 36.000,980.9 fs solid fc rgb "#EEEEEE" behind
set object 4 rectangle from 36.000,980.5 to 48.000,981.2 fs solid fc rgb "#DDDDDD" behind
set label 1 "error" at 0.500,981.38
set label 2 "estimation" at 4.500, 981.18
set label 3 "walk" at 10.500, 980.98
set label 4 "stair" at 36.500, 981.28
plot "baro_setup_issue.csv" using ($2/1000):4 with lines notitle lc rgb "black"

View File

@@ -0,0 +1,108 @@
% GNUPLOT: LaTeX picture with Postscript
\begingroup
\makeatletter
\providecommand\color[2][]{%
\GenericError{(gnuplot) \space\space\space\@spaces}{%
Package color not loaded in conjunction with
terminal option `colourtext'%
}{See the gnuplot documentation for explanation.%
}{Either use 'blacktext' in gnuplot or load the package
color.sty in LaTeX.}%
\renewcommand\color[2][]{}%
}%
\providecommand\includegraphics[2][]{%
\GenericError{(gnuplot) \space\space\space\@spaces}{%
Package graphicx or graphics not loaded%
}{See the gnuplot documentation for explanation.%
}{The gnuplot epslatex terminal needs graphicx.sty or graphics.sty.}%
\renewcommand\includegraphics[2][]{}%
}%
\providecommand\rotatebox[2]{#2}%
\@ifundefined{ifGPcolor}{%
\newif\ifGPcolor
\GPcolorfalse
}{}%
\@ifundefined{ifGPblacktext}{%
\newif\ifGPblacktext
\GPblacktexttrue
}{}%
% define a \g@addto@macro without @ in the name:
\let\gplgaddtomacro\g@addto@macro
% define empty templates for all commands taking text:
\gdef\gplbacktext{}%
\gdef\gplfronttext{}%
\makeatother
\ifGPblacktext
% no textcolor at all
\def\colorrgb#1{}%
\def\colorgray#1{}%
\else
% gray or color?
\ifGPcolor
\def\colorrgb#1{\color[rgb]{#1}}%
\def\colorgray#1{\color[gray]{#1}}%
\expandafter\def\csname LTw\endcsname{\color{white}}%
\expandafter\def\csname LTb\endcsname{\color{black}}%
\expandafter\def\csname LTa\endcsname{\color{black}}%
\expandafter\def\csname LT0\endcsname{\color[rgb]{1,0,0}}%
\expandafter\def\csname LT1\endcsname{\color[rgb]{0,1,0}}%
\expandafter\def\csname LT2\endcsname{\color[rgb]{0,0,1}}%
\expandafter\def\csname LT3\endcsname{\color[rgb]{1,0,1}}%
\expandafter\def\csname LT4\endcsname{\color[rgb]{0,1,1}}%
\expandafter\def\csname LT5\endcsname{\color[rgb]{1,1,0}}%
\expandafter\def\csname LT6\endcsname{\color[rgb]{0,0,0}}%
\expandafter\def\csname LT7\endcsname{\color[rgb]{1,0.3,0}}%
\expandafter\def\csname LT8\endcsname{\color[rgb]{0.5,0.5,0.5}}%
\else
% gray
\def\colorrgb#1{\color{black}}%
\def\colorgray#1{\color[gray]{#1}}%
\expandafter\def\csname LTw\endcsname{\color{white}}%
\expandafter\def\csname LTb\endcsname{\color{black}}%
\expandafter\def\csname LTa\endcsname{\color{black}}%
\expandafter\def\csname LT0\endcsname{\color{black}}%
\expandafter\def\csname LT1\endcsname{\color{black}}%
\expandafter\def\csname LT2\endcsname{\color{black}}%
\expandafter\def\csname LT3\endcsname{\color{black}}%
\expandafter\def\csname LT4\endcsname{\color{black}}%
\expandafter\def\csname LT5\endcsname{\color{black}}%
\expandafter\def\csname LT6\endcsname{\color{black}}%
\expandafter\def\csname LT7\endcsname{\color{black}}%
\expandafter\def\csname LT8\endcsname{\color{black}}%
\fi
\fi
\setlength{\unitlength}{0.0500bp}%
\ifx\gptboxheight\undefined%
\newlength{\gptboxheight}%
\newlength{\gptboxwidth}%
\newsavebox{\gptboxtext}%
\fi%
\setlength{\fboxrule}{0.5pt}%
\setlength{\fboxsep}{1pt}%
\begin{picture}(5040.00,2014.00)%
\gplgaddtomacro\gplbacktext{%
\csname LTb\endcsname%
\put(528,440){\makebox(0,0)[r]{\strut{}$980.5$}}%
\put(528,733){\makebox(0,0)[r]{\strut{}$980.7$}}%
\put(528,1025){\makebox(0,0)[r]{\strut{}$980.9$}}%
\put(528,1318){\makebox(0,0)[r]{\strut{}$981.1$}}%
\put(528,1610){\makebox(0,0)[r]{\strut{}$981.3$}}%
\put(528,1903){\makebox(0,0)[r]{\strut{}$981.5$}}%
\put(660,220){\makebox(0,0){\strut{}$0$}}%
\put(1499,220){\makebox(0,0){\strut{}$10$}}%
\put(2338,220){\makebox(0,0){\strut{}$20$}}%
\put(3176,220){\makebox(0,0){\strut{}$30$}}%
\put(4015,220){\makebox(0,0){\strut{}$40$}}%
\put(4854,220){\makebox(0,0){\strut{}$50$}}%
\put(702,1727){\makebox(0,0)[l]{\strut{}error}}%
\put(1037,1435){\makebox(0,0)[l]{\strut{}estimation}}%
\put(1541,1142){\makebox(0,0)[l]{\strut{}walk}}%
\put(3722,1581){\makebox(0,0)[l]{\strut{}stair}}%
}%
\gplgaddtomacro\gplfronttext{%
}%
\gplbacktext
\put(0,0){\includegraphics{baro_setup_issue}}%
\gplfronttext
\end{picture}%
\endgroup