SDR# has an original algorithm that compensates gain and phase imbalances between the IQ channels. Without this compensation strong signals are mirrored with respect to the center of the spectrum. All quadrature sampling receivers have their imaging problems. Solutions that were developed in this regard scale from manual adjustment of phase and gain parameters, to much complex mathematical models that find out these parameters by analyzing IQ signals and do some magic. The algorithm I developed is somewhere between these two extremes. Here’s a brief description of how it works:

The algorithm tries to figure out parameters that maximize an objective function (image
rejection utility function, actually). The function simply sums the distances between every FFT bin and its corresponding image. We build that function such that it takes two parameters, phase and gain errors (our target). The return value is a scalar. The nearer the parameters to optimality the higher the return value. The maximum point of this function corresponds to a perfectly balanced signal. Have a look on this plot:
Image rejection

For an ideal transmitted IQ signal:
x_i = cos(2\pi f_m t)
x_i = sin(2\pi f_m t)
The output of QSD/ADC stages subject to imbalances is defined by these equations:
x_i = cos(2\pi f_m t) - sin(2\pi f_m t)(1+\alpha)sin(\phi)
x_q = sin(2\pi f_m t) (1+\alpha) cos(\phi)
We first initialize the alpha and phi parameters with 0. The algorithm then moves by random steps and evaluates the utility function until it reaches (or approaches) the maximum. Only moves that improve the problem are retained. We iterate over many buffers improving the estimation every time. This is a multi-dimensional concave optimization problem. This approach is computationally effective, and often simple to implement. Sometimes, it’s the only way to solve problems when no known/valid analytic model exists.

Here’s a signal from FUNcube Dongle without IQ correction:
Without IQ correction

The same signal with IQ correction activated. Nice image rejection, isn’t it? ;-)
With IQ correction

The source code can be found here.

Notes:

  • This algorithm supposes that the phase and gain errors are constant over the spectrum, which is the case on most sound cards, but not all.
  • I have a few ideas to extend this algorithm to cover phase and gain errors that are dependent on frequency.

Update:
As of today Jun 14 2013 the algorithm and its associated code is licensed under MS-RSL for the SDR# project and under GNU GPL v3. For a commercial license, contact me at info *at* sdrsharp.com. In either case I’ll be pleased to help you.

Tagged with:
 

21 Responses to Automatic IQ Correction Algorithm

  1. […] prog (Youssef) for Windows. It is probably the best general software for rtlsdr devices in terms ofperformance. It is updated very often as well. At the rtlsdr.org wiki there is a guide to running the […]

  2. David says:

    You mention that some cards have frequency dependent errors. Isn’t it true that any card which introduces a fixed delay between the left and right channels would have a frequency dependent phase error and thus defeat your existing clever algorithm? I know that cards like this exist because I own one; it has a skew of exactly one sampling interval between channels and it is impossible to use SDR# to give good image rejection except at low spot frequencies. Giving the user the ability to specify the delay is all that is needed to give SDR# the ability to work with cards like this without changing your IQ correction strategy.

    Anyway, just a suggestion. Keep up the good work!

  3. KD4FNC says:

    I am a newbie, also. Is there a procedure for nulling out the spike in the center of the screen (no antenna connected)? The SDR-IQ program has a control for that purpose.
    Thanks in advance.
    Bill Beamon
    KD4FNC

  4. econjack says:

    I’m a real newbie here, but I did look at the source. If running under Windows, does the compiler optimize the variables of the float data type to double to exploit the math co-processor in the Intel processor? (I’m on vacation and don’t have access to my machine, but wonder if there is boxing-unboxing going on in the generated code or is that even an issue any more?)

Leave a Reply