Merged r4518:5130 from developer branch n4hy/ofdm into trunk, passes distcheck.
[debian/gnuradio] / gnuradio-core / src / lib / general / gr_ofdm_correlator.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006 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 2, 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_ofdm_correlator.h>
28 #include <gr_io_signature.h>
29
30 #define VERBOSE 0
31 #define M_TWOPI (2*M_PI)
32
33 gr_ofdm_correlator_sptr
34 gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int vlen, 
35                          unsigned int cplen,
36                          std::vector<gr_complex> known_symbol1, 
37                          std::vector<gr_complex> known_symbol2)
38 {
39   return gr_ofdm_correlator_sptr (new gr_ofdm_correlator (occupied_carriers, vlen, cplen,
40                                                           known_symbol1, known_symbol2));
41 }
42
43 gr_ofdm_correlator::gr_ofdm_correlator (unsigned occupied_carriers, unsigned int vlen, 
44                                         unsigned int cplen,
45                                         std::vector<gr_complex> known_symbol1, 
46                                         std::vector<gr_complex> known_symbol2)
47   : gr_block ("ofdm_correlator",
48               gr_make_io_signature (1, 1, sizeof(gr_complex)*vlen),
49               gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)),
50     d_occupied_carriers(occupied_carriers),
51     d_vlen(vlen),
52     d_cplen(cplen),
53     d_freq_shift_len(5),
54     d_known_symbol1(known_symbol1),
55     d_known_symbol2(known_symbol2),
56     d_coarse_freq(0),
57     d_phase_count(0)
58 {
59   d_diff_corr_factor.resize(d_occupied_carriers);
60   d_hestimate.resize(d_occupied_carriers);
61
62   std::vector<gr_complex>::iterator i1, i2;
63
64   int i = 0;
65   gr_complex one(1.0, 0.0);
66   for(i1 = d_known_symbol1.begin(), i2 = d_known_symbol2.begin(); i1 != d_known_symbol1.end(); i1++, i2++) {
67     d_diff_corr_factor[i] = one / ((*i1) * conj(*i2));
68     i++;
69   }
70 }
71
72 gr_ofdm_correlator::~gr_ofdm_correlator(void)
73 {
74 }
75
76 void
77 gr_ofdm_correlator::forecast (int noutput_items, gr_vector_int &ninput_items_required)
78 {
79   unsigned ninputs = ninput_items_required.size ();
80   for (unsigned i = 0; i < ninputs; i++)
81     ninput_items_required[i] = 2;
82 }
83
84 gr_complex
85 gr_ofdm_correlator::coarse_freq_comp(int freq_delta, int symbol_count)
86 {
87   return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_vlen*symbol_count),
88                     sin(-M_TWOPI*freq_delta*d_cplen/d_vlen*symbol_count));
89 }
90
91 bool
92 gr_ofdm_correlator::correlate(const gr_complex *previous, const gr_complex *current, 
93                               int zeros_on_left)
94 {
95   unsigned int i = 0;
96   int search_delta = 0;
97   bool found = false;
98
99   gr_complex h_sqrd = gr_complex(0.0,0.0);
100   float power = 0.0F;
101
102   while(!found && (abs(search_delta) < d_freq_shift_len)) {
103     h_sqrd = gr_complex(0.0,0.0);
104     power = 0.0F;
105
106     for(i = 0; i < d_occupied_carriers; i++) {
107       h_sqrd = h_sqrd + previous[i+zeros_on_left+search_delta] * 
108         conj(coarse_freq_comp(search_delta,1)*current[i+zeros_on_left+search_delta]) * 
109         d_diff_corr_factor[i];
110       power = power + norm(current[i+zeros_on_left+search_delta]); // No need to do coarse freq here
111     }
112     
113 #if VERBOSE
114       printf("bin %d\th_sqrd = ( %f, %f )\t power = %f\t real(h)/p = %f\t angle = %f\n", 
115              search_delta, h_sqrd.real(), h_sqrd.imag(), power, h_sqrd.real()/power, arg(h_sqrd)); 
116 #endif
117
118     if(h_sqrd.real() > 0.75*power) {
119       found = true;
120       d_coarse_freq = search_delta;
121       d_phase_count = 1;
122       d_snr_est = 10*log10(power/(power-h_sqrd.real()));
123
124       printf("CORR: Found, bin %d\tSNR Est %f dB\tcorr power fraction %f\n", 
125              search_delta, d_snr_est, h_sqrd.real()/power);
126       // search_delta,10*log10(h_sqrd.real()/fabs(h_sqrd.imag())),h_sqrd.real()/power);
127       break;
128     }
129     else {
130       if(search_delta <= 0)
131         search_delta = (-search_delta) + 1;
132       else
133         search_delta = -search_delta;
134     }
135   }
136   return found;
137 }
138
139 void
140 gr_ofdm_correlator::calculate_equalizer(const gr_complex *previous, const gr_complex *current, 
141                                         int zeros_on_left)
142 {
143   unsigned int i=0;
144
145   for(i = 0; i < d_occupied_carriers; i++) {
146     // FIXME possibly add small epsilon in divisor to protect from div 0
147     //d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left] +
148     //                      d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)*
149     //                                            current[i+zeros_on_left+d_coarse_freq]));
150     d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left+d_coarse_freq] +
151                              d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)*
152                                                    current[i+zeros_on_left+d_coarse_freq]));
153     
154 #if VERBOSE
155     fprintf(stderr, "%f %f ", d_hestimate[i].real(), d_hestimate[i].imag());
156 #endif
157   }
158 #if VERBOSE
159   fprintf(stderr, "\n");
160 #endif
161 }
162
163 int
164 gr_ofdm_correlator::general_work(int noutput_items,
165                                  gr_vector_int &ninput_items,
166                                  gr_vector_const_void_star &input_items,
167                                  gr_vector_void_star &output_items)
168 {
169   const gr_complex *in = (const gr_complex *)input_items[0];
170   const gr_complex *previous = &in[0];
171   const gr_complex *current = &in[d_vlen];
172
173   gr_complex *out = (gr_complex *) output_items[0];
174   
175   unsigned int i=0;
176
177   int unoccupied_carriers = d_vlen - d_occupied_carriers;
178   int zeros_on_left = (int)ceil(unoccupied_carriers/2.0);
179
180   bool corr = correlate(previous, current, zeros_on_left);
181   if(corr) {
182     calculate_equalizer(previous, current, zeros_on_left);
183   }
184
185   for(i = 0; i < d_occupied_carriers; i++) {
186     out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count)*current[i+zeros_on_left+d_coarse_freq];
187   }
188   d_phase_count++;
189   consume_each(1);
190   return 1;
191 }