Click here for pdf version of this page.

Power Spectrum

Consider a signal \(y(t)\) that is defined in the interval \([0,T]\). First, let us define the quantity \(Y(f)\), which is the fourier coefficient of \(y(t)\) at the frequency \(f\): \[Y(f)=\frac{1}{T}\int_0^Tdt\,y(t)e^{-j2\pi ft}\,\]

Note that \(Y(f)\) has the same physical dimensions as \(y(t)\).

The power spectrum (or power spectral density) of \(y(t)\) is defined in terms of the fourier coefficients \(Y(f)\). \[W_y(f)=\frac{|Y(f)|^2+|Y(-f)|^2}{\Delta f} = \frac{2|Y(f)|^2}{\Delta f}\]

Here \(\Delta f=1T\). Note that if \(y(t)\) is in units of volts, then \(W_y(f)\) has dimensions of \(\mathrm{V^2/Hz}\).

For a signal \(y(t)\) which is discretely sampled (e.g., by an analog-to-digital converter) at the rate of 1 sample every \(\Delta t\), the fourier frequency \(f\) ranges from \(\left[\frac{1}{T},\frac{1}{2\Delta t}\right]\).

This python script (power_spectrum.py) produces a power spectrum for a set of input data.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
power_spectrum.py

Original version from Amar Vutha, University of Toronto
Modifications by David Bailey (November 2019)

If run as-is, this program will plot the power spectrum of some internally generated
    test data. Change "False" to "True" in the "# DATA #" section below to input data
    from an external csv file (currently "scope_capture.csv").
"""

from __future__ import division
from numpy import abs, arange, cos, fft, pi, sin, sqrt
from matplotlib import pyplot
import csv

def power_spectrum(t,y):
    N = len(t)                       # this is the number of acquired data samples
    sampling_rate = 1/(t[1]-t[0])    # this is Delta f
    # FFT refers to the Fast Fourier Transform,
    # an efficient algorithm to calculate the fourier components Y(f)
    Y = fft.fft(y)/sqrt(N) 
    f = fft.fftfreq(N,1/sampling_rate)
    
    # Y has fourier components at both positive & negative frequencies
    # we will pick out only the positive frequencies, and use them to define W_y(f)
    
    return f[1:int(N/2)],(2 * abs(Y)**2/sampling_rate)[1:int(N/2)]


# DATA #
# Change "False" to "True" in this line below to input a waveform from a file
#    Leave it as "False" to analyse the Test data after the "else:"
if False : # Open file to read data
    with open('scope_capture.csv') as csvfile:
        readCSV = csv.reader(csvfile, delimiter=',')
        t,y = [],[]
        # Tek scope capture utility puts x,y values in columns 3 & 4
        for row in readCSV:
            t.append(float(row[3]))
            y.append(float(row[4]))
else :
    # Test data to demonstrate a power spectrum plot
    #   A 1 V amplitude 6 Hz sine and a 2 Volt 10 Hz cosine
    #   Since power goes as amplitude squared, we expect the 10 Hz signal peak to be 4
    #       times as the 6 Hz signal in the power spectrum.
    t = arange(0,10,0.01)
    y = 6 + sin(2*pi*6*t) + 2*cos(2*pi*10*t)

f,W = power_spectrum(t,y)

## Log-log plot of power spectrum

fig = pyplot.figure()
ax = fig.add_subplot(111)
ax.loglog(f,W)
ax.set_xlabel("frequency [Hz]")
ax.set_ylabel("power spectral density [V$^2$/Hz]")
pyplot.show()