diff options
Diffstat (limited to 'gnuradio-core/src/python/gnuradio/blksimpl')
17 files changed, 1892 insertions, 0 deletions
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am new file mode 100644 index 0000000000..415920b299 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am @@ -0,0 +1,49 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +include $(top_srcdir)/Makefile.common + +# EXTRA_DIST = run_tests.in +# TESTS = run_tests + +grblkspythondir = $(grpythondir)/blksimpl + +grblkspython_PYTHON = \ + __init__.py \ + am_demod.py \ + filterbank.py \ + fm_demod.py \ + fm_emph.py \ + gmsk2.py \ + gmsk2_pkt.py \ + nbfm_rx.py \ + nbfm_tx.py \ + pkt.py \ + rational_resampler.py \ + standard_squelch.py \ + wfm_rcv.py \ + wfm_rcv_pll.py \ + wfm_tx.py + + +noinst_PYTHON = + +CLEANFILES = *.pyc *.pyo diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py new file mode 100644 index 0000000000..a4917cf64c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/__init__.py @@ -0,0 +1 @@ +# make this a package diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py new file mode 100644 index 0000000000..309f5e6504 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/am_demod.py @@ -0,0 +1,75 @@ +# +# Copyright 2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, optfir + +class am_demod_cf(gr.hier_block): + """ + Generalized AM demodulation block with audio filtering. + + This block demodulates a band-limited, complex down-converted AM + channel into the the original baseband signal, applying low pass + filtering to the audio output. It produces a float stream in the + range [-1.0, +1.0]. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the AM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + @param audio_pass: audio low pass filter passband frequency + @type audio_pass: float + @param audio_stop: audio low pass filter stop frequency + @type audio_stop: float + """ + def __init__(self, fg, channel_rate, audio_decim, audio_pass, audio_stop): + MAG = gr.complex_to_mag() + DCR = gr.add_const_ff(-1.0) + + audio_taps = optfir.low_pass(0.5, # Filter gain + channel_rate, # Sample rate + audio_pass, # Audio passband + audio_stop, # Audio stopband + 0.1, # Passband ripple + 60) # Stopband attenuation + LPF = gr.fir_filter_fff(audio_decim, audio_taps) + + fg.connect(MAG, DCR, LPF) + gr.hier_block.__init__(self, fg, MAG, LPF) + +class demod_10k0a3e_cf(am_demod_cf): + """ + AM demodulation block, 10 KHz channel. + + This block demodulates an AM channel conformant to 10K0A3E emission + standards, such as broadcast band AM transmissions. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the AM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + am_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 5000, # Audio passband + 5500) # Audio stopband +
\ No newline at end of file diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real new file mode 100644 index 0000000000..1b3a14f3e7 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/digital_voice.py.real @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +""" +Digital voice Tx and Rx using GSM 13kbit vocoder and GMSK. + +Runs channel at 32kbit/sec. Currently uses fake channel coding, +but there's room for a rate 1/2 coder. +""" + +from gnuradio import gr, gru +from gnuradio.blksimpl.gmsk import gmsk_mod, gmsk_demod + +from gnuradio.vocoder import gsm_full_rate + +# Size of gsm full rate speech encoder output packet in bytes + +GSM_FRAME_SIZE = 33 + +# Size of packet in bytes that we send to GMSK modulator: +# +# Target: 256kS/sec air rate. +# +# 256kS 1 sym 1 bit 1 byte 0.020 sec 80 bytes +# ---- * ----- * ----- * ------ * --------- = -------- +# sec 8 S 1 sym 8 bits frame frame +# +# gr_simple_framer add 10 bytes of overhead. + +AIR_FRAME_SIZE = 70 + + +class digital_voice_tx(gr.hier_block): + """ + Hierarchical block for digital voice tranmission. + + The input is 8kS/sec floating point audio in the range [-1,+1] + The output is 256kS/sec GMSK modulated complex baseband signal in the range [-1,+1]. + """ + def __init__(self, fg): + samples_per_symbol = 8 + symbol_rate = 32000 + bt = 0.3 # Gaussian filter bandwidth * symbol time + + src_scale = gr.multiply_const_ff(32767) + f2s = gr.float_to_short() + voice_coder = gsm_full_rate.encode_sp() + + channel_coder = gr.fake_channel_encoder_pp(GSM_FRAME_SIZE, AIR_FRAME_SIZE) + p2s = gr.parallel_to_serial(gr.sizeof_char, AIR_FRAME_SIZE) + + mod = gmsk_mod(fg, sps=samples_per_symbol, + symbol_rate=symbol_rate, bt=bt, + p_size=AIR_FRAME_SIZE) + + fg.connect(src_scale, f2s, voice_coder, channel_coder, p2s, mod) + gr.hier_block.__init__(self, fg, src_scale, mod) + + +class digital_voice_rx(gr.hier_block): + """ + Hierarchical block for digital voice reception. + + The input is 256kS/sec GMSK modulated complex baseband signal. + The output is 8kS/sec floating point audio in the range [-1,+1] + """ + def __init__(self, fg): + samples_per_symbol = 8 + symbol_rate = 32000 + + demod = gmsk_demod(fg, sps=samples_per_symbol, + symbol_rate=symbol_rate, + p_size=AIR_FRAME_SIZE) + + s2p = gr.serial_to_parallel(gr.sizeof_char, AIR_FRAME_SIZE) + channel_decoder = gr.fake_channel_decoder_pp(AIR_FRAME_SIZE, GSM_FRAME_SIZE) + + voice_decoder = gsm_full_rate.decode_ps() + s2f = gr.short_to_float () + sink_scale = gr.multiply_const_ff(1.0/32767.) + + fg.connect(demod, s2p, channel_decoder, voice_decoder, s2f, sink_scale) + gr.hier_block.__init__(self, fg, demod, sink_scale) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py new file mode 100644 index 0000000000..bd23f7936d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/filterbank.py @@ -0,0 +1,160 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, 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_block): + """ + Uniformly modulated polyphase DFT filter bank: synthesis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, fg, 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 + + 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) + + fg.connect(self.ss2v, self.ifft, self.v2ss) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[i]) + fg.connect((self.v2ss, i), f) + fg.connect(f, (self.ss2s, i)) + + gr.hier_block.__init__(self, fg, self.ss2v, self.ss2s) + + +class analysis_filterbank(gr.hier_block): + """ + Uniformly modulated polyphase DFT filter bank: analysis + + See http://cnx.rice.edu/content/m10424/latest + """ + def __init__(self, fg, 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 + + 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) + + # build mpoints fir filters... + for i in range(mpoints): + f = gr.fft_filter_ccc(1, sub_taps[mpoints-i-1]) + fg.connect((self.s2ss, i), f) + fg.connect(f, (self.ss2v, i)) + + fg.connect(self.ss2v, self.fft, self.v2ss) + gr.hier_block.__init__(self, fg, self.s2ss, self.v2ss) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py new file mode 100644 index 0000000000..9487e0f0fc --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_demod.py @@ -0,0 +1,122 @@ +# +# Copyright 2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_deemph +from math import pi + +class fm_demod_cf(gr.hier_block): + """ + Generalized FM demodulation block with deemphasis and audio + filtering. + + This block demodulates a band-limited, complex down-converted FM + channel into the the original baseband signal, optionally applying + deemphasis. Low pass filtering is done on the resultant signal. It + produces an output float strem in the range of [-1.0, +1.0]. + + @param fg: flowgraph + @param channel_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param deviation: maximum FM deviation (default = 5000) + @type deviation: float + @param audio_decim: input to output decimation rate + @type audio_decim: integer + @param audio_pass: audio low pass filter passband frequency + @type audio_pass: float + @param audio_stop: audio low pass filter stop frequency + @type audio_stop: float + @param gain: gain applied to audio output (default = 1.0) + @type gain: float + @param tau: deemphasis time constant (default = 75e-6), specify 'None' + to prevent deemphasis + """ + def __init__(self, fg, channel_rate, audio_decim, deviation, + audio_pass, audio_stop, gain=1.0, tau=75e-6): + + """ + # Equalizer for ~100 us delay + delay = 100e-6 + num_taps = int(channel_rate*delay) + + mu = 1e-4/num_taps + print "CMA: delay =", delay, "n =", num_taps, "mu =", mu + CMA = gr.cma_equalizer_cc(num_taps, 1.0, mu) + """ + k = channel_rate/(2*pi*deviation) + QUAD = gr.quadrature_demod_cf(k) + + audio_taps = optfir.low_pass(gain, # Filter gain + channel_rate, # Sample rate + audio_pass, # Audio passband + audio_stop, # Audio stopband + 0.1, # Passband ripple + 60) # Stopband attenuation + LPF = gr.fir_filter_fff(audio_decim, audio_taps) + + if tau is not None: + DEEMPH = fm_deemph(fg, channel_rate, tau) + fg.connect(QUAD, DEEMPH, LPF) + else: + fg.connect(QUAD, LPF) + + gr.hier_block.__init__(self, fg, QUAD, LPF) + +class demod_20k0f3e_cf(fm_demod_cf): + """ + NBFM demodulation block, 20 KHz channels + + This block demodulates a complex, downconverted, narrowband FM + channel conforming to 20K0F3E emission standards, outputting + floats in the range [-1.0, +1.0]. + + @param fg: flowgraph + @param sample_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + fm_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 5000, # Deviation + 3000, # Audio passband frequency + 4000) # Audio stopband frequency + +class demod_200kf3e_cf(fm_demod_cf): + """ + WFM demodulation block, mono. + + This block demodulates a complex, downconverted, wideband FM + channel conforming to 200KF3E emission standards, outputting + floats in the range [-1.0, +1.0]. + + @param fg: flowgraph + @param sample_rate: incoming sample rate of the FM baseband + @type sample_rate: integer + @param audio_decim: input to output decimation rate + @type audio_decim: integer + """ + def __init__(self, fg, channel_rate, audio_decim): + fm_demod_cf.__init__(self, fg, channel_rate, audio_decim, + 75000, # Deviation + 15000, # Audio passband + 16000, # Audio stopband + 20.0) # Audio gain diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py new file mode 100644 index 0000000000..5c256f5d04 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/fm_emph.py @@ -0,0 +1,145 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +import math + + +# +# 1 +# H(s) = ------- +# 1 + s +# +# tau is the RC time constant. +# critical frequency: w_p = 1/tau +# +# We prewarp and use the bilinear z-transform to get our IIR coefficients. +# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# + +class fm_deemph(gr.hier_block): + """ + FM Deemphasis IIR filter. + """ + def __init__(self, fg, fs, tau=75e-6): + """ + @param fg: flow graph + @type fg: gr.flow_graph + @param fs: sampling frequency in Hz + @type fs: float + @param tau: Time constant in seconds (75us in US, 50us in EUR) + @type tau: float + """ + w_p = 1/tau + w_pp = math.tan (w_p / (fs * 2)) # prewarped analog freq + + a1 = (w_pp - 1)/(w_pp + 1) + b0 = w_pp/(1 + w_pp) + b1 = b0 + + btaps = [b0, b1] + ataps = [1, a1] + + if 0: + print "btaps =", btaps + print "ataps =", ataps + global plot1 + plot1 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True) + + deemph = gr.iir_filter_ffd(btaps, ataps) + gr.hier_block.__init__(self, fg, deemph, deemph) + +# +# 1 + s*t1 +# H(s) = ---------- +# 1 + s*t2 +# +# I think this is the right transfer function. +# +# +# This fine ASCII rendition is based on Figure 5-15 +# in "Digital and Analog Communication Systems", Leon W. Couch II +# +# +# R1 +# +-----||------+ +# | | +# o------+ +-----+--------o +# | C1 | | +# +----/\/\/\/--+ \ +# / +# \ R2 +# / +# \ +# | +# o--------------------------+--------o +# +# f1 = 1/(2*pi*t1) = 1/(2*pi*R1*C) +# +# 1 R1 + R2 +# f2 = ------- = ------------ +# 2*pi*t2 2*pi*R1*R2*C +# +# t1 is 75us in US, 50us in EUR +# f2 should be higher than our audio bandwidth. +# +# +# The Bode plot looks like this: +# +# +# /---------------- +# / +# / <-- slope = 20dB/decade +# / +# -------------/ +# f1 f2 +# +# We prewarp and use the bilinear z-transform to get our IIR coefficients. +# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# + +class fm_preemph(gr.hier_block): + """ + FM Preemphasis IIR filter. + """ + def __init__(self, fg, fs, tau=75e-6): + """ + @param fg: flow graph + @type fg: gr.flow_graph + @param fs: sampling frequency in Hz + @type fs: float + @param tau: Time constant in seconds (75us in US, 50us in EUR) + @type tau: float + """ + + # FIXME make this compute the right answer + + btaps = [1] + ataps = [1] + + if 0: + print "btaps =", btaps + print "ataps =", ataps + global plot2 + plot2 = gru.gnuplot_freqz (gru.freqz (btaps, ataps), fs, True) + + preemph = gr.iir_filter_ffd(btaps, ataps) + gr.hier_block.__init__(self, fg, preemph, preemph) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py new file mode 100644 index 0000000000..68d189679d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py @@ -0,0 +1,159 @@ +# +# GMSK modulation and demodulation. +# +# +# Copyright 2005,2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +# See gnuradio-examples/python/gmsk2 for examples + +from gnuradio import gr +from math import pi +import Numeric + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK mod/demod with steams of bytes as data i/o +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk2_mod(gr.hier_block): + + def __init__(self, fg, spb = 2, bt = 0.3): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud >= 2 + @type spb: integer + @param bt: Gaussian filter bandwidth * symbol time + @type bt: float + """ + if not isinstance(spb, int) or spb < 2: + raise TypeError, "sbp must be an integer >= 2" + self.spb = spb + + ntaps = 4 * spb # up to 3 bits in filter at once + sensitivity = (pi / 2) / spb # phase change per bit = pi / 2 + + # Turn it into NRZ data. + self.nrz = gr.bytes_to_syms() + + # Form Gaussian filter + + # Generate Gaussian response (Needs to be convolved with window below). + self.gaussian_taps = gr.firdes.gaussian( + 1, # gain + spb, # symbol_rate + bt, # bandwidth * symbol time + ntaps # number of taps + ) + + self.sqwave = (1,) * spb # rectangular window + self.taps = Numeric.convolve(Numeric.array(self.gaussian_taps),Numeric.array(self.sqwave)) + self.gaussian_filter = gr.interp_fir_filter_fff(spb, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + # Connect + fg.connect(self.nrz, self.gaussian_filter, self.fmmod) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.nrz, self.fmmod) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM + + +class gmsk2_demod(gr.hier_block): + + def __init__(self, fg, spb=2, omega=None, gain_mu=0.03, mu=0.5, + omega_relative_limit=0.000200, freq_error=0.0): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of symbols ready to be sliced at zero. + + @param fg: flow graph + @type fg: flow graph + @param spb: samples per baud + @type spb: integer + + Clock recovery parameters. These all have reasonble defaults. + + @param omega: nominal relative freq (defaults to spb) + @type omega: float + @param gain_mu: controls rate of mu adjustment + @type gain_mu: float + @param mu: fractional delay [0.0, 1.0] + @type mu: float + @param omega_relative_limit: sets max variation in omega + @type omega_relative_limit: float, typically 0.000200 (200 ppm) + @param freq_error: bit rate error as a fraction + @param float + """ + if spb < 2: + raise TypeError, "sbp >= 2" + self.spb = spb + + if omega is None: + omega = spb*(1+freq_error) + + gain_omega = .25*gain_mu*gain_mu # critically damped + + # Automatic gain control + self.preamp = gr.multiply_const_cc(10e-5) + self.agc = gr.agc_cc(1e-3, 1, 1, 1000) + + # Demodulate FM + sensitivity = (pi / 2) / spb + self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) + + alpha = 0.0008 + + # the clock recovery block tracks the symbol clock and resamples as needed. + # the output of the block is a stream of soft symbols (float) + self.clock_recovery = gr.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu, + omega_relative_limit) + + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample + self.slicer = gr.binary_slicer_fb() + + fg.connect(self.preamp, self.agc, self.fmdemod, self.clock_recovery, self.slicer) + + # Initialize base class + gr.hier_block.__init__(self, fg, self.preamp, self.slicer) + + def samples_per_baud(self): + return self.spb + + def bits_per_baud(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py new file mode 100644 index 0000000000..af586239a5 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py @@ -0,0 +1,174 @@ +# +# Copyright 2005,2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from math import pi +import Numeric + +from gnuradio import gr, packet_utils +import gnuradio.gr.gr_threading as _threading +import gmsk2 + + +def _deprecation_warning(old_name, new_name): + print '#' + print '# Warning: %s is deprecated and will be removed soon.' % (old_name,) + print '# Please use the modulation independent block, %s.' % (new_name,) + print "#" + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk2_mod_pkts(gr.hier_block): + """ + GSM modulator that is a GNU Radio source. + + Send packets by calling send_pkt + """ + def __init__(self, fg, access_code=None, msgq_limit=2, pad_for_usrp=True, *args, **kwargs): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) modulation. + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's between 1 and 64 long + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + + See gmsk_mod for remaining parameters + """ + _deprecation_warning('gmsk2_mod_pkts', 'mod_pkts') + + self.pad_for_usrp = pad_for_usrp + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + # accepts messages from the outside world + self.pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) + self.gmsk_mod = gmsk2.gmsk2_mod(fg, *args, **kwargs) + fg.connect(self.pkt_input, self.gmsk_mod) + gr.hier_block.__init__(self, fg, None, self.gmsk_mod) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self.pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = packet_utils.make_packet(payload, + self.gmsk_mod.samples_per_baud(), + self.gmsk_mod.bits_per_baud(), + self._access_code, + self.pad_for_usrp) + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + self.pkt_input.msgq().insert_tail(msg) + + + +class gmsk2_demod_pkts(gr.hier_block): + """ + GSM demodulator that is a GNU Radio sink. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, fg, access_code=None, callback=None, threshold=-1, *args, **kwargs): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param fg: flow graph + @type fg: flow graph + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) + @type threshold: int + + See gmsk_demod for remaining parameters. + """ + + _deprecation_warning('gmsk2_demod_pkts', 'demod_pkts') + + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + if threshold == -1: + threshold = 12 # FIXME raise exception + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + self.gmsk_demod = gmsk2.gmsk2_demod(fg, *args, **kwargs) + self.correlator = gr.correlate_access_code_bb(access_code, threshold) + + self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) + fg.connect(self.gmsk_demod, self.correlator, self.framer_sink) + + gr.hier_block.__init__(self, fg, self.gmsk_demod, None) + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + def carrier_sensed(self): + """ + Return True if we detect carrier. + """ + return False # FIXME + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + #def stop(self): + # self.keep_running = False + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string()) + if self.callback: + self.callback(ok, payload) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py new file mode 100644 index 0000000000..39059ec9ca --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_rx.py @@ -0,0 +1,87 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_deemph +from gnuradio.blksimpl.standard_squelch import standard_squelch + +class nbfm_rx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): + """ + Narrow Band FM Receiver. + + Takes a single complex baseband input stream and produces a single + float output stream of audio sample in the range [-1, +1]. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 5e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + + Exported sub-blocks (attributes): + squelch + quad_demod + deemph + audio_filter + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + squelch_threshold = 20 # dB + #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001) + + # FM Demodulator input: complex; output: float + k = quad_rate/(2*math.pi*max_dev) + self.quad_demod = gr.quadrature_demod_cf(k) + + # FM Deemphasis IIR filter + self.deemph = fm_deemph (fg, quad_rate, tau=tau) + + # compute FIR taps for audio filter + audio_decim = quad_rate // audio_rate + audio_taps = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + 4.5e3, # Audio LPF cutoff + 2.5e3, # Transition band + gr.firdes.WIN_HAMMING) # filter type + + print "len(audio_taps) =", len(audio_taps) + + # Decimating audio filter + # input: float; output: float; taps: float + self.audio_filter = gr.fir_filter_fff(audio_decim, audio_taps) + + fg.connect(self.quad_demod, self.deemph, self.audio_filter) + + gr.hier_block.__init__(self, fg, self.quad_demod, self.audio_filter) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py new file mode 100644 index 0000000000..2f636b67fa --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/nbfm_tx.py @@ -0,0 +1,95 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_preemph + +#from gnuradio import ctcss + +class nbfm_tx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): + """ + Narrow Band FM Transmitter. + + Takes a single float input stream of audio samples in the range [-1,+1] + and produces a single FM modulated complex baseband output. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 5e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + + do_interp = audio_rate != quad_rate + + if do_interp: + interp_factor = quad_rate / audio_rate + interp_taps = optfir.low_pass (interp_factor, # gain + quad_rate, # Fs + 4500, # passband cutoff + 7000, # stopband cutoff + 0.1, # passband ripple dB + 40) # stopband atten dB + + #print "len(interp_taps) =", len(interp_taps) + self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps) + + self.preemph = fm_preemph (fg, quad_rate, tau=tau) + + k = 2 * math.pi * max_dev / quad_rate + self.modulator = gr.frequency_modulator_fc (k) + + if do_interp: + fg.connect (self.interpolator, self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.interpolator, self.modulator) + else: + fg.connect(self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.preemph, self.modulator) + + +#class ctcss_gen_f(gr.sig_source_f): +# def __init__(self, sample_rate, tone_freq): +# gr.sig_source_f.__init__(self, sample_rate, gr.SIN_WAVE, tone_freq, 0.1, 0.0) +# +# def set_tone (self, tone): +# gr.sig_source_f.set_frequency(self,tone) + +class ctcss_gen_f(gr.hier_block): + def __init__(self, fg, sample_rate, tone_freq): + self.plgen = gr.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0) + + gr.hier_block.__init__(self, fg, self.plgen, self.plgen) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py new file mode 100644 index 0000000000..3ebb7229c6 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py @@ -0,0 +1,156 @@ +# +# Copyright 2005,2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from math import pi +import Numeric + +from gnuradio import gr, packet_utils +import gnuradio.gr.gr_threading as _threading + + +# ///////////////////////////////////////////////////////////////////////////// +# mod/demod with packets as i/o +# ///////////////////////////////////////////////////////////////////////////// + +class mod_pkts(gr.hier_block): + """ + Wrap an arbitrary digital modulator in our packet handling framework. + + Send packets by calling send_pkt + """ + def __init__(self, fg, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True): + """ + Hierarchical block for sending packets + + Packets to be sent are enqueued by calling send_pkt. + The output is the complex modulated signal at baseband. + + @param fg: flow graph + @type fg: flow graph + @param modulator: instance of modulator class (gr_block or hier_block) + @type modulator: complex baseband out + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's between 1 and 64 long + @param msgq_limit: maximum number of messages in message queue + @type msgq_limit: int + @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + + See gmsk_mod for remaining parameters + """ + self._modulator = modulator + self._pad_for_usrp = pad_for_usrp + + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + # accepts messages from the outside world + self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) + fg.connect(self._pkt_input, self._modulator) + gr.hier_block.__init__(self, fg, None, self._modulator) + + def send_pkt(self, payload='', eof=False): + """ + Send the payload. + + @param payload: data to send + @type payload: string + """ + if eof: + msg = gr.message(1) # tell self._pkt_input we're not sending any more packets + else: + # print "original_payload =", string_to_hex_list(payload) + pkt = packet_utils.make_packet(payload, + self._modulator.samples_per_baud(), + self._modulator.bits_per_baud(), + self._access_code, + self._pad_for_usrp) + #print "pkt =", string_to_hex_list(pkt) + msg = gr.message_from_string(pkt) + self._pkt_input.msgq().insert_tail(msg) + + + +class demod_pkts(gr.hier_block): + """ + Wrap an arbitrary digital demodulator in our packet handling framework. + + The input is complex baseband. When packets are demodulated, they are passed to the + app via the callback. + """ + + def __init__(self, fg, demodulator, access_code=None, callback=None, threshold=-1): + """ + Hierarchical block for demodulating and deframing packets. + + The input is the complex modulated signal at baseband. + Demodulated packets are sent to the handler. + + @param fg: flow graph + @type fg: flow graph + @param demodulator: instance of demodulator class (gr_block or hier_block) + @type demodulator: complex baseband in + @param access_code: AKA sync vector + @type access_code: string of 1's and 0's + @param callback: function of two args: ok, payload + @type callback: ok: bool; payload: string + @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) + @type threshold: int + """ + + self._demodulator = demodulator + if access_code is None: + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + + if threshold == -1: + threshold = 12 # FIXME raise exception + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY + self.correlator = gr.correlate_access_code_bb(access_code, threshold) + + self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) + fg.connect(self._demodulator, self.correlator, self.framer_sink) + + gr.hier_block.__init__(self, fg, self._demodulator, None) + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + + +class _queue_watcher_thread(_threading.Thread): + def __init__(self, rcvd_pktq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.rcvd_pktq = rcvd_pktq + self.callback = callback + self.keep_running = True + self.start() + + + def run(self): + while self.keep_running: + msg = self.rcvd_pktq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string()) + if self.callback: + self.callback(ok, payload) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py new file mode 100644 index 0000000000..8b928b102f --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/rational_resampler.py @@ -0,0 +1,137 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, 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 + """ + + global _plot + + 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 + ) + # print "len(resampler_taps) =", len(taps) + # _plot = gru.gnuplot_freqz(gru.freqz(taps, 1), 1) + + return taps + + + +class _rational_resampler_base(gr.hier_block): + """ + base class for all rational resampler variants. + """ + def __init__(self, resampler_base, fg, + 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 fg: flow graph + @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_block.__init__(self, fg, resampler, resampler) + + + +class rational_resampler_fff(_rational_resampler_base): + def __init__(self, fg, 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, fg, + interpolation, decimation, + taps, fractional_bw) + +class rational_resampler_ccf(_rational_resampler_base): + def __init__(self, fg, 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, fg, + interpolation, decimation, + taps, fractional_bw) + +class rational_resampler_ccc(_rational_resampler_base): + def __init__(self, fg, 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, fg, + interpolation, decimation, + taps, fractional_bw) + diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py new file mode 100644 index 0000000000..2c80dd5af7 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/standard_squelch.py @@ -0,0 +1,73 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir + +class standard_squelch(gr.hier_block): + def __init__(self, fg, audio_rate): + + self.input_node = gr.add_const_ff(0) # FIXME kludge + + self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615)) + self.low_square = gr.multiply_ff() + self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant + + self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615)) + self.hi_square = gr.multiply_ff() + self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) + + self.sub = gr.sub_ff(); + self.add = gr.add_ff(); + self.gate = gr.threshold_ff(0.3,0.43,0) + self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) + + self.div = gr.divide_ff() + self.squelch_mult = gr.multiply_ff() + + fg.connect (self.input_node, (self.squelch_mult, 0)) + + fg.connect (self.input_node,self.low_iir) + fg.connect (self.low_iir,(self.low_square,0)) + fg.connect (self.low_iir,(self.low_square,1)) + fg.connect (self.low_square,self.low_smooth,(self.sub,0)) + fg.connect (self.low_smooth, (self.add,0)) + + fg.connect (self.input_node,self.hi_iir) + fg.connect (self.hi_iir,(self.hi_square,0)) + fg.connect (self.hi_iir,(self.hi_square,1)) + fg.connect (self.hi_square,self.hi_smooth,(self.sub,1)) + fg.connect (self.hi_smooth, (self.add,1)) + + fg.connect (self.sub, (self.div, 0)) + fg.connect (self.add, (self.div, 1)) + fg.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1)) + + gr.hier_block.__init__(self, fg, self.input_node, self.squelch_mult) + + def set_threshold(self, threshold): + self.gate.set_hi(threshold) + + def threshold(self): + return self.gate.hi() + + def squelch_range(self): + return (0.0, 1.0, 1.0/100) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py new file mode 100644 index 0000000000..55dbbaa0c4 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv.py @@ -0,0 +1,72 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +from gnuradio.blksimpl.fm_emph import fm_deemph +import math + +class wfm_rcv(gr.hier_block): + def __init__ (self, fg, quad_rate, audio_decimation): + """ + Hierarchical block for demodulating a broadcast FM signal. + + The input is the downconverted complex baseband signal (gr_complex). + The output is the demodulated audio (float). + + @param fg: flow graph. + @type fg: flow graph + @param quad_rate: input sample rate of complex baseband input. + @type quad_rate: float + @param audio_decimation: how much to decimate quad_rate to get to audio. + @type audio_decimation: integer + """ + volume = 20. + + max_dev = 75e3 + fm_demod_gain = quad_rate/(2*math.pi*max_dev) + audio_rate = quad_rate / audio_decimation + + + # We assign to self so that outsiders can grab the demodulator + # if they need to. E.g., to plot its output. + # + # input: complex; output: float + self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) + + # input: float; output: float + self.deemph = fm_deemph (fg, audio_rate) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0, # gain + quad_rate, # sampling rate + audio_rate/2 - width_of_transition_band, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + # input: float; output: float + self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + + fg.connect (self.fm_demod, self.audio_filter, self.deemph) + + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + self.deemph) # tail of the pipeline diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py new file mode 100644 index 0000000000..d116090e0a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_rcv_pll.py @@ -0,0 +1,206 @@ +# +# Copyright 2005,2006 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +from gnuradio import gr +from gnuradio.blksimpl.fm_emph import fm_deemph +import math + +class wfm_rcv_pll(gr.hier_block): + def __init__ (self, fg, demod_rate, audio_decimation): + """ + Hierarchical block for demodulating a broadcast FM signal. + + The input is the downconverted complex baseband signal (gr_complex). + The output is two streams of the demodulated audio (float) 0=Left, 1=Right. + + @param fg: flow graph. + @type fg: flow graph + @param demod_rate: input sample rate of complex baseband input. + @type demod_rate: float + @param audio_decimation: how much to decimate demod_rate to get to audio. + @type audio_decimation: integer + """ + + bandwidth = 200e3 + audio_rate = demod_rate / audio_decimation + + + # We assign to self so that outsiders can grab the demodulator + # if they need to. E.g., to plot its output. + # + # input: complex; output: float + alpha = 0.25*bandwidth * math.pi / demod_rate + beta = alpha * alpha / 4.0 + max_freq = 2.0*math.pi*100e3/demod_rate + + self.fm_demod = gr.pll_freqdet_cf (alpha,beta,max_freq,-max_freq) + + # input: float; output: float + self.deemph_Left = fm_deemph (fg, audio_rate) + self.deemph_Right = fm_deemph (fg, audio_rate) + + # compute FIR filter taps for audio filter + width_of_transition_band = audio_rate / 32 + audio_coeffs = gr.firdes.low_pass (1.0 , # gain + demod_rate, # sampling rate + 15000 , + width_of_transition_band, + gr.firdes.WIN_HAMMING) + # input: float; output: float + self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) + if 1: + # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain + # We pick off the negative frequency half because we want to base band by it! + ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS + + stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0, + demod_rate, + -19020, + -18980, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + + #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs) + #print "stereo carrier filter ", stereo_carrier_filter_coeffs + #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate + + # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain + + stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0, + demod_rate, + 38000-15000/2, + 38000+15000/2, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) + #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs + # construct overlap add filter system from coefficients for stereo carrier + + self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs) + + # carrier is twice the picked off carrier so arrange to do a commplex multiply + + self.stereo_carrier_generator = gr.multiply_cc(); + + # Pick off the rds signal + + stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0, + demod_rate, + 57000 - 1500, + 57000 + 1500, + width_of_transition_band, + gr.firdes.WIN_HAMMING) + #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) + #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs + # construct overlap add filter system from coefficients for stereo carrier + + self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs) + self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs) + + + + + + + self.rds_carrier_generator = gr.multiply_cc(); + self.rds_signal_generator = gr.multiply_cc(); + self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex); + + + + alpha = 5 * 0.25 * math.pi / (audio_rate) + beta = alpha * alpha / 4.0 + max_freq = -2.0*math.pi*18990/audio_rate; + min_freq = -2.0*math.pi*19010/audio_rate; + + self.stereo_carrier_pll_recovery = gr.pll_carriertracking_cc(alpha,beta,max_freq,min_freq); + self.stereo_carrier_pll_recovery.squelch_enable(False); + + + # set up mixer (multiplier) to get the L-R signal at baseband + + self.stereo_basebander = gr.multiply_cc(); + + # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero + + self.LmR_real = gr.complex_to_real(); + self.Make_Left = gr.add_ff(); + self.Make_Right = gr.sub_ff(); + + self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs) + + + if 1: + + # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier + fg.connect (self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0)) + # send the already filtered carrier to the otherside of the carrier + fg.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1)) + # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz. + + # send the new carrier to one side of the mixer (multiplier) + fg.connect (self.stereo_carrier_generator, (self.stereo_basebander,0)) + # send the demphasized audio to the DSBSC pick off filter, the complex + # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier + fg.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1)) + # the result is BASEBANDED DSBSC with phase zero! + + # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer + fg.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0)) + #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter + fg.connect (self.LmR_real,(self.Make_Right,1)) + + # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone + fg.connect (self.stereo_basebander,(self.rds_carrier_generator,0)) + fg.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1)) + # take signal, filter off rds, send into mixer 0 channel + fg.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0)) + # take rds_carrier_generator output and send into mixer 1 channel + fg.connect (self.rds_carrier_generator,(self.rds_signal_generator,1)) + # send basebanded rds signal and send into "processor" which for now is a null sink + fg.connect (self.rds_signal_generator,self_rds_signal_processor) + + + if 1: + # pick off the audio, L+R that is what we used to have and send it to the summer + fg.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1)) + # take the picked off L+R audio and send it to the PLUS side of the subtractor + fg.connect(self.audio_filter,(self.Make_Right, 0)) + # The result of Make_Left gets (L+R) + (L-R) and results in 2*L + # The result of Make_Right gets (L+R) - (L-R) and results in 2*R + + + # kludge the signals into a stereo channel + kludge = gr.kludge_copy(gr.sizeof_float) + fg.connect(self.Make_Left , self.deemph_Left, (kludge, 0)) + fg.connect(self.Make_Right, self.deemph_Right, (kludge, 1)) + + #send it to the audio system + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + kludge) # tail of the pipeline + else: + fg.connect (self.fm_demod, self.audio_filter) + gr.hier_block.__init__(self, + fg, + self.fm_demod, # head of the pipeline + self.audio_filter) # tail of the pipeline diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py new file mode 100644 index 0000000000..505455571e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/wfm_tx.py @@ -0,0 +1,79 @@ +# +# 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# + +import math +from gnuradio import gr, optfir +from gnuradio.blksimpl.fm_emph import fm_preemph + +class wfm_tx(gr.hier_block): + def __init__(self, fg, audio_rate, quad_rate, tau=75e-6, max_dev=75e3): + """ + Wide Band FM Transmitter. + + Takes a single float input stream of audio samples in the range [-1,+1] + and produces a single FM modulated complex baseband output. + + @param fg: flow graph + @param audio_rate: sample rate of audio stream, >= 16k + @type audio_rate: integer + @param quad_rate: sample rate of output stream + @type quad_rate: integer + @param tau: preemphasis time constant (default 75e-6) + @type tau: float + @param max_dev: maximum deviation in Hz (default 75e3) + @type max_dev: float + + quad_rate must be an integer multiple of audio_rate. + """ + + # FIXME audio_rate and quad_rate ought to be exact rationals + audio_rate = int(audio_rate) + quad_rate = int(quad_rate) + + if quad_rate % audio_rate != 0: + raise ValueError, "quad_rate is not an integer multiple of audio_rate" + + + do_interp = audio_rate != quad_rate + + if do_interp: + interp_factor = quad_rate / audio_rate + interp_taps = optfir.low_pass (interp_factor, # gain + quad_rate, # Fs + 16000, # passband cutoff + 18000, # stopband cutoff + 0.1, # passband ripple dB + 40) # stopband atten dB + + print "len(interp_taps) =", len(interp_taps) + self.interpolator = gr.interp_fir_filter_fff (interp_factor, interp_taps) + + self.preemph = fm_preemph (fg, quad_rate, tau=tau) + + k = 2 * math.pi * max_dev / quad_rate + self.modulator = gr.frequency_modulator_fc (k) + + if do_interp: + fg.connect (self.interpolator, self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.interpolator, self.modulator) + else: + fg.connect(self.preemph, self.modulator) + gr.hier_block.__init__(self, fg, self.preemph, self.modulator) |