| Index: webrtc/modules/remote_bitrate_estimator/test/plot_dynamics.py
|
| diff --git a/webrtc/modules/remote_bitrate_estimator/test/plot_dynamics.py b/webrtc/modules/remote_bitrate_estimator/test/plot_dynamics.py
|
| index 164300141d3917be1482d40f4c0af4dfa43b9adf..dac47626debf72c3761db6ff53ee98cf11fd9d8b 100755
|
| --- a/webrtc/modules/remote_bitrate_estimator/test/plot_dynamics.py
|
| +++ b/webrtc/modules/remote_bitrate_estimator/test/plot_dynamics.py
|
| @@ -7,11 +7,15 @@
|
| # 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.
|
| -# Able to plot each flow separately. Other plot boxes can be added,
|
| -# currently one for Throughput, one for Latency and one for Packet Loss.
|
| +# 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
|
| import matplotlib.pyplot as plt
|
| import numpy
|
| import re
|
| @@ -20,144 +24,145 @@ import sys
|
| # Change this to True to save the figure to a file. Look below for details.
|
| save_figure = False
|
|
|
| -class Variable(object):
|
| - def __init__(self, variable):
|
| - self._ID = variable[0]
|
| - self._xlabel = variable[1]
|
| - self._ylabel = variable[2]
|
| - self._subplot = variable[3]
|
| - self._y_max = variable[4]
|
| +class ParsePlotLineException(Exception):
|
| + def __init__(self, reason, line):
|
| + super(ParsePlotLineException, self).__init__()
|
| + self.reason = reason
|
| + self.line = line
|
| +
|
| +
|
| +def parse_plot_line(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 = match.group(1)
|
| + ssrc = match.group(2)
|
| + alg_name = match.group(3).replace('_', ' ')
|
| +
|
| + return (var_name, ssrc, alg_name, time, value)
|
| +
|
| +
|
| +def generate_label(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):
|
| + 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):
|
| + ax = fig.add_subplot(n, 1, i+1)
|
| + self.subplots[i].plotSubplot(ax)
|
| +
|
| +
|
| +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 getID(self):
|
| - return self._ID
|
| -
|
| - def getXLabel(self):
|
| - return self._xlabel
|
| -
|
| - def getYLabel(self):
|
| - return self._ylabel
|
| -
|
| - def getSubplot(self):
|
| - return self._subplot
|
| -
|
| - def getYMax(self):
|
| - return self._y_max
|
| -
|
| - def getNumberOfFlows(self):
|
| - return len(self.samples)
|
| -
|
| -
|
| - def addSample(self, line):
|
| - groups = re.search(r'_(((\d)+((,(\d)+)*))_(\D+))#\d:(\d)@(\S+)', line)
|
| -
|
| - # Each variable will be plotted in a separated box.
|
| - var_name = groups.group(1)
|
| - alg_name = groups.group(9)
|
| -
|
| - alg_name = alg_name.replace('_', ' ')
|
| + def addSample(self, var_name, ssrc, alg_name, time, value):
|
| + if var_name not in self.var_names:
|
| + return
|
|
|
| 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] = []
|
|
|
| - if var_name not in self.samples[alg_name].keys():
|
| - self.samples[alg_name][var_name] = []
|
| -
|
| - sample = re.search(r'(\d+\.\d+)\t([-]?\d+\.\d+)', line)
|
| -
|
| - s = (sample.group(1), sample.group(2))
|
| - self.samples[alg_name][var_name].append(s)
|
| -
|
| -def plotVar(v, ax, show_legend, show_x_label):
|
| - if show_x_label:
|
| - ax.set_xlabel(v.getXLabel(), fontsize='large')
|
| - ax.set_ylabel(v.getYLabel(), fontsize='large')
|
| + self.samples[alg_name][ssrc][var_name].append((time, value))
|
|
|
| - for alg in v.samples.keys():
|
| + def plotSubplot(self, ax):
|
| + ax.set_xlabel(self.xlabel)
|
| + ax.set_ylabel(self.ylabel)
|
|
|
| - for series in v.samples[alg].keys():
|
| + 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)
|
|
|
| - x = [sample[0] for sample in v.samples[alg][series]]
|
| - y = [sample[1] for sample in v.samples[alg][series]]
|
| - x = numpy.array(x)
|
| - y = numpy.array(y)
|
| + ssrc_count = len(self.samples[alg_name].keys())
|
| + l = generate_label(var_name, ssrc, ssrc_count, alg_name)
|
| + plt.plot(x, y, label=l, linewidth=2.0)
|
| + count += 1
|
|
|
| - line = plt.plot(x, y, label=alg, linewidth=4.0)
|
| + plt.grid(True)
|
| + if count > 1:
|
| + plt.legend(loc='best')
|
|
|
| - colormap = {'Available0':'#AAAAAA',
|
| - 'Available1':'#AAAAAA',
|
| - 'GCC0':'#80D000',
|
| - 'GCC1':'#008000',
|
| - 'GCC2':'#00F000',
|
| - 'GCC3':'#00B000',
|
| - 'GCC4':'#70B020',
|
| - 'NADA0':'#0000AA',
|
| - 'NADA1':'#A0A0FF',
|
| - 'NADA2':'#0000FF',
|
| - 'NADA3':'#C0A0FF',
|
| - 'NADA4':'#9060B0',}
|
| -
|
| - flow_id = re.search(r'(\d+(,\d+)*)', series) # One or multiple ids.
|
| - key = alg + flow_id.group(1)
|
| -
|
| - if key in colormap:
|
| - plt.setp(line, color=colormap[key])
|
| - elif alg == 'TCP':
|
| - plt.setp(line, color='#AAAAAA')
|
| - else:
|
| - plt.setp(line, color='#654321')
|
| -
|
| - if alg.startswith('Available'):
|
| - plt.setp(line, linestyle='--')
|
| - plt.grid(True)
|
| -
|
| - # x1, x2, y1, y2
|
| - _, x2, _, y2 = plt.axis()
|
| - if v.getYMax() >= 0:
|
| - y2 = v.getYMax()
|
| - plt.axis((0, x2, 0, y2))
|
| -
|
| - if show_legend:
|
| - plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.40),
|
| - shadow=True, fontsize='large', ncol=len(v.samples))
|
|
|
| def main():
|
| - variables = [
|
| - ('Throughput_kbps', "Time (s)", "Throughput (kbps)", 1, 4000),
|
| - ('Delay_ms', "Time (s)", "One-way Delay (ms)", 2, 500),
|
| - ('Packet_Loss', "Time (s)", "Packet Loss Ratio", 3, 1.0),
|
| - ]
|
| -
|
| - var = []
|
| -
|
| - # Create objects.
|
| - for variable in variables:
|
| - var.append(Variable(variable))
|
| -
|
| - # Add samples to the objects.
|
| + 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(['offset_ms'], "Time (s)", "Offset")
|
| + detector_state.addSubplot(['gamma_ms'], "Time (s)", "Gamma")
|
| +
|
| + # Select which figures to plot here.
|
| + figures = [receiver, detector_state]
|
| +
|
| + # Add samples to the figures.
|
| for line in sys.stdin:
|
| if line.startswith("[ RUN ]"):
|
| test_name = re.search(r'\.(\w+)', line).group(1)
|
| if line.startswith("PLOT"):
|
| - for v in var:
|
| - if v.getID() in line:
|
| - v.addSample(line)
|
| -
|
| - matplotlib.rcParams.update({'font.size': 48/len(variables)})
|
| -
|
| - # Plot variables.
|
| - fig = plt.figure()
|
| -
|
| - # Offest and threshold on the same plot.
|
| - n = var[-1].getSubplot()
|
| - i = 0
|
| - for v in var:
|
| - ax = fig.add_subplot(n, 1, v.getSubplot())
|
| - plotVar(v, ax, i == 0, i == n - 1)
|
| - i += 1
|
| -
|
| - if save_figure:
|
| - fig.savefig(test_name + ".png")
|
| + try:
|
| + (var_name, ssrc, alg_name, time, value) = parse_plot_line(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(f.name)
|
| + f.plotFigure(fig)
|
| + if save_figure:
|
| + fig.savefig(test_name + f.name + ".png")
|
| plt.show()
|
|
|
| if __name__ == '__main__':
|
|
|