8 import gnome.gconf as gconf
9 from threading import Thread
11 #gobject.threads_init()
12 #gtk.gdk.threads_init()
14 PATH="/apps/pedometerhomewidget"
15 COUNTER=PATH+"/counter"
21 LOGGING=PATH+"/logging"
23 ICONSPATH = "/opt/pedometerhomewidget/"
25 class PedoIntervalCounter:
33 #TODO: check if last detected step is at the end of the interval
35 def __init__(self, coords, tval):
41 def setThreshold(self, value):
42 self.MIN_THRESHOLD = value
44 def setTimeSteps(self, value):
45 self.MIN_TIME_STEPS = value
47 def calc_mean(self, vals):
52 return sum / len(vals)
55 def calc_stdev(self, vals):
57 mean = self.calc_mean(vals)
59 rez+=pow(abs(mean-i),2)
60 return math.sqrt(rez/len(vals))
62 def calc_threshold(self, vals):
65 mean = self.calc_mean(vals)
66 threshold = max (abs(mean-vmax), abs(mean-vmin))
69 def count_steps(self, vals, t):
70 threshold = self.MIN_THRESHOLD
71 mean = self.calc_mean(vals)
76 if abs(vals[i] - mean) > threshold:
79 while i < len(vals) and t[i] < ntime:
84 def get_best_values(self, x, y, z):
85 dev1 = self.calc_stdev(x)
86 dev2 = self.calc_stdev(y)
87 dev3 = self.calc_stdev(z)
88 dev_max = max(dev1, dev2, dev3)
90 if ( abs(dev1 - dev_max ) < 0.001):
91 logger.info("X chosen as best axis, stdev %f" % dev1)
93 elif (abs(dev2 - dev_max) < 0.001):
94 logger.info("Y chosen as best axis, stdev %f" % dev2)
97 logger.info("Z chosen as best axis, stdev %f" % dev3)
100 def number_steps(self):
101 vals = self.get_best_values(self.x, self.y, self.z)
102 return self.count_steps(vals, self.t)
104 class PedoCounter(Thread):
105 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
106 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
107 LOGFILE = "/home/user/log_pedometer"
108 COORD_GET_INTERVAL = 0.01
117 stop_requested = False
118 update_function = None
123 def __init__(self, update_function = None):
124 Thread.__init__(self)
125 if not os.path.exists(self.COORD_FNAME):
126 self.COORD_FNAME = self.COORD_FNAME_SDK
128 self.update_function = update_function
130 def set_mode(self, mode):
131 #runnig, higher threshold to prevent fake steps
134 self.MIN_THRESHOLD = 650
135 self.MIN_TIME_STEPS = 0.35
138 self.MIN_THRESHOLD = 500
139 self.MIN_TIME_STEPS = 0.5
141 def set_logging(self, value):
144 #set height, will affect the distance
145 def set_height(self, height_interval):
146 if height_interval == 0:
147 self.STEP_LENGTH = 0.59
148 elif height_interval == 1:
149 self.STEP_LENGTH = 0.64
150 elif height_interval == 2:
151 self.STEP_LENGTH = 0.71
152 elif height_interval == 3:
153 self.STEP_LENGTH = 0.77
154 elif height_interval == 4:
155 self.STEP_LENGTH = 0.83
156 #increase step length if RUNNING
158 self.STEP_LENGTH *= 1.45
160 def get_rotation(self):
161 f = open(self.COORD_FNAME, 'r')
162 coords = [int(w) for w in f.readline().split()]
166 def reset_counter(self):
169 def get_counter(self):
172 def start_interval(self):
173 logger.info("New interval started")
176 coords = [[], [], []]
177 while not self.stop_requested and (len(t) == 0 or t[-1] < 5):
178 x,y,z = self.get_rotation()
179 coords[0].append(int(x))
180 coords[1].append(int(y))
181 coords[2].append(int(z))
182 now = time.time()-stime
184 self.file.write("%d %d %d %f\n" %(coords[0][-1], coords[1][-1], coords[2][-1], now))
187 time.sleep(self.COORD_GET_INTERVAL)
188 pic = PedoIntervalCounter(coords, t)
189 cnt = pic.number_steps()
191 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(t)))
194 logger.info("Total number of steps : %d" % self.counter)
197 def request_stop(self):
198 self.stop_requested = True
201 logger.info("Thread started")
203 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
204 self.file = open(self.LOGFILE + fname + ".txt", "w")
206 while 1 and not self.stop_requested:
207 last_cnt = self.start_interval()
208 if self.update_function is not None:
209 gobject.idle_add(self.update_function, self.counter, last_cnt)
214 logger.info("Thread has finished")
216 def get_distance(self, steps=None):
219 return self.STEP_LENGTH * steps;
221 class CustomButton(hildon.Button):
222 def __init__(self, icon):
223 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
225 self.set_size_request(int(32*1.4), int(30*1.0))
226 self.retval = self.connect("expose_event", self.expose)
228 def set_icon(self, icon):
231 def expose(self, widget, event):
232 self.context = widget.window.cairo_create()
233 self.context.rectangle(event.area.x, event.area.y,
234 event.area.width, event.area.height)
237 rect = self.get_allocation()
238 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
239 self.context.set_source_rgba(1, 1, 1, 0)
241 style = self.rc_get_style()
242 color = style.lookup_color("DefaultBackgroundColor")
243 if self.state == gtk.STATE_ACTIVE:
244 style = self.rc_get_style()
245 color = style.lookup_color("SelectionColor")
246 self.context.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
249 #img = cairo.ImageSurface.create_from_png(self.icon)
251 #self.context.set_source_surface(img)
252 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
254 img.set_from_file(self.icon)
255 buf = img.get_pixbuf()
256 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
258 self.context.set_source_pixbuf(buf, rect.x+(event.area.width/2-15)-8, rect.y+1)
259 self.context.scale(200,200)
264 class PedometerHomePlugin(hildondesktop.HomePluginItem):
267 #labels for current steps
268 labels = ["timer", "count", "dist", "avgSpeed"]
269 #labelsC = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
271 #labels for all time steps
272 #labelsT = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
292 gtk.gdk.threads_init()
293 #gobject.threads_init()
294 hildondesktop.HomePluginItem.__init__(self)
296 self.client = gconf.client_get_default()
298 self.totalCounter = self.client.get_int(COUNTER)
299 self.totalTime = self.client.get_int(TIMER)
300 self.mode = self.client.get_int(MODE)
301 self.height = self.client.get_int(HEIGHT)
302 self.unit = self.client.get_int(UNIT)
303 self.aspect = self.client.get_int(ASPECT)
304 self.logging = self.client.get_bool(LOGGING)
306 self.client.set_int(COUNTER, 0)
307 self.client.set_int(TIMER, 0)
308 self.client.set_int(MODE, 0)
309 self.client.set_int(HEIGHT, 0)
310 self.client.set_int(UNIT, 0)
311 self.client.set_int(ASPECT, 0)
312 self.client.set_bool(LOGGING, False)
314 self.pedometer = PedoCounter(self.update_values)
315 self.pedometer.set_mode(self.mode)
316 self.pedometer.set_height(self.height)
318 #self.button = gtk.Button("Start")
319 self.button = CustomButton(ICONSPATH + "play.png")
320 self.button.connect("clicked", self.button_clicked)
322 self.create_labels(self.labelsC)
323 self.create_labels(self.labelsT)
325 self.update_ui_values(self.labelsC, 0, 0)
326 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
328 mainHBox = gtk.HBox(spacing=1)
330 descVBox = gtk.VBox(spacing=1)
331 descVBox.add(self.new_label_heading())
332 descVBox.add(self.new_label_heading("Time:"))
333 descVBox.add(self.new_label_heading("Steps:"))
334 descVBox.add(self.new_label_heading("Distance:"))
335 descVBox.add(self.new_label_heading("Avg Speed:"))
337 currentVBox = gtk.VBox(spacing=1)
338 currentVBox.add(self.new_label_heading("Current"))
339 currentVBox.add(self.labelsC["timer"])
340 currentVBox.add(self.labelsC["count"])
341 currentVBox.add(self.labelsC["dist"])
342 currentVBox.add(self.labelsC["avgSpeed"])
343 self.currentBox = currentVBox
345 totalVBox = gtk.VBox(spacing=1)
346 totalVBox.add(self.new_label_heading("Total"))
347 totalVBox.add(self.labelsT["timer"])
348 totalVBox.add(self.labelsT["count"])
349 totalVBox.add(self.labelsT["dist"])
350 totalVBox.add(self.labelsT["avgSpeed"])
351 self.totalBox = totalVBox
353 buttonVBox = gtk.VBox(spacing=1)
354 buttonVBox.add(self.new_label_heading(""))
355 buttonVBox.add(self.button)
356 buttonVBox.add(self.new_label_heading(""))
358 mainHBox.add(buttonVBox)
359 mainHBox.add(descVBox)
360 mainHBox.add(currentVBox)
361 mainHBox.add(totalVBox)
363 self.mainhbox = mainHBox
369 self.connect("unrealize", self.close_requested)
370 self.set_settings(True)
371 self.connect("show-settings", self.show_settings)
373 def new_label_heading(self, title=""):
375 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
378 def create_labels(self, new_labels):
379 for label in self.labels:
381 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
382 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
383 new_labels[label] = l
385 def update_aspect(self):
387 self.currentBox.show_all()
388 self.totalBox.show_all()
389 elif self.aspect == 1:
390 self.currentBox.show_all()
391 self.totalBox.hide_all()
393 self.currentBox.hide_all()
394 self.totalBox.show_all()
396 def update_ui_values(self, labels, timer, steps):
397 def get_str_distance(meters):
400 return "%.2f km" % (meters/1000)
402 return "%.2f mi" % (meters/1609.344)
405 return "%d m" % meters
407 return "%d ft" % int(meters*3.2808)
409 def get_avg_speed(timer, dist):
420 return "N/A " + suffix
421 speed = 1.0 *dist / timer
422 #convert from meters per second to km/h or mi/h
424 return "%.2f %s" % (speed, suffix)
427 hours = int(tdelta / 3600)
428 tdelta -= 3600 * hours
429 mins = int(tdelta / 60)
433 strtime = "%.2d:%.2d:%.2d" % ( hours, mins, secs)
435 labels["timer"].set_label(strtime)
436 labels["count"].set_label(str(steps))
438 dist = self.pedometer.get_distance(steps)
440 labels["dist"].set_label(get_str_distance(dist))
441 labels["avgSpeed"].set_label(get_avg_speed(timer, dist))
443 def update_current(self):
444 self.update_ui_values(self.labelsC, self.time, self.counter)
446 def update_total(self):
447 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
449 def show_settings(self, widget):
450 def reset_total_counter(arg):
451 widget.totalCounter = 0
453 widget.update_total()
454 hildon.hildon_banner_show_information(self,"None", "Total counter was resetted")
456 def selector_changed(selector, data):
457 widget.mode = selector.get_active(0)
458 widget.client.set_int(MODE, widget.mode)
459 widget.pedometer.set_mode(widget.mode)
460 widget.pedometer.set_height(widget.height)
461 widget.update_current()
462 widget.update_total()
464 def selectorH_changed(selector, data):
465 widget.height = selectorH.get_active(0)
466 widget.client.set_int(HEIGHT, widget.height)
467 widget.pedometer.set_height(widget.height)
468 widget.update_current()
469 widget.update_total()
472 def selectorUnit_changed(selector, data):
473 widget.unit = selectorUnit.get_active(0)
474 widget.client.set_int(UNIT, widget.unit)
475 widget.update_current()
476 widget.update_total()
478 def selectorUI_changed(selector, data):
479 widget.aspect = selectorUI.get_active(0)
480 widget.client.set_int(ASPECT, widget.aspect)
481 widget.update_aspect()
483 def logButton_changed(checkButton):
484 widget.logging = checkButton.get_active()
485 widget.client.set_bool(LOGGING, widget.logging)
487 dialog = gtk.Dialog()
488 dialog.set_transient_for(self)
489 dialog.set_title("Settings")
491 dialog.add_button("OK", gtk.RESPONSE_OK)
492 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
493 button.set_title("Reset total counter")
494 button.set_alignment(0, 0.8, 1, 1)
495 button.connect("clicked", reset_total_counter)
497 selector = hildon.TouchSelector(text=True)
498 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
499 selector.append_text("Walk")
500 selector.append_text("Run")
501 selector.connect("changed", selector_changed)
503 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
504 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
505 modePicker.set_title("Select mode")
506 modePicker.set_selector(selector)
507 modePicker.set_active(widget.mode)
509 selectorH = hildon.TouchSelector(text=True)
510 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
511 selectorH.append_text("< 1.50 m")
512 selectorH.append_text("1.50 - 1.65 m")
513 selectorH.append_text("1.66 - 1.80 m")
514 selectorH.append_text("1.81 - 1.95 m")
515 selectorH.append_text(" > 1.95 m")
516 selectorH.connect("changed", selectorH_changed)
518 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
519 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
520 heightPicker.set_title("Select height")
521 heightPicker.set_selector(selectorH)
522 heightPicker.set_active(widget.height)
524 selectorUnit = hildon.TouchSelector(text=True)
525 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
526 selectorUnit.append_text("Metric (km)")
527 selectorUnit.append_text("English (mi)")
528 selectorUnit.connect("changed", selectorUnit_changed)
530 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
531 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
532 unitPicker.set_title("Units")
533 unitPicker.set_selector(selectorUnit)
534 unitPicker.set_active(widget.unit)
536 selectorUI = hildon.TouchSelector(text=True)
537 selectorUI = hildon.TouchSelector(text=True)
538 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
539 selectorUI.append_text("Show current + total")
540 selectorUI.append_text("Show only current")
541 selectorUI.append_text("Show only total")
542 selectorUI.connect("changed", selectorUI_changed)
544 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
545 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
546 UIPicker.set_title("Widget aspect")
547 UIPicker.set_selector(selectorUI)
548 UIPicker.set_active(widget.aspect)
550 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
551 logButton.set_label("Log data")
552 logButton.set_active(widget.logging)
553 logButton.connect("toggled", logButton_changed)
555 pan_area = hildon.PannableArea()
559 vbox.add(heightPicker)
564 pan_area.add_with_viewport(vbox)
565 pan_area.set_size_request(-1, 600)
566 dialog.vbox.add(pan_area)
568 response = dialog.run()
569 #hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
572 def close_requested(self, widget):
573 if self.pedometer is None:
576 self.pedometer.request_stop()
577 if self.pedometer.isAlive():
578 self.pedometer.join()
580 def update_values(self, totalCurent, lastInterval):
581 self.totalCounter += lastInterval
582 self.counter = totalCurent
584 tdelta = time.time() - self.time - self.startTime
586 self.totalTime += tdelta
588 self.update_current()
591 def button_clicked(self, button):
592 if self.pedometer is not None and self.pedometer.isAlive():
594 self.pedometer.request_stop()
595 self.pedometer.join()
596 self.client.set_int(COUNTER, self.totalCounter)
597 self.client.set_int(TIMER, int(self.totalTime))
598 #self.button.set_label("Start")
599 self.button.set_icon(ICONSPATH + "play.png")
601 self.pedometer = PedoCounter(self.update_values)
602 self.pedometer.set_mode(self.mode)
603 self.pedometer.set_height(self.height)
604 self.pedometer.set_logging(self.logging)
609 self.update_current()
611 self.pedometer.start()
612 self.startTime = time.time()
613 #self.button.set_label("Stop")
614 self.button.set_icon(ICONSPATH + "stop.png")
616 def do_expose_event(self, event):
617 cr = self.window.cairo_create()
618 cr.region(event.window.get_clip_region())
620 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
621 style = self.rc_get_style()
622 color = style.lookup_color("DefaultBackgroundColor")
623 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
626 width = self.allocation.width
627 height = self.allocation.height
629 x = self.allocation.x
630 y = self.allocation.y
632 cr.move_to(x+radius, y)
633 cr.line_to(x + width - radius, y)
634 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
635 cr.line_to(x + width, y + height - radius)
636 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
637 cr.line_to(x + radius, y + height)
638 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
639 cr.line_to(x, y + radius)
640 cr.curve_to(x, y + radius, x, y, x + radius, y)
642 cr.set_operator(cairo.OPERATOR_SOURCE)
645 color = style.lookup_color("ActiveTextColor")
646 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.5);
650 hildondesktop.HomePluginItem.do_expose_event(self, event)
652 def do_realize(self):
653 screen = self.get_screen()
654 self.set_colormap(screen.get_rgba_colormap())
655 self.set_app_paintable(True)
656 hildondesktop.HomePluginItem.do_realize(self)
658 hd_plugin_type = PedometerHomePlugin
660 # The code below is just for testing purposes.
661 # It allows to run the widget as a standalone process.
662 if __name__ == "__main__":
664 gobject.type_register(hd_plugin_type)
665 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
669 ############### old pedometer.py ###
673 from threading import Thread
675 logger = logging.getLogger("pedometer")
676 logger.setLevel(logging.INFO)
678 ch = logging.StreamHandler()
679 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
680 ch.setFormatter(formatter)
681 logger.addHandler(ch)