Oscilloscope animation of an electric signal in Python

Question:

Good evening,
I am new to Python. I am trying to process a signal saved in a npy file.
This file contains an electrical signal that I want to view as I do in the laboratory with the oscilloscope, so I want to generate an animation that shows me how the signal changes over time.
Here is my attempt:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

signal = np.load('signal.npy')

fig = plt.figure()

def animation(i):
    plt.cla()
    plt.plot(signal)
    # what to do here?

anim = FuncAnimation(fig, animation, frames = len(signal), interval = 10)
plt.show()

I have no idea what to do in the animation function.
Thanks in advance and sorry for my english

Asked By: user13794156

||

Answers:

In the matplotlib documentation you can see an example of a simulation of an oscilloscope here

Answered By: borjasanlei

Since I do not have access to your signal data, I generate mine in order to run the animation. Replace my random signal with yours.
A basic code to view you signal with respect to time could be this:

# import
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# signal generation
N = 10001
stop = 100
time = np.linspace(0, stop, N)
A = 1/4*np.cos(2*np.pi*(np.abs(time - stop/2)/stop)) + 1
f = np.concatenate((1*np.ones(int(N/4)), 2*np.ones(int(N/2) + 1), 1*np.ones(int(N/4))))
signal = A * np.sin(2*np.pi*f*time) + 0.05*np.random.randn(N)

# figure preparation
fig, ax = plt.subplots(1, 1, figsize = (8*0.9, 6*0.9))
displayed_period = int(2*f.min())
span = int(N/stop/f.min())

def animation(i):
    # delete previous frame
    ax.cla()

    # plot and set axes limits
    ax.plot(time[span*i: 1 + span*(i + displayed_period)],
            signal[span*i: 1 + span*(i + displayed_period)])
    ax.set_xlim([time[span*i], time[span*(i + displayed_period)]])
    ax.set_ylim([1.1*signal.min(), 1.1*signal.max()])

# run animation
anim = FuncAnimation(fig, animation, frames = int(len(time)/span - 1), interval = 10)
plt.show()

which gives this animation:

enter image description here

Explanation

In my case, the signal is a sine wave which changes amplitude and frequency over time (plus some noise). I choose to see two complete oscillations of my signal per each frame, so I set

displayed_period = int(2*f.min())

to be sure to see at least the two complete oscillations. Then I have to define the amount of time passed through x axis between a frame and the following, so I set:

span = int(N/stop/f.min())

That being said, when you run the code, the animation function is called multiple times, in each time the i counter increases by 1. So you can use this counter to slice the time and the signal arrays: time[span*i: 1 + span*(i + displayed_period)].
In this way you plot a displayed_period number of complete oscillations and, for each frame, you scroll the x axis by span element.
You have to set displayed_period and span according to your signal properties in order to get a similar result.


If you want a little bit customization like an oscilloscope, check this code:

# import
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# signal generation
N = 10001
stop = 100
time = np.linspace(0, stop, N)
A = 1/4*np.cos(2*np.pi*(np.abs(time - stop/2)/stop)) + 1
f = np.concatenate((1*np.ones(int(N/4)), 2*np.ones(int(N/2) + 1), 1*np.ones(int(N/4))))
signal = A * np.sin(2*np.pi*f*time) + 0.05*np.random.randn(N)

# color definition
black = '#0F110D'
grey = '#3B3D3A'
yellow = '#FFFF21'

# figure preparation
fig, ax = plt.subplots(1, 1, figsize = (8*0.9, 6*0.9))
displayed_period = int(2*f.min())
span = int(N/stop/f.min())

def animation(i):
    # delete previous frame
    ax.cla()

    # set background color and plot
    ax.set_facecolor(black)
    ax.plot(time[span*i: 1 + span*(i + displayed_period)],
            signal[span*i: 1 + span*(i + displayed_period)],
            color = yellow)

    # plot axes lines
    ax.hlines(y = 0,
              xmin = 0,
              xmax = stop,
              lw = 2,
              colors = grey)
    ax.vlines(x = time[int(span*i + (1 + span*displayed_period)/2)],
              ymin = 1.1*signal.min(),
              ymax = 1.1*signal.max(),
              lw = 2,
              colors = grey)

    # set grid, axes limits and ticks
    ax.grid(which = 'major',
            ls = '-',
            lw = 0.5,
            color = grey)
    ax.set_xlim([time[span*i], time[span*(i + displayed_period)]])
    ax.set_ylim([1.1*signal.min(), 1.1*signal.max()])
    plt.tick_params(axis = 'both',
                    which = 'both',
                    bottom = False,
                    left = False,
                    labelbottom = False,
                    labelleft = False)

# run animation
anim = FuncAnimation(fig, animation, frames = int(len(time)/span - 1), interval = 10)
anim.save('oscilloscope.gif', writer = 'imagemagick')
plt.show()

I do not changed the functionalities, only the aspect of the animation:

enter image description here

Answered By: Zephyr