def equalize_frame(sframe, x_preamble, fft_len, cp_len, cs_len): rx_preamble = sframe[cp_len:cp_len + len(x_preamble)] agc_factor = calculate_agc_factor(rx_preamble, x_preamble) # print('AGC values:', ref_e, rx_e, agc_factor) sframe *= agc_factor frame_start = cp_len + 2 * fft_len + cs_len + cp_len H, e0, e1 = preamble_estimate(rx_preamble, x_preamble, fft_len) H_estimate = estimate_frame_channel(H, fft_len, len(sframe)) H_p = estimate_frame_channel(H, fft_len, fft_len * 9) p = sframe[frame_start:frame_start + fft_len * 9] P = np.fft.fft(p) P *= np.fft.fftshift(np.conj(H_p)) p = np.fft.ifft(P) print('equalize p:', utils.calculate_signal_energy(p)) F = np.fft.fft(sframe) F *= np.fft.fftshift(np.conj(H_estimate)) sframe = np.fft.ifft(F) s = sframe[frame_start:frame_start + fft_len * 9] print('equalize s:', utils.calculate_signal_energy(s)) # plt.plot(s.real) # plt.plot(p.real) # plt.plot(s.imag) # plt.plot(p.imag) # plt.plot(np.abs(P)) # plt.plot(np.abs(F)) # # plt.plot(np.abs(P - F)) # plt.show() return sframe
def check_preamble_properties(preamble, x_preamble): x_1st = x_preamble[0:len(x_preamble) // 2] x_2nd = x_preamble[-len(x_preamble) // 2:] if not np.all(np.abs(x_1st - x_2nd) < 1e-12): print np.abs(x_1st - x_2nd) raise ValueError('preamble timeslots do not repeat!') from correlation import cross_correlate_naive, auto_correlate_halfs from utils import calculate_signal_energy x_ampl = np.sqrt(calculate_signal_energy(x_preamble)) preamble *= x_ampl x_preamble *= x_ampl x_energy = calculate_signal_energy(x_preamble) if np.abs(2. * auto_correlate_halfs(x_preamble) / x_energy) -1. > 1e-10: raise ValueError('auto correlating halfs of preamble fails!') print 'normalized preamble xcorr val: ', np.correlate(x_preamble, x_preamble) / x_energy print 'windowed normalized preamble: ', np.correlate(preamble[-len(x_preamble):], x_preamble) / x_energy fxc = np.correlate(preamble, x_preamble, 'full') / x_energy vxc = np.correlate(preamble, x_preamble, 'valid') / x_energy nxc = cross_correlate_naive(preamble, x_preamble) / x_energy import matplotlib.pyplot as plt plt.plot(np.abs(fxc)) plt.plot(np.abs(vxc)) plt.plot(np.abs(nxc)) plt.show()
def demodulate_frame(rx_data_frame, modulated_frame, rx_kernel, demapper, data, timeslots, fft_len, H_estimate=None): ref_data = demodulate_data_frame(modulated_frame, rx_kernel, demapper, len(data)) rx_data = demodulate_data_frame(rx_data_frame, rx_kernel, demapper, len(data)) if H_estimate is None: d = np.array([1 + 1j, -1 + 1j, -1 - 1j, 1 - 1j]) / np.sqrt(2.) else: d = rx_kernel.demodulate_equalize( rx_data_frame.astype(dtype=np.complex64), H_estimate.astype(dtype=np.complex64)) d = demapper.demap_from_resources(d.astype(dtype=np.complex64), len(data)) mse = np.sum(np.abs(rx_data - ref_data)**2) / len(ref_data) print('frame EQ demodulated energy', utils.calculate_signal_energy(rx_data), utils.calculate_signal_energy(rx_data) / len(rx_data), utils.calculate_signal_energy(ref_data) / len(ref_data)) # calculate_avg_phase(rx_data, ref_data) fber = calculate_frame_ber(ref_data, rx_data) print('Frame BER: ', fber, 'with MSE: ', mse) plot_constellation(ref_data, rx_data, d, 0, timeslots * fft_len) plt.show()
def check_preamble_properties(preamble, x_preamble): x_1st = x_preamble[0:len(x_preamble) // 2] x_2nd = x_preamble[-len(x_preamble) // 2:] if not np.all(np.abs(x_1st - x_2nd) < 1e-12): print(np.abs(x_1st - x_2nd)) raise ValueError("preamble timeslots do not repeat!") from correlation import cross_correlate_naive, auto_correlate_halfs from utils import calculate_signal_energy x_ampl = np.sqrt(calculate_signal_energy(x_preamble)) preamble *= x_ampl x_preamble *= x_ampl x_energy = calculate_signal_energy(x_preamble) if np.abs(2.0 * auto_correlate_halfs(x_preamble) / x_energy) - 1.0 > 1e-10: raise ValueError("auto correlating halfs of preamble fails!") print( "normalized preamble xcorr val: ", np.correlate(x_preamble, x_preamble) / x_energy, ) print( "windowed normalized preamble: ", np.correlate(preamble[-len(x_preamble):], x_preamble) / x_energy, ) fxc = np.correlate(preamble, x_preamble, "full") / x_energy vxc = np.correlate(preamble, x_preamble, "valid") / x_energy nxc = cross_correlate_naive(preamble, x_preamble) / x_energy import matplotlib.pyplot as plt plt.plot(np.abs(fxc)) plt.plot(np.abs(vxc)) plt.plot(np.abs(nxc)) plt.show()
def synchronize_time(frame, ref_frame, x_preamble, fft_len, cp_len, samp_rate=12.e6): kframe = kernel_synchronize_time(frame, x_preamble, fft_len, cp_len, len(ref_frame)) ac = sync.auto_correlate_signal(frame, fft_len) nm = np.argmax(np.abs(ac)) print('AC start: ', nm) # cfo = 2 * np.angle(ac[nm]) / (2. * np.pi) cfo = np.angle(ac[nm]) / (2. * np.pi) print('CFO:', cfo, cfo * samp_rate / fft_len) phase_inc = sync.cfo_to_phase_increment(-cfo, fft_len) wave = sync.complex_sine(phase_inc, len(frame), 0.0) # print(len(wave), len(frame)) # frame *= wave ac = sync.auto_correlate_signal(frame, fft_len) cfo = np.angle(ac[nm]) / (2. * np.pi) print('CFO:', cfo, cfo * samp_rate / fft_len) xc = np.correlate(frame, x_preamble, 'valid') cc = sync.multiply_valid(np.abs(ac), np.abs(xc)) nc = np.argmax(np.abs(cc)) print('correlation frame start:', nc) cfo = np.angle(ac[nc]) / (2. * np.pi) print('CFO:', cfo, cfo * samp_rate / fft_len) sample_nc = nc - cp_len print('sample frame start: ', sample_nc) p_len = cp_len + 2 * fft_len + cp_len // 2 + cp_len print('data frame start: ', sample_nc + p_len) phase = np.angle(xc[nc]) # phase = 0.0 print('phase:', phase) # frame *= np.exp(-1j * phase) ref_e = utils.calculate_signal_energy(x_preamble) rx_e = utils.calculate_signal_energy(frame[nc:nc + len(x_preamble)]) agc_factor = np.sqrt(ref_e / rx_e) print('AGC values:', ref_e, rx_e, agc_factor) frame *= agc_factor sframe = frame[sample_nc:sample_nc + len(ref_frame)] # plt.plot(np.abs(ref_frame)) # plt.plot(np.abs(frame)) # plt.plot(np.abs(ac)) # plt.plot(np.abs(xc)) # plt.plot(cc) # # # plt.axvline(sample_nc, color='y') # print(np.abs(kframe - sframe) < 1e-4) # plt.plot(np.abs(kframe)) # plt.plot(np.abs(sframe)) # plt.show() return sframe
def gfdm_modulate_fft(data, alpha, M, K, overlap): # this function aims to reproduce [0] Section IIIA H = get_frequency_domain_filter('rrc', alpha, M, K, overlap) filter_energy = calculate_signal_energy(H) scaling_factor = 1. / np.sqrt(filter_energy / M) H *= scaling_factor D = get_data_matrix(data, K, group_by_subcarrier=False) return gfdm_modulate_block(D, H, M, K, overlap, False)
def modulate_mapped_gfdm_block(data, ts, sc, active_sc, overlap, alpha, dc_free=False): # const gfdm_complex scaling_factor = gfdm_complex(1. / std::sqrt(std::abs(res) / n_timeslots), 0.0f); smap = get_subcarrier_map(sc, active_sc, dc_free=dc_free) dm = map_to_waveform_resource_grid(data, active_sc, sc, smap).T H = get_frequency_domain_filter('rrc', alpha, ts, sc, overlap) filter_energy = calculate_signal_energy(H) scaling_factor = 1. / np.sqrt(filter_energy / ts) H = H * scaling_factor # print filter_energy, scaling_factor, calculate_signal_energy(H) return gfdm_modulate_block(dm, H, ts, sc, overlap, False)
def demodulate_frame(rx_data_frame, modulated_frame, rx_kernel, demapper, data, timeslots, fft_len, H_estimate=None): ref_data = demodulate_data_frame(modulated_frame, rx_kernel, demapper, len(data)) rx_data = demodulate_data_frame(rx_data_frame, rx_kernel, demapper, len(data)) if H_estimate is None: d = np.array([1+1j, -1+1j, -1-1j, 1-1j]) / np.sqrt(2.) else: d = rx_kernel.demodulate_equalize(rx_data_frame.astype(dtype=np.complex64), H_estimate.astype(dtype=np.complex64)) d = demapper.demap_from_resources(d.astype(dtype=np.complex64), len(data)) mse = np.sum(np.abs(rx_data - ref_data) ** 2) / len(ref_data) print('frame EQ demodulated energy', utils.calculate_signal_energy(rx_data), utils.calculate_signal_energy(rx_data) / len(rx_data), utils.calculate_signal_energy(ref_data) / len(ref_data)) # calculate_avg_phase(rx_data, ref_data) fber = calculate_frame_ber(ref_data, rx_data) print('Frame BER: ', fber, 'with MSE: ', mse) plot_constellation(ref_data, rx_data, d, 0, timeslots * fft_len) plt.show()
def auto_correlate_signal(signal, K): plen = K * 2 slen = len(signal) ac = np.zeros(slen - plen, dtype=np.complex) for i in range(slen - plen): # calc auto-correlation c = signal[i:i+plen] #normalize value p = calculate_signal_energy(c) ac[i] = 2 * auto_correlate_halfs(c) / p return ac
def auto_correlate_signal(signal, K): plen = K * 2 slen = len(signal) ac = np.zeros(slen - plen, dtype=np.complex) for i in range(slen - plen): # calc auto-correlation c = signal[i:i + plen] #normalize value p = calculate_signal_energy(c) ac[i] = 2 * auto_correlate_halfs(c) / p return ac
def generate_sync_symbol(pn_symbols, filtertype, alpha, K, L, cp_len, ramp_len, cyclic_shift=0): H = get_frequency_domain_filter(filtertype, alpha, 2, K, L) filter_energy = calculate_signal_energy(H) scaling_factor = 1.0 / np.sqrt(filter_energy / 2) H = H * scaling_factor return get_sync_symbol(pn_symbols, H, K, L, cp_len, ramp_len, cyclic_shift)
def estimate_frame_channel(H, fft_len, frame_len): used_sc = 52 # subcarrier_map = mapping.get_subcarrier_map(fft_len, used_sc, dc_free=True) # subcarrier_map = np.roll(subcarrier_map, len(subcarrier_map) // 2) # ts = time.time() H[0] = (H[1] + H[-1]) / 2. # plt.plot(subcarrier_map, np.angle(H[subcarrier_map])) H = np.fft.fftshift(H) active_sc = np.arange((fft_len - used_sc)//2, (fft_len + used_sc)//2+1) active_sc = active_sc[3:-3] g = signal.gaussian(9, 1.0) g_factor = 1. g /= np.sqrt(g_factor * g.dot(g)) Ha = H[active_sc] Hb = np.concatenate((np.repeat(Ha[0], 4), Ha, np.repeat(Ha[-1], 4))) Hg = np.correlate(Hb, g) # print('channel averaging error:', utils.calculate_signal_energy(Ha), utils.calculate_signal_energy(Hg), utils.calculate_signal_energy(Ha) / utils.calculate_signal_energy(Hg)) Hg *= np.sqrt(utils.calculate_signal_energy(Ha) / utils.calculate_signal_energy(Hg)) freqs = np.fft.fftfreq(len(H)) freqs = np.fft.fftshift(freqs) fr_freqs = np.fft.fftfreq(frame_len) fr_freqs = np.fft.fftshift(fr_freqs) H_frame = np.interp(fr_freqs, freqs[active_sc], Hg.real) + 1j * np.interp(fr_freqs, freqs[active_sc], Hg.imag) # te = time.time() # print('interpolation timing:', te - ts) A = np.array([freqs[active_sc], np.ones(len(active_sc))]) # print(np.shape(A)) m, c = np.linalg.lstsq(A.T, H[active_sc])[0] Hl = m * freqs[active_sc] + c # plt.plot(freqs[active_sc], Ha.real) # plt.plot(freqs[active_sc], Ha.imag) # plt.plot(freqs[active_sc], Hg.real, marker='x', ms=10) # plt.plot(freqs[active_sc], Hl.real) # Hg = Ha fr_freqs = np.fft.fftfreq(frame_len) fr_freqs = np.fft.fftshift(fr_freqs) H_frame = np.interp(fr_freqs, freqs[active_sc], Hg.real) + 1j * np.interp(fr_freqs, freqs[active_sc], Hg.imag) # plt.plot(fr_freqs, H_frame.real) p_freqs = np.fft.fftfreq(fft_len * 9) p_freqs = np.fft.fftshift(p_freqs) H_p = np.interp(p_freqs, freqs[active_sc], Hg.real) + 1j * np.interp(p_freqs, freqs[active_sc], Hg.imag) # plt.plot(p_freqs, H_p.real) Hlf = m * fr_freqs + c # plt.plot(fr_freqs, Hlf.real) # plt.show() # plt.plot(xp, Hg.real, marker='x') # plt.plot(x, H_frame.real) # plt.plot(xf, H_p.real) # phase_m = m * fft_len / len(sframe) # p = np.arange(-frame_len, frame_len) # plt.plot(np.angle(H_p)) # plt.plot(np.angle(H_frame)) # # plt.plot(np.angle(Ha)) # plt.plot(np.angle(Hg)) # plt.plot(np.abs(Ha)) # plt.plot(np.abs(Hg)) # plt.show() return H_frame
def calculate_agc_factor(rx_preamble, x_preamble): ref_e = utils.calculate_signal_energy(x_preamble) rx_e = utils.calculate_signal_energy(rx_preamble) # print('AGC factor', np.sqrt(ref_e / rx_e)) return np.sqrt(ref_e / rx_e)
def rx_demodulate(frames, ref_frame, modulated_frame, x_preamble, data, rx_kernel, demapper, timeslots, fft_len, cp_len, cs_len): f_start = cp_len + 2 * fft_len + cs_len d_start = f_start + cp_len print('data start: ', d_start) sync_kernel = cgfdm.py_auto_cross_corr_multicarrier_sync_cc(64, 32, x_preamble) estimator = validation_utils.frame_estimator(x_preamble, fft_len, timeslots, 52) c_est = cgfdm.py_preamble_channel_estimator_cc(9, 64, 52, True, x_preamble) p_filter_taps = c_est.preamble_filter_taps() print(estimator._p_filter) print(p_filter_taps) print(np.abs(p_filter_taps - estimator._p_filter) < 1e-6) for f in frames[0:30]: rxs = time.time() rx_preamble = f[cp_len:cp_len + 2 * fft_len] agc_factor = sync_kernel.calculate_preamble_attenuation(rx_preamble.astype(dtype=np.complex64)) f = sync_kernel.normalize_power_level(f, agc_factor) rx_preamble = f[cp_len:cp_len + 2 * fft_len] H_estimate = estimator.estimate_frame(rx_preamble) rx_t_frame = f[d_start:d_start + fft_len * timeslots] kde = rx_kernel.demodulate_equalize(rx_t_frame.astype(dtype=np.complex64), H_estimate.astype(dtype=np.complex64)) de = demapper.demap_from_resources(kde.astype(dtype=np.complex64), len(data)) rxe = time.time() print('receiver chain time: ', 1e6 * (rxe - rxs), 'us') # p_active = np.concatenate((np.arange(1, 27), np.arange(37, 64))) # cp_est = c_est.estimate_preamble_channel(rx_preamble) # p_est = estimator._estimate_preamble(rx_preamble) # print('Python vs C Preamble channel correct:', np.all(np.abs(cp_est[p_active] - p_est[p_active]) < 1e-4), np.all(np.angle(cp_est[p_active]) - np.angle(p_est[p_active]) < 1e-6)) # pf = estimator._filter_preamble_estimate(p_est) # cf = c_est.filter_preamble_estimate(p_est.astype(dtype=np.complex64)) # print(np.all(np.angle(pf) - np.angle(cf) < 1e-6)) # frame_estimate = c_est.interpolate_frame(pf.astype(dtype=np.complex64)) rxs = time.time() frame_estimate = c_est.estimate_frame(rx_preamble) rxe = time.time() print('CPP channel estimator duration: ', 1e6 * (rxe - rxs), 'us') print('Python vs C Preamble channel correct:', np.all(np.abs(frame_estimate - H_estimate) < 1e-6)) H_estimate = frame_estimate # plt.plot(H_estimate.real) # plt.plot(H_estimate.imag) # plt.plot(frame_estimate.real) # plt.plot(frame_estimate.imag) # # # plt.plot(pf.real) # # plt.plot(pf.imag) # # plt.plot(cf.real) # # plt.plot(cf.imag) # plt.show() P = np.fft.fft(rx_t_frame) P *= np.conj(H_estimate) rx_t_frame = np.fft.ifft(P) # kd = rx_kernel.demodulate(rx_t_frame.astype(dtype=np.complex64)) # d = demapper.demap_from_resources(kd.astype(dtype=np.complex64), len(data)) print('kernel FER: ', calculate_frame_ber(data, de)) sframe = equalize_frame(f, x_preamble, fft_len, cp_len, cs_len) # rx_preamble = sframe[cp_len:cp_len + 2 * fft_len] # avg_phase = calculate_avg_phase(rx_preamble, x_preamble) # sframe *= np.exp(-1j * avg_phase) rx_data_frame = sframe[d_start:d_start + fft_len * timeslots] ekd = utils.calculate_signal_energy(rx_t_frame - rx_data_frame) print('MSE for kernel vs Python demod', ekd, ekd / len(rx_data_frame)) plt.show() # plt.scatter(d.real, d.imag, label='kernel') plt.scatter(de.real, de.imag, label='EQ kern') H_estimate = np.ones(len(H_estimate)) # demodulate_frame(rx_data_frame, modulated_frame, rx_kernel, demapper, data, timeslots, fft_len, H_estimate) demodulate_frame(rx_t_frame, modulated_frame, rx_kernel, demapper, data, timeslots, fft_len, H_estimate)
def generate_sync_symbol(pn_symbols, filtertype, alpha, K, L, cp_len, ramp_len): H = get_frequency_domain_filter(filtertype, alpha, 2, K, L) filter_energy = calculate_signal_energy(H) scaling_factor = 1. / np.sqrt(filter_energy / 2) H = H * scaling_factor return get_sync_symbol(pn_symbols, H, K, L, cp_len, ramp_len)