You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
9.5 KiB
290 lines
9.5 KiB
#!/usr/bin/env python2.6
|
|
#
|
|
# Copyright (C) 2011 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
#
|
|
# Plots debug log output from VelocityTracker.
|
|
# Enable DEBUG_VELOCITY to print the output.
|
|
#
|
|
# This code supports side-by-side comparison of two algorithms.
|
|
# The old algorithm should be modified to emit debug log messages containing
|
|
# the word "OLD".
|
|
#
|
|
|
|
import numpy as np
|
|
import matplotlib.pyplot as plot
|
|
import subprocess
|
|
import re
|
|
import fcntl
|
|
import os
|
|
import errno
|
|
import bisect
|
|
from datetime import datetime, timedelta
|
|
|
|
# Parameters.
|
|
timespan = 15 # seconds total span shown
|
|
scrolljump = 5 # seconds jump when scrolling
|
|
timeticks = 1 # seconds between each time tick
|
|
|
|
# Non-blocking stream wrapper.
|
|
class NonBlockingStream:
|
|
def __init__(self, stream):
|
|
fcntl.fcntl(stream, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
self.stream = stream
|
|
self.buffer = ''
|
|
self.pos = 0
|
|
|
|
def readline(self):
|
|
while True:
|
|
index = self.buffer.find('\n', self.pos)
|
|
if index != -1:
|
|
result = self.buffer[self.pos:index]
|
|
self.pos = index + 1
|
|
return result
|
|
|
|
self.buffer = self.buffer[self.pos:]
|
|
self.pos = 0
|
|
try:
|
|
chunk = os.read(self.stream.fileno(), 4096)
|
|
except OSError, e:
|
|
if e.errno == errno.EAGAIN:
|
|
return None
|
|
raise e
|
|
if len(chunk) == 0:
|
|
if len(self.buffer) == 0:
|
|
raise(EOFError)
|
|
else:
|
|
result = self.buffer
|
|
self.buffer = ''
|
|
self.pos = 0
|
|
return result
|
|
self.buffer += chunk
|
|
|
|
# Plotter
|
|
class Plotter:
|
|
def __init__(self, adbout):
|
|
self.adbout = adbout
|
|
|
|
self.fig = plot.figure(1)
|
|
self.fig.suptitle('Velocity Tracker', fontsize=12)
|
|
self.fig.set_dpi(96)
|
|
self.fig.set_size_inches(16, 12, forward=True)
|
|
|
|
self.velocity_x = self._make_timeseries()
|
|
self.velocity_y = self._make_timeseries()
|
|
self.velocity_magnitude = self._make_timeseries()
|
|
self.velocity_axes = self._add_timeseries_axes(
|
|
1, 'Velocity', 'px/s', [-5000, 5000],
|
|
yticks=range(-5000, 5000, 1000))
|
|
self.velocity_line_x = self._add_timeseries_line(
|
|
self.velocity_axes, 'vx', 'red')
|
|
self.velocity_line_y = self._add_timeseries_line(
|
|
self.velocity_axes, 'vy', 'green')
|
|
self.velocity_line_magnitude = self._add_timeseries_line(
|
|
self.velocity_axes, 'magnitude', 'blue')
|
|
self._add_timeseries_legend(self.velocity_axes)
|
|
|
|
shared_axis = self.velocity_axes
|
|
|
|
self.old_velocity_x = self._make_timeseries()
|
|
self.old_velocity_y = self._make_timeseries()
|
|
self.old_velocity_magnitude = self._make_timeseries()
|
|
self.old_velocity_axes = self._add_timeseries_axes(
|
|
2, 'Old Algorithm Velocity', 'px/s', [-5000, 5000],
|
|
sharex=shared_axis,
|
|
yticks=range(-5000, 5000, 1000))
|
|
self.old_velocity_line_x = self._add_timeseries_line(
|
|
self.old_velocity_axes, 'vx', 'red')
|
|
self.old_velocity_line_y = self._add_timeseries_line(
|
|
self.old_velocity_axes, 'vy', 'green')
|
|
self.old_velocity_line_magnitude = self._add_timeseries_line(
|
|
self.old_velocity_axes, 'magnitude', 'blue')
|
|
self._add_timeseries_legend(self.old_velocity_axes)
|
|
|
|
self.timer = self.fig.canvas.new_timer(interval=100)
|
|
self.timer.add_callback(lambda: self.update())
|
|
self.timer.start()
|
|
|
|
self.timebase = None
|
|
self._reset_parse_state()
|
|
|
|
# Initialize a time series.
|
|
def _make_timeseries(self):
|
|
return [[], []]
|
|
|
|
# Add a subplot to the figure for a time series.
|
|
def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
|
|
num_graphs = 2
|
|
height = 0.9 / num_graphs
|
|
top = 0.95 - height * index
|
|
axes = self.fig.add_axes([0.1, top, 0.8, height],
|
|
xscale='linear',
|
|
xlim=[0, timespan],
|
|
ylabel=ylabel,
|
|
yscale='linear',
|
|
ylim=ylim,
|
|
sharex=sharex)
|
|
axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold')
|
|
axes.set_xlabel('time (s)', fontsize=10, fontweight='bold')
|
|
axes.set_ylabel(ylabel, fontsize=10, fontweight='bold')
|
|
axes.set_xticks(range(0, timespan + 1, timeticks))
|
|
axes.set_yticks(yticks)
|
|
axes.grid(True)
|
|
|
|
for label in axes.get_xticklabels():
|
|
label.set_fontsize(9)
|
|
for label in axes.get_yticklabels():
|
|
label.set_fontsize(9)
|
|
|
|
return axes
|
|
|
|
# Add a line to the axes for a time series.
|
|
def _add_timeseries_line(self, axes, label, color, linewidth=1):
|
|
return axes.plot([], label=label, color=color, linewidth=linewidth)[0]
|
|
|
|
# Add a legend to a time series.
|
|
def _add_timeseries_legend(self, axes):
|
|
axes.legend(
|
|
loc='upper left',
|
|
bbox_to_anchor=(1.01, 1),
|
|
borderpad=0.1,
|
|
borderaxespad=0.1,
|
|
prop={'size': 10})
|
|
|
|
# Resets the parse state.
|
|
def _reset_parse_state(self):
|
|
self.parse_velocity_x = None
|
|
self.parse_velocity_y = None
|
|
self.parse_velocity_magnitude = None
|
|
self.parse_old_velocity_x = None
|
|
self.parse_old_velocity_y = None
|
|
self.parse_old_velocity_magnitude = None
|
|
|
|
# Update samples.
|
|
def update(self):
|
|
timeindex = 0
|
|
while True:
|
|
try:
|
|
line = self.adbout.readline()
|
|
except EOFError:
|
|
plot.close()
|
|
return
|
|
if line is None:
|
|
break
|
|
print line
|
|
|
|
try:
|
|
timestamp = self._parse_timestamp(line)
|
|
except ValueError, e:
|
|
continue
|
|
if self.timebase is None:
|
|
self.timebase = timestamp
|
|
delta = timestamp - self.timebase
|
|
timeindex = delta.seconds + delta.microseconds * 0.000001
|
|
|
|
if line.find(': position') != -1:
|
|
self.parse_velocity_x = self._get_following_number(line, 'vx=')
|
|
self.parse_velocity_y = self._get_following_number(line, 'vy=')
|
|
self.parse_velocity_magnitude = self._get_following_number(line, 'speed=')
|
|
self._append(self.velocity_x, timeindex, self.parse_velocity_x)
|
|
self._append(self.velocity_y, timeindex, self.parse_velocity_y)
|
|
self._append(self.velocity_magnitude, timeindex, self.parse_velocity_magnitude)
|
|
|
|
if line.find(': OLD') != -1:
|
|
self.parse_old_velocity_x = self._get_following_number(line, 'vx=')
|
|
self.parse_old_velocity_y = self._get_following_number(line, 'vy=')
|
|
self.parse_old_velocity_magnitude = self._get_following_number(line, 'speed=')
|
|
self._append(self.old_velocity_x, timeindex, self.parse_old_velocity_x)
|
|
self._append(self.old_velocity_y, timeindex, self.parse_old_velocity_y)
|
|
self._append(self.old_velocity_magnitude, timeindex, self.parse_old_velocity_magnitude)
|
|
|
|
# Scroll the plots.
|
|
if timeindex > timespan:
|
|
bottom = int(timeindex) - timespan + scrolljump
|
|
self.timebase += timedelta(seconds=bottom)
|
|
self._scroll(self.velocity_x, bottom)
|
|
self._scroll(self.velocity_y, bottom)
|
|
self._scroll(self.velocity_magnitude, bottom)
|
|
self._scroll(self.old_velocity_x, bottom)
|
|
self._scroll(self.old_velocity_y, bottom)
|
|
self._scroll(self.old_velocity_magnitude, bottom)
|
|
|
|
# Redraw the plots.
|
|
self.velocity_line_x.set_data(self.velocity_x)
|
|
self.velocity_line_y.set_data(self.velocity_y)
|
|
self.velocity_line_magnitude.set_data(self.velocity_magnitude)
|
|
self.old_velocity_line_x.set_data(self.old_velocity_x)
|
|
self.old_velocity_line_y.set_data(self.old_velocity_y)
|
|
self.old_velocity_line_magnitude.set_data(self.old_velocity_magnitude)
|
|
|
|
self.fig.canvas.draw_idle()
|
|
|
|
# Scroll a time series.
|
|
def _scroll(self, timeseries, bottom):
|
|
bottom_index = bisect.bisect_left(timeseries[0], bottom)
|
|
del timeseries[0][:bottom_index]
|
|
del timeseries[1][:bottom_index]
|
|
for i, timeindex in enumerate(timeseries[0]):
|
|
timeseries[0][i] = timeindex - bottom
|
|
|
|
# Extract a word following the specified prefix.
|
|
def _get_following_word(self, line, prefix):
|
|
prefix_index = line.find(prefix)
|
|
if prefix_index == -1:
|
|
return None
|
|
start_index = prefix_index + len(prefix)
|
|
delim_index = line.find(',', start_index)
|
|
if delim_index == -1:
|
|
return line[start_index:]
|
|
else:
|
|
return line[start_index:delim_index]
|
|
|
|
# Extract a number following the specified prefix.
|
|
def _get_following_number(self, line, prefix):
|
|
word = self._get_following_word(line, prefix)
|
|
if word is None:
|
|
return None
|
|
return float(word)
|
|
|
|
# Add a value to a time series.
|
|
def _append(self, timeseries, timeindex, number):
|
|
timeseries[0].append(timeindex)
|
|
timeseries[1].append(number)
|
|
|
|
# Parse the logcat timestamp.
|
|
# Timestamp has the form '01-21 20:42:42.930'
|
|
def _parse_timestamp(self, line):
|
|
return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f')
|
|
|
|
# Notice
|
|
print "Velocity Tracker plotting tool"
|
|
print "-----------------------------------------\n"
|
|
print "Please enable debug logging and recompile the code."
|
|
|
|
# Start adb.
|
|
print "Starting adb logcat.\n"
|
|
|
|
adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'Input:*', 'VelocityTracker:*'],
|
|
stdout=subprocess.PIPE)
|
|
adbout = NonBlockingStream(adb.stdout)
|
|
|
|
# Prepare plotter.
|
|
plotter = Plotter(adbout)
|
|
plotter.update()
|
|
|
|
# Main loop.
|
|
plot.show()
|