Updated license from GPL version 2 or later to GPL version 3 or later.
[debian/gnuradio] / gnuradio-core / src / lib / general / gr_mpsk_receiver_cc.cc
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_generic; //bpsk;
94     d_decision = &gr_mpsk_receiver_cc::decision_generic; //bpsk;
95     break;
96
97   case 4: // optimized algorithms for QPSK
98     d_phase_error_detector = &gr_mpsk_receiver_cc::phase_error_detector_generic; //qpsk;
99     d_decision = &gr_mpsk_receiver_cc::decision_generic; //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   set_history(3);                       // ensure 2 extra input sample is available
109 }
110
111 gr_mpsk_receiver_cc::~gr_mpsk_receiver_cc ()
112 {
113   delete d_interp;
114 }
115
116 void
117 gr_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required)
118 {
119   unsigned ninputs = ninput_items_required.size();
120   for (unsigned i=0; i < ninputs; i++)
121     ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps());
122   //ninput_items_required[i] = (int)(d_omega);
123
124 }
125
126 // FIXME add these back in an test difference in performance
127 float
128 gr_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const
129 {
130   float phase_error = ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() -
131                        (sample.imag()>0 ? 1.0 : -1.0) * sample.real());
132   return -phase_error;
133 }
134
135 // FIXME add these back in an test difference in performance
136 float
137 gr_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const
138 {
139   return (sample.real()*sample.imag());
140 }
141
142 float gr_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const
143 {
144   //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point]));
145   return -arg(sample*conj(d_constellation[d_current_const_point]));
146 }
147
148 // FIXME add these back in an test difference in performance
149 unsigned int
150 gr_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const
151 {
152   unsigned int index = 0;
153
154   // Implements a 1-demensional slicer
155   if(sample.real() > 0)
156     index = 1;
157   return index;
158 }
159
160 // FIXME add these back in an test difference in performance
161 unsigned int
162 gr_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const
163 {
164   unsigned int index = 0;
165
166   // Implements a simple slicer function
167   if((sample.real() < 0) && (sample.imag() > 0))
168     index = 1;
169   else if((sample.real() < 0) && (sample.imag() < 0))
170     index = 2;
171   else
172     index = 3;
173   return index;
174 }
175
176 unsigned int
177 gr_mpsk_receiver_cc::decision_generic(gr_complex sample) const
178 {
179   unsigned int min_m = 0;
180   float min_s = 65535;
181   
182   // Develop all possible constellation points and find the one that minimizes
183   // the Euclidean distance (error) with the sample
184   for(unsigned int m=0; m < d_M; m++) {
185     gr_complex diff = norm(d_constellation[m] - sample);
186     
187     if(fabs(diff.real()) < min_s) {
188       min_s = fabs(diff.real());
189       min_m = m;
190     }
191   }
192   // Return the index of the constellation point that minimizes the error
193   return min_m;
194 }
195
196
197 void
198 gr_mpsk_receiver_cc::make_constellation()
199 {
200   for(unsigned int m=0; m < d_M; m++) {
201     d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m));
202   }
203 }
204
205 void
206 gr_mpsk_receiver_cc::mm_sampler(const gr_complex symbol)
207 {
208   gr_complex sample, nco;
209
210   d_mu--;             // skip a number of symbols between sampling
211   d_phase += d_freq;  // increment the phase based on the frequency of the rotation
212
213   // Keep phase clamped and not walk to infinity
214   while(d_phase>M_TWOPI)
215     d_phase -= M_TWOPI;
216   while(d_phase<-M_TWOPI)
217     d_phase += M_TWOPI;
218
219   nco = gr_expj(d_phase+d_theta);   // get the NCO value for derotating the current sample
220   sample = nco*symbol;      // get the downconverted symbol
221   
222   // Fill up the delay line for the interpolator
223   d_dl[d_dl_idx] = sample;
224   d_dl[(d_dl_idx + DLLEN)] = sample;  // put this in the second half of the buffer for overflows
225   d_dl_idx = (d_dl_idx+1) % DLLEN;    // Keep the delay line index in bounds
226 }
227
228 void
229 gr_mpsk_receiver_cc::mm_error_tracking(gr_complex sample)
230 {
231   gr_complex u, x, y;
232   float mm_error = 0;
233
234   // Make sample timing corrections
235
236   // set the delayed samples
237   d_p_2T = d_p_1T;
238   d_p_1T = d_p_0T;
239   d_p_0T = sample;
240   d_c_2T = d_c_1T;
241   d_c_1T = d_c_0T;
242   
243   d_current_const_point = (*this.*d_decision)(d_p_0T);  // make a decision on the sample value
244   d_c_0T = d_constellation[d_current_const_point];
245   
246   x = (d_c_0T - d_c_2T) * conj(d_p_1T);
247   y = (d_p_0T - d_p_2T) * conj(d_c_1T);
248   u = y - x;
249   mm_error = u.real();   // the error signal is in the real part
250   
251   // limit mm_val
252   if (mm_error > 1.0)
253     mm_error = 1.0;
254   else if (mm_error < -1.0)
255     mm_error = -1.0;
256   
257   d_omega = d_omega + d_gain_omega * mm_error;  // update omega based on loop error
258
259   // make sure we don't walk away
260   if (d_omega > d_max_omega)
261     d_omega = d_max_omega;
262   else if (d_omega < d_min_omega)
263     d_omega = d_min_omega;
264   
265   d_mu += d_omega + d_gain_mu * mm_error;   // update mu based on loop error
266   
267 #if VERBOSE_MM
268   printf("mm: mu: %f   omega: %f  mm_error: %f  sample: %f+j%f  constellation: %f+j%f\n", 
269          d_mu, d_omega, mm_error, sample.real(), sample.imag(), 
270          d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag());
271 #endif
272 }
273
274
275 void
276 gr_mpsk_receiver_cc::phase_error_tracking(gr_complex sample)
277 {
278   float phase_error = 0;
279
280   // Make phase and frequency corrections based on sampled value
281   phase_error = (*this.*d_phase_error_detector)(sample);
282
283   if (phase_error > 1)
284     phase_error = 1;
285   else if (phase_error < -1)
286     phase_error = -1;  
287
288   d_freq += d_beta*phase_error;             // adjust frequency based on error
289   d_phase += d_freq + d_alpha*phase_error;  // adjust phase based on error
290   
291   // Make sure we stay within +-2pi
292   while(d_phase>M_TWOPI)
293     d_phase -= M_TWOPI;
294   while(d_phase<-M_TWOPI)
295     d_phase += M_TWOPI;
296   
297   // Limit the frequency range
298   if (d_freq > d_max_freq)
299     d_freq = d_max_freq;
300   else if (d_freq < d_min_freq)
301     d_freq = d_min_freq;
302
303 #if VERBOSE_COSTAS
304   printf("cl: phase_error: %f  phase: %f  freq: %f  sample: %f+j%f  constellation: %f+j%f\n",
305          phase_error, d_phase, d_freq, sample.real(), sample.imag(), 
306          d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag());
307 #endif
308 }
309
310 int
311 gr_mpsk_receiver_cc::general_work (int noutput_items,
312                                    gr_vector_int &ninput_items,
313                                    gr_vector_const_void_star &input_items,
314                                    gr_vector_void_star &output_items)
315 {
316   const gr_complex *in = (const gr_complex *) input_items[0];
317   gr_complex *out = (gr_complex *) output_items[0];
318
319   int i=0, o=0;
320
321   //while(i < ninput_items[0]) {    
322   while((o < noutput_items) && (i < ninput_items[0])) {
323     while((d_mu > 1) && (i < ninput_items[0]))  {
324       mm_sampler(in[i]);   // puts symbols into a buffer and adjusts d_mu
325       i++;
326     }
327     
328     if(i < ninput_items[0]) {
329       gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu);
330        
331       mm_error_tracking(interp_sample);     // corrects M&M sample time
332       phase_error_tracking(interp_sample);  // corrects phase and frequency offsets
333
334       out[o++] = interp_sample;
335     }
336   }
337
338   #if 0
339   printf("ninput_items: %d   noutput_items: %d   consuming: %d   returning: %d\n",
340          ninput_items[0], noutput_items, i, o);
341   #endif
342
343   consume_each(i);
344   return o;
345 }