777arc/textbook

Help with low pass filter

Closed this issue · 8 comments

Thank you for your textbook! I'm really bad at DSP, so please excuse any mistakes I might make.

I have a recording sampled at 10MSPS with two wideband signals (and a mystery signal to the right!).
image
How would I be able to split the two into separate WAV files for demodulation? I assume GNURadio is my friend here, but whenever I try it, it spits out garbage. I tried to high pass filter the GAC and low pass the HRPT, but for whatever reason, it does not work.

Next, I tried this chapter, https://pysdr.org/content/filters.html, and using the code generated some filter taps.

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

num_taps = 51 # it helps to use an odd number of taps
cut_off = 3000000 # Hz
sample_rate = 10000000 # Hz

# create our low pass filter
h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2)

# Shift the filter in frequency by multiplying by exp(j*2*pi*f0*t)
f0 = -3e6 # amount we will shift
Ts = 1.0/sample_rate # sample period
t = np.arange(0.0, Ts*len(h), Ts) # time vector. args are (start, stop, step)
exponential = np.exp(2j*np.pi*f0*t) # this is essentially a complex sine wave

h_band_pass = h * exponential # do the shift

# plot impulse response
plt.figure('impulse')
plt.plot(np.real(h_band_pass), '.-')
plt.plot(np.imag(h_band_pass), '.-')
plt.legend(['real', 'imag'], loc=1)

# plot the frequency response
H = np.abs(np.fft.fft(h_band_pass, 1024)) # take the 1024-point FFT and magnitude
H = np.fft.fftshift(H) # make 0 Hz in the center
w = np.linspace(-sample_rate/2, sample_rate/2, len(H)) # x axis
plt.figure('freq')
plt.plot(w, H, '.-')
plt.xlabel('Frequency [Hz]')
plt.show()

However, when viewed it in some sdr software (SDR++), not only is the signal not centered, it is weaker than in the original recording.
image
image

Do you have any ideas as to how to demod it and split it into different recordings?

Thank you very much for your time! :)

Very short recording (less than 1s)

baseband.zip

Do you want to split them into two separate files where they are each centered at baseband (i.e. before demodulation or any real processing)? Because if that's the case, you start with 1 IQ file and are left with 2 IQ files, you won't be able to use a wav file. I think wav files are usually used for the signal after demodulation, like for soft symbols, but I could be wrong. I would stick with GNU Radio's normal File Source and File Sink, which use complex floats, in IQ format. You can read/save these IQ files in Python as well, using examples from pysdr.

As far as taking 1 IQ file and pulling out individual signals, it's really just a frequency shift and low pass filter. You frequency shift to get your desired signal centered at 0 Hz, then do a low pass filter to filter out everything else. And optionally you can decimate but wait till you fully understand nyquist and decimation before doing it. Save the result to a new IQ file, and rinse and repeat.

You can do this entirely in GNU Radio, using the Frequency Xlating Filter block, combined with File Source and File Sink.

Looking at your screenshot, it seems you're using a 2 channel wav file to represent IQ? In that case use the Float to Complex like you are, but then add a Freq Xlating Filter block, connected to a File Sink, you don't even need the throttle.

Here use this as a guide https://wiki.gnuradio.org/index.php?title=Frequency_Xlating_FIR_Filter

You can start by setting decimation to 1 and transition_bw to your sample_rate*0.1

Thank you very much for your reply! I looked at the page, and for whatever reason, the signal is still not centered, even after multiple tries with different taps. I once got it in the center, but still it is much weaker than in the original recording :(

Original (zoomed in in SDR++)
image

Filtered
image

The SNR drop seems insignificant - however the demodulator barely shows an SNR of 3 whereas the SNR is clearly higher than that.

Maybe I'm doing something completely wrong? Because the signal seems mostly in the same place no matter what filters I use...

image

Couple things I see here,

  1. in your original signal, both of these wide signals have fairly low SNR, but when you do the frequency shift and low pass filter you shouldn't see ANY drop in SNR, it should look exactly the same, just rotated/isolated.
  2. you have center frequency set to -3 Hz, should that be MHz?
  3. can you share what you entered for taps (and all other params of that block), in this screenshot it only shows the resulting taps

Thank you very much for spotting that! I changed it to 3e6 and got this:
image

Which still is, for whatever reason, much, much weaker than in the original recording:
image

As for the taps, I used the following slightly modified PySDR taps script, however, this yielded the same result as using the GnuRadio taps before (bandpass filter taps).

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

num_taps = 51 # it helps to use an odd number of taps
cut_off = 3000000 # Hz
sample_rate = 10000000 # Hz

# create our low pass filter
h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2)

# Shift the filter in frequency by multiplying by exp(j*2*pi*f0*t)
f0 = -3e6 # amount we will shift
Ts = 1.0/sample_rate # sample period
t = np.arange(0.0, Ts*len(h), Ts) # time vector. args are (start, stop, step)
exponential = np.exp(2j*np.pi*f0*t) # this is essentially a complex sine wave

h_band_pass = h * exponential # do the shift

# plot impulse response
plt.figure('impulse')
plt.plot(np.real(h_band_pass), '.-')
plt.plot(np.imag(h_band_pass), '.-')
plt.legend(['real', 'imag'], loc=1)

# plot the frequency response
H = np.abs(np.fft.fft(h_band_pass, 1024)) # take the 1024-point FFT and magnitude
H = np.fft.fftshift(H) # make 0 Hz in the center
w = np.linspace(-sample_rate/2, sample_rate/2, len(H)) # x axis
plt.figure('freq')
plt.plot(w, H, '.-')
plt.xlabel('Frequency [Hz]')
plt.show()

Thank you so much for your support!!

In this case the taps are

[ 5.48597263e-18+0.00000000e+00j, -3.25735853e-04-1.00251187e-03j,
6.21153374e-04-4.51294343e-04j, -7.72119751e-04-5.60977836e-04j,
6.09410320e-04-1.87557211e-03j, 3.92310073e-18+1.44132383e-33j,
-1.00702220e-03-3.09929566e-03j, 2.07378098e-03-1.50669007e-03j,
-2.61158996e-03-1.89743117e-03j, 2.01069479e-03-6.18828224e-03j,
9.29874229e-18+6.83260499e-33j, -3.03019033e-03-9.32596689e-03j,
5.94693409e-03-4.32070053e-03j, -7.17065606e-03-5.20978658e-03j,
5.32164923e-03-1.63783522e-02j, 1.59434007e-17+1.75725312e-32j,
-7.64329885e-03-2.35236550e-02j, 1.48701233e-02-1.08037770e-02j,
-1.80042798e-02-1.30808749e-02j, 1.36378974e-02-4.19731323e-02j,
2.13190422e-17+3.13299562e-32j, -2.20407974e-02-6.78345992e-02j,
4.87904605e-02-3.54483445e-02j, -7.45357442e-02-5.41533880e-02j,
9.31431457e-02-2.86665126e-01j, -5.99572200e-01-3.23150462e-15j,
9.31431457e-02+2.86665126e-01j, -7.45357442e-02+5.41533880e-02j,
4.87904605e-02+3.54483445e-02j, -2.20407974e-02+6.78345992e-02j,
2.13190422e-17+4.69949343e-32j, 1.36378974e-02+4.19731323e-02j,
-1.80042798e-02+1.30808749e-02j, 1.48701233e-02+1.08037770e-02j,
-7.64329885e-03+2.35236550e-02j, 1.59434007e-17-1.56397650e-32j,
5.32164923e-03+1.63783522e-02j, -7.17065606e-03+5.20978658e-03j,
5.94693409e-03+4.32070053e-03j, -3.03019033e-03+9.32596689e-03j,
9.29874229e-18+2.73304200e-32j, 2.01069479e-03+6.18828224e-03j,
-2.61158996e-03+1.89743117e-03j, 2.07378098e-03+1.50669007e-03j,
-1.00702220e-03+3.09929566e-03j, 3.92310073e-18-2.88410465e-32j,
6.09410320e-04+1.87557211e-03j, -7.72119751e-04+5.60977836e-04j,
6.21153374e-04+4.51294343e-04j, -3.25735853e-04+1.00251187e-03j,
5.48597263e-18+5.91353164e-32j]

No don't use anything from pysdr for now, just stick to the firdes.lowpass example shown in the wiki, you need low pass filter taps for the Freq Xlating Filter. If you do it right there will be 0 change in SNR.

Excuse me for the long reply :)

While experimenting, I noticed something really, really strange. I'm using the simplest possible flowgraph - reading a wav file, shoving it through a throttle, and then writing it.
image
Strangely, when opened in SDR++, I see this.
image
However, when the QT Frequency graph is viewed, the output is this:
image
Which clearly shows the two signals. I am at a loss here. I am clearly not reading from the wrong file as I double check and restart my SDR software after each update.

Perhaps, if you have time, could you please attempt constructing a flowgraph?

Thank you so much!!!

baseband.zip