# How to scale measured QPSK points to determine magnitude error?

I am trying to determine the magnitude error between demodulated I,Q values and reference values on a constellation plot for a QPSK signal. The reference points are at (1,0),(0,1),(0,-1), and (-1,0). Of course as the amplitude of the input signal increases, the measured points move further away from the references. My (limited) understanding is that measured points would be near the expected to make the resulting constellation useful - where the proximity is affected by the quality of the signal, but not the amplitude.

Is there a standard scaling method used to shift the measured output to the range of the references for this purpose?

• The reference should be the transmitted constellation; in other words, if you increase the input signal, your reference also increases.
– MBaz
Commented Jan 20, 2017 at 18:14
• @MBaz - Ok, but if I degrade the input signal enough, wont there be a point where I cannot trust the references? Commented Jan 20, 2017 at 18:20

Even though have AGC etc in your system, it is not ensured that the received constellation points will lie exactly at the points that you would expect from the TX constellation.

There can be residual offset, gain and phase rotation (at least). In the following , there's a simple method to estimate and remove offset and gain. Essentially, you calculate the mean of the RX constellation, which is your estimated offset. Then, after offset removal, you measure the energy of the constellation and scale the constellation accordingly, such that the constellation has same energy as tx constellation energy:

N = 10000 # number of TX symbols
QAM = np.array([1+1j, 1-1j, -1+1j, -1-1j])
txEnergy = np.var(QAM)

tx = QAM[np.random.randint(4, size=(N,))]

chanGain = 2 # channel gain (combined of AGC, channel and all other processing)
offset = 0.3-0.5j # channel offset
sigma2 = 0.3  # noise variance

noise = np.sqrt(sigma2/2) * (np.random.randn(N) + 1j*np.random.randn(N))

rx = chanGain * tx + offset + noise  # emulate effects of channel + AGC + ...
# RX is the received signal after demodulation

# estimate mean and remove it from the data
rx_mean = np.mean(rx)
rx_mean_correct = rx - rx_mean

# estimate the energy of the constellation
rx_energy = np.var(rx_mean_correct)

print ("Estimated vs real offset: %s <-> %s" % (rx_mean, offset))
print ("Estimated vs real energy: %s <-> %s" % (rx_energy, chanGain**2*txEnergy))

# scale rx constellation to have same energy as tx constellation
corrected = rx_mean_correct / np.sqrt(rx_energy) * np.sqrt(txEnergy)

# calculate constellation MSE
MSE = np.var(rx_mean_correct - tx)
print ("RX MSE: %f" % MSE)

# plot the stuff
plt.plot(corrected.real, corrected.imag, 'x', label='corrected')
plt.plot(tx.real, tx.imag, 'x', label='tx')
plt.legend();


Program output

Estimated vs real offset: (0.300844347234-0.50994604822j) <-> (0.3-0.5j)
Estimated vs real energy: 8.29630318416 <-> 8.0
RX MSE: 2.297161


In order to remove the phase rotation, you need to have some information about the transmitted symbols. I.e. after the received constellation has been processed as above, you need to compare the RX constellation with the known TX points to get the rotation between them. Then, you rotate the RX constellation back according to the estimated phase rotation.

• Fantastic. This solves the problem. Thank you for your help. Commented Jan 21, 2017 at 13:53

Communication receivers will often have an automatic gain control (AGC) feature that will adjust the gain of the front-end amplifier in order to scale the received signal to some nominal amplitude. This can be useful if you're trying to compare the demodulated soft decisions against some reference constellation like you described. If the attenuation from transmitter to receiver is constant, you could implement this with a constant scale factor, but in practical systems, feedback is typically used.

For some modulation types, AGC is almost a requirement (e.g. digital modulations that use amplitude to encode information, like QAM constellations of size greater than 4). However, for QPSK and other schemes that only use phase modulation, it's not a necessity. All of the information encoded in the signal is in its phase only; its amplitude carries no data.

• The receiver I am working with does not use AGC. I understand that for QPSK the magnitude really does not matter. So if I assumed a 0 magnitude offset for purposes of EVM and MER, would those results still be valid? Commented Jan 20, 2017 at 18:27