summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rondeau2012-05-05 13:34:56 -0400
committerTom Rondeau2012-05-05 13:34:56 -0400
commit906fe742d1c6a6ca93b47261b0ee615d4779081b (patch)
tree5447f93ae5025bd27aa42b61f18d660a5aabdb2a
parent1e18d093dad92f798796f75136943adf088cfe60 (diff)
downloadgnuradio-906fe742d1c6a6ca93b47261b0ee615d4779081b.tar.gz
gnuradio-906fe742d1c6a6ca93b47261b0ee615d4779081b.tar.bz2
gnuradio-906fe742d1c6a6ca93b47261b0ee615d4779081b.zip
filter: added firdes for generating FIR filters to gr-filter.
Addes QA code in both Python and C++.
-rw-r--r--gr-filter/include/filter/CMakeLists.txt1
-rw-r--r--gr-filter/include/filter/firdes.h378
-rw-r--r--gr-filter/lib/CMakeLists.txt22
-rw-r--r--gr-filter/lib/firdes.cc855
-rw-r--r--gr-filter/lib/qa_filter.cc39
-rw-r--r--gr-filter/lib/qa_filter.h38
-rw-r--r--gr-filter/lib/qa_firdes.cc621
-rw-r--r--gr-filter/lib/qa_firdes.h56
-rw-r--r--gr-filter/lib/test_gr_filter.cc42
-rwxr-xr-xgr-filter/python/qa_fir_filter.py2
-rwxr-xr-xgr-filter/python/qa_firdes.py202
-rw-r--r--gr-filter/swig/filter_swig.i2
12 files changed, 2257 insertions, 1 deletions
diff --git a/gr-filter/include/filter/CMakeLists.txt b/gr-filter/include/filter/CMakeLists.txt
index f96128805..108509e9a 100644
--- a/gr-filter/include/filter/CMakeLists.txt
+++ b/gr-filter/include/filter/CMakeLists.txt
@@ -75,6 +75,7 @@ add_custom_target(filter_generated_includes DEPENDS
########################################################################
install(FILES
api.h
+ firdes.h
fir_filter.h
fft_filter.h
${generated_includes}
diff --git a/gr-filter/include/filter/firdes.h b/gr-filter/include/filter/firdes.h
new file mode 100644
index 000000000..172563ea0
--- /dev/null
+++ b/gr-filter/include/filter/firdes.h
@@ -0,0 +1,378 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2002,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 _FILTER_FIRDES_H_
+#define _FILTER_FIRDES_H_
+
+#include <filter/api.h>
+#include <vector>
+#include <cmath>
+#include <gr_complex.h>
+
+namespace gr {
+ namespace filter {
+
+ /*!
+ * \brief Finite Impulse Response (FIR) filter design functions.
+ * \ingroup filter_design
+ */
+
+ class FILTER_API firdes {
+ public:
+
+ enum win_type {
+ WIN_HAMMING = 0, // max attenuation 53 dB
+ WIN_HANN = 1, // max attenuation 44 dB
+ WIN_BLACKMAN = 2, // max attenuation 74 dB
+ WIN_RECTANGULAR = 3,
+ WIN_KAISER = 4, // max attenuation a function of beta, google it
+ WIN_BLACKMAN_hARRIS = 5,
+ WIN_BLACKMAN_HARRIS = 5, // alias for capitalization consistency
+ };
+
+
+ // ... class methods ...
+
+ /*!
+ * \brief use "window method" to design a low-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * The normalized width of the transition
+ * band is what sets the number of taps
+ * required. Narrow --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+ static std::vector<float>
+ low_pass(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a low-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * \p attenuation_dB required stopband attenuation
+ * The normalized width of the transition
+ * band and the required stop band
+ * attenuation is what sets the number of taps
+ * required. Narrow --> more taps
+ * More attenuatin --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ low_pass_2(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz beginning transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // out of band attenuation dB
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a high-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * The normalized width of the transition
+ * band is what sets the number of taps
+ * required. Narrow --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ high_pass(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a high-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * \p attenuation_dB out of band attenuation
+ * The normalized width of the transition
+ * band and the required stop band
+ * attenuation is what sets the number of taps
+ * required. Narrow --> more taps
+ * More attenuation --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ high_pass_2(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // out of band attenuation dB
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a band-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * The normalized width of the transition
+ * band is what sets the number of taps
+ * required. Narrow --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+ static std::vector<float>
+ band_pass(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a band-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * \p attenuation_dB out of band attenuation
+ * The normalized width of the transition
+ * band and the required stop band
+ * attenuation is what sets the number of taps
+ * required. Narrow --> more taps
+ * More attenuation --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ band_pass_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz beginning transition band
+ double high_cutoff_freq, // Hz beginning transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // out of band attenuation dB
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a complex band-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * The normalized width of the transition
+ * band is what sets the number of taps
+ * required. Narrow --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+ static std::vector<gr_complex>
+ complex_band_pass(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a complex band-pass FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * \p attenuation_dB out of band attenuation
+ * The normalized width of the transition
+ * band and the required stop band
+ * attenuation is what sets the number of taps
+ * required. Narrow --> more taps
+ * More attenuation --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<gr_complex>
+ complex_band_pass_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz beginning transition band
+ double high_cutoff_freq, // Hz beginning transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // out of band attenuation dB
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a band-reject FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * The normalized width of the transition
+ * band is what sets the number of taps
+ * required. Narrow --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ band_reject(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!
+ * \brief use "window method" to design a band-reject FIR filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p low_cutoff_freq: center of transition band (Hz)
+ * \p high_cutoff_freq: center of transition band (Hz)
+ * \p transition_width: width of transition band (Hz).
+ * \p attenuation_dB out of band attenuation
+ * The normalized width of the transition
+ * band and the required stop band
+ * attenuation is what sets the number of taps
+ * required. Narrow --> more taps
+ * More attenuation --> more taps
+ * \p window_type: What kind of window to use. Determines
+ * maximum attenuation and passband ripple.
+ * \p beta: parameter for Kaiser window
+ */
+
+ static std::vector<float>
+ band_reject_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz beginning transition band
+ double high_cutoff_freq, // Hz beginning transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // out of band attenuation dB
+ win_type window = WIN_HAMMING,
+ double beta = 6.76); // used only with Kaiser
+
+ /*!\brief design a Hilbert Transform Filter
+ *
+ * \p ntaps: Number of taps, must be odd
+ * \p window_type: What kind of window to use
+ * \p beta: Only used for Kaiser
+ */
+ static std::vector<float>
+ hilbert(unsigned int ntaps = 19,
+ win_type windowtype = WIN_RECTANGULAR,
+ double beta = 6.76);
+
+ /*!
+ * \brief design a Root Cosine FIR Filter (do we need a window?)
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p sampling_freq: sampling freq (Hz)
+ * \p symbol rate: symbol rate, must be a factor of sample rate
+ * \p alpha: excess bandwidth factor
+ * \p ntaps: number of taps
+ */
+ static std::vector<float>
+ root_raised_cosine(double gain,
+ double sampling_freq,
+ double symbol_rate, // Symbol rate, NOT bitrate (unless BPSK)
+ double alpha, // Excess Bandwidth Factor
+ int ntaps);
+
+ /*!
+ * \brief design a Gaussian filter
+ *
+ * \p gain: overall gain of filter (typically 1.0)
+ * \p symbols per bit: symbol rate, must be a factor of sample rate
+ * \p ntaps: number of taps
+ */
+ static std::vector<float>
+ gaussian(double gain,
+ double spb,
+ double bt, // Bandwidth to bitrate ratio
+ int ntaps);
+
+ // window functions ...
+ static std::vector<float> window (win_type type, int ntaps, double beta);
+
+ private:
+ static double bessi0(double x);
+ static void sanity_check_1f(double sampling_freq, double f1,
+ double transition_width);
+ static void sanity_check_2f(double sampling_freq, double f1, double f2,
+ double transition_width);
+ static void sanity_check_2f_c(double sampling_freq, double f1, double f2,
+ double transition_width);
+
+ static int compute_ntaps(double sampling_freq,
+ double transition_width,
+ win_type window_type, double beta);
+
+ static int compute_ntaps_windes(double sampling_freq,
+ double transition_width,
+ double attenuation_dB);
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _FILTER_FIRDES_H_ */
diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt
index e0af76700..f085987de 100644
--- a/gr-filter/lib/CMakeLists.txt
+++ b/gr-filter/lib/CMakeLists.txt
@@ -107,6 +107,7 @@ link_directories(${FFTW3F_LIBRARY_DIRS})
list(APPEND filter_sources
fir_filter.cc
fft_filter.cc
+ firdes_impl.cc
${generated_sources}
fft_filter_ccc_impl.cc
fft_filter_fff_impl.cc
@@ -124,3 +125,24 @@ add_library(gnuradio-filter SHARED ${filter_sources})
target_link_libraries(gnuradio-filter ${filter_libs})
GR_LIBRARY_FOO(gnuradio-filter RUNTIME_COMPONENT "filter_runtime" DEVEL_COMPONENT "filter_devel")
add_dependencies(gnuradio-filter gnuradio-fft filter_generated_includes filter_generated_swigs)
+
+
+########################################################################
+# QA C++ Code for gr-filter
+########################################################################
+list(APPEND test_gr_filter_sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_gr_filter.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/qa_filter.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/qa_firdes.cc
+)
+
+add_executable(test-gr-filter ${test_gr_filter_sources})
+target_link_libraries(
+ test-gr-filter
+ gnuradio-core
+ gnuradio-filter
+ ${CPPUNIT_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
+
+GR_ADD_TEST(test_gr_filter)
diff --git a/gr-filter/lib/firdes.cc b/gr-filter/lib/firdes.cc
new file mode 100644
index 000000000..5c3320d71
--- /dev/null
+++ b/gr-filter/lib/firdes.cc
@@ -0,0 +1,855 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2002,2007,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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <filter/firdes.h>
+#include <stdexcept>
+
+using std::vector;
+
+namespace gr {
+ namespace filter {
+
+#define IzeroEPSILON 1E-21 /* Max error acceptable in Izero */
+
+ static double Izero(double x)
+ {
+ double sum, u, halfx, temp;
+ int n;
+
+ sum = u = n = 1;
+ halfx = x/2.0;
+ do {
+ temp = halfx/(double)n;
+ n += 1;
+ temp *= temp;
+ u *= temp;
+ sum += u;
+ } while (u >= IzeroEPSILON*sum);
+ return(sum);
+ }
+
+
+ //
+ // === Low Pass ===
+ //
+
+ vector<float>
+ firdes::low_pass_2(double gain,
+ double sampling_freq, // Hz
+ double cutoff_freq, // Hz BEGINNING of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // attenuation dB
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_1f(sampling_freq, cutoff_freq, transition_width);
+
+ int ntaps = compute_ntaps_windes(sampling_freq,
+ transition_width,
+ attenuation_dB);
+
+ // construct the truncated ideal impulse response
+ // [sin(x)/x for the low pass case]
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq;
+ for(int n = -M; n <= M; n++) {
+ if (n == 0)
+ taps[n + M] = fwT0 / M_PI * w[n + M];
+ else {
+ // a little algebra gets this into the more familiar sin(x)/x form
+ taps[n + M] = sin(n * fwT0) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For low-pass, gain @ zero freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M];
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+ vector<float>
+ firdes::low_pass(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_1f(sampling_freq, cutoff_freq, transition_width);
+
+ int ntaps = compute_ntaps(sampling_freq,
+ transition_width,
+ window_type, beta);
+
+ // construct the truncated ideal impulse response
+ // [sin(x)/x for the low pass case]
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if(n == 0)
+ taps[n + M] = fwT0 / M_PI * w[n + M];
+ else {
+ // a little algebra gets this into the more familiar sin(x)/x form
+ taps[n + M] = sin(n * fwT0) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For low-pass, gain @ zero freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M];
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+
+ //
+ // === High Pass ===
+ //
+
+ vector<float>
+ firdes::high_pass_2(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // attenuation dB
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_1f(sampling_freq, cutoff_freq, transition_width);
+
+ int ntaps = compute_ntaps_windes(sampling_freq,
+ transition_width,
+ attenuation_dB);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if(n == 0)
+ taps[n + M] = (1 - (fwT0 / M_PI)) * w[n + M];
+ else {
+ // a little algebra gets this into the more familiar sin(x)/x form
+ taps[n + M] = -sin(n * fwT0) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For high-pass, gain @ fs/2 freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M] * cos(n * M_PI);
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+
+ vector<float>
+ firdes::high_pass(double gain,
+ double sampling_freq,
+ double cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_1f(sampling_freq, cutoff_freq, transition_width);
+
+ int ntaps = compute_ntaps(sampling_freq,
+ transition_width,
+ window_type, beta);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if(n == 0)
+ taps[n + M] = (1 - (fwT0 / M_PI)) * w[n + M];
+ else {
+ // a little algebra gets this into the more familiar sin(x)/x form
+ taps[n + M] = -sin(n * fwT0) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For high-pass, gain @ fs/2 freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M] * cos(n * M_PI);
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+ //
+ // === Band Pass ===
+ //
+
+ vector<float>
+ firdes::band_pass_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // attenuation dB
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f(sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq, transition_width);
+
+ int ntaps = compute_ntaps_windes(sampling_freq,
+ transition_width,
+ attenuation_dB);
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq;
+ double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if (n == 0)
+ taps[n + M] = (fwT1 - fwT0) / M_PI * w[n + M];
+ else {
+ taps[n + M] = (sin(n * fwT1) - sin(n * fwT0)) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For band-pass, gain @ center freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M] * cos(n * (fwT0 + fwT1) * 0.5);
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+
+ vector<float>
+ firdes::band_pass(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f(sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq,
+ transition_width);
+
+ int ntaps = compute_ntaps(sampling_freq,
+ transition_width,
+ window_type, beta);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq;
+ double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if (n == 0)
+ taps[n + M] = (fwT1 - fwT0) / M_PI * w[n + M];
+ else {
+ taps[n + M] = (sin(n * fwT1) - sin(n * fwT0)) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For band-pass, gain @ center freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M] * cos(n * (fwT0 + fwT1) * 0.5);
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+ //
+ // === Complex Band Pass ===
+ //
+
+ vector<gr_complex>
+ firdes::complex_band_pass_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // attenuation dB
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f_c(sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq,
+ transition_width);
+
+ int ntaps = compute_ntaps_windes(sampling_freq,
+ transition_width,
+ attenuation_dB);
+
+ vector<gr_complex> taps(ntaps);
+ vector<float> lptaps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ lptaps = low_pass_2(gain, sampling_freq,
+ (high_cutoff_freq - low_cutoff_freq)/2,
+ transition_width, attenuation_dB,
+ window_type, beta);
+
+ gr_complex *optr = &taps[0];
+ float *iptr = &lptaps[0];
+ float freq = M_PI * (high_cutoff_freq + low_cutoff_freq)/sampling_freq;
+ float phase = 0;
+ if (lptaps.size() & 01) {
+ phase = - freq * ( lptaps.size() >> 1 );
+ }
+ else
+ phase = - freq/2.0 * ((1 + 2*lptaps.size()) >> 1);
+
+ for(unsigned int i = 0; i < lptaps.size(); i++) {
+ *optr++ = gr_complex(*iptr * cos(phase), *iptr * sin(phase));
+ iptr++, phase += freq;
+ }
+
+ return taps;
+ }
+
+
+ vector<gr_complex>
+ firdes::complex_band_pass(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f_c (sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq,
+ transition_width);
+
+ int ntaps = compute_ntaps(sampling_freq,
+ transition_width,
+ window_type, beta);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<gr_complex> taps(ntaps);
+ vector<float> lptaps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ lptaps = low_pass(gain, sampling_freq,
+ (high_cutoff_freq - low_cutoff_freq)/2,
+ transition_width, window_type, beta);
+
+ gr_complex *optr = &taps[0];
+ float *iptr = &lptaps[0];
+ float freq = M_PI * (high_cutoff_freq + low_cutoff_freq)/sampling_freq;
+ float phase = 0;
+ if(lptaps.size() & 01) {
+ phase = - freq * ( lptaps.size() >> 1 );
+ }
+ else
+ phase = - freq/2.0 * ((1 + 2*lptaps.size()) >> 1);
+
+ for(unsigned int i=0;i<lptaps.size();i++) {
+ *optr++ = gr_complex(*iptr * cos(phase), *iptr * sin(phase));
+ iptr++, phase += freq;
+ }
+
+ return taps;
+ }
+
+ //
+ // === Band Reject ===
+ //
+
+ vector<float>
+ firdes::band_reject_2(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ double attenuation_dB, // attenuation dB
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f(sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq,
+ transition_width);
+
+ int ntaps = compute_ntaps_windes(sampling_freq,
+ transition_width,
+ attenuation_dB);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq;
+ double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if (n == 0)
+ taps[n + M] = 1.0 + ((fwT0 - fwT1) / M_PI * w[n + M]);
+ else {
+ taps[n + M] = (sin(n * fwT0) - sin(n * fwT1)) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For band-reject, gain @ zero freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M];
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+ vector<float>
+ firdes::band_reject(double gain,
+ double sampling_freq,
+ double low_cutoff_freq, // Hz center of transition band
+ double high_cutoff_freq, // Hz center of transition band
+ double transition_width, // Hz width of transition band
+ win_type window_type,
+ double beta) // used only with Kaiser
+ {
+ sanity_check_2f(sampling_freq,
+ low_cutoff_freq,
+ high_cutoff_freq,
+ transition_width);
+
+ int ntaps = compute_ntaps(sampling_freq,
+ transition_width,
+ window_type, beta);
+
+ // construct the truncated ideal impulse response times the window function
+
+ vector<float> taps(ntaps);
+ vector<float> w = window(window_type, ntaps, beta);
+
+ int M = (ntaps - 1) / 2;
+ double fwT0 = 2 * M_PI * low_cutoff_freq / sampling_freq;
+ double fwT1 = 2 * M_PI * high_cutoff_freq / sampling_freq;
+
+ for(int n = -M; n <= M; n++) {
+ if (n == 0)
+ taps[n + M] = 1.0 + ((fwT0 - fwT1) / M_PI * w[n + M]);
+ else {
+ taps[n + M] = (sin(n * fwT0) - sin(n * fwT1)) / (n * M_PI) * w[n + M];
+ }
+ }
+
+ // find the factor to normalize the gain, fmax.
+ // For band-reject, gain @ zero freq = 1.0
+
+ double fmax = taps[0 + M];
+ for(int n = 1; n <= M; n++)
+ fmax += 2 * taps[n + M];
+
+ gain /= fmax; // normalize
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] *= gain;
+
+ return taps;
+ }
+
+ //
+ // Hilbert Transform
+ //
+
+ vector<float>
+ firdes::hilbert(unsigned int ntaps,
+ win_type windowtype,
+ double beta)
+ {
+ if(!(ntaps & 1))
+ throw std::out_of_range("Hilbert: Must have odd number of taps");
+
+ vector<float> taps(ntaps);
+ vector<float> w = window (windowtype, ntaps, beta);
+ unsigned int h = (ntaps-1)/2;
+ float gain=0;
+ for(unsigned int i = 1; i <= h; i++) {
+ if(i & 1) {
+ float x = 1/(float)i;
+ taps[h+i] = x * w[h+i];
+ taps[h-i] = -x * w[h-i];
+ gain = taps[h+i] - gain;
+ }
+ else
+ taps[h+i] = taps[h-i] = 0;
+ }
+
+ gain = 2 * fabs(gain);
+ for(unsigned int i = 0; i < ntaps; i++)
+ taps[i] /= gain;
+ return taps;
+ }
+
+ //
+ // Gaussian
+ //
+
+ vector<float>
+ firdes::gaussian(double gain,
+ double spb,
+ double bt,
+ int ntaps)
+ {
+ vector<float> taps(ntaps);
+ double scale = 0;
+ double dt = 1.0/spb;
+ double s = 1.0/(sqrt(log(2.0)) / (2*M_PI*bt));
+ double t0 = -0.5 * ntaps;
+ double ts;
+ for(int i=0;i<ntaps;i++) {
+ t0++;
+ ts = s*dt*t0;
+ taps[i] = exp(-0.5*ts*ts);
+ scale += taps[i];
+ }
+ for(int i=0;i<ntaps;i++)
+ taps[i] = taps[i] / scale * gain;
+
+ return taps;
+ }
+
+
+ //
+ // Root Raised Cosine
+ //
+
+ vector<float>
+ firdes::root_raised_cosine(double gain,
+ double sampling_freq,
+ double symbol_rate,
+ double alpha,
+ int ntaps)
+ {
+ ntaps |= 1; // ensure that ntaps is odd
+
+ double spb = sampling_freq/symbol_rate; // samples per bit/symbol
+ vector<float> taps(ntaps);
+ double scale = 0;
+ for(int i = 0; i < ntaps; i++) {
+ double x1,x2,x3,num,den;
+ double xindx = i - ntaps/2;
+ x1 = M_PI * xindx/spb;
+ x2 = 4 * alpha * xindx / spb;
+ x3 = x2*x2 - 1;
+
+ if(fabs(x3) >= 0.000001) { // Avoid Rounding errors...
+ if(i != ntaps/2)
+ num = cos((1+alpha)*x1) + sin((1-alpha)*x1)/(4*alpha*xindx/spb);
+ else
+ num = cos((1+alpha)*x1) + (1-alpha) * M_PI / (4*alpha);
+ den = x3 * M_PI;
+ }
+ else {
+ if(alpha==1) {
+ taps[i] = -1;
+ continue;
+ }
+ x3 = (1-alpha)*x1;
+ x2 = (1+alpha)*x1;
+ num = (sin(x2)*(1+alpha)*M_PI
+ - cos(x3)*((1-alpha)*M_PI*spb)/(4*alpha*xindx)
+ + sin(x3)*spb*spb/(4*alpha*xindx*xindx));
+ den = -32 * M_PI * alpha * alpha * xindx/spb;
+ }
+ taps[i] = 4 * alpha * num / den;
+ scale += taps[i];
+ }
+
+ for(int i = 0; i < ntaps; i++)
+ taps[i] = taps[i] * gain / scale;
+
+ return taps;
+ }
+
+ //
+ // === Utilities ===
+ //
+
+ // delta_f / width_factor gives number of taps required.
+ static const float width_factor[5] = { // indexed by win_type
+ 3.3, // WIN_HAMMING
+ 3.1, // WIN_HANN
+ 5.5, // WIN_BLACKMAN
+ 2.0, // WIN_RECTANGULAR
+ //5.0 // WIN_KAISER (guesstimate compromise)
+ //2.0 // WIN_KAISER (guesstimate compromise)
+ 10.0 // WIN_KAISER
+ };
+
+ int
+ firdes::compute_ntaps_windes(double sampling_freq,
+ double transition_width, // this is frequency, not relative frequency
+ double attenuation_dB)
+ {
+ // Based on formula from Multirate Signal Processing for
+ // Communications Systems, fredric j harris
+ int ntaps = (int)(attenuation_dB*sampling_freq/(22.0*transition_width));
+ if ((ntaps & 1) == 0) // if even...
+ ntaps++; // ...make odd
+ return ntaps;
+ }
+
+ int
+ firdes::compute_ntaps(double sampling_freq,
+ double transition_width,
+ win_type window_type,
+ double beta)
+ {
+ // normalized transition width
+ double delta_f = transition_width / sampling_freq;
+
+ // compute number of taps required for given transition width
+ int ntaps = (int)(width_factor[window_type] / delta_f + 0.5);
+ if((ntaps & 1) == 0) // if even...
+ ntaps++; // ...make odd
+
+ return ntaps;
+ }
+
+ double
+ firdes::bessi0(double x)
+ {
+ double ax,ans;
+ double y;
+
+ ax=fabs(x);
+ if (ax < 3.75) {
+ y=x/3.75;
+ y*=y;
+ ans=1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492
+ +y*(0.2659732+y*(0.360768e-1+y*0.45813e-2)))));
+ }
+ else {
+ y=3.75/ax;
+ ans=(exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1
+ +y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2
+ +y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1
+ +y*0.392377e-2))))))));
+ }
+ return ans;
+ }
+
+ vector<float>
+ firdes::window (win_type type, int ntaps, double beta)
+ {
+ vector<float> taps(ntaps);
+ int M = ntaps - 1; // filter order
+
+ switch (type) {
+ case WIN_RECTANGULAR:
+ for(int n = 0; n < ntaps; n++)
+ taps[n] = 1;
+
+ case WIN_HAMMING:
+ for(int n = 0; n < ntaps; n++)
+ taps[n] = 0.54 - 0.46 * cos((2 * M_PI * n) / M);
+ break;
+
+ case WIN_HANN:
+ for(int n = 0; n < ntaps; n++)
+ taps[n] = 0.5 - 0.5 * cos((2 * M_PI * n) / M);
+ break;
+
+ case WIN_BLACKMAN:
+ for(int n = 0; n < ntaps; n++)
+ taps[n] = 0.42 - 0.50 * cos((2*M_PI * n) / (M-1))
+ - 0.08 * cos((4*M_PI * n) / (M-1));
+ break;
+
+ case WIN_BLACKMAN_hARRIS:
+ for(int n = -ntaps/2; n < ntaps/2; n++)
+ taps[n+ntaps/2] = 0.35875 + 0.48829*cos((2*M_PI * n) / (float)M) +
+ 0.14128*cos((4*M_PI * n) / (float)M) + 0.01168*cos((6*M_PI * n) / (float)M);
+ break;
+
+ case WIN_KAISER:
+ {
+ double IBeta = 1.0/Izero(beta);
+ double inm1 = 1.0/((double)(ntaps));
+ double temp;
+ //fprintf(stderr, "IBeta = %g; inm1 = %g\n", IBeta, inm1);
+
+ for(int i=0; i<ntaps; i++) {
+ temp = i * inm1;
+ //fprintf(stderr, "temp = %g\n", temp);
+ taps[i] = Izero(beta*sqrt(1.0-temp*temp)) * IBeta;
+ //fprintf(stderr, "taps[%d] = %g\n", i, taps[i]);
+ }
+ }
+ break;
+
+ default:
+ throw std::out_of_range("firdes:window: type out of range");
+ }
+
+ return taps;
+ }
+
+ void
+ firdes::sanity_check_1f(double sampling_freq,
+ double fa, // cutoff freq
+ double transition_width)
+ {
+ if(sampling_freq <= 0.0)
+ throw std::out_of_range("firdes check failed: sampling_freq > 0");
+
+ if(fa <= 0.0 || fa > sampling_freq / 2)
+ throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2");
+
+ if(transition_width <= 0)
+ throw std::out_of_range("gr_dirdes check failed: transition_width > 0");
+ }
+
+ void
+ firdes::sanity_check_2f(double sampling_freq,
+ double fa, // first cutoff freq
+ double fb, // second cutoff freq
+ double transition_width)
+ {
+ if (sampling_freq <= 0.0)
+ throw std::out_of_range("firdes check failed: sampling_freq > 0");
+
+ if (fa <= 0.0 || fa > sampling_freq / 2)
+ throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2");
+
+ if (fb <= 0.0 || fb > sampling_freq / 2)
+ throw std::out_of_range("firdes check failed: 0 < fb <= sampling_freq / 2");
+
+ if (fa > fb)
+ throw std::out_of_range("firdes check failed: fa <= fb");
+
+ if (transition_width <= 0)
+ throw std::out_of_range("firdes check failed: transition_width > 0");
+ }
+
+ void
+ firdes::sanity_check_2f_c(double sampling_freq,
+ double fa, // first cutoff freq
+ double fb, // second cutoff freq
+ double transition_width)
+ {
+ if(sampling_freq <= 0.0)
+ throw std::out_of_range("firdes check failed: sampling_freq > 0");
+
+ if(fa < -sampling_freq / 2 || fa > sampling_freq / 2)
+ throw std::out_of_range("firdes check failed: 0 < fa <= sampling_freq / 2");
+
+ if(fb < -sampling_freq / 2 || fb > sampling_freq / 2)
+ throw std::out_of_range("firdes check failed: 0 < fb <= sampling_freq / 2");
+
+ if(fa > fb)
+ throw std::out_of_range("firdes check failed: fa <= fb");
+
+ if(transition_width <= 0)
+ throw std::out_of_range("firdes check failed: transition_width > 0");
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/qa_filter.cc b/gr-filter/lib/qa_filter.cc
new file mode 100644
index 000000000..86d4860de
--- /dev/null
+++ b/gr-filter/lib/qa_filter.cc
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+/*
+ * This class gathers together all the test cases for the gr-filter
+ * directory into a single test suite. As you create new test cases,
+ * add them here.
+ */
+
+#include <qa_filter.h>
+#include <qa_firdes.h>
+
+CppUnit::TestSuite *
+qa_gr_filter::suite ()
+{
+ CppUnit::TestSuite *s = new CppUnit::TestSuite ("gr-filter");
+
+ s->addTest(gr::filter::qa_firdes::suite ());
+
+ return s;
+}
diff --git a/gr-filter/lib/qa_filter.h b/gr-filter/lib/qa_filter.h
new file mode 100644
index 000000000..427e7f9f6
--- /dev/null
+++ b/gr-filter/lib/qa_filter.h
@@ -0,0 +1,38 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+#ifndef _QA_GR_FILTER_H_
+#define _QA_GR_FILTER_H_
+
+#include <gruel/attributes.h>
+#include <cppunit/TestSuite.h>
+
+//! collect all the tests for the gr-filter directory
+
+class __GR_ATTR_EXPORT qa_gr_filter {
+ public:
+ //! return suite of tests for all of gr-filter directory
+ static CppUnit::TestSuite *suite ();
+};
+
+
+#endif /* _QA_GR_FILTER_H_ */
diff --git a/gr-filter/lib/qa_firdes.cc b/gr-filter/lib/qa_firdes.cc
new file mode 100644
index 000000000..c2fe399d5
--- /dev/null
+++ b/gr-filter/lib/qa_firdes.cc
@@ -0,0 +1,621 @@
+/* -*- 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.
+ */
+
+#include <qa_firdes.h>
+#include <filter/firdes.h>
+#include <cppunit/TestAssert.h>
+#include <gr_complex.h>
+#include <string.h>
+#include <iostream>
+#include <iomanip>
+#include <stdio.h>
+
+namespace gr {
+ namespace filter {
+
+#define NELEM(x) (sizeof(x) / sizeof(x[0]))
+
+ using std::vector;
+
+#if 0
+ static void
+ print_taps(std::ostream &s, vector<float> &v)
+ {
+
+ for(unsigned int i = 0; i < v.size(); i++) {
+ printf("tap[%2d] = %16.7e\n", i, v[i]);
+ }
+ }
+#endif
+
+ static void
+ check_symmetry(vector<float> &v)
+ {
+ int n = v.size();
+ int m = n / 2;
+
+ for(int i = 0; i < m; i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(v[i], v[n - i - 1], 1e-9);
+ }
+
+ const static float t1_exp[53] = {
+ -9.0525491e-04,
+ 2.0713841e-04,
+ 1.2388536e-03,
+ 2.9683491e-04,
+ -1.7744775e-03,
+ -1.3599906e-03,
+ 2.2031884e-03,
+ 3.2744040e-03,
+ -1.8868084e-03,
+ -5.9935520e-03,
+ 6.4301129e-18,
+ 8.9516686e-03,
+ 4.2178580e-03,
+ -1.0998557e-02,
+ -1.1173409e-02,
+ 1.0455756e-02,
+ 2.0686293e-02,
+ -5.2032238e-03,
+ -3.1896964e-02,
+ -7.4998410e-03,
+ 4.3362070e-02,
+ 3.2502845e-02,
+ -5.3328082e-02,
+ -8.5621715e-02,
+ 6.0117975e-02,
+ 3.1128189e-01,
+ 4.3769023e-01,
+ 3.1128189e-01,
+ 6.0117975e-02,
+ -8.5621715e-02,
+ -5.3328082e-02,
+ 3.2502845e-02,
+ 4.3362070e-02,
+ -7.4998410e-03,
+ -3.1896964e-02,
+ -5.2032238e-03,
+ 2.0686293e-02,
+ 1.0455756e-02,
+ -1.1173409e-02,
+ -1.0998557e-02,
+ 4.2178580e-03,
+ 8.9516686e-03,
+ 6.4301129e-18,
+ -5.9935520e-03,
+ -1.8868084e-03,
+ 3.2744040e-03,
+ 2.2031884e-03,
+ -1.3599906e-03,
+ -1.7744775e-03,
+ 2.9683491e-04,
+ 1.2388536e-03,
+ 2.0713841e-04,
+ -9.0525491e-04
+ };
+
+ const static float t2_exp[53] = {
+ 9.0380036e-04,
+ -2.0680559e-04,
+ -1.2368630e-03,
+ -2.9635796e-04,
+ 1.7716263e-03,
+ 1.3578053e-03,
+ -2.1996482e-03,
+ -3.2691427e-03,
+ 1.8837767e-03,
+ 5.9839217e-03,
+ -6.4197810e-18,
+ -8.9372853e-03,
+ -4.2110807e-03,
+ 1.0980885e-02,
+ 1.1155456e-02,
+ -1.0438956e-02,
+ -2.0653054e-02,
+ 5.1948633e-03,
+ 3.1845711e-02,
+ 7.4877902e-03,
+ -4.3292396e-02,
+ -3.2450620e-02,
+ 5.3242393e-02,
+ 8.5484132e-02,
+ -6.0021374e-02,
+ -3.1078172e-01,
+ 5.6184036e-01,
+ -3.1078172e-01,
+ -6.0021374e-02,
+ 8.5484132e-02,
+ 5.3242393e-02,
+ -3.2450620e-02,
+ -4.3292396e-02,
+ 7.4877902e-03,
+ 3.1845711e-02,
+ 5.1948633e-03,
+ -2.0653054e-02,
+ -1.0438956e-02,
+ 1.1155456e-02,
+ 1.0980885e-02,
+ -4.2110807e-03,
+ -8.9372853e-03,
+ -6.4197810e-18,
+ 5.9839217e-03,
+ 1.8837767e-03,
+ -3.2691427e-03,
+ -2.1996482e-03,
+ 1.3578053e-03,
+ 1.7716263e-03,
+ -2.9635796e-04,
+ -1.2368630e-03,
+ -2.0680559e-04,
+ 9.0380036e-04
+ };
+
+ const static float t3_exp[107] = {
+ -1.8970841e-06,
+ -7.1057165e-04,
+ 5.4005696e-04,
+ 4.6233178e-04,
+ 2.0572044e-04,
+ 3.5209916e-04,
+ -1.4098573e-03,
+ 1.1279077e-04,
+ -6.2994129e-04,
+ 1.1450432e-03,
+ 1.3637283e-03,
+ -6.4360141e-04,
+ 3.6509900e-04,
+ -3.2864159e-03,
+ 7.0192874e-04,
+ 3.7524730e-04,
+ 2.0256115e-03,
+ 3.0641893e-03,
+ -3.6618244e-03,
+ 7.5592739e-05,
+ -5.5586505e-03,
+ 2.3849572e-03,
+ 4.0114378e-03,
+ 1.6636450e-03,
+ 4.7835698e-03,
+ -1.0191196e-02,
+ -3.8158931e-04,
+ -5.5551580e-03,
+ 5.3901658e-03,
+ 1.1366769e-02,
+ -3.0000482e-03,
+ 4.9341680e-03,
+ -2.0093076e-02,
+ 5.5752542e-17,
+ 1.2093617e-03,
+ 8.6089745e-03,
+ 2.2382140e-02,
+ -1.6854567e-02,
+ 1.6913920e-03,
+ -3.1222520e-02,
+ 3.2711059e-03,
+ 2.2604836e-02,
+ 8.1451107e-03,
+ 3.7583180e-02,
+ -5.2293688e-02,
+ -8.0551542e-03,
+ -4.0092729e-02,
+ 1.5582236e-02,
+ 9.7452506e-02,
+ -1.6183170e-02,
+ 8.3281815e-02,
+ -2.8196752e-01,
+ -1.0965768e-01,
+ 5.2867508e-01,
+ -1.0965768e-01,
+ -2.8196752e-01,
+ 8.3281815e-02,
+ -1.6183170e-02,
+ 9.7452506e-02,
+ 1.5582236e-02,
+ -4.0092729e-02,
+ -8.0551542e-03,
+ -5.2293688e-02,
+ 3.7583180e-02,
+ 8.1451107e-03,
+ 2.2604836e-02,
+ 3.2711059e-03,
+ -3.1222520e-02,
+ 1.6913920e-03,
+ -1.6854567e-02,
+ 2.2382140e-02,
+ 8.6089745e-03,
+ 1.2093617e-03,
+ 5.5752542e-17,
+ -2.0093076e-02,
+ 4.9341680e-03,
+ -3.0000482e-03,
+ 1.1366769e-02,
+ 5.3901658e-03,
+ -5.5551580e-03,
+ -3.8158931e-04,
+ -1.0191196e-02,
+ 4.7835698e-03,
+ 1.6636450e-03,
+ 4.0114378e-03,
+ 2.3849572e-03,
+ -5.5586505e-03,
+ 7.5592739e-05,
+ -3.6618244e-03,
+ 3.0641893e-03,
+ 2.0256115e-03,
+ 3.7524730e-04,
+ 7.0192874e-04,
+ -3.2864159e-03,
+ 3.6509900e-04,
+ -6.4360141e-04,
+ 1.3637283e-03,
+ 1.1450432e-03,
+ -6.2994129e-04,
+ 1.1279077e-04,
+ -1.4098573e-03,
+ 3.5209916e-04,
+ 2.0572044e-04,
+ 4.6233178e-04,
+ 5.4005696e-04,
+ -7.1057165e-04,
+ -1.8970841e-06
+ };
+
+ const static float t4_exp[] = { // low pass
+ 0.001059958362,
+ 0.0002263929928,
+ -0.001277606934,
+ -0.0009675776237,
+ 0.001592264394,
+ 0.00243603508,
+ -0.001451682881,
+ -0.004769335967,
+ 5.281541594e-18,
+ 0.007567512803,
+ 0.003658855334,
+ -0.009761494584,
+ -0.01011830103,
+ 0.009636915289,
+ 0.0193619132,
+ -0.004935568199,
+ -0.03060629964,
+ -0.007267376408,
+ 0.04236677289,
+ 0.03197422624,
+ -0.05274848267,
+ -0.0850463286,
+ 0.05989059806,
+ 0.31065014,
+ 0.4370569289,
+ 0.31065014,
+ 0.05989059806,
+ -0.0850463286,
+ -0.05274848267,
+ 0.03197422624,
+ 0.04236677289,
+ -0.007267376408,
+ -0.03060629964,
+ -0.004935568199,
+ 0.0193619132,
+ 0.009636915289,
+ -0.01011830103,
+ -0.009761494584,
+ 0.003658855334,
+ 0.007567512803,
+ 5.281541594e-18,
+ -0.004769335967,
+ -0.001451682881,
+ 0.00243603508,
+ 0.001592264394,
+ -0.0009675776237,
+ -0.001277606934,
+ 0.0002263929928,
+ 0.001059958362,
+ };
+
+ const static float t5_exp[] = { //high pass
+ -0.001062123571,
+ -0.0002268554381,
+ 0.001280216733,
+ 0.000969554123,
+ -0.001595516922,
+ -0.002441011136,
+ 0.001454648213,
+ 0.004779078532,
+ -5.292330097e-18,
+ -0.007582970895,
+ -0.00366632943,
+ 0.009781434201,
+ 0.01013896987,
+ -0.009656600654,
+ -0.01940146461,
+ 0.004945650231,
+ 0.03066881932,
+ 0.00728222169,
+ -0.04245331511,
+ -0.03203954175,
+ 0.05285623297,
+ 0.08522006124,
+ -0.06001294032,
+ -0.3112847209,
+ 0.5630782247,
+ -0.3112847209,
+ -0.06001294032,
+ 0.08522006124,
+ 0.05285623297,
+ -0.03203954175,
+ -0.04245331511,
+ 0.00728222169,
+ 0.03066881932,
+ 0.004945650231,
+ -0.01940146461,
+ -0.009656600654,
+ 0.01013896987,
+ 0.009781434201,
+ -0.00366632943,
+ -0.007582970895,
+ -5.292330097e-18,
+ 0.004779078532,
+ 0.001454648213,
+ -0.002441011136,
+ -0.001595516922,
+ 0.000969554123,
+ 0.001280216733,
+ -0.0002268554381,
+ -0.001062123571,
+ };
+
+ const static float t6_exp[] = { // bandpass
+ 0.0002809273137,
+ -0.001047327649,
+ 7.936541806e-05,
+ -0.0004270860809,
+ 0.0007595835486,
+ 0.0008966081077,
+ -0.0004236323002,
+ 0.0002423936094,
+ -0.002212299034,
+ 0.0004807534278,
+ 0.0002620361629,
+ 0.001443728455,
+ 0.002229931997,
+ -0.002720607212,
+ 5.731141573e-05,
+ -0.004297634587,
+ 0.001878833398,
+ 0.003217151389,
+ 0.001357055153,
+ 0.003965090029,
+ -0.008576190099,
+ -0.0003257228818,
+ -0.004805727862,
+ 0.004721920472,
+ 0.01007549558,
+ -0.002688719891,
+ 0.004467967432,
+ -0.01837076992,
+ 5.119658377e-17,
+ 0.001125075156,
+ 0.008071650751,
+ 0.02113764361,
+ -0.01602453552,
+ 0.001618095324,
+ -0.03004053794,
+ 0.003163811285,
+ 0.0219683405,
+ 0.007950295694,
+ 0.03682873398,
+ -0.05142467469,
+ -0.00794606097,
+ -0.03965795785,
+ 0.01544955093,
+ 0.09681399167,
+ -0.01610304788,
+ 0.08297294378,
+ -0.2811714709,
+ -0.1094062924,
+ 0.5275565982,
+ -0.1094062924,
+ -0.2811714709,
+ 0.08297294378,
+ -0.01610304788,
+ 0.09681399167,
+ 0.01544955093,
+ -0.03965795785,
+ -0.00794606097,
+ -0.05142467469,
+ 0.03682873398,
+ 0.007950295694,
+ 0.0219683405,
+ 0.003163811285,
+ -0.03004053794,
+ 0.001618095324,
+ -0.01602453552,
+ 0.02113764361,
+ 0.008071650751,
+ 0.001125075156,
+ 5.119658377e-17,
+ -0.01837076992,
+ 0.004467967432,
+ -0.002688719891,
+ 0.01007549558,
+ 0.004721920472,
+ -0.004805727862,
+ -0.0003257228818,
+ -0.008576190099,
+ 0.003965090029,
+ 0.001357055153,
+ 0.003217151389,
+ 0.001878833398,
+ -0.004297634587,
+ 5.731141573e-05,
+ -0.002720607212,
+ 0.002229931997,
+ 0.001443728455,
+ 0.0002620361629,
+ 0.0004807534278,
+ -0.002212299034,
+ 0.0002423936094,
+ -0.0004236323002,
+ 0.0008966081077,
+ 0.0007595835486,
+ -0.0004270860809,
+ 7.936541806e-05,
+ -0.001047327649,
+ 0.0002809273137,
+ };
+
+ void
+ qa_firdes::t1()
+ {
+ vector<float> taps =
+ firdes::low_pass(1.0,
+ 8000,
+ 1750,
+ 500,
+ firdes::WIN_HAMMING);
+
+ // cout << "ntaps: " << taps.size() << endl;
+ // print_taps(cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t1_exp), taps.size());
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t1_exp[i], taps[i], 1e-9);
+
+ check_symmetry(taps);
+}
+
+ void
+ qa_firdes::t2()
+ {
+ vector<float> taps =
+ firdes::high_pass(1.0,
+ 8000,
+ 1750,
+ 500,
+ firdes::WIN_HAMMING);
+
+ // cout << "ntaps: " << taps.size() << endl;
+ // print_taps(cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t2_exp), taps.size());
+
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t2_exp[i], taps[i], 1e-9);
+
+ check_symmetry(taps);
+ }
+
+ void
+ qa_firdes::t3()
+ {
+ vector<float> taps =
+ firdes::band_pass(1.0,
+ 20e6,
+ 5.75e6 - (5.28e6/2),
+ 5.75e6 + (5.28e6/2),
+ 0.62e6,
+ firdes::WIN_HAMMING);
+
+ // cout << "ntaps: " << taps.size() << endl;
+ // print_taps(cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t3_exp), taps.size());
+
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t3_exp[i], taps[i], 1e-7);
+
+ check_symmetry(taps);
+ }
+
+ void
+ qa_firdes::t4()
+ {
+ vector<float> taps =
+ firdes::low_pass_2(1.0,
+ 8000,
+ 1750,
+ 500,
+ 66,
+ firdes::WIN_HAMMING);
+
+ // std::cout << "ntaps: " << taps.size() << std::endl;
+ // print_taps(std::cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t4_exp), taps.size());
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t4_exp[i], taps[i], 1e-9);
+
+ check_symmetry(taps);
+ }
+
+ void
+ qa_firdes::t5()
+ {
+ vector<float> taps =
+ firdes::high_pass_2(1.0,
+ 8000,
+ 1750,
+ 500,
+ 66,
+ firdes::WIN_HAMMING);
+
+ // std::cout << "ntaps: " << taps.size() << std::endl;
+ // print_taps(std::cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t5_exp), taps.size());
+
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t5_exp[i], taps[i], 1e-9);
+
+ check_symmetry(taps);
+}
+
+ void
+ qa_firdes::t6()
+ {
+ vector<float> taps =
+ firdes::band_pass_2(1.0,
+ 20e6,
+ 5.75e6 - (5.28e6/2),
+ 5.75e6 + (5.28e6/2),
+ 0.62e6,
+ 66,
+ firdes::WIN_HAMMING);
+
+ // std::cout << "ntaps: " << taps.size() << std::endl;
+ // print_taps(std::cout, taps);
+
+ CPPUNIT_ASSERT_EQUAL(NELEM(t6_exp), taps.size());
+
+ for(unsigned int i = 0; i < taps.size(); i++)
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(t6_exp[i], taps[i], 1e-7);
+
+ check_symmetry(taps);
+ }
+
+ void
+ qa_firdes::t7()
+ {
+ }
+
+ } /* namespace filter */
+} /* namespace gr */
diff --git a/gr-filter/lib/qa_firdes.h b/gr-filter/lib/qa_firdes.h
new file mode 100644
index 000000000..b27cf78b9
--- /dev/null
+++ b/gr-filter/lib/qa_firdes.h
@@ -0,0 +1,56 @@
+/* -*- 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.
+ */
+#ifndef _QA_FILTER_FIRDES_H_
+#define _QA_FILTER_FIRDES_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/TestCase.h>
+
+namespace gr {
+ namespace filter {
+
+ class qa_firdes : public CppUnit::TestCase {
+
+ CPPUNIT_TEST_SUITE(qa_firdes);
+ CPPUNIT_TEST(t1);
+ CPPUNIT_TEST(t2);
+ CPPUNIT_TEST(t3);
+ CPPUNIT_TEST(t4);
+ CPPUNIT_TEST(t5);
+ CPPUNIT_TEST(t6);
+ CPPUNIT_TEST(t7);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ void t1();
+ void t2();
+ void t3();
+ void t4();
+ void t5();
+ void t6();
+ void t7();
+ };
+
+ } /* namespace filter */
+} /* namespace gr */
+
+#endif /* _QA_FILTER_FIRDES_H_ */
diff --git a/gr-filter/lib/test_gr_filter.cc b/gr-filter/lib/test_gr_filter.cc
new file mode 100644
index 000000000..9027b7a99
--- /dev/null
+++ b/gr-filter/lib/test_gr_filter.cc
@@ -0,0 +1,42 @@
+/* -*- c++ -*- */
+/*
+ * 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 <cppunit/TextTestRunner.h>
+#include <cppunit/XmlOutputter.h>
+
+#include <gr_unittests.h>
+#include <qa_filter.h>
+
+int
+main (int argc, char **argv)
+{
+ CppUnit::TextTestRunner runner;
+ std::ofstream xmlfile(get_unittest_path("gr_filter.xml").c_str());
+ CppUnit::XmlOutputter *xmlout = new CppUnit::XmlOutputter(&runner.result(), xmlfile);
+
+ runner.addTest(qa_gr_filter::suite());
+ runner.setOutputter(xmlout);
+
+ bool was_successful = runner.run("", false);
+
+ return was_successful ? 0 : 1;
+}
diff --git a/gr-filter/python/qa_fir_filter.py b/gr-filter/python/qa_fir_filter.py
index f0f08afca..38bfd9ea5 100755
--- a/gr-filter/python/qa_fir_filter.py
+++ b/gr-filter/python/qa_fir_filter.py
@@ -70,7 +70,7 @@ class test_filter(gr_unittest.TestCase):
# results derived from original gr.fir_filter_ccc
expected_data = ((7.537424837948042e-20+7.537424837948042e-20j), (9.131923434324563e-05+9.131923434324563e-05j), (0.0003317668742965907+0.0003317668742965907j), (0.0007230418268591166+0.0007230418268591166j), (0.0012087896466255188+0.0012087896466255188j), (0.0013292605290189385+0.0013292605290189385j), (0.001120875240303576+0.001120875240303576j), (0.000744672492146492+0.000744672492146492j), (0.000429437990533188+0.000429437990533188j), (2.283908543176949e-05+2.283908543176949e-05j), (-0.0002245186478830874-0.0002245186478830874j), (-0.0001157080550910905-0.0001157080550910905j), (0.00041409023106098175+0.00041409023106098175j), (0.0009017843985930085+0.0009017843985930085j), (0.0012520025484263897+0.0012520025484263897j), (0.0014116164529696107+0.0014116164529696107j), (0.001393353333696723+0.001393353333696723j), (0.000912194955162704+0.000912194955162704j), (0.00022649182938039303+0.00022649182938039303j), (-0.00031363096786662936-0.00031363096786662936j), (-0.0003966730728279799-0.0003966730728279799j), (-0.00023757052258588374-0.00023757052258588374j), (0.00021952332463115454+0.00021952332463115454j), (0.0009092430118471384+0.0009092430118471384j), (0.001662317430600524+0.001662317430600524j), (0.0019024648936465383+0.0019024648936465383j), (0.0015955769922584295+0.0015955769922584295j), (0.0009144138311967254+0.0009144138311967254j), (0.0001872836146503687+0.0001872836146503687j), (-0.000581968342885375-0.000581968342885375j), (-0.0009886166080832481-0.0009886166080832481j), (-0.0007480768254026771-0.0007480768254026771j), (0.00018211957649327815+0.00018211957649327815j), (0.0012042406015098095+0.0012042406015098095j), (0.0020200139842927456+0.0020200139842927456j), (0.0023816542234271765+0.0023816542234271765j), (0.002195809967815876+0.002195809967815876j), (0.0012113333214074373+0.0012113333214074373j), (-0.00014088614261709154-0.00014088614261709154j), (-0.0012574587017297745-0.0012574587017297745j))
- taps = gr.firdes.low_pass(1, 1, 0.1, 0.01)
+ taps = filter.firdes.low_pass(1, 1, 0.1, 0.01)
src = gr.vector_source_c(src_data)
op = filter.fir_filter_ccc(1, taps)
dst = gr.vector_sink_c()
diff --git a/gr-filter/python/qa_firdes.py b/gr-filter/python/qa_firdes.py
new file mode 100755
index 000000000..cfd10435f
--- /dev/null
+++ b/gr-filter/python/qa_firdes.py
@@ -0,0 +1,202 @@
+#!/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 gr, gr_unittest
+import filter_swig as filter
+import sys
+
+class test_firdes(gr_unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_low_pass(self):
+ known_taps = (0.0030193300917744637, -0.004960992839187384,
+ 0.006678304169327021, -1.132049690556083e-17,
+ -0.0251916591078043, 0.07206480950117111,
+ -0.13062666356563568, 0.18007083237171173,
+ 0.7978920936584473, 0.18007083237171173,
+ -0.13062666356563568, 0.07206480950117111,
+ -0.0251916591078043, -1.132049690556083e-17,
+ 0.006678304169327021, -0.004960992839187384,
+ 0.0030193300917744637)
+ new_taps = filter.firdes.low_pass(1, 1, 0.4, 0.2)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_low_pass_2(self):
+ known_taps = (0.0024871660862118006, -4.403502608370943e-18,
+ -0.014456653036177158, 0.0543283149600029,
+ -0.116202212870121, 0.17504146695137024,
+ 0.7976038455963135, 0.17504146695137024,
+ -0.116202212870121, 0.0543283149600029,
+ -0.014456653036177158, -4.403502608370943e-18,
+ 0.0024871660862118006)
+ new_taps = filter.firdes.low_pass_2(1, 1, 0.4, 0.2, 60)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_high_pass(self):
+ known_taps = (-0.003062003292143345, 0.005031108390539885,
+ -0.0067726909182965755, 1.1480492661182674e-17,
+ 0.025547700002789497, -0.0730833187699318,
+ 0.13247284293174744, -0.18261581659317017,
+ 0.20229223370552063, -0.18261581659317017,
+ 0.13247284293174744, -0.0730833187699318,
+ 0.025547700002789497, 1.1480492661182674e-17,
+ -0.0067726909182965755, 0.005031108390539885,
+ -0.003062003292143345)
+ new_taps = filter.firdes.high_pass(1, 1, 0.4, 0.2)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_high_pass_2(self):
+ known_taps = (-0.0027197482995688915, 4.815287179370254e-18,
+ 0.01580853760242462, -0.05940871313214302,
+ 0.1270686239004135, -0.1914101094007492,
+ 0.21804752945899963, -0.1914101094007492,
+ 0.1270686239004135, -0.05940871313214302,
+ 0.01580853760242462, 4.815287179370254e-18,
+ -0.0027197482995688915)
+ new_taps = filter.firdes.high_pass_2(1, 1, 0.4, 0.2, 60)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_band_pass(self):
+ known_taps = (0.004961997736245394, -0.008152946829795837,
+ -0.004192151129245758, -5.749020235348687e-18,
+ 0.01581347920000553, 0.11843203753232956,
+ -0.21467317640781403, -0.11303528398275375,
+ 0.40520283579826355, -0.11303528398275375,
+ -0.21467317640781403, 0.11843203753232956,
+ 0.01581347920000553, -5.749020235348687e-18,
+ -0.004192151129245758, -0.008152946829795837,
+ 0.004961997736245394)
+ new_taps = filter.firdes.band_pass(1, 1, 0.2, 0.4, 0.2)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_band_pass_2(self):
+ known_taps = (-0.001676854444667697, -2.4018533253972557e-18,
+ 0.009746716357767582, 0.09589414298534393,
+ -0.20510689914226532, -0.11801345646381378,
+ 0.4350462853908539, -0.11801345646381378,
+ -0.20510689914226532, 0.09589414298534393,
+ 0.009746716357767582, -2.4018533253972557e-18,
+ -0.001676854444667697)
+ new_taps = filter.firdes.band_pass_2(1, 1, 0.2, 0.4, 0.2, 60)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_complex_band_pass(self):
+ known_taps = ((0.0024772135075181723+0.0017997993854805827j),
+ (-0.004070250317454338+0.002957213670015335j),
+ (-0.0020928815938532352-0.006441210396587849j),
+ (-2.8701231652956686e-18+2.805614574993832e-24j),
+ (0.007894645445048809-0.024297315627336502j),
+ (0.05912570655345917+0.04295721650123596j),
+ (-0.10717268288135529+0.07786571979522705j),
+ (-0.0564316064119339-0.17367789149284363j),
+ (0.20229223370552063-2.4115112751132983e-07j),
+ (-0.05643119290471077+0.17367802560329437j),
+ (-0.10717286914587021-0.07786546647548676j),
+ (0.05912560224533081-0.0429573580622673j),
+ (0.007894691079854965+0.024297300726175308j),
+ (-2.8701231652956686e-18+2.6687109203363464e-24j),
+ (-0.0020928694866597652+0.006441214121878147j),
+ (-0.004070255905389786-0.0029572059866040945j),
+ (0.0024772100150585175-0.0017998040420934558j))
+ new_taps = filter.firdes.complex_band_pass(1, 1, 0.2, 0.4, 0.2)
+ self.assertComplexTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_complex_band_pass_2(self):
+ known_taps = ((-0.0008404505206272006-0.0025866336654871702j),
+ (-1.2038217948425635e-18+1.1767648157397848e-24j),
+ (0.0048850891180336475-0.015034818090498447j),
+ (0.048062704503536224+0.03491950035095215j),
+ (-0.10280057787895203+0.07468919456005096j),
+ (-0.05914920195937157-0.18204176425933838j),
+ (0.21804752945899963-2.5993290364567656e-07j),
+ (-0.059148769825696945+0.18204189836978912j),
+ (-0.10280075669288635-0.07468894869089127j),
+ (0.04806262254714966-0.0349196158349514j),
+ (0.004885117989033461+0.015034808777272701j),
+ (-1.2038217948425635e-18+1.1193430388030685e-24j),
+ (-0.000840445572976023+0.002586635295301676j))
+ new_taps = filter.firdes.complex_band_pass_2(1, 1, 0.2, 0.4, 0.2, 60)
+ self.assertComplexTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_band_reject(self):
+ known_taps = (-0.004915320314466953, 0.008076251484453678,
+ 0.00415271520614624, 5.694938753309664e-18,
+ -0.01566472090780735, -0.11731793731451035,
+ 0.2126537412405014, 0.11197195947170258,
+ 0.6020866632461548, 0.11197195947170258,
+ 0.2126537412405014, -0.11731793731451035,
+ -0.01566472090780735, 5.694938753309664e-18,
+ 0.00415271520614624, 0.008076251484453678,
+ -0.004915320314466953)
+ new_taps = filter.firdes.band_reject(1, 1, 0.2, 0.4, 0.2)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_band_reject_2(self):
+ known_taps = (0.0015371545450761914, 2.201753372137003e-18,
+ -0.00893471110612154, -0.08790513873100281,
+ 0.1880193054676056, 0.1081816703081131,
+ 0.5982034206390381, 0.1081816703081131,
+ 0.1880193054676056, -0.08790513873100281,
+ -0.00893471110612154, 2.201753372137003e-18,
+ 0.0015371545450761914)
+ new_taps = filter.firdes.band_reject_2(1, 1, 0.2, 0.4, 0.2, 60)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_hilbert(self):
+ known_taps = (-0.010056184604763985, 0.0,
+ -0.08335155993700027, 0.0,
+ -0.5732954144477844, 0.0,
+ 0.5732954144477844, 0.0,
+ 0.08335155993700027, 0.0,
+ 0.010056184604763985)
+ new_taps = filter.firdes.hilbert(11)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_root_raised_cosine(self):
+ known_taps = (-0.04609205573797226, -0.02069387212395668,
+ 0.050548505038022995, 0.14850808680057526,
+ 0.23387153446674347, 0.2677156329154968,
+ 0.23387153446674347, 0.14850808680057526,
+ 0.050548505038022995, -0.02069387212395668,
+ -0.04609205573797226)
+ new_taps = filter.firdes.root_raised_cosine(1, 4, 1, 0.35, 11)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+ def test_gaussian(self):
+ known_taps = (0.0003600157215259969, 0.0031858310103416443,
+ 0.0182281993329525, 0.06743486225605011,
+ 0.16130395233631134, 0.24947398900985718,
+ 0.24947398900985718, 0.16130395233631134,
+ 0.06743486225605011, 0.0182281993329525,
+ 0.0031858310103416443, 0.0003600157215259969,
+ 2.630509879963938e-05)
+ new_taps = filter.firdes.gaussian(1, 4, 0.35, 13)
+ self.assertFloatTuplesAlmostEqual(known_taps, new_taps, 5)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_firdes, "test_firdes.xml")
+
diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i
index 6010455b7..a44131931 100644
--- a/gr-filter/swig/filter_swig.i
+++ b/gr-filter/swig/filter_swig.i
@@ -28,6 +28,7 @@
%include "gr_filter_swig_doc.i"
%{
+#include "filter/firdes.h"
#include "filter/fir_filter_fff.h"
#include "filter/fir_filter_ccf.h"
#include "filter/fir_filter_ccc.h"
@@ -35,6 +36,7 @@
#include "filter/fft_filter_fff.h"
%}
+%include "filter/firdes.h"
%include "filter/fir_filter_fff.h"
%include "filter/fir_filter_ccf.h"
%include "filter/fir_filter_ccc.h"