1 /* -*- c++ -*- */
2 /*
3 * Copyright 2014 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 "atsc_equalizer_impl.h"
28 #include "atsc_pnXXX_impl.h"
29 #include "atsc_types.h"
30 #include <gnuradio/io_signature.h>
31 #include <volk/volk.h>
32
33 namespace gr {
34 namespace dtv {
35
make()36 atsc_equalizer::sptr atsc_equalizer::make()
37 {
38 return gnuradio::get_initial_sptr(new atsc_equalizer_impl());
39 }
40
bin_map(int bit)41 static float bin_map(int bit) { return bit ? +5 : -5; }
42
init_field_sync_common(float * p,int mask)43 static void init_field_sync_common(float* p, int mask)
44 {
45 int i = 0;
46
47 p[i++] = bin_map(1); // data segment sync pulse
48 p[i++] = bin_map(0);
49 p[i++] = bin_map(0);
50 p[i++] = bin_map(1);
51
52 for (int j = 0; j < 511; j++) // PN511
53 p[i++] = bin_map(atsc_pn511[j]);
54
55 for (int j = 0; j < 63; j++) // PN63
56 p[i++] = bin_map(atsc_pn63[j]);
57
58 for (int j = 0; j < 63; j++) // PN63, toggled on field 2
59 p[i++] = bin_map(atsc_pn63[j] ^ mask);
60
61 for (int j = 0; j < 63; j++) // PN63
62 p[i++] = bin_map(atsc_pn63[j]);
63 }
64
atsc_equalizer_impl()65 atsc_equalizer_impl::atsc_equalizer_impl()
66 : gr::block("dtv_atsc_equalizer",
67 io_signature::make(1, 1, sizeof(atsc_soft_data_segment)),
68 io_signature::make(1, 1, sizeof(atsc_soft_data_segment)))
69 {
70 init_field_sync_common(training_sequence1, 0);
71 init_field_sync_common(training_sequence2, 1);
72
73 d_taps.resize(NTAPS, 0.0f);
74
75 d_buff_not_filled = true;
76
77 const int alignment_multiple = volk_get_alignment() / sizeof(float);
78 set_alignment(std::max(1, alignment_multiple));
79 }
80
~atsc_equalizer_impl()81 atsc_equalizer_impl::~atsc_equalizer_impl() {}
82
taps() const83 std::vector<float> atsc_equalizer_impl::taps() const { return d_taps; }
84
data() const85 std::vector<float> atsc_equalizer_impl::data() const
86 {
87 std::vector<float> ret(&data_mem2[0], &data_mem2[ATSC_DATA_SEGMENT_LENGTH - 1]);
88 return ret;
89 }
90
filterN(const float * input_samples,float * output_samples,int nsamples)91 void atsc_equalizer_impl::filterN(const float* input_samples,
92 float* output_samples,
93 int nsamples)
94 {
95 for (int j = 0; j < nsamples; j++) {
96 output_samples[j] = 0;
97 volk_32f_x2_dot_prod_32f(
98 &output_samples[j], &input_samples[j], &d_taps[0], NTAPS);
99 }
100 }
101
adaptN(const float * input_samples,const float * training_pattern,float * output_samples,int nsamples)102 void atsc_equalizer_impl::adaptN(const float* input_samples,
103 const float* training_pattern,
104 float* output_samples,
105 int nsamples)
106 {
107 static const double BETA = 0.00005; // FIXME figure out what this ought to be
108 // FIXME add gear-shifting
109
110 for (int j = 0; j < nsamples; j++) {
111 output_samples[j] = 0;
112 volk_32f_x2_dot_prod_32f(
113 &output_samples[j], &input_samples[j], &d_taps[0], NTAPS);
114
115 float e = output_samples[j] - training_pattern[j];
116
117 // update taps...
118 float tmp_taps[NTAPS];
119 volk_32f_s32f_multiply_32f(tmp_taps, &input_samples[j], BETA * e, NTAPS);
120 volk_32f_x2_subtract_32f(&d_taps[0], &d_taps[0], tmp_taps, NTAPS);
121 }
122 }
123
general_work(int noutput_items,gr_vector_int & ninput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)124 int atsc_equalizer_impl::general_work(int noutput_items,
125 gr_vector_int& ninput_items,
126 gr_vector_const_void_star& input_items,
127 gr_vector_void_star& output_items)
128 {
129 const atsc_soft_data_segment* in = (const atsc_soft_data_segment*)input_items[0];
130 atsc_soft_data_segment* out = (atsc_soft_data_segment*)output_items[0];
131
132 int output_produced = 0;
133 int i = 0;
134
135 if (d_buff_not_filled) {
136 memset(&data_mem[0], 0, NPRETAPS * sizeof(float));
137 memcpy(&data_mem[NPRETAPS], in[i].data, ATSC_DATA_SEGMENT_LENGTH * sizeof(float));
138 d_flags = in[i].pli._flags;
139 d_segno = in[i].pli._segno;
140 d_buff_not_filled = false;
141 i++;
142 }
143
144 for (; i < noutput_items; i++) {
145
146 memcpy(&data_mem[ATSC_DATA_SEGMENT_LENGTH + NPRETAPS],
147 in[i].data,
148 (NTAPS - NPRETAPS) * sizeof(float));
149
150 if (d_segno == -1) {
151 if (d_flags & 0x0010) {
152 adaptN(data_mem, training_sequence2, data_mem2, KNOWN_FIELD_SYNC_LENGTH);
153 // filterN(&data_mem[KNOWN_FIELD_SYNC_LENGTH], data_mem2,
154 // ATSC_DATA_SEGMENT_LENGTH - KNOWN_FIELD_SYNC_LENGTH);
155 } else if (!(d_flags & 0x0010)) {
156 adaptN(data_mem, training_sequence1, data_mem2, KNOWN_FIELD_SYNC_LENGTH);
157 // filterN(&data_mem[KNOWN_FIELD_SYNC_LENGTH], data_mem2,
158 // ATSC_DATA_SEGMENT_LENGTH - KNOWN_FIELD_SYNC_LENGTH);
159 }
160 } else {
161 filterN(data_mem, data_mem2, ATSC_DATA_SEGMENT_LENGTH);
162
163 memcpy(out[output_produced].data,
164 data_mem2,
165 ATSC_DATA_SEGMENT_LENGTH * sizeof(float));
166
167 out[output_produced].pli._flags = d_flags;
168 out[output_produced].pli._segno = d_segno;
169 output_produced++;
170 }
171
172 memcpy(data_mem, &data_mem[ATSC_DATA_SEGMENT_LENGTH], NPRETAPS * sizeof(float));
173 memcpy(&data_mem[NPRETAPS], in[i].data, ATSC_DATA_SEGMENT_LENGTH * sizeof(float));
174
175 d_flags = in[i].pli._flags;
176 d_segno = in[i].pli._segno;
177 }
178
179 consume_each(noutput_items);
180 return output_produced;
181 }
182
setup_rpc()183 void atsc_equalizer_impl::setup_rpc()
184 {
185 #ifdef GR_CTRLPORT
186 add_rpc_variable(
187 rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float>>(
188 alias(),
189 "taps",
190 &atsc_equalizer::taps,
191 pmt::make_f32vector(1, -10),
192 pmt::make_f32vector(1, 10),
193 pmt::make_f32vector(1, 0),
194 "",
195 "Equalizer Taps",
196 RPC_PRIVLVL_MIN,
197 DISPTIME)));
198
199 add_rpc_variable(
200 rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float>>(
201 alias(),
202 "data",
203 &atsc_equalizer::data,
204 pmt::make_f32vector(1, -10),
205 pmt::make_f32vector(1, 10),
206 pmt::make_f32vector(1, 0),
207 "",
208 "Post-equalizer Data",
209 RPC_PRIVLVL_MIN,
210 DISPTIME)));
211 #endif /* GR_CTRLPORT */
212 }
213
214 } /* namespace dtv */
215 } /* namespace gr */
216