blob: f4e6b453c9dffbd69452f89a749318840c2e43ae [file] [log] [blame]
Jeff Brown4519f072011-01-23 13:16:01 -08001#!/usr/bin/env python2.6
2#
3# Copyright (C) 2011 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18#
19# Plots debug log output from WindowOrientationListener.
20# See README.txt for details.
21#
22
23import numpy as np
24import matplotlib.pyplot as plot
25import subprocess
26import re
27import fcntl
28import os
29import errno
30import bisect
31from datetime import datetime, timedelta
32
33# Parameters.
34timespan = 15 # seconds total span shown
35scrolljump = 5 # seconds jump when scrolling
36timeticks = 1 # seconds between each time tick
37
38# Non-blocking stream wrapper.
39class NonBlockingStream:
40 def __init__(self, stream):
41 fcntl.fcntl(stream, fcntl.F_SETFL, os.O_NONBLOCK)
42 self.stream = stream
43 self.buffer = ''
44 self.pos = 0
45
46 def readline(self):
47 while True:
48 index = self.buffer.find('\n', self.pos)
49 if index != -1:
50 result = self.buffer[self.pos:index]
51 self.pos = index + 1
52 return result
53
54 self.buffer = self.buffer[self.pos:]
55 self.pos = 0
56 try:
57 chunk = os.read(self.stream.fileno(), 4096)
58 except OSError, e:
59 if e.errno == errno.EAGAIN:
60 return None
61 raise e
62 if len(chunk) == 0:
63 if len(self.buffer) == 0:
64 raise(EOFError)
65 else:
66 result = self.buffer
67 self.buffer = ''
68 self.pos = 0
69 return result
70 self.buffer += chunk
71
72# Plotter
73class Plotter:
74 def __init__(self, adbout):
75 self.adbout = adbout
76
77 self.fig = plot.figure(1)
78 self.fig.suptitle('Window Orientation Listener', fontsize=12)
79 self.fig.set_dpi(96)
80 self.fig.set_size_inches(16, 12, forward=True)
81
82 self.raw_acceleration_x = self._make_timeseries()
83 self.raw_acceleration_y = self._make_timeseries()
84 self.raw_acceleration_z = self._make_timeseries()
Jeff Brown5aa73ae2012-01-13 15:29:21 -080085 self.raw_acceleration_magnitude = self._make_timeseries()
Jeff Brown4519f072011-01-23 13:16:01 -080086 self.raw_acceleration_axes = self._add_timeseries_axes(
87 1, 'Raw Acceleration', 'm/s^2', [-20, 20],
88 yticks=range(-15, 16, 5))
89 self.raw_acceleration_line_x = self._add_timeseries_line(
90 self.raw_acceleration_axes, 'x', 'red')
91 self.raw_acceleration_line_y = self._add_timeseries_line(
92 self.raw_acceleration_axes, 'y', 'green')
93 self.raw_acceleration_line_z = self._add_timeseries_line(
94 self.raw_acceleration_axes, 'z', 'blue')
Jeff Brown5aa73ae2012-01-13 15:29:21 -080095 self.raw_acceleration_line_magnitude = self._add_timeseries_line(
96 self.raw_acceleration_axes, 'magnitude', 'orange', linewidth=2)
Jeff Brown4519f072011-01-23 13:16:01 -080097 self._add_timeseries_legend(self.raw_acceleration_axes)
98
99 shared_axis = self.raw_acceleration_axes
100
101 self.filtered_acceleration_x = self._make_timeseries()
102 self.filtered_acceleration_y = self._make_timeseries()
103 self.filtered_acceleration_z = self._make_timeseries()
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800104 self.filtered_acceleration_magnitude = self._make_timeseries()
Jeff Brown4519f072011-01-23 13:16:01 -0800105 self.filtered_acceleration_axes = self._add_timeseries_axes(
106 2, 'Filtered Acceleration', 'm/s^2', [-20, 20],
107 sharex=shared_axis,
108 yticks=range(-15, 16, 5))
109 self.filtered_acceleration_line_x = self._add_timeseries_line(
110 self.filtered_acceleration_axes, 'x', 'red')
111 self.filtered_acceleration_line_y = self._add_timeseries_line(
112 self.filtered_acceleration_axes, 'y', 'green')
113 self.filtered_acceleration_line_z = self._add_timeseries_line(
114 self.filtered_acceleration_axes, 'z', 'blue')
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800115 self.filtered_acceleration_line_magnitude = self._add_timeseries_line(
Jeff Brown4519f072011-01-23 13:16:01 -0800116 self.filtered_acceleration_axes, 'magnitude', 'orange', linewidth=2)
117 self._add_timeseries_legend(self.filtered_acceleration_axes)
118
119 self.tilt_angle = self._make_timeseries()
120 self.tilt_angle_axes = self._add_timeseries_axes(
121 3, 'Tilt Angle', 'degrees', [-105, 105],
122 sharex=shared_axis,
123 yticks=range(-90, 91, 30))
124 self.tilt_angle_line = self._add_timeseries_line(
125 self.tilt_angle_axes, 'tilt', 'black')
126 self._add_timeseries_legend(self.tilt_angle_axes)
127
128 self.orientation_angle = self._make_timeseries()
129 self.orientation_angle_axes = self._add_timeseries_axes(
130 4, 'Orientation Angle', 'degrees', [-25, 375],
131 sharex=shared_axis,
132 yticks=range(0, 361, 45))
133 self.orientation_angle_line = self._add_timeseries_line(
134 self.orientation_angle_axes, 'orientation', 'black')
135 self._add_timeseries_legend(self.orientation_angle_axes)
136
Jeff Brownc0347aa2011-09-23 17:26:09 -0700137 self.current_rotation = self._make_timeseries()
138 self.proposed_rotation = self._make_timeseries()
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800139 self.predicted_rotation = self._make_timeseries()
Jeff Brown4519f072011-01-23 13:16:01 -0800140 self.orientation_axes = self._add_timeseries_axes(
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800141 5, 'Current / Proposed Orientation', 'rotation', [-1, 4],
Jeff Brown4519f072011-01-23 13:16:01 -0800142 sharex=shared_axis,
143 yticks=range(0, 4))
Jeff Brownc0347aa2011-09-23 17:26:09 -0700144 self.current_rotation_line = self._add_timeseries_line(
145 self.orientation_axes, 'current', 'black', linewidth=2)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800146 self.predicted_rotation_line = self._add_timeseries_line(
147 self.orientation_axes, 'predicted', 'purple', linewidth=3)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700148 self.proposed_rotation_line = self._add_timeseries_line(
149 self.orientation_axes, 'proposed', 'green', linewidth=3)
Jeff Brown4519f072011-01-23 13:16:01 -0800150 self._add_timeseries_legend(self.orientation_axes)
151
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800152 self.time_until_settled = self._make_timeseries()
153 self.time_until_flat_delay_expired = self._make_timeseries()
154 self.time_until_swing_delay_expired = self._make_timeseries()
155 self.stability_axes = self._add_timeseries_axes(
156 6, 'Proposal Stability', 'ms', [-10, 600],
157 sharex=shared_axis,
158 yticks=range(0, 600, 100))
159 self.time_until_settled_line = self._add_timeseries_line(
160 self.stability_axes, 'time until settled', 'black', linewidth=2)
161 self.time_until_flat_delay_expired_line = self._add_timeseries_line(
162 self.stability_axes, 'time until flat delay expired', 'green')
163 self.time_until_swing_delay_expired_line = self._add_timeseries_line(
164 self.stability_axes, 'time until swing delay expired', 'blue')
165 self._add_timeseries_legend(self.stability_axes)
Jeff Brown4519f072011-01-23 13:16:01 -0800166
167 self.sample_latency = self._make_timeseries()
168 self.sample_latency_axes = self._add_timeseries_axes(
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800169 7, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
Jeff Brown4519f072011-01-23 13:16:01 -0800170 sharex=shared_axis,
171 yticks=range(0, 500, 100))
172 self.sample_latency_line = self._add_timeseries_line(
173 self.sample_latency_axes, 'latency', 'black')
174 self._add_timeseries_legend(self.sample_latency_axes)
175
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800176 self.fig.canvas.mpl_connect('button_press_event', self._on_click)
177 self.paused = False
178
Jeff Brown4519f072011-01-23 13:16:01 -0800179 self.timer = self.fig.canvas.new_timer(interval=100)
180 self.timer.add_callback(lambda: self.update())
181 self.timer.start()
182
183 self.timebase = None
184 self._reset_parse_state()
185
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800186 # Handle a click event to pause or restart the timer.
187 def _on_click(self, ev):
188 if not self.paused:
189 self.paused = True
190 self.timer.stop()
191 else:
192 self.paused = False
193 self.timer.start()
194
Jeff Brown4519f072011-01-23 13:16:01 -0800195 # Initialize a time series.
196 def _make_timeseries(self):
197 return [[], []]
198
199 # Add a subplot to the figure for a time series.
200 def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800201 num_graphs = 7
Jeff Brown4519f072011-01-23 13:16:01 -0800202 height = 0.9 / num_graphs
203 top = 0.95 - height * index
204 axes = self.fig.add_axes([0.1, top, 0.8, height],
205 xscale='linear',
206 xlim=[0, timespan],
207 ylabel=ylabel,
208 yscale='linear',
209 ylim=ylim,
210 sharex=sharex)
211 axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold')
212 axes.set_xlabel('time (s)', fontsize=10, fontweight='bold')
213 axes.set_ylabel(ylabel, fontsize=10, fontweight='bold')
214 axes.set_xticks(range(0, timespan + 1, timeticks))
215 axes.set_yticks(yticks)
216 axes.grid(True)
217
218 for label in axes.get_xticklabels():
219 label.set_fontsize(9)
220 for label in axes.get_yticklabels():
221 label.set_fontsize(9)
222
223 return axes
224
225 # Add a line to the axes for a time series.
226 def _add_timeseries_line(self, axes, label, color, linewidth=1):
227 return axes.plot([], label=label, color=color, linewidth=linewidth)[0]
228
229 # Add a legend to a time series.
230 def _add_timeseries_legend(self, axes):
231 axes.legend(
232 loc='upper left',
233 bbox_to_anchor=(1.01, 1),
234 borderpad=0.1,
235 borderaxespad=0.1,
236 prop={'size': 10})
237
238 # Resets the parse state.
239 def _reset_parse_state(self):
240 self.parse_raw_acceleration_x = None
241 self.parse_raw_acceleration_y = None
242 self.parse_raw_acceleration_z = None
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800243 self.parse_raw_acceleration_magnitude = None
Jeff Brown4519f072011-01-23 13:16:01 -0800244 self.parse_filtered_acceleration_x = None
245 self.parse_filtered_acceleration_y = None
246 self.parse_filtered_acceleration_z = None
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800247 self.parse_filtered_acceleration_magnitude = None
Jeff Brown4519f072011-01-23 13:16:01 -0800248 self.parse_tilt_angle = None
249 self.parse_orientation_angle = None
Jeff Brownc0347aa2011-09-23 17:26:09 -0700250 self.parse_current_rotation = None
251 self.parse_proposed_rotation = None
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800252 self.parse_predicted_rotation = None
253 self.parse_time_until_settled = None
254 self.parse_time_until_flat_delay_expired = None
255 self.parse_time_until_swing_delay_expired = None
Jeff Brown4519f072011-01-23 13:16:01 -0800256 self.parse_sample_latency = None
257
258 # Update samples.
259 def update(self):
260 timeindex = 0
261 while True:
262 try:
263 line = self.adbout.readline()
264 except EOFError:
265 plot.close()
266 return
267 if line is None:
268 break
269 print line
270
271 try:
272 timestamp = self._parse_timestamp(line)
273 except ValueError, e:
274 continue
275 if self.timebase is None:
276 self.timebase = timestamp
277 delta = timestamp - self.timebase
278 timeindex = delta.seconds + delta.microseconds * 0.000001
279
280 if line.find('Raw acceleration vector:') != -1:
281 self.parse_raw_acceleration_x = self._get_following_number(line, 'x=')
282 self.parse_raw_acceleration_y = self._get_following_number(line, 'y=')
283 self.parse_raw_acceleration_z = self._get_following_number(line, 'z=')
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800284 self.parse_raw_acceleration_magnitude = self._get_following_number(line, 'magnitude=')
Jeff Brown4519f072011-01-23 13:16:01 -0800285
286 if line.find('Filtered acceleration vector:') != -1:
287 self.parse_filtered_acceleration_x = self._get_following_number(line, 'x=')
288 self.parse_filtered_acceleration_y = self._get_following_number(line, 'y=')
289 self.parse_filtered_acceleration_z = self._get_following_number(line, 'z=')
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800290 self.parse_filtered_acceleration_magnitude = self._get_following_number(line, 'magnitude=')
Jeff Brown4519f072011-01-23 13:16:01 -0800291
292 if line.find('tiltAngle=') != -1:
293 self.parse_tilt_angle = self._get_following_number(line, 'tiltAngle=')
294
295 if line.find('orientationAngle=') != -1:
296 self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=')
297
Jeff Brown4519f072011-01-23 13:16:01 -0800298 if line.find('Result:') != -1:
Jeff Brownc0347aa2011-09-23 17:26:09 -0700299 self.parse_current_rotation = self._get_following_number(line, 'currentRotation=')
300 self.parse_proposed_rotation = self._get_following_number(line, 'proposedRotation=')
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800301 self.parse_predicted_rotation = self._get_following_number(line, 'predictedRotation=')
Jeff Brown4519f072011-01-23 13:16:01 -0800302 self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=')
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800303 self.parse_time_until_settled = self._get_following_number(line, 'timeUntilSettledMS=')
304 self.parse_time_until_flat_delay_expired = self._get_following_number(line, 'timeUntilFlatDelayExpiredMS=')
305 self.parse_time_until_swing_delay_expired = self._get_following_number(line, 'timeUntilSwingDelayExpiredMS=')
Jeff Brown4519f072011-01-23 13:16:01 -0800306
Jeff Brown4519f072011-01-23 13:16:01 -0800307 self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
308 self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
309 self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800310 self._append(self.raw_acceleration_magnitude, timeindex, self.parse_raw_acceleration_magnitude)
Jeff Brown4519f072011-01-23 13:16:01 -0800311 self._append(self.filtered_acceleration_x, timeindex, self.parse_filtered_acceleration_x)
312 self._append(self.filtered_acceleration_y, timeindex, self.parse_filtered_acceleration_y)
313 self._append(self.filtered_acceleration_z, timeindex, self.parse_filtered_acceleration_z)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800314 self._append(self.filtered_acceleration_magnitude, timeindex, self.parse_filtered_acceleration_magnitude)
Jeff Brown4519f072011-01-23 13:16:01 -0800315 self._append(self.tilt_angle, timeindex, self.parse_tilt_angle)
316 self._append(self.orientation_angle, timeindex, self.parse_orientation_angle)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700317 self._append(self.current_rotation, timeindex, self.parse_current_rotation)
318 if self.parse_proposed_rotation >= 0:
319 self._append(self.proposed_rotation, timeindex, self.parse_proposed_rotation)
320 else:
321 self._append(self.proposed_rotation, timeindex, None)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800322 if self.parse_predicted_rotation >= 0:
323 self._append(self.predicted_rotation, timeindex, self.parse_predicted_rotation)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700324 else:
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800325 self._append(self.predicted_rotation, timeindex, None)
326 self._append(self.time_until_settled, timeindex, self.parse_time_until_settled)
327 self._append(self.time_until_flat_delay_expired, timeindex, self.parse_time_until_flat_delay_expired)
328 self._append(self.time_until_swing_delay_expired, timeindex, self.parse_time_until_swing_delay_expired)
Jeff Brown4519f072011-01-23 13:16:01 -0800329 self._append(self.sample_latency, timeindex, self.parse_sample_latency)
330 self._reset_parse_state()
331
332 # Scroll the plots.
333 if timeindex > timespan:
334 bottom = int(timeindex) - timespan + scrolljump
335 self.timebase += timedelta(seconds=bottom)
336 self._scroll(self.raw_acceleration_x, bottom)
337 self._scroll(self.raw_acceleration_y, bottom)
338 self._scroll(self.raw_acceleration_z, bottom)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800339 self._scroll(self.raw_acceleration_magnitude, bottom)
Jeff Brown4519f072011-01-23 13:16:01 -0800340 self._scroll(self.filtered_acceleration_x, bottom)
341 self._scroll(self.filtered_acceleration_y, bottom)
342 self._scroll(self.filtered_acceleration_z, bottom)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800343 self._scroll(self.filtered_acceleration_magnitude, bottom)
Jeff Brown4519f072011-01-23 13:16:01 -0800344 self._scroll(self.tilt_angle, bottom)
345 self._scroll(self.orientation_angle, bottom)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700346 self._scroll(self.current_rotation, bottom)
347 self._scroll(self.proposed_rotation, bottom)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800348 self._scroll(self.predicted_rotation, bottom)
349 self._scroll(self.time_until_settled, bottom)
350 self._scroll(self.time_until_flat_delay_expired, bottom)
351 self._scroll(self.time_until_swing_delay_expired, bottom)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700352 self._scroll(self.sample_latency, bottom)
Jeff Brown4519f072011-01-23 13:16:01 -0800353
354 # Redraw the plots.
355 self.raw_acceleration_line_x.set_data(self.raw_acceleration_x)
356 self.raw_acceleration_line_y.set_data(self.raw_acceleration_y)
357 self.raw_acceleration_line_z.set_data(self.raw_acceleration_z)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800358 self.raw_acceleration_line_magnitude.set_data(self.raw_acceleration_magnitude)
Jeff Brown4519f072011-01-23 13:16:01 -0800359 self.filtered_acceleration_line_x.set_data(self.filtered_acceleration_x)
360 self.filtered_acceleration_line_y.set_data(self.filtered_acceleration_y)
361 self.filtered_acceleration_line_z.set_data(self.filtered_acceleration_z)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800362 self.filtered_acceleration_line_magnitude.set_data(self.filtered_acceleration_magnitude)
Jeff Brown4519f072011-01-23 13:16:01 -0800363 self.tilt_angle_line.set_data(self.tilt_angle)
364 self.orientation_angle_line.set_data(self.orientation_angle)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700365 self.current_rotation_line.set_data(self.current_rotation)
366 self.proposed_rotation_line.set_data(self.proposed_rotation)
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800367 self.predicted_rotation_line.set_data(self.predicted_rotation)
368 self.time_until_settled_line.set_data(self.time_until_settled)
369 self.time_until_flat_delay_expired_line.set_data(self.time_until_flat_delay_expired)
370 self.time_until_swing_delay_expired_line.set_data(self.time_until_swing_delay_expired)
Jeff Brown4519f072011-01-23 13:16:01 -0800371 self.sample_latency_line.set_data(self.sample_latency)
372
Jeff Brown4519f072011-01-23 13:16:01 -0800373 self.fig.canvas.draw_idle()
374
375 # Scroll a time series.
376 def _scroll(self, timeseries, bottom):
377 bottom_index = bisect.bisect_left(timeseries[0], bottom)
378 del timeseries[0][:bottom_index]
379 del timeseries[1][:bottom_index]
380 for i, timeindex in enumerate(timeseries[0]):
381 timeseries[0][i] = timeindex - bottom
382
383 # Extract a word following the specified prefix.
384 def _get_following_word(self, line, prefix):
385 prefix_index = line.find(prefix)
386 if prefix_index == -1:
387 return None
388 start_index = prefix_index + len(prefix)
389 delim_index = line.find(',', start_index)
390 if delim_index == -1:
391 return line[start_index:]
392 else:
393 return line[start_index:delim_index]
394
395 # Extract a number following the specified prefix.
396 def _get_following_number(self, line, prefix):
397 word = self._get_following_word(line, prefix)
398 if word is None:
399 return None
400 return float(word)
401
402 # Extract an array of numbers following the specified prefix.
403 def _get_following_array_of_numbers(self, line, prefix):
404 prefix_index = line.find(prefix + '[')
405 if prefix_index == -1:
406 return None
407 start_index = prefix_index + len(prefix) + 1
408 delim_index = line.find(']', start_index)
409 if delim_index == -1:
410 return None
411
412 result = []
413 while start_index < delim_index:
414 comma_index = line.find(', ', start_index, delim_index)
415 if comma_index == -1:
416 result.append(float(line[start_index:delim_index]))
417 break;
418 result.append(float(line[start_index:comma_index]))
419 start_index = comma_index + 2
420 return result
421
422 # Add a value to a time series.
423 def _append(self, timeseries, timeindex, number):
424 timeseries[0].append(timeindex)
425 timeseries[1].append(number)
426
427 # Parse the logcat timestamp.
428 # Timestamp has the form '01-21 20:42:42.930'
429 def _parse_timestamp(self, line):
430 return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f')
431
432# Notice
433print "Window Orientation Listener plotting tool"
434print "-----------------------------------------\n"
435print "Please turn on the Window Orientation Listener logging in Development Settings."
436
437# Start adb.
438print "Starting adb logcat.\n"
439
440adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'WindowOrientationListener:V'],
441 stdout=subprocess.PIPE)
442adbout = NonBlockingStream(adb.stdout)
443
444# Prepare plotter.
445plotter = Plotter(adbout)
446plotter.update()
447
448# Main loop.
449plot.show()