Maximally Decimated Polyphase Channelizer Help
Started by 8 months ago●3 replies●latest reply 8 months ago●261 viewsHere is my code. Hopefully someone can offer some critiques and point out anything I may have missed
# %% import numpy as np import matplotlib.pyplot as plt from scipy import signal # %% def dbscale(data): v0 = np.abs(data) / np.max(np.abs(data)) v0 = 20.0 * np.log10(v0) return v0 def polyphase_filter_response(polyphase_filter): N = polyphase_filter.size * 100 w = np.linspace(0, 1, N) p = 2 * np.pi * np.cumsum(w) data = np.exp(1j * p) channelizer_out = compute_channelizer(data, polyphase_filter) a = 20 * np.log10(np.abs(channelizer_out)) w = np.linspace(0, 1, a.shape[1]) plt.figure() for p in range(a.shape[0]): plt.plot(w, a[p], label=f'{p}') plt.legend() plt.title('Digital channelizer frequency response') plt.xlabel('Frequency [rad/sample]') plt.ylabel('Amplitude [dB]') plt.title('Polyphase Filter Response') def compute_channelizer(data, polyphase_filter): num_data_samples = data.size num_channels, coef_per_stage = polyphase_filter.shape reg = np.zeros((num_channels, coef_per_stage), dtype=np.complex128) channelizer = np.zeros((num_channels, num_data_samples // num_channels), dtype=np.complex128) num_loops = 0 for nn in range(0, num_data_samples, num_channels): reg[:, 1:] = reg[:, 0:-1] reg[:, 0] = data[nn:nn + num_channels] filter_output = np.zeros(num_channels, dtype=np.complex128) for mm in range(num_channels): # convolve the input with the filter and put it in reverse order in # filter output so the final stage is an ifft filter_output[num_channels - mm - 1] = reg[mm, ::-1].dot(polyphase_filter[mm]) channelizer[:, num_loops] = np.fft.ifft(filter_output, norm='forward') num_loops += 1 return channelizer # %% setup # for some reason frequencies like 0.2, 0.3, ... produce a DC value in the # channelizer output. I assume this is some artifact from the sampling. This # result is seems to be also present in the original matlab code. freq = [ 0.21, 0.51, 0.81, ] amp = [ 0.2, 0.5, 0.8, ]# sampling is 1 Hz in this example fs = 1 ts = 1 / fs num_channels = 10 coef_per_stage = 17 num_data_samples = 1200 t = np.arange(num_data_samples) * ts t_channelizer = np.arange(num_data_samples // num_channels) * ts * num_channels in_signal = np.zeros(num_data_samples, dtype=np.complex128) for i, f in enumerate(freq): in_signal += amp[i] * np.exp(1j * 2 * np.pi * f * t) # %% Filter Setup order = num_channels * coef_per_stage bands = np.array([0, 40, 60, 99, 100, 149, 150, 199, 200, 249, 250, 299, 300, 349, 350, 399, 400, 449, 450, 500]) / 500 gain = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) weight = np.array([1, 100, 140, 180, 220, 260, 300, 340, 380, 420], dtype=np.float32) filter_coef = signal.remez(order, bands, desired=gain, weight=weight, fs=2, grid_density=61) # %% filter response w, h = signal.freqz(filter_coef, 1, 2 ** 14) h_abs = np.abs(h) h_abs /= np.max(h_abs) plt.figure() plt.plot(fs / 2 * w / np.pi, 20 * np.log10(h_abs)) plt.grid(True) plt.ylabel("Magnitude (dB)") plt.xlabel("Frequency (Hz)") plt.title("Filter Respone") # %% Polyphase Filter - num_channels stages and coef_per_stage samples per stage polyphase_filter = filter_coef.reshape((num_channels, coef_per_stage), order='F').copy() # %% Polyphase Filer Response polyphase_filter_response(polyphase_filter) # %% apply the polyphase channelizer to the input channelizer = compute_channelizer(in_signal, polyphase_filter) # %% channelizer Plots plots plt.figure(figsize=(24, 8)) plt.subplot(2, 6, 1) plt.plot(t, in_signal.real, label='I') plt.plot(t, in_signal.imag, label='Q') plt.legend() plt.title('Time Series Input') in_sig_fft = dbscale(np.fft.fft(in_signal)) fft_freq = np.arange(in_signal.size) / (in_signal.size * ts) plt.subplot(2, 6, 2) plt.plot(fft_freq, in_sig_fft) plt.ylim(-100, 10) plt.title('Spectrum Input') for i in range(num_channels): plt.subplot(2, 6, 3 + i) plt.plot(t_channelizer, channelizer[i].real, label='I') plt.plot(t_channelizer, channelizer[i].imag, label='Q') plt.ylim(-1.1, 1.1) plt.title(f'Channel {i}') plt.show()
The Code seems to mostly work. Here are some of the plots from my code.
In the above plots I have added 3 signals with frequencies 0.21, 0,51, and 0.81 and they seem to be separated into their respective channels. However if I change the frequencies to 0.2, 0.5, 0.8 then the channel outputs are near constant. I don't understand. It may just be an artifact of the sampling rates?
after changed the following code I get this result.
freq = [ 0.2, 0.5, 0.8, ]
values like 0.19 and 0.21 work, but 0.2 gives the response above.
Isn't that exactly what you expect? If the signal is a pure sinewave, with frequency exactly equal to the channel center, then after down-conversion (channelization) that's at DC (0 Hz).
A 0 Hz sinewave is just a constant value, which is what you observe.
I think you are right! I just needed someone to spell it out for me. Thank you! I'm new to channelizers.
I'm quite sure I faced exactly the same confusion the first time I saw this phenomenon. Glad to help.