2 #Author: Mirestean Andrei < andrei.mirestean at gmail.com >
4 #This program is free software: you can redistribute it and/or modify
5 #it under the terms of the GNU General Public License as published by
6 #the Free Software Foundation, either version 3 of the License, or
7 #(at your option) any later version.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
26 PATH="/apps/pedometerhomewidget"
27 COUNTER=PATH+"/counter"
33 LOGGING=PATH+"/logging"
35 ICONSPATH = "/opt/pedometerhomewidget/"
37 class PedoIntervalCounter:
45 #TODO: check if last detected step is at the end of the interval
47 def __init__(self, coords, tval):
53 def setThreshold(self, value):
54 self.MIN_THRESHOLD = value
56 def setTimeSteps(self, value):
57 self.MIN_TIME_STEPS = value
59 def calc_mean(self, vals):
64 return sum / len(vals)
67 def calc_stdev(self, vals):
69 mean = self.calc_mean(vals)
71 rez+=pow(abs(mean-i),2)
72 return math.sqrt(rez/len(vals))
74 def calc_threshold(self, vals):
77 mean = self.calc_mean(vals)
78 threshold = max (abs(mean-vmax), abs(mean-vmin))
81 def count_steps(self, vals, t):
82 threshold = self.MIN_THRESHOLD
83 mean = self.calc_mean(vals)
88 if abs(vals[i] - mean) > threshold:
91 while i < len(vals) and t[i] < ntime:
96 def get_best_values(self, x, y, z):
97 dev1 = self.calc_stdev(x)
98 dev2 = self.calc_stdev(y)
99 dev3 = self.calc_stdev(z)
100 dev_max = max(dev1, dev2, dev3)
102 if ( abs(dev1 - dev_max ) < 0.001):
103 logger.info("X chosen as best axis, stdev %f" % dev1)
105 elif (abs(dev2 - dev_max) < 0.001):
106 logger.info("Y chosen as best axis, stdev %f" % dev2)
109 logger.info("Z chosen as best axis, stdev %f" % dev3)
112 def number_steps(self):
113 vals = self.get_best_values(self.x, self.y, self.z)
114 return self.count_steps(vals, self.t)
117 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
118 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
119 LOGFILE = "/home/user/log_pedometer"
120 COORD_GET_INTERVAL = 10
129 stop_requested = False
130 update_function = None
136 def __init__(self, update_function = None):
137 if not os.path.exists(self.COORD_FNAME):
138 self.COORD_FNAME = self.COORD_FNAME_SDK
140 self.update_function = update_function
142 def set_mode(self, mode):
143 #runnig, higher threshold to prevent fake steps
146 self.MIN_THRESHOLD = 650
147 self.MIN_TIME_STEPS = 0.35
150 self.MIN_THRESHOLD = 500
151 self.MIN_TIME_STEPS = 0.5
153 def set_logging(self, value):
156 #set height, will affect the distance
157 def set_height(self, height_interval):
158 if height_interval == 0:
159 self.STEP_LENGTH = 0.59
160 elif height_interval == 1:
161 self.STEP_LENGTH = 0.64
162 elif height_interval == 2:
163 self.STEP_LENGTH = 0.71
164 elif height_interval == 3:
165 self.STEP_LENGTH = 0.77
166 elif height_interval == 4:
167 self.STEP_LENGTH = 0.83
168 #increase step length if RUNNING
170 self.STEP_LENGTH *= 1.45
172 def get_rotation(self):
173 f = open(self.COORD_FNAME, 'r')
174 coords = [int(w) for w in f.readline().split()]
178 def reset_counter(self):
181 def get_counter(self):
185 logger.info("Counter started")
186 self.isRunning = True
188 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
189 self.file = open(self.LOGFILE + fname + ".txt", "w")
190 gobject.idle_add(self.run)
193 self.coords = [[], [], []]
194 self.stime = time.time()
196 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
199 def read_coords(self):
200 x,y,z = self.get_rotation()
201 self.coords[0].append(int(x))
202 self.coords[1].append(int(y))
203 self.coords[2].append(int(z))
204 now = time.time()-self.stime
206 self.file.write("%d %d %d %f\n" %(self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
211 if self.t[-1] > 5 or self.stop_requested:
213 gobject.idle_add(self.stop_interval)
216 def stop_interval(self):
217 pic = PedoIntervalCounter(self.coords, self.t)
218 cnt = pic.number_steps()
220 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
223 logger.info("Total number of steps : %d" % self.counter)
224 gobject.idle_add(self.update_function, self.counter, cnt)
226 if self.stop_requested:
227 gobject.idle_add(self.stop)
229 gobject.idle_add(self.run)
235 logger.info("Counter has finished")
237 def request_stop(self):
238 self.stop_requested = True
239 self.isRunning = False
241 def get_distance(self, steps=None):
244 return self.STEP_LENGTH * steps;
246 class CustomButton(hildon.Button):
247 def __init__(self, icon):
248 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
250 self.set_size_request(int(32*1.4), int(30*1.0))
251 self.retval = self.connect("expose_event", self.expose)
253 def set_icon(self, icon):
256 def expose(self, widget, event):
257 self.context = widget.window.cairo_create()
258 self.context.rectangle(event.area.x, event.area.y,
259 event.area.width, event.area.height)
262 rect = self.get_allocation()
263 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
264 self.context.set_source_rgba(1, 1, 1, 0)
266 style = self.rc_get_style()
267 color = style.lookup_color("DefaultBackgroundColor")
268 if self.state == gtk.STATE_ACTIVE:
269 style = self.rc_get_style()
270 color = style.lookup_color("SelectionColor")
271 self.context.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
274 #img = cairo.ImageSurface.create_from_png(self.icon)
276 #self.context.set_source_surface(img)
277 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
279 img.set_from_file(self.icon)
280 buf = img.get_pixbuf()
281 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
283 self.context.set_source_pixbuf(buf, rect.x+(event.area.width/2-15)-8, rect.y+1)
284 self.context.scale(200,200)
289 class PedometerHomePlugin(hildondesktop.HomePluginItem):
292 #labels for current steps
293 labels = ["timer", "count", "dist", "avgSpeed"]
294 #labelsC = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
296 #labels for all time steps
297 #labelsT = { "timer" : None, "count" : None, "dist" : None, "avgSpeed" : None }
316 hildondesktop.HomePluginItem.__init__(self)
318 self.client = gconf.client_get_default()
320 self.totalCounter = self.client.get_int(COUNTER)
321 self.totalTime = self.client.get_int(TIMER)
322 self.mode = self.client.get_int(MODE)
323 self.height = self.client.get_int(HEIGHT)
324 self.unit = self.client.get_int(UNIT)
325 self.aspect = self.client.get_int(ASPECT)
326 self.logging = self.client.get_bool(LOGGING)
328 self.client.set_int(COUNTER, 0)
329 self.client.set_int(TIMER, 0)
330 self.client.set_int(MODE, 0)
331 self.client.set_int(HEIGHT, 0)
332 self.client.set_int(UNIT, 0)
333 self.client.set_int(ASPECT, 0)
334 self.client.set_bool(LOGGING, False)
336 self.pedometer = PedoCounter(self.update_values)
337 self.pedometer.set_mode(self.mode)
338 self.pedometer.set_height(self.height)
340 #self.button = gtk.Button("Start")
341 self.button = CustomButton(ICONSPATH + "play.png")
342 self.button.connect("clicked", self.button_clicked)
344 self.create_labels(self.labelsC)
345 self.create_labels(self.labelsT)
347 self.update_ui_values(self.labelsC, 0, 0)
348 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
350 mainHBox = gtk.HBox(spacing=1)
352 descVBox = gtk.VBox(spacing=1)
353 descVBox.add(self.new_label_heading())
354 descVBox.add(self.new_label_heading("Time:"))
355 descVBox.add(self.new_label_heading("Steps:"))
356 descVBox.add(self.new_label_heading("Distance:"))
357 descVBox.add(self.new_label_heading("Avg Speed:"))
359 currentVBox = gtk.VBox(spacing=1)
360 currentVBox.add(self.new_label_heading("Current"))
361 currentVBox.add(self.labelsC["timer"])
362 currentVBox.add(self.labelsC["count"])
363 currentVBox.add(self.labelsC["dist"])
364 currentVBox.add(self.labelsC["avgSpeed"])
365 self.currentBox = currentVBox
367 totalVBox = gtk.VBox(spacing=1)
368 totalVBox.add(self.new_label_heading("Total"))
369 totalVBox.add(self.labelsT["timer"])
370 totalVBox.add(self.labelsT["count"])
371 totalVBox.add(self.labelsT["dist"])
372 totalVBox.add(self.labelsT["avgSpeed"])
373 self.totalBox = totalVBox
375 buttonVBox = gtk.VBox(spacing=1)
376 buttonVBox.add(self.new_label_heading(""))
377 buttonVBox.add(self.button)
378 buttonVBox.add(self.new_label_heading(""))
380 mainHBox.add(buttonVBox)
381 mainHBox.add(descVBox)
382 mainHBox.add(currentVBox)
383 mainHBox.add(totalVBox)
385 self.mainhbox = mainHBox
391 self.connect("unrealize", self.close_requested)
392 self.set_settings(True)
393 self.connect("show-settings", self.show_settings)
395 def new_label_heading(self, title=""):
397 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
400 def create_labels(self, new_labels):
401 for label in self.labels:
403 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
404 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
405 new_labels[label] = l
407 def update_aspect(self):
409 self.currentBox.show_all()
410 self.totalBox.show_all()
411 elif self.aspect == 1:
412 self.currentBox.show_all()
413 self.totalBox.hide_all()
415 self.currentBox.hide_all()
416 self.totalBox.show_all()
418 def update_ui_values(self, labels, timer, steps):
419 def get_str_distance(meters):
422 return "%.2f km" % (meters/1000)
424 return "%.2f mi" % (meters/1609.344)
427 return "%d m" % meters
429 return "%d ft" % int(meters*3.2808)
431 def get_avg_speed(timer, dist):
442 return "N/A " + suffix
443 speed = 1.0 *dist / timer
444 #convert from meters per second to km/h or mi/h
446 return "%.2f %s" % (speed, suffix)
449 hours = int(tdelta / 3600)
450 tdelta -= 3600 * hours
451 mins = int(tdelta / 60)
455 strtime = "%.2d:%.2d:%.2d" % ( hours, mins, secs)
457 labels["timer"].set_label(strtime)
458 labels["count"].set_label(str(steps))
460 dist = self.pedometer.get_distance(steps)
462 labels["dist"].set_label(get_str_distance(dist))
463 labels["avgSpeed"].set_label(get_avg_speed(timer, dist))
465 def update_current(self):
466 self.update_ui_values(self.labelsC, self.time, self.counter)
468 def update_total(self):
469 self.update_ui_values(self.labelsT, self.totalTime, self.totalCounter)
471 def show_settings(self, widget):
472 def reset_total_counter(arg):
473 widget.totalCounter = 0
475 widget.update_total()
476 hildon.hildon_banner_show_information(self,"None", "Total counter was resetted")
478 def selector_changed(selector, data):
479 widget.mode = selector.get_active(0)
480 widget.client.set_int(MODE, widget.mode)
481 widget.pedometer.set_mode(widget.mode)
482 widget.pedometer.set_height(widget.height)
483 widget.update_current()
484 widget.update_total()
486 def selectorH_changed(selector, data):
487 widget.height = selectorH.get_active(0)
488 widget.client.set_int(HEIGHT, widget.height)
489 widget.pedometer.set_height(widget.height)
490 widget.update_current()
491 widget.update_total()
494 def selectorUnit_changed(selector, data):
495 widget.unit = selectorUnit.get_active(0)
496 widget.client.set_int(UNIT, widget.unit)
497 widget.update_current()
498 widget.update_total()
500 def selectorUI_changed(selector, data):
501 widget.aspect = selectorUI.get_active(0)
502 widget.client.set_int(ASPECT, widget.aspect)
503 widget.update_aspect()
505 def logButton_changed(checkButton):
506 widget.logging = checkButton.get_active()
507 widget.client.set_bool(LOGGING, widget.logging)
509 dialog = gtk.Dialog()
510 dialog.set_title("Settings")
512 dialog.add_button("OK", gtk.RESPONSE_OK)
513 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
514 button.set_title("Reset total counter")
515 button.set_alignment(0, 0.8, 1, 1)
516 button.connect("clicked", reset_total_counter)
518 selector = hildon.TouchSelector(text=True)
519 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
520 selector.append_text("Walk")
521 selector.append_text("Run")
522 selector.connect("changed", selector_changed)
524 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
525 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
526 modePicker.set_title("Select mode")
527 modePicker.set_selector(selector)
528 modePicker.set_active(widget.mode)
530 selectorH = hildon.TouchSelector(text=True)
531 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
532 selectorH.append_text("< 1.50 m")
533 selectorH.append_text("1.50 - 1.65 m")
534 selectorH.append_text("1.66 - 1.80 m")
535 selectorH.append_text("1.81 - 1.95 m")
536 selectorH.append_text(" > 1.95 m")
537 selectorH.connect("changed", selectorH_changed)
539 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
540 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
541 heightPicker.set_title("Select height")
542 heightPicker.set_selector(selectorH)
543 heightPicker.set_active(widget.height)
545 selectorUnit = hildon.TouchSelector(text=True)
546 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
547 selectorUnit.append_text("Metric (km)")
548 selectorUnit.append_text("English (mi)")
549 selectorUnit.connect("changed", selectorUnit_changed)
551 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
552 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
553 unitPicker.set_title("Units")
554 unitPicker.set_selector(selectorUnit)
555 unitPicker.set_active(widget.unit)
557 selectorUI = hildon.TouchSelector(text=True)
558 selectorUI = hildon.TouchSelector(text=True)
559 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
560 selectorUI.append_text("Show current + total")
561 selectorUI.append_text("Show only current")
562 selectorUI.append_text("Show only total")
563 selectorUI.connect("changed", selectorUI_changed)
565 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
566 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
567 UIPicker.set_title("Widget aspect")
568 UIPicker.set_selector(selectorUI)
569 UIPicker.set_active(widget.aspect)
571 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
572 logButton.set_label("Log data")
573 logButton.set_active(widget.logging)
574 logButton.connect("toggled", logButton_changed)
576 pan_area = hildon.PannableArea()
580 vbox.add(heightPicker)
585 pan_area.add_with_viewport(vbox)
586 pan_area.set_size_request(-1, 300)
588 dialog.vbox.add(pan_area)
590 response = dialog.run()
591 #hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
594 def close_requested(self, widget):
595 if self.pedometer is None:
598 self.pedometer.request_stop()
600 def update_values(self, totalCurent, lastInterval):
601 self.totalCounter += lastInterval
602 self.counter = totalCurent
604 tdelta = time.time() - self.time - self.startTime
606 self.totalTime += tdelta
608 self.update_current()
611 def button_clicked(self, button):
612 if self.pedometer is not None and self.pedometer.isRunning:
614 self.pedometer.request_stop()
615 self.client.set_int(COUNTER, self.totalCounter)
616 self.client.set_int(TIMER, int(self.totalTime))
617 #self.button.set_label("Start")
618 self.button.set_icon(ICONSPATH + "play.png")
620 self.pedometer = PedoCounter(self.update_values)
621 self.pedometer.set_mode(self.mode)
622 self.pedometer.set_height(self.height)
623 self.pedometer.set_logging(self.logging)
628 self.update_current()
630 self.pedometer.start()
631 self.startTime = time.time()
632 #self.button.set_label("Stop")
633 self.button.set_icon(ICONSPATH + "stop.png")
635 def do_expose_event(self, event):
636 cr = self.window.cairo_create()
637 cr.region(event.window.get_clip_region())
639 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
640 style = self.rc_get_style()
641 color = style.lookup_color("DefaultBackgroundColor")
642 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.75);
645 width = self.allocation.width
646 height = self.allocation.height
648 x = self.allocation.x
649 y = self.allocation.y
651 cr.move_to(x+radius, y)
652 cr.line_to(x + width - radius, y)
653 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
654 cr.line_to(x + width, y + height - radius)
655 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
656 cr.line_to(x + radius, y + height)
657 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
658 cr.line_to(x, y + radius)
659 cr.curve_to(x, y + radius, x, y, x + radius, y)
661 cr.set_operator(cairo.OPERATOR_SOURCE)
664 color = style.lookup_color("ActiveTextColor")
665 cr.set_source_rgba (color.red/65535.0, color.green/65335.0, color.blue/65535.0, 0.5);
669 hildondesktop.HomePluginItem.do_expose_event(self, event)
671 def do_realize(self):
672 screen = self.get_screen()
673 self.set_colormap(screen.get_rgba_colormap())
674 self.set_app_paintable(True)
675 hildondesktop.HomePluginItem.do_realize(self)
677 hd_plugin_type = PedometerHomePlugin
679 # The code below is just for testing purposes.
680 # It allows to run the widget as a standalone process.
681 if __name__ == "__main__":
683 gobject.type_register(hd_plugin_type)
684 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
688 ############### old pedometer.py ###
692 logger = logging.getLogger("pedometer")
693 logger.setLevel(logging.INFO)
695 ch = logging.StreamHandler()
696 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
697 ch.setFormatter(formatter)
698 logger.addHandler(ch)