You know what’s great? Simulating stuff, especially simulating a whole system. There is one problem that usually shows up, though: how do you supply realistic data, similar to that you’re seeing on your scope? You can gather samples from the scope and feed it to the simulation, but doing tests on multiple data sets becomes tiresome and isn’t a robust solution.
So, how do you generate random noise for your simulation? You can use the python random package, but the random functions give you spectrally generic results (flat, Gaussian, etc.). What we want to do is take white, spectrally flat noise as an input and filter it to control its frequency spectrum. Sounds pretty simple, but how do we create this magical filter with a completely arbitrary frequency response?

Before we get too caught up in thinking about complex filters for a specific use case, it would be good to recall the FIR filter. It’s a simple filter that implements a weighted average to do its filtering. Arriving at the filter coefficients is somewhat of a mystery to some people: these coefficients are usually found by using some filtering toolbox somewhere so people don’t have to worry their pretty little heads about how the filter actually works. Luckily, the FIR filter is much easier to understand than its IIR relative. The FIR filter is uniquely useful in this case because of how its filtering is related to its coefficients: the filter’s coefficients are simply a summation of sine waves at the frequencies in the pass band. Due to the rectangular nature of commonly desired filter spectrums (pass-bands), this summation usually ends up taking the shape of a sinc pulse. The figure below shows the sine wave frequencies that define the pass-band for an arbitrary filter, and the following figure shows the summation of these sine waves.


Due to the discrete nature of the sine wave summation, the sinc pulse is somewhat deformed, but still adequately demonstrates typical FIR coefficients. Thinking a little bit more about this, we realize that an FIR filter is really just convolution of your data with a summation of all the sine waves of frequencies inside the pass band. Knowing this, we can step back a bit: if every signal is a summation of sine waves of varying frequencies, and we have a signal whose spectrum we are trying to use to filter data, why not just use the data itself as the FIR filter coefficients? The solution for this problem doesn’t even require messing around with FFTs and IFFTs. Now, our block diagram for the system is clear:

All right, now, making it in Python! You’ll need numpy and scipy to follow through this example. Matplotlib is also used for plotting purposes. First, we’ll need to create an FIR filter class. An FIR filter class exists in scipy, but it contains a lot of unneeded bloat for this application, and creating the class may help us understand FIR filters a little better. Firstly, FIR filters have two attributes: coefficients and state variables. State variables are stored data within the filter; for FIR filters, it is merely the last N inputs to the filter, where N is the number of coefficients used in the filter. So, our class will start out as:
class firFilter:
coefficients = numpy.array([])
stateVariables = numpy.array([])
The filter class needs only two functions: __init__() and processSample(). The first is the constructor the for the class, which puts everything in the right place and sets up all the important variables, in this case assigning the filter coefficients and initializing the state variable list. __init__() should look like:
def __init__(self, filterCoeffs):
if (type(filterCoeffs) == numpy.ndarray):
# Save input filter coefficients
self.coefficients = filterCoeffs
# Initialize filter state variables
self.stateVariables = numpy.array([0]*len(filterCoeffs))
else:
raise TypeError("Wrong type for filterCoeffs, "+
"should be numpy.ndarray.")
This allows anyone utilizing the class to create filters of arbitrary length. Next, the processSample() function needs to do simply that: take an input value, process the filter, and return the result. This amounts to popping the oldest sample from the state variables list and appending the newest sample. Thanks to some optimizations in numpy, processing the filter can be done with a simple matrix multiplication and sum. The processSample() function will look like:
def processSample(self, inputSample):
# Add new sample to state variables, remove oldest one
self.stateVariables = numpy.append(numpy.delete(self.stateVariables, 0),
inputSample)
# Calculate filter output
return sum(numpy.multiply(self.coefficients, self.stateVariables))
Simple, right? Now that we’ve created our FIR class, we’ll need to create the noise shaping class that utilizes it. It will need an instance of our new FIR class, as well as an array to hold the sampled noise data. It’ll start out like:
class shaper:
# This list of doubles controls signal amplitude across the frequency
# spectrum.
sampleNoise =numpy.array( [])
# This is te FIR filter instance used to filter noise
noiseFilter = None
Similar to the FIR class, this one will only need a couple functions: __init__() and getNoise(). __init__() (the class constructor) will need the noise sample as a list, and will construct the FIR filter instance:
def __init__(self, sampleData):
# Check sampleData is of correct type
if (type(sampleData) in [list, numpy.ndarray]):
self.sampleNoise = numpy.array(sampleData)
else:
raise TypeError("Incorrect type for sample data"+
". It should be numpy.ndarray or list.")
self.noiseFilter = firFilter(self.sampleNoise)
The getNoise() is even simpler. It requires no input, and supplies a random, spectrally shaped noise sample:
def getNoise(self):
return self.noiseFilter.processSample(random.random())
Now, we can test our hypothesis! To start, the sampled “noise” will be a sine wave of arbitrary frequency. This will make verification much easier, and it’s very easy to generate. It looks like:

Now, we can insert some code at the end of our python file to test out the noise generator:
mySampleData = [numpy.sin(numpy.pi*a/10) for a in range(0, 1024)]
x = shaper(sampleData = mySampleData)
from matplotlib import pyplot as pp
import scipy
for i in range(0, 5):
data = []
for i in range(0, 1024):
data.append(x.getNoise())
pp.subplot(2, 1, 1)
pp.plot(data)
pp.title("Generated Noise")
pp.subplot(2, 1, 2)
pp.plot(abs(scipy.fft(data)))
pp.title("Generated Noise Spectrum")
pp.show()
This will generate 5 sets of simulated noise. The length of noise generated can be arbitrary, but to avoid confusion, the output data will be of the same length as the input (different lengths makes the FFT result different between source and simulation data). A single instance of this simulated noise looks like:

And 5 samples of this generated noise looks like:

As we can see, we’ve succeeded in creating a noise of arbitrary frequency spectrum. Now we can add this noise to our input signal (or even simulate our input signal!) with a single function call: shaper.getNoise(). When using this class, make sure that you sample the noise data at the same rate that the system you’re simulating does, or decimate it down to the correct rate! Using a different sampling rate will stretch or squish the noise spectrum and make it inaccurate.
Thanks for reading!