summaryrefslogtreecommitdiff
path: root/gr-analog/python
diff options
context:
space:
mode:
authorTom Rondeau <trondeau@vt.edu>2012-11-01 10:50:10 -0400
committerTom Rondeau <trondeau@vt.edu>2012-11-01 10:50:10 -0400
commit1b83549181135e79ad00b9bae0d11ab27efa4148 (patch)
tree6b036dd9d26423528f49bad563c199a25a33f646 /gr-analog/python
parentbbe599e4606b728c9610a17285b1e50f9e9d4dcb (diff)
analog: removing PLL blocks. Also moved blks2impl files that used PLLs to gr-analog.
This probably impacts a lot of examples; once we've removed all files in gnuradio-core, we'll need to retest.
Diffstat (limited to 'gr-analog/python')
-rw-r--r--gr-analog/python/CMakeLists.txt10
-rw-r--r--gr-analog/python/__init__.py11
-rw-r--r--gr-analog/python/am_demod.py72
-rw-r--r--gr-analog/python/fm_demod.py103
-rw-r--r--gr-analog/python/fm_emph.py150
-rw-r--r--gr-analog/python/nbfm_rx.py86
-rw-r--r--gr-analog/python/nbfm_tx.py88
-rw-r--r--gr-analog/python/standard_squelch.py77
-rw-r--r--gr-analog/python/wfm_rcv.py68
-rw-r--r--gr-analog/python/wfm_rcv_fmdet.py221
-rw-r--r--gr-analog/python/wfm_rcv_pll.py188
-rw-r--r--gr-analog/python/wfm_tx.py78
12 files changed, 1152 insertions, 0 deletions
diff --git a/gr-analog/python/CMakeLists.txt b/gr-analog/python/CMakeLists.txt
index 08570eea33..f42ee20086 100644
--- a/gr-analog/python/CMakeLists.txt
+++ b/gr-analog/python/CMakeLists.txt
@@ -25,6 +25,16 @@ include(GrPython)
GR_PYTHON_INSTALL(
FILES
__init__.py
+ am_demod.py
+ fm_demod.py
+ fm_emph.py
+ nbfm_rx.py
+ nbfm_tx.py
+ standard_squelch.py
+ wfm_rcv.py
+ wfm_rcv_fmdet.py
+ wfm_rcv_pll.py
+ wfm_tx.py
DESTINATION ${GR_PYTHON_DIR}/gnuradio/analog
COMPONENT "analog_python"
)
diff --git a/gr-analog/python/__init__.py b/gr-analog/python/__init__.py
index fc345028b9..a5ab5715d7 100644
--- a/gr-analog/python/__init__.py
+++ b/gr-analog/python/__init__.py
@@ -25,3 +25,14 @@ Blocks and utilities for analog modulation and demodulation.
# The presence of this file turns this directory into a Python package
from analog_swig import *
+
+from am_demod import *
+from fm_demod import *
+from fm_emph import *
+from nbfm_rx import *
+from nbfm_tx import *
+from standard_squelch import *
+from wfm_rcv import *
+from wfm_rcv_fmdet import *
+from wfm_rcv_pll import *
+from wfm_tx import *
diff --git a/gr-analog/python/am_demod.py b/gr-analog/python/am_demod.py
new file mode 100644
index 0000000000..2730aa2622
--- /dev/null
+++ b/gr-analog/python/am_demod.py
@@ -0,0 +1,72 @@
+#
+# Copyright 2006,2007,2012 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 3, 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
+from gnuradio import filter
+
+class am_demod_cf(gr.hier_block2):
+ """
+ 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].
+
+ Args:
+ channel_rate: incoming sample rate of the AM baseband (integer)
+ audio_decim: input to output decimation rate (integer)
+ audio_pass: audio low pass filter passband frequency (float)
+ audio_stop: audio low pass filter stop frequency (float)
+ """
+ def __init__(self, channel_rate, audio_decim, audio_pass, audio_stop):
+ gr.hier_block2.__init__(self, "am_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Input signature
+
+ MAG = gr.complex_to_mag()
+ DCR = gr.add_const_ff(-1.0)
+
+ audio_taps = filter.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 = filter.fir_filter_fff(audio_decim, audio_taps)
+
+ self.connect(self, MAG, DCR, LPF, self)
+
+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.
+
+ Args:
+ channel_rate: incoming sample rate of the AM baseband (integer)
+ audio_decim: input to output decimation rate (integer)
+ """
+ def __init__(self, channel_rate, audio_decim):
+ am_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Audio passband
+ 5500) # Audio stopband
diff --git a/gr-analog/python/fm_demod.py b/gr-analog/python/fm_demod.py
new file mode 100644
index 0000000000..c14ed1682e
--- /dev/null
+++ b/gr-analog/python/fm_demod.py
@@ -0,0 +1,103 @@
+#
+# Copyright 2006,2007,2012 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 3, 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, analog, filter
+from gnuradio.blks2impl.fm_emph import fm_deemph
+from math import pi
+
+class fm_demod_cf(gr.hier_block2):
+ """
+ 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].
+
+ Args:
+ channel_rate: incoming sample rate of the FM baseband (integer)
+ deviation: maximum FM deviation (default = 5000) (float)
+ audio_decim: input to output decimation rate (integer)
+ audio_pass: audio low pass filter passband frequency (float)
+ audio_stop: audio low pass filter stop frequency (float)
+ gain: gain applied to audio output (default = 1.0) (float)
+ tau: deemphasis time constant (default = 75e-6), specify 'None' to prevent deemphasis
+ """
+ def __init__(self, channel_rate, audio_decim, deviation,
+ audio_pass, audio_stop, gain=1.0, tau=75e-6):
+ gr.hier_block2.__init__(self, "fm_demod_cf",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ k = channel_rate/(2*pi*deviation)
+ QUAD = analog.quadrature_demod_cf(k)
+
+ audio_taps = filter.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 = filter.fir_filter_fff(audio_decim, audio_taps)
+
+ if tau is not None:
+ DEEMPH = fm_deemph(channel_rate, tau)
+ self.connect(self, QUAD, DEEMPH, LPF, self)
+ else:
+ self.connect(self, QUAD, LPF, self)
+
+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].
+
+ Args:
+ sample_rate: incoming sample rate of the FM baseband (integer)
+ audio_decim: input to output decimation rate (integer)
+ """
+ def __init__(self, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 5000, # Deviation
+ 3000, # Audio passband frequency
+ 4500) # 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].
+
+ Args:
+ sample_rate: incoming sample rate of the FM baseband (integer)
+ audio_decim: input to output decimation rate (integer)
+ """
+ def __init__(self, channel_rate, audio_decim):
+ fm_demod_cf.__init__(self, channel_rate, audio_decim,
+ 75000, # Deviation
+ 15000, # Audio passband
+ 16000, # Audio stopband
+ 20.0) # Audio gain
diff --git a/gr-analog/python/fm_emph.py b/gr-analog/python/fm_emph.py
new file mode 100644
index 0000000000..2821f6e3cd
--- /dev/null
+++ b/gr-analog/python/fm_emph.py
@@ -0,0 +1,150 @@
+#
+# Copyright 2005,2007,2012 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 3, 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, filter
+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_block2):
+ """
+ FM Deemphasis IIR filter.
+ """
+
+
+ def __init__(self, fs, tau=75e-6):
+ """
+
+ Args:
+ fs: sampling frequency in Hz (float)
+ tau: Time constant in seconds (75us in US, 50us in EUR) (float)
+ """
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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 = filter.iir_filter_ffd(btaps, ataps)
+ self.connect(self, deemph, self)
+
+#
+# 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_block2):
+ """
+ FM Preemphasis IIR filter.
+ """
+ def __init__(self, fs, tau=75e-6):
+ """
+
+ Args:
+ fs: sampling frequency in Hz (float)
+ tau: Time constant in seconds (75us in US, 50us in EUR) (float)
+ """
+
+ gr.hier_block2.__init__(self, "fm_deemph",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # 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 = filter.iir_filter_ffd(btaps, ataps)
+ self.connect(self, preemph, self)
diff --git a/gr-analog/python/nbfm_rx.py b/gr-analog/python/nbfm_rx.py
new file mode 100644
index 0000000000..1d307328b1
--- /dev/null
+++ b/gr-analog/python/nbfm_rx.py
@@ -0,0 +1,86 @@
+#
+# Copyright 2005,2012 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 3, 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 math
+from gnuradio import gr
+from gnuradio import filter
+from gnuradio import analog
+import fm_deemph
+
+class nbfm_rx(gr.hier_block2):
+ def __init__(self, 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].
+
+ Args:
+ audio_rate: sample rate of audio stream, >= 16k (integer)
+ quad_rate: sample rate of output stream (integer)
+ tau: preemphasis time constant (default 75e-6) (float)
+ max_dev: maximum deviation in Hz (default 5e3) (float)
+
+ quad_rate must be an integer multiple of audio_rate.
+
+ Exported sub-blocks (attributes):
+ squelch
+ quad_demod
+ deemph
+ audio_filter
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_rx",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ # 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 = analog.quadrature_demod_cf(k)
+
+ # FM Deemphasis IIR filter
+ self.deemph = fm_deemph(quad_rate, tau=tau)
+
+ # compute FIR taps for audio filter
+ audio_decim = quad_rate // audio_rate
+ audio_taps = filter.firdes.low_pass(1.0, # gain
+ quad_rate, # sampling rate
+ 2.7e3, # Audio LPF cutoff
+ 0.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 = filter.fir_filter_fff(audio_decim, audio_taps)
+
+ self.connect(self, self.quad_demod, self.deemph, self.audio_filter, self)
diff --git a/gr-analog/python/nbfm_tx.py b/gr-analog/python/nbfm_tx.py
new file mode 100644
index 0000000000..a108aac49e
--- /dev/null
+++ b/gr-analog/python/nbfm_tx.py
@@ -0,0 +1,88 @@
+#
+# Copyright 2005,2012 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 3, 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 math
+from gnuradio import gr, filter
+from gnuradio import analog
+from fm_emph import fm_preemph
+
+class nbfm_tx(gr.hier_block2):
+ def __init__(self, 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.
+
+ Args:
+ audio_rate: sample rate of audio stream, >= 16k (integer)
+ quad_rate: sample rate of output stream (integer)
+ tau: preemphasis time constant (default 75e-6) (float)
+ max_dev: maximum deviation in Hz (default 5e3) (float)
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+
+ gr.hier_block2.__init__(self, "nbfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # 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 = filter.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 = filter.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph(quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = analog.frequency_modulator_fc(k)
+
+ if do_interp:
+ self.connect(self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)
+
+
+class ctcss_gen_f(gr.hier_block2):
+ def __init__(self, sample_rate, tone_freq):
+ gr.hier_block2.__init__(self, "ctcss_gen_f",
+ gr.io_signature(0, 0, 0), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self.plgen = analog.sig_source_f(sample_rate, gr.GR_SIN_WAVE, tone_freq, 0.1, 0.0)
+ self.connect(self.plgen, self)
diff --git a/gr-analog/python/standard_squelch.py b/gr-analog/python/standard_squelch.py
new file mode 100644
index 0000000000..986397a48e
--- /dev/null
+++ b/gr-analog/python/standard_squelch.py
@@ -0,0 +1,77 @@
+#
+# Copyright 2005,2007,2012 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 3, 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 math
+from gnuradio import gr
+from gnuradio import filter
+
+class standard_squelch(gr.hier_block2):
+ def __init__(self, audio_rate):
+ gr.hier_block2.__init__(self, "standard_squelch",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ self.input_node = gr.add_const_ff(0) # FIXME kludge
+
+ self.low_iir = filter.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615))
+ self.low_square = gr.multiply_ff()
+ self.low_smooth = filter.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant
+
+ self.hi_iir = filter.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615))
+ self.hi_square = gr.multiply_ff()
+ self.hi_smooth = filter.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 = filter.single_pole_iir_filter_ff(1/(0.01*audio_rate))
+
+ self.div = gr.divide_ff()
+ self.squelch_mult = gr.multiply_ff()
+
+ self.connect(self, self.input_node)
+ self.connect(self.input_node, (self.squelch_mult, 0))
+
+ self.connect(self.input_node,self.low_iir)
+ self.connect(self.low_iir,(self.low_square,0))
+ self.connect(self.low_iir,(self.low_square,1))
+ self.connect(self.low_square,self.low_smooth,(self.sub,0))
+ self.connect(self.low_smooth, (self.add,0))
+
+ self.connect(self.input_node,self.hi_iir)
+ self.connect(self.hi_iir,(self.hi_square,0))
+ self.connect(self.hi_iir,(self.hi_square,1))
+ self.connect(self.hi_square,self.hi_smooth,(self.sub,1))
+ self.connect(self.hi_smooth, (self.add,1))
+
+ self.connect(self.sub, (self.div, 0))
+ self.connect(self.add, (self.div, 1))
+ self.connect(self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1))
+ self.connect(self.squelch_mult, self)
+
+ 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/gr-analog/python/wfm_rcv.py b/gr-analog/python/wfm_rcv.py
new file mode 100644
index 0000000000..addbb1d258
--- /dev/null
+++ b/gr-analog/python/wfm_rcv.py
@@ -0,0 +1,68 @@
+#
+# Copyright 2005,2007,2012 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 3, 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, analog, filter
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv(gr.hier_block2):
+ def __init__ (self, 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).
+
+ Args:
+ quad_rate: input sample rate of complex baseband input. (float)
+ audio_decimation: how much to decimate quad_rate to get to audio. (integer)
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_float)) # Output signature
+
+ 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 = analog.quadrature_demod_cf(fm_demod_gain)
+
+ # input: float; output: float
+ self.deemph = fm_deemph(audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = filter.firdes.low_pass(1.0, # gain
+ quad_rate, # sampling rate
+ audio_rate/2 - width_of_transition_band,
+ width_of_transition_band,
+ filter.firdes.WIN_HAMMING)
+ # input: float; output: float
+ self.audio_filter = filter.fir_filter_fff(audio_decimation, audio_coeffs)
+
+ self.connect (self, self.fm_demod, self.audio_filter, self.deemph, self)
diff --git a/gr-analog/python/wfm_rcv_fmdet.py b/gr-analog/python/wfm_rcv_fmdet.py
new file mode 100644
index 0000000000..68c7a3a1d7
--- /dev/null
+++ b/gr-analog/python/wfm_rcv_fmdet.py
@@ -0,0 +1,221 @@
+#
+# Copyright 2005,2006,2012 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 3, 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, filter, analog
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_fmdet(gr.hier_block2):
+ def __init__ (self, 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.
+
+ Args:
+ demod_rate: input sample rate of complex baseband input. (float)
+ audio_decimation: how much to decimate demod_rate to get to audio. (integer)
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv_fmdet",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(2, 2, gr.sizeof_float)) # Output signature
+ lowfreq = -125e3/demod_rate
+ highfreq = 125e3/demod_rate
+ 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
+
+ self.fm_demod = analog.fmdet_cf(demod_rate, lowfreq, highfreq, 0.05)
+
+ # input: float; output: float
+ self.deemph_Left = fm_deemph(audio_rate)
+ self.deemph_Right = fm_deemph(audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = filter.firdes.low_pass(1.0 , # gain
+ demod_rate, # sampling rate
+ 15000 ,
+ width_of_transition_band,
+ filter.firdes.WIN_HAMMING)
+
+ # input: float; output: float
+ self.audio_filter = filter.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 = \
+ filter.firdes.complex_band_pass(10.0,
+ demod_rate,
+ -19020,
+ -18980,
+ width_of_transition_band,
+ filter.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 = \
+ filter.firdes.complex_band_pass(20.0,
+ demod_rate,
+ 38000-15000/2,
+ 38000+15000/2,
+ width_of_transition_band,
+ filter.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 = \
+ filter.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 = \
+ filter.firdes.complex_band_pass(30.0,
+ demod_rate,
+ 57000 - 1500,
+ 57000 + 1500,
+ width_of_transition_band,
+ filter.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.rds_signal_filter = \
+ filter.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);
+
+ loop_bw = 2*math.pi/100.0
+ max_freq = -2.0*math.pi*18990/audio_rate;
+ min_freq = -2.0*math.pi*19010/audio_rate;
+ self.stereo_carrier_pll_recovery = analog.pll_refout_cc(loop_bw,
+ max_freq,
+ min_freq);
+
+ #self.stereo_carrier_pll_recovery.squelch_enable(False)
+ ##pll_refout does not have squelch yet, so disabled for
+ #now
+
+ # 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 = \
+ filter.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
+ self.connect(self, 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
+ # the resulting signal from this multiplier is the carrier
+ # with correct phase but at -38000 Hz.
+ self.connect(self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1))
+
+ # send the new carrier to one side of the mixer (multiplier)
+ self.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
+ # the result is BASEBANDED DSBSC with phase zero!
+ self.connect(self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1))
+
+ # Pick off the real part since the imaginary is
+ # theoretically zero and then to one side of a summer
+ self.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
+ self.connect(self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and
+ # multiplying by pilot tone
+ self.connect(self.stereo_basebander,(self.rds_carrier_generator,0))
+ self.connect(self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+
+ # take signal, filter off rds, send into mixer 0 channel
+ self.connect(self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+
+ # take rds_carrier_generator output and send into mixer 1
+ # channel
+ self.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
+ self.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
+ self.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
+ self.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
+ self.connect(self.Make_Left , self.deemph_Left, (self, 0))
+ self.connect(self.Make_Right, self.deemph_Right, (self, 1))
+
+ # NOTE: mono support will require variable number of outputs in hier_block2s
+ # See ticket:174 in Trac database
+ #else:
+ # self.connect (self.fm_demod, self.audio_filter, self)
diff --git a/gr-analog/python/wfm_rcv_pll.py b/gr-analog/python/wfm_rcv_pll.py
new file mode 100644
index 0000000000..953cd538f1
--- /dev/null
+++ b/gr-analog/python/wfm_rcv_pll.py
@@ -0,0 +1,188 @@
+#
+# Copyright 2005,2006,2012 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 3, 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, filter, analog
+from gnuradio.blks2impl.fm_emph import fm_deemph
+import math
+
+class wfm_rcv_pll(gr.hier_block2):
+ def __init__(self, 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.
+
+ Args:
+ demod_rate: input sample rate of complex baseband input. (float)
+ audio_decimation: how much to decimate demod_rate to get to audio. (integer)
+ """
+ gr.hier_block2.__init__(self, "wfm_rcv_pll",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature(2, 2, gr.sizeof_float)) # Output signature
+ bandwidth = 250e3
+ 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
+ loop_bw = 2*math.pi/100.0
+ max_freq = 2.0*math.pi*90e3/demod_rate
+ self.fm_demod = analog.pll_freqdet_cf(loop_bw, max_freq,-max_freq)
+
+ # input: float; output: float
+ self.deemph_Left = fm_deemph(audio_rate)
+ self.deemph_Right = fm_deemph(audio_rate)
+
+ # compute FIR filter taps for audio filter
+ width_of_transition_band = audio_rate / 32
+ audio_coeffs = filter.firdes.low_pass(1.0 , # gain
+ demod_rate, # sampling rate
+ 15000 ,
+ width_of_transition_band,
+ filter.firdes.WIN_HAMMING)
+ # input: float; output: float
+ self.audio_filter = filter.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 = \
+ filter.firdes.complex_band_pass(10.0,
+ demod_rate,
+ -19020,
+ -18980,
+ width_of_transition_band,
+ filter.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 = \
+ filter.firdes.complex_band_pass(20.0,
+ demod_rate,
+ 38000-15000/2,
+ 38000+15000/2,
+ width_of_transition_band,
+ filter.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 = \
+ filter.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 = \
+ filter.firdes.complex_band_pass(30.0,
+ demod_rate,
+ 57000 - 1500,
+ 57000 + 1500,
+ width_of_transition_band,
+ filter.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.rds_signal_filter = \
+ filter.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);
+
+ loop_bw = 2*math.pi/100.0
+ max_freq = -2.0*math.pi*18990/audio_rate;
+ min_freq = -2.0*math.pi*19010/audio_rate;
+
+ self.stereo_carrier_pll_recovery = \
+ analog.pll_refout_cc(loop_bw, max_freq, min_freq);
+ #self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now
+
+ # 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 = \
+ filter.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
+ self.connect(self, 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
+ self.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)
+ self.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
+ self.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
+ self.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
+ self.connect(self.LmR_real,(self.Make_Right,1))
+
+ # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
+ self.connect(self.stereo_basebander,(self.rds_carrier_generator,0))
+ self.connect(self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1))
+ # take signal, filter off rds, send into mixer 0 channel
+ self.connect(self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
+ # take rds_carrier_generator output and send into mixer 1 channel
+ self.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
+ self.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
+ self.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
+ self.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
+ self.connect(self.Make_Left , self.deemph_Left, (self, 0))
+ self.connect(self.Make_Right, self.deemph_Right, (self, 1))
+ # NOTE: mono support will require variable number of outputs in hier_block2s
+ # See ticket:174 in Trac database
+ #else:
+ # self.connect (self.fm_demod, self.audio_filter, self)
diff --git a/gr-analog/python/wfm_tx.py b/gr-analog/python/wfm_tx.py
new file mode 100644
index 0000000000..4c074b5317
--- /dev/null
+++ b/gr-analog/python/wfm_tx.py
@@ -0,0 +1,78 @@
+#
+# Copyright 2005,2007,2012 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 3, 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 math
+from gnuradio import gr
+from gnuradio import filter
+from gnuradio import analog
+from fm_emph import fm_preemph
+
+class wfm_tx(gr.hier_block2):
+ def __init__(self, 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.
+
+ Args:
+ audio_rate: sample rate of audio stream, >= 16k (integer)
+ quad_rate: sample rate of output stream (integer)
+ tau: preemphasis time constant (default 75e-6) (float)
+ max_dev: maximum deviation in Hz (default 75e3) (float)
+
+ quad_rate must be an integer multiple of audio_rate.
+ """
+ gr.hier_block2.__init__(self, "wfm_tx",
+ gr.io_signature(1, 1, gr.sizeof_float), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
+
+ # 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 = filter.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 = filter.interp_fir_filter_fff (interp_factor, interp_taps)
+
+ self.preemph = fm_preemph(quad_rate, tau=tau)
+
+ k = 2 * math.pi * max_dev / quad_rate
+ self.modulator = analog.frequency_modulator_fc (k)
+
+ if do_interp:
+ self.connect(self, self.interpolator, self.preemph, self.modulator, self)
+ else:
+ self.connect(self, self.preemph, self.modulator, self)