Post Go back to editing

ZC706+FSCOMMS5 trying to transmit and receive QPSK symbols using pyadi-iio

Category: Software

Hello I'm trying to transmit and receive (via loopback with a BNC cable) QPSK symbols on channel 0 of the FSCOMMS5 . Unfortunately the received QPSK symbols seem to have 4 levels of amplitude as opposed to 2 (-1,1) for in-phase and quadrature. In the attached image, figures 0 & 1 plot the samples of transmitted in-phase and quadrature QPSK samples respectively. Whereas figures 2 & 3 plot the received samples of in-phase and quadrature QPSK samples respectively. I understand that a phase mismatch is expected, what I don't understand is why the received stream has 4 different amplitude levels when only two were expected.

I'm including the code below. It's basically a slightly modified version of the PlutoSDR transmit and receive example on PySDR.org

import numpy as np
import adi
import matplotlib.pyplot as plt


sample_rate = 1e6 # Hz
center_freq = 915e6 # Hz
num_samps = 500000 # number of samples per call to rx()
sdr = adi.FMComms5()
sdr.tx_enabled_channels = [0]
sdr.rx_enabled_channels = [0]
sdr.sample_rate = int(sample_rate)

# Config Tx
sdr.tx_rf_bandwidth = int(sample_rate) # filter cutoff, just set it to the same as sample rate
sdr.tx_lo = int(center_freq)
sdr.tx_hardwaregain_chan0 = 0 # Increase to increase tx power, valid range is -90 to 0 dB

# Config Rx
sdr.rx_lo = int(center_freq)
sdr.rx_rf_bandwidth = int(sample_rate)
sdr.rx_buffer_size = num_samps
sdr.gain_control_mode_chan0 = 'manual'
sdr.rx_hardwaregain_chan0 = 10.0 # dB, increase to increase the receive gain, but be careful not to saturate the ADC

# Create transmit waveform (QPSK, 16 samples per symbol)
num_symbols = 1000
x_int = np.random.randint(0, 4, num_symbols) # 0 to 3
x_degrees = x_int*360/4.0 + 45 # 45, 135, 225, 315 degrees
x_radians = x_degrees*np.pi/180.0 # sin() and cos() takes in radians
x_symbols = np.cos(x_radians) + 1j*np.sin(x_radians) # this produces our QPSK complex symbols
samples = np.repeat(x_symbols, 16) # 16 samples per symbol (rectangular pulses)
samples *= 2**14 # The PlutoSDR expects samples to be between -2^14 and +2^14, not -1 and +1 like some SDRs
plt.figure(0)
plt.plot(np.real(samples[0:1000]))
plt.figure(1)
plt.plot(np.imag(samples[0:1000]))

# Start the transmitter
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
sdr.tx(samples) # start transmitting

# Clear buffer just to be safe
for i in range (0, 10):
    raw_data = sdr.rx()

# Receive samples
rx_samples = sdr.rx()
print(rx_samples)

# Stop transmitting
sdr.tx_destroy_buffer()

# Calculate power spectral density (frequency domain version of signal)
psd = np.abs(np.fft.fftshift(np.fft.fft(rx_samples)))**2
psd_dB = 10*np.log10(psd)
f = np.linspace(sample_rate/-2, sample_rate/2, len(psd))

# Plot time domain
plt.figure(2)
plt.plot(np.real(rx_samples[:1000]))
plt.figure(3)
plt.plot(np.imag(rx_samples[:1000]))
plt.xlabel("Time")

# Plot freq domain
plt.figure(4)
plt.plot(f/1e6, psd_dB)
plt.xlabel("Frequency [MHz]")
plt.ylabel("PSD")
plt.show()

The figures are also attached.

 

I'd appreciate any pointers as to why the received and transmitted data has a different number of amplitude levels.

Parents
  • Plotting constellations instead of real/imag parts will reveal what is going on. You can not expect the same amplitudes at the receive side without a phase correction. Your constellation at the receive side is a rotated version of the transmitted one.

    Dump the angles of the received samples, you will observe an offset and the angles will be like alfa+45,alfa+135, alfa+225 ...

    You can correct that offset by multiplying the received samples by exp(-j*alfa).

  • Thanks for that tip! I plotted the received constellation and noticed that it was 45 degrees out of phase. Once I corrected that offset, both the constellation plots and the plots of the receives signal vs time looked correct. My next step is to add a pre-amble. Thanks again! 

Reply Children
No Data