I would like to use Gardner loop to remove the timing offset in order to demodulate the signal. My signal contains the frequency offset, however I know that the Gardner loop can handle substantial frequency offsets and that is why I apply it.
I have written the following piece of code:
def Gardner_loop(time_support, sig): sample_rate = 1/time_support sps = int(sample_rate/f_symb) # samples per symbol sig_n_1 = int(np.ceil(sps/2)) # x(n-1) sig_n_2 = int(sps) # x(n-2) loop_filter = f_symb//20 # BW of the loop filter loop_num = 10000 error_symbol = np.zeros(loop_num) error_list =  sig_wo_time_err =  step = 0 for i in range(1, loop_num): error_symbol[i] = sig.real[sig_n_1 + step + int(error_symbol[i-1]*loop_filter)]\ *(sig.real[sig_n_2 + step + int(error_symbol[i-1]*loop_filter)]\ - sig.real[step+int(error_symbol[i-1]*loop_filter)])\ + sig.imag[sig_n_1 + step + int(error_symbol[i-1]*loop_filter)]\ *(sig.imag[sig_n_2 + step + int(error_symbol[i-1]*loop_filter)]\ - sig.imag[step+int(error_symbol[i-1]*loop_filter)]) step+=sps error_list.append(error_symbol[i]) # Update sig_wo_time_err.append(sig.real[sig_n_2+step+int(error_symbol[i-1]*loop_filter)] \ + 1j*sig.imag[sig_n_2+step+int(error_symbol[i-1]*loop_filter)]) error_to_plot = np.array(error_symbol) plt.plot(np.arange(1, loop_num+1), error_to_plot) plt.xlabel('loop number') plt.ylabel('error') return np.array(sig_wo_time_err)
to remove timing offset. The BW of the loop filter I have selected from this article
In this case,
sps = Number of samples per symbol = sampling frequency / symbol rate = 4.7. However, it seems to me that the loop is not locked since the error fluctuates as in the picture below:
This is not interpolated signal.
My idea was to increase the number of samples per symbol to reduce the error and lock the loop. After interpolation, the number of samples per symbol = interpolated rate/symbol rate = 5. However, the error still remains and is depicted below:
I have spent many hours trying to tackle on this problem, however I still do not understand what am I doing wrong. So my question is: do I make a mistake somewhere in the code, or do I miss something else to remove the timing offset?
I have updated the code where I save the signal.
My constellation without interpolation for 1000 samples before timing recovery looks like this:
After symbol timing recovery, again for 1000 points (or for 1000 loops in the Gardner loop)
For the same 1000 loops, my error is represented in the picture below
Note that if I plot more than 1000 samples, constellation becomes not readable anymore (which I think is due to the phase offset)