8 import gnome.gconf as gconf
9 from threading import Thread
11 gobject.threads_init()
12 #gtk.gdk.threads_init()
15 PATH="/apps/pedometerhomewidget"
16 COUNTER=PATH+"/counter"
22 LOGGING=PATH+"/logging"
24 ICONSPATH = "/opt/pedometerhomewidget/"
26 class PedoIntervalCounter:
34 #TODO: check if last detected step is at the end of the interval
36 def __init__(self, coords, tval):
42 def setThreshold(self, value):
43 self.MIN_THRESHOLD = value
45 def setTimeSteps(self, value):
46 self.MIN_TIME_STEPS = value
48 def calc_mean(self, vals):
53 return sum / len(vals)
56 def calc_stdev(self, vals):
58 mean = self.calc_mean(vals)
60 rez+=pow(abs(mean-i),2)
61 return math.sqrt(rez/len(vals))
63 def calc_threshold(self, vals):
66 mean = self.calc_mean(vals)
67 threshold = max (abs(mean-vmax), abs(mean-vmin))
70 def count_steps(self, vals, t):
71 threshold = self.MIN_THRESHOLD
72 mean = self.calc_mean(vals)
77 if abs(vals[i] - mean) > threshold:
80 while i < len(vals) and t[i] < ntime:
85 def get_best_values(self, x, y, z):
86 dev1 = self.calc_stdev(x)
87 dev2 = self.calc_stdev(y)
88 dev3 = self.calc_stdev(z)
89 dev_max = max(dev1, dev2, dev3)
91 if ( abs(dev1 - dev_max ) < 0.001):
92 logger.info("X chosen as best axis, stdev %f" % dev1)
94 elif (abs(dev2 - dev_max) < 0.001):
95 logger.info("Y chosen as best axis, stdev %f" % dev2)
98 logger.info("Z chosen as best axis, stdev %f" % dev3)
101 def number_steps(self):
102 vals = self.get_best_values(self.x, self.y, self.z)
103 return self.count_steps(vals, self.t)
105 class PedoCounter(Thread):
106 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
107 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
108 LOGFILE = "/home/user/log_pedometer"
109 COORD_GET_INTERVAL = 0.01
118 stop_requested = False
119 update_function = None
122 def __init__(self, update_function = None):
123 Thread.__init__(self)
124 if not os.path.exists(self.COORD_FNAME):
125 self.COORD_FNAME = self.COORD_FNAME_SDK
127 self.update_function = update_function
129 def set_mode(self, mode):
130 #runnig, higher threshold to prevent fake steps
132 self.MIN_THRESHOLD = 650
133 self.MIN_TIME_STEPS = 0.35
136 self.MIN_THRESHOLD = 500
137 self.MIN_TIME_STEPS = 0.5
139 def set_logging(self, value):
142 #set height, will affect the distance
143 def set_height(self, height_interval):
144 if height_interval == 0:
146 elif height_interval == 1:
148 elif height_interval == 2:
150 elif height_interval == 3:
152 elif height_interval == 4:
155 def get_rotation(self):
156 f = open(self.COORD_FNAME, 'r')
157 coords = [int(w) for w in f.readline().split()]
161 def reset_counter(self):
164 def get_counter(self):
167 def start_interval(self):
168 logger.info("New interval started")
171 coords = [[], [], []]
172 while not self.stop_requested and (len(t) == 0 or t[-1] < 5):
173 x,y,z = self.get_rotation()
174 coords[0].append(int(x))
175 coords[1].append(int(y))
176 coords[2].append(int(z))
177 now = time.time()-stime
179 self.file.write("%d %d %d %f\n" %(coords[0][-1], coords[1][-1], coords[2][-1], now))
182 time.sleep(self.COORD_GET_INTERVAL)
183 pic = PedoIntervalCounter(coords, t)
184 cnt = pic.number_steps()
186 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(t)))
189 logger.info("Total number of steps : %d" % self.counter)
192 def request_stop(self):
193 self.stop_requested = True
196 logger.info("Thread started")
198 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
199 self.file = open(self.LOGFILE + fname + ".txt", "w")
201 while 1 and not self.stop_requested:
202 last_cnt = self.start_interval()
203 if self.update_function is not None:
204 gobject.idle_add(self.update_function, self.counter, last_cnt)
209 logger.info("Thread has finished")
211 def get_distance(self, steps=None):
214 return self.STEP_LENGTH * steps;
216 class CustomButton(hildon.Button):
217 def __init__(self, icon):
218 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
220 self.set_size_request(int(32*1.4), int(30*1.0))
221 self.retval = self.connect("expose_event", self.expose)
223 def set_icon(self, icon):
226 def expose(self, widget, event):
227 self.context = widget.window.cairo_create()
228 self.context.rectangle(event.area.x, event.area.y,
229 event.area.width, event.area.height)
232 rect = self.get_allocation()
233 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
234 self.context.set_source_rgba(1, 1, 1, 0)
236 style = self.rc_get_style()
237 color = style.lookup_color("DefaultBackgroundColor")
238 if self.state == gtk.STATE_ACTIVE:
239 style = self.rc_get_style()
240 color = style.lookup_color("SelectionColor")
241 self.context.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
244 #img = cairo.ImageSurface.create_from_png(self.icon)
246 #self.context.set_source_surface(img)
247 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
249 img.set_from_file(self.icon)
250 buf = img.get_pixbuf()
251 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
253 self.context.set_source_pixbuf(buf, rect.x+(event.area.width/2-15)-8, rect.y+1)
254 self.context.scale(200,200)
259 class PedometerHomePlugin(hildondesktop.HomePluginItem):
262 #labels for current steps
263 labelsC = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
265 #labels for all time steps
266 labelsT = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
284 #gtk.gdk.threads_init()
285 hildondesktop.HomePluginItem.__init__(self)
287 self.client = gconf.client_get_default()
289 self.totalCounter = self.client.get_int(COUNTER)
290 self.totalTime = self.client.get_int(TIMER)
291 self.mode = self.client.get_int(MODE)
292 self.height = self.client.get_int(HEIGHT)
293 self.unit = self.client.get_int(UNIT)
294 self.aspect = self.client.get_int(ASPECT)
295 self.logging = self.client.get_bool(LOGGING)
297 self.client.set_int(COUNTER, 0)
298 self.client.set_int(TIMER, 0)
299 self.client.set_int(MODE, 0)
300 self.client.set_int(HEIGHT, 0)
301 self.client.set_int(UNIT, 0)
302 self.client.set_int(ASPECT, 0)
303 self.client.set_bool(LOGGING, False)
305 self.pedometer = PedoCounter(self.update_values)
306 self.pedometer.set_mode(self.mode)
307 self.pedometer.set_height(self.height)
309 #self.button = gtk.Button("Start")
310 self.button = CustomButton(ICONSPATH + "play.png")
311 self.button.connect("clicked", self.button_clicked)
313 self.create_labels(self.labelsC)
314 self.create_labels(self.labelsT)
316 self.update_ui_values(self.labelsC, 0, 0)
317 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
319 mainHBox = gtk.HBox(spacing=1)
321 descVBox = gtk.VBox(spacing=1)
322 descVBox.add(gtk.Label())
323 descVBox.add(gtk.Label("Time:"))
324 descVBox.add(gtk.Label("Steps:"))
325 descVBox.add(gtk.Label("Distance:"))
326 descVBox.add(gtk.Label("Avg Speed:"))
328 currentVBox = gtk.VBox(spacing=1)
329 currentVBox.add(gtk.Label("Current"))
330 currentVBox.add(self.labelsC["timer"])
331 currentVBox.add(self.labelsC["count"])
332 currentVBox.add(self.labelsC["dist"])
333 currentVBox.add(self.labelsC["avgSpeed"])
334 self.currentBox = currentVBox
336 totalVBox = gtk.VBox(spacing=1)
337 totalVBox.add(gtk.Label("Total"))
338 totalVBox.add(self.labelsT["timer"])
339 totalVBox.add(self.labelsT["count"])
340 totalVBox.add(self.labelsT["dist"])
341 totalVBox.add(self.labelsT["avgSpeed"])
342 self.totalBox = totalVBox
344 buttonVBox = gtk.VBox(spacing=1)
345 buttonVBox.add(gtk.Label(""))
346 buttonVBox.add(self.button)
347 buttonVBox.add(gtk.Label(""))
349 mainHBox.add(buttonVBox)
350 mainHBox.add(descVBox)
351 mainHBox.add(currentVBox)
352 mainHBox.add(totalVBox)
354 self.mainhbox = mainHBox
360 self.connect("unrealize", self.close_requested)
361 self.set_settings(True)
362 self.connect("show-settings", self.show_settings)
364 def create_labels(self, labels):
365 labels["timer"] = gtk.Label()
366 labels["count"] = gtk.Label()
367 labels["dist"] = gtk.Label()
368 labels["avgSpeed"] = gtk.Label()
370 def update_aspect(self):
372 self.currentBox.show_all()
373 self.totalBox.show_all()
374 elif self.aspect == 1:
375 self.currentBox.show_all()
376 self.totalBox.hide_all()
378 self.currentBox.hide_all()
379 self.totalBox.show_all()
381 def update_ui_values(self, labels, timer, steps):
382 def get_str_distance(meters):
385 return "%.2f km" % (meters/1000)
387 return "%.2f mi" % (meters/1609.344)
390 return "%d m" % meters
392 return "%d ft" % int(meters*3.2808)
394 def get_avg_speed(timer, dist):
405 return "N/A " + suffix
406 speed = 1.0 *dist / timer
407 #convert from meters per second to km/h or mi/h
409 return "%.2f %s" % (speed, suffix)
412 hours = int(tdelta / 3600)
413 tdelta -= 3600 * hours
414 mins = int(tdelta / 60)
418 strtime = "%.2d:%.2d:%.2d" % ( hours, mins, secs)
420 labels["timer"].set_label(strtime)
421 labels["count"].set_label(str(steps))
423 dist = self.pedometer.get_distance(steps)
425 labels["dist"].set_label(get_str_distance(dist))
426 labels["avgSpeed"].set_label(get_avg_speed(timer, dist))
428 def update_current(self):
429 self.update_ui_values(self.labelsC, self.time, self.counter)
431 def update_total(self):
432 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
434 def show_settings(self, widget):
435 def reset_total_counter(arg):
436 widget.totalCounter = 0
438 widget.update_total()
439 hildon.hildon_banner_show_information(self,"None", "Total counter was resetted")
441 def selector_changed(selector, data):
442 widget.mode = selector.get_active(0)
443 widget.client.set_int(MODE, widget.mode)
445 def selectorH_changed(selector, data):
446 widget.height = selectorH.get_active(0)
447 widget.client.set_int(HEIGHT, widget.height)
449 def selectorUnit_changed(selector, data):
450 widget.unit = selectorUnit.get_active(0)
451 widget.client.set_int(UNIT, widget.unit)
452 widget.update_current()
453 widget.update_total()
455 def selectorUI_changed(selector, data):
456 widget.aspect = selectorUI.get_active(0)
457 widget.client.set_int(ASPECT, widget.aspect)
458 widget.update_aspect()
460 def logButton_changed(checkButton):
461 widget.logging = checkButton.get_active()
462 widget.client.set_bool(LOGGING, widget.logging)
464 dialog = gtk.Dialog()
465 dialog.set_transient_for(self)
466 dialog.set_title("Settings")
468 dialog.add_button("OK", gtk.RESPONSE_OK)
469 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
470 button.set_title("Reset total counter")
471 button.set_alignment(0, 0.8, 1, 1)
472 button.connect("clicked", reset_total_counter)
474 selector = hildon.TouchSelector(text=True)
475 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
476 selector.append_text("Walk")
477 selector.append_text("Run")
478 selector.connect("changed", selector_changed)
480 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
481 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
482 modePicker.set_title("Select mode")
483 modePicker.set_selector(selector)
484 modePicker.set_active(widget.mode)
486 selectorH = hildon.TouchSelector(text=True)
487 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
488 selectorH.append_text("< 1.50 m")
489 selectorH.append_text("1.50 - 1.65 m")
490 selectorH.append_text("1.66 - 1.80 m")
491 selectorH.append_text("1.81 - 1.95 m")
492 selectorH.append_text(" > 1.95 m")
493 selectorH.connect("changed", selectorH_changed)
495 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
496 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
497 heightPicker.set_title("Select height")
498 heightPicker.set_selector(selectorH)
499 heightPicker.set_active(widget.height)
501 selectorUnit = hildon.TouchSelector(text=True)
502 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
503 selectorUnit.append_text("Metric (km)")
504 selectorUnit.append_text("English (mi)")
505 selectorUnit.connect("changed", selectorUnit_changed)
507 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
508 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
509 unitPicker.set_title("Units")
510 unitPicker.set_selector(selectorUnit)
511 unitPicker.set_active(widget.unit)
513 selectorUI = hildon.TouchSelector(text=True)
514 selectorUI = hildon.TouchSelector(text=True)
515 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
516 selectorUI.append_text("Show current + total")
517 selectorUI.append_text("Show only current")
518 selectorUI.append_text("Show only total")
519 selectorUI.connect("changed", selectorUI_changed)
521 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
522 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
523 UIPicker.set_title("Widget aspect")
524 UIPicker.set_selector(selectorUI)
525 UIPicker.set_active(widget.aspect)
527 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
528 logButton.set_label("Log data")
529 logButton.set_active(widget.logging)
530 logButton.connect("toggled", logButton_changed)
532 dialog.vbox.add(button)
533 dialog.vbox.add(modePicker)
534 dialog.vbox.add(heightPicker)
535 dialog.vbox.add(unitPicker)
536 dialog.vbox.add(UIPicker)
537 dialog.vbox.add(logButton)
540 response = dialog.run()
541 hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
544 def close_requested(self, widget):
545 if self.pedometer is None:
548 self.pedometer.request_stop()
549 if self.pedometer.isAlive():
550 self.pedometer.join()
552 def update_values(self, totalCurent, lastInterval):
553 self.totalCounter += lastInterval
554 self.counter = totalCurent
556 tdelta = time.time() - self.time - self.startTime
558 self.totalTime += tdelta
560 self.update_current()
563 def button_clicked(self, button):
564 if self.pedometer is not None and self.pedometer.isAlive():
566 self.pedometer.request_stop()
567 self.pedometer.join()
568 self.client.set_int(COUNTER, self.totalCounter)
569 self.client.set_int(TIMER, int(self.totalTime))
570 #self.button.set_label("Start")
571 self.button.set_icon(ICONSPATH + "play.png")
573 self.pedometer = PedoCounter(self.update_values)
574 self.pedometer.set_mode(self.mode)
575 self.pedometer.set_height(self.height)
576 self.pedometer.set_logging(self.logging)
582 self.update_current()
584 self.pedometer.start()
585 self.startTime = time.time()
586 #self.button.set_label("Stop")
587 self.button.set_icon(ICONSPATH + "stop.png")
589 def do_expose_event(self, event):
590 cr = self.window.cairo_create()
591 cr.region(event.window.get_clip_region())
593 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
594 style = self.rc_get_style()
595 color = style.lookup_color("DefaultBackgroundColor")
596 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
599 width = self.allocation.width
600 height = self.allocation.height
602 x = self.allocation.x
603 y = self.allocation.y
605 cr.move_to(x+radius, y)
606 cr.line_to(x + width - radius, y)
607 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
608 cr.line_to(x + width, y + height - radius)
609 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
610 cr.line_to(x + radius, y + height)
611 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
612 cr.line_to(x, y + radius)
613 cr.curve_to(x, y + radius, x, y, x + radius, y)
615 cr.set_operator(cairo.OPERATOR_SOURCE)
618 color = style.lookup_color("ActiveTextColor")
619 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.5);
623 hildondesktop.HomePluginItem.do_expose_event(self, event)
625 def do_realize(self):
626 screen = self.get_screen()
627 self.set_colormap(screen.get_rgba_colormap())
628 self.set_app_paintable(True)
629 hildondesktop.HomePluginItem.do_realize(self)
631 hd_plugin_type = PedometerHomePlugin
633 # The code below is just for testing purposes.
634 # It allows to run the widget as a standalone process.
635 if __name__ == "__main__":
637 gobject.type_register(hd_plugin_type)
638 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
642 ############### old pedometer.py ###
646 from threading import Thread
648 logger = logging.getLogger("pedometer")
649 logger.setLevel(logging.INFO)
651 ch = logging.StreamHandler()
652 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
653 ch.setFormatter(formatter)
654 logger.addHandler(ch)