diff options
Diffstat (limited to 'gr-analog')
32 files changed, 1657 insertions, 207 deletions
diff --git a/gr-analog/CMakeLists.txt b/gr-analog/CMakeLists.txt index 4769ce7ac1..8186de0fa9 100644 --- a/gr-analog/CMakeLists.txt +++ b/gr-analog/CMakeLists.txt @@ -95,6 +95,7 @@ if(ENABLE_PYTHON) add_subdirectory(python) add_subdirectory(grc) add_subdirectory(examples) + add_subdirectory(examples/tags) endif(ENABLE_PYTHON) ######################################################################## diff --git a/gr-analog/examples/CMakeLists.txt b/gr-analog/examples/CMakeLists.txt index 9476009869..f0f55b50c8 100644 --- a/gr-analog/examples/CMakeLists.txt +++ b/gr-analog/examples/CMakeLists.txt @@ -21,6 +21,7 @@ include(GrPython) # Base stuff GR_PYTHON_INSTALL(PROGRAMS + fmtest.py DESTINATION ${GR_PKG_ANALOG_EXAMPLES_DIR} COMPONENT "analog_python" ) diff --git a/gr-analog/examples/fmtest.py b/gr-analog/examples/fmtest.py new file mode 100755 index 0000000000..ca02ee5729 --- /dev/null +++ b/gr-analog/examples/fmtest.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# +# Copyright 2009,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 +from gnuradio import analog +import sys, math, time + +try: + import scipy + from scipy import fftpack +except ImportError: + print "Error: Program requires scipy (see: www.scipy.org)." + sys.exit(1) + +try: + import pylab +except ImportError: + print "Error: Program requires matplotlib (see: matplotlib.sourceforge.net)." + sys.exit(1) + + +class fmtx(gr.hier_block2): + def __init__(self, lo_freq, audio_rate, if_rate): + + gr.hier_block2.__init__(self, "build_fm", + gr.io_signature(1, 1, gr.sizeof_float), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + fmtx = analog.nbfm_tx(audio_rate, if_rate, max_dev=5e3, tau=75e-6) + + # Local oscillator + lo = analog.sig_source_c(if_rate, # sample rate + analog.GR_SIN_WAVE, # waveform type + lo_freq, # frequency + 1.0, # amplitude + 0) # DC Offset + mixer = gr.multiply_cc() + + self.connect(self, fmtx, (mixer, 0)) + self.connect(lo, (mixer, 1)) + self.connect(mixer, self) + +class fmtest(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self._nsamples = 1000000 + self._audio_rate = 8000 + + # Set up N channels with their own baseband and IF frequencies + self._N = 5 + chspacing = 16000 + freq = [10, 20, 30, 40, 50] + f_lo = [0, 1*chspacing, -1*chspacing, 2*chspacing, -2*chspacing] + + self._if_rate = 4*self._N*self._audio_rate + + # Create a signal source and frequency modulate it + self.sum = gr.add_cc() + for n in xrange(self._N): + sig = analog.sig_source_f(self._audio_rate, analog.GR_SIN_WAVE, freq[n], 0.5) + fm = fmtx(f_lo[n], self._audio_rate, self._if_rate) + self.connect(sig, fm) + self.connect(fm, (self.sum, n)) + + self.head = gr.head(gr.sizeof_gr_complex, self._nsamples) + self.snk_tx = gr.vector_sink_c() + self.channel = filter.channel_model(0.1) + + self.connect(self.sum, self.head, self.channel, self.snk_tx) + + + # Design the channlizer + self._M = 10 + bw = chspacing/2.0 + t_bw = chspacing/10.0 + self._chan_rate = self._if_rate / self._M + self._taps = filter.firdes.low_pass_2(1, self._if_rate, bw, t_bw, + attenuation_dB=100, + window=filter.firdes.WIN_BLACKMAN_hARRIS) + tpc = math.ceil(float(len(self._taps)) / float(self._M)) + + print "Number of taps: ", len(self._taps) + print "Number of channels: ", self._M + print "Taps per channel: ", tpc + + self.pfb = filter.pfb.channelizer_ccf(self._M, self._taps) + + self.connect(self.channel, self.pfb) + + # Create a file sink for each of M output channels of the filter and connect it + self.fmdet = list() + self.squelch = list() + self.snks = list() + for i in xrange(self._M): + self.fmdet.append(analog.nbfm_rx(self._audio_rate, self._chan_rate)) + self.squelch.append(analog.standard_squelch(self._audio_rate*10)) + self.snks.append(gr.vector_sink_f()) + self.connect((self.pfb, i), self.fmdet[i], self.squelch[i], self.snks[i]) + + def num_tx_channels(self): + return self._N + + def num_rx_channels(self): + return self._M + +def main(): + + fm = fmtest() + + tstart = time.time() + fm.run() + tend = time.time() + + if 1: + fig1 = pylab.figure(1, figsize=(12,10), facecolor="w") + fig2 = pylab.figure(2, figsize=(12,10), facecolor="w") + fig3 = pylab.figure(3, figsize=(12,10), facecolor="w") + + Ns = 10000 + Ne = 100000 + + fftlen = 8192 + winfunc = scipy.blackman + + # Plot transmitted signal + fs = fm._if_rate + + d = fm.snk_tx.data()[Ns:Ns+Ne] + sp1_f = fig1.add_subplot(2, 1, 1) + + X,freq = sp1_f.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs, + window = lambda d: d*winfunc(fftlen), + visible=False) + X_in = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + f_in = scipy.arange(-fs/2.0, fs/2.0, fs/float(X_in.size)) + p1_f = sp1_f.plot(f_in, X_in, "b") + sp1_f.set_xlim([min(f_in), max(f_in)+1]) + sp1_f.set_ylim([-120.0, 20.0]) + + sp1_f.set_title("Input Signal", weight="bold") + sp1_f.set_xlabel("Frequency (Hz)") + sp1_f.set_ylabel("Power (dBW)") + + Ts = 1.0/fs + Tmax = len(d)*Ts + + t_in = scipy.arange(0, Tmax, Ts) + x_in = scipy.array(d) + sp1_t = fig1.add_subplot(2, 1, 2) + p1_t = sp1_t.plot(t_in, x_in.real, "b-o") + #p1_t = sp1_t.plot(t_in, x_in.imag, "r-o") + sp1_t.set_ylim([-5, 5]) + + # Set up the number of rows and columns for plotting the subfigures + Ncols = int(scipy.floor(scipy.sqrt(fm.num_rx_channels()))) + Nrows = int(scipy.floor(fm.num_rx_channels() / Ncols)) + if(fm.num_rx_channels() % Ncols != 0): + Nrows += 1 + + # Plot each of the channels outputs. Frequencies on Figure 2 and + # time signals on Figure 3 + fs_o = fm._audio_rate + for i in xrange(len(fm.snks)): + # remove issues with the transients at the beginning + # also remove some corruption at the end of the stream + # this is a bug, probably due to the corner cases + d = fm.snks[i].data()[Ns:Ne] + + sp2_f = fig2.add_subplot(Nrows, Ncols, 1+i) + X,freq = sp2_f.psd(d, NFFT=fftlen, noverlap=fftlen/4, Fs=fs_o, + window = lambda d: d*winfunc(fftlen), + visible=False) + #X_o = 10.0*scipy.log10(abs(fftpack.fftshift(X))) + X_o = 10.0*scipy.log10(abs(X)) + #f_o = scipy.arange(-fs_o/2.0, fs_o/2.0, fs_o/float(X_o.size)) + f_o = scipy.arange(0, fs_o/2.0, fs_o/2.0/float(X_o.size)) + p2_f = sp2_f.plot(f_o, X_o, "b") + sp2_f.set_xlim([min(f_o), max(f_o)+0.1]) + sp2_f.set_ylim([-120.0, 20.0]) + sp2_f.grid(True) + + sp2_f.set_title(("Channel %d" % i), weight="bold") + sp2_f.set_xlabel("Frequency (kHz)") + sp2_f.set_ylabel("Power (dBW)") + + + Ts = 1.0/fs_o + Tmax = len(d)*Ts + t_o = scipy.arange(0, Tmax, Ts) + + x_t = scipy.array(d) + sp2_t = fig3.add_subplot(Nrows, Ncols, 1+i) + p2_t = sp2_t.plot(t_o, x_t.real, "b") + p2_t = sp2_t.plot(t_o, x_t.imag, "r") + sp2_t.set_xlim([min(t_o), max(t_o)+1]) + sp2_t.set_ylim([-1, 1]) + + sp2_t.set_xlabel("Time (s)") + sp2_t.set_ylabel("Amplitude") + + + pylab.show() + + +if __name__ == "__main__": + main() diff --git a/gr-analog/examples/tags/CMakeLists.txt b/gr-analog/examples/tags/CMakeLists.txt new file mode 100644 index 0000000000..adeb655dea --- /dev/null +++ b/gr-analog/examples/tags/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 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. + +include(GrPython) + +GR_PYTHON_INSTALL(PROGRAMS + uhd_burst_detector.py + DESTINATION ${GR_PKG_DATA_DIR}/examples/tags + COMPONENT "core_python" +) + diff --git a/gr-analog/examples/tags/uhd_burst_detector.py b/gr-analog/examples/tags/uhd_burst_detector.py new file mode 100755 index 0000000000..3be0fb8c2e --- /dev/null +++ b/gr-analog/examples/tags/uhd_burst_detector.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# Copyright 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 eng_notation +from gnuradio import gr +from gnuradio import filter, analog +from gnuradio import uhd +from gnuradio import window +from gnuradio.eng_option import eng_option +from gnuradio.gr import firdes +from optparse import OptionParser + +class uhd_burst_detector(gr.top_block): + def __init__(self, uhd_address, options): + + gr.top_block.__init__(self) + + self.uhd_addr = uhd_address + self.freq = options.freq + self.samp_rate = options.samp_rate + self.gain = options.gain + self.threshold = options.threshold + self.trigger = options.trigger + + self.uhd_src = uhd.single_usrp_source( + device_addr=self.uhd_addr, + stream_args=uhd.stream_args('fc32')) + + self.uhd_src.set_samp_rate(self.samp_rate) + self.uhd_src.set_center_freq(self.freq, 0) + self.uhd_src.set_gain(self.gain, 0) + + taps = firdes.low_pass_2(1, 1, 0.4, 0.1, 60) + self.chanfilt = filter.fir_filter_ccc(10, taps) + self.tagger = gr.burst_tagger(gr.sizeof_gr_complex) + + # Dummy signaler to collect a burst on known periods + data = 1000*[0,] + 1000*[1,] + self.signal = gr.vector_source_s(data, True) + + # Energy detector to get signal burst + ## use squelch to detect energy + self.det = analog.simple_squelch_cc(self.threshold, 0.01) + ## convert to mag squared (float) + self.c2m = gr.complex_to_mag_squared() + ## average to debounce + self.avg = filter.single_pole_iir_filter_ff(0.01) + ## rescale signal for conversion to short + self.scale = gr.multiply_const_ff(2**16) + ## signal input uses shorts + self.f2s = gr.float_to_short() + + # Use file sink burst tagger to capture bursts + self.fsnk = gr.tagged_file_sink(gr.sizeof_gr_complex, self.samp_rate) + + + ################################################## + # Connections + ################################################## + self.connect((self.uhd_src, 0), (self.tagger, 0)) + self.connect((self.tagger, 0), (self.fsnk, 0)) + + if self.trigger: + # Connect a dummy signaler to the burst tagger + self.connect((self.signal, 0), (self.tagger, 1)) + + else: + # Connect an energy detector signaler to the burst tagger + self.connect(self.uhd_src, self.det) + self.connect(self.det, self.c2m, self.avg, self.scale, self.f2s) + self.connect(self.f2s, (self.tagger, 1)) + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.uhd_src_0.set_samp_rate(self.samp_rate) + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + parser.add_option("-a", "--address", type="string", default="addr=192.168.10.2", + help="select address of the device [default=%default]") + #parser.add_option("-A", "--antenna", default=None, + # help="select Rx Antenna (only on RFX-series boards)") + parser.add_option("-f", "--freq", type="eng_float", default=450e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=0, + help="set gain in dB [default=%default]") + parser.add_option("-R", "--samp-rate", type="eng_float", default=200000, + help="set USRP sample rate [default=%default]") + parser.add_option("-t", "--threshold", type="float", default=-60, + help="Set the detection power threshold (dBm) [default=%default") + parser.add_option("-T", "--trigger", action="store_true", default=False, + help="Use internal trigger instead of detector [default=%default]") + (options, args) = parser.parse_args() + + uhd_addr = options.address + + tb = uhd_burst_detector(uhd_addr, options) + tb.run() diff --git a/gr-analog/grc/analog_block_tree.xml b/gr-analog/grc/analog_block_tree.xml index b5b2ecd568..5d2ab6512b 100644 --- a/gr-analog/grc/analog_block_tree.xml +++ b/gr-analog/grc/analog_block_tree.xml @@ -37,6 +37,7 @@ <block>analog_ctcss_squelch_ff</block> <block>analog_pwr_squelch_xx</block> <block>analog_simple_squelch_cc</block> + <block>analog_standard_squelch</block> <block>analog_rail_ff</block> </cat> <cat> @@ -49,6 +50,7 @@ <cat> <name>Sources</name> <block>analog_sig_source_x</block> + <block>analog_const_source_x</block> <block>analog_noise_source_x</block> </cat> <cat> diff --git a/gr-analog/grc/analog_const_source_x.xml b/gr-analog/grc/analog_const_source_x.xml new file mode 100644 index 0000000000..de8c306264 --- /dev/null +++ b/gr-analog/grc/analog_const_source_x.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Constant Source: Custom wrapper +################################################### + --> +<block> + <name>Constant Source</name> + <key>analog_const_source_x</key> + <import>from gnuradio import analog</import> + <make>analog.sig_source_$(type.fcn)(0, analog.GR_CONST_WAVE, 0, 0, $const)</make> + <callback>set_offset($const)</callback> + <param> + <name>Output Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:c</opt> + <opt>const_type:complex</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:f</opt> + <opt>const_type:real</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>fcn:i</opt> + <opt>const_type:int</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>fcn:s</opt> + <opt>const_type:int</opt> + </option> + </param> + <param> + <name>Constant</name> + <key>const</key> + <value>0</value> + <type>$type.const_type</type> + </param> + <source> + <name>out</name> + <type>$type</type> + </source> +</block> diff --git a/gr-analog/grc/analog_standard_squelch.xml b/gr-analog/grc/analog_standard_squelch.xml new file mode 100644 index 0000000000..264c57ab56 --- /dev/null +++ b/gr-analog/grc/analog_standard_squelch.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Standard Squelch +################################################### + --> +<block> + <name>Standard Squelch</name> + <key>analog_standard_squelch</key> + <import>from gnuradio import analog</import> + <make>analog.standard_squelch(audio_rate=$audio_rate) +self.$(id).set_threshold($threshold)</make> + <callback>set_threshold($threshold)</callback> + <param> + <name>Audio Rate</name> + <key>audio_rate</key> + <type>real</type> + </param> + <param> + <name>Threshold</name> + <key>threshold</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>float</type> + </sink> + <source> + <name>out</name> + <type>float</type> + </source> +</block> diff --git a/gr-analog/include/analog/CMakeLists.txt b/gr-analog/include/analog/CMakeLists.txt index 4de94a88b3..33c4d55a79 100644 --- a/gr-analog/include/analog/CMakeLists.txt +++ b/gr-analog/include/analog/CMakeLists.txt @@ -102,7 +102,6 @@ install(FILES pwr_squelch_ff.h quadrature_demod_cf.h rail_ff.h - rotator.h sig_source_waveform.h simple_squelch_cc.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/analog diff --git a/gr-analog/include/analog/rotator.h b/gr-analog/include/analog/rotator.h deleted file mode 100644 index ee3849eae7..0000000000 --- a/gr-analog/include/analog/rotator.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2008,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. - */ - -#ifndef _ANALOG_ROTATOR_H_ -#define _ANALOG_ROTATOR_H_ - -#include <analog/api.h> -#include <gr_complex.h> - -namespace gr { - namespace analog { - - class rotator - { - private: - gr_complex d_phase; - gr_complex d_phase_incr; - unsigned int d_counter; - - public: - rotator() : d_phase (1), d_phase_incr (1), d_counter(0) - { } - - void set_phase(gr_complex phase) { d_phase = phase / abs(phase); } - void set_phase_incr(gr_complex incr) { d_phase_incr = incr / abs(incr); } - - gr_complex rotate (gr_complex in) - { - d_counter++; - - gr_complex z = in * d_phase; // rotate in by phase - d_phase *= d_phase_incr; // incr our phase (complex mult == add phases) - - if((d_counter % 512) == 0) - d_phase /= abs(d_phase); // Normalize to ensure multiplication is rotation - - return z; - } - }; - - } /* namespace analog */ -} /* namespace gr */ - -#endif /* _ANALOG_ROTATOR_H_ */ diff --git a/gr-analog/lib/CMakeLists.txt b/gr-analog/lib/CMakeLists.txt index aaf3c8656c..04a8645417 100644 --- a/gr-analog/lib/CMakeLists.txt +++ b/gr-analog/lib/CMakeLists.txt @@ -160,7 +160,6 @@ if(ENABLE_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/test_gr_analog.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_analog.cc ${CMAKE_CURRENT_SOURCE_DIR}/qa_sincos.cc - ${CMAKE_CURRENT_SOURCE_DIR}/qa_rotator.cc ) add_executable(test-gr-analog ${test_gr_analog_sources}) diff --git a/gr-analog/lib/qa_analog.cc b/gr-analog/lib/qa_analog.cc index c3d51863bd..f1e3a45e27 100644 --- a/gr-analog/lib/qa_analog.cc +++ b/gr-analog/lib/qa_analog.cc @@ -35,7 +35,6 @@ qa_gr_analog::suite() CppUnit::TestSuite *s = new CppUnit::TestSuite("gr-analog"); s->addTest(gr::analog::qa_sincos::suite()); - s->addTest(gr::analog::qa_rotator::suite()); return s; } diff --git a/gr-analog/lib/qa_rotator.cc b/gr-analog/lib/qa_rotator.cc deleted file mode 100644 index b722f32c43..0000000000 --- a/gr-analog/lib/qa_rotator.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2002,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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <gruel/attributes.h> -#include <cppunit/TestAssert.h> -#include <qa_rotator.h> -#include <analog/rotator.h> -#include <stdio.h> -#include <cmath> -#include <gr_expj.h> - -namespace gr { - namespace analog { - - // error vector magnitude - __GR_ATTR_UNUSED static float - error_vector_mag(gr_complex a, gr_complex b) - { - return abs(a-b); - } - - void - qa_rotator::t1() - { - static const unsigned int N = 100000; - - rotator r; - - double phase_incr = 2*M_PI / 1003; - double phase = 0; - - // Old code: We increment then return the rotated value, thus we - // need to start one tick back - // r.set_phase(gr_complex(1,0) * conj(gr_expj(phase_incr))); - - r.set_phase(gr_complex(1,0)); - r.set_phase_incr(gr_expj(phase_incr)); - - for(unsigned i = 0; i < N; i++) { - gr_complex expected = gr_expj(phase); - gr_complex actual = r.rotate(gr_complex(1, 0)); - -#if 0 - float evm = error_vector_mag(expected, actual); - printf("[%6d] expected: (%8.6f, %8.6f) actual: (%8.6f, %8.6f) evm: %8.6f\n", - i, expected.real(), expected.imag(), actual.real(), actual.imag(), evm); -#endif - - CPPUNIT_ASSERT_COMPLEXES_EQUAL(expected, actual, 0.0001); - - phase += phase_incr; - if(phase >= 2*M_PI) - phase -= 2*M_PI; - } - } - - } /* namespace analog */ -} /* namespace gr */ diff --git a/gr-analog/lib/qa_rotator.h b/gr-analog/lib/qa_rotator.h deleted file mode 100644 index a22e41ec26..0000000000 --- a/gr-analog/lib/qa_rotator.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,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. - */ - -#ifndef _QA_ANALOG_ROTATOR_H_ -#define _QA_ANALOG_ROTATOR_H_ - -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/TestCase.h> - -namespace gr { - namespace analog { - - class qa_rotator : public CppUnit::TestCase - { - CPPUNIT_TEST_SUITE(qa_rotator); - CPPUNIT_TEST(t1); - CPPUNIT_TEST_SUITE_END(); - - private: - void t1(); - }; - - } /* namespace analog */ -} /* namespace gr */ - -#endif /* _QA_ANALOG_ROTATOR_H_ */ diff --git a/gr-analog/lib/sig_source_X_impl.cc.t b/gr-analog/lib/sig_source_X_impl.cc.t index 60653dc1bb..ad8b7e4b0c 100644 --- a/gr-analog/lib/sig_source_X_impl.cc.t +++ b/gr-analog/lib/sig_source_X_impl.cc.t @@ -211,7 +211,7 @@ namespace gr { #endif default: - throw std::runtime_error("gr_sig_source: invalid waveform"); + throw std::runtime_error("analog::sig_source: invalid waveform"); } return noutput_items; 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..1976a076ca --- /dev/null +++ b/gr-analog/python/fm_demod.py @@ -0,0 +1,108 @@ +# +# 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, filter +from fm_emph import fm_deemph +from math import pi + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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..b2c86db70f --- /dev/null +++ b/gr-analog/python/nbfm_rx.py @@ -0,0 +1,90 @@ +# +# 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 fm_emph import fm_deemph + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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 = analog.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 + filter.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..eed5bc08d0 --- /dev/null +++ b/gr-analog/python/nbfm_tx.py @@ -0,0 +1,92 @@ +# +# 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 fm_emph import fm_preemph + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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/qa_agc.py b/gr-analog/python/qa_agc.py index dc4922cf84..2733ed3fae 100755 --- a/gr-analog/python/qa_agc.py +++ b/gr-analog/python/qa_agc.py @@ -105,8 +105,8 @@ class test_agc (gr_unittest.TestCase): (1.26766037940979-0.92100900411605835j)) sampling_freq = 100 - src1 = gr.sig_source_c(sampling_freq, gr.GR_SIN_WAVE, - sampling_freq * 0.10, 100.0) + src1 = analog.sig_source_c(sampling_freq, analog.GR_SIN_WAVE, + sampling_freq * 0.10, 100.0) dst1 = gr.vector_sink_c() head = gr.head(gr.sizeof_gr_complex, int (5*sampling_freq * 0.10)) @@ -193,8 +193,8 @@ class test_agc (gr_unittest.TestCase): -3.3931560516357422) sampling_freq = 100 - src1 = gr.sig_source_f (sampling_freq, gr.GR_SIN_WAVE, - sampling_freq * 0.10, 100.0) + src1 = analog.sig_source_f(sampling_freq, analog.GR_SIN_WAVE, + sampling_freq * 0.10, 100.0) dst1 = gr.vector_sink_f () head = gr.head (gr.sizeof_float, int (5*sampling_freq * 0.10)) @@ -283,8 +283,8 @@ class test_agc (gr_unittest.TestCase): (0.80901449918746948-0.5877833366394043j)) sampling_freq = 100 - src1 = gr.sig_source_c(sampling_freq, gr.GR_SIN_WAVE, - sampling_freq * 0.10, 100) + src1 = analog.sig_source_c(sampling_freq, analog.GR_SIN_WAVE, + sampling_freq * 0.10, 100) dst1 = gr.vector_sink_c() head = gr.head(gr.sizeof_gr_complex, int(5*sampling_freq * 0.10)) @@ -373,8 +373,8 @@ class test_agc (gr_unittest.TestCase): -0.61937344074249268) sampling_freq = 100 - src1 = gr.sig_source_f(sampling_freq, gr.GR_SIN_WAVE, - sampling_freq * 0.10, 100) + src1 = analog.sig_source_f(sampling_freq, analog.GR_SIN_WAVE, + sampling_freq * 0.10, 100) dst1 = gr.vector_sink_f() head = gr.head(gr.sizeof_float, int(5*sampling_freq * 0.10)) @@ -449,8 +449,8 @@ class test_agc (gr_unittest.TestCase): (0.80901449918746948-0.5877833366394043j)) sampling_freq = 100 - src1 = gr.sig_source_c(sampling_freq, gr.GR_SIN_WAVE, - sampling_freq * 0.10, 100) + src1 = analog.sig_source_c(sampling_freq, analog.GR_SIN_WAVE, + sampling_freq * 0.10, 100) dst1 = gr.vector_sink_c() head = gr.head(gr.sizeof_gr_complex, int(5*sampling_freq * 0.10)) diff --git a/gr-analog/python/qa_fmdet.py b/gr-analog/python/qa_fmdet.py index b90ef2ffa4..a76ac1d247 100755 --- a/gr-analog/python/qa_fmdet.py +++ b/gr-analog/python/qa_fmdet.py @@ -62,7 +62,7 @@ class test_fmdet_cf(gr_unittest.TestCase): # block is saying, not what the values should actually be. def est_fmdet_cf_002(self): N = 100 - src = gr.sig_source_c(1, gr.GR_SIN_WAVE, 0.2, 1) + src = analog.sig_source_c(1, analog.GR_SIN_WAVE, 0.2, 1) head = gr.head(gr.sizeof_gr_complex, N) op = analog.fmdet_cf(1, 0.1, 0.3, 0.1) dst = gr.vector_sink_f() diff --git a/gr-analog/python/qa_pll_carriertracking.py b/gr-analog/python/qa_pll_carriertracking.py index a292059d1b..e383639d49 100755 --- a/gr-analog/python/qa_pll_carriertracking.py +++ b/gr-analog/python/qa_pll_carriertracking.py @@ -141,7 +141,7 @@ class test_pll_carriertracking(gr_unittest.TestCase): maxf = 1 minf = -1 - src = gr.sig_source_c(sampling_freq, gr.GR_COS_WAVE, freq, 1.0) + src = analog.sig_source_c(sampling_freq, analog.GR_COS_WAVE, freq, 1.0) pll = analog.pll_carriertracking_cc(loop_bw, maxf, minf) head = gr.head(gr.sizeof_gr_complex, int (freq)) dst = gr.vector_sink_c() diff --git a/gr-analog/python/qa_pll_freqdet.py b/gr-analog/python/qa_pll_freqdet.py index 1f808afa61..cc8757c965 100755 --- a/gr-analog/python/qa_pll_freqdet.py +++ b/gr-analog/python/qa_pll_freqdet.py @@ -141,7 +141,7 @@ class test_pll_freqdet(gr_unittest.TestCase): maxf = 1 minf = -1 - src = gr.sig_source_c(sampling_freq, gr.GR_COS_WAVE, freq, 1.0) + src = analog.sig_source_c(sampling_freq, analog.GR_COS_WAVE, freq, 1.0) pll = analog.pll_freqdet_cf(loop_bw, maxf, minf) head = gr.head(gr.sizeof_float, int (freq)) dst = gr.vector_sink_f() diff --git a/gr-analog/python/qa_pll_refout.py b/gr-analog/python/qa_pll_refout.py index 2831b2909d..c63136bc0c 100755 --- a/gr-analog/python/qa_pll_refout.py +++ b/gr-analog/python/qa_pll_refout.py @@ -141,7 +141,7 @@ class test_pll_refout(gr_unittest.TestCase): maxf = 1 minf = -1 - src = gr.sig_source_c(sampling_freq, gr.GR_COS_WAVE, freq, 1.0) + src = analog.sig_source_c(sampling_freq, analog.GR_COS_WAVE, freq, 1.0) pll = analog.pll_refout_cc(loop_bw, maxf, minf) head = gr.head(gr.sizeof_gr_complex, int (freq)) dst = gr.vector_sink_c() 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..d35d219275 --- /dev/null +++ b/gr-analog/python/wfm_rcv.py @@ -0,0 +1,73 @@ +# +# 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 +from fm_emph import fm_deemph +import math + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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..0fc9de1b88 --- /dev/null +++ b/gr-analog/python/wfm_rcv_fmdet.py @@ -0,0 +1,226 @@ +# +# 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 +from fm_emph import fm_deemph +import math + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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..c26dfb4928 --- /dev/null +++ b/gr-analog/python/wfm_rcv_pll.py @@ -0,0 +1,193 @@ +# +# 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 +from fm_emph import fm_deemph +import math + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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..be662310db --- /dev/null +++ b/gr-analog/python/wfm_tx.py @@ -0,0 +1,82 @@ +# +# 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 fm_emph import fm_preemph + +try: + from gnuradio import analog +except ImportError: + import analog_swig as analog + +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) |