on
Measuring I/Q imbalance in radio receivers
What is I/Q imbalance?
In Low- and zero-IF receivers two signals (I and Q) are used to capture the incoming radio transmission. The reason for using two signal instead of one is so that frequencies below the desired reception frequency and those above can be separated. In essence, without using two signals the lower and upper sidebands would overlap, interfering with each other.
For the above to work, the I and Q signals should be relative phase of 90 degrees with respect to each other. In addition, the amplitude of the received signals in I and Q should be exactly the same. Any imperfection and some of the undesired sideband (also termed image) will leak into theother. These imperfections are said to cause “I/Q imbalance”.
How severe?
Figure 1 shows the image attenuation in dB as a function of phase imbalance ($\phi$) and gain imbalance ($g$).
For example, if there is a phase imbalance of $\phi=1$ degree and a gain imbalance of $g=1.01$ (either I or Q has 1% too much gain), then
the opposite sideband suppression is just 40 dB.

Figure 1: Image attenuation
No matter how good our signal processing is, without compensating for I/Q imbalance we cannot achieve more than 40 dB in practice.
For the mathematically inclined, the image attenuation ratio (IAR) in dB can be calculated using the following formula: $$ IAR_{dB} = 10\log_{10} \left ( \dfrac{1 + 2g \cos(\phi)+g^2}{1 - 2g \cos(\phi)+g^2} \right )$$
Measuring I/Q imbalance
The I/Q imbalance parameters $\phi$ and $g$ can be measured using statistics. Luckily this is not an expensive operation in the numerical sense. In fact, the algorithm can be executed in real-time.
For each complex input sample, I and Q, we must perform the following:
float t1 = I*Q
float t2 = I*I
float t3 = Q*Q
Then we add the t-variables to their associated accumulator. The accumulator variables are reset every N samples. A practically useful number for N is 16384.
if (counter == N)
{
calc_imbalance(accu1, accu2, accu3);
accu1 = 0;
accu2 = 0;
accu3 = 0;
counter = 0;
}
counter++;
accu1 += t1;
accu2 += t2;
accu3 += t3;
The calc_imbalance() is called once every N samples and updates the I/Q imbalance figures.
This function smoothes the accumulator variables using first-order lowpass filters:
void calc_imbalance(float accu1, float accu2, float accu3)
{
// note: saccu1, saccu2, saccu3 are stored outside
// this function and are filter state variables.
saccu1 = 0.95*saccu1 + 0.05*accu1;
saccu2 = 0.95*saccu2 + 0.05*accu2;
saccu3 = 0.95*saccu3 + 0.05*accu3;
// this reporting does not need to be done
// for every call of calc_imbalance.
// it's safe to skip the following code
// every M samples if needed:
if ((saccu2 > 0.0) && (saccu3 > 0.0))
{
float g = sqrt(saccu3 / saccu2);
float phi = asin(saccu1/saccu3)*180.0/3.1415927; // in degrees
// for small angles asin(x) can be approximated by
// x + x^3/6
// or even just x.
print("g = %f\n", g);
print("phi = %f deg.\n",phi);
}
}
For completeness, here is the boilerplate stuff:
float accu1;
float accu2;
float accu3;
float saccu1;
float saccu2;
float saccu3;
const int N = 256;
int counter = 0;
void init()
{
accu1 = 0.0;
accu2 = 0.0;
accu3 = 0.0;
saccu1 = 0.0;
saccu2 = 0.0;
saccu3 = 0.0;
}
When there is not much I/Q imbalance, this should report a gain g close to 1 and a imbalance angle phi close to zero.
References
The method implemented is described more throughly in A Low-complexity Feed-forward I/Q Imbalance Compensation Algorithm