summaryrefslogtreecommitdiff
path: root/gr-trellis/doc/gr-trellis.xml
diff options
context:
space:
mode:
authoreb2006-08-07 16:28:55 +0000
committereb2006-08-07 16:28:55 +0000
commite9a821632def12c450110e164d0d1c0275d41946 (patch)
treefb21aedc8c457039e5c6482a1730f90de0f1e136 /gr-trellis/doc/gr-trellis.xml
parent5d94acf997672a5101db4984a590aa4f1d2d6e03 (diff)
downloadgnuradio-e9a821632def12c450110e164d0d1c0275d41946.tar.gz
gnuradio-e9a821632def12c450110e164d0d1c0275d41946.tar.bz2
gnuradio-e9a821632def12c450110e164d0d1c0275d41946.zip
Merged anastas/wip changes r3156:3218 into trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3219 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-trellis/doc/gr-trellis.xml')
-rw-r--r--gr-trellis/doc/gr-trellis.xml653
1 files changed, 653 insertions, 0 deletions
diff --git a/gr-trellis/doc/gr-trellis.xml b/gr-trellis/doc/gr-trellis.xml
new file mode 100644
index 000000000..9aacaeab3
--- /dev/null
+++ b/gr-trellis/doc/gr-trellis.xml
@@ -0,0 +1,653 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "docbookx.dtd" [
+ <!ENTITY test_tcm_listing SYSTEM "test_tcm.py">
+]>
+
+<article>
+
+<articleinfo>
+ <title>Trellis-based algorithms for GNU Radio</title>
+ <author>
+ <firstname>Achilleas</firstname>
+ <surname>Anastasopoulos</surname>
+ <affiliation>
+ <address>
+ <email>anastas@umich.edu</email>
+ </address>
+ </affiliation>
+ </author>
+
+<revhistory>
+ <revision>
+ <revnumber>v0.0</revnumber>
+ <date>2006-08-03</date>
+ <revremark>
+ First cut.
+ </revremark>
+ </revision>
+
+</revhistory>
+
+<abstract><para>This document provides a description of the
+Finite State Machine (FSM) implementation and the related
+trellis-based encoding and decoding algorithms
+for GNU Radio.
+</para></abstract>
+
+</articleinfo>
+
+
+
+
+<!--=====================================================-->
+<sect1 id="intro"><title>Introduction</title>
+
+<para>....</para>
+
+<para>
+The basic goal of the implementation is to have a generic way of
+describing an FSM that is decoupled from whether it describes a
+convolutional
+code (CC), a trellis code (TC), an inter-symbol interference (ISI)
+channel, or any
+other communication system that can be modeled with an FSM.
+To achieve this goal, we need to separate the pure FSM descrition from the
+rest of the model details. For instance, in the case of a rate 2/3 TC,
+the FSM should not involve details about the modulation used (it can
+be an 8-ary PAM, or 8-PSK, etc). Similarly, when attempting maximum likelihood
+sequence detection (MLSD)--using for instance the Viterbi algorithm (VA)--
+the VA implementation should not be concerned with the channel details
+(such as modulations, channel type, hard or soft inputs, etc).
+Clearly, having generality as the primary goal implies some penalty
+on the code efficiency, as compared to fully custom implementations.
+</para>
+
+<para>
+We will now describe the implementation of the basic ingedient, the FSM.
+</para>
+
+</sect1>
+
+
+<!--=====================================================-->
+<sect1 id="fsm"><title>The FSM class</title>
+
+<para>An FSM describes the evolution of a system with inputs
+x<subscript>k</subscript>, states s<subscript>k</subscript> and outputs y<subscript>k</subscript>. At time k the FSM state is s<subscript>k</subscript>.
+Upon reception of a new input symbol x<subscript>k</subscript>, it outputs an output symbol
+y<subscript>k</subscript> which is a function of both x<subscript>k</subscript> and s<subscript>k</subscript>.
+It will then move to a next state s<subscript>k+1</subscript>.
+An FSM has a finite number of states, input and output symbols.
+All these are formally described as follows:
+</para>
+
+<itemizedlist>
+<listitem><para>The input alphabet A<subscript>I</subscript>={0,1,2,...,I-1}, with cardinality I, so that x<subscript>k</subscript> takes values in A<subscript>I</subscript>.</para></listitem>
+<listitem><para>The state alphabet A<subscript>S</subscript>={0,1,2,...,S-1}, with cardinality S, so that s<subscript>k</subscript> takes values in A<subscript>S</subscript>.</para></listitem>
+<listitem><para>The output alphabet A<subscript>O</subscript>={0,1,2,...,O-1}, with cardinality O, so that y<subscript>k</subscript> takes values in A<subscript>O</subscript></para></listitem>
+<listitem><para>The "next-state" function NS: A<subscript>S</subscript> x A<subscript>I</subscript> --> A<subscript>S</subscript>,
+with the meaning
+s<subscript>k+1</subscript> = NS(s<subscript>k</subscript>, x<subscript>k</subscript>)</para></listitem>
+<listitem><para>The "output-symbol" function OS: A<subscript>S</subscript> x A<subscript>I</subscript> --> A<subscript>S</subscript>,
+with the meaning
+y<subscript>k</subscript> = OS(s<subscript>k</subscript>, x<subscript>k</subscript>)</para></listitem>
+</itemizedlist>
+
+<para>
+Thus, a complete description of the FSM is given by the
+the five-tuple (I,S,O,NS,OS).
+Observe that implementation details are hidden
+in how the outside world interprets these input and output
+integer symbols.
+Here is an example of an FSM describing the (2,1) CC
+with constraint length 3 and generator polynomial matrix
+(1+D+D<superscript>2</superscript> 1+D<superscript>2</superscript>)
+from Proakis-Salehi pg. 779.
+</para>
+
+
+<example id="cc_ex"><title>(2,1) CC with generator polynomials (1+D+D<superscript>2</superscript> 1+D<superscript>2</superscript>)</title>
+
+<para>
+This CC accepts 1 bit at a time, and outputs 2 bits at a time.
+It has a shift register storing the last two input bits.
+In particular,
+b<subscript>k</subscript>(0)=x<subscript>k</subscript>+
+x<subscript>k-1</subscript>+x<subscript>k-2</subscript>, and
+b<subscript>k</subscript>(1)=x<subscript>k</subscript>+
+x<subscript>k-2</subscript>, where addition is mod-2.
+We can represent the state of this system
+as s<subscript>k</subscript> = (x<subscript>k-1</subscript> x<subscript>k-2</subscript>)<subscript>10</subscript>. In addition we can represent its
+output symbol as y<subscript>k</subscript> = (b<subscript>k</subscript>(1) b<subscript>k</subscript>(0))<subscript>10</subscript>.
+Based on the above assumptions, the input alphabet A<subscript>I</subscript>={0,1}, so I=2;
+the state alphabet A<subscript>S</subscript>={0,1,2,3}, so S=4; and
+the output alphabet A<subscript>O</subscript>={0,1,2,3}, so O=4.
+The "next-state" function NS(,) is given by
+<programlisting>
+s<subscript>k</subscript> x<subscript>k</subscript> s<subscript>k+1</subscript>
+0 0 0
+0 1 2
+1 0 0
+1 1 2
+2 0 1
+2 1 3
+3 0 1
+3 1 3
+</programlisting>
+The "output-symbol" function OS(,) can be given by
+<programlisting>
+s<subscript>k</subscript> x<subscript>k</subscript> y<subscript>k</subscript>
+0 0 0
+0 1 3
+1 0 3
+1 1 0
+2 0 1
+2 1 2
+3 0 2
+3 1 1
+</programlisting>
+</para>
+
+<para>
+Note that although the CC outputs 2 bits per time period, following
+our approach, there is only one (quaternary) output symbol per
+time period (for instance, here we use the decimal representation
+of the 2-bits). Also note that the modulation used is not part of
+the FSM description: it can be BPSK, OOK, BFSK, QPSK with or without Gray mapping, etc;
+it is up to the rest of the program to interpret the meaning of
+the symbol y<subscript>k</subscript>.
+</para>
+
+</example>
+
+
+<para>
+The C++ implementation of the FSM class keeps private information about
+I,S,O,NS,OS and public methods to read and write them. The NS
+and OS matrices are implemented as STL 1-dimensional vectors.
+</para>
+
+<programlisting>
+class fsm {
+private:
+ int d_I;
+ int d_S;
+ int d_O;
+ std::vector&lt;int&gt; d_NS;
+ std::vector&lt;int&gt; d_OS;
+ std::vector&lt;int&gt; d_PS;
+ std::vector&lt;int&gt; d_PI;
+public:
+ fsm();
+ fsm(const fsm &amp;FSM);
+ fsm(const int I, const int S, const int O, const std::vector&lt;int&gt; &amp;NS, const std::vector&lt;int&gt; &amp;OS);
+ fsm(const char *name);
+ fsm(const int mod_size, const int ch_length);
+ int I () const { return d_I; }
+ int S () const { return d_S; }
+ int O () const { return d_O; }
+ const std::vector&lt;int&gt; &amp; NS () const { return d_NS; }
+ const std::vector&lt;int&gt; &amp; OS () const { return d_OS; }
+ const std::vector&lt;int&gt; &amp; PS () const { return d_PS; }
+ const std::vector&lt;int&gt; &amp; PI () const { return d_PI; }
+};
+</programlisting>
+
+<para>
+As can be seen, other than the trivial and the copy constructor,
+there are three additional
+ways to construct an FSM.
+</para>
+
+<itemizedlist>
+<listitem>
+<para>Supplying the parameters I,S,O,NS,OS:</para>
+<programlisting>
+ fsm(const int I, const int S, const int O, const std::vector&lt;int&gt; &amp;NS, const std::vector&lt;int&gt; &amp;OS);
+</programlisting>
+</listitem>
+
+<listitem>
+<para>Giving a filename containing all the FSM information:</para>
+<programlisting>
+ fsm(const char *name);
+</programlisting>
+<para>This information has to be in the following format</para>
+<programlisting>
+I S O
+
+NS(0,0) NS(0,1) ... NS(0,I-1)
+NS(1,0) NS(1,1) ... NS(1,I-1)
+...
+NS(S-1,0) NS(S-1,1) ... NS(S-1,I-1)
+
+OS(0,0) OS(0,1) ... OS(0,I-1)
+OS(1,0) OS(1,1) ... OS(1,I-1)
+...
+OS(S-1,0) OS(S-1,1) ... OS(S-1,I-1)
+</programlisting>
+<para>For instance, the file containing the information for the example mentioned above is of the form</para>
+<programlisting>
+2 4 4
+
+0 2
+0 2
+1 3
+1 3
+
+0 3
+3 0
+1 2
+2 1
+</programlisting>
+</listitem>
+
+<listitem>
+<para>The third way is specific to FSMs resulting from shift registers, and the output symbol being the entire transition (ie, current_state and current_input). These FSMs are usefull when describibg ISI channels. In particular the state is comprised of the.....
+</para>
+<programlisting>
+ fsm(const int mod_size, const int ch_length);
+</programlisting>
+</listitem>
+
+</itemizedlist>
+
+
+<para>
+Finally, as can be seen from the above description, there are
+two more variables included in the FSM class implementation,
+the PS and the PI matrices. These are computed internally
+when an FSM is instantiated and their meaning is as follows.
+Sometimes (eg, in the traceback operation of the VA) we need
+to trace the history of the state or the input sequence.
+To do this we would like to know for a given state s<subscript>k</subscript>, what are the possible previous states s<subscript>k-1</subscript>
+and what input symbols x<subscript>k-1</subscript> will get us from
+s<subscript>k-1</subscript> to s<subscript>k</subscript>. This information can be derived from NS; however we want to have it ready in a
+convenient format.
+In the following we assume that for any state,
+the number of incoming transitions is the same as the number of
+outgoing transitions, ie, equal to I. All applications of interest
+have FSMs satisfying this requirement.
+
+If we arbitrarily index the incoming transitions to the current state
+by "i", then as i goes from 0 to I-1, PS(s<subscript>k</subscript>,i)
+gives all previous states s<subscript>k-1</subscript>,
+and PI(s<subscript>k</subscript>,i) gives all previous inputs x<subscript>k-1</subscript>.
+In other words, for any given s<subscript>k</subscript> and any index i=0,1,...I-1, starting from
+s<subscript>k-1</subscript>=PS(s<subscript>k</subscript>,i)
+with input
+x<subscript>k-1</subscript>=PI(s<subscript>k</subscript>,i)
+will get us to the state s<subscript>k</subscript>.
+More formally, for any i=0,1,...I-1 we have
+s<subscript>k</subscript> = NS(PS(s<subscript>k</subscript>,i),PI(s<subscript>k</subscript>,i)).
+
+</para>
+
+
+</sect1>
+
+
+
+
+
+<!--=====================================================-->
+<sect1 id="tcm"><title>TCM: A Complete Example</title>
+
+<para>
+We now discuss through a concrete example how
+the above FSM model can be used in GNU Radio.
+
+The communication system that we want to simulate
+consists of a source generating the
+input information in packets, a CC encoding each packet separately,
+a memoryless modulator,
+an additive white Gaussian noise (AWGN) channel, and
+the VA performing MLSD.
+The program source is as follows.
+</para>
+
+
+
+<programlisting>
+#!/usr/bin/env python
+
+from gnuradio import gr
+from gnuradio import audio
+from gnuradio import trellis
+from gnuradio import eng_notation
+import math
+import sys
+import random
+import fsm_utils
+
+def run_test (f,Kb,bitspersymbol,K,dimensionality,constellation,N0,seed):
+ fg = gr.flow_graph ()
+
+
+ # TX
+ src = gr.lfsr_32k_source_s()
+ src_head = gr.head (gr.sizeof_short,Kb/16) # packet size in shorts
+ s2fsmi = gr.packed_to_unpacked_ss(bitspersymbol,gr.GR_MSB_FIRST) # unpack shorts to symbols compatible with the FSM input cardinality
+ enc = trellis.encoder_ss(f,0) # initial state = 0
+ mod = gr.chunks_to_symbols_sf(constellation,dimensionality)
+
+
+ # CHANNEL
+ add = gr.add_ff()
+ noise = gr.noise_source_f(gr.GR_GAUSSIAN,math.sqrt(N0/2),seed)
+
+
+ # RX
+ metrics = trellis.metrics_f(f.O(),dimensionality,constellation,trellis.TRELLIS_EUCLIDEAN) # data preprocessing to generate metrics for Viterbi
+ va = trellis.viterbi_s(f,K,0,-1) # Put -1 if the Initial/Final states are not set.
+ fsmi2s = gr.unpacked_to_packed_ss(bitspersymbol,gr.GR_MSB_FIRST) # pack FSM input symbols to shorts
+ dst = gr.check_lfsr_32k_s();
+
+
+ fg.connect (src,src_head,s2fsmi,enc,mod)
+ fg.connect (mod,(add,0))
+ fg.connect (noise,(add,1))
+ fg.connect (add,metrics)
+ fg.connect (metrics,va,fsmi2s,dst)
+
+
+ fg.run()
+
+ # A bit of cheating: run the program once and print the
+ # final encoder state..
+ # Then put it as the last argument in the viterbi block
+ #print "final state = " , enc.ST()
+
+ ntotal = dst.ntotal ()
+ nright = dst.nright ()
+ runlength = dst.runlength ()
+ return (ntotal,ntotal-nright)
+
+
+
+
+def main(args):
+ nargs = len (args)
+ if nargs == 3:
+ fname=args[0]
+ esn0_db=float(args[1]) # Es/No in dB
+ rep=int(args[2]) # number of times the experiment is run to collect enough errors
+ else:
+ sys.stderr.write ('usage: test_tcm.py fsm_fname Es/No_db repetitions\n')
+ sys.exit (1)
+
+ # system parameters
+ f=trellis.fsm(fname) # get the FSM specification from a file (will hopefully be automated in the future...)
+ Kb=1024*16 # packet size in bits (make it multiple of 16 so it can be packed in a short)
+ bitspersymbol = int(round(math.log(f.I())/math.log(2))) # bits per FSM input symbol
+ K=Kb/bitspersymbol # packet size in trellis steps
+ modulation = fsm_utils.psk4 # see fsm_utlis.py for available predefined modulations
+ dimensionality = modulation[0]
+ constellation = modulation[1]
+ if len(constellation)/dimensionality != f.O():
+ sys.stderr.write ('Incompatible FSM output cardinality and modulation size.\n')
+ sys.exit (1)
+ # calculate average symbol energy
+ Es = 0
+ for i in range(len(constellation)):
+ Es = Es + constellation[i]**2
+ Es = Es / (len(constellation)/dimensionality)
+ N0=Es/pow(10.0,esn0_db/10.0); # noise variance
+
+
+
+ tot_s=0
+ terr_s=0
+ for i in range(rep):
+ (s,e)=run_test(f,Kb,bitspersymbol,K,dimensionality,constellation,N0,-long(666+i)) # run experiment with different seed to get different noise realizations
+ tot_s=tot_s+s
+ terr_s=terr_s+e
+ if (i%100==0):
+ print i,s,e,tot_s,terr_s, '%e' % ((1.0*terr_s)/tot_s)
+ # estimate of the (short) error rate
+ print tot_s,terr_s, '%e' % ((1.0*terr_s)/tot_s)
+
+
+if __name__ == '__main__':
+ main (sys.argv[1:])
+</programlisting>
+
+<para>
+The program is called by
+</para>
+<programlisting>
+./test_tcm.py fsm_fname Es/No_db repetitions
+</programlisting>
+<para>
+where "fsm_fname" is the file containing the FSM specification of the
+tested TCM code, "Es/No_db" is the SNR in dB, and "repetitions"
+are the number of packets to be transmitted and received in order to
+collect sufficient number of errors for an accurate estimate of the
+error rate.
+</para>
+
+<para>
+The FSM is first intantiated in "main" by
+</para>
+<programlisting>
+ f=trellis.fsm(fname)
+</programlisting>
+
+
+
+
+
+
+
+<para>
+Each packet has size Kb bits
+(we choose Kb to be a multiple of 16 so that all bits fit nicely into shorts and can be generated by the lfsr GNU Radio).
+Assuming that the FSM input has cardinality I, each input symbol consists
+of bitspersymbol=log<subscript>2</subscript>( I ). The Kb/16 shorts are now
+unpacked to K=Kb/bitspersymbol input
+symbols that will drive the FSM encoder.
+</para>
+<programlisting>
+ Kb=1024*16 # packet size in bits (make it multiple of 16 so it can be packed in a short)
+ bitspersymbol = int(round(math.log(f.I())/math.log(2))) # bits per FSM input symbol
+ K=Kb/bitspersymbol # packet size in trellis steps
+</programlisting>
+
+
+
+<para>
+The FSM will produce K output symbols (remeber the FSM produces always one output symbol for each input symbol). Each of these symbols needs to be modulated. Since we are simulating the communication system, we need not simulate the actual waveforms. An M-ary, N-dimensional
+modulation is completely specified by a set of M, N-dimensional real vectors. In "fsm_utils.py" file we give a number of useful modulations with the following format: modulation = (N,constellation), where
+constellation=[c11,c12,...,c1N,c21,c22,...,c2N,...,cM1,cM2,...cMN].
+The meaning of the above is that every constellation point c_i
+is an N-dimnsional vector c_i=(ci1,ci2,...,ciN)
+For instance, 4-ary PAM is represented as
+(1,[-3, -1, 1, 3]), while QPSK is represented as
+(2,[1, 0, 0, 1, 0, -1, -1, 0]). In our example we choose QPSK modulation.
+Clearly, M should be equal to the cardinality of the FSM output, O.
+Finally the average symbol energy and noise variance are calculated.
+</para>
+<programlisting>
+ modulation = fsm_utils.psk4 # see fsm_utlis.py for available predefined modulations
+ dimensionality = modulation[0]
+ constellation = modulation[1]
+ if len(constellation)/dimensionality != f.O():
+ sys.stderr.write ('Incompatible FSM output cardinality and modulation size.\n')
+ sys.exit (1)
+ # calculate average symbol energy
+ Es = 0
+ for i in range(len(constellation)):
+ Es = Es + constellation[i]**2
+ Es = Es / (len(constellation)/dimensionality)
+ N0=Es/pow(10.0,esn0_db/10.0); # noise variance
+</programlisting>
+
+
+
+<para>
+Then, "run_test" is called with a different "seed" so that we get
+different noise realizations.
+</para>
+<programlisting>
+ (s,e)=run_test(f,Kb,bitspersymbol,K,dimensionality,constellation,N0,-long(666+i)) # run experiment with different seed to get different noise realizations
+</programlisting>
+
+
+
+<para>
+Let us examine now the "run_test" function.
+First we set up the transmitter blocks.
+The Kb/16 shorts are first unpacked to
+symbols consistent with the FSM input alphabet.
+The FSm encoder requires the FSM specification,
+and an initial state (which is set to 0 in this example).
+</para>
+<programlisting>
+ # TX
+ src = gr.lfsr_32k_source_s()
+ src_head = gr.head (gr.sizeof_short,Kb/16) # packet size in shorts
+ s2fsmi = gr.packed_to_unpacked_ss(bitspersymbol,gr.GR_MSB_FIRST) # unpack shorts to symbols compatible with the FSM input cardinality
+ enc = trellis.encoder_ss(f,0) # initial state = 0
+</programlisting>
+
+
+
+
+
+<para>
+The "chunks_to_symbols_sf" is essentially a memoryless mapper which
+for each input symbol y_k
+outputs a sequence of N numbers ci1,ci2,...,ciN representing the
+coordianates of the constellation symbol c_i with i=y_k.
+</para>
+<programlisting>
+ mod = gr.chunks_to_symbols_sf(constellation,dimensionality)
+</programlisting>
+
+<para>
+The channel is AWGN with appropriate noise variance.
+For each transmitted symbol c_k=(ck1,ck2,...,ckN) we receive a noisy version
+r_k=(rk1,rk2,...,rkN).
+</para>
+<programlisting>
+ add = gr.add_ff()
+ noise = gr.noise_source_f(gr.GR_GAUSSIAN,math.sqrt(N0/2),seed)
+</programlisting>
+
+
+
+<para>
+Part of the design methodology was to decouple the FSM and VA from
+the details of the modulation, channel, receiver front-end etc.
+In order for the VA to run, we only need to provide it with
+a number representing a cost associated with each transition
+in the trellis. Then the VA will find the sequence with
+the smallest total cost through the trellis.
+The cost associated with a transition (s_k,x_k) is only a function
+of the output y_k = OS(s_k,x_k), and the observation
+vector r_k. Thus, for each time period, k,
+we need to label each of the SxI transitions with such a cost.
+This means that for each time period we need to evaluate
+O such numbers (one for each possible output symbol y_k).
+This is done
+in "metrics_f". In particular, metrics_f is a memoryless device
+taking N inputs at a time and producing O outputs. The N inputs are
+rk1,rk2,...,rkN.
+The O outputs
+are the costs associated with observations rk1,rk2,...,rkN and
+hypothesized output symbols c_1,c_2,...,c_M. For instance,
+if we choose to perform soft-input VA, we need to evaluate
+the Euclidean distance between r_k and each of c_1,c_2,...,c_M,
+for each of the K transmitted symbols.
+Other options are available as well; for instance, we can
+do hard decision demodulation and feed the VA with
+symbol Hamming distances, or even bit Hamming distances, etc.
+These are all implemented in "metrics_f".
+</para>
+<programlisting>
+ # RX
+ metrics = trellis.metrics_f(f.O(),dimensionality,constellation,trellis.TRELLIS_EUCLIDEAN) # data preprocessing to generate metrics for Viterbi
+</programlisting>
+
+<para>
+Now the VA can run once it is supplied by the initial and final states.
+The initial state is known to be 0; the final state is usually
+forced to some value by padding the information sequence appropriately.
+In this example, we always send the the same info sequence (we only randomize noise) so we can evaluate off line the final state and then provide it to the VA (a value of -1 signifies that there is no fixed initial
+or final state). The VA outputs the estimates of the symbols x_k which
+are then packed to shorts and compared with the transmitted sequence.
+</para>
+<programlisting>
+ va = trellis.viterbi_s(f,K,0,-1) # Put -1 if the Initial/Final states are not set.
+ fsmi2s = gr.unpacked_to_packed_ss(bitspersymbol,gr.GR_MSB_FIRST) # pack FSM input symbols to shorts
+ dst = gr.check_lfsr_32k_s();
+</programlisting>
+
+
+
+
+<para>
+The function returns the number of shorts and the number of shorts in error. Observe that this way the estimated error rate refers to
+16-bit-symbol error rate.
+</para>
+<programlisting>
+return (ntotal,ntotal-nright)
+</programlisting>
+
+
+
+</sect1>
+
+
+<!--=====================================================-->
+<sect1 id="future"><title>Future Work</title>
+
+
+
+<itemizedlist>
+
+<listitem>
+<para>
+Improve the documentation :-)
+</para>
+</listitem>
+
+<listitem>
+<para>
+automate fsm generation from generator polynomials
+(feedforward or feedback form).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Optimize the VA code.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Provide implementation of soft-input soft-output (SISO) decoders for
+potential use in concatenated systems. Also a host of suboptimal
+decoders, eg, sphere decoding, M- and T- algorithms, sequential decoding, etc.
+</para>
+</listitem>
+
+
+<listitem>
+<para>
+Although turbo decoding is rediculously slow in software,
+we can design it in pronciple. The question is, should
+we use the FSM and SISO abstractions and cnnect them
+through GNU radio or should we implement turbo-decoding
+as a single block (issues with buffering between blocks).
+</para>
+</listitem>
+
+</itemizedlist>
+
+</sect1>
+
+
+</article>