1 /* -*- c++ -*- */
2 /*
3  * Copyright 2015,2016,2018,2019 Free Software Foundation, Inc.
4  *
5  * This is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3, or (at your option)
8  * any later version.
9  *
10  * This software is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this software; see the file COPYING.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "dvbt_reference_signals_impl.h"
26 #include <gnuradio/expj.h>
27 #include <gnuradio/io_signature.h>
28 #include <gnuradio/math.h>
29 #include <algorithm>
30 #include <complex>
31 
32 namespace gr {
33 namespace dtv {
34 
35 // Number of symbols in a frame
36 const int dvbt_pilot_gen::d_symbols_per_frame = SYMBOLS_PER_FRAME;
37 // Number of frames in a superframe
38 const int dvbt_pilot_gen::d_frames_per_superframe = FRAMES_PER_SUPERFRAME;
39 
40 // 2k mode
41 // Scattered pilots # of carriers
42 const int dvbt_pilot_gen::d_spilot_carriers_size_2k = SCATTERED_PILOT_SIZE_2k;
43 // Continual pilots # of carriers and positions
44 const int dvbt_pilot_gen::d_cpilot_carriers_size_2k = CONTINUAL_PILOT_SIZE_2k;
45 const int
46     dvbt_pilot_gen::d_cpilot_carriers_2k[dvbt_pilot_gen::d_cpilot_carriers_size_2k] = {
47         0,    48,   54,   87,   141,  156,  192,  201,  255,  279,  282,  333,
48         432,  450,  483,  525,  531,  618,  636,  714,  759,  765,  780,  804,
49         873,  888,  918,  939,  942,  969,  984,  1050, 1101, 1107, 1110, 1137,
50         1140, 1146, 1206, 1269, 1323, 1377, 1491, 1683, 1704
51     };
52 // TPS pilots # of carriers and positions
53 const int dvbt_pilot_gen::d_tps_carriers_size_2k = TPS_PILOT_SIZE_2k;
54 const int dvbt_pilot_gen::d_tps_carriers_2k[dvbt_pilot_gen::d_tps_carriers_size_2k] = {
55     34,  50,   209,  346,  413,  569,  595,  688, 790,
56     901, 1073, 1219, 1262, 1286, 1469, 1594, 1687
57 };
58 
59 // 8k mode
60 // Scattered pilots # of carriers
61 const int dvbt_pilot_gen::d_spilot_carriers_size_8k = SCATTERED_PILOT_SIZE_8k;
62 // Continual pilots # of carriers and positions
63 const int dvbt_pilot_gen::d_cpilot_carriers_size_8k = CONTINUAL_PILOT_SIZE_8k;
64 const int
65     dvbt_pilot_gen::d_cpilot_carriers_8k[dvbt_pilot_gen::d_cpilot_carriers_size_8k] = {
66         0,    48,   54,   87,   141,  156,  192,  201,  255,  279,  282,  333,  432,
67         450,  483,  525,  531,  618,  636,  714,  759,  765,  780,  804,  873,  888,
68         918,  939,  942,  969,  984,  1050, 1101, 1107, 1110, 1137, 1140, 1146, 1206,
69         1269, 1323, 1377, 1491, 1683, 1704, 1752, 1758, 1791, 1845, 1860, 1896, 1905,
70         1959, 1983, 1986, 2037, 2136, 2154, 2187, 2229, 2235, 2322, 2340, 2418, 2463,
71         2469, 2484, 2508, 2577, 2592, 2622, 2643, 2646, 2673, 2688, 2754, 2805, 2811,
72         2814, 2841, 2844, 2850, 2910, 2973, 3027, 3081, 3195, 3387, 3408, 3456, 3462,
73         3495, 3549, 3564, 3600, 3609, 3663, 3687, 3690, 3741, 3840, 3858, 3891, 3933,
74         3939, 4026, 4044, 4122, 4167, 4173, 4188, 4212, 4281, 4296, 4326, 4347, 4350,
75         4377, 4392, 4458, 4509, 4515, 4518, 4545, 4548, 4554, 4614, 4677, 4731, 4785,
76         4899, 5091, 5112, 5160, 5166, 5199, 5253, 5268, 5304, 5313, 5367, 5391, 5394,
77         5445, 5544, 5562, 5595, 5637, 5643, 5730, 5748, 5826, 5871, 5877, 5892, 5916,
78         5985, 6000, 6030, 6051, 6054, 6081, 6096, 6162, 6213, 6219, 6222, 6249, 6252,
79         6258, 6318, 6381, 6435, 6489, 6603, 6795, 6816
80     };
81 // TPS pilots # of carriers and positions
82 const int dvbt_pilot_gen::d_tps_carriers_size_8k = TPS_PILOT_SIZE_8k;
83 const int dvbt_pilot_gen::d_tps_carriers_8k[dvbt_pilot_gen::d_tps_carriers_size_8k] = {
84     34,   50,   209,  346,  413,  569,  595,  688,  790,  901,  1073, 1219, 1262, 1286,
85     1469, 1594, 1687, 1738, 1754, 1913, 2050, 2117, 2273, 2299, 2392, 2494, 2605, 2777,
86     2923, 2966, 2990, 3173, 3298, 3391, 3442, 3458, 3617, 3754, 3821, 3977, 4003, 4096,
87     4198, 4309, 4481, 4627, 4670, 4694, 4877, 5002, 5095, 5146, 5162, 5321, 5458, 5525,
88     5681, 5707, 5800, 5902, 6013, 6185, 6331, 6374, 6398, 6581, 6706, 6799
89 };
90 
91 // TPS sync sequence for odd and even frames
92 const int dvbt_pilot_gen::d_tps_sync_size = 16; // TODO
93 const int dvbt_pilot_gen::d_tps_sync_even[d_tps_sync_size] = { 0, 0, 1, 1, 0, 1, 0, 1,
94                                                                1, 1, 1, 0, 1, 1, 1, 0 };
95 const int dvbt_pilot_gen::d_tps_sync_odd[d_tps_sync_size] = { 1, 1, 0, 0, 1, 0, 1, 0,
96                                                               0, 0, 0, 1, 0, 0, 0, 1 };
97 
98 /*
99  * Constructor of class
100  */
dvbt_pilot_gen(const dvbt_configure & c)101 dvbt_pilot_gen::dvbt_pilot_gen(const dvbt_configure& c)
102     : config(c),
103       d_spilot_index(0),
104       d_cpilot_index(0),
105       d_tpilot_index(0),
106       d_symbol_index(0),
107       d_symbol_index_known(0),
108       d_frame_index(0),
109       d_superframe_index(0),
110       d_freq_offset_max(8),
111       d_trigger_index(0),
112       d_payload_index(0),
113       d_chanestim_index(0),
114       d_prev_mod_symbol_index(0),
115       d_mod_symbol_index(0)
116 {
117     // Determine parameters from config file
118     d_Kmin = config.d_Kmin;
119     d_Kmax = config.d_Kmax;
120     d_fft_length = config.d_fft_length;
121     d_payload_length = config.d_payload_length;
122     d_zeros_on_left = config.d_zeros_on_left;
123     d_zeros_on_right = config.d_zeros_on_right;
124     d_cp_length = config.d_cp_length;
125 
126     // Set-up pilot data depending on transmission mode
127     if (config.d_transmission_mode == T2k) {
128         d_spilot_carriers_size = d_spilot_carriers_size_2k;
129         d_cpilot_carriers_size = d_cpilot_carriers_size_2k;
130         d_cpilot_carriers = d_cpilot_carriers_2k;
131         d_tps_carriers_size = d_tps_carriers_size_2k;
132         d_tps_carriers = d_tps_carriers_2k;
133     } else if (config.d_transmission_mode == T8k) {
134         d_spilot_carriers_size = d_spilot_carriers_size_8k;
135         d_cpilot_carriers_size = d_cpilot_carriers_size_8k;
136         d_cpilot_carriers = d_cpilot_carriers_8k;
137         d_tps_carriers_size = d_tps_carriers_size_8k;
138         d_tps_carriers = d_tps_carriers_8k;
139     } else {
140         d_spilot_carriers_size = d_spilot_carriers_size_2k;
141         d_cpilot_carriers_size = d_cpilot_carriers_size_2k;
142         d_cpilot_carriers = d_cpilot_carriers_2k;
143         d_tps_carriers_size = d_tps_carriers_size_2k;
144         d_tps_carriers = d_tps_carriers_2k;
145     }
146 
147     d_freq_offset = 0;
148     d_carrier_freq_correction = 0.0;
149     d_sampling_freq_correction = 0.0;
150 
151     // allocate PRBS buffer
152     d_wk = new char[d_Kmax - d_Kmin + 1];
153     if (d_wk == NULL) {
154         std::cerr << "Reference Signals, cannot allocate memory for d_wk." << std::endl;
155         throw std::bad_alloc();
156     }
157     // Generate wk sequence
158     generate_prbs();
159 
160     // allocate buffer for scattered pilots
161     d_spilot_carriers_val = new (std::nothrow) gr_complex[d_Kmax - d_Kmin + 1];
162     if (d_spilot_carriers_val == NULL) {
163         std::cerr
164             << "Reference Signals, cannot allocate memory for d_spilot_carriers_val."
165             << std::endl;
166         delete[] d_wk;
167         throw std::bad_alloc();
168     }
169 
170     // allocate buffer for channel gains (for each useful carrier)
171     d_channel_gain = new (std::nothrow) gr_complex[d_Kmax - d_Kmin + 1];
172     if (d_channel_gain == NULL) {
173         std::cerr << "Reference Signals, cannot allocate memory for d_channel_gain."
174                   << std::endl;
175         delete[] d_spilot_carriers_val;
176         delete[] d_wk;
177         throw std::bad_alloc();
178     }
179 
180     // Allocate buffer for continual pilots phase diffs
181     d_known_phase_diff = new (std::nothrow) float[d_cpilot_carriers_size - 1];
182     if (d_known_phase_diff == NULL) {
183         std::cerr << "Reference Signals, cannot allocate memory for d_known_phase_diff."
184                   << std::endl;
185         delete[] d_channel_gain;
186         delete[] d_spilot_carriers_val;
187         delete[] d_wk;
188         throw std::bad_alloc();
189     }
190 
191     // Obtain phase diff for all continual pilots
192     for (int i = 0; i < (d_cpilot_carriers_size - 1); i++) {
193         d_known_phase_diff[i] = norm(get_cpilot_value(d_cpilot_carriers[i + 1]) -
194                                      get_cpilot_value(d_cpilot_carriers[i]));
195     }
196 
197     d_cpilot_phase_diff = new (std::nothrow) float[d_cpilot_carriers_size - 1];
198     if (d_cpilot_phase_diff == NULL) {
199         std::cerr << "Reference Signals, cannot allocate memory for d_cpilot_phase_diff."
200                   << std::endl;
201         delete[] d_known_phase_diff;
202         delete[] d_channel_gain;
203         delete[] d_spilot_carriers_val;
204         delete[] d_wk;
205         throw std::bad_alloc();
206     }
207 
208     // Allocate buffer for derotated input symbol
209     d_derot_in = new (std::nothrow) gr_complex[d_fft_length];
210     if (d_derot_in == NULL) {
211         std::cerr << "Reference Signals, cannot allocate memory for d_derot_in."
212                   << std::endl;
213         delete[] d_cpilot_phase_diff;
214         delete[] d_known_phase_diff;
215         delete[] d_channel_gain;
216         delete[] d_spilot_carriers_val;
217         delete[] d_wk;
218         throw std::bad_alloc();
219     }
220 
221     // allocate buffer for first tps symbol constellation
222     d_tps_carriers_val = new (std::nothrow) gr_complex[d_tps_carriers_size];
223     if (d_tps_carriers_val == NULL) {
224         std::cerr << "Reference Signals, cannot allocate memory for d_tps_carriers_val."
225                   << std::endl;
226         delete[] d_derot_in;
227         delete[] d_cpilot_phase_diff;
228         delete[] d_known_phase_diff;
229         delete[] d_channel_gain;
230         delete[] d_spilot_carriers_val;
231         delete[] d_wk;
232         throw std::bad_alloc();
233     }
234 
235     // allocate tps data buffer
236     d_tps_data = new (std::nothrow) unsigned char[d_symbols_per_frame];
237     if (d_tps_data == NULL) {
238         std::cerr << "Reference Signals, cannot allocate memory for d_tps_data."
239                   << std::endl;
240         delete[] d_tps_carriers_val;
241         delete[] d_derot_in;
242         delete[] d_cpilot_phase_diff;
243         delete[] d_known_phase_diff;
244         delete[] d_channel_gain;
245         delete[] d_spilot_carriers_val;
246         delete[] d_wk;
247         throw std::bad_alloc();
248     }
249 
250     d_prev_tps_symbol = new (std::nothrow) gr_complex[d_tps_carriers_size];
251     if (d_prev_tps_symbol == NULL) {
252         std::cerr << "Reference Signals, cannot allocate memory for d_prev_tps_symbol."
253                   << std::endl;
254         delete[] d_tps_data;
255         delete[] d_tps_carriers_val;
256         delete[] d_derot_in;
257         delete[] d_cpilot_phase_diff;
258         delete[] d_known_phase_diff;
259         delete[] d_channel_gain;
260         delete[] d_spilot_carriers_val;
261         delete[] d_wk;
262         throw std::bad_alloc();
263     }
264     std::fill_n(d_prev_tps_symbol, d_tps_carriers_size, 0);
265 
266     d_tps_symbol = new (std::nothrow) gr_complex[d_tps_carriers_size];
267     if (d_tps_symbol == NULL) {
268         std::cerr << "Reference Signals, cannot allocate memory for d_tps_symbol."
269                   << std::endl;
270         delete[] d_prev_tps_symbol;
271         delete[] d_tps_data;
272         delete[] d_tps_carriers_val;
273         delete[] d_derot_in;
274         delete[] d_cpilot_phase_diff;
275         delete[] d_known_phase_diff;
276         delete[] d_channel_gain;
277         delete[] d_spilot_carriers_val;
278         delete[] d_wk;
279         throw std::bad_alloc();
280     }
281     std::fill_n(d_tps_symbol, d_tps_carriers_size, 0);
282 
283     // Init receive TPS data vector
284     for (int i = 0; i < d_symbols_per_frame; i++) {
285         d_rcv_tps_data.push_back(0);
286     }
287 
288     // Init TPS sync sequence
289     for (int i = 0; i < d_tps_sync_size; i++) {
290         d_tps_sync_evenv.push_back(d_tps_sync_even[i]);
291         d_tps_sync_oddv.push_back(d_tps_sync_odd[i]);
292     }
293 
294     // Allocate buffer for channel estimation carriers
295     d_chanestim_carriers = new (std::nothrow) int[d_Kmax - d_Kmin + 1];
296     if (d_chanestim_carriers == NULL) {
297         std::cerr << "Reference Signals, cannot allocate memory for d_chanestim_carriers."
298                   << std::endl;
299         delete[] d_tps_symbol;
300         delete[] d_prev_tps_symbol;
301         delete[] d_tps_data;
302         delete[] d_tps_carriers_val;
303         delete[] d_derot_in;
304         delete[] d_cpilot_phase_diff;
305         delete[] d_known_phase_diff;
306         delete[] d_channel_gain;
307         delete[] d_spilot_carriers_val;
308         delete[] d_wk;
309         throw std::bad_alloc();
310     }
311 
312     // Allocate buffer for payload carriers
313     d_payload_carriers = new (std::nothrow) int[d_Kmax - d_Kmin + 1];
314     if (d_payload_carriers == NULL) {
315         std::cerr << "Reference Signals, cannot allocate memory for d_payload_carriers."
316                   << std::endl;
317         delete[] d_chanestim_carriers;
318         delete[] d_tps_symbol;
319         delete[] d_prev_tps_symbol;
320         delete[] d_tps_data;
321         delete[] d_tps_carriers_val;
322         delete[] d_derot_in;
323         delete[] d_cpilot_phase_diff;
324         delete[] d_known_phase_diff;
325         delete[] d_channel_gain;
326         delete[] d_spilot_carriers_val;
327         delete[] d_wk;
328         throw std::bad_alloc();
329     }
330 
331     // Reset the pilot generator
332     reset_pilot_generator();
333     // Format TPS data with current values
334     format_tps_data();
335 }
336 
337 /*
338  * Destructor of class
339  */
~dvbt_pilot_gen()340 dvbt_pilot_gen::~dvbt_pilot_gen()
341 {
342     delete[] d_payload_carriers;
343     delete[] d_chanestim_carriers;
344     delete[] d_tps_symbol;
345     delete[] d_prev_tps_symbol;
346     delete[] d_tps_data;
347     delete[] d_tps_carriers_val;
348     delete[] d_derot_in;
349     delete[] d_cpilot_phase_diff;
350     delete[] d_known_phase_diff;
351     delete[] d_channel_gain;
352     delete[] d_spilot_carriers_val;
353     delete[] d_wk;
354 }
355 
356 /*
357  * Generate PRBS sequence
358  * X^11 + X^2 + 1
359  * en 300 744 - section 4.5.2
360  */
generate_prbs()361 void dvbt_pilot_gen::generate_prbs()
362 {
363     // init PRBS register with 1s
364     unsigned int reg_prbs = (1 << 11) - 1;
365 
366     for (int k = 0; k < (d_Kmax - d_Kmin + 1); k++) {
367         d_wk[k] = (char)(reg_prbs & 0x01);
368         int new_bit = ((reg_prbs >> 2) ^ (reg_prbs >> 0)) & 0x01;
369         reg_prbs = (reg_prbs >> 1) | (new_bit << 10);
370     }
371 }
372 
373 /*
374  * Generate shortened BCH(67, 53) codes from TPS data
375  * Extend the code with 60 bits and use BCH(127, 113)
376  */
generate_bch_code()377 void dvbt_pilot_gen::generate_bch_code()
378 {
379     // TODO
380     // DO other way: if (feedback == 1) reg = reg ^ polymomial
381     // else nothing
382 
383     //(n, k) = (127, 113) = (60+67, 60+53)
384     unsigned int reg_bch = 0;
385     unsigned char data_in[113];
386 
387     // fill in 60 zeros
388     memset(&data_in[0], 0, 60);
389     // fill in TPS data - start bit not included
390     memcpy(&data_in[60], &d_tps_data[1], 53);
391 
392     // X^14+X^9+X^8+X^6+X^5+X^4+X^2+X+1
393     for (int i = 0; i < 113; i++) {
394         int feedback = 0x1 & (data_in[i] ^ reg_bch);
395         reg_bch = reg_bch >> 1;
396         reg_bch |= feedback << 13;
397         reg_bch = reg_bch ^ (feedback << 12) ^ (feedback << 11) ^ (feedback << 9) ^
398                   (feedback << 8) ^ (feedback << 7) ^ (feedback << 5) ^ (feedback << 4);
399     }
400 
401     for (int i = 0; i < 14; i++) {
402         d_tps_data[i + 54] = 0x1 & (reg_bch >> i);
403     }
404 }
405 
verify_bch_code(std::deque<char> data)406 int dvbt_pilot_gen::verify_bch_code(std::deque<char> data)
407 {
408     int ret = 0;
409 
410     // TODO
411     // DO other way: if (feedback == 1) reg = reg ^ polymomial
412     // else nothing
413 
414     //(n, k) = (127, 113) = (60+67, 60+53)
415     unsigned int reg_bch = 0;
416     unsigned char data_in[113];
417 
418     // fill in 60 zeros
419     memset(&data_in[0], 0, 60);
420     // fill in TPS data - start bit not included
421     // memcpy(&data_in[60], &data[1], 53);
422     for (int i = 0; i < 53; i++) {
423         data_in[60 + i] = data[1 + i];
424     }
425 
426     // X^14+X^9+X^8+X^6+X^5+X^4+X^2+X+1
427     for (int i = 0; i < 113; i++) {
428         int feedback = 0x1 & (data_in[i] ^ reg_bch);
429         reg_bch = reg_bch >> 1;
430         reg_bch |= feedback << 13;
431         reg_bch = reg_bch ^ (feedback << 12) ^ (feedback << 11) ^ (feedback << 9) ^
432                   (feedback << 8) ^ (feedback << 7) ^ (feedback << 5) ^ (feedback << 4);
433     }
434 
435     for (int i = 0; i < 14; i++) {
436         if ((unsigned int)data[i + 54] != (0x1 & (reg_bch >> i))) {
437             ret = -1;
438             break;
439         }
440     }
441 
442     return ret;
443 }
444 
set_symbol_index(int sindex)445 void dvbt_pilot_gen::set_symbol_index(int sindex) { d_symbol_index = sindex; }
446 
get_symbol_index()447 int dvbt_pilot_gen::get_symbol_index() { return d_symbol_index; }
448 
set_tps_data()449 void dvbt_pilot_gen::set_tps_data() {}
450 
get_tps_data()451 void dvbt_pilot_gen::get_tps_data() {}
452 
453 /*
454  * Reset pilot generator
455  */
reset_pilot_generator()456 void dvbt_pilot_gen::reset_pilot_generator()
457 {
458     d_spilot_index = 0;
459     d_cpilot_index = 0;
460     d_tpilot_index = 0;
461     d_payload_index = 0;
462     d_chanestim_index = 0;
463     d_symbol_index = 0;
464     d_frame_index = 0;
465     d_superframe_index = 0;
466     d_symbol_index_known = 0;
467     d_equalizer_ready = 0;
468 }
469 
470 /*
471  * Init scattered pilot generator
472  */
get_current_spilot(int sindex) const473 int dvbt_pilot_gen::get_current_spilot(int sindex) const
474 {
475     // TODO - can be optimized for same symbol_index
476     return (d_Kmin + 3 * (sindex % 4) + 12 * d_spilot_index);
477 }
478 
get_spilot_value(int spilot)479 gr_complex dvbt_pilot_gen::get_spilot_value(int spilot)
480 {
481     // TODO - can be calculated at the beginning
482     return gr_complex(4 * 2 * (0.5 - d_wk[spilot]) / 3, 0);
483 }
484 
set_spilot_value(int spilot,gr_complex val)485 void dvbt_pilot_gen::set_spilot_value(int spilot, gr_complex val)
486 {
487     d_spilot_carriers_val[spilot] = val;
488 }
489 
set_channel_gain(int spilot,gr_complex val)490 void dvbt_pilot_gen::set_channel_gain(int spilot, gr_complex val)
491 {
492     // Gain gval=rxval/txval
493     d_channel_gain[spilot] = gr_complex((4 * 2 * (0.5 - d_wk[spilot]) / 3), 0) / val;
494 }
advance_spilot(int sindex)495 void dvbt_pilot_gen::advance_spilot(int sindex)
496 {
497     // TODO - do in a simpler way?
498     int size = d_spilot_carriers_size;
499 
500     if (sindex == 0) {
501         size = d_spilot_carriers_size + 1;
502     }
503 
504     // TODO - fix this - what value should we use?
505     ++d_spilot_index;
506     d_spilot_index = d_spilot_index % size;
507 }
508 
get_first_spilot()509 int dvbt_pilot_gen::get_first_spilot()
510 {
511     d_spilot_index = 0;
512 
513     return (d_Kmin + 3 * (d_symbol_index % 4));
514 }
515 
get_last_spilot() const516 int dvbt_pilot_gen::get_last_spilot() const
517 {
518     int size = d_spilot_carriers_size - 1;
519 
520     if (d_symbol_index == 0) {
521         size = d_spilot_carriers_size;
522     }
523 
524     return (d_Kmin + 3 * (d_symbol_index % 4) + 12 * size);
525 }
526 
get_next_spilot()527 int dvbt_pilot_gen::get_next_spilot()
528 {
529     int pilot = (d_Kmin + 3 * (d_symbol_index % 4) + 12 * (++d_spilot_index));
530 
531     if (pilot > d_Kmax) {
532         pilot = d_Kmax;
533     }
534 
535     return pilot;
536 }
537 
process_spilot_data(const gr_complex * in)538 int dvbt_pilot_gen::process_spilot_data(const gr_complex* in)
539 {
540     // This is channel estimator
541     // Interpolate the gain between carriers to obtain
542     // gain for non pilot carriers - we use linear interpolation
543 
544     /*************************************************************/
545     // Find out the OFDM symbol index (value 0 to 3) sent
546     // in current block by correlating scattered symbols with
547     // current block - result is (symbol index % 4)
548     /*************************************************************/
549     float max = 0;
550     float sum = 0;
551 
552     for (int scount = 0; scount < 4; scount++) {
553         d_spilot_index = 0;
554         d_cpilot_index = 0;
555         d_chanestim_index = 0;
556 
557         for (int k = 0; k < (d_Kmax - d_Kmin + 1); k++) {
558             // Keep data for channel estimation
559             if (k == get_current_spilot(scount)) {
560                 set_chanestim_carrier(k);
561                 advance_spilot(scount);
562                 advance_chanestim();
563             }
564         }
565 
566         gr_complex c = gr_complex(0.0, 0.0);
567 
568         // This should be of range 0 to d_chanestim_index bit for now we use just a
569         // small number of spilots to obtain the symbol index
570         for (int j = 0; j < 10; j++) {
571             c += get_spilot_value(d_chanestim_carriers[j]) *
572                  conj(in[d_zeros_on_left + d_chanestim_carriers[j]]);
573         }
574         sum = norm(c);
575 
576         if (sum > max) {
577             max = sum;
578             d_mod_symbol_index = scount;
579         }
580     }
581 
582     /*************************************************************/
583     // Keep data for channel estimator
584     // This method interpolates scattered measurements across one OFDM symbol
585     // It does not use measurements from the previous OFDM symbols (does not use history)
586     // as it may have encountered a phase change for the current phase only
587     /*************************************************************/
588 
589     d_spilot_index = 0;
590     d_cpilot_index = 0;
591     d_chanestim_index = 0;
592 
593     for (int k = 0; k < (d_Kmax - d_Kmin + 1); k++) {
594         // Keep data for channel estimation
595         if (k == get_current_spilot(d_mod_symbol_index)) {
596             set_chanestim_carrier(k);
597             advance_spilot(d_mod_symbol_index);
598             advance_chanestim();
599         }
600 
601         // Keep data for channel estimation
602         if (k == get_current_cpilot()) {
603             set_chanestim_carrier(k);
604             advance_cpilot();
605             advance_chanestim();
606         }
607     }
608 
609     // We use both scattered pilots and continual pilots
610     for (int i = 0, startk = d_chanestim_carriers[0]; i < d_chanestim_index; i++) {
611         // Get a carrier from the list of carriers
612         // used for channel estimation
613         int k = d_chanestim_carriers[i];
614 
615         set_channel_gain(k, in[k + d_zeros_on_left]);
616 
617         // Calculate tg(alpha) due to linear interpolation
618         gr_complex tg_alpha =
619             (d_channel_gain[k] - d_channel_gain[startk]) / gr_complex(11.0, 0.0);
620 
621         // Calculate interpolation for all intermediate values
622         for (int j = 1; j < (k - startk); j++) {
623             gr_complex current = d_channel_gain[startk] + tg_alpha * gr_complex(j, 0.0);
624             d_channel_gain[startk + j] = current;
625         }
626 
627         startk = k;
628     }
629 
630     // Signal that equalizer is ready
631     d_equalizer_ready = 1;
632 
633     int diff_sindex = (d_mod_symbol_index - d_prev_mod_symbol_index + 4) % 4;
634 
635     d_prev_mod_symbol_index = d_mod_symbol_index;
636 
637     return diff_sindex;
638 }
639 
640 /*
641  * Init continual pilot generator
642  */
get_current_cpilot() const643 int dvbt_pilot_gen::get_current_cpilot() const
644 {
645     return d_cpilot_carriers[d_cpilot_index];
646 }
647 
get_cpilot_value(int cpilot)648 gr_complex dvbt_pilot_gen::get_cpilot_value(int cpilot)
649 {
650     // TODO - can be calculated at the beginning
651     return gr_complex((float)(4 * 2 * (0.5 - d_wk[cpilot])) / 3, 0);
652 }
653 
advance_cpilot()654 void dvbt_pilot_gen::advance_cpilot()
655 {
656     ++d_cpilot_index;
657     d_cpilot_index = d_cpilot_index % d_cpilot_carriers_size;
658 }
659 
process_cpilot_data(const gr_complex * in)660 void dvbt_pilot_gen::process_cpilot_data(const gr_complex* in)
661 {
662     // Look for maximum correlation for cpilots
663     // in order to obtain post FFT integer frequency correction
664 
665     float max = 0;
666     float sum = 0;
667     int start = 0;
668     float phase;
669 
670     for (int i = d_zeros_on_left - d_freq_offset_max;
671          i < d_zeros_on_left + d_freq_offset_max;
672          i++) {
673         sum = 0;
674         for (int j = 0; j < (d_cpilot_carriers_size - 1); j++) {
675             phase = norm(in[i + d_cpilot_carriers[j + 1]] - in[i + d_cpilot_carriers[j]]);
676             sum += d_known_phase_diff[j] * phase;
677         }
678 
679         if (sum > max) {
680             max = sum;
681             start = i;
682         }
683     }
684 
685     d_freq_offset = start - d_zeros_on_left;
686 }
687 
compute_oneshot_csft(const gr_complex * in)688 void dvbt_pilot_gen::compute_oneshot_csft(const gr_complex* in)
689 {
690     gr_complex left_corr_sum = 0.0;
691     gr_complex right_corr_sum = 0.0;
692     int half_size = (d_cpilot_carriers_size - 1) / 2;
693 
694     // TODO init this in constructor
695     float carrier_coeff =
696         1.0 / (2 * GR_M_PI * (1 + float(d_cp_length) / float(d_fft_length)) * 2);
697     float sampling_coeff = 1.0 / (2 * GR_M_PI *
698                                   ((1 + float(d_cp_length) / float(d_fft_length)) *
699                                    ((float)d_cpilot_carriers_size / 2.0)));
700 
701     float left_angle, right_angle;
702 
703     // Compute cpilots correlation between previous symbol and current symbol
704     // in both halves of the cpilots. The cpilots are distributed evenly
705     // on left and right sides of the center frequency.
706 
707     for (int j = 0; j < half_size; j++) {
708         left_corr_sum += in[d_freq_offset + d_zeros_on_left + d_cpilot_carriers[j]] *
709                          std::conj(in[d_freq_offset + d_fft_length + d_zeros_on_left +
710                                       d_cpilot_carriers[j]]);
711     }
712 
713     for (int j = half_size + 1; j < d_cpilot_carriers_size; j++) {
714         right_corr_sum += in[d_freq_offset + d_zeros_on_left + d_cpilot_carriers[j]] *
715                           std::conj(in[d_freq_offset + d_fft_length + d_zeros_on_left +
716                                        d_cpilot_carriers[j]]);
717     }
718 
719     left_angle = std::arg(left_corr_sum);
720     right_angle = std::arg(right_corr_sum);
721 
722     d_carrier_freq_correction = (right_angle + left_angle) * carrier_coeff;
723     d_sampling_freq_correction = (right_angle - left_angle) * sampling_coeff;
724 }
725 
frequency_correction(const gr_complex * in,gr_complex * out)726 gr_complex* dvbt_pilot_gen::frequency_correction(const gr_complex* in, gr_complex* out)
727 {
728     // TODO - use PI control loop to calculate tracking corrections
729     int symbol_count = 1;
730 
731     for (int k = 0; k < d_fft_length; k++) {
732         // TODO - for 2k mode the continuous pilots are not split evenly
733         // between left/right center frequency. Probably the scattered
734         // pilots needs to be added.
735 
736         float correction = (float)d_freq_offset + d_carrier_freq_correction;
737 
738         gr_complex c = gr_expj(-2 * GR_M_PI * correction * (d_fft_length + d_cp_length) /
739                                d_fft_length * symbol_count);
740 
741         // TODO - vectorize this operation
742         out[k] = c * in[k + d_freq_offset];
743     }
744 
745     return (out);
746 }
747 
748 /*
749  * Init tps sequence, return values for first position
750  * If first symbol then init tps DBPSK data
751  */
get_current_tpilot() const752 int dvbt_pilot_gen::get_current_tpilot() const { return d_tps_carriers[d_tpilot_index]; }
753 
get_tpilot_value(int tpilot)754 gr_complex dvbt_pilot_gen::get_tpilot_value(int tpilot)
755 {
756     // TODO - it can be calculated at the beginnning
757     if (d_symbol_index == 0) {
758         d_tps_carriers_val[d_tpilot_index] = gr_complex(2 * (0.5 - d_wk[tpilot]), 0);
759     } else {
760         if (d_tps_data[d_symbol_index] == 1) {
761             d_tps_carriers_val[d_tpilot_index] =
762                 gr_complex(-d_tps_carriers_val[d_tpilot_index].real(), 0);
763         }
764     }
765 
766     return d_tps_carriers_val[d_tpilot_index];
767 }
768 
advance_tpilot()769 void dvbt_pilot_gen::advance_tpilot()
770 {
771     ++d_tpilot_index;
772     d_tpilot_index = d_tpilot_index % d_tps_carriers_size;
773 }
774 
775 /*
776  * Set a number of bits to a specified value
777  */
set_tps_bits(int start,int stop,unsigned int data)778 void dvbt_pilot_gen::set_tps_bits(int start, int stop, unsigned int data)
779 {
780     for (int i = start; i >= stop; i--) {
781         d_tps_data[i] = data & 0x1;
782         data = data >> 1;
783     }
784 }
785 
786 /*
787  * Clause 4.6
788  * Format data that will be sent with TPS signals
789  * en 300 744 - section 4.6.2
790  * s0 Initialization
791  * s1-s16 Synchronization word
792  * s17-s22 Length Indicator
793  * s23-s24 Frame Number
794  * S25-s26 Constellation
795  * s27, s28, s29 Hierarchy information
796  * s30, s31, s32 Code rate, HP stream
797  * s33, s34, s35 Code rate, LP stream
798  * s36, s37 Guard interval
799  * s38, s39 Transmission mode
800  * s40, s47 Cell identifier
801  * s48-s53 All set to "0"
802  * s54-s67 Error protection (BCH code)
803  */
format_tps_data()804 void dvbt_pilot_gen::format_tps_data()
805 {
806     // Clause 4.6.3
807     set_tps_bits(0, 0, d_wk[0]);
808     // Clause 4.6.2.2
809     if (d_frame_index % 2) {
810         set_tps_bits(16, 1, 0xca11);
811     } else {
812         set_tps_bits(16, 1, 0x35ee);
813     }
814     // Clause 4.6.2.3
815     if (config.d_include_cell_id) {
816         set_tps_bits(22, 17, 0x1f);
817     } else {
818         set_tps_bits(22, 17, 0x17);
819     }
820     // Clause 4.6.2.4
821     set_tps_bits(24, 23, d_frame_index);
822     // Clause 4.6.2.5
823     set_tps_bits(26, 25, config.d_constellation);
824     // Clause 4.6.2.6
825     set_tps_bits(29, 27, config.d_hierarchy);
826     // Clause 4.6.2.7
827     switch (config.d_code_rate_HP) {
828     case C1_2:
829         set_tps_bits(32, 30, 0);
830         break;
831     case C2_3:
832         set_tps_bits(32, 30, 1);
833         break;
834     case C3_4:
835         set_tps_bits(32, 30, 2);
836         break;
837     case C5_6:
838         set_tps_bits(32, 30, 3);
839         break;
840     case C7_8:
841         set_tps_bits(32, 30, 4);
842         break;
843     default:
844         set_tps_bits(32, 30, 0);
845         break;
846     }
847     switch (config.d_code_rate_LP) {
848     case C1_2:
849         set_tps_bits(35, 33, 0);
850         break;
851     case C2_3:
852         set_tps_bits(35, 33, 1);
853         break;
854     case C3_4:
855         set_tps_bits(35, 33, 2);
856         break;
857     case C5_6:
858         set_tps_bits(35, 33, 3);
859         break;
860     case C7_8:
861         set_tps_bits(35, 33, 4);
862         break;
863     default:
864         set_tps_bits(35, 33, 0);
865         break;
866     }
867     // Clause 4.6.2.8
868     set_tps_bits(37, 36, config.d_guard_interval);
869     // Clause 4.6.2.9
870     set_tps_bits(39, 38, config.d_transmission_mode);
871     // Clause 4.6.2.10
872     if (d_frame_index % 2) {
873         set_tps_bits(47, 40, config.d_cell_id & 0xff);
874     } else {
875         set_tps_bits(47, 40, (config.d_cell_id >> 8) & 0xff);
876     }
877     // These bits are set to zero
878     set_tps_bits(53, 48, 0);
879     // Clause 4.6.2.11
880     generate_bch_code();
881 }
882 
process_tps_data(const gr_complex * in,const int diff_symbol_index)883 int dvbt_pilot_gen::process_tps_data(const gr_complex* in, const int diff_symbol_index)
884 {
885     int end_frame = 0;
886 
887     // Look for TPS data only - demodulate DBPSK
888     // Calculate phase difference between previous symbol
889     // and current one to determine the current bit.
890     // Use majority voting for decision
891     int tps_majority_zero = 0;
892 
893     for (int k = 0; k < d_tps_carriers_size; k++) {
894         // Use equalizer to correct data and frequency correction
895         gr_complex val =
896             in[d_zeros_on_left + d_tps_carriers[k]] * d_channel_gain[d_tps_carriers[k]];
897 
898         if (!d_symbol_index_known || (d_symbol_index != 0)) {
899             gr_complex phdiff = val * conj(d_prev_tps_symbol[k]);
900 
901             if (phdiff.real() >= 0.0) {
902                 tps_majority_zero++;
903             } else {
904                 tps_majority_zero--;
905             }
906         }
907 
908         d_prev_tps_symbol[k] = val;
909     }
910 
911     // Insert obtained TPS bit into FIFO
912     // Insert the same bit into FIFO in the case
913     // diff_symbol_index is more than one. This will happen
914     // in the case of losing 1 to 3 symbols.
915     // This could be corrected by BCH decoder afterwards.
916 
917     for (int i = 0; i < diff_symbol_index; i++) {
918         // Take out the front entry first
919         d_rcv_tps_data.pop_front();
920 
921         // Add data at tail
922         if (!d_symbol_index_known || (d_symbol_index != 0)) {
923             if (tps_majority_zero >= 0) {
924                 d_rcv_tps_data.push_back(0);
925             } else {
926                 d_rcv_tps_data.push_back(1);
927             }
928         } else {
929             d_rcv_tps_data.push_back(0);
930         }
931     }
932 
933     // Match synchronization signatures
934     if (std::equal(d_rcv_tps_data.begin() + 1,
935                    d_rcv_tps_data.begin() + d_tps_sync_evenv.size(),
936                    d_tps_sync_evenv.begin())) {
937         // Verify parity for TPS data
938         if (!verify_bch_code(d_rcv_tps_data)) {
939             d_frame_index = (d_rcv_tps_data[23] << 1) | (d_rcv_tps_data[24]);
940 
941             d_symbol_index_known = 1;
942             end_frame = 1;
943         } else {
944             d_symbol_index_known = 0;
945             end_frame = 0;
946         }
947 
948         // Clear up FIFO
949         for (int i = 0; i < d_symbols_per_frame; i++) {
950             d_rcv_tps_data[i] = 0;
951         }
952     } else if (std::equal(d_rcv_tps_data.begin() + 1,
953                           d_rcv_tps_data.begin() + d_tps_sync_oddv.size(),
954                           d_tps_sync_oddv.begin())) {
955         // Verify parity for TPS data
956         if (!verify_bch_code(d_rcv_tps_data)) {
957             d_frame_index = (d_rcv_tps_data[23] << 1) | (d_rcv_tps_data[24]);
958 
959             d_symbol_index_known = 1;
960             end_frame = 1;
961         } else {
962             d_symbol_index_known = 0;
963             end_frame = 0;
964         }
965 
966         // Clear up FIFO
967         for (int i = 0; i < d_symbols_per_frame; i++) {
968             d_rcv_tps_data[i] = 0;
969         }
970     }
971 
972     return end_frame;
973 }
974 
set_chanestim_carrier(int k)975 void dvbt_pilot_gen::set_chanestim_carrier(int k)
976 {
977     d_chanestim_carriers[d_chanestim_index] = k;
978 }
979 
advance_chanestim()980 void dvbt_pilot_gen::advance_chanestim() { d_chanestim_index++; }
981 
get_current_payload()982 int dvbt_pilot_gen::get_current_payload() { return d_payload_carriers[d_payload_index]; }
983 
set_payload_carrier(int k)984 void dvbt_pilot_gen::set_payload_carrier(int k)
985 {
986     d_payload_carriers[d_payload_index] = k;
987 }
988 
advance_payload()989 void dvbt_pilot_gen::advance_payload() { d_payload_index++; }
990 
process_payload_data(const gr_complex * in,gr_complex * out)991 void dvbt_pilot_gen::process_payload_data(const gr_complex* in, gr_complex* out)
992 {
993     // reset indexes
994     d_spilot_index = 0;
995     d_cpilot_index = 0;
996     d_tpilot_index = 0;
997     d_payload_index = 0;
998     d_chanestim_index = 0;
999     int is_payload = 1;
1000 
1001     // process one block - one symbol
1002     for (int k = 0; k < (d_Kmax - d_Kmin + 1); k++) {
1003         is_payload = 1;
1004 
1005         // Keep data for channel estimation
1006         // This depends on the symbol index
1007         if (k == get_current_spilot(d_mod_symbol_index)) {
1008             advance_spilot(d_mod_symbol_index);
1009             is_payload = 0;
1010         }
1011 
1012         // Keep data for frequency correction
1013         // and channel estimation
1014         if (k == get_current_cpilot()) {
1015             advance_cpilot();
1016             is_payload = 0;
1017         }
1018 
1019         if (k == get_current_tpilot()) {
1020             advance_tpilot();
1021             is_payload = 0;
1022         }
1023 
1024         // Keep payload carrier number
1025         // This depends on the symbol index
1026         if (is_payload) {
1027             set_payload_carrier(k);
1028             advance_payload();
1029         }
1030     }
1031 
1032     if (d_equalizer_ready) {
1033         // Equalize payload data according to channel estimator
1034         for (int i = 0; i < d_payload_index; i++) {
1035             out[i] = in[d_zeros_on_left + d_payload_carriers[i]] *
1036                      d_channel_gain[d_payload_carriers[i]];
1037         }
1038     } else {
1039         // If equ not ready, return 0
1040         for (int i = 0; i < d_payload_length; i++) {
1041             out[0] = gr_complex(0.0, 0.0);
1042         }
1043     }
1044 }
1045 
update_output(const gr_complex * in,gr_complex * out)1046 void dvbt_pilot_gen::update_output(const gr_complex* in, gr_complex* out)
1047 {
1048     int is_payload = 1;
1049     int payload_count = 0;
1050 
1051     // move to the next symbol
1052     // re-genereate TPS data
1053     format_tps_data();
1054 
1055     // reset indexes
1056     payload_count = 0;
1057     d_spilot_index = 0;
1058     d_cpilot_index = 0;
1059     d_tpilot_index = 0;
1060 
1061     for (int i = 0; i < d_zeros_on_left; i++) {
1062         out[i] = gr_complex(0.0, 0.0);
1063     }
1064 
1065     // process one block - one symbol
1066     for (int k = d_Kmin; k < (d_Kmax - d_Kmin + 1); k++) {
1067         is_payload = 1;
1068         if (k == get_current_spilot(d_symbol_index)) {
1069             out[d_zeros_on_left + k] = get_spilot_value(k);
1070             advance_spilot(d_symbol_index);
1071             is_payload = 0;
1072         }
1073 
1074         if (k == get_current_cpilot()) {
1075             out[d_zeros_on_left + k] = get_cpilot_value(k);
1076             advance_cpilot();
1077             is_payload = 0;
1078         }
1079 
1080         if (k == get_current_tpilot()) {
1081             out[d_zeros_on_left + k] = get_tpilot_value(k);
1082             advance_tpilot();
1083             is_payload = 0;
1084         }
1085 
1086         if (is_payload == 1) {
1087             out[d_zeros_on_left + k] = in[payload_count++];
1088         }
1089     }
1090 
1091     // update indexes
1092     if (++d_symbol_index == d_symbols_per_frame) {
1093         d_symbol_index = 0;
1094         if (++d_frame_index == d_frames_per_superframe) {
1095             d_frame_index = 0;
1096             d_superframe_index++;
1097         }
1098     }
1099 
1100     for (int i = (d_fft_length - d_zeros_on_right); i < d_fft_length; i++) {
1101         out[i] = gr_complex(0.0, 0.0);
1102     }
1103 }
1104 
parse_input(const gr_complex * in,gr_complex * out,int * symbol_index,int * frame_index)1105 int dvbt_pilot_gen::parse_input(const gr_complex* in,
1106                                 gr_complex* out,
1107                                 int* symbol_index,
1108                                 int* frame_index)
1109 {
1110     d_trigger_index++;
1111 
1112     // Obtain frequency correction based on cpilots.
1113     // Obtain channel estimation based on both
1114     // cpilots and spilots.
1115     // We use spilot correlation for finding the symbol index modulo 4
1116     // The diff between previous sym index and current index is used
1117     // to advance the symbol index inside a frame (0 to 67)
1118     // Then based on the TPS data we find out the start of a frame
1119 
1120     // Process cpilot data
1121     // This is post FFT integer frequency offset estimation
1122     // This is called before all other processing
1123     process_cpilot_data(in);
1124 
1125     // Compute one shot Post-FFT Carrier and Sampling Frequency Tracking
1126     // Obtain fractional Carrer and Sampling frequency corrections
1127     // Before this moment it is assumed to have corrected this:
1128     // - symbol timing (pre-FFT)
1129     // - symbol frequency correction (pre-FFT)
1130     // - integer frequency correction (post-FFT)
1131     // TODO - call this just in the acquisition mode
1132     compute_oneshot_csft(in);
1133 
1134     // Gather all corrections and obtain a corrected OFDM symbol:
1135     // - input symbol shift (post-FFT)
1136     // - integer frequency correction (post-FFT)
1137     // - fractional frequency (carrier and sampling) corrections (post-FFT)
1138     // TODO - use PI to update the corrections
1139     frequency_correction(in, d_derot_in);
1140 
1141     // Process spilot data
1142     // This is channel estimation function
1143     int diff_symbol_index = process_spilot_data(d_derot_in);
1144 
1145     // Correct symbol index so that all subsequent processing
1146     // use correct symbol index
1147     d_symbol_index = (d_symbol_index + diff_symbol_index) % d_symbols_per_frame;
1148 
1149     // Symbol index is used in other modules too
1150     *symbol_index = d_symbol_index;
1151     // Frame index is used in other modules too
1152     *frame_index = d_frame_index;
1153 
1154     // Process TPS data
1155     // If a frame is recognized then signal end of frame
1156     int frame_end = process_tps_data(d_derot_in, diff_symbol_index);
1157 
1158     // We are just at the end of a frame
1159     if (frame_end) {
1160         d_symbol_index = d_symbols_per_frame - 1;
1161     }
1162 
1163     // Process payload data with correct symbol index
1164     process_payload_data(d_derot_in, out);
1165 
1166     // noutput_items should be 1 in this case
1167     return 1;
1168 }
1169 
1170 dvbt_reference_signals::sptr
make(int itemsize,int ninput,int noutput,dvb_constellation_t constellation,dvbt_hierarchy_t hierarchy,dvb_code_rate_t code_rate_HP,dvb_code_rate_t code_rate_LP,dvb_guardinterval_t guard_interval,dvbt_transmission_mode_t transmission_mode,int include_cell_id,int cell_id)1171 dvbt_reference_signals::make(int itemsize,
1172                              int ninput,
1173                              int noutput,
1174                              dvb_constellation_t constellation,
1175                              dvbt_hierarchy_t hierarchy,
1176                              dvb_code_rate_t code_rate_HP,
1177                              dvb_code_rate_t code_rate_LP,
1178                              dvb_guardinterval_t guard_interval,
1179                              dvbt_transmission_mode_t transmission_mode,
1180                              int include_cell_id,
1181                              int cell_id)
1182 {
1183     return gnuradio::get_initial_sptr(new dvbt_reference_signals_impl(itemsize,
1184                                                                       ninput,
1185                                                                       noutput,
1186                                                                       constellation,
1187                                                                       hierarchy,
1188                                                                       code_rate_HP,
1189                                                                       code_rate_LP,
1190                                                                       guard_interval,
1191                                                                       transmission_mode,
1192                                                                       include_cell_id,
1193                                                                       cell_id));
1194 }
1195 
1196 /*
1197  * The private constructor
1198  */
dvbt_reference_signals_impl(int itemsize,int ninput,int noutput,dvb_constellation_t constellation,dvbt_hierarchy_t hierarchy,dvb_code_rate_t code_rate_HP,dvb_code_rate_t code_rate_LP,dvb_guardinterval_t guard_interval,dvbt_transmission_mode_t transmission_mode,int include_cell_id,int cell_id)1199 dvbt_reference_signals_impl::dvbt_reference_signals_impl(
1200     int itemsize,
1201     int ninput,
1202     int noutput,
1203     dvb_constellation_t constellation,
1204     dvbt_hierarchy_t hierarchy,
1205     dvb_code_rate_t code_rate_HP,
1206     dvb_code_rate_t code_rate_LP,
1207     dvb_guardinterval_t guard_interval,
1208     dvbt_transmission_mode_t transmission_mode,
1209     int include_cell_id,
1210     int cell_id)
1211     : block("dvbt_reference_signals",
1212             io_signature::make(1, 1, itemsize * ninput),
1213             io_signature::make(1, 1, itemsize * noutput)),
1214       config(constellation,
1215              hierarchy,
1216              code_rate_HP,
1217              code_rate_LP,
1218              guard_interval,
1219              transmission_mode,
1220              include_cell_id,
1221              cell_id),
1222       d_pg(config)
1223 {
1224     d_ninput = ninput;
1225     d_noutput = noutput;
1226 }
1227 
1228 /*
1229  * Our virtual destructor.
1230  */
~dvbt_reference_signals_impl()1231 dvbt_reference_signals_impl::~dvbt_reference_signals_impl() {}
1232 
forecast(int noutput_items,gr_vector_int & ninput_items_required)1233 void dvbt_reference_signals_impl::forecast(int noutput_items,
1234                                            gr_vector_int& ninput_items_required)
1235 {
1236     ninput_items_required[0] = noutput_items;
1237 }
1238 
general_work(int noutput_items,gr_vector_int & ninput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)1239 int dvbt_reference_signals_impl::general_work(int noutput_items,
1240                                               gr_vector_int& ninput_items,
1241                                               gr_vector_const_void_star& input_items,
1242                                               gr_vector_void_star& output_items)
1243 {
1244     const gr_complex* in = (const gr_complex*)input_items[0];
1245     gr_complex* out = (gr_complex*)output_items[0];
1246 
1247     for (int i = 0; i < noutput_items; i++) {
1248         d_pg.update_output(&in[i * d_ninput], &out[i * d_noutput]);
1249     }
1250 
1251     // Tell runtime system how many input items we consumed on
1252     // each input stream.
1253     consume_each(noutput_items);
1254 
1255     // Tell runtime system how many output items we produced.
1256     return noutput_items;
1257 }
1258 
1259 } /* namespace dtv */
1260 } /* namespace gr */
1261