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