Square wave example

Square wave example#

Can we express basically any periodic function/signal as a sum of just cosines and sines with different frequencies?
Yes! This notebook will demonstrate that a square wave function of 1 Hz can be constructed as a sum of sine functions in the form below:

\[ x(t) = \sin(2\pi t) + \frac{\sin(3 \cdot 2\pi t)}{3} + \frac{\sin(5 \cdot 2\pi t)}{5} + \dots \frac{\sin(N \cdot 2\pi t)}{N} \]

\(x(t)\) as a function of time t , and with odd number N.

import numpy as np
from matplotlib import pyplot as plt
%config InlineBackend.figure_formats = ['svg']

First, create the square wave function and the first sine term in the above equation

from scipy.signal import square
L = 1
t = np.linspace(- L, L, 200) # 200 time steps from -1 to 1 seconds
ysquare = square(2 * np.pi * t) * np.pi / 4
x1 = np.sin(2 * np.pi* t)
plt.plot(t, ysquare, 'r--', label='square')
plt.plot(t, x1, 'b-', label='sine')
plt.axhline(0, color='k', alpha=0.5)
plt.axvline(0, color='k', alpha=0.5)
plt.legend();
../_images/15463e12ee8e454b5c1805ca6c96238b1574542919ff5d0e3f1935ef3203163c.svg

Now, we add the second sine term x2

x2 = 1 / 3 * np.sin(2 * np.pi * 3 * t)
plt.plot(t, ysquare, 'r--', label='square')
plt.plot(t, x1 + x2, 'b-', label='sine')
plt.axhline(0, color='k', alpha=0.5)
plt.axvline(0, color='k', alpha=0.5)
plt.legend();
../_images/5940029e1fde8b98760442d8ad129e8fc7190e3cc51514a5559b9df6cba6a2fb.svg

Then, the third sine term x3. We see that a square wave starts to take shape as more terms are included.

x3 = 1 / 5 * np.sin(2 * np.pi * 5 * t)
plt.plot(t, ysquare, 'r--', label='square')
plt.plot(t, x1 + x2 + x3, 'b-', label='sine')
plt.axhline(0, color='k', alpha=0.5)
plt.axvline(0, color='k', alpha=0.5)
plt.legend();
../_images/ea3fe0d56b1e4a0f455f382417e226ff6a357b95d27a3ca2c37780717613ee6b.svg

To observe the effects of adding more sine terms, we define a function to do so.

# Define variables 
x = np.zeros(len(t)) # create x array that is the same length as t 
odds = np.arange(1,1000,2) # array consists of odd numbers from 1,3,5, to 999

# Define a function that takes certain length of odds array as inputs to construct the x series
def transform(number, plot=False, print_value=False):
    """
    number: the length of the odds array that is used for constructing signal x(t) in array x
    """
    for i in range(len(t)):
        term = []
        for j in range(len(odds[0:number])):
            term.append(1 / odds[j] * np.sin(2 * np.pi * odds[j] *t[i]))
        x[i] = np.sum(term)
    if plot==True:
        plt.plot(t,x, label=f'N={odds[number-1]}')
        plt.xlabel('time (sec)')
        plt.ylabel('x[t]')
        plt.legend()
    if print_value==True:
        return x

# Take a look at the x series given by different number of sine terms
# Test number of terms N = 1,9,19,49

plt.figure()
plt.plot(t, ysquare,'r--', label='square')
plt.axhline(0, color='k', alpha=0.5)
plt.axvline(0, color='k', alpha=0.5)
for number in (np.array([1, 5, 9, 25]) + 1) / 2:
    transform(int(number), True, False)
# You can replace any odd number with np.array([1, 5, 9, 25]) to see the effect of using different number of sine terms (N)
../_images/7721f645e4b9f5987bb1b7a6ab7be933983b3a07ee11053ae5aa0d0cabe513b5.svg

Using SymPy!#

The calculation of Fourier series is (among others) implemented in SymPy, see documentation. The calculations above are repeated using sympy:

import sympy as sym

x = sym.symbols('x')
N = sym.symbols('N')
f = sym.Heaviside(x+1) - sym.Heaviside(x+1/2)*2+sym.Heaviside(x)*2 - sym.Heaviside(x-1/2)*2
s = sym.fourier_series(f, (x, 0, 1))
s1  = s.truncate(1)
s5  = s.truncate(5)
s9  = s.truncate(9)
s25 = s.truncate(25)
display(s5)
\[\displaystyle \frac{4 \sin{\left(2 \pi x \right)}}{\pi} + \frac{4 \sin{\left(6 \pi x \right)}}{3 \pi} + \frac{4 \sin{\left(10 \pi x \right)}}{5 \pi} + \frac{4 \sin{\left(14 \pi x \right)}}{7 \pi} + \frac{4 \sin{\left(18 \pi x \right)}}{9 \pi}\]
p = sym.plot(f, s1, s5, s9, s25, (x, -1, 1), show=False, legend=True,ylabel='x[t]',xlabel=('time (sec)'))
p[0].line_color = 'red'
p[0].label = 'square'
p[1].label = 'N = 1'
p[2].label = 'N = 5'
p[3].label = 'N = 9'
p[4].label = 'N = 25'
p.show()
../_images/8fe4db35ad744bc8a12458442c6f1a35e14bca3e1fb11dc99062b86d99438bc4.svg

Conclusion#

We can observe that the created function becomes more like a square wave function as the number of terms (N) increases. So, a square wave function can be simulated by a sum of harmonically related sine functions.

Attribution

This chapter is written by Christiaan Tiberius. Find out more here.