This notebook illustrates the use of the SciPy library by providing example programs that use the discrete Fourier transform functionality of scipy.fftpack. Students with a mathematical background that includes trigonometry should be able to understand time series and Fourier analysis.
To prepare yourself for understanding the material in this notebook, you should study sections 1, 2, and 4 of the online book Digital Signal Processing - DSP.
To access each individual section in that online book, open the Contents in the upper-left of the screen. Then click on the section title. This will expose the subsections in that section. Click on a subsection title to cause the contents of the subsection to appear in the right-most portion of the screen.
The program code in that book was written using the Java programming language. However, you are not expected to know or to understand that Java program code. Instead, you are expected to learn the mathematical concepts that the Java code was designed to implement and to apply that knowledge to the use of the SciPy library. You should be able to learn the concepts by studying the text and observing the comments in the code.
Once you understand those mathematical concepts, the material in this notebook will show you how use the high-level Python functionality built into the SciPy library to do similar things by writing Python code. Don't forget, however, that the high-level Python functionality will mean very little to you if you don't understand the underlying mathematical concepts.
In reviewing the material in the online book for inclusion in this document, I discovered that there are several images missing from the online version of the book. I will import and display the missing images near the end of this notebook in the section titled The missing images so that you can refer to them in this notebook when you discover that they are missing from the online book.
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.ticker import FormatStrFormatter
from scipy import fftpack
Much of the code in the following examples is the same from one example to the next. Therefore, in order to reduce the redundancy, some of the common code has been broken out into the following utility functions.
def plotSignal(theTimeVec,theData,theLabel,theTitle):
fig,ax0 = plt.subplots(1,1,
facecolor='0.75',
linewidth=3,
edgecolor='Black')
#Create the plot
ax0.plot(theTimeVec, theData, label=theLabel)
#Decorate the plot
ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
if theLabel == 'none':
ax0.legend_.remove()
ax0.set_ylabel('Amplitude')
ax0.set_xlabel('Time in seconds')
ax0.grid(True)
ax0.xaxis.set_major_locator(ticker.MultipleLocator(10*time_step))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(2*time_step))
ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='black',
length=10,labelcolor='blue',width=2)
ax0.set_title(theTitle)
ax0.xaxis.set_major_formatter(FormatStrFormatter('%.4f'))
ax0.yaxis.set_major_formatter(FormatStrFormatter('%.1f'))
def displayInfo(sample_freq):
#Print some useful information
number_freqs = sample_freq.size
print('Number freqs =',number_freqs)
freq_interval = sample_freq[1]
print('Compute a value every ',freq_interval, 'Hz')
print('Nyquist freq range =',-(freq_interval*number_freqs/2),
' to',(freq_interval*number_freqs/2)-freq_interval)
return freq_interval
def plotSpectrum(theSampleFreq,theData,theLabel,theTitle):
fig,ax0 = plt.subplots(1,1,
facecolor='0.75',
linewidth=3,
edgecolor='Black')
# Select only the positive frequency values for plotting.
pos_mask = np.where(theSampleFreq >= 0)
freqs = theSampleFreq[pos_mask]
pos_amp = theData[pos_mask]
#Create the plot
ax0.plot(freqs, pos_amp,
label=theLabel)
#Decorate the plot
ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
if theLabel == 'none':
ax0.legend_.remove()
plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude')
ax0.grid(True)
freq_interval = theSampleFreq[1]
ax0.xaxis.set_major_locator(ticker.MultipleLocator(10*freq_interval))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(2*freq_interval))
ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='black',
length=10,labelcolor='blue',width=2)
ax0.set_title(theTitle)
ax0.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
def plotLogSpectrum(theSampleFreq,theData,theLabel,theTitle):
fig,ax0 = plt.subplots(1,1,
facecolor='0.75',
linewidth=3,
edgecolor='Black')
# Select only the positive frequency values for plotting.
pos_mask = np.where(theSampleFreq >= 0)
freqs = theSampleFreq[pos_mask]
pos_amp = theData[pos_mask]
#Create the plot
ax0.plot(freqs, pos_amp,
label=theLabel)
#Decorate the plot
ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
if theLabel == 'none':
ax0.legend_.remove()
plt.xlabel('Frequency [Hz]')
plt.ylabel('Log Amplitude')
ax0.grid(True)
freq_interval = theSampleFreq[1]
ax0.xaxis.set_major_locator(ticker.MultipleLocator(10*freq_interval))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(2*freq_interval))
ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='black',
length=10,labelcolor='blue',width=2)
ax0.set_title(theTitle)
ax0.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
ax0.set_yscale('log')
def plotComplexSpectrum(theSampleFreq,theData,theTitle):
fig,ax0 = plt.subplots(1,1,
facecolor='0.75',
linewidth=3,
edgecolor='Black')
#Create the plot
ax0.plot(theSampleFreq, theData.real,label='real')
ax0.plot(theSampleFreq, theData.imag,label='imaginary')
#Decorate the plot
ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude')
ax0.grid(True)
freq_interval = theSampleFreq[1]
ax0.xaxis.set_major_locator(ticker.MultipleLocator(10*freq_interval))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(2*freq_interval))
ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='black',
length=10,labelcolor='blue',width=2)
ax0.set_title(theTitle)
ax0.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
'''
The purpose of this function is to plot the real data resulting from
an inverse Fourier transform of a complex spectrum. If the complex
spectrum represents the Fourier transform of a real time series, the
inverse Fourier transform will reproduce the original time series and
this function will plot it.
'''
def plotInverseTransformReal(theData,timeStep,theTitle):
fig,ax0 = plt.subplots(1,1,
facecolor='0.75',
linewidth=3,
edgecolor='Black')
#Extract the real data
realData = theData.real
#Create a time base for plotting in seconds
timeBase = np.arange(0, realData.size*timeStep,timeStep)
#Create the plot using only the real data
ax0.plot(timeBase,realData,label='Real data only')
#Decorate the plot
ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
plt.xlabel('Time in seconds')
plt.ylabel('Amplitude')
ax0.grid(True)
ax0.xaxis.set_major_locator(ticker.MultipleLocator(10*timeStep))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(2*timeStep))
ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='black',
length=10,labelcolor='blue',width=2)
ax0.set_title(theTitle)
ax0.xaxis.set_major_formatter(FormatStrFormatter('%.4f'))
We will begin our exploration of time series and Fourier analysis by creating a sinusoidal signal at a frequency of 440 Hz. This frequency, (also known as the Stuttgart pitch), is the musical note of A above middle C and serves as a general tuning standard for musical pitch.
Once having created the signal, we will use the FFT function of the SciPy library to compute the complex spectrum of the signal. Then we will use the np.absolute function to convert it to an amplitude spectrum.
We will plot the raw sinusoidal signal and the amplitude spectrum of the signal. Note that most of the code in the following program involves creating the signal, plotting the signal, and plotting the spectrum. By virtue of the high level functions available in the SciPy library, only two statements are required to compute the complex spectrum and to convert it into an amplitude spectrum.
We will also save each of the images in an output jpeg file and print some useful information along the way.
freq = 440 #cycles per second (Hz)
period = 1/freq #seconds per sample
#Create 20 samples for each cycle of the sinusoidal signal
time_step = period/20 #sampling interval
print('time between samples =',time_step, 'seconds')
print('samp rate =',1/time_step, 'samp per second')
#Create a time base sufficient to describe two cycles of the
# sinusoid with an interval of time_step.
time_vec = np.arange(0, 2*period,time_step)
#Create a sinusoidal signal sampled at the times in the time base
sig = (np.sin(2 * np.pi * time_vec / period))
plotSignal(time_vec,sig,
'440 Hz sine wave','440 Hz signal @ 20 samp/cycle')
#Save the plot to an output file.
plt.savefig('sig01.jpg')
#Display the plot below.
plt.show()
We begin by calling the fftpack.fft(sig) function to compute the complex spectrum of the input signal.
Then we call the np.absolute(sig_fft) function to convert the complex spectrum into an amplitude spectrum.
The function named np.absolute returns an ndarray containing the absolute value of each element in the input array. If the input array contains complex data, (a + ib for example), the returned values are $\sqrt{ a^2 + b^2 }$. This results in an amplitude spectrum. If the square root operation were omitted, that would result in a power spectrum.
# Perform FFT on the signal. This produces a complex spectrum.
sig_fft = fftpack.fft(sig)
# Convert the complex spectrum into an amplitude spectrum.
amplitude = np.absolute(sig_fft)
Call the function named fftpack.fftfreq to get the discrete Fourier transform sample frequencies. In other words, the function returns the actual frequencies at which the spectral values are computed for a given signal length and time step size. Use these values to compute and display some useful information.
# Get the frequencies at which the amplitude was computed.
sample_freq = fftpack.fftfreq(sig.size,d=time_step)
freq_interval = displayInfo(sample_freq)
The code in this section is similar to code that you have been seeing since the middle of the prerequisite course so it shouldn't require much explanation.
A total of 40 spectral values were computed over the frequency range from -4400 Hz to +4180 Hz with a computation at 0 Hz and a frequency computation interval of 220 Hz. Thus two of the computed values (-440 Hz and 440 Hz) fall exactly on the frequency of the sinusoidal signal at 440 Hz. However, only the spectral data for the positive frequencies (0 to +4180 Hz) were plotted in this presentation.
As you can see from the plot below, the amplitude spectrum consists of a single line at 440 Hz with all other spectral values equal to zero.
plotSpectrum(sample_freq,amplitude,
'none','Amplitude spectrum for single tone @ 440 Hz.')
#Write a copy of the plot to an output file
plt.savefig('spectrum01.jpg')
#Display the plot in this notebook
plt.show()
Now we will add some background noise so that the spectrum won't be completely flat with an obvious peak at the frequency of the signal.
For this case, we will increase the frequency of the signal to 880 Hz.
# Seed the random number generator
np.random.seed(1234)
freq = 880 #cycles per second (Hz)
period = 1/freq #seconds per sample
#Create 10 samples for each cycle of the sinusoidal signal
time_step = period/10 #sampling interval
print('time between samples =',time_step, 'seconds')
print('samp rate =',1/time_step, 'samp per second')
#Create a time base sufficient to describe four cycles of the
# sinusoid with an interval of time_step.
time_vec = np.arange(0, 4*period,time_step)
#Create a sinusoidal signal sampled at the times in the time base
signal = (np.sin(2 * np.pi * time_vec / period))
#Create random noise samples for each time increment
noise = (np.random.randn(time_vec.size))
#Scale the noise and add it to the signal.
sig = signal + 1.5*noise
We begin by plotting the sinusoidal signal alone so that we can reference it when examining the plot of signal plus noise.
Then we plot the signal plus noise. As you can see from the second plot below, it is very difficult to visually identify the signal once the noise has been added.
#First plot the signal alone as a reference
plotSignal(time_vec,signal,
'none','880 Hz signal @ 10 samp/cycle w/o background noise')
#Save the plot to an output file.
plt.savefig('sig02A.jpg')
#Display the plot below.
plt.show()
#Now plot the signal plus noise
plotSignal(time_vec,sig,
'none','880 Hz signal @ 10 samp/cycle with background noise')
#Save the plot to an output file.
plt.savefig('sig02B.jpg')
#Display the plot below.
plt.show()
# Perform FFT on the signal plus noise. This produces
# a complex spectrum.
sig_fft = fftpack.fft(sig)
# Convert the complex spectrum into an amplitude spectrum.
amplitude = np.absolute(sig_fft)
# Get the frequencies at which the amplitude was computed.
sample_freq = fftpack.fftfreq(sig.size,d=time_step)
freq_interval = displayInfo(sample_freq)
Next we plot the amplitude spectrum of the signal plus noise. As you can see in the plot below, the highest peak in the amplitude spectrum is at 880 Hz, the frequency of the sinusoidal signal.
plotSpectrum(sample_freq,amplitude,'none',
'Amplitude spectrum for single tone with background noise.')
#Write a copy of the plot to an output file
plt.savefig('spectrum02.jpg')
#Display the plot in this notebook
plt.show()
Next, we will examine a more complex periodic time series: a triangular waveform.
This online document explains how you can approximate a triangular waveform with the sum of five sinusoids.
The following code creates a triangular waveform with a fundamental frequency of 440 Hz.
freq = 440 #cycles per second (Hz) for the fundamental frequency
period = 1/freq #seconds per sample for the fundamental frequency
#Create 20 samples for each cycle of the fundamental frequency
time_step = period/20 #sampling interval
print('time between samples =',time_step, 'seconds')
print('samp rate =',1/time_step, 'samp per second')
#Create a time base sufficient to describe four cycles of the
# waveform with an interval of time_step.
time_vec = np.arange(0, 4*period,time_step)
#Create a triangular wave sampled at the times in the time base
#Begin by creating the five required sinusoids at the correct
#frequencies and amplitudes.
sig1 = (np.cos(2 * np.pi * time_vec / period))
sig2 = (np.cos(2 * np.pi * time_vec * 3 / period))/9
sig3 = (np.cos(2 * np.pi * time_vec * 5 / period))/25
sig4 = (np.cos(2 * np.pi * time_vec * 7 / period))/49
sig5 = (np.cos(2 * np.pi * time_vec * 9 / period))/81
#Now add the five sinusoids together to create the triangular
#waveform.
sig = sig1 + sig2 + sig3 + sig4 + sig5
As you can see from the plot below, the sum of the five sinusoids creates a very good version of a triangular waveform.
plotSignal(time_vec,sig,'Triangular waveform',
'440 Hz triangular waveform.')
#Save the plot to an output file.
plt.savefig('sig03.jpg')
#Display the plot below.
plt.show()
# Perform FFT on the signal. This produces a complex spectrum.
sig_fft = fftpack.fft(sig)
# Convert the complex spectrum into an amplitude spectrum.
amplitude = np.absolute(sig_fft)
# Get the frequencies at which the amplitude was computed.
sample_freq = fftpack.fftfreq(sig.size,d=time_step)
freq_interval = displayInfo(sample_freq)
The following plot is a semilog plot of the amplitude spectrum of the triangular waveform. In other words, the horizontal scale is linear while the vertical scale is logarithmic. This presentation is sometimes useful for plotting data that has a large spread between the largest and smallest values.
The five peaks associated with the five sinusoids that were added together to form the triangular waveform are clearly visible in this presentation.
plotLogSpectrum(sample_freq,amplitude,'none',
'Log amplitude spectrum for triangular waveform.')
#Write a copy of the plot to an output file
plt.savefig('spectrum03A.jpg')
#Display the plot in this notebook
plt.show()
The following spectral presentation is the same as the presentation that was used for the previous examples in this notebook. As you can see, the height of the fifth spectral peak on the far right is almost too small to see. Compare this with the semilog presentation above to see the advantages and the disadvantages of each type of presentation.
plotSpectrum(sample_freq,amplitude,'none',
'Linear amplitude spectrum for triangular waveform.')
#Write a copy of the plot to an output file
plt.savefig('spectrum03B.jpg')
#Display the plot in this notebook
plt.show()
Up to this point, we have assimed that the signal being analyzed was a short segment extracted from an infinitely long time series. Of course, there are no infinitely long time series with the possible exception of the behavior of the universe.
In reality, all signals are transient in nature. For this example, we will analyze the spectrum of a signal that might be produced by striking a piano key once or plucking a guitair string once. That action produces a sound with a specific fundamental frequency that decays over time. Such a signal is often referred to as a damped signal.
As you will see, the spectrum of a transient is significantly different from the spectrum of an infinitely long time series.
The following code creates a short pulse with a fundamental frequency of 440 Hz and an amplitude that decays to zero over time.
freq = 440 #cycles per second (Hz)
period = 1/freq #seconds per sample
#Create 20 samples for each cycle of the sinusoidal signal
time_step = period/20 #sampling interval
print('time between samples =',time_step, 'seconds')
print('samp rate =',1/time_step, 'samp per second')
#Create a time base sufficient to describe four cycles of the
# sinusoid with an interval of time_step.
time_vec = np.arange(0, 4*period,time_step)
#Create a damped sinusoidal signal sampled at the times
#in the time base
sig = (np.exp(-600*time_vec) * np.sin(2 * np.pi * time_vec / period))
As you can see in the following plot, the signal begins with a large amplitude and decays over time to near zero amplitude.
plotSignal(time_vec,sig,'Damped sinusoid',
'440 Hz damped signal @ 20 samp/cycle')
#Save the plot to an output file.
plt.savefig('sig04A.jpg')
#Display the plot below.
plt.show()
# Perform FFT on the damped signal. This produces a complex spectrum.
sig_fft = fftpack.fft(sig)
# Convert the complex spectrum into an amplitude spectrum.
amplitude = np.absolute(sig_fft)
sample_freq = fftpack.fftfreq(sig.size,d=time_step)
Compare this amplitude spectrum with the Amplitude spectrum of the 440 Hz tone. Both spectra have a peak at 440 Hz. However, the spectrum of the damped sinusoid also contains energy at frequencies on both sides of 440 Hz whereas (theoretically), all of the energy in the infinitely long 440 Hz tone is concentrated at the single peak. In other words, the energy in a transient signal is spread continuously across the spectrum while (theoretically) all of the energy in an infinitely long periodic time series exists only at discrete lines in the spectrum.
plotSpectrum(sample_freq,amplitude,'none',
'Amplitude spectrum for damped 440 Hz sine wave')
#Write a copy of the plot to an output file
plt.savefig('spectrum04A.jpg')
#Display the plot in this notebook
plt.show()
Up to this point, we have been converting the complex spectral data returned by the FFT function into amplitude data and plotting the amplitude data. In addition, we have been plotting the amplitude for only positive frequencies.
The following code plots the real and imaginary parts of the complex spectrum for the damped signal on the same plot including both positive and negative frequencies with zero at the center. Note that the real part is symmetric about zero and the imaginary part is asymmetric about zero. This will always be true for the complex spectrum of a purly real time series such as the damped signal.
plotComplexSpectrum(sample_freq,sig_fft,
'Complex spectrum of damped sinusoid')
#Write a copy of the plot to an output file
plt.savefig('spectrum04B.jpg')
#Display the plot in this notebook
plt.show()
In theory, it is possible to create any periodic time series by adding together the required set of properly weighted sinusoidal waveforms.
We saw earlier that only five such sinusoidal waveforms must be added together to produce a reasonable representation of a triangular waveform.
We saw in this document that you can create a reasonable representation of a square waveform by adding five such sinusoidal waveforms and can create a better representation of a square waveform by adding ten such sinusoidal waveforms.
In this section, we will attempt to reconstruct the damped signal by adding together ten weighted cosine waveforms and ten weighted sine waveforms.
We begin by printing the first ten complex values from the complex Fourier transform of the damped signal. We will use the real values to weight the cosine waveforms and will use the imaginary values to weight the sine waveforms.
for i in range(10):
print(sig_fft[i])
Next we will use the real and imaginary values to create and add the weighted cosine and sine waveforms.
freq = 440 #cycles per second (Hz) for the fundamental frequency
period = 1/freq #seconds per sample for the fundamental frequency
#Create 20 samples for each cycle of the fundamental frequency
time_step = period/20 #sampling interval
print('time between samples =',time_step, 'seconds')
print('samp rate =',1/time_step, 'samp per second')
#Create a time base sufficient to describe one cycle of the
# waveform with an interval of time_step.
time_vec = np.arange(0, 1*period,time_step)
#Create a damped signal sampled at the times in the time base
#Begin by creating the required sinusoids at the correct
#frequencies and amplitudes.
real1 = (np.cos(2*np.pi*(-time_vec) / period)) * 3.0008074839
real2 = (np.cos(2*np.pi*(-time_vec)*1 / period)) * 3.15430859328
real3 = (np.cos(2*np.pi*(-time_vec)*2 / period)) * 3.67569743
real4 = (np.cos(2*np.pi*(-time_vec)*3 / period)) * 4.48044623533
real5 = (np.cos(2*np.pi*(-time_vec)*4 / period)) * 0.756914526665
real6 = (np.cos(2*np.pi*(-time_vec)*5 / period)) * (-2.94324478242)
real7 = (np.cos(2*np.pi*(-time_vec)*6 / period)) * (-2.06424616477)
real8 = (np.cos(2*np.pi*(-time_vec)*7 / period)) * (-1.40353012152)
real9 = (np.cos(2*np.pi*(-time_vec)*8 / period)) * (-1.01458999737)
real10 = (np.cos(2*np.pi*(-time_vec)*9 / period)) * (-0.772020135771)
imag1 = (np.sin(2*np.pi*(-time_vec)/ period)) * 0
imag2 = (np.sin(2*np.pi*(-time_vec)*1 / period)) * (-0.350505906093)
imag3 = (np.sin(2*np.pi*(-time_vec)*2 / period)) * (-1.00788225156)
imag4 = (np.sin(2*np.pi*(-time_vec)*3 / period)) * (-3.02740277913)
imag5 = (np.sin(2*np.pi*(-time_vec)*4 / period)) * (-7.2169300327)
imag6 = (np.sin(2*np.pi*(-time_vec)*5 / period)) * (-3.07066675783)
imag7 = (np.sin(2*np.pi*(-time_vec)*6 / period)) * (-1.10293241054)
imag8 = (np.sin(2*np.pi*(-time_vec)*7 / period)) * (-0.518899096949)
imag9 = (np.sin(2*np.pi*(-time_vec)*8 / period)) * (-0.290332304468)
imag10 = (np.sin(2*np.pi*(-time_vec)*9 / period)) * (-0.181128412721)
#Now add the sinusoids together to create the waveform.
sig = real1 + real2 + real3 + real4 + real5 \
+ real6 + real7 + real8 + real9 + real10 \
+ imag1 + imag2 + imag3 + imag4 + imag5 \
+ imag6 + imag7 + imag8 + imag9 + imag10
The following image shows the damped signal that was reconstructed using the 20 weighted sinusoids as described above. Compare this image with the image of the original damped signal. As you can see, this reconstruction is far from perfect. However it does serve to illustrate the construction of periodic waveforms by adding properly weighted sinusoids.
The next section will explain how to do this using the inverse Fourier transform.
plotSignal(time_vec,sig,'none',
'Reconstruction of damped signal')
#Save the plot to an output file.
plt.savefig('sig04B.jpg')
#Display the plot below.
plt.show()
The Fourier transform is reversible. In other words, if you compute the Fourier transform of a time series (producing a complex spectrum), you can compute an inverse Fourier transform on that complex spectrum and reconstruct the original time series. (This assumes that you do not modify the values in the complex spectrum before computing the inverse transform.)
In effect, the inverse Fourse transform is a mathematically correct implementation of what we attempted to by approximation in the previous section.
If the original time series was purely real, the imaginary part of the inverse transform will be zero and the real part of the inverse transform will reproduce the original time series.
The following code computes the inverse Fourier transform of the complex spectrum for the damped signal.
If the complex spectrum represents an unmodified version of the Fourier transform of a real time series, the real part of the inverse transform will reproduce the original time series and the imaginary part of the inverse transform will be zero.
# Perform inverse FFT on the complex spectrum.
iSig_fft = fftpack.ifft(sig_fft)
The following code plots the real part of the inverse Fourier transform demonstrating that the result is a replica of the original real time series (see Plot the damped signal) on which the Fourier transform was computed. Hence, the Fourier transform is reversible provided that the complex spectrum is not modified.
plotInverseTransformReal(iSig_fft,time_step,'Inverse transform results')
#Write a copy of the plot to an output file
plt.savefig('spectrum05.jpg')
#Display the plot in this notebook
plt.show()
The purpose of including the following images in this notebook was explained near the beginning of this notebook in the section titled Missing images.
from IPython.core.display import Image
Image('dsp00108fige.png')
Image('java1487a.png')
Image('java1487b.png')
Image('java1487c.png')
Author: Prof. Richard G. Baldwin
Affiliation: Professor of Computer Information Technology at Austin Community College in Austin, TX.
File: Fourier01.html
Revised: 05/29/18
Copyright 2018 Richard G. Baldwin