Statistics
| Branch: | Tag: | Revision:

root / gnuradio-core / src / lib / filter / gr_pfb_clock_sync_ccf.h @ d702e27d

History | View | Annotate | Download (8.8 kB)

1
/* -*- c++ -*- */
2
/*
3
 * Copyright 2009,2010 Free Software Foundation, Inc.
4
 * 
5
 * This file is part of GNU Radio
6
 * 
7
 * GNU Radio is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3, or (at your option)
10
 * any later version.
11
 * 
12
 * GNU Radio is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 * 
17
 * You should have received a copy of the GNU General Public License
18
 * along with GNU Radio; see the file COPYING.  If not, write to
19
 * the Free Software Foundation, Inc., 51 Franklin Street,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
24
#ifndef INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H
25
#define        INCLUDED_GR_PFB_CLOCK_SYNC_CCF_H
26
27
#include <gr_block.h>
28
29
class gr_pfb_clock_sync_ccf;
30
typedef boost::shared_ptr<gr_pfb_clock_sync_ccf> gr_pfb_clock_sync_ccf_sptr;
31
gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain,
32
                                                       const std::vector<float> &taps,
33
                                                       unsigned int filter_size=32,
34
                                                       float init_phase=0,
35
                                                       float max_rate_deviation=1.5);
36
37
class gr_fir_ccf;
38
39
/*!
40
 * \class gr_pfb_clock_sync_ccf
41
 *
42
 * \brief Timing synchronizer using polyphase filterbanks
43
 *
44
 * \ingroup filter_blk
45
 * 
46
 * This block performs timing synchronization for PAM signals by minimizing the
47
 * derivative of the filtered signal, which in turn maximizes the SNR and 
48
 * minimizes ISI.
49
 *
50
 * This approach works by setting up two filterbanks; one filterbanke contains the 
51
 * signal's pulse shaping matched filter (such as a root raised cosine filter),
52
 * where each branch of the filterbank contains a different phase of the filter.
53
 * The second filterbank contains the derivatives of the filters in the first 
54
 * filterbank. Thinking of this in the time domain, the first filterbank contains
55
 * filters that have a sinc shape to them. We want to align the output signal to
56
 * be sampled at exactly the peak of the sinc shape. The derivative of the sinc
57
 * contains a zero at the maximum point of the sinc (sinc(0) = 1, sinc(0)' = 0).
58
 * Furthermore, the region around the zero point is relatively linear. We make
59
 * use of this fact to generate the error signal.
60
 *
61
 * If the signal out of the derivative filters is d_i[n] for the ith filter, and
62
 * the output of the matched filter is x_i[n], we calculate the error as:
63
 *    e[n] = (Re{x_i[n]} * Re{d_i[n]} + Im{x_i[n]} * Im{d_i[n]}) / 2.0
64
 * This equation averages the error in the real and imaginary parts. There are two
65
 * reasons we multiply by the signal itself. First, if the symbol could be positive
66
 * or negative going, but we want the error term to always tell us to go in the 
67
 * same direction depending on which side of the zero point we are on. The sign of
68
 * x_i[n] adjusts the error term to do this. Second, the magnitude of x_i[n] scales
69
 * the error term depending on the symbol's amplitude, so larger signals give us
70
 * a stronger error term because we have more confidence in that symbol's value.
71
 * Using the magnitude of x_i[n] instead of just the sign is especially good for
72
 * signals with low SNR.
73
 *
74
 * The error signal, e[n], gives us a value proportional to how far away from the zero
75
 * point we are in the derivative signal. We want to drive this value to zero, so we
76
 * set up a second order loop. We have two variables for this loop; d_k is the filter
77
 * number in the filterbank we are on and d_rate is the rate which we travel through
78
 * the filters in the steady state. That is, due to the natural clock differences between
79
 * the transmitter and receiver, d_rate represents that difference and would traverse
80
 * the filter phase paths to keep the receiver locked. Thinking of this as a second-order
81
 * PLL, the d_rate is the frequency and d_k is the phase. So we update d_rate and d_k
82
 * using the standard loop equations based on two error signals, d_alpha and d_beta.
83
 * We have these two values set based on each other for a critically damped system, so in
84
 * the block constructor, we just ask for "gain," which is d_alpha while d_beta is
85
 * equal to (gain^2)/4.
86
 *
87
 * The clock sync block needs to know the number of samples per second (sps), because it
88
 * only returns a single point representing the sample. The sps can be any positive real
89
 * number and does not need to be an integer. The filter taps must also be specified. The
90
 * taps are generated by first conceiving of the prototype filter that would be the signal's
91
 * matched filter. Then interpolate this by the number of filters in the filterbank. These
92
 * are then distributed among all of the filters. So if the prototype filter was to have
93
 * 45 taps in it, then each path of the filterbank will also have 45 taps. This is easily
94
 * done by building the filter with the sample rate multiplied by the number of filters
95
 * to use.
96
 *
97
 * The number of filters can also be set and defaults to 32. With 32 filters, you get a
98
 * good enough resolution in the phase to produce very small, almost unnoticeable, ISI.
99
 * Going to 64 filters can reduce this more, but after that there is very little gained
100
 * for the extra complexity.
101
 *
102
 * The initial phase is another settable parameter and refers to the filter path the
103
 * algorithm initially looks at (i.e., d_k starts at init_phase). This value defaults 
104
 * to zero, but it might be useful to start at a different phase offset, such as the mid-
105
 * point of the filters.
106
 *
107
 * The final parameter is the max_rate_devitation, which defaults to 1.5. This is how far
108
 * we allow d_rate to swing, positive or negative, from 0. Constraining the rate can help
109
 * keep the algorithm from walking too far away to lock during times when there is no signal.
110
 *
111
 */
112
113
class gr_pfb_clock_sync_ccf : public gr_block
114
{
115
 private:
116
  /*!
117
   * Build the polyphase filterbank timing synchronizer.
118
   * \param sps (double) The number of samples per second in the incoming signal
119
   * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default.
120
   * \param taps (vector<int>) The filter taps.
121
   * \param filter_size (uint) The number of filters in the filterbank (default = 32).
122
   * \param init_phase (float) The initial phase to look at, or which filter to start 
123
   *                           with (default = 0).
124
   * \param max_rate_deviation (float) Distance from 0 d_rate can get (default = 1.5).
125
   *
126
   */
127
  friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain,
128
                                                                const std::vector<float> &taps,
129
                                                                unsigned int filter_size,
130
                                                                float init_phase,
131
                                                                float max_rate_deviation);
132
133
  bool                           d_updated;
134
  double                   d_sps;
135
  double                   d_sample_num;
136
  float                    d_alpha;
137
  float                    d_beta;
138
  int                      d_nfilters;
139
  std::vector<gr_fir_ccf*> d_filters;
140
  std::vector<gr_fir_ccf*> d_diff_filters;
141
  std::vector< std::vector<float> > d_taps;
142
  std::vector< std::vector<float> > d_dtaps;
143
  float                    d_k;
144
  float                    d_rate;
145
  float                    d_rate_i;
146
  float                    d_rate_f;
147
  float                    d_max_dev;
148
  int                      d_filtnum;
149
  int                      d_taps_per_filter;
150
151
  /*!
152
   * Build the polyphase filterbank timing synchronizer.
153
   */
154
  gr_pfb_clock_sync_ccf (double sps, float gain,
155
                         const std::vector<float> &taps,
156
                         unsigned int filter_size,
157
                         float init_phase,
158
                         float max_rate_deviation);
159
  
160
  void create_diff_taps(const std::vector<float> &newtaps,
161
                        std::vector<float> &difftaps);
162
163
public:
164
  ~gr_pfb_clock_sync_ccf ();
165
  
166
  /*!
167
   * Resets the filterbank's filter taps with the new prototype filter
168
   */
169
  void set_taps (const std::vector<float> &taps,
170
                 std::vector< std::vector<float> > &ourtaps,
171
                 std::vector<gr_fir_ccf*> &ourfilter);
172
173
  /*!
174
   * Returns the taps of the matched filter
175
   */
176
  std::vector<float> channel_taps(int channel);
177
178
  /*!
179
   * Returns the taps in the derivative filter
180
   */
181
  std::vector<float> diff_channel_taps(int channel);
182
183
  /*!
184
   * Print all of the filterbank taps to screen.
185
   */
186
  void print_taps();
187
188
  /*!
189
   * Print all of the filterbank taps of the derivative filter to screen.
190
   */
191
  void print_diff_taps();
192
193
  /*!
194
   * Set the gain value alpha for the control loop
195
   */  
196
  void set_alpha(float alpha)
197
  {
198
    d_alpha = alpha;
199
  }
200
201
  /*!
202
   * Set the gain value beta for the control loop
203
   */  
204
  void set_beta(float beta)
205
  {
206
    d_beta = beta;
207
  }
208
209
  /*!
210
   * Set the maximum deviation from 0 d_rate can have
211
   */  
212
  void set_max_rate_deviation(float m)
213
  {
214
    d_max_dev = m;
215
  }
216
  
217
  bool check_topology(int ninputs, int noutputs);
218
219
  int general_work (int noutput_items,
220
                    gr_vector_int &ninput_items,
221
                    gr_vector_const_void_star &input_items,
222
                    gr_vector_void_star &output_items);
223
};
224
225
#endif