[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Discuss-gnuradio] Fun with filter banks - grab many NBFM signals at onc
From: |
Johnathan Corgan |
Subject: |
[Discuss-gnuradio] Fun with filter banks - grab many NBFM signals at once |
Date: |
Fri, 09 Jun 2006 09:53:28 -0700 |
User-agent: |
Thunderbird 1.5.0.2 (X11/20060522) |
Here is a quick and dirty app I cooked up to grab a slice of spectrum
and simultaneously demodulate NBFM and create .wav files for each of an
equally spaced set of channels.
In other words, you can grab a MHz of spectrum, specify 25 KHz centers,
and get .wav files of all the audio transmissions on each center
frequency (using NBFM modulation.)
The start and stop frequencies, channel spacing, channel filter
parameters, demodulated audio filter parameters, and final audio output
file rate are all configurable (though none are checked for sanity at
this point.)
The heart of the app uses gr.analysis_filterbank to channelize a swath
of spectrum, then attaches an FM demodulator to each output channel.
As almost no resampling is done, there are a few restrictions in usage:
The difference in the start and stop frequency must be a possible
decimated output rate of the USRP. So you can grab a MHz or two or
800K, but not, say, 625K of spectrum.
Furthermore, the spectrum width must be an even multiple of the channel
spacing. This complicates things, as it ultimately requires that the
channel spacing be an integral divisor of the USRP ADC rate. This
means, for example, you can do 20K or 25K centers, but not 15K centers,
common in the ham bands.
The audio output from the low pass filter is resampled to equal the
specified 'output-rate' parameter.
Finally, the output files will contain the audio transmissions
interspersed with all the white noise in between transmissions, unless
you apply the "squelch skipping" patch mentioned a couple days ago.
This updates the simple_squelch_cc block to optionally drop samples
instead of outputting zeros when the signal is below the power
threshold. The result in this case is that the final audio files have
all the inter-transmission silence/white-noise removed.
Here is an example command line to grab 464.0 to 465.0 MHz from
daughterboard B with gain of 70, set the channel squelch level to 0 db,
demodulate 40 channels at 25 KHz centers, use a channel bandpass filter
of 8000 KHz (one-sided), an audio filter of 3 KHz, and create output
files resampled to 8000 sps:
./channelizer.py \
--rx-board B \
--gain 70 \
--lower-freq 464.0M \
--upper-freq 465.0M \
--channel-rate 25000 \
--channel-pass 8000 \
--channel-stop 10000 \
--squelch 0 \
--audio-pass 3000 \
--audio-stop 4000 \
--audio-rate 25000 \
--output-rate 8000
The sox program is then be used to convert the output files into .wav
format.
Enjoy.
-Johnathan, AE6HO
#!/bin/sh
./channelizer.py \
--rx-board B \
--gain 70 \
--lower-freq 464.0M \
--upper-freq 465.0M \
--channel-rate 25000 \
--channel-pass 8000 \
--channel-stop 10000 \
--squelch 0 \
--audio-pass 3000 \
--audio-stop 4000 \
--audio-rate 25000 \
--output-rate 8000
for fn in *.raw ; do \
sox -r 8000 -s -w $fn -t wav `basename $fn .raw`.wav
rm $fn ;
done
#!/usr/bin/env python
from math import pi
from gnuradio import gr, gru, usrp, optfir, audio, eng_notation, blks
from gnuradio.eng_option import eng_option
from optparse import OptionParser
class fm_demod(gr.hier_block):
def __init__(self,
fg, # Flow graph
channel_rate, # Channel sample rate
deviation): # Maximum deviation
k = channel_rate/(2*pi*deviation)
QUAD = gr.quadrature_demod_cf(k)
DEEMPH = blks.fm_deemph(fg, channel_rate, 75e-6)
fg.connect(QUAD, DEEMPH)
gr.hier_block.__init__(self, fg, QUAD, DEEMPH)
class channel_sink(gr.hier_block):
def __init__(self, fg, freq, options, args):
try: # Uses skipping squelch patch
RFSQL = gr.simple_squelch_cc(options.squelch, 5e-3, True)
except: # Existing gnuradio squelch block
RFSQL = gr.simple_squelch_cc(options.squelch, 5e-3)
AGC = gr.agc_cc(1e-3, 1.0, 1.0)
DEMOD = fm_demod(fg, options.channel_rate, 5000)
taps = optfir.low_pass(1.0, options.channel_rate, options.audio_pass,
options.audio_stop, 0.1, 60)
LPF = gr.fir_filter_fff(options.channel_rate/options.audio_rate, taps)
out_lcm = gru.lcm(options.audio_rate, options.output_rate)
out_interp = int(out_lcm//options.audio_rate)
out_decim = int(out_lcm//options.output_rate)
RESAMP = blks.rational_resampler_fff(fg, out_interp, out_decim)
GAIN = gr.multiply_const_ff(32768.0)
F2S = gr.float_to_short()
SINK = gr.file_sink(gr.sizeof_short, `int(freq)`+".raw")
fg.connect(RFSQL, AGC, DEMOD, LPF, RESAMP, GAIN, F2S, SINK)
gr.hier_block.__init__(self, fg, RFSQL, SINK)
class channelizer(blks.analysis_filterbank):
def __init__(self,
fg, # Flow graph
if_rate, # IF input sample rate (complex)
channel_rate, # Final channel sample rate (complex)
channel_pass, # Occupied spectrum for narrowband channel
channel_stop): # Total channel + guard band
num_channels = int(if_rate/channel_rate)
taps = optfir.low_pass(1.0, if_rate, channel_pass, channel_stop, 0.1,
60)
print "Channelizer has", len(taps), "taps for", num_channels,
"channels."
blks.analysis_filterbank.__init__(self, fg, num_channels, taps)
class app_flow_graph(gr.flow_graph):
def __init__(self, options, args):
gr.flow_graph.__init__(self)
self.options = options
self.args = args
if_rate = options.upper_freq - options.lower_freq
center_freq = options.lower_freq + if_rate/2
num_channels = int(if_rate/options.channel_rate)
# Use USRP as data source
self.SRC = usrp.source_c()
if options.rx_board is None:
options.rx_board = usrp.pick_rx_subdevice(self.SRC)
self.subdev = usrp.selected_subdev(self.SRC, options.rx_board)
self.SRC.set_mux(usrp.determine_rx_mux_value(self.SRC,
options.rx_board))
usrp_decim = int(self.SRC.adc_rate()/if_rate)
self.SRC.set_decim_rate(usrp_decim)
self.subdev.set_gain(options.gain)
self.CHAN = channelizer(self, if_rate, options.channel_rate,
options.channel_pass, options.channel_stop)
# Connect pipeline
self.connect(self.SRC, self.CHAN)
for i in range(num_channels):
freq = options.lower_freq + i*options.channel_rate
if freq > options.lower_freq + if_rate/2:
freq -= if_rate/2
else:
freq += if_rate/2
self.connect((self.CHAN, i), channel_sink(self, freq, options,
args))
self.set_center_freq(center_freq)
print "Using %s daughterboard" % (self.subdev.side_and_name(),)
print "Center frequency is", self.center_freq
print "USRP decimation rate is", usrp_decim
print "Using IF rate of", if_rate
def set_center_freq(self, frequency):
self.center_freq = frequency
result = usrp.tune(self.SRC, 0, self.subdev,
frequency+self.options.calibration)
def main():
parser = OptionParser(option_class=eng_option)
parser.add_option("", "--lower-freq", type="eng_float", help="set low
frequency to FREQ", metavar="FREQ")
parser.add_option("", "--upper-freq", type="eng_float", help="set low
frequency to FREQ", metavar="FREQ")
parser.add_option("", "--squelch", type="eng_float", help="set receive
squelch to DB", metavar="DB")
parser.add_option("", "--rx-board", type="subdev", help="select USRP Rx
side A or B (default=first daughterboard found)", metavar="SIDE")
parser.add_option("", "--channel-pass", type="int", help="set channel
bandpass frequency to Hz", metavar="Hz")
parser.add_option("", "--channel-stop", type="int", help="set channel
stopband frequency to Hz", metavar="Hz")
parser.add_option("", "--channel-rate", type="int", help="set channel
sample rate to RATE, complex", metavar="RATE")
parser.add_option("", "--audio-rate", type="int", help="set final audio
sampling rate, float", metavar="RATE")
parser.add_option("", "--audio-pass", type="int", help="set audio filter
passband Hz", metavar="Hz")
parser.add_option("", "--audio-stop", type="int", help="set audio filter
stopband Hz", metavar="Hz")
parser.add_option("", "--output-rate", type="int", help="set output sample
rate to RATE, float", metavar="RATE")
parser.add_option("", "--calibration", type="eng_float", default=0.0,
help="set frequency offset to Hz", metavar="Hz")
parser.add_option("", "--gain", type="int", help="set RF gain",
metavar="dB")
(options, args) = parser.parse_args()
# FIXME: parameter sanity checks
fg = app_flow_graph(options, args)
fg.start()
raw_input("Press enter to exit:")
fg.stop()
if __name__ == "__main__":
main()
- [Discuss-gnuradio] Fun with filter banks - grab many NBFM signals at once,
Johnathan Corgan <=