What a view. . .

What a view. . .

Saturday, January 23, 2016

Group Delay

"EngNotes" are my engineering notes.  This is my way of creating a digital version of an engineering notebook.  This particular entry is on Group Delay.

I had one of those brain fart moments at work recently where you start to question everything and suddenly feel very small.  Why is the phase slope of a delay line negative!?  After a five minute mental battle I decided to give up and ask someone else - it was late and my brain had turned to mush.  After some really simple equations on a white board my confidant was stumped too.  All the usual sources confirmed what I had memorized, that it is negative, but I couldn't rationalize it to myself.

To my future self who begins to doubt again, don't think of phase slope induced by group delay this way:
If you increase frequency, the amount of phase traversed over a particular amount of time increases.  Therefore phase slope of a delay line is positive.
If you are stuck thinking the above, you can even write out an equation that is so simple and appears to contradict god and his uncle.  The statement above has a problem with its frame of reference.  Below is the right way to think about the problem:
Phase is always a relative measurement.  When characterizing the phase slope of a delay line, you are characterizing the phase of the output relative to the input at a particular point in time.  Think transfer function.  The output phase of a delay line will be retarded relative to the input.  Another way to put it is the output phase of a delay line will be behind (negative) relative to phase at the input of the delay line.
Equations to help set my brain straight for next time:

$ \phi(t)_{output} = 2 \Pi f(t-t_{0}) $
$ \phi(t)_{input} = 2 \Pi ft $
$ \tau = -\frac{\Delta\phi}{2\Pi \Delta f} $

Wednesday, December 30, 2015

Notes of an Aspiring Woodworker

"EngNotes" are my engineering notes.  This is my way of creating a digital version of an engineering notebook.  This particular entry is on Woodworking.  This is a living post, I will be updating it as I learn more.




Rules of the Road

  1. Always sand with the grain
    • Random orbital sanders help allow you to sand any direction due to the random nature
  2. Wood swells/expands cross/perpendicular to the grain
    • Boards tend to get wider, not longer
  3. In general, hardwoods are from deciduous trees and softwoods are from conifers
    • Hardwoods are not automatically denser than softwoods, although it is common
  4. Oil based polyurethane tends to yellow the finish, water based polyurethane doesn't
  5. Use natural brushes for oil-based finishes and synthetic brushes for latex, acrylic or water-based finishes. Rollers and rags can work for either type of finish.
  6. Always sticker lumber that has been finished to the final dimension until you get ready to use it so that air can circulate evenly around all sides of the boards.

Tips and Tricks

  1. Put painters tape on edges to help contain glue squeeze-out
  2. Use packing tape or wax paper on surfaces that may stick to squeeze-out (like clamps)
  3. Cauls are used to keep a surface straight while clamping in the other direction
  4. Use a tack cloth to clean and remove dust
    • Typically cheesecloth soaked in varnish to make it sticky
  5. Janka is the wood hardness scale.

Glues

  1. Traditional wood glue is PVA glue
    • Sets up relatively quickly, 5-10min.
    • Tightbond Type III takes twice as long to set as Typebond I and II
  2. Polyurethane glue
    • Gorilla glue is a common brand
    • Longer cure time than PVA glue
    • Expands as it dries
    • Requires moisture to cure
      • Can come from moisture in the air
      • To accelerate cure, wet the surfaces to be bonded
  3. Hide glue
    • From animal hides 
    • Water soluble
      • Don't use on things that need to be water resistant 

Finishes

  1. Oils
    • Drying oils cure in the presence of oxygen
      • Boiled Linseed Oil (BLO), Tung Oil
      • Boiled linseed cures much faster than tung because it has additives
      • These oils heat up when curing.  Old rags can spontaneously combust.
    • Non-drying oils do not dry in the presence of oxygen
      • Vegetable (peanut, olive), mineral oil
  2. Varnish is a very general term, consists of a resin dissolved in something
    • Polyurethane, alkyd, phenolic are common resins
    • Often varnish can be mixed with oils 
  3. Lacquer
    • Hardens through evaporation of solvent, not chemical reaction
    • Can be redissolved by a solvent
  4. Shellac is bug poop
    • Like lacquer, it hardens through evaporation of a solvent
    • Alcohol is a common solvent
    • Since alcohol is a solvent, its not recommended for surfaces like bar tops
    • Can be affected by heat
  5. Lacquer and shellac tend to be harder but more brittle than varnish
  6. Coats of shellac and lacquer "melt into" previous coats because application of a new coat  partially dissolves the existing coats
  7. Coats of varnish build distinct layers
  8. Rubbing out varnish is more difficult than lacquer or shellac because you have to make sure you don't rub through the final coat.  If you do and reach a coat below, you can end up with "halos" at the edges of the rub-through.
    • Don't use a thinned or wiping varnish for the final coat of varnish that is to be rubbed out

Lumber Yard Lingo

  1. All 1/2 measurements are rounded up
    • Almost always true, example: A 2x4 is 1.5" x 3.5"
  2. Rough wood is ordered and priced in board feet
  3. Thickness is measured in quarter inches
    • Example: 4/4 is 1", 8/4 is 2"
  4. Milling
    • Edges
      • Specified by the number of edges that are straight
      • Examples: SL1E (straight line 1 edge), SL2E (straight line 2 edges)
    • Faces
      • H/M (Hit and Miss) the board will be rough planed on both faces.  This only scrapes off the rough and high spots.  There will be low spots left.
      • S1S (Surfaced 1 Side) the board will be planed flat on one face
      • S2S (Surfaced 2 Sides) the board will be planed flat on both faces
      • S3S (Surfaced 3 Sides) essentially S2S + SL1E
      • S4S (Surfaced 4 Sides) S2S + SL2E. All 4 sides will be milled and planed true
  5. Grade refers to the expected yield (dimensional lumber) expected from rough stock
    • FAS (First and Seconds), highest grade. min size 6in x 8ft, yield is 83% and up
    • Sel (Select) this is virtually the same as FAS, but the min board size is lesser (4in x 6ft)
    • 1 Com (#1 Common) often called Cabinet Grade, min board size is 3-in x 4-ft, yield is 66% and up
    • 2 Com (#2 Common) often called Economy Grade
  6. Formula for calculating Board Feet: Length(in) x Width(in) x Thickness(in) / 144
    • When calculating board feet, anything under one inch is rounded up to 1 inch
      • A board that's 3/4 x 5 x 17 would be calculated as (1 x 5 x 17)/144
    • Over an inch is left as-is, 2-5/8 would be left as 2-5/8
condensed from Reddit post and comments

Sunday, March 29, 2015

EngNote - J/S Ratio

"EngNotes" are my engineering notes.  This is my way of creating a digital version of an engineering notebook.  This particular entry is on Jam to Signal Ratio.
The Jam to Signal Ratio (J/S) is a useful quantity when evaluating an EW scenario.  Below is a simplistic derivation of how to calculate the Jam to Signal Ratio.

Note that the isotropic antenna effective area is present twice in the jammer's equation and only once in the target's equation.  This is due to the fact that the jammer must "convert" the received power density into power before re-transmitting.


$ \begin{matrix}
P_{target.rx} & = & P_{radar}Gain_{radar.tx}(\frac{1}{4\pi Range^{2}})\sigma _{rcs}(\frac{1}{4\pi Range^{2}})(\frac{\lambda ^{2}}{4\pi})Gain_{radar.rx} \\
P_{jam.rx} & = & P_{radar}Gain_{radar.tx}(\frac{1}{4\pi Range^{2}})(\frac{\lambda ^{2}}{4\pi})Gain_{sys}(\frac{1}{4\pi Range^{2}})(\frac{\lambda ^{2}}{4\pi})Gain_{radar.rx} \\
\frac{Jam}{Signal} & = & \frac{P_{jam.rx}}{P_{target.rx}} \\
 & = & (\frac{\lambda ^{2}}{4\pi})Gain_{sys}(\frac{1}{\sigma _{rcs}}) \\
\end{matrix} $
Where:

$ (\frac{\lambda ^{2}}{4\pi}) = IsotropicAntenna_{EffectiveArea} $


EngNote - Polarization

"EngNotes" are my engineering notes.  This is my way of creating a digital version of an engineering notebook.  This particular entry is on Polarization.
Textbooks and online references often don't give a complete and practical discussion of how to calculate the impact of a wave's polarization on the magnitude and phase of a received signal.  As a result, I made a polarization toolbox for Matlab to serve as a quick reference.  There is a link to the toolbox in the Code section below.


Cross Polarization Discrimination for a Circular Antenna

$ XPD(dB) = 20log_{10}\frac{axRatio+1}{axRatio-1}$



Code

GitHub

Good References

[1] Polarization in Electromagnetic Systems, Warren Stutzman, 1992
The best practical resource on polarization I have found



EngNote - Linear Least Squares

"EngNotes" are my engineering notes.  This is my way of creating a digital version of an engineering notebook.  This particular entry is on Linear Least Squares.



I often find myself using linear least squares curve fitting to estimate a data set.  Below is a quick write-up of linear-least squares that includes a derivation AND some sample Matlab code.  After the write-up I've also included more advanced versions of linear least squares curve fitting - weighted, constrained, and regularized linear least squares.  Often times I have found these variants to be necessary for the problem at hand.  At the very end there are some links to reference material that is invaluable.


Linear Least Squares Write-Up

The Problem
Formulating an equation to describe how one variable depends on another given a data set (xi,yi) i = 1,2,3,...,n where xi is the independent variable and yi is the dependent variable is known as curve fitting. One quantitative way to assess the quality of an equation's fit to the data set is by finding the sum of the squares of the offset (or residual) between the data and the describing equation.

Illustration 1: Example Data Set and Fit Equation

Introduction
The quality of an equation's fit to a data set is important. Linear least squares is a common and straightforward method used to find a function that best fits a data set. Keep in mind that linear least squares assumes the data set being modeled can be represented as a linear combination of basis functions. The basis functions do not need to be linear in nature, but the combination of them must.

Consider trying to fit a polynomial to a data set. The method of linear least squares will attempt to find the coefficients ( a,b,c,... ) of the polynomial p = a + bx + cx2 + … that minimize the sum of the squares of the offsets:

$S=\sum\limits_{i=1}^{n}(a+bx_{i}+cx^{2}_{i}+...-y_{i})^{2}$

1, x, x2, … compose the basis functions of the fit. Notice that the basis functions are not confined to be linear functions. The coefficients ( a,b,c,... ) form a weighting for the linear combination of the basis functions.

Derivation
When S, the sum of the squares of the offsets (errors), is viewed as a function of the polynomial's coefficients ( a,b,c,... ), S is minimized when its gradient is 0. Solving for the gradient results in n gradient equations:

$\frac{\partial S}{\partial a}=0, \frac{\partial S}{\partial b}=0, \frac{\partial S}{\partial c}=0, ...$

By combining all of the gradient equations the coefficients ( a,b,c,... ) that minimize S can be solved for. Below is an example of the steps involved in fitting a first-order polynomial (a line) to a data set.

Example
Data points (xi,yi) generated by:

$y_{i} = x_{i} + noise$

First order polynomial to be fit to the data set:

$a + bx_{i}$

Equation for the sum of the squares of the error:

$S=\sum\limits_{i=1}^{n}(a+bx_{i}-y_{i})^{2}$

Solving for the two gradient equations:

$\frac{\partial S}{\partial a}=-2\sum\limits_{i=1}^{n}(y_{i}-(a+bx_{i}))=0$
$na+b\sum\limits_{i=1}^{n}x_{i} = \sum\limits_{i=1}^{n}y_{i}$
$\frac{\partial S}{\partial b}=-2\sum\limits_{i=1}^{n}(y_{i}-(a+bx_{i}))x_{i}=0$
$a\sum\limits_{i=1}^{n}x_{i}+b\sum\limits_{i=1}^{n}x_{i}^{2} = \sum\limits_{i=1}^{n}x_{i}y_{i}$

Converting the two equations into matrix form gives:

$ \begin{bmatrix} n & \sum\limits_{i=1}^{n}x_{i} \\
\sum\limits_{i=1}^{n}x_{i} & \sum\limits_{i=1}^{n}x_{i}^{2} \end{bmatrix}

\begin{bmatrix} a \\ b \end{bmatrix} = \begin{bmatrix} \sum\limits_{i=1}^{n}y_{i} \\ \sum\limits_{i=1}^{n}x_{i}y_{i} \end{bmatrix} $

Rearranging and solving for (a,b) yields:

$ \begin{bmatrix} a \\ b \end{bmatrix} =
\begin{bmatrix} n & \sum\limits_{i=1}^{n}x_{i} \\ \sum\limits_{i=1}^{n}x_{i} & \sum\limits_{i=1}^{n}x_{i}^{2} \end{bmatrix}^{-1}
\begin{bmatrix} \sum\limits_{i=1}^{n}y_{i} \\ \sum\limits_{i=1}^{n}x_{i}y_{i} \end{bmatrix} $

The general form of linear least squares is almost always given in the form of the following equation.  Reference [1] contains a detailed derivation.

$ a = (X^{T}X)^{-1}X^{T}y $


Examples

Linear Least Squares

$ a = (X^{T}X)^{-1}X^{T}y $

X contains basis functions in the columns of the matrix and different measurement states in the rows.  The vector 'a' is the coefficient vector being sought.  The vector 'y' is the measurement vector being fit.

% Linear least squares example
% ----------------------------

% Create the independent variable
x = [0:25].';

% Create the dependent variable (linear line of slope 1 in this example)
yTrue = x+5;

% Create observations of the dependent variable
% The rand call is adding noise to the observations
yMeas = x + rand(size(x)) + 5;

% Create the basis functions (model) that will be used to estimate the true 
% system. In this case, the basis functions are a constant function and a
% linear function (matches true system).
basisFunc = [ones(size(x)), x];

% Calculate the linear least squares fit
coefs = (basisFunc' * basisFunc)^-1 * basisFunc' * yMeas;
llsFit = basisFunc*coefs;

% Make a plot illustrating the fit
figure; plot(x,yMeas,'o','LineWidth',2);
hold all; plot(x,llsFit,'LineWidth',2);
grid on; legend('Measured Data','Predicted Fit');
text(1,25, ['Fit Equation:  '  num2str(coefs(2)) 'x + ' num2str(coefs(1))])
text(1,26.25, ['True Equation: y = x + 5']);


Weighted Linear Least Squares

$ a = (X^{T}WX)^{-1}X^{T}Wy $
$ W = diag(w) $

Weighted linear least squares allows one to assign less importance to the fit of particular measurement points.  A common reason to do this is if the statistics of some of the measurements you are trying to fit to are different from those of others.  An exaggerated case is given below for illustration.

Applying weights is a somewhat obvious extension.  Simply multiply the measurements and the basis function matrix by a diagonal matrix of the weight (importance) vector.

% Weighted linear least squares
% -----------------------------

% Create a dependent variable
x = [0:99].';
% Define a function to be measured/estimated (5*x)
% Add some noise to it (randn) to simulate a measurement process
y = 5*x + randn(size(x))*5;
basisFunc = [ ones(size(x)) x ];

% Calculate a plain-jane linear-least squares
coefs = (basisFunc'*basisFunc)^-1 * basisFunc' * y;

% Plot the results
figure;
subplot(3,1,1);
plot(x,y,'o');
hold all; plot(x,basisFunc*coefs,'LineWidth',2);
title('Linear Least Squares Fit With No Gross Measurement Error');
ylim([0 525]);

% Pick a measurement(s) and give it a gross error.  I strategicly picked
% two points to make it easier to see the impact.
y(100) = 1;
y(1) = 500;
% Calculate the plain-jane linear least squares with the gross error
coefs = (basisFunc'*basisFunc)^-1 * basisFunc' * y;

% Plot the results to illustrate how the gross error "pulls off" the fit
subplot(3,1,2);
plot(x,y,'o');
hold all; plot(x,basisFunc*coefs,'LineWidth',2);
title('Linear Least Squares Fit With A Gross Measurement Error');
ylim([0 525]);

% Create a weight vector - in this case the weight vector is relatively
% arbitrary to prove a point.  In general, determining the weight vector is
% the secret sauce of the weighted linear least squares.
weight = ones(size(y));
weight(100) = 0;
weight(1) = 0;
weight = diag(weight);

% Calculate the weighted linear least squares solution
coefs = (basisFunc'*weight*basisFunc)^-1 * basisFunc' * weight * y;

% Plot the weighted result to illustrate how the gross error no longer
% "pulls off" the fit
subplot(3,1,3);
plot(x,y,'o');
hold all; plot(x,basisFunc*coefs,'LineWidth',2);
title('Weighted Linear Least Squares Fit With A Gross Measurement Error');
ylim([0 525]);


Regularized Linear Least Squares

$ a = (X^{T}X + \mu I)^{-1}X^{T}y $
where $\mu$ is known as the regularization weight and $I$ is the identity matrix

If your basis functions don't model the process being fit, some are nearly dependent, or the number of basis functions is large, linear least squares can over-fit.  An over-fit solution can look good on paper but it can cause the fit to do "weird" things when evaluating a fit between measured points  (interpolation).  Said another way, it can increase a fit's sensitivity to input noise.  A common symptom of over-fitting is that the coefficients of the fit begin to grow rapidly.

One way around over-fitting is to use regularized linear least squares.  This technique adds a second objective to the least squares solution - minimizing the squared sum of the coefficients themselves.  The trick to this technique is in picking a regularization weight.  Too large of a weight and the fit favors shrinking the coefficients too much, resulting in a poor fit.  Too small of a weight and you wind up over-fitting.  Over-fitting and the balancing act of a regularization weight versus fit is illustrated by an L-curve.

Constrained Linear Least Squares

Often there are parts of the fit process I would like to control.  At one point in time I wanted the sum of the coefficients I was finding to sum to a specific value.  This can be easily accomplished by adding a faux value to all of the basis functions (adding an extra row to the X matrix) and a corresponding faux measurement to the measurement vector (adding an extra element to the y vector).

Keep in mind, this isn't a hard constraint but is part of the fitting process.  As a result, it isn't necessarily or exactly met.  You can increase the importance of the constraint over the fit by increasing the value of the faux values added.


% Constrained linear least squares
% -----------------------------

% Make up a measurement vector
meas = [-6 ; 0.7 ; 0.0];
% Define a regularization weight (not necessarily needed)
lambda = 0.00005;

basisFunc = [0.5 -3.0 0.0 ; 0.3 -1 0.0 ; 0.8 0.3 0.0 ; 0.71 2.3 0.0].';

for weightNdx = 0:99
  % Add the constraint, making the number larger increases the importance of 
  % the constraint
  meas(4) = weightNdx/10;
  basisFunc(4,:) = weightNdx/10;
  
  % Calculate the regularized linear least squares solution
  coefs = (basisFunc' * basisFunc + lambda .* eye(size(basisFunc,2)))^-1 * ...
    basisFunc' * meas;
  
  % Debug evaluation
  coefSize(weightNdx+1) = sum(coefs);
  
end

% Plot the sum of the coeficients to show it approaching one
figure; plot(linspace(0,9.9,100),coefSize,'LineWidth',2);
title('Sum of the Coefficients of the Fit');
ylabel('Sum of Coefficients');
xlabel('Weight Assigned to Sum');



Good References

[1] The Mathematical Derivation of Least Squares [Local Mirror]
http://isites.harvard.edu/fs/docs/icb.topic515975.files/OLSDerivation.pdf

[2] Least-Squares [Local Mirror]
http://stanford.edu/class/ee103/lectures/least-squares/least-squares_slides.pdf

[3] Regularized Least-Squares and Gauss-Newton Method [Local Mirror]
http://see.stanford.edu/materials/lsoeldsee263/07-ls-reg.pdf

[4] Constrained Linear Least Squares [Local Mirror]
http://people.duke.edu/~hpgavin/cee201/constrained-least-squares.pdf

[5] The L-curve and its use in thenumerical treatment of inverse problems [Local Mirror]
https://www.sintef.no/globalassets/project/evitameeting/2005/lcurve.pdf

[6] Choosing the Regularization Parameter [Local Mirror]
http://www2.compute.dtu.dk/~pcha/DIP/chap5.pdf



Saturday, December 20, 2014

pyFmRadio - Radio Broadcast Data System (RBDS) & Radio Data System (RDS)

I decided to add one last bit of functionality to my first Software Defined Radio (SDR) project - pyFmRadio.  There happens to be a low bit-rate digital signal hidden away in the FM spectrum known as Radio Broadcast Data System (RBDS) also known as Radio Data System (RDS) outside the United States.  I added a receiver for this signal to pyFmRadio.

The RBDS signal operates at 1187.5bps and if you account for the embedded block error correction code you wind up at a data rate of 730bps (1187.5 * 16/26).  What is neat about this signal is that it carries some metadata about the radio station and the current broadcast (song, artist, DJ, etc).  A handful of car radios display this data.  Apparently it is/was more prevalent in Europe than the US.

I chose to add this functionality because I wanted to learn a little bit about the mechanics of decoding a (albeit primitive) digital signal.  Some highlights of the signal include pulse-shaped BPSK modulation, Manchester coding, and cyclic block error correction coding,

Spectrum Details

Below is a cartoon image of a broadcast FM radio station's demodulated spectrum.  Off to the right, centered at 57KHz, is the RBDS (RDS) signal.  Note, 57KHz happens to be the third harmonic of the pilot tone.  This comes in handy when demodulating the signal.  The bandwidth of the RBDS signal is approximately 4.8KHz.

Cartoon Illustration of FM Spectrum Single Sideband

Below are two images from a real demodulated FM station highlighting the RBDS signal.

FM Station After Demodulation
Zoom in on RBDS Signal From Above Plot

Modulation Details

The carrier frequency of the RBDS signal is locked to the third harmonic of the 19KHz pilot tone at 57KHz.  You can see this in the above plots.  The RBDS signal is centered at 57KHz.  The signal is also phase locked.  According to the RBDS standard, the signal could also be phase locked w/an offset of 90 degrees.

The RBDS spectrum shown above is the product of a pulse-shaped BPSK signal.  The pulse shaping applied to the signal is that of a root raised cosine filter (RRC).  The root raised cosine filter is commonly used in communication systems on both the receive and transmit side.  The filter helps to bandlimit the signal and when combined with a RRC filter in the receiver, creates a raised cosine filter that reduces the inter-symbol interference (ISI) of the communications channel.  Below is a picture of the pulse-shaped BPSK RBDS signal in the time domain.

Magnitude of RBDS Signal in Time Domain

Receiver Details

The first step of an RBDS receiver is to perform the carrier and subcarrier demodulation.  First the carrier is FM demodulated.  Then the pilot tone is tripled and used to down-mix the subcarrier.  Next the resulting signal is low-pass filtered and decimated using an efficient polyphase implementation.  What is left is a baseband modulated signal.  This entire process is detailed in the image below.

Carrier and Subcarrier Demodulation

The next step in the receiver is baseband demodulation.  This step involves matched filtering, symbol synchronization, carrier synchronization, and differential Manchester decoding.  Data bits are the output. The image below illustrates this process.

Baseband Demodulation

First up in baseband demodulation is matched filtering.  The basebanded signal is match filtered using a root raised cosine filter.  Since the transmitted signal was also filtered using a root raised cosine filter, the overall result is that of a raised cosine which helps mitigate inter-symbol interference.  Below are images of the root raised cosine matched filter impulse response, the filter frequency response, and the RBDS signal after matched filtering.
Root Raised Cosine Impulse Response
Root Raised Cosine Frequency Response
Match Filtered RBDS Signal

Next the receiver performs timing recovery also known as symbol synchronization.  The receiver must know where to sample the match filtered signal to recover the symbol bits.  Nominally, an RBDS signal has a symbol rate of 2375 symbols/sec.   At the 25KHz rate of the basebanded signal, this corresponds to ~10.5 samples per symbol.  In the image directly above there are peaks and valleys.  The peaks represent symbol times.

A magnitude based symbol synchronization technique known as an "Early-Late" algorithm was chosen to perform the synchronization.  Such a method does not require carrier synchronization prior to symbol synchronization. The image below below illustrates a symbol synchronized state where only peaks are selected.  The red circles represent the samples selected for each symbol.  There are approximately 10.5 samples between each of the circles.  The output of this process is 2375 symbols/sec.
Match Filtered RBDS Signal With Symbol Synchronization

Symbol synchronization is followed by carrier synchronization.  While the signal was previously basebanded (nearly basebanded) by triple the pilot tone, there may still exist a small frequency offset and phase offset that prohibits demodulating the BPSK signal.  If the receiver attempted to convert the sampled symbols to data bits prior to carrier synchronization the process would fail.  This is easily observed by looking at a constellation diagram of the signal prior to carrier synchronization.
Unlocked Constellation Diagram

Removing these offsets is accomplished by a variation of a Costas loop.  The loop implementation is shown in the baseband demodulation diagram above.  Once the Costas loop has synchronized to the carrier, the BPSK signal can be properly decoded.  Constellation diagrams after synchronization are given below.  The first diagram shows the behavior of the receiver at the beginning stages of synchronization.  Notice the "swirls" while the loop acquires synchronization.  Once the loop is locked (carrier synchronized), the constellation diagram looks like the second.
Carrier Locking Constellation Diagram
Carrier Locked Constellation Diagram

The last step before data bits can be recovered is to perform differential Manchester decoding.  Differential Manchester encoding has the benefit that during decoding, the process is resilient to signal/phase inversion.  The process uses transitions and not absolute polarity to decode.

Receiver Bitstream Details

Now that the bits are extracted, the receiver must perform block synchronization in order to identify the data encoded by the bits.  The RBDS bitstream is made up of "groups" of 104 bits.  Each group is made up of four blocks.  Each block is made up of 26 bits and of those 26 bits, 16 are information bits and 10 are checkword bits.  Below is a snippet from the RBDS standard document illustrating this structure.

RBDS Standard Block Definition 

The 10 bits that make up the checkword are used for error correction.  However, the checkword is also "stamped" with an offset word prior to transmission.  Each block has a defined offset word applied to it.  The purpose of adding a defined offset word to each checkword is to allow the receiver to synchronize or identify the blocks.

Block Synchronization

Specific information and data is encoded in each block.  Hence it is important to synchronize and identify blocks in the bitstream.  Synchronization and block identification is accomplished by performing a block search routine.  Once synchronized, the receiver can commence decoding.

The block search routine incrementally checks the bits in the bitstream and looks for a particular syndrome.  A syndrome is a special signature that a parity check algorithm produces.  The parity check algorithm is part of the error correction process that is dual-purposed for block synchronization.  If the offset word were not applied to the checkword, the parity check algorithm would produce a predictable '0' at its output if the block contained no errors.  Adding an offset to each checkword purposefully adds an error to the block.  Now when the parity check algorithm is run on the block (now with an intentional error), it produces a non-zero output.  The value of the output (called a syndrome) depends on the error introduced.  Each block has a unique error and hence a unique syndrome.  Once a valid syndrome is found in the search routine, block synchronization is achieved and block decoding can commence.

The parity check algorithm is performed by application of a parity check matrix.  This matrix is specific to the error correction code.  It is a 26x10 matrix.  The 1x26 data vector is multiplied by the parity check matrix (bit addition is accomplished by xoring).  The output of this multiplication is the syndrome.

Error Correction

The error correction routine uses a burst-error-correcting shortened cyclic code.  This is a (26,16) block code that is able to correct burst errors (continuous errors) up to five bits in length.  Prior to running the error correction (and detection) algorithm, the syndrome corresponding to the offset word is removed from the calculated syndrome so that only unintentional errors are present in the syndrome.

One way to implement this error correction algorithm can be done via a look-up-table (LUT).  Syndromes and their corresponding error vectors for all of the possible correctable error vectors can be calculated ahead of time.  This information is stored into a LUT.  At run-time, the receiver calculates the syndrome corresponding to the current 26 bits in a block.  If the syndrome is zero, there is no error (or the error is not detectable).  If the syndrome is non-zero, the LUT is searched for that particular syndrome and if found the error vector also in the LUT is used to correct the block error.  If the syndrome is not present in the LUT, the error was detectable but not correctable.

Another method of performing error correction (and detection) from a cyclic code is to use the Meggitt algorithm.  This is the particular algorithm that is given in the RBDS Standard.  The Meggitt algorithm is an incremental shift-register based error correction/detection algorithm.  The Meggitt algorithm is the method implemented in the pyRmRadio RBDS receiver.



Code



Good References:

http://www.nrscstandards.org/DocumentArchive/NRSC-4-A%20Standard%202005.pdf

http://www.nrscstandards.org/documentarchive/NRSC-4-A%20Annexes%202005.pdf

The care and feeding of digital, pulse-shaping filters [Local Mirror]

Digital Pulse-Shaping Filter Basics [Local Mirror]
http://www.analog.com/static/imported-files/application_notes/AN-922.pdf

Digital Filtering Part 2: Pulse Shaping [Local Mirror]
http://cmclab.rice.edu/433/slides/4-FilteringPart2.pdf

Matched Filtering and Timing Recoveryin Digital Receivers [Local Mirror]
mobiledevdesign.com/images/archive/0901Litwin32.pdf

Synchronisation in Digital Receivers [Local Mirror]
http://www.cs.tut.fi/kurssit/TLT-5806/Synch.pdf

Symbol Timing Recovery for QPSK Digital Modulations [Local Mirror]
http://www.gaussianwaves.com/2013/11/symbol-timing-recovery-for-qpsk-digital-modulations/

An Introduction to Error Correcting Codes [Local Mirror]
http://circuit.ucsd.edu/~tjavidi/ECE154C/154C/Lecture_Notes_files/ErrorCorrectionI.pdf

Decoding of Cyclic Codes and Intro to BCH Codes [Local Mirror]
http://www.site.uottawa.ca/~damours/courses/ELG_5372/Lecture15.pdf

Cyclic Codes [Local Mirror]
wits.ice.nsysu.edu.tw/course/pdfdownload/95.../CC-04-CyclicCode.pdf



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