##ThinkDSP
This notebook contains solutions to exercises in Chapter 4: Noise
Copyright 2015 Allen Downey
Exercise: ``A Soft Murmur'' is a web site that plays a mixture of natural noise sources, including rain, waves, wind, etc. At http://asoftmurmur.com/about/ you can find their list of recordings, most of which are at http://freesound.org.
Download a few of these files and compute the spectrum of each signal. Does the power spectrum look like white noise, pink noise, or Brownian noise? How does the spectrum vary over time?
I chose a recording of ocean waves. I selected a short segment:
And here's its spectrum:
Amplitude drops off with frequency, so this might be red or pink noise. We can check by looking at the power spectrum on a log-log scale.
This structure, with increasing and then decreasing amplitude, seems to be common in natural noise sources.
Above , it might be dropping off linearly, but we can't really tell.
To see how the spectrum changes over time, I'll select another segment:
And plot the two spectrums:
Here they are again, plotting power on a log-log scale.
So the structure seems to be consistent over time.
We can also look at a spectrogram:
Within this segment, the overall amplitude drops off, but the mixture of frequencies seems consistent.
Exercise: In a noise signal, the mixture of frequencies changes over time. In the long run, we expect the power at all frequencies to be equal, but in any sample, the power at each frequency is random.
To estimate the long-term average power at each frequency, we can break a long signal into segments, compute the power spectrum for each segment, and then compute the average across the segments. You can read more about this algorithm at http://en.wikipedia.org/wiki/Bartlett's_method.
Implement Bartlett's method and use it to estimate the power spectrum for a noise wave. Hint: look at the implementation of make_spectrogram
.
bartlett_method
makes a spectrogram and extracts spec_map
, which maps from times to Spectrum objects. It computes the PSD for each spectrum, adds them up, and puts the results into a Spectrum object.
Now we can see the relationship between power and frequency more clearly. It is not a simple linear relationship, but it is consistent across different segments, even in details like the notches near 5000 Hz, 6000 Hz, and above 10,000 Hz.
Exercise: At http://www.coindesk.com you can download the daily price of a BitCoin as a CSV file. Read this file and compute the spectrum of BitCoin prices as a function of time. Does it resemble white, pink, or Brownian noise?
The slope is -1.8, which is similar to red noise (which should have a slope of -2).
Exercise: A Geiger counter is a device that detects radiation. When an ionizing particle strikes the detector, it outputs a surge of current. The total output at a point in time can be modeled as uncorrelated Poisson (UP) noise, where each sample is a random quantity from a Poisson distribution, which corresponds to the number of particles detected during an interval.
Write a class called UncorrelatedPoissonNoise
that inherits from thinkdsp._Noise
and provides evaluate
. It should use np.random.poisson
to generate random values from a Poisson distribution. The parameter of this function, lam
, is the average number of particles during each interval. You can use the attribute amp
to specify lam
. For example, if the framerate is 10 kHz and amp
is 0.001, we expect about 10 “clicks” per second.
Generate about a second of UP noise and listen to it. For low values of amp
, like 0.001, it should sound like a Geiger counter. For higher values it should sound like white noise. Compute and plot the power spectrum to see whether it looks like white noise.
Here's what it sounds like at low levels of "radiation".
To check that things worked, we compare the expected number of particles and the actual number:
Here's what the wave looks like:
And here's its power spectrum on a log-log scale.
Looks like white noise, and the slope is close to 0.
With a higher arrival rate, it sounds more like white noise:
It looks more like a signal:
And the spectrum converges on Gaussian noise.
Exercise: The algorithm in this chapter for generating pink noise is conceptually simple but computationally expensive. There are more efficient alternatives, like the Voss-McCartney algorithm. Research this method, implement it, compute the spectrum of the result, and confirm that it has the desired relationship between power and frequency.
Solution: The fundamental idea of this algorithm is to add up several sequences of random numbers that get updates at different sampling rates. The first source should get updated at every time step; the second source every other time step, the third source ever fourth step, and so on.
In the original algorithm, the updates are evenly spaced. In an alternative proposed at http://www.firstpr.com.au/dsp/pink-noise/, they are randomly spaced.
My implementation starts with an array with one row per timestep and one column for each of the white noise sources. Initially, the first row and the first column are random and the rest of the array is Nan.
The next step is to choose the locations where the random sources change. If the number of rows is , the number of changes in the first column is , the number in the second column is on average, the number in the third column is on average, etc.
So the total number of changes in the matrix is on average; since of those are in the first column, the other are in the rest of the matrix.
To place the remaining changes, we generate random columns from a geometric distribution with . If we generate a value out of bounds, we set it to 0 (so the first column gets the extras).
Within each column, we choose a random row from a uniform distribution. Ideally we would choose without replacement, but it is faster and easier to choose with replacement, and I doubt it matters.
Now we can put random values at rach of the change points.
Next we want to do a zero-order hold to fill in the NaNs. NumPy doesn't do that, but Pandas does. So I'll create a DataFrame:
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 0.676519 | 0.782480 | 0.199706 | 0.749595 | 0.796866 |
1 | 0.019476 | NaN | NaN | NaN | NaN |
2 | 0.861379 | 0.760812 | 0.837277 | NaN | NaN |
3 | 0.997684 | NaN | 0.881947 | NaN | 0.456875 |
4 | 0.766742 | NaN | NaN | NaN | NaN |
And then use fillna
along the columns.
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 0.676519 | 0.782480 | 0.199706 | 0.749595 | 0.796866 |
1 | 0.019476 | 0.782480 | 0.199706 | 0.749595 | 0.796866 |
2 | 0.861379 | 0.760812 | 0.837277 | 0.749595 | 0.796866 |
3 | 0.997684 | 0.760812 | 0.881947 | 0.749595 | 0.456875 |
4 | 0.766742 | 0.760812 | 0.881947 | 0.749595 | 0.456875 |
Finally we add up the rows.
If we put the results into a Wave, here's what it looks like:
Here's the whole process in a function:
To test it I'll generate 11025 values:
And make them into a Wave:
Here's what it looks like:
As expected, it is more random-walk-like than white noise, but more random looking than red noise.
Here's what it sounds like:
And here's the power spectrum:
The estimated slope is close to -1.
We can get a better sense of the average power spectrum by generating a longer sample:
And using Barlett's method to compute the average.
It's pretty close to a straight line, with some curvature at the highest frequencies.
And the slope is close to -1.