1#!/usr/bin/env python 2# Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3# 4# Use of this source code is governed by a BSD-style license 5# that can be found in the LICENSE file in the root of the source 6# tree. An additional intellectual property rights grant can be found 7# in the file PATENTS. All contributing project authors may 8# be found in the AUTHORS file in the root of the source tree. 9 10# This script is used to plot simulation dynamics. The expected format is 11# PLOT <plot_number> <var_name>:<ssrc>@<alg_name> <time> <value> 12# <var_name> may optionally be followed by #<axis_alignment> but it is 13# deprecated. <plot_number> is also deprecated. 14# Each combination <var_name>:<ssrc>@<alg_name> is stored in it's own time 15# series. The main function defines which time series should be displayed and 16# whether they should should be displayed in the same or separate windows. 17 18 19import matplotlib.pyplot as plt 20import numpy 21import re 22import sys 23 24# Change this to True to save the figure to a file. Look below for details. 25SAVE_FIGURE = False 26 27class ParsePlotLineException(Exception): 28 def __init__(self, reason, line): 29 super(ParsePlotLineException, self).__init__() 30 self.reason = reason 31 self.line = line 32 33 34def ParsePlotLine(line): 35 split_line = line.split() 36 if len(split_line) != 5: 37 raise ParsePlotLineException("Expected 5 arguments on line", line) 38 (plot, _, annotated_var, time, value) = split_line 39 if plot != "PLOT": 40 raise ParsePlotLineException("Line does not begin with \"PLOT\\t\"", line) 41 # The variable name can contain any non-whitespace character except "#:@" 42 match = re.match(r'([^\s#:@]+)(?:#\d)?:(\d+)@(\S+)', annotated_var) 43 44 if match == None: 45 raise ParsePlotLineException("Could not parse variable name, ssrc and \ 46 algorithm name", annotated_var) 47 var_name = match.group(1) 48 ssrc = match.group(2) 49 alg_name = match.group(3).replace('_', ' ') 50 51 return (var_name, ssrc, alg_name, time, value) 52 53 54def GenerateLabel(var_name, ssrc, ssrc_count, alg_name): 55 label = var_name 56 if ssrc_count > 1 or ssrc != "0": 57 label = label + " flow " + ssrc 58 if alg_name != "-": 59 label = label + " " + alg_name 60 return label 61 62 63class Figure(object): 64 def __init__(self, name): 65 self.name = name 66 self.subplots = [] 67 68 def AddSubplot(self, var_names, xlabel, ylabel): 69 self.subplots.append(Subplot(var_names, xlabel, ylabel)) 70 71 def AddSample(self, var_name, ssrc, alg_name, time, value): 72 for s in self.subplots: 73 s.AddSample(var_name, ssrc, alg_name, time, value) 74 75 def PlotFigure(self, fig): 76 n = len(self.subplots) 77 for i in range(n): 78 axis = fig.add_subplot(n, 1, i+1) 79 self.subplots[i].PlotSubplot(axis) 80 81class Subplot(object): 82 def __init__(self, var_names, xlabel, ylabel): 83 self.xlabel = xlabel 84 self.ylabel = ylabel 85 self.var_names = var_names 86 self.samples = dict() 87 88 def AddSample(self, var_name, ssrc, alg_name, time, value): 89 if var_name not in self.var_names: 90 return 91 92 if alg_name not in self.samples.keys(): 93 self.samples[alg_name] = {} 94 if ssrc not in self.samples[alg_name].keys(): 95 self.samples[alg_name][ssrc] = {} 96 if var_name not in self.samples[alg_name][ssrc].keys(): 97 self.samples[alg_name][ssrc][var_name] = [] 98 99 self.samples[alg_name][ssrc][var_name].append((time, value)) 100 101 def PlotSubplot(self, axis): 102 axis.set_xlabel(self.xlabel) 103 axis.set_ylabel(self.ylabel) 104 105 count = 0 106 for alg_name in self.samples.keys(): 107 for ssrc in self.samples[alg_name].keys(): 108 for var_name in self.samples[alg_name][ssrc].keys(): 109 x = [sample[0] for sample in self.samples[alg_name][ssrc][var_name]] 110 y = [sample[1] for sample in self.samples[alg_name][ssrc][var_name]] 111 x = numpy.array(x) 112 y = numpy.array(y) 113 ssrc_count = len(self.samples[alg_name].keys()) 114 l = GenerateLabel(var_name, ssrc, ssrc_count, alg_name) 115 if l == 'MaxThroughput_': 116 plt.plot(x, y, label=l, linewidth=4.0) 117 else: 118 plt.plot(x, y, label=l, linewidth=2.0) 119 count += 1 120 121 plt.grid(True) 122 if count > 1: 123 plt.legend(loc='best') 124 125 126def main(): 127 receiver = Figure("PacketReceiver") 128 receiver.AddSubplot(['Throughput_kbps', 'MaxThroughput_', 'Capacity_kbps', 129 'PerFlowCapacity_kbps', 'MetricRecorderThroughput_kbps'], 130 "Time (s)", "Throughput (kbps)") 131 receiver.AddSubplot(['Delay_ms_', 'Delay_ms'], "Time (s)", 132 "One-way delay (ms)") 133 receiver.AddSubplot(['Packet_Loss_'], "Time (s)", "Packet Loss Ratio") 134 135 kalman_state = Figure("KalmanState") 136 kalman_state.AddSubplot(['kc', 'km'], "Time (s)", "Kalman gain") 137 kalman_state.AddSubplot(['slope_1/bps'], "Time (s)", "Slope") 138 kalman_state.AddSubplot(['var_noise'], "Time (s)", "Var noise") 139 140 detector_state = Figure("DetectorState") 141 detector_state.AddSubplot(['T', 'threshold'], "Time (s)", "Offset") 142 143 trendline_state = Figure("TrendlineState") 144 trendline_state.AddSubplot(["accumulated_delay_ms", "smoothed_delay_ms"], 145 "Time (s)", "Delay (ms)") 146 trendline_state.AddSubplot(["trendline_slope"], "Time (s)", "Slope") 147 148 target_bitrate = Figure("TargetBitrate") 149 target_bitrate.AddSubplot(['target_bitrate_bps', 'acknowledged_bitrate'], 150 "Time (s)", "Bitrate (bps)") 151 152 min_rtt_state = Figure("MinRttState") 153 min_rtt_state.AddSubplot(['MinRtt'], "Time (s)", "Time (ms)") 154 155 # Select which figures to plot here. 156 figures = [receiver, detector_state, trendline_state, target_bitrate, 157 min_rtt_state] 158 159 # Add samples to the figures. 160 for line in sys.stdin: 161 if line.startswith("[ RUN ]"): 162 test_name = re.search(r'\.(\w+)', line).group(1) 163 if line.startswith("PLOT"): 164 try: 165 (var_name, ssrc, alg_name, time, value) = ParsePlotLine(line) 166 for f in figures: 167 # The sample will be ignored bv the figures that don't need it. 168 f.AddSample(var_name, ssrc, alg_name, time, value) 169 except ParsePlotLineException as e: 170 print e.reason 171 print e.line 172 173 # Plot figures. 174 for f in figures: 175 fig = plt.figure(f.name) 176 f.PlotFigure(fig) 177 if SAVE_FIGURE: 178 fig.savefig(test_name + f.name + ".png") 179 plt.show() 180 181if __name__ == '__main__': 182 main() 183