discuss-gnuradio
[Top][All Lists]
Advanced

[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()

reply via email to

[Prev in Thread] Current Thread [Next in Thread]