aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/motan/motan_graph.py
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2021-07-29 16:59:20 -0400
committerKevin O'Connor <kevin@koconnor.net>2021-08-22 12:22:11 -0400
commit42080751d73ded254e0283c9b6aa6a66d1f352aa (patch)
tree2e1bb4fc2c96d8bacdd3e2374e12ceae4e503256 /scripts/motan/motan_graph.py
parent171a73e3801d0b5b15117ab6b287bf07b96f28b2 (diff)
downloadkutter-42080751d73ded254e0283c9b6aa6a66d1f352aa.tar.gz
kutter-42080751d73ded254e0283c9b6aa6a66d1f352aa.tar.xz
kutter-42080751d73ded254e0283c9b6aa6a66d1f352aa.zip
motan_graph: Initial support for graphing data log
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'scripts/motan/motan_graph.py')
-rwxr-xr-xscripts/motan/motan_graph.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/scripts/motan/motan_graph.py b/scripts/motan/motan_graph.py
new file mode 100755
index 00000000..b593c1ce
--- /dev/null
+++ b/scripts/motan/motan_graph.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+# Script to perform motion analysis and graphing
+#
+# Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+import sys, optparse, ast
+import matplotlib
+import readlog, analyzers
+try:
+ import urlparse
+except:
+ import urllib.parse as urlparse
+
+
+######################################################################
+# Graphing
+######################################################################
+
+def plot_motion(amanager, graphs):
+ # Generate data
+ for graph in graphs:
+ for dataset, plot_params in graph:
+ amanager.setup_dataset(dataset)
+ amanager.generate_datasets()
+ datasets = amanager.get_datasets()
+ times = amanager.get_dataset_times()
+ # Build plot
+ fontP = matplotlib.font_manager.FontProperties()
+ fontP.set_size('x-small')
+ fig, rows = matplotlib.pyplot.subplots(nrows=len(graphs), sharex=True)
+ if len(graphs) == 1:
+ rows = [rows]
+ rows[0].set_title("Motion Analysis")
+ for graph, graph_ax in zip(graphs, rows):
+ graph_units = graph_twin_units = twin_ax = None
+ for dataset, plot_params in graph:
+ label = amanager.get_label(dataset)
+ ax = graph_ax
+ if graph_units is None:
+ graph_units = label['units']
+ ax.set_ylabel(graph_units)
+ elif label['units'] != graph_units:
+ if graph_twin_units is None:
+ ax = twin_ax = graph_ax.twinx()
+ graph_twin_units = label['units']
+ ax.set_ylabel(graph_twin_units)
+ elif label['units'] == graph_twin_units:
+ ax = twin_ax
+ else:
+ graph_units = "Unknown"
+ ax.set_ylabel(graph_units)
+ pparams = {'label': label['label'], 'alpha': 0.8}
+ pparams.update(plot_params)
+ ax.plot(times, datasets[dataset], **pparams)
+ ax.legend(loc='best', prop=fontP)
+ ax.grid(True)
+ rows[-1].set_xlabel('Time (s)')
+ return fig
+
+
+######################################################################
+# Startup
+######################################################################
+
+def setup_matplotlib(output_to_file):
+ global matplotlib
+ if output_to_file:
+ matplotlib.use('Agg')
+ import matplotlib.pyplot, matplotlib.dates, matplotlib.font_manager
+ import matplotlib.ticker
+
+def parse_graph_description(desc):
+ if '?' not in desc:
+ return (desc, {})
+ dataset, params = desc.split('?', 1)
+ params = {k: v for k, v in urlparse.parse_qsl(params)}
+ for fkey in ['alpha']:
+ if fkey in params:
+ params[fkey] = float(params[fkey])
+ return (dataset, params)
+
+def list_datasets():
+ datasets = readlog.list_datasets() + analyzers.list_datasets()
+ out = ["\nAvailable datasets:\n"]
+ for dataset, desc in datasets:
+ out.append("%-24s: %s\n" % (dataset, desc))
+ out.append("\n")
+ sys.stdout.write("".join(out))
+ sys.exit(0)
+
+def main():
+ # Parse command-line arguments
+ usage = "%prog [options] <logname>"
+ opts = optparse.OptionParser(usage)
+ opts.add_option("-o", "--output", type="string", dest="output",
+ default=None, help="filename of output graph")
+ opts.add_option("-s", "--skip", type="float", default=0.,
+ help="Set the start time to graph")
+ opts.add_option("-d", "--duration", type="float", default=5.,
+ help="Number of seconds to graph")
+ opts.add_option("--segment-time", type="float", default=0.000100,
+ help="Analysis segment time (default 0.000100 seconds)")
+ opts.add_option("-g", "--graph", help="Graph to generate (python literal)")
+ opts.add_option("-l", "--list-datasets", action="store_true",
+ help="List available datasets")
+ options, args = opts.parse_args()
+ if options.list_datasets:
+ list_datasets()
+ if len(args) != 1:
+ opts.error("Incorrect number of arguments")
+ log_prefix = args[0]
+
+ # Open data files
+ lmanager = readlog.LogManager(log_prefix)
+ lmanager.setup_index()
+ lmanager.seek_time(options.skip)
+ amanager = analyzers.AnalyzerManager(lmanager, options.segment_time)
+ amanager.set_duration(options.duration)
+
+ # Default graphs to draw
+ graph_descs = [
+ ["trapq:toolhead:velocity?color=green"],
+ ["trapq:toolhead:accel?color=green"],
+ ["deviation:stepq:stepper_x-kin:stepper_x?color=blue"],
+ ]
+ if options.graph is not None:
+ graph_descs = ast.literal_eval(options.graph)
+ graphs = [[parse_graph_description(g) for g in graph_row]
+ for graph_row in graph_descs]
+
+ # Draw graph
+ setup_matplotlib(options.output is not None)
+ fig = plot_motion(amanager, graphs)
+
+ # Show graph
+ if options.output is None:
+ matplotlib.pyplot.show()
+ else:
+ fig.set_size_inches(8, 6)
+ fig.savefig(options.output)
+
+if __name__ == '__main__':
+ main()