1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#!/usr/bin/env python2.6
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Copyright (C) 2011 The Android Open Source Project
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Licensed under the Apache License, Version 2.0 (the "License");
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# you may not use this file except in compliance with the License.
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# You may obtain a copy of the License at
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#      http://www.apache.org/licenses/LICENSE-2.0
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Unless required by applicable law or agreed to in writing, software
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# distributed under the License is distributed on an "AS IS" BASIS,
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# See the License for the specific language governing permissions and
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# limitations under the License.
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Plots debug log output from VelocityTracker.
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Enable DEBUG_VELOCITY to print the output.
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# This code supports side-by-side comparison of two algorithms.
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# The old algorithm should be modified to emit debug log messages containing
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# the word "OLD".
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport numpy as np
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport matplotlib.pyplot as plot
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport subprocess
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport re
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport fcntl
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport os
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport errno
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport bisect
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskifrom datetime import datetime, timedelta
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Parameters.
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskitimespan = 15 # seconds total span shown
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiscrolljump = 5 # seconds jump when scrolling
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskitimeticks = 1 # seconds between each time tick
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Non-blocking stream wrapper.
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiclass NonBlockingStream:
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def __init__(self, stream):
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fcntl.fcntl(stream, fcntl.F_SETFL, os.O_NONBLOCK)
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.stream = stream
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.buffer = ''
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.pos = 0
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def readline(self):
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    while True:
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      index = self.buffer.find('\n', self.pos)
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if index != -1:
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = self.buffer[self.pos:index]
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.pos = index + 1
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return result
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self.buffer = self.buffer[self.pos:]
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self.pos = 0
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      try:
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        chunk = os.read(self.stream.fileno(), 4096)
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      except OSError, e:
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if e.errno == errno.EAGAIN:
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          return None
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        raise e
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if len(chunk) == 0:
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if len(self.buffer) == 0:
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          raise(EOFError)
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        else:
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          result = self.buffer
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          self.buffer = ''
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          self.pos = 0
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski          return result
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self.buffer += chunk
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Plotter
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiclass Plotter:
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def __init__(self, adbout):
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.adbout = adbout
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.fig = plot.figure(1)
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.fig.suptitle('Velocity Tracker', fontsize=12)
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.fig.set_dpi(96)
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.fig.set_size_inches(16, 12, forward=True)
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_x = self._make_timeseries()
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_y = self._make_timeseries()
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_magnitude = self._make_timeseries()
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_axes = self._add_timeseries_axes(
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        1, 'Velocity', 'px/s', [-5000, 5000],
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        yticks=range(-5000, 5000, 1000))
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_x = self._add_timeseries_line(
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.velocity_axes, 'vx', 'red')
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_y = self._add_timeseries_line(
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.velocity_axes, 'vy', 'green')
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_magnitude = self._add_timeseries_line(
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.velocity_axes, 'magnitude', 'blue')
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self._add_timeseries_legend(self.velocity_axes)
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    shared_axis = self.velocity_axes
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_x = self._make_timeseries()
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_y = self._make_timeseries()
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_magnitude = self._make_timeseries()
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_axes = self._add_timeseries_axes(
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        2, 'Old Algorithm Velocity', 'px/s', [-5000, 5000],
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        sharex=shared_axis,
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        yticks=range(-5000, 5000, 1000))
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_x = self._add_timeseries_line(
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.old_velocity_axes, 'vx', 'red')
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_y = self._add_timeseries_line(
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.old_velocity_axes, 'vy', 'green')
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_magnitude = self._add_timeseries_line(
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.old_velocity_axes, 'magnitude', 'blue')
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self._add_timeseries_legend(self.old_velocity_axes)
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.timer = self.fig.canvas.new_timer(interval=100)
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.timer.add_callback(lambda: self.update())
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.timer.start()
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.timebase = None
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self._reset_parse_state()
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Initialize a time series.
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _make_timeseries(self):
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return [[], []]
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Add a subplot to the figure for a time series.
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    num_graphs = 2
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    height = 0.9 / num_graphs
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    top = 0.95 - height * index
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes = self.fig.add_axes([0.1, top, 0.8, height],
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        xscale='linear',
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        xlim=[0, timespan],
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        ylabel=ylabel,
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        yscale='linear',
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        ylim=ylim,
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        sharex=sharex)
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold')
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.set_xlabel('time (s)', fontsize=10, fontweight='bold')
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.set_ylabel(ylabel, fontsize=10, fontweight='bold')
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.set_xticks(range(0, timespan + 1, timeticks))
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.set_yticks(yticks)
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.grid(True)
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for label in axes.get_xticklabels():
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      label.set_fontsize(9)
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for label in axes.get_yticklabels():
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      label.set_fontsize(9)
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return axes
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Add a line to the axes for a time series.
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _add_timeseries_line(self, axes, label, color, linewidth=1):
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return axes.plot([], label=label, color=color, linewidth=linewidth)[0]
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Add a legend to a time series.
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _add_timeseries_legend(self, axes):
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    axes.legend(
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        loc='upper left',
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        bbox_to_anchor=(1.01, 1),
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        borderpad=0.1,
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        borderaxespad=0.1,
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        prop={'size': 10})
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Resets the parse state.
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _reset_parse_state(self):
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_velocity_x = None
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_velocity_y = None
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_velocity_magnitude = None
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_old_velocity_x = None
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_old_velocity_y = None
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.parse_old_velocity_magnitude = None
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Update samples.
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def update(self):
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    timeindex = 0
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    while True:
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      try:
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        line = self.adbout.readline()
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      except EOFError:
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        plot.close()
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if line is None:
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        break
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      print line
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      try:
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        timestamp = self._parse_timestamp(line)
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      except ValueError, e:
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        continue
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if self.timebase is None:
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.timebase = timestamp
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      delta = timestamp - self.timebase
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      timeindex = delta.seconds + delta.microseconds * 0.000001
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if line.find(': position') != -1:
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_velocity_x = self._get_following_number(line, 'vx=')
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_velocity_y = self._get_following_number(line, 'vy=')
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_velocity_magnitude = self._get_following_number(line, 'speed=')
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.velocity_x, timeindex, self.parse_velocity_x)
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.velocity_y, timeindex, self.parse_velocity_y)
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.velocity_magnitude, timeindex, self.parse_velocity_magnitude)
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      if line.find(': OLD') != -1:
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_old_velocity_x = self._get_following_number(line, 'vx=')
208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_old_velocity_y = self._get_following_number(line, 'vy=')
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self.parse_old_velocity_magnitude = self._get_following_number(line, 'speed=')
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.old_velocity_x, timeindex, self.parse_old_velocity_x)
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.old_velocity_y, timeindex, self.parse_old_velocity_y)
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        self._append(self.old_velocity_magnitude, timeindex, self.parse_old_velocity_magnitude)
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    # Scroll the plots.
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if timeindex > timespan:
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      bottom = int(timeindex) - timespan + scrolljump
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self.timebase += timedelta(seconds=bottom)
218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.velocity_x, bottom)
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.velocity_y, bottom)
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.velocity_magnitude, bottom)
221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.old_velocity_x, bottom)
222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.old_velocity_y, bottom)
223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      self._scroll(self.old_velocity_magnitude, bottom)
224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    # Redraw the plots.
226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_x.set_data(self.velocity_x)
227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_y.set_data(self.velocity_y)
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.velocity_line_magnitude.set_data(self.velocity_magnitude)
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_x.set_data(self.old_velocity_x)
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_y.set_data(self.old_velocity_y)
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.old_velocity_line_magnitude.set_data(self.old_velocity_magnitude)
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    self.fig.canvas.draw_idle()
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Scroll a time series.
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _scroll(self, timeseries, bottom):
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bottom_index = bisect.bisect_left(timeseries[0], bottom)
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    del timeseries[0][:bottom_index]
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    del timeseries[1][:bottom_index]
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for i, timeindex in enumerate(timeseries[0]):
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      timeseries[0][i] = timeindex - bottom
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Extract a word following the specified prefix.
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _get_following_word(self, line, prefix):
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    prefix_index = line.find(prefix)
246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if prefix_index == -1:
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      return None
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    start_index = prefix_index + len(prefix)
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    delim_index = line.find(',', start_index)
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if delim_index == -1:
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      return line[start_index:]
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    else:
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      return line[start_index:delim_index]
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Extract a number following the specified prefix.
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _get_following_number(self, line, prefix):
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    word = self._get_following_word(line, prefix)
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if word is None:
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski      return None
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return float(word)
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Add a value to a time series.
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _append(self, timeseries, timeindex, number):
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    timeseries[0].append(timeindex)
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    timeseries[1].append(number)
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Parse the logcat timestamp.
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  # Timestamp has the form '01-21 20:42:42.930'
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski  def _parse_timestamp(self, line):
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f')
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Notice
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiprint "Velocity Tracker plotting tool"
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiprint "-----------------------------------------\n"
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiprint "Please enable debug logging and recompile the code."
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Start adb.
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiprint "Starting adb logcat.\n"
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiadb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'Input:*', 'VelocityTracker:*'],
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    stdout=subprocess.PIPE)
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiadbout = NonBlockingStream(adb.stdout)
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Prepare plotter.
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiplotter = Plotter(adbout)
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiplotter.update()
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski# Main loop.
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiplot.show()
290