blob: 02b20523e8d416d1cdd988e482f0a8a18ba327b5 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
# This script is used to plot simulation dynamics. The expected format is
# PLOT <plot_number> <var_name>:<ssrc>@<alg_name> <time> <value>
# <var_name> may optionally be followed by #<axis_alignment> but it is
# deprecated. <plot_number> is also deprecated.
# Each combination <var_name>:<ssrc>@<alg_name> is stored in it's own time
# series. The main function defines which time series should be displayed and
# whether they should should be displayed in the same or separate windows.
import matplotlib.pyplot as plt
import numpy
import re
import sys
# Change this to True to save the figure to a file. Look below for details.
class ParsePlotLineException(Exception):
def __init__(self, reason, line):
super(ParsePlotLineException, self).__init__()
self.reason = reason
self.line = line
def ParsePlotLine(line):
split_line = line.split()
if len(split_line) != 5:
raise ParsePlotLineException("Expected 5 arguments on line", line)
(plot, _, annotated_var, time, value) = split_line
if plot != "PLOT":
raise ParsePlotLineException("Line does not begin with \"PLOT\\t\"", line)
# The variable name can contain any non-whitespace character except "#:@"
match = re.match(r'([^\s#:@]+)(?:#\d)?:(\d+)@(\S+)', annotated_var)
if match == None:
raise ParsePlotLineException("Could not parse variable name, ssrc and \
algorithm name", annotated_var)
var_name =
ssrc =
alg_name ='_', ' ')
return (var_name, ssrc, alg_name, time, value)
def GenerateLabel(var_name, ssrc, ssrc_count, alg_name):
label = var_name
if ssrc_count > 1 or ssrc != "0":
label = label + " flow " + ssrc
if alg_name != "-":
label = label + " " + alg_name
return label
class Figure(object):
def __init__(self, name): = name
self.subplots = []
def AddSubplot(self, var_names, xlabel, ylabel):
self.subplots.append(Subplot(var_names, xlabel, ylabel))
def AddSample(self, var_name, ssrc, alg_name, time, value):
for s in self.subplots:
s.AddSample(var_name, ssrc, alg_name, time, value)
def PlotFigure(self, fig):
n = len(self.subplots)
for i in range(n):
axis = fig.add_subplot(n, 1, i+1)
class Subplot(object):
def __init__(self, var_names, xlabel, ylabel):
self.xlabel = xlabel
self.ylabel = ylabel
self.var_names = var_names
self.samples = dict()
def AddSample(self, var_name, ssrc, alg_name, time, value):
if var_name not in self.var_names:
if alg_name not in self.samples.keys():
self.samples[alg_name] = {}
if ssrc not in self.samples[alg_name].keys():
self.samples[alg_name][ssrc] = {}
if var_name not in self.samples[alg_name][ssrc].keys():
self.samples[alg_name][ssrc][var_name] = []
self.samples[alg_name][ssrc][var_name].append((time, value))
def PlotSubplot(self, axis):
count = 0
for alg_name in self.samples.keys():
for ssrc in self.samples[alg_name].keys():
for var_name in self.samples[alg_name][ssrc].keys():
x = [sample[0] for sample in self.samples[alg_name][ssrc][var_name]]
y = [sample[1] for sample in self.samples[alg_name][ssrc][var_name]]
x = numpy.array(x)
y = numpy.array(y)
ssrc_count = len(self.samples[alg_name].keys())
l = GenerateLabel(var_name, ssrc, ssrc_count, alg_name)
if l == 'MaxThroughput_':
plt.plot(x, y, label=l, linewidth=4.0)
plt.plot(x, y, label=l, linewidth=2.0)
count += 1
if count > 1:
def main():
receiver = Figure("PacketReceiver")
receiver.AddSubplot(['Throughput_kbps', 'MaxThroughput_', 'Capacity_kbps',
'PerFlowCapacity_kbps', 'MetricRecorderThroughput_kbps'],
"Time (s)", "Throughput (kbps)")
receiver.AddSubplot(['Delay_ms_', 'Delay_ms'], "Time (s)",
"One-way delay (ms)")
receiver.AddSubplot(['Packet_Loss_'], "Time (s)", "Packet Loss Ratio")
kalman_state = Figure("KalmanState")
kalman_state.AddSubplot(['kc', 'km'], "Time (s)", "Kalman gain")
kalman_state.AddSubplot(['slope_1/bps'], "Time (s)", "Slope")
kalman_state.AddSubplot(['var_noise'], "Time (s)", "Var noise")
detector_state = Figure("DetectorState")
detector_state.AddSubplot(['T', 'threshold'], "Time (s)", "Offset")
trendline_state = Figure("TrendlineState")
trendline_state.AddSubplot(["accumulated_delay_ms", "smoothed_delay_ms"],
"Time (s)", "Delay (ms)")
trendline_state.AddSubplot(["trendline_slope"], "Time (s)", "Slope")
target_bitrate = Figure("TargetBitrate")
target_bitrate.AddSubplot(['target_bitrate_bps', 'acknowledged_bitrate'],
"Time (s)", "Bitrate (bps)")
min_rtt_state = Figure("MinRttState")
min_rtt_state.AddSubplot(['MinRtt'], "Time (s)", "Time (ms)")
# Select which figures to plot here.
figures = [receiver, detector_state, trendline_state, target_bitrate,
# Add samples to the figures.
for line in sys.stdin:
if line.startswith("[ RUN ]"):
test_name ='\.(\w+)', line).group(1)
if line.startswith("PLOT"):
(var_name, ssrc, alg_name, time, value) = ParsePlotLine(line)
for f in figures:
# The sample will be ignored bv the figures that don't need it.
f.AddSample(var_name, ssrc, alg_name, time, value)
except ParsePlotLineException as e:
print e.reason
print e.line
# Plot figures.
for f in figures:
fig = plt.figure(
fig.savefig(test_name + + ".png")
if __name__ == '__main__':