What a view. . .

What a view. . .

Sunday, November 2, 2014

EngNote - ADCs & Dithering: When adding noise is a good thing

The concept of dithering seems counter-intuitive. In short, you add noise to improve performance. Why does this work, what performance are you getting by adding noise? An example that I commonly deal with is combating quantization noise and its resulting impacts to an ADC's spurious free dynamic range (SFDR).

Quantization Overview

Let's assume we have a 3bit ADC. This ADC only has 8 unique values it can produce. An input signal gets mapped to a uniform grid (in an ideal ADC) of 8 distinct values, which is a non-linear operation. The result of this operation is quantization noise. Below is an illustration.

Quantization Noise Resulting from Sinusoidal Inputs

When an ADC is operating with sinusoidal inputs, the quantization error it produces ends up manifesting itself as spurious content in the spectrum of the digital output.  The spurious content is bad because the spurs themselves cannot be averaged out and can limit dynamic range by masking other weaker signals that may be present.

Quantization noise impacts depend on the input frequency.  There are some interesting things that result from this dependence.  As an example, consider an input frequency of fs/4 (the ADC's sampling rate divided by four).  The quantization noise creates spurious content at a single frequency which happens to be at exactly that of the input signal, fs/4.  Note the amplitude of the spur is also dependent on the phase of the signal relative to the ADC sampling points.  To see this behavior and phase dependence, try an example with a signal that is in-phase with the ADC sampling points and then again with a signal that has a phase offset.


# Fs/4 Example - 0deg phase
fs = 100;
ts = 1/fs;
endTime = 100;
time = np.linspace(0,endTime-ts,endTime*fs);
freq = fs/4;
numBits = 8;
# Generate signals
sig = np.sin(2*np.pi*freq*time);
quantSig = np.round(sig*2**(numBits-1))/2**(numBits-1);
# Fs/4 Example - 45deg phase
fs = 100;
ts = 1/fs;
endTime = 100;
time = np.linspace(0,endTime-ts,endTime*fs);
freq = fs/4;
numBits = 8;
# Generate signals
sig = np.sin(2*np.pi*freq*time+np.pi/4);
quantSig = np.round(sig*2**(numBits-1))/2**(numBits-1);

A more common situation is when the sinusoidal input is not an integer division of the sampling frequency.  In this case, spurs are spread across the spectrum.  There is a pattern to this whole madness but I don't have the specifics worked out yet.

The commonly used Signal to Quantization Noise Ratio Equation makes an assumption about the spectrum of the quantization noise.  The assumption is that the quantization error can be approximated by an uncorrelated saw-tooth waveform.  When dealing with sinusoidal inputs at specific frequency/phase combinations such as the example above, those assumptions do not hold.

$ SQNR_{dB}=6.02*numBits+1.76 $
Commonly Used Signal to Quantization Noise Ratio Equation

How Dithering Helps

So how does dithering, the act of adding random noise, help?  Often times to find signals below the noise floor of an ADC, "to dig really low", we integrate over many ADC samples to obtain processing gain.  However, if there are quantization spurs generated by another higher power signal going into the ADC, the low power signal being looked for may be corrupted or overlooked because of the quantization noise spurs that cannot be averaged out.  The act of dithering helps this process by decorrelating the quantization noise.

Consider a simple example where a constant (not sinusoidal) signal is sent into a 1bit ADC.  If the signal is at 0.75 (ignore units) and the ADC can, depending on its input, either output a 0.0 or 1.0, the signal will be reported as a 1.0 till the cows come him.  If multiple measurements of the ADC are integrated together, the answer will always be the same, 1.0.

ADC Equation
$ \begin{matrix} ADC\left ( x \right ) = 0.0, & x < 0.5\\ ADC\left ( x \right ) = 1.0, & x \geq 0.5 \end{matrix} $ 
ADC Integration
$ \frac{1}{N}\sum_{n=0}^{N-1}ADC\left (x_{n} \right ) = \frac{1}{N}\sum_{n=0}^{N-1}ADC\left (0.75 \right ) = \frac{1}{N}\sum_{n=0}^{N-1}1.0 = 1.0 $

If Guassian random noise is added to the input, a form of dithering, the input to the ADC will vary.  With the right amount of noise, the signal will occasionally be below the 0.5 threshold and other times above.  If averaging is employed on this signal, the result of the averaging can be much closer to the true value of the signal attempting to be measured.

ADC Equation
$ \begin{matrix} ADC\left ( x \right ) = 0.0, & x < 0.5\\ ADC\left ( x \right ) = 1.0, & x \geq 0.5 \end{matrix} $ 
ADC Integration
$ \frac{1}{N}\sum_{n=0}^{N-1}ADC\left (0.75 + N\left ( 0,1 \right ) \right ) = \frac{1}{N}\sum_{n=0}^{N-1}ADC\left (N\left ( 0.75,1 \right ) \right ) $
$ \lim\limits_{N\rightarrow\infty} \frac{1}{N}\sum_{n=0}^{N-1}ADC\left (N\left ( 0.75,1 \right ) \right ) \approx 0.75 $

The reason for the approximation above is because the amount of decorrelation (reduction in spurs) depends on the amplitude of the noise added.  If the Guassian noise added is much smaller than the quantization error, there will be little benefit.  In the extreme case, if the added noise is zero then the results are back to where they started where averaging has no benefit.  In the other extreme case, if the Guassian noise is extremely large, a large amount of averaging is required to obtain an accurate measurement due to the error in the noise itself.  A rule of thumb used to obtain a balance between these two extremes is to try to ensure that the Guassian noise is ~6dB larger than that of the quantization noise.  Depending on application, this should be treated on a case-by-case basis. 

Wrap Up

Putting everything together, below is an illustration of how dithering can help.  Note that the spurs are reduced (below the noise floor introduced by the Guassian noise) and averaging is accomplished via the size of the FFT employed to create the spectrum.
# --------------------------------------------------------------------------------------
# Complex Dither Example
# --------------------------------------------------------------------------------------
fs = 100;
ts = 1/fs;
endTime = 1000;
time = np.linspace(0,endTime-ts,endTime*fs);
freq = fs/8.1;
freqVect = np.linspace(-1,1,len(time))*fs/2;
numBits = 8;
win = np.hamming(len(time));
signal = np.sin(2*np.pi*freq*time+np.pi/4);

sigPlusNoise = signal + np.random.randn(len(signal)) / 2**numBits;
quantSig = np.round(signal*2**(numBits-1))/2**(numBits-1);
quantSigPlusNoise = np.round(sigPlusNoise*2**(numBits-1))/2**(numBits-1);

sigSpect = np.fft.fft(win*signal);
sigSpect = np.fft.fftshift(sigSpect);
spurSpect = np.fft.fft(win*(signal-quantSig));
spurSpect = np.fft.fftshift(spurSpect);
noiseSpect = np.fft.fft(win*(quantSigPlusNoise-signal));
noiseSpect = np.fft.fftshift(noiseSpect);
plot.figure();
plot.plot(freqVect,20*np.log10(abs(sigSpect)),linewidth=2.0);
plot.plot(freqVect,20*np.log10(abs(spurSpect)),linewidth=2.0);
plot.plot(freqVect,20*np.log10(abs(noiseSpect)),linewidth=2.0);
plot.xlim(-fs/2,fs/2);
plot.ylim(0,100);
plot.title('Quantization Noise Dither Example');
plot.xlabel('Frequency (Hz)');
plot.ylabel('Amplitude (dBx)');
plot.legend(['True Signal','Quantization Noise','Dithered Quantization Noise']);
plot.grid();
plot.show();


Good References

http://www.utdallas.edu/~cpb021000/EE%204361/Great%20DSP%20Papers/Harris%20on%20Windows.pdf
http://www.analog.com/static/imported-files/tutorials/MT-001.pdf
Analog Devices Application Note [local mirror]
http://www.analog.com/static/imported-files/tutorials/MT-003.pdf

Thursday, October 9, 2014

pyFmRadio - A Stereo FM Receiver For Your PC



My first software defined radio (SDR) project is a stereo FM receiver.  The project uses a rtl-sdr receiver and python under Linux.  It probably works on Win/Mac but I haven't verified that it does.  Design details are given below along with some nice supporting plots.  Skip to the bottom for the code.  Oh and this was my first time using python.  Have mercy.  ;)


Basics of FM Modulation

First things first.  Lets discuss how FM radio works.  Audio (voice, music, etc) is encoded onto a higher frequency signal (carrier) by varying the frequency of the carrier based on the amplitude of the audio signal.


Initial Signals
$Audio = A(t)$
$Carrier = \sin(2\pi f_ct )$

Frequency Modulated Signal
$Inst. Frequency = f_c + kA(t)$
$Phase = 2\pi\int\limits_0^tf_c + kA(t)\mathrm{d}t$
$Signal=\sin(2\pi f_ct + 2\pi k\int\limits_0^tA(t)\mathrm{d}t)$


A couple of things to remember in the above equations.
  • The audio signal is in units of amplitude.  Volts, quanta, etc.
  • The variable 'k' is a scaling term that controls the allowable frequency swing or bandwidth of the modulated signal.  It has units of $^{Hz}\!/\!_{Amp}$.
  • The frequency of the modulated signal is directly related to the amplitude of the audio signal.  If the amplitude is zero, the frequency of the modulated signal is equal to the carrier frequency.
  • Since $Frequency=\,^{\mathrm{d}phase}\!\!/\!_{\mathrm{d}t}$, the phase of the modulated signal is the integral of the frequency.  Since the carrier's frequency is unchanging, its integral is straight forward.  The integral of $A(t)$ generally cannot be simplified since it depends on a dynamic changing signal.  #RealWorldCalculus

Python code to create plot:
# Import the plotting library
from matplotlib import pyplot as plot

# Define the setup
fCarrier = 10;
fAudio = 1;
fs = 1000;
timeEnd = 1;
time = np.linspace(0,2,fs*timeEnd);

# Create the signals
carrier = np.sin(2*np.pi*fCarrier*time);
audio = np.sin(2*np.pi*fAudio*time);
audioInt = -np.cos(2*np.pi*fAudio*time);
freqMod = np.sin(2*np.pi*fCarrier*time + 2*np.pi*1*audioInt);

# Plot the signals
plot.plot(time,audio+3);
plot.plot(time,carrier);
plot.plot(time,freqMod-3);

# Add some description
plot.text(0.75,4.2,'Audio Signal');
plot.text(0.75,1.2,'Carrier Signal');
plot.text(0.75,-1.8,'FM Signal');

# Label and cleanup the axes
ax = plot.gca();
plot.xlabel('Time (sec)'); plot.ylim(-4.5,5); plot.title('Frequency Modulation Illustration');
ax.get_yaxis().set_ticks([]);



FM Broadcast Radio

Before diving into the design details of the FM receiver, I'll briefly discuss broadcast FM radio in the United States.  Some quick notes are immediately below and supporting illustrations follow. 
  • Each station is allowed to occupy 200KHz (Double-Side-Band) of bandwidth
  • Once demodulated, two audio signals are present, the Left+Right (mono) signal and the Left-Right (diff) signal
    • A handful of other signals are present but I am ignoring them for this project
  • The two audio signals allow for mono or stereo functionality
    • Stereo is formed by simple arithmetic: $Left =  mono+diff$, $Right=mono-diff$
  • The audio signals occupy 15KHz of bandwidth (Single-Side-Band)
  • The mono signal is at baseband while the diff signal is a Dual-Side-Band-Suppressed-Carrier (DSB-SC) 38KHz above
  • A pilot tone at 19KHz exists which can be used (after doubling) to baseband the difference signal.
FM Station Received via RTLSDR Receiver (still FM modulated)
Same FM Station As Above After Demodulation (and carrier frequency removed)
Cartoon Representation of Demodulated FM Signal (single-side-band)


Receiver Block Diagram

Having discussed FM and FM Broadcast Radio its time to jump right into the receiver design.  Below is a block diagram of pyFmRadio.

FM Demodulation and Audio Processing Block Diagram
Play-By-Play:
  • The RTLSDR receiver is set to a sample rate of 250Ksps.  It generates complex, 8bit IQ samples.
  • The receiver is centered on the carrier frequency of the signal.  This means the carrier frequency is no longer present in the FM signal equation:
$FM\,Signal=\sin(2\pi f_ct + 2\pi k\int\limits_0^tA(t)\mathrm{d}t)$ goes to $FM\,Signal=\sin(2\pi k\int\limits_0^tA(t)\mathrm{d}t)$ 
  • The "basebanded" FM signal is immediately demodulated.  The difference between consecutive samples is calculated via a delay discriminator and the arc-tangent function is used to calculate the angle of the differences.  This is approximately equal to differentiating the FM signal's phase or finding the instantaneous frequency.  You can see from the equation above that the instantaneous frequency of the basebanded signal is equal to the audio signal.
  • The Left+Right signal is singled out from the rest of the audio signals via filtering and its sampling rate is reduced to an audio sample rate.  This process is implemented as a polyphase decimation operation by making use of a nobel identity.  This implementation saves a considerable amount of processing time compared to a non-polyphase implementation where filtering is done first and then decimation if performed.
  • Using a high-q peaking filter, the 19KHz pilot tone is singled out from the demodulated signal.
  • The real-valued pilot signal is converted to an analytic signal via the Hilbert transform.
  • The frequency of the analytic pilot signal is easily doubled by computing the square of the signal.
  • The frequency-doubled pilot signal now serves as a frequency and phase coherent carrier (reconstructed carrier) that can be used to demodulate the Dual-Sideband-Suppressed-Carrier (DSB-SC) Left-Right signal.
  • The Left-Right signal is mixed down to baseband using the frequency-doubled pilot and it then undergoes the same polyphase decimation as the Left+Right signal did.
  • The two signals are scaled and DC filtered.
  • Next the two signals are passed through a de-emphasis filter.  The act of FM demodulation (specifically the derivative operation) amplifies high-frequency noise which degrades the SNR of higher frequency audio content.  To help with this, broadcasters apply a pre-emphasis filter that amplifies high frequencies relative to low frequencies.  On the receive side, the reciprocal operation (de-emphasis) is performed to flatten the signal's frequency response to its original state.  This process improves the Signal-to-Noise-Ratio (SNR) of the FM signal by reducing high-frequency noise.
  • Using addition and subtraction, the two signals (Left+Right and Left-Right) are converted into stereo signals (Left and Right).
  • That is it, now you have audio!
Viola! Audio!


Code




Copyright 2014 David Swiston

Tuesday, September 30, 2014

Kenwood KA-3500 Volume Control

I really like the vintage Kenwood KA-3500 amplifier I use as my home stereo.  It sounds great and the brushed aluminum front has a sweet retro style.  However, whenever watching movies or listening to music, I'd always find myself loath to get up to adjust the volume.  The solution?  Replace the volume circuit of the original amplifier with a digital volume control circuit that could be controlled via a remote control or a rotary encoder.

The Arduino Micro is the computing brains of the operation.  It handles decoding/interrupts from a rotary encoder and an IR sensor.  It also interfaces with the audiophile grade Burr-Brown OP2311 digital volume control driver and the LED volume display board.  Writing the embedded C and worrying about Gray Codes, interrupts, code efficiency, and low-level bit handling was an interesting diversion from what I normally think about at work.

I also designed my own PCBs using CadSoft Eagle.  For someone with little experience in board design (this guy) this was a tedious but rewarding task.  I paid special attention to the board layout, followed data sheet suggestions, and made sure to keep the digital signals away from the line-level audio signals.  The effort paid off as the circuit is dead quiet.  Next up?  Wideband RF circuit designs (yeah right)!


Finished Product

Front of Amplifier With New LED Volume Indicator
Inside of Amplifier
Volume control circuit is mounted to the heatsink on the bottom left
Volume Control Circuit (1)
Volume Control Circuit (2)


Code




Artwork


Volume Control Board

LED Volume Board

Top Board Masks



Board CAD (Eagle) Files



Bill Of Materials (BOM)




Sunday, September 28, 2014

The Awesome Radio Spectrum

I am always awestruck at the amount of invisible EM waves whipping around us at the speed of light.  Gigabits of information are bombarding us every second via these waves.  A lot of it is critical information like the ILS that guides planes when landing or the TV station that broadcasts the Bears game.  Some of it comes from the ground - FM radio, TV, and cellular.  Some of it comes from 13,000 miles away in space from things like GPS.

While flying to Chicago I decided to check out what the radio spectrum looks like 2,000ft above Chicagoland.  Using my trusty RTL-SDR and a rather lackluster antenna I was able to capture some spectrum plots amidst two skeptical row-mates who were baffled by the weirdo next to them.

It's always fascinating to be able to visualize these invisible sources of information and entertainment.  Below are three captures at different frequency bands.

A Lot of HDTV Stations

More HDTV Stations and the 700MHz Cell Band - 4G LTE anyone?

850MHz Cellular

For my own future reference and for anyone who cares to read this, below is a link to the US frequency allocations.  It really is crowded!

US Radio Frequency Allocations


Friday, September 26, 2014

Make Your Own PCB Boards

A few years ago I embarked on a project to add remote control functionality to a vintage amplifier of mine (a Kenwood KA-3500).  Sarah and I had gotten tired of having to get up during a movie to turn the volume up or down but my appreciation for vintage audio gear wouldn't let me replace the amplifier.

While working on this project I discovered an easy to use and inexpensive place to get PCB boards made.  It's known as OSH Park and is run by a fellow who lives out on the US west coast (Portland I believe).  He collects individuals' orders (CAD artwork), groups them onto larger panels, and then ships them out to a fab house.  The panels ship back to him, he breaks them down into individual boards, and then ships them back to you.  The boards look fantastic!

I've attached a picture of one of the boards I made using OSH Park's service.

Volume Control Board (2.5" x 2")

OSH Park
https://oshpark.com/