158 lines
5.6 KiB
Python
Executable file
158 lines
5.6 KiB
Python
Executable file
#! /usr/bin/env python3
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
#
|
|
# This scripts plots graphs produced by our drift correction code.
|
|
#
|
|
# Install dependencies with:
|
|
# > pip install bokeh pandas
|
|
#
|
|
# Generate the csv data file with the DriftControllerGraphs log module:
|
|
# > MOZ_LOG=raw,sync,DriftControllerGraphs:5 \
|
|
# > MOZ_LOG_FILE=/tmp/driftcontrol.csv \
|
|
# > ./mach gtest '*AudioDrift*StepResponse'
|
|
#
|
|
# Generate the graphs with this script:
|
|
# > ./dom/media/driftcontrol/plot.py /tmp/driftcontrol.csv.moz_log
|
|
#
|
|
# The script should produce a file plot.html in the working directory and
|
|
# open it in the default browser.
|
|
|
|
import argparse
|
|
from collections import OrderedDict
|
|
|
|
import pandas
|
|
from bokeh.io import output_file, show
|
|
from bokeh.layouts import gridplot
|
|
from bokeh.models import TabPanel, Tabs
|
|
from bokeh.plotting import figure
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
prog="plot.py for DriftControllerGraphs",
|
|
description="""Takes a csv file of DriftControllerGraphs data
|
|
(from a single DriftController instance) and plots
|
|
them into plot.html in the current working directory.
|
|
|
|
The easiest way to produce the data is with MOZ_LOG:
|
|
MOZ_LOG=raw,sync,DriftControllerGraphs:5 \
|
|
MOZ_LOG_FILE=/tmp/driftcontrol.csv \
|
|
./mach gtest '*AudioDrift*StepResponse'""",
|
|
)
|
|
parser.add_argument("csv_file", type=str)
|
|
args = parser.parse_args()
|
|
|
|
all_df = pandas.read_csv(args.csv_file)
|
|
|
|
# Filter on distinct ids to support multiple plotting sources
|
|
tabs = []
|
|
for id in list(OrderedDict.fromkeys(all_df["id"])):
|
|
df = all_df[all_df["id"] == id]
|
|
|
|
t = df["t"]
|
|
buffering = df["buffering"]
|
|
avgbuffered = df["avgbuffered"]
|
|
desired = df["desired"]
|
|
buffersize = df["buffersize"]
|
|
inlatency = df["inlatency"]
|
|
outlatency = df["outlatency"]
|
|
inframesavg = df["inframesavg"]
|
|
outframesavg = df["outframesavg"]
|
|
inrate = df["inrate"]
|
|
outrate = df["outrate"]
|
|
steadystaterate = df["steadystaterate"]
|
|
nearthreshold = df["nearthreshold"]
|
|
corrected = df["corrected"]
|
|
hysteresiscorrected = df["hysteresiscorrected"]
|
|
configured = df["configured"]
|
|
|
|
output_file("plot.html")
|
|
|
|
fig1 = figure()
|
|
# Variables with more variation are plotted after smoother variables
|
|
# because latter variables are drawn on top and so visibility of
|
|
# individual values in the variables with more variation is improved
|
|
# (when both variables are shown).
|
|
fig1.line(
|
|
t, inframesavg, color="violet", legend_label="Average input packet size"
|
|
)
|
|
fig1.line(
|
|
t, outframesavg, color="purple", legend_label="Average output packet size"
|
|
)
|
|
fig1.line(t, inlatency, color="hotpink", legend_label="In latency")
|
|
fig1.line(t, outlatency, color="firebrick", legend_label="Out latency")
|
|
fig1.line(t, desired, color="goldenrod", legend_label="Desired buffering")
|
|
fig1.line(
|
|
t, avgbuffered, color="orangered", legend_label="Average buffered estimate"
|
|
)
|
|
fig1.line(t, buffering, color="dodgerblue", legend_label="Actual buffering")
|
|
fig1.line(t, buffersize, color="seagreen", legend_label="Buffer size")
|
|
fig1.varea(
|
|
t,
|
|
[d - h for (d, h) in zip(desired, nearthreshold)],
|
|
[d + h for (d, h) in zip(desired, nearthreshold)],
|
|
alpha=0.2,
|
|
color="goldenrod",
|
|
legend_label='"Near" band (won\'t reduce desired buffering outside)',
|
|
)
|
|
|
|
slowConvergenceSecs = 30
|
|
adjustmentInterval = 1
|
|
slowHysteresis = 1
|
|
avgError = avgbuffered - desired
|
|
absAvgError = [abs(e) for e in avgError]
|
|
slow_offset = [e / slowConvergenceSecs - slowHysteresis for e in absAvgError]
|
|
fast_offset = [e / adjustmentInterval for e in absAvgError]
|
|
low_offset, high_offset = zip(
|
|
*[
|
|
(s, f) if e >= 0 else (-f, -s)
|
|
for (e, s, f) in zip(avgError, slow_offset, fast_offset)
|
|
]
|
|
)
|
|
|
|
fig2 = figure(x_range=fig1.x_range)
|
|
fig2.varea(
|
|
t,
|
|
steadystaterate + low_offset,
|
|
steadystaterate + high_offset,
|
|
alpha=0.2,
|
|
color="goldenrod",
|
|
legend_label="Deadband (won't change in rate within)",
|
|
)
|
|
fig2.line(t, inrate, color="hotpink", legend_label="Nominal in sample rate")
|
|
fig2.line(t, outrate, color="firebrick", legend_label="Nominal out sample rate")
|
|
fig2.line(
|
|
t,
|
|
steadystaterate,
|
|
color="orangered",
|
|
legend_label="Estimated in rate with drift",
|
|
)
|
|
fig2.line(
|
|
t, corrected, color="dodgerblue", legend_label="Corrected in sample rate"
|
|
)
|
|
fig2.line(
|
|
t,
|
|
hysteresiscorrected,
|
|
color="seagreen",
|
|
legend_label="Hysteresis-corrected in sample rate",
|
|
)
|
|
fig2.line(
|
|
t, configured, color="goldenrod", legend_label="Configured in sample rate"
|
|
)
|
|
|
|
fig1.legend.location = "top_left"
|
|
fig2.legend.location = "top_right"
|
|
for fig in (fig1, fig2):
|
|
fig.legend.background_fill_alpha = 0.6
|
|
fig.legend.click_policy = "hide"
|
|
|
|
tabs.append(TabPanel(child=gridplot([[fig1, fig2]]), title=str(id)))
|
|
|
|
show(Tabs(tabs=tabs))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|