1 /* -*- c++ -*- */
2 /*
3  * Copyright 2015 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 #include <gnuradio/fec/generic_encoder.h>
24 #include <gnuradio/fec/tpc_common.h>
25 #include <gnuradio/fec/tpc_encoder.h>
26 
27 #include <math.h>
28 #include <stdio.h>
29 #include <volk/volk.h>
30 #include <boost/assign/list_of.hpp>
31 #include <sstream>
32 #include <vector>
33 
34 #include <string.h>  // for memcpy
35 #include <algorithm> // for std::reverse
36 
37 namespace gr {
38 namespace fec {
make(std::vector<int> row_polys,std::vector<int> col_polys,int krow,int kcol,int bval,int qval)39 generic_encoder::sptr tpc_encoder::make(std::vector<int> row_polys,
40                                         std::vector<int> col_polys,
41                                         int krow,
42                                         int kcol,
43                                         int bval,
44                                         int qval)
45 {
46     return generic_encoder::sptr(
47         new tpc_encoder(row_polys, col_polys, krow, kcol, bval, qval));
48 }
49 
tpc_encoder(std::vector<int> row_polys,std::vector<int> col_polys,int krow,int kcol,int bval,int qval)50 tpc_encoder::tpc_encoder(std::vector<int> row_polys,
51                          std::vector<int> col_polys,
52                          int krow,
53                          int kcol,
54                          int bval,
55                          int qval)
56     : d_rowpolys(row_polys),
57       d_colpolys(col_polys),
58       d_krow(krow),
59       d_kcol(kcol),
60       d_bval(bval),
61       d_qval(qval)
62 {
63     // first we operate on data chunks of get_input_size()
64     // TODO: should we verify this and throw an error if it doesn't match?  YES
65     // hwo do we do that?
66 
67     // calculate the input and output sizes
68     inputSize = (d_krow * d_kcol - (d_bval + d_qval));
69     rowEncoder_K = ceil(
70         log(d_rowpolys[0]) /
71         log(2)); // rowEncoder_K is the constraint length of the row encoder polynomial
72     rowEncoder_n = d_rowpolys.size();
73     rowEncoder_m = rowEncoder_K - 1;
74     colEncoder_K = ceil(
75         log(d_colpolys[0]) /
76         log(2)); // colEncoder_K is the constraint length of the col encoder polynomial
77     colEncoder_n = d_colpolys.size();
78     colEncoder_m = colEncoder_K - 1;
79 
80     outputSize = ((d_krow + rowEncoder_m) * rowEncoder_n) *
81                      ((d_kcol + colEncoder_m) * colEncoder_n) -
82                  d_bval;
83 
84     fp = NULL;
85     // DEBUG_PRINT("inputSize=%d outputSize=%d\n", inputSize, outputSize);
86     // fp = fopen("c_encoder_output.txt", "w");
87 
88     // resize internal matrices
89     rowNumStates = 1 << (rowEncoder_K - 1); // 2^(row_mm)
90     colNumStates = 1 << (colEncoder_K - 1); // 2^(col_mm)
91     rowOutputs.resize(2, std::vector<int>(rowNumStates, 0));
92     rowNextStates.resize(2, std::vector<int>(rowNumStates, 0));
93     colOutputs.resize(2, std::vector<int>(colNumStates, 0));
94     colNextStates.resize(2, std::vector<int>(colNumStates, 0));
95     ;
96 
97     rowTail.resize(rowNumStates, 0);
98     colTail.resize(colNumStates, 0);
99 
100     // precalculate the state transition matrix for the row polynomial
101     tpc_common::precomputeStateTransitionMatrix_RSCPoly(
102         rowNumStates, d_rowpolys, rowEncoder_K, rowEncoder_n, rowOutputs, rowNextStates);
103 
104     // calculate the tail for the row
105     tpc_common::rsc_tail(rowTail, d_rowpolys, rowNumStates, rowEncoder_m);
106 
107     // precalculate the state transition matrix for the column polynomial
108     tpc_common::precomputeStateTransitionMatrix_RSCPoly(
109         colNumStates, d_colpolys, colEncoder_K, colEncoder_n, colOutputs, colNextStates);
110     // calculate the tail for the col
111     tpc_common::rsc_tail(colTail, d_colpolys, colNumStates, colEncoder_m);
112 
113     // pre-allocate memory we use for encoding
114     inputSizeWithPad = d_bval + d_qval + inputSize;
115     inputWithPad.resize(inputSizeWithPad, 0);
116 
117     numRowsToEncode =
118         inputSizeWithPad /
119         d_krow; // this should be OK w/ integer division -- TODO: check this?
120     rowToEncode.resize(d_krow, 0);
121     rowEncoded_block.resize(d_krow + (rowEncoder_m * rowEncoder_n), 0);
122     rowEncodedBits.resize(d_kcol, std::vector<uint8_t>(rowEncoder_m * rowEncoder_n, 0));
123 
124     numColsToEncode = d_krow + (rowEncoder_m * rowEncoder_n);
125     colToEncode.resize(d_kcol, 0);
126     colEncoded_block.resize(d_kcol + (colEncoder_m * colEncoder_n), 0);
127     colEncodedBits.resize(d_krow + (rowEncoder_m * rowEncoder_n),
128                           std::vector<uint8_t>(colEncoder_m * colEncoder_n, 0));
129 }
130 
get_output_size()131 int tpc_encoder::get_output_size() { return outputSize; }
132 
get_input_size()133 int tpc_encoder::get_input_size() { return inputSize; }
134 
block_conv_encode(std::vector<uint8_t> & output,std::vector<uint8_t> input,std::vector<std::vector<int>> transOutputVec,std::vector<std::vector<int>> transNextStateVec,std::vector<int> tail,size_t KK,size_t nn)135 void tpc_encoder::block_conv_encode(std::vector<uint8_t>& output,
136                                     std::vector<uint8_t> input,
137                                     std::vector<std::vector<int>> transOutputVec,
138                                     std::vector<std::vector<int>> transNextStateVec,
139                                     std::vector<int> tail,
140                                     size_t KK,
141                                     size_t nn)
142 {
143     size_t outsym, ii, jj;
144     int state = 0;
145     size_t LL = input.size();
146 
147     std::vector<int> binVec(nn, 0);
148 
149     // encode data bits one bit at a time
150     for (ii = 0; ii < LL; ii++) {
151 
152         // determine the output symbol
153         outsym = transOutputVec[(int)input[ii]][state];
154         // determine the next state
155         state = transNextStateVec[(int)input[ii]][state];
156 
157         // Convert symbol to a binary vector
158         tpc_common::itob(binVec, outsym, nn);
159 
160         // Assign to output : TODO: investigate using memcpy for this?
161         for (jj = 0; jj < nn; jj++) {
162             output[nn * ii + jj] = binVec[jj];
163         }
164     }
165 
166     // encode tail
167     for (ii = LL; ii < LL + KK - 1; ii++) {
168 
169         // determine the output symbol
170         outsym = transOutputVec[tail[state]][state];
171         // determine the next state
172         state = transNextStateVec[tail[state]][state];
173 
174         // Convert symbol to a binary vector
175         tpc_common::itob(binVec, outsym, nn);
176 
177         // Assign to output : TODO: investigate using memcpy for this?
178         for (jj = 0; jj < nn; jj++) {
179             output[nn * ii + jj] = binVec[jj];
180         }
181     }
182 }
183 
generic_work(void * inBuffer,void * outBuffer)184 void tpc_encoder::generic_work(void* inBuffer, void* outBuffer)
185 {
186     const uint8_t* in = (const uint8_t*)inBuffer;
187     uint8_t* out = (uint8_t*)outBuffer;
188 
189     size_t ii, jj; // indexing var
190 
191     // DEBUG_PRINT_UCHAR_ARRAY(in, inputSize);
192 
193     // TODO: probably a better way to do this than memcpy?
194     memcpy(&inputWithPad[d_bval + d_qval], in, sizeof(unsigned char) * inputSize);
195 
196     // DEBUG_PRINT("Input with Pad -->\n");
197     // DEBUG_PRINT_UCHAR_ARRAY(&inputWithPad[0], inputSizeWithPad);
198     // DEBUG_PRINT_F(fp, "Input with Pad -->\n");
199     // DEBUG_PRINT_UCHAR_ARRAY_F(fp, &inputWithPad[0], inputSizeWithPad);
200 
201     // encode the row data
202     for (ii = 0; ii < numRowsToEncode; ii++) {
203         // populate rowToEncode
204         memcpy(
205             &rowToEncode[0], &inputWithPad[ii * d_krow], sizeof(unsigned char) * d_krow);
206 
207         // DEBUG_PRINT("Encoding row=[%d] -->\n",ii);
208         // DEBUG_PRINT_UCHAR_ARRAY(&rowToEncode[0], d_krow);
209         // DEBUG_PRINT_F(fp, "Encoding row=[%d] -->\n",ii);
210         // DEBUG_PRINT_UCHAR_ARRAY_F(fp, &rowToEncode[0], d_krow);
211 
212         // encode it
213         block_conv_encode(rowEncoded_block,
214                           rowToEncode,
215                           rowOutputs,
216                           rowNextStates,
217                           rowTail,
218                           rowEncoder_K,
219                           rowEncoder_n);
220 
221         // DEBUG_PRINT("Row Encoded Block=[%d] -->\n",ii);
222         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR(&rowEncoded_block[0], tmp);
223         // DEBUG_PRINT_F(fp, "Row Encoded Block=[%d] -->\n",ii);
224         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR_F(fp, &rowEncoded_block[0], tmp);
225 
226         // store only the encoded bits, b/c we read out the data in a special way
227         memcpy(&rowEncodedBits[ii][0],
228                &rowEncoded_block[d_krow],
229                sizeof(uint8_t) * (rowEncoder_m * rowEncoder_n));
230 
231         //         DEBUG_PRINT("Row Encoded Bits");
232         //         tmp = rowEncoder_m*rowEncoder_n;
233         //         DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR(&rowEncodedBits[ii][0], tmp);
234     }
235 
236     // encode the column data
237     size_t numDataColsToEncode = d_krow;
238     size_t numCheckColsToEncode = numColsToEncode - numDataColsToEncode;
239     for (ii = 0; ii < numDataColsToEncode; ii++) {
240         // populate colToEncode
241         for (jj = 0; jj < d_kcol; jj++) {
242             colToEncode[jj] = inputWithPad[jj * d_krow + ii];
243         }
244 
245         // DEBUG_PRINT("Encoding col=[%d] -->\n",ii);
246         // DEBUG_PRINT_UCHAR_ARRAY(&colToEncode[0], d_kcol);
247         // DEBUG_PRINT_F(fp, "Encoding col=[%d] -->\n",ii);
248         // DEBUG_PRINT_UCHAR_ARRAY_F(fp, &colToEncode[0], d_kcol);
249 
250         // encode it
251         block_conv_encode(colEncoded_block,
252                           colToEncode,
253                           colOutputs,
254                           colNextStates,
255                           colTail,
256                           colEncoder_K,
257                           colEncoder_n);
258 
259         // DEBUG_PRINT("Col Encoded Block=[%d] -->\n",ii);
260         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR(&colEncoded_block[0], tmp);
261         // DEBUG_PRINT_F(fp, "Col Encoded Block=[%d] -->\n",ii);
262         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR_F(fp, &colEncoded_block[0], tmp);
263 
264         // store only the encoded bits, b/c we read the data out in a special way
265         memcpy(&colEncodedBits[ii][0],
266                &colEncoded_block[d_kcol],
267                sizeof(uint8_t) * (colEncoder_m * colEncoder_n));
268 
269         //         DEBUG_PRINT("Col Encoded Bits");
270         //         tmp = colEncoder_m*colEncoder_n;
271         //         DEBUG_PRINT_FLOAT_ARRAY(&colEncodedBits[ii][0], tmp);
272     }
273 
274     // encode checks on checks (encode the row-encoded bits)
275     for (ii = 0; ii < numCheckColsToEncode; ii++) {
276         // populate colToEncode
277         for (jj = 0; jj < d_kcol; jj++) {
278             colToEncode[jj] = rowEncodedBits[jj][ii]; // indexing is weird b/c of the way
279                                                       // we declared the vector :(
280         }
281 
282         // DEBUG_PRINT("Encoding col=[%d] -->\n",ii+numDataColsToEncode);
283         // DEBUG_PRINT_UCHAR_ARRAY(&colToEncode[0], d_kcol);
284         // DEBUG_PRINT_F(fp, "Encoding col=[%d] -->\n",ii+numDataColsToEncode);
285         // DEBUG_PRINT_UCHAR_ARRAY_F(fp, &colToEncode[0], d_kcol);
286 
287         // encode it
288         block_conv_encode(colEncoded_block,
289                           colToEncode,
290                           colOutputs,
291                           colNextStates,
292                           colTail,
293                           colEncoder_K,
294                           colEncoder_n);
295 
296         // DEBUG_PRINT("Col Encoded Block=[%d] -->\n",ii+numDataColsToEncode);
297         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR(&colEncoded_block[0], tmp);
298 
299         // DEBUG_PRINT_F(fp, "Col Encoded Block=[%d] -->\n",ii+numDataColsToEncode);
300         // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR_F(fp, &colEncoded_block[0], tmp);
301 
302         // store only the encoded bits, b/c we read the data out in a special way
303         memcpy(&colEncodedBits[ii + numDataColsToEncode][0],
304                &colEncoded_block[d_kcol],
305                sizeof(uint8_t) * (colEncoder_m * colEncoder_n));
306 
307         //         DEBUG_PRINT("Col Encoded Bits");
308         //         tmp = colEncoder_m*colEncoder_n;
309         //         DEBUG_PRINT_FLOAT_ARRAY(&colEncodedBits[ii][0], tmp);
310     }
311 
312     unsigned char* inputDataPtr;
313     uint8_t* outputDataPtr = out;
314 
315     int curRowInRowEncodedBits = 0;
316     // read out the data along the rows into the "out" array
317 
318     // skip B zeros & do the first row
319     inputDataPtr = &inputWithPad[d_bval];
320     if (d_bval > d_krow) {
321         throw std::runtime_error("bval must be < krow");
322     }
323     size_t firstRowRemainingBits = d_krow - d_bval;
324     for (ii = 0; ii < firstRowRemainingBits; ii++) {
325         *outputDataPtr++ = (uint8_t)(*inputDataPtr++);
326     }
327 
328     // copy the encoded bits
329     memcpy(outputDataPtr,
330            &rowEncodedBits[curRowInRowEncodedBits++][0],
331            sizeof(uint8_t) * (rowEncoder_m * rowEncoder_n));
332 
333     outputDataPtr += (rowEncoder_m * rowEncoder_n);
334 
335     // copy out the rest of the data
336     for (ii = 1; ii < d_kcol; ii++) { // ii starts at 1, b/c we already did idx=0
337         // copy systematic bits
338         for (jj = 0; jj < d_krow; jj++) {
339             *outputDataPtr++ = (uint8_t)(*inputDataPtr++);
340         }
341 
342         // copy the encoded bits
343         memcpy(outputDataPtr,
344                &rowEncodedBits[curRowInRowEncodedBits++][0],
345                sizeof(uint8_t) * (rowEncoder_m * rowEncoder_n));
346 
347         outputDataPtr += (rowEncoder_m * rowEncoder_n);
348     }
349 
350     // copy the encoded cols
351     for (ii = 0; ii < (colEncoder_m * colEncoder_n); ii++) {
352         // copy checks
353         for (jj = 0; jj < d_krow; jj++) {
354             *outputDataPtr++ = colEncodedBits[jj][ii];
355         }
356         int kk = jj;
357         // copy checks on checks
358         for (jj = 0; jj < (rowEncoder_m * rowEncoder_n); jj++) {
359             *outputDataPtr++ = colEncodedBits[kk++][ii];
360         }
361     }
362 
363     // DEBUG_PRINT("Output\n");
364     // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR(out, outputSize);
365     // DEBUG_PRINT_F(fp, "Output\n");
366     // DEBUG_PRINT_FLOAT_ARRAY_AS_UCHAR_F(fp, out, outputSize);
367 }
368 
~tpc_encoder()369 tpc_encoder::~tpc_encoder()
370 {
371     if (fp)
372         fclose(fp);
373 }
374 
375 } // namespace fec
376 } // namespace gr
377