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