e396eeb8e68a13c855c06cbcca597baf49747e6a
[debian/gnuradio] / gnuradio-core / src / lib / general / gr_ofdm_correlator.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 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_ofdm_correlator.h>
28 #include <gr_io_signature.h>
29 #include <gr_expj.h>
30
31 #define VERBOSE 0
32 #define M_TWOPI (2*M_PI)
33 #define MAX_NUM_SYMBOLS 1000
34
35 gr_ofdm_correlator_sptr
36 gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length, 
37                          unsigned int cplen,
38                          const std::vector<gr_complex> &known_symbol1, 
39                          const std::vector<gr_complex> &known_symbol2,
40                          unsigned int max_fft_shift_len)
41 {
42   return gr_ofdm_correlator_sptr (new gr_ofdm_correlator (occupied_carriers, fft_length, cplen,
43                                                           known_symbol1, known_symbol2,
44                                                           max_fft_shift_len));
45 }
46
47 gr_ofdm_correlator::gr_ofdm_correlator (unsigned occupied_carriers, unsigned int fft_length, 
48                                         unsigned int cplen,
49                                         const std::vector<gr_complex> &known_symbol1, 
50                                         const std::vector<gr_complex> &known_symbol2,
51                                         unsigned int max_fft_shift_len)
52   : gr_block ("ofdm_correlator",
53               gr_make_io_signature (1, 1, sizeof(gr_complex)*fft_length),
54               gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))),
55     d_occupied_carriers(occupied_carriers),
56     d_fft_length(fft_length),
57     d_cplen(cplen),
58     d_freq_shift_len(max_fft_shift_len),
59     d_known_symbol1(known_symbol1),
60     d_known_symbol2(known_symbol2),
61     d_coarse_freq(0),
62     d_phase_count(0)
63 {
64   d_diff_corr_factor.resize(d_occupied_carriers);
65   d_hestimate.resize(d_occupied_carriers);
66
67   std::vector<gr_complex>::iterator i1, i2;
68
69   unsigned int i = 0, j = 0;
70   gr_complex one(1.0, 0.0);
71   for(i1 = d_known_symbol1.begin(), i2 = d_known_symbol2.begin(); i1 != d_known_symbol1.end(); i1++, i2++) {
72     d_diff_corr_factor[i] = one / ((*i1) * conj(*i2));
73     i++;
74   }
75   
76   d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS];
77   for(i = 0; i <= 2*d_freq_shift_len; i++) {
78     for(j = 0; j < MAX_NUM_SYMBOLS; j++) {
79       d_phase_lut[j + i*MAX_NUM_SYMBOLS] =  gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j);
80     }
81   }
82 }
83
84 gr_ofdm_correlator::~gr_ofdm_correlator(void)
85 {
86   delete [] d_phase_lut;
87 }
88
89 void
90 gr_ofdm_correlator::forecast (int noutput_items, gr_vector_int &ninput_items_required)
91 {
92   unsigned ninputs = ninput_items_required.size ();
93   for (unsigned i = 0; i < ninputs; i++)
94     ninput_items_required[i] = 2;
95 }
96
97 gr_complex
98 gr_ofdm_correlator::coarse_freq_comp(int freq_delta, int symbol_count)
99 {
100   //  return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count),
101   //        sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count));
102
103   return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count);
104
105   //assert(d_freq_shift_len + freq_delta >= 0);
106   //assert(symbol_count <= MAX_NUM_SYMBOLS);
107
108   //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count];
109 }
110
111 bool
112 gr_ofdm_correlator::correlate(const gr_complex *previous, const gr_complex *current, 
113                               int zeros_on_left)
114 {
115   unsigned int i = 0;
116   int search_delta = 0;
117   bool found = false;
118
119   gr_complex h_sqrd = gr_complex(0.0,0.0);
120   float power = 0.0F;
121
122   while(!found && ((unsigned)abs(search_delta) <= d_freq_shift_len)) {
123     h_sqrd = gr_complex(0.0,0.0);
124     power = 0.0F;
125
126     for(i = 0; i < d_occupied_carriers; i++) {
127       h_sqrd = h_sqrd + previous[i+zeros_on_left+search_delta] * 
128         conj(coarse_freq_comp(search_delta,1)*current[i+zeros_on_left+search_delta]) * 
129         d_diff_corr_factor[i];
130       
131       power = power + norm(current[i+zeros_on_left+search_delta]); // No need to do coarse freq here
132     }
133     
134 #if VERBOSE
135     printf("bin %d\th_sqrd = ( %f, %f )\t power = %f\t real(h)/p = %f\t angle = %f\n", 
136            search_delta, h_sqrd.real(), h_sqrd.imag(), power, h_sqrd.real()/power, arg(h_sqrd)); 
137 #endif      
138     // FIXME: Look at h_sqrd.read() > power
139     if((h_sqrd.real() > 0.82*power) && (h_sqrd.real() < 1.1 * power)) {
140       found = true;
141       //printf("search delta: %d\n", search_delta);
142       d_coarse_freq = search_delta;
143       d_phase_count = 1;
144       //d_snr_est = 10*log10(power/(power-h_sqrd.real()));
145
146       // check for low noise power; sets maximum SNR at 100 dB
147       if(fabs(h_sqrd.imag()) <= 1e-12) {
148         d_snr_est = 100.0;
149       }
150       else {
151         d_snr_est = 10*log10(fabs(h_sqrd.real()/h_sqrd.imag()));
152       }
153
154 #if VERBOSE
155       printf("CORR: Found, bin %d\tSNR Est %f dB\tcorr power fraction %f\n", 
156              search_delta, d_snr_est, h_sqrd.real()/power);
157 #endif
158
159       // search_delta,10*log10(h_sqrd.real()/fabs(h_sqrd.imag())),h_sqrd.real()/power);
160       break;
161     }
162     else {
163       if(search_delta <= 0)
164         search_delta = (-search_delta) + 2;
165       else
166         search_delta = -search_delta;
167     }
168   }
169   return found;
170 }
171
172 void
173 gr_ofdm_correlator::calculate_equalizer(const gr_complex *previous, const gr_complex *current, 
174                                         int zeros_on_left)
175 {
176   unsigned int i=0;
177
178   for(i = 0; i < d_occupied_carriers; i++) {
179     // FIXME possibly add small epsilon in divisor to protect from div 0
180     //d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left] +
181     //                      d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)*
182     //                                            current[i+zeros_on_left+d_coarse_freq]));
183     d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left+d_coarse_freq] +
184                              d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)*
185                                                    current[i+zeros_on_left+d_coarse_freq]));
186   }
187 #if VERBOSE
188   fprintf(stderr, "\n");
189 #endif
190 }
191
192 int
193 gr_ofdm_correlator::general_work(int noutput_items,
194                                  gr_vector_int &ninput_items,
195                                  gr_vector_const_void_star &input_items,
196                                  gr_vector_void_star &output_items)
197 {
198   const gr_complex *in = (const gr_complex *)input_items[0];
199   const gr_complex *previous = &in[0];
200   const gr_complex *current = &in[d_fft_length];
201
202   gr_complex *out = (gr_complex *) output_items[0];
203   char *sig = (char *) output_items[1];
204   
205   unsigned int i=0;
206
207   int unoccupied_carriers = d_fft_length - d_occupied_carriers;
208   int zeros_on_left = (int)ceil(unoccupied_carriers/2.0);
209
210   bool corr = correlate(previous, current, zeros_on_left);
211   if(corr) {
212     calculate_equalizer(previous, current, zeros_on_left);
213     sig[0] = 1;
214   }
215   else {
216     sig[0] = 0;
217   }
218
219   for(i = 0; i < d_occupied_carriers; i++) {
220     out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count)*current[i+zeros_on_left+d_coarse_freq];
221   }
222   
223
224   d_phase_count++;
225   if(d_phase_count == MAX_NUM_SYMBOLS) {
226     d_phase_count = 1;
227   }
228
229   consume_each(1);
230   return 1;
231 }