summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rondeau2011-07-30 11:28:47 -0400
committerTom Rondeau2011-07-30 11:28:47 -0400
commite1b4e60823c2b938500932b0868d88698e48d525 (patch)
tree290554eb452d906b073aaec3896985628a483bc5
parent8b3c4ccf922c602ae77dad7f3911b16bdd0112d3 (diff)
downloadgnuradio-e1b4e60823c2b938500932b0868d88698e48d525.tar.gz
gnuradio-e1b4e60823c2b938500932b0868d88698e48d525.tar.bz2
gnuradio-e1b4e60823c2b938500932b0868d88698e48d525.zip
core: made significant improvements in FLL algorithm. Now converges very nicely.
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc259
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h195
-rw-r--r--gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i23
3 files changed, 384 insertions, 93 deletions
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
index c32398e6d..65e273e8a 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2009,2010 Free Software Foundation, Inc.
+ * Copyright 2009-2011 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -25,9 +25,6 @@
#endif
#include <gr_fll_band_edge_cc.h>
-#include <gr_fir_ccc.h>
-#include <gr_fir_util.h>
-#include <gri_fft.h>
#include <gr_io_signature.h>
#include <gr_expj.h>
#include <gr_math.h>
@@ -45,110 +42,246 @@ float sinc(float x)
-gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float gain_alpha, float gain_beta)
+gr_fll_band_edge_cc_sptr
+gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
+ int filter_size, float bandwidth)
{
return gnuradio::get_initial_sptr(new gr_fll_band_edge_cc (samps_per_sym, rolloff,
- filter_size, gain_alpha, gain_beta));
+ filter_size, bandwidth));
}
-static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(gr_complex)};
+static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)};
static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int));
gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta)
+ int filter_size, float bandwidth)
: gr_sync_block ("fll_band_edge_cc",
gr_make_io_signature (1, 1, sizeof(gr_complex)),
gr_make_io_signaturev (1, 4, iosig)),
- d_alpha(alpha), d_beta(beta), d_updated (false)
+ d_updated (false)
{
+ // Initialize samples per symbol
+ if(samps_per_sym <= 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid number of sps. Must be > 0.");
+ }
+ d_sps = samps_per_sym;
+
+ // Initialize rolloff factor
+ if(rolloff < 0 || rolloff > 1.0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1].");
+ }
+ d_rolloff = rolloff;
+
+ // Initialize filter length
+ if(filter_size <= 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid filter size. Must be > 0.");
+ }
+ d_filter_size = filter_size;
+
+ // Initialize loop bandwidth
+ if(bandwidth < 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid bandwidth. Must be >= 0.");
+ }
+ d_loop_bw = bandwidth;
+
// base this on the number of samples per symbol
d_max_freq = M_TWOPI * (2.0/samps_per_sym);
d_min_freq = -M_TWOPI * (2.0/samps_per_sym);
+ // Set the damping factor for a critically damped system
+ d_damping = sqrt(2.0)/2.0;
+
+ // Set the bandwidth, which will then call update_gains()
+ set_loop_bandwidth(bandwidth);
+
+ // Build the band edge filters
+ design_filter(d_sps, d_rolloff, d_filter_size);
+
+ // Initialize loop values
d_freq = 0;
d_phase = 0;
+}
- set_alpha(alpha);
+gr_fll_band_edge_cc::~gr_fll_band_edge_cc ()
+{
+}
- design_filter(samps_per_sym, rolloff, filter_size);
+void
+gr_fll_band_edge_cc::set_loop_bandwidth(float bw)
+{
+ if(bw < 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid bandwidth. Must be >= 0.");
+ }
+
+ d_loop_bw = bw;
+ update_gains();
}
-gr_fll_band_edge_cc::~gr_fll_band_edge_cc ()
+void
+gr_fll_band_edge_cc::set_damping_factor(float df)
{
- delete d_filter_lower;
- delete d_filter_upper;
+ if(df < 0 || df > 1.0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid damping factor. Must be in [0,1].");
+ }
+
+ d_damping = df;
+ update_gains();
}
+
void
-gr_fll_band_edge_cc::set_alpha(float alpha)
-{
- //float eta = sqrt(2.0)/2.0;
- //float theta = alpha;
- //d_alpha = (4*eta*theta) / (1.0 + 2.0*eta*theta + theta*theta);
- //d_beta = (4*theta*theta) / (1.0 + 2.0*eta*theta + theta*theta);
+gr_fll_band_edge_cc::set_alpha(float alpha)
+{
+ if(alpha < 0 || alpha > 1.0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid alpha. Must be in [0,1].");
+ }
d_alpha = alpha;
}
void
+gr_fll_band_edge_cc::set_beta(float beta)
+{
+ if(beta < 0 || beta > 1.0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid beta. Must be in [0,1].");
+ }
+ d_beta = beta;
+}
+
+void
+gr_fll_band_edge_cc::set_samples_per_symbol(float sps)
+{
+ if(sps <= 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid number of sps. Must be > 0.");
+ }
+ d_sps = sps;
+ design_filter(d_sps, d_rolloff, d_filter_size);
+}
+
+void
+gr_fll_band_edge_cc::set_rolloff(float rolloff)
+{
+ if(rolloff < 0 || rolloff > 1.0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1].");
+ }
+ d_rolloff = rolloff;
+ design_filter(d_sps, d_rolloff, d_filter_size);
+}
+
+void
+gr_fll_band_edge_cc::set_filter_size(int filter_size)
+{
+ if(filter_size <= 0) {
+ throw std::out_of_range ("gr_fll_band_edge_cc: invalid filter size. Must be > 0.");
+ }
+ d_filter_size = filter_size;
+ design_filter(d_sps, d_rolloff, d_filter_size);
+}
+
+float
+gr_fll_band_edge_cc::get_loop_bandwidth()
+{
+ return d_loop_bw;
+}
+
+float
+gr_fll_band_edge_cc::get_damping_factor()
+{
+ return d_damping;
+}
+
+float
+gr_fll_band_edge_cc::get_alpha()
+{
+ return d_alpha;
+}
+
+float
+gr_fll_band_edge_cc::get_beta()
+{
+ return d_beta;
+}
+
+float
+gr_fll_band_edge_cc::get_samples_per_symbol()
+{
+ return d_sps;
+}
+
+float
+gr_fll_band_edge_cc::get_rolloff()
+{
+ return d_rolloff;
+}
+
+int
+gr_fll_band_edge_cc:: get_filter_size()
+{
+ return d_filter_size;
+}
+
+void
+gr_fll_band_edge_cc::update_gains()
+{
+ float denom = (1.0 + 2.0*d_damping*d_loop_bw + d_loop_bw*d_loop_bw);
+ d_alpha = (4*d_damping*d_loop_bw) / denom;
+ d_beta = (4*d_loop_bw*d_loop_bw) / denom;
+}
+
+void
gr_fll_band_edge_cc::design_filter(float samps_per_sym, float rolloff, int filter_size)
{
int M = rint(filter_size / samps_per_sym);
float power = 0;
+
+ // Create the baseband filter by adding two sincs together
std::vector<float> bb_taps;
for(int i = 0; i < filter_size; i++) {
float k = -M + i*2.0/samps_per_sym;
float tap = sinc(rolloff*k - 0.5) + sinc(rolloff*k + 0.5);
power += tap;
-
+
bb_taps.push_back(tap);
}
+ d_taps_lower.resize(filter_size);
+ d_taps_upper.resize(filter_size);
+
+ // Create the band edge filters by spinning the baseband
+ // filter up and down to the right places in frequency.
+ // Also, normalize the power in the filters
int N = (bb_taps.size() - 1.0)/2.0;
- std::vector<gr_complex> taps_lower;
- std::vector<gr_complex> taps_upper;
- for(unsigned int i = 0; i < bb_taps.size(); i++) {
+ for(int i = 0; i < filter_size; i++) {
float tap = bb_taps[i] / power;
-
+
float k = (-N + (int)i)/(2.0*samps_per_sym);
- gr_complex t1 = tap * gr_expj(-2*M_PI*(1+rolloff)*k);
- gr_complex t2 = tap * gr_expj(2*M_PI*(1+rolloff)*k);
-
- taps_lower.push_back(t1);
- taps_upper.push_back(t2);
+ gr_complex t1 = tap * gr_expj(-M_TWOPI*(1+rolloff)*k);
+ gr_complex t2 = tap * gr_expj(M_TWOPI*(1+rolloff)*k);
+
+ d_taps_lower[filter_size-i-1] = t1;
+ d_taps_upper[filter_size-i-1] = t2;
}
-
- std::vector<gr_complex> vtaps(0, taps_lower.size());
- d_filter_upper = gr_fir_util::create_gr_fir_ccc(vtaps);
- d_filter_lower = gr_fir_util::create_gr_fir_ccc(vtaps);
-
- d_filter_lower->set_taps(taps_lower);
- d_filter_upper->set_taps(taps_upper);
-
+
d_updated = true;
// Set the history to ensure enough input items for each filter
set_history(filter_size+1);
-
}
void
gr_fll_band_edge_cc::print_taps()
{
unsigned int i;
- std::vector<gr_complex> taps_upper = d_filter_upper->get_taps();
- std::vector<gr_complex> taps_lower = d_filter_lower->get_taps();
printf("Upper Band-edge: [");
- for(i = 0; i < taps_upper.size(); i++) {
- printf(" %.4e + %.4ej,", taps_upper[i].real(), taps_upper[i].imag());
+ for(i = 0; i < d_taps_upper.size(); i++) {
+ printf(" %.4e + %.4ej,", d_taps_upper[i].real(), d_taps_upper[i].imag());
}
printf("]\n\n");
printf("Lower Band-edge: [");
- for(i = 0; i < taps_lower.size(); i++) {
- printf(" %.4e + %.4ej,", taps_lower[i].real(), taps_lower[i].imag());
+ for(i = 0; i < d_taps_lower.size(); i++) {
+ printf(" %.4e + %.4ej,", d_taps_lower[i].real(), d_taps_lower[i].imag());
}
printf("]\n\n");
}
@@ -163,11 +296,11 @@ gr_fll_band_edge_cc::work (int noutput_items,
float *frq = NULL;
float *phs = NULL;
- gr_complex *err = NULL;
- if(output_items.size() > 2) {
+ float *err = NULL;
+ if(output_items.size() == 4) {
frq = (float *) output_items[1];
phs = (float *) output_items[2];
- err = (gr_complex *) output_items[3];
+ err = (float *) output_items[3];
}
if (d_updated) {
@@ -176,21 +309,24 @@ gr_fll_band_edge_cc::work (int noutput_items,
}
int i;
+ float error;
gr_complex nco_out;
gr_complex out_upper, out_lower;
- float error;
- float avg_k = 0.1;
for(i = 0; i < noutput_items; i++) {
nco_out = gr_expj(d_phase);
- out[i] = in[i] * nco_out;
-
- out_upper = (d_filter_upper->filter(&out[i]));
- out_lower = (d_filter_lower->filter(&out[i]));
- error = -real((out_upper + out_lower) * conj(out_upper - out_lower));
- d_error = avg_k*error + avg_k*d_error; // average error
+ out[i+d_filter_size-1] = in[i] * nco_out;
+
+ // Perform the dot product of the output with the filters
+ out_upper = 0;
+ out_lower = 0;
+ for(int k = 0; k < d_filter_size; k++) {
+ out_upper += d_taps_upper[k] * out[i+k];
+ out_lower += d_taps_lower[k] * out[i+k];
+ }
+ error = norm(out_lower) - norm(out_upper);
- d_freq = d_freq + d_beta * d_error;
- d_phase = d_phase + d_freq + d_alpha * d_error;
+ d_freq = d_freq + d_beta * error;
+ d_phase = d_phase + d_freq + d_alpha * error;
if(d_phase > M_PI)
d_phase -= M_TWOPI;
@@ -202,13 +338,12 @@ gr_fll_band_edge_cc::work (int noutput_items,
else if (d_freq < d_min_freq)
d_freq = d_min_freq;
- if(output_items.size() > 2) {
+ if(output_items.size() == 4) {
frq[i] = d_freq;
phs[i] = d_phase;
- err[i] = d_error;
+ err[i] = error;
}
}
-
return noutput_items;
}
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
index db060793e..dba5e6e26 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009,2011 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -29,10 +29,7 @@
class gr_fll_band_edge_cc;
typedef boost::shared_ptr<gr_fll_band_edge_cc> gr_fll_band_edge_cc_sptr;
gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta);
-
-class gr_fir_ccc;
-class gri_fft_complex;
+ int filter_size, float bandwidth);
/*!
* \class gr_fll_band_edge_cc
@@ -52,6 +49,9 @@ class gri_fft_complex;
* provides an error signal at the DC term that is directly proportional to the carrier frequency.
* We then make a second-order loop using the error signal that is the running average of e(t).
*
+ * In practice, the above equation can be simplified by just comparing the absolute value squared
+ * of the output of both filters: abs(x_l(t))^2 - abs(x_u(t))^2 = norm(x_l(t)) - norm(x_u(t)).
+ *
* In theory, the band-edge filter is the derivative of the matched filter in frequency,
* (H_be(f) = \\frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point
* of the matched filter's rolloff (if it's a raised-cosine, the derivative of a cosine is a sine).
@@ -63,6 +63,10 @@ class gri_fft_complex;
*
* Note: We use FIR filters here because the filters have to have a flat phase response over the
* entire frequency range to allow their comparisons to be valid.
+ *
+ * It is very important that the band edge filters be the derivatives of the pulse shaping filter,
+ * and that they be linear phase. Otherwise, the variance of the error will be very large.
+ *
*/
class gr_fll_band_edge_cc : public gr_sync_block
@@ -73,58 +77,199 @@ class gr_fll_band_edge_cc : public gr_sync_block
* \param samps_per_sym (float) Number of samples per symbol of signal
* \param rolloff (float) Rolloff factor of signal
* \param filter_size (int) Size (in taps) of the filter
- * \param alpha (float) Loop gain 1
- * \param beta (float) Loop gain 2
+ * \param bandwdith (float) Loop bandwidth
*/
friend gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta);
+ int filter_size, float bandwidth);
- float d_alpha;
- float d_beta;
+ float d_sps;
+ float d_rolloff;
+ int d_filter_size;
float d_max_freq;
float d_min_freq;
- gr_fir_ccc* d_filter_upper;
- gr_fir_ccc* d_filter_lower;
+ float d_loop_bw;
+ float d_damping;
+ float d_alpha;
+ float d_beta;
+
+ std::vector<gr_complex> d_taps_lower;
+ std::vector<gr_complex> d_taps_upper;
bool d_updated;
- float d_error;
+
float d_freq;
- float d_phase;
+ float d_phase;
/*!
* Build the FLL
* \param samps_per_sym (float) number of samples per symbol
* \param rolloff (float) Rolloff (excess bandwidth) of signal filter
* \param filter_size (int) number of filter taps to generate
- * \param alpha (float) Alpha gain in the control loop
- * \param beta (float) Beta gain in the control loop
+ * \param bandwidth (float) Loop bandwidth
*/
gr_fll_band_edge_cc(float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta);
-
-public:
- ~gr_fll_band_edge_cc ();
+ int filter_size, float bandwidth);
/*!
+ * \brief Update the gains, alpha and beta, of the loop filter.
+ */
+ void update_gains();
+
+ /*!
* Design the band-edge filter based on the number of samples per symbol,
* filter rolloff factor, and the filter size
+ *
* \param samps_per_sym (float) Number of samples per symbol of signal
* \param rolloff (float) Rolloff factor of signal
* \param filter_size (int) Size (in taps) of the filter
*/
void design_filter(float samps_per_sym, float rolloff, int filter_size);
+public:
+ ~gr_fll_band_edge_cc ();
+
+ /*******************************************************************
+ SET FUNCTIONS
+ *******************************************************************/
+
+ /*!
+ * \brief Set the loop bandwidth
+ *
+ * Set the loop filter's bandwidth to \p bw. This should be between
+ * 2*pi/200 and 2*pi/100 (in rads/samp). It must also be a positive
+ * number.
+ *
+ * When a new damping factor is set, the gains, alpha and beta, of the loop
+ * are recalculated by a call to update_gains().
+ *
+ * \param bw (float) new bandwidth
+ *
+ */
+ void set_loop_bandwidth(float bw);
+
+ /*!
+ * \brief Set the loop damping factor
+ *
+ * Set the loop filter's damping factor to \p df. The damping factor
+ * should be sqrt(2)/2.0 for critically damped systems.
+ * Set it to anything else only if you know what you are doing. It must
+ * be a number between 0 and 1.
+ *
+ * When a new damping factor is set, the gains, alpha and beta, of the loop
+ * are recalculated by a call to update_gains().
+ *
+ * \param df (float) new damping factor
+ *
+ */
+ void set_damping_factor(float df);
+
/*!
- * Set the alpha gainvalue
- * \param alpha (float) new gain value
+ * \brief Set the loop gain alpha
+ *
+ * Set's the loop filter's alpha gain parameter.
+ *
+ * This value should really only be set by adjusting the loop bandwidth
+ * and damping factor.
+ *
+ * \param alpha (float) new alpha gain
+ *
*/
void set_alpha(float alpha);
/*!
- * Set the beta gain value
- * \param beta (float) new gain value
+ * \brief Set the loop gain beta
+ *
+ * Set's the loop filter's beta gain parameter.
+ *
+ * This value should really only be set by adjusting the loop bandwidth
+ * and damping factor.
+ *
+ * \param beta (float) new beta gain
+ *
+ */
+ void set_beta(float beta);
+
+ /*!
+ * \brief Set the number of samples per symbol
+ *
+ * Set's the number of samples per symbol the system should use. This value
+ * is uesd to calculate the filter taps and will force a recalculation.
+ *
+ * \param sps (float) new samples per symbol
+ *
+ */
+ void set_samples_per_symbol(float sps);
+
+ /*!
+ * \brief Set the rolloff factor of the shaping filter
+ *
+ * This sets the rolloff factor that is used in the pulse shaping filter
+ * and is used to calculate the filter taps. Changing this will force a
+ * recalculation of the filter taps.
+ *
+ * This should be the same value that is used in the transmitter's pulse
+ * shaping filter. It must be between 0 and 1 and is usually between
+ * 0.2 and 0.5 (where 0.22 and 0.35 are commonly used values).
+ *
+ * \param rolloff (float) new shaping filter rolloff factor [0,1]
+ *
+ */
+ void set_rolloff(float rolloff);
+
+ /*!
+ * \brief Set the number of taps in the filter
+ *
+ * This sets the number of taps in the band-edge filters. Setting this will
+ * force a recalculation of the filter taps.
+ *
+ * This should be about the same number of taps used in the transmitter's
+ * shaping filter and also not very large. A large number of taps will
+ * result in a large delay between input and frequency estimation, and
+ * so will not be as accurate. Between 30 and 70 taps is usual.
+ *
+ * \param filter_size (float) number of taps in the filters
+ *
+ */
+ void set_filter_size(int filter_size);
+
+ /*******************************************************************
+ GET FUNCTIONS
+ *******************************************************************/
+
+ /*!
+ * \brief Returns the loop bandwidth
+ */
+ float get_loop_bandwidth();
+
+ /*!
+ * \brief Returns the loop damping factor
+ */
+ float get_damping_factor();
+
+ /*!
+ * \brief Returns the loop gain alpha
+ */
+ float get_alpha();
+
+ /*!
+ * \brief Returns the loop gain beta
+ */
+ float get_beta();
+
+ /*!
+ * \brief Returns the number of sampler per symbol used for the filter
+ */
+ float get_samples_per_symbol();
+
+ /*!
+ * \brief Returns the rolloff factor used for the filter
+ */
+ float get_rolloff();
+
+ /*!
+ * \brief Returns the number of taps of the filter
*/
- void set_beta(float beta) { d_beta = beta; }
+ int get_filter_size();
/*!
* Print the taps to screen.
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
index c9c792c8a..3617dc395 100644
--- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
+++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009,2011 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -23,19 +23,30 @@
GR_SWIG_BLOCK_MAGIC(gr,fll_band_edge_cc);
gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta);
+ int filter_size, float bandwidth);
class gr_fll_band_edge_cc : public gr_sync_block
{
private:
gr_fll_band_edge_cc (float samps_per_sym, float rolloff,
- int filter_size, float alpha, float beta);
+ int filter_size, float bandwidth);
public:
~gr_fll_band_edge_cc ();
- void set_alpha (float alpha);
- void set_beta (float beta);
- void design_filter(float samps_per_sym, float rolloff, int filter_size);
+ void set_loop_bandwidth(float bw);
+ void set_damping_factor(float df);
+ void set_alpha(float alpha);
+ void set_beta(float beta);
+ void set_samples_per_symbol(float sps);
+ void set_rolloff(float rolloff);
+ void set_filter_size(int filter_size);
+ float get_loop_bandwidth();
+ float get_damping_factor();
+ float get_alpha();
+ float get_beta();
+ float get_samples_per_symbol();
+ float get_rolloff();
+ int get_filter_size();
void print_taps();
};