Waterloo Rocketry regularly conducts engine tests, where the team measures pressures, thrust, and masses during the course of a rocket engine burn. For the purposes of the team YouTube page, we wanted to overlay an animated plot that would present the data as it evolved in real time. The problem for me, as the media lead, was to find a way to animate this plot properly.

My first attempt was naive. I knew very little of code back then, but I figured I could do it using video editing tools. This kind of task might be trivial if I were skilled with a tool like Adobe After Effects or Blender, but I unfortunately wasn’t. My low-tech, first-pass solution was to simply use a left-to-right wipe transition. I used a wipe transition to move between an image of the plot with no data, to one where the data was present. This had some pretty hilarious consequences for my workflow. For example, the width of space on the plot left and right of the lower and upper x-limits needed to be taken into account. I therefore had to physically measure those widths (like, with a ruler pressed up against my laptop screen), scale them with the x-axis, and add the resulting extra time to the wipe transition. The result was pretty terrible, as it resulted in this weird vertical line that moved across the plot like an Etch-A-Sketch eraser. Take a look at this video from 2015 to see what I mean:

It wouldn’t be until 2018 until I got fed up and looked for a better method. I googled around to try to find a pre-built solution for this problem, but couldn’t find anything. It was at that point that I decided to just do it myself, and hopefully I’d learn something along the way. It helped that by then I was more proficient with code.

The script started quite modestly. It would produce a series of graph images, each representing a single frame of an animation. I could then use video editing software to stitch all the frames together. At this basic level, there were a few interesting problems to think about. The first is that the data might be recorded at 1000 Hz, but I would need the animation to run at 30 frames per second. I needed to do a little bit of math to make sure I was plotting the correct amount of data each frame. I also had to come up with my own automatic graph limits, since the defaults wouldn’t work for our cases. For example, if the sensor registered some slight negative thrust during the pre-burn period, the graph might automatically set a negative lower limit to include that data. My solution was to compare the order of magnitude of the maximum and minimum. If the maximum was more than three orders of magnitude greater than the minimum, then the minimum would be set to zero. For graphs where all the data is negative, the reverse would apply.

Feature Creep

Since I had so much fun making the first version, I decided to keep going. Sometimes our data was noisy; it would be nice to have a built in smoothing function. I looked around for methods to smooth out noisy data and learned about Savitzky-Golay filtering. This feature was really easy to implement since it was a built-in function in SciPy.

Once, our data acquisition wasn’t configured properly and sampled at a low frequency. For those situations, It would be nice to have an interpolation feature so that the graph would evolve smoothly even if the data didn’t. Like with smoothing, interpolation functionality is built-in to SciPy, so this was pretty easy.

The remaining need to bring the exported animation frames into video editing software in order to produce the final animation is a pain point. I used OpenCV to add functionality that directly exports the complete animation into a video file, saving significant time in the timeplot animation workflow.

The last significant addition was a GUI. This was more out of self-interest than anything else; I wanted to find out what it was like to try to create a GUI in Python. The Tkinter package provides GUI tools, which I incompetently cobbled together to produce something that worked. More details can be found on my Github. I have very little formal training in software, and GUIs were a challenge. There was a good amount of frustration, confusion, and wondering why that gosh darn text wasn’t justifying on the side that I expected. The result wasn’t polished, but it did work. I think it’s a pretty decent GUI for a first attempt.

Final Thoughts

What I hear from people all the time is that if you want to learn how to code, it doesn’t really matter what you do. What matters is that you sit down and do it. With this project, I definitely found that to be true. The act of physically writing code helps to reinforce my syntactic knowledge and made me think about organization and the interesting little problems that need to be solved. This project also introduced me to Savitzky-Golay filtering, which has proven useful at my current job (at the time of writing).

This project is no longer necessary as a result of the development of gganimate.

I hope that I can continue to build my coding skills, and it’s nice for my projects to serve a useful purpose like this one.