fca005a8411222172028b9a678155f46a64328eb
[pedometerwidget] / pedometer_widget_home.py
1 import gtk
2 import hildondesktop
3 import gobject
4 import os
5 import time
6 import hildon
7 import gnome.gconf as gconf
8
9 print "!!!!"
10 #gobject.threads_init()
11
12 PATH="/apps/pedometerhomewidget"
13 COUNTER=PATH+"/counter"
14 TIMER=PATH+"/timer"
15 MODE=PATH+"/mode"
16 HEIGHT=PATH+"/height"
17 UNIT=PATH+"/unit"
18 ASPECT=PATH+"/aspect"
19
20 class PedometerHomePlugin(hildondesktop.HomePluginItem):
21     button = None
22
23     #labels for current steps
24     labelsC = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
25
26     #labels for all time steps
27     labelsT = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
28
29     pedometer = None
30     startTime = None
31
32     totalCounter = 0
33     totalTime = 0
34     mode = 0
35     height = 0
36     unit = 0
37
38     counter = 0
39     time = 0
40     aspect = 0
41
42     def __init__(self):
43
44         gtk.gdk.threads_init()
45         hildondesktop.HomePluginItem.__init__(self)
46
47         self.client = gconf.client_get_default()
48         try:
49             self.totalCounter = self.client.get_int(COUNTER)
50             self.totalTime = self.client.get_int(TIMER)
51             self.mode = self.client.get_int(MODE)
52             self.height = self.client.get_int(HEIGHT)
53             self.unit = self.client.get_int(UNIT)
54             self.aspect = self.client.get_int(ASPECT)
55         except:
56             self.client.set_int(COUNTER, 0)
57             self.client.set_int(TIMER, 0)
58             self.client.set_int(MODE, 0)
59             self.client.set_int(HEIGHT, 0)
60             self.client.set_int(UNIT, 0)
61             self.client.set_int(ASPECT, 0)
62
63         self.pedometer = PedoCounter(self.update_values)
64         self.pedometer.set_mode(self.mode)
65         self.pedometer.set_height(self.height)
66
67         self.button = gtk.Button("Start")
68         self.button.connect("clicked", self.button_clicked)
69
70         self.create_labels(self.labelsC)
71         self.create_labels(self.labelsT)
72
73         self.update_ui_values(self.labelsC, 0, 0)
74         self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
75
76         mainHBox = gtk.HBox(spacing=1)
77
78         descVBox = gtk.VBox(spacing=1)
79         descVBox.add(gtk.Label())
80         descVBox.add(gtk.Label("Time:"))
81         descVBox.add(gtk.Label("Steps:"))
82         descVBox.add(gtk.Label("Distance:"))
83         descVBox.add(gtk.Label("Avg Speed:"))
84
85         currentVBox = gtk.VBox(spacing=1)
86         currentVBox.add(gtk.Label("Current"))
87         currentVBox.add(self.labelsC["timer"])
88         currentVBox.add(self.labelsC["count"])
89         currentVBox.add(self.labelsC["dist"])
90         currentVBox.add(self.labelsC["avgSpeed"])
91         self.currentBox = currentVBox
92
93         totalVBox = gtk.VBox(spacing=1)
94         totalVBox.add(gtk.Label("Total"))
95         totalVBox.add(self.labelsT["timer"])
96         totalVBox.add(self.labelsT["count"])
97         totalVBox.add(self.labelsT["dist"])
98         totalVBox.add(self.labelsT["avgSpeed"])
99         self.totalBox = totalVBox
100
101         mainHBox.add(self.button)
102         mainHBox.add(descVBox)
103         mainHBox.add(currentVBox)
104         mainHBox.add(totalVBox)
105
106         mainHBox.show_all()
107         self.add(mainHBox)
108         self.update_aspect()
109
110         self.connect("unrealize", self.close_requested)
111         self.set_settings(True)
112         self.connect("show-settings", self.show_settings)
113
114     def create_labels(self, labels):
115         labels["timer"] = gtk.Label()
116         labels["count"] = gtk.Label()
117         labels["dist"] = gtk.Label()
118         labels["avgSpeed"] = gtk.Label()
119
120     def update_aspect(self):
121         if self.aspect == 0:
122             self.currentBox.show_all()
123             self.totalBox.show_all()
124         elif self.aspect == 1:
125             self.currentBox.show_all()
126             self.totalBox.hide_all()
127         else:
128             self.currentBox.hide_all()
129             self.totalBox.show_all()
130
131     def update_ui_values(self, labels, timer, steps):
132         def get_str_distance(meters):
133             if meters > 1000:
134                 if self.unit == 0:
135                     return str(meters/1000) + " km"
136                 else:
137                     return str(meters/1609.344) + " mi"
138             else:
139                 if self.unit == 0:
140                     return str(meters) + " m"
141                 else:
142                     return str(meters*3.2808) + " ft"
143
144         def get_avg_speed(timer, dist):
145             suffix = ""
146             conv = 0
147             if self.unit:
148                 suffix = "mi/h"
149                 conv = 2.23693629
150             else:
151                 suffix = "km/h"
152                 conv = 3.6
153
154             if timer == 0:
155                 return "N/A " + suffix
156             speed = 1.0 *dist / timer
157             #convert from meters per second to km/h or mi/h
158             speed *= conv
159             return "%.2f %s" % (speed, suffix)
160
161         tdelta = timer
162         hours = int(tdelta / 3600)
163         tdelta -= 3600 * hours
164         mins = int(tdelta / 60)
165         tdelta -= 60 * mins
166         secs = int(tdelta)
167
168         strtime = "%.2d:%.2d:%.2d" % ( hours, mins, secs)
169
170         labels["timer"].set_label(strtime)
171         labels["count"].set_label(str(steps))
172
173         dist = self.pedometer.get_distance(steps)
174
175         labels["dist"].set_label(get_str_distance(dist))
176         labels["avgSpeed"].set_label(get_avg_speed(timer, dist))
177
178     def update_current(self):
179         self.update_ui_values(self.labelsC, self.time, self.counter)
180
181     def update_total(self):
182         self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
183
184     def show_settings(self, widget):
185         def reset_total_counter(arg):
186             widget.totalCounter = 0
187             widget.totalTime = 0
188             widget.update_total()
189             hildon.hildon_banner_show_information(self,"None", "Total counter was resetted")
190
191         def selector_changed(selector, data):
192             widget.mode = selector.get_active(0)
193             widget.client.set_int(MODE, widget.mode)
194
195         def selectorH_changed(selector, data):
196             widget.height = selectorH.get_active(0)
197             widget.client.set_int(HEIGHT, widget.height)
198
199         def selectorUnit_changed(selector, data):
200             widget.unit = selectorUnit.get_active(0)
201             widget.client.set_int(UNIT, widget.unit)
202             widget.update_current()
203             widget.update_total()
204
205         def selectorUI_changed(selector, data):
206             widget.aspect = selectorUI.get_active(0)
207             widget.client.set_int(ASPECT, widget.aspect)
208             widget.update_aspect()
209
210         dialog = gtk.Dialog()
211         dialog.set_transient_for(self)
212         dialog.set_title("Settings")
213
214         dialog.add_button("OK", gtk.RESPONSE_OK)
215         button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
216         button.set_title("Reset total counter")
217         button.set_alignment(0, 0.8, 1, 1)
218         button.connect("clicked", reset_total_counter)
219
220         selector = hildon.TouchSelector(text=True)
221         selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
222         selector.append_text("Walk")
223         selector.append_text("Run")
224         selector.connect("changed", selector_changed)
225
226         modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
227         modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
228         modePicker.set_title("Select mode")
229         modePicker.set_selector(selector)
230         modePicker.set_active(widget.mode)
231
232         selectorH = hildon.TouchSelector(text=True)
233         selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
234         selectorH.append_text("< 1.50 m")
235         selectorH.append_text("1.50 - 1.65 m")
236         selectorH.append_text("1.66 - 1.80 m")
237         selectorH.append_text("1.81 - 1.95 m")
238         selectorH.append_text(" > 1.95 m")
239         selectorH.connect("changed", selectorH_changed)
240
241         heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
242         heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
243         heightPicker.set_title("Select height")
244         heightPicker.set_selector(selectorH)
245         heightPicker.set_active(widget.height)
246
247         selectorUnit = hildon.TouchSelector(text=True)
248         selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
249         selectorUnit.append_text("Metric (km)")
250         selectorUnit.append_text("English (mi)")
251         selectorUnit.connect("changed", selectorUnit_changed)
252
253         unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
254         unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
255         unitPicker.set_title("Units")
256         unitPicker.set_selector(selectorUnit)
257         unitPicker.set_active(widget.unit)
258
259         selectorUI = hildon.TouchSelector(text=True)
260         selectorUI = hildon.TouchSelector(text=True)
261         selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
262         selectorUI.append_text("Show current + total")
263         selectorUI.append_text("Show only current")
264         selectorUI.append_text("Show only total")
265         selectorUI.connect("changed", selectorUI_changed)
266
267         UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
268         UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
269         UIPicker.set_title("Widget aspect")
270         UIPicker.set_selector(selectorUI)
271         UIPicker.set_active(widget.aspect)
272
273         dialog.vbox.add(button)
274         dialog.vbox.add(modePicker)
275         dialog.vbox.add(heightPicker)
276         dialog.vbox.add(unitPicker)
277         dialog.vbox.add(UIPicker)
278
279         dialog.show_all()
280         response = dialog.run()
281         hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
282         dialog.destroy()
283
284     def close_requested(self, widget):
285         if self.pedometer is None:
286             return
287
288         self.pedometer.request_stop()
289         if self.pedometer.isAlive():
290             self.pedometer.join()
291
292     def update_values(self, totalCurent, lastInterval):
293         self.totalCounter += lastInterval
294         self.counter = totalCurent
295
296         tdelta = time.time() - self.time - self.startTime
297         self.time += tdelta
298         self.totalTime += tdelta
299
300         self.update_current()
301         self.update_total()
302
303     def button_clicked(self, button):
304         print "button clicked"
305
306         if self.pedometer is not None and self.pedometer.isAlive():
307             #counter is running
308             self.pedometer.request_stop()
309             self.pedometer.join()
310             self.client.set_int(COUNTER, self.totalCounter)
311             self.client.set_int(TIMER, int(self.totalTime))
312             self.button.set_label("Start")
313             self.desc.hide_all()
314         else:
315             self.desc.show_all()
316             self.pedometer = PedoCounter(self.update_values)
317             self.pedometer.set_mode(self.mode)
318             self.pedometer.set_height(self.height)
319
320             self.time = 0
321             self.counter = 0
322
323             self.update_current()
324
325             self.pedometer.start()
326             self.startTime = time.time()
327             self.button.set_label("Stop")
328
329         print "button clicked finished"
330
331 hd_plugin_type = PedometerHomePlugin
332
333 # The code below is just for testing purposes.
334 # It allows to run the widget as a standalone process.
335 if __name__ == "__main__":
336     import gobject
337     gobject.type_register(hd_plugin_type)
338     obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
339     obj.show_all()
340     gtk.main()
341
342 ############### old pedometer.py ###
343 import math
344 import logging
345
346 from threading import Thread
347
348 logger = logging.getLogger("pedometer")
349 logger.setLevel(logging.INFO)
350
351 ch = logging.StreamHandler()
352 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
353 ch.setFormatter(formatter)
354 logger.addHandler(ch)
355
356 class PedoIntervalCounter:
357     MIN_THRESHOLD = 500
358     MIN_TIME_STEPS = 0.5
359     x = []
360     y = []
361     z = []
362     t = []
363
364     #TODO: check if last detected step is at the end of the interval
365
366     def __init__(self, coords, tval):
367         self.x = coords[0]
368         self.y = coords[1]
369         self.z = coords[2]
370         self.t = tval
371
372     def setThreshold(self, value):
373         self.MIN_THRESHOLD = value
374
375     def setTimeSteps(self, value):
376         self.MIN_TIME_STEPS = value
377
378     def calc_mean(self, vals):
379         sum = 0
380         for i in vals:
381             sum+=i
382         if len(vals) > 0:
383             return sum / len(vals)
384         return 0
385
386     def calc_stdev(self, vals):
387         rez = 0
388         mean = self.calc_mean(vals)
389         for i in vals:
390             rez+=pow(abs(mean-i),2)
391         return math.sqrt(rez/len(vals))
392
393     def calc_threshold(self, vals):
394         vmax = max(vals)
395         vmin = min(vals)
396         mean = self.calc_mean(vals)
397         threshold = max (abs(mean-vmax), abs(mean-vmin))
398         return threshold
399
400     def count_steps(self, vals, t):
401         threshold = self.MIN_THRESHOLD
402         mean = self.calc_mean(vals)
403         cnt = 0
404
405         i=0
406         while i < len(vals):
407             if abs(vals[i] - mean) > threshold:
408                 cnt+=1
409                 ntime = t[i] + 0.5
410                 while i < len(vals) and t[i] < ntime:
411                     i+=1
412             i+=1
413         return cnt
414
415     def get_best_values(self, x, y, z):
416         dev1 = self.calc_stdev(x)
417         dev2 = self.calc_stdev(y)
418         dev3 = self.calc_stdev(z)
419         dev_max = max(dev1, dev2, dev3)
420
421         if ( abs(dev1 - dev_max ) < 0.001):
422             logger.info("X chosen as best axis, stdev %f" % dev1)
423             return x
424         elif (abs(dev2 - dev_max) < 0.001):
425             logger.info("Y chosen as best axis, stdev %f" % dev2)
426             return y
427         else:
428             logger.info("Z chosen as best axis, stdev %f" % dev3)
429             return z
430
431     def number_steps(self):
432         vals = self.get_best_values(self.x, self.y, self.z)
433         return self.count_steps(vals, self.t)
434
435 class PedoCounter(Thread):
436     COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
437     COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
438     COORD_GET_INTERVAL = 0.01
439     COUNT_INTERVAL = 5
440
441     STEP_LENGTH = 0.7
442
443     MIN_THRESHOLD = 500
444     MIN_TIME_STEPS = 0.5
445
446     counter = 0
447     stop_requested = False
448     update_function = None
449
450     def __init__(self, update_function = None):
451         Thread.__init__(self)
452         if not os.path.exists(self.COORD_FNAME):
453             self.COORD_FNAME = self.COORD_FNAME_SDK
454
455         self.update_function = update_function
456
457     def set_mode(self, mode):
458         #runnig, higher threshold to prevent fake steps
459         if mode == 1:
460             self.MIN_THRESHOLD = 600
461             self.MIN_TIME_STEPS = 0.35
462         #walking
463         else:
464             self.MIN_THRESHOLD = 500
465             self.MIN_TIME_STEPS = 0.5
466
467     #set height, will affect the distance
468     def set_height(self, height_interval):
469         if height_interval == 0:
470             STEP_LENGTH = 0.5
471         elif height_interval == 1:
472             STEP_LENGTH = 0.6
473         elif height_interval == 2:
474             STEP_LENGTH = 0.7
475         elif height_interval == 3:
476             STEP_LENGTH = 0.8
477         elif height_interval == 4:
478             STEP_LENGTH = 0.9
479
480     def get_rotation(self):
481         f = open(self.COORD_FNAME, 'r')
482         coords = [int(w) for w in f.readline().split()]
483         f.close()
484         return coords
485
486     def reset_counter(self):
487         counter = 0
488
489     def get_counter(self):
490         return counter
491
492     def start_interval(self):
493         logger.info("New interval started")
494         stime = time.time()
495         t=[]
496         coords = [[], [], []]
497         while not self.stop_requested and (len(t) == 0 or t[-1] < 5):
498             x,y,z = self.get_rotation()
499             coords[0].append(int(x))
500             coords[1].append(int(y))
501             coords[2].append(int(z))
502             now = time.time()-stime
503             t.append(now)
504             time.sleep(self.COORD_GET_INTERVAL)
505         pic = PedoIntervalCounter(coords, t)
506         cnt = pic.number_steps()
507
508         logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(t)))
509
510         self.counter += cnt
511         logger.info("Total number of steps : %d" % self.counter)
512         return cnt
513
514     def request_stop(self):
515         self.stop_requested = True
516
517     def run(self):
518         logger.info("Thread started")
519         while 1 and not self.stop_requested:
520             last_cnt = self.start_interval()
521             if self.update_function is not None:
522                 gobject.idle_add(self.update_function, self.counter, last_cnt)
523
524         logger.info("Thread has finished")
525
526     def get_distance(self, steps=None):
527         if steps == None:
528             steps = self.counter
529         return self.STEP_LENGTH * steps;
530