Est. read time: 1 minute | Last updated: September 23, 2024 by John Gentile


Contents

Open In Colab

Frequency Error Detector (FED)

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

from rfproto import measurements, multirate, plot, sig_gen
fs = 100.0e3
f_start = -fs/2
f_end = fs/2
num_samples = int(fs) # 1 second

lfm_chirp_sig = sig_gen.cmplx_dt_lfm_chirp(100, f_start, f_end, fs, num_samples)
mid_pt = int(len(lfm_chirp_sig)/2)
margin = 1000
plot.samples(np.real(lfm_chirp_sig[mid_pt - margin:mid_pt + margin]))
plt.show()

f, t, Zxx = signal.stft(np.real(lfm_chirp_sig), fs, nperseg=100)
plt.pcolormesh(t, f, np.abs(Zxx), vmin=0, vmax=2, shading='gouraud')
plt.title('STFT Magnitude')
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.show()

png

png

i_z1 = 0
q_z1 = 0
freq_disc = np.zeros(len(lfm_chirp_sig))

for i in range(len(lfm_chirp_sig)):
    i_val = np.real(lfm_chirp_sig[i])
    q_val = np.imag(lfm_chirp_sig[i])
    a0 = i_z1 * q_val
    a1 = q_z1 * i_val
    freq_disc[i] = a1 - a0
    i_z1 = i_val
    q_z1 = q_val

plt.plot(freq_disc)
plt.show()

png

Blind, Non-Data Aided (NDA) FLL can be made by RRC/match filtering, Mth power to fold phase into sinusoid which has frequency offset at MM times the CFO, LPF’ing then using L&R type autocorrelation FED (see above) loop filtered to drive frequency error to zero.

Phase Error Detector (PED)

Combined Carrier Recovery

When frequency offset is not significant (e.x. majority of signal bandwidth is still within passband of matched filter, and/or when Coarse Frequency Correction (CFC) has already been applied upstream), frequency and phase errors can be jointly compensated in a carrier recovery scheme.

symbol_rate = 5e6
output_fs = 8.0 * symbol_rate
in_symbols = np.random.randint(0, 4, 2400).tolist()
output_iq = sig_gen.gen_mod_signal(
    "QPSK",
    in_symbols,
    output_fs,
    symbol_rate,
    "RRC",
    0.25,
)

plot.IQ(output_iq, alpha=0.1)
plot.plt.show()

png

plot.spec_an(output_iq, fs=output_fs, fft_shift=True, show_SFDR=False, y_unit="dB")
plt.show()

png

downsampled=multirate.decimate(output_iq[1:], 4)
plot.spec_an(downsampled, fs=output_fs/4, fft_shift=True, show_SFDR=False, y_unit="dB")
plt.show()

plot.IQ(downsampled, alpha=0.1)
plt.show()

png

png

References