blob: 3a44cb2cbfd8a66bfdbbdbd7737ac9bee93f2674 [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()
85 self.raw_acceleration_axes = self._add_timeseries_axes(
86 1, 'Raw Acceleration', 'm/s^2', [-20, 20],
87 yticks=range(-15, 16, 5))
88 self.raw_acceleration_line_x = self._add_timeseries_line(
89 self.raw_acceleration_axes, 'x', 'red')
90 self.raw_acceleration_line_y = self._add_timeseries_line(
91 self.raw_acceleration_axes, 'y', 'green')
92 self.raw_acceleration_line_z = self._add_timeseries_line(
93 self.raw_acceleration_axes, 'z', 'blue')
94 self._add_timeseries_legend(self.raw_acceleration_axes)
95
96 shared_axis = self.raw_acceleration_axes
97
98 self.filtered_acceleration_x = self._make_timeseries()
99 self.filtered_acceleration_y = self._make_timeseries()
100 self.filtered_acceleration_z = self._make_timeseries()
101 self.magnitude = self._make_timeseries()
102 self.filtered_acceleration_axes = self._add_timeseries_axes(
103 2, 'Filtered Acceleration', 'm/s^2', [-20, 20],
104 sharex=shared_axis,
105 yticks=range(-15, 16, 5))
106 self.filtered_acceleration_line_x = self._add_timeseries_line(
107 self.filtered_acceleration_axes, 'x', 'red')
108 self.filtered_acceleration_line_y = self._add_timeseries_line(
109 self.filtered_acceleration_axes, 'y', 'green')
110 self.filtered_acceleration_line_z = self._add_timeseries_line(
111 self.filtered_acceleration_axes, 'z', 'blue')
112 self.magnitude_line = self._add_timeseries_line(
113 self.filtered_acceleration_axes, 'magnitude', 'orange', linewidth=2)
114 self._add_timeseries_legend(self.filtered_acceleration_axes)
115
116 self.tilt_angle = self._make_timeseries()
117 self.tilt_angle_axes = self._add_timeseries_axes(
118 3, 'Tilt Angle', 'degrees', [-105, 105],
119 sharex=shared_axis,
120 yticks=range(-90, 91, 30))
121 self.tilt_angle_line = self._add_timeseries_line(
122 self.tilt_angle_axes, 'tilt', 'black')
123 self._add_timeseries_legend(self.tilt_angle_axes)
124
125 self.orientation_angle = self._make_timeseries()
126 self.orientation_angle_axes = self._add_timeseries_axes(
127 4, 'Orientation Angle', 'degrees', [-25, 375],
128 sharex=shared_axis,
129 yticks=range(0, 361, 45))
130 self.orientation_angle_line = self._add_timeseries_line(
131 self.orientation_angle_axes, 'orientation', 'black')
132 self._add_timeseries_legend(self.orientation_angle_axes)
133
Jeff Brownc0347aa2011-09-23 17:26:09 -0700134 self.current_rotation = self._make_timeseries()
135 self.proposed_rotation = self._make_timeseries()
136 self.proposal_rotation = self._make_timeseries()
Jeff Brown4519f072011-01-23 13:16:01 -0800137 self.orientation_axes = self._add_timeseries_axes(
Jeff Brownc0347aa2011-09-23 17:26:09 -0700138 5, 'Current / Proposed Orientation and Confidence', 'rotation', [-1, 4],
Jeff Brown4519f072011-01-23 13:16:01 -0800139 sharex=shared_axis,
140 yticks=range(0, 4))
Jeff Brownc0347aa2011-09-23 17:26:09 -0700141 self.current_rotation_line = self._add_timeseries_line(
142 self.orientation_axes, 'current', 'black', linewidth=2)
143 self.proposal_rotation_line = self._add_timeseries_line(
144 self.orientation_axes, 'proposal', 'purple', linewidth=3)
145 self.proposed_rotation_line = self._add_timeseries_line(
146 self.orientation_axes, 'proposed', 'green', linewidth=3)
Jeff Brown4519f072011-01-23 13:16:01 -0800147 self._add_timeseries_legend(self.orientation_axes)
148
Jeff Brownc0347aa2011-09-23 17:26:09 -0700149 self.proposal_confidence = [[self._make_timeseries(), self._make_timeseries()]
150 for i in range(0, 4)]
151 self.proposal_confidence_polys = []
Jeff Brown4519f072011-01-23 13:16:01 -0800152
153 self.sample_latency = self._make_timeseries()
154 self.sample_latency_axes = self._add_timeseries_axes(
Jeff Brownc0347aa2011-09-23 17:26:09 -0700155 6, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
Jeff Brown4519f072011-01-23 13:16:01 -0800156 sharex=shared_axis,
157 yticks=range(0, 500, 100))
158 self.sample_latency_line = self._add_timeseries_line(
159 self.sample_latency_axes, 'latency', 'black')
160 self._add_timeseries_legend(self.sample_latency_axes)
161
162 self.timer = self.fig.canvas.new_timer(interval=100)
163 self.timer.add_callback(lambda: self.update())
164 self.timer.start()
165
166 self.timebase = None
167 self._reset_parse_state()
168
169 # Initialize a time series.
170 def _make_timeseries(self):
171 return [[], []]
172
173 # Add a subplot to the figure for a time series.
174 def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
Jeff Brownc0347aa2011-09-23 17:26:09 -0700175 num_graphs = 6
Jeff Brown4519f072011-01-23 13:16:01 -0800176 height = 0.9 / num_graphs
177 top = 0.95 - height * index
178 axes = self.fig.add_axes([0.1, top, 0.8, height],
179 xscale='linear',
180 xlim=[0, timespan],
181 ylabel=ylabel,
182 yscale='linear',
183 ylim=ylim,
184 sharex=sharex)
185 axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold')
186 axes.set_xlabel('time (s)', fontsize=10, fontweight='bold')
187 axes.set_ylabel(ylabel, fontsize=10, fontweight='bold')
188 axes.set_xticks(range(0, timespan + 1, timeticks))
189 axes.set_yticks(yticks)
190 axes.grid(True)
191
192 for label in axes.get_xticklabels():
193 label.set_fontsize(9)
194 for label in axes.get_yticklabels():
195 label.set_fontsize(9)
196
197 return axes
198
199 # Add a line to the axes for a time series.
200 def _add_timeseries_line(self, axes, label, color, linewidth=1):
201 return axes.plot([], label=label, color=color, linewidth=linewidth)[0]
202
203 # Add a legend to a time series.
204 def _add_timeseries_legend(self, axes):
205 axes.legend(
206 loc='upper left',
207 bbox_to_anchor=(1.01, 1),
208 borderpad=0.1,
209 borderaxespad=0.1,
210 prop={'size': 10})
211
212 # Resets the parse state.
213 def _reset_parse_state(self):
214 self.parse_raw_acceleration_x = None
215 self.parse_raw_acceleration_y = None
216 self.parse_raw_acceleration_z = None
217 self.parse_filtered_acceleration_x = None
218 self.parse_filtered_acceleration_y = None
219 self.parse_filtered_acceleration_z = None
220 self.parse_magnitude = None
221 self.parse_tilt_angle = None
222 self.parse_orientation_angle = None
Jeff Brownc0347aa2011-09-23 17:26:09 -0700223 self.parse_current_rotation = None
224 self.parse_proposed_rotation = None
225 self.parse_proposal_rotation = None
226 self.parse_proposal_confidence = None
Jeff Brown4519f072011-01-23 13:16:01 -0800227 self.parse_sample_latency = None
228
229 # Update samples.
230 def update(self):
231 timeindex = 0
232 while True:
233 try:
234 line = self.adbout.readline()
235 except EOFError:
236 plot.close()
237 return
238 if line is None:
239 break
240 print line
241
242 try:
243 timestamp = self._parse_timestamp(line)
244 except ValueError, e:
245 continue
246 if self.timebase is None:
247 self.timebase = timestamp
248 delta = timestamp - self.timebase
249 timeindex = delta.seconds + delta.microseconds * 0.000001
250
251 if line.find('Raw acceleration vector:') != -1:
252 self.parse_raw_acceleration_x = self._get_following_number(line, 'x=')
253 self.parse_raw_acceleration_y = self._get_following_number(line, 'y=')
254 self.parse_raw_acceleration_z = self._get_following_number(line, 'z=')
255
256 if line.find('Filtered acceleration vector:') != -1:
257 self.parse_filtered_acceleration_x = self._get_following_number(line, 'x=')
258 self.parse_filtered_acceleration_y = self._get_following_number(line, 'y=')
259 self.parse_filtered_acceleration_z = self._get_following_number(line, 'z=')
260
261 if line.find('magnitude=') != -1:
262 self.parse_magnitude = self._get_following_number(line, 'magnitude=')
263
264 if line.find('tiltAngle=') != -1:
265 self.parse_tilt_angle = self._get_following_number(line, 'tiltAngle=')
266
267 if line.find('orientationAngle=') != -1:
268 self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=')
269
Jeff Brown4519f072011-01-23 13:16:01 -0800270 if line.find('Result:') != -1:
Jeff Brownc0347aa2011-09-23 17:26:09 -0700271 self.parse_current_rotation = self._get_following_number(line, 'currentRotation=')
272 self.parse_proposed_rotation = self._get_following_number(line, 'proposedRotation=')
273 self.parse_proposal_rotation = self._get_following_number(line, 'proposalRotation=')
274 self.parse_proposal_confidence = self._get_following_number(line, 'proposalConfidence=')
Jeff Brown4519f072011-01-23 13:16:01 -0800275 self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=')
276
Jeff Brown4519f072011-01-23 13:16:01 -0800277 self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
278 self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
279 self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z)
280 self._append(self.filtered_acceleration_x, timeindex, self.parse_filtered_acceleration_x)
281 self._append(self.filtered_acceleration_y, timeindex, self.parse_filtered_acceleration_y)
282 self._append(self.filtered_acceleration_z, timeindex, self.parse_filtered_acceleration_z)
283 self._append(self.magnitude, timeindex, self.parse_magnitude)
284 self._append(self.tilt_angle, timeindex, self.parse_tilt_angle)
285 self._append(self.orientation_angle, timeindex, self.parse_orientation_angle)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700286 self._append(self.current_rotation, timeindex, self.parse_current_rotation)
287 if self.parse_proposed_rotation >= 0:
288 self._append(self.proposed_rotation, timeindex, self.parse_proposed_rotation)
289 else:
290 self._append(self.proposed_rotation, timeindex, None)
291 if self.parse_proposal_rotation >= 0:
292 self._append(self.proposal_rotation, timeindex, self.parse_proposal_rotation)
293 else:
294 self._append(self.proposal_rotation, timeindex, None)
295 for i in range(0, 4):
296 self._append(self.proposal_confidence[i][0], timeindex, i)
297 if i == self.parse_proposal_rotation:
298 self._append(self.proposal_confidence[i][1], timeindex,
299 i + self.parse_proposal_confidence)
300 else:
301 self._append(self.proposal_confidence[i][1], timeindex, i)
Jeff Brown4519f072011-01-23 13:16:01 -0800302 self._append(self.sample_latency, timeindex, self.parse_sample_latency)
303 self._reset_parse_state()
304
305 # Scroll the plots.
306 if timeindex > timespan:
307 bottom = int(timeindex) - timespan + scrolljump
308 self.timebase += timedelta(seconds=bottom)
309 self._scroll(self.raw_acceleration_x, bottom)
310 self._scroll(self.raw_acceleration_y, bottom)
311 self._scroll(self.raw_acceleration_z, bottom)
312 self._scroll(self.filtered_acceleration_x, bottom)
313 self._scroll(self.filtered_acceleration_y, bottom)
314 self._scroll(self.filtered_acceleration_z, bottom)
315 self._scroll(self.magnitude, bottom)
316 self._scroll(self.tilt_angle, bottom)
317 self._scroll(self.orientation_angle, bottom)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700318 self._scroll(self.current_rotation, bottom)
319 self._scroll(self.proposed_rotation, bottom)
320 self._scroll(self.proposal_rotation, bottom)
Jeff Brown4519f072011-01-23 13:16:01 -0800321 for i in range(0, 4):
Jeff Brownc0347aa2011-09-23 17:26:09 -0700322 self._scroll(self.proposal_confidence[i][0], bottom)
323 self._scroll(self.proposal_confidence[i][1], bottom)
324 self._scroll(self.sample_latency, bottom)
Jeff Brown4519f072011-01-23 13:16:01 -0800325
326 # Redraw the plots.
327 self.raw_acceleration_line_x.set_data(self.raw_acceleration_x)
328 self.raw_acceleration_line_y.set_data(self.raw_acceleration_y)
329 self.raw_acceleration_line_z.set_data(self.raw_acceleration_z)
330 self.filtered_acceleration_line_x.set_data(self.filtered_acceleration_x)
331 self.filtered_acceleration_line_y.set_data(self.filtered_acceleration_y)
332 self.filtered_acceleration_line_z.set_data(self.filtered_acceleration_z)
333 self.magnitude_line.set_data(self.magnitude)
334 self.tilt_angle_line.set_data(self.tilt_angle)
335 self.orientation_angle_line.set_data(self.orientation_angle)
Jeff Brownc0347aa2011-09-23 17:26:09 -0700336 self.current_rotation_line.set_data(self.current_rotation)
337 self.proposed_rotation_line.set_data(self.proposed_rotation)
338 self.proposal_rotation_line.set_data(self.proposal_rotation)
Jeff Brown4519f072011-01-23 13:16:01 -0800339 self.sample_latency_line.set_data(self.sample_latency)
340
Jeff Brownc0347aa2011-09-23 17:26:09 -0700341 for poly in self.proposal_confidence_polys:
Jeff Brown4519f072011-01-23 13:16:01 -0800342 poly.remove()
Jeff Brownc0347aa2011-09-23 17:26:09 -0700343 self.proposal_confidence_polys = []
Jeff Brown4519f072011-01-23 13:16:01 -0800344 for i in range(0, 4):
Jeff Brownc0347aa2011-09-23 17:26:09 -0700345 self.proposal_confidence_polys.append(self.orientation_axes.fill_between(
346 self.proposal_confidence[i][0][0],
347 self.proposal_confidence[i][0][1],
348 self.proposal_confidence[i][1][1],
Jeff Brown4519f072011-01-23 13:16:01 -0800349 facecolor='goldenrod', edgecolor='goldenrod'))
350
351 self.fig.canvas.draw_idle()
352
353 # Scroll a time series.
354 def _scroll(self, timeseries, bottom):
355 bottom_index = bisect.bisect_left(timeseries[0], bottom)
356 del timeseries[0][:bottom_index]
357 del timeseries[1][:bottom_index]
358 for i, timeindex in enumerate(timeseries[0]):
359 timeseries[0][i] = timeindex - bottom
360
361 # Extract a word following the specified prefix.
362 def _get_following_word(self, line, prefix):
363 prefix_index = line.find(prefix)
364 if prefix_index == -1:
365 return None
366 start_index = prefix_index + len(prefix)
367 delim_index = line.find(',', start_index)
368 if delim_index == -1:
369 return line[start_index:]
370 else:
371 return line[start_index:delim_index]
372
373 # Extract a number following the specified prefix.
374 def _get_following_number(self, line, prefix):
375 word = self._get_following_word(line, prefix)
376 if word is None:
377 return None
378 return float(word)
379
380 # Extract an array of numbers following the specified prefix.
381 def _get_following_array_of_numbers(self, line, prefix):
382 prefix_index = line.find(prefix + '[')
383 if prefix_index == -1:
384 return None
385 start_index = prefix_index + len(prefix) + 1
386 delim_index = line.find(']', start_index)
387 if delim_index == -1:
388 return None
389
390 result = []
391 while start_index < delim_index:
392 comma_index = line.find(', ', start_index, delim_index)
393 if comma_index == -1:
394 result.append(float(line[start_index:delim_index]))
395 break;
396 result.append(float(line[start_index:comma_index]))
397 start_index = comma_index + 2
398 return result
399
400 # Add a value to a time series.
401 def _append(self, timeseries, timeindex, number):
402 timeseries[0].append(timeindex)
403 timeseries[1].append(number)
404
405 # Parse the logcat timestamp.
406 # Timestamp has the form '01-21 20:42:42.930'
407 def _parse_timestamp(self, line):
408 return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f')
409
410# Notice
411print "Window Orientation Listener plotting tool"
412print "-----------------------------------------\n"
413print "Please turn on the Window Orientation Listener logging in Development Settings."
414
415# Start adb.
416print "Starting adb logcat.\n"
417
418adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'WindowOrientationListener:V'],
419 stdout=subprocess.PIPE)
420adbout = NonBlockingStream(adb.stdout)
421
422# Prepare plotter.
423plotter = Plotter(adbout)
424plotter.update()
425
426# Main loop.
427plot.show()