Houston, we have a trunk.
[debian/gnuradio] / gr-error-correcting-codes / src / lib / libecc / decoder_viterbi.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., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <decoder_viterbi.h>
28 #include <assert.h>
29 #include <iostream>
30
31 const int g_max_block_size_bits = 10000000;
32 const int g_max_num_streams = 10;
33 const int g_num_bits_per_byte = 8;
34
35 #define DO_TIME_THOUGHPUT 0
36 #define DO_PRINT_DEBUG_INST 0
37 #define DO_PRINT_DEBUG_INST_0 0
38 #define DO_PRINT_DEBUG_INST_1 0
39 #define DO_PRINT_DEBUG_INST_2 0
40 #define DO_PRINT_DEBUG_FSM 0
41 #define DO_PRINT_DEBUG_INIT 0
42 #define DO_PRINT_DEBUG_UP 0
43 #define DO_PRINT_DEBUG_UP_0 0
44 #define DO_PRINT_DEBUG_UP_1 0
45 #define DO_PRINT_DEBUG_MIDDLE 0
46 #define DO_PRINT_DEBUG_MIDDLE_0 0
47 #define DO_PRINT_DEBUG_MIDDLE_1 0
48 #define DO_PRINT_DEBUG_TERM 0
49 #define DO_PRINT_DEBUG_TERM_1 0
50 #define DO_PRINT_DEBUG_OUTPUT 0
51 #define DO_PRINT_DEBUG_OUTPUT_0 0
52 #define DO_PRINT_DEBUG_EXIT 0
53 #define DO_PRINT_DEBUG 0
54
55 #if DO_TIME_THOUGHPUT
56 #include <mld/mld_timer.h>
57 #endif
58 #if DO_PRINT_DEBUG
59 #include <mld/n2bs.h>
60 #endif
61
62 decoder_viterbi::decoder_viterbi
63 (int sample_precision,
64  encoder_convolutional* l_encoder)
65 {
66   // make sure the sample precitions makes sense
67
68   if ((sample_precision < 0) | (sample_precision > 32)) {
69     std::cerr << "decoder_viterbi: "
70       "Requested sample_precision (" << sample_precision <<
71       "must be between 0 and 32.\n";
72     assert (0);
73   }
74
75   // make sure that the encoder is "valid"
76
77   if (! l_encoder) {
78     std::cerr << "decoder_viterbi: Error: Encoder is a NULL pointer.\n";
79     assert (0);
80   }
81
82   // keep around a pointer to the encoder
83
84   d_encoder = l_encoder;
85
86   // fill the class variables
87
88   d_block_size_bits = d_encoder->block_size_bits ();
89   d_do_streaming = (d_block_size_bits == 0);
90   d_n_code_inputs = d_encoder->n_code_inputs ();
91   d_n_code_outputs = d_encoder->n_code_outputs ();
92   d_do_termination = d_encoder->do_termination ();
93 #if 0
94   d_total_memory = d_encoder->total_memory ();
95 #endif
96
97   // NOTE: d_n_states is a 'long', and thus is quite limited in terms
98   // of max memory and # of input streams to 2^32 states This is OK,
99   // since that many states would be impossibly slow to decode!  might
100   // make this a "long long" (64 bits) in the future
101
102   d_n_states = 1 << d_total_memory;
103   d_n_input_combinations = 1 << d_n_code_inputs;
104
105   // really nothing else to do here, since this class doesn't "know"
106   // how to process streaming versus block decoding, or partial
107   // trellis versus full-trellis.  Those will be handled by
108   // sub-classes which inherit from this one.
109
110   if (DO_PRINT_DEBUG_INST_0) {
111     std::cout << "Init:\n" <<
112       "d_block_size_bits          = " << d_block_size_bits << "\n" <<
113       "d_n_code_inputs            = " << d_n_code_inputs << "\n" <<
114       "d_n_code_outputs           = " << d_n_code_outputs << "\n" <<
115       "d_do_streaming             = " <<
116       ((d_do_streaming == true) ? "true" : "false") << "\n" <<
117       "d_do_termination           = " <<
118       ((d_do_termination == true) ? "true" : "false") << "\n" <<
119       "d_total_memory             = " << d_total_memory << "\n" <<
120       "d_n_states                 = " << d_n_states << "\n" <<
121       "d_n_input_combinations     = " << d_n_input_combinations << "\n";
122   }
123
124   // always start the fsm in the "init" state
125
126   d_fsm_state = fsm_dec_viterbi_init;
127
128   // create a vector of indexes to states when doing "up" or "termination";
129
130   d_up_term_states_ndx[0] = new size_t [d_n_states];
131   d_up_term_states_ndx[1] = new size_t [d_n_states];
132
133   // get the total number of out bits per stream
134
135   d_n_total_inputs_per_stream = d_block_size_bits;
136   if (d_do_termination == true)
137     d_n_total_inputs_per_stream += d_max_memory;
138
139
140
141 }
142
143 decoder_viterbi::~decoder_viterbi
144 ()
145 {
146   // reverse over from allocation
147
148   delete [] d_up_term_states_ndx[0];
149   delete [] d_up_term_states_ndx[1];
150
151   // delete the state's structures
152
153   state_t_ptr t_state = d_states[0];
154   for (size_t n = d_n_states; n > 0; n--, t_state++) {
155     connection_t_ptr t_connection = t_state->d_connections;
156     for (size_t m = d_n_input_combinations; m > 0; m--, t_connection++) {
157       delete [] t_connection->d_output_bits;
158     }
159     delete [] t_state->d_connections;
160   }
161   delete [] d_states[0];
162
163   t_state = d_states[1];
164   for (size_t n = d_n_states; n > 0; n--, t_state++) {
165     connection_t_ptr t_connection = t_state->d_connections;
166     for (size_t m = d_n_input_combinations; m > 0; m--, t_connection++) {
167       delete [] t_connection->d_output_bits;
168     }
169     delete [] t_state->d_connections;
170   }
171   delete [] d_states[1];
172
173   // delete the save buffer
174
175   char** t_save_buffer = d_save_buffer;
176   for (size_t n = 0; n < d_n_code_inputs; n++) {
177     delete [] (*t_save_buffer++);
178   }
179   delete [] d_save_buffer;
180 }
181
182 void
183 decoder_viterbi::reset_metrics
184 (u_char which)
185 {
186   state_t_ptr t_state = d_states[which&1];
187   for (size_t n = d_n_states; n > 0; n--, t_state++) {
188     t_state->d_max_metric = -1e10;
189 #if 0
190 // probably don't need to do these, try removing them later
191     t_state->d_max_state_ndx = 0;
192     t_state->d_max_input = -1;
193 #endif
194   }
195 }
196
197 void
198 decoder_viterbi::zero_metrics
199 (u_char which)
200 {
201   state_t_ptr t_state = d_states[which&1];
202   for (size_t n = d_n_states; n > 0; n--, t_state++) {
203     t_state->d_max_metric = 0;
204 #if 0
205 // probably don't need to do these, try removing them later
206     t_state->d_max_state_ndx = -1;
207     t_state->d_max_input = -1;
208 #endif
209   }
210 }
211
212 //FIXME 
213
214 char
215 decoder_viterbi::get_next_input
216 (const char** in_buf,
217 size_t code_input_n)
218 {
219   return (0);
220 }
221
222 void
223 decoder_viterbi::decode_private
224 (const char** in_buf,
225  char** out_buf)
226 {
227 #if 0
228
229 #if DO_TIME_THOUGHPUT
230   struct timeval t_tp;
231   start_timer (&t_tp);
232 #endif
233 #if DO_PRINT_DEBUG
234   size_t t_state_print_bits = d_total_memory + 1;
235   size_t t_mem_print_bits = d_max_memory + 2;
236 #endif
237 // setup variables for quicker access
238   const char **in_buf = (const char **) &input_items[0];
239   char **out_buf = (char **) &output_items[0];
240   int t_in_buf_ndx = 0, t_out_buf_ndx = 0;
241   int t_out_bit_shift = 0;
242   int t_ninput_items = compute_n_input_metrics (noutput_items);
243   int t_noutput_bits = noutput_items;
244
245 #if DO_PRINT_DEBUG_INST
246   std::cout << "# output items = " << noutput_items << " (" <<
247     t_noutput_bytes << " Bytes, " << (t_noutput_bytes * g_num_bits_per_byte) <<
248     " bits), # input items = " << t_ninput_items << " Symbols\n";
249   for (size_t n = 0; n < ninput_items.size(); n++) {
250     std::cout << "# input items [" << n << "] = " << ninput_items[n] << "\n";
251   }
252 #endif
253
254 // put any leftover bits into the output
255   if (d_n_saved_bits != 0) {
256 // copy the leftover from the save buffer
257 // check to make sure it will all fit
258     size_t t_noutput_bits = t_noutput_bytes * g_num_bits_per_byte;
259     size_t t_n_copy;
260     if (t_noutput_bits < d_n_saved_bits) {
261 // asking for fewer bits than available; set to copy
262 // just what's being asked for
263       t_n_copy = t_noutput_bytes;
264     } else {
265 // asking for at least as many bits as available; set to copy all
266       t_out_buf_ndx = d_n_saved_bits / g_num_bits_per_byte;
267       t_out_bit_shift = d_n_saved_bits % g_num_bits_per_byte;
268       t_n_copy = t_out_buf_ndx + (t_out_bit_shift != 0 ? 1 : 0);
269     }
270 // do the copy for all output streams (code inputs)
271 // copy starting at save buffer index "start"
272     for (size_t n = 0; n < d_n_code_inputs; n++)
273       bcopy (&(d_save_buffer[n][d_n_saved_bits_start_ndx]),
274              out_buf[n], t_n_copy);
275 #if DO_PRINT_DEBUG_INST
276     std::cout << "Copied " << t_n_copy << " Byte" <<
277       (t_n_copy != 1 ? "s" : "") << ": s_b[][" <<
278       d_n_saved_bits_start_ndx << "] => o_b[][0]\n" <<
279       "# saved bits = " << d_n_saved_bits <<
280       ", o_b_ndx = " << t_out_buf_ndx <<
281       ", bit shift = " << t_out_bit_shift << "\n";
282 #endif
283 //  update the number of saved bits and start
284     if (t_noutput_bits < d_n_saved_bits) {
285 // asking for fewer bit than available: update
286 // the number of saved bits and their starting index
287       d_n_saved_bits_start_ndx += t_noutput_bytes;
288       d_n_saved_bits -= (t_noutput_bytes * g_num_bits_per_byte);
289 #if DO_PRINT_DEBUG_INST
290       std::cout << "Updated # saved bits = " << d_n_saved_bits <<
291         ", index = " << d_n_saved_bits_start_ndx << "\n";
292 #endif
293 // nothing to consume; return the desired number of output items
294       return (noutput_items);
295     } else {
296       d_n_saved_bits_start_ndx = d_n_saved_bits = 0;
297     }
298   }
299 #if DO_PRINT_DEBUG_INST
300   std::cout << "Starting FSM in state: " <<
301     (d_fsm_state == fsm_dec_viterbi_init ? "init" :
302      (d_fsm_state == fsm_dec_viterbi_doing_up ? "up" :
303       (d_fsm_state == fsm_dec_viterbi_doing_middle ? "middle" :
304        (d_fsm_state == fsm_dec_viterbi_doing_term ? "term" :
305         (d_fsm_state == fsm_dec_viterbi_output ? "output" : "unknown"))))) << "\n";
306 #endif
307
308 // while there are input items to create
309   while (t_out_buf_ndx < t_noutput_bytes) {
310 #if DO_PRINT_DEBUG_FMS
311     std::cout << "Starting 'while': processing " << t_in_buf_ndx << " of " <<
312       t_ninput_items << " items.\nJumping to state '" <<
313       (d_fsm_state == fsm_dec_viterbi_init ? "init" :
314        (d_fsm_state == fsm_dec_viterbi_doing_up ? "up" :
315         (d_fsm_state == fsm_dec_viterbi_doing_middle ? "middle" :
316          (d_fsm_state == fsm_dec_viterbi_doing_term ? "term" :
317           (d_fsm_state == fsm_dec_viterbi_output ? "output" : "unknown"))))) << "'\n";
318 #endif
319 // jump to the correct state in the fsm
320     switch (d_fsm_state) {
321     case fsm_dec_viterbi_doing_up:
322 #if DO_PRINT_DEBUG_FSM
323       std::cout << "Starting fsm_dec_viterbi_doing_up\n";
324 #endif
325 // set the number of up_down indices
326       size_t t_n_up_down_ndx = 1 << (d_n_code_inputs *
327                                      d_time_count);
328 // stay in this state until the correct number of input symbols are
329 // reached; exit also if we run out of input symbols to process
330       while ((d_time_count < d_max_memory) &
331              (t_in_buf_ndx < t_ninput_items)) {
332 #if DO_PRINT_DEBUG_UP_0
333         std::cout << "Doing 'while' loop:\n" <<
334           "t_n_up_down_ndx    = " << t_n_up_down_ndx << "\n" <<
335           "d_time_count       = " << d_time_count << "\n" <<
336           "d_max_memory       = " << d_max_memory << "\n" <<
337           "t_in_buf_ndx       = " << t_in_buf_ndx << "\n" <<
338           "t_ninput_items     = " << t_ninput_items << "\n";
339 #endif
340 // use the "from" states, loop over all inputs and compute the metric for
341 // each & store it in the "to" state at the end of the connection.
342 // no need to compare metrics yet, since none join into any given state
343
344 #if 0
345 // reset the "to" state's metrics
346 // probably don't need to do this; try removing later
347         reset_metrics (d_states_ndx ^ 1);
348 #if DO_PRINT_DEBUG_UP
349         std::cout << "Reset Metrics\n";
350 #endif
351 #endif
352
353 // reset the state's index for each set of new inputs
354         size_t* t_state_ndx_ptr = d_up_term_states_ndx[d_up_term_ndx];
355         size_t* t_next_state_ndx_ptr = d_up_term_states_ndx[d_up_term_ndx ^ 1];
356 // loop over all current stored "up" states
357         for (size_t n = 0; n < t_n_up_down_ndx; n++) {
358           size_t t_state_ndx = *t_state_ndx_ptr++;
359 // get a pointer to this state's structure
360           state_t_ptr t_state = &(d_states[d_states_ndx][t_state_ndx]);
361 // get the connections for all inputs
362           connection_t_ptr t_connection = t_state->d_connections;
363 #if DO_PRINT_DEBUG_UP_0
364           std::cout << "Looping over all 'up' states:\n" <<
365             "n                  = " << n << "\n" <<
366             "t_n_up_down_ndx    = " << t_n_up_down_ndx << "\n" <<
367             "d_states_ndx       = " << d_states_ndx << "\n" <<
368             "t_state_ndx        = " << t_state_ndx << "\n" <<
369             "d_n_input_combs    = " << d_n_input_combinations << "\n" <<
370             "t_state            = " << t_state << "\n" <<
371             "t_connection       = " << t_connection << "\n";
372 #endif
373 // loop over all possible input values, 1 bit per input stream
374           for (size_t q = 0; q < d_n_input_combinations; q++, t_connection++) {
375 // find the "to" state for this connection
376             state_t_ptr t_to_state = t_connection->d_to;
377 // get the output bits for this connection
378             float* t_output_bit = t_connection->d_output_bits;
379 // start with this state's metric
380             float t_metric = t_state->d_max_metric;
381 #if DO_PRINT_DEBUG_UP_0
382             std::cout <<
383               "to state index     = " << t_connection->d_to_ndx << "\n" <<
384               "current metric     = " << t_metric << "\n";
385 #endif
386             if (d_do_mux_inputs == true) {
387 // if using mux'ed input streams, handle differently
388               const float* t_in_buf = &(in_buf[0][t_in_buf_ndx]);
389 // loop over all encoder-output values
390               for (size_t r = d_n_code_outputs; r > 0; r--) {
391 #if DO_PRINT_DEBUG_UP
392                 std::cout << "in_sym = " << *t_in_buf << ", code_out_bit = " <<
393                   *t_output_bit << " ==> metric -> ";
394 #endif
395                 t_metric += ((*t_in_buf++) * (*t_output_bit++));
396 #if DO_PRINT_DEBUG_UP
397                 std::cout << t_metric << "\n";
398 #endif
399               }
400             } else {
401 // loop over all encoder-output values
402               for (size_t r = 0; r < d_n_code_outputs; r++) {
403 #if DO_PRINT_DEBUG_UP
404                 std::cout << "in_sym = " << in_buf[r][t_in_buf_ndx] <<
405                   ", code_out_bit = " << *t_output_bit << " ==> metric -> ";
406 #endif
407                 t_metric += (in_buf[r][t_in_buf_ndx] * (*t_output_bit++));
408 #if DO_PRINT_DEBUG_UP
409                 std::cout << t_metric << "\n";
410 #endif
411               }
412             }
413 // get the "to" state index
414             size_t t_to_ndx = t_connection->d_to_ndx;
415 // store the metric in the "to" state; should not have been used before
416             t_to_state->d_max_metric = t_metric;
417 // add the "to" state index to the "up" state list
418             *t_next_state_ndx_ptr++ = t_to_ndx;
419 // update the traceback structure, depending on which variety it is
420 // doing full trellis before decoding; use d_out_buf
421 // simple: get the current state & output state
422             traceback_t_ptr t_out_buf = &(d_out_buf[d_time_count]
423                                           [t_state_ndx]);
424             traceback_t_ptr t_next_out_buf = &(d_out_buf[d_time_count+1]
425                                                [t_to_ndx]);
426 #if DO_PRINT_DEBUG_UP_1
427             std::cout << "d_o_b[" << d_time_count+1 << "] => d_o_b prev\n" <<
428               "ndx[" << n << "] == " << t_state_ndx <<
429               ", s[" << n2bs(t_state_ndx,t_state_print_bits) <<
430               "]: max_ndx = " <<
431               n2bs(t_state->d_max_state_ndx,t_state_print_bits) <<
432               ", input = " << n2bs(q, d_n_code_inputs+1) <<
433               ": " << t_next_out_buf << " => " << t_out_buf << "\n";
434 #endif
435 // and connect output to the current, set inputs on output
436             t_next_out_buf->d_prev = t_out_buf;
437             t_next_out_buf->d_inputs = q;
438 // finished (for) this input value
439           }
440 // finished (for) this "up_term" state
441         }
442 // increment the in_buf index, depending on mux'ing or not
443         t_in_buf_ndx += (d_do_mux_inputs == false) ? 1 : d_n_code_outputs;
444 // increment the time counter
445         d_time_count++;
446 // update the number of "up_term" states
447         d_up_term_ndx ^= 1;
448 // increase the number of using states
449         t_n_up_down_ndx <<= d_n_code_inputs;
450 // change which d_states' index to use as starting
451         d_states_ndx ^= 1;
452 // finished (while) staying in this fsm state or not
453       }
454 // if reached the end of doing the "up" part of the trellis,
455 // switch states into the middle
456       if (d_time_count == d_max_memory) {
457 #if DO_PRINT_DEBUG_FSM
458         std::cout << "Setting FSM to fsm_dec_viterbi_doing_middle\n";
459 #endif
460         d_fsm_state = fsm_dec_viterbi_doing_middle;
461       }
462 #if DO_PRINT_DEBUG_FSM
463       std::cout << "Exited fsm_dec_viterbi_doing_up\n";
464 #endif
465       break;
466     case (fsm_dec_viterbi_doing_middle):
467 #if DO_PRINT_DEBUG_FSM
468       std::cout << "Entered fsm_dec_viterbi_doing_middle\n";
469 #endif
470 // stay in this state until the correct number of input symbols is
471 // reached (if doing block coding), or until there are no more input
472 // symbols to parse
473       while (((d_block_size_bits != 0) &
474               (d_time_count < d_block_size_bits) &
475               (t_in_buf_ndx < t_ninput_items)) |
476              ((d_block_size_bits == 0) &
477               (t_in_buf_ndx < t_ninput_items))) {
478 // use all states, loop over all inputs, compute the metric for
479 // each; compare the current metric with all previous stored metric in the
480 // endpoint of the connection to find the highest one.
481
482 // reset the "to" state's metrics
483         reset_metrics (d_states_ndx ^ 1);
484 // get the state's index for each set of new inputs
485         state_t_ptr t_state = d_states[d_states_ndx];
486 #if DO_PRINT_DEBUG_MIDDLE
487         std::cout << "Time Count " << (d_time_count+1) << " of " <<
488           d_block_size_bits << "\n" <<
489           "d_states_ndx = " << d_states_ndx << "\n";;
490 #endif
491 // loop over all current states
492         for (size_t n = 0; n < d_n_states; n++, t_state++) {
493 // loop over all possible input values, 1 bit per input stream
494           connection_t_ptr t_connection = t_state->d_connections;
495 #if DO_PRINT_DEBUG_MIDDLE_0
496           std::cout << "Looping over all 'middle' states: " <<
497             n << " of " << d_n_states << "\n";
498 #endif
499           for (size_t q = 0; q < d_n_input_combinations; q++, t_connection++) {
500 #if DO_PRINT_DEBUG_MIDDLE_0
501             std::cout << "Input = " << n2bs(q, d_n_code_inputs+1) << "\n";
502 #endif
503 // start with this state's metric
504             float t_metric = t_state->d_max_metric;
505 // get the "to" state
506             state_t_ptr t_to_state = t_connection->d_to;
507 // get that state's metric
508             float t_to_metric = t_to_state->d_max_metric;
509 // see if the computation is even needed;
510 // maximum change is d_n_code_outputs if all bits match exactly
511             if ((t_to_metric - t_metric) > ((float) 2*d_n_code_outputs)) {
512 #if DO_PRINT_DEBUG_MIDDLE_0
513               std::cout << "!not computing metric!\n";
514 #endif
515               continue;
516             }
517 // metrics are close enough; do the computation and the compare
518 // get the output bits for this connection
519             float* t_output_bit = t_connection->d_output_bits;
520             if (d_do_mux_inputs == true) {
521 // if using mux'ed input streams, handle differently
522               const float* t_in_buf = &(in_buf[0][t_in_buf_ndx]);
523 // loop over all encoder-output values
524               for (size_t r = d_n_code_outputs; r > 0; r--) {
525 #if DO_PRINT_DEBUG_MIDDLE_0
526                 std::cout << "in_sym = " << *t_in_buf << ", code_out_bit = " <<
527                   *t_output_bit << " ==> metric -> ";
528 #endif
529                 t_metric += ((*t_in_buf++) * (*t_output_bit++));
530 #if DO_PRINT_DEBUG_MIDDLE_0
531                 std::cout << t_metric << "\n";
532 #endif
533               }
534             } else {
535 // loop over all encoder-output values
536               for (size_t r = 0; r < d_n_code_outputs; r++) {
537 #if DO_PRINT_DEBUG_MIDDLE_0
538                 std::cout << "in_sym = " << in_buf[r][t_in_buf_ndx] <<
539                   ", code_out_bit = " << *t_output_bit << " ==> metric -> ";
540 #endif
541                 t_metric += (in_buf[r][t_in_buf_ndx] * (*t_output_bit++));
542 #if DO_PRINT_DEBUG_MIDDLE_0
543                 std::cout << t_metric << "\n";
544 #endif
545               }
546             }
547 // done with this input value; compare old and new metrics
548             if (t_metric > t_to_metric) {
549 #if DO_PRINT_DEBUG_MIDDLE
550               std::cout << "New Metric to s[" <<
551                 n2bs (t_connection->d_to_ndx,t_state_print_bits) << "]: ";
552               if (t_to_state->d_max_metric == -1e10) {
553                 std::cout << "setting to ";
554               } else {
555                 std::cout << "was s[" <<
556                   n2bs(t_to_state->d_max_state_ndx,t_state_print_bits) <<
557                   "]i[" << n2bs (t_to_state->d_max_input, d_n_code_inputs+1) <<
558                   "]@ " << t_to_state->d_max_metric << "  now ";
559               }
560               std::cout << "s[" << n2bs(n,t_state_print_bits) << "] i[" <<
561                 n2bs (q, d_n_code_inputs+1) << "]@ " << t_metric << "\n";
562 #endif
563 // new metric is better; copy that info into the "to" state
564               t_to_state->d_max_metric = t_metric;
565               t_to_state->d_max_state_ndx = n;
566               t_to_state->d_max_input = q;
567             }
568 // finished (for) this input value
569           }
570 // finished (for) this state
571         }
572 // done with all states and all inputs; now update the traceback structure
573 // change which d_states' index to use as starting
574         d_states_ndx ^= 1;
575 // get the state's index for the "to" set of new inputs to get the
576 // "max" stuff from
577         t_state = d_states[d_states_ndx];
578 // update the traceback structure
579 // loop over all current states
580         traceback_t_ptr t_prev_out_bufs = d_out_buf[d_time_count];
581         traceback_t_ptr t_out_bufs = d_out_buf[d_time_count+1];
582 #if DO_PRINT_DEBUG_MIDDLE_1
583         std::cout << "d_o_b[" << d_time_count+1 << "] => d_o_b prev\n";
584 #endif
585         for (size_t n = d_n_states; n > 0; n--, t_state++) {
586 // simple: get the current state & output state
587 // and connect output to the current, set inputs on output
588           traceback_t_ptr t_out_buf = t_out_bufs++;
589           traceback_t_ptr t_prev_out_buf =
590             &(t_prev_out_bufs[t_state->d_max_state_ndx]);
591 #if DO_PRINT_DEBUG_MIDDLE_1
592           std::cout << "s[" << n2bs(d_n_states-n,t_state_print_bits) <<
593             "]: max_ndx = " <<
594             n2bs(t_state->d_max_state_ndx,t_state_print_bits) <<
595             ", input = " << n2bs(t_state->d_max_input, d_n_code_inputs+1) <<
596             ": " << t_out_buf << " => " << t_prev_out_buf << "\n";
597 #endif
598           t_out_buf->d_prev = t_prev_out_buf;
599           t_out_buf->d_inputs = t_state->d_max_input;
600 // finished doing this state
601         }
602 // increment the in_buf index, depending on mux'ing or not
603         t_in_buf_ndx += (d_do_mux_inputs == false) ? 1 : d_n_code_outputs;
604 // increment the time counter
605         d_time_count++;
606 // finished (while) staying in this fsm state or not
607       }
608       if ((d_block_size_bits != 0) &
609           (d_time_count == d_block_size_bits)) {
610 #if DO_PRINT_DEBUG_FSM
611         std::cout << "Setting FSM to fsm_dec_viterbi_doing_term\n";
612 #endif
613         d_fsm_state = fsm_dec_viterbi_doing_term;
614       }
615       break;
616 #if DO_PRINT_DEBUG_FSM
617       std::cout << "Exited fsm_dec_viterbi_doing_middle\n";
618 #endif
619     case (fsm_dec_viterbi_doing_term):
620 #if DO_PRINT_DEBUG_FSM
621       std::cout << "Entered fsm_dec_viterbi_doing_term\n";
622 #endif
623 // set the "next" up_down index to the end of their states
624       size_t t_time_count = d_max_memory - (d_time_count - d_block_size_bits);
625       t_n_up_down_ndx = 1 << (d_n_code_inputs * t_time_count);
626 // stay in this state until the correct number of input symbols are
627 // reached; exit also if we run out of input symbols to process
628       while ((t_time_count > 0) &
629              (t_in_buf_ndx < t_ninput_items)) {
630 #if DO_PRINT_DEBUG_TERM
631         std::cout << "Doing time " << (d_max_memory - t_time_count + 1) <<
632           " of " << d_max_memory << "; starting buf[" << t_in_buf_ndx <<
633           "] of [" << t_ninput_items << "]\n";
634 #endif
635 // use the "to" states,
636 // FIXME: loop over just the "0" inputs
637 // and compute the metric for
638 // each, compare & store it in the "to" state at the end of the connection.
639
640 // reset the "to" state's metrics
641         reset_metrics (d_states_ndx ^ 1);
642 // reset the state's index for each set of new inputs
643         size_t* t_state_ndx_ptr = d_up_term_states_ndx[d_up_term_ndx];
644         size_t* t_next_state_ndx_ptr = d_up_term_states_ndx[d_up_term_ndx ^ 1];
645 // loop over all current stored "term" state indeces
646         for (size_t n = 0; n < t_n_up_down_ndx; n++) {
647           size_t t_state_ndx = *t_state_ndx_ptr++;
648           state_t_ptr t_state = &(d_states[d_states_ndx][t_state_ndx]);
649 // loop over just the all 0 input value (d_connections[0])
650           connection_t_ptr t_connection = t_state->d_connections;
651 // get the "to" state
652           state_t_ptr t_to_state = t_connection->d_to;
653 // start with this state's metric
654           float t_metric = t_state->d_max_metric;
655 // get that state's metric
656           float t_to_metric = t_to_state->d_max_metric;
657 #if DO_PRINT_DEBUG_TERM
658           std::cout << "Term state " << (n+1) << " of " << t_n_up_down_ndx <<
659             "; using d_s[" << d_states_ndx << "][" <<
660             n2bs(t_state_ndx,t_state_print_bits) << "]\n";
661 #endif
662 // see if the computation is even needed;
663 // maximum change is d_n_code_outputs if all bits match exactly
664           if ((t_to_metric - t_metric) > ((float) 2*d_n_code_outputs)) {
665 #if DO_PRINT_DEBUG_TERM
666             std::cout << "!not computing metric!\n";
667 #endif
668             continue;
669           }
670 // metrics are close enough; do the computation and the compare
671 // get the output bits for this connection
672           float* t_output_bit = t_connection->d_output_bits;
673           if (d_do_mux_inputs == true) {
674 // if using mux'ed input streams, handle differently
675             const float* t_in_buf = &(in_buf[0][t_in_buf_ndx]);
676 // loop over all encoder-output values
677             for (size_t r = d_n_code_outputs; r > 0; r--) {
678               t_metric += ((*t_in_buf++) * (*t_output_bit++));
679             }
680           } else {
681 // loop over all encoder-output values
682             for (size_t r = 0; r < d_n_code_outputs; r++) {
683               t_metric += (in_buf[r][t_in_buf_ndx] * (*t_output_bit++));
684             }
685           }
686 // done with this input value; compare old and new metrics
687 // see if it's the best metric
688           if (t_metric > t_to_metric) {
689 #if DO_PRINT_DEBUG_TERM
690             std::cout << "New Metric to s[" <<
691               n2bs (t_connection->d_to_ndx,t_state_print_bits) << "]: ";
692             if (t_to_state->d_max_metric == -1e10) {
693               std::cout << "setting to ";
694             } else {
695               std::cout << "was s[" <<
696                 n2bs(t_to_state->d_max_state_ndx,t_state_print_bits) <<
697                 "]i[" << n2bs (t_to_state->d_max_input, d_n_code_inputs+1) <<
698                 "]@ " << t_to_state->d_max_metric << "  now ";
699             }
700             std::cout << "s[" << n2bs(t_state_ndx,t_state_print_bits) <<
701               "] i[" << n2bs (0, d_n_code_inputs+1) << "]@ " << t_metric << "\n";
702 #endif
703             t_to_state->d_max_metric = t_metric;
704             t_to_state->d_max_state_ndx = t_state_ndx;
705             t_to_state->d_max_input = 0;
706           }
707 // add the "to" state's index to the "next" set of state's indices list
708           *t_next_state_ndx_ptr++ = t_connection->d_to_ndx;
709 // finished (for) this state
710         }
711 // done with all states and all inputs; now update the traceback structure
712 // change which d_states' index to use as starting
713         d_states_ndx ^= 1;
714 // update the number of "up_term" states
715         d_up_term_ndx ^= 1;
716 // reduce the number of using states
717         t_n_up_down_ndx >>= d_n_code_inputs;
718 // reset the state's index for each set of new inputs
719         t_state_ndx_ptr = d_up_term_states_ndx[d_up_term_ndx];
720 // update the traceback structure
721 // loop over all current states
722         traceback_t_ptr t_prev_out_bufs = d_out_buf[d_time_count];
723         traceback_t_ptr t_out_bufs = d_out_buf[d_time_count+1];
724 #if DO_PRINT_DEBUG_TERM_1
725         std::cout << "d_o_b[" << d_time_count+1 << "] => d_o_b prev\n";
726 #endif
727 // loop over all current stored "term" state indices
728         for (size_t n = 0; n < t_n_up_down_ndx; n++) {
729 // get the start index and then pointer to each of the "to" states,
730 // which hold the newest "max" stuff
731           size_t t_state_ndx = *t_state_ndx_ptr++;
732           state_t_ptr t_state = &(d_states[d_states_ndx][t_state_ndx]);
733 // simple: get the current state & output state
734 // and connect output to the current, set inputs on output
735           traceback_t_ptr t_out_buf = &(t_out_bufs[t_state_ndx]);
736           traceback_t_ptr t_prev_out_buf =
737             &(t_prev_out_bufs[t_state->d_max_state_ndx]);
738 #if DO_PRINT_DEBUG_TERM_1
739           std::cout << "ndx[" << n << "] == " << t_state_ndx <<
740             ", s[" << n2bs(t_state_ndx,t_state_print_bits) <<
741             "]: max_ndx = " <<
742             n2bs(t_state->d_max_state_ndx,t_state_print_bits) <<
743             ", input = " << n2bs(t_state->d_max_input, d_n_code_inputs+1) <<
744             ": " << t_out_buf << " => " << t_prev_out_buf << "\n";
745 #endif
746           t_out_buf->d_prev = t_prev_out_buf;
747           t_out_buf->d_inputs = t_state->d_max_input;
748 // finished (for) this state
749         }
750 // increment the in_buf index, depending on mux'ing or not
751         t_in_buf_ndx += (d_do_mux_inputs == false) ? 1 : d_n_code_outputs;
752 // increment the time counters
753         t_time_count--;
754         d_time_count++;
755 // finished (while) staying in this fsm state or not
756       }
757       if (t_time_count == 0) {
758 // finished this trellis, now move as much of the decoded input to the
759 // output buffer as possible
760 #if DO_PRINT_DEBUG_FSM
761         std::cout << "Setting FSM to fsm_dec_viterbi_output\n";
762 #endif
763         d_fsm_state = fsm_dec_viterbi_output;
764       }
765 #if DO_PRINT_DEBUG_FSM
766       std::cout << "Exited fsm_dec_viterbi_doing_term\n";
767 #endif
768       break;
769     case (fsm_dec_viterbi_output):
770 #if DO_PRINT_DEBUG_FSM
771       std::cout << "Entered fsm_dec_viterbi_output.\n";
772 #endif
773 // this is done in reverse bit order (last time to first time)
774 // using the traceback structure in d_out_buf, starting with
775 // the maximum valued one in the last time slot, then using
776 // the traceback's "d_prev" link to trace the trellis back
777
778 // see where the output data will terminate
779       int t_next_out_buf_ndx = (t_out_buf_ndx +
780                                 (d_block_size_bits / g_num_bits_per_byte));
781       int t_next_out_bit_shift = (t_out_bit_shift +
782                                   (d_block_size_bits % g_num_bits_per_byte));
783       if (t_next_out_bit_shift >= g_num_bits_per_byte) {
784         t_next_out_bit_shift -= g_num_bits_per_byte;
785         t_next_out_buf_ndx++;
786       }
787 #if DO_PRINT_DEBUG_OUTPUT
788       std::cout << "First bit in at out[][" << t_out_buf_ndx << "].[" <<
789         t_out_bit_shift << "] -> " << d_block_size_bits << " bits == " <<
790         (d_block_size_bits / g_num_bits_per_byte) << " Byte" <<
791         ((d_block_size_bits / g_num_bits_per_byte) != 1 ? "s" : "") <<
792         " + " << (d_block_size_bits % g_num_bits_per_byte) << " bit" <<
793         ((d_block_size_bits % g_num_bits_per_byte) != 1 ? "s" : "") <<
794         "\nNext set of bits start at out[][" << t_next_out_buf_ndx <<
795         "].[" << t_next_out_bit_shift << "]\n";
796 #endif
797 // find the starting traceback structure
798       traceback_t_ptr t_out_buf;
799       if (d_do_termination == true) {
800 // FIXME: assume termination state == 0
801 #if DO_PRINT_DEBUG_OUTPUT_0
802         std::cout << "Using termination; going through trellis for " <<
803           d_max_memory << " bit" <<
804           ((d_max_memory != 1) ? "s" : "") << "\n";
805 #endif
806         t_out_buf = &(d_out_buf[d_time_count][0]);
807 #if DO_PRINT_DEBUG_OUTPUT_0
808         std::cout << "Starting traceback ptr " << t_out_buf << "\n";
809 #endif
810 // skip over the termination bits
811         for (size_t n = d_max_memory; n > 0; n--) {
812           t_out_buf = t_out_buf->d_prev;
813 #if DO_PRINT_DEBUG_OUTPUT_0
814           std::cout << "Next traceback ptr " << t_out_buf << "\n";
815 #endif
816         }
817       } else {
818 // no termination but doing block coding;
819 // FIXME: use the state with the bext metric
820 // get the state's index for each set of new inputs
821         state_t_ptr t_state = d_states[d_states_ndx];
822         float t_max_metric = t_state->d_max_metric;
823         size_t t_max_state_ndx = 0;
824         t_state++;
825 // loop over all current states
826         for (size_t n = 1; n < d_n_states; n++, t_state++) {
827 // start with this state's metric
828           float t_metric = t_state->d_max_metric;
829 // compare with current max
830           if (t_metric > t_max_metric) {
831             t_max_metric = t_metric;
832             t_max_state_ndx = n;
833           }
834         }
835 // get the correct out_buf reference to start with
836         t_out_buf = &(d_out_buf[d_time_count][t_max_state_ndx]);
837       }
838 #if DO_PRINT_DEBUG_OUTPUT
839       std::cout << "Found starting traceback ptr " << t_out_buf <<
840         "; getting all " << d_block_size_bits << " bits per stream.\n";
841 #endif
842 // now we've got the starting traceback structure address;
843 // check to make sure there is enough space in each output buffer
844       size_t t_block_bit_ndx = d_block_size_bits;
845       if ((t_next_out_buf_ndx > t_noutput_bytes) |
846           ((t_next_out_buf_ndx == t_noutput_bytes) &
847            (t_next_out_bit_shift != 0))) {
848 // not enough space for all output bits;
849 // find the number of extra bits to save
850         size_t t_save_buf_ndx = t_next_out_buf_ndx - t_noutput_bytes;
851         size_t t_n_extra_bits = ((t_save_buf_ndx * g_num_bits_per_byte) +
852                                  t_next_out_bit_shift);
853 // set the number of saved bits
854         d_n_saved_bits = t_n_extra_bits;
855 // remove the extra bits from the number to copy to the output buffer
856         t_block_bit_ndx -= t_n_extra_bits;
857 // find the actual output buf index, once we get there
858         t_next_out_buf_ndx -= t_save_buf_ndx;
859 #if DO_PRINT_DEBUG_OUTPUT
860         std::cout << "Not enough output buffer space:\n" <<
861           "len (o_b) = " << t_noutput_bytes << " ==> " <<
862           t_n_extra_bits << " extra bit" <<
863           (t_n_extra_bits != 1 ? "s" : "") << " == " <<
864           t_save_buf_ndx << " Byte" <<
865           (t_save_buf_ndx != 1 ? "s" : "") << ", " <<
866           t_next_out_bit_shift << " bit" <<
867           (t_next_out_bit_shift != 1 ? "s" : "") << "\n";
868 #endif
869 // zero each output buffers' bytes
870         size_t t_n_zero = t_save_buf_ndx + (t_next_out_bit_shift ? 1 : 0);
871 #if DO_PRINT_DEBUG_OUTPUT
872         size_t t_n_out_bytes = ((d_block_size_bits / g_num_bits_per_byte) +
873                                 ((d_block_size_bits % g_num_bits_per_byte) ? 1 : 0));
874         std::cout << "Zeroing save buffer from for " << t_n_zero <<
875           " Byte" << (t_n_zero != 1 ? "s" : "") << " (of " <<
876           d_block_size_bits << " bit" <<
877           (d_block_size_bits != 1 ? "s" : "") << " == " << t_n_out_bytes <<
878           " Byte" << (t_n_out_bytes != 1 ? "s" : "") << ")\n";
879 #endif
880         for (size_t m = 0; m < d_n_code_inputs; m++)
881           bzero (d_save_buffer[m], t_n_zero);
882 // loop over all extra bits
883         for (size_t n = t_n_extra_bits; n > 0; n--) {
884 // first decrement the output bit & byte as necessary
885           if (--t_next_out_bit_shift < 0) {
886             t_next_out_bit_shift += g_num_bits_per_byte;
887             t_save_buf_ndx--;
888           }
889 // get the encoder inputs to output
890           size_t t_inputs = t_out_buf->d_inputs;
891 // loop over all code inputs (decoder-outputs), 1 bit per stream
892           for (size_t m = 0; m < d_n_code_inputs; m++) {
893             d_save_buffer[m][t_save_buf_ndx] |=
894               ((t_inputs & 1) << t_next_out_bit_shift);
895             t_inputs >>= 1;
896           }
897           t_out_buf = t_out_buf->d_prev;
898 #if DO_PRINT_DEBUG_OUTPUT_0
899           std::cout << "Next traceback ptr " << t_out_buf << "\n";
900 #endif
901         }
902 // at exit, "t_out_buf_ndx" should be == t_noutput_bytes, and
903 // "t_out_bit_shift" should be 0; check these!
904 #if DO_PRINT_DEBUG_OUTPUT
905         std::cout << "n_o_b_ndx (" << t_next_out_buf_ndx << ") ?=? " <<
906           "#o_B (" << t_noutput_bytes << ") & t_o_b_sh (" <<
907           t_next_out_bit_shift << ") ?=? 0\n";
908         assert (t_next_out_buf_ndx == t_noutput_bytes);
909         assert (t_next_out_bit_shift == 0);
910 #endif
911       }
912 // set the correct output buffer index and bit shift
913       t_out_bit_shift = t_next_out_bit_shift;
914       t_out_buf_ndx = t_next_out_buf_ndx;
915 // copy any remaining bits looping backwards in bit-time
916 // through the traceback trellis
917       for (size_t n = t_block_bit_ndx; n > 0; n--) {
918 // first decrement the output bit & byte as necessary
919         if (--t_out_bit_shift < 0) {
920           t_out_bit_shift += g_num_bits_per_byte;
921           t_out_buf_ndx--;
922         }
923 // get the encoder inputs to output
924         size_t t_inputs = t_out_buf->d_inputs;
925 // loop over all code inputs (decoder-outputs), 1 bit per stream
926         for (size_t m = 0; m < d_n_code_inputs; m++) {
927           out_buf[m][t_out_buf_ndx] |= ((t_inputs & 1) << t_out_bit_shift);
928           t_inputs >>= 1;
929         }
930         t_out_buf = t_out_buf->d_prev;
931 #if DO_PRINT_DEBUG_OUTPUT_0
932         std::cout << "Next traceback ptr " << t_out_buf << "\n";
933 #endif
934       }
935 // set the next output byte and bit-shift
936       t_out_bit_shift = t_next_out_bit_shift;
937       t_out_buf_ndx = t_next_out_buf_ndx;
938 #if DO_PRINT_DEBUG_FSM
939       std::cout << "Set FSM to fsm_dec_viterbi_init\n";
940 #endif
941       d_fsm_state = fsm_dec_viterbi_init;
942 #if DO_PRINT_DEBUG_FSM
943       std::cout << "Exited fsm_dec_viterbi_output\n";
944 #endif
945       break;
946     case (fsm_dec_viterbi_init):
947 #if DO_PRINT_DEBUG_FSM
948       std::cout << "Entered fsm_dec_viterbi_init\n";
949 #endif
950 // this is called immediately (first input bit upon startup),
951 // or after termination of a trellis.
952
953 // reset states to the 0'th one
954       d_up_term_ndx = d_states_ndx = 0;
955 // zero the metrics for those states
956       zero_metrics (0);
957 #if 0
958 // might not need to do this; check and see after it works
959 // reset up_term states and number so that there is 1 item, the "0" state.
960       bzero (d_up_term_states_ndx[0], sizeof (size_t) * d_n_states);
961       bzero (d_up_term_states_ndx[1], sizeof (size_t) * d_n_states);
962 #else
963       d_up_term_states_ndx[0][0] = 0;
964 #endif
965 // reset time count back to the start
966       d_time_count = 0;
967 #if 0
968 // reset the traceback structures
969 // might not need to do this; check and see after it works
970       traceback_t_hdl t_out_bufs = d_out_buf;
971       for (size_t n = d_n_traceback_els; n > 0; n--) {
972         traceback_t_ptr t_out_buf = (*t_out_bufs++);
973         for (size_t m = d_n_states; m > 0; m--, t_out_buf++) {
974           t_out_buf->d_prev = NULL;
975           t_out_buf->d_inputs = -1;
976         }
977       }
978 #endif
979 // set the fsm to "doing up"
980 #if DO_PRINT_DEBUG_FSM
981       std::cout << "Set FSM to fsm_dec_viterbi_doing_up\n";
982 #endif
983       d_fsm_state = fsm_dec_viterbi_doing_up;
984 #if DO_PRINT_DEBUG_FSM
985       std::cout << "Exited fsm_dec_viterbi_init\n";
986 #endif
987       break;
988 // should never get here!
989     default:
990       assert (0);
991 // done (switch) with FSM
992     }
993 // done (while) there are inputs
994   }
995
996 // consume all of the input items on all input streams
997 // "ninput_items[]" doesn't seem to be reliable,
998 // so compute this from the actual number of blocks processed
999 #if DO_PRINT_DEBUG_EXIT
1000   std::cout << "Exiting FSM in state: " <<
1001     (d_fsm_state == fsm_dec_viterbi_init ? "init" :
1002      (d_fsm_state == fsm_dec_viterbi_doing_up ? "up" :
1003       (d_fsm_state == fsm_dec_viterbi_doing_middle ? "middle" :
1004        (d_fsm_state == fsm_dec_viterbi_doing_term ? "term" :
1005         (d_fsm_state == fsm_dec_viterbi_output ? "output" : "unknown"))))) << "\n" <<
1006     "Consuming " << t_in_buf_ndx <<
1007     " input items on each input stream (of " << t_ninput_items << ").\n";
1008 #endif
1009   consume_each (t_in_buf_ndx);
1010
1011 // make sure the number of output items makes sense
1012 // t_out_buf_ndx always points to the current index
1013 // t_out_bit_shift always points to the next bit position to be written
1014   int t_leftover_bytes = t_out_buf_ndx % d_out_stream_el_size_bytes;
1015   int t_leftover_bits = ((t_leftover_bytes * g_num_bits_per_byte) +
1016                          t_out_bit_shift);
1017   int t_noutput_items = noutput_items;
1018 #if DO_PRINT_DEBUG_EXIT
1019   std::cout << "Final o_b[" << t_out_buf_ndx << "][" <<
1020     t_out_bit_shift << "] of " << t_noutput_bytes <<
1021     ", el_size = " << d_out_stream_el_size_bytes <<
1022     ", lo_Bytes = " << t_leftover_bytes <<
1023     ", t_lo_bits = " << t_leftover_bits << "\n" <<
1024     "Desired # output items = " << noutput_items << "\n";
1025 #endif
1026   if (t_leftover_bits != 0) {
1027     // should never get here!
1028 #if 1
1029     assert (0);
1030 #else
1031     int t_ndx = t_out_buf_ndx - t_leftover_bytes;
1032     size_t t_n_copy = t_leftover_bytes + ((t_out_bit_shift != 0) ? 1 : 0);
1033     assert (t_n_copy <= d_out_stream_el_size_bytes);
1034 // copy the leftover into the save buffer
1035     for (size_t n = 0; n < d_n_code_inputs; n++) {
1036       bcopy (&(out_buf[n][t_ndx]), d_save_buffer[n], t_n_copy);
1037     }
1038     t_noutput_items = t_ndx / d_out_stream_el_size_bytes;
1039     d_n_saved_bits = t_leftover_bits;
1040 #if DO_PRINT_DEBUG_EXIT
1041     std::cout << "Copied " << t_n_copy << " Byte" <<
1042       (t_n_copy != 1 ? "s" : "") << " from o_b[][" << t_ndx <<
1043       "] into each save buffer.\n" <<
1044       "Actual #output items = " << t_noutput_items <<
1045       ", # saved bit(s) = " << d_n_saved_bits << "\n";
1046 #endif
1047 #endif
1048   }
1049
1050 #endif
1051
1052 #if DO_TIME_THOUGHPUT
1053   u_long d_t = end_timer (&t_tp);
1054
1055   std::cout << "dec_blk_conv_soft_full: Completed " << t_ninput_items <<
1056     " bits in " << d_t << " usec => " <<
1057     1e6*(((double)(t_ninput_items))/((double) d_t)) <<
1058     " b/s\n";
1059 #endif
1060 }