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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
27 #include "code_convolutional_trellis.h"
31 #define DO_TIME_THOUGHPUT 0
32 #define DO_PRINT_DEBUG 1
33 #define DO_PRINT_DEBUG_ENCODE 1
35 #include <mld/mld_timer.h>
38 static const int g_max_block_size_bits = 10000000;
39 static const int g_max_num_streams = 10;
40 static const int g_num_bits_per_byte = 8;
44 * sum the number of set bits, mod 2, for the output bit
48 code_convolutional_trellis::sum_bits_mod2
52 // there are faster ways to do this, but this works for now; could
53 // certainly do a single inline asm, which most processors provide
54 // to deal with summing the bits in an integer.
55 // this routine can be overridden by another method if desired.
57 char t_out_bit = (char)(in_mem & 1);
58 for (size_t r = max_memory; r > 0; r--) {
60 t_out_bit ^= ((char)(in_mem & 1));
66 code_convolutional_trellis::code_convolutional_trellis_init
70 const std::vector<int>& code_generators,
71 const std::vector<int>* code_feedback,
75 // do error checking on the input arguments
77 // make sure the block length makes sense
79 if ((block_size_bits < 0) | (block_size_bits > g_max_block_size_bits)) {
80 std::cerr << "code_convolutional_trellis: " <<
81 "Requested block length (" << block_size_bits <<
82 " bits) must be between 0 and " << g_max_block_size_bits <<
83 " bits, with 0 being a streaming encoder.\n";
87 // check to make sure the number of input streams makes sense
89 if ((n_code_inputs <= 0) | (n_code_inputs > g_max_num_streams)) {
90 std::cerr << "code_convolutional_trellis: " <<
91 "Requested number of input streams (" <<
92 n_code_inputs << ") must be between 1 and " <<
93 g_max_num_streams << ".\n";
97 // check to make sure the number of output streams makes sense
99 if ((n_code_outputs <= 0) | (n_code_outputs > g_max_num_streams)) {
100 std::cerr << "code_convolutional_trellis: " <<
101 "Requested number of output streams (" <<
102 n_code_outputs << ") must be between 1 and " <<
103 g_max_num_streams << ".\n";
107 // make sure the code_generator is the correct length
109 if (code_generators.size () !=
110 ((size_t)(n_code_inputs * n_code_outputs))) {
111 std::cerr << "code_convolutional_trellis: " <<
112 "Number of code generator entries (" << code_generators.size () <<
113 ") is not equal to the product of the number of input and output" <<
114 " streams (" << (n_code_inputs * n_code_outputs) << ").\n";
118 // check for feedback (== NULL or not)
120 d_do_feedback = (code_feedback != NULL);
122 // create the class block variables
124 d_block_size_bits = block_size_bits;
125 d_n_code_inputs = n_code_inputs;
126 d_n_code_outputs = n_code_outputs;
127 d_do_streaming = (block_size_bits == 0);
128 d_do_termination = (d_do_streaming == true) ? false : do_termination;
130 if (DO_PRINT_DEBUG) {
132 "d_block_size_bits = " << d_block_size_bits << "\n"
133 "d_n_code_inputs = " << d_n_code_inputs << "\n"
134 "d_n_code_outputs = " << d_n_code_outputs << "\n"
135 "d_do_streaming = " <<
136 ((d_do_streaming == true) ? "true" : "false") << "\n"
137 "d_do_termination = " <<
138 ((d_do_termination == true) ? "true" : "false") << "\n"
139 "d_do_feedback = " <<
140 ((d_do_feedback == true) ? "true" : "false") << "\n";
143 // allocate the vectors for doing the encoding. use memory_t (an
144 // interger type, at least 32 bits) bits to represent memory and the
145 // code, as it makes the operations quite simple the state vectors.
147 // d_states is a "matrix" [#input by #outputs] containing indices
148 // to memory_t's; this is done to make feedback function properly,
149 // and doesn't effect the computation time for feedforward. The
150 // issue is that any code with the same feedback can use the same
151 // memory - thus reducing the actual number of memories required.
152 // These overlapping encoders will use the same actual memory, but
153 // given that there is no way to know a-priori where they are, use
154 // pointers over the full I/O matrix-space to make sure each I/O
155 // encoder uses the correct memory.
156 // reference the matrix using "maoi(i,o)" ... see .h file.
158 // code generators (feedforward part) are [#inputs x #outputs],
159 // always - one for each I/O combination.
160 // reference the matrix using "maoi(i,o)" ... see .h file
162 d_code_generators.assign (d_n_code_inputs * d_n_code_outputs, 0);
164 // check the feedback for correctness, before anything else, since
165 // any feedback impacts the total # of delays in the encoder:
166 // without feedback, this is the sum of the individual delays max'ed
167 // over each input (if siao) or output (if soai).
169 if (d_do_feedback == true) {
170 memory_t t_OR_all_feedback = 0;
171 for (size_t n = 0; n < d_n_code_outputs; n++) {
172 for (size_t m = 0; m < d_n_code_inputs; m++) {
173 memory_t t_in_code = (*code_feedback)[maoi(m,n)];
175 // OR this feedback with the overall,
176 // to check for any delays used at all
178 t_OR_all_feedback |= t_in_code;
182 // check to see if all the feedback entries were either "0" or "1",
183 // which implies no feedback; warn the user in that case and reset
184 // the do_feedback parameter to false.
186 if ((t_OR_all_feedback | 1) == 1) {
187 std::cout << "code_convolutional_trellis: Warning: " <<
188 "No feedback is required, ignoring feedback.\n";
189 d_do_feedback = false;
193 // copy over the FF code generators
195 for (size_t n = 0; n < d_n_code_outputs; n++)
196 for (size_t m = 0; m < d_n_code_inputs; m++)
197 d_code_generators[maio(m,n)] = code_generators[maio(m,n)];
199 // check the input FF (and FB) code generators for correctness, and
200 // find the minimum memory configuration: combining via a single
201 // input / all outputs (SIAO), or a single output / all inputs (SOAI).
203 // for FF only, look over both the SOAI and SIAO realizations to
204 // find the minimum total # of delays, and use that realization
205 // (SOAI is preferred if total # of delays is equal, since it's much
206 // simpler to implement).
209 // for SIAO, check each input row (all outputs for a given input)
210 // for unique feedback; duplicate feedback entries can be
211 // combined into a single computation to reduce total # of delays.
212 // for SOAI: check each output column (all inputs for a given
213 // output) for unique feedback; duplicate feedback entries can
214 // be combined into a simgle computation (ditto).
216 // check for SOAI all '0' output
218 for (size_t n = 0; n < d_n_code_outputs; n++) {
219 memory_t t_all_inputs_zero = 0;
220 for (size_t m = 0; m < d_n_code_inputs; m++)
221 t_all_inputs_zero |= d_code_generators[maio(m,n)];
223 // check this input to see if all encoders were '0'; this might be
224 // OK for some codes, but warn the user just in case
226 if (t_all_inputs_zero == 0) {
227 std::cout << "code_convolutional_trellis: Warning:"
228 "Output " << n+1 << " (of " << d_n_code_outputs <<
229 ") will always be 0.\n";
233 // check for SIAO all '0' input
235 for (size_t m = 0; m < d_n_code_inputs; m++) {
236 memory_t t_all_outputs_zero = 0;
237 for (size_t n = 0; n < d_n_code_outputs; n++)
238 t_all_outputs_zero |= d_code_generators[maio(m,n)];
240 // check this input to see if all encoders were '0'; this might be
241 // OK for some codes, but warn the user just in case
243 if (t_all_outputs_zero == 0) {
244 std::cout << "code_convolutional_trellis: Warning:"
245 "Input " << m+1 << " (of " << d_n_code_inputs <<
246 ") will not be used; all encoders are '0'.\n";
250 // check and compute memory requirements in order to determine which
251 // realization uses the least memory; create and save findings to
252 // not have to re-do these computations later.
254 // single output, all inputs (SOAI) realization:
255 // reset the global parameters
257 d_code_feedback.assign (d_n_code_inputs * d_n_code_outputs, 0);
258 d_n_delays.assign (d_n_code_inputs * d_n_code_outputs, 0);
259 d_io_num.assign (d_n_code_inputs * d_n_code_outputs, 0);
260 d_states_ndx.assign (d_n_code_inputs * d_n_code_outputs, 0);
261 d_max_delay = d_total_n_delays = d_n_memories = 0;
262 d_do_encode_soai = true;
264 for (size_t n = 0; n < d_n_code_outputs; n++) {
265 size_t t_max_mem = 0;
266 size_t t_n_unique_fb_prev_start = d_n_memories;
268 for (size_t m = 0; m < d_n_code_inputs; m++) {
269 get_memory_requirements (m, n, t_max_mem,
270 t_n_unique_fb_prev_start, code_feedback);
271 if (d_do_feedback == false) {
272 d_states_ndx[maio(m,n)] = n;
275 if (d_do_feedback == false) {
276 // not feedback; just store memory requirements for this output
277 d_total_n_delays += t_max_mem;
278 d_n_delays[n] = t_max_mem;
282 if (d_do_feedback == false) {
283 d_n_memories = d_n_code_outputs;
286 // store the parameters for SOAI
288 std::vector<size_t> t_fb_generators_soai, t_n_delays_soai, t_io_num_soai;
289 std::vector<size_t> t_states_ndx_soai;
290 size_t t_max_delay_soai, t_total_n_delays_soai, t_n_memories_soai;
292 t_fb_generators_soai.assign (d_code_feedback.size (), 0);
293 t_fb_generators_soai = d_code_feedback;
294 t_n_delays_soai.assign (d_n_delays.size (), 0);
295 t_n_delays_soai = d_n_delays;
296 t_io_num_soai.assign (d_io_num.size (), 0);
297 t_io_num_soai = d_io_num;
298 t_states_ndx_soai.assign (d_states_ndx.size (), 0);
299 t_states_ndx_soai = d_states_ndx;
301 t_n_memories_soai = d_n_memories;
302 t_total_n_delays_soai = d_total_n_delays;
303 t_max_delay_soai = d_max_delay;
305 // single input, all outputs (SIAO) realization
306 // reset the global parameters
308 d_code_feedback.assign (d_n_code_inputs * d_n_code_outputs, 0);
309 d_n_delays.assign (d_n_code_inputs * d_n_code_outputs, 0);
310 d_io_num.assign (d_n_code_inputs * d_n_code_outputs, 0);
311 d_states_ndx.assign (d_n_code_inputs * d_n_code_outputs, 0);
312 d_max_delay = d_total_n_delays = d_n_memories = 0;
313 d_do_encode_soai = false;
315 for (size_t m = 0; m < d_n_code_inputs; m++) {
316 size_t t_max_mem = 0;
317 size_t t_n_unique_fb_prev_start = d_n_memories;
319 for (size_t n = 0; n < d_n_code_outputs; n++) {
320 get_memory_requirements (m, n, t_max_mem,
321 t_n_unique_fb_prev_start, code_feedback);
322 if (d_do_feedback == false) {
323 d_states_ndx[maio(m,n)] = m;
326 if (d_do_feedback == false) {
327 // not feedback; just store memory requirements for this output
328 d_total_n_delays += t_max_mem;
329 d_n_delays[m] = t_max_mem;
333 if (d_do_feedback == false) {
334 d_n_memories = d_n_code_inputs;
337 if (DO_PRINT_DEBUG) {
339 " t_total_n_delays_siao = " << d_total_n_delays << "\n"
340 " t_total_n_delays_soai = " << t_total_n_delays_soai << "\n";
343 // pick which realization to use; soai is preferred since it's faster
344 // ... but unfortunately it's also less likely
346 if (d_total_n_delays < t_total_n_delays_soai) {
348 // nothing else to do, since the global variables already hold
349 // the correct values.
352 d_do_encode_soai = true;
353 d_code_feedback = t_fb_generators_soai;
354 d_n_delays = t_n_delays_soai;
355 d_io_num = t_io_num_soai;
356 d_states_ndx = t_states_ndx_soai;
357 d_n_memories = t_n_memories_soai;
358 d_total_n_delays = t_total_n_delays_soai;
359 d_max_delay = t_max_delay_soai;
362 // make sure the block length makes sense, #2
364 if ((d_do_streaming == false) & (d_block_size_bits < d_max_delay)) {
365 std::cerr << "code_convolutional_trellis: " <<
366 "Requested block length (" << d_block_size_bits <<
367 " bit" << (d_block_size_bits > 1 ? "s" : "") <<
368 ") must be at least 1 memory length (" << d_max_delay <<
369 " bit" << (d_max_delay > 1 ? "s" : "") <<
370 " for this code) when doing block coding.\n";
374 // check & mask off the init states
376 d_n_states = (1 << d_total_n_delays);
377 d_n_input_combinations = (1 << d_n_code_inputs);
379 memory_t t_mask = (memory_t)((2 << d_total_n_delays) - 1);
381 if (end_memory_state & t_mask) {
382 std::cout << "code_convolutional_trellis: Warning: " <<
383 "provided end memory state out (" << end_memory_state <<
384 ") is out of the state range [0, " <<
385 (d_n_states-1) << "]; masking off the unused bits.\n";
387 end_memory_state &= t_mask;
390 // create the max_mem_mask to be used in encoding
392 d_max_mem_masks.assign (d_n_memories, 0);
394 for (size_t m = 0; m < d_n_memories; m++) {
395 if (d_n_delays[m] == sizeof (memory_t) * g_num_bits_per_byte)
396 d_max_mem_masks[m] = ((memory_t) -1);
398 d_max_mem_masks[m] = (memory_t)((2 << (d_n_delays[m])) - 1);
401 if (DO_PRINT_DEBUG) {
403 " d_n_memories = " << d_n_memories << "\n"
404 " d_total_n_delays = " << d_total_n_delays << "\n"
405 " d_max_delay = " << d_max_delay << "\n"
406 " d_do_encode_soai = " <<
407 ((d_do_encode_soai == true) ? "true" : "false") << "\n";
412 d_memory.assign (d_n_memories, 0);
414 // create the inputs and outputs buffers
416 d_current_inputs.assign (d_n_code_inputs, 0);
417 d_current_outputs.assign (d_n_code_outputs, 0);
419 // create the trellis for this code:
423 if (d_do_termination == true) {
425 // create the termination lookup table
427 create_termination_table (end_memory_state);
432 code_convolutional_trellis::get_memory_requirements
433 (size_t m, // input number
434 size_t n, // output number
436 size_t& t_n_unique_fb_prev_start,
437 const std::vector<int>* code_feedback)
439 size_t t_in_code = d_code_generators[maio(m,n)];
441 // find the memory requirement for this code generator
443 size_t t_code_mem_ff = max_bit_position (t_in_code);
445 // check to see if this is bigger than any others in this row/column
447 if (t_code_mem_ff > t_max_mem)
448 t_max_mem = t_code_mem_ff;
450 if (DO_PRINT_DEBUG) {
451 std::cout << "c_g[" << m << "][" << n << "]{" <<
452 maio(m,n) << "} = " << n2bs(t_in_code, 8) <<
453 ", code_mem = " << t_code_mem_ff;
456 // check the feedback portion, if it exists;
457 // for soai, check all the inputs which generate this output for
458 // uniqueness; duplicate entries can be combined to reduce total
459 // # of memories as well as required computations.
461 if (d_do_feedback == true) {
462 if (DO_PRINT_DEBUG) {
466 // get the FB code; AND off the LSB for correct functionality
467 // during internal computations.
469 t_in_code = ((memory_t)((*code_feedback)[maio(m,n)]));
470 t_in_code &= ((memory_t)(-2));
472 // find the memory requirement
474 size_t t_code_mem_fb = max_bit_position (t_in_code);
476 if (DO_PRINT_DEBUG) {
477 std::cout << "c_f[" << m << "][" << n << "]{" <<
478 maio(m,n) << "} = " << n2bs(t_in_code, 8) <<
479 ", code_mem = " << t_code_mem_fb;
482 // check to see if this feedback is unique
484 size_t l_n_unique_fb = t_n_unique_fb_prev_start;
485 while (l_n_unique_fb < d_n_memories) {
486 if (d_code_feedback[l_n_unique_fb] == t_in_code)
490 if (l_n_unique_fb == d_n_memories) {
492 // this is a unique feedback;
494 d_code_feedback[l_n_unique_fb] = t_in_code;
495 d_n_delays[l_n_unique_fb] = t_code_mem_fb;
497 // increase the number of unique feedback codes
501 // store memory requirements for this output
503 if (t_max_mem < t_code_mem_fb)
504 t_max_mem = t_code_mem_fb;
505 d_total_n_delays += t_max_mem;
507 if (DO_PRINT_DEBUG) {
508 std::cout << ", uq # " << l_n_unique_fb <<
509 ", tot_mem = " << d_total_n_delays;
512 // not a unique feedback, but the FF might require more memory
514 if (DO_PRINT_DEBUG) {
515 std::cout << ", !uq # " << l_n_unique_fb <<
516 " = " << d_n_delays[l_n_unique_fb];
519 if (d_n_delays[l_n_unique_fb] < t_code_mem_ff) {
520 d_total_n_delays += (t_code_mem_ff - d_n_delays[l_n_unique_fb]);
521 d_n_delays[l_n_unique_fb] = t_code_mem_ff;
523 if (DO_PRINT_DEBUG) {
524 std::cout << " => " << d_n_delays[l_n_unique_fb] <<
525 ", tot_mem = " << d_total_n_delays;
529 d_io_num[l_n_unique_fb] = ((d_do_encode_soai == true) ? n : m);
530 d_states_ndx[maio(m,n)] = l_n_unique_fb;
532 if (DO_PRINT_DEBUG) {
535 if (d_max_delay < t_max_mem)
536 d_max_delay = t_max_mem;
540 code_convolutional_trellis::create_trellis
543 if (DO_PRINT_DEBUG_ENCODE) {
544 std::cout << "c_t: # states = " << d_n_states <<
545 ", d_n_input_combinations = " << d_n_input_combinations << "\n";
548 // first dimension is the number of states
550 d_trellis.resize (d_n_states);
552 // second dimension (one per first dimension) is the number of input
555 for (size_t m = 0; m < d_n_states; m++) {
556 d_trellis[m].resize (d_n_input_combinations);
557 for (size_t n = 0; n < d_n_input_combinations; n++) {
558 connection_t_ptr t_connection = &(d_trellis[m][n]);
559 t_connection->d_output_bits.resize (d_n_code_outputs);
563 // fill in the trellis
565 for (size_t m = 0; m < d_n_states; m++) {
566 for (size_t n = 0; n < d_n_input_combinations; n++) {
567 connection_t_ptr t_connection = &(d_trellis[m][n]);
568 encode_single (m, n, t_connection->d_to_state,
569 t_connection->d_output_bits);
570 if (DO_PRINT_DEBUG_ENCODE) {
571 std::cout << "set d_t[" << n2bs(m,d_total_n_delays) << "][" <<
572 n2bs(n,d_n_code_inputs) << "] : to_st = " <<
573 n2bs(t_connection->d_to_state,d_total_n_delays) << "\n";
580 code_convolutional_trellis::demux_state
582 std::vector<memory_t>& memories)
584 // de-mux bits for the given memory state;
585 // copy them into the provided vector;
586 // assumes state bits start after the LSB (not at &1)
588 memories.resize (d_n_memories);
589 if (DO_PRINT_DEBUG_ENCODE) {
590 std::cout << "in_st = " << n2bs(in_state,d_total_n_delays) << " ->\n";
592 for (size_t m = 0; m < d_n_memories; m++) {
593 memories[m] = (in_state << 1) & d_max_mem_masks[m];
594 in_state >>= d_n_delays[m];
595 if (DO_PRINT_DEBUG_ENCODE) {
596 std::cout << " #d = " << d_n_delays[m] << ", mem[" << m << "] = " <<
597 n2bs(memories[m], d_n_delays[m]+1) << "\n";
603 code_convolutional_trellis::mux_state
604 (const std::vector<memory_t>& memories)
606 // mux bits for the given memory states in d_memory
607 // assumes state bits start after the LSB (not at &1)
608 memory_t t_state = 0;
610 for (size_t m = 0; m < d_n_memories; m++) {
611 t_state |= (memories[m] >> 1) << shift;
612 shift += d_n_delays[m];
613 if (DO_PRINT_DEBUG_ENCODE) {
614 std::cout << " #d = " << d_n_delays[m] << ", mem[" << m << "] = " <<
615 n2bs(memories[m], d_n_delays[m]+1) << " -> st = " <<
616 n2bs(t_state, d_total_n_delays) << "\n";
623 code_convolutional_trellis::demux_inputs
625 std::vector<char>& in_vec)
627 for (size_t m = 0; m < d_n_code_inputs; m++, inputs >>= 1) {
628 in_vec[m] = (char)(inputs & 1);
633 code_convolutional_trellis::mux_inputs
634 (const std::vector<char>& in_vec)
636 size_t bit_shift = 0;
638 for (size_t m = 0; m < in_vec.size(); m++, bit_shift++) {
639 inputs |= (((memory_t)(in_vec[m]&1)) << bit_shift);
645 code_convolutional_trellis::encode_single
649 std::vector<char>& out_bits)
651 // set input parameters
653 demux_state (in_state, d_memory);
654 demux_inputs (inputs, d_current_inputs);
656 // call the correct function to do the work
658 if (d_do_encode_soai == true) {
659 if (d_do_feedback == true) {
660 encode_single_soai_fb ();
662 encode_single_soai ();
665 if (d_do_feedback == true) {
666 encode_single_siao_fb ();
668 encode_single_siao ();
672 // retrieve the output parameters
674 out_state = mux_state (d_memory);
675 out_bits = d_current_outputs;
679 code_convolutional_trellis::encode_lookup
681 const std::vector<char>& inputs,
682 std::vector<char>& out_bits)
684 if (DO_PRINT_DEBUG_ENCODE) {
685 std::cout << "using d_t[" << state << "][" << mux_inputs(inputs) <<
690 connection_t_ptr t_connection = &(d_trellis[state][mux_inputs(inputs)]);
692 if (DO_PRINT_DEBUG_ENCODE) {
693 std::cout << t_connection << ": to_state = "
694 << t_connection->d_to_state << "\n";
697 state = t_connection->d_to_state;
698 out_bits = t_connection->d_output_bits;
702 code_convolutional_trellis::get_termination_inputs
703 (memory_t term_start_state,
705 std::vector<char>& inputs)
707 inputs.resize (d_n_code_inputs);
708 for (size_t m = 0; m < d_n_code_inputs; m++) {
709 inputs[m] = ((d_term_inputs[term_start_state][m]) >> bit_num) & 1;
714 code_convolutional_trellis::create_termination_table
715 (memory_t end_memory_state)
717 // somewhat involved, but basically start with the terminating state
718 // and work backwards d_total_n_delays, then create a
719 // std::vector<memory_t> of length n_states, once per path required
720 // to get from the given state to the desired termination state.
722 // each entry represents the bits required to terminate that
723 // particular state, listed in order from LSB for the first input
724 // bit to the MSB for the last input bit.
727 // create a reverse trellis
728 // it's temporary, just for doing the termination, so just do it locally
732 // first dimension is the number of states
734 t_trellis.resize (d_n_states);
736 // second dimension (one per first dimension) is the number of input
739 for (size_t m = 0; m < d_n_states; m++) {
740 t_trellis[m].resize (d_n_input_combinations);
743 std::vector<char> outputs (d_n_code_outputs);
746 // fill in the trellis
748 for (memory_t m = 0; m < d_n_states; m++) {
749 for (memory_t n = 0; n < d_n_input_combinations; n++) {
750 encode_single (m, n, to_state, outputs);
752 connection_t_ptr t_connection = &(t_trellis[to_state][n]);
753 t_connection->d_to_state = m;
755 t_connection->d_output_bits.resize (d_n_code_outputs);
756 t_connection->d_output_bits = outputs;
761 // create the output vectors
763 term_input_t t_term_inputs;
764 t_term_inputs.resize (d_n_states);
765 for (size_t m = 0; m < d_n_states; m++) {
766 t_term_inputs[m].assign (d_n_code_inputs, 0);
770 std::vector<memory_t> t_used_states;
771 t_used_states.assign (d_n_states, 0);
773 // setup the first state
775 t_states[0] = end_memory_state;
778 for (size_t m = 0; m < d_total_n_delays; m++) {
779 for (size_t n = 0; n < n_states; n++) {
780 memory_t t_end_state = t_states[n];
781 for (size_t p = 0; p < d_n_code_inputs; p++) {
782 connection_t_ptr t_connection = &(t_trellis[t_end_state][p]);
783 memory_t_ptr t_mem = &(t_term_inputs[t_end_state][p]);
791 t_inputs[0] = t_trellis
796 code_convolutional_trellis::encode_single_soai
799 // single-output, all inputs; no feedback
801 if (DO_PRINT_DEBUG) {
802 std::cout << "Starting encode_single_soai.\n";
805 // shift memories down by 1 bit to make room for feedback; no
808 for (size_t p = 0; p < d_n_memories; p++) {
809 if (DO_PRINT_DEBUG) {
810 std::cout << "m_i[" << p << "] = " <<
811 n2bs(d_memory[p], 1+d_n_delays[p]);
816 if (DO_PRINT_DEBUG) {
817 std::cout << " >>= 1 -> " <<
818 n2bs(d_memory[p], 1+d_n_delays[p]) << "\n";
822 // for each input bit, if that bit's a '1', then XOR the code
823 // generators into the correct state's memory.
825 for (size_t m = 0; m < d_n_code_inputs; m++) {
826 if (DO_PRINT_DEBUG) {
827 std::cout << "c_i[" << m << "] = " <<
828 n2bs(d_current_inputs[m],1);
830 if (d_current_inputs[m] == 1) {
831 if (DO_PRINT_DEBUG) {
834 for (size_t n = 0; n < d_n_code_outputs; n++) {
835 if (DO_PRINT_DEBUG) {
836 std::cout << "m_i[s_ndx[" << m << "][" << n << "] == " <<
837 d_states_ndx[maio(m,n)] << "] = " <<
838 n2bs(d_memory[d_states_ndx[maio(m,n)]],
839 1+d_n_delays[d_states_ndx[maio(m,n)]]);
842 d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
844 if (DO_PRINT_DEBUG) {
845 std::cout << " ^= c_g[][] == " <<
846 n2bs(d_code_generators[maio(m,n)],
847 1+d_n_delays[d_states_ndx[maio(m,n)]]) <<
848 " -> " << n2bs(d_memory[d_states_ndx[maio(m,n)]],
849 1+d_n_delays[d_states_ndx[maio(m,n)]]) << "\n";
852 } else if (DO_PRINT_DEBUG) {
853 std::cout << " ... nothing to do\n";
857 for (size_t p = 0; p < d_n_code_outputs; p++) {
858 d_current_outputs[p] = 0;
861 // create the output bits, by XOR'ing the individual unique
862 // memory(ies) into the correct output bit
864 for (size_t p = 0; p < d_n_memories; p++) {
865 d_current_outputs[d_io_num[p]] ^= ((char)(d_memory[p] & 1));
868 if (DO_PRINT_DEBUG) {
869 std::cout << "ending encode_single_soai.\n";
874 code_convolutional_trellis::encode_single_soai_fb
877 // single-output, all inputs; with feedback
879 if (DO_PRINT_DEBUG) {
880 std::cout << "Starting encode_single_soai_fb.\n";
883 // shift memories down by 1 bit to make room for feedback; no
886 for (size_t p = 0; p < d_n_memories; p++) {
887 if (DO_PRINT_DEBUG) {
888 std::cout << "m_i[" << p << "] = " << d_memory[p];
893 if (DO_PRINT_DEBUG) {
894 std::cout << " -> " << d_memory[p] << "\n";
898 // for each input bit, if that bit's a '1', then XOR the code
899 // generators into the correct state's memory.
901 for (size_t m = 0; m < d_n_code_inputs; m++) {
902 if (d_current_inputs[m] == 1) {
903 for (size_t n = 0; n < d_n_code_outputs; n++) {
904 d_memory[d_states_ndx[maio(m,n)]] ^= d_code_generators[maio(m,n)];
909 for (size_t p = 0; p < d_n_code_outputs; p++) {
910 d_current_outputs[p] = 0;
913 // create the output bits, by XOR'ing the individual unique
914 // memory(ies) into the correct output bit
916 for (size_t p = 0; p < d_n_memories; p++) {
917 d_current_outputs[d_io_num[p]] ^= ((char)(d_memory[p] & 1));
920 // now that the output bits are fully created, XOR the FB back
921 // into the memories; the feedback bits have the LSB (&1) masked
922 // off already so that it doesn't contribute.
924 for (size_t p = 0; p < d_n_memories; p++) {
925 if (d_current_outputs[d_io_num[p]] == 1) {
926 d_memory[p] ^= d_code_feedback[p];
930 if (DO_PRINT_DEBUG) {
931 std::cout << "ending encode_single_soai.\n";
936 code_convolutional_trellis::encode_single_siao
939 // single input, all outputs; no feedback
941 if (DO_PRINT_DEBUG) {
942 std::cout << "starting encode_single_siao.\n";
945 // update the memories with the current input bits;
947 // for each unique memory (1 per input), shift the delays and mask
948 // off the extra high bits; then XOR in the input bit.
950 for (size_t p = 0; p < d_n_memories; p++) {
951 d_memory[p] |= ((memory_t)(d_current_inputs[d_io_num[p]]));
954 // create the output bits: for each output, loop over all inputs,
955 // find the output bits for each encoder, and XOR each together
956 // then sum (would usually be sum then XOR, but they're mutable in
957 // base-2 and it's faster this way).
959 for (size_t n = 0; n < d_n_code_outputs; n++) {
961 for (size_t m = 0; m < d_n_code_inputs; m++) {
962 t_mem ^= ((d_memory[d_states_ndx[maio(m,n)]]) &
963 d_code_generators[maio(m,n)]);
965 d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
968 // post-shift & mask the memory to guarantee that output
969 // state is in the correct bit positions (1 up, not the LSB)
971 for (size_t p = 0; p < d_n_memories; p++) {
972 d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
975 if (DO_PRINT_DEBUG) {
976 std::cout << "ending encode_single_siao.\n";
981 code_convolutional_trellis::encode_single_siao_fb
984 // single input, all outputs; with feedback
986 if (DO_PRINT_DEBUG) {
987 std::cout << "starting encode_single_siao_fb.\n";
990 // update the memories with the current input bits;
992 // for each unique memory (1 per input), shift the delays and mask
993 // off the extra high bits; then XOR in the input bit.
994 // with FB: find the feedback bit, and OR it into the input bit's slot;
996 for (size_t p = 0; p < d_n_memories; p++) {
997 memory_t t_mem = d_memory[p];
998 memory_t t_fb = t_mem & d_code_feedback[p];
999 char t_fb_bit = sum_bits_mod2 (t_fb, d_max_delay);
1000 t_mem |= ((memory_t) t_fb_bit);
1001 d_memory[p] = t_mem ^ ((memory_t)(d_current_inputs[d_io_num[p]]));
1004 // create the output bits: for each output, loop over all inputs,
1005 // find the output bits for each encoder, and XOR each together
1006 // then sum (would usually be sum then XOR, but they're mutable in
1007 // base-2 and it's faster this way).
1009 for (size_t n = 0; n < d_n_code_outputs; n++) {
1011 for (size_t m = 0; m < d_n_code_inputs; m++) {
1012 t_mem ^= ((d_memory[d_states_ndx[maio(m,n)]]) &
1013 d_code_generators[maio(m,n)]);
1015 d_current_outputs[n] = sum_bits_mod2 (t_mem, d_max_delay);
1018 // post-shift & mask the memory to guarantee that output
1019 // state is in the correct bit positions (1 up, not the LSB)
1021 for (size_t p = 0; p < d_n_memories; p++) {
1022 d_memory[p] = (d_memory[p] << 1) & d_max_mem_masks[p];
1025 if (DO_PRINT_DEBUG) {
1026 std::cout << "ending encode_loop_siao_fb.\n";