├── .classpath
├── .gitignore
├── .project
├── AndroidManifest.xml
├── LICENSE.txt
├── README.txt
├── default.properties
├── res
├── drawable-hdpi
│ └── icon.png
├── drawable-ldpi
│ ├── arrow.png
│ ├── arrowred.png
│ ├── icon.png
│ └── stairs.png
├── drawable-mdpi
│ ├── arrow.png
│ ├── arrowred.png
│ └── icon.png
├── layout
│ ├── calibrator.xml
│ ├── displayroute.xml
│ └── selectroom.xml
├── values
│ └── strings.xml
└── xml
│ ├── map1og.osm
│ ├── map1ug.osm
│ ├── map2og.osm
│ ├── map3og.osm
│ ├── mapeg.osm
│ └── parkinglot.osm
├── src
└── de
│ └── uvwxy
│ ├── footpath
│ ├── ToolBox.java
│ ├── core
│ │ ├── NPConfig.java
│ │ ├── Positioner.java
│ │ ├── Positioner_OnlineBestFit.java
│ │ ├── Positioner_OnlineFirstFit.java
│ │ ├── StepDetection.java
│ │ └── StepTrigger.java
│ ├── graph
│ │ ├── Graph.java
│ │ ├── GraphEdge.java
│ │ ├── GraphElement.java
│ │ ├── GraphNode.java
│ │ ├── GraphWay.java
│ │ └── LatLonPos.java
│ ├── gui
│ │ ├── Calibrator.java
│ │ ├── Loader.java
│ │ ├── Navigator.java
│ │ ├── PaintBoxHistory.java
│ │ ├── PaintBoxMap.java
│ │ └── Tile.java
│ └── log
│ │ ├── AudioWriter.java
│ │ ├── DataLogger.java
│ │ └── FWriter.java
│ └── paintbox
│ ├── PaintBox.java
│ └── PaintThread.java
├── tools
├── 4228_4222.png
├── 4228_5056.png
├── README
└── URL2QR.jar
└── update-revfile.sh
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /gen/
3 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | FootPathMaster
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | FootPath (Infrastructureless indoor navigation for smart phones)
2 |
3 | Copyright (C) 2010-2011, Paul Smith
4 | Copyright (C) 2010-2011, Jó Ágila Bitsch
5 | Copyright (C) 2010-2011, Chair of Computer Science 4, RWTH Aachen University,
6 |
7 |
8 |
9 | FREE SOFTWARE
10 | #############
11 |
12 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License along with this program. If not, see .
17 |
18 | IF YOU NEED ANOTHER LICENSE
19 | ###########################
20 |
21 | If you are planning to integrate FootPath into a commercial product, please contact us for licensing options via email at:
22 |
23 | jo.bitsch@cs.rwth-aachen.de
24 |
25 |
26 | IF YOU REALLY LIKE OUR SOFTWARE
27 | ###############################
28 |
29 | Buy us a beer when the situation arises :-)
30 |
31 |
32 | OTHER ISSUES
33 | ############
34 |
35 | This software is currently a proof of concept. It needs some more work to make it generally useable for a non technical person.
36 |
37 | Open issues include:
38 | * Automatically downloading available indoor maps
39 | * Better UI
40 | * Looking at several possible paths at once.
41 |
42 |
43 | HOW TO USE THIS SOFTWARE
44 | ########################
45 | * Create your maps with JOSM and put them into the folder res/xml
46 | * NOTE: ways and nodes should have an attribute indoor=yes and a name
47 | * compare with our maps. (TODO)
48 | * Change lines 199-204 in Loader.java to load your maps on startup
49 | * Run update-revfile.sh after every commit, so that log messages refer to the correct revision.
50 |
--------------------------------------------------------------------------------
/default.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "build.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-8
12 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-ldpi/arrow.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/arrowred.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-ldpi/arrowred.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/stairs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-ldpi/stairs.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-mdpi/arrow.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/arrowred.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-mdpi/arrowred.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/res/layout/calibrator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/layout/displayroute.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/layout/selectroom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
12 |
15 |
19 |
22 |
26 |
30 |
34 |
38 |
42 |
46 |
50 |
51 |
55 |
58 |
61 |
64 |
68 |
69 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello World, Loader!
4 | FootPath
5 |
6 |
--------------------------------------------------------------------------------
/res/xml/map1ug.osm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
--------------------------------------------------------------------------------
/res/xml/parkinglot.osm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/ToolBox.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath;
2 |
3 | import java.util.ArrayList;
4 |
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Paint.Style;
8 |
9 | /**
10 | *
11 | * @author Paul Smith
12 | *
13 | */
14 | public class ToolBox {
15 | public static float strokeWidth = 2.0f;
16 |
17 | public static Paint redPaint() {
18 | Paint p = new Paint();
19 | p.setStrokeWidth(strokeWidth);
20 | p.setStyle(Style.FILL);
21 | p.setColor(Color.RED);
22 | return p;
23 | }
24 |
25 | public static Paint redPaint(float textSize) {
26 | Paint p = new Paint();
27 | p.setTextSize(textSize);
28 | p.setStrokeWidth(strokeWidth);
29 | p.setStyle(Style.FILL);
30 | p.setColor(Color.RED);
31 | return p;
32 | }
33 |
34 | public static Paint greenPaint() {
35 | Paint p = new Paint();
36 | p.setStrokeWidth(strokeWidth);
37 | p.setStyle(Style.FILL);
38 | p.setColor(Color.GREEN);
39 | return p;
40 | }
41 |
42 | public static Paint greenPaint(float textSize) {
43 | Paint p = new Paint();
44 | p.setTextSize(textSize);
45 | p.setStrokeWidth(strokeWidth);
46 | p.setStyle(Style.FILL);
47 | p.setColor(Color.GREEN);
48 | return p;
49 | }
50 |
51 | public static Paint bluePaint() {
52 | Paint p = new Paint();
53 | p.setStrokeWidth(strokeWidth);
54 | p.setStyle(Style.FILL);
55 | p.setColor(Color.BLUE);
56 | return p;
57 | }
58 |
59 | public static Paint transparentBluePaint() {
60 | Paint p = new Paint();
61 | p.setStrokeWidth(strokeWidth);
62 | p.setStyle(Style.STROKE);
63 | p.setColor(Color.BLUE);
64 | return p;
65 | }
66 |
67 | public static Paint bluePaint(float textSize) {
68 | Paint p = new Paint();
69 | p.setTextSize(textSize);
70 | p.setStrokeWidth(strokeWidth);
71 | p.setStyle(Style.FILL);
72 | p.setColor(Color.BLUE);
73 | return p;
74 | }
75 |
76 |
77 | public static Paint myPaint(int strokeWidth, int color) {
78 | Paint p = new Paint();
79 | p.setStrokeWidth(strokeWidth);
80 | p.setStyle(Style.FILL);
81 | p.setColor(color);
82 | return p;
83 | }
84 |
85 | public static Paint myPaint(int strokeWidth, int color, int alpha) {
86 | Paint p = new Paint();
87 | p.setStrokeWidth(strokeWidth);
88 | p.setStyle(Style.FILL);
89 | p.setColor(color);
90 | p.setAlpha(alpha);
91 | return p;
92 | }
93 |
94 | /**
95 | * Creates a normal double array out of an ArrayList.
96 | *
97 | * @param al
98 | * the ArrayList to convert
99 | * @return the double[] array made out of array list
100 | */
101 | public static double[] arrayListToArrayDouble(ArrayList al) {
102 | // create array with appropriate size
103 | double[] a = new double[al.size()];
104 | for (int i = 0; i < al.size(); i++) {
105 | // fill it
106 | a[i] = al.get(i).doubleValue();
107 | }
108 | // voila
109 | return a;
110 | }
111 |
112 | /**
113 | * Creates a normal int array out of an ArrayList.
114 | *
115 | * @param al
116 | * the ArrayList to convert
117 | * @return the int[] array made out of array list
118 | */
119 | public static int[] arrayListToArrayInt(ArrayList al) {
120 | // create array with appropriate size
121 | int[] a = new int[al.size()];
122 | for (int i = 0; i < al.size(); i++) {
123 | // fill it
124 | a[i] = al.get(i).intValue();
125 | }
126 | // voila
127 | return a;
128 | }
129 |
130 | // precision to two decimal places behind .
131 | public static double tdp(double d) {
132 | return ((int) (d * 100)) / 100.0;
133 | }
134 |
135 | public static double[] arrayClone(double[] array) {
136 | double[] buf = new double[array.length];
137 | for (int i = 0; i < array.length; i++) {
138 | buf[i] = array[i];
139 | }
140 | return buf;
141 | }
142 |
143 |
144 | public static double lowpassFilter(double old_value, double new_value, double a) {
145 | return old_value + a * (new_value - old_value);
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/NPConfig.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | /**
4 | * A class to manage a configuration of the navigation
5 | * @author Paul Smith
6 | *
7 | */
8 | public class NPConfig{
9 | // Points to the current edge we are walking on
10 | public int npPointer;
11 | // How far we have come on this edge
12 | public double npCurLen;
13 | // How many unmatched steps we have since the last matched step
14 | public int npUnmatchedSteps;
15 | // Which step the last matched step is
16 | public int npLastMatchedStep;
17 | // How many total matched steps we have
18 | public int npMatchedSteps;
19 | // The step size
20 | public double npStepSize;
21 |
22 | public NPConfig(){
23 | this.npPointer = 0;
24 | this.npCurLen = 0.0;
25 | this.npUnmatchedSteps = 0;
26 | this.npLastMatchedStep = 0;
27 | this.npMatchedSteps = 0;
28 | this.npStepSize = 1.0; // 1.0m...
29 | }
30 |
31 | public NPConfig(NPConfig conf) {
32 | this.npCurLen = conf.npCurLen;
33 | this.npLastMatchedStep = conf.npLastMatchedStep;
34 | this.npMatchedSteps = conf.npMatchedSteps;
35 | this.npPointer = conf.npPointer;
36 | this.npStepSize = conf.npStepSize;
37 | this.npUnmatchedSteps = conf.npUnmatchedSteps;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/Positioner.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | /**
4 | *
5 | * @author Paul Smith
6 | *
7 | */
8 | public abstract class Positioner {
9 | public abstract void addStep(double direction);
10 | public abstract double getProgress();
11 |
12 | /**
13 | * Check if the difference of the given angles in degrees is less than the given alowed difference
14 | * @param v the first angle
15 | * @param t the second angle
16 | * @param diff the allowed difference
17 | * @return true if v <= diff away from t
18 | */
19 | public static boolean isInRange(double v, double t, double diff) {
20 | if(Math.abs(v-t)<=diff){
21 | return true;
22 | }
23 | if(Math.abs((v+diff)%360-(t+diff)%360)<=diff){
24 | return true;
25 | }
26 | return false;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/Positioner_OnlineBestFit.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | import java.util.LinkedList;
4 |
5 | import de.uvwxy.footpath.graph.GraphEdge;
6 | import de.uvwxy.footpath.gui.Navigator;
7 |
8 | /**
9 | * A class calculating the position concerning Best Fit
10 | *
11 | * @author Paul Smith
12 | *
13 | */
14 | public class Positioner_OnlineBestFit extends Positioner{
15 | private Navigator nav = null;
16 | private NPConfig conf = null;
17 | private LinkedList edges = null;
18 | private double[][] c = null;
19 | private double all_dist = 0.0;
20 | private double avg_steplength = 0.0;
21 | private double[] from_map = null;
22 | private LinkedList s = null;
23 | private double[][] dyn = null;
24 | private final int INITIAL_DYN_SIZE = 2;
25 | private int currentStep = 0;
26 | private double progress = 0.0;
27 |
28 | public Positioner_OnlineBestFit(Navigator nav, LinkedList edges, NPConfig conf){
29 | this.nav = nav;
30 | this.edges = edges;
31 | this.conf = conf;
32 |
33 | c = new double[edges.size()][2];
34 | // Setup c:
35 | double tempLen = 0.0;
36 | for(int i = 0; i < edges.size(); i++){
37 | GraphEdge temp = edges.get(i);
38 | tempLen+=temp.getLen();
39 | c[i][0] = tempLen;
40 | c[i][1] = temp.getCompDir();
41 | }
42 |
43 | // Setup all_dist:
44 | all_dist = c[edges.size()-1][0];
45 |
46 | // Setup avg_steplength:
47 | this.avg_steplength = nav.getStepLengthInMeters();
48 |
49 | // Setup n:
50 | double[] n = new double[(int)(all_dist/this.avg_steplength)];
51 | for(int i = 0; i < n.length; i++){
52 | n[i] = this.avg_steplength*i;
53 | }
54 |
55 | // Setup from_map:
56 | from_map = new double[n.length];
57 | for(int i = 0; i < n.length; i++){
58 | // This code below uses directions directly from edges
59 | int edge_i = 0;
60 | while(!(c[edge_i][0] > n[i])){
61 | edge_i++;
62 | }
63 | from_map[i]=c[edge_i][1];
64 | }
65 |
66 | // s: a list to store detected step headings
67 | s = new LinkedList();
68 |
69 | // dyn: the last two lines from the matrix D
70 | dyn = new double[INITIAL_DYN_SIZE][from_map.length+1];
71 |
72 | // initialization
73 | for(int x = 0; x < INITIAL_DYN_SIZE; x++){
74 | for(int y = 0; y < dyn[0].length; y++){
75 | if ( x == 0 && y == 0 ){
76 | dyn[x][y] = 0.0;
77 | } else if (x == 0){
78 | dyn[x][y] = Double.POSITIVE_INFINITY;
79 | } else if (y == 0){
80 | dyn[x][y] = Double.POSITIVE_INFINITY;
81 | }
82 | // lens[x][y] = 0.0;
83 | }
84 | }
85 |
86 |
87 | }
88 |
89 | boolean firstStep = false;
90 |
91 | public void addStep(double direction){
92 | if(firstStep){
93 | dyn[0][0] = Double.POSITIVE_INFINITY;
94 | }
95 | firstStep = true;
96 |
97 | double t1,t2,t3;
98 |
99 | currentStep++;
100 | int x = currentStep;
101 |
102 | s.add(new Double(direction));
103 |
104 | // calculate new line of the matrix D:
105 | for(int y = 1; y < dyn[0].length; y++){
106 | // top
107 | t1=dyn[x % 2][y-1] + score(getFromS(x-1), getFromMap(y-2), false);
108 | // left
109 | t2=dyn[(x-1)%2][y] + score(getFromS(x-2), getFromMap(y-1), false);
110 | // diagonal
111 | t3=dyn[(x-1)%2][y-1] + score(getFromS(x-1), getFromMap(y-1), true);
112 |
113 | dyn[x%2][y] = Math.min(Math.min(t1, t2),t3);
114 | }
115 |
116 | int y_min = -1;
117 | double f_min = Double.POSITIVE_INFINITY;
118 | for ( int y_ = 1; y_ < dyn[0].length - 1; y_++){
119 | if ( f_min > dyn[x%2][y_] ){
120 | f_min = dyn[x%2][y_];
121 | y_min = y_;
122 | }
123 | }
124 |
125 | // y_min + 1 : index i is step i + 1 ( array starting at 0)
126 | progress = (y_min + 1) * avg_steplength;
127 |
128 | // Update fields in NPConfig conf:
129 | // Find out which edge we are on and how far on that edge:
130 | double tempLen = edges.get(0).getLen();
131 | int edgeIndex = 0;
132 | while(tempLen < progress && edgeIndex < edges.size() ){
133 | edgeIndex++;
134 | tempLen += edges.get(edgeIndex).getLen();
135 | }
136 |
137 | conf.npCurLen = progress;
138 | for(int i = 0; i < edgeIndex; i++){
139 | conf.npCurLen-=edges.get(i).getLen();
140 | }
141 |
142 | conf.npPointer = edgeIndex;
143 | conf.npLastMatchedStep = y_min;
144 | conf.npMatchedSteps++;
145 | conf.npUnmatchedSteps = conf.npMatchedSteps - y_min;
146 | }
147 |
148 | private double getFromMap(int i){
149 | if ( i < 0 )
150 | return 0.0;
151 | else
152 | return from_map[i];
153 | }
154 |
155 | private double getFromS(int i){
156 | if ( i < 0 )
157 | return 0.0;
158 | else
159 | return s.get(i).doubleValue();
160 | }
161 | private double score(double x, double y, boolean diagonal){
162 | double ret = 2.0; // = penalty
163 |
164 | // Sanitize:
165 | double t = Math.abs(x-y);
166 | t = (t>180.0) ? 360.0 - t : t;
167 |
168 | // And score:
169 | if ( t < 45.0 ){
170 | ret = 0.0;
171 | } else if ( t < 90.0 ){
172 | ret = 1.0;
173 | } else if ( t < 120.0 ){
174 | ret = 2.0;
175 | } else {
176 | ret = 10.0;
177 | }
178 |
179 | ret = !diagonal ? ret + 1.5 : ret;
180 | return ret;
181 | }
182 |
183 | public double getProgress(){
184 | return progress;
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/Positioner_OnlineFirstFit.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | import java.util.LinkedList;
4 |
5 | import de.uvwxy.footpath.graph.GraphEdge;
6 | import de.uvwxy.footpath.gui.Navigator;
7 |
8 | /**
9 | * A class calculating the position concerning First Fit
10 | *
11 | * @author Paul Smith
12 | *
13 | */
14 | public class Positioner_OnlineFirstFit extends Positioner{
15 | private NPConfig conf;
16 | private LinkedList navPathEdges = null;
17 | private int totalStepsWalked = 0;
18 |
19 | // store each step, with its direction
20 | private LinkedList dirHistory = new LinkedList();
21 | // If this value is passed, lookahead is started
22 | private int maxFallBackSteps = 4;
23 |
24 | private Navigator nav = null;
25 | public Positioner_OnlineFirstFit(Navigator nav, LinkedList navPathEdges, NPConfig conf){
26 | this.navPathEdges = navPathEdges;
27 | this.conf = conf;
28 | this.nav = nav;
29 | }
30 |
31 | /**
32 | * This is called each time a step is detected, and thus information about
33 | * the users whereabouts need to be updated
34 | */
35 | public void addStep(double compValue) {
36 |
37 | totalStepsWalked++;
38 | dirHistory.add(new Double(compValue));
39 |
40 | if (conf.npPointer < navPathEdges.size()) {
41 | // we haven't reached a destination
42 | // successive matching, incoming values are/have been
43 | // roughly(in range of acceptanceWidth) correct, and not more than maxFallBackSteps have been unmatched
44 | if (isInRange(compValue, navPathEdges.get(conf.npPointer).getCompDir(), nav.getAcceptanceWidth())
45 | && ( (conf.npUnmatchedSteps <= maxFallBackSteps)) // unmatched steps might be errors
46 | || (nav.getNavPathLenLeft()0){
62 | // Calculate length from number of steps on stairs
63 | conf.npCurLen += navPathEdges.get(conf.npPointer).getLen()/navPathEdges.get(conf.npPointer).getSteps();
64 | } else if(navPathEdges.get(conf.npPointer).getSteps()==-1){
65 | // Naive length for steps on stairs if steps undefined
66 | conf.npCurLen += nav.getNaiveStairsWidth();
67 | } else {
68 | // Error in data: Edge isStairs, but not undefined/defined number of steps
69 | conf.npCurLen += conf.npStepSize;
70 | }
71 | } else {
72 | conf.npCurLen += conf.npStepSize;
73 | }
74 |
75 |
76 | if (conf.npCurLen >= navPathEdges.get(conf.npPointer).getLen()) {
77 | // Edge length was exceeded so skip to next edge
78 | conf.npPointer++;
79 | // ;)
80 | // Log.i("FOOTPATH", "progress on path");
81 | // Reset amount of walked length to remainder of step length
82 | conf.npCurLen = conf.npCurLen - navPathEdges.get(conf.npPointer - 1).getLen();
83 | // Stop navigating if we have passed the last edge
84 | if (conf.npPointer >= navPathEdges.size()) {
85 | nav.setNavigating(false);
86 | }
87 | }
88 | } else {
89 | // Step did not match current assumed edge, try lookahead, if we
90 | // calculated with steps being larger in reality
91 | // If steps are smaller in reality than try to wait and resize the step size
92 |
93 | // Increase amount of unmatched steps
94 | conf.npUnmatchedSteps++;
95 | // Log.i("FOOTPATH", "Unmatched steps = " + conf.npUnmatchedSteps);
96 |
97 | // Do not do look ahead if the direction matches the last edge.
98 | // Wait for user to turn on to this edge
99 | if(conf.npPointer >= 1 && conf.npCurLen <= conf.npStepSize && isInRange(compValue, navPathEdges.get(conf.npPointer-1).getCompDir(),
100 | nav.getAcceptanceWidth())){
101 | return;
102 | }
103 |
104 | // Enough steps unmatched to start lookahead
105 | if(conf.npUnmatchedSteps > maxFallBackSteps){
106 |
107 | conf = findMatch(conf, true);
108 | // CALL FOR NEW POSITION FIRST FIND
109 | }
110 |
111 | }// -> else
112 | } // if
113 | }
114 |
115 | public void recalcPos(){
116 | // RECALCULATE POSITION FROM WHOLE ROUTE DATA
117 | NPConfig x = new NPConfig(conf);
118 | // reset route to beginning
119 | x.npLastMatchedStep = -1;
120 | x.npPointer = 0;
121 | x.npCurLen = 0.0;
122 | x.npUnmatchedSteps = dirHistory.size() - 1;
123 |
124 | x = findMatch(x, false);
125 | x.npLastMatchedStep = dirHistory.size() - 1;
126 | x.npMatchedSteps = x.npLastMatchedStep;
127 |
128 | // Log.i("FOOTPAHT", "Recalculated route");
129 | // Update position on path
130 | conf = x;
131 | }
132 |
133 | /**
134 | * Calculate the best/first matching position on path, from given configuration
135 | * @param npC the current configuration to base calculation on
136 | * @param first set to true if first match should be returned
137 | * @return new configuration for position
138 | */
139 | private NPConfig findMatch(NPConfig npC, boolean first){
140 |
141 | if(dirHistory == null){
142 | return npC;
143 | }
144 |
145 | if(dirHistory.size() == 0){
146 | return npC;
147 | }
148 |
149 | // Move backwards through walkedHistory, and look forwards to match it to an edge.
150 | // This works based on the assumption that, at some point, we have correct values again.
151 | // Accept only if we have found at least minMetresToMatch on a single edge
152 |
153 | double lastDir = dirHistory.get(dirHistory.size()-1); // last unmatched value
154 |
155 | // Log.i("FOOTPATH", "Searching for " + lastDir);
156 | // Log.i("FOOTPATH", "Current edge " + npC.npPointer);
157 |
158 | int maxBackLogCount = Integer.MIN_VALUE;
159 | int newPointer = npC.npPointer;
160 | double newCurLen = 0.0;
161 | int minCount = 4; // minimal 4 steps to match backwards
162 | // Go through all remaining edges and find first edge matching current direction
163 | for(int localPointer = npC.npPointer; localPointer < navPathEdges.size(); localPointer++){
164 |
165 | // Log.i("FOOTPATH", "Found edge (" + localPointer + ") in direction " + navPathEdges.get(localPointer).getCompDir());
166 | if(isInRange(navPathEdges.get(localPointer).getCompDir(),lastDir,nav.getAcceptanceWidth())){
167 | // There is an edge matching a direction on path
168 | // Log.i("FOOTPATH", "Edge (" + localPointer + ") is in range");
169 | UglyObject o = new UglyObject();
170 | o.count = 0;
171 | int oldCount = o.count;
172 | o.historyPointer = dirHistory.size()-1;
173 | int backLogPointer = localPointer;
174 | double edgeDir = navPathEdges.get(backLogPointer).getCompDir();
175 | double edgeLength = navPathEdges.get(backLogPointer).getLen();
176 | // Log.i("FOOTPATH", "Summing up path length " + lastDir);
177 | // Log.i("FOOTPATH", "backlogPointer = " + backLogPointer + ">" + npC.npPointer + " npLastMatchedStep");
178 | while(backLogPointer > npC.npPointer && findMatchingSteps(o, edgeDir, npC, edgeLength, npC.npStepSize)){
179 | oldCount = o.count;
180 | // Log.i("FOOTPATH", "Found matching steps into edgeDir = " + edgeDir + ", oldCount = "
181 | // + oldCount + ", " + o.historyPointer);
182 | backLogPointer--;
183 |
184 | if(backLogPointer<0){
185 | break;
186 | }
187 |
188 | edgeDir = navPathEdges.get(backLogPointer).getCompDir();
189 | // remember last count. on last loop o.count is set to zero
190 | }
191 | // Log.i("FOOTPATH", "Found " + oldCount + " matching steps");
192 | if(oldCount >= minCount && oldCount>maxBackLogCount){
193 | maxBackLogCount = oldCount;
194 | newPointer = localPointer;
195 | newCurLen = amountInSameDirection(dirHistory.size()-1,navPathEdges.get(localPointer), npC);
196 |
197 | if(first){
198 | break;
199 | }
200 | }
201 | } else {
202 |
203 | // Log.i("FOOTPATH", "Edge (" + localPointer + ") not in range");
204 | }
205 | }
206 |
207 | if(maxBackLogCount != Integer.MIN_VALUE){
208 | // Log.i("FOOTPATH", "Found new position with " + (double)maxBackLogCount/(double)(dirHistory.size()-1-npC.npLastMatchedStep) + " confidence");
209 |
210 | if(newPointer == npC.npPointer){
211 | // Jump along same edge
212 | npC.npCurLen += newCurLen;
213 | if(npC.npCurLen > navPathEdges.get(npC.npPointer).getLen()){
214 | // Don not exceed edge!
215 | npC.npCurLen = navPathEdges.get(npC.npPointer).getLen();
216 | }
217 | } else {
218 | // Jump along a different edge
219 | npC.npPointer = newPointer;
220 | npC.npCurLen = newCurLen;
221 | if(npC.npCurLen > navPathEdges.get(npC.npPointer).getLen()){
222 | // Don not exceed edge!
223 | npC.npCurLen = navPathEdges.get(npC.npPointer).getLen();
224 | }
225 | }
226 |
227 | npC.npUnmatchedSteps = 0;
228 | npC.npLastMatchedStep = dirHistory.size() - 1;
229 | }
230 |
231 | return npC;
232 | }
233 |
234 | private class UglyObject{
235 | int count;
236 | int historyPointer;
237 | }
238 |
239 | private boolean findMatchingSteps(UglyObject o, double edgeDir, NPConfig npC, double edgeLength, double stepLength){
240 | int oldCount = o.count;
241 | // Log.i("FOOTPATH", "findMatchingSteps: " + oldCount);
242 | while(o.historyPointer >= 0
243 | && o.historyPointer > npC.npLastMatchedStep
244 | && isInRange(dirHistory.get(o.historyPointer), edgeDir, nav.getAcceptanceWidth())){
245 | o.count++;
246 | o.historyPointer--;
247 |
248 | double lengthToAdd = 0.0;
249 | if(navPathEdges.get(npC.npPointer).isStairs()){
250 | // Don't use step length because of stairs
251 | if(navPathEdges.get(npC.npPointer).getSteps()>0){
252 | // Calculate length from number of steps on stairs
253 | lengthToAdd = navPathEdges.get(npC.npPointer).getLen()/navPathEdges.get(npC.npPointer).getSteps();
254 | } else if(navPathEdges.get(npC.npPointer).getSteps()==-1){
255 | // Naive length for steps on stairs if steps undefined
256 | lengthToAdd= nav.getNaiveStairsWidth();
257 | } else {
258 | // Error in data: Edge isStairs, but not undefined/defined number of steps
259 | lengthToAdd = npC.npStepSize;
260 | }
261 | } else {
262 | lengthToAdd = npC.npStepSize;
263 | }
264 |
265 | if(edgeLength <= (o.count-oldCount)*lengthToAdd){
266 | // Log.i("FOOTPATH", "findMatchingSteps/ edgeLength (" + edgeLength + ") <= "
267 | // + (o.count-oldCount)*lengthToAdd);
268 | // return true if whole edge has been traveled along
269 | return true;
270 | }
271 | }
272 | // Log.i("FOOTPATH", "found: " + oldCount);
273 | if(oldCount != o.count){
274 | return true;
275 | } else {
276 | return false;
277 | }
278 | }
279 |
280 |
281 | private double amountInSameDirection(int historyPointer, GraphEdge edge, NPConfig npC){
282 | double retLength = 0.0;
283 |
284 | while(historyPointer >= 0
285 | && historyPointer >= npC.npLastMatchedStep
286 | && isInRange(edge.getCompDir(), dirHistory.get(historyPointer), nav.getAcceptanceWidth())){
287 | // Log.i("FOOTPATH", "Adding amount into direction " + edge.getCompDir());
288 | historyPointer--;
289 | double lengthToAdd = 0.0;
290 | if(edge.isStairs()){
291 | // Don't use step length because of stairs
292 | if(edge.getSteps()>0){
293 | // Calculate length from number of steps on stairs
294 | lengthToAdd = edge.getLen()/edge.getSteps();
295 | } else if(edge.getSteps()==-1){
296 | // Naive length for steps on stairs if steps undefined
297 | lengthToAdd= nav.getNaiveStairsWidth();
298 | } else {
299 | // Error in data: Edge isStairs, but not undefined/defined number of steps
300 | lengthToAdd = npC.npStepSize;
301 | }
302 | } else {
303 | lengthToAdd = npC.npStepSize;
304 | }
305 | retLength += lengthToAdd;
306 | }
307 | return retLength;
308 | }
309 |
310 | @Override
311 | public double getProgress() {
312 | double len = 0.0;
313 | // sum all traversed edges
314 | for(int i = 0; i < conf.npPointer; i++){
315 | len += navPathEdges.get(i).getLen();
316 | }
317 | // and how far we have walked on current edge
318 | len += conf.npCurLen;
319 | return len;
320 | }
321 |
322 |
323 | }
324 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/StepDetection.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | import java.util.List;
4 | import java.util.Timer;
5 | import java.util.TimerTask;
6 |
7 | import android.content.Context;
8 | import android.hardware.Sensor;
9 | import android.hardware.SensorEvent;
10 | import android.hardware.SensorEventListener;
11 | import android.hardware.SensorManager;
12 | import android.util.Log;
13 | import de.uvwxy.footpath.ToolBox;
14 |
15 | /**
16 | * This class is fed with data from the Accelerometer and Compass sensors. If a step is detected on the acc
17 | * data it calls the trigger function on its interface StepTrigger, with the given direction.
18 | *
19 | * Usage:
20 | * Create an object: stepDetection = new StepDetection(this, this, a, peak, step_timeout_ms);
21 | * @author Paul Smith
22 | *
23 | */
24 | public class StepDetection {
25 | public final long INTERVAL_MS = 1000/30;
26 |
27 | // Hold an interface to notify the outside world of detected steps
28 | private StepTrigger st;
29 | // Context needed to get access to sensor service
30 | private Context context;
31 |
32 | private static SensorManager sm; // Holds references to the SensorManager
33 | List lSensor; // List of all sensors
34 |
35 | // private double compassValue = -1.0; // Last compass value
36 |
37 | private static final int vhSize = 6;
38 | private double[] values_history = new double[vhSize];
39 | private int vhPointer = 0;
40 |
41 | private double a;
42 | private double peak;
43 | private int step_timeout_ms;
44 | private long last_step_ts = 0;
45 | // private double old_z = 0.0;
46 |
47 | // last acc is low pass filtered
48 | private double[] lastAcc = new double[] {0.0, 0.0, 0.0};
49 | // last comp is untouched
50 | private double[] lastComp = new double[] {0.0, 0.0, 0.0};
51 |
52 | private int round = 0;
53 |
54 | /**
55 | * Handles sensor events. Updates the sensor
56 | */
57 | public SensorEventListener mySensorEventListener = new SensorEventListener() {
58 | @Override
59 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
60 | // Auto-generated method stub
61 | }
62 |
63 | @Override
64 | public void onSensorChanged(SensorEvent event) {
65 | switch (event.sensor.getType()) {
66 | case Sensor.TYPE_ACCELEROMETER:
67 | st.dataHookAcc(System.currentTimeMillis(), event.values[0], event.values[1], event.values[2]);
68 | // just update the oldest z value
69 | lastAcc[0] = ToolBox.lowpassFilter(lastAcc[0], event.values[0], a);
70 | lastAcc[1] = ToolBox.lowpassFilter(lastAcc[1], event.values[1], a);
71 | lastAcc[2] = ToolBox.lowpassFilter(lastAcc[2], event.values[2], a);
72 | break;
73 | case Sensor.TYPE_ORIENTATION:
74 | st.dataHookComp(System.currentTimeMillis(), event.values[0], event.values[1], event.values[2]);
75 | lastComp[0] = event.values[0];
76 | lastComp[1] = event.values[1];
77 | lastComp[2] = event.values[2];
78 | break;
79 | default:
80 | }// switch (event.sensor.getType())
81 | }
82 | };
83 |
84 | public double getA() {
85 | return a;
86 | }
87 |
88 | public double getPeak() {
89 | return peak;
90 | }
91 |
92 | public int getStep_timeout_ms() {
93 | return step_timeout_ms;
94 | }
95 |
96 | public void setA(double a) {
97 | this.a = a;
98 | }
99 |
100 | public void setPeak(double peak) {
101 | this.peak = peak;
102 | }
103 |
104 | public void setStep_timeout_ms(int stepTimeoutMs) {
105 | step_timeout_ms = stepTimeoutMs;
106 | }
107 |
108 | public StepDetection(Context context, StepTrigger st, double a, double peak, int step_timeout_ms){
109 | this.context = context;
110 | this.st = st;
111 | this.a = a;
112 | this.peak = peak;
113 | this.step_timeout_ms = step_timeout_ms;
114 | }
115 |
116 | /**
117 | * Enable step detection
118 | */
119 | public void load(){
120 | // Sensors
121 | sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
122 | lSensor = sm.getSensorList(Sensor.TYPE_ALL);
123 |
124 | for (int i = 0; i < lSensor.size(); i++) {
125 | // Register only compass and accelerometer
126 | if (lSensor.get(i).getType() == Sensor.TYPE_ACCELEROMETER
127 | || lSensor.get(i).getType() == Sensor.TYPE_ORIENTATION) {
128 | sm.registerListener(mySensorEventListener, lSensor.get(i), SensorManager.SENSOR_DELAY_FASTEST);
129 | }
130 | }
131 |
132 | // Register timer
133 | timer = new Timer("UpdateData", false);
134 | TimerTask task = new TimerTask(){
135 |
136 | @Override
137 | public void run() {
138 | updateData();
139 | }
140 | };
141 | timer.schedule(task, 0, INTERVAL_MS);
142 | }
143 |
144 | /**
145 | * Disable step detection
146 | */
147 | public void unload(){
148 | timer.cancel();
149 | timer.purge();
150 | timer = null;
151 | sm.unregisterListener(mySensorEventListener);
152 | }
153 |
154 | /**
155 | * This is called every INTERVAL_MS ms from the TimerTask.
156 | */
157 | private void updateData(){
158 | // Get current time for time stamps
159 | long now_ms = System.currentTimeMillis();
160 |
161 | // Create local value for compass and old_z, such that it is consistent during logs
162 | // (It might change in between, which is circumvented by this)
163 |
164 | // array.clone() does not work here!!
165 | // this does not work as well!!
166 | // double[] oldAcc = {lastAcc[0],lastAcc[1],lastAcc[2]};
167 | // double[] oldComp = {lastComp[0],lastComp[1],lastComp[2]};
168 | double[] oldAcc = new double[3];
169 | System.arraycopy(lastAcc, 0, oldAcc, 0, 3);
170 | double[] oldComp = new double[3];
171 | System.arraycopy(lastComp, 0, oldComp, 0, 3);
172 | double lCompass = oldComp[0];
173 | double lOld_z = oldAcc[2];
174 | st.timedDataHook(now_ms, oldAcc, oldComp);
175 |
176 | addData(lOld_z);
177 |
178 | // Check if a step is detected upon data
179 | if((now_ms - last_step_ts) > step_timeout_ms && checkForStep(peak)){
180 | // Set latest detected step to "now"
181 | last_step_ts = now_ms;
182 | // Call algorithm for navigation/updating position
183 | st.trigger(now_ms, lCompass);
184 | Log.i("FOOTPATH", "Detected step in round = " + round + " @ "+ now_ms);
185 |
186 | }
187 | round++;
188 | }
189 |
190 | private void addData(double value){
191 | values_history[vhPointer % vhSize] = value;
192 | vhPointer++;
193 | vhPointer = vhPointer % vhSize;
194 | }
195 |
196 | private boolean checkForStep(double peakSize) {
197 | // Add value to values_history
198 |
199 | int lookahead = 5;
200 | double diff = peakSize;
201 |
202 |
203 | for( int t = 1; t <= lookahead; t++){
204 | if((values_history[(vhPointer - 1 - t + vhSize + vhSize) % vhSize] -
205 | values_history[(vhPointer - 1 + vhSize) % vhSize]
206 | > diff)){
207 | Log.i("FOOTPATH", "Detected step with t = " + t + ", diff = " + diff + " < " + (values_history[(vhPointer - 1 - t + vhSize + vhSize) % vhSize] -
208 | values_history[(vhPointer - 1 + vhSize) % vhSize]));
209 | return true;
210 | }
211 | }
212 | return false;
213 | }
214 |
215 | Timer timer;
216 | }
217 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/core/StepTrigger.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.core;
2 |
3 | /**
4 | * An interface to be notified abuot detected steps and their directions. Also
5 | * there are hooks to to obtain values from sensors.
6 | *
7 | * @author Paul Smith
8 | *
9 | */
10 | public interface StepTrigger {
11 |
12 | /**
13 | * Called each time a step is triggered.
14 | *
15 | * @param now_ms the time stamp of the detected step
16 | * @param compDir the compass bearing
17 | */
18 | public void trigger(long now_ms, double compDir);
19 |
20 | /**
21 | * Called each time the accelerometer sensor values change
22 | *
23 | * @param now_ms the time stamp of the changed values
24 | * @param x x-axis
25 | * @param y y-axis
26 | * @param z z-axis
27 | */
28 | public void dataHookAcc(long now_ms, double x, double y, double z);
29 |
30 | /**
31 | * Called each time the compass sensor values change
32 | *
33 | * @param now_ms the time stamp of the changed values
34 | * @param x x-axis
35 | * @param y y-axis
36 | * @param z z-axis
37 | */
38 | public void dataHookComp(long now_ms, double x, double y, double z);
39 |
40 | /**
41 | * Called each time a sample is used to detect steps
42 | *
43 | * @param now_ms the time stamp of the sample
44 | * @param acc the accelerometer value (z-axis)
45 | * @param comp the compass bearing
46 | */
47 | public void timedDataHook(long now_ms, double[] acc, double[] comp);
48 | }
49 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/Graph.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | import java.io.IOException;
4 | import java.util.LinkedList;
5 | import java.util.Stack;
6 |
7 | import org.xmlpull.v1.XmlPullParser;
8 | import org.xmlpull.v1.XmlPullParserException;
9 |
10 |
11 | import android.content.res.XmlResourceParser;
12 |
13 | /**
14 | * This class is used to create a graph from XML files stored in the directory
15 | * res/xml. Data from multiple files/layers can be joined into a single map/graph
16 | * with the function mergeNodes(). After graph creation use functions implemented
17 | * in this class to find routes, nodes, etc.
18 | *
19 | * @author Paul Smith
20 | *
21 | */
22 | public class Graph {
23 | public LinkedList nodes;
24 | public LinkedList edges;
25 |
26 | private GraphNode[] array_nodes_by_id;
27 | private GraphNode[] array_nodes_by_name;
28 |
29 | public Graph(){
30 | nodes = new LinkedList();
31 | edges = new LinkedList();
32 | }
33 |
34 | public boolean addToGraphFromXMLResourceParser(XmlResourceParser xrp) throws XmlPullParserException, IOException{
35 | boolean ret = false; // return value
36 | if(xrp == null){
37 | return ret;
38 | }
39 |
40 | boolean isOsmData = false; // flag to wait for osm data
41 |
42 | GraphNode tempNode = new GraphNode(); // temporary node to be added to all nodes in file
43 | GraphNode NULL_NODE = new GraphNode(); // 'NULL' node to point to, for dereferencing
44 | GraphWay tempWay = new GraphWay(); // temporary way to be added to all nodes in file
45 | GraphWay NULL_WAY = new GraphWay(); // 'NULL' node to point to, for dereferencing
46 |
47 | LinkedList allNodes = new LinkedList(); // store all nodes found in file
48 | LinkedList allWays = new LinkedList(); // store all ways found in file
49 |
50 | xrp.next();
51 | int eventType = xrp.getEventType();
52 | while (eventType != XmlPullParser.END_DOCUMENT) {
53 | switch(eventType){
54 | case XmlPullParser.START_DOCUMENT:
55 | break;
56 | case XmlPullParser.START_TAG:
57 | if(!isOsmData){
58 | if(xrp.getName().equals("osm")){
59 | isOsmData = true; // osm
60 | // TODO: Test for correct version? (v0.6)
61 | }
62 | } else {
63 | int attributeCount = xrp.getAttributeCount();
64 | if(xrp.getName().equals("node")){ // node
65 | tempNode = new GraphNode();
66 | for(int i = 0; i < attributeCount; i++){
67 | if(xrp.getAttributeName(i).equals("id")){
68 | tempNode.setId(xrp.getAttributeIntValue(i, 0)); // node.id
69 | } if(xrp.getAttributeName(i).equals("lat")){
70 | tempNode.setLat(Double.parseDouble(xrp.getAttributeValue(i))); // node.lat
71 | } if(xrp.getAttributeName(i).equals("lon")){
72 | tempNode.setLon(Double.parseDouble(xrp.getAttributeValue(i))); // node.lon
73 | }
74 | }
75 | } else if(xrp.getName().equals("tag")){ // tag
76 | if(tempNode != NULL_NODE){ // node.tag
77 | for(int i = 0; i < attributeCount; i++){
78 | if(xrp.getAttributeName(i).equals("k")
79 | && xrp.getAttributeValue(i).equals("indoor")){ // node.tag.indoor
80 | String v = xrp.getAttributeValue(i + 1);
81 | tempNode.setIndoors(v.equals("yes"));
82 | if(v.equals("door")){
83 | tempNode.setIndoors(true);
84 | tempNode.setDoor(true); // this is a door (which is always inDOORS) ;)
85 | }
86 | } else if(xrp.getAttributeName(i).equals("k")
87 | && xrp.getAttributeValue(i).equals("name")){ // node.tag.name
88 | String v = xrp.getAttributeValue(i + 1);
89 | tempNode.setName(v);
90 | } else if(xrp.getAttributeName(i).equals("k")
91 | && xrp.getAttributeValue(i).equals("merge_id")){ // node.tag.merge_id
92 | String v = xrp.getAttributeValue(i + 1);
93 | tempNode.setMergeId(v);
94 | } else if(xrp.getAttributeName(i).equals("k")
95 | && xrp.getAttributeValue(i).equals("step_count")){ // node.tag.step_count
96 | int v = xrp.getAttributeIntValue(i + 1, Integer.MAX_VALUE);
97 | tempNode.setSteps(v);
98 | } else if(xrp.getAttributeName(i).equals("k")
99 | && xrp.getAttributeValue(i).equals("level")){ // node.tag.level
100 | String v = xrp.getAttributeValue(i + 1);
101 | float f = Float.parseFloat(v);
102 | tempNode.setLevel(f);
103 | }
104 |
105 | }
106 | } else { // way.tag
107 | for(int i = 0; i < attributeCount; i++){
108 | if(xrp.getAttributeName(i).equals("k")
109 | && xrp.getAttributeValue(i).equals("wheelchair")){ // way.tag.wheelchair
110 | String v = xrp.getAttributeValue(i + 1);
111 | short wheelchair = (short) (v.equals("yes") ? 1 : v.equals("limited")?0: -1);
112 | tempWay.setWheelchair(wheelchair);
113 | } else if(xrp.getAttributeName(i).equals("k")
114 | && xrp.getAttributeValue(i).equals("step_count")){ // way.tag.step_count
115 | int v = xrp.getAttributeIntValue(i + 1, Integer.MAX_VALUE);
116 | tempWay.setSteps(v);
117 | } else if(xrp.getAttributeName(i).equals("k")
118 | && xrp.getAttributeValue(i).equals("level")){ // way.tag.level
119 | String v = xrp.getAttributeValue(i + 1);
120 | float f = Float.parseFloat(v);
121 | tempWay.setLevel(f);
122 | } else if(xrp.getAttributeName(i).equals("k")
123 | && xrp.getAttributeValue(i).equals("indoor")){ // way.tag.indoor
124 | String v = xrp.getAttributeValue(i + 1);
125 | tempWay.setIndoor(v.equals("yes"));
126 | }else if(xrp.getAttributeName(i).equals("k")
127 | && xrp.getAttributeValue(i).equals("highway")){ // way.tag.highway
128 | String v = xrp.getAttributeValue(i + 1);
129 | if(v.equals("steps")){
130 | tempWay.setWheelchair((short)-1);
131 | if(tempWay.getSteps() == 0){ // no steps configured before
132 | tempWay.setSteps(-1); // so set to undefined (but present),
133 | // otherwise might be set later
134 | }
135 | }
136 | if(v.equals("elevator")){
137 | tempWay.setWheelchair((short)1);
138 | tempWay.setSteps(-2);
139 | }
140 | }
141 | }
142 | }
143 |
144 | } else if(xrp.getName().equals("way")){ // way
145 | tempWay = new GraphWay();
146 | for(int i = 0; i < attributeCount; i++){
147 | if(xrp.getAttributeName(i).equals("id")){
148 | tempWay.setId(xrp.getAttributeIntValue(i, 0)); // way.id
149 | }
150 | }
151 | } else if(xrp.getName().equals("nd")){ // way.nd
152 | for(int i = 0; i < attributeCount; i++){
153 | if(xrp.getAttributeName(i).equals("ref")){ // way.nd.ref
154 | String v = xrp.getAttributeValue(i);
155 | int ref = Integer.parseInt(v);
156 | tempWay.addRef(ref);
157 | }
158 | }
159 | }
160 | }
161 | break;
162 | case XmlPullParser.END_TAG:
163 | if(isOsmData){
164 | if(xrp.getName().equals("osm")){
165 | ret = true;
166 | } else if(xrp.getName().equals("node")){ // node
167 | allNodes.add(tempNode);
168 | tempNode = NULL_NODE;
169 | } else if(xrp.getName().equals("tag")){ // tag
170 |
171 | } else if(xrp.getName().equals("way")){ // way
172 | allWays.add(tempWay);
173 | tempWay = NULL_WAY;
174 | } else if(xrp.getName().equals("nd")){ // way.nd
175 |
176 | }
177 | }
178 | break;
179 | default:
180 | }
181 | eventType = xrp.next();
182 | }
183 |
184 | LinkedList remainingWays = new LinkedList();
185 |
186 | for(GraphWay way : allWays){ // find ways which are indoors at some point
187 | LinkedList refs = way.getRefs();
188 | if(way.isIndoor()){ // whole path is indoors -> keep
189 | remainingWays.add(way);
190 | } else { // check for path with indoor node
191 | boolean stop = false;
192 | for(Integer ref : refs){ // check if there is a node on path which is indoors
193 | for(GraphNode node : allNodes){
194 | if(node.getId() == ref.intValue()){
195 | remainingWays.add(way);
196 | stop = true; // found indoor node on path to be added to graph
197 | // thus stop both for loops and continue with next way
198 | }
199 | if(stop)
200 | break;
201 | }
202 | if(stop)
203 | break;
204 | }
205 | }
206 | }
207 |
208 | if(remainingWays.size() == 0) // return false, nothing to be added to graph
209 | return false;
210 |
211 | for(GraphWay way : remainingWays){
212 | short wheelchair = way.getWheelchair();
213 | float level = way.getLevel();
214 | boolean indoor = way.isIndoor();
215 | GraphNode firstNode = getNode(allNodes,way.getRefs().get(0).intValue());
216 | for(int i = 1; i <= way.getRefs().size() - 1; i++){
217 | GraphNode nextNode = getNode(allNodes,way.getRefs().get(i).intValue());
218 | double len = getDistance(firstNode.getLat(), // get length between P1 and P2
219 | firstNode.getLon(),
220 | nextNode.getLat(),
221 | nextNode.getLon());
222 | double compDegree = getInitialBearing(firstNode.getLat(), // get initial bearing between P1 and P2
223 | firstNode.getLon(),
224 | nextNode.getLat(),
225 | nextNode.getLon());
226 | GraphEdge tempEdge = new GraphEdge(firstNode, nextNode, len, compDegree, wheelchair, level,indoor);
227 | if(way.getSteps()>0){ // make edge a staircase if steps_count
228 | tempEdge.setStairs(true); // was set correctly
229 | tempEdge.setElevator(false);
230 | tempEdge.setSteps(way.getSteps());
231 | } else if(way.getSteps()==-1){
232 | tempEdge.setStairs(true); // make edge a staircase if steps_count
233 | tempEdge.setElevator(false);
234 | tempEdge.setSteps(-1); // was set to -1 (undefined steps)
235 | } else if(way.getSteps()==-2){
236 | tempEdge.setStairs(false); // make edge an elevator if steps_count
237 | tempEdge.setElevator(true);
238 | tempEdge.setSteps(-2); // was set to -2
239 | } else if(way.getSteps() == 0){
240 | tempEdge.setStairs(false);
241 | tempEdge.setElevator(false);
242 | tempEdge.setSteps(0);
243 | }
244 | edges.add(tempEdge); // add edge to graph
245 | if(!nodes.contains(firstNode)){
246 | nodes.add(firstNode); // add node to graph if not present
247 | }
248 | firstNode = nextNode;
249 | }
250 |
251 | if(!nodes.contains(firstNode)){
252 | nodes.add(firstNode); // add last node to graph if not present
253 | }
254 | }
255 |
256 | return ret;
257 | }
258 |
259 | // use this to add edges for stairs to flags, this should be called once
260 | public void mergeNodes(){
261 | // Edges are note inserted anymore.
262 | // Nodes are "Merged". Currently.
263 | LinkedList nodesWithMergeId = new LinkedList();
264 | // Collect all relevant nodes to merge
265 | for(GraphNode node: nodes){
266 | if(node.getMergeId()!=null){
267 | nodesWithMergeId.add(node);
268 | }
269 | }
270 | for(GraphNode node: nodesWithMergeId){
271 | for(GraphNode otherNode: nodesWithMergeId){
272 | // Only merge if same id, but not same node!
273 | if(node.getMergeId() != null && node.getMergeId().equals(otherNode.getMergeId())
274 | && !node.equals(otherNode)){
275 | // Update all references pointing to otherNode to node
276 | for(GraphEdge edge: edges){
277 | if(edge.getNode0().equals(otherNode)){
278 | edge.setNode0(node);
279 | }
280 | if(edge.getNode1().equals(otherNode)){
281 | edge.setNode1(node);
282 | }
283 | }
284 | // otherNode was merged/removed, do not check
285 | otherNode.setMergeId(null);
286 | }
287 | }
288 | }
289 | // Create arrays for binary search
290 | array_nodes_by_id = sortNodesById(nodes);
291 | array_nodes_by_name = sortNodesByName(nodes);
292 | // Add edges to node, faster look up for neighbors
293 | for(GraphEdge edge: edges){
294 | GraphNode n0 = edge.getNode0();
295 | GraphNode n1 = edge.getNode1();
296 | if(!n0.getLocEdges().contains(edge)){
297 | n0.getLocEdges().add(edge);
298 | }
299 | if(!n1.getLocEdges().contains(edge)){
300 | n1.getLocEdges().add(edge);
301 | }
302 | }
303 | }
304 |
305 |
306 | public Stack getShortestPath(String from, String to,
307 | boolean staircase, boolean elevator, boolean outside){
308 | GraphNode gnFrom = getNodeFromName(from);
309 | GraphNode gnTo = getNodeFromName(to);
310 | return getShortestPath(gnFrom, gnTo, staircase, elevator, outside);
311 | }
312 | public Stack getShortestPath(int from, String to,
313 | boolean staircase, boolean elevator, boolean outside){
314 | GraphNode gnFrom = getNode(from);
315 | GraphNode gnTo = getNodeFromName(to);
316 | return getShortestPath(gnFrom, gnTo, staircase, elevator, outside);
317 | }
318 |
319 | // Returns a stack of nodes, with the destination at the bottom using
320 | // Dykstra's algorithm
321 | public Stack getShortestPath(GraphNode from, GraphNode to,
322 | boolean staircase, boolean elevator, boolean outside){
323 |
324 | if(from == null || to == null){
325 | return null;
326 | }
327 |
328 | int remaining_nodes = array_nodes_by_id.length;
329 | GraphNode[] previous = new GraphNode[array_nodes_by_id.length];
330 | double[] dist = new double[array_nodes_by_id.length];
331 | boolean[] visited = new boolean[array_nodes_by_id.length];
332 |
333 | // Set initial values
334 | for(int i = 0; i < array_nodes_by_id.length; i++){
335 | dist[i] = Double.POSITIVE_INFINITY;
336 | previous[i] = null;
337 | visited[i] = false;
338 | }
339 | dist[getNodePosInIdArray(from)] = 0;
340 | while(remaining_nodes>0){
341 | // Vertex u in q with smallest dist[]
342 | GraphNode u;
343 | double minDist = Double.POSITIVE_INFINITY;
344 | int u_i = -1;
345 | for(int i = 0; i < array_nodes_by_id.length; i++){
346 | if(!visited[i] && dist[i] nOuIq = getNeighbours(visited, u, staircase, elevator, outside);
365 | if(u.equals(to)){
366 | // u = to -> found path to destination
367 | // Build stack of nodes, destination at the botton
368 | Stack s = new Stack();
369 | while(previous[u_i]!=null){
370 | s.push(u);
371 | u_i = getNodePosInIdArray(u);
372 | u = previous[u_i];
373 | }
374 | return s;
375 | }else {
376 | remaining_nodes--;
377 | }
378 | for(GraphNode v : nOuIq){
379 | double dist_alt = dist[u_i] + dist(u,v);
380 | int v_i = getNodePosInIdArray(v);
381 | if(dist_alt < dist[v_i]){
382 | dist[v_i] = dist_alt;
383 | previous[v_i] = u;
384 | }
385 | }
386 | }
387 | return null;
388 | }
389 |
390 | // Returns the distance of two nodes by finding the corresponding edge and reading the len value
391 | private double dist(GraphNode from, GraphNode to){
392 | double ret = Double.POSITIVE_INFINITY;
393 | ret = getEdge(from,to).getLen();
394 | return ret;
395 | }
396 |
397 | // Returns all neighbors of given node from a given subset (list) of nodes in this graph
398 | private LinkedList getNeighbours(boolean[] visited, GraphNode node, boolean staircase, boolean elevator, boolean outside){
399 |
400 | LinkedList ret = new LinkedList();
401 | for(GraphEdge edge : node.getLocEdges()){ // check all edges if they contain node
402 | if(edge.isStairs() && !staircase ){ // edge has steps, but not allowed -> skip
403 | continue;
404 | }
405 | if(edge.isElevator() && !elevator ){ // edge is elevator, but not allowed -> skip
406 | continue;
407 | }
408 | if(!edge.isIndoor() && !outside){ // edge is outdoors, but not allowed -> skip
409 | continue;
410 | }
411 |
412 | GraphNode buf = null;
413 | if(edge.getNode0().equals(node)){ // node0 is node
414 | buf = edge.getNode1(); // add node1
415 | } else if(edge.getNode1().equals(node)){ // node 1 is node
416 | buf = edge.getNode0(); // add node0
417 | }
418 | if(outside){
419 | if(buf!=null){ // if outside, all nodes are allowed
420 | if(!ret.contains(buf) && !visited[getNodePosInIdArray(buf)]){
421 | ret.add(buf); // add buf only once, iff not visited
422 | }
423 | }
424 | } else { // if !outside, only indoor nodes are allowed
425 | if(buf!=null && buf.isIndoors()){
426 | if(!ret.contains(buf) && !visited[getNodePosInIdArray(buf)]){
427 | ret.add(buf); // add buf only once, iff not visited
428 | }
429 | }
430 | }
431 | }
432 | return ret;
433 | }
434 |
435 | // return node pos via binary search
436 | private int getNodePosInIdArray(GraphNode node){
437 | int u = 0;
438 | int o = array_nodes_by_id.length-1;
439 | int m = 0;
440 |
441 | while(!(o < u)) {
442 | m = (u+o)/2;
443 | if(node.getId() == array_nodes_by_id[m].getId()){
444 | return m;
445 | }
446 | if(node.getId() < array_nodes_by_id[m].getId()){
447 | o = m-1;
448 | } else {
449 | u = m+1;
450 | }
451 | }
452 | return -1;
453 | }
454 |
455 | // This is the faster version which can be used after parsing the data
456 | public GraphNode getNode(int id){
457 | int u = 0;
458 | int o = array_nodes_by_id.length-1;
459 | int m = 0;
460 |
461 | while(!(o < u)) {
462 | m = (u+o)/2;
463 | if(id == array_nodes_by_id[m].getId()){
464 | return array_nodes_by_id[m];
465 | }
466 | if(id < array_nodes_by_id[m].getId()){
467 | o = m-1;
468 | } else {
469 | u = m+1;
470 | }
471 | }
472 | return null;
473 | }
474 |
475 | // This is the slower version which is used during parsing
476 | private GraphNode getNode(LinkedList list, int id){
477 | for(GraphNode node: list){
478 | if(node.getId() == id)
479 | return node;
480 | }
481 | return null;
482 | }
483 |
484 | // return all names of nodes != null in a String array
485 | public String[] getRoomList(){
486 | String[] retArray = new String[array_nodes_by_name.length];
487 | for(int i = 0; i < retArray.length; i++){
488 | retArray[i] = array_nodes_by_name[i].getName();
489 | }
490 | return retArray;
491 | }
492 |
493 | // creates a linked list form a stack, top to bottom
494 | public LinkedList getPathEdges(Stack navPath) {
495 | LinkedList pathEdges = new LinkedList();
496 | GraphNode a = navPath.pop();
497 | while(!navPath.isEmpty()){
498 | GraphNode b = navPath.pop();
499 | GraphEdge e = this.getEdge(a,b);
500 | if(e!=null){
501 | pathEdges.add(e);
502 | } else {
503 | return null;
504 | }
505 | a = b;
506 | }
507 | return pathEdges;
508 | }
509 |
510 | // returns the edge containing nodes a and b
511 | public GraphEdge getEdge(GraphNode a, GraphNode b) {
512 | GraphEdge ret = null;
513 | for(GraphEdge edge : a.getLocEdges()){ // return edge if
514 | if(edge.getNode0().equals(a) && edge.getNode1().equals(b)){ // node0=a, node1=b
515 | ret = edge; // or
516 | }else if(edge.getNode1().equals(a) && edge.getNode0().equals(b)){ // node0=b, node1=a
517 | ret = edge;
518 | } // else null
519 | }
520 | return ret;
521 | }
522 |
523 | /**
524 | * Returns the distance between two nodes
525 | * @param node_0 first node
526 | * @param node_1 second node
527 | * @return the distance in meters
528 | */
529 | public double getDistance(LatLonPos pos_0, GraphNode node_1){
530 | return getDistance(pos_0.getLat(), pos_0.getLon(), node_1.getLat(), node_1.getLon());
531 | }
532 |
533 | /**
534 | * Returns the distance between two points given in latitude/longitude
535 | * @param lat_1 latitude of first point
536 | * @param lon_1 longitude of first point
537 | * @param lat_2 latitude of second point
538 | * @param lon_2 longitude of second point
539 | * @return the distance in meters
540 | */
541 | public double getDistance(double lat_1, double lon_1, double lat_2, double lon_2) {
542 | // source: http://www.movable-type.co.uk/scripts/latlong.html
543 | double dLon = lon_2 - lon_1;
544 | double dLat = lat_2 - lat_1;
545 | lat_1 = Math.toRadians(lat_1);
546 | lon_1 = Math.toRadians(lon_1);
547 | lat_2 = Math.toRadians(lat_2);
548 | lon_2 = Math.toRadians(lon_2);
549 | dLon = Math.toRadians(dLon);
550 | dLat = Math.toRadians(dLat);
551 |
552 | double r = 6378137; // km
553 | double a = Math.sin(dLat/2)*Math.sin(dLat/2) +
554 | Math.cos(lat_1)*Math.cos(lat_2) *
555 | Math.sin(dLon/2)*Math.sin(dLon/2);
556 | double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
557 | return c*r;
558 | }
559 |
560 | public double getInitialBearing(double lat_1, double lon_1, double lat_2, double lon_2) {
561 | // source: http://www.movable-type.co.uk/scripts/latlong.html
562 | double dLon = lon_2 - lon_1;
563 | lat_1 = Math.toRadians(lat_1);
564 | lon_1 = Math.toRadians(lon_1);
565 | lat_2 = Math.toRadians(lat_2);
566 | lon_2 = Math.toRadians(lon_2);
567 | dLon = Math.toRadians(dLon);
568 | double y = Math.sin(dLon) * Math.cos(lat_2);
569 | double x = Math.cos(lat_1) * Math.sin(lat_2) -
570 | Math.sin(lat_1) * Math.cos(lat_2) * Math.cos(dLon);
571 | double b = Math.atan2(y, x);
572 | b = Math.toDegrees(b);
573 | return (b < 0) ? b + 360.0 : b;
574 | }
575 |
576 | // returns the node with the given name, binary search
577 | public GraphNode getNodeFromName(String name){
578 | int u = 0;
579 | int o = array_nodes_by_name.length-1;
580 | int m = 0;
581 |
582 | while(!(o < u)) {
583 | m = (u+o)/2;
584 | if(name.equals(array_nodes_by_name[m].getName())){
585 | return array_nodes_by_name[m];
586 | }
587 | if(name.compareTo(array_nodes_by_name[m].getName()) < 0){
588 | o = m-1;
589 | } else {
590 | u = m+1;
591 | }
592 | }
593 |
594 | return null;
595 | }
596 |
597 | /**
598 | * Returns the closest node to a position at the given level
599 | * @param pos the position
600 | * @param level the level
601 | * @param indoor set to true if indoor nodes should be included
602 | * @param maxMeters limit of distance to a node
603 | * @return the closest GraphNode
604 | */
605 | public GraphNode getClosestNodeToLatLonPos(LatLonPos pos, float level, boolean indoor, int maxMeters){
606 | double minDistance = Double.MAX_VALUE;
607 | double tempDistance = Double.MAX_VALUE;
608 | GraphNode minDistNode = null;
609 |
610 | for(GraphNode node: nodes){
611 | // First: node has to be at the same level
612 | // Second: if indoor = true, then take all nodes
613 | // Third: if indoor = false check if node is not indoors!
614 | if(node.getLevel() == level && (indoor || (node.isIndoors()==indoor))){
615 | tempDistance = getDistance(pos, node);
616 | if(tempDistance < minDistance){
617 | minDistance = tempDistance;
618 | minDistNode = node;
619 | }
620 | }
621 | }
622 | if(minDistance < maxMeters){
623 | return minDistNode;
624 | } else {
625 | return null;
626 | }
627 | }
628 |
629 | public double getClosestDistanceToNode(LatLonPos pos, float level, boolean indoor){
630 | double minDistance = Double.MAX_VALUE;
631 | double tempDistance = Double.MAX_VALUE;
632 |
633 | for(GraphNode node: nodes){
634 | // First: node has to be at the same level
635 | // Second: if indoor = true, then take all nodes
636 | // Third: if indoor = false check if node is not indoors!
637 | if(node.getLevel() == level && (indoor || (node.isIndoors()!=indoor))){
638 | tempDistance = getDistance(pos, node);
639 | if(tempDistance < minDistance){
640 | minDistance = tempDistance;
641 | }
642 | }
643 | }
644 | return minDistance;
645 | }
646 |
647 | // creates an array, containing only nodes _with_ a name, sorted ascending
648 | private GraphNode[] sortNodesByName(LinkedList nodes){
649 | GraphNode[] node_array;
650 | GraphNode temp = null;
651 | int num_nulls = 0;
652 | int c = 0;
653 | boolean not_sorted = true;
654 | // count number of nodes without a name (null)
655 | for(int i = 0; i < nodes.size(); i++){
656 | if(nodes.get(i) != null && nodes.get(i).getName() == null){
657 | num_nulls++;
658 | }
659 | }
660 | // create an array for nodes with a name
661 | node_array = new GraphNode[nodes.size() - num_nulls];
662 | for(GraphNode node: nodes){
663 | if(node != null && node.getName() != null){
664 | // insert node with name into array
665 | node_array[c] = node;
666 | c++;
667 | }
668 | }
669 | // sort by name (bubble sort)
670 | while(not_sorted){
671 | not_sorted = false;
672 | for(int i = 0; i < node_array.length - 1; i++){
673 | if(node_array[i].getName().compareTo(node_array[i+1].getName()) > 0){
674 | temp = node_array[i];
675 | node_array[i] = node_array[i+1];
676 | node_array[i+1] = temp;
677 | not_sorted = true;
678 | }
679 | }
680 | }
681 | // return array which had no nulls
682 | return node_array;
683 | }
684 |
685 | // creates an array,sorted by id ascending
686 | private GraphNode[] sortNodesById(LinkedList nodes){
687 | GraphNode[] node_array;
688 | GraphNode temp = null;
689 | int c = 0;
690 | boolean not_sorted = true;
691 | // create an array for all nodes
692 | node_array = new GraphNode[nodes.size()];
693 | for(GraphNode node: nodes){
694 | if(node != null){
695 | // insert node
696 | node_array[c] = node;
697 | c++;
698 | }
699 | }
700 | // sort by id (bubble sort)
701 | while(not_sorted){
702 | not_sorted = false;
703 | for(int i = 0; i < node_array.length - 1; i++){
704 | if(node_array[i].getId() > node_array[i+1].getId()){
705 | temp = node_array[i];
706 | node_array[i] = node_array[i+1];
707 | node_array[i+1] = temp;
708 | not_sorted = true;
709 | }
710 | }
711 | }
712 | return node_array;
713 | }
714 | }
715 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/GraphEdge.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | /**
4 | * A class to maintain an edge in the graph.
5 | *
6 | * @author Paul Smith
7 | *
8 | */
9 | public class GraphEdge {
10 | private GraphNode node0;
11 | private GraphNode node1;
12 | private double len;
13 | private double bearing;
14 | private short wheelchair;
15 | private boolean isStairs = false;
16 | private boolean isElevator = false;
17 |
18 | // >0 := number correct steps given
19 | // 0 := no steps
20 | // -1 := undefined number of steps
21 | // -2 := elevator
22 | private int numSteps = 0;
23 |
24 | private float level;
25 | private boolean isIndoor;
26 |
27 | /**
28 | * Constructor to create an empty edge with everything set to 0/null/false
29 | */
30 | public GraphEdge() {
31 | this.node0 = null;
32 | this.node1 = null;
33 | this.len = 0.0;
34 | this.wheelchair = 1;
35 | this.level = Float.MAX_VALUE;
36 | this.isIndoor = false;
37 | }
38 |
39 | /**
40 | * Constructor to create an edge with given parameters.
41 | *
42 | * @param node0 the first GraphNode
43 | * @param node1 the second GraphNode
44 | * @param len the length of this edge
45 | * @param compDir the direction of this edge (node0 -> node1)
46 | * @param wheelchair the value concerning the wheelchair attribute
47 | * @param level the level of this edge
48 | * @param isIndoor true if is indoor
49 | */
50 | public GraphEdge(GraphNode node0, GraphNode node1, double len, double compDir, short wheelchair, float level, boolean isIndoor) {
51 | this.node0 = node0;
52 | this.node1 = node1;
53 | this.len = len;
54 | this.bearing = compDir;
55 | this.wheelchair = wheelchair;
56 | this.level = level;
57 | this.isIndoor = isIndoor;
58 | }
59 |
60 | public double getCompDir() {
61 | return bearing;
62 | }
63 |
64 | public GraphNode getNode0() {
65 | return node0;
66 | }
67 |
68 | public GraphNode getNode1() {
69 | return node1;
70 | }
71 |
72 | public double getLen() {
73 | return len;
74 | }
75 |
76 | public short getWheelchair() {
77 | return wheelchair;
78 | }
79 |
80 | public boolean isStairs(){
81 | return isStairs;
82 | }
83 |
84 | public boolean isElevator(){
85 | return isElevator;
86 | }
87 |
88 | public int getSteps(){
89 | return numSteps;
90 | }
91 |
92 | public float getLevel() {
93 | return level;
94 | }
95 |
96 | public boolean isIndoor(){
97 | return isIndoor;
98 | }
99 |
100 | public void setCompDir(double compDir) {
101 | this.bearing = compDir;
102 | }
103 |
104 | public void setNode0(GraphNode node0) {
105 | this.node0 = node0;
106 | }
107 |
108 | public void setNode1(GraphNode node1) {
109 | this.node1 = node1;
110 | }
111 |
112 | public void setLen(double len) {
113 | this.len = len;
114 | }
115 |
116 | public void setWheelchair(short wheelchair) {
117 | this.wheelchair = wheelchair;
118 | }
119 |
120 | public void setStairs(boolean isStairs) {
121 | this.isStairs = isStairs;
122 | }
123 |
124 | public void setElevator(boolean isElevator) {
125 | this.isElevator = isElevator;
126 | }
127 |
128 | public void setSteps(int numSteps){
129 | this.numSteps = numSteps;
130 | if(numSteps>0)
131 | this.setWheelchair((short)-1);//if steps, NO wheelchair
132 | }
133 |
134 | public void setLevel(float level) {
135 | this.level = level;
136 | }
137 |
138 | public void setLevel(boolean isIndoor){
139 | this.isIndoor = isIndoor;
140 | }
141 |
142 | public boolean equals(GraphEdge edge){
143 | if(edge == null)
144 | return false;
145 | return this.node0.equals(edge.getNode0()) && this.node1.equals(edge.getNode1())
146 | || this.node0.equals(edge.getNode1()) && this.node1.equals(edge.getNode0());
147 | }
148 |
149 | public boolean contains(GraphNode node){
150 | return getNode0().equals(node) || getNode1().equals(node);
151 | }
152 |
153 | public String toString(){
154 | String ret = "\nEdge(" + this.node0.getId() + " to " + this.node1.getId() + "): ";
155 | ret += "\n Length: " + this.len;
156 | ret += "\n Bearing: " + this.bearing;
157 | if(isStairs()){
158 | ret += "\n Staircase with: " + this.getSteps() + " steps";
159 | }
160 | if(isElevator()){
161 | ret += "\n Elevator: yes";
162 | }
163 | ret+="\n Level: " + level;
164 | return ret;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/GraphElement.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | /**
4 | *
5 | * @author Paul Smith
6 | *
7 | */
8 | public class GraphElement {
9 | // nothing, just to have a parent for GraphNode and GraphEdge
10 | // TODO: delete this?
11 | }
12 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/GraphNode.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | import java.util.LinkedList;
4 |
5 |
6 | /**
7 | * A class to maintain a node with given parameters found in OSM/XML data.
8 | *
9 | * @author Paul Smith
10 | *
11 | */
12 | public class GraphNode {
13 | private double lon;
14 | private double lat;
15 | private String name;
16 | private boolean isInDoors;
17 | private boolean isDoor;
18 | private float level;
19 | private int id;
20 | private String merge_id;
21 | private int numSteps = 0;
22 | private LinkedList loc_edges;
23 |
24 | /**
25 | * Constructor to create an empty node with everything set to 0/null/false
26 | */
27 | public GraphNode() {
28 | this.lon = 0.0;
29 | this.lat = 0.0;
30 | this.name = null;
31 | this.isInDoors = false;
32 | this.isDoor = false;
33 | this.level = 0;
34 | this.merge_id = null;
35 | loc_edges = new LinkedList();
36 | }
37 |
38 | /**
39 | * Constructor to create a node with given parameters.
40 | *
41 | * @param lon the longitude
42 | * @param lat the latitude
43 | * @param name the name
44 | * @param indoors true if is indoor node
45 | * @param isDoor true if is door
46 | * @param level the level
47 | */
48 | public GraphNode(double lon, double lat, String name, boolean indoors, boolean isDoor, float level) {
49 | this.lon = lon;
50 | this.lat = lat;
51 | this.name = name;
52 | this.isInDoors = indoors;
53 | this.isDoor = isDoor;
54 | this.level = level;
55 | this.merge_id = null;
56 | loc_edges = new LinkedList();
57 | }
58 |
59 | /**
60 | * Creates a LatLonPos object to return the location.
61 | *
62 | * @return the new LatLonPos object of this node's location
63 | */
64 | public LatLonPos getPos(){
65 | LatLonPos ret = new LatLonPos();
66 | ret.setLat(lat);
67 | ret.setLon(lon);
68 | ret.setLevel(level);
69 | return ret;
70 | }
71 |
72 | public double getLon() {
73 | return lon;
74 | }
75 |
76 | public double getLat() {
77 | return lat;
78 | }
79 |
80 | public String getName() {
81 | return name;
82 | }
83 |
84 | public boolean isIndoors() {
85 | return isInDoors;
86 | }
87 |
88 | public boolean isDoor() {
89 | return isDoor;
90 | }
91 |
92 | public float getLevel() {
93 | return level;
94 | }
95 |
96 | public int getId() {
97 | return id;
98 | }
99 |
100 | public String getMergeId() {
101 | return this.merge_id;
102 | }
103 |
104 | public int getSteps(){
105 | return numSteps;
106 | }
107 |
108 | public LinkedList getLocEdges(){
109 | return loc_edges;
110 | }
111 |
112 | public void setLon(double lon) {
113 | this.lon = lon;
114 | }
115 |
116 | public void setLat(double lat) {
117 | this.lat = lat;
118 | }
119 |
120 | public void setName(String name) {
121 | this.name = name;
122 | }
123 |
124 | public void setIndoors(boolean indoors) {
125 | this.isInDoors = indoors;
126 | }
127 |
128 | public void setDoor(boolean door){
129 | this.isDoor = door;
130 | }
131 |
132 | public void setLevel(float level) {
133 | this.level = level;
134 | }
135 |
136 | public void setId(int id){
137 | this.id = id;
138 | }
139 |
140 | public void setMergeId(String mergeId) {
141 | this.merge_id = mergeId;
142 | }
143 |
144 | public void setSteps(int numSteps){
145 | this.numSteps = numSteps;
146 | }
147 |
148 | public boolean equals(GraphNode node){
149 | if(node == null)
150 | return false;
151 | return this.getId()==node.getId();
152 | }
153 |
154 | public String toString(){
155 | String ret = "\nNode(" + this.id +"): ";
156 | ret += name!=null?name:"N/A";
157 | ret += isInDoors ? " (indoors)" : " (outdoors)";
158 | ret += "\n Level: " + this.level;
159 | ret += "\n Lat: " + this.lat;
160 | ret += "\n Lon: " + this.lon;
161 | if(getMergeId()!=null){
162 | ret +="\n Merges with: " + getMergeId();
163 | }
164 | return ret;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/GraphWay.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | import java.util.LinkedList;
4 |
5 | /**
6 | * A class to maintain a way with given parameters found in OSM/XML data.
7 | *
8 | * @author Paul Smith
9 | *
10 | */
11 | public class GraphWay {
12 | // all nodes on this path ( ref0 -> ref1 -> ref2 -> ...)
13 | private LinkedList refs;
14 | private int id;
15 | private short wheelchair;
16 |
17 | // >0 := number correct steps given
18 | // 0 := no steps
19 | // -1 := undefined number of steps
20 | // -2 := elevator
21 | private int numSteps = 0;
22 |
23 | // Float.MAX_VALUE == undefined!
24 | private float level;
25 | private boolean isIndoor;
26 |
27 | /**
28 | * Constructor to create an empty way.
29 | */
30 | public GraphWay() {
31 | this.refs = new LinkedList();
32 | this.id = 0;
33 | this.wheelchair = 1;
34 | this.level = Float.MAX_VALUE;
35 | }
36 |
37 | /**
38 | * Constructor to create a coordinate with given parameters.
39 | *
40 | * @param refs a LinkedList of Integers, references to GraphNodes
41 | * @param id the id of this way
42 | * @param wheelchair the value concerning the wheelchair attribute
43 | * @param level the level of this way
44 | */
45 | public GraphWay(LinkedList refs, int id, short wheelchair, float level) {
46 | this.refs = refs;
47 | this.id = id;
48 | this.wheelchair = wheelchair;
49 | this.level = level;
50 | }
51 |
52 | public LinkedList getRefs() {
53 | return refs;
54 | }
55 |
56 | public int getId() {
57 | return id;
58 | }
59 |
60 | public short getWheelchair() {
61 | return wheelchair;
62 | }
63 |
64 | public int getSteps(){
65 | return numSteps;
66 | }
67 |
68 | public float getLevel(){
69 | return level;
70 | }
71 |
72 | public boolean isIndoor(){
73 | return isIndoor;
74 | }
75 |
76 | public void setRefs(LinkedList refs) {
77 | this.refs = refs;
78 | }
79 |
80 | public void setId(int id) {
81 | this.id = id;
82 | }
83 |
84 | public void setWheelchair(short wheelchair) {
85 | this.wheelchair = wheelchair;
86 | }
87 |
88 | public void setSteps(int numSteps){
89 | this.numSteps = numSteps;
90 | }
91 |
92 | public void setLevel(float level){
93 | this.level = level;
94 | }
95 |
96 | public void setIndoor(boolean isIndoor){
97 | this.isIndoor = isIndoor;
98 | }
99 |
100 | public void addRef(int ref){
101 | this.refs.add(new Integer(ref));
102 | }
103 |
104 | public String toString(){
105 | String ret = "\nWay(" + this.id +"): ";
106 | ret += this.wheelchair >= 0 ? "(wheelchair)" : "(non-wheelchair)";
107 | ret += "\nRefs:";
108 | for(Integer ref: refs){
109 | ret += "\n " + ref.intValue();
110 | }
111 | return ret;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/graph/LatLonPos.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.graph;
2 |
3 | /**
4 | * A class to maintain a coordinate consisting of latitude and longitude, with
5 | * a given level.
6 | *
7 | * @author Paul Smith
8 | *
9 | */
10 | public class LatLonPos {
11 | private double lat;
12 | private double lon;
13 |
14 | // Float.MAX_VALUE == undefined!
15 | private float level;
16 |
17 | // planet radius in meters
18 | private static final int r = 6378137;
19 | // meters per degree
20 | private static final double scale = (Math.PI * r)/180.0;
21 |
22 | /**
23 | * Constructor to create a 0/0/undefined level coordinate.
24 | */
25 | public LatLonPos() {
26 | this.lat = 0.0;
27 | this.lon = 0.0;
28 | this.level = Float.MAX_VALUE;
29 | }
30 |
31 | /**
32 | * Constructor to create a coordinate with given parameters.
33 | *
34 | * @param lat the latitude
35 | * @param lon the longitude
36 | * @param level the level
37 | */
38 | public LatLonPos(double lat, double lon, float level) {
39 | super();
40 | this.lat = lat;
41 | this.lon = lon;
42 | this.level = level;
43 | }
44 |
45 | public double getLat() {
46 | return lat;
47 | }
48 |
49 | public double getLon() {
50 | return lon;
51 | }
52 |
53 | public float getLevel() {
54 | return level;
55 | }
56 |
57 | public void setLat(double lat) {
58 | this.lat = lat;
59 | }
60 |
61 | public void setLon(double lon) {
62 | this.lon = lon;
63 | }
64 |
65 | public void setLevel(float level) {
66 | this.level = level;
67 | }
68 |
69 | /**
70 | * Return the x value of this coordinate in meters concerning the mercator
71 | * projection.
72 | *
73 | * @return x value in meters
74 | */
75 | public double getMercatorX(){
76 | // source: http://mathworld.wolfram.com/MercatorProjection.html
77 | double x = lon;
78 | // translate into meters
79 | x*=scale;
80 | return x;
81 | }
82 |
83 | /**
84 | * Return the y value of this coordinate in meters concerning the mercator
85 | * projection.
86 | *
87 | * @return y value in meters
88 | */
89 | public double getMercatorY(){
90 | // source: http://mathworld.wolfram.com/MercatorProjection.html
91 | double y = 0.5*Math.log(
92 | (1+Math.sin(Math.toRadians(lat)))
93 | /(1-Math.sin(Math.toRadians(lat))) );
94 | // rad to degrees
95 | y = Math.toDegrees(y);
96 | // translate into meters
97 | y*=scale;
98 | return y;
99 | }
100 |
101 | public void moveIntoDirection(LatLonPos nextNode, double factor){
102 | // First step: Do Mercator Projection with latitude.
103 | lat = lat + (nextNode.lat - lat)*factor;
104 | lon = lon + (nextNode.lon - lon)*factor;
105 | }
106 |
107 | /**
108 | * Create a clone of this Object.
109 | *
110 | * @return the new clone
111 | */
112 | public LatLonPos clone(){
113 | LatLonPos ret = new LatLonPos();
114 | ret.setLat(lat);
115 | ret.setLon(lon);
116 | ret.setLevel(level);
117 | return ret;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/Calibrator.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import android.app.Activity;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.view.SurfaceView;
7 | import android.view.ViewGroup.LayoutParams;
8 | import android.widget.LinearLayout;
9 | import android.widget.SeekBar;
10 | import android.widget.SeekBar.OnSeekBarChangeListener;
11 | import android.widget.TextView;
12 | import de.uvwxy.footpath.R;
13 | import de.uvwxy.footpath.core.StepDetection;
14 | import de.uvwxy.footpath.core.StepTrigger;
15 |
16 | /**
17 | * This Activity is used to calibrate the parameters concerning step detection
18 | *
19 | * @author Paul Smith
20 | *
21 | */
22 | public class Calibrator extends Activity implements StepTrigger {
23 | public static final String CALIB_DATA = "CALIBDATA";
24 | private StepDetection stepDetection;
25 |
26 | PaintBoxHistory svHistory;
27 |
28 | // GUI
29 | TextView tvPeak = null;
30 | TextView tvFilter = null;
31 | TextView tvTimeout = null;
32 | SeekBar sbPeak = null;
33 | SeekBar sbFilter = null;
34 | SeekBar sbTimeout = null;
35 |
36 | float peak; // threshold for step detection
37 | float a; // value for low pass filter
38 | int step_timeout_ms; // distance in ms between each step
39 |
40 | OnSeekBarChangeListener sbListener = new OnSeekBarChangeListener(){
41 |
42 | @Override
43 | public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
44 | if(arg0.equals(sbPeak)){
45 | peak = sbPeak.getProgress()/10.0f;
46 | stepDetection.setPeak(peak);
47 | tvPeak.setText("Set peak value: (" + peak + ")");
48 | } else if(arg0.equals(sbFilter)){
49 | a = sbFilter.getProgress()/100.0f;
50 | stepDetection.setA(a);
51 | tvFilter.setText("Set filter value: (" + a + ")");
52 | } else if(arg0.equals(sbTimeout)){
53 | step_timeout_ms = sbTimeout.getProgress();
54 | stepDetection.setStep_timeout_ms(step_timeout_ms);
55 | tvTimeout.setText("Set step timeout: (" + step_timeout_ms + ")");
56 | }
57 | }
58 |
59 | @Override
60 | public void onStartTrackingTouch(SeekBar arg0) {}
61 |
62 | @Override
63 | public void onStopTrackingTouch(SeekBar arg0) {}
64 |
65 | };
66 |
67 | private void loadSettings(){
68 | a = getSharedPreferences(CALIB_DATA,0).getFloat("a", 0.5f);
69 | peak = getSharedPreferences(CALIB_DATA,0).getFloat("peak", 0.5f);
70 | step_timeout_ms = getSharedPreferences(CALIB_DATA,0).getInt("timeout", 666);
71 |
72 | // Update GUI elements
73 | sbPeak.setProgress((int)(peak*10));
74 | sbFilter.setProgress((int)(a*100));
75 | sbTimeout.setProgress(step_timeout_ms);
76 |
77 | tvPeak.setText("Set peak value: (" + peak + ")");
78 | tvFilter.setText("Set filter value: (" + a + ")");
79 | tvTimeout.setText("Set step timeout: (" + step_timeout_ms + ")");
80 | }
81 |
82 | private void saveSettings(){
83 | // Save current values to settings
84 | SharedPreferences settings = getSharedPreferences(CALIB_DATA, 0);
85 | SharedPreferences.Editor editor = settings.edit();
86 | editor.putFloat("a", a);
87 | editor.putFloat("peak", peak);
88 | editor.putInt("timeout",step_timeout_ms);
89 | // Apply changes
90 | editor.commit();
91 | }
92 |
93 | @Override
94 | public void dataHookAcc(long nowMs, double x, double y, double z) {}
95 |
96 | @Override
97 | public void dataHookComp(long nowMs, double x, double y, double z) {}
98 |
99 | @Override
100 | public void timedDataHook(long nowMs, double[] acc, double[] comp) {svHistory.addTriple(nowMs, acc);}
101 |
102 | @Override
103 | public void trigger(long nowMs, double compDir) {svHistory.addStepTS(nowMs);}
104 |
105 | /** Called when the activity is first created. */
106 | @Override
107 | public void onCreate(Bundle savedInstanceState) {
108 | super.onCreate(savedInstanceState);
109 | setContentView(R.layout.calibrator);
110 |
111 | tvPeak = (TextView) findViewById(R.id.tvPeak);
112 | tvFilter = (TextView) findViewById(R.id.tvFilter);
113 | tvTimeout = (TextView) findViewById(R.id.tvTimeout);
114 |
115 | sbPeak = (SeekBar) findViewById(R.id.sbPeak);
116 | sbFilter = (SeekBar) findViewById(R.id.sbFilter);
117 | sbTimeout = (SeekBar) findViewById(R.id.sbTimeout);
118 |
119 | // Load settings after creation of GUI-elements, to set their values
120 | loadSettings();
121 | stepDetection = new StepDetection(this, this, a, peak, step_timeout_ms);
122 | // Add OnSeekBarChangeListener after creation of step detection, because object is used
123 | sbPeak.setOnSeekBarChangeListener(sbListener);
124 | sbFilter.setOnSeekBarChangeListener(sbListener);
125 | sbTimeout.setOnSeekBarChangeListener(sbListener);
126 |
127 | LinearLayout linLayout = (LinearLayout) findViewById(R.id.LinearLayout01); // get pointer to layout
128 | SurfaceView svOld = (SurfaceView) findViewById(R.id.svHistory); // get SurfaceView defined in xml
129 | LayoutParams lpHistory = svOld.getLayoutParams(); // get its layout params
130 |
131 | long samples_per_second = 1000/stepDetection.INTERVAL_MS;
132 | int history_in_seconds = 4;
133 | int samples_per_history = (int)(history_in_seconds * samples_per_second);
134 |
135 | // create PaintBox (-24.0 to 24.0, 100 entries)
136 | svHistory = new PaintBoxHistory(this, 48.0, samples_per_history, history_in_seconds);
137 |
138 | linLayout.removeView(svOld); // and remove surface view from layout
139 | linLayout.addView(svHistory, lpHistory); // add surface view clone to layout
140 |
141 | }
142 |
143 | @Override
144 | public void onPause() {
145 | super.onPause();
146 | saveSettings();
147 | stepDetection.unload();
148 | }
149 |
150 | @Override
151 | public void onDestroy() {
152 | super.onDestroy();
153 | }
154 |
155 | @Override
156 | public void onResume() {
157 | super.onResume();
158 | loadSettings();
159 | stepDetection.load();
160 | }
161 |
162 | }
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/Loader.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import java.io.IOException;
4 |
5 | import org.xmlpull.v1.XmlPullParserException;
6 |
7 | import android.app.Activity;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.SharedPreferences;
11 | import android.content.res.Resources.NotFoundException;
12 | import android.location.Location;
13 | import android.location.LocationListener;
14 | import android.location.LocationManager;
15 | import android.os.Bundle;
16 | import android.util.Log;
17 | import android.view.View;
18 | import android.view.View.OnClickListener;
19 | import android.widget.AdapterView;
20 | import android.widget.ArrayAdapter;
21 | import android.widget.Button;
22 | import android.widget.CheckBox;
23 | import android.widget.EditText;
24 | import android.widget.Spinner;
25 | import android.widget.Toast;
26 | import android.widget.AdapterView.OnItemSelectedListener;
27 | import de.uvwxy.footpath.R;
28 | import de.uvwxy.footpath.Rev;
29 | import de.uvwxy.footpath.graph.Graph;
30 | import de.uvwxy.footpath.graph.GraphNode;
31 | import de.uvwxy.footpath.graph.LatLonPos;
32 |
33 | /**
34 | *
35 | * @author Paul Smith
36 | *
37 | */
38 | public class Loader extends Activity {
39 |
40 | public static final String LOADER_SETTINGS = "FootPathSettings";
41 |
42 | // GRAPH
43 | private static Graph g; // Holds the data structure
44 | private String nodeFrom; // Node name to start from, i.e. "5052"
45 | private int closestNodeID; // Node ID if closest node was found via GPS
46 | private String nodeTo; // Node name to navigate to
47 | private int iNodeFrom = 0; // Selected index from spFrom
48 | private int iNodeTo = 0; // Selected index from spTo
49 | private String[] rooms = null; // Array of all room names added to drop down lists
50 | private LocationManager locationManager;
51 |
52 | // GUI
53 | private Spinner spFrom = null; // Drop down lists
54 | private Spinner spTo = null;
55 | private Button bGo = null; // Buttons
56 | private Button bLoad = null;
57 | private Button bSave = null;
58 | private Button bCalibrate = null;
59 | private Button bGPS = null;
60 | private Button bQRCode = null;
61 | private EditText et01 = null; // EditText, body height
62 | private CheckBox cbStairs = null; // Check boxes, for route selection mode
63 | private CheckBox cbElevator = null;
64 | private CheckBox cbOutside = null;
65 | private CheckBox cbLog = null;
66 | private CheckBox cbAudio = null;
67 | private ArrayAdapter adapter1 = null; // Adapter to manage drop down lists
68 | private ArrayAdapter adapter2 = null;
69 |
70 | // LISTENERS
71 | OnItemSelectedListener spinnerListener = new OnItemSelectedListener() {
72 | @Override
73 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
74 | // List from: save selected name and position in list
75 | if (parent.equals(spFrom)) {
76 | nodeFrom = (String) spFrom.getSelectedItem();
77 | iNodeFrom = position;
78 | }
79 | // List from: save selected name and position in list
80 | if (parent.equals(spTo)) {
81 | nodeTo = (String) spTo.getSelectedItem();
82 | iNodeTo = position;
83 | }
84 |
85 | }
86 |
87 | @Override
88 | public void onNothingSelected(AdapterView> arg0) {
89 | shortToast("You have to selected nothing");
90 | }
91 | };
92 |
93 | OnClickListener onListener = new OnClickListener() {
94 |
95 | @Override
96 | public void onClick(View v) {
97 | // Distinguish which button was pressed
98 | if (v.equals(bGo)) {
99 | if(nodeFrom.equals(nodeTo)){
100 | shortToast("Please.... " + nodeFrom + " to " + nodeTo + "?");
101 | return;
102 | }
103 | startNavigation();
104 | } else if(v.equals(bLoad)){
105 | // Load values from settings, second parameter is passed if value is not found
106 | int tNodeFrom = getSharedPreferences(LOADER_SETTINGS,0).getInt("nodeFrom", 0);
107 | int tNodeTo = getSharedPreferences(LOADER_SETTINGS,0).getInt("nodeTo", 0);
108 | float sizeIncm = getSharedPreferences(LOADER_SETTINGS,0).getFloat("sizeIncm", 191.0f);
109 | boolean stairs = getSharedPreferences(LOADER_SETTINGS,0).getBoolean("stairs", true);
110 | boolean elevator = getSharedPreferences(LOADER_SETTINGS,0).getBoolean("elevator", true);
111 | boolean outside = getSharedPreferences(LOADER_SETTINGS,0).getBoolean("outside", true);
112 | // Update GUI elements corresponding to variables
113 | cbStairs.setChecked(stairs);
114 | cbElevator.setChecked(elevator);
115 | cbOutside.setChecked(outside);
116 | spFrom.setSelection(tNodeFrom, true);
117 | spTo.setSelection(tNodeTo, true);
118 | et01.setText("" + sizeIncm);
119 | } else if(v.equals(bSave)){
120 | // Save current values to settings
121 | SharedPreferences settings = getSharedPreferences(LOADER_SETTINGS, 0);
122 | SharedPreferences.Editor editor = settings.edit();
123 | editor.putInt("nodeFrom", iNodeFrom);
124 | editor.putInt("nodeTo", iNodeTo);
125 | editor.putFloat("sizeIncm", Float.parseFloat(et01.getText().toString()));
126 | editor.putBoolean("stairs", cbStairs.isChecked());
127 | editor.putBoolean("elevator", cbElevator.isChecked());
128 | editor.putBoolean("outside", cbOutside.isChecked());
129 | // Apply changes
130 | editor.commit();
131 | } else if(v.equals(bCalibrate)){
132 | Intent intenCalibrator = new Intent(Loader.this, Calibrator.class);
133 | startActivityForResult(intenCalibrator, 2);
134 | } else if(v.equals(bGPS)){
135 | bGPS.setEnabled(false);
136 | bGPS.setText("Wait for fix");
137 | initGPS();
138 | } else if(v.equals(bQRCode)){
139 | // Source: http://code.google.com/p/zxing/wiki/ScanningViaIntent
140 | Intent intent = new Intent("com.google.zxing.client.android.SCAN");
141 | intent.setPackage("com.google.zxing.client.android");
142 | intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
143 | startActivityForResult(intent, 0);
144 | }
145 | }
146 | };
147 |
148 | LocationListener locationListener = new LocationListener() {
149 | // Called when a new location is found by the GPS location provider.
150 | public void onLocationChanged(Location location) {
151 | GraphNode closestNode;
152 | LatLonPos pos;
153 | String nodeName;
154 | longToast("GPS location found, searching for nearest node");
155 |
156 | pos = new LatLonPos(location.getLatitude(),location.getLongitude(),0);
157 | // Third parameter set to false, such that only outdoor nodes are accepted
158 | closestNode = g.getClosestNodeToLatLonPos(pos, 0, false, 17);
159 |
160 | if(closestNode != null){
161 | closestNodeID = closestNode.getId();
162 | nodeName = (closestNode.getName()==null)?"" + closestNodeID : closestNode.getName();
163 | longToast("Closest Node is " + nodeName + "\n\n Please select target\n" +
164 | "The selected room as lcation will be ignored.");
165 | bGPS.setText(nodeName);
166 | locationManager.removeUpdates(this);
167 | bGPS.setEnabled(true);
168 | } else {
169 | // As the level is hard coded to 0, this should never appear,
170 | // as long as all maps have a level beginning at 0.
171 | double dist = g.getClosestDistanceToNode(pos, 0, false);
172 | longToast("No node found on this level (0)!\n\nDistance is " + dist + " meters");
173 | }
174 |
175 | }
176 |
177 | public void onStatusChanged(String provider, int status, Bundle extras) {}
178 |
179 | public void onProviderEnabled(String provider) {}
180 |
181 | public void onProviderDisabled(String provider) {}
182 | };
183 |
184 |
185 | // Navigator needs static access to graph
186 | public static Graph getGraph(){
187 | return g;
188 | }
189 |
190 | /** Called when the activity is first created. */
191 | @Override
192 | public void onCreate(Bundle savedInstanceState) {
193 | super.onCreate(savedInstanceState);
194 | setContentView(R.layout.selectroom);
195 |
196 | // Create new graph
197 | g = new Graph();
198 | // And add layer(s) of ways
199 | try {
200 | g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.map1ug));
201 | g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.mapeg));
202 | g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.map1og));
203 | g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.map2og));
204 | g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.map3og));
205 | // g.addToGraphFromXMLResourceParser(this.getResources().getXml(R.xml.parkinglot));
206 | g.mergeNodes();
207 | rooms = g.getRoomList();
208 | } catch (NotFoundException e) {
209 | longToast("Error: resource not found:\n\n" + e);
210 | } catch (XmlPullParserException e) {
211 | longToast("Error: xml error:\n\n" + e);
212 | } catch (IOException e) {
213 | longToast("Error: io error:\n\n" + e);
214 | }
215 |
216 | // GUI - Create references to elements on the screen
217 | spFrom = (Spinner) findViewById(R.id.Spinner01);
218 | spTo = (Spinner) findViewById(R.id.Spinner02);
219 | bGo = (Button) findViewById(R.id.btnGo);
220 | bLoad = (Button) findViewById(R.id.btnLoad);
221 | bSave = (Button) findViewById(R.id.btnSave);
222 | bCalibrate = (Button) findViewById(R.id.btnCalibrate);
223 | bGPS = (Button) findViewById(R.id.btnGPS);
224 | bQRCode = (Button) findViewById(R.id.btnQR);
225 | et01 = (EditText) findViewById(R.id.EditText01);
226 | cbStairs = (CheckBox) findViewById(R.id.cbStairs);
227 | cbElevator = (CheckBox) findViewById(R.id.cbElevators);
228 | cbOutside = (CheckBox) findViewById(R.id.cbOutside);
229 | cbLog = (CheckBox) findViewById(R.id.cbLog);
230 | cbAudio = (CheckBox) findViewById(R.id.cbAudio);
231 | this.setTitle("Footpath r(" + Rev.rev.substring(0,8) + ")");
232 |
233 | // Drop down lists: create entries of room names from rooms
234 | adapter1 = new ArrayAdapter(this, android.R.layout.simple_spinner_item, rooms);
235 | adapter1.setDropDownViewResource(android.R.layout.simple_spinner_item);
236 | adapter2 = new ArrayAdapter(this, android.R.layout.simple_spinner_item, rooms);
237 | adapter2.setDropDownViewResource(android.R.layout.simple_spinner_item);
238 |
239 | spFrom.setAdapter(adapter1);
240 | spTo.setAdapter(adapter2);
241 |
242 | // Set select/click listeners
243 | spFrom.setOnItemSelectedListener(spinnerListener);
244 | spTo.setOnItemSelectedListener(spinnerListener);
245 | bGo.setOnClickListener(onListener);
246 | bLoad.setOnClickListener(onListener);
247 | bSave.setOnClickListener(onListener);
248 | bCalibrate.setOnClickListener(onListener);
249 | bGPS.setOnClickListener(onListener);
250 | bQRCode.setOnClickListener(onListener);
251 |
252 | locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
253 | }
254 |
255 | @Override
256 | public void onResume() {
257 | super.onResume();
258 | }
259 |
260 | public void onActivityResult(int requestCode, int resultCode, Intent intent) {
261 | // Source: http://code.google.com/p/zxing/wiki/ScanningViaIntent
262 |
263 | Log.i("FOOTPATH", "requestCode = " + requestCode);
264 | Log.i("FOOTPATH", "resultCode = " + resultCode);
265 |
266 | if (requestCode == 0) {
267 | if (resultCode == RESULT_OK) {
268 | String contents = intent.getStringExtra("SCAN_RESULT");
269 | String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
270 |
271 | // Handle successful scan
272 | // FORMAT: "http://......?....&fN=&tN=&.....
273 |
274 | if(!format.equals("QR_CODE")){
275 | this.longToast("Scan result was not a QR Code");
276 | return;
277 | }
278 |
279 | String[] split = contents.split("\\?");
280 |
281 | if(split.length!=2){
282 | this.longToast("URL: " + contents + "\n\n is in the wrong format!");
283 | return;
284 | }
285 |
286 | int progress = 0;
287 | String sVarString = split[1];
288 | String[] sVars = sVarString.split("\\&");
289 |
290 | for(String s : sVars){
291 | if(s.split("=")[0].equals("fN")){
292 | this.nodeFrom = s.split("=")[1];
293 | progress++;
294 | } else if(s.split("=")[0].equals("tN")){
295 | this.nodeTo = s.split("=")[1];
296 | progress++;
297 | }
298 | }
299 |
300 | for(int i = 0; i < rooms.length; i++){
301 | if(rooms[i].equals(nodeFrom)){
302 | this.spFrom.setSelection(i);
303 | }
304 | if(rooms[i].equals(nodeTo)){
305 | this.spTo.setSelection(i);
306 | }
307 | }
308 |
309 | if(progress==2){
310 | if(nodeFrom.equals(nodeTo)){
311 | longToast("Even if the URL was correct, you will not be going far from " + nodeFrom + " to " + nodeTo);
312 | }
313 | longToast("Starting navigation from " + nodeFrom + " to " + nodeTo);
314 | startNavigation();
315 | }
316 | } else if (resultCode == RESULT_CANCELED) {
317 | // Handle cancel
318 | }
319 | } else if (requestCode == 1){
320 | if(resultCode == RESULT_CANCELED){
321 | longToast("No Route found!");
322 | }
323 | }
324 | }
325 |
326 | @Override
327 | public void onPause() {
328 | super.onPause();
329 | bGPS.setEnabled(true);
330 | locationManager.removeUpdates(locationListener);
331 | }
332 |
333 | private void shortToast(String s) {
334 | Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
335 | }
336 |
337 | private void longToast(String s) {
338 | Toast.makeText(this, s, Toast.LENGTH_LONG).show();
339 | }
340 |
341 | private void initGPS(){
342 | // Register the listener with the Location Manager to receive location updates
343 | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
344 | longToast("Waiting for GPS fix\n\nPlease wait for a notification to continue.\n\nYou can select a destination.");
345 | }
346 | private void startNavigation(){
347 | Log.i("FOOTPATH", "Starting navigation intent");
348 | // Create intent for navigation
349 | Intent intentNavigator = new Intent(Loader.this, Navigator.class);
350 | // Add values to be passed to navigator
351 | intentNavigator.putExtra("from", nodeFrom);
352 | intentNavigator.putExtra("fromId", closestNodeID);
353 | intentNavigator.putExtra("to", nodeTo);
354 | intentNavigator.putExtra("stairs", cbStairs.isChecked());
355 | intentNavigator.putExtra("elevator", cbElevator.isChecked());
356 | intentNavigator.putExtra("outside", cbOutside.isChecked());
357 | intentNavigator.putExtra("log", cbLog.isChecked());
358 | intentNavigator.putExtra("audio", cbAudio.isChecked());
359 | // Source: http://www.pedometersaustralia.com/g/13868/measure-step-length-.html
360 | intentNavigator.putExtra("stepLength", Float.parseFloat(et01.getText().toString()) * 0.415f);
361 | // Start intent for navigation
362 | startActivityForResult(intentNavigator, 1);
363 | }
364 | }
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/Navigator.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import java.io.IOException;
4 | import java.util.LinkedList;
5 | import java.util.Stack;
6 |
7 | import android.app.Activity;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.view.SurfaceView;
11 | import android.view.View;
12 | import android.view.View.OnClickListener;
13 | import android.view.ViewGroup.LayoutParams;
14 | import android.view.Window;
15 | import android.view.WindowManager;
16 | import android.widget.Button;
17 | import android.widget.LinearLayout;
18 | import android.widget.ZoomControls;
19 | import de.uvwxy.footpath.R;
20 | import de.uvwxy.footpath.ToolBox;
21 | import de.uvwxy.footpath.core.NPConfig;
22 | import de.uvwxy.footpath.core.Positioner;
23 | import de.uvwxy.footpath.core.Positioner_OnlineBestFit;
24 | import de.uvwxy.footpath.core.Positioner_OnlineFirstFit;
25 | import de.uvwxy.footpath.core.StepDetection;
26 | import de.uvwxy.footpath.core.StepTrigger;
27 | import de.uvwxy.footpath.graph.Graph;
28 | import de.uvwxy.footpath.graph.GraphEdge;
29 | import de.uvwxy.footpath.graph.GraphNode;
30 | import de.uvwxy.footpath.graph.LatLonPos;
31 | import de.uvwxy.footpath.log.AudioWriter;
32 | import de.uvwxy.footpath.log.DataLogger;
33 | /**
34 | *
35 | * @author Paul Smith
36 | *
37 | */
38 | public class Navigator extends Activity implements StepTrigger {
39 | public static final String LOG_DIR = "routelog/";
40 |
41 | // #########################################################################
42 | // ######################### Fields 'n Listener ############################
43 | // #########################################################################
44 |
45 | // GUI Elements
46 | private PaintBoxMap pbMap; // Objects to handle the graphics
47 | private Button btnRecalc;
48 | private Button btnSwitchFit;
49 | private Graph g; // Reference to graph
50 |
51 |
52 | // Route information
53 | private String nodeFrom; // Node we will start from, i.e. "5052"
54 | private int nodeFromId = 0; // This is used if we choose nearest location from GPS fix
55 | private String nodeTo; // Node we plan to end up with
56 | private boolean staircase;
57 | private boolean elevator;
58 | private boolean outside;
59 | private LinkedList navPathEdges; // Contains path with corrected compass bearings
60 | // used by PaintBoxMap to paint the path
61 | private LinkedList simplifiedEdges;
62 | private LinkedList tempEdges; // Stores the original edges on path
63 | // Needs to be global: is used for logging in onResume()
64 | private double navPathLen = 0.0; // Total length of path
65 | private double naiveStairsWidth = 0.25; // Naive amount in meters to use as stairs step length
66 |
67 |
68 | // Navigation
69 | private double acceptanceWidth = 42.0; // Amount of derivation allowed for compassValue to path
70 | private StepDetection stepDetection;
71 | private Positioner posBestFit = null; // Object to do another progress estimation
72 | private Positioner posFirstFit = null;
73 | private NPConfig confBestFit = null;
74 | private NPConfig confFirstFit = null;
75 | private NPConfig conf = null;
76 |
77 | // Progress information
78 | private boolean isNavigating = false; // Set to false when nodeTo is reached
79 | private int totalStepsWalked = 0; // Total number of detected steps
80 |
81 |
82 | // Runtime information
83 | private double compassValue = -1.0;
84 |
85 | private LinkedList zVarHistory = new LinkedList(); // store the variance of each step
86 | private int historySize = 64; // Back log of last 64 values to calculate variance
87 | private double[] x_History = new double[historySize];
88 | private double[] y_History = new double[historySize];
89 | private double[] z_History = new double[historySize];
90 | private int historyPtr = 0;
91 |
92 |
93 | // Logging
94 | private DataLogger logger;
95 | private boolean log = false;
96 | private boolean logAudio = false;
97 | private AudioWriter avwCapture;
98 |
99 |
100 | // Listeners
101 | OnClickListener onClick = new OnClickListener(){
102 |
103 | @Override
104 | public void onClick(View arg0) {
105 | if(arg0.equals(btnRecalc)){
106 | if(true){
107 | ((Positioner_OnlineFirstFit) posFirstFit).recalcPos();
108 | }
109 |
110 | } else if (arg0.equals(btnSwitchFit)){
111 | if ( btnSwitchFit.getText().equals("Switch to First Fit Algorithm")){
112 | btnSwitchFit.setText("Switch to Best Fit Algorithm");
113 | conf = confFirstFit;
114 | } else {
115 | btnSwitchFit.setText("Switch to First Fit Algorithm");
116 | conf = confBestFit;
117 | }
118 | }
119 |
120 | }
121 |
122 | };
123 |
124 | // #########################################################################
125 | // ######################### Getters 'n Setters ############################
126 | // #########################################################################
127 |
128 | public LinkedList getNavPathEdges(){
129 | return navPathEdges;
130 | }
131 | public double getAcceptanceWidth() {
132 | return this.acceptanceWidth;
133 | }
134 |
135 | // last compass value
136 | public double getCompassValue() {
137 | return compassValue;
138 | }
139 |
140 | public GraphEdge getCurrentEdge(NPConfig conf){
141 | if(conf.npPointer >= navPathEdges.size()){
142 | return navPathEdges.get(navPathEdges.size()-1);
143 | }
144 | GraphEdge ret = navPathEdges.get(conf.npPointer);
145 | return ret;
146 | }
147 |
148 | public float getCurrentFloorLevel() {
149 | return getLastSeenNode(conf).getLevel();
150 | }
151 |
152 | public double getEstimatedStepLength(){
153 | double pathLength = getNavPathWalked();
154 | double totalSteps = getTotalStepsWalked();
155 | for(int i = 0; i < conf.npPointer; i++){
156 | GraphEdge edge = navPathEdges.get(i);
157 | if(edge.isStairs()){
158 | if(edge.getSteps()==-1){
159 | pathLength -= edge.getLen();
160 | totalSteps -= edge.getLen()/naiveStairsWidth;
161 | } else if(edge.getSteps() > 0){
162 | pathLength -= edge.getLen();
163 | totalSteps -= edge.getSteps();
164 | }
165 | }
166 | }
167 |
168 | return pathLength/totalSteps;
169 |
170 | }
171 |
172 | public double getNaiveStairsWidth(){
173 | return naiveStairsWidth;
174 | }
175 |
176 | public GraphNode getLastSeenNode(NPConfig conf){
177 | GraphEdge currentEdge = getCurrentEdge(conf);
178 | GraphEdge previousEdge = getPreviousEdge(conf);
179 | if(previousEdge == null){ // no previous edge, thus this is the first edge
180 | return getRouteBegin();
181 | }
182 | // last seen node is node which is in current and previous edge
183 | GraphNode ret = currentEdge.getNode0();
184 | if(!previousEdge.contains(ret)){
185 | ret = currentEdge.getNode1();
186 | }
187 | return ret;
188 | }
189 |
190 | public double getNavPathLen() {
191 | return navPathLen;
192 | }
193 |
194 | public double getNavPathLenLeft(){
195 | return navPathLen - getNavPathWalked();
196 | }
197 |
198 | public double getNavPathDir() {
199 | if(conf.npPointer >= navPathEdges.size()){
200 | return navPathEdges.get(navPathEdges.size()-1).getCompDir();
201 | }
202 | return navPathEdges.get(conf.npPointer).getCompDir();
203 | }
204 |
205 | // returns remaining meters on edge
206 | public double getNavPathEdgeLenLeft() {
207 | // catch route end, return -1.0
208 | if (conf.npPointer >= navPathEdges.size())
209 | return -1.0;
210 | return navPathEdges.get(conf.npPointer).getLen() - conf.npCurLen;
211 | }
212 |
213 |
214 | // return show far we have walked on the path
215 | public double getNavPathWalked(){
216 | double len = 0.0;
217 | // sum all traversed edges
218 | for(int i = 0; i < conf.npPointer; i++){
219 | len += navPathEdges.get(i).getLen();
220 | }
221 | // and how far we have walked on current edge
222 | len += conf.npCurLen;
223 | return len;
224 | }
225 |
226 | public LatLonPos getPosition() {
227 | return getPosition(conf);
228 | }
229 |
230 | // estimated(?) position of user
231 | public LatLonPos getPosition(NPConfig conf) {
232 | LatLonPos ret = new LatLonPos();
233 | GraphNode lastSeenNode = getLastSeenNode(conf);
234 | GraphEdge currentEdge = getCurrentEdge(conf);
235 | GraphNode nextNode = currentEdge.getNode0().equals(lastSeenNode)? currentEdge.getNode1() : currentEdge.getNode0();
236 |
237 | // catch route end, return destination
238 | if (conf.npPointer >= navPathEdges.size()) {
239 | GraphNode lastNode = this.getRouteEnd();
240 | ret.setLat(lastNode.getLat());
241 | ret.setLon(lastNode.getLon());
242 | ret.setLevel(lastNode.getLevel());
243 | return ret;
244 | }
245 |
246 | ret.setLevel(lastSeenNode.getLevel());
247 | ret.setLat(lastSeenNode.getLat());
248 | ret.setLon(lastSeenNode.getLon());
249 |
250 | // move pos into direction; amount of traveled m on edge
251 | ret.moveIntoDirection(nextNode.getPos(), conf.npCurLen/navPathEdges.get(conf.npPointer).getLen());
252 | return ret;
253 | }
254 |
255 | public GraphEdge getPreviousEdge(NPConfig conf){
256 | if(conf.npPointer == 0){
257 | // no previous edge
258 | return null;
259 | }
260 | return navPathEdges.get(conf.npPointer - 1);
261 | }
262 |
263 | public GraphNode getRouteBegin() {
264 | if(nodeFromId == 0){
265 | return g.getNodeFromName(nodeFrom);
266 | } else {
267 | return g.getNode(nodeFromId);
268 | }
269 | }
270 |
271 | public GraphNode getRouteEnd() {
272 | return g.getNodeFromName(nodeTo);
273 | }
274 |
275 | // length of each step
276 | public double getStepLengthInMeters() {
277 | return conf.npStepSize;
278 | }
279 |
280 | // total steps walked
281 | public int getTotalStepsWalked() {
282 | return totalStepsWalked;
283 | }
284 |
285 | // total steps not roughly in correct direction
286 | public int getUnmatchedSteps() {
287 | return conf.npUnmatchedSteps;
288 | }
289 |
290 | // are we navigating?
291 | public boolean isNavigating() {
292 | return isNavigating;
293 | }
294 |
295 | /**
296 | * Returns the variance from the back log for x values
297 | * @return the variance for x
298 | */
299 | public double getVarianceOfX(){
300 | return varianceOfSet(x_History);
301 | }
302 | /**
303 | * Returns the variance from the back log for y values
304 | * @return the variance for y
305 | */
306 | public double getVarianceOfY(){
307 | return varianceOfSet(y_History);
308 | }
309 | /**
310 | * Returns the variance from the back log for z values
311 | * @return the variance for z
312 | */
313 | public double getVarianceOfZ(){
314 | return varianceOfSet(z_History);
315 | }
316 |
317 | public void setNavigating(boolean b){
318 | this.isNavigating = b;
319 | }
320 |
321 | // #########################################################################
322 | // ########################## Step/Data Callbacks ##########################
323 | // #########################################################################
324 |
325 | @Override
326 | public void trigger(long now_ms, double compDir) {
327 | this.totalStepsWalked++;
328 | if (!isNavigating) {
329 | // Destination was reached
330 | return;
331 | }
332 |
333 | if(log){
334 | logger.logStep(now_ms, compDir);
335 | }
336 |
337 | posBestFit.addStep(compDir);
338 | posFirstFit.addStep(compDir);
339 |
340 | Log.i("FOOTPATH", "posBestFit: " + posBestFit.getProgress());
341 | Log.i("FOOTPATH", "posFirstFit: " + posFirstFit.getProgress());
342 | if(log){
343 | // Write location to file after detected step
344 | LatLonPos bestPos = getPosition(confBestFit);
345 | LatLonPos firstPos = getPosition(confFirstFit);
346 | logger.logPosition(now_ms, bestPos.getLat(), bestPos.getLon(), posBestFit.getProgress()/this.navPathLen
347 | , firstPos.getLat(), firstPos.getLon(), posFirstFit.getProgress()/this.navPathLen);
348 | }
349 | }
350 |
351 | @Override
352 | public void dataHookAcc(long now_ms, double x, double y, double z){
353 | // add values to history (for variance)
354 | addTriple(x, y, z);
355 | if(log){
356 | logger.logRawAcc(now_ms, x, y, z);
357 | }
358 | }
359 |
360 | @Override
361 | public void dataHookComp(long now_ms, double x, double y, double z){
362 | if(log){
363 | logger.logRawCompass(now_ms, x, y, z);
364 | }
365 | compassValue = ToolBox.lowpassFilter(compassValue, x, 0.5);
366 | }
367 |
368 | @Override
369 | public void timedDataHook(long now_ms, double[] acc, double[] comp){
370 | double varZ = getVarianceOfZ();
371 | zVarHistory.add(new Double(acc[2]));
372 |
373 | if(log){
374 | logger.logTimedVariance(now_ms, varZ);
375 | }
376 | if(log){
377 | // Write Compass and Accelerometer data
378 | logger.logTimedAcc(now_ms, acc[2]);
379 | logger.logTimedCompass(now_ms, comp[0]);
380 | }
381 | }
382 |
383 |
384 | // #########################################################################
385 | // ######################## Activity Life Cycle ############################
386 | // #########################################################################
387 |
388 |
389 | /** Called when the activity is first created. */
390 | @Override
391 | public void onCreate(Bundle savedInstanceState) {
392 | super.onCreate(savedInstanceState);
393 | requestWindowFeature(Window.FEATURE_NO_TITLE);
394 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
395 | WindowManager.LayoutParams.FLAG_FULLSCREEN);
396 | setContentView(R.layout.displayroute);
397 |
398 | Stack navPathStack;
399 |
400 | // Get location and destination
401 |
402 | nodeFrom = this.getIntent().getStringExtra("from");
403 | nodeFromId = this.getIntent().getIntExtra("fromId",0);
404 | nodeTo = this.getIntent().getStringExtra("to");
405 |
406 | // Get reference to graph object
407 | this.g = Loader.getGraph();
408 | staircase = this.getIntent().getBooleanExtra("stairs", true);
409 | elevator = this.getIntent().getBooleanExtra("elevator", false);
410 | outside = this.getIntent().getBooleanExtra("outside", true);
411 | log = this.getIntent().getBooleanExtra("log", false);
412 | logAudio = this.getIntent().getBooleanExtra("audio", false);
413 |
414 | // calculate route
415 | if(nodeFromId==0){
416 | navPathStack = this.g.getShortestPath(nodeFrom, nodeTo, staircase, elevator, outside);
417 | logger = new DataLogger(this, System.currentTimeMillis(), nodeFrom, nodeTo);
418 | } else {
419 | navPathStack = this.g.getShortestPath(nodeFromId, nodeTo, staircase, elevator, outside);
420 | logger = new DataLogger(this, System.currentTimeMillis(), "" + nodeFromId, nodeTo);
421 | }
422 |
423 | if(navPathStack != null){ // no route found!
424 | // The navPathStack consists of the correct order of nodes on the path
425 | // From these nodes the corresponding edge is used to get the original
426 | // data connected to it. What has to be recalculated is the initial bearing
427 | // because this is depending on the order of nodes being passed.
428 |
429 | // List to store the new edges in
430 | tempEdges = new LinkedList();
431 | // Get first node. This is always the 'left' node, when considering
432 | // a path going from left to right.
433 | GraphNode node0 = navPathStack.pop();
434 |
435 | while(!navPathStack.isEmpty()){
436 | // Get 'right' node
437 | GraphNode node1 = navPathStack.pop();
438 | // Get Edge connecting 'left' and 'right' nodes
439 | GraphEdge origEdge = g.getEdge(node0,node1);
440 |
441 | // Get data which remains unchanged
442 | double len = origEdge.getLen();
443 | short wheelchair = origEdge.getWheelchair();
444 | float level = origEdge.getLevel();
445 | boolean indoor = origEdge.isIndoor();
446 |
447 | // Direction has to be recalculated
448 | double dir = g.getInitialBearing(node0.getLat(), node0.getLon(), node1.getLat(), node1.getLon());
449 |
450 | // Create new edge
451 | GraphEdge tempEdge = new GraphEdge(node0, node1,len,dir,wheelchair,level,indoor);
452 | // Update additional values
453 | tempEdge.setElevator(origEdge.isElevator());
454 | tempEdge.setStairs(origEdge.isStairs());
455 | tempEdge.setSteps(origEdge.getSteps());
456 | tempEdges.add(tempEdge);
457 |
458 | // Update path length
459 | navPathLen += origEdge.getLen();
460 |
461 | // 'right' node is new 'left' node
462 | node0 = node1;
463 | }
464 |
465 | Log.i("FOOTPATH", "Number of edges before merge: " + tempEdges.size());
466 |
467 | // Now that we have the correct order of nodes, and initial bearings of edges
468 | // we look for successive edges with little difference in their bearing
469 | // to simplify the path, having less but longer edges
470 |
471 | // Allow a difference of 5 degrees to both sides
472 | double diff = 8.0;
473 | simplifiedEdges = new LinkedList();
474 | // The current edge to find equaling edges to
475 | GraphEdge edge_i = null;
476 | // The first node of current edge
477 | GraphNode node_i_0 = null;
478 | // This will be the last node of the last edge equaling edge_i
479 | GraphNode node_x_1 = null;
480 |
481 | // Data to sum up for needed merge;
482 | short wheelchair;
483 | float level;
484 | boolean indoor;
485 | boolean stairs;
486 | boolean elevator;
487 | int steps;
488 | int last_i = -1;
489 | // Iterate over all edges
490 | for (int i = 0; i < tempEdges.size(); i++){
491 | edge_i = tempEdges.get(i);
492 | node_i_0 = tempEdges.get(i).getNode0();
493 | level = edge_i.getLevel();
494 | indoor = edge_i.isIndoor();
495 | stairs = edge_i.isStairs();
496 | elevator = edge_i.isElevator();
497 | steps = edge_i.getSteps();
498 | wheelchair = edge_i.getWheelchair();
499 | Log.i("FOOTPATH", "Edge (" + (i+1) + "/" + tempEdges.size() + ") dir: " + edge_i.getCompDir());
500 | last_i = i;
501 | for (int j = i + 1; j < tempEdges.size(); j++){
502 | GraphEdge edge_j = tempEdges.get(j);
503 | // Only merge edges if they are identical in their characteristics
504 | if(edge_i.getLevel() == edge_j.getLevel()
505 | && edge_i.isElevator() == edge_j.isElevator()
506 | && edge_i.isIndoor() == edge_j.isIndoor()
507 | && edge_i.isStairs() == edge_j.isStairs()
508 | && Positioner.isInRange(edge_i.getCompDir(), tempEdges.get(j).getCompDir(), diff)){
509 | Log.i("FOOTPATH", "Adding " + edge_j.getCompDir());
510 | // Edge_i and edge_j can be merged
511 | // Save last node1 of last edge_j equaling edge_i
512 | node_x_1 = edge_j.getNode1();
513 |
514 | // Set number of steps only if defined (-1 := undefined, but steps)
515 | if(steps != -1){
516 | // only change 0 or defined steps
517 | if(edge_j.getSteps() != -1){
518 | steps += edge_j.getSteps();
519 | } else {
520 | // edge_j has no defined step count, thus set to undefined
521 | steps = -1;
522 | }
523 | }
524 | } else {
525 | Log.i("FOOTPATH", "Not Merging " + edge_j.getCompDir());
526 | // Edge_i and edge_j can not be merged
527 | // Merge possible previously found edges and add them
528 |
529 | // Point to latest edge to try matching from
530 | i = j-1;
531 |
532 | // Nothing can be merged, leave edge_i as is
533 | if(node_x_1 == null){
534 | Log.i("FOOTPATH", "Created same edge i " + edge_i.getLen() + " and direction " + edge_i.getCompDir());
535 | // Add same edge_i
536 | simplifiedEdges.add(edge_i);
537 | break;
538 | } else {
539 | // Add modified new edge
540 | double bearing = g.getInitialBearing(node_i_0.getLat(), node_i_0.getLon(), node_x_1.getLat(), node_x_1.getLon());
541 | double len = g.getDistance(node_i_0.getLat(), node_i_0.getLon(), node_x_1.getLat(), node_x_1.getLon());
542 | GraphEdge tempEdge = new GraphEdge(node_i_0, node_x_1, len, bearing, wheelchair, level, indoor);
543 | tempEdge.setElevator(elevator);
544 | tempEdge.setStairs(stairs);
545 | tempEdge.setSteps(steps);
546 | simplifiedEdges.add(tempEdge);
547 | Log.i("FOOTPATH", "Created edge with length of " + tempEdge.getLen() + " and direction " + tempEdge.getCompDir());
548 | // Reset last node to null to distinguish if something has to be merged
549 | node_x_1 = null;
550 | break;
551 | }
552 | }
553 | }
554 | }
555 |
556 | if(last_i != -1){
557 | for(int i = last_i; i < tempEdges.size(); i++){
558 | Log.i("FOOTPATH", "Adding missing edges");
559 | simplifiedEdges.add(tempEdges.get(i));
560 | }
561 | }
562 |
563 | // Set current path
564 | navPathEdges = simplifiedEdges;
565 | Log.i("FOOTPATH", "EDGES: " + navPathEdges);
566 | Log.i("FOOTPATH", "Number of edges after merge: " + navPathEdges.size());
567 |
568 | // Get handles to button and zoom controls and save their configuration
569 | ZoomControls zoomControls = (ZoomControls) findViewById(R.id.zoomCtrl);
570 | btnRecalc = (Button) findViewById(R.id.btnRecalc);
571 | btnSwitchFit = (Button) findViewById(R.id.btnSwitchFit);
572 |
573 | // Load fancy graphics
574 | pbMap = new PaintBoxMap(this, this);
575 | // REPLACING :: has to be done in order of appearance on display (top to bottom)
576 | replaceSurfaceView(pbMap, (SurfaceView) findViewById(R.id.svPath)); // svPath with pbNavigator
577 |
578 | btnRecalc.setOnClickListener(onClick);
579 | btnSwitchFit.setOnClickListener(onClick);
580 | zoomControls.setOnZoomInClickListener(new OnClickListener() {
581 | public void onClick(View v) {
582 | pbMap.zoomIn();
583 | }
584 | });
585 |
586 | zoomControls.setOnZoomOutClickListener(new OnClickListener() {
587 | public void onClick(View v) {
588 | pbMap.zoomOut();
589 | }
590 | });
591 |
592 | confBestFit = new NPConfig();
593 | confBestFit.npCurLen = 0.0;
594 | confBestFit.npLastMatchedStep = -1;
595 | confBestFit.npMatchedSteps = 0;
596 | confBestFit.npPointer = 0;
597 | // /100.0f -> cm to m
598 | confBestFit.npStepSize = this.getIntent().getFloatExtra("stepLength", 191.0f/0.415f/100.0f)/100.0f;
599 | confBestFit.npUnmatchedSteps = 0;
600 |
601 | confFirstFit = new NPConfig(confBestFit);
602 |
603 | // Create correct pointer to chosen positioner
604 | conf = confBestFit;
605 |
606 | double a = getSharedPreferences(Calibrator.CALIB_DATA,0).getFloat("a", 0.5f);
607 | double peak = getSharedPreferences(Calibrator.CALIB_DATA,0).getFloat("peak", 0.5f);
608 | int step_timeout_ms = getSharedPreferences(Calibrator.CALIB_DATA,0).getInt("timeout", 666);
609 |
610 | stepDetection = new StepDetection(this, this, a, peak, step_timeout_ms);
611 |
612 | posBestFit = new Positioner_OnlineBestFit(this, this.navPathEdges, confBestFit);
613 | posFirstFit = new Positioner_OnlineFirstFit(this, this.navPathEdges, confFirstFit);
614 |
615 |
616 |
617 | setNavigating( true );
618 | } else { // navPathStack was null
619 | this.setResult(RESULT_CANCELED);
620 | this.finish();
621 | }
622 | }
623 |
624 | @Override
625 | public void onPause() {
626 | super.onPause();
627 | stepDetection.unload();
628 | if(log){
629 | // Log to info file
630 | logger.logInfo("a: " + stepDetection.getA());
631 | logger.logInfo("peak: " + stepDetection.getPeak());
632 | logger.logInfo("step timeout (ms): " + stepDetection.getStep_timeout_ms());
633 | logger.logInfo("Recognised steps: " + this.totalStepsWalked);
634 | logger.logInfo("Estimated stepsize: " + this.getEstimatedStepLength());
635 | logger.logInfo("Output of columns:");
636 | logger.stopLogging();
637 | if(logAudio){
638 | avwCapture.stopCapture();
639 | avwCapture.unregisterCapture();
640 | }
641 | }
642 |
643 | }
644 |
645 | @Override
646 | public void onDestroy() {
647 | super.onDestroy();
648 | }
649 |
650 | @Override
651 | public void onResume() {
652 | super.onResume();
653 | if(log){
654 | logger.startLogging();
655 | // Only log route if files opened correctly
656 | if(logger.started()){
657 |
658 | if(log){
659 | for(GraphEdge e: navPathEdges){
660 | logger.logSimpleRoute(e.getNode0().getLat(), e.getNode0().getLon());
661 | }
662 | GraphEdge e = navPathEdges.get(navPathEdges.size()-1);
663 | logger.logSimpleRoute(e.getNode1().getLat(), e.getNode1().getLon());
664 | }
665 | if(log){
666 | for(GraphEdge e: tempEdges){
667 | logger.logRoute(e.getNode0().getLat(), e.getNode0().getLon());
668 | }
669 | GraphEdge e = tempEdges.get(tempEdges.size()-1);
670 | logger.logRoute(e.getNode1().getLat(), e.getNode1().getLon());
671 | }
672 |
673 |
674 |
675 |
676 | // Create files for AudioWrite here, with correct file name as other log files
677 | if(nodeFromId==0){
678 | if(logAudio){
679 | avwCapture = new AudioWriter("" + logger.getRouteId() + "_" + nodeFrom + "_" + nodeTo +"/", "video.3gp");
680 | }
681 | } else {
682 | if(logAudio){
683 | avwCapture = new AudioWriter("" + logger.getRouteId() + "_" + nodeFromId + "_" + nodeTo +"/", "video.3gp");
684 | }
685 | }
686 |
687 | if(logAudio){
688 | try {
689 | avwCapture.registerCapture();
690 | avwCapture.startCapture();
691 | } catch (IllegalStateException e) {
692 | e.printStackTrace();
693 | } catch (IOException e) {
694 | e.printStackTrace();
695 | }
696 | }
697 | }
698 |
699 | }
700 |
701 | stepDetection.load();
702 |
703 | }
704 |
705 |
706 |
707 | // #########################################################################
708 | // ############################## Functions ################################
709 | // #########################################################################
710 |
711 | private void replaceSurfaceView(SurfaceView svNew, SurfaceView svOld) {
712 | LayoutParams layParam = svOld.getLayoutParams();
713 | LinearLayout ll = (LinearLayout) findViewById(R.id.ll01);
714 | ll.removeView(svOld);
715 | ll.addView(svNew, layParam);
716 | }
717 |
718 |
719 | /**
720 | * Add values to backlog (for variance)
721 | * @param x Sensor x value
722 | * @param y Sensor y value
723 | * @param z Sensor z value
724 | */
725 | private void addTriple(double x, double y, double z) {
726 | x_History[(historyPtr + 1) % historySize] = x;
727 | y_History[(historyPtr + 1) % historySize] = y;
728 | z_History[(historyPtr + 1) % historySize] = z;
729 | historyPtr++;
730 | }
731 | /**
732 | * Calculates the mean of a given set
733 | * @param set the set
734 | * @return the mean value
735 | */
736 | private double meanOfSet(double[] set) {
737 | double res = 0.0;
738 | for (double i : set) {
739 | res += i;
740 | }
741 | return res / set.length;
742 |
743 | }
744 | /**
745 | * Calculates the variance of a given set
746 | * @param set the set
747 | * @return the variance value
748 | */
749 | private double varianceOfSet(double[] set) {
750 | double res = 0.0;
751 | double mean = meanOfSet(set);
752 | for (double i : set) {
753 | res += (i - mean) * (i - mean);
754 | }
755 | return res / set.length;
756 | }
757 |
758 | // -1 := left
759 | // 0 := straight on
760 | // 1 := right
761 | public int getNextTurn(){
762 | if(conf.npPointer == navPathEdges.size()-1){
763 | // Walking on the last edge, go straight on
764 | return 0;
765 | }
766 |
767 | if(Positioner.isInRange(navPathEdges.get(conf.npPointer).getCompDir(),navPathEdges.get(conf.npPointer+1).getCompDir(),10)){
768 | // +- 10 degrees is straight on
769 | return 0;
770 | }
771 | if(Positioner.isInRange(navPathEdges.get(conf.npPointer).getCompDir()-90,navPathEdges.get(conf.npPointer+1).getCompDir(),90)){
772 | // This edge -90 degrees is in range of next edge
773 | // -> next turn is left turn
774 | return -1;
775 | }
776 | // Else its a right turn
777 | return 1;
778 | }
779 |
780 | }
781 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/PaintBoxHistory.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import de.uvwxy.footpath.ToolBox;
8 | import de.uvwxy.paintbox.PaintBox;
9 |
10 | /**
11 | *
12 | * @author Paul Smith
13 | *
14 | */
15 | class PaintBoxHistory extends PaintBox {
16 | //private Context context;
17 |
18 | private int historySize;
19 | private double[] x_History;
20 | private double[] y_History;
21 | private double[] z_History;
22 | private long[] time_History;
23 | private int historyPtr = 0;
24 | private double valueRange = Double.NEGATIVE_INFINITY;
25 | private int drawWidth = 0;
26 | private int drawHeight = 0;
27 | //private double scale_x = 1;
28 | private double scale_y = 1;
29 | private int offset_y = 0;
30 | boolean once = true;
31 |
32 | private int seconds = 1; // # of seconds to show on screen
33 | private int num_steps = 0;
34 |
35 | /**
36 | *
37 | * @param context
38 | * the context under which is painted
39 | * @param valueRange
40 | * range of values to be display around zero
41 | * @param seconds
42 | * @param tvSteps
43 | * @param drawWidth
44 | * the width of the paintable area
45 | * @param drawHeight
46 | * the height of the paintable area
47 | */
48 | public PaintBoxHistory(Context context, double valueRange, int historySize, int seconds) {
49 | super(context);
50 | // save to have e.g. access to asserts
51 | // this.context = context;
52 | this.valueRange = valueRange;
53 | this.historySize = historySize;
54 | x_History = new double[historySize];
55 | y_History = new double[historySize];
56 | z_History = new double[historySize];
57 | time_History = new long[historySize];
58 | this.seconds = seconds;
59 | }
60 |
61 | private int getPosOnScreen(long x_ms, long current_ms) {
62 | long diff_ms = current_ms - x_ms;
63 |
64 | // addition because diff_ms is negative!
65 | int res = (int) (drawWidth - (((double) drawWidth / (double) seconds) / 1000.0) * diff_ms);
66 | return res;
67 | }
68 |
69 | @Override
70 | protected void onDraw(Canvas canvas) {
71 | if (once) {
72 | setDimensions();
73 | }
74 | canvas.drawColor(Color.WHITE);
75 | canvas.drawLine(0, offset_y, drawWidth, offset_y, ToolBox.myPaint(1, Color.BLACK));
76 | Paint paint = ToolBox.myPaint(2, Color.RED);
77 | paint.setTextSize(40.0f);
78 | canvas.drawText("Steps: " + num_steps, 10, getHeight()-40, paint);
79 | long uptime_ms = System.currentTimeMillis();
80 |
81 | for(long ts : tenLastSteps){
82 | canvas.drawLine(getPosOnScreen(ts, uptime_ms), 0,
83 | getPosOnScreen(ts, uptime_ms), drawHeight, ToolBox.myPaint(2, Color.RED));
84 | }
85 |
86 | drawDataSet(canvas, x_History, ToolBox.myPaint(2, Color.RED), uptime_ms);
87 | drawDataSet(canvas, y_History, ToolBox.myPaint(2, Color.GREEN), uptime_ms);
88 | drawDataSet(canvas, z_History, ToolBox.myPaint(2, Color.BLUE), uptime_ms);
89 |
90 | canvas.drawText("" + varianceOfSet(x_History), 10, 10, ToolBox.myPaint(2, Color.BLACK));
91 | canvas.drawText("" + varianceOfSet(y_History), 10, 32, ToolBox.myPaint(2, Color.BLACK));
92 | canvas.drawText("" + varianceOfSet(z_History), 10, 54, ToolBox.myPaint(2, Color.BLACK));
93 | }
94 |
95 | private void drawDataSet(Canvas canvas, double[] set, Paint paint, long uptime_ms) {
96 | int item0, item1;
97 | for (int i = 0; i < historySize - 1; i++) {
98 | item0 = (historyPtr + 1 + i) % historySize;
99 | item1 = (historyPtr + 2 + i) % historySize;
100 | double y1 = -set[item0] * scale_y + offset_y;
101 | double y2 = -set[item1] * scale_y + offset_y;
102 |
103 | canvas.drawLine(getPosOnScreen(time_History[item0], uptime_ms), (int) y1,
104 | getPosOnScreen(time_History[item1], uptime_ms), (int) y2, paint);
105 |
106 | }
107 | }
108 |
109 | public void setDimensions() {
110 | this.drawWidth = this.getWidth();
111 | this.drawHeight = this.getHeight();
112 | // scale_x = this.drawWidth / historySize;
113 | scale_y = this.drawHeight / valueRange;
114 | offset_y = this.drawHeight / 2;
115 | }
116 |
117 | /**
118 | * Call this timed
119 | * @param t
120 | * @param x
121 | * @param y
122 | * @param z
123 | */
124 | public void addTriple(long t, double[] acc) {
125 | x_History[(historyPtr + 1) % historySize] = acc[0];
126 | y_History[(historyPtr + 1) % historySize] = acc[1];
127 | z_History[(historyPtr + 1) % historySize] = acc[2];
128 | time_History[(historyPtr + 1) % historySize] = t;
129 | historyPtr++;
130 | }
131 |
132 | /**
133 | * Calculate the mean of a given array
134 | *
135 | * @param set
136 | * the input data
137 | * @return mean of the input data
138 | */
139 | private double meanOfSet(double[] set) {
140 | double res = 0.0;
141 | for (double i : set) {
142 | res += i;
143 | }
144 | return res / set.length;
145 |
146 | }
147 |
148 | /**
149 | * Calculate the variance of agiven array
150 | *
151 | * @param set
152 | * the input data
153 | * @return variance of the input data
154 | */
155 | private double varianceOfSet(double[] set) {
156 | double res = 0.0;
157 | double mean = meanOfSet(set);
158 | for (double i : set) {
159 | res += (i - mean) * (i - mean);
160 | }
161 | return res / set.length;
162 | }
163 |
164 |
165 | private int stepHistorySize = 10;
166 | private long[] tenLastSteps = new long[stepHistorySize];
167 | private int shPointer = 0;
168 |
169 | public void addStepTS(long ts){
170 | num_steps++;
171 | tenLastSteps[shPointer % stepHistorySize] = ts; // add value to values_history
172 | shPointer++;
173 | shPointer = shPointer % stepHistorySize;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/PaintBoxMap.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.net.HttpURLConnection;
8 | import java.net.MalformedURLException;
9 | import java.net.URL;
10 | import java.util.LinkedList;
11 |
12 | import android.content.Context;
13 | import android.content.SharedPreferences;
14 | import android.graphics.Bitmap;
15 | import android.graphics.BitmapFactory;
16 | import android.graphics.Canvas;
17 | import android.graphics.Color;
18 | import android.graphics.Matrix;
19 | import android.graphics.Paint;
20 | import android.graphics.RectF;
21 | import android.os.Environment;
22 | import android.util.Log;
23 | import de.uvwxy.footpath.R;
24 | import de.uvwxy.footpath.ToolBox;
25 | import de.uvwxy.footpath.core.Positioner;
26 | import de.uvwxy.footpath.graph.GraphEdge;
27 | import de.uvwxy.footpath.graph.LatLonPos;
28 | import de.uvwxy.paintbox.PaintBox;
29 |
30 | /**
31 | *
32 | * @author Paul Smith
33 | *
34 | */
35 | class PaintBoxMap extends PaintBox {
36 | private static final String MAP_SETTINGS = "PaintBoxMap";
37 |
38 | private Tile[] tiles; // array to store osm tiles
39 | private Bitmap arrow; // png, this is the user position
40 | private Bitmap arrowred; // user position in red
41 | private Bitmap stairs; // icon to show stairs on map
42 |
43 | private Context context;
44 | private Navigator navigator; // object to get data from (location, bearing,..)
45 |
46 | private LinkedList edges; // all edges on the path, in right order
47 | private LatLonPos lbBound; // left bottom position of bounding box (lat/lon)
48 | private LatLonPos rtBound; // rigt top position of bounding box (lat/lon)
49 |
50 | private float gScale = 1.0f; // global scaling, pressing the zoom buttons will change this
51 | private float scaleFactor = 0.6f; // value added/removed when changing zoom level
52 |
53 | private boolean runOnce = true; // needed to create/load resources once
54 |
55 | public PaintBoxMap(Context context, Navigator navigator) {
56 | super(context);
57 | this.context = context;
58 | this.navigator = navigator;
59 | // Load saved zoom level
60 | this.gScale = context.getSharedPreferences(MAP_SETTINGS,0).getFloat("gScale",1.0f);
61 | }
62 |
63 | // create lbBound and rtBound
64 | private void setBoundaries() {
65 | double latMin = Double.POSITIVE_INFINITY;
66 | double latMax = Double.NEGATIVE_INFINITY;
67 | double lonMin = Double.POSITIVE_INFINITY;
68 | double lonMax = Double.NEGATIVE_INFINITY;
69 | for(GraphEdge edge : edges){ // edges contain only edges from path
70 | // but still, almost every node is searched twice ([a,b][b,c][c,d]...)
71 | double n0lat = edge.getNode0().getLat();
72 | double n1lat = edge.getNode1().getLat();
73 | double n0lon = edge.getNode0().getLon();
74 | double n1lon = edge.getNode1().getLon();
75 |
76 | if(n0lat < latMin) // find minimum lat
77 | latMin = n0lat;
78 | if(n1lat < latMin)
79 | latMin = n1lat;
80 | if(n0lon < lonMin) // find minimum lon
81 | lonMin = n0lon;
82 | if(n1lon < lonMin)
83 | lonMin = n1lon;
84 |
85 | if(n0lat > latMax) // find maximum lat
86 | latMax = n0lat;
87 | if(n1lat > latMax)
88 | latMax = n1lat;
89 | if(n0lon > lonMax) // find maximum lon
90 | lonMax = n0lon;
91 | if(n1lon > lonMax)
92 | lonMax = n1lon;
93 | }
94 |
95 | lbBound = new LatLonPos(latMin, lonMin, -1337);
96 | rtBound = new LatLonPos(latMax, lonMax, -1337);
97 | }
98 |
99 | // load tiles for given zoom level, from sdcard or http
100 | private Tile[] loadTiles(int zoomlevel, LatLonPos lbBoundary, LatLonPos rtBoundary){
101 | // source: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
102 |
103 | // find out which tiles to get
104 | int x0 = getTileX(lbBoundary.getLon(), zoomlevel); // point 0 left top
105 | int y0 = getTileY(rtBoundary.getLat(), zoomlevel);
106 | int x1 = getTileX(rtBoundary.getLon(), zoomlevel); // point 1 right top
107 | int y2 = getTileY(lbBoundary.getLat(), zoomlevel); // point 2 left bottom
108 |
109 | int diffX = x1 - x0;
110 | int diffY = y2 - y0;
111 |
112 | int arraySize = (diffX+3)*(diffY+3);
113 | arraySize = arraySize <= 0 ? 1 : arraySize; // fix size if only one tile needed
114 | Tile[] res = new Tile[arraySize];
115 |
116 | // check if dir exists
117 | File dir = new File(Environment.getExternalStorageDirectory(),"footpath/");
118 | if(!dir.exists()){
119 | dir.mkdir();
120 | }
121 | int counter = 0;
122 | boolean downloadFailed = false;
123 | for(int x = -1; x <= diffX+1; x++){ // x/y = -1 to have some more tiles around the building
124 | for(int y = -1; y <= diffY+1; y++){ // because some parts of the building might be overlapping
125 | // and thus not visible (nicer graphics)
126 | File f = new File(Environment.getExternalStorageDirectory(),"footpath/tile."
127 | + zoomlevel + "." + (x0+x) + "." + (y0+y) + ".png");
128 | if(f.exists()){
129 | // file existed -> read it
130 | res[counter] = new Tile(zoomlevel, x0+x, y0+y, BitmapFactory.decodeFile(f.getPath()));
131 | Log.i("FOOTPATH", "Loading from sd footpath/tile." + zoomlevel + "." + (x0+x) + "." + (y0+y) + ".png)");
132 | } else {
133 | // file did not exist -> download it
134 | URL u = null;
135 | try {
136 | u= new URL("http://tile.openstreetmap.org/" + zoomlevel + "/" + (x0+x) + "/" + (y0+y) + ".png");
137 | } catch (MalformedURLException e) {
138 | Log.i("FOOTPATH", "URL creation failed (http://tile.openstreetmap.org/" + zoomlevel + "/" + (x0+x) + "/" + (y0+y) + ".png)" + "\n" + e);
139 | }
140 | try {
141 | HttpURLConnection c = (HttpURLConnection)u.openConnection();
142 | c.setDoInput(true);
143 | InputStream is = c.getInputStream();
144 | res[counter] = new Tile(zoomlevel, x0+x, y0+y, BitmapFactory.decodeStream(is));
145 | Log.i("FOOTPATH", "Download succeeded (" + u.toString() + ")");
146 | // -> and save it
147 | try {
148 | FileOutputStream out = new FileOutputStream(f);
149 | res[counter].getBitmap().compress(Bitmap.CompressFormat.PNG, 90, out);
150 | Log.i("FOOTPATH", "Writing of file suceeded (" + f.toString() + ")");
151 | } catch (Exception e) {
152 | Log.i("FOOTPATH", "Writing of file failed (" + f.toString() + ")" + "\n" + e);
153 | }
154 | } catch (IOException e) {
155 | Log.i("FOOTPATH", "Download failed (" + u.toString() + ")" + "\n" + e);
156 | downloadFailed = true;
157 | }
158 | }
159 | counter++;
160 | }
161 | }
162 | if(downloadFailed){
163 | // TODO: Give feedback on failed download of map tiles
164 | }
165 | return res;
166 | }
167 |
168 | private int getTileX(double lon, int zoom) {
169 | return (int)Math.floor( (lon + 180) / 360 * (1< green, else red)
317 | Paint wPaint = edges.get(i).getWheelchair()<0 ? ToolBox.redPaint() : ToolBox.greenPaint();
318 |
319 | if(edges.get(i).isStairs()){ // draw stairs icon
320 | Matrix m = new Matrix();
321 | m.setScale(1.0f, 1.0f);
322 | m.postTranslate(globalOffsetX + getPosX(newNodePos, localScale) - stairs.getWidth()/2.0f ,
323 | globalOffsetY + getPosY(newNodePos, localScale) - stairs.getHeight()/2.0f);
324 | canvas.drawBitmap(stairs,m,null);
325 | }
326 | canvas.drawLine(globalOffsetX + getPosX(oldNodePos, localScale), // draw path
327 | globalOffsetY + getPosY(oldNodePos, localScale),
328 | globalOffsetX + getPosX(newNodePos, localScale),
329 | globalOffsetY + getPosY(newNodePos, localScale),
330 | wPaint);
331 |
332 | oldNodePos = newNodePos;
333 |
334 | }
335 | }
336 |
337 | float scaleWidth; // displacement to center path: x
338 | float scaleHeight; // displacement to center path: y
339 | float globalOffsetX = 0.0f; // in case we want to "move" path in x
340 | float globalOffsetY = 0.0f; // in case we want to "move" path in y
341 |
342 | private float getPosX(LatLonPos p, double localScale){
343 | return mercatXToScreen(p.getMercatorX(), localScale);
344 | }
345 |
346 | private float getPosY(LatLonPos p, double localScale){
347 | return mercatYToScreen(p.getMercatorY(), localScale);
348 | }
349 |
350 | private float mercatXToScreen(double x, double localScale){ // this returns the position relative to the middle of the
351 | double highX = rtBound.getMercatorX(); // width of all data
352 | double lowX = lbBound.getMercatorX();
353 | double w = (highX - lowX);
354 | double xs = w/2 - (highX - x); // xs = relative position to center of data
355 | xs *=localScale;
356 | return (float)xs;
357 | }
358 |
359 | private float mercatYToScreen(double y, double localScale){ // this returns the position relative to the middle of the
360 | double highY = rtBound.getMercatorY(); // height of all data
361 | double lowY = lbBound.getMercatorY();
362 | double h = (highY - lowY);
363 | double ys = h/2 - (highY - y); // ys = relative position to center of data
364 | ys *= -1.0*localScale;
365 | return (float)ys;
366 | }
367 |
368 | public void zoomOut() {
369 | if (gScale <= scaleFactor) {
370 | } else {
371 | gScale -= scaleFactor;
372 | SharedPreferences settings = context.getSharedPreferences(MAP_SETTINGS, 0);
373 | SharedPreferences.Editor editor = settings.edit();
374 | editor.putFloat("gScale", gScale);
375 | editor.commit();
376 | }
377 | }
378 |
379 | public void zoomIn() {
380 | if (gScale >= 100.0f) {
381 | } else {
382 | gScale += scaleFactor;
383 | SharedPreferences settings = context.getSharedPreferences(MAP_SETTINGS, 0);
384 | SharedPreferences.Editor editor = settings.edit();
385 | editor.putFloat("gScale", gScale);
386 | editor.commit();
387 | }
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/gui/Tile.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.gui;
2 |
3 | import android.graphics.Bitmap;
4 | import de.uvwxy.footpath.graph.LatLonPos;
5 | /**
6 | *
7 | * @author Paul Smith
8 | *
9 | */
10 | public class Tile {
11 | int zoomlevel;
12 | int x;
13 | int y;
14 | Bitmap bitmap;
15 | public Tile(int zoomlevel, int x, int y, Bitmap bitmap) {
16 | this.zoomlevel = zoomlevel;
17 | this.x = x;
18 | this.y = y;
19 | this.bitmap = bitmap;
20 | }
21 | public int getZoomlevel() {
22 | return zoomlevel;
23 | }
24 | public int getX() {
25 | return x;
26 | }
27 | public int getY() {
28 | return y;
29 | }
30 | public Bitmap getBitmap() {
31 | return bitmap;
32 | }
33 | public void setZoomlevel(int zoomlevel) {
34 | this.zoomlevel = zoomlevel;
35 | }
36 | public void setX(int x) {
37 | this.x = x;
38 | }
39 | public void setY(int y) {
40 | this.y = y;
41 | }
42 | public void setBitmap(Bitmap bitmap) {
43 | this.bitmap = bitmap;
44 | }
45 | public LatLonPos getLatLonPosLeftTop(){
46 | // source: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
47 | double lon = 0.0;
48 | double lat = 0.0;
49 | double n = Math.pow(2, zoomlevel);
50 | lon = x/n*360.0-180.0;
51 | double lat_rad = Math.atan(Math.sinh(Math.PI*(1.0-2.0*y/n)));
52 | lat = lat_rad * 180.0/Math.PI;
53 | return new LatLonPos(lat,lon,-1337);
54 | }
55 | public LatLonPos getLatLonPosRightBottom(){
56 | // source: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
57 | double lon = 0.0;
58 | double lat = 0.0;
59 | double n = Math.pow(2, zoomlevel);
60 | lon = (x+1)/n*360.0-180.0;
61 | double lat_rad = Math.atan(Math.sinh(Math.PI*(1.0-2.0*(y+1)/n)));
62 | lat = lat_rad * 180.0/Math.PI;
63 | return new LatLonPos(lat,lon,-1337);
64 | }
65 | }
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/log/AudioWriter.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.log;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | import de.uvwxy.footpath.gui.Navigator;
7 |
8 | import android.media.MediaRecorder;
9 | import android.os.Environment;
10 |
11 | /**
12 | * A class for recording of an audio/video file, with input from the
13 | * microphone/camera . The directory written to is LOG_DIR within the directory
14 | * retrieved from getExternalStorageDirectory(). The file name is given to the
15 | * constructor.
16 | *
17 | * Usage:
18 | *
19 | * o = new AudioWriter()
20 | * o.registerCapture()
21 | * o.startCapture()
22 | * [... magic moment in time ...]
23 | * o.stopCapture()
24 | * o.unregisterCapture()
25 | *
26 | *
27 | * @author Paul Smith
28 | *
29 | */
30 | public class AudioWriter {
31 | private MediaRecorder recorder;
32 | private String filePath;
33 |
34 | /**
35 | * The only constructor
36 | *
37 | * @param fileName the file name
38 | */
39 | public AudioWriter(String sub_directory, String fileName) {
40 | File dir = new File(Environment.getExternalStorageDirectory(),
41 | Navigator.LOG_DIR + sub_directory);
42 | dir.mkdir();
43 | this.filePath = dir.getAbsolutePath() + "/" + fileName;
44 | }
45 |
46 | /**
47 | * Call this to initialize everything needed to capture from AudioSource.MIC
48 | *
49 | * @throws IllegalStateException
50 | * @throws IOException
51 | */
52 | public void registerCapture() throws IllegalStateException, IOException {
53 | recorder = new MediaRecorder();
54 | recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
55 | recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
56 | recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
57 | recorder.setOutputFile(filePath);
58 | recorder.prepare();
59 | }
60 |
61 | /**
62 | * Call this to start capture
63 | */
64 | public void startCapture() {
65 | recorder.start();
66 | }
67 |
68 | /**
69 | * Call this to stop capture
70 | */
71 | public void stopCapture() {
72 | recorder.stop();
73 | recorder.reset();
74 | }
75 |
76 | /**
77 | * Call this to release capture device
78 | */
79 | public void unregisterCapture() {
80 | recorder.release();
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/log/DataLogger.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.log;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.util.List;
5 |
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.location.Location;
11 | import android.location.LocationListener;
12 | import android.location.LocationManager;
13 | import android.net.wifi.ScanResult;
14 | import android.net.wifi.WifiManager;
15 | import android.os.Bundle;
16 | import android.util.Log;
17 | import de.uvwxy.footpath.Rev;
18 |
19 | /**
20 | * Interface to log compass, accelerometer, GPS, WiFi and route
21 | * Use startLogging() to register file writers and GPS/WiFi interfaces
22 | * Use log[..](params) from class using the DataLogger interface
23 | * GPS/WiFi Logging will be handled in this object
24 | * Use stopLogging() to close everything mentioned above
25 | * @author Paul Smith
26 | *
27 | */
28 | public class DataLogger {
29 | // Objects to work with
30 | private long routeID;
31 | private String from;
32 | private String to;
33 |
34 | private boolean started = false;
35 | private boolean accOpen = false; // To check if files have been opened successfully
36 | private boolean compOpen = false;
37 |
38 | private FWriter fwCompass; // Log data to /sdcard/routelog/
39 | private FWriter fwAccelerometer;
40 | private FWriter fwVariance;
41 | private FWriter fwPosition;
42 | private FWriter fwSteps;
43 | private FWriter fwGPS;
44 | private FWriter fwWifi;
45 | private FWriter fwRawAccel;
46 | private FWriter fwRawCompass;
47 | private FWriter fwRoute;
48 | private FWriter fwSimpleRoute;
49 | private FWriter fwInfo;
50 | private LocationManager locationManager = null;
51 |
52 | private WifiManager wm01;
53 | private WifiReceiver wr01;
54 | private List lScanResult;
55 |
56 | private Context context;
57 |
58 | /*
59 | * Handles writing of GPS data.
60 | */
61 | LocationListener locationListener = new LocationListener() {
62 | @Override
63 | public void onLocationChanged(Location location) {
64 | try {
65 | fwGPS.openFileOnCard();
66 | fwGPS.appendLineToFile(""
67 | + System.currentTimeMillis() + ","
68 | + location.getLatitude() + ","
69 | + location.getLongitude());
70 | fwGPS.closeFileOnCard();
71 | } catch (FileNotFoundException e) {
72 | e.printStackTrace();
73 | }
74 | }
75 | @Override
76 | public void onStatusChanged(String provider, int status, Bundle extras) {
77 | }
78 | @Override
79 | public void onProviderEnabled(String provider) {
80 | }
81 | @Override
82 | public void onProviderDisabled(String provider) {
83 | }
84 |
85 | };
86 |
87 | /**
88 | * A class that receives the scan results of a WiFi scan. After each scan a
89 | * new scan is started.
90 | *
91 | * @author Paul
92 | *
93 | */
94 | class WifiReceiver extends BroadcastReceiver {
95 | // Wifi
96 | WifiManager wmLocal;
97 |
98 | public WifiReceiver(WifiManager wm01) {
99 | wmLocal = wm01;
100 | }
101 |
102 | @Override
103 | public void onReceive(Context c, Intent intent) {
104 | lScanResult = wm01.getScanResults();
105 | boolean retry = true;
106 | int tries = 0;
107 | while(retry && tries < 3){
108 | try {
109 | fwWifi.openFileOnCard();
110 | fwWifi.appendLineToFile("Time passed (ms): "
111 | + (System.currentTimeMillis()));
112 | retry = false;
113 | } catch (FileNotFoundException e) {
114 | tries++;
115 | }
116 | }
117 | // retry was set to false, can log data
118 | if(!retry){
119 | for (int i = 0; i < lScanResult.size(); i++) {
120 | fwWifi.appendLineToFile((new Integer(i + 1).toString()
121 | + "." + lScanResult.get(i)).toString());
122 | }
123 | fwWifi.closeFileOnCard();
124 | }
125 | // After each scan, start a new scan.
126 | wmLocal.startScan();
127 | }
128 | }
129 |
130 | public DataLogger(Context context, long routeID, String from, String to){
131 | this.routeID = routeID;
132 | this.from = from;
133 | this.to = to;
134 | this.context = context;
135 | locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
136 | }
137 |
138 | public void logTimedCompass(long timestamp, double value){
139 | writeTofwObject(fwCompass, "" + timestamp + ", " + value);
140 | }
141 | public void logTimedVariance(long timestamp, double value){
142 | writeTofwObject(fwVariance, "" + timestamp + ", " + value);
143 | }
144 |
145 | public void logRawCompass(long timestamp, double x, double y, double z){
146 | if(!compOpen){
147 | boolean retry = true;
148 | int tries = 0;
149 | while(retry && tries < 3){
150 | try {
151 | fwRawCompass.openFileOnCard();
152 | retry = false;
153 | compOpen = true;
154 | } catch (FileNotFoundException e) {
155 | tries++;
156 | }
157 | }
158 | }
159 | if(compOpen){
160 | fwRawCompass.appendLineToFile("" + timestamp + ", " + x + ", " + y + ", " + z);
161 | }
162 | }
163 |
164 | public void logTimedAcc(long timestamp, double value){
165 | writeTofwObject(fwAccelerometer, "" + timestamp + ", " + value);
166 | }
167 |
168 | public void logRawAcc(long timestamp, double x, double y, double z){
169 | if(!accOpen){
170 | boolean retry = true;
171 | int tries = 0;
172 | while(retry && tries < 3){
173 | try {
174 | fwRawAccel.openFileOnCard();
175 | retry = false;
176 | accOpen = true;
177 | } catch (FileNotFoundException e) {
178 | tries++;
179 | }
180 | }
181 | }
182 | if(accOpen){
183 | fwRawAccel.appendLineToFile("" + timestamp + ", " + x + ", " + y + ", " + z);
184 | }
185 | }
186 |
187 | public void logPosition(long timestamp, double latBest, double lonBest, double progressBest,
188 | double latFirst, double lonFirst, double progressFirst){
189 | // NOTE: progress is in [0,1]
190 | writeTofwObject(fwPosition, "" + timestamp + ", " + latBest + ", " + lonBest + ", " + progressBest
191 | + latFirst + ", " + lonFirst + ", " + progressFirst);
192 | }
193 |
194 | public void logRoute(double lat0, double lon0){
195 | writeTofwObject(fwRoute, "" + lat0 + ", " + lon0);
196 | }
197 | public void logSimpleRoute(double lat0, double lon0){
198 | writeTofwObject(fwSimpleRoute, "" + lat0 + ", " + lon0);
199 | }
200 |
201 | public void logStep(long timestamp, double direction){
202 | writeTofwObject(fwSteps, "" + timestamp + ", " + direction);
203 | }
204 |
205 | public void logInfo(String s){
206 | writeTofwObject(fwInfo, s);
207 | }
208 |
209 | public boolean started(){
210 | return started;
211 | }
212 |
213 | public long getRouteId(){
214 | return routeID;
215 | }
216 |
217 | public void startLogging(){
218 | Log.i("FOOTPATH", "Starting Logging");
219 | routeID = System.currentTimeMillis();
220 | createFileObjects();
221 |
222 | try {
223 | fwRawAccel.openFileOnCard();
224 | accOpen = true;
225 | fwRawCompass.openFileOnCard();
226 | compOpen = true;
227 | } catch (FileNotFoundException e) {
228 | e.printStackTrace();
229 | }
230 |
231 | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,
232 | 0, locationListener);
233 | wm01 = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
234 | wr01 = new WifiReceiver(wm01);
235 | context.registerReceiver(wr01, new IntentFilter(
236 | WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
237 | Log.i("FOOTPATH", "Registered WifiReceiver");
238 | wm01.startScan();
239 | Log.i("FOOTPATH", "Started WiFi Scan");
240 | started = true;
241 | }
242 |
243 | public void stopLogging(){
244 | started = false;
245 | locationManager.removeUpdates(locationListener);
246 | context.unregisterReceiver(wr01);
247 | if(accOpen){
248 | fwRawAccel.closeFileOnCard();
249 | }
250 | if(compOpen){
251 | fwRawCompass.closeFileOnCard();
252 | }
253 | }
254 |
255 | private void createFileObjects() {
256 | fwAccelerometer = new FWriter("" + routeID + "_" + from + "_" + to, "acc.csv");
257 | fwCompass = new FWriter("" + routeID + "_" + from + "_" + to, "comp.csv");
258 | fwVariance = new FWriter("" + routeID + "_" + from + "_" + to, "zVar.csv");
259 | fwPosition = new FWriter("" + routeID + "_" + from + "_" + to, "pos.csv");
260 | fwSteps = new FWriter("" + routeID + "_" + from + "_" + to, "steps.csv");
261 | fwGPS = new FWriter("" + routeID + "_" + from + "_" + to, "gps.csv");
262 | fwWifi = new FWriter("" + routeID + "_" + from + "_" + to, "wifi.csv");
263 | fwRawAccel = new FWriter("" + routeID + "_" + from + "_" + to, "rawacc.csv");
264 | fwRawCompass = new FWriter("" + routeID + "_" + from + "_" + to, "rawcomp.csv");
265 | fwRoute = new FWriter("" + routeID + "_" + from + "_" + to, "route.csv");
266 | fwSimpleRoute = new FWriter("" + routeID + "_" + from + "_" + to, "simpleroute.csv");
267 | fwInfo = new FWriter("" + routeID + "_" + from + "_" + to, "info(rev. " + Rev.rev + ").txt");
268 | }
269 |
270 | private void writeTofwObject(FWriter fw, String data){
271 | boolean retry = true;
272 | int tries = 0;
273 | while(retry && tries < 3){
274 | try {
275 | fw.openFileOnCard();
276 | fw.appendLineToFile(data);
277 | fw.closeFileOnCard();
278 | retry = false;
279 | } catch (FileNotFoundException e) {
280 | //e.printStackTrace();
281 | tries++;
282 | }
283 | }
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/de/uvwxy/footpath/log/FWriter.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.footpath.log;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.PrintWriter;
8 |
9 | import de.uvwxy.footpath.gui.Navigator;
10 |
11 | import android.os.Environment;
12 | import android.util.Log;
13 |
14 | /**
15 | * A class for opening, writing to and closing of a text file. The directory
16 | * written to is LOG_DIR in the directory retrieved from
17 | * getExternalStorageDirectory(). The file name is given to the constructor.
18 | *
19 | * @author Paul Smith
20 | *
21 | */
22 | public class FWriter {
23 | PrintWriter p;
24 | FileOutputStream fos;
25 | String fileName;
26 | boolean open = false;
27 |
28 | String sub_directory;
29 | /**
30 | * Constructor
31 | *
32 | * @param fileName
33 | * the file name
34 | */
35 | public FWriter(String sub_directory, String fileName) {
36 | this.fileName = fileName;
37 | this.sub_directory = sub_directory;
38 | }
39 |
40 | /**
41 | * Creates the directory LOG_DIR on the external storage and opens the file
42 | * for writing. There is no problem if LOG_DIR already exists.
43 | *
44 | * @throws FileNotFoundException
45 | */
46 | public void openFileOnCard() throws FileNotFoundException {
47 | // check if first dir exists
48 | File dir = new File(Environment.getExternalStorageDirectory(),Navigator.LOG_DIR);
49 | if(!dir.exists()){
50 | dir.mkdir();
51 | }
52 | // check if second dir exists
53 | dir = new File(Environment.getExternalStorageDirectory(),
54 | Navigator.LOG_DIR + sub_directory);
55 | if(!dir.exists()){
56 | dir.mkdir();
57 | }
58 | // check if file exists
59 | File f = new File (dir, fileName);
60 | if(!f.exists()){
61 | try {
62 | // create it
63 | f.createNewFile();
64 | } catch (IOException e) {
65 | Log.i("FOOTPATH", "ERROR: " + e.toString());
66 | throw new FileNotFoundException("IOException");
67 | }
68 | }
69 | try {
70 | fos = new FileOutputStream(f, true);
71 | p = new PrintWriter(fos);
72 | } catch (IOException e) {
73 | Log.i("FOOTPATH", "ERROR: " + e.toString());
74 | throw new FileNotFoundException("IOException");
75 | }
76 |
77 | open = true;
78 | }
79 |
80 | /**
81 | * Appends a string to a text file followed by a new line
82 | *
83 | * @param data
84 | * the line to append
85 | */
86 | public void appendLineToFile(String data) {
87 | p.append(data + "\n");
88 | }
89 |
90 | /**
91 | * Closes the previously opened file.F
92 | */
93 | public void closeFileOnCard() {
94 | if(open){
95 | p.close();
96 | open = false;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/de/uvwxy/paintbox/PaintBox.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.paintbox;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.view.SurfaceHolder;
6 | import android.view.SurfaceView;
7 |
8 | /**
9 | * A class managing the creation of a canvas to draw on.
10 | *
11 | * Usage:
12 | *
13 | * Create a class which overrides onDraw. Once the surface is created onDraw()
14 | * is called in an infinite loop from a PaintThread. Destroying the surface
15 | * stops the background thread calling onDraw().
16 | *
17 | * @author Paul Smith
18 | *
19 | */
20 | public abstract class PaintBox extends SurfaceView implements SurfaceHolder.Callback {
21 |
22 | @Override
23 | public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
24 | }
25 |
26 | @Override
27 | public void surfaceCreated(SurfaceHolder arg0) {
28 | pThread = new PaintThread(getHolder(), this);
29 | pThread.setRunning(true);
30 | pThread.start();
31 | }
32 |
33 | @Override
34 | public void surfaceDestroyed(SurfaceHolder arg0) {
35 | boolean retry = true;
36 | pThread.setRunning(false);
37 | while (retry) {
38 | try {
39 | pThread.join();
40 | retry = false;
41 | } catch (InterruptedException e) {
42 | }
43 | }
44 | }
45 |
46 | @Override
47 | protected abstract void onDraw(Canvas canvas);
48 |
49 | PaintThread pThread;
50 |
51 | public PaintBox(Context context) {
52 | super(context);
53 | getHolder().addCallback(this);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/de/uvwxy/paintbox/PaintThread.java:
--------------------------------------------------------------------------------
1 | package de.uvwxy.paintbox;
2 |
3 | import android.graphics.Canvas;
4 | import android.view.SurfaceHolder;
5 |
6 | /**
7 | * A class to create a thread to repaint the graphics.
8 | *
9 | * @author Paul Smith
10 | *
11 | */
12 | class PaintThread extends Thread {
13 | private SurfaceHolder surfaceHolder;
14 | private PaintBox pBox;
15 | private boolean bRunning = false;
16 |
17 | public PaintThread(SurfaceHolder surfaceHolder, PaintBox pBox) {
18 | this.surfaceHolder = surfaceHolder;
19 | this.pBox = pBox;
20 | }
21 |
22 | public void setRunning(boolean run) {
23 | bRunning = run;
24 | }
25 |
26 | @Override
27 | public void run() {
28 | Canvas c;
29 | while (bRunning) {
30 | c = null;
31 | try {
32 | c = surfaceHolder.lockCanvas(null);
33 | synchronized (surfaceHolder) {
34 | pBox.onDraw(c);
35 | }
36 | } finally {
37 | if (c != null) {
38 | surfaceHolder.unlockCanvasAndPost(c);
39 | }
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/tools/4228_4222.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/tools/4228_4222.png
--------------------------------------------------------------------------------
/tools/4228_5056.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/COMSYS/FootPath/d3cfd0d9030e66a3fef30e83161cfc541a984dab/tools/4228_5056.png
--------------------------------------------------------------------------------
/tools/README:
--------------------------------------------------------------------------------
1 | Usage: java -jar URL2QR.jar