diff options
20 files changed, 588 insertions, 327 deletions
diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2.cc b/gnuradio-core/src/lib/runtime/gr_hier_block2.cc index 9811b9defa..fc74ff431b 100644 --- a/gnuradio-core/src/lib/runtime/gr_hier_block2.cc +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2.cc @@ -29,7 +29,7 @@ #include <gr_hier_block2_detail.h> #include <iostream> -#define GR_HIER_BLOCK2_DEBUG 1 +#define GR_HIER_BLOCK2_DEBUG 0 gr_hier_block2_sptr gr_make_hier_block2(const std::string &name, gr_io_signature_sptr input_signature, diff --git a/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc index 307cbabeb5..4fefed28b5 100644 --- a/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc +++ b/gnuradio-core/src/lib/runtime/gr_hier_block2_detail.cc @@ -30,7 +30,7 @@ #include <stdexcept> #include <iostream> -#define GR_HIER_BLOCK2_DETAIL_DEBUG 1 +#define GR_HIER_BLOCK2_DETAIL_DEBUG 0 gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) : d_owner(owner), diff --git a/gnuradio-core/src/lib/runtime/gr_runtime_impl.cc b/gnuradio-core/src/lib/runtime/gr_runtime_impl.cc index 54fc7d19a6..e792774b92 100644 --- a/gnuradio-core/src/lib/runtime/gr_runtime_impl.cc +++ b/gnuradio-core/src/lib/runtime/gr_runtime_impl.cc @@ -38,7 +38,7 @@ #include <stdexcept> #include <iostream> -#define GR_RUNTIME_IMPL_DEBUG 1 +#define GR_RUNTIME_IMPL_DEBUG 0 static gr_runtime_impl *s_runtime = 0; diff --git a/gnuradio-core/src/lib/runtime/gr_simple_flowgraph_detail.cc b/gnuradio-core/src/lib/runtime/gr_simple_flowgraph_detail.cc index a725adb6eb..432cde0aed 100644 --- a/gnuradio-core/src/lib/runtime/gr_simple_flowgraph_detail.cc +++ b/gnuradio-core/src/lib/runtime/gr_simple_flowgraph_detail.cc @@ -33,7 +33,7 @@ #include <stdexcept> #include <map> -#define GR_SIMPLE_FLOWGRAPH_DETAIL_DEBUG 1 +#define GR_SIMPLE_FLOWGRAPH_DETAIL_DEBUG 0 gr_edge_sptr gr_make_edge(const gr_endpoint &src, const gr_endpoint &dst) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl2/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl2/Makefile.am index 4852ff3fbf..201a85b39a 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl2/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blksimpl2/Makefile.am @@ -31,9 +31,11 @@ grblkspython_PYTHON = \ dbpsk.py \ dqpsk.py \ d8psk.py \ + filterbank.py \ gmsk.py \ pkt.py \ - psk.py + psk.py \ + rational_resampler.py noinst_PYTHON = diff --git a/gnuradio-core/src/python/gnuradio/blksimpl2/filterbank.py b/gnuradio-core/src/python/gnuradio/blksimpl2/filterbank.py new file mode 100644 index 0000000000..38587bae8b --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl2/filterbank.py @@ -0,0 +1,174 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import gr, gru + +def _generate_synthesis_taps(mpoints): + return [] # FIXME + + +def _split_taps(taps, mpoints): + assert (len(taps) % mpoints) == 0 + result = [list() for x in range(mpoints)] + for i in xrange(len(taps)): + (result[i % mpoints]).append(taps[i]) + return [tuple(x) for x in result] + + +class synthesis_filterbank(gr.hier_block2): + """ + Uniformly modulated polyphase DFT filter bank: synthesis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, mpoints, taps=None): + """ + Takes M complex streams in, produces single complex stream out + that runs at M times the input sample rate + + @param fg: flow_graph + @param mpoints: number of freq bins/interpolation factor/subbands + @param taps: filter taps for subband filter + + The channel spacing is equal to the input sample rate. + The total bandwidth and output sample rate are equal the input + sample rate * nchannels. + + Output stream to frequency mapping: + + channel zero is at zero frequency. + + if mpoints is odd: + + Channels with increasing positive frequencies come from + channels 1 through (N-1)/2. + + Channel (N+1)/2 is the maximum negative frequency, and + frequency increases through N-1 which is one channel lower + than the zero frequency. + + if mpoints is even: + + Channels with increasing positive frequencies come from + channels 1 through (N/2)-1. + + Channel (N/2) is evenly split between the max positive and + negative bins. + + Channel (N/2)+1 is the maximum negative frequency, and + frequency increases through N-1 which is one channel lower + than the zero frequency. + + Channels near the frequency extremes end up getting cut + off by subsequent filters and therefore have diminished + utility. + """ + item_size = gr.sizeof_gr_complex + gr.hier_block2.__init__(self, "synthesis_filterbank", + gr.io_signature(mpoints, mpoints, item_size), + gr.io_signature(1, 1, item_size)) + + if taps is None: + taps = _generate_synthesis_taps(mpoints) + + # pad taps to multiple of mpoints + r = len(taps) % mpoints + if r != 0: + taps = taps + (mpoints - r) * (0,) + + # split in mpoints separate set of taps + sub_taps = _split_taps(taps, mpoints) + + self.ss2v = gr.streams_to_vector(item_size, mpoints) + self.ifft = gr.fft_vcc(mpoints, False, []) + self.v2ss = gr.vector_to_streams(item_size, mpoints) + # mpoints filters go in here... + self.ss2s = gr.streams_to_stream(item_size, mpoints) + + for i in range(mpoints): + self.connect((self, i), (self.ss2v, i)) + + self.connect(self.ss2v, self.ifft, self.v2ss, self) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[i]) + self.connect((self.v2ss, i), f) + self.connect(f, (self.ss2s, i)) + + +class analysis_filterbank(gr.hier_block2): + """ + Uniformly modulated polyphase DFT filter bank: analysis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, mpoints, taps=None): + """ + Takes 1 complex stream in, produces M complex streams out + that runs at 1/M times the input sample rate + + @param fg: flow_graph + @param mpoints: number of freq bins/interpolation factor/subbands + @param taps: filter taps for subband filter + + Same channel to frequency mapping as described above. + """ + item_size = gr.sizeof_gr_complex + gr.hier_block2.__init__(self, "analysis_filterbank", + gr.io_signature(1, 1, item_size), + gr.io_signature(mpoints, mpoints, item_size)) + + if taps is None: + taps = _generate_synthesis_taps(mpoints) + + # pad taps to multiple of mpoints + r = len(taps) % mpoints + if r != 0: + taps = taps + (mpoints - r) * (0,) + + # split in mpoints separate set of taps + sub_taps = _split_taps(taps, mpoints) + + # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps) + + self.s2ss = gr.stream_to_streams(item_size, mpoints) + # filters here + self.ss2v = gr.streams_to_vector(item_size, mpoints) + self.fft = gr.fft_vcc(mpoints, True, []) + self.v2ss = gr.vector_to_streams(item_size, mpoints) + + self.connect(self, self.s2ss) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1]) + self.connect((self.s2ss, i), f) + self.connect(f, (self.ss2v, i)) + + self.connect(self.ss2v, self.fft, self.v2ss) + + for i in range(mpoints): + self.connect((self.v2ss, i), (self, i)) + + + diff --git a/gnuradio-core/src/python/gnuradio/blksimpl2/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blksimpl2/rational_resampler.py new file mode 100644 index 0000000000..93e90f3464 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl2/rational_resampler.py @@ -0,0 +1,131 @@ +# +# Copyright 2005,2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru + +_plot = None + +def design_filter(interpolation, decimation, fractional_bw): + """ + Given the interpolation rate, decimation rate and a fractional bandwidth, + design a set of taps. + + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param fractional_bw: fractional bandwidth in (0, 0.5) 0.4 works well. + @type fractional_bw: float + @returns: sequence of numbers + """ + + if fractional_bw >= 0.5 or fractional_bw <= 0: + raise ValueError, "Invalid fractional_bandwidth, must be in (0, 0.5)" + + beta = 5.0 + trans_width = 0.5 - fractional_bw + mid_transition_band = 0.5 - trans_width/2 + + taps = gr.firdes.low_pass(interpolation, # gain + 1, # Fs + mid_transition_band/interpolation, # trans mid point + trans_width/interpolation, # transition width + gr.firdes.WIN_KAISER, + beta # beta + ) + + return taps + + + +class _rational_resampler_base(gr.hier_block2): + """ + base class for all rational resampler variants. + """ + def __init__(self, resampler_base, + interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter. + + Either taps or fractional_bw may be specified, but not both. + If neither is specified, a reasonable default, 0.4, is used as + the fractional_bw. + + @param interpolation: interpolation factor + @type interpolation: integer > 0 + @param decimation: decimation factor + @type decimation: integer > 0 + @param taps: optional filter coefficients + @type taps: sequence + @param fractional_bw: fractional bandwidth in (0, 0.5), measured at final freq (use 0.4) + @type fractional_bw: float + """ + + if not isinstance(interpolation, int) or interpolation < 1: + raise ValueError, "interpolation must be an integer >= 1" + + if not isinstance(decimation, int) or decimation < 1: + raise ValueError, "decimation must be an integer >= 1" + + if taps is None and fractional_bw is None: + fractional_bw = 0.4 + + d = gru.gcd(interpolation, decimation) + interpolation = interpolation // d + decimation = decimation // d + + if taps is None: + taps = design_filter(interpolation, decimation, fractional_bw) + + resampler = resampler_base(interpolation, decimation, taps) + gr.hier_block2.__init__(self, resampler.name(), + gr.io_signature(1, 1, resampler.input_signature().sizeof_stream_item(0)), + gr.io_signature(1, 1, resampler.output_signature().sizeof_stream_item(1))) + + self.connect(self, resampler, self) + + +class rational_resampler_fff(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + float input, float output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_fff, + interpolation, decimation, taps, fractional_bw) + +class rational_resampler_ccf(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and float taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccf, + interpolation, decimation, taps, fractional_bw) + +class rational_resampler_ccc(_rational_resampler_base): + def __init__(self, interpolation, decimation, taps=None, fractional_bw=None): + """ + Rational resampling polyphase FIR filter with + complex input, complex output and complex taps. + """ + _rational_resampler_base.__init__(self, gr.rational_resampler_base_ccc, + interpolation, decimation, taps, fractional_bw) diff --git a/gr-pager/src/Makefile.am b/gr-pager/src/Makefile.am index b818be0e34..85f3465119 100644 --- a/gr-pager/src/Makefile.am +++ b/gr-pager/src/Makefile.am @@ -29,7 +29,7 @@ TESTS = \ bin_SCRIPTS = \ usrp_flex.py \ - aypabtu.py + usrp_flex_all.py noinst_PYTHON = \ qa_pager.py @@ -70,8 +70,8 @@ ourpython_PYTHON = \ pager_swig.py \ flex_demod.py \ usrp_flex.py \ - aypabtu.py - + usrp_flex_all.py + ourlib_LTLIBRARIES = _pager_swig.la # These are the source files that go into the shared library diff --git a/gr-pager/src/aypabtu.py b/gr-pager/src/aypabtu.py deleted file mode 100755 index c742744c36..0000000000 --- a/gr-pager/src/aypabtu.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python - -from math import pi -from gnuradio import gr, gru, usrp, optfir, audio, eng_notation, blks, pager -from gnuradio.eng_option import eng_option -from optparse import OptionParser -from string import split, join, printable - -class usrp_source_c(gr.hier_block): - """ - Create a USRP source object supplying complex floats. - - Selects user supplied subdevice or chooses first available one. - - Calibration value is the offset from the tuned frequency to - the actual frequency. - """ - def __init__(self, fg, subdev_spec, decim, gain=None, calibration=0.0): - self._decim = decim - self._src = usrp.source_c() - if subdev_spec is None: - subdev_spec = usrp.pick_rx_subdevice(self._src) - self._subdev = usrp.selected_subdev(self._src, subdev_spec) - self._src.set_mux(usrp.determine_rx_mux_value(self._src, subdev_spec)) - self._src.set_decim_rate(self._decim) - - # If no gain specified, set to midrange - if gain is None: - g = self._subdev.gain_range() - gain = (g[0]+g[1])/2.0 - - self._subdev.set_gain(gain) - self._cal = calibration - - gr.hier_block.__init__(self, fg, self._src, self._src) - - def tune(self, freq): - result = usrp.tune(self._src, 0, self._subdev, freq+self._cal) - # TODO: deal with residual - - def rate(self): - return self._src.adc_rate()/self._decim - -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) - blks.analysis_filterbank.__init__(self, fg, num_channels, taps) - -class app_flow_graph(gr.flow_graph): - def __init__(self, options, args, queue): - gr.flow_graph.__init__(self) - self.options = options - self.args = args - - # FIXME: Parameterize - # - # Difference between upper and lower must evenly divide into USRP sample rate - # and also must be divisible by 25000 - options.channel_rate = 25000 - options.channel_pass = 8000 - options.channel_stop = 10000 - - if_rate = options.upper_freq - options.lower_freq - center_freq = options.lower_freq + if_rate/2 - num_channels = int(if_rate/options.channel_rate) - decim = int(64000000/if_rate) - - self.SRC = usrp_source_c(self, options.rx_board, decim, options.gain, options.calibration) - self.CHAN = channelizer(self, if_rate, options.channel_rate, options.channel_pass, options.channel_stop) - - self.connect(self.SRC, self.CHAN) - for i in range(num_channels): - freq = options.lower_freq + i*options.channel_rate - if freq > center_freq: - freq -= if_rate/2 - else: - freq += if_rate/2 - FLEX = pager.flex_demod(self, options.channel_rate, queue) - self.connect((self.CHAN, i), FLEX.INPUT) - - self.SRC.tune(center_freq) - -def make_printable(data): - return "".join(char for char in data if char in printable) - -def main(): - parser = OptionParser(option_class=eng_option) - parser.add_option("", "--upper-freq", type="eng_float", help="lower Rx frequency", metavar="LOWER") - parser.add_option("", "--lower-freq", type="eng_float", help="upper Rx frequency", metavar="UPPER") - parser.add_option("-R", "--rx-board", type="subdev", help="select USRP Rx side A or B (default=first daughterboard found)", metavar="SIDE") - parser.add_option("-c", "--calibration", type="eng_float", default=0.0, help="set frequency offset to Hz", metavar="Hz") - parser.add_option("-g", "--gain", type="int", help="set RF gain", metavar="dB") - (options, args) = parser.parse_args() - - # FIXME: parameter sanity checks - - queue = gr.msg_queue() - fg = app_flow_graph(options, args, queue) - try: - fg.start() - while 1: - msg = queue.delete_head() # Blocking read - fields = split(msg.to_string(), chr(128)) - print join([make_printable(field) for field in fields], '|') - - except KeyboardInterrupt: - fg.stop() - -if __name__ == "__main__": - main() diff --git a/gr-pager/src/flex_demod.py b/gr-pager/src/flex_demod.py index 0d35cf3f42..fc9dfeeac5 100644 --- a/gr-pager/src/flex_demod.py +++ b/gr-pager/src/flex_demod.py @@ -19,70 +19,43 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, gru, optfir, blks +from gnuradio import gr, gru, optfir, blks2 from math import pi import pager_swig -chan_rate = 16000 - -class flex_demod: +class flex_demod(gr.hier_block2): """ FLEX pager protocol demodulation block. This block demodulates a band-limited, complex down-converted baseband channel into FLEX protocol frames. - Flow graph (so far): - - RSAMP - Resample incoming stream to 16000 sps - QUAD - Quadrature demodulator converts FSK to baseband amplitudes - LPF - Low pass filter to remove noise prior to slicer - SLICER - Converts input to one of four symbols (0, 1, 2, 3) - SYNC - Converts symbol stream to four phases of FLEX blocks - DEINTx - Deinterleaves FLEX blocks into datawords - PARSEx - Parse a single FLEX phase worth of data words into pages - --- - - @param fg: flowgraph - @param channel_rate: incoming sample rate of the baseband channel - @type sample_rate: integer """ + def __init__(self, queue, freq=0.0, verbose=False, log=False): + gr.hier_block2.__init__(self, "flex_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(0,0,0)) - def __init__(self, fg, channel_rate, queue): - k = chan_rate/(2*pi*4800) # 4800 Hz max deviation - QUAD = gr.quadrature_demod_cf(k) - self.INPUT = QUAD - - if channel_rate != chan_rate: - interp = gru.lcm(channel_rate, chan_rate)/channel_rate - decim = gru.lcm(channel_rate, chan_rate)/chan_rate - RESAMP = blks.rational_resampler_ccf(fg, interp, decim) - self.INPUT = RESAMP - - taps = optfir.low_pass(1.0, chan_rate, 3200, 6400, 0.1, 60) - LPF = gr.fir_filter_fff(1, taps) - SLICER = pager_swig.slicer_fb(.001, .00001) # Attack, decay - SYNC = pager_swig.flex_sync(chan_rate) - - if channel_rate != chan_rate: - fg.connect(RESAMP, QUAD, LPF, SLICER, SYNC) - else: - fg.connect(QUAD, LPF, SLICER, SYNC) - - DEINTA = pager_swig.flex_deinterleave() - PARSEA = pager_swig.flex_parse(queue) - - DEINTB = pager_swig.flex_deinterleave() - PARSEB = pager_swig.flex_parse(queue) - - DEINTC = pager_swig.flex_deinterleave() - PARSEC = pager_swig.flex_parse(queue) - - DEINTD = pager_swig.flex_deinterleave() - PARSED = pager_swig.flex_parse(queue) + k = 25000/(2*pi*1600) # 4800 Hz max deviation + quad = gr.quadrature_demod_cf(k) + self.connect(self, quad) - fg.connect((SYNC, 0), DEINTA, PARSEA) - fg.connect((SYNC, 1), DEINTB, PARSEB) - fg.connect((SYNC, 2), DEINTC, PARSEC) - fg.connect((SYNC, 3), DEINTD, PARSED) + rsamp = blks2.rational_resampler_fff(16, 25) + slicer = pager_swig.slicer_fb(1e-5) # DC removal averaging filter constant + sync = pager_swig.flex_sync(16000) + + self.connect(quad, rsamp, slicer, sync) + + for i in range(4): + self.connect((sync, i), pager_swig.flex_deinterleave(), pager_swig.flex_parse(queue, freq)) + + if log: + suffix = '_'+ "%3.3f" % (freq/1e6,) + '.dat' + quad_sink = gr.file_sink(gr.sizeof_float, 'quad'+suffix) + rsamp_sink = gr.file_sink(gr.sizeof_float, 'rsamp'+suffix) + slicer_sink = gr.file_sink(gr.sizeof_char, 'slicer'+suffix) + self.connect(rsamp, rsamp_sink) + self.connect(quad, quad_sink) + self.connect(slicer, slicer_sink) +
\ No newline at end of file diff --git a/gr-pager/src/pager.i b/gr-pager/src/pager.i index cc73a54734..f8e17965be 100644 --- a/gr-pager/src/pager.i +++ b/gr-pager/src/pager.i @@ -39,12 +39,12 @@ GR_SWIG_BLOCK_MAGIC(pager,slicer_fb); -pager_slicer_fb_sptr pager_make_slicer_fb(float alpha, float beta); +pager_slicer_fb_sptr pager_make_slicer_fb(float alpha); class pager_slicer_fb : public gr_sync_block { private: - pager_slicer_fb(float alpha, float beta); + pager_slicer_fb(float alpha); public: }; @@ -81,12 +81,12 @@ public: GR_SWIG_BLOCK_MAGIC(pager,flex_parse); -pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue); +pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue, float freq); class pager_flex_parse : public gr_block { private: - pager_flex_parse(gr_msg_queue_sptr queue); + pager_flex_parse(gr_msg_queue_sptr queue, float freq); public: }; diff --git a/gr-pager/src/pager_flex_parse.cc b/gr-pager/src/pager_flex_parse.cc index 7178ba516c..012b171702 100644 --- a/gr-pager/src/pager_flex_parse.cc +++ b/gr-pager/src/pager_flex_parse.cc @@ -1,5 +1,5 @@ /* - * Copyright 2004,2006 Free Software Foundation, Inc. + * Copyright 2004,2006,2007 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -28,17 +28,19 @@ #include <gr_io_signature.h> #include <ctype.h> #include <iostream> +#include <iomanip> -pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue) +pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue, float freq) { - return pager_flex_parse_sptr(new pager_flex_parse(queue)); + return pager_flex_parse_sptr(new pager_flex_parse(queue, freq)); } -pager_flex_parse::pager_flex_parse(gr_msg_queue_sptr queue) : +pager_flex_parse::pager_flex_parse(gr_msg_queue_sptr queue, float freq) : gr_sync_block("flex_parse", - gr_make_io_signature(1, 1, sizeof(gr_int32)), - gr_make_io_signature(0, 0, 0)), - d_queue(queue) + gr_make_io_signature(1, 1, sizeof(gr_int32)), + gr_make_io_signature(0, 0, 0)), + d_queue(queue), + d_freq(freq) { d_count = 0; } @@ -131,7 +133,11 @@ void pager_flex_parse::parse_data() continue; // Invalid offsets d_payload.str(""); - d_payload << d_capcode << FIELD_DELIM << d_type << FIELD_DELIM; + d_payload.setf(std::ios::showpoint); + d_payload << std::setprecision(6) << std::setw(7) + << d_freq/1e6 << FIELD_DELIM + << std::setw(10) << d_capcode << FIELD_DELIM + << flex_page_desc[d_type] << FIELD_DELIM; if (is_alphanumeric_page(d_type)) parse_alphanumeric(mw1, mw2-1, j); @@ -142,9 +148,6 @@ void pager_flex_parse::parse_data() else parse_unknown(mw1, mw2); - //std::cout << d_payload.str() << std::endl; - //fflush(stdout); - gr_message_sptr msg = gr_make_message_from_string(std::string(d_payload.str())); d_queue->handle(msg); } @@ -166,8 +169,8 @@ void pager_flex_parse::parse_alphanumeric(int mw1, int mw2, int j) mw2--; } - d_payload << frag << FIELD_DELIM; - d_payload << cont << FIELD_DELIM; + //d_payload << frag << FIELD_DELIM; + //d_payload << cont << FIELD_DELIM; for (int i = mw1; i <= mw2; i++) { gr_int32 dw = d_datawords[i]; diff --git a/gr-pager/src/pager_flex_parse.h b/gr-pager/src/pager_flex_parse.h index e5a225b28a..dd0510b6dc 100644 --- a/gr-pager/src/pager_flex_parse.h +++ b/gr-pager/src/pager_flex_parse.h @@ -30,7 +30,7 @@ class pager_flex_parse; typedef boost::shared_ptr<pager_flex_parse> pager_flex_parse_sptr; -pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue); +pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue, float freq); /*! * \brief flex parse description @@ -43,8 +43,8 @@ class pager_flex_parse : public gr_sync_block { private: // Constructors - friend pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue); - pager_flex_parse(gr_msg_queue_sptr queue); + friend pager_flex_parse_sptr pager_make_flex_parse(gr_msg_queue_sptr queue, float freq); + pager_flex_parse(gr_msg_queue_sptr queue, float freq); std::ostringstream d_payload; gr_msg_queue_sptr d_queue; // Destination for decoded pages @@ -52,10 +52,11 @@ private: int d_count; // Count of received codewords gr_int32 d_datawords[88]; // 11 blocks of 8 32-bit words - page_type_t d_type; // Current page type + page_type_t d_type; // Current page type int d_capcode; // Current page destination address bool d_laddr; // Current page has long address - + float d_freq; // Channel frequency + void parse_data(); // Handle a frame's worth of data void parse_capcode(gr_int32 aw1, gr_int32 aw2); void parse_alphanumeric(int mw1, int mw2, int j); diff --git a/gr-pager/src/pager_slicer_fb.cc b/gr-pager/src/pager_slicer_fb.cc index 98e8a2b000..57b1b02498 100644 --- a/gr-pager/src/pager_slicer_fb.cc +++ b/gr-pager/src/pager_slicer_fb.cc @@ -1,5 +1,5 @@ /* - * Copyright 2004,2006 Free Software Foundation, Inc. + * Copyright 2004,2006,2007 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,23 +26,19 @@ #include <pager_slicer_fb.h> #include <gr_io_signature.h> -pager_slicer_fb_sptr pager_make_slicer_fb(float alpha, float beta) +pager_slicer_fb_sptr pager_make_slicer_fb(float alpha) { - return pager_slicer_fb_sptr(new pager_slicer_fb(alpha, beta)); + return pager_slicer_fb_sptr(new pager_slicer_fb(alpha)); } -pager_slicer_fb::pager_slicer_fb(float alpha, float beta) : +pager_slicer_fb::pager_slicer_fb(float alpha) : gr_sync_block ("slicer_fb", gr_make_io_signature (1, 1, sizeof(float)), gr_make_io_signature (1, 1, sizeof(unsigned char))) { d_alpha = alpha; - d_beta = beta; - d_max = 0.0; - d_hi = 0.0; + d_beta = 1.0-alpha; d_avg = 0.0; - d_lo = 0.0; - d_min = 0.0; } // Tracks average, minimum, and peak, then converts input into one of: @@ -53,34 +49,22 @@ unsigned char pager_slicer_fb::slice(float sample) unsigned char decision; // Update DC level and remove - d_avg = d_avg*(1.0-d_alpha)+sample*d_alpha; + d_avg = d_avg*d_beta+sample*d_alpha; sample -= d_avg; if (sample > 0) { - if (sample > d_hi) { // In max region - d_max = d_max*(1.0-d_alpha) + sample*d_alpha; + if (sample > 2.0) decision = 3; - } - else { - d_max -= (d_max-d_avg)*d_beta; // decay otherwise + else decision = 2; - } } else { - if (sample < d_lo) { // In min region - d_min = d_min*(1.0-d_alpha) + sample*d_alpha; + if (sample < -2.0) decision = 0; - } - else { - d_min -= (d_min-d_avg)*d_beta; // decay otherwise + else decision = 1; - } } - d_hi = d_max*2.0/3.0; - d_lo = d_min*2.0/3.0; - - //fprintf(stderr, "%f %d\n", sample, decision); return decision; } diff --git a/gr-pager/src/pager_slicer_fb.h b/gr-pager/src/pager_slicer_fb.h index 4751bb47eb..cde7aa94e2 100644 --- a/gr-pager/src/pager_slicer_fb.h +++ b/gr-pager/src/pager_slicer_fb.h @@ -1,5 +1,5 @@ /* - * Copyright 2006 Free Software Foundation, Inc. + * Copyright 2006,2007 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -27,7 +27,7 @@ class pager_slicer_fb; typedef boost::shared_ptr<pager_slicer_fb> pager_slicer_fb_sptr; -pager_slicer_fb_sptr pager_make_slicer_fb(float alpha, float beta); +pager_slicer_fb_sptr pager_make_slicer_fb(float alpha); /*! * \brief slicer description @@ -36,18 +36,14 @@ pager_slicer_fb_sptr pager_make_slicer_fb(float alpha, float beta); class pager_slicer_fb : public gr_sync_block { private: - friend pager_slicer_fb_sptr pager_make_slicer_fb(float alpha, float beta); - pager_slicer_fb(float alpha, float beta); + friend pager_slicer_fb_sptr pager_make_slicer_fb(float alpha); + pager_slicer_fb(float alpha); unsigned char slice(float sample); - float d_alpha; // Attack constant - float d_beta; // Decay constant - float d_max; // Maximum value for symbol comparison - float d_hi; // High side decision boundary + float d_alpha; // DC removal time constant + float d_beta; // 1.0-d_alpha float d_avg; // Average value for DC offset subtraction - float d_lo; // Low side decision boundary - float d_min; // Minimum value for symbol comparison public: int work (int noutput_items, diff --git a/gr-pager/src/pageri_flex_modes.cc b/gr-pager/src/pageri_flex_modes.cc index b2bb4e29cc..7126a889a7 100644 --- a/gr-pager/src/pageri_flex_modes.cc +++ b/gr-pager/src/pageri_flex_modes.cc @@ -1,5 +1,5 @@ /* - * Copyright 2006 Free Software Foundation, Inc. + * Copyright 2006,2007 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -34,6 +34,18 @@ const int num_flex_modes = sizeof(flex_modes)/sizeof(flex_modes[0]); unsigned char flex_bcd[17] = "0123456789 U -]["; +const char *flex_page_desc[] = +{ + "ENC", + "UNK", + "TON", + "NUM", + "SPN", + "ALN", + "BIN", + "NNM" +}; + int find_flex_mode(gr_int32 sync_code) { for (int i = 0; i < num_flex_modes; i++) diff --git a/gr-pager/src/pageri_flex_modes.h b/gr-pager/src/pageri_flex_modes.h index 09e5952b9f..438579b27b 100644 --- a/gr-pager/src/pageri_flex_modes.h +++ b/gr-pager/src/pageri_flex_modes.h @@ -1,5 +1,5 @@ /* - * Copyright 2006 Free Software Foundation, Inc. + * Copyright 2006,2007 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -35,6 +35,7 @@ typedef struct flex_mode flex_mode_t; extern const flex_mode_t flex_modes[]; +extern const char *flex_page_desc[]; extern const int num_flex_modes; int find_flex_mode(gr_int32 sync_code); extern unsigned char flex_bcd[]; diff --git a/gr-pager/src/usrp_flex.py b/gr-pager/src/usrp_flex.py index a1a790333e..095a7cb9cb 100755 --- a/gr-pager/src/usrp_flex.py +++ b/gr-pager/src/usrp_flex.py @@ -31,101 +31,92 @@ from string import split, join This example application demonstrates receiving and demodulating the FLEX pager protocol. -A receive chain is built up of the following signal processing -blocks: - -USRP - Daughter board source generating complex baseband signal. -CHAN - Low pass filter to select channel bandwidth -AGC - Automatic gain control leveling signal at [-1.0, +1.0] -FLEX - FLEX pager protocol decoder - The following are required command line parameters: --f FREQ USRP receive frequency +-f FREQ USRP receive frequency The following are optional command line parameters: -R SUBDEV Daughter board specification, defaults to first found +-F FILE Read samples from a file instead of USRP. -c FREQ Calibration offset. Gets added to receive frequency. Defaults to 0.0 Hz. -g GAIN Daughterboard gain setting. Defaults to mid-range. --r RFSQL RF squelch in db. Defaults to -50.0. +-l Log flow graph to files (LOTS of data) +-v Verbose output Once the program is running, ctrl-break (Ctrl-C) stops operation. """ -class usrp_source_c(gr.hier_block): - """ - Create a USRP source object supplying complex floats. - - Selects user supplied subdevice or chooses first available one. - - Calibration value is the offset from the tuned frequency to - the actual frequency. - """ - def __init__(self, fg, subdev_spec, decim, gain=None, calibration=0.0): - self._decim = decim - self._src = usrp.source_c() - if subdev_spec is None: - subdev_spec = usrp.pick_rx_subdevice(self._src) - self._subdev = usrp.selected_subdev(self._src, subdev_spec) - self._src.set_mux(usrp.determine_rx_mux_value(self._src, subdev_spec)) - self._src.set_decim_rate(self._decim) - - # If no gain specified, set to midrange - if gain is None: - g = self._subdev.gain_range() - gain = (g[0]+g[1])/2.0 - - self._subdev.set_gain(gain) - self._cal = calibration - - gr.hier_block.__init__(self, fg, self._src, self._src) - - def tune(self, freq): - result = usrp.tune(self._src, 0, self._subdev, freq+self._cal) - # TODO: deal with residual - - def rate(self): - return self._src.adc_rate()/self._decim - -class app_flow_graph(gr.flow_graph): - def __init__(self, options, args, queue): - gr.flow_graph.__init__(self) +class app_top_block(gr.top_block): + def __init__(self, options, queue): + gr.top_block.__init__(self, "usrp_flex") self.options = options - self.args = args - - USRP = usrp_source_c(self, # Flow graph - options.rx_subdev_spec, # Daugherboard spec - 256, # IF decimation ratio gets 250K if_rate - options.gain, # Receiver gain - options.calibration) # Frequency offset - USRP.tune(options.frequency) - - if_rate = USRP.rate() - channel_rate = 25000 - channel_decim = int(if_rate / channel_rate) - - CHAN_taps = optfir.low_pass(1.0, # Filter gain - if_rate, # Sample rate - 8000, # One sided modulation bandwidth - 10000, # One sided channel bandwidth - 0.1, # Passband ripple - 60) # Stopband attenuation - - CHAN = gr.freq_xlating_fir_filter_ccf(channel_decim, # Decimation rate - CHAN_taps, # Filter taps - 0.0, # Offset frequency - if_rate) # Sample rate - - AGC = gr.agc_cc(1.0/channel_rate, # Time constant - 1.0, # Reference power - 1.0, # Initial gain - 1.0) # Maximum gain + + if options.from_file is None: + # Set up USRP source with specified RX daughterboard + src = usrp.source_c() + if options.rx_subdev_spec == None: + options.subdev_spec = usrp.pick_rx_subdevice(src) + subdev = usrp.selected_subdev(src, options.rx_subdev_spec) + src.set_mux(usrp.determine_rx_mux_value(src, options.rx_subdev_spec)) + + # Grab 250 KHz of spectrum (sample rate becomes 250 ksps complex) + src.set_decim_rate(256) + + # If no gain specified, set to midrange + if options.gain is None: + g = subdev.gain_range() + options.gain = (g[0]+g[1])/2.0 + subdev.set_gain(options.gain) + + # Tune daughterboard + actual_frequency = options.frequency+options.calibration + tune_result = usrp.tune(src, 0, subdev, actual_frequency) + if not tune_result: + sys.stderr.write("Failed to set center frequency to"+`actual_frequency`+"\n") + sys.exit(1) + + if options.verbose: + print "Using RX daughterboard", subdev.side_and_name() + print "USRP gain is", options.gain + print "USRP tuned to", actual_frequency + + else: + # Use supplied file as source of samples + src = gr.file_source(gr.sizeof_gr_complex, options.from_file) + if options.verbose: + print "Reading samples from", options.from_file + + if options.log and not options.from_file: + usrp_sink = gr.file_sink(gr.sizeof_gr_complex, 'usrp.dat') + self.connect(src, usrp_sink) + + # Set up 22KHz-wide bandpass about center frequency. Decimate by 10 + # to get channel rate of 25Ksps + taps = optfir.low_pass(1.0, # Filter gain + 250e3, # Sample rate + 11000, # One-sided modulation bandwidth + 12500, # One-sided channel bandwidth + 0.1, # Passband ripple + 60) # Stopband attenuation - FLEX = pager.flex_demod(self, 25000, queue) + if options.verbose: + print "Channel filter has", len(taps), "taps." + + chan = gr.freq_xlating_fir_filter_ccf(10, # Decimation rate + taps, # Filter taps + 0.0, # Offset frequency + 250e3) # Sample rate - self.connect(USRP, CHAN, AGC, FLEX.INPUT) + if options.log: + chan_sink = gr.file_sink(gr.sizeof_gr_complex, 'chan.dat') + self.connect(chan, chan_sink) + + # FLEX protocol demodulator + flex = pager.flex_demod(queue, options.frequency, options.verbose, options.log) + + self.connect(src, chan, flex) def main(): parser = OptionParser(option_class=eng_option) @@ -137,20 +128,28 @@ def main(): help="set frequency offset to Hz", metavar="Hz") parser.add_option("-g", "--gain", type="int", default=None, help="set RF gain", metavar="dB") + parser.add_option("-l", "--log", action="store_true", default=False, + help="log flowgraph to files (LOTS of data)") + parser.add_option("-v", "--verbose", action="store_true", default=False, + help="display debug output") + parser.add_option("-F", "--from-file", default=None, + help="read samples from file instead of USRP") (options, args) = parser.parse_args() - if len(args) > 0 or options.frequency == None: + if len(args) > 0 or (options.frequency == None and options.from_file == None): print "Run 'usrp_flex.py -h' for options." sys.exit(1) - if options.frequency < 1e6: - options.frequency *= 1e6 - - queue = gr.msg_queue() + if options.frequency == None: + options.frequency = 0.0 - fg = app_flow_graph(options, args, queue) + # Flow graph emits pages into message queue + queue = gr.msg_queue() + tb = app_top_block(options, queue) + r = gr.runtime(tb) + try: - fg.start() + r.start() while 1: if not queue.empty_p(): msg = queue.delete_head() # Blocking read @@ -167,7 +166,7 @@ def main(): time.sleep(1) except KeyboardInterrupt: - fg.stop() + r.stop() if __name__ == "__main__": main() diff --git a/gr-pager/src/usrp_flex_all.py b/gr-pager/src/usrp_flex_all.py new file mode 100755 index 0000000000..6fe9bc34c2 --- /dev/null +++ b/gr-pager/src/usrp_flex_all.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +from gnuradio import gr, gru, usrp, optfir, eng_notation, blks2, pager +from gnuradio.eng_option import eng_option +from optparse import OptionParser +from string import split, join, printable +import time + +class app_top_block(gr.top_block): + def __init__(self, options, queue): + gr.top_block.__init__(self, "usrp_flex_all") + + if options.from_file is not None: + src = gr.file_source(gr.sizeof_gr_complex, options.from_file) + if options.verbose: + print "Reading samples from file", options.from_file + else: + src = usrp.source_c() + if options.rx_subdev_spec is None: + options.rx_subdev_spec = usrp.pick_rx_subdevice(src) + subdev = usrp.selected_subdev(src, options.rx_subdev_spec) + src.set_mux(usrp.determine_rx_mux_value(src, options.rx_subdev_spec)) + src.set_decim_rate(20) + result = usrp.tune(src, 0, subdev, 930.5e6+options.calibration) + if options.verbose: + print "Using", subdev.name(), " for receiving." + print "Tuned USRP to", 930.5e6+options.calibration + + taps = gr.firdes.low_pass(1.0, + 1.0, + 1.0/128.0*0.4, + 1.0/128.0*0.1, + gr.firdes.WIN_HANN) + + if options.verbose: + print "Channel filter has", len(taps), "taps" + + bank = blks2.analysis_filterbank(128, taps) + self.connect(src, bank) + + if options.log and options.from_file == None: + src_sink = gr.file_sink(gr.sizeof_gr_complex, 'usrp.dat') + self.connect(src, src_sink) + + for i in range(128): + if i < 64: + freq = 930.5e6+i*25e3 + else: + freq = 928.9e6+(i-64)*25e3 + + if ((i < 20) or (i >= 60 and i < 68) or (i >= 108)): # Non-forward channel frequencies + self.connect((bank, i), gr.null_sink(gr.sizeof_gr_complex)) + else: + self.connect((bank, i), pager.flex_demod(queue, freq, options.verbose, options.log)) + if options.log: + self.connect((bank, i), gr.file_sink(gr.sizeof_gr_complex, 'chan_'+'%3.3f'%(freq/1e6)+'.dat')) + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-R", "--rx-subdev-spec", type="subdev", + help="select USRP Rx side A or B (default=first daughterboard found)") + parser.add_option("-c", "--calibration", type="eng_float", default=0.0, + help="set frequency offset to Hz", metavar="Hz") + parser.add_option("-g", "--gain", type="int", + help="set RF gain", metavar="dB") + parser.add_option("-F", "--from-file", default=None, + help="Read from file instead of USRP") + parser.add_option("-l", "--log", action="store_true", default=False, + help="log flowgraph to files (LOTS of data)") + parser.add_option("-v", "--verbose", action="store_true", default=False, + help="display debug output") + (options, args) = parser.parse_args() + + if options.verbose: + print options + + queue = gr.msg_queue() + tb = app_top_block(options, queue) + r = gr.runtime(tb) + + try: + r.start() + while 1: + if not queue.empty_p(): + msg = queue.delete_head() # Blocking read + page = join(split(msg.to_string(), chr(128)), '|') + disp = [] + for n in range(len(page)): + if ord(page[n]) < 32: + disp.append('.') + else: + disp.append(page[n]) + print join(disp, '') + + else: + time.sleep(1) + + except KeyboardInterrupt: + r.stop() + +if __name__ == "__main__": + main() diff --git a/gr-usrp/src/db_dbs_rx.py b/gr-usrp/src/db_dbs_rx.py index 5541df8c74..377bc2ba09 100644 --- a/gr-usrp/src/db_dbs_rx.py +++ b/gr-usrp/src/db_dbs_rx.py @@ -264,7 +264,7 @@ class db_dbs_rx (db_base.db_base): vcofreq = 2 * freq self._set_ade(1) rmin=max(2,self._refclk_freq()/2e6) - rmax=min(128,self._refclk_freq()/150e3) + rmax=min(128,self._refclk_freq()/500e3) r = 2 n=0 best_r = 2 |