3 * Copyright 2006 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
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)
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.
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.
27 #include "code_convolutional_trellis.h"
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
38 #include <mld/mld_timer.h>
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;
47 * sum the number of set bits, mod 2, for the output bit
51 code_convolutional_trellis::sum_bits_mod2
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.
60 char t_out_bit = (char)(in_mem & 1);
61 for (size_t r = max_memory; r > 0; r--) {
63 t_out_bit ^= ((char)(in_mem & 1));
69 code_convolutional_trellis::code_convolutional_trellis_init
73 const std::vector<int>& code_generators,
74 const std::vector<int>* code_feedback,
78 // do error checking on the input arguments
80 // make sure the block length makes sense
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";
90 // check to make sure the number of input streams makes sense
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";
100 // check to make sure the number of output streams makes sense
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";
110 // make sure the code_generator is the correct length
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";
121 // check for feedback (== NULL or not)
123 d_do_feedback = (code_feedback != NULL);
125 // create the class block variables
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;
133 if (DO_PRINT_DEBUG_VARS) {
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";
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.
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.
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
165 d_code_generators.assign (d_n_code_inputs * d_n_code_outputs, 0);
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).
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)];
178 // OR this feedback with the overall,
179 // to check for any delays used at all
181 t_OR_all_feedback |= t_in_code;
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.
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;
196 // copy over the FF code generators
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)];
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).
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).
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).
219 // check for SOAI all '0' output
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)];
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
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";
236 // check for SIAO all '0' input
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)];
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
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";
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.
257 // single output, all inputs (SOAI) realization:
258 // reset the global parameters
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;
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;
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;
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;
285 if (d_do_feedback == false) {
286 d_n_memories = d_n_code_outputs;
289 // store the parameters for SOAI
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;
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;
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;
309 // single input, all outputs (SIAO) realization
310 // reset the global parameters
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;
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;
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;
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;
337 if (d_do_feedback == false) {
338 d_n_memories = d_n_code_inputs;
341 if (DO_PRINT_DEBUG_VARS) {
343 " t_total_n_delays_siao = " << d_total_n_delays << "\n"
344 " t_total_n_delays_soai = " << t_total_n_delays_soai << "\n";
347 // pick which realization to use;
348 // siao is preferred since it's easier to debug, and more likely
350 if (d_total_n_delays <= t_total_n_delays_soai) {
352 // nothing else to do, since the global variables already hold
353 // the correct values.
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;
366 // make sure the block length makes sense, #2
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";
378 // check & mask off the init states
380 d_n_states = (1 << d_total_n_delays);
381 d_n_input_combinations = (1 << d_n_code_inputs);
383 if (DO_PRINT_DEBUG_VARS) {
385 " d_n_states = " << d_n_states << "\n"
386 " d_n_input_combinations = " << d_n_input_combinations << "\n";
389 if ((d_do_feedback == true) & (d_do_encode_soai == true)) {
390 // create the individual output bits used in soai feedback
392 d_ind_outputs.assign (d_n_memories, 0);
395 // create the max_mem_mask to be used in encoding
397 d_max_mem_masks.assign (d_n_memories, 0);
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);
403 d_max_mem_masks[m] = (memory_t)((2 << (d_n_delays[m])) - 1);
406 if (DO_PRINT_DEBUG_VARS) {
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))
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";
423 d_memory.assign (d_n_memories, 0);
425 // create the inputs and outputs buffers
427 d_current_inputs.assign (d_n_code_inputs, 0);
428 d_current_outputs.assign (d_n_code_outputs, 0);
430 // create the trellis for this code:
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;
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";
441 end_memory_state &= t_mask;
446 if (d_do_termination == true) {
448 // create the termination lookup table
450 create_termination_table (end_memory_state);
455 code_convolutional_trellis::get_memory_requirements
456 (size_t m, // input number
457 size_t n, // output number
459 size_t& t_n_unique_fb_prev_start,
460 const std::vector<int>* code_feedback)
462 size_t t_in_code = d_code_generators[maio(m,n)];
464 // find the memory requirement for this code generator
466 size_t t_code_mem_ff = max_bit_position (t_in_code);
468 // check to see if this is bigger than any others in this row/column
470 if (t_code_mem_ff > t_max_mem)
471 t_max_mem = t_code_mem_ff;
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;
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.
484 if (d_do_feedback == true) {
485 if (DO_PRINT_DEBUG) {
489 // get the FB code; AND off the LSB for correct functionality
490 // during internal computations.
492 t_in_code = ((memory_t)((*code_feedback)[maio(m,n)]));
493 t_in_code &= ((memory_t)(-2));
495 // find the memory requirement
497 size_t t_code_mem_fb = max_bit_position (t_in_code);
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;
505 // check to see if this feedback is unique
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)
513 if (l_n_unique_fb == d_n_memories) {
515 // this is a unique feedback;
517 d_code_feedback[l_n_unique_fb] = t_in_code;
518 d_n_delays[l_n_unique_fb] = t_code_mem_fb;
520 // increase the number of unique feedback codes
524 // store memory requirements for this output
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;
530 if (DO_PRINT_DEBUG) {
531 std::cout << ", uq # " << l_n_unique_fb <<
532 ", tot_mem = " << d_total_n_delays;
535 // not a unique feedback, but the FF might require more memory
537 if (DO_PRINT_DEBUG) {
538 std::cout << ", !uq # " << l_n_unique_fb <<
539 " = " << d_n_delays[l_n_unique_fb];
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;
546 if (DO_PRINT_DEBUG) {
547 std::cout << " => " << d_n_delays[l_n_unique_fb] <<
548 ", tot_mem = " << d_total_n_delays;
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;
555 if (DO_PRINT_DEBUG) {
558 if (d_max_delay < t_max_mem)
559 d_max_delay = t_max_mem;
563 code_convolutional_trellis::create_trellis
566 // first dimension is the number of states
568 d_trellis.resize (d_n_states);
570 // second dimension (one per first dimension) is the number of input
573 for (size_t m = 0; m < d_n_states; m++) {
574 d_trellis[m].resize (d_n_input_combinations);
577 // fill in the trellis
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,
584 t_connection->d_to_state,
585 t_connection->d_output_bits);
591 code_convolutional_trellis::demux_state
593 std::vector<memory_t>& memories)
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)
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];
607 code_convolutional_trellis::mux_state
608 (const std::vector<memory_t>& memories)
610 // mux bits for the given memory states in d_memory
611 // assumes state bits start after the LSB (not at &1)
613 memory_t t_state = 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];
623 code_convolutional_trellis::demux_inputs
625 std::vector<char>& in_vec)
627 // de-mux bits for the given inputs;
628 // copy them into the provided vector;
630 for (size_t m = 0; m < d_n_code_inputs; m++, inputs >>= 1) {
631 in_vec[m] = (char)(inputs & 1);
636 code_convolutional_trellis::mux_inputs
637 (const std::vector<char>& in_vec)
639 // mux bits for the given inputs
641 size_t bit_shift = 0;
643 for (size_t m = 0; m < in_vec.size(); m++, bit_shift++) {
644 inputs |= (((memory_t)(in_vec[m]&1)) << bit_shift);
650 code_convolutional_trellis::demux_outputs
652 std::vector<char>& out_vec)
654 // de-mux bits for the given outputs;
655 // copy them into the provided vector;
657 for (size_t m = 0; m < d_n_code_outputs; m++, outputs >>= 1) {
658 out_vec[m] = (char)(outputs & 1);
663 code_convolutional_trellis::mux_outputs
664 (const std::vector<char>& out_vec)
666 // mux bits for the given outputs
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);
677 code_convolutional_trellis::encode_single
683 // set input parameters
685 demux_state (in_state, d_memory);
686 demux_inputs (inputs, d_current_inputs);
688 // call the correct function to do the work
690 if (d_do_encode_soai == true) {
691 if (d_do_feedback == true) {
692 encode_single_soai_fb ();
694 encode_single_soai ();
697 if (d_do_feedback == true) {
698 encode_single_siao_fb ();
700 encode_single_siao ();
704 // retrieve the output parameters
706 out_state = mux_state (d_memory);
707 out_bits = mux_outputs (d_current_outputs);
711 code_convolutional_trellis::encode_lookup
713 const std::vector<char>& inputs,
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);
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;
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";
732 code_convolutional_trellis::encode_lookup
734 const std::vector<char>& inputs,
735 std::vector<char>& out_bits)
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);
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);
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";
754 code_convolutional_trellis::create_termination_table
755 (memory_t end_memory_state)
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.
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.
766 // create a reverse trellis
767 // it's temporary, just for doing the termination, so just do it locally
771 // first dimension is the number of states
773 t_trellis.resize (d_n_states);
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
778 for (size_t m = 0; m < d_n_states; m++) {
779 t_trellis[m].reserve (d_n_input_combinations);
782 std::vector<memory_t> tmp(d_n_memories);
783 demux_state (end_memory_state, tmp);
785 memory_t to_state, outputs;
788 // fill in the trellis; discard the outputs
789 // set the trellis node's output bits to the input
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;
795 encode_single ((memory_t) m,
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);
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";
812 if (DO_PRINT_DEBUG_TERM) {
813 std::cout << "Trellis:\n";
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";
825 // need 2 of most buffers: one for the current-in-use variables, and
826 // one for the to-be-determined variables
828 // create the term input bit vectors
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);
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);
840 // create the list of "in-use" states for the current t_term_inputs
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);
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);
850 // termporary 'inputs' place holder, in order to use the class's
851 // built-in inputs demux'er.
853 std::vector<char> t_inputs;
854 t_inputs.assign (d_n_code_inputs, 0);
856 // setup the first state
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;
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.
870 size_t n_input_term_bits = 0;
872 while ((n_states_used[t_which_input] < d_n_states) &
873 (n_input_term_bits < 2*d_total_n_delays)) {
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";
882 // loop over all current in-use states
884 for (size_t m = 0; m < n_states_used[t_which_input]; m++) {
886 // get the current state to work with
888 size_t t_state_ndx = t_used_states_ndx[t_which_input][m];
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?
896 memory_t t_input = t_trellis[t_state_ndx][p].d_output_bits;
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";
904 // copy over the current state's input bits to the 'from'
905 // state's input bits, in the "current" term inputs
907 t_term_inputs[t_which_input^1][t_from_state] =
908 t_term_inputs[t_which_input][t_state_ndx];
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...
914 demux_inputs (t_input, t_inputs);
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];
919 t_term |= ((memory_t)(t_inputs[n] & 1));
920 t_term_inputs[t_which_input^1][t_from_state][n] = t_term;
923 // add this from state to the list of states for the next run
925 t_used_states_ndx[t_which_input^1][n_states_used[t_which_input^1]] =
928 // and set that this state is in use
930 t_in_use_states[t_which_input^1][t_from_state] = true;
932 // increase the number of next used states
934 n_states_used[t_which_input^1] += 1;
939 // update / reset variables for this run-through
941 // swap buffers ("^1" is always the next set of buffers)
945 // zero the next # of states used
947 n_states_used[t_which_input^1] = 0;
949 // reset the next 'in use' buffer
951 t_in_use_states[t_which_input^1].assign (d_n_states, false);
953 // increase the number of required term bits (per stream)
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;
964 d_n_bits_to_term = n_input_term_bits;
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);
971 d_term_inputs = t_term_inputs[t_which_input];
973 if (DO_PRINT_DEBUG_TERM_END) {
974 std::cout << "# Term inputs / stream = " << d_n_bits_to_term << "\n";
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";
987 code_convolutional_trellis::encode_single_soai
990 // single-output, all inputs; no feedback
992 if (DO_PRINT_DEBUG) {
993 std::cout << "Starting encode_single_soai.\n";
996 // shift memories down by 1 bit to make room for feedback; no
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]);
1007 if (DO_PRINT_DEBUG) {
1008 std::cout << " >>= 1 -> " <<
1009 n2bs(d_memory[p], 1+d_n_delays[p]) << "\n";
1013 // for each input bit, if that bit's a '1', then XOR the code
1014 // generators into the correct state's memory.
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);
1021 if (d_current_inputs[m] == 1) {
1022 if (DO_PRINT_DEBUG) {
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)]]);
1033 d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
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";
1043 } else if (DO_PRINT_DEBUG) {
1044 std::cout << " ... nothing to do\n";
1048 for (size_t p = 0; p < d_n_code_outputs; p++) {
1049 d_current_outputs[p] = 0;
1052 // create the output bits, by XOR'ing the individual unique
1053 // memory(ies) into the correct output bit
1055 for (size_t p = 0; p < d_n_memories; p++) {
1056 d_current_outputs[d_io_num[p]] ^= ((char)(d_memory[p] & 1));
1059 if (DO_PRINT_DEBUG) {
1060 std::cout << "ending encode_single_soai.\n";
1065 code_convolutional_trellis::encode_single_soai_fb
1068 // single-output, all inputs; with feedback
1070 if (DO_PRINT_DEBUG) {
1071 std::cout << "Starting encode_single_soai_fb.\n";
1074 // shift memories down by 1 bit to make room for feedback; no
1075 // masking required.
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]);
1085 if (DO_PRINT_DEBUG) {
1086 std::cout << " >>= 1 -> " <<
1087 n2bs(d_memory[p], 1+d_n_delays[p]) << "\n";
1091 // for each input bit, if that bit's a '1', then XOR the code
1092 // generators into the correct state's memory.
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";
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]);
1109 d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
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";
1119 for (size_t p = 0; p < d_n_code_outputs; p++) {
1120 d_current_outputs[p] = 0;
1123 // create the output bits, by XOR'ing the individual unique
1124 // memory(ies) into the correct output bit
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);
1133 d_ind_outputs[p] = ((char)(d_memory[p] & 1));
1134 d_current_outputs[d_io_num[p]] ^= d_ind_outputs[p];
1136 if (DO_PRINT_DEBUG) {
1137 std::cout << " -> " << n2bs(d_current_outputs[d_io_num[p]],1) << "\n";
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.
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]);
1152 if (d_ind_outputs[p] == 1) {
1153 d_memory[p] ^= d_code_feedback[p];
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]);
1161 if (DO_PRINT_DEBUG) {
1166 if (DO_PRINT_DEBUG) {
1167 std::cout << "ending encode_single_soai_fb.\n";
1172 code_convolutional_trellis::encode_single_siao
1175 // single input, all outputs; no feedback
1177 if (DO_PRINT_DEBUG) {
1178 std::cout << "starting encode_single_siao.\n";
1181 // update the memories with the current input bits;
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.
1186 for (size_t p = 0; p < d_n_memories; p++) {
1187 d_memory[p] |= ((memory_t)(d_current_inputs[d_io_num[p]]));
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).
1195 for (size_t n = 0; n < d_n_code_outputs; n++) {
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)]);
1201 d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
1204 // post-shift & mask the memory to guarantee that output
1205 // state is in the correct bit positions (1 up, not the LSB)
1207 for (size_t p = 0; p < d_n_memories; p++) {
1208 d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
1211 if (DO_PRINT_DEBUG) {
1212 std::cout << "ending encode_single_siao.\n";
1217 code_convolutional_trellis::encode_single_siao_fb
1220 // single input, all outputs; with feedback
1222 if (DO_PRINT_DEBUG) {
1223 std::cout << "starting encode_single_siao_fb.\n";
1226 // update the memories with the current input bits;
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;
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]]));
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).
1245 for (size_t n = 0; n < d_n_code_outputs; n++) {
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)]);
1251 d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
1254 // post-shift & mask the memory to guarantee that output
1255 // state is in the correct bit positions (1 up, not the LSB)
1257 for (size_t p = 0; p < d_n_memories; p++) {
1258 d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
1261 if (DO_PRINT_DEBUG) {
1262 std::cout << "ending encode_loop_siao_fb.\n";