Revert "Move frameworks/base/tools/ to frameworks/tools/"

This reverts commit 9f6a119c8a.
This commit is contained in:
Adam Lesinski
2014-01-23 18:17:42 -08:00
parent 7023df08f1
commit 282e181b58
291 changed files with 67426 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
This directory contains a simple python script for visualizing
the behavior of the WindowOrientationListener.
PREREQUISITES
-------------
1. Python 2.6
2. numpy
3. matplotlib
USAGE
-----
The tool works by scaping the debug log output from WindowOrientationListener
for interesting data and then plotting it.
1. Plug in the device. Ensure that it is the only device plugged in
since this script is of very little brain and will get confused otherwise.
2. Enable the Window Orientation Listener debugging data log.
adb shell setprop debug.orientation.log true
adb shell stop
adb shell start
3. Run "orientationplot.py".
WHAT IT ALL MEANS
-----------------
The tool displays several time series graphs that plot the output of the
WindowOrientationListener. Here you can see the raw accelerometer data,
filtered accelerometer data, measured tilt and orientation angle, confidence
intervals for the proposed orientation and accelerometer latency.
Things to look for:
1. Ensure the filtering is not too aggressive. If the filter cut-off frequency is
less than about 1Hz, then the filtered accelorometer data becomes too smooth
and the latency for orientation detection goes up. One way to observe this
is by holding the device vertically in one orientation then sharply turning
it 90 degrees to a different orientation. Compared the rapid changes in the
raw accelerometer data with the smoothed out filtered data. If the filtering
is too aggressive, the filter response may lag by hundreds of milliseconds.
2. Ensure that there is an appropriate gap between adjacent orientation angles
for hysteresis. Try holding the device in one orientation and slowly turning
it 90 degrees. Note that the confidence intervals will all drop to 0 at some
point in between the two orientations; that is the gap. The gap should be
observed between all adjacent pairs of orientations when turning the device
in either direction.
Next try holding the device in one orientation and rapidly turning it end
over end to a midpoint about 45 degrees between two opposing orientations.
There should be no gap observed initially. The algorithm should pick one
of the orientations and settle into it (since it is obviously quite
different from the original orientation of the device). However, once it
settles, the confidence values should start trending to 0 again because
the measured orientation angle is now within the gap between the new
orientation and the adjacent orientation.
In other words, the hysteresis gap applies only when the measured orientation
angle (say, 45 degrees) is between the current orientation's ideal angle
(say, 0 degrees) and an adjacent orientation's ideal angle (say, 90 degrees).
3. Accelerometer jitter. The accelerometer latency graph displays the interval
between sensor events as reported by the SensorEvent.timestamp field. It
should be a fairly constant 60ms. If the latency jumps around wildly or
greatly exceeds 60ms then there is a problem with the accelerometer or the
sensor manager.
4. The orientation angle is not measured when the tilt is too close to 90 or -90
degrees (refer to MAX_TILT constant). Consequently, you should expect there
to be no data. Likewise, all dependent calculations are suppressed in this case
so there will be no orientation proposal either.
5. Each orientation has its own bound on allowable tilt angles. It's a good idea to
verify that these limits are being enforced by gradually varying the tilt of
the device until it is inside/outside the limit for each orientation.
6. Orientation changes should be significantly harder when the device is held
overhead. People reading on tablets in bed often have their head turned
a little to the side, or they hold the device loosely so its orientation
can be a bit unusual. The tilt is a good indicator of whether the device is
overhead.

View File

@@ -0,0 +1,457 @@
#!/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 WindowOrientationListener.
# See README.txt for details.
#
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('Window Orientation Listener', fontsize=12)
self.fig.set_dpi(96)
self.fig.set_size_inches(16, 12, forward=True)
self.raw_acceleration_x = self._make_timeseries()
self.raw_acceleration_y = self._make_timeseries()
self.raw_acceleration_z = self._make_timeseries()
self.raw_acceleration_magnitude = self._make_timeseries()
self.raw_acceleration_axes = self._add_timeseries_axes(
1, 'Raw Acceleration', 'm/s^2', [-20, 20],
yticks=range(-15, 16, 5))
self.raw_acceleration_line_x = self._add_timeseries_line(
self.raw_acceleration_axes, 'x', 'red')
self.raw_acceleration_line_y = self._add_timeseries_line(
self.raw_acceleration_axes, 'y', 'green')
self.raw_acceleration_line_z = self._add_timeseries_line(
self.raw_acceleration_axes, 'z', 'blue')
self.raw_acceleration_line_magnitude = self._add_timeseries_line(
self.raw_acceleration_axes, 'magnitude', 'orange', linewidth=2)
self._add_timeseries_legend(self.raw_acceleration_axes)
shared_axis = self.raw_acceleration_axes
self.filtered_acceleration_x = self._make_timeseries()
self.filtered_acceleration_y = self._make_timeseries()
self.filtered_acceleration_z = self._make_timeseries()
self.filtered_acceleration_magnitude = self._make_timeseries()
self.filtered_acceleration_axes = self._add_timeseries_axes(
2, 'Filtered Acceleration', 'm/s^2', [-20, 20],
sharex=shared_axis,
yticks=range(-15, 16, 5))
self.filtered_acceleration_line_x = self._add_timeseries_line(
self.filtered_acceleration_axes, 'x', 'red')
self.filtered_acceleration_line_y = self._add_timeseries_line(
self.filtered_acceleration_axes, 'y', 'green')
self.filtered_acceleration_line_z = self._add_timeseries_line(
self.filtered_acceleration_axes, 'z', 'blue')
self.filtered_acceleration_line_magnitude = self._add_timeseries_line(
self.filtered_acceleration_axes, 'magnitude', 'orange', linewidth=2)
self._add_timeseries_legend(self.filtered_acceleration_axes)
self.tilt_angle = self._make_timeseries()
self.tilt_angle_axes = self._add_timeseries_axes(
3, 'Tilt Angle', 'degrees', [-105, 105],
sharex=shared_axis,
yticks=range(-90, 91, 30))
self.tilt_angle_line = self._add_timeseries_line(
self.tilt_angle_axes, 'tilt', 'black')
self._add_timeseries_legend(self.tilt_angle_axes)
self.orientation_angle = self._make_timeseries()
self.orientation_angle_axes = self._add_timeseries_axes(
4, 'Orientation Angle', 'degrees', [-25, 375],
sharex=shared_axis,
yticks=range(0, 361, 45))
self.orientation_angle_line = self._add_timeseries_line(
self.orientation_angle_axes, 'orientation', 'black')
self._add_timeseries_legend(self.orientation_angle_axes)
self.current_rotation = self._make_timeseries()
self.proposed_rotation = self._make_timeseries()
self.predicted_rotation = self._make_timeseries()
self.orientation_axes = self._add_timeseries_axes(
5, 'Current / Proposed Orientation', 'rotation', [-1, 4],
sharex=shared_axis,
yticks=range(0, 4))
self.current_rotation_line = self._add_timeseries_line(
self.orientation_axes, 'current', 'black', linewidth=2)
self.predicted_rotation_line = self._add_timeseries_line(
self.orientation_axes, 'predicted', 'purple', linewidth=3)
self.proposed_rotation_line = self._add_timeseries_line(
self.orientation_axes, 'proposed', 'green', linewidth=3)
self._add_timeseries_legend(self.orientation_axes)
self.time_until_settled = self._make_timeseries()
self.time_until_flat_delay_expired = self._make_timeseries()
self.time_until_swing_delay_expired = self._make_timeseries()
self.time_until_acceleration_delay_expired = self._make_timeseries()
self.stability_axes = self._add_timeseries_axes(
6, 'Proposal Stability', 'ms', [-10, 600],
sharex=shared_axis,
yticks=range(0, 600, 100))
self.time_until_settled_line = self._add_timeseries_line(
self.stability_axes, 'time until settled', 'black', linewidth=2)
self.time_until_flat_delay_expired_line = self._add_timeseries_line(
self.stability_axes, 'time until flat delay expired', 'green')
self.time_until_swing_delay_expired_line = self._add_timeseries_line(
self.stability_axes, 'time until swing delay expired', 'blue')
self.time_until_acceleration_delay_expired_line = self._add_timeseries_line(
self.stability_axes, 'time until acceleration delay expired', 'red')
self._add_timeseries_legend(self.stability_axes)
self.sample_latency = self._make_timeseries()
self.sample_latency_axes = self._add_timeseries_axes(
7, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
sharex=shared_axis,
yticks=range(0, 500, 100))
self.sample_latency_line = self._add_timeseries_line(
self.sample_latency_axes, 'latency', 'black')
self._add_timeseries_legend(self.sample_latency_axes)
self.fig.canvas.mpl_connect('button_press_event', self._on_click)
self.paused = False
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()
# Handle a click event to pause or restart the timer.
def _on_click(self, ev):
if not self.paused:
self.paused = True
self.timer.stop()
else:
self.paused = False
self.timer.start()
# 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 = 7
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_raw_acceleration_x = None
self.parse_raw_acceleration_y = None
self.parse_raw_acceleration_z = None
self.parse_raw_acceleration_magnitude = None
self.parse_filtered_acceleration_x = None
self.parse_filtered_acceleration_y = None
self.parse_filtered_acceleration_z = None
self.parse_filtered_acceleration_magnitude = None
self.parse_tilt_angle = None
self.parse_orientation_angle = None
self.parse_current_rotation = None
self.parse_proposed_rotation = None
self.parse_predicted_rotation = None
self.parse_time_until_settled = None
self.parse_time_until_flat_delay_expired = None
self.parse_time_until_swing_delay_expired = None
self.parse_time_until_acceleration_delay_expired = None
self.parse_sample_latency = 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('Raw acceleration vector:') != -1:
self.parse_raw_acceleration_x = self._get_following_number(line, 'x=')
self.parse_raw_acceleration_y = self._get_following_number(line, 'y=')
self.parse_raw_acceleration_z = self._get_following_number(line, 'z=')
self.parse_raw_acceleration_magnitude = self._get_following_number(line, 'magnitude=')
if line.find('Filtered acceleration vector:') != -1:
self.parse_filtered_acceleration_x = self._get_following_number(line, 'x=')
self.parse_filtered_acceleration_y = self._get_following_number(line, 'y=')
self.parse_filtered_acceleration_z = self._get_following_number(line, 'z=')
self.parse_filtered_acceleration_magnitude = self._get_following_number(line, 'magnitude=')
if line.find('tiltAngle=') != -1:
self.parse_tilt_angle = self._get_following_number(line, 'tiltAngle=')
if line.find('orientationAngle=') != -1:
self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=')
if line.find('Result:') != -1:
self.parse_current_rotation = self._get_following_number(line, 'currentRotation=')
self.parse_proposed_rotation = self._get_following_number(line, 'proposedRotation=')
self.parse_predicted_rotation = self._get_following_number(line, 'predictedRotation=')
self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=')
self.parse_time_until_settled = self._get_following_number(line, 'timeUntilSettledMS=')
self.parse_time_until_flat_delay_expired = self._get_following_number(line, 'timeUntilFlatDelayExpiredMS=')
self.parse_time_until_swing_delay_expired = self._get_following_number(line, 'timeUntilSwingDelayExpiredMS=')
self.parse_time_until_acceleration_delay_expired = self._get_following_number(line, 'timeUntilAccelerationDelayExpiredMS=')
self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z)
self._append(self.raw_acceleration_magnitude, timeindex, self.parse_raw_acceleration_magnitude)
self._append(self.filtered_acceleration_x, timeindex, self.parse_filtered_acceleration_x)
self._append(self.filtered_acceleration_y, timeindex, self.parse_filtered_acceleration_y)
self._append(self.filtered_acceleration_z, timeindex, self.parse_filtered_acceleration_z)
self._append(self.filtered_acceleration_magnitude, timeindex, self.parse_filtered_acceleration_magnitude)
self._append(self.tilt_angle, timeindex, self.parse_tilt_angle)
self._append(self.orientation_angle, timeindex, self.parse_orientation_angle)
self._append(self.current_rotation, timeindex, self.parse_current_rotation)
if self.parse_proposed_rotation >= 0:
self._append(self.proposed_rotation, timeindex, self.parse_proposed_rotation)
else:
self._append(self.proposed_rotation, timeindex, None)
if self.parse_predicted_rotation >= 0:
self._append(self.predicted_rotation, timeindex, self.parse_predicted_rotation)
else:
self._append(self.predicted_rotation, timeindex, None)
self._append(self.time_until_settled, timeindex, self.parse_time_until_settled)
self._append(self.time_until_flat_delay_expired, timeindex, self.parse_time_until_flat_delay_expired)
self._append(self.time_until_swing_delay_expired, timeindex, self.parse_time_until_swing_delay_expired)
self._append(self.time_until_acceleration_delay_expired, timeindex, self.parse_time_until_acceleration_delay_expired)
self._append(self.sample_latency, timeindex, self.parse_sample_latency)
self._reset_parse_state()
# Scroll the plots.
if timeindex > timespan:
bottom = int(timeindex) - timespan + scrolljump
self.timebase += timedelta(seconds=bottom)
self._scroll(self.raw_acceleration_x, bottom)
self._scroll(self.raw_acceleration_y, bottom)
self._scroll(self.raw_acceleration_z, bottom)
self._scroll(self.raw_acceleration_magnitude, bottom)
self._scroll(self.filtered_acceleration_x, bottom)
self._scroll(self.filtered_acceleration_y, bottom)
self._scroll(self.filtered_acceleration_z, bottom)
self._scroll(self.filtered_acceleration_magnitude, bottom)
self._scroll(self.tilt_angle, bottom)
self._scroll(self.orientation_angle, bottom)
self._scroll(self.current_rotation, bottom)
self._scroll(self.proposed_rotation, bottom)
self._scroll(self.predicted_rotation, bottom)
self._scroll(self.time_until_settled, bottom)
self._scroll(self.time_until_flat_delay_expired, bottom)
self._scroll(self.time_until_swing_delay_expired, bottom)
self._scroll(self.time_until_acceleration_delay_expired, bottom)
self._scroll(self.sample_latency, bottom)
# Redraw the plots.
self.raw_acceleration_line_x.set_data(self.raw_acceleration_x)
self.raw_acceleration_line_y.set_data(self.raw_acceleration_y)
self.raw_acceleration_line_z.set_data(self.raw_acceleration_z)
self.raw_acceleration_line_magnitude.set_data(self.raw_acceleration_magnitude)
self.filtered_acceleration_line_x.set_data(self.filtered_acceleration_x)
self.filtered_acceleration_line_y.set_data(self.filtered_acceleration_y)
self.filtered_acceleration_line_z.set_data(self.filtered_acceleration_z)
self.filtered_acceleration_line_magnitude.set_data(self.filtered_acceleration_magnitude)
self.tilt_angle_line.set_data(self.tilt_angle)
self.orientation_angle_line.set_data(self.orientation_angle)
self.current_rotation_line.set_data(self.current_rotation)
self.proposed_rotation_line.set_data(self.proposed_rotation)
self.predicted_rotation_line.set_data(self.predicted_rotation)
self.time_until_settled_line.set_data(self.time_until_settled)
self.time_until_flat_delay_expired_line.set_data(self.time_until_flat_delay_expired)
self.time_until_swing_delay_expired_line.set_data(self.time_until_swing_delay_expired)
self.time_until_acceleration_delay_expired_line.set_data(self.time_until_acceleration_delay_expired)
self.sample_latency_line.set_data(self.sample_latency)
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)
# Extract an array of numbers following the specified prefix.
def _get_following_array_of_numbers(self, line, prefix):
prefix_index = line.find(prefix + '[')
if prefix_index == -1:
return None
start_index = prefix_index + len(prefix) + 1
delim_index = line.find(']', start_index)
if delim_index == -1:
return None
result = []
while start_index < delim_index:
comma_index = line.find(', ', start_index, delim_index)
if comma_index == -1:
result.append(float(line[start_index:delim_index]))
break;
result.append(float(line[start_index:comma_index]))
start_index = comma_index + 2
return result
# 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 "Window Orientation Listener plotting tool"
print "-----------------------------------------\n"
print "Please turn on the Window Orientation Listener logging in Development Settings."
# Start adb.
print "Starting adb logcat.\n"
adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'WindowOrientationListener:V'],
stdout=subprocess.PIPE)
adbout = NonBlockingStream(adb.stdout)
# Prepare plotter.
plotter = Plotter(adbout)
plotter.update()
# Main loop.
plot.show()