Statistics
| Branch: | Tag: | Revision:

root / gnuradio-core / src / lib / general / gr_mpsk_receiver_cc.cc @ 0c659602

History | View | Annotate | Download (9.4 kB)

1
/* -*- c++ -*- */
2
/*
3
 * Copyright 2005,2006,2007 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
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <gr_io_signature.h>
28
#include <gr_prefs.h>
29
#include <gr_mpsk_receiver_cc.h>
30
#include <stdexcept>
31
#include <gr_math.h>
32
#include <gr_expj.h>
33
#include <gri_mmse_fir_interpolator_cc.h>
34
35
36
#define M_TWOPI (2*M_PI)
37
#define VERBOSE_MM     0     // Used for debugging symbol timing loop
38
#define VERBOSE_COSTAS 0     // Used for debugging phase and frequency tracking
39
40
// Public constructor
41
42
gr_mpsk_receiver_cc_sptr 
43
gr_make_mpsk_receiver_cc(unsigned int M, float theta,
44
                         float alpha, float beta,
45
                         float fmin, float fmax,
46
                         float mu, float gain_mu, 
47
                         float omega, float gain_omega, float omega_rel)
48
{
49
  return gr_mpsk_receiver_cc_sptr (new gr_mpsk_receiver_cc (M, theta, 
50
                                                            alpha, beta,
51
                                                            fmin, fmax,
52
                                                            mu, gain_mu, 
53
                                                            omega, gain_omega, omega_rel));
54
}
55
56
gr_mpsk_receiver_cc::gr_mpsk_receiver_cc (unsigned int M, float theta, 
57
                                          float alpha, float beta,
58
                                          float fmin, float fmax,
59
                                          float mu, float gain_mu, 
60
                                          float omega, float gain_omega, float omega_rel)
61
  : gr_block ("mpsk_receiver_cc",
62
              gr_make_io_signature (1, 1, sizeof (gr_complex)),
63
              gr_make_io_signature (1, 1, sizeof (gr_complex))),
64
    d_M(M), d_theta(theta), 
65
    d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0),
66
    d_current_const_point(0),
67
    d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), 
68
    d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0),
69
    d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0)
70
{
71
  d_interp = new gri_mmse_fir_interpolator_cc();
72
  d_dl_idx = 0;
73
74
  set_omega(omega);
75
76
  if (omega <= 0.0)
77
    throw std::out_of_range ("clock rate must be > 0");
78
  if (gain_mu <  0  || gain_omega < 0)
79
    throw std::out_of_range ("Gains must be non-negative");
80
  
81
  assert(d_interp->ntaps() <= DLLEN);
82
  
83
  // zero double length delay line.
84
  for (unsigned int i = 0; i < 2 * DLLEN; i++)
85
    d_dl[i] = gr_complex(0.0,0.0);
86
87
  // build the constellation vector from M
88
  make_constellation();
89
  
90
  // Select a phase detector and a decision maker for the modulation order
91
  switch(d_M) {
92
  case 2:  // optimized algorithms for BPSK
93
    d_phase_error_detector = &gr_mpsk_receiver_cc::phase_error_detector_bpsk; //bpsk;
94
    d_decision = &gr_mpsk_receiver_cc::decision_bpsk;
95
    break;
96
97
  case 4: // optimized algorithms for QPSK
98
    d_phase_error_detector = &gr_mpsk_receiver_cc::phase_error_detector_qpsk; //qpsk;
99
    d_decision = &gr_mpsk_receiver_cc::decision_qpsk;
100
    break;
101
102
  default: // generic algorithms for any M (power of 2?) but not pretty
103
    d_phase_error_detector = &gr_mpsk_receiver_cc::phase_error_detector_generic;
104
    d_decision = &gr_mpsk_receiver_cc::decision_generic;
105
    break;
106
  }
107
}
108
109
gr_mpsk_receiver_cc::~gr_mpsk_receiver_cc ()
110
{
111
  delete d_interp;
112
}
113
114
void
115
gr_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required)
116
{
117
  unsigned ninputs = ninput_items_required.size();
118
  for (unsigned i=0; i < ninputs; i++)
119
    ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps());
120
}
121
122
// FIXME add these back in an test difference in performance
123
float
124
gr_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const
125
{
126
  float phase_error = -((sample.real()>0 ? 1.0 : -1.0) * sample.imag() -
127
                        (sample.imag()>0 ? 1.0 : -1.0) * sample.real());
128
  return -phase_error;
129
}
130
131
float
132
gr_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const
133
{
134
  return -(sample.real()*sample.imag());
135
}
136
137
float gr_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const
138
{
139
  //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point]));
140
  return -arg(sample*conj(d_constellation[d_current_const_point]));
141
}
142
143
unsigned int
144
gr_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const
145
{
146
  return (gr_branchless_binary_slicer(sample.real()) ^ 1);
147
  //return gr_binary_slicer(sample.real()) ^ 1;
148
}
149
150
unsigned int
151
gr_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const
152
{
153
  unsigned int index;
154
155
  //index = gr_branchless_quad_0deg_slicer(sample);
156
  index = gr_quad_0deg_slicer(sample);
157
  return index;
158
}
159
160
unsigned int
161
gr_mpsk_receiver_cc::decision_generic(gr_complex sample) const
162
{
163
  unsigned int min_m = 0;
164
  float min_s = 65535;
165
  
166
  // Develop all possible constellation points and find the one that minimizes
167
  // the Euclidean distance (error) with the sample
168
  for(unsigned int m=0; m < d_M; m++) {
169
    gr_complex diff = norm(d_constellation[m] - sample);
170
    
171
    if(fabs(diff.real()) < min_s) {
172
      min_s = fabs(diff.real());
173
      min_m = m;
174
    }
175
  }
176
  // Return the index of the constellation point that minimizes the error
177
  return min_m;
178
}
179
180
181
void
182
gr_mpsk_receiver_cc::make_constellation()
183
{
184
  for(unsigned int m=0; m < d_M; m++) {
185
    d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m));
186
  }
187
}
188
189
void
190
gr_mpsk_receiver_cc::mm_sampler(const gr_complex symbol)
191
{
192
  gr_complex sample, nco;
193
194
  d_mu--;             // skip a number of symbols between sampling
195
  d_phase += d_freq;  // increment the phase based on the frequency of the rotation
196
197
  // Keep phase clamped and not walk to infinity
198
  while(d_phase > M_TWOPI)
199
    d_phase -= M_TWOPI;
200
  while(d_phase < -M_TWOPI)
201
    d_phase += M_TWOPI;
202
  
203
  nco = gr_expj(d_phase+d_theta);   // get the NCO value for derotating the current sample
204
  sample = nco*symbol;      // get the downconverted symbol
205
  
206
  // Fill up the delay line for the interpolator
207
  d_dl[d_dl_idx] = sample;
208
  d_dl[(d_dl_idx + DLLEN)] = sample;  // put this in the second half of the buffer for overflows
209
  d_dl_idx = (d_dl_idx+1) % DLLEN;    // Keep the delay line index in bounds
210
}
211
212
void
213
gr_mpsk_receiver_cc::mm_error_tracking(gr_complex sample)
214
{
215
  gr_complex u, x, y;
216
  float mm_error = 0;
217
218
  // Make sample timing corrections
219
220
  // set the delayed samples
221
  d_p_2T = d_p_1T;
222
  d_p_1T = d_p_0T;
223
  d_p_0T = sample;
224
  d_c_2T = d_c_1T;
225
  d_c_1T = d_c_0T;
226
  
227
  d_current_const_point = (*this.*d_decision)(d_p_0T);  // make a decision on the sample value
228
  d_c_0T = d_constellation[d_current_const_point];
229
  
230
  x = (d_c_0T - d_c_2T) * conj(d_p_1T);
231
  y = (d_p_0T - d_p_2T) * conj(d_c_1T);
232
  u = y - x;
233
  mm_error = u.real();   // the error signal is in the real part
234
  mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val
235
    
236
  d_omega = d_omega + d_gain_omega * mm_error;  // update omega based on loop error
237
  d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel);   // make sure we don't walk away
238
  
239
  d_mu += d_omega + d_gain_mu * mm_error;   // update mu based on loop error
240
  
241
#if VERBOSE_MM
242
  printf("mm: mu: %f   omega: %f  mm_error: %f  sample: %f+j%f  constellation: %f+j%f\n", 
243
         d_mu, d_omega, mm_error, sample.real(), sample.imag(), 
244
         d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag());
245
#endif
246
}
247
248
249
void
250
gr_mpsk_receiver_cc::phase_error_tracking(gr_complex sample)
251
{
252
  float phase_error = 0;
253
254
  // Make phase and frequency corrections based on sampled value
255
  phase_error = (*this.*d_phase_error_detector)(sample);
256
257
  phase_error = gr_branchless_clip(phase_error, 1.0);
258
    
259
  d_freq += d_beta*phase_error;             // adjust frequency based on error
260
  d_phase += d_freq + d_alpha*phase_error;  // adjust phase based on error
261
262
  // Make sure we stay within +-2pi
263
  while(d_phase > M_TWOPI)
264
    d_phase -= M_TWOPI;
265
  while(d_phase < -M_TWOPI)
266
    d_phase += M_TWOPI;
267
  
268
  // Limit the frequency range
269
  d_freq = gr_branchless_clip(d_freq, d_max_freq);
270
  
271
#if VERBOSE_COSTAS
272
  printf("cl: phase_error: %f  phase: %f  freq: %f  sample: %f+j%f  constellation: %f+j%f\n",
273
         phase_error, d_phase, d_freq, sample.real(), sample.imag(), 
274
         d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag());
275
#endif
276
}
277
278
int
279
gr_mpsk_receiver_cc::general_work (int noutput_items,
280
                                   gr_vector_int &ninput_items,
281
                                   gr_vector_const_void_star &input_items,
282
                                   gr_vector_void_star &output_items)
283
{
284
  const gr_complex *in = (const gr_complex *) input_items[0];
285
  gr_complex *out = (gr_complex *) output_items[0];
286
287
  int i=0, o=0;
288
289
  while((o < noutput_items) && (i < ninput_items[0])) {
290
    while((d_mu > 1) && (i < ninput_items[0]))  {
291
      mm_sampler(in[i]);   // puts symbols into a buffer and adjusts d_mu
292
      i++;
293
    }
294
    
295
    if(i < ninput_items[0]) {
296
      gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu);
297
       
298
      mm_error_tracking(interp_sample);     // corrects M&M sample time
299
      phase_error_tracking(interp_sample);  // corrects phase and frequency offsets
300
301
      out[o++] = interp_sample;
302
    }
303
  }
304
305
  #if 0
306
  printf("ninput_items: %d   noutput_items: %d   consuming: %d   returning: %d\n",
307
         ninput_items[0], noutput_items, i, o);
308
  #endif
309
310
  consume_each(i);
311
  return o;
312
}
313