Updated FSF address in all files. Fixes ticket:51
[debian/gnuradio] / gr-error-correcting-codes / src / lib / libecc / code_convolutional_trellis.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 "code_convolutional_trellis.h"
28 #include <assert.h>
29 #include <iostream>
30
31 #define DO_TIME_THOUGHPUT 0
32 #define DO_PRINT_DEBUG 0
33 #define DO_PRINT_DEBUG_VARS 0
34 #define DO_PRINT_DEBUG_TERM 0
35 #define DO_PRINT_DEBUG_TERM_END 0
36 #define DO_PRINT_DEBUG_LOOKUP 0
37
38 #include <mld/mld_timer.h>
39 #include <mld/n2bs.h>
40
41 static const int g_max_block_size_bits = 10000000;
42 static const int g_max_num_streams = 10;
43 static const int g_num_bits_per_byte = 8;
44
45 /*
46  * sum_bits_mod2:
47  * sum the number of set bits, mod 2, for the output bit
48  */
49
50 char
51 code_convolutional_trellis::sum_bits_mod2
52 (memory_t in_mem,
53  size_t max_memory)
54 {
55   // there are faster ways to do this, but this works for now; could
56   // certainly do a single inline asm, which most processors provide
57   // to deal with summing the bits in an integer.
58   // this routine can be overridden by another method if desired.
59
60   char t_out_bit = (char)(in_mem & 1);
61   for (size_t r = max_memory; r > 0; r--) {
62     in_mem >>= 1;
63     t_out_bit ^= ((char)(in_mem & 1));
64   }
65   return (t_out_bit);
66 }
67
68 void
69 code_convolutional_trellis::code_convolutional_trellis_init
70 (int block_size_bits,
71  int n_code_inputs,
72  int n_code_outputs,
73  const std::vector<int>& code_generators,
74  const std::vector<int>* code_feedback,
75  bool do_termination,
76  int end_memory_state)
77 {
78   // do error checking on the input arguments
79
80   // make sure the block length makes sense
81
82   if ((block_size_bits < 0) | (block_size_bits > g_max_block_size_bits)) {
83     std::cerr << "code_convolutional_trellis: " <<
84       "Requested block length (" << block_size_bits <<
85       " bits) must be between 0 and " << g_max_block_size_bits <<
86       " bits, with 0 being a streaming encoder.\n";
87     assert (0);
88   }
89
90   // check to make sure the number of input streams makes sense
91
92   if ((n_code_inputs <= 0) | (n_code_inputs > g_max_num_streams)) {
93     std::cerr << "code_convolutional_trellis: " <<
94       "Requested number of input streams (" <<
95       n_code_inputs << ") must be between 1 and " <<
96       g_max_num_streams << ".\n";
97     assert (0);
98   }
99
100   // check to make sure the number of output streams makes sense
101
102   if ((n_code_outputs <= 0) | (n_code_outputs > g_max_num_streams)) {
103     std::cerr << "code_convolutional_trellis: " <<
104       "Requested number of output streams (" <<
105       n_code_outputs << ") must be between 1 and " <<
106       g_max_num_streams << ".\n";
107     assert (0);
108   }
109
110   // make sure the code_generator is the correct length
111
112   if (code_generators.size () !=
113       ((size_t)(n_code_inputs * n_code_outputs))) {
114     std::cerr << "code_convolutional_trellis: " <<
115       "Number of code generator entries (" << code_generators.size () <<
116       ") is not equal to the product of the number of input and output" <<
117       " streams (" << (n_code_inputs * n_code_outputs) << ").\n";
118     assert (0);
119   }
120
121   // check for feedback (== NULL or not)
122
123   d_do_feedback = (code_feedback != NULL);
124
125   // create the class block variables
126
127   d_block_size_bits = block_size_bits;
128   d_n_code_inputs = n_code_inputs;
129   d_n_code_outputs = n_code_outputs;
130   d_do_streaming = (block_size_bits == 0);
131   d_do_termination = (d_do_streaming == true) ? false : do_termination;
132
133   if (DO_PRINT_DEBUG_VARS) {
134     std::cout <<
135       "d_block_size_bits = " << d_block_size_bits << "\n"
136       "d_n_code_inputs   = " << d_n_code_inputs << "\n"
137       "d_n_code_outputs  = " << d_n_code_outputs << "\n"
138       "d_do_streaming    = " <<
139       ((d_do_streaming == true) ? "true" : "false") << "\n"
140       "d_do_termination  = " <<
141       ((d_do_termination == true) ? "true" : "false") << "\n"
142       "d_do_feedback     = " <<
143       ((d_do_feedback == true) ? "true" : "false") << "\n";
144   }
145
146   // allocate the vectors for doing the encoding.  use memory_t (an
147   // interger type, at least 32 bits) bits to represent memory and the
148   // code, as it makes the operations quite simple the state vectors.
149
150   // d_states is a "matrix" [#input by #outputs] containing indices
151   // to memory_t's; this is done to make feedback function properly,
152   // and doesn't effect the computation time for feedforward.  The
153   // issue is that any code with the same feedback can use the same
154   // memory - thus reducing the actual number of memories required.
155   // These overlapping encoders will use the same actual memory, but
156   // given that there is no way to know a-priori where they are, use
157   // pointers over the full I/O matrix-space to make sure each I/O
158   // encoder uses the correct memory.
159   // reference the matrix using "maoi(i,o)" ... see .h file.
160
161   // code generators (feedforward part) are [#inputs x #outputs],
162   // always - one for each I/O combination.
163   // reference the matrix using "maoi(i,o)" ... see .h file
164
165   d_code_generators.assign (d_n_code_inputs * d_n_code_outputs, 0);
166
167   // check the feedback for correctness, before anything else, since
168   // any feedback impacts the total # of delays in the encoder:
169   // without feedback, this is the sum of the individual delays max'ed
170   // over each input (if siao) or output (if soai).
171
172   if (d_do_feedback == true) {
173     memory_t t_OR_all_feedback = 0;
174     for (size_t n = 0; n < d_n_code_outputs; n++) {
175       for (size_t m = 0; m < d_n_code_inputs; m++) {
176         memory_t t_in_code = (*code_feedback)[maoi(m,n)];
177
178         // OR this feedback with the overall,
179         // to check for any delays used at all
180
181         t_OR_all_feedback |= t_in_code;
182       }
183     }
184
185     // check to see if all the feedback entries were either "0" or "1",
186     // which implies no feedback; warn the user in that case and reset
187     // the do_feedback parameter to false.
188
189     if ((t_OR_all_feedback | 1) == 1) {
190       std::cout << "code_convolutional_trellis: Warning: " <<
191         "No feedback is required, ignoring feedback.\n";
192       d_do_feedback = false;
193     }
194   }
195
196   // copy over the FF code generators
197
198   for (size_t n = 0; n < d_n_code_outputs; n++)
199     for (size_t m = 0; m < d_n_code_inputs; m++)
200       d_code_generators[maio(m,n)] = code_generators[maio(m,n)];
201
202   // check the input FF (and FB) code generators for correctness, and
203   // find the minimum memory configuration: combining via a single
204   // input / all outputs (SIAO), or a single output / all inputs (SOAI).
205   //
206   // for FF only, look over both the SOAI and SIAO realizations to
207   // find the minimum total # of delays, and use that realization
208   // (SOAI is preferred if total # of delays is equal, since it's much
209   // simpler to implement).
210   //
211   // for FB:
212   //   for SIAO, check each input row (all outputs for a given input)
213   //     for unique feedback; duplicate feedback entries can be
214   //     combined into a single computation to reduce total # of delays.
215   //   for SOAI: check each output column (all inputs for a given
216   //     output) for unique feedback; duplicate feedback entries can
217   //     be combined into a simgle computation (ditto).
218
219   // check for SOAI all '0' output
220
221   for (size_t n = 0; n < d_n_code_outputs; n++) {
222     memory_t t_all_inputs_zero = 0;
223     for (size_t m = 0; m < d_n_code_inputs; m++)
224       t_all_inputs_zero |= d_code_generators[maio(m,n)];
225
226     // check this input to see if all encoders were '0'; this might be
227     // OK for some codes, but warn the user just in case
228
229     if (t_all_inputs_zero == 0) {
230       std::cout << "code_convolutional_trellis: Warning:"
231         "Output " << n+1 << " (of " << d_n_code_outputs <<
232         ") will always be 0.\n";
233     }
234   }
235
236   // check for SIAO all '0' input
237
238   for (size_t m = 0; m < d_n_code_inputs; m++) {
239     memory_t t_all_outputs_zero = 0;
240     for (size_t n = 0; n < d_n_code_outputs; n++)
241       t_all_outputs_zero |= d_code_generators[maio(m,n)];
242
243     // check this input to see if all encoders were '0'; this might be
244     // OK for some codes, but warn the user just in case
245
246     if (t_all_outputs_zero == 0) {
247       std::cout << "code_convolutional_trellis: Warning:"
248         "Input " << m+1 << " (of " << d_n_code_inputs <<
249         ") will not be used; all encoders are '0'.\n";
250     }
251   }
252
253   // check and compute memory requirements in order to determine which
254   // realization uses the least memory; create and save findings to
255   // not have to re-do these computations later.
256
257   // single output, all inputs (SOAI) realization:
258   // reset the global parameters
259
260   d_code_feedback.assign (d_n_code_inputs * d_n_code_outputs, 0);
261   d_n_delays.assign (d_n_code_inputs * d_n_code_outputs, 0);
262   d_io_num.assign (d_n_code_inputs * d_n_code_outputs, 0);
263   d_states_ndx.assign (d_n_code_inputs * d_n_code_outputs, 0);
264   d_max_delay = d_total_n_delays = d_n_memories = 0;
265   d_do_encode_soai = true;
266
267   for (size_t n = 0; n < d_n_code_outputs; n++) {
268     size_t t_max_mem = 0;
269     size_t t_n_unique_fb_prev_start = d_n_memories;
270
271     for (size_t m = 0; m < d_n_code_inputs; m++) {
272       get_memory_requirements (m, n, t_max_mem,
273                                t_n_unique_fb_prev_start, code_feedback);
274       if (d_do_feedback == false) {
275         d_states_ndx[maio(m,n)] = n;
276       }
277     }
278     if (d_do_feedback == false) {
279       // not feedback; just store memory requirements for this output
280       d_total_n_delays += t_max_mem;
281       d_n_delays[n] = t_max_mem;
282       d_io_num[n] = n;
283     }
284   }
285   if (d_do_feedback == false) {
286     d_n_memories = d_n_code_outputs;
287   }
288
289   // store the parameters for SOAI
290
291   std::vector<memory_t> t_fb_generators_soai;
292   std::vector<size_t>  t_n_delays_soai, t_io_num_soai;
293   std::vector<size_t> t_states_ndx_soai;
294   size_t t_max_delay_soai, t_total_n_delays_soai, t_n_memories_soai;
295
296   t_fb_generators_soai.assign (d_code_feedback.size (), 0);
297   t_fb_generators_soai = d_code_feedback;
298   t_n_delays_soai.assign (d_n_delays.size (), 0);
299   t_n_delays_soai = d_n_delays;
300   t_io_num_soai.assign (d_io_num.size (), 0);
301   t_io_num_soai = d_io_num;
302   t_states_ndx_soai.assign (d_states_ndx.size (), 0);
303   t_states_ndx_soai = d_states_ndx;
304
305   t_n_memories_soai = d_n_memories;
306   t_total_n_delays_soai = d_total_n_delays;
307   t_max_delay_soai = d_max_delay;
308
309   // single input, all outputs (SIAO) realization
310   // reset the global parameters
311
312   d_code_feedback.assign (d_n_code_inputs * d_n_code_outputs, 0);
313   d_n_delays.assign (d_n_code_inputs * d_n_code_outputs, 0);
314   d_io_num.assign (d_n_code_inputs * d_n_code_outputs, 0);
315   d_states_ndx.assign (d_n_code_inputs * d_n_code_outputs, 0);
316   d_max_delay = d_total_n_delays = d_n_memories = 0;
317   d_do_encode_soai = false;
318
319   for (size_t m = 0; m < d_n_code_inputs; m++) {
320     size_t t_max_mem = 0;
321     size_t t_n_unique_fb_prev_start = d_n_memories;
322
323     for (size_t n = 0; n < d_n_code_outputs; n++) {
324       get_memory_requirements (m, n, t_max_mem,
325                                t_n_unique_fb_prev_start, code_feedback);
326       if (d_do_feedback == false) {
327         d_states_ndx[maio(m,n)] = m;
328       }
329     }
330     if (d_do_feedback == false) {
331       // not feedback; just store memory requirements for this output
332       d_total_n_delays += t_max_mem;
333       d_n_delays[m] = t_max_mem;
334       d_io_num[m] = m;
335     }
336   }
337   if (d_do_feedback == false) {
338     d_n_memories = d_n_code_inputs;
339   }
340
341   if (DO_PRINT_DEBUG_VARS) {
342     std::cout <<
343       "  t_total_n_delays_siao  = " << d_total_n_delays << "\n"
344       "  t_total_n_delays_soai  = " << t_total_n_delays_soai << "\n";
345   }
346
347   // pick which realization to use;
348   // siao is preferred since it's easier to debug, and more likely
349
350   if (d_total_n_delays <= t_total_n_delays_soai) {
351     // use siao
352     // nothing else to do, since the global variables already hold
353     // the correct values.
354   } else {
355     // use soai
356     d_do_encode_soai = true;
357     d_code_feedback = t_fb_generators_soai;
358     d_n_delays = t_n_delays_soai;
359     d_io_num = t_io_num_soai;
360     d_states_ndx = t_states_ndx_soai;
361     d_n_memories = t_n_memories_soai;
362     d_total_n_delays = t_total_n_delays_soai;
363     d_max_delay = t_max_delay_soai;
364   }
365
366   // make sure the block length makes sense, #2
367
368   if ((d_do_streaming == false) & (d_block_size_bits < d_max_delay)) {
369     std::cerr << "code_convolutional_trellis: " <<
370       "Requested block length (" << d_block_size_bits <<
371       " bit" << (d_block_size_bits > 1 ? "s" : "") <<
372       ") must be at least 1 memory length (" << d_max_delay <<
373       " bit" << (d_max_delay > 1 ? "s" : "") <<
374       " for this code) when doing block coding.\n";
375     assert (0);
376   }
377
378   // check & mask off the init states
379
380   d_n_states = (1 << d_total_n_delays);
381   d_n_input_combinations = (1 << d_n_code_inputs);
382
383   if (DO_PRINT_DEBUG_VARS) {
384     std::cout <<
385       "  d_n_states             = " << d_n_states << "\n"
386       "  d_n_input_combinations = " << d_n_input_combinations << "\n";
387   }
388
389   if ((d_do_feedback == true) & (d_do_encode_soai == true)) {
390     // create the individual output bits used in soai feedback
391
392     d_ind_outputs.assign (d_n_memories, 0);
393   }
394
395   // create the max_mem_mask to be used in encoding
396
397   d_max_mem_masks.assign (d_n_memories, 0);
398
399   for (size_t m = 0; m < d_n_memories; m++) {
400     if (d_n_delays[m] == sizeof (memory_t) * g_num_bits_per_byte)
401       d_max_mem_masks[m] = ((memory_t) -1);
402     else
403       d_max_mem_masks[m] = (memory_t)((2 << (d_n_delays[m])) - 1);
404   }
405
406   if (DO_PRINT_DEBUG_VARS) {
407     std::cout <<
408       "  d_n_memories      = " << d_n_memories << "\n"
409       "  d_total_n_delays  = " << d_total_n_delays << " : [";
410     for (size_t m = 0; m < d_n_memories; m++) {
411       std::cout << d_n_delays[m];
412       if (m != (d_n_memories-1))
413         std::cout << ", ";
414     }
415     std::cout << "]\n" <<
416       "  d_max_delay       = " << d_max_delay << "\n"
417       "  d_do_encode_soai  = " << 
418       ((d_do_encode_soai == true) ? "true" : "false") << "\n";
419   }
420
421   // zero the memories
422
423   d_memory.assign (d_n_memories, 0);
424
425   // create the inputs and outputs buffers
426
427   d_current_inputs.assign (d_n_code_inputs, 0);
428   d_current_outputs.assign (d_n_code_outputs, 0);
429
430   // create the trellis for this code:
431
432   memory_t t_mask = (memory_t)((1 << d_total_n_delays) - 1);
433   memory_t t_end_memory_state = (memory_t) end_memory_state;
434
435   if (t_end_memory_state != (t_end_memory_state & t_mask)) {
436     std::cout << "code_convolutional_trellis: Warning: " <<
437       "provided end memory state out (" << end_memory_state <<
438       ") is out of the state range [0, " <<
439       (d_n_states-1) << "]; masking off the unused bits.\n";
440
441     end_memory_state &= t_mask;
442   }
443
444   create_trellis ();
445
446   if (d_do_termination == true) {
447
448     // create the termination lookup table
449
450     create_termination_table (end_memory_state);
451   }
452 }
453
454 void
455 code_convolutional_trellis::get_memory_requirements
456 (size_t m,   // input number
457  size_t n,   // output number
458  size_t& t_max_mem,
459  size_t& t_n_unique_fb_prev_start,
460  const std::vector<int>* code_feedback)
461 {
462   size_t t_in_code = d_code_generators[maio(m,n)];
463
464   // find the memory requirement for this code generator
465
466   size_t t_code_mem_ff = max_bit_position (t_in_code);
467
468   // check to see if this is bigger than any others in this row/column
469
470   if (t_code_mem_ff > t_max_mem)
471     t_max_mem = t_code_mem_ff;
472
473   if (DO_PRINT_DEBUG) {
474     std::cout << "c_g[" << m << "][" << n << "]{" <<
475       maio(m,n) << "} = " << n2bs(t_in_code, 8) <<
476       ", code_mem = " << t_code_mem_ff;
477   }
478
479   // check the feedback portion, if it exists;
480   // for soai, check all the inputs which generate this output for
481   // uniqueness; duplicate entries can be combined to reduce total
482   // # of memories as well as required computations.
483
484   if (d_do_feedback == true) {
485     if (DO_PRINT_DEBUG) {
486       std::cout << "\n";
487     }
488
489     // get the FB code; AND off the LSB for correct functionality
490     // during internal computations.
491
492     t_in_code = ((memory_t)((*code_feedback)[maio(m,n)]));
493     t_in_code &= ((memory_t)(-2));
494
495     // find the memory requirement
496
497     size_t t_code_mem_fb = max_bit_position (t_in_code);
498
499     if (DO_PRINT_DEBUG) {
500       std::cout << "c_f[" << m << "][" << n << "]{" <<
501         maio(m,n) << "} = " << n2bs(t_in_code, 8) <<
502         ", code_mem = " << t_code_mem_fb;
503     }
504
505     // check to see if this feedback is unique
506
507     size_t l_n_unique_fb = t_n_unique_fb_prev_start;
508     while (l_n_unique_fb < d_n_memories) {
509       if (d_code_feedback[l_n_unique_fb] == t_in_code)
510         break;
511       l_n_unique_fb++;
512     }
513     if (l_n_unique_fb == d_n_memories) {
514
515       // this is a unique feedback;
516
517       d_code_feedback[l_n_unique_fb] = t_in_code;
518       d_n_delays[l_n_unique_fb] = t_code_mem_fb;
519
520       // increase the number of unique feedback codes
521
522       d_n_memories++;
523
524       // store memory requirements for this output
525
526       if (t_max_mem < t_code_mem_fb)
527         t_max_mem = t_code_mem_fb;
528       d_total_n_delays += t_max_mem;
529
530       if (DO_PRINT_DEBUG) {
531         std::cout << ",  uq # " << l_n_unique_fb <<
532           ", tot_mem = " << d_total_n_delays;
533       }
534     } else {
535       // not a unique feedback, but the FF might require more memory
536
537       if (DO_PRINT_DEBUG) {
538         std::cout << ", !uq # " << l_n_unique_fb <<
539           " = " << d_n_delays[l_n_unique_fb];
540       }
541
542       if (d_n_delays[l_n_unique_fb] < t_code_mem_ff) {
543         d_total_n_delays += (t_code_mem_ff - d_n_delays[l_n_unique_fb]);
544         d_n_delays[l_n_unique_fb] = t_code_mem_ff;
545
546         if (DO_PRINT_DEBUG) {
547           std::cout << " => " << d_n_delays[l_n_unique_fb] <<
548             ", tot_mem = " << d_total_n_delays;
549         }
550       }
551     }
552     d_io_num[l_n_unique_fb] = ((d_do_encode_soai == true) ? n : m);
553     d_states_ndx[maio(m,n)] = l_n_unique_fb;
554   }
555   if (DO_PRINT_DEBUG) {
556     std::cout << "\n";
557   }
558   if (d_max_delay < t_max_mem)
559     d_max_delay = t_max_mem;
560 }
561
562 void
563 code_convolutional_trellis::create_trellis
564 ()
565 {
566   // first dimension is the number of states
567
568   d_trellis.resize (d_n_states);
569
570   // second dimension (one per first dimension) is the number of input
571   // combinations
572
573   for (size_t m = 0; m < d_n_states; m++) {
574     d_trellis[m].resize (d_n_input_combinations);
575   }
576
577   // fill in the trellis
578
579   for (size_t m = 0; m < d_n_states; m++) {
580     for (size_t n = 0; n < d_n_input_combinations; n++) {
581       connection_t_ptr t_connection = &(d_trellis[m][n]);
582       encode_single ((memory_t) m,
583                      (memory_t) n,
584                      t_connection->d_to_state,
585                      t_connection->d_output_bits);
586     }
587   }
588 }
589
590 void
591 code_convolutional_trellis::demux_state
592 (memory_t in_state,
593  std::vector<memory_t>& memories)
594 {
595   // de-mux bits for the given memory state;
596   // copy them into the provided vector;
597   // assumes state bits start after the LSB (not at &1)
598
599   memories.resize (d_n_memories);
600   for (size_t m = 0; m < d_n_memories; m++) {
601     memories[m] = (in_state << 1) & d_max_mem_masks[m];
602     in_state >>= d_n_delays[m];
603   }
604 }
605
606 memory_t
607 code_convolutional_trellis::mux_state
608 (const std::vector<memory_t>& memories)
609 {
610   // mux bits for the given memory states in d_memory
611   // assumes state bits start after the LSB (not at &1)
612
613   memory_t t_state = 0;
614   size_t shift = 0;
615   for (size_t m = 0; m < d_n_memories; m++) {
616     t_state |= (memories[m] >> 1) << shift;
617     shift += d_n_delays[m];
618   }
619   return (t_state);
620 }
621
622 void
623 code_convolutional_trellis::demux_inputs
624 (memory_t inputs,
625  std::vector<char>& in_vec)
626 {
627   // de-mux bits for the given inputs;
628   // copy them into the provided vector;
629
630   for (size_t m = 0; m < d_n_code_inputs; m++, inputs >>= 1) {
631     in_vec[m] = (char)(inputs & 1);
632   }
633 }
634
635 memory_t
636 code_convolutional_trellis::mux_inputs
637 (const std::vector<char>& in_vec)
638 {
639   // mux bits for the given inputs
640
641   size_t bit_shift = 0;
642   memory_t inputs = 0;
643   for (size_t m = 0; m < in_vec.size(); m++, bit_shift++) {
644     inputs |= (((memory_t)(in_vec[m]&1)) << bit_shift);
645   }
646   return (inputs);
647 }
648
649 void
650 code_convolutional_trellis::demux_outputs
651 (memory_t outputs,
652  std::vector<char>& out_vec)
653 {
654   // de-mux bits for the given outputs;
655   // copy them into the provided vector;
656
657   for (size_t m = 0; m < d_n_code_outputs; m++, outputs >>= 1) {
658     out_vec[m] = (char)(outputs & 1);
659   }
660 }
661
662 memory_t
663 code_convolutional_trellis::mux_outputs
664 (const std::vector<char>& out_vec)
665 {
666   // mux bits for the given outputs
667
668   size_t bit_shift = 0;
669   memory_t outputs = 0;
670   for (size_t m = 0; m < out_vec.size(); m++, bit_shift++) {
671     outputs |= (((memory_t)(out_vec[m]&1)) << bit_shift);
672   }
673   return (outputs);
674 }
675
676 void
677 code_convolutional_trellis::encode_single
678 (memory_t in_state,
679  memory_t inputs,
680  memory_t& out_state,
681  memory_t& out_bits)
682 {
683   // set input parameters
684
685   demux_state (in_state, d_memory);
686   demux_inputs (inputs, d_current_inputs);
687
688   // call the correct function to do the work
689
690   if (d_do_encode_soai == true) {
691     if (d_do_feedback == true) {
692       encode_single_soai_fb ();
693     } else {
694       encode_single_soai ();
695     }
696   } else {
697     if (d_do_feedback == true) {
698       encode_single_siao_fb ();
699     } else {
700       encode_single_siao ();
701     }
702   }
703
704   // retrieve the output parameters
705
706   out_state = mux_state (d_memory);
707   out_bits = mux_outputs (d_current_outputs);
708 }
709
710 void
711 code_convolutional_trellis::encode_lookup
712 (memory_t& state,
713  const std::vector<char>& inputs,
714  memory_t& out_bits)
715 {
716   if (DO_PRINT_DEBUG_LOOKUP) {
717     std::cout << "e_l: in_st = " << n2bs(state,d_total_n_delays) <<
718       ", in = " << n2bs(mux_inputs(inputs),d_n_code_inputs);
719   }
720
721   connection_t_ptr t_connection = &(d_trellis[state][mux_inputs(inputs)]);
722   state = t_connection->d_to_state;
723   out_bits = t_connection->d_output_bits;
724
725   if (DO_PRINT_DEBUG_LOOKUP) {
726     std::cout << " -> out_st = " << n2bs(state,d_total_n_delays) <<
727       ", out = " << n2bs(out_bits, d_n_code_outputs) << "\n";
728   }
729 }
730
731 void
732 code_convolutional_trellis::encode_lookup
733 (memory_t& state,
734  const std::vector<char>& inputs,
735  std::vector<char>& out_bits)
736 {
737   if (DO_PRINT_DEBUG_LOOKUP) {
738     std::cout << "e_l: in_st = " << n2bs(state,d_total_n_delays) <<
739       ", in = " << n2bs(mux_inputs(inputs),d_n_code_inputs);
740   }
741
742   connection_t_ptr t_connection = &(d_trellis[state][mux_inputs(inputs)]);
743   state = t_connection->d_to_state;
744   demux_outputs (t_connection->d_output_bits, out_bits);
745
746   if (DO_PRINT_DEBUG_LOOKUP) {
747     std::cout << " -> out_st = " << n2bs(state,d_total_n_delays) <<
748       ", out = " << n2bs(t_connection->d_output_bits,
749                          d_n_code_outputs) << "\n";
750   }
751 }
752
753 void
754 code_convolutional_trellis::create_termination_table
755 (memory_t end_memory_state)
756 {
757   // somewhat involved, but basically start with the terminating state
758   // and work backwards d_total_n_delays, then create a
759   // std::vector<memory_t> of length n_states, once per path required
760   // to get from the given state to the desired termination state.
761   //
762   // each entry represents the bits required to terminate that
763   // particular state, listed in order from LSB for the first input
764   // bit to the MSB for the last input bit.
765
766   // create a reverse trellis
767   // it's temporary, just for doing the termination, so just do it locally
768
769   trellis_t t_trellis;
770
771   // first dimension is the number of states
772
773   t_trellis.resize (d_n_states);
774
775   // second dimension (one per first dimension) is the number of input
776   // combinations; reserve so that the size() can be used for adding new
777
778   for (size_t m = 0; m < d_n_states; m++) {
779     t_trellis[m].reserve (d_n_input_combinations);
780   }
781
782   std::vector<memory_t> tmp(d_n_memories);
783   demux_state (end_memory_state, tmp);
784
785   memory_t to_state, outputs;
786   connection_t t_conn;
787
788   // fill in the trellis; discard the outputs
789   // set the trellis node's output bits to the input
790
791   for (size_t m = 0; m < d_n_states; m++) {
792     for (size_t n = 0; n < d_n_input_combinations; n++) {
793       to_state = outputs = 0;
794
795       encode_single ((memory_t) m,
796                      (memory_t) n,
797                      to_state,
798                      outputs);
799
800       t_conn.d_to_state = (memory_t) m;
801       t_conn.d_output_bits = (memory_t) n;
802       t_trellis[to_state].push_back (t_conn);
803
804       if (DO_PRINT_DEBUG_TERM) {
805         std::cout << "[" << n2bs(m,d_total_n_delays) << "][" <<
806           n2bs(n,d_n_code_inputs) << "] -> " <<
807           n2bs(to_state, d_total_n_delays) << "\n";
808       }
809     }
810   }
811
812   if (DO_PRINT_DEBUG_TERM) {
813     std::cout << "Trellis:\n";
814
815     for (size_t m = 0; m < d_n_states; m++) {
816       for (size_t n = 0; n < d_n_input_combinations; n++) {
817         std::cout << "[" << n2bs(t_trellis[m][n].d_to_state,
818                                  d_total_n_delays) << "] : [" <<
819           n2bs(t_trellis[m][n].d_output_bits, d_n_code_inputs) << "] -> " <<
820           n2bs(m, d_total_n_delays) << "\n";
821       }
822     }
823   }
824
825   // need 2 of most buffers: one for the current-in-use variables, and
826   // one for the to-be-determined variables
827
828   // create the term input bit vectors
829
830   term_input_t t_term_inputs[2];
831   t_term_inputs[0].resize (d_n_states);
832   for (size_t m = 0; m < d_n_states; m++) {
833     t_term_inputs[0][m].assign (d_n_code_inputs, 0);
834   }
835   t_term_inputs[1].resize (d_n_states);
836   for (size_t m = 0; m < d_n_states; m++) {
837     t_term_inputs[1][m].assign (d_n_code_inputs, 0);
838   }
839
840   // create the list of "in-use" states for the current t_term_inputs
841
842   std::vector<size_t> t_used_states_ndx[2];
843   t_used_states_ndx[0].assign (d_n_states, 0);
844   t_used_states_ndx[1].assign (d_n_states, 0);
845
846   std::vector<bool> t_in_use_states[2];
847   t_in_use_states[0].assign (d_n_states, false);
848   t_in_use_states[1].assign (d_n_states, false);
849
850   // termporary 'inputs' place holder, in order to use the class's
851   // built-in inputs demux'er.
852
853   std::vector<char> t_inputs;
854   t_inputs.assign (d_n_code_inputs, 0);
855
856   // setup the first state
857
858   size_t t_which_input = 0;
859   t_used_states_ndx[t_which_input][0] = (size_t) end_memory_state;
860   t_in_use_states[t_which_input][(size_t) end_memory_state] = true;
861   size_t n_states_used[2];
862   n_states_used[t_which_input] = 1;
863   n_states_used[t_which_input^1] = 0;
864
865   // loop until either the number of states has been reached, or the
866   // number of input term bits (per stream) is too large (in which
867   // case this code can't be terminated ... shouldn't happen, but it's
868   // here just in case.
869
870   size_t n_input_term_bits = 0;
871
872   while ((n_states_used[t_which_input] < d_n_states) &
873          (n_input_term_bits < 2*d_total_n_delays)) {
874
875     if (DO_PRINT_DEBUG_TERM) {
876       std::cout << "Starting loop:\n# states in use = " <<
877         n_states_used[t_which_input] << " (of " << d_n_states <<
878         "), # term bits = " << n_input_term_bits << " (of between " <<
879         d_total_n_delays << " and " << (2*d_total_n_delays) << ")\n";
880     }
881
882     // loop over all current in-use states
883
884     for (size_t m = 0; m < n_states_used[t_which_input]; m++) {
885
886       // get the current state to work with
887
888       size_t t_state_ndx = t_used_states_ndx[t_which_input][m];
889
890       for (size_t p = 0; p < d_n_input_combinations; p++) {
891         memory_t t_from_state = t_trellis[t_state_ndx][p].d_to_state;
892         if (t_in_use_states[t_which_input^1][t_from_state] == false) {
893           // not currently in use; make use of it
894           // if it's already in use, then why duplicate the inputs?
895
896           memory_t t_input = t_trellis[t_state_ndx][p].d_output_bits;
897
898           if (DO_PRINT_DEBUG_TERM) {
899             std::cout << "doing st[" << n2bs(t_state_ndx,d_total_n_delays) <<
900               "] <- [" << n2bs(t_from_state,d_total_n_delays) << "]: in = " <<
901               n2bs(t_input, d_n_code_inputs) << "\n";
902           }
903
904           // copy over the current state's input bits to the 'from'
905           // state's input bits, in the "current" term inputs
906
907           t_term_inputs[t_which_input^1][t_from_state] =
908             t_term_inputs[t_which_input][t_state_ndx];
909
910           // update the copied bits with the current inputs, in the
911           // correct bit position: LSB (&1) -> first input, LSB+1 (&2)
912           // -> second input, etc...
913
914           demux_inputs (t_input, t_inputs);
915
916           for (size_t n = 0; n < d_n_code_inputs; n++) {
917             memory_t t_term = t_term_inputs[t_which_input^1][t_from_state][n];
918             t_term <<= 1;
919             t_term |= ((memory_t)(t_inputs[n] & 1));
920             t_term_inputs[t_which_input^1][t_from_state][n] = t_term;
921           }
922
923           // add this from state to the list of states for the next run
924
925           t_used_states_ndx[t_which_input^1][n_states_used[t_which_input^1]] =
926             t_from_state;
927
928           // and set that this state is in use
929
930           t_in_use_states[t_which_input^1][t_from_state] = true;
931
932           // increase the number of next used states
933
934           n_states_used[t_which_input^1] += 1;
935         }
936       }
937     }
938
939     // update / reset variables for this run-through
940
941     // swap buffers ("^1" is always the next set of buffers)
942
943     t_which_input ^= 1;
944
945     // zero the next # of states used
946
947     n_states_used[t_which_input^1] = 0;
948
949     // reset the next 'in use' buffer
950
951     t_in_use_states[t_which_input^1].assign (d_n_states, false);
952
953     // increase the number of required term bits (per stream)
954
955     n_input_term_bits++;
956   }
957
958   if (n_states_used[t_which_input] != d_n_states) {
959     std::cerr << "code_convolutional_trellis::create_termination_table: "
960       "Warning: Unable to determine all required termination inputs for the "
961       "provided termination state.  Turning termination off.\n";
962     d_do_termination = false;
963   } else {
964     d_n_bits_to_term = n_input_term_bits;
965
966     d_term_inputs.resize (d_n_states);
967     for (size_t m = 0; m < d_n_states; m++) {
968       d_term_inputs[m].assign (d_n_code_inputs, 0);
969     }
970
971     d_term_inputs = t_term_inputs[t_which_input];
972
973     if (DO_PRINT_DEBUG_TERM_END) {
974       std::cout << "# Term inputs / stream = " << d_n_bits_to_term << "\n";
975
976       for (size_t m = 0; m < d_n_states; m++) {
977         for (size_t n = 0; n < d_n_code_inputs; n++) {
978           std::cout << " [" << n2bs(m,d_total_n_delays) << "][" << n <<
979             "] = " << n2bs(d_term_inputs[m][n], d_n_bits_to_term) << "\n";
980         }
981       }
982     }
983   }
984 }
985
986 void
987 code_convolutional_trellis::encode_single_soai
988 ()
989 {
990   // single-output, all inputs; no feedback
991
992   if (DO_PRINT_DEBUG) {
993     std::cout << "Starting encode_single_soai.\n";
994   }
995
996   // shift memories down by 1 bit to make room for feedback; no
997   // masking required.
998
999   for (size_t p = 0; p < d_n_memories; p++) {
1000     if (DO_PRINT_DEBUG) {
1001       std::cout << "m_i[" << p << "] = " <<
1002         n2bs(d_memory[p], 1+d_n_delays[p]);
1003     }
1004
1005     d_memory[p] >>= 1;
1006
1007     if (DO_PRINT_DEBUG) {
1008       std::cout << "  >>= 1 -> " <<
1009         n2bs(d_memory[p], 1+d_n_delays[p]) << "\n";
1010     }
1011   }
1012
1013   // for each input bit, if that bit's a '1', then XOR the code
1014   // generators into the correct state's memory.
1015
1016   for (size_t m = 0; m < d_n_code_inputs; m++) {
1017     if (DO_PRINT_DEBUG) {
1018       std::cout << "c_i[" << m << "] = " <<
1019         n2bs(d_current_inputs[m],1);
1020     }
1021     if (d_current_inputs[m] == 1) {
1022       if (DO_PRINT_DEBUG) {
1023         std::cout << "\n";
1024       }
1025       for (size_t n = 0; n < d_n_code_outputs; n++) {
1026         if (DO_PRINT_DEBUG) {
1027           std::cout << "m_i[s_ndx[" << m << "][" << n << "] == " <<
1028             d_states_ndx[maio(m,n)] << "] = " <<
1029             n2bs(d_memory[d_states_ndx[maio(m,n)]],
1030                  1+d_n_delays[d_states_ndx[maio(m,n)]]);
1031         }
1032
1033         d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
1034
1035         if (DO_PRINT_DEBUG) {
1036           std::cout << " ^= c_g[][] == " <<
1037             n2bs(d_code_generators[maio(m,n)],
1038                  1+d_n_delays[d_states_ndx[maio(m,n)]]) <<
1039             " -> " << n2bs(d_memory[d_states_ndx[maio(m,n)]],
1040                            1+d_n_delays[d_states_ndx[maio(m,n)]]) << "\n";
1041         }
1042       }
1043     } else if (DO_PRINT_DEBUG) {
1044       std::cout << " ... nothing to do\n";
1045     }
1046   }
1047
1048   for (size_t p = 0; p < d_n_code_outputs; p++) {
1049     d_current_outputs[p] = 0;
1050   }
1051
1052   // create the output bits, by XOR'ing the individual unique
1053   // memory(ies) into the correct output bit
1054
1055   for (size_t p = 0; p < d_n_memories; p++) {
1056     d_current_outputs[d_io_num[p]] ^= ((char)(d_memory[p] & 1));
1057   }
1058
1059   if (DO_PRINT_DEBUG) {
1060     std::cout << "ending encode_single_soai.\n";
1061   }
1062 }
1063
1064 void
1065 code_convolutional_trellis::encode_single_soai_fb
1066 ()
1067 {
1068   // single-output, all inputs; with feedback
1069
1070   if (DO_PRINT_DEBUG) {
1071     std::cout << "Starting encode_single_soai_fb.\n";
1072   }
1073
1074   // shift memories down by 1 bit to make room for feedback; no
1075   // masking required.
1076
1077   for (size_t p = 0; p < d_n_memories; p++) {
1078     if (DO_PRINT_DEBUG) {
1079       std::cout << "m_i[" << p << "] = " <<
1080         n2bs(d_memory[p], 1+d_n_delays[p]);
1081     }
1082
1083     d_memory[p] >>= 1;
1084
1085     if (DO_PRINT_DEBUG) {
1086       std::cout << " >>= 1 -> " <<
1087         n2bs(d_memory[p], 1+d_n_delays[p]) << "\n";
1088     }
1089   }
1090
1091   // for each input bit, if that bit's a '1', then XOR the code
1092   // generators into the correct state's memory.
1093
1094   for (size_t m = 0; m < d_n_code_inputs; m++) {
1095     if (DO_PRINT_DEBUG) {
1096       std::cout << "in[" << m << "] = " << ((int)d_current_inputs[m]) << "\n";
1097     }
1098
1099     if (d_current_inputs[m] == 1) {
1100       for (size_t n = 0; n < d_n_code_outputs; n++) {
1101         if (DO_PRINT_DEBUG) {
1102           size_t p = d_states_ndx[maio(m,n)];
1103           std::cout << "d_m[" << p << "] = " <<
1104             n2bs(d_memory[p], 1+d_n_delays[p]) <<
1105             " ^= c_g[" << maio(m,n) << "] = " <<
1106             n2bs(d_code_generators[maio(m,n)],1+d_n_delays[p]);
1107         }
1108
1109         d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
1110
1111         if (DO_PRINT_DEBUG) {
1112           size_t p = d_states_ndx[maio(m,n)];
1113           std::cout << " -> " << n2bs(d_memory[p],1+d_n_delays[p]) << "\n";
1114         }
1115       }
1116     }
1117   }
1118
1119   for (size_t p = 0; p < d_n_code_outputs; p++) {
1120     d_current_outputs[p] = 0;
1121   }
1122
1123   // create the output bits, by XOR'ing the individual unique
1124   // memory(ies) into the correct output bit
1125
1126   for (size_t p = 0; p < d_n_memories; p++) {
1127     if (DO_PRINT_DEBUG) {
1128       std::cout << "c_o[" << d_io_num[p] << "] = " <<
1129         n2bs(d_current_outputs[d_io_num[p]],1) << " ^= m[" <<
1130         p << "]&1 = " << n2bs(d_memory[p],1);
1131     }
1132
1133     d_ind_outputs[p] = ((char)(d_memory[p] & 1));
1134     d_current_outputs[d_io_num[p]] ^= d_ind_outputs[p];
1135
1136     if (DO_PRINT_DEBUG) {
1137       std::cout << " -> " << n2bs(d_current_outputs[d_io_num[p]],1) << "\n";
1138     }
1139   }
1140
1141   // now that the output bits are fully created, XOR the FB back
1142   // into the memories; the feedback bits have the LSB (&1) masked
1143   // off already so that it doesn't contribute.
1144
1145   for (size_t p = 0; p < d_n_memories; p++) {
1146     if (DO_PRINT_DEBUG) {
1147       std::cout << "i_o[" << p << "] = " <<
1148         n2bs(d_ind_outputs[p],1) << ", m[" << p << "] = " <<
1149         n2bs(d_memory[p],1+d_n_delays[p]);
1150     }
1151
1152     if (d_ind_outputs[p] == 1) {
1153       d_memory[p] ^= d_code_feedback[p];
1154
1155       if (DO_PRINT_DEBUG) {
1156         std::cout << " ^= c_f[" << p << "] = " <<
1157           n2bs(d_code_feedback[p],1+d_n_delays[p]) <<
1158           " -> " << n2bs(d_memory[p],1+d_n_delays[p]);
1159       }
1160     }
1161     if (DO_PRINT_DEBUG) {
1162       std::cout << "\n";
1163     }
1164   }
1165
1166   if (DO_PRINT_DEBUG) {
1167     std::cout << "ending encode_single_soai_fb.\n";
1168   }
1169 }
1170
1171 void
1172 code_convolutional_trellis::encode_single_siao
1173 ()
1174 {
1175   // single input, all outputs; no feedback
1176
1177   if (DO_PRINT_DEBUG) {
1178     std::cout << "starting encode_single_siao.\n";
1179   }
1180
1181   // update the memories with the current input bits;
1182
1183   // for each unique memory (1 per input), shift the delays and mask
1184   // off the extra high bits; then XOR in the input bit.
1185
1186   for (size_t p = 0; p < d_n_memories; p++) {
1187     d_memory[p] |= ((memory_t)(d_current_inputs[d_io_num[p]]));
1188   }
1189
1190   // create the output bits: for each output, loop over all inputs,
1191   // find the output bits for each encoder, and XOR each together
1192   // then sum (would usually be sum then XOR, but they're mutable in
1193   // base-2 and it's faster this way).
1194
1195   for (size_t n = 0; n < d_n_code_outputs; n++) {
1196     memory_t t_mem = 0;
1197     for (size_t m = 0; m < d_n_code_inputs; m++) {
1198       t_mem ^= ((d_memory[d_states_ndx[maio(m,n)]]) &
1199                 d_code_generators[maio(m,n)]);
1200     }
1201     d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
1202   }
1203
1204   // post-shift & mask the memory to guarantee that output
1205   // state is in the correct bit positions (1 up, not the LSB)
1206
1207   for (size_t p = 0; p < d_n_memories; p++) {
1208     d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
1209   }
1210
1211   if (DO_PRINT_DEBUG) {
1212     std::cout << "ending encode_single_siao.\n";
1213   }
1214 }
1215
1216 void
1217 code_convolutional_trellis::encode_single_siao_fb
1218 ()
1219 {
1220   // single input, all outputs; with feedback
1221
1222   if (DO_PRINT_DEBUG) {
1223     std::cout << "starting encode_single_siao_fb.\n";
1224   }
1225
1226   // update the memories with the current input bits;
1227
1228   // for each unique memory (1 per input), shift the delays and mask
1229   // off the extra high bits; then XOR in the input bit.
1230   // with FB: find the feedback bit, and OR it into the input bit's slot;
1231
1232   for (size_t p = 0; p < d_n_memories; p++) {
1233     memory_t t_mem = d_memory[p];
1234     memory_t t_fb = t_mem & d_code_feedback[p];
1235     char t_fb_bit = sum_bits_mod2 (t_fb, d_max_delay);
1236     t_mem |= ((memory_t) t_fb_bit);
1237     d_memory[p] = t_mem ^ ((memory_t)(d_current_inputs[d_io_num[p]]));
1238   }
1239
1240   // create the output bits: for each output, loop over all inputs,
1241   // find the output bits for each encoder, and XOR each together
1242   // then sum (would usually be sum then XOR, but they're mutable in
1243   // base-2 and it's faster this way).
1244
1245   for (size_t n = 0; n < d_n_code_outputs; n++) {
1246     memory_t t_mem = 0;
1247     for (size_t m = 0; m < d_n_code_inputs; m++) {
1248       t_mem ^= ((d_memory[d_states_ndx[maio(m,n)]]) &
1249                 d_code_generators[maio(m,n)]);
1250     }
1251     d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
1252   }
1253
1254   // post-shift & mask the memory to guarantee that output
1255   // state is in the correct bit positions (1 up, not the LSB)
1256
1257   for (size_t p = 0; p < d_n_memories; p++) {
1258     d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
1259   }
1260
1261   if (DO_PRINT_DEBUG) {
1262     std::cout << "ending encode_loop_siao_fb.\n";
1263   }
1264 }